diff options
Diffstat (limited to 'docs/design')
| -rw-r--r-- | docs/design/wrapup-routing-spec.org | 177 |
1 files changed, 177 insertions, 0 deletions
diff --git a/docs/design/wrapup-routing-spec.org b/docs/design/wrapup-routing-spec.org new file mode 100644 index 0000000..396fb45 --- /dev/null +++ b/docs/design/wrapup-routing-spec.org @@ -0,0 +1,177 @@ +#+TITLE: Wrap-Up Inbox/Transcript Routing — Spec +#+AUTHOR: Craig Jennings +#+DATE: 2026-06-13 +#+TODO: TODO | DONE SUPERSEDED CANCELLED + +* Metadata +| Status | draft | +|----------+-----------------------------------------------------| +| Owner | Craig Jennings | +|----------+-----------------------------------------------------| +| Reviewer | Codex (spec-review) | +|----------+-----------------------------------------------------| +| Related | [[file:../../todo.org][todo.org: wrap-up routing task]] · [[file:2026-06-13-wrapup-inbox-transcript-routing-proposal.org][archsetup proposal]] | +|----------+-----------------------------------------------------| + +* Summary + +At wrap-up, an inbox handoff that belongs to another project has nowhere to go but the current project's =todo.org= or a deferral. This adds an optional routing step to =wrap-it-up.org=: surface the items that belong elsewhere, recommend a destination project for each, and move the whole batch there on one confirmation. A parallel step files meeting-transcript recordings into the right project's =assets/=. + +* Problem / Context + +=process-inbox.org= dispositions each handoff as act / fold / file / reject, and "file as TODO" lands the task in the *current* project's =todo.org=. When the real home is a different project, the choices today are: file it locally and let it rot in the wrong tracker, hand-edit two projects' =todo.org= files, or defer it and carry the debt to next session. + +The wrap-up's existing Step 3 "Inbox sanity check" only counts unprocessed items and blocks the wrap until they clear. It answers "is the inbox clean?" — it doesn't route anything. + +Meeting transcripts have the same homelessness: a recording dropped during a session belongs in some project's =assets/=, but nothing moves it there at wrap. + +The friction is small per-item but recurring, and the manual cross-project edit is error-prone (two files, two repos, easy to leave one half-done). + +* Goals and Non-Goals + +** Goals +- At wrap-up, surface inbox items (and transcripts) whose home is a different project, with a recommended destination each. +- Move the whole batch on one confirmation ("go with recommendations") or leave it entirely ("skip"). No per-item triage. +- Move a task/event item as a proper org task into the destination's "Open Work" section per =todo-format.md=; move a transcript as a flat-filed artifact per =working-files.md=. +- Keep the move atomic and visible (it shows in the destination's next git diff, with a provenance note). +- Discover any project with a =todo.org= as a candidate destination, not only =.ai/= projects. + +** Non-Goals +- Not a wrap gate. A skip is a clean, complete wrap. +- Not per-item triage. The interaction is batch-level: go or skip. +- Not a replacement for =process-inbox.org='s value gate. Routing assumes the item is already an accepted keeper. +- Not a confidence-free auto-mover. A low-confidence destination recommendation says so, and the batch "go" stays trustworthy because the surfaced list is reviewable before the keystroke. + +** Scope tiers +- v1: task/event routing to a destination project's =todo.org=. The interaction, the recommendation engine, the atomic move helper, the widened project discovery. +- Out of scope: per-item destination editing, an interactive correction loop, moving items that aren't accepted keepers. +- vNext: meeting-transcript filing (gated on the unresolved source-location decision and the file-vs-file+extract question — see Decisions). + +* Design + +** User-facing (the wrap interaction) + +The router is a new sub-step of =wrap-it-up.org='s Step 3, running after the existing inbox sanity check. When it finds inbox items whose recommended home is a different project, it surfaces them as a list, one line each: the item, the recommended destination project, and a confidence marker when the inference is weak. Then two options, batch-level: + +1. Go with the recommendations — apply every recommended move. +2. Skip — leave the whole batch in place. A skip is a clean wrap. + +That is the entire interaction. No per-item walk. The surfaced list is the review surface; the single keystroke is trustworthy because the list was reviewable and low-confidence recommendations flagged themselves. + +A move of a task/event relocates it into the destination project's "Open Work" section as a proper org task (terse heading, body for detail, tags on the heading line, per =todo-format.md=), and removes it from the source. A skipped or unroutable item stays where it is; the existing sanity check still governs whether the wrap is clean. + +** Implementer (the mechanics) + +*Destination discovery.* Widen the project-discovery filter from "directory with a =.ai/protocols.org= marker" (what =inbox-send.py= and the =ai= launcher use) to "directory with a =todo.org= containing a level-1 'Open Work' heading." A plain code repo Craig keeps a =todo.org= in is a valid destination; an =.ai/= directory is not required. + +*Destination anchor.* Reuse =todo-cleanup.el='s existing matcher: =tc--find-section= locates the unique level-1 heading containing "Open Work" (case-insensitive) and returns =nil= / ='multiple= when absent or ambiguous. A destination whose =todo.org= lacks a clean Open Work heading is surfaced and skipped, never guessed at. + +*The move helper.* A small tool inserts a task subtree under a named project's "Open Work" heading and removes the source atomically — extend =todo-cleanup.el= (it already owns the section matcher and the subtree-move logic for =--archive-done=) or add a sibling =.ai/scripts= tool. Hand-editing across two repos is the error-prone path this replaces. + +*Recommendation engine.* Infer the destination from the item's content — project names, file paths, topic words — matched against the discovered project list. Conservative by design: a weak match is labeled low-confidence so "go" stays a safe single keystroke. The engine is the interesting, uncertain part; it earns the spec. + +*Cross-project write discipline.* Moving an item into project X's =todo.org= writes into X's scope (=cross-project.md=). The batch "go" authorizes it, but the move stays visible (X's next git diff) and leaves a one-line provenance note on the moved task naming the source project. + +* Alternatives Considered + +** Per-item triage instead of batch go/skip +- Good, because it gives precise control over each destination. +- Bad, because it taxes the common case (a batch that's all-correct, or all-stay) with a walk. Craig explicitly asked for two options, not a triage loop. +- Neutral, because per-item correction could return as a vNext refinement if batch-only proves too blunt. + +** Fold the router into the existing Inbox sanity check step +- Good, because one inbox step is simpler than two. +- Bad, because the sanity check *gates* the wrap (blocks until clean) and the router is *optional* (skip is clean). Merging a blocking check with an optional action muddies both. +- Neutral, because the two could share discovery code while staying separate steps. (This is open decision D1.) + +** Reuse process-inbox's "file as TODO" with a destination argument +- Good, because it avoids a second mechanism. +- Bad, because =process-inbox= runs per-item mid-session against the local project; the router runs at wrap, batch-level, cross-project. Different cadence, different scope. +- Neutral, because both ultimately call the same atomic move helper — the helper is the shared primitive, the two callers stay distinct. + +* Decisions [/] + +** DONE Reuse the Open Work matcher for destination anchoring +- Context: the move needs a reliable insertion point in the destination =todo.org=; guessing risks corrupting another project's file. +- Decision: We will reuse =todo-cleanup.el='s =tc--find-section "open work"= matcher, which already handles the unique / missing / ambiguous cases, and skip+surface any destination without a clean Open Work heading. +- Consequences: easier — no new parser, consistent with =--archive-done=. Harder — destinations must carry the "Open Work" heading convention, so a project with a differently-named section is silently unroutable until it conforms. + +** DONE Move atomically through a helper, never hand-edit two repos +- Context: a move touches two files in two repos; a half-done move loses or duplicates a task. +- Decision: We will route every move through one helper (extend =todo-cleanup.el= or a sibling =.ai/scripts= tool) that inserts under the destination's Open Work heading and removes the source as one operation. +- Consequences: easier — no partial-move corruption, one place to test. Harder — a new helper to build and cover with tests before the router can ship. + +** DONE Cross-project writes stay visible and carry provenance +- Context: writing into another project's =todo.org= crosses the =cross-project.md= scope boundary. +- Decision: We will treat the batch "go" as the authorization, leave the move visible in the destination's git diff, and stamp a one-line provenance note (source project + date) on each moved task. +- Consequences: easier — the boundary rule is honored without a per-move prompt. Harder — the destination's next session sees an externally-authored task it didn't file, so the provenance note is load-bearing, not decorative. + +** TODO Separate router step or merge into the inbox sanity check +- Owner / by-when: Craig / before implementation. +- Context: the sanity check gates the wrap; the router is optional. archsetup leans separate; so do I (a blocking check and an optional action read more clearly apart). But it's Craig's wrap-cadence call. +- Decision: We will (proposed) keep the router a *separate* optional sub-step running after the sanity check, sharing only the discovery code. +- Consequences: easier — each step has one job, the optional one can't accidentally block. Harder — two inbox-touching steps in one wrap, slightly more workflow prose. + +** TODO Transcript routing — scope and trigger +- Owner / by-when: Craig / before any transcript work starts. +- Context: transcripts file as artifacts, not tasks, and a meeting usually produces both a recording to keep and action items to track. Two unknowns block it: where recordings accumulate (a recordings inbox, a downloads dir, wherever the meeting tooling drops them), and whether filing should also extract action items into the destination's =todo.org=. +- Decision: We will (proposed) defer transcript routing to vNext, file the recording as an artifact only, and let action items route through the v1 task-router as a separate batch — keeping documents and tasks orthogonal. +- Consequences: easier — v1 ships without the unresolved source-location dependency. Harder — a meeting's recording and its action items get handled by two mechanisms, which Craig may prefer combined into one "process this transcript" move. + +** TODO Reconcile with the process-inbox defer-and-stage router +- Owner / by-when: Craig + author / before implementation. +- Context: the 2026-06-12 Skeptical Review added a defer-and-stage path in =process-inbox.org= that files a =[#B]= VERIFY for shared-asset proposals parked for review. That also moves an inbox item toward a =todo.org= task — overlapping surface with this router. +- Decision: We will (proposed) keep them distinct: defer-and-stage parks a *proposal under review* locally as a VERIFY; this router moves an *accepted keeper* to its home project. Shared primitive is the atomic move helper, not the policy. +- Consequences: easier — two clear, non-competing policies. Harder — the workflows must state the boundary so a future reader doesn't collapse them into one and reintroduce the ambiguity. + +* Implementation phases + +** Phase 1 — Widened project discovery +A discovery function returning every project with a =todo.org= that has a clean Open Work heading, reusing =tc--find-section=. Unit-tested against fixtures: =.ai/= project, plain-code-repo-with-todo, todo-without-Open-Work (excluded), ambiguous-Open-Work (excluded). Leaves the tree working — nothing calls it yet. + +** Phase 2 — Atomic cross-project move helper +Extend =todo-cleanup.el= (or sibling tool) with a "move this subtree into project X's Open Work" operation that inserts at the destination and removes the source as one step, stamping the provenance line. ERT coverage: successful move, missing-destination-heading refusal, source-removal-on-success, no-partial-move-on-failure. + +** Phase 3 — Recommendation engine +Infer destination from item content against the discovered list, with a confidence label. Unit-tested: strong match (project named in item), weak match (topic-only → low-confidence), no match (stays put). Pure function over (item, project-list) → (destination, confidence). + +** Phase 4 — Wrap-up step wiring +Add the router sub-step to =wrap-it-up.org= Step 3: surface the batch, the two options, apply-on-go via the Phase 2 helper. Per the D1/D5 decisions once settled. Sync the =.ai/= mirror. + +** Phase 5 — Transcript routing (vNext, gated on the transcript decision) +Only after the transcript-scope decision resolves. File a recording into the destination =assets/= per =working-files.md=, batch go/skip mirroring the task router. + +* Acceptance criteria +- [ ] At wrap, an inbox item naming another project is surfaced with that project as the recommended destination. +- [ ] "Go" moves every recommended item into its destination's Open Work section as a valid org task with a provenance line, and removes it from the source. +- [ ] "Skip" leaves every item in place and the wrap completes cleanly. +- [ ] A destination =todo.org= without a clean Open Work heading is surfaced and skipped, never corrupted. +- [ ] A low-confidence recommendation is visibly labeled in the surfaced list. +- [ ] A plain code repo with a =todo.org= (no =.ai/=) is a valid destination. +- [ ] A failed move leaves both source and destination unchanged (no partial move). + +* Readiness dimensions +- Data model & ownership: items are org subtrees; the destination owns the moved task after the move (provenance note records origin). N/A for remote/cached state — all local files. +- Errors, empty states & failure: missing/ambiguous Open Work heading → skip+surface; failed move → atomic no-op; empty routable set → router stays silent (no prompt). +- Security & privacy: N/A — local org files, no credentials or external services. +- Observability: the move shows in the destination's git diff plus the provenance line; the surfaced batch list is the pre-move view. +- Performance & scale: bounded by inbox size (single digits) and project count (tens); no hot path. +- Reuse & lost opportunities: reuses =tc--find-section= and todo-cleanup's subtree-move; widens existing discovery rather than adding a parallel one. +- Architecture fit & weak points: the recommendation engine is the weak point (a wrong-confident destination is the worst failure) — mitigated by the confidence label and reviewable batch list. +- Config surface: possibly a discovery-root list (defaults to =~/projects/=, =~/code/=, matching =inbox-send.py=). Name it if it needs to be user-visible. +- Documentation plan: =wrap-it-up.org= step prose; a note in =cross-project.md= that the router is a sanctioned cross-project write path. +- Dev tooling: ERT for the elisp helper + discovery; the existing =make test= picks up new test files by glob. +- Rollout, compatibility & rollback: additive workflow step; rollback is removing the sub-step. No persisted-data migration. +- External APIs & deps: none. + +* Risks, Rabbit Holes, and Drawbacks +- *Recommendation accuracy is the rabbit hole.* A confidently-wrong destination silently files a task in the wrong project. Dodge: keep the engine conservative, label low confidence, and keep the batch list reviewable before the keystroke. Don't chase a clever inference model in v1. +- *Two inbox-touching steps* (sanity check + router) risk reading as redundant. Dodge: the D1 decision states the gate-vs-optional split in the workflow prose. +- *Scope creep into transcripts* before the source-location question is answered would stall v1. Dodge: transcripts are explicitly vNext behind decision D4. + +* Review and iteration history + +** 2026-06-13 Sat @ 01:23:13 -0500 — Claude Code (rulesets) — author +- What: initial draft. Problem, goals/scope tiers, two-altitude design, alternatives, six decisions (three DONE from grounding, three TODO for Craig), five implementation phases, acceptance criteria, readiness dimensions, risks. +- Why: the archsetup 2026-06-13 handoff cleared the spec bar in inbox triage and was filed spec-bound rather than applied. This draft turns the proposal into a reviewable design with the open questions isolated as decision tasks. +- Artifacts: proposal source at =docs/design/2026-06-13-wrapup-inbox-transcript-routing-proposal.org=; grounded against =wrap-it-up.org= Step 3, =todo-cleanup.el= =tc--find-section=, and =inbox-send.py= discovery. |
