@warnyin/sdlc 0.2.0 → 0.2.2

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 CHANGED
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.2 (2026-08-20)
4
+
5
+ - `init` (and `update`) now drop a `.gitkeep` in `sdlc/changes/archive/`, so the
6
+ directory survives a commit and is still there after a clone. 0.2.1 made
7
+ `archive` recover from the missing directory; this stops it going missing.
8
+ The marker is not payload-owned — prune never reclaims it and the installer
9
+ never warns about it.
10
+
11
+ ## 0.2.1 (2026-08-20)
12
+
13
+ - **Fix**: `archive` failed with `ENOENT` on the first change a repo ever ships.
14
+ `init` scaffolds `sdlc/changes/archive/`, but git does not track empty
15
+ directories, so the folder is absent for everyone who clones before that first
16
+ ship. The rename is now preceded by a `mkdir -p` of the archive root.
17
+
18
+ The failure landed mid-phase-2, after the delta had been merged into the living
19
+ specs, evals promoted, `status: shipped` stamped and the ship event journalled —
20
+ a repo left half-shipped while the CLI reported total failure. The directory is
21
+ now prepared next to the other destination checks, before phase 1 computes a
22
+ single merge, so an unusable archive path aborts with the specs untouched.
23
+
3
24
  ## 0.2.0 (2026-08-20)
4
25
 
5
26
  `init` is now an installer you can actually see working — still zero dependencies.
package/bin/cli.mjs CHANGED
@@ -189,6 +189,12 @@ function scaffoldSdlc(projectRoot, tools, ctx) {
189
189
  fs.mkdirSync(path.join(sdlcRoot, dir), { recursive: true });
190
190
  }
191
191
 
192
+ // git does not track empty directories, so changes/archive/ vanishes for
193
+ // anyone who clones before the first change ships. Not manifested: an empty
194
+ // marker is nothing for prune to reclaim or for the installer to warn about.
195
+ const gitkeep = path.join(sdlcRoot, 'changes', 'archive', '.gitkeep');
196
+ if (!fs.existsSync(gitkeep)) fs.writeFileSync(gitkeep, '');
197
+
192
198
  // Seeds are user-owned from birth: created once, never manifested/overwritten.
193
199
  const seed = (rel, templateName, transform = (s) => s) => {
194
200
  const dest = path.join(sdlcRoot, rel);
@@ -437,6 +443,11 @@ export function cmdArchive(projectRoot, changeId, { strict = true } = {}) {
437
443
  // while reporting failure.
438
444
  const date = new Date().toISOString().slice(0, 10);
439
445
  const destDir = path.join(sdlcRoot, 'changes', 'archive', `${date}-${changeId}`);
446
+ // `init` scaffolds changes/archive/, but git does not track empty directories:
447
+ // it is absent for anyone who cloned before the first change shipped. Prepare
448
+ // it here, with the other destination checks, so a bad archive path fails
449
+ // while the specs are still untouched instead of ENOENT-ing at the rename.
450
+ fs.mkdirSync(path.dirname(destDir), { recursive: true });
440
451
  if (fs.existsSync(destDir)) {
441
452
  throw new Error(`archive target already exists: ${toPosix(path.relative(projectRoot, destDir))} — nothing was merged`);
442
453
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@warnyin/sdlc",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
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": {