Skip to content

feat: reachability-enforced "done" — orphan-module gate (Gate 6)#22

Closed
anombyte93 wants to merge 13 commits into
feat/atlas-coin-oracle-gate-cutoverfrom
feat/reachability-gate
Closed

feat: reachability-enforced "done" — orphan-module gate (Gate 6)#22
anombyte93 wants to merge 13 commits into
feat/atlas-coin-oracle-gate-cutoverfrom
feat/reachability-gate

Conversation

@anombyte93

Copy link
Copy Markdown
Owner

Reachability-enforced "done" — the orphan-module gate

Folds a user's field feedback into the engine: "atlas optimizes 'tasks marked done', a proxy that breaks on orphan modules — tie 'done' back to reachability at every gate." (They shipped 10/19 "done" tasks that were green-tested but imported by nothing.)

Stacked on #21 (the oracle cutover). The two are orthogonal axes:

The 7 recommendations — all implemented & tested

  1. done = reachable, not just green — the composition of the gates below.
  2. reachableVia (tier-gated required) — every wired/live task must name the route/component/CLI/API it wires into (validation.py + backend.py schema hint).
  3. promise > evidence_promise_evidence_mismatch: a title that outruns its testStrategy ("Prisma connector" with a fixture-only test) is renamed down or blocked. Precise terms (prisma/webhook/connector/endpoint/migration/cli) hard-block; ambiguous (store/route/sync/client/api) warn-only.
  4. tier (spike|domain-model|wired|live) — deterministic default in enrich; only wired/live require reachability (tasks.py).
  5. pre-ship sweep + Gate 6reachability.py (git-diff new modules → importer grep → WIRED|ORPHAN|EXEMPT|ERROR, fail-closed) + a reachability-sweep CLI that writes the verdict into the CDD card + gate_reachability blocks done-orphans in both ship-check copies.
  6. set-status captures evidencerun_set_status (tier-gated) refuses orphan→done and persists the verdict (auto-reads from the CDD card), instead of a bare flag flip.
  7. hand-authored lint — free: the same run_validate_tasks chokepoint covers AI-parsed and hand-authored tasks.

Enforcement (per the design decisions)

Hard-block the ship gate + per-task auto-downgrade orphan done → scaffold (a new first-class non-done status, honestly surfaced as ⚠ N scaffolded). reachableVia is tier-gated so existing graphs (default domain-model) keep validating and shipping unchanged — no migration break.

Exemptions (so the gate isn't noise)

A true orphan is a module with no nameable consumer. CLIs/routes/tools/plugins/dynamic-imports declare themselves via a reachableVia scheme (cli:/route:/tool:/hook:/dynamic:) → EXEMPT. Test/spec/fixture/__main__/migration files are filtered out.

Proof

tests/core/test_reachability_e2e.py — real git repo + real ship-check subprocess: an orphan wired task blocks the ship (non-vacuous — wiring it flips to shippable; scaffolding it blocks honestly), and with the oracle stubbed PASS the orphan still blocks — the "green test on dead code" bug, closed.

Tests

python3 -m pytest -q568 passing, 0 failures (213 reachability-specific). Two-stage review on every task caught + fixed real defects (fail-open on git/grep error, false-WIRED ×3 incl. nested-tests/TS-substring/.spec, keyword over-matching, the scaffold-status P0).

Documented v1 limitation (follow-up)

Python commented-out imports (# import bar) can still false-WIRED via grep — AST upgrade deferred; find_importers's signature is stable for the swap.

🤖 Generated with Claude Code

claude and others added 13 commits June 16, 2026 18:50
…+ importer sweep + verdict)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…fault in enrich + schema hint

Adds phaseConfig.tier to every enriched task, derived from the same keyword
signals that drive complexity classification.  Priority order: live (user-test /
user validation checkpoint) > spike (research/investigate/evaluate stems) > wired
(integrate/auth/api/deploy/database/cli/mcp/…) > domain-model (safe default).

Key design decisions:
- _classify_tier() is a standalone helper next to _classify_task(), testable in
  isolation.  It uses two new prefix-stem regexes (_TIER_RESEARCH_RE,
  _WIRED_KEYWORDS) that drop the trailing \b so "investigate", "evaluate",
  "integrate" etc. are caught — existing exact-word regexes are not modified.
- run_enrich_tasks() is idempotent in two ways: (a) a task with no phaseConfig
  gets a full phaseConfig including tier; (b) a task with phaseConfig but no
  tier (pre-feature enrichment) gets tier backfilled; (c) a pre-existing
  non-empty tier is preserved (honours LLM or manually-set values).
- Default is domain-model — existing graphs never suddenly require reachability.
- TASKS_SCHEMA_HINT in backend.py gains the tier field and a one-line rule.
- 33 new tests in tests/core/test_tier.py; full suite 422 passed, 3 skipped.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…; drop review/assess from spike)

- cli: \bcli(?!ent|mat|nic) — excludes client/climate/clinic/clinical; CLI, cli-based, cli entrypoint still fire
- auth: \bauthenticat|\bauthoriz|\bauth\b — excludes author/authority/authentic; auth token, authenticate still fire
- wire: \bwire(?!frame|less) — excludes wireframe/wireless; wiring/wire X into Y still fire
- api: anchored to \bapi\b (word-boundary both sides) — confirmed no collision with rapid/therapist
- _TIER_RESEARCH_RE: drop review and assess (high-frequency innocent words); keep audit, spike, investigate, evaluate, etc.
- 12 negative regression tests added to tests/core/test_tier.py (each fails against pre-fix code)
- All 45 tier tests green; full suite 434 passed 3 skipped

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…DD reachability verdict is ORPHAN/ERROR/missing

Adds gate_reachability() to both skel/ship-check.py (stdlib-only, binding) and
prd_taskmaster/shipcheck.py (display heuristic), wired into run_all_gates after
Gate 5 (oracle). Reads the 'reachability' block from each task's CDD card;
fail-closed on missing card, missing block, ORPHAN, ERROR, or unknown verdict for
wired/live-tier done tasks. domain-model/spike/untiered and non-done tasks skipped.
31 new tests in tests/core/test_ship_check_reachability.py; full suite 465 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ector + warnings channel

- Add warnings channel to run_validate_tasks (alongside problems); hard-block only
  for wired/live tiers; spike/domain-model get advisory warnings only.
- Add reachableVia to TASKS_SCHEMA_HINT, parse prompt, and _task_summaries so it
  survives expansion; wired/live empty reachableVia is a hard-block, untiered/lower
  tiers get a warning; bare-word (unscoped) values get an advisory warning.
- Add _promise_evidence_mismatch() detector: maps high-altitude claim terms
  (prisma, cli, client, api, connector/adapter/sync/webhook/integration/endpoint/route)
  with word-boundary anchored regexes against testStrategy altitude
  (fixture-only vs live); mismatches are hard-blocked for wired/live, warned for
  spike/domain-model; includes down-rank suggested_title map.
- 27 new tests in tests/core/test_validate_reachability.py; 0 existing tests broken.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… ambiguous(store/route/sync/client/api)=warn-only

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…lity gate (wired/live require WIRED/EXEMPT verdict)

- run_set_status gains evidence_ref + reachability params (signature backward-compatible: tag/non-done paths unchanged)
- wired/live tasks marked done without a WIRED/EXEMPT reachability dict now raise CommandError (fail-closed gate)
- ORPHAN/ERROR/unknown/absent verdicts all block; domain-model/spike/untiered tasks keep bare flip
- doneEvidence (evidence_ref + timestamp) and reachability dict persisted additively on the task object when provided
- set_task_status MCP wrapper passes both new params through
- 16 new TDD tests green; full suite 517 passed 3 skipped
…uto-reads it; execute-task sweeps + auto-downgrades orphans

- Part A: new `prd-taskmaster reachability-sweep --task <id> --start-commit <sha>`
  subcommand (prd_taskmaster/reachability_cmd.py + cli.py wiring).
  Loads task, runs sweep_task(), writes verdict dict into .atlas-ai/cdd/task-<id>.json
  under "reachability" key (atomic, additive). exit 0 for WIRED/EXEMPT, exit 1 for ORPHAN/ERROR.

- Part B: `set-status` gains --evidence-ref and --reachability flags.
  Auto-read fallback in run_set_status(): when marking done with no explicit
  reachability, reads the verdict from the CDD card — so Step 9b sweep → Step 10
  mark-done works without extra flags. ORPHAN/ERROR from auto-read still blocks
  (RA5 gate preserved). Untiered/domain-model tasks unchanged.

- Part C: execute-task/SKILL.md updated — documents task_start_sha capture,
  Step 9b reachability sweep (mandatory for wired/live), and Step 10 branching:
  WIRED/EXEMPT → done, ORPHAN/ERROR → auto-downgrade to scaffold + continue loop.
  Throughline: "a green test on a module imported by nothing is not done — wire it
  or it ships as scaffold."

- 27 new tests in tests/core/test_reachability_sweep_cmd.py; full suite 544 passed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…t); scaffold blocks ship gate

- Add "scaffold" to VALID_STATUSES in task_state.py so set-status --status scaffold no
  longer raises "unknown status" (the auto-downgrade path in execute-task Step 10 now works).
- scaffold is NOT done, NOT deferred — a deliberate incomplete state for orphan modules;
  Gate 2 (gate_tasks requires every task done) already blocks any scaffold task from SHIP_CHECK_OK.
- Count scaffold tasks in _count_tasks and surface them in execute_panel with a ⚠ line
  ("N scaffolded — orphan, blocks ship gate") for honest operator visibility.
- Tests: set-status scaffold succeeds (API + CLI); scaffold task verified to block run_all_gates;
  VALID_STATUSES membership assertion; pipeline_state tag_counts dict updated for scaffold key.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…s, scaffold honest; orthogonal to oracle

Adds 11 genuine end-to-end tests spanning the full reachability lifecycle:
real git repo, real reachability-sweep CLI, real ship-check subprocess, fake
oracle stub via ATLAS_ORACLE_CMD so Gate 5 passes and Gate 6 is the deciding
gate.

Scenarios covered:
- A: orphan module → sweep writes ORPHAN → ship-check exits 1, stderr names
  ORPHAN + task id (non-vacuous acceptance criterion)
- B: oracle/test orthogonality — task's own test passes (oracle PASS) while
  reachability is ORPHAN, proving the two axes are independent (the user's
  exact bug: 10 of 19 'done' tasks were orphan dead code)
- C: wire module (edit existing app.py to import Widget) → re-sweep writes
  WIRED → set-status done auto-reads it → ship-check prints SHIP_CHECK_OK
- D: set-status scaffold → Gate 2 blocks honestly (not Gate 6), proving the
  scaffold path is an honest "not done" exit

Bug fixed during e2e development (uncovered by real sweep): gate_reachability
in skel/ship-check.py joined modules with ', '.join() but sweep_task writes
module dicts, not strings — fixed to extract m["module"] from each dict entry.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… skel/.npmignore (pyc excluded from pack)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… (close JS/TS false-WIRED)

Extend _EXCLUDE_NAME_RE to match *.spec.<ext> and *.cy.<ext> for all
supported language extensions (py/ts/js/jsx/tsx/go/cjs/mjs).  This
prevents co-located Jest/Vitest/Angular spec files and Cypress .cy files
from being counted as production importers in find_importers, and from
being swept as new modules in new_modules_for_task.  Without this fix a
module imported only by src/bar.spec.ts would return WIRED (false pass).

Also aligns the find_importers grep globs to include *.cjs alongside
the existing *.mjs so a .cjs-only importer is detected correctly.

Five new regression tests added in TestSpecCyImporterIsOrphan confirming:
- bar.spec.ts sole importer → ORPHAN (was WIRED pre-fix)
- bar.cy.ts sole importer → ORPHAN (was WIRED pre-fix)
- bar.cy.js sole importer → ORPHAN (was WIRED pre-fix)
- spec + real production importer → still WIRED
- new foo.spec.ts excluded from new_modules_for_task

Full suite: 568 passed, 3 skipped, 0 failures.
@anombyte93

Copy link
Copy Markdown
Owner Author

Reachability gate (Gate 6) already merged to main via the 5.3.0 catch-up. Branch fully represented in main (git cherry +0).

@anombyte93 anombyte93 closed this Jun 17, 2026
@anombyte93 anombyte93 deleted the feat/reachability-gate branch June 17, 2026 07:06
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants