software-defence-factory 0.4.2 → 0.4.4
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/.github/ISSUE_TEMPLATE/factory-task.yml +5 -2
- package/bin/software-defence-factory.mjs +26 -39
- package/docs/quickstart.md +17 -2
- package/docs/recovery.md +78 -0
- package/docs/setup.md +17 -5
- package/factory/bounded-log.mjs +37 -0
- package/factory/execution-profile.mjs +39 -0
- package/factory/executor.mjs +16 -21
- package/factory/image-install.mjs +189 -0
- package/factory/installation-lock.mjs +15 -0
- package/factory/processes.mjs +29 -6
- package/factory/queue.mjs +21 -6
- package/factory/server.mjs +3 -4
- package/factory/services.mjs +2 -2
- package/factory/supervisor.mjs +10 -2
- package/factory/ui/assets/{index-QZs3WmRB.js → index-BS9sWkVd.js} +1 -1
- package/factory/ui/index.html +1 -1
- package/kit/README.md +4 -0
- package/kit/repository.md +86 -0
- package/package.json +4 -2
- package/scripts/export-kit.mjs +1 -1
- package/scripts/probe-diagnostics.mjs +37 -0
- package/scripts/probe-review.mjs +48 -0
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// Opt-in Docker proof, restricted to the documented synthetic fixture.
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { readFileSync, writeFileSync, readdirSync, existsSync } from 'node:fs';
|
|
4
|
+
import { resolve, join } from 'node:path';
|
|
5
|
+
import { configAt, api, save, json, run, stream, sleep, ROOT } from '../factory/lib.mjs';
|
|
6
|
+
|
|
7
|
+
const state=resolve(process.argv[2] || ''), original=configAt(state);
|
|
8
|
+
assert.equal(original.agent,'mock','Use a separate synthetic installation');
|
|
9
|
+
assert.equal(readFileSync(join(original.repo,'value.txt'),'utf8'),'broken\n');
|
|
10
|
+
assert(!(await api(state,'/api/v1/status')).jobs.some(j=>['queued','running','awaiting_approval'].includes(j.state)),'Finish active fixture jobs first');
|
|
11
|
+
const agent=`import {writeFileSync} from 'node:fs';
|
|
12
|
+
let prompt='';for await(const chunk of process.stdin)prompt+=chunk;
|
|
13
|
+
const revised=prompt.includes('SYNTHETIC_REVIEW_FIXED');
|
|
14
|
+
if(process.env.FACTORY_PHASE==='build'){writeFileSync('/workspace/value.txt','fixed\\n');if(revised)writeFileSync('/workspace/revision.txt','review concern fixed\\n');}
|
|
15
|
+
if(process.env.FACTORY_PHASE==='review')writeFileSync('/output/review.json',JSON.stringify({verdict:revised?'pass':prompt.includes('REVIEW_BLOCKED')?'blocked':'changes',summary:'Synthetic independent review fixture; no model judgment.',findings:revised?[]:[{message:'Revise the synthetic candidate'}]}));
|
|
16
|
+
writeFileSync('/output/agent-report.md','Synthetic phase complete; no model call.\\n');`;
|
|
17
|
+
const snapshot=async id=>(await api(state,'/api/v1/status')).jobs.find(j=>j.id===id);
|
|
18
|
+
async function wait(id,stateWanted){for(let i=0;i<160;i++){const j=await snapshot(id);if(j.state===stateWanted)return j;if(['failed','cancelled','interrupted'].includes(j.state)&&j.state!==stateWanted)throw new Error(j.runs.at(-1).summary);await sleep(250);}throw new Error('Fixture timed out');}
|
|
19
|
+
const results=[];
|
|
20
|
+
try {
|
|
21
|
+
// Inline the deterministic fixture in the selected Node image; no inference or image rebuild.
|
|
22
|
+
save(join(state,'factory.json'),{...original,command:['node','--input-type=module','-e',agent]});
|
|
23
|
+
for(const verdict of ['changes','blocked']) {
|
|
24
|
+
const {id}=await api(state,'/api/v1/jobs',{workflow:'software',repository:'app',title:`Review revision ${verdict}`,spec:`SYNTHETIC_REVIEW_${verdict.toUpperCase()}`});
|
|
25
|
+
let job=await wait(id,'failed');const failed=job.runs.at(-1),folder=join(state,'jobs',id);
|
|
26
|
+
assert.equal(failed.command,'review');assert.equal(failed.review_verdict,verdict);assert.equal(job.can_request_changes,true);
|
|
27
|
+
const first=json(join(folder,'candidate.json'));
|
|
28
|
+
const proofPath=join(folder,'artifacts',failed.id,'review.json'), priorProof=readFileSync(proofPath,'utf8');
|
|
29
|
+
await assert.rejects(api(state,`/api/v1/jobs/${id}/approve`,{run_id:failed.id}),/not awaiting/);
|
|
30
|
+
await assert.rejects(api(state,`/api/v1/jobs/${id}/request_changes`,{run_id:failed.id,feedback:''}),/feedback/);
|
|
31
|
+
const feedback=join(state,`feedback-${verdict}.md`);writeFileSync(feedback,'SYNTHETIC_REVIEW_FIXED: address the synthetic review concern.\n',{mode:0o600});
|
|
32
|
+
await stream(process.execPath,[join(ROOT,'bin/software-defence-factory.mjs'),'revise',id,'--file',feedback,'--state',state],{env:{...process.env,SDF_BOOTSTRAPPED:'1'}});
|
|
33
|
+
await assert.rejects(api(state,`/api/v1/jobs/${id}/request_changes`,{run_id:failed.id,feedback:'duplicate'}),/changed|reviewed|already changing/);
|
|
34
|
+
job=await wait(id,'awaiting_approval');const next=json(join(folder,'candidate.json'));
|
|
35
|
+
assert.notEqual(next.head,first.head);assert.equal(readFileSync(proofPath,'utf8'),priorProof);
|
|
36
|
+
const prior=job.runs.find(r=>r.id===failed.id);assert.equal(prior.state,'failed');assert.equal(prior.summary,failed.summary);assert.equal(prior.review_verdict,verdict);
|
|
37
|
+
const archived=readdirSync(folder).find(n=>n.startsWith('previous-checkout-'));
|
|
38
|
+
assert.equal(run('git',['-C',join(folder,archived),'rev-parse','HEAD']),first.head);
|
|
39
|
+
assert.equal(json(join(folder,'checks.json')).head,next.head);assert.equal(json(join(folder,'review.json')).head,next.head);
|
|
40
|
+
assert(!existsSync(join(folder,'accepted.json')));
|
|
41
|
+
await assert.rejects(api(state,`/api/v1/jobs/${id}/approve`,{run_id:failed.id}),/changed/);
|
|
42
|
+
await api(state,`/api/v1/jobs/${id}/approve`,{run_id:job.runs.at(-1).id});await wait(id,'succeeded');
|
|
43
|
+
assert.equal(json(join(folder,'accepted.json')).head,next.head);
|
|
44
|
+
results.push({verdict,job:id,base:first.base,firstHead:first.head,revisedHead:next.head,passed:true});
|
|
45
|
+
console.log(`PASS ${verdict}: failed review -> CLI revision -> fresh checks/review -> current approval; previous candidate and evidence retained`);
|
|
46
|
+
}
|
|
47
|
+
save(join(state,'review-qualification.json'),{synthetic:true,results});
|
|
48
|
+
} finally {save(join(state,'factory.json'),original);}
|