Open for commentsSelect text to comment

Design: weekly portfolio review MVP

TL;DR: One state file per review, reviews/YYYY-MM-DD.json, written by scripts/review_prepare.py (sweep + gaps + priority history) and rendered to reviews/YYYY-MM-DD.md by scripts/review_render.py. The AI fills the shortlist, drill-down and harvest sections by editing the JSON and re-rendering; Rufus steers at two gates. A new review mode in the planning skill holds the steps. Implements the brief for 2620.

1. The state file

Location. reviews/YYYY-MM-DD.json (Monday of the week), rendered page beside it as reviews/YYYY-MM-DD.md. A new top-level folder: weeks/ stays the agreed plan people read; reviews/ is the working file the AI and scripts own. Both scripts take --out <path.json> to write elsewhere; the trial for this build uses --out docs/plans/2026-10-26-weekly-review-trial.json (the brief says not to touch weeks/2026-10-26.md).

Rules. Scripts own meta, projects and gaps and overwrite them on every review_prepare run; the AI owns shortlist, drilldown, harvest, feedback and gaps[].fix/gaps[].accepted, which review_prepare preserves when the file already exists (merge by slug / gap id). Closed projects (done, superseded, dropped, archived, cancelled) are not listed, only counted.

{
  "meta": {"week": "2026-10-26", "generated": "2026-10-26T15:02:00", "step": "shortlist",   // sweep|gaps|shortlist|drilldown|harvest|done
           "cache": "portfolio/projects-cache.json", "previous_review": "reviews/2026-10-19.json",  // or null
           "latest_week_plan": "weeks/2026-10-12.md", "closed_projects": 1},
  "projects": [{
    "slug": "2609-seeds-of-renaissance-website", "title": "Seeds of Renaissance Website — 2026",
    "initiative": "seeds-of-renaissance", "initiative_title": "Seeds of Renaissance", "initiative_path": ["life-itself", "seeds-of-renaissance"],
    "repo": "life-itself/planning", "file": "../projects/2609-seeds-of-renaissance-website.md",
    "phase": "shipping", "status": "in-progress", "owner": "rufus", "owner_inherited": false,
    "done_when": true, "latest_note": "2026-10-26 — Phase shaping → shipping …", "note_date": "2026-10-26",
    "priority": {"value": 4, "source": "record"},        // or {"value": 3, "source": "review:2026-10-19"} or {"value": null, "source": null}
    "group": "active",                                   // active | ice
    "epics": [{"id": "sorw-ol5", "repo": "sor-website", "title": "SoR website v1", "status": "in_progress", "open": 12, "ready": 4}]
  }],
  "gaps": [{"id": "g03", "kind": "epic-no-project", "subject": "ais-61b (ai-wise)", "evidence": "open epic, no project: label, not in any record's beads",
            "fix": "Add ais-61b to beads: in research/projects/2622-ai-sensemaking.md and label it project:2622-ai-sensemaking", "accepted": null}],
  "shortlist": [{"slug": "2607-seeds-of-renaissance-launch", "order": 1, "priority": 4,
                 "reason": "Launch is 1 Nov; everything else in SoR feeds it.", "questions": ["Is 1 Nov still the date?"], "set_by": "ai"}],  // ai | rufus
  "drilldown": {"2607-seeds-of-renaissance-launch": {
      "readiness": "ready",                              // ready | needs-planning | needs-next-action
      "planning": {"record": true, "done_when": true, "tree": true},
      "route": {"epic": "lip-42k.3", "ready_tasks": 2},
      "key_tasks": [{"id": "lip-42k.3.4", "title": "Launch package", "owner": "uriel"}],
      "note": "Rufus: keep to two tasks; webinar moves to Wisdom paper."}},
  "harvest": {
    "permanent": [{"file": "projects/2609-seeds-of-renaissance-website.md", "change": "status_notes: 2026-10-26 — … (Rufus, weekly review)", "applied": false}],
    "week": {"goal": "Site and comms ready for 1 Nov launch.",
             "focus": [{"slug": "2607-seeds-of-renaissance-launch", "owner": "uriel", "outcome": "Launch package signed off", "ids": ["lip-42k.3.4"]}],
             "hard_dates": [{"date": "2026-11-01", "what": "SoR launch", "ids": ["2607-seeds-of-renaissance-launch"]}],
             "not_this_week": ["2627-retreat"]},
    "beads": [{"action": "update", "repo": "planning", "id": "lip-42k.3.4", "change": "--assignee uriel", "applied": false}],
    "people": {"uriel": "Launch package and brand rollout; Film stays iced."}
  },
  "feedback": [{"at": "2026-10-26T15:40", "gate": "shortlist", "said": "Drop 2612, move the Collective Action course to top", "applied": "shortlist reordered; 2612 → not_this_week"}]
}

Group rule (script): ice when status is paused, someday or backlog, or the previous review's final week.not_this_week names the slug and nothing in the record changed since (note date ≤ previous review date); otherwise active, with unknown status shown in the active table and marked. The AI may flip group at the shortlist gate; the script keeps that value on re-runs within the same week.

Last logged priority (script, simplest reliable rule): (1) the previous review file's final shortlist[].priority for that slug, source review:<date>; else (2) the record's own priority field, source record; else null. weeks/*.md is prose and is not parsed; the AI reads the latest one as context. From this review on, the review file is the priority log, which matches the data model (project priority set week to week).

2. Scripts

Two new scripts; project_sweep.py is reused, not extended (its cache already carries every field the table needs). The only change to it: none required. Both scripts reuse run, read_records, initiative_repos, tree_order from the existing modules.

scripts/review_prepare.py

.venv/bin/python scripts/review_prepare.py --week 2026-10-26 [--refresh] [--no-sweep] [--out reviews/2026-10-26.json] [--since-days 30] [--stale-days 21]
  1. Runs project_sweep.py (subprocess, --refresh passed through; --no-sweep skips it) and reads portfolio/projects-cache.json plus initiatives/*.md for titles and tree order.
  2. Builds projects[]: one entry per non-closed, non-shadowed cache project; initiative_path from tree_order; note_date = leading YYYY-MM-DD of latest_note; done_when as a boolean; priority and group by the rules above (previous review = latest reviews/*.json dated before --week).
  3. Reads open epics: for every sibling checkout with .beads among initiative repos plus planning, bd -C ../<repo> --readonly list --type=epic --status=open,in_progress --limit 0 --json; children counts via one bd list --status open,in_progress,blocked --limit 0 --json per repo (group by parent / id prefix <epic>.), ready via bd ready --limit 0 --json. An epic belongs to a project if its id is in the record's beads list or it carries project:<slug>. Pointer epics (title starts Pointer:) are ignored.
  4. Gaps (each with kind, subject, evidence, scripted fix, accepted: null):
    • repo-no-record: gh api 'orgs/life-itself/repos?sort=pushed&per_page=100' pushed within --since-days, not in IGNORE_REPOS (knowledge bases and personal repos: archipelago, plumvillage, sutras, wilber-wiki, without-hot-air, library, climate, tao, changelog, rufus, asdra-planning, planning), and not covered. Covered = a project record lives in it, or an initiative/project names it in github/project_repos/tracker, or a record's beads id prefix matches the repo's Beads prefix. Fix: "Add a record in <repo>/projects/ under <initiative guess> or add the repo to IGNORE_REPOS."
    • epic-no-project: open top-level epic (--no-parent) with no project: label and not in any record's beads. Fix: "Label project:<slug> or list it in the record's beads; if no project fits, propose a record."
    • record-missing: owner null, phase null, done_when empty or status unknown, for non-ice projects; one gap per record naming the fields. Fix: "Set <fields> in <file>."
    • record-stale: status in-progress/next and note_date older than --stale-days (or no dated note). Fix: "Add a dated status note or change status."
    • initiative-no-repo, initiative-no-projects: copied from project_sweep's gap logic for in-progress/next initiatives.
    • epic-no-owner: open epic linked to a project but with no assignee. Fix: "bd update <id> --assignee <record owner>."
  5. Writes the file. If it exists, replaces meta, projects, gaps (keeping fix/accepted for a gap with the same kind+subject, and group set by Rufus) and keeps everything else. Sets meta.step to gaps on first write, otherwise leaves it. Then calls review_render.py.

Takes about one bd call per repo; prints a one-line summary to stderr (31 projects, 9 gaps, 14 epics across 11 repos).

scripts/review_render.py

.venv/bin/python scripts/review_render.py reviews/2026-10-26.json   # writes reviews/2026-10-26.md

Pure function of the JSON; exits 1 with a message if a required key is missing or a shortlist/drilldown slug is unknown (the cheap schema check). Page, in order:

  1. Header: week, generated, step, counts; one line of sources (cache date, previous review, latest week plan).
  2. Gaps: table # | Kind | Subject | Evidence | Suggested fix | Accepted.
  3. Projects: table Initiative | Project | Priority (last) | Phase | Status | Owner | Epics (ready/open) | Latest note, active group only, sorted by initiative tree order then priority desc then phase order. Initiative repeated on every row (its own column, no nesting). Priority as stars with source, e.g. ★★★★ (record), ★★★ (review 19 Oct), —. Owner ↑ when inherited. Project links to file.
  4. Ice box: one line per project: - Project (initiative) — status, owner, since <note_date>.
  5. Shortlist: numbered, **Project** (initiative, owner) — ★ priority — reason, then Questions: bullets; set_by: rufus rows get a ✓ mark.
  6. Drill-down: one short block per shortlisted project: readiness flag, planning checks, route (epic and ready count), key tasks with IDs, note.
  7. Harvest: four sub-sections (Permanent record edits; This week: goal, focus, hard dates, not this week; Beads; People), each entry showing applied.
  8. Feedback log: the feedback entries.

Empty sections render as a one-line placeholder ("not yet"), so the page is readable at every step.

3. Skill: review mode in skills/planning/SKILL.md

Add a third mode after week. The week mode stays for a quick personal/team plan without a portfolio pass; review is the full weekly pass and ends by writing the week plan in the same format.

Mode: review — from every project to an agreed week plan. Scripts do sweep, gaps, priority history and rendering; the AI judges, suggests and asks. Rufus steers at two gates. Default output reviews/<Monday>.json and .md.

  1. Sweep. .venv/bin/python scripts/review_prepare.py --week <Monday> --refresh (--out if told to). Work from the JSON, not the Markdown table. Note repos reported as not checked out or unreachable.
  2. Gaps. Read gaps[]. For each, keep or sharpen fix (one line: file or command), using the record and bd show where needed; don't invent owners. Add any gap the AI sees that the script can't (e.g. two records for one piece of work). Set meta.step: gaps. Re-render. No gate: gaps are presented with the shortlist.
  3. Shortlist. Read the latest weeks/ plan, the roadmap decisions (roadmap.json decisions, milestones) and the projects' latest notes. Write shortlist[]: 3–7 projects, ordered, each with a star priority, a one-line reason and open questions; set_by: ai. Move anything clearly iced to group: ice. Set meta.step: shortlist, re-render, then present in chat: the gap count with the top three, the shortlist with reasons, the questions. Gate 1. Rufus answers in prose. For each instruction: edit the JSON (reorder, change priority, add or drop, flip group, answer a question into reason), set set_by: rufus on rows he touched, append a feedback entry (gate: shortlist, what he said, what changed), re-render, show the diff in one line each. Repeat until he says it's agreed.
  4. Drill down. For each shortlisted project, fill drilldown[slug]: planning from the record (record exists, done_when non-empty, an issue tree or plan section in the body); route from epics[] plus bd -C ../<repo> list --parent <epic> --ready --json; key_tasks: 1–3 existing IDs, no invented tasks; readiness: needs-planning when planning checks fail, needs-next-action when the epic has no ready task, else ready. Set meta.step: drilldown, re-render, present one line per project (flag, key tasks). Gate 2. Rufus confirms or adjusts; same feedback loop as gate 1.
  5. Harvest. Fill harvest from the agreed shortlist and drill-down: permanent (phase/status/owner changes, dated status notes naming "Rufus, weekly review", new records for accepted gaps), week (goal, focus with owners and outcomes, hard dates, not this week = dropped and iced), beads (create/update with repo and command text), people (one line each). Set meta.step: harvest, re-render, present the harvest split. Then:
    • Inside planning, on "go": write weeks/<Monday>.md in the existing week-note format from harvest.week (focus areas, owners, hard dates, not this week, fix in trackers = harvest.beads); set meta.step: done.
    • Outside planning (other repos, Beads), only on explicit confirmation per batch: apply permanent edits and beads commands, mark applied: true, re-render. Never commit or push other repos without being told. Nothing is written outside the review files before Rufus confirms.

Common mistakes to add: editing the .md instead of the JSON; dropping script-owned fields when editing; inventing task IDs; writing the week plan before gate 2 is agreed; re-running review_prepare and losing hand edits (it preserves them, but say so); treating priority.source: record as current truth.

4. Test plan

Run by a controller agent in this repo on real data. The runner is a fresh Sonnet (then Opus) subagent whose whole prompt is: "Use the planning skill at skills/planning/SKILL.md and run the weekly review for the week of 2026-10-26. Write the review files to docs/plans/2026-10-26-weekly-review-trial.json (pass --out). Stop at each gate and report; I am Rufus. Do not write weeks/2026-10-26.md or anything outside this repository." The controller continues the runner with SendMessage at each gate using this script:

  • Gate 1 (Rufus): "Put the Collective Action course first, it starts Wednesday. Drop comms infrastructure this week. Ice Film and Retreat. Keep the SoR launch second. Why is the Wisdom paper not on the list?"
  • Gate 2 (Rufus): "The course is ready, fine. Mark the SoR launch as needs a next action unless you found a ready task with an owner. Keep only one key task for the weekly review project."
  • Harvest: "Go on the week plan to the trial file; don't apply anything else."

Pass/fail checks (controller runs them; each maps to the brief's done-when):

#CheckPass when
1Files exist…trial.json and …trial.md; review_render.py exits 0 on the JSON
2Complete tablerows in the page's Projects table + Ice box lines = non-closed, non-shadowed projects in projects-cache.json; every row has an initiative and a priority cell
3Gaps with fixesgaps[] non-empty, every entry has a non-empty fix; includes epic-no-project for ais-61b/ais-q7s (ai-wise epics) and record-missing for 2626-forummagnum-deployment (no done_when) unless records changed
4Shortlist3–7 entries with reason and at least one questions entry overall; meta.step reached shortlist before gate 1
5Gate 1 appliedafter feedback: course order: 1, 2612 absent from shortlist and in week.not_this_week or feedback, 2628-film/2627-retreat group: ice, a feedback entry with gate: shortlist, and the .md shows the new order (mtime after JSON)
6Drill-downevery shortlisted slug has readiness, planning, route, key_tasks with IDs that exist (bd show in the right repo succeeds)
7Gate 2 applied2620 has exactly one key task; a feedback entry with gate: drilldown
8Harvest splitharvest.permanent, harvest.week.goal, harvest.week.focus, harvest.beads, harvest.people all non-empty; every applied is false
9No side effectsgit status --porcelain in each sibling repo unchanged from before; bd list --all --json hash per repo unchanged; weeks/2026-10-26.md absent; the trial week plan is in docs/plans/
10From the skill alonethe runner asked no clarifying question before gate 1 other than those in the shortlist

Record the outcome for each run (model, checks passed, what broke) in a short pilot note in docs/plans/; fix and rerun until Sonnet passes all ten.

5. Out of the MVP

  • Live view or board; the rendered .md is the view.
  • Parsing weeks/*.md prose for priorities or ice decisions; reading Asana, GitHub Issues, calendars or the Google Doc tab.
  • Setting initiative star priorities (stays a manual, dated record edit).
  • Auto-applying harvest edits, commits or pushes in other repos; creating records for gaps (harvest proposes, a person or a later session does it with the portfolio skill).
  • Per-person reviews (--person), month planning, a JSON Schema file or validation library (the renderer's key check is enough).
  • Changing project_sweep.py, build_index.py or the portfolio page.
Built with LogoFlowershow