@warnyin/sdlc 0.2.1 → 0.3.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.
- package/CHANGELOG.md +29 -0
- package/README.md +1 -0
- package/bin/cli.mjs +20 -1
- package/package.json +1 -1
- package/payload/adapters/claude/commands/sdlc/auto.md +5 -5
- package/payload/playbook/README.md +1 -1
- package/payload/playbook/auto.md +12 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.3.0 (2026-08-21)
|
|
4
|
+
|
|
5
|
+
- **`/sdlc:auto` resumes an open change** instead of always starting at `new`. It
|
|
6
|
+
resolves its entry stage from `sdlc status` first: an argument naming an active
|
|
7
|
+
change maps that change's status to the entry stage and the pipeline starts
|
|
8
|
+
there, keeping the tier, Delta and Assumptions it was already triaged with. Only
|
|
9
|
+
an argument matching no active change starts at `new`. The entry stage is stated
|
|
10
|
+
in the plan line, so a resume is never silent. The status → stage table stays in
|
|
11
|
+
`next.md` alone rather than being copied into a second place that can drift.
|
|
12
|
+
- **Fix (ownership)**: `installFile` dropped a file's manifest entry whenever it
|
|
13
|
+
kept the file. The next run then saw a path it had never installed, which
|
|
14
|
+
permanently disarmed `update`'s refresh branch — the file froze at its old
|
|
15
|
+
payload version and every later run relabelled it user-modified. That affects
|
|
16
|
+
anyone who re-runs `init` to upgrade before `update`. A kept file now carries
|
|
17
|
+
its recorded hash forward; prune is unaffected (its guard compares the file on
|
|
18
|
+
disk against that same hash) and in fact strictly safer, since a kept file is no
|
|
19
|
+
longer even a prune candidate.
|
|
20
|
+
- A file whose content still matches its recorded hash is reported as
|
|
21
|
+
`kept (ours, older version — run update to refresh)` instead of
|
|
22
|
+
`kept (user-modified)`, which sent people hunting for an edit they never made.
|
|
23
|
+
|
|
24
|
+
## 0.2.2 (2026-08-20)
|
|
25
|
+
|
|
26
|
+
- `init` (and `update`) now drop a `.gitkeep` in `sdlc/changes/archive/`, so the
|
|
27
|
+
directory survives a commit and is still there after a clone. 0.2.1 made
|
|
28
|
+
`archive` recover from the missing directory; this stops it going missing.
|
|
29
|
+
The marker is not payload-owned — prune never reclaims it and the installer
|
|
30
|
+
never warns about it.
|
|
31
|
+
|
|
3
32
|
## 0.2.1 (2026-08-20)
|
|
4
33
|
|
|
5
34
|
- **Fix**: `archive` failed with `ENOENT` on the first change a repo ever ships.
|
package/README.md
CHANGED
|
@@ -30,6 +30,7 @@ Then in your coding agent:
|
|
|
30
30
|
```
|
|
31
31
|
/sdlc:init # interview → constitution + harness (the one human gate)
|
|
32
32
|
/sdlc:auto Add rate limiting # AI runs new → contract → build → verify → ship
|
|
33
|
+
/sdlc:auto add-rate-limiting # already opened it with /sdlc:new? auto resumes from there
|
|
33
34
|
```
|
|
34
35
|
|
|
35
36
|
## How it works
|
package/bin/cli.mjs
CHANGED
|
@@ -101,7 +101,20 @@ export function installFile(projectRoot, destRel, content, ctx) {
|
|
|
101
101
|
ctx.manifest.set(relPosix, nextHash);
|
|
102
102
|
return tally(ctx, 'updated');
|
|
103
103
|
}
|
|
104
|
-
|
|
104
|
+
// Keeping the content must not forget the ownership. Dropping the entry made
|
|
105
|
+
// the next run see a file we had never installed, which permanently disarmed
|
|
106
|
+
// update's refresh branch (disk hash === old manifest hash) — the file froze
|
|
107
|
+
// at its old payload version and every later run relabelled it user-modified.
|
|
108
|
+
// The recorded hash stays the one we last wrote, so prune's "disk must match
|
|
109
|
+
// the manifest" guard still refuses to touch a file the user really did edit.
|
|
110
|
+
const owned = ctx.oldManifest?.get(relPosix);
|
|
111
|
+
if (owned) ctx.manifest.set(relPosix, owned);
|
|
112
|
+
// Matching the recorded hash proves nobody touched it — `install` simply does
|
|
113
|
+
// not refresh. Calling that "user-modified" sent people hunting for an edit
|
|
114
|
+
// they never made.
|
|
115
|
+
ctx.warnings.push(owned === currentHash
|
|
116
|
+
? `kept (ours, older version — run \`npx @warnyin/sdlc update\` to refresh): ${relPosix}`
|
|
117
|
+
: `kept (user-modified): ${relPosix}`);
|
|
105
118
|
return tally(ctx, 'kept');
|
|
106
119
|
}
|
|
107
120
|
|
|
@@ -189,6 +202,12 @@ function scaffoldSdlc(projectRoot, tools, ctx) {
|
|
|
189
202
|
fs.mkdirSync(path.join(sdlcRoot, dir), { recursive: true });
|
|
190
203
|
}
|
|
191
204
|
|
|
205
|
+
// git does not track empty directories, so changes/archive/ vanishes for
|
|
206
|
+
// anyone who clones before the first change ships. Not manifested: an empty
|
|
207
|
+
// marker is nothing for prune to reclaim or for the installer to warn about.
|
|
208
|
+
const gitkeep = path.join(sdlcRoot, 'changes', 'archive', '.gitkeep');
|
|
209
|
+
if (!fs.existsSync(gitkeep)) fs.writeFileSync(gitkeep, '');
|
|
210
|
+
|
|
192
211
|
// Seeds are user-owned from birth: created once, never manifested/overwritten.
|
|
193
212
|
const seed = (rel, templateName, transform = (s) => s) => {
|
|
194
213
|
const dest = path.join(sdlcRoot, rel);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@warnyin/sdlc",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Spec-driven, AI-driven SDLC framework — token-lean specs, contract-first changes, autonomous pipeline with managed hooks. Operationalizes the Day-1 'New SDLC with Vibe Coding' work process.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Run the whole pipeline autonomously
|
|
3
|
-
argument-hint: "<title>"
|
|
4
|
-
---
|
|
5
|
-
Read `sdlc/.playbook/auto.md` and execute it now. Arguments: $ARGUMENTS
|
|
1
|
+
---
|
|
2
|
+
description: Run the whole pipeline autonomously — resumes an open change or opens a new one, then contract → build → verify → ship (pauses only on policy escalations)
|
|
3
|
+
argument-hint: "<title|change-id>"
|
|
4
|
+
---
|
|
5
|
+
Read `sdlc/.playbook/auto.md` and execute it now. Arguments: $ARGUMENTS
|
|
@@ -9,7 +9,7 @@ new → [design] → contract → build → verify → [review] → ship
|
|
|
9
9
|
| Command | Day-1 phase | Reads | Writes | Gate (automatic unless noted) |
|
|
10
10
|
|---|---|---|---|---|
|
|
11
11
|
| /sdlc:init | Configure harness | interview | constitution, harness.md | human approves (once) |
|
|
12
|
-
| /sdlc:auto | whole loop |
|
|
12
|
+
| /sdlc:auto | whole loop | status (resumes an open change) | everything below | escalation only |
|
|
13
13
|
| /sdlc:new | Requirements | specs Purpose headers | change.md | validator: delta + assumptions |
|
|
14
14
|
| /sdlc:design | Architecture | change + touched specs | change.md § Design | escalate irreversible only |
|
|
15
15
|
| /sdlc:contract | Contract-first | change.md | contract/*, failing tests | adversarial panel + validator |
|
package/payload/playbook/auto.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# /sdlc:auto <title> — the whole pipeline, one command
|
|
1
|
+
# /sdlc:auto <title|change-id> — the whole pipeline, one command
|
|
2
2
|
|
|
3
3
|
Runs new → [design] → contract → build → verify → [review] → ship, each stage by
|
|
4
4
|
its own playbook, WITHOUT pausing for the human except on the Autonomy-policy
|
|
@@ -10,8 +10,18 @@ escalation conditions:
|
|
|
10
10
|
- token budget exceeded (if the user set one),
|
|
11
11
|
- ship policy requires human approval (deep tier).
|
|
12
12
|
|
|
13
|
+
Entry stage — resolve this first, never assume `new`:
|
|
14
|
+
- Run `npx @warnyin/sdlc status`. If the argument names an active change (or one
|
|
15
|
+
is active and the argument describes it), RESUME: map its status to the entry
|
|
16
|
+
stage with `next.md` §2 and start the pipeline there.
|
|
17
|
+
- Resume never rewrites an existing `change.md` — a change already triaged keeps
|
|
18
|
+
its tier, Delta and Assumptions. Re-run a stage only if its output is missing
|
|
19
|
+
or the validator rejects it.
|
|
20
|
+
- Start at `new` only when the argument matches no active change.
|
|
21
|
+
|
|
13
22
|
Rules:
|
|
14
|
-
- Announce the plan in ≤3 lines after triage (id, tier, task count
|
|
23
|
+
- Announce the plan in ≤3 lines after triage (id, tier, task count, entry stage),
|
|
24
|
+
so a resume is never silent. Then work.
|
|
15
25
|
- Between stages run `npx @warnyin/sdlc validate <id>` — a red validator is a
|
|
16
26
|
hard stop for that stage, not a suggestion.
|
|
17
27
|
- On escalation: stop at the exact step, state what is needed in ≤5 lines, wait.
|