diff --git a/.abcd/development/intents/drafts/itd-82-drain-ledger-triage.md b/.abcd/development/intents/drafts/itd-82-drain-ledger-triage.md new file mode 100644 index 00000000..9bfe923d --- /dev/null +++ b/.abcd/development/intents/drafts/itd-82-drain-ledger-triage.md @@ -0,0 +1,86 @@ +--- +id: itd-82 +slug: drain-ledger-triage +kind: standalone +suggested_kind: standalone +bundle: null +spec_id: null +reclassification_history: [] +builds_on: [itd-4, itd-29, itd-46] +related_adrs: [adr-25, adr-27] +severity: major +--- + +# One Verb Sorts The Open Ledger Into Work That Ships Itself And Work That Needs A Human To Think + +## Press Release + +> **abcd turns "drain the backlog" from a nightly babysitting job into a single unattended verb.** `abcd drain` reads the open issue ledger, triages every ready issue, and routes each to the machine that already knows how to handle it: a self-contained defect is fixed autonomously — detector-first, gated by `make preflight`, opened as one PR per issue and never self-merged — while an issue that turns out to need a design decision is **promoted to an intent draft** (the `capture promote` path) seeded from the issue body, so the judgement lands in the intent→spec pipeline where a human owns it, not in a risky autonomous guess. Blocked, ops-only, and already-decided issues are skipped with a logged reason. When the run stops, one handoff shows every issue's disposition — this PR, that intent, or skipped-because. The maintainer's night collapses to two queues in the morning: PRs to review, and drafted intents to design. +> +> "I used to point the loop at the ledger and then hover, because the moment it hit something design-shaped it would either stall or — worse — try to design it," said Alice, a maintainer. "Now `drain` fixes the mechanical stuff while I sleep and *files the hard ones as intents for me to grill*. I wake up to a merge queue and a think queue, not a mess." + +## Why This Matters + +The C-phase `/abcd:run` dogfood proved the autonomous fix-loop works against `backlog: ledger`: detector-first fixes, dual-review catching real trust-boundary BLOCKs, one PR per issue. But it also proved the **triage** is the load-bearing, human-shaped part. Across the run, a person (the orchestrator) had to decide, per issue: is this a self-contained fix, a design-STOP that must not be attempted autonomously, or an ops/setup task that is not code at all? Get that wrong and the loop either stalls on a design decision or — the real hazard — makes one silently. + +That classifier is exactly what a maintainer should not hand-run every night, and exactly what abcd already has the surfaces to automate *around*: the run engine fixes (itd-29 / the run protocol's `backlog: ledger` mode), and `capture promote` (itd-46 / spc-30) elevates an issue to an intent draft with a transactional four-field back-link. What is missing is the **sorting hat in front of them** — the verb that reads the ledger, classifies each issue, and dispatches to fix / promote / skip. + +This matters beyond one nightly drain: it is the honest bridge between abcd's two substrates. Raw captures (iss-N, itd-4) accumulate faster than a human can triage; the designed value pipeline is intent→spec→run (itd-29). `abcd drain` is the conveyor that moves mechanical work straight to a PR and design work up into the intent pipeline, so neither queue silts up. It is project-agnostic: every abcd repo captures issues, every one benefits from a machine that drains the fixable and elevates the rest. + +## What's In Scope + +- **`abcd drain` — the ledger-triage front door.** Reads `abcd capture list --open --json`, takes ready issues (`blocked_by_open` empty), and per issue classifies + routes: + - **Fixable / self-contained defect** → the autonomous run path (itd-29 / run protocol, `backlog: ledger`): detector-first fix, `make preflight` gate, correctness + (trust-boundary) security review, one PR per issue, ledger `resolve` folded into the branch, **never self-merged**. + - **Design-STOP / feature-shaped / premise-change** → `capture promote ` (itd-46): draft an intent seeded from the issue body with the four-field back-link (`source_issue`/`promoted_to` + `related_issues` ↔ `related_intents`). The issue stays open; **no autonomous design or adoption decision is made** — the draft waits for a human to grill and sign off. + - **Ops / blocked / already-decided** → skip, with a logged reason (never a silent drop). +- **The classifier.** A deterministic pre-filter on ledger fields (drop blocked; honour an explicit design-STOP marker; route category=`future-work-seed`/feature-shaped to promote) plus a host-delegated LLM judgement (adr-25) for the ambiguous "is this genuinely self-contained?" call. **Fail-safe default: when confidence is low, treat as design-STOP (promote/defer), never as auto-fixable** — a wrong "fix it" is far costlier than a wrong "a human should look." +- **Auditable disposition handoff.** On stop/pause, a machine-readable record of every issue's route (fixed → PR, promoted → itd-N, skipped → reason) plus the classifier's rationale, so the triage is reviewable and re-runnable. +- **Cut A (this intent's shipping slice): fix-path first.** `abcd drain` ships with the classifier + the fix route + the skip route working against the existing run engine. Because `capture promote` (itd-46) is not yet built, the promote route in cut A **flags** each design-STOP issue as "needs promotion → design" with its reason and does **not** attempt an unbuilt verb — degrading loudly, never silently. The full promote route lands in a second cut once itd-46 ships. + +## What's Out of Scope + +- Building `capture promote` itself — that is **itd-46 / spc-30**; this intent *consumes* it (and, in cut A, stubs its slot). +- The autonomous fix engine and its resilience surface — **itd-29** and the run protocol; `drain` orchestrates them, it does not reimplement them. +- Merging PRs and designing promoted intents — both are **human gates by design**, not automation targets. +- Draining a `milestones`/spec backlog — `drain` is ledger-specific; spec execution is `abcd spec start` (itd-29). +- Any change to the ledger schema or the capture verbs beyond consuming `list`/`resolve`/`promote`. + +## Acceptance Criteria + +> _BDD format, per the [itd-1 discipline](../disciplines/itd-1-acceptance-gates.md)._ + +- **Given** an open ledger holding a self-contained defect, a `future-work-seed`/feature-shaped issue, and a blocked issue, **when** `abcd drain` runs, **then** each is routed distinctly — the defect to the fix path, the feature-shaped one to the promote path, the blocked one to skip — and every ready issue receives exactly one logged disposition (no silent drop). +- **Given** a fixable issue on the fix path, **when** drain works it, **then** a detector is armed and watched to fail, the fix lands behind it, `make preflight` is green, one PR is opened (not merged) with the ledger `resolve` folded into the branch, and the PR is one-issue-scoped. +- **Given** a design-STOP / feature-shaped issue and a built `capture promote` (post-cut-A), **when** drain routes it, **then** an intent draft is created seeded from the issue body with the four-field back-link, the issue remains open, and **no** adoption/design verdict is recorded autonomously. +- **Given** cut A (itd-46 not yet built), **when** drain hits a design-STOP issue, **then** it emits a "needs promotion" flag with the classifier's reason and does **not** invoke an unbuilt verb — the degradation is explicit in the handoff, never a silent skip. +- **Given** an issue whose self-contained-vs-design classification is low-confidence, **when** drain classifies it, **then** it routes to the design side (promote/defer), never to an autonomous fix. +- **Given** a completed or paused drain, **when** it writes its handoff, **then** every issue's route is recorded (fixed → PR number, promoted → itd-N, skipped → reason) and is machine-readable and re-runnable. +- **Given** a crafted `iss-N` argument containing path-traversal or unexpected characters, **when** drain resolves it, **then** it is validated against `^iss-[0-9]+$` and rejected otherwise — no file outside the ledger directories is read, written, or moved. + +## Prior Art + +- **[itd-29](../planned/itd-29-autonomous-run-resilience.md)** (autonomous-run resilience) — the run/execution surface `drain` dispatches the fix path onto; `drain` is the *planning/triage* half itd-29's run half assumes exists. +- **[itd-46](../planned/itd-46-abcd-intent-quoted-text-create-symmetric.md)** / **spc-30** (symmetric intent/capture create) — owns `capture promote `, the issue→intent elevator `drain` reuses. Cut A stubs it. +- **[itd-4](../planned/itd-4-issue-capture.md)** (issue capture) — the ledger substrate (`list`/`resolve`) `drain` reads and mutates. +- **`.abcd/development/plans/2026-07-12-abcd-run-protocol.md`** + the ledger-drain run plan — the C-phase evidence that the fix loop works and that triage is the missing, human-shaped piece. +- **adr-25** (host-delegated LLM default) — the classifier's "is this self-contained?" judgement rides the host, no bundled judge. +- **adr-27** (autonomous-run receipt gating) — the fix path's review-gating discipline. + +## SOTA + +> _Per the [sota-per-intent principle](../../principles/sota-per-intent.md): existing alternatives + rough maturity, then the chosen path._ + +- **Issue triage / auto-routing.** Alternatives: label-based deterministic triage bots (GitHub Actions triage, *mature* but crude — they route on metadata, they cannot judge "is this a self-contained fix"); autonomous SWE agents that pick issues off a tracker and open PRs (*emerging/2025–26*, e.g. hosted "agent picks an issue" products — capable but opinionated, heavyweight, and they own the whole loop rather than plugging into an existing engine). → **Path 2 (basic-with-seam):** a deterministic pre-filter on ledger fields + a host-delegated LLM classification for the ambiguous call, dispatching to abcd's *own* existing engines (run, promote). No new dependency; the classifier is the only new logic. +- **Classifier confidence / fail-safe.** The judge-calibration direction (per-criterion, explicit low-confidence, report-not-act) argues for an ordinal confidence and a safe default — adopted as *design*: low confidence routes to the human side, never to an autonomous fix. +- **Verdict — Path 2.** No new dependency ⇒ no hard stop; the seams (itd-29 run, itd-46 promote, adr-25 host judge) are load-bearing and already exist ⇒ no bespoke-no-seam stop. **A `sota-researcher` confirmation on the autonomous-triage landscape is a pre-build gate** (the 2025–26 agent-picks-an-issue products move fast; confirm none is a better adopt-target than bespoke triage before building). + +## Open Questions + +- **Name of the deterministic design-STOP marker.** Does an issue carry an explicit `disposition`/`run_eligible` field (set at capture or by a first triage pass, durable and reusable), or does `drain` re-derive the classification every run? Leaning durable (record the judgement once, per reality-is-filable), which also lets `capture` set it at file time. +- **Cut-A promote-flag surface.** Where does the "needs promotion" flag live in cut A — a handoff line only, or a written marker on the issue — so the eventual `capture promote` (itd-46) can pick it up without re-triage? +- **Batch sizing / budget.** Does one `drain` invocation take all ready fixable issues, or cap per invocation and rely on the run's per-burst budget for pacing? +- **Relationship to `abcd spec start`.** Is `drain` its own verb, or a `--backlog ledger` mode of the run surface? (This intent assumes a distinct verb for the ledger-specific triage; revisit if the surfaces converge.) + +## Audit Notes + +_None yet — populated by `intent-fidelity-reviewer` when this intent ships._ diff --git a/.abcd/development/plans/2026-07-12-abcd-cli-drain-2.md b/.abcd/development/plans/2026-07-12-abcd-cli-drain-2.md new file mode 100644 index 00000000..0633b81b --- /dev/null +++ b/.abcd/development/plans/2026-07-12-abcd-cli-drain-2.md @@ -0,0 +1,88 @@ +# Run plan — abcd-cli ledger drain #2 (follow-up fixables) + +**Status:** the second C-phase `/abcd:run` drain against abcd-cli's ledger, scoped +to the two self-contained fixables surfaced by drain #1 +([`2026-07-12-abcd-cli-ledger-drain-run.md`](2026-07-12-abcd-cli-ledger-drain-run.md)). +This is a deliberately small, safe batch — the hand-authored precursor to +`abcd drain`'s triage ([itd-82](../intents/drafts/itd-82-drain-ledger-triage.md)). + +Invoke (30m work / 30m pause, dynamic ScheduleWakeup 1800s): + +``` +/loop 30m Follow the protocol in .abcd/development/plans/2026-07-12-abcd-run-protocol.md + for the run plan at .abcd/development/plans/2026-07-12-abcd-cli-drain-2.md. +``` + +## Run contract + +``` +backlog: ledger # abcd capture list --open --json; ready = blocked_by_open empty +gate: make preflight # build + vet + test + race + record-lint + docs-lint + lint-reviews +definition_of_done: per issue — detector-first (fix-the-detector). Arm the issue's + named detector against its stated acceptance corpus, watch it flag, then drain the + fix behind it. A green gate is the floor; the issue's own detector is the bar. + Fold `abcd capture resolve ` into the fix branch. +ledger_verb: abcd capture +reviewers: + correctness: ruthless-reviewer # every non-trivial diff + security: security-reviewer # trust-boundary diffs (default-on; both items qualify) +branch_policy: fix/- from main; merge-commit-only; never force-push; + never commit to main; delete branch after merge (via gh api, not a gate-tripping push) +commit_trailer: Assisted-by: Claude:claude-opus-4-8 +pr_policy: one PR per issue; Closes # only if the ledger issue maps to one; + do not merge; do not enable auto-merge +budget: 30m wall | 6 worker-agents/burst | write NEXT.md + run-journal.json continuously +strike_limit: 3 # failed + died entries combined, keyed on iss-N +scope_fence: ONLY the two issues in the Ready backlog below. This is a curated batch, + NOT an open-ledger sweep — do NOT auto-continue into other open issues (the older + cluster iss-33/34/37-49 is un-triaged; the new follow-ups iss-76/79 are the vetted + set). When both are done (or STOP), end the loop and report. +stop_conditions: + - An issue is a maintainer DESIGN-STOP / adjudication, not a self-contained fix — SKIP. + - A fix would need a new dependency, a bespoke no-seam solution, a DB migration, or + a CI-config change not already in scope. + - A fix turns out to be a feature/contract change rather than the stated bug. + - Both Ready-backlog issues are resolved (or hit a STOP) — the curated batch is drained. +irreversible: + - none expected; if a fix touches persisted-state format or a schema_version, treat + as irreversible (human checkpoint + rollback), even though not named here. +``` + +## Ready backlog (the ONLY two issues in scope) + +Both self-contained, detector-first, trust-boundary (security reviewer applies): + +- **iss-76** `json-error-abspath-leak` (minor, bug) — `cli.Run` routes every command + error through the `--json` envelope, so any verb returning a bare `*fs.PathError` + emits an absolute local path into machine JSON (the systemic version of the leak + drain #1 fixed for docs-lint). Detector: a table test running each verb's known + filesystem-error path under `--json`, asserting the envelope carries no absolute + path. Fix: sanitise PathError/LinkError-bearing errors at the `Run()` boundary (or + audit which errors reach the envelope). Composes with the iss-29 fix already on main. +- **iss-79** `storeoriginal-inline-atomic-write` (minor, tech-debt) — `memory/ingest.go` + `storeOriginal` (~:806) is a fifth inline temp+`O_EXCL`+rename durable write left + untouched by the iss-32 consolidation; it lacks parent-dir fsync + explicit chmod. + Detector: extend `TestNoNonCanonicalAtomicWritePrimitives` to catch inline + temp+rename sequences (or a targeted test), then route storeOriginal through + `fsutil.WriteFileAtomic(target, material.rawBytes, 0o644)`, keeping the existing + sources-dir symlink guard. Watch the security-relevant symlink guard is preserved. + +## Explicit skips (do NOT work these) + +- **iss-28** `hermetic-git-test-env` — SKIP (assessed drain #1: cross-repo scaffolding + feature, design-STOP; awaits maintainer design / promotion to intent). +- **iss-35** `brief-surface-reconciliation` — SKIP (maintainer design-STOP). +- **iss-77** `launch-payload-omits-agents-hooks` — SKIP (design-shaped: the public + includes set is a maintainer call). +- **iss-78** `launch-dryrun-needs-version-location` — SKIP (ops/setup, not a code fix). +- **The older review cluster (iss-33/34/37-49)** — SKIP: un-triaged this batch. They + need a triage pass (the `abcd drain` classifier, itd-82) before they are drain-safe. + +## Why this is a safe batch + +Two minor, self-contained defects, each with a pre-stated detector and acceptance +corpus, both composing cleanly with fixes already merged (iss-29, iss-32). No +irreversible actions. The scope fence + explicit skips keep the loop from wandering +into un-triaged or design-shaped work. It also exercises two detector shapes drain #1 +did not: a per-verb `--json` error-shape table test (iss-76) and extending the +canonical-primitive detector to inline sequences (iss-79).