intentdna 1.9.0 → 1.9.3
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/README.md +227 -87
- package/dist/cli/commands/run-lifecycle.d.ts +61 -11
- package/dist/cli/commands/run-lifecycle.js +184 -50
- package/dist/cli/commands/run-observer.d.ts +15 -0
- package/dist/cli/commands/run-observer.js +210 -0
- package/dist/cli/commands/run.d.ts +16 -20
- package/dist/cli/commands/run.js +461 -659
- package/dist/cli/commands/sync.js +16 -12
- package/dist/cli/index.js +14 -5
- package/dist/compiler/compile.d.ts +1 -0
- package/dist/compiler/compile.js +1 -1
- package/dist/compiler/controller.d.ts +16 -0
- package/dist/compiler/controller.js +693 -0
- package/dist/compiler/input-resolver.d.ts +2 -0
- package/dist/compiler/input-resolver.js +22 -6
- package/dist/hooks/cli.d.ts +45 -0
- package/dist/hooks/cli.js +263 -31
- package/dist/hooks/host-event.d.ts +57 -0
- package/dist/hooks/host-event.js +187 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hooks/protocol.d.ts +7 -0
- package/dist/hooks/schema.d.ts +1 -0
- package/dist/hooks/schema.js +89 -1
- package/dist/hooks/state.d.ts +23 -1
- package/dist/hooks/state.js +9 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/mcp/index.js +2 -0
- package/dist/mcp/tools-run.d.ts +4 -0
- package/dist/mcp/tools-run.js +419 -0
- package/dist/mcp/tools-state.d.ts +1 -1
- package/dist/mcp/tools-state.js +13 -241
- package/dist/runtime/artifact-store.d.ts +146 -0
- package/dist/runtime/artifact-store.js +1436 -0
- package/dist/runtime/canonical-attempt-outcome.d.ts +12 -0
- package/dist/runtime/canonical-attempt-outcome.js +195 -0
- package/dist/runtime/canonical-json.d.ts +10 -0
- package/dist/runtime/canonical-json.js +105 -0
- package/dist/runtime/canonical-run-application.d.ts +93 -0
- package/dist/runtime/canonical-run-application.js +229 -0
- package/dist/runtime/canonical-run-service.d.ts +454 -0
- package/dist/runtime/canonical-run-service.js +4760 -0
- package/dist/runtime/canonical-runtime-composition.d.ts +22 -0
- package/dist/runtime/canonical-runtime-composition.js +34 -0
- package/dist/runtime/canonical-runtime-projection.d.ts +3 -0
- package/dist/runtime/canonical-runtime-projection.js +4 -0
- package/dist/runtime/canonical-target-compiler.d.ts +46 -0
- package/dist/runtime/canonical-target-compiler.js +514 -0
- package/dist/runtime/claude-agent-identity.d.ts +2 -0
- package/dist/runtime/claude-agent-identity.js +21 -0
- package/dist/runtime/executable-run-plan.d.ts +328 -0
- package/dist/runtime/executable-run-plan.js +1485 -0
- package/dist/runtime/execution-authority.d.ts +83 -0
- package/dist/runtime/execution-authority.js +98 -0
- package/dist/runtime/handoff-resolver.js +4 -1
- package/dist/runtime/harness-pull-adapter.d.ts +146 -0
- package/dist/runtime/harness-pull-adapter.js +315 -0
- package/dist/runtime/index.d.ts +39 -14
- package/dist/runtime/index.js +29 -7
- package/dist/runtime/local-execution-authority.d.ts +194 -0
- package/dist/runtime/local-execution-authority.js +2897 -0
- package/dist/runtime/local-execution-reconciliation.d.ts +20 -0
- package/dist/runtime/local-execution-reconciliation.js +107 -0
- package/dist/runtime/local-execution-supervisor-script.d.ts +7 -0
- package/dist/runtime/local-execution-supervisor-script.js +793 -0
- package/dist/runtime/local-provider-sandbox.d.ts +42 -0
- package/dist/runtime/local-provider-sandbox.js +154 -0
- package/dist/runtime/local-verifier-execution.d.ts +24 -0
- package/dist/runtime/local-verifier-execution.js +290 -0
- package/dist/runtime/local-windows-execution-supervisor-script.d.ts +6 -0
- package/dist/runtime/local-windows-execution-supervisor-script.js +788 -0
- package/dist/runtime/plan-store.d.ts +30 -0
- package/dist/runtime/plan-store.js +231 -0
- package/dist/runtime/process-tree.d.ts +9 -0
- package/dist/runtime/process-tree.js +97 -24
- package/dist/runtime/providers/codex.js +2 -2
- package/dist/runtime/push-driver.d.ts +184 -0
- package/dist/runtime/push-driver.js +1002 -0
- package/dist/runtime/result-store.d.ts +34 -3
- package/dist/runtime/result-store.js +1073 -2
- package/dist/runtime/run-binding.d.ts +68 -0
- package/dist/runtime/run-binding.js +278 -0
- package/dist/runtime/run-contracts.d.ts +575 -0
- package/dist/runtime/run-contracts.js +58 -7
- package/dist/runtime/run-controller.d.ts +1 -6
- package/dist/runtime/run-controller.js +0 -41
- package/dist/runtime/run-store.d.ts +85 -7
- package/dist/runtime/run-store.js +2528 -186
- package/dist/runtime/skill-adapter.d.ts +18 -7
- package/dist/runtime/skill-adapter.js +100 -742
- package/dist/runtime/structured-output-validator.d.ts +44 -0
- package/dist/runtime/structured-output-validator.js +171 -0
- package/dist/runtime/verifier-command-binding.d.ts +22 -0
- package/dist/runtime/verifier-command-binding.js +162 -0
- package/dist/runtime/verifier.d.ts +46 -6
- package/dist/runtime/verifier.js +355 -53
- package/dist/runtime/windows-job-keeper.d.ts +47 -0
- package/dist/runtime/windows-job-keeper.js +231 -0
- package/dist/runtime/worker-executor.d.ts +17 -1
- package/dist/runtime/worker-executor.js +26 -5
- package/dist/runtime/workflow-plan-adapter.d.ts +19 -0
- package/dist/runtime/workflow-plan-adapter.js +502 -6
- package/dist/runtime/workflow-runtime-manifest.d.ts +1 -0
- package/dist/runtime/workflow-runtime-manifest.js +5 -0
- package/dist/runtime/workspace-isolation.d.ts +29 -2
- package/dist/runtime/workspace-isolation.js +488 -11
- package/dist/runtime/workspace-observation.d.ts +44 -0
- package/dist/runtime/workspace-observation.js +216 -0
- package/dist/schema/controller-registry.d.ts +0 -7
- package/dist/schema/controller-registry.js +0 -8
- package/dist/schema/types.d.ts +84 -10
- package/dist/schema/types.js +2 -0
- package/dist/schema/validate.js +151 -1
- package/dist/schema/validators/controllers.d.ts +1 -1
- package/dist/schema/validators/controllers.js +126 -41
- package/dist/schema/workflow-authoring-contract.js +2 -0
- package/dist/schema/yaml-parser.js +1 -1
- package/dist/templates/flutter-rewrite-new.dna.yaml +1 -0
- package/dist/templates/flutter-rewrite.dna.yaml +87 -60
- package/dist/templates/safe-refactoring.dna.yaml +1 -0
- package/native/windows-job-keeper/README.md +80 -0
- package/native/windows-job-keeper/bin/aarch64/intentdna-windows-job-keeper.exe +0 -0
- package/native/windows-job-keeper/bin/x86_64/intentdna-windows-job-keeper.exe +0 -0
- package/native/windows-job-keeper/keeper.c +979 -0
- package/native/windows-job-keeper/manifest.json +33 -0
- package/package.json +6 -2
- package/scripts/build-windows-job-keeper.mjs +172 -0
- package/scripts/verify-windows-job-keeper.mjs +184 -0
|
@@ -170,9 +170,9 @@ verifier_policy:
|
|
|
170
170
|
allow_commands:
|
|
171
171
|
- "flutter analyze {{v2_path}}/core"
|
|
172
172
|
- "node -e \"const fs=require('node:fs'); const cp=require('node:child_process'); const path=require('node:path'); const fail=m=>{console.error('INTENTDNA_DIAGNOSTIC:core_align_'+m);process.exit(1)}; const root=fs.realpathSync(process.cwd()); const inside=p=>{if(typeof p!=='string'||!p||path.isAbsolute(p))fail('path');const r=path.resolve(root,p);if(r!==root&&!r.startsWith(root+path.sep))fail('path');let stat=null;try{stat=fs.lstatSync(r)}catch(e){if(!e||e.code!=='ENOENT')fail('path_unreadable')}if(stat){if(stat.isSymbolicLink())fail('path_symlink');const real=fs.realpathSync(r);if(real!==root&&!real.startsWith(root+path.sep))fail('path')}return r}; const rel=p=>path.relative(root,inside(p)).split(path.sep).join('/')||'.'; const under=(p,base)=>p===base||p.startsWith(base+'/'); const reviewPath='.dna/reviews/core-align-review.json'; if(!fs.existsSync(inside(reviewPath)))fail('review_missing'); const d=JSON.parse(fs.readFileSync(inside(reviewPath),'utf8')); const head=cp.execFileSync('git',['rev-parse','HEAD'],{encoding:'utf8'}).trim(); if(d.reviewed_commit!==head||d.git_head!==head)fail('commit'); if(d.verdict!=='APPROVE')fail('review_verdict'); if(d.production_default_wiring_verified!==true)fail('production_default_wiring'); if(d.analyzer_passed!==true)fail('analyzer'); if(d.requires_non_core_changes!==false)fail('non_core_changes'); if(!Array.isArray(d.missing_paths)||d.missing_paths.length)fail('missing_paths'); if(typeof d.summary!=='string'||!d.summary.trim())fail('summary'); const updated=Date.parse(d.updated_at); const commitTime=Number(cp.execFileSync('git',['show','-s','--format=%ct',head],{encoding:'utf8'}).trim())*1000; const reviewMtime=fs.statSync(inside(reviewPath)).mtimeMs; if(!Number.isFinite(updated)||updated<commitTime||Math.abs(reviewMtime-updated)>300000)fail('review_stale'); const core=rel('{{v2_path}}/core'); if(!Array.isArray(d.evidence_paths)||!d.evidence_paths.length)fail('evidence'); for(const p of d.evidence_paths){const n=rel(p);const r=inside(p);if(!under(n,core)||!fs.existsSync(r)||!fs.statSync(r).isFile())fail('evidence')} const output=cp.execFileSync('git',['diff-tree','--root','--no-commit-id','--name-only','-r',head],{encoding:'utf8'}).trim(); const changed=output?output.split(/\\r?\\n/).filter(Boolean):[]; if(!changed.length)fail('empty_commit'); for(const p of changed){const n=rel(p);if(!under(n,core))fail('non_core_commit')} const dirty=cp.execFileSync('git',['status','--porcelain','--untracked-files=all','--',core],{encoding:'utf8'}).trim(); if(dirty)fail('uncommitted_core'); \""
|
|
173
|
-
- "node -e \"const fs=require('node:fs'); const cp=require('node:child_process'); const crypto=require('node:crypto'); const path=require('node:path'); const mod=process.
|
|
174
|
-
- "node -e \"const fs=require('node:fs'); const cp=require('node:child_process'); 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=>{console.error('INTENTDNA_DIAGNOSTIC:'+m);process.exit(1)}; const same=(a,b)=>Array.isArray(a)&&a.length===b.length&&a.every((v,i)=>v===b[i]); const legacySame=(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 modern=s=>s&&typeof s.sidecar_semantic_sha256==='string'&&typeof s.diagnosis_semantic_sha256==='string'; const root=fs.realpathSync(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');let stat=null;try{stat=fs.lstatSync(r)}catch(e){if(!e||e.code!=='ENOENT')fail('path_unreadable')}if(stat){if(stat.isSymbolicLink())fail('path_symlink');const real=fs.realpathSync(r);if(real!==root&&!real.startsWith(root+path.sep))fail('path_escape')}return r}; const rel=p=>path.relative(root,inside(p)).split(path.sep).join('/')||'.'; const under=(p,base)=>p===base||p.startsWith(base+'/'); const readJson=p=>JSON.parse(fs.readFileSync(inside(p),'utf8')); const hash=v=>crypto.createHash('sha256').update(v).digest('hex'); const fileHash=p=>hash(fs.readFileSync(inside(p))); const normalizedDiagnosisHash=p=>hash(fs.readFileSync(inside(p),'utf8').replace(/\\s+/g,' ').trim()); const canon=v=>Array.isArray(v)?'['+v.map(canon).join(',')+']':v&&typeof v==='object'?'{'+Object.keys(v).sort().map(k=>JSON.stringify(k)+':'+canon(v[k])).join(',')+'}':JSON.stringify(v); 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 progressPath='.dna/fix-progress/'+mod+'.json'; const diagnosisPath='.dna/specs/diagnosis-'+mod+'.md'; const behaviorPath='docs/behavior/'+mod+'.md'; const v1Root=rel('{{v1_path}}'); const v2Root=rel('{{v2_path}}'); if(v1Root==='.'||v2Root==='.'||under(v1Root,v2Root)||under(v2Root,v1Root))fail('configured_roots'); const data=readJson(contractPath); if(data.contract_version!=='diagnosis-contract/v2')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 idOk=id=>typeof id==='string'&&/^[A-Za-z0-9][A-Za-z0-9_.:-]*$/.test(id); const ids=new Set(); for(const f of observed){if(!idOk(f&&f.id)||ids.has(f.id))fail('observed_failure_ids');ids.add(f.id)} const workIds=new Set(); for(const item of work){if(!idOk(item&&item.id)||workIds.has(item.id))fail('work_item_ids');workIds.add(item.id)} 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)rel(p);if(f.kind!==undefined)fail('observed_failure_kind_forbidden');failureById.set(f.id,f)} const mapped=new Set(); const allAllowed=new Set(); const v1Evidence=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)rel(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');const v2Evidence=new Set(item.v2_evidence_paths.map(rel));for(const p of item.v1_evidence_paths){const n=rel(p);if(!under(n,v1Root))fail('work_item_v1_evidence_root');v1Evidence.add(n)}for(const p of item.v2_evidence_paths)rel(p);if(!Array.isArray(item.allowed_change_paths)||!item.allowed_change_paths.length)fail('work_item_allowed_change_paths');const approved=new Set();for(const p of item.allowed_change_paths){if(typeof p!=='string'||['*','?','[',']'].some(c=>p.includes(c)))fail('work_item_allowed_change_path_pattern');const n=rel(p);const r=inside(p);if(under(n,v1Root))fail('work_item_allowed_change_path_v1');if(!(under(n,v2Root)||under(n,'test')||under(n,'lib')))fail('work_item_allowed_change_path_root');if(fs.existsSync(r)?!fs.statSync(r).isFile():!path.basename(n).includes('.'))fail('work_item_allowed_change_path_not_file');if(!v2Evidence.has(n))fail('work_item_allowed_change_path_not_evidence');if(approved.has(n))fail('work_item_allowed_change_path_duplicate');approved.add(n);allAllowed.add(n)}}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'); for(const k of ['failed','skipped','hung']){if(!Number.isInteger(counts[k])||counts[k]<0)fail('baseline_count_'+k)} if(counts.failed+counts.skipped+counts.hung!==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 snap=data.contract_snapshot||{}; const baselineHash=fileHash(data.baseline.source_path); const diagnosisHash=fileHash(diagnosisPath); 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!==baselineHash)fail('baseline_sha256'); if(snap.diagnosis_artifact_sha256!==diagnosisHash)fail('diagnosis_sha256'); const stale=data.staleness; if(!stale||stale.is_stale!==false)fail('stale_contract'); const source=(entry,p,n,expected)=>{if(!entry||entry.path!==p)fail('staleness_'+n+'_path');if(entry.sha256!==expected)fail('staleness_'+n+'_sha256')}; source(stale.behavior_doc,behaviorPath,'behavior_doc',fileHash(behaviorPath)); source(stale.baseline,data.baseline.source_path,'baseline',baselineHash); source(stale.diagnosis_artifact,diagnosisPath,'diagnosis_artifact',diagnosisHash); const expectedSources=[...new Set(work.filter(i=>action.includes(i.kind)).flatMap(i=>[...i.v1_evidence_paths,...i.v2_evidence_paths]).map(rel))].sort(); if(!Array.isArray(stale.source_files)||stale.source_files.length!==expectedSources.length)fail('staleness_source_files'); let relaxed=false; let head=''; if(fs.existsSync(inside(progressPath))){const progressMtime=fs.statSync(inside(progressPath)).mtimeMs;const artifactMtime=Math.max(fs.statSync(inside(contractPath)).mtimeMs,fs.statSync(inside(diagnosisPath)).mtimeMs,fs.existsSync(inside(reviewPath))?fs.statSync(inside(reviewPath)).mtimeMs:0);if(progressMtime>=artifactMtime){const progress=readJson(progressPath);head=cp.execFileSync('git',['rev-parse','HEAD'],{encoding:'utf8'}).trim();if(progress.module!==mod||progress.verdict!=='CONTINUE'||progress.module_complete!==false||progress.diagnosis_invalid!==false)fail('progress_state');if(progress.diagnosis_artifact!==diagnosisPath||progress.review_artifact!=='.dna/reviews/'+mod+'-review.json')fail('progress_artifacts');if(progress.git_head!==head||progress.last_fix_commit!==head)fail('progress_commit');if(!Number.isInteger(progress.round)||progress.round<1||typeof progress.run_id!=='string'||!progress.run_id.trim())fail('progress_provenance');const progressTime=Date.parse(progress.updated_at);if(!Number.isFinite(progressTime)||progressTime+300000<artifactMtime)fail('progress_updated_at');relaxed=true}} const headState=p=>{try{const body=cp.execFileSync('git',['show',head+':'+p],{encoding:null,stdio:['ignore','pipe','ignore']});return {exists:true,sha256:hash(body)}}catch{return {exists:false,sha256:null}}}; const seenSources=new Set(); for(let i=0;i<expectedSources.length;i++){const p=expectedSources[i];const entry=stale.source_files[i];if(entry&&seenSources.has(entry.path))fail('staleness_source_file_duplicate');if(!entry||entry.path!==p)fail('staleness_source_file_path');seenSources.add(entry.path);if(typeof entry.exists!=='boolean')fail('staleness_source_file_exists');if(entry.exists?(typeof entry.sha256!=='string'||!/^[a-f0-9]{64}$/.test(entry.sha256)):entry.sha256!==null)fail('staleness_source_file_snapshot');if(v1Evidence.has(p)&&entry.exists!==true)fail('staleness_v1_source_missing');const r=inside(p);const currentExists=fs.existsSync(r);if(currentExists&&!fs.statSync(r).isFile())fail('staleness_source_file_not_file');const current={exists:currentExists,sha256:currentExists?fileHash(p):null};if(relaxed&&allAllowed.has(p)){const committed=headState(p);if(current.exists!==committed.exists||current.sha256!==committed.sha256)fail('staleness_source_file_uncommitted')}else if(current.exists!==entry.exists||current.sha256!==entry.sha256)fail('staleness_source_file_sha256')} const semantic=JSON.parse(JSON.stringify(data)); delete semantic.contract_snapshot.sidecar_semantic_sha256; delete semantic.contract_snapshot.diagnosis_artifact_sha256; delete semantic.staleness.diagnosis_artifact; const sidecarSemanticHash=hash(canon(semantic)); const diagnosisSemanticHash=normalizedDiagnosisHash(diagnosisPath); if(snap.sidecar_semantic_sha256!==sidecarSemanticHash||snap.diagnosis_semantic_sha256!==diagnosisSemanticHash)fail('contract_snapshot_semantic'); if(fs.existsSync(inside(reviewPath))){const prev=readJson(reviewPath);if(prev.verdict==='REQUEST_REANALYSIS'){const ps=prev.contract_snapshot||{};const unchanged=modern(ps)?ps.sidecar_semantic_sha256===sidecarSemanticHash&&ps.diagnosis_semantic_sha256===diagnosisSemanticHash:legacySame(ps,snap);if(unchanged)fail('reanalysis_unchanged');const cm=fs.statSync(inside(contractPath)).mtimeMs;const dm=fs.statSync(inside(diagnosisPath)).mtimeMs;const rm=fs.statSync(inside(reviewPath)).mtimeMs;if(cm<=rm||dm<=rm)fail('reanalysis_not_rewritten')}} \""
|
|
175
|
-
- "node -e \"const fs=require('node:fs'); const path=require('node:path'); const mod=process.
|
|
173
|
+
- "node -e \"const fs=require('node:fs'); const cp=require('node:child_process'); const crypto=require('node:crypto'); const path=require('node:path'); const mod=process.argv[1]; const fail=m=>{console.error('INTENTDNA_DIAGNOSTIC:fix_scope_'+m);process.exit(1)}; const root=fs.realpathSync(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');let stat=null;try{stat=fs.lstatSync(r)}catch(e){if(!e||e.code!=='ENOENT')fail('path_unreadable')}if(stat){if(stat.isSymbolicLink())fail('path_symlink');const real=fs.realpathSync(r);if(real!==root&&!real.startsWith(root+path.sep))fail('path_escape')}return r}; const rel=p=>path.relative(root,inside(p)).split(path.sep).join('/')||'.'; const under=(p,base)=>p===base||p.startsWith(base+'/'); const hash=v=>crypto.createHash('sha256').update(v).digest('hex'); const canon=v=>Array.isArray(v)?'['+v.map(canon).join(',')+']':v&&typeof v==='object'?'{'+Object.keys(v).sort().map(k=>JSON.stringify(k)+':'+canon(v[k])).join(',')+'}':JSON.stringify(v); const same=(a,b)=>Array.isArray(a)&&Array.isArray(b)&&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.sidecar_semantic_sha256===b.sidecar_semantic_sha256&&a.diagnosis_semantic_sha256===b.diagnosis_semantic_sha256&&a.observed_failure_count===b.observed_failure_count&&a.work_item_count===b.work_item_count&&same(a.allowed_kinds,b.allowed_kinds); 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=JSON.parse(fs.readFileSync(inside(contractPath),'utf8')); if(data.contract_version!=='diagnosis-contract/v2'||data.module!==mod||data.diagnosis_artifact!==diagnosisPath)fail('contract'); const snap=data.contract_snapshot||{}; const semantic=JSON.parse(JSON.stringify(data)); delete semantic.contract_snapshot.sidecar_semantic_sha256; delete semantic.contract_snapshot.diagnosis_artifact_sha256; delete semantic.staleness.diagnosis_artifact; if(snap.sidecar_semantic_sha256!==hash(canon(semantic)))fail('contract_snapshot'); const review=JSON.parse(fs.readFileSync(inside(reviewPath),'utf8')); if(review.contract_version!=='diagnosis-contract-review/v1'||review.contract_valid!==true||review.verdict!=='APPROVE')fail('review'); if(review.contract_artifact_reviewed!==contractPath||review.artifact_reviewed!==diagnosisPath||!snapSame(review.contract_snapshot,snap))fail('review_snapshot'); if(!Array.isArray(review.evidence_paths)||!review.evidence_paths.length)fail('review_evidence'); for(const p of review.evidence_paths){const r=inside(p);if(!fs.existsSync(r)||!fs.statSync(r).isFile())fail('review_evidence')} const cm=fs.statSync(inside(contractPath)).mtimeMs; const rm=fs.statSync(inside(reviewPath)).mtimeMs; const updated=Date.parse(review.updated_at); if(!Number.isFinite(updated)||rm<cm||updated+300000<cm||Math.abs(rm-updated)>300000)fail('review_stale'); const v1=rel(process.argv[2]); const v2=rel(process.argv[3]); const action=new Set(['BUG','UNIMPLEMENTED','INFRA','TEST_BUG']); const allowed=new Set(); for(const item of data.work_items||[]){if(!action.has(item.kind))continue;const evidence=new Set((item.v2_evidence_paths||[]).map(rel));for(const p of item.allowed_change_paths||[]){if(typeof p!=='string'||['*','?','[',']'].some(c=>p.includes(c)))fail('allowed_path_pattern');const n=rel(p);const r=inside(p);if(under(n,v1))fail('v1_change');if(!(under(n,v2)||under(n,'test')||under(n,'lib')))fail('allowed_path_root');if(fs.existsSync(r)?!fs.statSync(r).isFile():!path.basename(n).includes('.'))fail('allowed_path_not_file');if(!evidence.has(n))fail('allowed_path_not_evidence');allowed.add(n)}} if(!allowed.size)fail('allowed_paths'); const head=cp.execFileSync('git',['rev-parse','HEAD'],{encoding:'utf8'}).trim(); const output=cp.execFileSync('git',['diff-tree','--root','--no-commit-id','--name-only','-r',head],{encoding:'utf8'}).trim(); const changed=output?output.split(/\\r?\\n/).filter(Boolean):[]; if(!changed.length)fail('empty_commit'); for(const p of changed){const n=rel(p);if(under(n,v1))fail('v1_change');if(!allowed.has(n))fail('outside_diagnosis')} const dirty=cp.execFileSync('git',['status','--porcelain','--untracked-files=all','--',...allowed],{encoding:'utf8'}).trim(); if(dirty)fail('uncommitted_allowed_path'); \" \"$ARGUMENTS\" \"{{v1_path}}\" \"{{v2_path}}\""
|
|
174
|
+
- "node -e \"const fs=require('node:fs'); const cp=require('node:child_process'); const crypto=require('node:crypto'); const path=require('node:path'); const mod=process.argv[1]; const allowed=['BUG','UNIMPLEMENTED','INFRA','REMOVED','TEST_BUG']; const action=['BUG','UNIMPLEMENTED','INFRA','TEST_BUG']; const fail=m=>{console.error('INTENTDNA_DIAGNOSTIC:'+m);process.exit(1)}; const same=(a,b)=>Array.isArray(a)&&a.length===b.length&&a.every((v,i)=>v===b[i]); const legacySame=(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 modern=s=>s&&typeof s.sidecar_semantic_sha256==='string'&&typeof s.diagnosis_semantic_sha256==='string'; const root=fs.realpathSync(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');let stat=null;try{stat=fs.lstatSync(r)}catch(e){if(!e||e.code!=='ENOENT')fail('path_unreadable')}if(stat){if(stat.isSymbolicLink())fail('path_symlink');const real=fs.realpathSync(r);if(real!==root&&!real.startsWith(root+path.sep))fail('path_escape')}return r}; const rel=p=>path.relative(root,inside(p)).split(path.sep).join('/')||'.'; const under=(p,base)=>p===base||p.startsWith(base+'/'); const readJson=p=>JSON.parse(fs.readFileSync(inside(p),'utf8')); const hash=v=>crypto.createHash('sha256').update(v).digest('hex'); const fileHash=p=>hash(fs.readFileSync(inside(p))); const normalizedDiagnosisHash=p=>hash(fs.readFileSync(inside(p),'utf8').replace(/\\s+/g,' ').trim()); const canon=v=>Array.isArray(v)?'['+v.map(canon).join(',')+']':v&&typeof v==='object'?'{'+Object.keys(v).sort().map(k=>JSON.stringify(k)+':'+canon(v[k])).join(',')+'}':JSON.stringify(v); 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 progressPath='.dna/fix-progress/'+mod+'.json'; const diagnosisPath='.dna/specs/diagnosis-'+mod+'.md'; const behaviorPath='docs/behavior/'+mod+'.md'; const v1Root=rel(process.argv[2]); const v2Root=rel(process.argv[3]); if(v1Root==='.'||v2Root==='.'||under(v1Root,v2Root)||under(v2Root,v1Root))fail('configured_roots'); const data=readJson(contractPath); if(data.contract_version!=='diagnosis-contract/v2')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 idOk=id=>typeof id==='string'&&/^[A-Za-z0-9][A-Za-z0-9_.:-]*$/.test(id); const ids=new Set(); for(const f of observed){if(!idOk(f&&f.id)||ids.has(f.id))fail('observed_failure_ids');ids.add(f.id)} const workIds=new Set(); for(const item of work){if(!idOk(item&&item.id)||workIds.has(item.id))fail('work_item_ids');workIds.add(item.id)} 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)rel(p);if(f.kind!==undefined)fail('observed_failure_kind_forbidden');failureById.set(f.id,f)} const mapped=new Set(); const allAllowed=new Set(); const v1Evidence=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)rel(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');const v2Evidence=new Set(item.v2_evidence_paths.map(rel));for(const p of item.v1_evidence_paths){const n=rel(p);if(!under(n,v1Root))fail('work_item_v1_evidence_root');v1Evidence.add(n)}for(const p of item.v2_evidence_paths)rel(p);if(!Array.isArray(item.allowed_change_paths)||!item.allowed_change_paths.length)fail('work_item_allowed_change_paths');const approved=new Set();for(const p of item.allowed_change_paths){if(typeof p!=='string'||['*','?','[',']'].some(c=>p.includes(c)))fail('work_item_allowed_change_path_pattern');const n=rel(p);const r=inside(p);if(under(n,v1Root))fail('work_item_allowed_change_path_v1');if(!(under(n,v2Root)||under(n,'test')||under(n,'lib')))fail('work_item_allowed_change_path_root');if(fs.existsSync(r)?!fs.statSync(r).isFile():!path.basename(n).includes('.'))fail('work_item_allowed_change_path_not_file');if(!v2Evidence.has(n))fail('work_item_allowed_change_path_not_evidence');if(approved.has(n))fail('work_item_allowed_change_path_duplicate');approved.add(n);allAllowed.add(n)}}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'); for(const k of ['failed','skipped','hung']){if(!Number.isInteger(counts[k])||counts[k]<0)fail('baseline_count_'+k)} if(counts.failed+counts.skipped+counts.hung!==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 snap=data.contract_snapshot||{}; const baselineHash=fileHash(data.baseline.source_path); const diagnosisHash=fileHash(diagnosisPath); 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!==baselineHash)fail('baseline_sha256'); if(snap.diagnosis_artifact_sha256!==diagnosisHash)fail('diagnosis_sha256'); const stale=data.staleness; if(!stale||stale.is_stale!==false)fail('stale_contract'); const source=(entry,p,n,expected)=>{if(!entry||entry.path!==p)fail('staleness_'+n+'_path');if(entry.sha256!==expected)fail('staleness_'+n+'_sha256')}; source(stale.behavior_doc,behaviorPath,'behavior_doc',fileHash(behaviorPath)); source(stale.baseline,data.baseline.source_path,'baseline',baselineHash); source(stale.diagnosis_artifact,diagnosisPath,'diagnosis_artifact',diagnosisHash); const expectedSources=[...new Set(work.filter(i=>action.includes(i.kind)).flatMap(i=>[...i.v1_evidence_paths,...i.v2_evidence_paths]).map(rel))].sort(); if(!Array.isArray(stale.source_files)||stale.source_files.length!==expectedSources.length)fail('staleness_source_files'); let relaxed=false; let head=''; if(fs.existsSync(inside(progressPath))){const progressMtime=fs.statSync(inside(progressPath)).mtimeMs;const artifactMtime=Math.max(fs.statSync(inside(contractPath)).mtimeMs,fs.statSync(inside(diagnosisPath)).mtimeMs,fs.existsSync(inside(reviewPath))?fs.statSync(inside(reviewPath)).mtimeMs:0);if(progressMtime>=artifactMtime){const progress=readJson(progressPath);head=cp.execFileSync('git',['rev-parse','HEAD'],{encoding:'utf8'}).trim();if(progress.module!==mod||progress.verdict!=='CONTINUE'||progress.module_complete!==false||progress.diagnosis_invalid!==false)fail('progress_state');if(progress.diagnosis_artifact!==diagnosisPath||progress.review_artifact!=='.dna/reviews/'+mod+'-review.json')fail('progress_artifacts');if(progress.git_head!==head||progress.last_fix_commit!==head)fail('progress_commit');if(!Number.isInteger(progress.round)||progress.round<1||typeof progress.run_id!=='string'||!progress.run_id.trim())fail('progress_provenance');const progressTime=Date.parse(progress.updated_at);if(!Number.isFinite(progressTime)||progressTime+300000<artifactMtime)fail('progress_updated_at');relaxed=true}} const headState=p=>{try{const body=cp.execFileSync('git',['show',head+':'+p],{encoding:null,stdio:['ignore','pipe','ignore']});return {exists:true,sha256:hash(body)}}catch{return {exists:false,sha256:null}}}; const seenSources=new Set(); for(let i=0;i<expectedSources.length;i++){const p=expectedSources[i];const entry=stale.source_files[i];if(entry&&seenSources.has(entry.path))fail('staleness_source_file_duplicate');if(!entry||entry.path!==p)fail('staleness_source_file_path');seenSources.add(entry.path);if(typeof entry.exists!=='boolean')fail('staleness_source_file_exists');if(entry.exists?(typeof entry.sha256!=='string'||!/^[a-f0-9]{64}$/.test(entry.sha256)):entry.sha256!==null)fail('staleness_source_file_snapshot');if(v1Evidence.has(p)&&entry.exists!==true)fail('staleness_v1_source_missing');const r=inside(p);const currentExists=fs.existsSync(r);if(currentExists&&!fs.statSync(r).isFile())fail('staleness_source_file_not_file');const current={exists:currentExists,sha256:currentExists?fileHash(p):null};if(relaxed&&allAllowed.has(p)){const committed=headState(p);if(current.exists!==committed.exists||current.sha256!==committed.sha256)fail('staleness_source_file_uncommitted')}else if(current.exists!==entry.exists||current.sha256!==entry.sha256)fail('staleness_source_file_sha256')} const semantic=JSON.parse(JSON.stringify(data)); delete semantic.contract_snapshot.sidecar_semantic_sha256; delete semantic.contract_snapshot.diagnosis_artifact_sha256; delete semantic.staleness.diagnosis_artifact; const sidecarSemanticHash=hash(canon(semantic)); const diagnosisSemanticHash=normalizedDiagnosisHash(diagnosisPath); if(snap.sidecar_semantic_sha256!==sidecarSemanticHash||snap.diagnosis_semantic_sha256!==diagnosisSemanticHash)fail('contract_snapshot_semantic'); if(fs.existsSync(inside(reviewPath))){const prev=readJson(reviewPath);if(prev.verdict==='REQUEST_REANALYSIS'){const ps=prev.contract_snapshot||{};const unchanged=modern(ps)?ps.sidecar_semantic_sha256===sidecarSemanticHash&&ps.diagnosis_semantic_sha256===diagnosisSemanticHash:legacySame(ps,snap);if(unchanged)fail('reanalysis_unchanged');const cm=fs.statSync(inside(contractPath)).mtimeMs;const dm=fs.statSync(inside(diagnosisPath)).mtimeMs;const rm=fs.statSync(inside(reviewPath)).mtimeMs;if(cm<=rm||dm<=rm)fail('reanalysis_not_rewritten')}} \" \"$ARGUMENTS\" \"{{v1_path}}\" \"{{v2_path}}\""
|
|
175
|
+
- "node -e \"const fs=require('node:fs'); const path=require('node:path'); const mod=process.argv[1]; const allowed=['BUG','UNIMPLEMENTED','INFRA','REMOVED','TEST_BUG']; const fail=m=>{console.error('INTENTDNA_DIAGNOSTIC:'+m);process.exit(1)}; 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.sidecar_semantic_sha256===b.sidecar_semantic_sha256&&a.diagnosis_semantic_sha256===b.diagnosis_semantic_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=fs.realpathSync(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');let stat=null;try{stat=fs.lstatSync(r)}catch(e){if(!e||e.code!=='ENOENT')fail('path_unreadable')}if(stat){if(stat.isSymbolicLink())fail('path_symlink');const real=fs.realpathSync(r);if(real!==root&&!real.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); const snap=data.contract_snapshot||{}; if(data.contract_version!=='diagnosis-contract/v2'||data.module!==mod)fail('contract_version'); 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'); const updated=Date.parse(review.updated_at); if(!Number.isFinite(updated))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){const r=inside(p);if(!fs.existsSync(r)||!fs.statSync(r).isFile())fail('review_evidence_paths')} 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||updated+300000<Math.max(cm,dm)||Math.abs(rm-updated)>300000)fail('review_stale'); \" \"$ARGUMENTS\""
|
|
176
176
|
allow_builtin_asserts:
|
|
177
177
|
- clean_working_tree
|
|
178
178
|
roles:
|
|
@@ -363,59 +363,54 @@ controllers:
|
|
|
363
363
|
fix_reviewer: fix_reviewer
|
|
364
364
|
test_runner: test_runner
|
|
365
365
|
stop_format: configured_stop_report
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
- "The controller must schedule only from $progress_artifact_path."
|
|
415
|
-
- "Manual /dna-frw-diagnosis and /dna-frw-fix flows remain separate and available."
|
|
416
|
-
- "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."
|
|
417
|
-
- "Do not use the generic workflow policy block; branch only from the progress JSON verdict."
|
|
418
|
-
|
|
366
|
+
runtime_program:
|
|
367
|
+
schema_version: intentdna.controller_program.v1
|
|
368
|
+
diagnosis:
|
|
369
|
+
analyzer_step_id: analyze
|
|
370
|
+
reviewer_step_id: review
|
|
371
|
+
decision:
|
|
372
|
+
step_id: review
|
|
373
|
+
output_name: diagnosis-review-verdict
|
|
374
|
+
schema_ref: intentdna.frw.diagnosis-review.v1
|
|
375
|
+
json_pointer: /verdict
|
|
376
|
+
approved_value: APPROVE
|
|
377
|
+
request_changes_value: REQUEST_REANALYSIS
|
|
378
|
+
fix:
|
|
379
|
+
surgeon_step_id: fix_bugs
|
|
380
|
+
reviewer_step_id: review_changes
|
|
381
|
+
verifier_step_id: verify_report
|
|
382
|
+
decision:
|
|
383
|
+
step_id: verify_report
|
|
384
|
+
output_name: fix-progress-verdict
|
|
385
|
+
schema_ref: intentdna.frw.fix-progress.v1
|
|
386
|
+
json_pointer: /verdict
|
|
387
|
+
pass_value: PASS
|
|
388
|
+
continue_value: CONTINUE
|
|
389
|
+
request_changes_value: REQUEST_CHANGES
|
|
390
|
+
blocked_value: BLOCKED
|
|
391
|
+
diagnosis_contract_invalid_value: DIAGNOSIS_CONTRACT_INVALID
|
|
392
|
+
provenance_invalid_value: PROVENANCE_INVALID
|
|
393
|
+
reuse:
|
|
394
|
+
mode_input: diagnosis_source
|
|
395
|
+
legacy:
|
|
396
|
+
diagnosis_spec_reference_input: legacy_diagnosis_spec_path
|
|
397
|
+
diagnosis_spec_digest_input: legacy_diagnosis_spec_digest
|
|
398
|
+
diagnosis_contract_reference_input: legacy_diagnosis_contract_path
|
|
399
|
+
diagnosis_contract_digest_input: legacy_diagnosis_contract_digest
|
|
400
|
+
canonical:
|
|
401
|
+
source_plan_input: canonical_source_plan_id
|
|
402
|
+
diagnosis_result_input: canonical_diagnosis_result_id
|
|
403
|
+
review_result_input: canonical_diagnosis_review_result_id
|
|
404
|
+
approval_reuse:
|
|
405
|
+
enabled: true
|
|
406
|
+
policy_id: exact-diagnosis-approval-v1
|
|
407
|
+
require_exact_artifact_ids: true
|
|
408
|
+
require_exact_artifact_digests: true
|
|
409
|
+
outputs:
|
|
410
|
+
diagnosis_spec: diagnosis-spec
|
|
411
|
+
diagnosis_contract: diagnosis-contract
|
|
412
|
+
diagnosis_review: diagnosis-review
|
|
413
|
+
diagnosis_review_verdict: diagnosis-review-verdict
|
|
419
414
|
workflows:
|
|
420
415
|
behavior-lock:
|
|
421
416
|
name: Behavior Lock
|
|
@@ -605,13 +600,15 @@ workflows:
|
|
|
605
600
|
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.
|
|
606
601
|
9. Before handing off to review, the producer-side diagnosis contract completion gate must pass. It rejects grouped observations that hide per-test failures, duplicate/bad ids, unmapped observed_failures, missing evidence, count mismatches, bad hashes, stale state, and no-material-change reanalysis after REQUEST_REANALYSIS. Reanalysis compares the canonical sidecar with its self-digest and raw diagnosis snapshot fields omitted, plus a whitespace-normalized diagnosis hash, so a real sidecar-only correction is allowed while formatting-only Markdown changes are not.
|
|
607
602
|
completion:
|
|
608
|
-
- command_success: "node -e \"const fs=require('node:fs'); const cp=require('node:child_process'); 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=>{console.error('INTENTDNA_DIAGNOSTIC:'+m);process.exit(1)}; const same=(a,b)=>Array.isArray(a)&&a.length===b.length&&a.every((v,i)=>v===b[i]); const legacySame=(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 modern=s=>s&&typeof s.sidecar_semantic_sha256==='string'&&typeof s.diagnosis_semantic_sha256==='string'; const root=fs.realpathSync(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');let stat=null;try{stat=fs.lstatSync(r)}catch(e){if(!e||e.code!=='ENOENT')fail('path_unreadable')}if(stat){if(stat.isSymbolicLink())fail('path_symlink');const real=fs.realpathSync(r);if(real!==root&&!real.startsWith(root+path.sep))fail('path_escape')}return r}; const rel=p=>path.relative(root,inside(p)).split(path.sep).join('/')||'.'; const under=(p,base)=>p===base||p.startsWith(base+'/'); const readJson=p=>JSON.parse(fs.readFileSync(inside(p),'utf8')); const hash=v=>crypto.createHash('sha256').update(v).digest('hex'); const fileHash=p=>hash(fs.readFileSync(inside(p))); const normalizedDiagnosisHash=p=>hash(fs.readFileSync(inside(p),'utf8').replace(/\\s+/g,' ').trim()); const canon=v=>Array.isArray(v)?'['+v.map(canon).join(',')+']':v&&typeof v==='object'?'{'+Object.keys(v).sort().map(k=>JSON.stringify(k)+':'+canon(v[k])).join(',')+'}':JSON.stringify(v); 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 progressPath='.dna/fix-progress/'+mod+'.json'; const diagnosisPath='.dna/specs/diagnosis-'+mod+'.md'; const behaviorPath='docs/behavior/'+mod+'.md'; const v1Root=rel('{{v1_path}}'); const v2Root=rel('{{v2_path}}'); if(v1Root==='.'||v2Root==='.'||under(v1Root,v2Root)||under(v2Root,v1Root))fail('configured_roots'); const data=readJson(contractPath); if(data.contract_version!=='diagnosis-contract/v2')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 idOk=id=>typeof id==='string'&&/^[A-Za-z0-9][A-Za-z0-9_.:-]*$/.test(id); const ids=new Set(); for(const f of observed){if(!idOk(f&&f.id)||ids.has(f.id))fail('observed_failure_ids');ids.add(f.id)} const workIds=new Set(); for(const item of work){if(!idOk(item&&item.id)||workIds.has(item.id))fail('work_item_ids');workIds.add(item.id)} 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)rel(p);if(f.kind!==undefined)fail('observed_failure_kind_forbidden');failureById.set(f.id,f)} const mapped=new Set(); const allAllowed=new Set(); const v1Evidence=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)rel(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');const v2Evidence=new Set(item.v2_evidence_paths.map(rel));for(const p of item.v1_evidence_paths){const n=rel(p);if(!under(n,v1Root))fail('work_item_v1_evidence_root');v1Evidence.add(n)}for(const p of item.v2_evidence_paths)rel(p);if(!Array.isArray(item.allowed_change_paths)||!item.allowed_change_paths.length)fail('work_item_allowed_change_paths');const approved=new Set();for(const p of item.allowed_change_paths){if(typeof p!=='string'||['*','?','[',']'].some(c=>p.includes(c)))fail('work_item_allowed_change_path_pattern');const n=rel(p);const r=inside(p);if(under(n,v1Root))fail('work_item_allowed_change_path_v1');if(!(under(n,v2Root)||under(n,'test')||under(n,'lib')))fail('work_item_allowed_change_path_root');if(fs.existsSync(r)?!fs.statSync(r).isFile():!path.basename(n).includes('.'))fail('work_item_allowed_change_path_not_file');if(!v2Evidence.has(n))fail('work_item_allowed_change_path_not_evidence');if(approved.has(n))fail('work_item_allowed_change_path_duplicate');approved.add(n);allAllowed.add(n)}}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'); for(const k of ['failed','skipped','hung']){if(!Number.isInteger(counts[k])||counts[k]<0)fail('baseline_count_'+k)} if(counts.failed+counts.skipped+counts.hung!==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 snap=data.contract_snapshot||{}; const baselineHash=fileHash(data.baseline.source_path); const diagnosisHash=fileHash(diagnosisPath); 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!==baselineHash)fail('baseline_sha256'); if(snap.diagnosis_artifact_sha256!==diagnosisHash)fail('diagnosis_sha256'); const stale=data.staleness; if(!stale||stale.is_stale!==false)fail('stale_contract'); const source=(entry,p,n,expected)=>{if(!entry||entry.path!==p)fail('staleness_'+n+'_path');if(entry.sha256!==expected)fail('staleness_'+n+'_sha256')}; source(stale.behavior_doc,behaviorPath,'behavior_doc',fileHash(behaviorPath)); source(stale.baseline,data.baseline.source_path,'baseline',baselineHash); source(stale.diagnosis_artifact,diagnosisPath,'diagnosis_artifact',diagnosisHash); const expectedSources=[...new Set(work.filter(i=>action.includes(i.kind)).flatMap(i=>[...i.v1_evidence_paths,...i.v2_evidence_paths]).map(rel))].sort(); if(!Array.isArray(stale.source_files)||stale.source_files.length!==expectedSources.length)fail('staleness_source_files'); let relaxed=false; let head=''; if(fs.existsSync(inside(progressPath))){const progressMtime=fs.statSync(inside(progressPath)).mtimeMs;const artifactMtime=Math.max(fs.statSync(inside(contractPath)).mtimeMs,fs.statSync(inside(diagnosisPath)).mtimeMs,fs.existsSync(inside(reviewPath))?fs.statSync(inside(reviewPath)).mtimeMs:0);if(progressMtime>=artifactMtime){const progress=readJson(progressPath);head=cp.execFileSync('git',['rev-parse','HEAD'],{encoding:'utf8'}).trim();if(progress.module!==mod||progress.verdict!=='CONTINUE'||progress.module_complete!==false||progress.diagnosis_invalid!==false)fail('progress_state');if(progress.diagnosis_artifact!==diagnosisPath||progress.review_artifact!=='.dna/reviews/'+mod+'-review.json')fail('progress_artifacts');if(progress.git_head!==head||progress.last_fix_commit!==head)fail('progress_commit');if(!Number.isInteger(progress.round)||progress.round<1||typeof progress.run_id!=='string'||!progress.run_id.trim())fail('progress_provenance');const progressTime=Date.parse(progress.updated_at);if(!Number.isFinite(progressTime)||progressTime+300000<artifactMtime)fail('progress_updated_at');relaxed=true}} const headState=p=>{try{const body=cp.execFileSync('git',['show',head+':'+p],{encoding:null,stdio:['ignore','pipe','ignore']});return {exists:true,sha256:hash(body)}}catch{return {exists:false,sha256:null}}}; const seenSources=new Set(); for(let i=0;i<expectedSources.length;i++){const p=expectedSources[i];const entry=stale.source_files[i];if(entry&&seenSources.has(entry.path))fail('staleness_source_file_duplicate');if(!entry||entry.path!==p)fail('staleness_source_file_path');seenSources.add(entry.path);if(typeof entry.exists!=='boolean')fail('staleness_source_file_exists');if(entry.exists?(typeof entry.sha256!=='string'||!/^[a-f0-9]{64}$/.test(entry.sha256)):entry.sha256!==null)fail('staleness_source_file_snapshot');if(v1Evidence.has(p)&&entry.exists!==true)fail('staleness_v1_source_missing');const r=inside(p);const currentExists=fs.existsSync(r);if(currentExists&&!fs.statSync(r).isFile())fail('staleness_source_file_not_file');const current={exists:currentExists,sha256:currentExists?fileHash(p):null};if(relaxed&&allAllowed.has(p)){const committed=headState(p);if(current.exists!==committed.exists||current.sha256!==committed.sha256)fail('staleness_source_file_uncommitted')}else if(current.exists!==entry.exists||current.sha256!==entry.sha256)fail('staleness_source_file_sha256')} const semantic=JSON.parse(JSON.stringify(data)); delete semantic.contract_snapshot.sidecar_semantic_sha256; delete semantic.contract_snapshot.diagnosis_artifact_sha256; delete semantic.staleness.diagnosis_artifact; const sidecarSemanticHash=hash(canon(semantic)); const diagnosisSemanticHash=normalizedDiagnosisHash(diagnosisPath); if(snap.sidecar_semantic_sha256!==sidecarSemanticHash||snap.diagnosis_semantic_sha256!==diagnosisSemanticHash)fail('contract_snapshot_semantic'); if(fs.existsSync(inside(reviewPath))){const prev=readJson(reviewPath);if(prev.verdict==='REQUEST_REANALYSIS'){const ps=prev.contract_snapshot||{};const unchanged=modern(ps)?ps.sidecar_semantic_sha256===sidecarSemanticHash&&ps.diagnosis_semantic_sha256===diagnosisSemanticHash:legacySame(ps,snap);if(unchanged)fail('reanalysis_unchanged');const cm=fs.statSync(inside(contractPath)).mtimeMs;const dm=fs.statSync(inside(diagnosisPath)).mtimeMs;const rm=fs.statSync(inside(reviewPath)).mtimeMs;if(cm<=rm||dm<=rm)fail('reanalysis_not_rewritten')}} \""
|
|
603
|
+
- command_success: "node -e \"const fs=require('node:fs'); const cp=require('node:child_process'); const crypto=require('node:crypto'); const path=require('node:path'); const mod=process.argv[1]; const allowed=['BUG','UNIMPLEMENTED','INFRA','REMOVED','TEST_BUG']; const action=['BUG','UNIMPLEMENTED','INFRA','TEST_BUG']; const fail=m=>{console.error('INTENTDNA_DIAGNOSTIC:'+m);process.exit(1)}; const same=(a,b)=>Array.isArray(a)&&a.length===b.length&&a.every((v,i)=>v===b[i]); const legacySame=(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 modern=s=>s&&typeof s.sidecar_semantic_sha256==='string'&&typeof s.diagnosis_semantic_sha256==='string'; const root=fs.realpathSync(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');let stat=null;try{stat=fs.lstatSync(r)}catch(e){if(!e||e.code!=='ENOENT')fail('path_unreadable')}if(stat){if(stat.isSymbolicLink())fail('path_symlink');const real=fs.realpathSync(r);if(real!==root&&!real.startsWith(root+path.sep))fail('path_escape')}return r}; const rel=p=>path.relative(root,inside(p)).split(path.sep).join('/')||'.'; const under=(p,base)=>p===base||p.startsWith(base+'/'); const readJson=p=>JSON.parse(fs.readFileSync(inside(p),'utf8')); const hash=v=>crypto.createHash('sha256').update(v).digest('hex'); const fileHash=p=>hash(fs.readFileSync(inside(p))); const normalizedDiagnosisHash=p=>hash(fs.readFileSync(inside(p),'utf8').replace(/\\s+/g,' ').trim()); const canon=v=>Array.isArray(v)?'['+v.map(canon).join(',')+']':v&&typeof v==='object'?'{'+Object.keys(v).sort().map(k=>JSON.stringify(k)+':'+canon(v[k])).join(',')+'}':JSON.stringify(v); 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 progressPath='.dna/fix-progress/'+mod+'.json'; const diagnosisPath='.dna/specs/diagnosis-'+mod+'.md'; const behaviorPath='docs/behavior/'+mod+'.md'; const v1Root=rel(process.argv[2]); const v2Root=rel(process.argv[3]); if(v1Root==='.'||v2Root==='.'||under(v1Root,v2Root)||under(v2Root,v1Root))fail('configured_roots'); const data=readJson(contractPath); if(data.contract_version!=='diagnosis-contract/v2')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 idOk=id=>typeof id==='string'&&/^[A-Za-z0-9][A-Za-z0-9_.:-]*$/.test(id); const ids=new Set(); for(const f of observed){if(!idOk(f&&f.id)||ids.has(f.id))fail('observed_failure_ids');ids.add(f.id)} const workIds=new Set(); for(const item of work){if(!idOk(item&&item.id)||workIds.has(item.id))fail('work_item_ids');workIds.add(item.id)} 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)rel(p);if(f.kind!==undefined)fail('observed_failure_kind_forbidden');failureById.set(f.id,f)} const mapped=new Set(); const allAllowed=new Set(); const v1Evidence=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)rel(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');const v2Evidence=new Set(item.v2_evidence_paths.map(rel));for(const p of item.v1_evidence_paths){const n=rel(p);if(!under(n,v1Root))fail('work_item_v1_evidence_root');v1Evidence.add(n)}for(const p of item.v2_evidence_paths)rel(p);if(!Array.isArray(item.allowed_change_paths)||!item.allowed_change_paths.length)fail('work_item_allowed_change_paths');const approved=new Set();for(const p of item.allowed_change_paths){if(typeof p!=='string'||['*','?','[',']'].some(c=>p.includes(c)))fail('work_item_allowed_change_path_pattern');const n=rel(p);const r=inside(p);if(under(n,v1Root))fail('work_item_allowed_change_path_v1');if(!(under(n,v2Root)||under(n,'test')||under(n,'lib')))fail('work_item_allowed_change_path_root');if(fs.existsSync(r)?!fs.statSync(r).isFile():!path.basename(n).includes('.'))fail('work_item_allowed_change_path_not_file');if(!v2Evidence.has(n))fail('work_item_allowed_change_path_not_evidence');if(approved.has(n))fail('work_item_allowed_change_path_duplicate');approved.add(n);allAllowed.add(n)}}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'); for(const k of ['failed','skipped','hung']){if(!Number.isInteger(counts[k])||counts[k]<0)fail('baseline_count_'+k)} if(counts.failed+counts.skipped+counts.hung!==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 snap=data.contract_snapshot||{}; const baselineHash=fileHash(data.baseline.source_path); const diagnosisHash=fileHash(diagnosisPath); 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!==baselineHash)fail('baseline_sha256'); if(snap.diagnosis_artifact_sha256!==diagnosisHash)fail('diagnosis_sha256'); const stale=data.staleness; if(!stale||stale.is_stale!==false)fail('stale_contract'); const source=(entry,p,n,expected)=>{if(!entry||entry.path!==p)fail('staleness_'+n+'_path');if(entry.sha256!==expected)fail('staleness_'+n+'_sha256')}; source(stale.behavior_doc,behaviorPath,'behavior_doc',fileHash(behaviorPath)); source(stale.baseline,data.baseline.source_path,'baseline',baselineHash); source(stale.diagnosis_artifact,diagnosisPath,'diagnosis_artifact',diagnosisHash); const expectedSources=[...new Set(work.filter(i=>action.includes(i.kind)).flatMap(i=>[...i.v1_evidence_paths,...i.v2_evidence_paths]).map(rel))].sort(); if(!Array.isArray(stale.source_files)||stale.source_files.length!==expectedSources.length)fail('staleness_source_files'); let relaxed=false; let head=''; if(fs.existsSync(inside(progressPath))){const progressMtime=fs.statSync(inside(progressPath)).mtimeMs;const artifactMtime=Math.max(fs.statSync(inside(contractPath)).mtimeMs,fs.statSync(inside(diagnosisPath)).mtimeMs,fs.existsSync(inside(reviewPath))?fs.statSync(inside(reviewPath)).mtimeMs:0);if(progressMtime>=artifactMtime){const progress=readJson(progressPath);head=cp.execFileSync('git',['rev-parse','HEAD'],{encoding:'utf8'}).trim();if(progress.module!==mod||progress.verdict!=='CONTINUE'||progress.module_complete!==false||progress.diagnosis_invalid!==false)fail('progress_state');if(progress.diagnosis_artifact!==diagnosisPath||progress.review_artifact!=='.dna/reviews/'+mod+'-review.json')fail('progress_artifacts');if(progress.git_head!==head||progress.last_fix_commit!==head)fail('progress_commit');if(!Number.isInteger(progress.round)||progress.round<1||typeof progress.run_id!=='string'||!progress.run_id.trim())fail('progress_provenance');const progressTime=Date.parse(progress.updated_at);if(!Number.isFinite(progressTime)||progressTime+300000<artifactMtime)fail('progress_updated_at');relaxed=true}} const headState=p=>{try{const body=cp.execFileSync('git',['show',head+':'+p],{encoding:null,stdio:['ignore','pipe','ignore']});return {exists:true,sha256:hash(body)}}catch{return {exists:false,sha256:null}}}; const seenSources=new Set(); for(let i=0;i<expectedSources.length;i++){const p=expectedSources[i];const entry=stale.source_files[i];if(entry&&seenSources.has(entry.path))fail('staleness_source_file_duplicate');if(!entry||entry.path!==p)fail('staleness_source_file_path');seenSources.add(entry.path);if(typeof entry.exists!=='boolean')fail('staleness_source_file_exists');if(entry.exists?(typeof entry.sha256!=='string'||!/^[a-f0-9]{64}$/.test(entry.sha256)):entry.sha256!==null)fail('staleness_source_file_snapshot');if(v1Evidence.has(p)&&entry.exists!==true)fail('staleness_v1_source_missing');const r=inside(p);const currentExists=fs.existsSync(r);if(currentExists&&!fs.statSync(r).isFile())fail('staleness_source_file_not_file');const current={exists:currentExists,sha256:currentExists?fileHash(p):null};if(relaxed&&allAllowed.has(p)){const committed=headState(p);if(current.exists!==committed.exists||current.sha256!==committed.sha256)fail('staleness_source_file_uncommitted')}else if(current.exists!==entry.exists||current.sha256!==entry.sha256)fail('staleness_source_file_sha256')} const semantic=JSON.parse(JSON.stringify(data)); delete semantic.contract_snapshot.sidecar_semantic_sha256; delete semantic.contract_snapshot.diagnosis_artifact_sha256; delete semantic.staleness.diagnosis_artifact; const sidecarSemanticHash=hash(canon(semantic)); const diagnosisSemanticHash=normalizedDiagnosisHash(diagnosisPath); if(snap.sidecar_semantic_sha256!==sidecarSemanticHash||snap.diagnosis_semantic_sha256!==diagnosisSemanticHash)fail('contract_snapshot_semantic'); if(fs.existsSync(inside(reviewPath))){const prev=readJson(reviewPath);if(prev.verdict==='REQUEST_REANALYSIS'){const ps=prev.contract_snapshot||{};const unchanged=modern(ps)?ps.sidecar_semantic_sha256===sidecarSemanticHash&&ps.diagnosis_semantic_sha256===diagnosisSemanticHash:legacySame(ps,snap);if(unchanged)fail('reanalysis_unchanged');const cm=fs.statSync(inside(contractPath)).mtimeMs;const dm=fs.statSync(inside(diagnosisPath)).mtimeMs;const rm=fs.statSync(inside(reviewPath)).mtimeMs;if(cm<=rm||dm<=rm)fail('reanalysis_not_rewritten')}} \" \"$ARGUMENTS\" \"{{v1_path}}\" \"{{v2_path}}\""
|
|
609
604
|
handoff:
|
|
610
605
|
produces:
|
|
611
606
|
- type: file
|
|
607
|
+
artifact_id: diagnosis-spec
|
|
612
608
|
path: ".dna/specs/diagnosis-$ARGUMENTS.md"
|
|
613
609
|
description: "Diagnosis spec for module"
|
|
614
610
|
- type: file
|
|
611
|
+
artifact_id: diagnosis-contract
|
|
615
612
|
path: ".dna/specs/diagnosis-$ARGUMENTS.contract.json"
|
|
616
613
|
description: "Structured diagnosis contract sidecar mapping baseline observed failures to work items"
|
|
617
614
|
|
|
@@ -674,22 +671,44 @@ workflows:
|
|
|
674
671
|
|
|
675
672
|
APPROVE → diagnosis complete.
|
|
676
673
|
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.
|
|
674
|
+
canonical_runtime:
|
|
675
|
+
prompt_handoff_bindings:
|
|
676
|
+
- diagnosis-spec
|
|
677
|
+
- diagnosis-contract
|
|
678
|
+
handoff_sources:
|
|
679
|
+
diagnosis-spec: analyze
|
|
680
|
+
diagnosis-contract: analyze
|
|
681
|
+
require_attested_independent_submit: true
|
|
682
|
+
completion:
|
|
683
|
+
- command_success: "test -s \"{{handoff.review:diagnosis-spec.materialized_path}}\" && test -s \"{{handoff.review:diagnosis-contract.materialized_path}}\""
|
|
684
|
+
- command_success: "node -e \"const fs=require('node:fs'); const crypto=require('node:crypto'); const mod=process.argv[1]; const fail=m=>{console.error('INTENTDNA_DIAGNOSTIC:'+m);process.exit(1)}; const read=p=>JSON.parse(fs.readFileSync(p,'utf8')); const hash=p=>crypto.createHash('sha256').update(fs.readFileSync(p)).digest('hex'); if(!/^[A-Za-z0-9_-]+$/.test(mod))fail('module'); const diagnosisInput=process.argv[2]; const contractInput=process.argv[3]; const reviewPath='.dna/specs/diagnosis-'+mod+'.review.json'; for(const p of [diagnosisInput,contractInput,reviewPath]){if(!fs.existsSync(p)||!fs.statSync(p).isFile())fail('artifact_missing')} const contract=read(contractInput); const review=read(reviewPath); if(contract.contract_version!=='diagnosis-contract/v2'||contract.module!==mod)fail('contract_identity'); if(review.contract_version!=='diagnosis-contract-review/v1'||review.contract_valid!==true||review.verdict!=='APPROVE')fail('review_verdict'); if(review.contract_artifact_reviewed!==contractInput||review.artifact_reviewed!==diagnosisInput)fail('review_snapshot_binding'); const snap=contract.contract_snapshot||{}; const reviewed=review.contract_snapshot||{}; const keys=['baseline_source_sha256','diagnosis_artifact_sha256','sidecar_semantic_sha256','diagnosis_semantic_sha256','observed_failure_count','work_item_count']; for(const key of keys){if(reviewed[key]!==snap[key])fail('review_contract_snapshot')} if(JSON.stringify(reviewed.allowed_kinds)!==JSON.stringify(snap.allowed_kinds))fail('review_allowed_kinds'); if(hash(diagnosisInput)!==snap.diagnosis_artifact_sha256)fail('diagnosis_snapshot_digest'); const updated=Date.parse(review.updated_at); if(!Number.isFinite(updated))fail('review_updated_at'); const reviewMtime=fs.statSync(reviewPath).mtimeMs; const inputMtime=Math.max(fs.statSync(diagnosisInput).mtimeMs,fs.statSync(contractInput).mtimeMs); if(reviewMtime<inputMtime||updated+300000<inputMtime||Math.abs(reviewMtime-updated)>300000)fail('review_stale'); if(!Array.isArray(review.evidence_paths)||!review.evidence_paths.length)fail('review_evidence_paths'); \" \"$ARGUMENTS\" \"{{handoff.review:diagnosis-spec.materialized_path}}\" \"{{handoff.review:diagnosis-contract.materialized_path}}\""
|
|
685
|
+
outputs:
|
|
686
|
+
- type: state
|
|
687
|
+
artifact_id: diagnosis-review-verdict
|
|
688
|
+
schema_ref: intentdna.frw.diagnosis-review.v1
|
|
689
|
+
description: "Canonical diagnosis review verdict used only by the typed controller"
|
|
690
|
+
prompt_suffix: |
|
|
691
|
+
Canonical runtime output contract: return the diagnosis review JSON as a structured output named diagnosis-review-verdict with schema_ref intentdna.frw.diagnosis-review.v1 in the final outputs envelope. This is in addition to writing the declared review file.
|
|
677
692
|
completion:
|
|
678
|
-
- command_success: "node -e \"const fs=require('node:fs'); const cp=require('node:child_process'); 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=>{console.error('INTENTDNA_DIAGNOSTIC:'+m);process.exit(1)}; const same=(a,b)=>Array.isArray(a)&&a.length===b.length&&a.every((v,i)=>v===b[i]); const legacySame=(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 modern=s=>s&&typeof s.sidecar_semantic_sha256==='string'&&typeof s.diagnosis_semantic_sha256==='string'; const root=fs.realpathSync(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');let stat=null;try{stat=fs.lstatSync(r)}catch(e){if(!e||e.code!=='ENOENT')fail('path_unreadable')}if(stat){if(stat.isSymbolicLink())fail('path_symlink');const real=fs.realpathSync(r);if(real!==root&&!real.startsWith(root+path.sep))fail('path_escape')}return r}; const rel=p=>path.relative(root,inside(p)).split(path.sep).join('/')||'.'; const under=(p,base)=>p===base||p.startsWith(base+'/'); const readJson=p=>JSON.parse(fs.readFileSync(inside(p),'utf8')); const hash=v=>crypto.createHash('sha256').update(v).digest('hex'); const fileHash=p=>hash(fs.readFileSync(inside(p))); const normalizedDiagnosisHash=p=>hash(fs.readFileSync(inside(p),'utf8').replace(/\\s+/g,' ').trim()); const canon=v=>Array.isArray(v)?'['+v.map(canon).join(',')+']':v&&typeof v==='object'?'{'+Object.keys(v).sort().map(k=>JSON.stringify(k)+':'+canon(v[k])).join(',')+'}':JSON.stringify(v); 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 progressPath='.dna/fix-progress/'+mod+'.json'; const diagnosisPath='.dna/specs/diagnosis-'+mod+'.md'; const behaviorPath='docs/behavior/'+mod+'.md'; const v1Root=rel('{{v1_path}}'); const v2Root=rel('{{v2_path}}'); if(v1Root==='.'||v2Root==='.'||under(v1Root,v2Root)||under(v2Root,v1Root))fail('configured_roots'); const data=readJson(contractPath); if(data.contract_version!=='diagnosis-contract/v2')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 idOk=id=>typeof id==='string'&&/^[A-Za-z0-9][A-Za-z0-9_.:-]*$/.test(id); const ids=new Set(); for(const f of observed){if(!idOk(f&&f.id)||ids.has(f.id))fail('observed_failure_ids');ids.add(f.id)} const workIds=new Set(); for(const item of work){if(!idOk(item&&item.id)||workIds.has(item.id))fail('work_item_ids');workIds.add(item.id)} 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)rel(p);if(f.kind!==undefined)fail('observed_failure_kind_forbidden');failureById.set(f.id,f)} const mapped=new Set(); const allAllowed=new Set(); const v1Evidence=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)rel(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');const v2Evidence=new Set(item.v2_evidence_paths.map(rel));for(const p of item.v1_evidence_paths){const n=rel(p);if(!under(n,v1Root))fail('work_item_v1_evidence_root');v1Evidence.add(n)}for(const p of item.v2_evidence_paths)rel(p);if(!Array.isArray(item.allowed_change_paths)||!item.allowed_change_paths.length)fail('work_item_allowed_change_paths');const approved=new Set();for(const p of item.allowed_change_paths){if(typeof p!=='string'||['*','?','[',']'].some(c=>p.includes(c)))fail('work_item_allowed_change_path_pattern');const n=rel(p);const r=inside(p);if(under(n,v1Root))fail('work_item_allowed_change_path_v1');if(!(under(n,v2Root)||under(n,'test')||under(n,'lib')))fail('work_item_allowed_change_path_root');if(fs.existsSync(r)?!fs.statSync(r).isFile():!path.basename(n).includes('.'))fail('work_item_allowed_change_path_not_file');if(!v2Evidence.has(n))fail('work_item_allowed_change_path_not_evidence');if(approved.has(n))fail('work_item_allowed_change_path_duplicate');approved.add(n);allAllowed.add(n)}}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'); for(const k of ['failed','skipped','hung']){if(!Number.isInteger(counts[k])||counts[k]<0)fail('baseline_count_'+k)} if(counts.failed+counts.skipped+counts.hung!==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 snap=data.contract_snapshot||{}; const baselineHash=fileHash(data.baseline.source_path); const diagnosisHash=fileHash(diagnosisPath); 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!==baselineHash)fail('baseline_sha256'); if(snap.diagnosis_artifact_sha256!==diagnosisHash)fail('diagnosis_sha256'); const stale=data.staleness; if(!stale||stale.is_stale!==false)fail('stale_contract'); const source=(entry,p,n,expected)=>{if(!entry||entry.path!==p)fail('staleness_'+n+'_path');if(entry.sha256!==expected)fail('staleness_'+n+'_sha256')}; source(stale.behavior_doc,behaviorPath,'behavior_doc',fileHash(behaviorPath)); source(stale.baseline,data.baseline.source_path,'baseline',baselineHash); source(stale.diagnosis_artifact,diagnosisPath,'diagnosis_artifact',diagnosisHash); const expectedSources=[...new Set(work.filter(i=>action.includes(i.kind)).flatMap(i=>[...i.v1_evidence_paths,...i.v2_evidence_paths]).map(rel))].sort(); if(!Array.isArray(stale.source_files)||stale.source_files.length!==expectedSources.length)fail('staleness_source_files'); let relaxed=false; let head=''; if(fs.existsSync(inside(progressPath))){const progressMtime=fs.statSync(inside(progressPath)).mtimeMs;const artifactMtime=Math.max(fs.statSync(inside(contractPath)).mtimeMs,fs.statSync(inside(diagnosisPath)).mtimeMs,fs.existsSync(inside(reviewPath))?fs.statSync(inside(reviewPath)).mtimeMs:0);if(progressMtime>=artifactMtime){const progress=readJson(progressPath);head=cp.execFileSync('git',['rev-parse','HEAD'],{encoding:'utf8'}).trim();if(progress.module!==mod||progress.verdict!=='CONTINUE'||progress.module_complete!==false||progress.diagnosis_invalid!==false)fail('progress_state');if(progress.diagnosis_artifact!==diagnosisPath||progress.review_artifact!=='.dna/reviews/'+mod+'-review.json')fail('progress_artifacts');if(progress.git_head!==head||progress.last_fix_commit!==head)fail('progress_commit');if(!Number.isInteger(progress.round)||progress.round<1||typeof progress.run_id!=='string'||!progress.run_id.trim())fail('progress_provenance');const progressTime=Date.parse(progress.updated_at);if(!Number.isFinite(progressTime)||progressTime+300000<artifactMtime)fail('progress_updated_at');relaxed=true}} const headState=p=>{try{const body=cp.execFileSync('git',['show',head+':'+p],{encoding:null,stdio:['ignore','pipe','ignore']});return {exists:true,sha256:hash(body)}}catch{return {exists:false,sha256:null}}}; const seenSources=new Set(); for(let i=0;i<expectedSources.length;i++){const p=expectedSources[i];const entry=stale.source_files[i];if(entry&&seenSources.has(entry.path))fail('staleness_source_file_duplicate');if(!entry||entry.path!==p)fail('staleness_source_file_path');seenSources.add(entry.path);if(typeof entry.exists!=='boolean')fail('staleness_source_file_exists');if(entry.exists?(typeof entry.sha256!=='string'||!/^[a-f0-9]{64}$/.test(entry.sha256)):entry.sha256!==null)fail('staleness_source_file_snapshot');if(v1Evidence.has(p)&&entry.exists!==true)fail('staleness_v1_source_missing');const r=inside(p);const currentExists=fs.existsSync(r);if(currentExists&&!fs.statSync(r).isFile())fail('staleness_source_file_not_file');const current={exists:currentExists,sha256:currentExists?fileHash(p):null};if(relaxed&&allAllowed.has(p)){const committed=headState(p);if(current.exists!==committed.exists||current.sha256!==committed.sha256)fail('staleness_source_file_uncommitted')}else if(current.exists!==entry.exists||current.sha256!==entry.sha256)fail('staleness_source_file_sha256')} const semantic=JSON.parse(JSON.stringify(data)); delete semantic.contract_snapshot.sidecar_semantic_sha256; delete semantic.contract_snapshot.diagnosis_artifact_sha256; delete semantic.staleness.diagnosis_artifact; const sidecarSemanticHash=hash(canon(semantic)); const diagnosisSemanticHash=normalizedDiagnosisHash(diagnosisPath); if(snap.sidecar_semantic_sha256!==sidecarSemanticHash||snap.diagnosis_semantic_sha256!==diagnosisSemanticHash)fail('contract_snapshot_semantic'); if(fs.existsSync(inside(reviewPath))){const prev=readJson(reviewPath);if(prev.verdict==='REQUEST_REANALYSIS'){const ps=prev.contract_snapshot||{};const unchanged=modern(ps)?ps.sidecar_semantic_sha256===sidecarSemanticHash&&ps.diagnosis_semantic_sha256===diagnosisSemanticHash:legacySame(ps,snap);if(unchanged)fail('reanalysis_unchanged');const cm=fs.statSync(inside(contractPath)).mtimeMs;const dm=fs.statSync(inside(diagnosisPath)).mtimeMs;const rm=fs.statSync(inside(reviewPath)).mtimeMs;if(cm<=rm||dm<=rm)fail('reanalysis_not_rewritten')}} \""
|
|
679
|
-
- command_success: "node -e \"const fs=require('node:fs'); const path=require('node:path'); const mod=process.
|
|
693
|
+
- command_success: "node -e \"const fs=require('node:fs'); const cp=require('node:child_process'); const crypto=require('node:crypto'); const path=require('node:path'); const mod=process.argv[1]; const allowed=['BUG','UNIMPLEMENTED','INFRA','REMOVED','TEST_BUG']; const action=['BUG','UNIMPLEMENTED','INFRA','TEST_BUG']; const fail=m=>{console.error('INTENTDNA_DIAGNOSTIC:'+m);process.exit(1)}; const same=(a,b)=>Array.isArray(a)&&a.length===b.length&&a.every((v,i)=>v===b[i]); const legacySame=(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 modern=s=>s&&typeof s.sidecar_semantic_sha256==='string'&&typeof s.diagnosis_semantic_sha256==='string'; const root=fs.realpathSync(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');let stat=null;try{stat=fs.lstatSync(r)}catch(e){if(!e||e.code!=='ENOENT')fail('path_unreadable')}if(stat){if(stat.isSymbolicLink())fail('path_symlink');const real=fs.realpathSync(r);if(real!==root&&!real.startsWith(root+path.sep))fail('path_escape')}return r}; const rel=p=>path.relative(root,inside(p)).split(path.sep).join('/')||'.'; const under=(p,base)=>p===base||p.startsWith(base+'/'); const readJson=p=>JSON.parse(fs.readFileSync(inside(p),'utf8')); const hash=v=>crypto.createHash('sha256').update(v).digest('hex'); const fileHash=p=>hash(fs.readFileSync(inside(p))); const normalizedDiagnosisHash=p=>hash(fs.readFileSync(inside(p),'utf8').replace(/\\s+/g,' ').trim()); const canon=v=>Array.isArray(v)?'['+v.map(canon).join(',')+']':v&&typeof v==='object'?'{'+Object.keys(v).sort().map(k=>JSON.stringify(k)+':'+canon(v[k])).join(',')+'}':JSON.stringify(v); 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 progressPath='.dna/fix-progress/'+mod+'.json'; const diagnosisPath='.dna/specs/diagnosis-'+mod+'.md'; const behaviorPath='docs/behavior/'+mod+'.md'; const v1Root=rel(process.argv[2]); const v2Root=rel(process.argv[3]); if(v1Root==='.'||v2Root==='.'||under(v1Root,v2Root)||under(v2Root,v1Root))fail('configured_roots'); const data=readJson(contractPath); if(data.contract_version!=='diagnosis-contract/v2')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 idOk=id=>typeof id==='string'&&/^[A-Za-z0-9][A-Za-z0-9_.:-]*$/.test(id); const ids=new Set(); for(const f of observed){if(!idOk(f&&f.id)||ids.has(f.id))fail('observed_failure_ids');ids.add(f.id)} const workIds=new Set(); for(const item of work){if(!idOk(item&&item.id)||workIds.has(item.id))fail('work_item_ids');workIds.add(item.id)} 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)rel(p);if(f.kind!==undefined)fail('observed_failure_kind_forbidden');failureById.set(f.id,f)} const mapped=new Set(); const allAllowed=new Set(); const v1Evidence=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)rel(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');const v2Evidence=new Set(item.v2_evidence_paths.map(rel));for(const p of item.v1_evidence_paths){const n=rel(p);if(!under(n,v1Root))fail('work_item_v1_evidence_root');v1Evidence.add(n)}for(const p of item.v2_evidence_paths)rel(p);if(!Array.isArray(item.allowed_change_paths)||!item.allowed_change_paths.length)fail('work_item_allowed_change_paths');const approved=new Set();for(const p of item.allowed_change_paths){if(typeof p!=='string'||['*','?','[',']'].some(c=>p.includes(c)))fail('work_item_allowed_change_path_pattern');const n=rel(p);const r=inside(p);if(under(n,v1Root))fail('work_item_allowed_change_path_v1');if(!(under(n,v2Root)||under(n,'test')||under(n,'lib')))fail('work_item_allowed_change_path_root');if(fs.existsSync(r)?!fs.statSync(r).isFile():!path.basename(n).includes('.'))fail('work_item_allowed_change_path_not_file');if(!v2Evidence.has(n))fail('work_item_allowed_change_path_not_evidence');if(approved.has(n))fail('work_item_allowed_change_path_duplicate');approved.add(n);allAllowed.add(n)}}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'); for(const k of ['failed','skipped','hung']){if(!Number.isInteger(counts[k])||counts[k]<0)fail('baseline_count_'+k)} if(counts.failed+counts.skipped+counts.hung!==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 snap=data.contract_snapshot||{}; const baselineHash=fileHash(data.baseline.source_path); const diagnosisHash=fileHash(diagnosisPath); 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!==baselineHash)fail('baseline_sha256'); if(snap.diagnosis_artifact_sha256!==diagnosisHash)fail('diagnosis_sha256'); const stale=data.staleness; if(!stale||stale.is_stale!==false)fail('stale_contract'); const source=(entry,p,n,expected)=>{if(!entry||entry.path!==p)fail('staleness_'+n+'_path');if(entry.sha256!==expected)fail('staleness_'+n+'_sha256')}; source(stale.behavior_doc,behaviorPath,'behavior_doc',fileHash(behaviorPath)); source(stale.baseline,data.baseline.source_path,'baseline',baselineHash); source(stale.diagnosis_artifact,diagnosisPath,'diagnosis_artifact',diagnosisHash); const expectedSources=[...new Set(work.filter(i=>action.includes(i.kind)).flatMap(i=>[...i.v1_evidence_paths,...i.v2_evidence_paths]).map(rel))].sort(); if(!Array.isArray(stale.source_files)||stale.source_files.length!==expectedSources.length)fail('staleness_source_files'); let relaxed=false; let head=''; if(fs.existsSync(inside(progressPath))){const progressMtime=fs.statSync(inside(progressPath)).mtimeMs;const artifactMtime=Math.max(fs.statSync(inside(contractPath)).mtimeMs,fs.statSync(inside(diagnosisPath)).mtimeMs,fs.existsSync(inside(reviewPath))?fs.statSync(inside(reviewPath)).mtimeMs:0);if(progressMtime>=artifactMtime){const progress=readJson(progressPath);head=cp.execFileSync('git',['rev-parse','HEAD'],{encoding:'utf8'}).trim();if(progress.module!==mod||progress.verdict!=='CONTINUE'||progress.module_complete!==false||progress.diagnosis_invalid!==false)fail('progress_state');if(progress.diagnosis_artifact!==diagnosisPath||progress.review_artifact!=='.dna/reviews/'+mod+'-review.json')fail('progress_artifacts');if(progress.git_head!==head||progress.last_fix_commit!==head)fail('progress_commit');if(!Number.isInteger(progress.round)||progress.round<1||typeof progress.run_id!=='string'||!progress.run_id.trim())fail('progress_provenance');const progressTime=Date.parse(progress.updated_at);if(!Number.isFinite(progressTime)||progressTime+300000<artifactMtime)fail('progress_updated_at');relaxed=true}} const headState=p=>{try{const body=cp.execFileSync('git',['show',head+':'+p],{encoding:null,stdio:['ignore','pipe','ignore']});return {exists:true,sha256:hash(body)}}catch{return {exists:false,sha256:null}}}; const seenSources=new Set(); for(let i=0;i<expectedSources.length;i++){const p=expectedSources[i];const entry=stale.source_files[i];if(entry&&seenSources.has(entry.path))fail('staleness_source_file_duplicate');if(!entry||entry.path!==p)fail('staleness_source_file_path');seenSources.add(entry.path);if(typeof entry.exists!=='boolean')fail('staleness_source_file_exists');if(entry.exists?(typeof entry.sha256!=='string'||!/^[a-f0-9]{64}$/.test(entry.sha256)):entry.sha256!==null)fail('staleness_source_file_snapshot');if(v1Evidence.has(p)&&entry.exists!==true)fail('staleness_v1_source_missing');const r=inside(p);const currentExists=fs.existsSync(r);if(currentExists&&!fs.statSync(r).isFile())fail('staleness_source_file_not_file');const current={exists:currentExists,sha256:currentExists?fileHash(p):null};if(relaxed&&allAllowed.has(p)){const committed=headState(p);if(current.exists!==committed.exists||current.sha256!==committed.sha256)fail('staleness_source_file_uncommitted')}else if(current.exists!==entry.exists||current.sha256!==entry.sha256)fail('staleness_source_file_sha256')} const semantic=JSON.parse(JSON.stringify(data)); delete semantic.contract_snapshot.sidecar_semantic_sha256; delete semantic.contract_snapshot.diagnosis_artifact_sha256; delete semantic.staleness.diagnosis_artifact; const sidecarSemanticHash=hash(canon(semantic)); const diagnosisSemanticHash=normalizedDiagnosisHash(diagnosisPath); if(snap.sidecar_semantic_sha256!==sidecarSemanticHash||snap.diagnosis_semantic_sha256!==diagnosisSemanticHash)fail('contract_snapshot_semantic'); if(fs.existsSync(inside(reviewPath))){const prev=readJson(reviewPath);if(prev.verdict==='REQUEST_REANALYSIS'){const ps=prev.contract_snapshot||{};const unchanged=modern(ps)?ps.sidecar_semantic_sha256===sidecarSemanticHash&&ps.diagnosis_semantic_sha256===diagnosisSemanticHash:legacySame(ps,snap);if(unchanged)fail('reanalysis_unchanged');const cm=fs.statSync(inside(contractPath)).mtimeMs;const dm=fs.statSync(inside(diagnosisPath)).mtimeMs;const rm=fs.statSync(inside(reviewPath)).mtimeMs;if(cm<=rm||dm<=rm)fail('reanalysis_not_rewritten')}} \" \"$ARGUMENTS\" \"{{v1_path}}\" \"{{v2_path}}\""
|
|
694
|
+
- command_success: "node -e \"const fs=require('node:fs'); const path=require('node:path'); const mod=process.argv[1]; const allowed=['BUG','UNIMPLEMENTED','INFRA','REMOVED','TEST_BUG']; const fail=m=>{console.error('INTENTDNA_DIAGNOSTIC:'+m);process.exit(1)}; 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.sidecar_semantic_sha256===b.sidecar_semantic_sha256&&a.diagnosis_semantic_sha256===b.diagnosis_semantic_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=fs.realpathSync(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');let stat=null;try{stat=fs.lstatSync(r)}catch(e){if(!e||e.code!=='ENOENT')fail('path_unreadable')}if(stat){if(stat.isSymbolicLink())fail('path_symlink');const real=fs.realpathSync(r);if(real!==root&&!real.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); const snap=data.contract_snapshot||{}; if(data.contract_version!=='diagnosis-contract/v2'||data.module!==mod)fail('contract_version'); 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'); const updated=Date.parse(review.updated_at); if(!Number.isFinite(updated))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){const r=inside(p);if(!fs.existsSync(r)||!fs.statSync(r).isFile())fail('review_evidence_paths')} 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||updated+300000<Math.max(cm,dm)||Math.abs(rm-updated)>300000)fail('review_stale'); \" \"$ARGUMENTS\""
|
|
680
695
|
handoff:
|
|
681
696
|
consumes:
|
|
682
697
|
- type: file
|
|
698
|
+
artifact_id: diagnosis-spec
|
|
683
699
|
path: ".dna/specs/diagnosis-$ARGUMENTS.md"
|
|
684
700
|
description: "Diagnosis spec from analyzer"
|
|
685
701
|
- type: file
|
|
702
|
+
artifact_id: diagnosis-contract
|
|
686
703
|
path: ".dna/specs/diagnosis-$ARGUMENTS.contract.json"
|
|
687
704
|
description: "Diagnosis contract sidecar from analyzer"
|
|
688
705
|
produces:
|
|
689
706
|
- type: file
|
|
707
|
+
artifact_id: diagnosis-review
|
|
690
708
|
path: ".dna/specs/diagnosis-$ARGUMENTS.review.json"
|
|
691
709
|
description: "Durable diagnosis review verdict with independent contract validation (APPROVE or REQUEST_REANALYSIS)"
|
|
692
710
|
- type: summary
|
|
711
|
+
artifact_id: diagnosis-review-summary
|
|
693
712
|
required: false
|
|
694
713
|
description: "Review verdict (APPROVE or REQUEST_REANALYSIS)"
|
|
695
714
|
|
|
@@ -756,7 +775,7 @@ workflows:
|
|
|
756
775
|
After each fix: run tests ONCE for that specific file unless the shared boundary rule requires the affected module behavior suite.
|
|
757
776
|
Commit only the round's coherent changes, with message including what changed, why, and which tests were targeted. The completion gate compares every file in the current HEAD commit with the approved allowed_change_paths set and rejects any extra path.
|
|
758
777
|
completion:
|
|
759
|
-
- command_success: "node -e \"const fs=require('node:fs'); const cp=require('node:child_process'); const crypto=require('node:crypto'); const path=require('node:path'); const mod=process.
|
|
778
|
+
- command_success: "node -e \"const fs=require('node:fs'); const cp=require('node:child_process'); const crypto=require('node:crypto'); const path=require('node:path'); const mod=process.argv[1]; const fail=m=>{console.error('INTENTDNA_DIAGNOSTIC:fix_scope_'+m);process.exit(1)}; const root=fs.realpathSync(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');let stat=null;try{stat=fs.lstatSync(r)}catch(e){if(!e||e.code!=='ENOENT')fail('path_unreadable')}if(stat){if(stat.isSymbolicLink())fail('path_symlink');const real=fs.realpathSync(r);if(real!==root&&!real.startsWith(root+path.sep))fail('path_escape')}return r}; const rel=p=>path.relative(root,inside(p)).split(path.sep).join('/')||'.'; const under=(p,base)=>p===base||p.startsWith(base+'/'); const hash=v=>crypto.createHash('sha256').update(v).digest('hex'); const canon=v=>Array.isArray(v)?'['+v.map(canon).join(',')+']':v&&typeof v==='object'?'{'+Object.keys(v).sort().map(k=>JSON.stringify(k)+':'+canon(v[k])).join(',')+'}':JSON.stringify(v); const same=(a,b)=>Array.isArray(a)&&Array.isArray(b)&&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.sidecar_semantic_sha256===b.sidecar_semantic_sha256&&a.diagnosis_semantic_sha256===b.diagnosis_semantic_sha256&&a.observed_failure_count===b.observed_failure_count&&a.work_item_count===b.work_item_count&&same(a.allowed_kinds,b.allowed_kinds); 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=JSON.parse(fs.readFileSync(inside(contractPath),'utf8')); if(data.contract_version!=='diagnosis-contract/v2'||data.module!==mod||data.diagnosis_artifact!==diagnosisPath)fail('contract'); const snap=data.contract_snapshot||{}; const semantic=JSON.parse(JSON.stringify(data)); delete semantic.contract_snapshot.sidecar_semantic_sha256; delete semantic.contract_snapshot.diagnosis_artifact_sha256; delete semantic.staleness.diagnosis_artifact; if(snap.sidecar_semantic_sha256!==hash(canon(semantic)))fail('contract_snapshot'); const review=JSON.parse(fs.readFileSync(inside(reviewPath),'utf8')); if(review.contract_version!=='diagnosis-contract-review/v1'||review.contract_valid!==true||review.verdict!=='APPROVE')fail('review'); if(review.contract_artifact_reviewed!==contractPath||review.artifact_reviewed!==diagnosisPath||!snapSame(review.contract_snapshot,snap))fail('review_snapshot'); if(!Array.isArray(review.evidence_paths)||!review.evidence_paths.length)fail('review_evidence'); for(const p of review.evidence_paths){const r=inside(p);if(!fs.existsSync(r)||!fs.statSync(r).isFile())fail('review_evidence')} const cm=fs.statSync(inside(contractPath)).mtimeMs; const rm=fs.statSync(inside(reviewPath)).mtimeMs; const updated=Date.parse(review.updated_at); if(!Number.isFinite(updated)||rm<cm||updated+300000<cm||Math.abs(rm-updated)>300000)fail('review_stale'); const v1=rel(process.argv[2]); const v2=rel(process.argv[3]); const action=new Set(['BUG','UNIMPLEMENTED','INFRA','TEST_BUG']); const allowed=new Set(); for(const item of data.work_items||[]){if(!action.has(item.kind))continue;const evidence=new Set((item.v2_evidence_paths||[]).map(rel));for(const p of item.allowed_change_paths||[]){if(typeof p!=='string'||['*','?','[',']'].some(c=>p.includes(c)))fail('allowed_path_pattern');const n=rel(p);const r=inside(p);if(under(n,v1))fail('v1_change');if(!(under(n,v2)||under(n,'test')||under(n,'lib')))fail('allowed_path_root');if(fs.existsSync(r)?!fs.statSync(r).isFile():!path.basename(n).includes('.'))fail('allowed_path_not_file');if(!evidence.has(n))fail('allowed_path_not_evidence');allowed.add(n)}} if(!allowed.size)fail('allowed_paths'); const head=cp.execFileSync('git',['rev-parse','HEAD'],{encoding:'utf8'}).trim(); const output=cp.execFileSync('git',['diff-tree','--root','--no-commit-id','--name-only','-r',head],{encoding:'utf8'}).trim(); const changed=output?output.split(/\\r?\\n/).filter(Boolean):[]; if(!changed.length)fail('empty_commit'); for(const p of changed){const n=rel(p);if(under(n,v1))fail('v1_change');if(!allowed.has(n))fail('outside_diagnosis')} const dirty=cp.execFileSync('git',['status','--porcelain','--untracked-files=all','--',...allowed],{encoding:'utf8'}).trim(); if(dirty)fail('uncommitted_allowed_path'); \" \"$ARGUMENTS\" \"{{v1_path}}\" \"{{v2_path}}\""
|
|
760
779
|
handoff:
|
|
761
780
|
consumes:
|
|
762
781
|
- type: file
|
|
@@ -954,6 +973,14 @@ workflows:
|
|
|
954
973
|
- CONTINUE: this round made progress or review found safe, actionable, in-diagnosis issues that should continue in the next fix invocation
|
|
955
974
|
- REQUEST_CHANGES: verification found issues in this round's changes
|
|
956
975
|
- BLOCKED: review blocks verification, user confirmation is needed, evidence conflicts, unsafe/out-of-scope changes, or repeated no-progress prevents safe continuation
|
|
976
|
+
canonical_runtime:
|
|
977
|
+
outputs:
|
|
978
|
+
- type: state
|
|
979
|
+
artifact_id: fix-progress-verdict
|
|
980
|
+
schema_ref: intentdna.frw.fix-progress.v1
|
|
981
|
+
description: "Canonical typed fix-loop verdict and provenance summary"
|
|
982
|
+
prompt_suffix: |
|
|
983
|
+
Canonical runtime output contract: return the exact progress JSON as a structured output named fix-progress-verdict with schema_ref intentdna.frw.fix-progress.v1 in the final outputs envelope. The verdict may also be DIAGNOSIS_CONTRACT_INVALID or PROVENANCE_INVALID when the corresponding typed failure is detected. This is in addition to writing the declared progress files.
|
|
957
984
|
handoff:
|
|
958
985
|
consumes:
|
|
959
986
|
- type: file
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# Windows Job Keeper
|
|
2
|
+
|
|
3
|
+
This helper owns one unnamed Windows Job Object and launches exactly one pre-resolved
|
|
4
|
+
provider executable into it. It has no non-system runtime dependency.
|
|
5
|
+
|
|
6
|
+
## Input protocol
|
|
7
|
+
|
|
8
|
+
The helper reads a strict, bounded UTF-8 line protocol from standard input. Paths
|
|
9
|
+
and arguments are lowercase or uppercase hexadecimal UTF-8 so newlines and other
|
|
10
|
+
delimiters cannot be ambiguous. NUL and invalid UTF-8 are rejected.
|
|
11
|
+
|
|
12
|
+
```text
|
|
13
|
+
IDNA_WINDOWS_JOB_KEEPER_V1
|
|
14
|
+
exe:<hex UTF-8 absolute executable path>
|
|
15
|
+
cwd:<hex UTF-8 working directory, or empty>
|
|
16
|
+
stdin:<hex UTF-8 absolute input file path, or empty for NUL>
|
|
17
|
+
stdout:<hex UTF-8 absolute new output file path>
|
|
18
|
+
stderr:<hex UTF-8 absolute new error file path>
|
|
19
|
+
mode:argv
|
|
20
|
+
argc:<decimal 0..4096>
|
|
21
|
+
arg:<hex UTF-8 argument 0>
|
|
22
|
+
...
|
|
23
|
+
envc:<decimal 0..4096>
|
|
24
|
+
env:<hex UTF-8 key=value entry 0>
|
|
25
|
+
...
|
|
26
|
+
natural_drain_grace_ms:<decimal 0..300000>
|
|
27
|
+
forced_drain_grace_ms:<decimal 0..300000>
|
|
28
|
+
end
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
For verbatim command-line mode, replace the `mode:argv`, `argc`, and `arg` records
|
|
32
|
+
with:
|
|
33
|
+
|
|
34
|
+
```text
|
|
35
|
+
mode:verbatim
|
|
36
|
+
command:<hex UTF-8 exact command line>
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
`argv` mode constructs a Windows CRT-compatible command line from the resolved
|
|
40
|
+
executable and argument vector. `verbatim` mode passes the trusted resolver's exact
|
|
41
|
+
command line to `CreateProcessW`; the decoded command is limited to 32766 UTF-16
|
|
42
|
+
code units because the Windows limit includes the terminating NUL. It is not quoted
|
|
43
|
+
or rewritten by the helper.
|
|
44
|
+
|
|
45
|
+
Environment entries represent the complete effective provider environment; the
|
|
46
|
+
helper never inherits its own ambient environment. Each entry must have a nonempty
|
|
47
|
+
key followed by `=`, keys cannot contain `=`, and case-insensitive duplicate keys
|
|
48
|
+
are rejected. Entries are sorted case-insensitively and materialized as a bounded
|
|
49
|
+
double-NUL UTF-16 block for `CREATE_UNICODE_ENVIRONMENT`. The block is limited to
|
|
50
|
+
32767 UTF-16 code units.
|
|
51
|
+
|
|
52
|
+
The helper opens provider stdin with `OPEN_EXISTING`; stdout and stderr use
|
|
53
|
+
`CREATE_NEW` and reject an existing destination. Handles are inheritable and only
|
|
54
|
+
those three pass through `PROC_THREAD_ATTRIBUTE_HANDLE_LIST`. Keeper stdin remains
|
|
55
|
+
a separate control channel managed by the parent.
|
|
56
|
+
|
|
57
|
+
After `end`, the helper creates the suspended provider, assigns it to the Job, and
|
|
58
|
+
emits `contained_ready`. It does not run the provider until it receives exactly
|
|
59
|
+
`release\n`, allowing the parent to durably persist containment identity first.
|
|
60
|
+
After release, the only valid record is `terminate\n`. EOF at either gate means the
|
|
61
|
+
parent disappeared and kills the Job. Any other record is a protocol error. The
|
|
62
|
+
complete config is limited to 8 MiB and each line to 1 MiB.
|
|
63
|
+
|
|
64
|
+
## Output protocol
|
|
65
|
+
|
|
66
|
+
Standard output contains NDJSON facts. `contained_ready` includes keeper and root
|
|
67
|
+
PIDs and creation `FILETIME`s plus a SHA-256 identifier for the non-exported Job
|
|
68
|
+
nonce. The Job itself is unnamed and cannot be opened by identifier. `released`
|
|
69
|
+
confirms the suspended root was resumed. `completed` includes the
|
|
70
|
+
observed Windows root exit code and is emitted only after Job accounting reports
|
|
71
|
+
zero active processes. Errors are NDJSON on standard error; error cleanup never
|
|
72
|
+
emits `completed`.
|
|
73
|
+
|
|
74
|
+
The provider starts suspended, is assigned to a Job configured with
|
|
75
|
+
`JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE`, and is resumed only after its identity is
|
|
76
|
+
captured and the release gate passes. A normal root exit lets descendants finish
|
|
77
|
+
naturally for `natural_drain_grace_ms`; expiry, terminate, protocol failure, or
|
|
78
|
+
parent EOF terminates the Job. Forced cleanup must reach zero active processes
|
|
79
|
+
within `forced_drain_grace_ms` or fails closed without a completed fact. Closing or
|
|
80
|
+
crashing the helper closes the Job handle and terminates every member.
|