From 7c120073a7de96e67a4f51e539c45d2d22d74f81 Mon Sep 17 00:00:00 2001 From: Craig Jennings Date: Thu, 2 Jul 2026 00:43:15 -0400 Subject: feat(routing): wire the wrap-up cross-project router end to end This closes the build half of the wrap-up routing spec: Phases 2 and 4 here, with the engine and discovery already shipped. inbox.org's "File as TODO" disposition now runs route_recommend on each keeper and stamps :ROUTE_CANDIDATE: on strong and weak matches, so the wrap-up router has a candidate set without ever scanning the standing backlog. wrap-it-up.org Step 3 gains the optional router after the inbox sanity check, with the gate-vs-optional split named in the prose: surface the batch with destinations and confidence labels, then go or skip. An empty set stays silent. The go path is mechanical rather than prose-driven: the new route-batch helper lists candidates read-only, and on go extracts each subtree (children ride along, markers stripped, headings promoted), delivers it via inbox-send for provenance, and removes the local copy only after a successful send, rewriting todo.org per send so a crash never strands an already-sent task locally. Overlapping candidate spans (a tagged child inside a tagged parent) are a loud conflict, left in place with a non-zero exit, because routing either span would silently take the other along. A 13-test bats suite covers list/backlog exclusion, empty-set silence, delivery with provenance and children, promotion, drawer pruning, the no-todo.org destination, failed-send recovery with the marker intact, the nested-candidate conflict, and duplicate-marker dedupe. cross-project.md notes the router as a sanctioned cross-project write path. --- todo.org | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) (limited to 'todo.org') diff --git a/todo.org b/todo.org index 1882a89..b85f288 100644 --- a/todo.org +++ b/todo.org @@ -128,14 +128,8 @@ The 2026-06-23 inbox consolidation (24ca58d) merged =process-inbox= + =monitor-i *** 2026-06-28 Sun @ 13:02:42 -0400 Built the recommendation engine + destination discovery Added =.ai/scripts/route_recommend.py= (canonical + mirror): pure =recommend(item, projects) → (destination, confidence)= with strong (name/path literal, word-boundary matched, dot-stripped alias aware), weak (distinctive name-token overlap), and none tiers; a multi-way top-tier tie downgrades to weak with a deterministic pick (most overlap, then alphabetical); empty list → none. The CLI (=--item=, =--exclude=) reuses =inbox-send.py='s =discover_projects= via importlib so the candidate set matches inbox-send's project universe. 13 tests (the five spec'd cases + boundary/path/strong-beats-weak + 3 sandboxed CLI integration tests), full =make test= green. Covers spec Phases 1 + 3. Next sub-tasks (=:ROUTE_CANDIDATE:= marker, wrap-up router) call this engine. -*** TODO [#B] =:ROUTE_CANDIDATE:= marker in inbox process mode :feature:solo: -Extend =inbox.org= process mode's "File as TODO" disposition (core §3 / Phase D) to stamp =:ROUTE_CANDIDATE: = on any keeper whose inferred home differs from the current project (uses the engine above). Edit the canonical, sync the =.ai/= mirror, verify sync-check clean. Spec Phase 2 / D8. Spec: [[file:docs/specs/wrapup-routing-spec.org]]. (Originally targeted =process-inbox.org=, merged into =inbox.org= by the 2026-06-23 consolidation.) - -*** TODO [#B] Wrap-up router sub-step in wrap-it-up.org :feature:solo: -Add the optional router to =wrap-it-up.org= Step 3 after the inbox sanity check: surface the =:ROUTE_CANDIDATE:= batch (task / destination / delivery mode / confidence), go/skip; on go, per candidate =inbox-send= a one-task handoff to the destination's =inbox/= and remove the keeper from the local =todo.org=; empty set = silent. Name the gate-vs-optional split in the prose. Edit canonical + sync mirror. Spec Phase 4 / D7 / D9. Spec: [[file:docs/specs/wrapup-routing-spec.org]]. - -*** TODO [#B] Wrap-up routing — test surface :test:solo: -Unit: recommendation engine (strong/weak/none, two-project tie, empty list); marker stamping (cross-project keeper tagged, local keeper not, standing backlog never). Integration (bats, fixture projects + temp =todo.org=): go issues N =inbox-send= calls to the right inboxes with sources removed; skip leaves all in place; empty set = zero interaction; a candidate whose destination has =inbox/= but no =todo.org= still delivers. Spec: [[file:docs/specs/wrapup-routing-spec.org]] (Acceptance criteria). +*** 2026-07-02 Thu @ 00:36:12 -0400 Phases 2 + 4 + test surface landed — marker, router, route-batch helper +inbox.org's "File as TODO" disposition now runs route_recommend on each keeper and stamps =:ROUTE_CANDIDATE: = on strong/weak matches (none stamps nothing; local keepers stay unstamped) — spec Phase 2 / D8. wrap-it-up.org Step 3 gained the optional router directly after the inbox sanity check, with the gate-vs-optional split named in the prose: surface the batch (task / destination / delivery mode / confidence, weak visibly labeled), go/skip, empty set = zero interaction — spec Phase 4 / D7 / D9. The go path is mechanical: new =.ai/scripts/route-batch= (--list read-only, --go extracts the subtree minus the marker with children riding along and headings promoted, delivers via inbox-send for provenance, removes the local subtree only after a successful send; a failed send leaves the task in place and exits non-zero). Test surface: engine unit tests existed (13); route-batch adds a 9-test bats suite (list/backlog-exclusion, empty-set silence, list-modifies-nothing = skip semantics, delivery + provenance + children, local-task survival, drawer-minus-marker, inbox-without-todo.org delivery, empty go, failed-send recovery). cross-project.md notes the router as a sanctioned cross-project write path. make test green, sync clean. *** TODO [#B] Wrap-up routing — manual end-to-end validation :test: What we're verifying: a real keeper routes through a live wrap and the destination actually files it. -- cgit v1.2.3