@dtlabs/ai-toolkit 0.24.8 → 0.24.9

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,4 +1,13 @@
1
1
  ## 0.1.4 (2026-07-21)
2
+ ## [0.24.9](https://github.com/dtlab-tech/ai-toolkit/compare/v0.24.8...v0.24.9) (2026-11-03)
3
+
4
+
5
+ ### Bug Fixes
6
+
7
+ * **pm-phase1:** never overwrite a good skeleton with a non-document reply, and repair last-cycle AC traceability once ([8f2f32f](https://github.com/dtlab-tech/ai-toolkit/commit/8f2f32f1852ade4551da62cbac8bc07d8f0c2961))
8
+ * **pm-phase1:** never overwrite a good skeleton with a non-document reply, and repair last-cycle AC traceability once ([3038efe](https://github.com/dtlab-tech/ai-toolkit/commit/3038efed641464b93da50377cd834cc1d4a85e48))
9
+ * **pm-phase1:** never overwrite a good skeleton with a non-document reply, and repair last-cycle AC traceability once ([784f814](https://github.com/dtlab-tech/ai-toolkit/commit/784f8141fb4afdefe2b2592f9d8b57a4f41207c2))
10
+
2
11
  ## [0.24.8](https://github.com/dtlab-tech/ai-toolkit/compare/v0.24.7...v0.24.8) (2026-11-02)
3
12
 
4
13
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dtlabs/ai-toolkit",
3
- "version": "0.24.8",
3
+ "version": "0.24.9",
4
4
  "description": "AI Toolkit — reusable agents and procedures for developers to build AI-powered applications.",
5
5
  "files": [
6
6
  "docs/",
@@ -39,6 +39,32 @@ return c.run(meta.name, featureDir, prefix, async () => {
39
39
  // per-UC and per-component generation loops' own fail-fast validity check.
40
40
  const USE_CASE_RETRY_ATTEMPTS = 2
41
41
  const COMPONENT_RETRY_ATTEMPTS = 2
42
+ const SKELETON_RETRY_ATTEMPTS = 2
43
+
44
+ // A skeleton worker occasionally answers with a question or an apology instead of the document
45
+ // ("I have not generated the skeleton yet ... I'm asking here"). The skeleton was written
46
+ // straight onto req/index.md or spec/index.md and only checked afterwards, so one such reply
47
+ // destroyed the good document that was there (FTR-034, revision cycle 2). The reply is now
48
+ // checked for every numbered section BEFORE the target file is touched; on a bad reply the
49
+ // worker is asked once more, and if that fails too the run stops with the existing file intact.
50
+ async function writeSkeleton(label, workerId, basePrompt, sectionCount, outPath, transform) {
51
+ let raw
52
+ for (let attempt = 1; attempt <= SKELETON_RETRY_ATTEMPTS; attempt++) {
53
+ const prompt = attempt === 1 ? basePrompt : basePrompt +
54
+ '\n\nYour previous reply was not the document. This is a non-interactive run: nobody can answer a question. ' +
55
+ 'Reply with ONLY the complete document, every numbered section included, and with no preamble. Where feature.md ' +
56
+ 'leaves a point undecided, record it inside the document as an open question instead of asking.'
57
+ raw = await c.worker(workerId, prompt, attempt === 1 ? undefined : { label: `${label}:skeleton:retry:${attempt}` })
58
+ if (missingSections(raw, sectionCount).length === 0) {
59
+ c.writeOutput(outPath, transform ? transform(raw) : raw)
60
+ return
61
+ }
62
+ }
63
+ // same message the post-write completeness check has always produced (truncation guard)
64
+ try { assertCompleteSections(label, raw, sectionCount) } catch (e) {
65
+ throw new Error(`${e.message}; after ${SKELETON_RETRY_ATTEMPTS} attempts the existing file was left untouched`)
66
+ }
67
+ }
42
68
 
43
69
  // Templates live under the toolkit's own installed scripts/templates/ — outside this project
44
70
  // for a globally-installed toolkit, which a non-interactive `--print` worker invocation has no
@@ -215,8 +241,9 @@ return c.run(meta.name, featureDir, prefix, async () => {
215
241
  // 1. Skeleton (no declared worker() output — host writes it itself, mirroring FTR-020's
216
242
  // skeleton step)
217
243
  if (regenSkeleton) {
218
- const skeleton = await c.worker(reqId, withTemplate(featurePath, 'req/index.md', 'gaia-requirements-index-template.md') + revisionSection(revisionFindings))
219
- c.writeOutput(reqIndexPath, skeleton)
244
+ await writeSkeleton('Requirements', reqId,
245
+ withTemplate(featurePath, 'req/index.md', 'gaia-requirements-index-template.md') + revisionSection(revisionFindings),
246
+ 9, reqIndexPath)
220
247
  }
221
248
 
222
249
  // 2. Structural self-consistency pre-check (req-skeleton-check.js, same
@@ -565,8 +592,9 @@ return c.run(meta.name, featureDir, prefix, async () => {
565
592
 
566
593
  // 1. Skeleton (no declared worker() output — host writes it itself)
567
594
  if (regenSkeleton) {
568
- const skeleton = await c.worker(specId, withTemplate(featurePath, 'spec/index.md', 'gaia-tech-spec-index-template.md') + revisionSection(revisionFindings))
569
- c.writeOutput(specIndexPath, applyComponentSlugs(skeleton, manifest))
595
+ await writeSkeleton('Tech-Spec', specId,
596
+ withTemplate(featurePath, 'spec/index.md', 'gaia-tech-spec-index-template.md') + revisionSection(revisionFindings),
597
+ 12, specIndexPath, skeleton => applyComponentSlugs(skeleton, manifest))
570
598
  }
571
599
 
572
600
  // 2. Structural pre-check (tech-spec-graph-checks.js, unmodified — FTR-023 — now pointed at
@@ -769,14 +797,29 @@ return c.run(meta.name, featureDir, prefix, async () => {
769
797
  break
770
798
  }
771
799
  if (cycle === 3) {
772
- // The deterministic AC<->component traceability gaps stay a hard failure: they are
773
- // mechanical and the Work Breakdown relies on that mapping. What the validator judges
800
+ // The deterministic AC<->component traceability gaps stay a hard failure if the repair
801
+ // below cannot close them: they are mechanical and the Work Breakdown relies on that
802
+ // mapping. What the validator judges
774
803
  // (open design points, drill-down findings) is for a person to accept or reject, and Gate
775
804
  // 1 is that review: regenerating again cannot decide a point feature.md leaves undecided
776
805
  // (on FTR-034 three full runs of three cycles each never converged for that reason).
777
- if (structuralFindings.length) throw new Error('Validation gaps remain after 3 cycles')
778
- openGaps = mergedFindings
779
- validationSummary = `${openGaps.length} open gap(s) after 3 cycles, left for Gate 1 review (see the Validation Report)`
806
+ //
807
+ // A traceability gap found on the LAST cycle used to be unrecoverable: a Requirements
808
+ // revision on cycle 2 can add acceptance criteria the Tech-Spec does not yet address, and
809
+ // that gap is only routed to a Tech-Spec revision on the cycle after. Give it one repair:
810
+ // regenerate the Tech-Spec once with the gaps as feedback, then re-check mechanically.
811
+ let repaired = false
812
+ if (structuralFindings.length) {
813
+ revisionFindings = structuralFindings
814
+ await generateTechSpec(true)
815
+ assembledTechSpec = assembleTechSpec()
816
+ if (computeAcComponentTraceabilityGaps(c.read(reqIndexPath), c.read(specIndexPath)).length) throw new Error('Validation gaps remain after 3 cycles')
817
+ repaired = true
818
+ c.append(file('Validation-Report'), '\n\n## Post-validation repair\nThe AC traceability findings listed above were repaired after the last validation cycle by regenerating the Tech-Spec once. The repaired Tech-Spec was checked mechanically (every AC is addressed) but was not re-validated by the validator.\n')
819
+ }
820
+ openGaps = mergedFindings.filter(f => !structuralFindings.includes(f))
821
+ validationSummary = `${openGaps.length} open gap(s) after 3 cycles, left for Gate 1 review (see the Validation Report)` +
822
+ (repaired ? '; the Tech-Spec was regenerated once to repair AC traceability and not re-validated' : '')
780
823
  break
781
824
  }
782
825
  if (!mergedNeedsRequirementsFix && !mergedNeedsTechSpecFix) throw new Error(`Validation agent reported gaps on cycle ${cycle} but flagged neither document for revision`)