Skip to content

Trinity agent - #1

Open
vichitra-studio wants to merge 6 commits into
mainfrom
trinity_agent
Open

Trinity agent#1
vichitra-studio wants to merge 6 commits into
mainfrom
trinity_agent

Conversation

@vichitra-studio

Copy link
Copy Markdown
Owner

No description provided.

vichitra-studio added a commit that referenced this pull request Apr 30, 2026
…[drift-fixes]

Drift #1 — prompt_16a field-name drift:
- Remove literal `assumption` field reference from prompt_16a_impl_planner.md;
  adopt schema-pointer style matching other prompts (schema owns field names).
- Add `proposed_assumption` to field shape bullet list (source, impact, proposed_assumption).
- Migration template documents legacy assumption → proposed_assumption rename.

Drift #2 — hallucination-lint E530 wrapping pressure:
- Add canon-ref bypass: sibling command_ref with structurally-resolved cn:* id
  skips prefix check; actual ref resolution stays with canonical-integrity (E110/E210).
- Add project-level allowlist merge: _load_command_prefixes now accepts
  project_canon_dir and merges <spec-root>/canon/command_prefixes.json into
  toolkit defaults via shared _merge_prefixes_from_file helper.
- command_prefixes.json: add _description clarifying scope (universal verbs only).
- Add _unwrap_shell_c to _extract_path_from_string so bash -c "..." wrappers
  do not trigger false LINKED_TEST_FILE_NOT_FOUND on linked_test_expectation.
- Rewrite E530 message to name both escape hatches (message preserves
  INVENTED_ENUM_OR_ID substring for backward test compatibility).

DRY / SoC:
- Extract extract_command_string() and is_resolved_canonical_ref() into
  linter_utils.py; import in step_16.py and hallucination_lint.py respectively.
- Relocate _is_resolved_ref from canonical/integrity.py to linter_utils.py;
  re-import under same alias — no API change, no circular import.

step_16.py: apply extract_command_string at all three E301/E302 enforcement sites
so object-form test_commands[] items (schema oneOf [string, object]) are
validated symmetrically with string-form items.

schema/16_impl_context.schema.json: add 'task' to spec_ref.type enum and
corresponding skip in _check_behavior_validation_pairing (intentional, unrelated).

prompts/16b + 16c + migration template updated to document new object-form shape
and both escape hatches. CLAUDE.md troubleshooting section updated.

Tests: 189 passing; new end-to-end tests (no mock-what-we-test violations) cover
both escape hatches, malformed allowlist fallback, bash -c unwrap, and
object-form E301/E302 across all three step_16 sites.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
vichitra-studio added a commit that referenced this pull request Jun 24, 2026
… host CI validate-all

Resolves the single P1 (D11) finding from the fresh re-audit of d67fbea, plus
a same-class bug it surfaced in the generated host CI workflow:

- D11: the source_of_truth/render_target extension to changelog/format.yaml
  (an enforced schema surface consumed by changelog_parser.py) is now documented
  with a change_schema entry in changelog/unreleased.yaml and a mirroring bullet
  in changelog/unreleased.md (source/render parity preserved: 5 entries <-> 5 bullets).
- init_project.py _render_ci_workflow: the generated host CI step ran the bare
  `validate-all spec --repo-root ./devspec_toolkit` (no --spec-root/--git-root), so
  project canon was not discovered and every host PR emitted false E110s. Now runs
  `spec-check spec --repo-root ./devspec_toolkit --spec-root ./spec --git-root .`,
  matching the pre-commit hook fix and CLAUDE.md core rule #1.
- Added test_ci_uses_spec_check_not_validate_all asserting the generated CI uses
  spec-check with both project-canon flags and no validate-all (closes the I8 test
  gap that let this hide); documented the CI fix in both changelog files.

Full suite: 2819 passed, 4 skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
vichitra-studio added a commit that referenced this pull request Jul 12, 2026
…#19)

* fix(DEVSPEC-103,105,106): fix generated pre-commit hook config for macOS + submodule deployments

- Replace bare `python` with `devspec_env/bin/python` in all hook entries
  (macOS ships no bare `python` binary; venv-pinned interpreter required)
- Rename validate-all hook to spec-check; swap subcommand + add
  --spec-root ./spec --git-root . flags to seed-lint and canonical-integrity
- Fix governance hook: pass_filenames false→true so pre-commit passes the
  commit-msg file path as a trailing positional arg consumed by --message;
  --message must remain the last flag
- Fix bare-prefix substitution in _build_pre_commit_config: add second
  replace pass for devspec_toolkit/ (files: patterns) after ./devspec_toolkit
  (flag patterns) to avoid partial substitution when toolkit root differs
- Expand hook-presence tests: add dag-lint, extraction-intent-check,
  canon-schema-alignment, canonical-integrity presence assertions; strengthen
  path-substitution test to cover both ./prefix and bare-prefix forms

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(DEVSPEC-104): relax canon schema minItems:1 to allow empty bootstrap registry

New host repos bootstrapped by init_project.py start with entries:[]
in spec/canon/manifest.json — the prior minItems:1 constraint on
vc:core:canon caused E520 schema_invalid on every new project until
after Step 03 (specdev canon-accept). Set minItems:0 to reflect that
an empty project-canon registry is valid during the bootstrap phase.

Adds a regression test verifying that a bootstrap manifest with
entries:[] passes lint_canon_dir schema validation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(DEVSPEC-104): address pr-audit findings — changelog, lifecycle tests, schema read

- changelog/unreleased.yaml: add fix entry for pre-commit hook changes
  (devspec_env/bin/python path, validate-all→spec-check rename, flags)
- changelog/unreleased.md: populate Fixed section with both fix entries
- test_canonical_lint.py: add sunset/retired E420 lifecycle negative tests;
  replace hardcoded inline canon schema with real schema/core/canon.schema.json read
- test_init_project_submodule.py: add fallback-path test for missing template;
  extend test_path_substitution_applied to assert devspec_toolkit/tools absent

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(audit-20260623): resolve 9 pr-audit findings — fixtures, lint tests, changelog parity, extractor

Remediates all findings from audit run 20260623-091938-fbe17bf:

- T1-T3 (D5): canon fixture manifests drop forbidden root key
  canonical_refs_used and add required entry fields; consumer tests
  (E410 alias collision, E420 deprecated-timestamp) preserved.
- T4 (I8): negative test that lint_canon_dir() rejects manifests with
  additionalProperties via E520 schema_invalid.
- T5 (I8): test for the alias-branch E420 deprecated-lifecycle path
  (lint.py:194-198), distinct from the entry-level branch.
- T6 (D11): document devspec-governance commit-msg hook
  pass_filenames false->true fix in changelog Fixed section.
- T7 (I11): extractor YAML branch now populates typed Keep-a-Changelog
  arrays via an explicit change_type->category map (no prefix rule, so
  rename_*/merge_steps/split_step/internal_restructure are not dropped);
  regex fallback emits dicts; EXTRACTOR_VERSION 1.0.6->1.0.7.
- T8 (D11): declare source_of_truth/render_target on unreleased.yaml and
  register both keys in format.yaml optional_fields. The pass_filenames
  fix is also added to unreleased.yaml so the source actually contains
  what the .md render shows (parity holds, not just keys present).
- T9 (D12): reclassify the minItems-relaxation entry fix->change_schema.

Full suite: 2818 passed, 4 skipped. All per-task acceptance gates green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(audit-20260623-2): document format.yaml extension + fix generated host CI validate-all

Resolves the single P1 (D11) finding from the fresh re-audit of d67fbea, plus
a same-class bug it surfaced in the generated host CI workflow:

- D11: the source_of_truth/render_target extension to changelog/format.yaml
  (an enforced schema surface consumed by changelog_parser.py) is now documented
  with a change_schema entry in changelog/unreleased.yaml and a mirroring bullet
  in changelog/unreleased.md (source/render parity preserved: 5 entries <-> 5 bullets).
- init_project.py _render_ci_workflow: the generated host CI step ran the bare
  `validate-all spec --repo-root ./devspec_toolkit` (no --spec-root/--git-root), so
  project canon was not discovered and every host PR emitted false E110s. Now runs
  `spec-check spec --repo-root ./devspec_toolkit --spec-root ./spec --git-root .`,
  matching the pre-commit hook fix and CLAUDE.md core rule #1.
- Added test_ci_uses_spec_check_not_validate_all asserting the generated CI uses
  spec-check with both project-canon flags and no validate-all (closes the I8 test
  gap that let this hide); documented the CI fix in both changelog files.

Full suite: 2819 passed, 4 skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(submodule): pass project-canon flags on spec-validating command examples

Submodule-deployment doc examples and two prompt-contract seeds invoked
spec-validating commands without --spec-root/--git-root, so a host following
them would not resolve project canon and would see false E110s — the same bug
class fixed in the generated CI workflow (init_project.py).

Flag-consistency pass across reference.md and the three workflow runbooks: every
command confirmed to accept both flags (validate-all, matrix, fixtures-lint,
seed-lint, traceability-check, hallucination-lint, spec-quality-lint,
canonical-integrity, invariants-check, governance-check) now passes
--spec-root ./spec --git-root . in submodule examples. Commands that take only
--repo-root (dag-lint, canonical-lint canon, diagnostics) and single-file
`validate spec/NN.json` forms are deliberately left unchanged. The reference.md
"Standard (non-submodule) usage" example is intentionally kept flagless to
contrast with the submodule example beside it.

Contract seeds switched to the unified gate to match init_project.py:
- prompt_12_ci_gates.md: example CI gate -> spec-check + project-canon flags
- prompt_15_scaffold.md: example scaffold validator -> spec-check

prompt-sync: OK. Full suite: 2819 passed, 4 skipped. Changelog parity preserved
(6 yaml entries <-> 6 md bullets).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(audit-e953793): correct doc flags, prompt examples, changelog accuracy, venv-name propagation

Remediates the 10-task fix plan from PR audit run 20260623-191127-e953793
(theme-based fresh full re-audit of bugs/1.1.1). Applied in 4 file-disjoint
chunks, each verified by a fresh full reviewer against schema/canon authority.

Host code:
- T1 init_project.py: thread --venv-name through _build_pre_commit_config
  (mirrors _render_ci_workflow); parametrize governance hook; add
  discriminating custom-venv test. Default output byte-identical.
- T10 extract_digest_changelog.sh: PYTHON=${DEVSPEC_PYTHON:-python3} + Py>=3.9 note.

Docs (flag-consistency per CLAUDE.md submodule matrix):
- T4 reference.md: add spec-check; fix forward-replay-check & canonical-lint flags.
- T5 workflow_bootstrap_legacy.md: canon-accept --git-root; canonical-lint flags; spec-check.
- T6/T7 workflow_{feature_extension,migration}.md: spec-check alongside validate-all.

Prompts:
- T2 prompt_15_scaffold.md: replace invalid 'specdev-tools' prefix with run_specdev.sh + flags.
- T3 prompt_12_ci_gates.md: populate canonical_refs_used with cn:core:environment:ci.

Changelog:
- T8 unreleased.yaml: correct false parser-consumption claim (keys are permitted
  optional fields, neither required nor read).
- T9 unreleased.md: re-render change_schema entries under ### Changed; 6:6 YAML/MD parity.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(audit-5486e9a): resolve 4 mechanical findings from terminal re-audit

Follow-up to 5486e9a. The terminal theme-based /devspec_pr_audit
(run 20260624-115549-5486e9a) confirmed the prior 10-task fix plan
resolved + clean, and surfaced 5 new findings. This commit lands the
4 mechanical ones (each fresh-reviewed, full test suite green); the 5th
(governance commit-msg hook pass_filenames) is a CLI-contract design
decision deferred separately.

- reference.md: add --spec-root/--git-root to canon-schema-alignment
  example (cli.py:400-405 accepts + consumes them; matches ~10 sibling
  examples the branch already updated).
- prompt_12_ci_gates.md: Operating-Flow Gate bullet now lists spec-check
  not validate-all (consistent with the corrected example + Core rule 1).
- changelog/unreleased.{yaml,md}: reword changes[3] — drop ambiguous
  "neither required nor read"; state precisely that source_of_truth/
  render_target are declared optional_fields accepted as top-level keys
  without their values being consumed. 1:1 YAML/MD parity preserved.
- test_init_project_submodule.py: scope the os.path.exists mock to
  init_project.os.path.exists.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(seed-lint): add W555 for thin Step-00 out_of_scope seeds (DEVSPEC-107)

Seed-lint passed silently when seeds routed to Step 00 carried no
out-of-scope / non-goal content, yet the charter schema mandates
out_of_scope minItems:3. Authors hit a gate-fail-or-hallucinate fork
at Step 00, one phase after the seed layer where the gap originates.

Add W555 STEP00_SEED_OUT_OF_SCOPE_THIN: warn-only and non-promotable.
It aggregates substantive out-of-scope items across ALL Step-00-routed
seeds and fires once if the combined count < 3, mirroring the charter
schema constraint at the seed phase. Non-promotable by design: an error
would recreate the same fork upstream for legitimately sparse projects.

The counter skips bracket-placeholders, template scaffold labels
(**Expectation**:/**Content**:), empty bullets, and backtick/tilde
fenced code blocks. Detection is gated on "00" in step_requirements.

Also update seed_overview.md §3.2 to mirror §3.1's "list at least 3"
guidance so the template stops nudging authors into the shortfall.

Verified end-to-end: W555 surfaces in default `specdev spec-check`
output for a thin seed and is absent for a rich one. Full suite green
(2839 passed). Unit + integration coverage added; no mocks of code
under test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(audit-fc11407): resolve 3 P2 findings from W555 commit-scoped audit

Commit-scoped PR audit of the W555 changeset (run 20260624-195742-fc11407,
--base f4336cb) returned STATUS OK with 4 P2 findings. Dispositions:

- T1 (D10, fix): _count_substantive_out_of_scope tracked fenced code blocks
  with a single boolean toggled by both ``` and ~~~, causing cross-delimiter
  leakage (a ```-opened fence containing a ~~~ line flipped the state, so
  in-fence bullets were counted / out-of-fence bullets skipped). Now tracks the
  opening delimiter and closes only on a matching delimiter. Added discriminating
  test test_fires_when_cross_delimiter_fence_contains_fake_bullets. Fence-length
  matching per CommonMark remains the documented accepted limitation.

- T2 (D14, won't-fix): the minItems:3 threshold stays restated inline rather
  than read from 00_charter.schema.json at runtime. Documented the rationale in
  code: it follows the validation layer's mirror-with-comment convention
  (step_16, step_16b, linter_utils) and avoids coupling the seed layer to a
  Step-00 schema — the separation W555 exists to preserve.

- T3 (I8, fix): test_non_promotable_codes_excluded omitted W553/W554/W606 from
  its non_promotable assertion set (pre-existing debt; errors.py already lists
  them). Folded them in since this commit touches that exact assertion.

- T4 (I12, fix): corrected an inverted comment in the integration test — E520
  missing_schema_uri IS emitted when $schema is absent; it is simply irrelevant
  to the W555 assertions.

Full suite: 2840 passed, 4 skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(audit-6bf1660): make cross-delimiter fence test discriminating + doc fenced-block exclusion

Re-audit of the W555 remediation (run 20260624-215210-6bf1660, --base
f4336cb, AHEAD=2) returned STATUS OK with 2 findings. Both fixed:

- I8 (P1, test): test_fires_when_cross_delimiter_fence_contains_fake_bullets
  had no discriminating power — its fake bullets sat under "## 2. Examples"
  (not an out-of-scope heading), so in_section was never True for them and
  they could not be counted under ANY fence implementation; the assertion
  passed for both the correct fence_char impl and the old boolean-toggle bug.
  Rewrote the seed so the fake bullets live inside a backtick-opened fence
  containing a ~~~ line AND under the out-of-scope heading, with one genuine
  bullet outside. Now the correct impl keeps the fence open (count=1 → W555
  fires) while the old impl toggles the fence off at ~~~ and counts the fake
  bullets (count=3 → no fire). Added a direct _count_substantive_out_of_scope
  == 1 assertion alongside the W555-fires assertion. Discrimination verified:
  real function returns 1, a throwaway old-impl simulation returns 3.

- D3 (P2, doc): error-codes.md W555 "Substantive" definition listed only two
  exclusions (bracket placeholders, scaffold labels); added the third the code
  enforces — content inside fenced code blocks (``` or ~~~).

No production code changed (seed_lint.py untouched). Full suite: 2840 passed,
4 skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(audit-8e68b93): document+test pre-existing E410, fix doc/test-count staleness

Round-3 audit (run 20260624-231358-8e68b93, --base f4336cb, AHEAD=3)
returned STATUS OK with 5 findings. All resolved (user opted to also clear
the pre-existing E410 debt surfaced because seed_lint.py is in the diff):

- D3 (P1): E410 CANONICAL_ALIAS_COLLISION was emitted (canon lint + seed-lint
  duplicate seed_id) but undocumented. Added an error-codes.md entry covering
  both trigger contexts with resolutions. Pre-existing behavior — no code change.
- I8 (P1): the seed-manifest duplicate-seed_id -> E410 path had no test. Added
  test_duplicate_seed_id_fires_e410 (real temp-dir manifest with two entries
  sharing a seed_id; asserts E410; no mocks).
- D7 (P2): W555 doc now notes fence tracking matches the opening delimiter
  (a ~~~ inside a ```-opened fence is content, not a close).
- D3 (P2): integration test docstring "17 unit tests" -> "18".
- I12 (P2): unit-test module Coverage list now enumerates the 18th
  (cross-delimiter fence discrimination) test.

No production code changed (seed_lint.py untouched). Full suite: 2841 passed,
4 skipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(DEVSPEC-100): Phase 1 remediation checkpoint — WI-1..WI-8

Eight converged work items (P1 crashes + silent-under-review), each driven through a fresh-reviewer review-fix loop to zero findings. Checkpoint commit so per-unit diffs stay clean for Phase 2 review.

- WI-1: p5_finalize.py 3-site absent-fix_plan crash fix + G3 blocked-status guard + new test_p5_finalize.py (4 ACs)
- WI-2: specdev-trinity reviewer dispatch milestone_start_ref injection + step-5b actions[].target primary read
- WI-3: pr-audit-context-author routing.json p0/ path fix
- WI-4: pr-audit-discovery-mechanical registry-generate --dry-run -> --out (3 sites) + D1 failure-mode row
- WI-5: specdev-step existence check + specdev-impl author-mode questions[]-shaped blocker (F-4 two-surface contract)
- WI-6: skill frontmatter disambiguation + delete specdev-trinity-plan skill + init_project allowlist + trinity body-note removal
- WI-7: specdev-review wave-loop model (deterministic theme clustering) + theme_group enum + SPECDEV_REVIEW_CONCURRENCY
- WI-8: specdev-trinity post-convergence 16c evidence-synthesis (F-1 two-branch fr_coverage) + new test_wi8_green_derivation.py (anchored)

Plan + per-unit review records: WIP/devspec-100/ (untracked). Follow-ups parked: FU-1 (D9 extraction_paths gap), FU-2 (CHANGELOG note). Not pushed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(DEVSPEC-100): Phase 2 Wave B1 — WI-10, WI-9, WI-13, WI-14

IU-9 (WI-10): trinity SKILL.md — add 'orchestrates, does NOT author' to
  folded description scalar (exactly once; closes WI-6 AC8), Prerequisites
  section, error message -> /specdev-step 16a.
IU-10 (WI-9): specdev-review SKILL.md Step 2a — per-round full+fresh invariant.
IU-13 (WI-13): pr-audit-context-verifier — kind enum defers to schema
  authority (11 values incl seed-grounding), mirrors sibling agents.
IU-14 (WI-14): pr-audit-discovery-semantic — remove forward_to_p3 phantom flag.

All four converged ZERO-FINDINGS on round 1 of fresh full review.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(DEVSPEC-100): Phase 2 IU-11 (WI-11) — pr-audit bin-packing budget + theme grouping

protocol.md §3: raise per-bin budget ~200 -> ~600, add ~800 hard cap with
precedence (cap > budget > ceil(N/3)), theme-first packing, two worked
examples, orphan cross-theme co-location capped at ~800.
pr-audit-context-author.md step 6: replace global impact-sort with
slice(=theme)-first grouping from routing.json; waves <=6 bins; own
concurrency env var (not SPECDEV_REVIEW_CONCURRENCY).
Theme grounded in existing slices.yaml/routing.json taxonomy (no invented
semantics). Converged round 2 (1 LOW consistency defect fixed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(DEVSPEC-100): Phase 2 IU-12 (WI-12) — post-fix verification + fix-apply automation

Deliverable A (verification): NEW scripts/p6_verify.py — reads fix_plan.json,
runs each task's acceptance_command in topological order (DFS, cycle +
unknown-dep detection), reports pass/fail per task (exit 0/1/2). protocol.md
§12 documents the workflow. p5_finalize.py build_summary() now EMITS the
conditional '## Next steps' SUMMARY.md footer when findings>0 (was doc-only;
closes AC2). SKILL.md §9 footer format + --post-fix subsection.
Deliverable B (auto-apply): NEW agent pr-audit-fix-apply.md (Option B-NL) —
per-task apply-then-gate-on-acceptance_command with max_rounds retry; reuses
specdev-trinity-impl pattern; no schema change.
Tests: test_p5_finalize.py +2 discriminating footer tests (present/absent) →
6 passed. Converged round 2.

Known documented trade-off (AC4): --post-fix re-audit runs full branch diff
(touched files are a subset); no --only-files scoping primitive — see FU-3.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(DEVSPEC-100): Phase 2 IU-15 (WI-16) — wire validate_agent_outputs blocked-status gate

validate_agent_outputs.py: on strict-mode schema-failure, merge-write
{status: blocked, blocked_reason: '<schema> failed validation for <artifact>'}
into <run-dir>/manifest.json before exit 1 (new _write_blocked_manifest helper;
preserves other keys; safe when manifest absent). Manifest path derived from
--run-dir (no --manifest flag).
SKILL.md: new explicit P4->P5 gate section — run validate_agent_outputs.py
--run-dir <dir> --strict; non-zero -> P5 skipped; documents blocked_reason.
Closes the loop WI-1's G3 guard left open (guard already prints blocked_reason).
Smoke-tested end-to-end (writer + p5_finalize reader agree on path). Converged R1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(DEVSPEC-100): Phase 2 IU-16 (WI-15) — broaden pr_audit integration test harness

test_p5_finalize.py: +2 (malformed findings -> exit 2; missing manifest -> exit 1).
test_validate_agent_outputs.py (NEW, 2): absent fix_plan -> exit 0 not-blocked
  (regression lock); schema-invalid -> manifest status=blocked + blocked_reason,
  exit 1 (exercises IU-15's _write_blocked_manifest write-path, per R3-1 ordering).
test_tier0_checks.py (NEW, 8): zero-findings-per-check paths; T0-10
  specdev-unavailable -> P2 gap kind/catalog_tag=D9 finding.
All fixture-based file-in/file-out via main()/direct calls. 18 passed.
Tests verified faithful to real script behavior (no fabricated-behavior tests).
Converged round 2 (2 LOW unused-import removals). Test-only; no script changes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(DEVSPEC-100): Phase 3 Wave C1 (IU-17/18/19) — trinity errors_remaining, p2/-absent meta-finding, T0-09 CLI prefix

IU-17 (WI-17): specdev-trinity-impl.md execute mode — non-zero run_command exit
now maps to gate_status:"errors" + errors_remaining (exit code + truncated
stderr), NOT ambiguities_raised[]; errors_remaining added to both execute-mode
return-contract examples (clean + partial) for shape consistency.

IU-18 (WI-18): pr-audit-cross-boundary.md Mode B — emit a p2/-absent meta-finding
when the p2/ fragment dir is missing instead of silently skipping; procedure
prose, failure-modes table, and invocation template all aligned.

IU-19 (WI-19): tier0_checks.py T0-09 — broaden the CLI-change trigger from a
single cli.py equality to CLI_PATHS set + CLI_PREFIX (commands/) prefix match,
closing the commands/ subpackage coverage hole; +2 discriminating tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(DEVSPEC-100): Phase 3 IU-20 (WI-20) — author-extend mode for additive artifact edits

Adds a third specdev-impl mode, author-extend, for additively inserting new
operator-intent content into an EXISTING spec/NN artifact (vs author, which
re-authors the whole file). Approved design: WIP/devspec-100/wi-20-design.md.

- .claude/agents/specdev-impl.md: new "Mode: author-extend" section — detect
  existing artifact (Bash ls), read via `specdev json read`, subagent authors the
  content object from intent (+optional seed_path) + prompt contract + upstream,
  apply via `specdev json insert/patch` (NEVER Write). Post-insert gate = the
  same SCOPED spec-check reused from author/fix (no full forward-replay by the
  leaf agent) + always emit a forward_replay_debt notice referencing the existing
  W595 CONTENT_STALENESS code. Own blocker counter author_extend_blocker_round.
- .claude/skills/specdev-step/SKILL.md: `--extend "<intent>" [--seed <path>]`
  arg + Step-1 dispatch path sending ONLY {mode,target,intent,seed_path?} (thin
  orchestrator — no content payload built); author_extend_blocker_round bridge.

Dual fresh review (cross-file dispatch seam + design/AC fidelity): no P1/P2;
2 LOW — allow-list seed_path precision (fixed), insert_pointer consumer-side
row (accepted as designed per §4 defense-in-depth).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(DEVSPEC-100): F-1b — reviewer FR-coverage gate matched zero FRs (false-negative)

specdev-trinity-reviewer.md criterion #6 ("Linked-fixture coverage rolled up")
selected FR bindings via spec_ref.type=='doc' / id=='vc:04-fr-list', but real
ms_*_plan.json fixtures encode FR bindings as spec_ref.type=='fr' (id e.g.
fr-user-login). The old selector matched zero checklist items, so the
fixture-coverage gate silently never fired — a milestone shipping with missing
fixture coverage would never raise kind:coverage. Same root cause as WI-8 (which
fixed the impl-side FR filter); this fixes the reviewer-side predicate.

Doc-criterion change (agent .md), verified statically + against fixtures; no
runtime test. Fresh review: CONVERGED.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* docs(DEVSPEC-100): FU-2 — changelog note for removed /specdev-trinity-plan skill

WI-6 removed the /specdev-trinity-plan skill (functionality now
/specdev-trinity --phase plan). Add the operator-facing migration note to
changelog/unreleased.md (### Removed) + unreleased.yaml (internal_restructure)
so hosts vendoring the toolkit as a submodule know to re-run init to refresh
the stale skill symlink after bumping. Closes FU-2.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* refactor(pr-audit): scope --post-fix closing loop via existing --base, not full re-audit

The --post-fix closing-loop re-audit re-ran a full P0–P5 audit over the whole
branch. p6_verify.py already confirms each fix via its acceptance_command, so the
re-audit's only remaining job is to catch fix-introduced side effects — which a
diff scoped to the fix changes fully covers. Reuse the EXISTING --base flag
instead of building an --only-files primitive:

  1. capture PRE_FIX_SHA before applying,
  2. apply + p6_verify gate (unchanged),
  3. on pass, commit the verified fixes (stage only fix_plan.tasks[].file),
  4. re-run /devspec_pr_audit --base <PRE_FIX_SHA> → scopes to the fix commit.

SKILL.md §9 + protocol.md §12.2 rewritten; removes the "no --only-files primitive
exists" note (no new flag needed). Behavior change: --post-fix now commits the
verified fixes (previously left in the working tree) — required for the scoped
diff. No-op fixes (nothing staged) skip the commit + re-audit. Full branch-vs-main
audit still available via a normal run. Closes the DEVSPEC-121 need without a new
primitive; §12.1/§12.3 unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* fix(pr-audit): apply 20260701-125732-47caab1 fix_plan (10 tasks)

Remediates findings from the agent-change PR audit (--base 3423097)
covering the DEVSPEC-100 agent-contract rewrite and the pr-audit
--post-fix refactor:

- Real regressions from our own work: unused mode:apply dispatch field
  (pr-audit-fix-apply.md), a gitignored test fixture that only ran on
  the author's machine (test_p5_finalize.py AC2), and a failure-signal
  regex missing non-zero-exit detection (test_wi8_green_derivation.py).
- Pre-existing doc drift in touched files: stale digest-directory
  naming, a wrong schema line citation, a hallucinated "verified"
  schema claim, and two protocol.md/agent-contract shape mismatches
  (meta_findings[] fields, fix_plan finding-reference fields).
- Changelog backfill for the prompt_16c behavior change, the
  devspec_pr_audit skill expansion, and the agent-contract rewrites.

Deferred (filed as follow-up, not applied here): a new changelog-
enforcement CI check and a yaml/md parity test — both new safeguards
rather than fixes for anything currently broken.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(pr-audit): apply 20260701-144050-f13cd74 closing-audit fix_plan (4 tasks)

Round-2 remediation from the scoped closing re-audit (--base 47caab1)
of the first fix batch. The closing audit caught issues the first
review-fix loop introduced or left incomplete:

- specdev-trinity-reviewer.md: the prior scope_kind fix only corrected
  one of two identical false claims, leaving a contradiction within
  the same file (lines 94-95 vs 143-144).
- changelog/unreleased.{yaml,md}: two backfilled entries contained
  factual inaccuracies — validate_agent_outputs.py was not actually
  broadened for post-fix output shapes (p6_verify.py is the real new
  artifact), and anchor/roadmap sync is performed by
  specdev-trinity/SKILL.md's Step C3, not the named agent-contract
  files.
- test_wi8_green_derivation.py: added dedicated assertions for the
  'exit code:'/'exit code=' variants the failure-signal regex already
  handles but the regression test didn't independently verify.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(pr-audit): correct changelog agent-file attribution (round 3)

The scoped closing re-audit (--base f13cd74) caught a third defect in
the same changelog entry: the internal_restructure note attributed all
11 rewritten .claude/agents/*.md files to "IU-11/12/15/16", but git
history shows 4 of them were touched only in Phase 1 (d0724c5) or
Phase 2 Wave B1 (2c2f7b6), and IU-15/16 touched zero agent-contract
files. Two prior rounds already rewrote this same passage and both
introduced new inaccuracies, so per the review-fix loop's stopping
rule this fix was made directly rather than delegated a third time,
then independently re-verified by a fresh P3 cross-boundary pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* fix(DEVSPEC-122): stop treating deferred/wont-have/out-of-scope as absence across the validation pipeline

Root cause (found via a real vc_payments incident: a task had to be
deleted rather than deferred because deferring it threw a hard error):
many coverage/completeness checks excluded checklist_status:"deferred"
(or FR priority:"wont-have", or capability scope:"out") as if the item
didn't exist, instead of treating it as "exists, paused/cancelled,
documented why."

Schema:
- 14_roadmap.schema.json: add "deferred"/"wont_do" task status + required
  status_reason, so a roadmap task can express pause/cancellation directly
  instead of it being inferred from checklist coverage.
- 16_impl_context.schema.json: add "wont_do" checklist_status + required
  wont_do_reason (enforced via allOf/if/then, not the unenforced prose
  pattern wont-have/scope:out use).
- 08_fixtures.schema.json: out_of_scope[] block (fr_id/rationale), mirroring
  05_interface_contracts.schema.json's existing one.

8 confirmed bugs fixed, each empirically verified against real validator
output (not just static reading):
- E304 ROADMAP_TASK_UNCOVERED: deferred/wont_do checklist items now count
  as coverage; the milestone-fallback branch skips deferred milestones;
  a task itself marked deferred/wont_do no longer needs checklist coverage.
- E307 BEHAVIOR_VALIDATION_PAIRING: deferred/wont_do items count in the
  pairing set instead of being skipped.
- W576 TASK_EXECUTION_MISSING: skips deferred/wont_do tasks, plus a
  defense-in-depth check when every covering checklist item is
  deferred/wont_do but the task's own status hasn't caught up yet.
- W568 UNCOVERED_CAPABILITY: exempts scope:"out" and scope:"future"
  capabilities (the capability-level analog of FR wont-have/deferred).
- matrix.py fr_coverage (SPECDEV_MATRIX_STRICT=1): excludes wont-have and
  Step-05/Step-08 out-of-scope FRs from every fr_with_* ratio, with
  fr_with_fixture using its own Step-08-based denominator since Step 05
  and Step 08 out-of-scope are independent exemptions.
- step_11.py W583/W615: red-team threat coverage now traces each
  API/invariant back to its FR(s) and exempts it only when every traced
  FR is wont-have.
- cli.py completeness-check: total_frs/total_caps denominators now match
  the exemption sets the checks they summarize actually use (Step-05 vs
  Step-08 FR exemptions are independent; scope:out/future capabilities
  both exempt).
- New W616 PAUSED_OR_CANCELLED_ITEM_MARKED_VERIFIED: flags a checklist
  item left at implementation.status:"verified" after being paused or
  cancelled, found by adversarial testing rather than static reading.

Verified via three escalating review rounds: static code read, empirical
validator execution against constructed fixtures, and two independent
fresh-agent review passes with no memory of prior conclusions. Every new
assertion was checked with a revert-source/confirm-fail/restore
discriminator to prove it exercises real behavior rather than passing
vacuously. No mocking in any new test. Full suite: 2937 passed, 4 skipped,
107 subtests (baseline 2885). specdev registry-generate: zero diff.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(pr-audit): apply 20260702-100406-04dda8d fix_plan (25 tasks)

Remediates findings from the DEVSPEC-122 rollout PR audit (--base
3bde863), spanning doc drift, missing error-code documentation,
stale comments, and two real gaps in cross-step/traceability
validation:

- New validation logic: E536 CONTRADICTORY_OUT_OF_SCOPE_FR_FIXTURE
  (traceability_closure.py) and a Step 08 out_of_scope[].fr_id
  cross-step check (step_08.py), both mirroring existing E535/E590
  patterns; each with a covering unit test.
- Bonus fix found while writing T14's regression test (not in the
  original 25 findings): validate_step_16 dereferenced a non-dict
  `plan` value without a type guard, crashing before step_16a's own
  isinstance check could fire its intended E520. Added the missing
  guard in step_16.py.
- Doc/prompt drift: 18 new or promoted error-code headings in
  error-codes.md, corrected false claims and duplicate bullets in
  prompt_16a/16b, restored a dropped breaking-change sentence in the
  changelog, and cross-references/exemption docs across several
  prompt files.
- Fixture completeness: positive + negative fixtures for Step 08's
  out_of_scope[] field.

Three fix_plan tasks (T10, T11, T12) were mis-scoped to a single file
by the audit's own P4 consolidation loop even though their
change_summary required a source + test file pair; found and
corrected during --post-fix dispatch, then implemented directly
rather than delegated further given the fabrication risk on the only
real logic in this fix_plan. T10's acceptance_command also targeted a
pytest file with no collectible tests (always exit 5 regardless of
fixture content) — replaced with a direct `specdev validate` gate,
verified under the exact subprocess.run(shell=True) mechanism
p6_verify.py uses after a first correction attempt still broke on
nested-shell $?-expansion.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(pr-audit): apply 20260702-152521-64cd062 fix_plan (19 tasks)

Closes 22 findings from the scoped closing re-audit of fix commit 64cd062
(base 04dda8d): backfills ~40 undocumented error/warning codes and a
step_16.py E520 message family in error-codes.md, fixes schema/08's
allOf/unevaluatedProperties composition (phantom "Unevaluated properties"
error), wires the step_08 out_of_scope fixtures into the bridge test
suite, adds W570 branch coverage and a step_08 no-phantom regression
test, resolves several prompt/docs drift items (prompt_08 downstream
count and traceRef pointer, prompt_11 threatCategory restatement,
prompt_14 completeness-assessment qualifier, prompt_16a checklist_status
delegation and probe-command claim), documents 17 previously-undocumented
specdev subcommands in reference.md, and adds changelog entries for the
isinstance(plan, dict) guard, E536, and E590.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(pr-audit): apply 20260702-180836-78f7e58 fix_plan (19 tasks)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(pr-audit): apply 20260702-235732-0789b8a fix_plan (4 tasks)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* refactor(pr-audit): switch Tier-2 dispatch from impact-score bin-packing to theme-based

The old algorithm computed a per-file impact score (type_weight x expansion-set
size x |applies|) and greedily packed files into ~600/800-unit bins, only
merging themes with a single changed file. Any theme with >=2 files always got
its own bin, which was the dominant cause of over-fragmentation on typical
multi-area PRs (up to 15 bins on an 8-theme change).

Replace with one discovery-semantic bin per touched theme (slice) by default,
each carrying its full applies (drift_types + invariants) set so review of a
theme is never split by aspect. Cross-theme merging is removed. A theme only
splits into sub-bins on overflow (~15+ files), grouped by pipeline step or
shared directory, never by score.

Removes now-dead type_weight/complexity_per_file fields from slices.yaml
(confirmed unused by any script). Updated protocol.md §3,
pr-audit-context-author.md, pr-audit-context-verifier.md,
pr-audit-discovery-semantic.md, and SKILL.md consistently.

Validated against 7 historical audit runs: bin count drops from 55 to 35
across the sample (-36%), with the two most fragmented runs roughly halved
(04dda8d 15->8, 64cd062 14->7).

* fix(pr-audit): apply 20260710-183622-68cd048 fix_plan (4 tasks)

- SKILL.md: correct tier2_bins shape description ({bin_id,theme,files[]}
  object, not a bare array) and worked-example sourcing annotation
- pr-audit-context-verifier.md: mark the duplicated Output schema
  constraints block as keep-in-sync with discovery-semantic.md; add a
  bundle_warnings[] resolution check to the verifier procedure
- pr-audit-context-author.md: stop citing nonexistent catalogs.md
  per-item severity annotations for severity_priors; reframe as agent
  judgment guided by the general P0/P1/P2 meaning section

All 4 acceptance commands verified via p6_verify.py (4/4 PASS).

* fix(pr-audit): apply 20260710-191504-117eb16 fix_plan (2 tasks)

- pr-audit-context-verifier.md: replace step 10's worked example, which
  overlapped step 8's existing digests_needed check, with a genuinely
  distinct case (a missing digest for a file outside any tier2_bins
  entry); also correct the JSON path shorthand to include the missing
  .files[] hop
- pr-audit-context-verifier.md: fix the keep-in-sync comment above
  "Output schema constraints" to stop claiming byte-identity with
  discovery-semantic.md's block (they differ by the upstream_refs[]
  paragraph, which this file covers elsewhere) and instead scope the
  sync requirement to the shared core schema-constraint content

Both findings were introduced by the prior fix commit (117eb16) and
surfaced by that commit's own closing re-audit. Both acceptance
commands verified via p6_verify.py (2/2 PASS).

* fix(analysis): scan plan.ambiguities[] in upstream-backlog, guard against misfiled anchors

DEVSPEC-123: `specdev upstream-backlog` previously only read
execution.emergent_ambiguities[] (16b/16c), silently missing
plan.ambiguities[] (16a). Now scans both, unified via a shared severity
rank across the two vocabularies (blocking/non_blocking vs.
low/medium/high/critical), and discloses via new W617 how many records
the default --status open view is hiding.

Follow-up: a Trinity Anchor misfiled inside impl_context/ (the existing
W609 condition) also has a plan.ambiguities[] array, but in the anchor's
own crossCycleAmbiguityItem shape, not the 16a binary scale — without a
guard it would spuriously fail every entry as E520. _iter_plans now
skips artifact_role == "anchor" files, matching validate.py's existing
(whitespace-tolerant) discriminator.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(pr-audit): apply 20260711-080019-b7fdff3 fix_plan (4 tasks)

* fix(registry): recurse deep-nested registry entries + close seed drift blind spot

DEVSPEC-125: two independent toolkit-side gaps.

Gap 1 — entry_key_registry omitted the 14_roadmap `criterion` entry
(3-deep: milestones -> tasks -> acceptance_criteria). Root cause was a
one-level nesting cap: the generator and every consumer used
partition("[].") (first-split only). Step 04 (fr -> criterion) is only
2-deep so it masked the gap. Fix:
- generate._scan_nested_arrays now recurses, symmetric with the
  already-recursive registry schema.
- new shared iter_array_path(data, path) walker in core/entry_key_registry;
  list_entries recurses via _flatten_entry. json_utils, matrix, and
  registry_check adopt the walker (three single-level idioms removed).
- registry_check no longer treats a MISSING nested array as drift (nested
  arrays may be optional, e.g. task.acceptance_criteria; requiredness is
  owned by schema validation + the regenerate byte gate). It still fires
  for a present-but-wrong-shape nested array. Top-level behavior unchanged.
- regenerated tools/entry_key_registry.json + golden (byte-identical,
  +1 criterion entry). extraction_paths.json unchanged (top-level only).

Gap 2 — seed drift-detection ignored manifest seeds missing from the
index. build_seed_index hashes every manifest seed, so an index missing
one is stale-by-omission: edits to it tripped no drift signal.
check_freshness now cross-checks manifest vs index and marks manifest-only
seeds untracked; CLI `context freshness` emits W595 SEED_UNTRACKED. New
sub-reason documented in docs/developers/error-codes.md.

Tests updated depth-agnostically: the fixture double now carries the
3-deep criterion; four invariant/agreement sites that walked `nested` one
level (green only by coincidence — criterion also lives at depth-1 under
04_fr) are now recursive. golden==tools byte-identical; registry-generate
idempotent.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(pr-audit): apply 20260711-131728-55f376c fix_plan (2 tasks)

- error-codes.md: correct the R003/E622 trigger text — it claimed missing
  arrays are "checked for both top-level and nested arrays", but the prior
  commit (55f376c) narrowed nested-array drift detection so a missing
  nested array (which may be schema-optional) is no longer flagged; only
  a present-but-wrong-shape nested array still fires E622
- registry_check.py: correct the matching module-docstring bullet ("the
  same two checks apply" for nested arrays), same reason

Both were introduced by 55f376c and surfaced by this commit's own
closing audit (run 20260711-131728-55f376c). Both acceptance commands
verified via p6_verify.py (2/2 PASS).

Also fixes a stale fixture path in test_entry_key_registry.py's module
docstring (missing tools/ path segment), surfaced by the same audit run
as an out-of-scope I12 observation and folded in here.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(pr-audit): apply 20260711-140153-017ff65 fix_plan (1 task)

error-codes.md: restore the dropped grammatical subject in the E622
REGISTRY_DRIFT trigger sentence at line 995 — the prior fix commit
(017ff65) removed "the declared array path is missing," but left the
remaining list items ("is not an array, or its entries lack...")
without a subject.

Introduced by 017ff65 and surfaced by its own closing audit (run
20260711-140153-017ff65). Acceptance command verified via p6_verify.py
(1/1 PASS).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(pr-audit): apply 20260711-144049-01e1c62 fix_plan (17 tasks)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(pr-audit): apply 20260711-162609-e06fd20 fix_plan (8 tasks)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(pr-audit): apply 20260711-174402-ec6fc20 fix_plan (5 tasks)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(pr-audit): apply 20260711-183343-6a27cbb fix_plan (3 tasks)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(pr-audit): harden changelog_parser.py against OSError from unreadable I/O

Applies 20260711-192839-2396f85 fix_plan (T1: widen validate_changelog's
except clause to catch OSError from load_format; T2: test coverage), plus
a consolidated pass on the 3 other unguarded exists()/iterdir()/stat() call
sites flagged by that run's P3 cross-boundary sweep (list_versions,
load_version, get_toolkit_version) — each now degrades to its already-
documented failure mode (raise FileNotFoundError, or return None/[])
instead of letting a bare PermissionError/OSError propagate uncaught.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* fix(pr-audit): guard load_format/load_version open() against OSError

Closing-audit-5 follow-up on f932289: the OSError hardening pass guarded
each function's exists() probe but left the immediately-following open()
call unguarded — reachable via cli.py/schema_differ.py callers of
load_version() that only catch (FileNotFoundError, ValueError). Wraps
both open() calls in try/except OSError, converting to the same
already-documented FileNotFoundError contract.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
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.

1 participant