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