contract-driven-delivery 2.0.15 → 2.0.16

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
+ ## [2.0.16] - 2026-05-06
4
+
5
+ New-change scaffold hardening so freshly opened proposals use the installed
6
+ kit version even when an existing project has stale templates on disk.
7
+
8
+ ### Changed
9
+
10
+ - **`cdd-kit new` stamps `tasks.yml` with the real change id**: new changes no
11
+ longer start with the `<change-id>` placeholder in the machine-validated task
12
+ metadata.
13
+
14
+ ### Fixed
15
+
16
+ - **Fresh proposals ignore stale project templates**: regression coverage now
17
+ locks `cdd-kit new` to bundled package templates, so old
18
+ `specs/templates/*` files in a user repo cannot leak into a newly created
19
+ change.
20
+ - **Postinstall sync coverage includes workflow skills**: regression coverage
21
+ now verifies npm postinstall updates standalone skills such as `/cdd-new`,
22
+ keeping agent-log instructions aligned with the installed gate.
23
+
3
24
  ## [2.0.15] - 2026-05-06
4
25
 
5
26
  Prompt guidance patch for agent-log evidence and closeout learning ownership.
package/dist/cli/index.js CHANGED
@@ -8678,7 +8678,7 @@ __export(migrate_exports, {
8678
8678
  });
8679
8679
  import { join as join18 } from "path";
8680
8680
  import { cpSync as cpSync2, existsSync as existsSync15, mkdirSync as mkdirSync7, readdirSync as readdirSync8, readFileSync as readFileSync18, renameSync, rmSync as rmSync2, writeFileSync as writeFileSync8 } from "fs";
8681
- import yaml3 from "js-yaml";
8681
+ import yaml2 from "js-yaml";
8682
8682
  function backupChangeDir(cwd, changeId, sessionStamp) {
8683
8683
  const backupRoot = join18(cwd, ".cdd", "migrate-backup", sessionStamp);
8684
8684
  const backupDir2 = join18(backupRoot, changeId);
@@ -8854,7 +8854,7 @@ function migrateTasksFile(changeId, changeDir, enableContextGovernance, detected
8854
8854
  out["section"] = r.section;
8855
8855
  return out;
8856
8856
  });
8857
- const yamlOut = yaml3.dump(data, { lineWidth: -1, noRefs: true });
8857
+ const yamlOut = yaml2.dump(data, { lineWidth: -1, noRefs: true });
8858
8858
  pendingWrites.push({ path: newPath, content: yamlOut });
8859
8859
  pendingDeletes.push({ path: legacyPath });
8860
8860
  changed.push(`tasks.md -> tasks.yml (${tasksRows.length} task(s) migrated)`);
@@ -8922,7 +8922,7 @@ function migrateAgentLogs(changeDir, changed, pendingWrites, pendingDeletes) {
8922
8922
  continue;
8923
8923
  const raw = readFileSync18(fullPath, "utf8");
8924
8924
  const parsed = parseLegacyAgentLog(raw);
8925
- const yamlOut = yaml3.dump(parsed, { lineWidth: -1, noRefs: true });
8925
+ const yamlOut = yaml2.dump(parsed, { lineWidth: -1, noRefs: true });
8926
8926
  pendingWrites.push({ path: yamlFull, content: yamlOut });
8927
8927
  pendingDeletes.push({ path: fullPath });
8928
8928
  changed.push(`agent-log/${f} -> agent-log/${yamlName}`);
@@ -10146,7 +10146,7 @@ __export(archive_exports, {
10146
10146
  });
10147
10147
  import { join as join23 } from "path";
10148
10148
  import { existsSync as existsSync19, mkdirSync as mkdirSync10, renameSync as renameSync2, readFileSync as readFileSync23, writeFileSync as writeFileSync11, appendFileSync, cpSync as cpSync3, rmSync as rmSync3 } from "fs";
10149
- import yaml4 from "js-yaml";
10149
+ import yaml3 from "js-yaml";
10150
10150
  async function archive(changeId) {
10151
10151
  const cwd = process.cwd();
10152
10152
  const changeDir = join23(cwd, "specs", "changes", changeId);
@@ -10166,7 +10166,7 @@ async function archive(changeId) {
10166
10166
  if (existsSync19(tasksPath)) {
10167
10167
  try {
10168
10168
  const raw = readFileSync23(tasksPath, "utf8");
10169
- const data = yaml4.load(raw);
10169
+ const data = yaml3.load(raw);
10170
10170
  if (data?.status === "gate-blocked") {
10171
10171
  log.warn("tasks.yml has status: gate-blocked \u2014 archiving anyway (change was paused).");
10172
10172
  }
@@ -10222,7 +10222,7 @@ __export(abandon_exports, {
10222
10222
  });
10223
10223
  import { join as join24 } from "path";
10224
10224
  import { existsSync as existsSync20, readFileSync as readFileSync24, writeFileSync as writeFileSync12, appendFileSync as appendFileSync2, mkdirSync as mkdirSync11 } from "fs";
10225
- import yaml5 from "js-yaml";
10225
+ import yaml4 from "js-yaml";
10226
10226
  async function abandon(changeId, opts) {
10227
10227
  const cwd = process.cwd();
10228
10228
  const changeDir = join24(cwd, "specs", "changes", changeId);
@@ -10233,12 +10233,12 @@ async function abandon(changeId, opts) {
10233
10233
  }
10234
10234
  if (existsSync20(tasksPath)) {
10235
10235
  const raw = readFileSync24(tasksPath, "utf8");
10236
- const data = yaml5.load(raw) ?? {};
10236
+ const data = yaml4.load(raw) ?? {};
10237
10237
  data["status"] = "abandoned";
10238
10238
  if (!data["change-id"]) {
10239
10239
  data["change-id"] = changeId;
10240
10240
  }
10241
- writeFileSync12(tasksPath, yaml5.dump(data, { lineWidth: -1, noRefs: true }), "utf8");
10241
+ writeFileSync12(tasksPath, yaml4.dump(data, { lineWidth: -1, noRefs: true }), "utf8");
10242
10242
  }
10243
10243
  const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0];
10244
10244
  const archiveDir = join24(cwd, "specs", "archive");
@@ -10276,7 +10276,7 @@ __export(list_changes_exports, {
10276
10276
  });
10277
10277
  import { join as join25 } from "path";
10278
10278
  import { existsSync as existsSync21, readdirSync as readdirSync13, readFileSync as readFileSync25 } from "fs";
10279
- import yaml6 from "js-yaml";
10279
+ import yaml5 from "js-yaml";
10280
10280
  async function listChanges() {
10281
10281
  const cwd = process.cwd();
10282
10282
  const changesDir = join25(cwd, "specs", "changes");
@@ -10296,7 +10296,7 @@ async function listChanges() {
10296
10296
  if (existsSync21(tasksPath)) {
10297
10297
  try {
10298
10298
  const raw = readFileSync25(tasksPath, "utf8");
10299
- const data = yaml6.load(raw);
10299
+ const data = yaml5.load(raw);
10300
10300
  if (data?.status)
10301
10301
  status = data.status;
10302
10302
  pending = (data?.tasks ?? []).filter((t) => t.status === "pending").length;
@@ -11104,7 +11104,6 @@ init_paths();
11104
11104
  import { join as join9, relative as relative3 } from "path";
11105
11105
  import { createHash as createHash4 } from "crypto";
11106
11106
  import { existsSync as existsSync8, readFileSync as readFileSync8, readdirSync as readdirSync5, writeFileSync as writeFileSync4 } from "fs";
11107
- import yaml from "js-yaml";
11108
11107
  init_logger();
11109
11108
  init_context_scan();
11110
11109
  init_digest();
@@ -11173,6 +11172,20 @@ function parseDependsOn(raw) {
11173
11172
  return [];
11174
11173
  return raw.split(",").map((item) => item.trim()).filter(Boolean);
11175
11174
  }
11175
+ function applyScaffoldMetadata(tasksPath, changeId, dependencies) {
11176
+ if (!existsSync8(tasksPath))
11177
+ return;
11178
+ let raw = readFileSync8(tasksPath, "utf8");
11179
+ raw = raw.replace(/^change-id:\s*<change-id>\s*$/m, `change-id: ${changeId}`);
11180
+ if (dependencies.length > 0) {
11181
+ const dependsOn = [
11182
+ "depends-on:",
11183
+ ...dependencies.map((dep) => ` - ${JSON.stringify(dep)}`)
11184
+ ].join("\n");
11185
+ raw = raw.replace(/^depends-on:\s*\[\]\s*$/m, dependsOn);
11186
+ }
11187
+ writeFileSync4(tasksPath, raw, "utf8");
11188
+ }
11176
11189
  async function newChange(name, opts) {
11177
11190
  if (!SAFE_NAME.test(name)) {
11178
11191
  log.error(`Invalid change name: "${name}". Use letters, numbers, hyphens, or underscores (max 64 chars).`);
@@ -11220,15 +11233,10 @@ async function newChange(name, opts) {
11220
11233
  log.dim(tmpl);
11221
11234
  written += 1;
11222
11235
  }
11236
+ const tasksPath = join9(changeDir, "tasks.yml");
11237
+ applyScaffoldMetadata(tasksPath, name, dependencies);
11223
11238
  if (dependencies.length > 0) {
11224
- const tasksPath = join9(changeDir, "tasks.yml");
11225
- if (existsSync8(tasksPath)) {
11226
- const raw = readFileSync8(tasksPath, "utf8");
11227
- const data = yaml.load(raw) ?? {};
11228
- data["depends-on"] = dependencies;
11229
- writeFileSync4(tasksPath, yaml.dump(data, { lineWidth: -1, noRefs: true }), "utf8");
11230
- log.dim(`depends-on: ${dependencies.join(", ")}`);
11231
- }
11239
+ log.dim(`depends-on: ${dependencies.join(", ")}`);
11232
11240
  }
11233
11241
  log.blank();
11234
11242
  log.ok(`${written} template(s) created in specs/changes/${name}`);
@@ -11330,7 +11338,7 @@ init_logger();
11330
11338
  import { existsSync as existsSync13, readFileSync as readFileSync16, readdirSync as readdirSync7 } from "fs";
11331
11339
  import { homedir as homedir3 } from "os";
11332
11340
  import { join as join16 } from "path";
11333
- import yaml2 from "js-yaml";
11341
+ import yaml from "js-yaml";
11334
11342
  import picomatch2 from "picomatch";
11335
11343
 
11336
11344
  // src/schemas/agent-log.schema.ts
@@ -11571,7 +11579,7 @@ function loadContextPolicy(cwd) {
11571
11579
  function loadYamlFile(path) {
11572
11580
  try {
11573
11581
  const raw = readFileSync16(path, "utf8");
11574
- return { data: yaml2.load(raw, { schema: yaml2.JSON_SCHEMA }), parseError: null };
11582
+ return { data: yaml.load(raw, { schema: yaml.JSON_SCHEMA }), parseError: null };
11575
11583
  } catch (err) {
11576
11584
  return { data: null, parseError: err.message };
11577
11585
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "contract-driven-delivery",
3
- "version": "2.0.15",
3
+ "version": "2.0.16",
4
4
  "description": "Contract-driven delivery kit for AI coding agents with deterministic context indexes, manifest-backed read-scope governance, and orchestrated contracts-first delivery.",
5
5
  "keywords": [
6
6
  "contract-driven",