@dzhechkov/skills-feature-adr 1.3.32 → 1.3.36

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dzhechkov/skills-feature-adr",
3
- "version": "1.3.32",
3
+ "version": "1.3.36",
4
4
  "description": "Adaptive Feature Development skill pack for Claude Code — 11-step pipeline with Complexity Router (S/M/L/XL), ADR-driven architecture, 15 agentic-qe skills, multi-agent fleet QE. Supports --full-qe, --full-qe-extended, --with-learning, and --knowledge-extractor modes.",
5
5
  "bin": {
6
6
  "skills-feature-adr": "./bin/cli.js"
@@ -36,11 +36,20 @@ async function run(options) {
36
36
  const templatesDir = getTemplatesDir();
37
37
 
38
38
  // ── b) Get installed components ───────────────────────────────────────
39
- const installedKeys = manifest.components || [];
40
- if (installedKeys.length === 0) {
39
+ const manifestKeys = manifest.components || [];
40
+ if (manifestKeys.length === 0) {
41
41
  warn('Manifest lists no installed components. Consider running init instead.');
42
42
  process.exit(1);
43
43
  }
44
+ // UNION the manifest's components with the CORE components the NEW template provides. A component
45
+ // ADDED upstream AFTER this install (e.g. `workflows`, bundled in a later release) would otherwise be
46
+ // invisible to `update` — never installed, and worse, a file it ships (`.claude/workflows/feature-adr.js`)
47
+ // could be mis-flagged as an orphan "dropped from template" and DELETED. Installing the union fixes
48
+ // both: new core components get added, and every file the current template provides stays in the
49
+ // rebuilt file-set, so it is updated (never orphaned). Optional components are only touched if they
50
+ // were already installed (they stay driven by the manifest).
51
+ const newCoreKeys = Object.keys(COMPONENTS).filter((k) => fileExists(path.join(templatesDir, COMPONENTS[k].src)));
52
+ const installedKeys = [...new Set([...manifestKeys, ...newCoreKeys])];
44
53
 
45
54
  info(`Current version: ${dim(manifest.version)}`);
46
55
  const pkgPath = path.resolve(__dirname, '../../package.json');
@@ -274,24 +283,25 @@ async function run(options) {
274
283
  }
275
284
 
276
285
  if (totalAdded === 0 && totalModified === 0 && orphans.length === 0) {
277
- if (keptModified.length > 0) {
278
- // Nothing to copy, but files were kept. Refresh the per-file baseline so a
279
- // legacy (no-hashes) install self-heals into the 3-way regime and a kept
280
- // conflict records the current template as its baseline (D3/D4). We do NOT
281
- // bump the version \u2014 no template bytes were actually applied. Never write
282
- // under --dry-run.
283
- if (!dryRun) {
284
- const { newHashes } = rebuildManifestData();
285
- if (Object.keys(newHashes).length > 0) {
286
- manifest.hashes = newHashes;
287
- manifest.updatedAt = new Date().toISOString();
288
- writeManifest(targetDir, manifest);
289
- }
286
+ // Nothing to copy, but the MANIFEST may still be stale: a component newly covered by the union
287
+ // (or a file the template ships that the old manifest never tracked) must be re-tracked so the
288
+ // manifest matches reality \u2014 otherwise a later `remove`/`update` mis-reasons about ownership. Also
289
+ // refreshes the per-file baseline so a legacy (no-hashes) install self-heals into the 3-way regime.
290
+ // Never bump the version (no template bytes applied) and never write under --dry-run.
291
+ if (!dryRun) {
292
+ const { allFiles, newHashes } = rebuildManifestData();
293
+ const prevFiles = JSON.stringify((manifest.files || []).map(toManifestPath).sort());
294
+ const nextFiles = JSON.stringify(allFiles.slice().sort());
295
+ const compsChanged = JSON.stringify((manifest.components || []).slice().sort()) !== JSON.stringify(installedKeys.slice().sort());
296
+ if (prevFiles !== nextFiles || compsChanged || Object.keys(newHashes).length > 0) {
297
+ manifest.files = allFiles.sort();
298
+ manifest.components = installedKeys;
299
+ if (Object.keys(newHashes).length > 0) manifest.hashes = newHashes;
300
+ manifest.updatedAt = new Date().toISOString();
301
+ writeManifest(targetDir, manifest);
290
302
  }
291
- success('No files to update \u2014 locally modified file(s) kept.');
292
- } else {
293
- success('Everything is up to date!');
294
303
  }
304
+ success(keptModified.length > 0 ? 'No files to update \u2014 locally modified file(s) kept.' : 'Everything is up to date!');
295
305
  process.exit(0);
296
306
  }
297
307
 
@@ -60,6 +60,12 @@ Without this, the Code agent returns null and the run stalls. With `coder: 'code
60
60
  detects the null, logs *"Claude unavailable (limit?) — falling back to Codex auto"*, and finishes
61
61
  the code + tests on Codex — no restart, no lost work.
62
62
 
63
+ **Codex-landed barrier (Step 7.5):** Codex applies edits OUT-OF-BAND via its own runtime, so a naive
64
+ pipeline runs Step-8 QE before the async write flushes and false-grades *"Step 7 never ran"* (grade D on
65
+ real, landed code — observed on the goap-ed25519 fix). When Codex was the coder, feature-adr now polls
66
+ `git status` (excluding pipeline artifacts) up to ~30s until the code changes appear and hands the
67
+ confirmed file list to QE. Claude-coded runs are synchronous, so the barrier is skipped (zero cost).
68
+
63
69
  **Example invocations:**
64
70
  ```js
65
71
  // Plan on Codex, code+QE fall back to Codex only if Claude runs out, auto:
@@ -366,7 +366,7 @@ to Codex — always ASKING first, always with a Claude fallback (never blocks):
366
366
  - **Model choice** → `args.codexModel` (`auto` default (Codex self-selects) · or an id your account exposes (e.g. `gpt-5.5`)).
367
367
 
368
368
  *Scenario:* an L/XL run hits the session limit during coding → with `coder: 'codex-fallback'` feature-adr
369
- logs *"Claude unavailable (limit?) — falling back to Codex auto"* and finishes on Codex, no restart.
369
+ logs *"Claude unavailable (limit?) — falling back to Codex auto"* and finishes on Codex, no restart. Codex writes out-of-band, so feature-adr confirms the edits LANDED (`git status`, ~30s poll) before QE — no false "Step 7 never ran" grade.
370
370
 
371
371
  *Example:* `Workflow({ scriptPath: '.claude/workflows/feature-adr.js', args: { slug, description, tier:
372
372
  'M', planner: 'codex', coder: 'codex-fallback', qeReviewer: 'codex-fallback', codexModel: 'auto' } }) // 'auto' = Codex picks top; or pin e.g. 'gpt-5.5'`.
@@ -113,9 +113,21 @@ if (code === null && (CODER === 'codex' || CODER === 'codex-fallback')) {
113
113
  if (codexCode) { code = { wrote: [FDIR + '/07_code_changes/change_manifest.md'], summary: String(codexCode).slice(0, 500) }; coderUsed = CODER === 'codex' ? 'codex' : 'codex-fallback' }
114
114
  }
115
115
 
116
+ // Step 7.5: Codex-landed barrier. Codex applies edits OUT-OF-BAND via its own runtime; without this,
117
+ // Step-8 QE reads the tree before the async write flushes and false-grades "Step 7 never ran" (grade D
118
+ // on real, landed code — observed on the goap-ed25519 crypto fix). Poll git status (excluding pipeline
119
+ // artifacts) up to ~30s until real code changes appear, and hand the confirmed file list to QE so it
120
+ // reviews the ACTUAL landed changes. Claude-coded runs are synchronous → this barrier is skipped.
121
+ let landedNote = ''
122
+ if (coderUsed === 'codex' || coderUsed === 'codex-fallback') {
123
+ const barrierCmd = 'for i in 1 2 3 4 5 6; do n=$(git -C ' + REPO + ' status --porcelain 2>/dev/null | grep -vE "features/|[.]dz/|[.]agentic-qe/|roam/" | wc -l); [ "$n" -gt 0 ] && break; sleep 5; done; echo "changed=$n"; git -C ' + REPO + ' status --porcelain 2>/dev/null | grep -vE "features/|[.]dz/|[.]agentic-qe/|roam/" | head -40'
124
+ const probe = await agent('Confirm the Codex Step-7 edits have LANDED in the working tree BEFORE QE runs (Codex writes out-of-band). Run EXACTLY this via Bash and return its stdout verbatim, nothing else:\n' + barrierCmd, { label: 'code:confirm-landed', phase: 'Code' })
125
+ landedNote = '\n\nCODEX-CODED (out-of-band): review the CONFIRMED landed working-tree changes below — do NOT report "Step 7 never ran" if files are listed. If changed=0 the implementation genuinely did not land, then grade accordingly.\n' + String(probe || '(landed-probe failed)').slice(0, 1500)
126
+ }
127
+
116
128
  // Step 8: QE (brutal-honesty, agentic-qe) + MANDATORY teach
117
129
  phase('QE')
118
- const qePrompt = 'Step 8 (QE - brutal-honesty review, agentic-qe) of /feature-adr for "' + DESC + '" (' + SLUG + '). Adversarially review the SHIPPED code (read it): correctness, edge cases, error handling, and the LOAD-BEARING property the ADR named (ASSERT it has a test - the recurring lesson). Grade A/B/C/D honestly. Assess code-test adequacy + doc-test presence. List CONFIRMED gaps with severity. Write ' + FDIR + '/08_qe_report.md. MANDATORY SELF-LEARNING STORE (close the loop, never skip): via Bash run `dz teach "<a durable reusable lesson from this feature - a rule/pattern/pitfall, NOT a checkpoint echo>" --reward <0.7-0.95> --domain <area>` for each genuine lesson (1-3 max, high-signal), then run `' + DZ + ' statusline --fa-record --slug ' + SLUG + ' --step "Step 8 QE" --recalled 3 --stored <count taught> --mode ' + MODE + ' --project ' + REPO + '` (run it verbatim via Bash, do not skip). Do NOT teach trivia or invent gaps. Return {grade, gaps, codeTestsAdequate, docTestsPresent}.'
130
+ const qePrompt = 'Step 8 (QE - brutal-honesty review, agentic-qe) of /feature-adr for "' + DESC + '" (' + SLUG + '). Adversarially review the SHIPPED code (read it): correctness, edge cases, error handling, and the LOAD-BEARING property the ADR named (ASSERT it has a test - the recurring lesson). Grade A/B/C/D honestly. Assess code-test adequacy + doc-test presence. List CONFIRMED gaps with severity. Write ' + FDIR + '/08_qe_report.md. MANDATORY SELF-LEARNING STORE (close the loop, never skip): via Bash run `dz teach "<a durable reusable lesson from this feature - a rule/pattern/pitfall, NOT a checkpoint echo>" --reward <0.7-0.95> --domain <area>` for each genuine lesson (1-3 max, high-signal), then run `' + DZ + ' statusline --fa-record --slug ' + SLUG + ' --step "Step 8 QE" --recalled 3 --stored <count taught> --mode ' + MODE + ' --project ' + REPO + '` (run it verbatim via Bash, do not skip). Do NOT teach trivia or invent gaps. Return {grade, gaps, codeTestsAdequate, docTestsPresent}.' + landedNote
119
131
  let qe = null
120
132
  let qeReviewerUsed = 'claude'
121
133
  if (QE_REVIEWER !== 'codex') {