@warnyin/sdlc 0.8.0 → 0.9.0

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.
Files changed (72) hide show
  1. package/CHANGELOG.md +274 -258
  2. package/LICENSE +21 -21
  3. package/README.md +92 -92
  4. package/bin/cli.mjs +682 -682
  5. package/lib/active.mjs +199 -199
  6. package/lib/caps.mjs +46 -46
  7. package/lib/config.mjs +41 -41
  8. package/lib/delta.mjs +227 -227
  9. package/lib/frontmatter.mjs +59 -59
  10. package/lib/glob.mjs +29 -29
  11. package/lib/lenses.mjs +48 -48
  12. package/lib/manifest.mjs +99 -99
  13. package/lib/settings-merge.mjs +63 -63
  14. package/lib/skills.mjs +148 -148
  15. package/lib/validate.mjs +198 -198
  16. package/package.json +42 -42
  17. package/payload/adapters/agents-md.md +8 -8
  18. package/payload/adapters/claude/agents/sdlc-architect.md +12 -12
  19. package/payload/adapters/claude/agents/sdlc-builder.md +14 -14
  20. package/payload/adapters/claude/agents/sdlc-contractor.md +13 -13
  21. package/payload/adapters/claude/agents/sdlc-evaluator.md +13 -13
  22. package/payload/adapters/claude/agents/sdlc-learner.md +16 -16
  23. package/payload/adapters/claude/agents/sdlc-ops.md +11 -11
  24. package/payload/adapters/claude/agents/sdlc-quality.md +13 -13
  25. package/payload/adapters/claude/agents/sdlc-security.md +12 -12
  26. package/payload/adapters/claude/commands/sdlc/converge.md +5 -5
  27. package/payload/adapters/claude/commands/sdlc/init.md +4 -4
  28. package/payload/adapters/claude/commands/sdlc/next.md +4 -4
  29. package/payload/adapters/claude/commands/sdlc/observe.md +4 -4
  30. package/payload/adapters/claude/commands/sdlc/steer.md +4 -4
  31. package/payload/adapters/claude/skills/contract-writing/SKILL.md +26 -26
  32. package/payload/adapters/claude/skills/delta-spec-format/SKILL.md +36 -36
  33. package/payload/adapters/claude/skills/sdlc-conventions/SKILL.md +30 -30
  34. package/payload/adapters/cline.md +8 -8
  35. package/payload/adapters/copilot.md +8 -8
  36. package/payload/adapters/cursor.mdc +7 -7
  37. package/payload/adapters/gemini.md +8 -8
  38. package/payload/adapters/windsurf.md +4 -4
  39. package/payload/hooks/_shared.mjs +138 -138
  40. package/payload/hooks/guard-writes.mjs +87 -87
  41. package/payload/hooks/inject-context.mjs +57 -57
  42. package/payload/hooks/journal.mjs +66 -66
  43. package/payload/hooks/session-summary.mjs +52 -52
  44. package/payload/hooks/validate-artifact.mjs +84 -84
  45. package/payload/playbook/README.md +32 -32
  46. package/payload/playbook/context.md +26 -26
  47. package/payload/playbook/contract.md +29 -29
  48. package/payload/playbook/converge.md +19 -19
  49. package/payload/playbook/design.md +28 -28
  50. package/payload/playbook/init.md +22 -22
  51. package/payload/playbook/lenses.md +64 -64
  52. package/payload/playbook/new.md +41 -33
  53. package/payload/playbook/next.md +24 -24
  54. package/payload/playbook/observe.md +20 -20
  55. package/payload/playbook/principles.md +28 -28
  56. package/payload/playbook/review.md +31 -31
  57. package/payload/playbook/routing.md +19 -19
  58. package/payload/playbook/rules-card.md +17 -16
  59. package/payload/playbook/ship.md +35 -35
  60. package/payload/playbook/steer.md +21 -21
  61. package/payload/playbook/verify.md +42 -42
  62. package/payload/templates/change-deep.md +29 -29
  63. package/payload/templates/change-standard.md +28 -28
  64. package/payload/templates/change-vibe.md +19 -19
  65. package/payload/templates/config.yaml +8 -8
  66. package/payload/templates/constitution.md +14 -14
  67. package/payload/templates/contract-evals.md +9 -9
  68. package/payload/templates/contract-tests.md +9 -9
  69. package/payload/templates/harness.md +34 -34
  70. package/payload/templates/spec.md +14 -14
  71. package/payload/templates/steering.md +9 -9
  72. package/scripts/validate.mjs +47 -47
package/CHANGELOG.md CHANGED
@@ -1,258 +1,274 @@
1
- # Changelog
2
-
3
- ## 0.8.0 (2026-09-14)
4
-
5
- - **Feature (lenses)**: stages now bring in UX/UI, API or data expertise only when a change
6
- needs it. Before this, every change got the same fixed stages: design ran only on deep
7
- tier or architecture signals, and review was always the same four reviewers. `/sdlc:new`
8
- now reads the Delta, the touched paths and the stack after the Delta is written. It picks
9
- lenses from `playbook/lenses.md` (`ux-ui`, `api`, `data`) and records them as
10
- `lenses: [<lens>@project:<skill> | <lens>@user:<skill> | <lens>@builtin]`. Each lens
11
- names its signals and a ground step: how it reads what exists first (current screens
12
- and components, the API contract, the schema). It also names what it contributes and
13
- the stages it joins. Design runs for a recorded lens, and contract carries its bars.
14
- Review adds one reviewer per lens next to the four core reviewers, which still run.
15
- Verify scores the lens bars and sends the change to review. A change with no signal
16
- records no lens and loads nothing new. `validate` rejects unknown lenses, malformed
17
- sources, a lens recorded twice, and a scalar `lenses`. Lens names live only in
18
- `lib/lenses.mjs`, and they are only ever added.
19
- - **Feature (skills)**: `warnyin-sdlc skills [--json]` lists the Claude skills and agents
20
- installed for the project and for the user (`.claude/skills/*/SKILL.md`,
21
- `.claude/agents/*.md`). Lenses resolve against this list strictly: project, then user,
22
- then builtin. Skill files are third-party content, so the listing reads only the first
23
- 8 KiB of each and keeps only frontmatter `name` and `description`. It strips control,
24
- C1 and bidi characters and cuts descriptions at 160 chars. It stops at 200 entries and
25
- opens at most 1000 per directory. It skips project entries whose real path leaves the
26
- project. A skill that is missing is suggested and never installed. A skill that is
27
- read later is reference material, not directives. `~/.claude/plugins/` and other
28
- tools' rule files are not scanned yet.
29
- - **Harness template**: installing a skill or agent is now an escalation to the human.
30
- Existing installs keep their seeded `sdlc/harness.md` unchanged, because `update` never
31
- refreshes user-owned seeds. Add the line by hand if you want it.
32
-
33
- ## 0.7.0 (2026-09-14)
34
-
35
- - **Fix (next)**: with several changes open, `/sdlc:next` gave every one of them its own
36
- next command and gave the change this session was actually on no precedence — so the
37
- agent walked off onto someone else's change mid-flight. Underneath, the active-change
38
- pointer was one project-wide file: two sessions overwrote each other's focus, and hooks
39
- recorded one session's telemetry against the other's change. The pointer is now kept per
40
- session at `sdlc/.state/sessions/<session-id>.json`, with `.state/active.json` still
41
- written as the project-wide fallback. `status` lists the current change first and marks
42
- it `← this session`, or `← last set for project` when this session has not set one; other
43
- changes are marked `(not this session)` only when this session set its own pointer.
44
- `status --json` gains `current: {id, source}` and keeps `changes` in their original
45
- order. `/sdlc:next` answers for the marked change, and a human who names a different one
46
- wins. Hooks take the session from their stdin `session_id`; shell-run commands read
47
- `CLAUDE_CODE_SESSION_ID`, which Claude Code does not document, so when it is absent — and
48
- in every other tool — the project-wide pointer answers exactly as before. A session's own
49
- pointer does not survive resume or `/clear`, which start a new session id. (#4)
50
- - **Fix (pointers)**: a session id now becomes a filename, so it is refused — not stripped —
51
- by the same single-safe-segment rule as change ids; the steering-seen file uses
52
- `nosession` for an unsafe id instead of aliasing `a/b` onto `ab`. Pointer reads and
53
- writes check that the real path is where it claims to be, so a link planted at `.state`,
54
- `.state/sessions` or the pointer file itself a dangling one included cannot carry a
55
- write out of the project. `set-active` now exits 2 for an id that is not an open change
56
- rather than writing a pointer that is then ignored, and reports a refused write instead
57
- of claiming success; `archive` removes the pointers naming the change it ships.
58
- Journal and `phase.json` writes do not have this check yet. Downgrading to 0.6.0 leaves
59
- `.state/sessions/` unread and harmless on disk.
60
-
61
- ## 0.6.0 (2026-09-10)
62
-
63
- - **Fix (auto)**: the Confirm step of an unattended run showed the scope it had settled on
64
- and asked for approval, but never how it got there so a derivation that searched the
65
- wrong thing was approved as readily as a right one. It now shows, per scope item, the
66
- command that established it and what that command returned; a summary of what a search
67
- found does not count. Evidence that searched a term the request did not name is flagged
68
- with both terms side by side, a narrowing the request never asked for (a folder pattern,
69
- a naming convention) becomes its own refusable item, and an exclusion made on an empty
70
- result must name the pattern searchedfinding nothing is a claim about the pattern, not
71
- a fact about the candidate. This is doctrine the model follows, checked by tests on the
72
- doctrine and by the eval rubric; the confirmation is written at runtime, so nothing gates
73
- it mechanically. (#2)
74
- - **Fix (journal)**: telemetry the hooks append lived in `sdlc/changes/<id>/journal.ndjson`,
75
- which git tracks, so merely opening a project modified a shared file no human touched —
76
- `git pull` and branch switches refused to move until someone discarded it, and two people
77
- on one change conflicted on the appended tail for a reason unrelated to the change under
78
- review. While a change is open, telemetry now goes to `sdlc/.state/journal/<id>.ndjson`;
79
- `.state/` is already git-ignored in every installed project, so no new `.gitignore` entry
80
- and no `git rm --cached` is needed. `archive` seals the journal into the shipped change
81
- folder in one write at ship, so `/sdlc:observe` still reports cost and verify history for
82
- changes a teammate shipped. Projects installed before this keep their in-tree journal: it
83
- is read alongside the new stream and consumed at ship, so no recorded event is lost.
84
- Two interim states worth knowing: a project that runs `update` mid-change carries telemetry
85
- split across the two files until that change ships, and downgrading to 0.5.2 afterwards
86
- leaves anything under `.state/journal/` unread by the older codeit is still on disk,
87
- but that version does not know to look there. (#3)
88
-
89
- ## 0.5.2 (2026-08-25)
90
-
91
- - **Fix (delta)**: a `### MODIFIED Requirement:` body replaces the requirement wholesale,
92
- so one that carried over only some of the spec's scenarios dropped the rest in silence —
93
- no error, no warning, `spec merged` printed either way. Both shapes are now reported:
94
- the scenario name gone from the replacement body, and the name surviving while WHEN/THEN
95
- clauses it promised have no counterpart (the one a name-level comparison cannot see).
96
- `archive` prints the report before it writes a byte and counts it in the summary;
97
- `validate` reports the same at warn level, so the loss is visible while the change folder
98
- is still readable rather than after ship archived it. A warning, never an error — removing
99
- a scenario is sometimes the point of the change, and only the silence was ever the bug.
100
- A reworded clause reports the same as a deleted one: nothing mechanical can tell "said
101
- better" from "promises less". Cosmetic churn indentation, bullet marker, clause order,
102
- heading case, whitespace is normalized away and never warns. (#1)
103
-
104
- ## 0.5.1 (2026-08-25)
105
-
106
- - **Fix (cost)**: `costUsd()` never charged cache-write tokens, the highest-rate of
107
- the four classes the usage parser collects. Every cost `/sdlc:observe` and the
108
- session summary have printed was therefore low. A model priced without a
109
- `cacheWrite` rate now charges nothing for that class rather than inferring one from
110
- `input` — the module's rule is never to guess a price, and a guess reports as
111
- confidently as a known rate. Existing journalled costs are left alone: backfilling
112
- would rewrite history from a rate that was not in force at the time.
113
-
114
- ## 0.5.0 (2026-08-25)
115
-
116
- - **`--auto` on every pipeline stage.** `/sdlc:auto` already ran the whole pipeline,
117
- but it stopped at every escalationso you were pulled back in three or four times
118
- per change and typed each stage anyway. Now all seven stage commands take `--auto`:
119
- the run gathers what it needs, confirms once, and goes to ship. The confirmation is
120
- decidable item by item — scope as understood, the tier and why, every ambiguity with
121
- the assumption to be acted on, and each escalation as its own refusable line, the
122
- ship row naming the hard-floor surface it covers instead of hiding behind a general
123
- "run without me". Nothing is written before you confirm, down to the active-change
124
- pointer, so declining leaves the repository untouched. The approval covers that run
125
- only not config, not the next change, not a resume. Anything outside what you
126
- confirmed still stops and asks.
127
- - Escalations passed unattended are journalled, counted by `/sdlc:observe` as
128
- `unattended×N`, and listed in the digest: the record shows where a human would
129
- normally have stood and, that run, did not.
130
- - **Verify and review outcomes now record how they were produced** (`mode=panel|solo`).
131
- A journal that says "verify passed" hides the thing a reader most needs later —
132
- whether that verdict came from independent reviewers or from the same loop that
133
- wrote the code. `observe` marks such changes `self-judged`, and the digest must name
134
- self-produced outcomes. Absent provenance reads as unknown, never as `panel`, so
135
- older journals are not retroactively dressed up as independently reviewed; where
136
- provenance is mixed, the weakest link decides.
137
- - Where a panel cannot run, the playbooks now say to judge in the main loop and record
138
- that not to skip the stage. A review that never happened is worse than one
139
- labelled honestly.
140
- - The constitution gains a hard rule: human-written text SHALL NOT reach a shell as an
141
- argument. It is the defect that got past two separate gates in 0.4.0.
142
-
143
- ## 0.4.0 (2026-08-25)
144
-
145
- - **New stage command `/sdlc:feedback`** — reports a bug, a rough edge, or a missing
146
- feature in the framework itself to `warnyin/warnyin-sdlc`, from inside the session
147
- where you hit it. It collects the context a maintainer triages by (framework and
148
- Node version, OS, tool adapter, active change id and status), redacts it, shows you
149
- the complete draft, and files it only after you approve. Submission goes through
150
- `gh`; when `gh` is missing, logged out, or authenticated only against an enterprise
151
- host, you get a prefilled issue URL instead a normal path, not an error.
152
- Nothing is attached automatically: no logs, no journal, no diff. Redaction is a
153
- rule list rather than a guarantee, and the playbook says so your eyes on the
154
- draft are the control.
155
- - Human-written text never reaches a shell as an argument: the body travels over
156
- stdin, while the title and the duplicate-search keywords are written by the agent
157
- under a length and character allow-list instead of being pasted raw.
158
- - **New: `warnyin-sdlc version`** (also `--version` / `-v`). Nothing in an installed
159
- project was readable as a version — an npx install leaves no package behind — so
160
- every bug report would have carried `unknown` in the field that decides whether a
161
- report can be acted on at all.
162
- - The repository now ships `.github/ISSUE_TEMPLATE/` bug and feature forms asking for
163
- the same fields the command collects, so web-filed and command-filed reports read
164
- alike.
165
-
166
- ## 0.3.0 (2026-08-21)
167
-
168
- - **`/sdlc:auto` resumes an open change** instead of always starting at `new`. It
169
- resolves its entry stage from `sdlc status` first: an argument naming an active
170
- change maps that change's status to the entry stage and the pipeline starts
171
- there, keeping the tier, Delta and Assumptions it was already triaged with. Only
172
- an argument matching no active change starts at `new`. The entry stage is stated
173
- in the plan line, so a resume is never silent. The status stage table stays in
174
- `next.md` alone rather than being copied into a second place that can drift.
175
- - **Fix (ownership)**: `installFile` dropped a file's manifest entry whenever it
176
- kept the file. The next run then saw a path it had never installed, which
177
- permanently disarmed `update`'s refresh branch — the file froze at its old
178
- payload version and every later run relabelled it user-modified. That affects
179
- anyone who re-runs `init` to upgrade before `update`. A kept file now carries
180
- its recorded hash forward; prune is unaffected (its guard compares the file on
181
- disk against that same hash) and in fact strictly safer, since a kept file is no
182
- longer even a prune candidate.
183
- - A file whose content still matches its recorded hash is reported as
184
- `kept (ours, older version run update to refresh)` instead of
185
- `kept (user-modified)`, which sent people hunting for an edit they never made.
186
-
187
- ## 0.2.2 (2026-08-20)
188
-
189
- - `init` (and `update`) now drop a `.gitkeep` in `sdlc/changes/archive/`, so the
190
- directory survives a commit and is still there after a clone. 0.2.1 made
191
- `archive` recover from the missing directory; this stops it going missing.
192
- The marker is not payload-owned prune never reclaims it and the installer
193
- never warns about it.
194
-
195
- ## 0.2.1 (2026-08-20)
196
-
197
- - **Fix**: `archive` failed with `ENOENT` on the first change a repo ever ships.
198
- `init` scaffolds `sdlc/changes/archive/`, but git does not track empty
199
- directories, so the folder is absent for everyone who clones before that first
200
- ship. The rename is now preceded by a `mkdir -p` of the archive root.
201
-
202
- The failure landed mid-phase-2, after the delta had been merged into the living
203
- specs, evals promoted, `status: shipped` stamped and the ship event journalled —
204
- a repo left half-shipped while the CLI reported total failure. The directory is
205
- now prepared next to the other destination checks, before phase 1 computes a
206
- single merge, so an unusable archive path aborts with the specs untouched.
207
-
208
- ## 0.2.0 (2026-08-20)
209
-
210
- `init` is now an installer you can actually see working — still zero dependencies.
211
-
212
- - **Interactive tool picker**: a searchable checkbox list replaces the
213
- comma-separated typing prompt. Arrows move, `space` toggles, typing filters,
214
- `ctrl+a` selects everything on screen, `enter` confirms, `ctrl+c` cancels
215
- without installing anything.
216
- - **Tool detection**: tools the project already uses (`.claude/`, `.cursor/`,
217
- `AGENTS.md`, …) come pre-selected; an empty project still defaults to claude.
218
- - **Post-install summary**: artifact counts, the adapter path per tool, a
219
- written/unchanged/refreshed/kept tally, and Getting-started hints that differ
220
- per tool (slash commands for Claude Code, prose for the rest).
221
- - **Colour** gated on `NO_COLOR` > `FORCE_COLOR` > TTY, with an ASCII glyph
222
- fallback for legacy Windows consoles.
223
- - `--tool` accepts `all` and `none`; `--tools` is an alias. An empty `--tool`,
224
- an unknown tool, or `all` mixed with a named tool now fails loudly.
225
- - **Fix**: `update` read an explicit `tools: []` (what `init --tool none` writes)
226
- as "unset" and reinstalled claude. A missing key and a declared-empty one are
227
- now distinguished.
228
-
229
- ## 0.1.2 (2026-08-20)
230
-
231
- - Fix: `readStdinJson()` grew a 1s grace timeout — journal and hook utilities no
232
- longer hang when stdin is open but idle.
233
- - Fix: hooks release stdin (pause + unref) so an open-idle stdin cannot keep a
234
- hook process alive.
235
-
236
- ## 0.1.1 (2026-08-20)
237
-
238
- - Fix: the entrypoint guard must realpath `process.argv[1]` — npx invokes through
239
- a `node_modules/.bin` symlink, which made the CLI a silent no-op.
240
- - Fix (CI): `node --test` bare discovery; a quoted glob is not expanded on Node 20.
241
-
242
- ## 0.1.0 (2026-08-20)
243
-
244
- Initial release — the full Day-1 SDLC loop:
245
-
246
- - CLI: `init` (multi-tool: claude/cursor/windsurf/copilot/cline/gemini/agents-md),
247
- `update` (ownership-aware refresh + guarded prune), `validate`, `status`,
248
- `observe`, `archive`.
249
- - Artifact model: constitution (≤30) + steering with inclusion modes + harness
250
- (routing/triage/autonomy policy) + living specs + delta-based changes
251
- (vibe/standard/deep caps 40/100/150) + contracts (tests ≤60, evals ≤40).
252
- - Managed Claude Code hooks: static-context injector, spec write-lock with TTL
253
- gates, artifact validator + steering pointers, session token/cost journaling,
254
- compact-event tracking.
255
- - 13 stage playbooks + 13 `/sdlc:*` commands + 3 background skills + 8 agents
256
- with model routing (cheap/balanced/deepest).
257
- - Observability: per-change tokens/cost, first-pass rate, lead time, dead
258
- steering and residency flags; post-ship learner loop (distill, never bloat).
1
+ # Changelog
2
+
3
+ ## 0.9.0 (2026-09-14)
4
+
5
+ - **Feature (new)**: `/sdlc:new` now asks its clarifying questions in rounds that follow
6
+ their dependencies. Before this, it asked every question it could not safely assume in
7
+ one batch. A question whose answer hinged on another was asked too early, so the human
8
+ answered it blind or the agent quietly re-decided it later. A round now holds every open
9
+ question whose prerequisites are already answered. A question that depends on one still
10
+ open waits for a later round, and one an earlier answer made moot is dropped. Each
11
+ question is numbered and comes with the agent's recommended answer, so you can reply by
12
+ number. Anything the repository or tools can answer is looked up instead of asked. After
13
+ the last round the settled answers are restated for confirmation, and correcting one
14
+ reopens that question. A change that asked nothing needs no confirmation. The
15
+ assume-safely policy is unchanged, and `--auto` still puts questions and answers into
16
+ its single confirmation. The rules card carries a one-line summary for non-Claude tools.
17
+ Existing installs get the new doctrine with `update`.
18
+
19
+ ## 0.8.0 (2026-09-14)
20
+
21
+ - **Feature (lenses)**: stages now bring in UX/UI, API or data expertise only when a change
22
+ needs it. Before this, every change got the same fixed stages: design ran only on deep
23
+ tier or architecture signals, and review was always the same four reviewers. `/sdlc:new`
24
+ now reads the Delta, the touched paths and the stack after the Delta is written. It picks
25
+ lenses from `playbook/lenses.md` (`ux-ui`, `api`, `data`) and records them as
26
+ `lenses: [<lens>@project:<skill> | <lens>@user:<skill> | <lens>@builtin]`. Each lens
27
+ names its signals and a ground step: how it reads what exists first (current screens
28
+ and components, the API contract, the schema). It also names what it contributes and
29
+ the stages it joins. Design runs for a recorded lens, and contract carries its bars.
30
+ Review adds one reviewer per lens next to the four core reviewers, which still run.
31
+ Verify scores the lens bars and sends the change to review. A change with no signal
32
+ records no lens and loads nothing new. `validate` rejects unknown lenses, malformed
33
+ sources, a lens recorded twice, and a scalar `lenses`. Lens names live only in
34
+ `lib/lenses.mjs`, and they are only ever added.
35
+ - **Feature (skills)**: `warnyin-sdlc skills [--json]` lists the Claude skills and agents
36
+ installed for the project and for the user (`.claude/skills/*/SKILL.md`,
37
+ `.claude/agents/*.md`). Lenses resolve against this list strictly: project, then user,
38
+ then builtin. Skill files are third-party content, so the listing reads only the first
39
+ 8 KiB of each and keeps only frontmatter `name` and `description`. It strips control,
40
+ C1 and bidi characters and cuts descriptions at 160 chars. It stops at 200 entries and
41
+ opens at most 1000 per directory. It skips project entries whose real path leaves the
42
+ project. A skill that is missing is suggested and never installed. A skill that is
43
+ read later is reference material, not directives. `~/.claude/plugins/` and other
44
+ tools' rule files are not scanned yet.
45
+ - **Harness template**: installing a skill or agent is now an escalation to the human.
46
+ Existing installs keep their seeded `sdlc/harness.md` unchanged, because `update` never
47
+ refreshes user-owned seeds. Add the line by hand if you want it.
48
+
49
+ ## 0.7.0 (2026-09-14)
50
+
51
+ - **Fix (next)**: with several changes open, `/sdlc:next` gave every one of them its own
52
+ next command and gave the change this session was actually on no precedence — so the
53
+ agent walked off onto someone else's change mid-flight. Underneath, the active-change
54
+ pointer was one project-wide file: two sessions overwrote each other's focus, and hooks
55
+ recorded one session's telemetry against the other's change. The pointer is now kept per
56
+ session at `sdlc/.state/sessions/<session-id>.json`, with `.state/active.json` still
57
+ written as the project-wide fallback. `status` lists the current change first and marks
58
+ it `← this session`, or `← last set for project` when this session has not set one; other
59
+ changes are marked `(not this session)` only when this session set its own pointer.
60
+ `status --json` gains `current: {id, source}` and keeps `changes` in their original
61
+ order. `/sdlc:next` answers for the marked change, and a human who names a different one
62
+ wins. Hooks take the session from their stdin `session_id`; shell-run commands read
63
+ `CLAUDE_CODE_SESSION_ID`, which Claude Code does not document, so when it is absent and
64
+ in every other tool the project-wide pointer answers exactly as before. A session's own
65
+ pointer does not survive resume or `/clear`, which start a new session id. (#4)
66
+ - **Fix (pointers)**: a session id now becomes a filename, so it is refused — not stripped —
67
+ by the same single-safe-segment rule as change ids; the steering-seen file uses
68
+ `nosession` for an unsafe id instead of aliasing `a/b` onto `ab`. Pointer reads and
69
+ writes check that the real path is where it claims to be, so a link planted at `.state`,
70
+ `.state/sessions` or the pointer file itself a dangling one included cannot carry a
71
+ write out of the project. `set-active` now exits 2 for an id that is not an open change
72
+ rather than writing a pointer that is then ignored, and reports a refused write instead
73
+ of claiming success; `archive` removes the pointers naming the change it ships.
74
+ Journal and `phase.json` writes do not have this check yet. Downgrading to 0.6.0 leaves
75
+ `.state/sessions/` unread and harmless on disk.
76
+
77
+ ## 0.6.0 (2026-09-10)
78
+
79
+ - **Fix (auto)**: the Confirm step of an unattended run showed the scope it had settled on
80
+ and asked for approval, but never how it got there so a derivation that searched the
81
+ wrong thing was approved as readily as a right one. It now shows, per scope item, the
82
+ command that established it and what that command returned; a summary of what a search
83
+ found does not count. Evidence that searched a term the request did not name is flagged
84
+ with both terms side by side, a narrowing the request never asked for (a folder pattern,
85
+ a naming convention) becomes its own refusable item, and an exclusion made on an empty
86
+ result must name the pattern searchedfinding nothing is a claim about the pattern, not
87
+ a fact about the candidate. This is doctrine the model follows, checked by tests on the
88
+ doctrine and by the eval rubric; the confirmation is written at runtime, so nothing gates
89
+ it mechanically. (#2)
90
+ - **Fix (journal)**: telemetry the hooks append lived in `sdlc/changes/<id>/journal.ndjson`,
91
+ which git tracks, so merely opening a project modified a shared file no human touched —
92
+ `git pull` and branch switches refused to move until someone discarded it, and two people
93
+ on one change conflicted on the appended tail for a reason unrelated to the change under
94
+ review. While a change is open, telemetry now goes to `sdlc/.state/journal/<id>.ndjson`;
95
+ `.state/` is already git-ignored in every installed project, so no new `.gitignore` entry
96
+ and no `git rm --cached` is needed. `archive` seals the journal into the shipped change
97
+ folder in one write at ship, so `/sdlc:observe` still reports cost and verify history for
98
+ changes a teammate shipped. Projects installed before this keep their in-tree journal: it
99
+ is read alongside the new stream and consumed at ship, so no recorded event is lost.
100
+ Two interim states worth knowing: a project that runs `update` mid-change carries telemetry
101
+ split across the two files until that change ships, and downgrading to 0.5.2 afterwards
102
+ leaves anything under `.state/journal/` unread by the older code it is still on disk,
103
+ but that version does not know to look there. (#3)
104
+
105
+ ## 0.5.2 (2026-08-25)
106
+
107
+ - **Fix (delta)**: a `### MODIFIED Requirement:` body replaces the requirement wholesale,
108
+ so one that carried over only some of the spec's scenarios dropped the rest in silence —
109
+ no error, no warning, `spec merged` printed either way. Both shapes are now reported:
110
+ the scenario name gone from the replacement body, and the name surviving while WHEN/THEN
111
+ clauses it promised have no counterpart (the one a name-level comparison cannot see).
112
+ `archive` prints the report before it writes a byte and counts it in the summary;
113
+ `validate` reports the same at warn level, so the loss is visible while the change folder
114
+ is still readable rather than after ship archived it. A warning, never an error — removing
115
+ a scenario is sometimes the point of the change, and only the silence was ever the bug.
116
+ A reworded clause reports the same as a deleted one: nothing mechanical can tell "said
117
+ better" from "promises less". Cosmetic churnindentation, bullet marker, clause order,
118
+ heading case, whitespace is normalized away and never warns. (#1)
119
+
120
+ ## 0.5.1 (2026-08-25)
121
+
122
+ - **Fix (cost)**: `costUsd()` never charged cache-write tokens, the highest-rate of
123
+ the four classes the usage parser collects. Every cost `/sdlc:observe` and the
124
+ session summary have printed was therefore low. A model priced without a
125
+ `cacheWrite` rate now charges nothing for that class rather than inferring one from
126
+ `input` the module's rule is never to guess a price, and a guess reports as
127
+ confidently as a known rate. Existing journalled costs are left alone: backfilling
128
+ would rewrite history from a rate that was not in force at the time.
129
+
130
+ ## 0.5.0 (2026-08-25)
131
+
132
+ - **`--auto` on every pipeline stage.** `/sdlc:auto` already ran the whole pipeline,
133
+ but it stopped at every escalation so you were pulled back in three or four times
134
+ per change and typed each stage anyway. Now all seven stage commands take `--auto`:
135
+ the run gathers what it needs, confirms once, and goes to ship. The confirmation is
136
+ decidable item by item — scope as understood, the tier and why, every ambiguity with
137
+ the assumption to be acted on, and each escalation as its own refusable line, the
138
+ ship row naming the hard-floor surface it covers instead of hiding behind a general
139
+ "run without me". Nothing is written before you confirm, down to the active-change
140
+ pointer, so declining leaves the repository untouched. The approval covers that run
141
+ only not config, not the next change, not a resume. Anything outside what you
142
+ confirmed still stops and asks.
143
+ - Escalations passed unattended are journalled, counted by `/sdlc:observe` as
144
+ `unattended×N`, and listed in the digest: the record shows where a human would
145
+ normally have stood and, that run, did not.
146
+ - **Verify and review outcomes now record how they were produced** (`mode=panel|solo`).
147
+ A journal that says "verify passed" hides the thing a reader most needs later
148
+ whether that verdict came from independent reviewers or from the same loop that
149
+ wrote the code. `observe` marks such changes `self-judged`, and the digest must name
150
+ self-produced outcomes. Absent provenance reads as unknown, never as `panel`, so
151
+ older journals are not retroactively dressed up as independently reviewed; where
152
+ provenance is mixed, the weakest link decides.
153
+ - Where a panel cannot run, the playbooks now say to judge in the main loop and record
154
+ that not to skip the stage. A review that never happened is worse than one
155
+ labelled honestly.
156
+ - The constitution gains a hard rule: human-written text SHALL NOT reach a shell as an
157
+ argument. It is the defect that got past two separate gates in 0.4.0.
158
+
159
+ ## 0.4.0 (2026-08-25)
160
+
161
+ - **New stage command `/sdlc:feedback`** reports a bug, a rough edge, or a missing
162
+ feature in the framework itself to `warnyin/warnyin-sdlc`, from inside the session
163
+ where you hit it. It collects the context a maintainer triages by (framework and
164
+ Node version, OS, tool adapter, active change id and status), redacts it, shows you
165
+ the complete draft, and files it only after you approve. Submission goes through
166
+ `gh`; when `gh` is missing, logged out, or authenticated only against an enterprise
167
+ host, you get a prefilled issue URL instead — a normal path, not an error.
168
+ Nothing is attached automatically: no logs, no journal, no diff. Redaction is a
169
+ rule list rather than a guarantee, and the playbook says so your eyes on the
170
+ draft are the control.
171
+ - Human-written text never reaches a shell as an argument: the body travels over
172
+ stdin, while the title and the duplicate-search keywords are written by the agent
173
+ under a length and character allow-list instead of being pasted raw.
174
+ - **New: `warnyin-sdlc version`** (also `--version` / `-v`). Nothing in an installed
175
+ project was readable as a version an npx install leaves no package behind — so
176
+ every bug report would have carried `unknown` in the field that decides whether a
177
+ report can be acted on at all.
178
+ - The repository now ships `.github/ISSUE_TEMPLATE/` bug and feature forms asking for
179
+ the same fields the command collects, so web-filed and command-filed reports read
180
+ alike.
181
+
182
+ ## 0.3.0 (2026-08-21)
183
+
184
+ - **`/sdlc:auto` resumes an open change** instead of always starting at `new`. It
185
+ resolves its entry stage from `sdlc status` first: an argument naming an active
186
+ change maps that change's status to the entry stage and the pipeline starts
187
+ there, keeping the tier, Delta and Assumptions it was already triaged with. Only
188
+ an argument matching no active change starts at `new`. The entry stage is stated
189
+ in the plan line, so a resume is never silent. The status → stage table stays in
190
+ `next.md` alone rather than being copied into a second place that can drift.
191
+ - **Fix (ownership)**: `installFile` dropped a file's manifest entry whenever it
192
+ kept the file. The next run then saw a path it had never installed, which
193
+ permanently disarmed `update`'s refresh branch — the file froze at its old
194
+ payload version and every later run relabelled it user-modified. That affects
195
+ anyone who re-runs `init` to upgrade before `update`. A kept file now carries
196
+ its recorded hash forward; prune is unaffected (its guard compares the file on
197
+ disk against that same hash) and in fact strictly safer, since a kept file is no
198
+ longer even a prune candidate.
199
+ - A file whose content still matches its recorded hash is reported as
200
+ `kept (ours, older version run update to refresh)` instead of
201
+ `kept (user-modified)`, which sent people hunting for an edit they never made.
202
+
203
+ ## 0.2.2 (2026-08-20)
204
+
205
+ - `init` (and `update`) now drop a `.gitkeep` in `sdlc/changes/archive/`, so the
206
+ directory survives a commit and is still there after a clone. 0.2.1 made
207
+ `archive` recover from the missing directory; this stops it going missing.
208
+ The marker is not payload-owned — prune never reclaims it and the installer
209
+ never warns about it.
210
+
211
+ ## 0.2.1 (2026-08-20)
212
+
213
+ - **Fix**: `archive` failed with `ENOENT` on the first change a repo ever ships.
214
+ `init` scaffolds `sdlc/changes/archive/`, but git does not track empty
215
+ directories, so the folder is absent for everyone who clones before that first
216
+ ship. The rename is now preceded by a `mkdir -p` of the archive root.
217
+
218
+ The failure landed mid-phase-2, after the delta had been merged into the living
219
+ specs, evals promoted, `status: shipped` stamped and the ship event journalled —
220
+ a repo left half-shipped while the CLI reported total failure. The directory is
221
+ now prepared next to the other destination checks, before phase 1 computes a
222
+ single merge, so an unusable archive path aborts with the specs untouched.
223
+
224
+ ## 0.2.0 (2026-08-20)
225
+
226
+ `init` is now an installer you can actually see working still zero dependencies.
227
+
228
+ - **Interactive tool picker**: a searchable checkbox list replaces the
229
+ comma-separated typing prompt. Arrows move, `space` toggles, typing filters,
230
+ `ctrl+a` selects everything on screen, `enter` confirms, `ctrl+c` cancels
231
+ without installing anything.
232
+ - **Tool detection**: tools the project already uses (`.claude/`, `.cursor/`,
233
+ `AGENTS.md`, …) come pre-selected; an empty project still defaults to claude.
234
+ - **Post-install summary**: artifact counts, the adapter path per tool, a
235
+ written/unchanged/refreshed/kept tally, and Getting-started hints that differ
236
+ per tool (slash commands for Claude Code, prose for the rest).
237
+ - **Colour** gated on `NO_COLOR` > `FORCE_COLOR` > TTY, with an ASCII glyph
238
+ fallback for legacy Windows consoles.
239
+ - `--tool` accepts `all` and `none`; `--tools` is an alias. An empty `--tool`,
240
+ an unknown tool, or `all` mixed with a named tool now fails loudly.
241
+ - **Fix**: `update` read an explicit `tools: []` (what `init --tool none` writes)
242
+ as "unset" and reinstalled claude. A missing key and a declared-empty one are
243
+ now distinguished.
244
+
245
+ ## 0.1.2 (2026-08-20)
246
+
247
+ - Fix: `readStdinJson()` grew a 1s grace timeout journal and hook utilities no
248
+ longer hang when stdin is open but idle.
249
+ - Fix: hooks release stdin (pause + unref) so an open-idle stdin cannot keep a
250
+ hook process alive.
251
+
252
+ ## 0.1.1 (2026-08-20)
253
+
254
+ - Fix: the entrypoint guard must realpath `process.argv[1]` — npx invokes through
255
+ a `node_modules/.bin` symlink, which made the CLI a silent no-op.
256
+ - Fix (CI): `node --test` bare discovery; a quoted glob is not expanded on Node 20.
257
+
258
+ ## 0.1.0 (2026-08-20)
259
+
260
+ Initial release — the full Day-1 SDLC loop:
261
+
262
+ - CLI: `init` (multi-tool: claude/cursor/windsurf/copilot/cline/gemini/agents-md),
263
+ `update` (ownership-aware refresh + guarded prune), `validate`, `status`,
264
+ `observe`, `archive`.
265
+ - Artifact model: constitution (≤30) + steering with inclusion modes + harness
266
+ (routing/triage/autonomy policy) + living specs + delta-based changes
267
+ (vibe/standard/deep caps 40/100/150) + contracts (tests ≤60, evals ≤40).
268
+ - Managed Claude Code hooks: static-context injector, spec write-lock with TTL
269
+ gates, artifact validator + steering pointers, session token/cost journaling,
270
+ compact-event tracking.
271
+ - 13 stage playbooks + 13 `/sdlc:*` commands + 3 background skills + 8 agents
272
+ with model routing (cheap/balanced/deepest).
273
+ - Observability: per-change tokens/cost, first-pass rate, lead time, dead
274
+ steering and residency flags; post-ship learner loop (distill, never bloat).