aboutsummaryrefslogtreecommitdiff
path: root/docs/design/wrapup-routing-spec.org
diff options
context:
space:
mode:
Diffstat (limited to 'docs/design/wrapup-routing-spec.org')
-rw-r--r--docs/design/wrapup-routing-spec.org181
1 files changed, 0 insertions, 181 deletions
diff --git a/docs/design/wrapup-routing-spec.org b/docs/design/wrapup-routing-spec.org
deleted file mode 100644
index 0091806..0000000
--- a/docs/design/wrapup-routing-spec.org
+++ /dev/null
@@ -1,181 +0,0 @@
-#+TITLE: Wrap-Up Inbox/Transcript Routing — Spec
-#+AUTHOR: Craig Jennings
-#+DATE: 2026-06-13
-#+TODO: TODO | DONE SUPERSEDED CANCELLED
-
-* Metadata
-| Status | ready for review |
-|----------+-----------------------------------------------------|
-| 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. Its input is filed keepers, not raw inbox files (decision: Reading B): tasks =process-inbox= accepted and filed into the local =todo.org= this session whose inferred home is a different project. When the router finds such a keeper, it surfaces it in a list, one line each: the task, 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)
-
-*Candidate set (what the router considers).* Reading B means the router does not scan the whole local backlog — it would otherwise suggest moving legitimate local tasks every wrap. The candidate set is keepers =process-inbox= filed this session whose inferred home differs from the current project. How those are marked is an implementation detail for Phase 3/4: either =process-inbox= tags a cross-project-candidate keeper at file time, or the router infers from a =CREATED= stamp dated this session plus content. The reviewer should pin which; the design constraint is "session-filed inbox keepers only, never the standing backlog."
-
-*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 share discovery code while staying separate steps. (Resolved: D1 keeps them separate, with the router acting on filed keepers rather than inbox files.)
-
-** 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 [6/6]
-
-** 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.
-
-** DONE Separate router step, operating on filed keepers (Reading B)
-- Context: the sanity check gates the wrap on inbox/ contents; the router is optional. The deeper question was the router's input — raw inbox files (Reading A, which overlaps the sanity check) or already-filed keepers that belong elsewhere (Reading B, a todo-routing concern).
-- Decision: We will keep the router a separate optional sub-step after the sanity check, and its input is Reading B: accepted keepers process-inbox filed into the local =todo.org= whose inferred home is another project. The sanity check stays a pure inbox gate; the router is a todo-routing action that shares only the destination-discovery code.
-- Consequences: easier — each step has one job, the gate can't be muddied by an optional action, and the router never competes with the inbox gate over the same files. Harder — the candidate set (which local tasks the router considers) needs a marking mechanism (see the Implementer "candidate set" note); Reading A's "dispose raw inbox files at wrap" convenience is given up.
-
-** DONE Transcript routing deferred to vNext
-- 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 defer transcript routing to vNext. Both the source-location dependency and the file-only-vs-extract-action-items question are deferred with it, to be settled when the vNext work is specced. v1 ships task routing only.
-- Consequences: easier — v1 isn't blocked on the unresolved source location. Harder — until vNext, a meeting recording still has no automatic home; only its action items (if filed as tasks) route through v1.
-
-** DONE Keep defer-and-stage and the router as distinct policies
-- 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 turns an inbox item into a =todo.org= task — overlapping surface with this router.
-- Decision: We will keep them distinct. Defer-and-stage parks a proposal-under-review locally as a VERIFY; the router moves an accepted keeper to its home project as a TODO. They differ on review status (proposal vs accepted) and destination (local vs cross-project), and share only the atomic move helper, not the policy. Reading B makes the split clean: the router acts on accepted keepers, never on proposals under review.
-- Consequences: easier — two clear, non-competing policies on one shared primitive. Harder — the workflow prose must name the boundary so a future reader doesn't collapse them 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 + candidate-set marking
-Infer destination from item content against the discovered list, with a confidence label. Pure function over (item, project-list) → (destination, confidence). Unit-tested: strong match (project named in item), weak match (topic-only → low-confidence), no match (stays put). Also settle the candidate-set marking (tag at file time vs CREATED-this-session inference) so the router considers only session-filed inbox keepers, never the standing backlog.
-
-** 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.
-
-** 2026-06-13 Sat @ 01:36:28 -0500 — Craig Jennings + Claude Code (rulesets) — author
-- What: resolved all three open decisions. The router's input is Reading B (filed keepers that belong elsewhere, not raw inbox files), so D1 keeps it a separate sub-step from the inbox gate and D5 keeps it distinct from the defer-and-stage router; D4 defers transcript routing to vNext. Reworked the design (input definition, a candidate-set note bounding the router to session-filed keepers) and Phase 3 to match. Cookie now [6/6]; Status moved to ready-for-review.
-- Why: Craig chose Reading B after the A-vs-B input ambiguity surfaced as the root under D1 and D5. Reading B keeps the inbox gate, the router, and defer-and-stage each simple instead of entangling three mechanisms.
-- Artifacts: this spec; the candidate-set marking mechanism is the one detail flagged for spec-review to pin.