spec-ctx 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +119 -16
- package/action.yml +8 -0
- package/dist/adopt/write.js +9 -4
- package/dist/assets.generated.js +1 -1
- package/dist/check/check.js +23 -0
- package/dist/check/hints.js +16 -11
- package/dist/cli/args.js +5 -4
- package/dist/cli/commands.js +89 -29
- package/dist/cli/config.js +14 -0
- package/dist/cli/gate.js +133 -4
- package/dist/cli/help.js +25 -7
- package/dist/cli/workflow.js +28 -11
- package/dist/config.js +9 -1
- package/dist/gate/rerun.js +169 -0
- package/dist/graph/bundle.js +145 -26
- package/dist/graph/files.js +31 -0
- package/dist/graph/input.js +37 -0
- package/dist/graph/links.js +96 -0
- package/dist/graph/path.js +54 -0
- package/dist/graph/query.js +3 -0
- package/dist/graph/related.js +58 -11
- package/dist/handoff/handoff.js +11 -7
- package/dist/handoff/questions.js +9 -1
- package/dist/ids/ids.js +4 -0
- package/dist/index/build.js +33 -4
- package/dist/index/clock.js +55 -0
- package/dist/index/scan.js +42 -11
- package/dist/index/structure.js +97 -0
- package/dist/init/init.js +28 -12
- package/dist/loop/fails.js +15 -8
- package/dist/loop/next.js +89 -37
- package/dist/runcmd.js +5 -0
- package/dist/util/time.js +1 -0
- package/dist/verify/run.js +3 -2
- package/dist/version.js +1 -1
- package/package.json +1 -1
- package/skills/specctx-run/SKILL.md +9 -4
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ Six commands on a small project. Each clip is one command.
|
|
|
37
37
|
|
|
38
38
|
OpenSpec names a requirement by its header text. A reword breaks every link to it. `adopt` gives each requirement and scenario an ID that stays. Run it once on a project that has specs. A new project gets IDs from `init --all`.
|
|
39
39
|
|
|
40
|
-

|
|
40
|
+

|
|
41
41
|
|
|
42
42
|
You see: the dry run prints each header and the ID it gets. Then `adopt` writes them.
|
|
43
43
|
|
|
@@ -45,7 +45,7 @@ You see: the dry run prints each header and the ID it gets. Then `adopt` writes
|
|
|
45
45
|
|
|
46
46
|
`status` shows the open change, the task counts, and the next command to run.
|
|
47
47
|
|
|
48
|
-

|
|
48
|
+

|
|
49
49
|
|
|
50
50
|
You see: one change, two tasks done, one open, and the hint.
|
|
51
51
|
|
|
@@ -53,15 +53,15 @@ You see: one change, two tasks done, one open, and the hint.
|
|
|
53
53
|
|
|
54
54
|
`task next` picks the first open task. It prints the requirement IDs the task cites and the Verify command, the shell command that proves the task is done.
|
|
55
55
|
|
|
56
|
-

|
|
56
|
+

|
|
57
57
|
|
|
58
58
|
You see: task `add-mfa/1.3`, its requirement, and `grep -q totp src/login.ts`.
|
|
59
59
|
|
|
60
60
|
### 4. Only the spec this task needs
|
|
61
61
|
|
|
62
|
-
`task start` prints the
|
|
62
|
+
`task start` prints the `context` answer for the task. It holds the task line and the requirements it cites with their scenarios. It holds the code and test lines that tag them, the decisions, and the deltas and designs of its change. All of it fits a character budget. The last line is the size of that text next to the size of all spec text.
|
|
63
63
|
|
|
64
|
-

|
|
64
|
+

|
|
65
65
|
|
|
66
66
|
You see: 711 characters, 3.6 percent of the tree. The agent reads this instead of the tree.
|
|
67
67
|
|
|
@@ -69,15 +69,15 @@ You see: 711 characters, 3.6 percent of the tree. The agent reads this instead o
|
|
|
69
69
|
|
|
70
70
|
`task done` runs the Verify command. On exit 1 the box stays open. On exit 0 the box flips to `[x]` and an evidence file records the files that were checked. The agent cannot tick the box by hand.
|
|
71
71
|
|
|
72
|
-

|
|
72
|
+

|
|
73
73
|
|
|
74
74
|
You see: a red refusal, the edit, then a green done line.
|
|
75
75
|
|
|
76
76
|
### 6. Hand off to the next session
|
|
77
77
|
|
|
78
|
-
`handoff write` puts the state in one file: the change, the done tasks, the current task, and the open questions. The next session starts from it. The questions live in `.ctx/questions.json`. `handoff ask` adds one and `handoff resolve <n>` answers it.
|
|
78
|
+
`handoff write` puts the state in one file: the change, the done tasks, the current task, and the open questions. The next session starts from it. The questions live in `.ctx/questions.json`. `handoff ask` adds one and `handoff resolve <n>` answers it. A plan or archive question needs `--approve` or `--reject`, and a rejection keeps the stop.
|
|
79
79
|
|
|
80
|
-

|
|
80
|
+

|
|
81
81
|
|
|
82
82
|
You see: the head of `.ctx/handoff.md`.
|
|
83
83
|
|
|
@@ -107,6 +107,26 @@ graph LR
|
|
|
107
107
|
|
|
108
108
|
The ID is the stable key. A reword of a header does not change it. The index is the graph of IDs: tasks, requirements, scenarios, decisions, code tags, and test tags. `bundle` walks the edges to the configured depth inside a character budget. A decision record joins the graph through its `Refs:` line. The demo fixture has none, so the graph above shows no decision.
|
|
109
109
|
|
|
110
|
+
### Ask for the context
|
|
111
|
+
|
|
112
|
+
`specctx context <task|ID|path|change>` is the one command to run before you edit. It takes one input and prints the spec text that relates to it, with one provenance line for each block. A task gets the requirements it cites and the code and test lines that tag them. It also gets the decisions that name them, and the deltas and designs of its change. A file gets the requirements it tags and the code and test lines of those requirements, so its own tests appear. `task start` prints this same answer. This is the output for task `045/2.1` of this repository, with the text of each block left out:
|
|
113
|
+
|
|
114
|
+
```text
|
|
115
|
+
$ specctx context 045/2.1
|
|
116
|
+
-- 045/2.1 | openspec/changes/archive/2026-10-30-045-query-by-file/tasks.md:11 | done | requested
|
|
117
|
+
-- 045/D2 | openspec/changes/archive/2026-10-30-045-query-by-file/design.md:22 | archived | design named in 045/2.1
|
|
118
|
+
-- 045/D3 | openspec/changes/archive/2026-10-30-045-query-by-file/design.md:23 | archived | design named in 045/2.1
|
|
119
|
+
-- REQ-IDX-008 | openspec/specs/index/spec.md:133 | active | delta in change of 045/2.1
|
|
120
|
+
-- REQ-IDX-009 | openspec/specs/index/spec.md:146 | active | delta in change of 045/2.1
|
|
121
|
+
-- REQ-QRY-005 | openspec/specs/query/spec.md:76 | active | delta in change of 045/2.1
|
|
122
|
+
-- REQ-QRY-006 | openspec/specs/query/spec.md:93 | active | ref from 045/2.1
|
|
123
|
+
-- REQ-QRY-008 | openspec/specs/query/spec.md:106 | active | delta in change of 045/2.1
|
|
124
|
+
-- src/graph/files.ts:1 | src/graph/files.ts:1 | active | implements REQ-QRY-006
|
|
125
|
+
-- tests/query/file-edges.test.ts:1 | tests/query/file-edges.test.ts:1 | active | covers REQ-QRY-006
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
`bundle` gives the same answer for one input. It also takes several inputs, `--block`, and `--all`. When the text passes the budget, blocks drop in this order. First go the archived decisions and the entries two steps away. Then go the specs. Then go the code and test lines. The input and the requirements it cites stay.
|
|
129
|
+
|
|
110
130
|
`specctx related` prints each neighbor of an ID and the reason for the link. This is the output for the demo project after its last step:
|
|
111
131
|
|
|
112
132
|
```text
|
|
@@ -118,6 +138,53 @@ code src/login.ts:1 active src/login.ts:1 implements REQ-UA-004
|
|
|
118
138
|
test tests/login.test.ts:1 active tests/login.test.ts:1 covers REQ-UA-004
|
|
119
139
|
```
|
|
120
140
|
|
|
141
|
+
### Ask about a file
|
|
142
|
+
|
|
143
|
+
`related`, `context`, and `bundle` take a file path where they take an ID. Run it before you edit a file. The reason on each line tells you how strong the link is. `implements` comes from a code tag. `covers` comes from a `Covers:` tag. `mentions` comes from a path in backticks in a task line of an active change. This is the output for one file of this repository:
|
|
144
|
+
|
|
145
|
+
```text
|
|
146
|
+
$ specctx related src/util/hash.ts
|
|
147
|
+
adr ADR-004 active decisions/ADR-004-tasks-md-is-the-view-evidence-is-the-record.md:1 ref from REQ-TASK-004
|
|
148
|
+
req REQ-CHK-003 active openspec/specs/check/spec.md:64 ref to REQ-TASK-004
|
|
149
|
+
req REQ-STAT-002 active openspec/specs/metrics/spec.md:19 ref to REQ-TASK-004
|
|
150
|
+
req REQ-TASK-004 active openspec/specs/task-workflow/spec.md:89 implements, tag at src/util/hash.ts:1
|
|
151
|
+
req REQ-VER-004 active openspec/specs/verify-runner/spec.md:37 ref to REQ-TASK-004
|
|
152
|
+
scn SCN-TASK-004-1 active openspec/specs/task-workflow/spec.md:94 scenario of REQ-TASK-004
|
|
153
|
+
task 002/3.3 done openspec/changes/archive/2026-10-24-002-ids-and-tasks/tasks.md:17 task cites REQ-TASK-004
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
A path that is not a file in the repository exits 2. A file with no links prints `no links: <path>` and exits 0.
|
|
157
|
+
|
|
158
|
+
### Ask about a change or a task
|
|
159
|
+
|
|
160
|
+
`related`, `context`, and `bundle` take a change key or a folder name where they take an ID. A change reaches the requirements and scenarios of its delta specs and the capabilities they touch. This is the output for an archived change of this repository:
|
|
161
|
+
|
|
162
|
+
```text
|
|
163
|
+
$ specctx related 045
|
|
164
|
+
req REQ-IDX-008 active openspec/specs/index/spec.md:133 delta in change/045
|
|
165
|
+
req REQ-IDX-009 active openspec/specs/index/spec.md:146 delta in change/045
|
|
166
|
+
req REQ-QRY-005 active openspec/specs/query/spec.md:76 delta in change/045
|
|
167
|
+
req REQ-QRY-006 active openspec/specs/query/spec.md:93 delta in change/045
|
|
168
|
+
req REQ-QRY-008 active openspec/specs/query/spec.md:106 delta in change/045
|
|
169
|
+
scn SCN-IDX-008-1 active openspec/specs/index/spec.md:138 delta in change/045
|
|
170
|
+
scn SCN-IDX-008-2 active openspec/specs/index/spec.md:142 delta in change/045
|
|
171
|
+
scn SCN-IDX-009-1 active openspec/specs/index/spec.md:151 delta in change/045
|
|
172
|
+
scn SCN-IDX-009-2 active openspec/specs/index/spec.md:155 delta in change/045
|
|
173
|
+
scn SCN-QRY-005-1 active openspec/specs/query/spec.md:81 delta in change/045
|
|
174
|
+
scn SCN-QRY-005-2 active openspec/specs/query/spec.md:85 delta in change/045
|
|
175
|
+
scn SCN-QRY-005-3 active openspec/specs/query/spec.md:89 delta in change/045
|
|
176
|
+
scn SCN-QRY-006-1 active openspec/specs/query/spec.md:98 delta in change/045
|
|
177
|
+
scn SCN-QRY-006-3 active openspec/specs/query/spec.md:102 delta in change/045
|
|
178
|
+
scn SCN-QRY-008-1 active openspec/specs/query/spec.md:111 delta in change/045
|
|
179
|
+
scn SCN-QRY-008-2 active openspec/specs/query/spec.md:115 delta in change/045
|
|
180
|
+
scn SCN-QRY-008-3 active openspec/specs/query/spec.md:119 delta in change/045
|
|
181
|
+
scn SCN-QRY-008-4 active openspec/specs/query/spec.md:123 delta in change/045
|
|
182
|
+
cap cap/index active openspec/specs/index/spec.md:1 capability touched by change/045
|
|
183
|
+
cap cap/query active openspec/specs/query/spec.md:1 capability touched by change/045
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
A task reaches the same delta entries and the design decisions that its line names, for example `specctx related 045/2.1`. A requirement reaches its capability, and `related cap/query` lists the requirements of one capability. A modified requirement shows its main version beside the delta. The index stores only the `change` and `cap` entries and the design links of tasks. The other edges come from the folder structure when you ask. `query` hides the `change` and `cap` entries unless you pass `--kind change` or `--kind cap`.
|
|
187
|
+
|
|
121
188
|
## The life cycle for agents
|
|
122
189
|
|
|
123
190
|
`init --all` installs one skill, `specctx-run`. The user types one prompt, such as "build this" or "continue". The skill loops until the change reaches the archive or a human must decide.
|
|
@@ -138,7 +205,7 @@ specctx gate --strict # check, baseline, linters, openspec validate, le
|
|
|
138
205
|
specctx handoff write # .ctx/handoff.md for the next session
|
|
139
206
|
```
|
|
140
207
|
|
|
141
|
-
The rule: the agent never opens `openspec/specs/` by hand. It uses `task start` or `
|
|
208
|
+
The rule: the agent never opens `openspec/specs/` by hand. It uses `task start` or `context`. It never ticks a checkbox and never runs `/opsx:apply`. The skill is in [`skills/`](https://github.com/sonqh/spec-ctx/blob/main/skills/). Install it alone with `npx skills add sonqh/spec-ctx` or `specctx init --skills`.
|
|
142
209
|
|
|
143
210
|
## Install
|
|
144
211
|
|
|
@@ -158,7 +225,7 @@ The examples below use `specctx`. With `npx`, write `npx -y spec-ctx` in its pla
|
|
|
158
225
|
|
|
159
226
|
## Two paths
|
|
160
227
|
|
|
161
|
-
[`docs/getting-started.md`](docs/getting-started.md) walks the fresh path on upstream OpenSpec with real output, for a developer who knows OpenSpec.
|
|
228
|
+
[`docs/getting-started.md`](https://github.com/sonqh/spec-ctx/blob/main/docs/getting-started.md) walks the fresh path on upstream OpenSpec with real output, for a developer who knows OpenSpec.
|
|
162
229
|
|
|
163
230
|
### Fresh project
|
|
164
231
|
|
|
@@ -180,6 +247,7 @@ specctx baseline write
|
|
|
180
247
|
specctx gate --strict
|
|
181
248
|
```
|
|
182
249
|
|
|
250
|
+
- `adopt --dry-run` exits 1 when headers need IDs. A script with `set -e` must allow that code.
|
|
183
251
|
- `adopt` gives an ID to every requirement and scenario header without one, in `openspec/specs/` and in the deltas of active changes. A delta header gets the main ID by its text or through its RENAMED line. A delta header that matches neither keeps its text and goes into `adopt-report.md`.
|
|
184
252
|
- `adopt` derives a capability code from each folder name, for example `UA` for `user-auth`. It writes the codes into `spec-ctx.toml`. Rename a code there before the IDs spread.
|
|
185
253
|
- `adopt` runs `check` before and after the write. A finding that the write added undoes every file. A second run prints `nothing to adopt`.
|
|
@@ -198,8 +266,9 @@ Both paths have an end to end test on upstream OpenSpec: `tests/paths/fresh.sh`
|
|
|
198
266
|
| `status` | Active changes with task counts, the current task, and open questions. |
|
|
199
267
|
| `next` | The next action of the loop from the files on disk: `plan`, `cite`, `start`, `work`, `fix`, `repair`, `gate`, `archive`, `ask`, or `stop`. `--json` prints one object. Flag: `--change`. |
|
|
200
268
|
| `query <term...>` | Entries that hold every term. Flags: `--kind`, `--change`, `--limit`, `--all`. |
|
|
201
|
-
| `
|
|
202
|
-
| `
|
|
269
|
+
| `context <task\|ID\|path\|change>` | The spec text, code lines, and test lines that relate to one input, in a budget. `task start` prints the same answer. Flags: `--budget`, `--depth`. |
|
|
270
|
+
| `related <ID\|path\|change>` | Linked entries with the reason for each link. A file path or a change works too. Flags: `--depth`, `--all`. |
|
|
271
|
+
| `bundle <ID\|path\|change...>` | The answer of `context` for several inputs. Flags: `--budget`, `--depth`, `--all`, `--block`. |
|
|
203
272
|
| `check` | ID and link integrity. `--strict` adds test coverage and evidence. `retitle-needs-renamed` names a retitled delta header that needs a RENAMED pair before `openspec archive`. |
|
|
204
273
|
| `id new`, `id retire` | Allocate and retire IDs. |
|
|
205
274
|
| `task next`, `start`, `verify`, `done`, `reopen` | The task state machine. |
|
|
@@ -249,6 +318,9 @@ base = "origin/main" # the ref whose merge base holds the baseline that gate co
|
|
|
249
318
|
[loop]
|
|
250
319
|
max_attempts = 3 # Verify failures on one task before task done writes a question
|
|
251
320
|
archive = "ask" # "ask" stops before archive for a human. "auto" lets the loop archive.
|
|
321
|
+
|
|
322
|
+
[cli]
|
|
323
|
+
run = "specctx" # the prefix of every command that spec-ctx prints. init writes npx -y spec-ctx@<version> under npx.
|
|
252
324
|
```
|
|
253
325
|
|
|
254
326
|
With `brackets = true`, `init --schema` installs templates with `[REQ-<CAP>-NNN]` and `[SCN-<CAP>-NNN-N]` in the headers. The merged rules show the same form. Task citations, `Refs:` lines, and tags stay bare. After you change the value, run `specctx init --schema` again. It switches each template and rule that you did not edit. It keeps the ones you edited, unless you add `--force`.
|
|
@@ -284,10 +356,41 @@ npx spec-ctx lint ste --baseline 0 --disable synonym-rotation README.md
|
|
|
284
356
|
|
|
285
357
|
The action runs `specctx gate`: `check --strict`, both markdown linters, `openspec validate --all --strict`, and the length check.
|
|
286
358
|
|
|
359
|
+
### Rerun
|
|
360
|
+
|
|
361
|
+
```yaml
|
|
362
|
+
on: pull_request
|
|
363
|
+
permissions:
|
|
364
|
+
contents: read
|
|
365
|
+
jobs:
|
|
366
|
+
rerun:
|
|
367
|
+
runs-on: ubuntu-latest
|
|
368
|
+
steps:
|
|
369
|
+
- uses: actions/checkout@v4
|
|
370
|
+
with: { fetch-depth: 0 }
|
|
371
|
+
- uses: sonqh/spec-ctx@v0
|
|
372
|
+
with:
|
|
373
|
+
rerun: true
|
|
374
|
+
base: origin/${{ github.base_ref }}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
With `rerun: true` the action runs `specctx gate --rerun` and no other step. It runs the Verify command of each task that the pull request closed and compares the exit code with the evidence. `base` is the ref for the merge base. Without `rerun`, the action ignores `base`.
|
|
378
|
+
|
|
379
|
+
On `push`, the default base gives a merge base equal to HEAD, so the rerun compares nothing. It prints one line and passes. Pass `base: ${{ github.event.before }}` on push. A push that creates a branch gives forty zeros. The rerun prints `rerun: base is the zero commit (a new branch), nothing to compare` and passes. After a force push the old commit may be missing from the clone. The rerun then prints `rerun: base <ref> is not in this clone. Fetch it or use fetch-depth: 0` and exits 1.
|
|
380
|
+
|
|
381
|
+
A Verify command is code from the pull request. The boundary:
|
|
382
|
+
|
|
383
|
+
- Use the `pull_request` and `push` events only. The action exits 1 on any other event.
|
|
384
|
+
- Never use `pull_request_target` or `workflow_run` with code from a pull request. The action exits 1 on `pull_request_target`.
|
|
385
|
+
- Set `permissions: contents: read`.
|
|
386
|
+
- Give the job no secrets.
|
|
387
|
+
- For fork pull requests, turn on the repository setting that asks for approval before a workflow runs.
|
|
388
|
+
- Check out with `fetch-depth: 0`. The merge base needs full history.
|
|
389
|
+
|
|
287
390
|
## Status
|
|
288
391
|
|
|
289
|
-
- Published: `spec-ctx@0.
|
|
290
|
-
- Archived: 001 to 025 and
|
|
392
|
+
- Published: `spec-ctx@0.4.1` on npm, with tags `v0.4.1` and `v0` and binaries on the GitHub release.
|
|
393
|
+
- Archived: 001 to 025, 027 to 034, and 036 to 049. No change is open. See `CHANGELOG.md` for each release.
|
|
291
394
|
- A version bump on `main` publishes the next release. See `docs/CONTRIBUTING.md`.
|
|
292
395
|
- Upstream: the ID scheme is proposed as a comment on [OpenSpec#1967](https://github.com/Fission-AI/OpenSpec/issues/1967#issuecomment-6414314730). The schema bundle is submitted to the community table in [OpenSpec#2157](https://github.com/Fission-AI/OpenSpec/issues/2157). No pull request until a maintainer asks.
|
|
293
396
|
|
|
@@ -297,9 +400,9 @@ The action runs `specctx gate`: `check --strict`, both markdown linters, `opensp
|
|
|
297
400
|
|
|
298
401
|
| change | context_ratio | evidence_ratio | stale_catches | cycle_time_median_s | debt |
|
|
299
402
|
| --- | --- | --- | --- | --- | --- |
|
|
300
|
-
| total | 0.
|
|
403
|
+
| total | 0.0189 (n=135) | 0.94 (236/252) | 65 (of 118 reopens) | 60 (n=135) | 0 (0 baselined + 0 new) |
|
|
301
404
|
|
|
302
|
-
- `context_ratio`: the size of one task bundle divided by the size of all spec text. Lower is better.
|
|
405
|
+
- `context_ratio`: the size of one task bundle divided by the size of all spec text. Lower is better. The meaning changed on 2026-10-30 with change 047-context. Before, `task start` printed the cited requirements. Now it prints the `context` answer, which holds more. `bundle_chars` in the evidence of older tasks keeps the old size, so the two do not compare.
|
|
303
406
|
- `evidence_ratio`: done tasks with a passed Verify command, divided by all done tasks. The rest have a manual note. Higher is better.
|
|
304
407
|
- `stale_catches`: reopened tasks that `check --strict` flagged as stale, out of all reopened tasks. A higher share means the check found the drift before a person did.
|
|
305
408
|
- `cycle_time_median_s`: the median number of seconds from `task start` to `task done`. Lower is faster.
|
package/action.yml
CHANGED
|
@@ -16,6 +16,12 @@ inputs:
|
|
|
16
16
|
specctx:
|
|
17
17
|
description: Command that runs specctx. Empty means npx with the version input.
|
|
18
18
|
default: ""
|
|
19
|
+
rerun:
|
|
20
|
+
description: Run only gate --rerun. It reruns the Verify command of each task that the pull request closed. Use it on pull_request or push only. On push, pass the commit before the push (github.event.before) as base.
|
|
21
|
+
default: "false"
|
|
22
|
+
base:
|
|
23
|
+
description: With rerun, the ref for the merge base, such as origin/main. On push, use the commit before the push (github.event.before). Empty uses the default of spec-ctx.toml. Without rerun, the action ignores it.
|
|
24
|
+
default: ""
|
|
19
25
|
runs:
|
|
20
26
|
using: composite
|
|
21
27
|
steps:
|
|
@@ -30,4 +36,6 @@ runs:
|
|
|
30
36
|
INPUT_STRICT: ${{ inputs.strict }}
|
|
31
37
|
INPUT_MARKDOWN: ${{ inputs.markdown }}
|
|
32
38
|
INPUT_SPECCTX: ${{ inputs.specctx }}
|
|
39
|
+
INPUT_RERUN: ${{ inputs.rerun }}
|
|
40
|
+
INPUT_BASE: ${{ inputs.base }}
|
|
33
41
|
SPECCTX_ACTION_PATH: ${{ github.action_path }}
|
package/dist/adopt/write.js
CHANGED
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
import { existsSync, readFileSync, rmSync, writeFileSync } from "node:fs";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { UsageExit } from "../cli/errors.js";
|
|
5
|
-
import { CONFIG_FILE
|
|
5
|
+
import { CONFIG_FILE } from "../config.js";
|
|
6
|
+
import { loadConfigOrExit } from "../cli/config.js";
|
|
7
|
+
import { runCommand } from "../runcmd.js";
|
|
6
8
|
import { dumps } from "../util/json.js";
|
|
7
9
|
import { configText, withCaps } from "./config-write.js";
|
|
8
10
|
export const REPORT_FILE = "adopt-report.md";
|
|
9
11
|
async function findings(root) {
|
|
10
12
|
const { buildIndex } = await import("../index/build.js");
|
|
11
13
|
const { check } = await import("../check/check.js");
|
|
12
|
-
const cfg =
|
|
14
|
+
const cfg = loadConfigOrExit(root);
|
|
13
15
|
const data = buildIndex(root, cfg);
|
|
14
16
|
return new Set(check(root, cfg, data, false).map((f) => `${f.file}:${f.line} ${f.rule} ${f.id}`));
|
|
15
17
|
}
|
|
@@ -56,7 +58,7 @@ export function reportText(plan) {
|
|
|
56
58
|
}
|
|
57
59
|
return out.join("\n") + "\n";
|
|
58
60
|
}
|
|
59
|
-
export async function applyAdopt(root,
|
|
61
|
+
export async function applyAdopt(root, cfg, plan, io, json) {
|
|
60
62
|
const cfgPath = join(root, CONFIG_FILE);
|
|
61
63
|
const cfgBefore = existsSync(cfgPath) ? readFileSync(cfgPath, "utf8") : null;
|
|
62
64
|
const cfgAfter = withCaps(configText(root), plan.caps);
|
|
@@ -119,8 +121,11 @@ export async function applyAdopt(root, _cfg, plan, io, json) {
|
|
|
119
121
|
// REQ-CLI-007 only the codes in spec-ctx.toml changed: no header, no report
|
|
120
122
|
if (!report)
|
|
121
123
|
io.out(`wrote the codes to ${CONFIG_FILE}\nnothing to adopt\n`);
|
|
122
|
-
else
|
|
124
|
+
else {
|
|
123
125
|
io.out(`adopted ${plan.edits.length} header(s) in ${files} file(s). Report: ${REPORT_FILE}\n`);
|
|
126
|
+
// REQ-ADOPT-002 adopt writes no baseline. The next step is a separate commit (044/D4, ADR-006 item 6).
|
|
127
|
+
io.out(`Commit the baseline with a subject that starts with baseline:.\nnext: ${runCommand(cfg.cli.run, "baseline write")}\n`);
|
|
128
|
+
}
|
|
124
129
|
}
|
|
125
130
|
return 0;
|
|
126
131
|
}
|
package/dist/assets.generated.js
CHANGED
|
@@ -10,5 +10,5 @@ export const ASSETS = {
|
|
|
10
10
|
"skills/specctx-bootstrap/SKILL.md": "LS0tCm5hbWU6IHNwZWNjdHgtYm9vdHN0cmFwCmRlc2NyaXB0aW9uOiBTdGFydCBhIHNlc3Npb24gaW4gYW4gT3BlblNwZWMgcHJvamVjdCB0aGF0IHVzZXMgc3BlYy1jdHguIFVzZSBhdCB0aGUgc3RhcnQgb2YgZXZlcnkgc2Vzc2lvbiwgYW5kIG9uICJzdGFydCIsICJib290c3RyYXAiLCBvciAid2hhdCBzaG91bGQgSSB3b3JrIG9uIi4gTG9hZHMgb25seSB0aGUgc3BlYyB0ZXh0IHRoYXQgdGhlIG5leHQgdGFzayBuZWVkcy4KLS0tCgpUaGlzIHNraWxsIGlzIG5vdyBgc3BlY2N0eC1ydW5gLiBSdW4gaXQuCg==",
|
|
11
11
|
"skills/specctx-done/SKILL.md": "LS0tCm5hbWU6IHNwZWNjdHgtZG9uZQpkZXNjcmlwdGlvbjogQ2xvc2UgYSB0YXNrIGluIGFuIE9wZW5TcGVjIHByb2plY3QgdGhhdCB1c2VzIHNwZWMtY3R4LiBSdW5zIHRoZSBWZXJpZnkgY29tbWFuZCwgd3JpdGVzIGV2aWRlbmNlLCBzZXRzIHRoZSBjaGVja2JveCwgd3JpdGVzIHRoZSBoYW5kb2ZmLCBhbmQgc2hvd3MgdGhlIG1ldHJpY3MuIFVzZSB3aGVuIHRoZSB3b3JrIG9mIGEgdGFzayBpcyBjb21wbGV0ZSwgb3Igb24gImRvbmUiLCAiY2xvc2UgdGhlIHRhc2siLCBhbmQgImVuZCB0aGUgc2Vzc2lvbiIuCi0tLQoKVGhpcyBza2lsbCBpcyBub3cgYHNwZWNjdHgtcnVuYC4gUnVuIGl0Lgo=",
|
|
12
12
|
"skills/specctx-next/SKILL.md": "LS0tCm5hbWU6IHNwZWNjdHgtbmV4dApkZXNjcmlwdGlvbjogUGljayBhbmQgc3RhcnQgdGhlIG5leHQgdGFzayBpbiB0aGUgbWlkZGxlIG9mIGEgc2Vzc2lvbiBpbiBhbiBPcGVuU3BlYyBwcm9qZWN0IHRoYXQgdXNlcyBzcGVjLWN0eC4gVXNlIGFmdGVyIHlvdSBjbG9zZSBhIHRhc2ssIG9yIG9uICJuZXh0IHRhc2siIGFuZCAid2hhdCBpcyBuZXh0Ii4KLS0tCgpUaGlzIHNraWxsIGlzIG5vdyBgc3BlY2N0eC1ydW5gLiBSdW4gaXQuCg==",
|
|
13
|
-
"skills/specctx-run/SKILL.md": "LS0tCm5hbWU6IHNwZWNjdHgtcnVuCmRlc2NyaXB0aW9uOiBSdW4gdGhlIHNwZWMtY3R4IGxvb3AgaW4gYW4gT3BlblNwZWMgcHJvamVjdC4gVXNlIG9uICJydW4iLCAiY29udGludWUiLCAibmV4dCB0YXNrIiwgImJ1aWxkIHRoaXMiLCAiY2Fycnkgb24iLCBhbmQgb24gYSBmaXJzdCBtZXNzYWdlIHRoYXQgZGVzY3JpYmVzIGEgY2hhbmdlIHRvIGJ1aWxkLiBUaGUgZW5naW5lIHBpY2tzIHRoZSBuZXh0IGFjdGlvbi4gVGhlIHNraWxsIGFjdHMgb24gaXQgdW50aWwgdGhlIGNoYW5nZSByZWFjaGVzIHRoZSBhcmNoaXZlIG9yIGEgaHVtYW4gbXVzdCBkZWNpZGUuCi0tLQoKIyBzcGVjY3R4LXJ1bgoKUnVuIGBzcGVjY3R4YCBhcyBgbnB4IHNwZWMtY3R4YCB3aGVuIGl0IGlzIG5vdCBpbnN0YWxsZWQuIGBvcGVuc3BlY2AgaXMgdGhlIGNvbW1hbmQgaW4gYFtnYXRlXSBvcGVuc3BlY2Agb2YgYHNwZWMtY3R4LnRvbWxgLiBXaGVuIHRoYXQga2V5IGlzIGFic2VudCwgYG9wZW5zcGVjYCBpcyBgbnB4IC15IEBmaXNzaW9uLWFpL29wZW5zcGVjQGxhdGVzdGAuCgojIyBUaGUgbG9vcAoKMS4gUnVuIGBzcGVjY3R4IG5leHQgLS1qc29uYC4gUmVhZCBgYWN0aW9uYCwgYGNoYW5nZWAgKGA8Yz5gKSwgYHRhc2tgIChgPHQ+YCksIGBjb21tYW5kYCwgYHJlYXNvbmAsIGFuZCBgaHVtYW5gLgoyLiBBY3Qgb24gYGFjdGlvbmAgYXMgdGhlIHRhYmxlIGJlbG93IHNheXMuCjMuIFJ1biBgc3BlY2N0eCBoYW5kb2ZmIHdyaXRlYC4KNC4gUmVwZWF0IGZyb20gc3RlcCAxIHVudGlsIGBhY3Rpb25gIGlzIGBzdG9wYCBvciBgaHVtYW5gIGlzIHRydWUuCgojIyBBY3Rpb25zCgotIGBwbGFuYDogdGFrZSB0aGUgY2hhbmdlIG5hbWUgZnJvbSB0aGUgcmVxdWVzdCBvZiB0aGUgdXNlciBpbiBrZWJhYiBjYXNlLiBUaGF0IG5hbWUgaXMgYDxjPmAuIEZvbGxvdyB0aGUgdXBzdHJlYW0gYG9wZW5zcGVjLXByb3Bvc2VgIHNraWxsIGZvciBgPGM+YC4gUmVhZCBleGlzdGluZyByZXF1aXJlbWVudHMgd2l0aCBgc3BlY2N0eCBidW5kbGUgPGlkPmAgb3IgYHNwZWNjdHggcXVlcnlgLCBuZXZlciBgb3BlbnNwZWMgc2hvd2AuIFRha2UgZXZlcnkgSUQgZnJvbSBgc3BlY2N0eCBpZCBuZXcgUkVRIDxDQVA+
|
|
13
|
+
"skills/specctx-run/SKILL.md": "LS0tCm5hbWU6IHNwZWNjdHgtcnVuCmRlc2NyaXB0aW9uOiBSdW4gdGhlIHNwZWMtY3R4IGxvb3AgaW4gYW4gT3BlblNwZWMgcHJvamVjdC4gVXNlIG9uICJydW4iLCAiY29udGludWUiLCAibmV4dCB0YXNrIiwgImJ1aWxkIHRoaXMiLCAiY2Fycnkgb24iLCBhbmQgb24gYSBmaXJzdCBtZXNzYWdlIHRoYXQgZGVzY3JpYmVzIGEgY2hhbmdlIHRvIGJ1aWxkLiBUaGUgZW5naW5lIHBpY2tzIHRoZSBuZXh0IGFjdGlvbi4gVGhlIHNraWxsIGFjdHMgb24gaXQgdW50aWwgdGhlIGNoYW5nZSByZWFjaGVzIHRoZSBhcmNoaXZlIG9yIGEgaHVtYW4gbXVzdCBkZWNpZGUuCi0tLQoKIyBzcGVjY3R4LXJ1bgoKUnVuIGBzcGVjY3R4YCBhcyBgbnB4IHNwZWMtY3R4YCB3aGVuIGl0IGlzIG5vdCBpbnN0YWxsZWQuIGBvcGVuc3BlY2AgaXMgdGhlIGNvbW1hbmQgaW4gYFtnYXRlXSBvcGVuc3BlY2Agb2YgYHNwZWMtY3R4LnRvbWxgLiBXaGVuIHRoYXQga2V5IGlzIGFic2VudCwgYG9wZW5zcGVjYCBpcyBgbnB4IC15IEBmaXNzaW9uLWFpL29wZW5zcGVjQGxhdGVzdGAuCgojIyBUaGUgbG9vcAoKMS4gUnVuIGBzcGVjY3R4IG5leHQgLS1qc29uYC4gUmVhZCBgYWN0aW9uYCwgYGNoYW5nZWAgKGA8Yz5gKSwgYHRhc2tgIChgPHQ+YCksIGBjb21tYW5kYCwgYHJlYXNvbmAsIGFuZCBgaHVtYW5gLgoyLiBBY3Qgb24gYGFjdGlvbmAgYXMgdGhlIHRhYmxlIGJlbG93IHNheXMuCjMuIFJ1biBgc3BlY2N0eCBoYW5kb2ZmIHdyaXRlYC4KNC4gUmVwZWF0IGZyb20gc3RlcCAxIHVudGlsIGBhY3Rpb25gIGlzIGBzdG9wYCBvciBgaHVtYW5gIGlzIHRydWUuCgojIyBBY3Rpb25zCgotIGBwbGFuYDogdGFrZSB0aGUgY2hhbmdlIG5hbWUgZnJvbSB0aGUgcmVxdWVzdCBvZiB0aGUgdXNlciBpbiBrZWJhYiBjYXNlLiBUaGF0IG5hbWUgaXMgYDxjPmAuIEZvbGxvdyB0aGUgdXBzdHJlYW0gYG9wZW5zcGVjLXByb3Bvc2VgIHNraWxsIGZvciBgPGM+YC4gUmVhZCBleGlzdGluZyByZXF1aXJlbWVudHMgd2l0aCBgc3BlY2N0eCBidW5kbGUgPGlkPmAgb3IgYHNwZWNjdHggcXVlcnlgLCBuZXZlciBgb3BlbnNwZWMgc2hvd2AuIFRha2UgZXZlcnkgSUQgZnJvbSBgc3BlY2N0eCBpZCBuZXcgUkVRIDxDQVA+YCBhbmQgYHNwZWNjdHggaWQgbmV3IFNDTiA8Q0FQPiAtLWZvciA8UkVRPmAuIFdoZW4gdGhlIGNoYW5nZSBuZWVkcyBzZXZlcmFsIElEcyBvZiBvbmUga2luZCwgcnVuIGBzcGVjY3R4IGlkIG5ldyAtLWNvdW50IDxuPmAgb25jZS4gR2l2ZSBldmVyeSB0YXNrIGEgYFtSRVEtLi4uXWAgY2l0YXRpb24gYW5kIGEgYFZlcmlmeTpgIGNvZGUgc3Bhbi4gVGhlbiBydW4gYHNwZWNjdHggY2hlY2sgLS1zdHJpY3RgIGFuZCBgc3BlY2N0eCBnYXRlIC0tc3RyaWN0YC4gVGhlIGdhdGUgcnVucyB0aGUgT3BlblNwZWMgdmFsaWRhdGUgYW5kIHRoZSBzcGVjLWxlbmd0aCBjaGVjay4gRml4IGV2ZXJ5IGZpbmRpbmcuIFRoZW4gcnVuIGBzcGVjY3R4IG5leHQgLS1qc29uYCBhbmQgcnVuIHRoZSBjb21tYW5kIGl0IHByaW50cy4gVGhpcyBpcyB0aGUgcGxhbiBzdG9wLiBgbmV4dGAgcmV0dXJucyBgYXNrYCB3aXRoIGBodW1hbjogdHJ1ZWAgdW50aWwgdGhlIGh1bWFuIGFwcHJvdmVzIHRoZSBxdWVzdGlvbi4gWW91IG5ldmVyIHJlc29sdmUgYSBwbGFuIHF1ZXN0aW9uIHlvdXJzZWxmLgotIGBjaXRlYDogcnVuIGBzcGVjY3R4IHF1ZXJ5IC0tY2hhbmdlIDxjPiAtLWtpbmQgcmVxYC4gQWRkIHRoZSBgW1JFUS0uLi5dYCBjaXRhdGlvbiBhbmQgYSBgVmVyaWZ5OmAgY29kZSBzcGFuIHRvIHRoZSB0YXNrIGxpbmUgbmFtZWQgaW4gYHRhc2tgLiBSdW4gYHNwZWNjdHggY2hlY2tgLiBDb250aW51ZS4KLSBgc3RhcnRgOiBydW4gYHNwZWNjdHggdGFzayBzdGFydCA8dD5gLiBSZWFkIHRoZSBidW5kbGUgaXQgcHJpbnRzLiBDb250aW51ZS4KLSBgd29ya2A6IGltcGxlbWVudCB0aGUgdGFzay4gVGFnIGNvZGUgd2l0aCBgLy8gUkVRLS4uLmAuIFRhZyB0ZXN0cyB3aXRoIGAvLyBDb3ZlcnM6IFJFUS0uLi5gLiBUaGVuIHJ1biBgc3BlY2N0eCB0YXNrIGRvbmUgPHQ+YC4KLSBgZml4YDogcmVhZCBgcmVhc29uYC4gSXQgaG9sZHMgdGhlIHRhaWwgb2YgdGhlIGZhaWxlZCBWZXJpZnkgb3V0cHV0LiBGaXggdGhlIGNhdXNlLiBSdW4gYHNwZWNjdHggdGFzayBkb25lIDx0PmAgYWdhaW4uCi0gYHJlcGFpcmA6IHJ1biB0aGUgY29tbWFuZCBpbiB0aGUgYGNvbW1hbmRgIGZpZWxkLiBJdCBydW5zIFZlcmlmeSBhZ2FpbiBmb3IgYSB0aWNrZWQgdGFzayB3aG9zZSBldmlkZW5jZSBpcyBhYnNlbnQgb3Igc3RhbGUuCi0gYGdhdGVgOiBydW4gYHNwZWNjdHggZ2F0ZSAtLXN0cmljdGAuIEZpeCBldmVyeSBmaW5kaW5nLiBGb3IgYSBgc3RhbGUtZXZpZGVuY2VgIGZpbmRpbmcsIGdvIGJhY2sgdG8gc3RlcCAxLiBgbmV4dGAgcmV0dXJucyBgcmVwYWlyYCBhbmQgaXRzIGNvbW1hbmQgcmVwYWlycyBldmVyeSBzdGFsZSB0YXNrLiBSdW4gdGhlIGdhdGUgYWdhaW4gdW50aWwgaXQgZXhpdHMgMC4KLSBgYXJjaGl2ZWA6IHdoZW4gYGh1bWFuYCBpcyB0cnVlLCBydW4gYHNwZWNjdHggaGFuZG9mZiBhc2sgLS1jaGFuZ2UgPGM+ICJhcmNoaXZlIDxjPj8iYCBhbmQgc3RvcC4gVGhpcyBpcyB0aGUgYXJjaGl2ZSBzdG9wLiBgbmV4dGAgcmV0dXJucyBgYXNrYCB1bnRpbCB0aGUgaHVtYW4gYXBwcm92ZXMgdGhlIHF1ZXN0aW9uLiBZb3UgbmV2ZXIgcmVzb2x2ZSBhbiBhcmNoaXZlIHF1ZXN0aW9uIHlvdXJzZWxmLiBBZnRlciBhIHJlamVjdGlvbiwgYG5leHRgIHJldHVybnMgYGFyY2hpdmVgIHdpdGggYGh1bWFuOiB0cnVlYCBhZ2Fpbi4gV2hlbiBgaHVtYW5gIGlzIGZhbHNlLCB0aGUgdXNlciBhcHByb3ZlZCBvciBgW2xvb3BdIGFyY2hpdmVgIGlzIGBhdXRvYC4gVGhlbiBydW4gZWFjaCBzdGVwIHVuZGVyIGBvcGVyYXRpb25zLmFyY2hpdmUuZ3VpZGFuY2VgIGluIGBvcGVuc3BlYy9jb25maWcueWFtbGAuIFRoZW4gcnVuIHRoZSBjb21tYW5kIGluIHRoZSBgY29tbWFuZGAgZmllbGQuIFRoZW4gcnVuIGBvcGVuc3BlYyB2YWxpZGF0ZSAtLWFsbCAtLXN0cmljdGAsIGBzcGVjY3R4IGNoZWNrIC0tc3RyaWN0YCwgYW5kIGBzcGVjY3R4IGhhbmRvZmYgd3JpdGVgLgotIGBhc2tgOiBwcmludCBgcmVhc29uYC4gSXQgaXMgdGhlIG9wZW4gcXVlc3Rpb24uIFN0b3AuIFlvdSBuZXZlciByZXNvbHZlIGEgcGxhbiBvciBhcmNoaXZlIHF1ZXN0aW9uIHlvdXJzZWxmLiBUaGUgaHVtYW4gZG9lcywgd2l0aCBgc3BlY2N0eCBoYW5kb2ZmIHJlc29sdmUgPG4+IC0tYXBwcm92ZSAiPGFuc3dlcj4iYCBvciBgLS1yZWplY3QgIjxhbnN3ZXI+ImAuIEEgcmVqZWN0aW9uIGtlZXBzIHRoZSBzdG9wLiBBbnkgb3RoZXIgcXVlc3Rpb24gdGFrZXMgYHNwZWNjdHggaGFuZG9mZiByZXNvbHZlIDxuPiAiPGFuc3dlcj4iYC4KLSBgc3RvcGA6IHByaW50IGByZWFzb25gLiBTdG9wLgoKIyMgUnVsZXMKCi0gVGhpcyBza2lsbCBvdmVycmlkZXMgdHdvIHN0ZXBzIG9mIHRoZSB1cHN0cmVhbSBgb3BlbnNwZWMtcHJvcG9zZWAgc2tpbGwuIEl0IHJlYWRzIHNwZWNzIHdpdGggYHNwZWNjdHggYnVuZGxlYCBvciBgc3BlY2N0eCBxdWVyeWAsIG5vdCBgb3BlbnNwZWMgc2hvd2AuIEl0IG5ldmVyIHJ1bnMgdGhlIGNsb3NpbmcgYC9vcHN4OmFwcGx5YC4KLSBOZXZlciB0aWNrIGEgY2hlY2tib3ggYnkgaGFuZC4gYHNwZWNjdHggdGFzayBkb25lYCBpcyB0aGUgb25seSB3YXkgYSB0YXNrIGNsb3Nlcy4KLSBOZXZlciB1c2UgYC0tbWFudWFsYCBvbiBhIHRhc2sgd2l0aCBhIFZlcmlmeSBjb21tYW5kLgotIE5ldmVyIGNhbGwgYC9vcHN4OmFwcGx5YC4gVGhpcyBza2lsbCByZXBsYWNlcyBpdC4KLSBUbyB3cml0ZSBhIE1PRElGSUVEIGJsb2NrLCBydW4gYHNwZWNjdHggYnVuZGxlIDxSRVE+IC0tYmxvY2tgLiBJdCBwcmludHMgdGhlIGJsb2NrIGFzIGl0IHNpdHMgaW4gdGhlIHNwZWMgZmlsZS4gQSBNT0RJRklFRCBibG9jayBrZWVwcyBldmVyeSBzY2VuYXJpbyB0aXRsZSB1bmNoYW5nZWQuCi0gYGdhdGVgIGNvdW50cyB0aGUgUmVmcyBsaW5lIGluIHRoZSA1MDAgY2hhcmFjdGVycyBvZiBhIHJlcXVpcmVtZW50LgotIFRoZSBDSEFOR0VMT0cgZW50cnkgZ29lcyB1bmRlciBgIyMgVW5yZWxlYXNlZGAsIG5ldmVyIHVuZGVyIGEgcmVsZWFzZWQgdmVyc2lvbi4KLSBBZnRlciBhbiBlZGl0IHVuZGVyIGBza2lsbHMvYCwgcnVuIGBidW4gcnVuIGJ1aWxkICYmIG5vZGUgZGlzdC9jbGkuanMgaW5pdCAtLXNraWxscyAtLWZvcmNlYCB0byByZWZyZXNoIHRoZSBjb3BpZXMuCi0gTmV2ZXIgcmVhZCBgb3BlbnNwZWMvc3BlY3MvYCBvciBgb3BlbnNwZWMvY2hhbmdlcy8qL3NwZWNzL2AgZGlyZWN0bHkuIFVzZSB0aGUgYnVuZGxlIGZyb20gYHRhc2sgc3RhcnRgIG9yIGBzcGVjY3R4IGJ1bmRsZSA8aWQ+YC4KLSBTdG9wIG9uIGFueSBgYWN0aW9uYCBub3QgaW4gdGhlIGxpc3QgYWJvdmUuIFByaW50IHRoZSBvYmplY3QgYW5kIGFzayB0aGUgdXNlci4KLSBLZWVwIG5vIHN0YXRlIGluIHlvdXIgY29udGV4dC4gQSBuZXcgc2Vzc2lvbiBzdGFydHMgdGhlIHNhbWUgbG9vcCBhZ2FpbiB3aXRoIHN0ZXAgMS4K",
|
|
14
14
|
};
|
package/dist/check/check.js
CHANGED
|
@@ -190,6 +190,29 @@ export function check(root, cfg, data, strict) {
|
|
|
190
190
|
if (e.kind === "scn" && !named.has(e.id))
|
|
191
191
|
findings.push([e.file, e.line, "scenario-untested", e.id]);
|
|
192
192
|
}
|
|
193
|
+
// REQ-CHK-008 a delta scenario needs a test once its change has no open task (038/D1, D2)
|
|
194
|
+
const tasksOf = new Map();
|
|
195
|
+
for (const e of entries) {
|
|
196
|
+
if (e.kind !== "task" || e.change === "main" || e.file.includes("/archive/"))
|
|
197
|
+
continue;
|
|
198
|
+
const t = tasksOf.get(e.change) ?? { open: 0, all: 0 };
|
|
199
|
+
t.all++;
|
|
200
|
+
if (e.status !== "done")
|
|
201
|
+
t.open++;
|
|
202
|
+
tasksOf.set(e.change, t);
|
|
203
|
+
}
|
|
204
|
+
for (const e of entries) {
|
|
205
|
+
if ((e.kind !== "scn" && e.kind !== "req") || e.change === "main" || (e.status !== "active" && e.status !== "modified"))
|
|
206
|
+
continue;
|
|
207
|
+
const t = tasksOf.get(e.change);
|
|
208
|
+
if (!t || t.open > 0 || pending.has(e.id))
|
|
209
|
+
continue;
|
|
210
|
+
// REQ-CHK-009 a delta requirement needs a Covers tag on the same terms (039/D2)
|
|
211
|
+
if (e.kind === "req" && !covered.has(e.id))
|
|
212
|
+
findings.push([e.file, e.line, "uncovered", e.id]);
|
|
213
|
+
if (e.kind === "scn" && !named.has(e.id))
|
|
214
|
+
findings.push([e.file, e.line, "scenario-untested", e.id]);
|
|
215
|
+
}
|
|
193
216
|
findings.push(...evidenceFindings(root, entries, listEvidence(root)));
|
|
194
217
|
}
|
|
195
218
|
const uniq = new Map();
|
package/dist/check/hints.js
CHANGED
|
@@ -1,28 +1,32 @@
|
|
|
1
1
|
// REQ-CHK-004 the rules of check and one fix hint per rule. The check summary prints the hint of each rule it found.
|
|
2
2
|
/** `hard` rules set exit 1. `advisory` rules print a warning and keep exit 0. */
|
|
3
3
|
export const RULES = {
|
|
4
|
-
"missing-id": { level: "hard", hint: "Add an ID to the header. Run
|
|
4
|
+
"missing-id": { level: "hard", hint: "Add an ID to the header. Run {run} adopt --dry-run to see the plan." },
|
|
5
5
|
"scenario-id-mismatch": { level: "hard", hint: "Give the scenario the number of its requirement." },
|
|
6
6
|
"bad-ref": { level: "hard", hint: "Write only IDs on the Refs line, separated by commas." },
|
|
7
7
|
"task-without-req": { level: "hard", hint: "Cite a requirement in brackets on the task line, for example [REQ-ABC-001]." },
|
|
8
8
|
"task-without-verify": { level: "hard", hint: "Add Verify: and a command in backticks to the task line." },
|
|
9
|
-
"duplicate-id": { level: "hard", hint: "Give one of the two entries a new ID from
|
|
9
|
+
"duplicate-id": { level: "hard", hint: "Give one of the two entries a new ID from {run} id new." },
|
|
10
10
|
"duplicate-change-key": { level: "hard", hint: "Rename one change folder, so that each key names one change." },
|
|
11
|
-
"reused-id": { level: "hard", hint: "The ID is retired. Get a new ID from
|
|
12
|
-
|
|
11
|
+
"reused-id": { level: "hard", hint: "The ID is retired. Get a new ID from {run} id new." },
|
|
12
|
+
// REQ-CHK-007 the hint names the string literal cause and the fix (033/D3)
|
|
13
|
+
"dangling-ref": {
|
|
14
|
+
level: "hard",
|
|
15
|
+
hint: "No entry has this ID. Fix the ID or remove the link. A tag inside a string literal or fixture text also reads as a link. Build that text at run time, or move the file under a path in [scan] ignore.",
|
|
16
|
+
},
|
|
13
17
|
"unknown-task-req": { level: "hard", hint: "The task cites an ID that the index does not hold. Fix the ID on the task line." },
|
|
14
18
|
"req-without-scn": { level: "hard", hint: "Add a scenario under the requirement." },
|
|
15
19
|
"code-tag-no-canonical": { level: "hard", hint: "The tag names an archived requirement. Tag the current requirement." },
|
|
16
20
|
"unknown-cap": { level: "hard", hint: "Add the code to caps under [ids] in spec-ctx.toml." },
|
|
17
21
|
uncovered: { level: "hard", hint: "Add a test with a Covers tag for the requirement." },
|
|
18
|
-
"scenario-untested": { level: "hard", hint: "Name the scenario ID in a test." },
|
|
19
|
-
"done-without-evidence": { level: "hard", hint: "Run
|
|
22
|
+
"scenario-untested": { level: "hard", hint: "Name the scenario ID in a test with a // Covers: <SCN> tag." },
|
|
23
|
+
"done-without-evidence": { level: "hard", hint: "Run {run} task done. It runs the Verify command and writes the evidence." },
|
|
20
24
|
// REQ-CHK-006 the hint names the why argument of task reopen (031/D8)
|
|
21
|
-
"stale-evidence": { level: "hard", hint: "Run
|
|
22
|
-
"evidence-without-done": { level: "hard", hint: "Run
|
|
25
|
+
"stale-evidence": { level: "hard", hint: "Run {run} task reopen <task> <why>, then {run} task done <task>." },
|
|
26
|
+
"evidence-without-done": { level: "hard", hint: "Run {run} task done, or remove the evidence file." },
|
|
23
27
|
// REQ-CHK-005 advisory in plain check. check --strict reports it as a hard finding (028/D5).
|
|
24
28
|
"weak-verify": { level: "advisory", hint: "Name a test file or a command whose exit code proves the task." },
|
|
25
|
-
"manual-evidence": { level: "advisory", hint: "Give the task a Verify command and run
|
|
29
|
+
"manual-evidence": { level: "advisory", hint: "Give the task a Verify command and run {run} task done again." },
|
|
26
30
|
// REQ-CHK-001 the hint is the block to paste. The finding line carries the ID and both titles (021/D3).
|
|
27
31
|
// REQ-ADOPT-010 FROM copies the main header and TO copies the delta header, each in its own form (027/D4).
|
|
28
32
|
"retitle-needs-renamed": {
|
|
@@ -30,6 +34,7 @@ export const RULES = {
|
|
|
30
34
|
hint: "Add to the delta before the MODIFIED block. Write each ID as its header writes it, bare or in brackets:\n## RENAMED Requirements\n- FROM: `### Requirement: <ID> <old title>`\n- TO: `### Requirement: <ID> <new title>`",
|
|
31
35
|
},
|
|
32
36
|
};
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
/** REQ-CLI-010 `{run}` in a hint stands for the run prefix of `[cli] run` (043/D1). */
|
|
38
|
+
export function hintOf(rule, run = "specctx") {
|
|
39
|
+
return (RULES[rule]?.hint ?? "Read the check rules in the check spec.").replaceAll("{run}", run);
|
|
35
40
|
}
|
package/dist/cli/args.js
CHANGED
|
@@ -7,10 +7,11 @@ export const COMMAND_FLAGS = {
|
|
|
7
7
|
next: { change: "string" },
|
|
8
8
|
query: { kind: "string", change: "string", all: "bool", limit: "int" },
|
|
9
9
|
related: { depth: "int", all: "bool" },
|
|
10
|
-
bundle: { budget: "int", depth: "int", all: "bool" },
|
|
10
|
+
bundle: { budget: "int", depth: "int", all: "bool", block: "bool" },
|
|
11
|
+
context: { budget: "int", depth: "int" },
|
|
11
12
|
check: { strict: "bool" },
|
|
12
13
|
stats: { change: "string", write: "bool" },
|
|
13
|
-
gate: { strict: "bool", markdown: "string" },
|
|
14
|
+
gate: { strict: "bool", markdown: "string", rerun: "bool", base: "string" },
|
|
14
15
|
"id new": { for: "string", count: "int" },
|
|
15
16
|
"id retire": {},
|
|
16
17
|
"task next": { change: "string" },
|
|
@@ -19,8 +20,8 @@ export const COMMAND_FLAGS = {
|
|
|
19
20
|
"task done": { manual: "string" },
|
|
20
21
|
"task reopen": {},
|
|
21
22
|
"handoff write": {},
|
|
22
|
-
"handoff ask": { change: "string" },
|
|
23
|
-
"handoff resolve": {},
|
|
23
|
+
"handoff ask": { change: "string", plan: "bool" },
|
|
24
|
+
"handoff resolve": { approve: "bool", reject: "bool" },
|
|
24
25
|
adopt: { "dry-run": "bool" },
|
|
25
26
|
"baseline write": {},
|
|
26
27
|
"baseline prune": {},
|