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