Repository navigation
feat: every implemented issue leaves a spec change or a declared exemption (#480) - #490
Merged
Merged
Conversation
…t, tests first (#480) Tasks 2.1 to 2.5 of the Spectra change spec-on-every-issue. Four subcommands, each printing one JSON line with a fixed exit code: - declared: reads the **Spec**: line of an Implementation Complete comment (change <names> / no behaviour change / third-party clone / legacy / invalid). Lines inside code fences are not read. - archived: for a declared change, the change directory is gone and exactly one dated archive directory has its name, at the revision named; the three other forms pass without reading the repository. - links: every requirement added or changed in base..head has Examples whose titles are unique in the capability and whose markers (spec-example: <capability> / <title>) sit in a tracked file outside openspec/ that is not .md, or a `No executable check:` line with a reason. --base is required. - preflight: third-party clone, linked worktree, no spectra on PATH, no openspec/ directory, or ready. Anything that cannot be read is error (exit 2), never ok or pass; a revision that starts with a dash is a usage error. Tests: scripts/tests/spec-change, 221 checks, one per row of the four Example tables plus boundaries; they failed against an empty skeleton before the code was written. Refs #480
…d-all; spec directory as Spectra resolves it (#480) Tasks 2.6 and 3.1 of spec-on-every-issue, and the SKILL text of 3.2 to 3.4 (their end-to-end runs are still open). - references/spec-wrapper.md: the contract both wrappers follow — preflight, what the issue leaves (change, or one of four closed no-behaviour-change cases), light-form propose through spectra-propose, apply through spectra-apply with each Example's test first, the close-out in a fixed order (spectra-verify, spectra archive, check it landed, commit, links, archived), the declaration line, and fix rounds as idd-<N>-fix<k>. - idd-implement: Step 0.6 spec preflight before any artifact or commit, propose in Step 2 unless idd-plan already did, apply in Step 3, Step 4.5 close-out, the marker and the **Spec**: line in the Implementation Complete template; the tree-lock step says a linked worktree cannot write a spec change. - idd-plan: proposes, presents the proposal and the tasks in plan mode, hands the change name to idd-implement. - idd-all: Step 3b.6 closes out the Spectra tier and posts the Implementation Complete with the marker and no Checklist section. - spec-change.py reads the spec directory from .spectra.yaml spec_dir (openspec when absent), at the revision checked; an absolute or escaping value is an error. Spectra 3 initialises new repositories at docs/spectra, found while running spectra init for the tests; the requirement, an Example table and task 2.6 were added to the change. Tests: spec-change 257 (the 18 spec-directory checks failed first; a chain with the real spectra CLI archives under docs/spectra), spec-wrapper-contract 42 (failed before the text existed), authoritative-source-producer 28 (the new comment shape leaves the checklist source at Current Status > Tasks), and every suite that reads the three skills passes. Refs #480
…ts for the archive (#480) Task 4.3 of spec-on-every-issue, and the SKILL text of 4.1 and 4.2 (their end-to-end runs are still open). - idd-close Step 1.57, spec archive gate: the newest Implementation Complete by a writer is checked with spec-change.py archived against origin/<default>; refuse or error stops the close, in manual and orchestrated runs alike. An issue with no Implementation Complete is not held to it. - idd-verify: the shared pre-step builds a spec context after the external-writes block (code profile only): the declaration, the spec files changed in BASE_SHA..FROZEN_SHA, and the results of links and archived, with the review rules (behaviour no requirement states is HIGH; a false no-behaviour-change declaration is HIGH; a marked test that does not check its Example is MEDIUM or above). Staged to spec-block.md, read by the five manual reviewers and the codex leg, appended to the pai contextBlock. Step 3 turns missing links, an archive refuse and either check's error into Blocking rows. The lens focus strings are unchanged. - idd-all auto-fix and idd-implement Step 0.6: a fix that adds or changes a requirement is a further change idd-<N>-fix<k>, archived before the next verify round, and a new Implementation Complete lists every change; a Blocking fix leaves a test that fails before it. Tests: spec-close-gate 16 (the skill-text checks failed first; rows 1-4 run against a real remote, before and after the merge), spec-wrapper-contract 61 (19 new checks failed first). Every suite that reads idd-verify, idd-close, idd-all or idd-implement passes. Refs #480
…per; follow-ups filed (#480) Task 5.3 of spec-on-every-issue and the documentation part of 5.1 (the measured cost of a Simple issue still waits for the end-to-end runs). - rules/sdd-integration.md: the flow of every tier archives before verify; rule 4 is now "archive on the branch before verify (every tier)" and points at idd-close Step 1.57; progress for an issue with a change lives in its tasks.md. - CLAUDE.md, idd-diagnose's next-step table and usecase-routing row 10 say the same; no document still says Simple skips the spec or that archiving happens at close. - CHANGELOG [Unreleased]: the BREAKING entry, spec-change.py, and the known limits (linked worktree, a repository without Spectra, markers tie by name only). README rows of idd-implement, idd-plan, idd-close. - Follow-ups: #487 (Examples and checks for the existing requirements), #488 (isolate with a standalone clone, so issues that write a spec can run while the main tree is locked). Refs #480
…en a run is unattended (#480) Found while preparing the end-to-end runs: spectra-propose parks the change it creates, and spectra-apply asks with AskUserQuestion whether to continue a parked change. Inside an unattended /idd-all that question has no one to answer it. - Step 3: the wrapper checks is_unattended "$CWD" (unattended-contract.md) and, when unattended, appends an UNATTENDED MODE note to the propose and apply args: no AskUserQuestion (conservative choice written as an ASSUMPTION: line), no park or continue questions, commits end in (#N). - Step 4: the wrapper runs spectra unpark itself when the change is parked, before spectra-apply. Tried against Spectra 3: a parked change is listed again after the lines run. - unattended-contract.md lists the wrapper among the detectors. spec-wrapper-contract 67 (6 new checks failed first). Refs #480
…rements (#480) Found by the self-check of task 5.2: archiving spec-on-every-issue in a scratch clone appended its requirement to openspec/specs/idd-verify/spec.md as "\n\n---\n### Requirement: ...". The requirement before it (Codex channel is fully dependency-resolved) then read as changed, because its block ran to the end of the file and now carried the blank lines and the ---, and links reported it as missing an Example. requirements() now drops trailing blank lines and thematic breaks (---, ***, ___) from each block. A block whose text really changed is still reported (new test). After the fix the self-check prints ok for links and pass for archived. spec-change 265 (the appended-requirement case failed first). Refs #480
…5.1 (#480) Sandbox runs on 2026-11-11 with a snapshot of this branch: - 3.2: unattended /idd-all on a Simple issue left an archived change and a new docs/spectra spec on the branch; declared reads change, archived prints pass, links prints ok. - 3.5: a README-only issue declared no behaviour change (case a), no change directory. The third-party half rests on the preflight tests. - 3.3: accepted by contract tests only, by the decision of 2026-11-11. - 4.1: verify reported a Blocking naming the Example whose marker was removed and a HIGH for behaviour no requirement states; with the declaration switched to no behaviour change it reported a HIGH that the exemption does not hold. - 5.1: the CHANGELOG gives the one measured cost (5 minutes, about 3 in propose and close-out; one run, no control). Refs #480
A fix run after the 4.1 verify left an archived idd-27-fix1 on the branch (a new requirement for -h, two existing ones changed), restored the missing marker, and posted a new Implementation Complete listing both changes; archived prints pass and links prints ok. The idd-all auto-fix half is covered by the contract suite only. The run hit the actionability gate reading verify findings in ### Blocking as external blockers, which is tracked separately. Refs #480
- 3.4: an unattended /idd-all run that diagnose routed to the Spectra tier archived its change after apply and posted an Implementation Complete with the marker and a Spec line and no Checklist section; archived prints pass at that head. The same run fixed a verify finding by a further archived change idd-29-...-fix1, so the idd-all half of 4.2 ran as well; its note now says so. - 5.2: ticked on the scratch-clone self-check (links ok, archived pass); the check runs again after the real archive commit. Refs #480
…dit seen in a sandbox run (#480) spectra-verify (contract step 5.1) reported one critical and ten warnings; a Spectra-tier sandbox run edited a file under archive/ to make the link check pass. All fixed here before archiving: - critical: idd-all's end-of-run report still asked for /spectra-archive on the default branch after the merge, citing the removed "Close triggers archive". Removed; the report says Step 3b.6 already archived. The merge-and-close test that pinned the #392 reminder now pins its absence. - idd-all Phase 3b runs the spec preflight first (Step 3b.0.5): a third-party clone goes to the Simple path, the other stops halt. - idd-diagnose's Spectra hand-off points at contract steps 5 and 6 and at the archived tasks path, so a hand-run Spectra issue is not left legacy. - idd-implement Step 5a fills the checklist from the archived tasks.md when the issue has a change; the template no longer says TaskList only. - idd-verify: the spec context runs only under VERIFY_PROFILE=code; a failed comment fetch is caught at the fetch (SPEC_FETCH_OK) and reported as unreadable instead of reading an empty file as legacy (tried with a gh that fails, and against a sandbox issue); the DA focus carries a closed summary (declaration form, links and archived status); Step 3 writes a Blocking row for any links result but ok, any archived result but pass, and an unreadable or invalid declaration. - contract: after the archive, a requirement fix is a further change through step 7, never an edit under archive/ or of the main spec; a fix change is named exactly idd-<N>-fix<k>; the Spec line holds only the declaration. - spec: the requirement on the wrappers now states the unpark and the unattended note (plus a scenario), and the Spectra tier's preflight. design: six Examples; the acceptance runs are the ones done. README: the linked-worktree limit. spec-wrapper-contract 87 (17 new checks failed first); every suite that reads the edited files passes (27 suites). spectra validate: valid; spectra analyze: no findings. Refs #480
…ed, preflight before the Spectra check (#480) Change idd-480-fix1 (contract step 7: the fix adds and modifies requirements, so it is a further Spectra change). Round 1 reported five HIGH findings and several MEDIUM; fixed here: - spec-change.py ic: chooses the issue's Implementation Complete from the comments JSON (several pages accepted), newest by OWNER, MEMBER or COLLABORATOR. idd-verify and idd-close both use it. - idd-close Step 1.57 fetched nothing: it used IC_BODY, which no step set, so every case read an empty file and passed. It now fetches with gh api --paginate, chooses with ic, and stops with error when the comments cannot be read. spec-close-gate runs the block as written with a stand-in gh (row 5 is now executed, not grepped); the three new rows failed before the fix. - links: error for a heading meant as a requirement that does not parse (### Requirement Missing colon, or no name) and for a head with no tracked file under specs/; a marker counts only when nothing but whitespace or a comment closer follows the title (alpha table no longer satisfies alpha). A specs/ holding only .gitkeep stays ok. - idd-all Phase 3b: the spec preflight runs before the Spectra presence gate, so a third-party clone without Spectra proceeds; propose gets the Example and marker rules; the fix rule covers a rejected exemption. - Contract and idd-all: an unreadable permission stops instead of passing --can-write no; step 7 says a rejected no-behaviour-change is fixed by the issue's first change. - idd-verify: reviewers judge No executable check reasons. A backtick put into the SPEC_BLOCK string ran as a command; the contract suite now refuses one there. - Docs: a linked worktree stops every issue (idd-implement, idd-all batch mode, CHANGELOG). Not fixed here, filed as #491: legacy comments pass every gate, a declared change is not tied to the issue, and the round's LOW findings. Tests: spec-change 304, spec-close-gate 23, spec-wrapper-contract 101; the 30 suites that read the changed files pass. Refs #480
- ic exits 2 on an empty file (gh exited 0 and printed nothing is not "no comments"), and skips a comment whose body is not a string. - links treats only headings that start with ### Requirement as meant for a requirement, as the spec says; a level-2 "Requirement notes" heading is not an error. - An unreadable permission sets PF to an error JSON, so the existing status table stops on it (tried with a gh that prints nothing). - The idd-verify requirement exempts third-party clones as the code does. - Stale text: idd-all phase 0 warning, the verify comment on selection, the close-gate test header; a third-party Spectra-tier issue rerouted to idd-implement keeps a TaskList. spec-change 312, spec-wrapper-contract 106, spec-close-gate 23 (new checks failed first); the 30 suites that read the changed files pass. Refs #480
…op on any preflight failure, fenced spec data (#480) Change idd-480-fix2 (contract step 7). Round 2 had no HIGH; six MEDIUM and one LOW were Blocking, two MEDIUM and five LOW In-scope fix. Fixed here: - links: CommonMark fences (same character, at least as long, nothing after); Example and No-executable-check lines inside a fence do not count; a requirement name used twice in a capability is an error. - preflight: an unreadable .spectra.yaml is error (one JSON line, exit 2); a spec_dir starting with '-' or naming the root is error; git ls-tree gets '--'; no --path-format (git 2.31). - The contract and idd-all Step 3b.0 stop on no output, non-JSON or an unknown status. Both preflight blocks are now run by the contract suite with a stand-in gh and a PATH with and without spectra: the six rows of the new Example give the same status in both copies. - ic: the marker may follow leading whitespace. - idd-close Step 1.57: a failed gh repo view or git fetch is error, not a pass on a stale ref; --retroactive lists Step 1.57 among the skipped gates. - idd-verify: the spec context runs only with a git range; its data sits in a per-run fence with the rules outside; the DA focus gains the spec checks only when a spec context was built (run without a range: no block, context and DA focus unchanged). verify-profiles says what of the default profile is unchanged. - The manual Spectra path (diagnose hand-off, routing rule) runs the preflight first; the checklist-source rule is in the wrapper requirement; docs/commands.md no longer says archive after close. - Tests: every nested bash runs with --norc --noprofile and stdin from /dev/null. /bin/bash 3.2 read ~/.bashrc when stdin was a socket and put a real spectra on the "clean" PATH (one run in ten failed). spec-change 334, spec-close-gate 25, spec-wrapper-contract 132 (new checks failed first); the 31 suites that read the changed files pass. Refs #480
- idd-verify: under the code profile a missing range is reported as an error (fails closed) instead of skipping the spec checks; only prose and academic skip them (VERIFY_PROFILE defaults to code in this shell). The contract suite now runs the block three ways: prose, code without a range, code with a range and the fence token planted in the data. - idd-close Step 1.57 reads the declaration first and fetches the default branch only for a declared change, so a failed fetch no longer holds an issue with no Implementation Complete or with an exemption. - spec_dir starting with - or naming the root, a .spectra.yaml that is not a regular file, and a code fence that never closes are errors, now stated in the spec with Example rows. - Docs: diagnose hand-off wording and route table, verify-profiles on the DA focus; the proposal no longer lists CHANGELOG. spec-change 345, spec-close-gate 27, spec-wrapper-contract 138 (new checks failed first); the 31 suites that read the changed files pass. Refs #480
…literal paths, the fetched commit (#480) Change idd-480-fix3 (contract step 7). Round 3 had no HIGH; nine MEDIUM were Blocking and nine LOW or INFO In-scope fix, mostly parsers letting an edge case through. Fixed here: - declared and requirements() share the CommonMark fence rule; a fence counts at any indentation (inside a list item too); lines end only at \n, \r\n or \r (str.splitlines also broke at U+2028); a requirement heading may carry up to three leading spaces, as Spectra reads it. - A .spectra.yaml that is a directory or a symlink at the revision is an error; a spec_dir starting with ':' is an error, and every git call uses literal pathspecs; no git on PATH is an error with one JSON line; spec_dir values in messages are cut at 60 characters. - idd-close Step 1.57 checks the commit the fetch returned (FETCH_HEAD), reads the declaration form with python instead of jq, and its result table stops on no output. The spec now limits the default-branch stop to a declared change, as the code does. - idd-verify: the spec's No-executable-check line gives way to an Example (the spec context the reviewers get) whose three rows the contract suite already runs; VERIFY_PROFILE and the no-range error are written into the spec; the changed-spec list names main specs only; stale comments fixed. - The contract's close-out link check stops when it cannot find SPEC_BASE. spec-change 370, spec-close-gate 29, spec-wrapper-contract 144 (new checks failed first; the indented-heading row now names the right requirement); the 31 suites that read the changed files pass. Refs #480
- The marker search reads lines the CommonMark way too: a marker followed by U+2028 and more text no longer counts. - A .spectra.yaml that is a symbolic link is an error in the working tree as it is at a revision (now tested at both). - The contract and idd-all explain the close-out base as the merge-base with the freshly fetched default branch (FETCH_HEAD). - The Example row "code without a range" asserts both errors and the DA suffix; FENCE_RE (no longer used) is gone; spec_dir_of splits lines the same way; the declared rows sit in the declared section. spec-change 378, spec-close-gate 29, spec-wrapper-contract 148 (new checks failed first); the 31 suites that read the changed files pass. Refs #480
…c plan line, unquoted paths (#480) Change idd-480-fix4 (contract step 7). Round 4 had two HIGH and three MEDIUM Blocking, four In-scope fix. Fixed here: - HIGH: idd-implement Step 2.5's "skip this step when there is a change" also skipped the routing table, so a direct /idd-implement on a Plan issue no longer offered /idd-plan. A change now skips only the TaskList. - HIGH: the Implementation Plan's **Spec plan**: line, which later steps read, is now in the spec and the contract, in three forms: change <name>, no behaviour change (<a|b|c|d>), rejected (change <name> left parked). idd-plan writes rejected when the maintainer aborts; the wrapper applies an existing change only when the newest plan names it, so a rejected proposal is no longer applied by the next /idd-implement or /idd-all. - Every git call runs with core.quotePath=false (and verify's changed-spec list): a capability named 中文能力 came back quoted and went unchecked. - Contract steps 3 and 4 (unattended check, unpark) are fixed bash and now have an Example that runs them with a stand-in spectra; the preflight rows also check whether the run proceeds or stops (PF_RC). - spec_dir: a value in quotes is read whole (" #" inside it is not a comment); ic catches RecursionError and exits 2. spec-change 387, spec-wrapper-contract 166, spec-close-gate 29 (new checks failed first); the 31 suites that read the changed files pass. Refs #480
…fore the propose (#480) spectra-verify on idd-480-fix4 found two critical gaps in the round-4 fix: the rule "apply an existing change only when the newest plan names it" was in the contract only, while idd-implement still applied any idd-<N>- change (and its own Step 2 plan would have become the newest plan); and the /idd-plan offer sat in Step 2.5, after the propose. - spec-change.py plan reads the **Spec plan**: line of the newest Implementation Plan by a writer (four forms: change, no behaviour change, third-party clone, rejected), with an Example of nine rows. - Contract step 3 runs it before any propose or comment and stops on rejected or on a change no approved plan names; idd-implement Step 0.6 does this before Step 2 posts its plan, and asks a direct Plan-tier invocation about /idd-plan before it proposes. - idd-plan writes the line for every form and re-presents a rejected change for approval instead of leaving the issue stuck. - "spec_dir: # a comment" is an error again (a regression of fix4). - Tests: steps 3-4 rows also assert "attended"; PF_RC must be set. spec-change 416, spec-wrapper-contract 184, spec-close-gate 29 (new checks failed first); the 31 suites that read the changed files pass. Refs #480
55 tasks done
…d, NUL-separated paths (#480) - idd-plan writes `**Spec plan**: proposed (change <name>)` before plan mode and changes it to `change <name>` only on Approved; contract step 3 stops on `proposed` and `rejected` and names /idd-plan - step 3 checks `spectra list`; an unreadable list stops instead of proposing - idd-implement and fix rounds post their plan line before they propose - git paths are read NUL-separated (ls-tree -z, diff -z) - install-spectra names `brew install --cask spectra-app` - PLAN_RE needs the end of the heading; the Plan question is asked once Change: idd-480-fix5
…proved (#480) spectra-verify on idd-480-fix5 found that a `proposed` change sent the user back to /idd-plan, which then stopped again. idd-plan now presents the open change whenever contract step 3 stops because it is not approved (proposed, rejected, no plan names it, or a plan names another change). Step 3 also stops when `spectra list` prints something that is not JSON. Two text checks that passed before fix5 now check the changed passages. Change: idd-480-fix5
Verify round 6 found that idd-plan could not read a parked change's files (spectra-propose parks it; spectra 3.0.0 keeps parked files outside the changes directory) and that a revision changed only the plan comment. idd-plan now runs Step 3.5 before plan mode: unpark if parked, find the change directory with `spectra instructions apply --json`, stop when the proposal, the tasks or a spec delta is missing, and show those files in plan mode. A revision edits the files and runs `spectra validate`; an abort parks the change again. Change: idd-480-fix6
The ExitPlanMode template has a place for the change files; Step 0 lists Step 3.5 and handle_plan_response follows the new Revised and Aborted rows; "proposal and tasks" becomes "proposal, spec delta and tasks". Change: idd-480-fix6
…d-implement proposes (#480) Verify rounds 4 to 7 each found a HIGH in approving the Spectra proposal in plan mode; round 7 showed that a revision cannot be made in the change's files while the session stays in plan mode. Per the maintainer's decision, idd-plan no longer proposes: it writes `proposed (change <name>)` before plan mode and `change <name>` on approval, and idd-implement proposes in the light form, as in the Simple tier. Step 3.5 and the unreleased `rejected` form are removed. Approving the proposal itself moves to #497. Change: idd-480-fix7
idd-plan looks up the open change with spectra list; the README, the Step 6 example and the flow diagram match the new flow; the contract test, pasted twice by mistake in f5b6199, is one file again. Change: idd-480-fix7
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Every issue IDD implements now leaves a spec change, or declares why it does not. Refs #480.
What changes
scripts/spec-change.py:declared,archived,links,preflight,ic,plan, one JSON line each. The spec directory is read from.spectra.yamlspec_dir(openspecwhen absent; Spectra 3 initialises new repositories atdocs/spectra).references/spec-wrapper.md: the shared contract.idd-implementwraps the implementation of a Simple or Plan issue in a Spectra change in the light form (propose, apply, close out). In the attended Plan tieridd-planhas the maintainer approve the Implementation Plan in plan mode, then hands the approved change name toidd-implement.idd-allcloses out the Spectra tier the same way. The change is archived on the branch before verify.**Spec**:line:change <names>,no behaviour change(four closed cases) orthird-party clone. The Implementation Plan carries a**Spec plan**:line; an open change is used only when the newest plan names it as approved.idd-verify(code profile): every reviewer gets the declaration, the spec files changed in the range, and the results oflinksandarchived. Behaviour no requirement states is HIGH; a falseno behaviour changeis HIGH; a check that is notok/passand an unreadable declaration are Blocking.idd-closeStep 1.57: refuses while the declared change is not archived on the default branch.idd-<N>-fix<k>.BREAKING for the flow, not for any file format: Simple and Plan issues that change behaviour now go through Spectra. See the CHANGELOG
[Unreleased]entry for the known limits (linked worktrees stop; a repository without Spectra stops with the install or init command).How it was checked
spec-change428,spec-wrapper-contract238,spec-close-gate29 checks. CI (suites) passes on87bd9d0, and verify round 8 ranrun-all-tests.sh(73 suites) on the same commit.openspec/changes/archive/2026-11-11-spec-on-every-issue/tasks.md. The attended Plan tier was checked by contract tests only, not run end to end.idd-480-fix1toidd-480-fix7, each archived on the branch. Round 8 is PASS with 0 blocking; its one HIGH (raised by Codex, rejected in the report) was accepted as rejected by the maintainer, who decided the merge: feat: IDD 的每一張 issue 不論走哪個 tier 都要更新規格(與 Spectra 深度整合) #480 (comment)spec-change.py links --base 03f5559printsokandarchived --ref HEADprintspasson87bd9d0.Left for later
|in verify report tables).