intentdna 1.8.0 → 1.8.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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/dist/cli/commands/run.d.ts +2 -0
- package/dist/cli/commands/run.js +43 -40
- package/dist/hooks/cli.d.ts +4 -0
- package/dist/hooks/cli.js +21 -406
- package/dist/runtime/skill-adapter.js +44 -5
- package/dist/runtime/verifier.d.ts +51 -0
- package/dist/runtime/verifier.js +462 -0
- package/dist/templates/flutter-rewrite.dna.yaml +80 -4
- package/package.json +1 -1
|
@@ -152,9 +152,11 @@ context_files:
|
|
|
152
152
|
analysis_reviewer:
|
|
153
153
|
- "docs/behavior/{{ARGUMENTS}}.md"
|
|
154
154
|
- ".dna/specs/diagnosis-{{ARGUMENTS}}.md"
|
|
155
|
+
- ".dna/specs/diagnosis-{{ARGUMENTS}}.contract.json"
|
|
155
156
|
surgeon:
|
|
156
157
|
- ".dna/specs/diagnosis-{{ARGUMENTS}}.md"
|
|
157
158
|
- ".dna/specs/diagnosis-{{ARGUMENTS}}.review.json"
|
|
159
|
+
- ".dna/specs/diagnosis-{{ARGUMENTS}}.contract.json"
|
|
158
160
|
fix_reviewer:
|
|
159
161
|
- "docs/behavior/{{ARGUMENTS}}.md"
|
|
160
162
|
- ".dna/specs/diagnosis-{{ARGUMENTS}}.md"
|
|
@@ -163,6 +165,8 @@ context_files:
|
|
|
163
165
|
- ".dna/specs/diagnosis-{{ARGUMENTS}}.md"
|
|
164
166
|
|
|
165
167
|
verifier_policy:
|
|
168
|
+
allow_commands:
|
|
169
|
+
- "node -e \"const fs=require('node:fs');const crypto=require('node:crypto');const path=require('node:path');const mod=process.env.ARGUMENTS||'$ARGUMENTS';const allowed=['BUG','UNIMPLEMENTED','INFRA','REMOVED','TEST_BUG'];const action=['BUG','UNIMPLEMENTED','INFRA','TEST_BUG'];const fail=m=>{throw new Error(m)};const same=(a,b)=>Array.isArray(a)&&a.length===b.length&&a.every((v,i)=>v===b[i]);const snapSame=(a,b)=>a&&b&&a.baseline_source_sha256===b.baseline_source_sha256&&a.diagnosis_artifact_sha256===b.diagnosis_artifact_sha256&&a.observed_failure_count===b.observed_failure_count&&a.work_item_count===b.work_item_count&&same(a.allowed_kinds,b.allowed_kinds);const root=process.cwd();const inside=p=>{if(typeof p!=='string'||!p||path.isAbsolute(p))fail('path_invalid');const r=path.resolve(root,p);if(r!==root&&!r.startsWith(root+path.sep))fail('path_escape');return r};const readJson=p=>JSON.parse(fs.readFileSync(inside(p),'utf8'));if(!/^[A-Za-z0-9_-]+$/.test(mod))fail('module');const contractPath='.dna/specs/diagnosis-'+mod+'.contract.json';const reviewPath='.dna/specs/diagnosis-'+mod+'.review.json';const diagnosisPath='.dna/specs/diagnosis-'+mod+'.md';const data=readJson(contractPath);if(data.contract_version!=='diagnosis-contract/v1')fail('contract_version');if(data.module!==mod)fail('module_mismatch');if(data.diagnosis_artifact!==diagnosisPath)fail('diagnosis_artifact_path');if(!same(data.allowed_kinds,allowed))fail('allowed_kinds');const observed=Array.isArray(data.observed_failures)?data.observed_failures:fail('observed_failures');const work=Array.isArray(data.work_items)?data.work_items:fail('work_items');if(!observed.length)fail('empty_observed_failures');if(!work.length)fail('empty_work_items');const ids=new Set(observed.map(x=>x&&x.id).filter(Boolean));if(ids.size!==observed.length)fail('observed_failure_ids');const failureById=new Map();for(const f of observed){if(typeof f.test_name!=='string'||!f.test_name.trim())fail('observed_failure_test_name');if(typeof f.symptom!=='string'||!f.symptom.trim())fail('observed_failure_symptom');if(!Array.isArray(f.evidence_paths)||!f.evidence_paths.length)fail('observed_failure_evidence');for(const p of f.evidence_paths)inside(p);if(f.kind!==undefined)fail('observed_failure_kind_forbidden');failureById.set(f.id,f)}const mapped=new Set();for(const item of work){if(!allowed.includes(item.kind))fail('invalid_kind');if(item.kind==='PLACEHOLDER_CONTRACT')fail('placeholder_kind');if(!Array.isArray(item.observed_failure_ids)||!item.observed_failure_ids.length)fail('work_item_observed_failure_ids');if(!Array.isArray(item.evidence_paths)||!item.evidence_paths.length)fail('work_item_evidence');for(const p of item.evidence_paths)inside(p);if(action.includes(item.kind)){if(!Array.isArray(item.v1_evidence_paths)||!item.v1_evidence_paths.length)fail('work_item_v1_evidence');if(!Array.isArray(item.v2_evidence_paths)||!item.v2_evidence_paths.length)fail('work_item_v2_evidence');for(const p of item.v1_evidence_paths)inside(p);for(const p of item.v2_evidence_paths)inside(p)}for(const id of item.observed_failure_ids){const f=failureById.get(id);if(!f)fail('unknown_observed_failure_id');if(action.includes(item.kind)&&(!f.test_name||!String(f.test_name).trim()))fail('action_test_evidence');mapped.add(id)}}if(mapped.size!==ids.size)fail('unmapped_observed_failures');for(const f of observed.filter(x=>String(x.symptom).includes('PLACEHOLDER_CONTRACT'))){const kinds=new Set(work.filter(i=>i.observed_failure_ids.includes(f.id)).map(i=>i.kind));if(![...kinds].every(k=>k==='TEST_BUG'||k==='UNIMPLEMENTED')||kinds.size===0)fail('placeholder_mapping')}const counts=data.baseline&&data.baseline.counts;if(!data.baseline||!data.baseline.source_path||!counts)fail('baseline');inside(data.baseline.source_path);for(const k of ['failed','skipped','hung']){if(!Number.isInteger(counts[k])||counts[k]<0)fail('baseline_count_'+k)}const total=counts.failed+counts.skipped+counts.hung;if(total!==observed.length)fail('baseline_count_mismatch');const summary=data.summary_counts||{};for(const kind of allowed){if(!Number.isInteger(summary[kind])||summary[kind]!==work.filter(x=>x.kind===kind).length)fail('summary_count_'+kind)}const sha=p=>crypto.createHash('sha256').update(fs.readFileSync(inside(p))).digest('hex');const snap=data.contract_snapshot||{};if(snap.observed_failure_count!==observed.length||snap.work_item_count!==work.length||!same(snap.allowed_kinds,allowed))fail('contract_snapshot');if(snap.baseline_source_sha256!==sha(data.baseline.source_path))fail('baseline_sha256');if(snap.diagnosis_artifact_sha256!==sha(diagnosisPath))fail('diagnosis_sha256');if(!data.staleness||data.staleness.is_stale!==false)fail('stale_contract');const review=readJson(reviewPath);if(review.contract_version!=='diagnosis-contract-review/v1')fail('review_contract_version');if(review.contract_valid!==true)fail('review_contract_valid');if(review.verdict!=='APPROVE')fail('review_verdict');if(review.contract_artifact_reviewed!==contractPath)fail('review_contract_artifact_path');if(review.artifact_reviewed!==diagnosisPath)fail('review_artifact_path');if(!snapSame(review.contract_snapshot,snap))fail('review_contract_snapshot');if(!review.updated_at||Number.isNaN(Date.parse(review.updated_at)))fail('review_updated_at');if(!Array.isArray(review.evidence_paths)||!review.evidence_paths.length)fail('review_evidence_paths');for(const p of review.evidence_paths)inside(p);const cm=fs.statSync(inside(contractPath)).mtimeMs;const dm=fs.statSync(inside(diagnosisPath)).mtimeMs;const rm=fs.statSync(inside(reviewPath)).mtimeMs;if(rm<cm||rm<dm)fail('review_stale');\""
|
|
166
170
|
allow_builtin_asserts:
|
|
167
171
|
- clean_working_tree
|
|
168
172
|
|
|
@@ -255,6 +259,9 @@ roles:
|
|
|
255
259
|
- "For each failing, skipped, and hung test: read test code + v1 impl + v2 impl"
|
|
256
260
|
- "Classify each as BUG / UNIMPLEMENTED / INFRA / REMOVED / TEST_BUG (skipped tests are usually UNIMPLEMENTED)"
|
|
257
261
|
- "Output diagnosis spec with v1 code snippets + v2 current state + evidence paths"
|
|
262
|
+
- "Also write .dna/specs/diagnosis-$ARGUMENTS.contract.json as the structured sidecar mapping baseline observed_failures to diagnosis work_items"
|
|
263
|
+
- "The sidecar allowed_kinds array must be exactly BUG, UNIMPLEMENTED, INFRA, REMOVED, TEST_BUG in that order; PLACEHOLDER_CONTRACT is never a kind"
|
|
264
|
+
- "A placeholder-contract symptom may appear only in observed_failures.symptom and must map to concrete TEST_BUG and/or UNIMPLEMENTED work_items with evidence"
|
|
258
265
|
- "If reanalyzing after REQUEST_REANALYSIS, update every reviewer sections_to_fix item and report sections_updated"
|
|
259
266
|
- "DO NOT include fix prescriptions, implementation order, call-site instructions, or Notes for Surgeon"
|
|
260
267
|
- "DO NOT run tests. DO NOT edit app code. Analysis only."
|
|
@@ -270,9 +277,10 @@ roles:
|
|
|
270
277
|
instructions:
|
|
271
278
|
- "REQUIRED FIRST: Read all context files listed in SKILL.md"
|
|
272
279
|
- "Verify analyzer spec: v1 references exist? Classifications sound?"
|
|
280
|
+
- "Independently validate .dna/specs/diagnosis-$ARGUMENTS.contract.json against the template sidecar contract; do not use Bash"
|
|
273
281
|
- "Check missing: any failing test not covered?"
|
|
274
282
|
- "Verify diagnosis remains evidence-only: no fix prescriptions, implementation order, call-site instructions, or Notes for Surgeon"
|
|
275
|
-
- "Output a structured verdict block with verdict, artifact_reviewed, sections_to_fix, evidence_paths, confidence, summary, and updated_at"
|
|
283
|
+
- "Output a structured verdict block with contract_version, contract_valid, failure_reason, contract_artifact_reviewed, contract_snapshot, verdict, artifact_reviewed, sections_to_fix, evidence_paths, confidence, summary, and updated_at"
|
|
276
284
|
- "Write the same structured verdict JSON to .dna/specs/diagnosis-$ARGUMENTS.review.json before finishing"
|
|
277
285
|
|
|
278
286
|
fix_reviewer:
|
|
@@ -327,9 +335,12 @@ controllers:
|
|
|
327
335
|
policy:
|
|
328
336
|
diagnosis_reuse_criteria:
|
|
329
337
|
- "$diagnosis_artifact_path exists"
|
|
338
|
+
- ".dna/specs/diagnosis-$ARGUMENTS.contract.json exists and passes the exact verifier_policy allow_commands node -e completion.command_success validator"
|
|
330
339
|
- "$diagnosis_review_artifact_path exists and verdict is APPROVE"
|
|
340
|
+
- "$diagnosis_review_artifact_path contract_valid is true and contract_artifact_reviewed equals .dna/specs/diagnosis-$ARGUMENTS.contract.json"
|
|
331
341
|
- "behavior doc is not newer than diagnosis spec"
|
|
332
342
|
- "baseline artifact is not newer than diagnosis spec"
|
|
343
|
+
- "baseline artifact is not newer than diagnosis contract sidecar"
|
|
333
344
|
- "user did not explicitly pass reanalyze or scope changed"
|
|
334
345
|
- "progress JSON does not say diagnosis_invalid"
|
|
335
346
|
progress_provenance_checks:
|
|
@@ -346,12 +357,14 @@ controllers:
|
|
|
346
357
|
- "CONTINUE -> next fresh context fix round"
|
|
347
358
|
- "REQUEST_CHANGES -> stop with the configured stop report"
|
|
348
359
|
- "BLOCKED -> stop with the configured stop report"
|
|
360
|
+
- "DIAGNOSIS_CONTRACT_INVALID -> stop before fix with Verdict BLOCKED, failure_reason DIAGNOSIS_CONTRACT_INVALID, and progress diagnosis_invalid true"
|
|
349
361
|
verdict_mapping:
|
|
350
362
|
- "PASS / CONTINUE / REQUEST_CHANGES / BLOCKED mapping:"
|
|
351
363
|
- "- PASS: success stop"
|
|
352
364
|
- "- CONTINUE: start the next fresh context round"
|
|
353
365
|
- "- REQUEST_CHANGES: stop, report reviewer/verifier requested changes"
|
|
354
366
|
- "- BLOCKED: stop, report blocker"
|
|
367
|
+
- "- DIAGNOSIS_CONTRACT_INVALID: stop before fix, propagate diagnosis_invalid true in progress JSON, and require /dna-frw-diagnosis reanalysis"
|
|
355
368
|
- "- MAX_ROUNDS: stop, report max rounds reached and next focus"
|
|
356
369
|
stop_report:
|
|
357
370
|
- "On REQUEST_CHANGES, BLOCKED, or MAX_ROUNDS, output exactly these Chinese sections:"
|
|
@@ -363,6 +376,7 @@ controllers:
|
|
|
363
376
|
- "Do not infer module completion from raw test output."
|
|
364
377
|
- "The controller must schedule only from $progress_artifact_path."
|
|
365
378
|
- "Manual /dna-frw-diagnosis and /dna-frw-fix flows remain separate and available."
|
|
379
|
+
- "Manual /dna-frw-fix must run the same diagnosis contract preflight before edits and stop with DIAGNOSIS_CONTRACT_INVALID when the sidecar or independent review contract is invalid."
|
|
366
380
|
- "Do not use the generic workflow policy block; branch only from the progress JSON verdict."
|
|
367
381
|
|
|
368
382
|
workflows:
|
|
@@ -463,12 +477,33 @@ workflows:
|
|
|
463
477
|
- Evidence paths for every classification
|
|
464
478
|
- NO fix prescriptions, implementation order, call-site instructions, or "Notes for Surgeon"
|
|
465
479
|
- NO running tests (analysis only)
|
|
466
|
-
6.
|
|
480
|
+
6. Write .dna/specs/diagnosis-$ARGUMENTS.contract.json with this sidecar contract:
|
|
481
|
+
- contract_version: "diagnosis-contract/v1"
|
|
482
|
+
- module: "$ARGUMENTS"
|
|
483
|
+
- diagnosis_artifact: ".dna/specs/diagnosis-$ARGUMENTS.md"
|
|
484
|
+
- baseline: { "source_path": "structured baseline path or explicit fallback file", "counts": { "failed": N, "skipped": N, "hung": N } }
|
|
485
|
+
- allowed_kinds: ["BUG", "UNIMPLEMENTED", "INFRA", "REMOVED", "TEST_BUG"] exactly, no extra values
|
|
486
|
+
- observed_failures: one entry per failing/skipped/hung test with id, test_name, symptom, evidence_paths, and no kind field
|
|
487
|
+
- work_items: one or more entries mapping observed_failure_ids to kind, evidence_paths, v1_evidence_paths, and v2_evidence_paths
|
|
488
|
+
- summary_counts: exact count of work_items by BUG / UNIMPLEMENTED / INFRA / REMOVED / TEST_BUG
|
|
489
|
+
- contract_snapshot: baseline_source_sha256, diagnosis_artifact_sha256, observed_failure_count, work_item_count, allowed_kinds
|
|
490
|
+
- staleness: source mtimes or hashes for behavior_doc, baseline, diagnosis_artifact, and is_stale false
|
|
491
|
+
7. Contract rules:
|
|
492
|
+
- observed_failures must not be empty; work_items must not be empty.
|
|
493
|
+
- Every observed_failure id must be mapped by at least one work_item.
|
|
494
|
+
- Every work_item kind must be one of the five allowed kinds exactly.
|
|
495
|
+
- PLACEHOLDER_CONTRACT is forbidden as a kind. If a placeholder-contract symptom exists, keep it in observed_failures.symptom and map it to concrete TEST_BUG and/or UNIMPLEMENTED work_items with evidence.
|
|
496
|
+
- REMOVED may be diagnosed but must be marked manual/user-approved before any future fix can execute it.
|
|
497
|
+
- Mixed unknown kinds, missing evidence, count mismatches, missing/changed contract_snapshot, and stale baseline/diagnosis snapshots make the contract invalid.
|
|
498
|
+
8. If this is a reanalysis after REQUEST_REANALYSIS or DIAGNOSIS_CONTRACT_INVALID, read the reviewer verdict first, update every section listed in sections_to_fix plus the summary table and contract sidecar, and report sections_updated in your final response. Reanalysis is not successful unless both the spec artifact and contract sidecar are rewritten.
|
|
467
499
|
handoff:
|
|
468
500
|
produces:
|
|
469
501
|
- type: file
|
|
470
502
|
path: ".dna/specs/diagnosis-$ARGUMENTS.md"
|
|
471
503
|
description: "Diagnosis spec for module"
|
|
504
|
+
- type: file
|
|
505
|
+
path: ".dna/specs/diagnosis-$ARGUMENTS.contract.json"
|
|
506
|
+
description: "Structured diagnosis contract sidecar mapping baseline observed failures to work items"
|
|
472
507
|
|
|
473
508
|
- id: review
|
|
474
509
|
role: analysis_reviewer
|
|
@@ -484,10 +519,32 @@ workflows:
|
|
|
484
519
|
3. Classifications reasonable? (INFRA not mistaken for BUG)
|
|
485
520
|
4. Enough evidence detail for surgeon to act without adding fix prescriptions?
|
|
486
521
|
5. Diagnosis remains evidence-only: no implementation order, call-site instructions, or "Notes for Surgeon"?
|
|
522
|
+
6. .dna/specs/diagnosis-$ARGUMENTS.contract.json satisfies the structured sidecar contract:
|
|
523
|
+
- contract_version is "diagnosis-contract/v1"
|
|
524
|
+
- allowed_kinds is exactly ["BUG", "UNIMPLEMENTED", "INFRA", "REMOVED", "TEST_BUG"]
|
|
525
|
+
- observed_failures and work_items are non-empty and every observed_failure is mapped
|
|
526
|
+
- baseline counts equal observed_failures count
|
|
527
|
+
- summary_counts match work_items by kind
|
|
528
|
+
- every work_item has evidence_paths and only allowed kinds
|
|
529
|
+
- PLACEHOLDER_CONTRACT is rejected as a kind but placeholder-contract symptoms are allowed when mapped to TEST_BUG and/or UNIMPLEMENTED
|
|
530
|
+
- contract_snapshot includes diagnosis_artifact_sha256, baseline_source_sha256, observed_failure_count, work_item_count, and allowed_kinds
|
|
531
|
+
- staleness.is_stale is false; stale baseline/diagnosis/contract snapshots are invalid
|
|
532
|
+
- REMOVED work_items are reviewable diagnosis facts only and must not authorize automatic fix execution
|
|
487
533
|
|
|
488
534
|
Output exactly one structured verdict block:
|
|
489
535
|
```json
|
|
490
536
|
{
|
|
537
|
+
"contract_version": "diagnosis-contract-review/v1",
|
|
538
|
+
"contract_valid": true,
|
|
539
|
+
"failure_reason": "",
|
|
540
|
+
"contract_artifact_reviewed": ".dna/specs/diagnosis-$ARGUMENTS.contract.json",
|
|
541
|
+
"contract_snapshot": {
|
|
542
|
+
"baseline_source_sha256": "sha256 from contract sidecar",
|
|
543
|
+
"diagnosis_artifact_sha256": "sha256 from contract sidecar",
|
|
544
|
+
"observed_failure_count": 0,
|
|
545
|
+
"work_item_count": 0,
|
|
546
|
+
"allowed_kinds": ["BUG", "UNIMPLEMENTED", "INFRA", "REMOVED", "TEST_BUG"]
|
|
547
|
+
},
|
|
491
548
|
"verdict": "APPROVE" | "REQUEST_REANALYSIS",
|
|
492
549
|
"artifact_reviewed": ".dna/specs/diagnosis-$ARGUMENTS.md",
|
|
493
550
|
"sections_to_fix": ["section ids or titles; empty when approved"],
|
|
@@ -501,16 +558,21 @@ workflows:
|
|
|
501
558
|
Write the same verdict JSON to .dna/specs/diagnosis-$ARGUMENTS.review.json before finishing.
|
|
502
559
|
|
|
503
560
|
APPROVE → diagnosis complete.
|
|
504
|
-
REQUEST_REANALYSIS → sections_to_fix must be passed back to analyze. docs/behavior/blocked_items.md is historical context only and must not be treated as the current run verdict.
|
|
561
|
+
REQUEST_REANALYSIS → sections_to_fix must be passed back to analyze. If contract_valid is false, set failure_reason to DIAGNOSIS_CONTRACT_INVALID and include the exact invalid rule in sections_to_fix. docs/behavior/blocked_items.md is historical context only and must not be treated as the current run verdict.
|
|
562
|
+
completion:
|
|
563
|
+
- command_success: "node -e \"const fs=require('node:fs');const crypto=require('node:crypto');const path=require('node:path');const mod=process.env.ARGUMENTS||'$ARGUMENTS';const allowed=['BUG','UNIMPLEMENTED','INFRA','REMOVED','TEST_BUG'];const action=['BUG','UNIMPLEMENTED','INFRA','TEST_BUG'];const fail=m=>{throw new Error(m)};const same=(a,b)=>Array.isArray(a)&&a.length===b.length&&a.every((v,i)=>v===b[i]);const snapSame=(a,b)=>a&&b&&a.baseline_source_sha256===b.baseline_source_sha256&&a.diagnosis_artifact_sha256===b.diagnosis_artifact_sha256&&a.observed_failure_count===b.observed_failure_count&&a.work_item_count===b.work_item_count&&same(a.allowed_kinds,b.allowed_kinds);const root=process.cwd();const inside=p=>{if(typeof p!=='string'||!p||path.isAbsolute(p))fail('path_invalid');const r=path.resolve(root,p);if(r!==root&&!r.startsWith(root+path.sep))fail('path_escape');return r};const readJson=p=>JSON.parse(fs.readFileSync(inside(p),'utf8'));if(!/^[A-Za-z0-9_-]+$/.test(mod))fail('module');const contractPath='.dna/specs/diagnosis-'+mod+'.contract.json';const reviewPath='.dna/specs/diagnosis-'+mod+'.review.json';const diagnosisPath='.dna/specs/diagnosis-'+mod+'.md';const data=readJson(contractPath);if(data.contract_version!=='diagnosis-contract/v1')fail('contract_version');if(data.module!==mod)fail('module_mismatch');if(data.diagnosis_artifact!==diagnosisPath)fail('diagnosis_artifact_path');if(!same(data.allowed_kinds,allowed))fail('allowed_kinds');const observed=Array.isArray(data.observed_failures)?data.observed_failures:fail('observed_failures');const work=Array.isArray(data.work_items)?data.work_items:fail('work_items');if(!observed.length)fail('empty_observed_failures');if(!work.length)fail('empty_work_items');const ids=new Set(observed.map(x=>x&&x.id).filter(Boolean));if(ids.size!==observed.length)fail('observed_failure_ids');const failureById=new Map();for(const f of observed){if(typeof f.test_name!=='string'||!f.test_name.trim())fail('observed_failure_test_name');if(typeof f.symptom!=='string'||!f.symptom.trim())fail('observed_failure_symptom');if(!Array.isArray(f.evidence_paths)||!f.evidence_paths.length)fail('observed_failure_evidence');for(const p of f.evidence_paths)inside(p);if(f.kind!==undefined)fail('observed_failure_kind_forbidden');failureById.set(f.id,f)}const mapped=new Set();for(const item of work){if(!allowed.includes(item.kind))fail('invalid_kind');if(item.kind==='PLACEHOLDER_CONTRACT')fail('placeholder_kind');if(!Array.isArray(item.observed_failure_ids)||!item.observed_failure_ids.length)fail('work_item_observed_failure_ids');if(!Array.isArray(item.evidence_paths)||!item.evidence_paths.length)fail('work_item_evidence');for(const p of item.evidence_paths)inside(p);if(action.includes(item.kind)){if(!Array.isArray(item.v1_evidence_paths)||!item.v1_evidence_paths.length)fail('work_item_v1_evidence');if(!Array.isArray(item.v2_evidence_paths)||!item.v2_evidence_paths.length)fail('work_item_v2_evidence');for(const p of item.v1_evidence_paths)inside(p);for(const p of item.v2_evidence_paths)inside(p)}for(const id of item.observed_failure_ids){const f=failureById.get(id);if(!f)fail('unknown_observed_failure_id');if(action.includes(item.kind)&&(!f.test_name||!String(f.test_name).trim()))fail('action_test_evidence');mapped.add(id)}}if(mapped.size!==ids.size)fail('unmapped_observed_failures');for(const f of observed.filter(x=>String(x.symptom).includes('PLACEHOLDER_CONTRACT'))){const kinds=new Set(work.filter(i=>i.observed_failure_ids.includes(f.id)).map(i=>i.kind));if(![...kinds].every(k=>k==='TEST_BUG'||k==='UNIMPLEMENTED')||kinds.size===0)fail('placeholder_mapping')}const counts=data.baseline&&data.baseline.counts;if(!data.baseline||!data.baseline.source_path||!counts)fail('baseline');inside(data.baseline.source_path);for(const k of ['failed','skipped','hung']){if(!Number.isInteger(counts[k])||counts[k]<0)fail('baseline_count_'+k)}const total=counts.failed+counts.skipped+counts.hung;if(total!==observed.length)fail('baseline_count_mismatch');const summary=data.summary_counts||{};for(const kind of allowed){if(!Number.isInteger(summary[kind])||summary[kind]!==work.filter(x=>x.kind===kind).length)fail('summary_count_'+kind)}const sha=p=>crypto.createHash('sha256').update(fs.readFileSync(inside(p))).digest('hex');const snap=data.contract_snapshot||{};if(snap.observed_failure_count!==observed.length||snap.work_item_count!==work.length||!same(snap.allowed_kinds,allowed))fail('contract_snapshot');if(snap.baseline_source_sha256!==sha(data.baseline.source_path))fail('baseline_sha256');if(snap.diagnosis_artifact_sha256!==sha(diagnosisPath))fail('diagnosis_sha256');if(!data.staleness||data.staleness.is_stale!==false)fail('stale_contract');const review=readJson(reviewPath);if(review.contract_version!=='diagnosis-contract-review/v1')fail('review_contract_version');if(review.contract_valid!==true)fail('review_contract_valid');if(review.verdict!=='APPROVE')fail('review_verdict');if(review.contract_artifact_reviewed!==contractPath)fail('review_contract_artifact_path');if(review.artifact_reviewed!==diagnosisPath)fail('review_artifact_path');if(!snapSame(review.contract_snapshot,snap))fail('review_contract_snapshot');if(!review.updated_at||Number.isNaN(Date.parse(review.updated_at)))fail('review_updated_at');if(!Array.isArray(review.evidence_paths)||!review.evidence_paths.length)fail('review_evidence_paths');for(const p of review.evidence_paths)inside(p);const cm=fs.statSync(inside(contractPath)).mtimeMs;const dm=fs.statSync(inside(diagnosisPath)).mtimeMs;const rm=fs.statSync(inside(reviewPath)).mtimeMs;if(rm<cm||rm<dm)fail('review_stale');\""
|
|
505
564
|
handoff:
|
|
506
565
|
consumes:
|
|
507
566
|
- type: file
|
|
508
567
|
path: ".dna/specs/diagnosis-$ARGUMENTS.md"
|
|
509
568
|
description: "Diagnosis spec from analyzer"
|
|
569
|
+
- type: file
|
|
570
|
+
path: ".dna/specs/diagnosis-$ARGUMENTS.contract.json"
|
|
571
|
+
description: "Diagnosis contract sidecar from analyzer"
|
|
510
572
|
produces:
|
|
511
573
|
- type: file
|
|
512
574
|
path: ".dna/specs/diagnosis-$ARGUMENTS.review.json"
|
|
513
|
-
description: "Durable diagnosis review verdict (APPROVE or REQUEST_REANALYSIS)"
|
|
575
|
+
description: "Durable diagnosis review verdict with independent contract validation (APPROVE or REQUEST_REANALYSIS)"
|
|
514
576
|
- type: summary
|
|
515
577
|
required: false
|
|
516
578
|
description: "Review verdict (APPROVE or REQUEST_REANALYSIS)"
|
|
@@ -528,6 +590,9 @@ workflows:
|
|
|
528
590
|
|
|
529
591
|
Before editing, read .dna/specs/diagnosis-$ARGUMENTS.review.json.
|
|
530
592
|
- If it is missing, invalid, or verdict is not APPROVE, stop immediately; do not edit.
|
|
593
|
+
- Also read .dna/specs/diagnosis-$ARGUMENTS.contract.json and perform the same preflight used by the diagnosis review completion validator before any Edit.
|
|
594
|
+
- If the sidecar is missing, invalid, stale, has unmapped observed_failures, bad counts, bad snapshot, unsupported kinds, or review contract_valid is not true, stop before edits with DIAGNOSIS_CONTRACT_INVALID.
|
|
595
|
+
- If stopping for DIAGNOSIS_CONTRACT_INVALID, write/update .dna/fix-progress/$ARGUMENTS.json with verdict BLOCKED, failure_reason DIAGNOSIS_CONTRACT_INVALID, diagnosis_invalid true, and evidence_paths naming the sidecar/review problem; also update the markdown progress artifact.
|
|
531
596
|
- Manual fix invocations must obey this same gate.
|
|
532
597
|
- If verdict is REQUEST_REANALYSIS, run diagnosis reanalysis first instead of fixing.
|
|
533
598
|
|
|
@@ -545,6 +610,7 @@ workflows:
|
|
|
545
610
|
|
|
546
611
|
Otherwise process issues in priority order: INFRA → BUG → TEST_BUG → UNIMPLEMENTED.
|
|
547
612
|
REMOVED is always skipped unless the user explicitly approved handling it.
|
|
613
|
+
REMOVED must never be auto-executed from diagnosis contract membership alone.
|
|
548
614
|
|
|
549
615
|
For each issue:
|
|
550
616
|
- BUG: read v1 file, edit v2 (use Riverpod per refactoring-workflow-v2.md)
|
|
@@ -565,6 +631,9 @@ workflows:
|
|
|
565
631
|
- type: file
|
|
566
632
|
path: ".dna/specs/diagnosis-$ARGUMENTS.md"
|
|
567
633
|
description: "Diagnosis spec"
|
|
634
|
+
- type: file
|
|
635
|
+
path: ".dna/specs/diagnosis-$ARGUMENTS.contract.json"
|
|
636
|
+
description: "Validated diagnosis contract sidecar"
|
|
568
637
|
- type: file
|
|
569
638
|
path: ".dna/specs/diagnosis-$ARGUMENTS.review.json"
|
|
570
639
|
description: "Approved diagnosis review gate"
|
|
@@ -663,6 +732,7 @@ workflows:
|
|
|
663
732
|
- review REQUEST_CHANGES without safe actionable scope -> Verdict: BLOCKED, Failure reason: BLOCKED_BY_REVIEW.
|
|
664
733
|
- 0 failed + remaining UNIMPLEMENTED skipped > 0 -> Verdict: CONTINUE, Module complete: no, Next Round Focus: next diagnosis-backed UNIMPLEMENTED test/evidence path.
|
|
665
734
|
- progress contradicts diagnosis/test evidence -> Verdict: BLOCKED, Failure reason: PROGRESS_DIAGNOSIS_CONFLICT.
|
|
735
|
+
- diagnosis contract preflight failed -> Verdict: BLOCKED, Failure reason: DIAGNOSIS_CONTRACT_INVALID, diagnosis_invalid: true.
|
|
666
736
|
- new red caused by current round -> Verdict: REQUEST_CHANGES.
|
|
667
737
|
- new red appears to be newly exposed baseline gap AND remaining count decreases -> Verdict: CONTINUE.
|
|
668
738
|
- ambiguous new red -> Verdict: BLOCKED, Failure reason: AMBIGUOUS_NEW_RED.
|
|
@@ -677,6 +747,7 @@ workflows:
|
|
|
677
747
|
- Blocked: K tests skipped for TEST_BUG / INFRA / REMOVED / user-approved blocked reasons
|
|
678
748
|
- Remaining: R tests still failing or diagnosis-backed UNIMPLEMENTED skipped
|
|
679
749
|
- Remaining UNIMPLEMENTED skipped: U diagnosis-backed skipped tests still remaining
|
|
750
|
+
- Diagnosis invalid: true/false
|
|
680
751
|
- Failure reason: why progress stopped, if any
|
|
681
752
|
- Next round focus: the priority/classification/test file to continue with, if verdict is CONTINUE or REQUEST_CHANGES
|
|
682
753
|
- Rollback decision: say whether the current round commit should be reverted or fixed forward, with one plain-language reason
|
|
@@ -712,6 +783,7 @@ workflows:
|
|
|
712
783
|
"blocked_count": 0,
|
|
713
784
|
"remaining_count": 0,
|
|
714
785
|
"remaining_unimplemented_skipped_count": 0,
|
|
786
|
+
"diagnosis_invalid": false,
|
|
715
787
|
"next_round_focus": "",
|
|
716
788
|
"failure_reason": "",
|
|
717
789
|
"last_fix_commit": null,
|
|
@@ -732,6 +804,7 @@ workflows:
|
|
|
732
804
|
- actionability, scope, and risk are required and must mirror the review verdict when review is REQUEST_CHANGES; use none/none/none when review is APPROVE.
|
|
733
805
|
- module_complete is a required boolean and must match the Markdown Module complete value.
|
|
734
806
|
- fixed_count, new_red_count, blocked_count, remaining_count, and remaining_unimplemented_skipped_count are required non-negative integers.
|
|
807
|
+
- diagnosis_invalid is a required boolean; set true only when failure_reason is DIAGNOSIS_CONTRACT_INVALID.
|
|
735
808
|
- next_round_focus is required and non-empty when verdict is CONTINUE or REQUEST_CHANGES; empty string is allowed when verdict is PASS.
|
|
736
809
|
- failure_reason is required and non-empty when verdict is BLOCKED or REQUEST_CHANGES; empty string is allowed when verdict is PASS or CONTINUE. When review REQUEST_CHANGES is normalized to verdict CONTINUE, keep reviewer failure details in Review Issues To Address First instead of failure_reason.
|
|
737
810
|
- last_fix_commit is the commit hash string if available, otherwise null.
|
|
@@ -757,6 +830,9 @@ workflows:
|
|
|
757
830
|
- type: file
|
|
758
831
|
path: ".dna/specs/diagnosis-$ARGUMENTS.md"
|
|
759
832
|
description: "Diagnosis spec"
|
|
833
|
+
- type: file
|
|
834
|
+
path: ".dna/specs/diagnosis-$ARGUMENTS.contract.json"
|
|
835
|
+
description: "Diagnosis contract sidecar"
|
|
760
836
|
produces:
|
|
761
837
|
- type: file
|
|
762
838
|
path: ".dna/fix-progress/$ARGUMENTS.md"
|