automated-qa 0.11.0

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.
Files changed (187) hide show
  1. package/LICENSE +22 -0
  2. package/NOTICE +9 -0
  3. package/README.md +100 -0
  4. package/SKILL.md +65 -0
  5. package/agent/prebuilt/darwin-universal/qa-agent +0 -0
  6. package/agent/prebuilt/linux-arm64/qa-agent +0 -0
  7. package/agent/prebuilt/linux-x64/qa-agent +0 -0
  8. package/agent/release-manifest.json +31 -0
  9. package/bin/autonomous-cache.mjs +2 -0
  10. package/bin/bench-generate.mjs +232 -0
  11. package/bin/bench-graph-query.mjs +2 -0
  12. package/bin/bench-score.mjs +10 -0
  13. package/bin/classify.mjs +15 -0
  14. package/bin/coherence-playwright.mjs +1 -0
  15. package/bin/coherence-runner.mjs +6 -0
  16. package/bin/crud-runner.mjs +12 -0
  17. package/bin/experiment-executor.mjs +10 -0
  18. package/bin/experiment-runner.mjs +3 -0
  19. package/bin/inventory.mjs +17 -0
  20. package/bin/probe.js +879 -0
  21. package/bin/qa.mjs +16 -0
  22. package/bin/rules.mjs +5 -0
  23. package/bin/run-benchmarks.mjs +2 -0
  24. package/bin/runtime-packs.mjs +3 -0
  25. package/bin/semantic-explorer.mjs +3 -0
  26. package/bin/shadow-collector.mjs +3 -0
  27. package/bin/shadow-replay.mjs +6 -0
  28. package/bin/soak-marathon.mjs +15 -0
  29. package/bin/sweep.mjs +43 -0
  30. package/bin/validate-priors.mjs +3 -0
  31. package/bin/verify-artifacts.mjs +3 -0
  32. package/bin/verify-manifest.mjs +2 -0
  33. package/install-skill.sh +45 -0
  34. package/lib/qa/agent-availability.mjs +1 -0
  35. package/lib/qa/agent-result.mjs +1 -0
  36. package/lib/qa/analysis.mjs +5 -0
  37. package/lib/qa/atlas-coverage.mjs +11 -0
  38. package/lib/qa/atlas-v1.mjs +1 -0
  39. package/lib/qa/atlas.mjs +2 -0
  40. package/lib/qa/auth/coordinator.mjs +1 -0
  41. package/lib/qa/auth/launcher.mjs +3 -0
  42. package/lib/qa/auth/lease.mjs +1 -0
  43. package/lib/qa/auth/oracle.mjs +1 -0
  44. package/lib/qa/auth/personas.mjs +3 -0
  45. package/lib/qa/autonomous-cache.mjs +3 -0
  46. package/lib/qa/block-ledger.mjs +1 -0
  47. package/lib/qa/capture-report.mjs +63 -0
  48. package/lib/qa/ci.mjs +11 -0
  49. package/lib/qa/cli.mjs +538 -0
  50. package/lib/qa/coherence/analyze.mjs +1 -0
  51. package/lib/qa/coherence/cache-key.mjs +6 -0
  52. package/lib/qa/coherence/causes.mjs +1 -0
  53. package/lib/qa/coherence/concurrency.mjs +1 -0
  54. package/lib/qa/coherence/contracts.mjs +1 -0
  55. package/lib/qa/coherence/entity-aliases.mjs +1 -0
  56. package/lib/qa/coherence/enums.mjs +1 -0
  57. package/lib/qa/coherence/family-report.mjs +1 -0
  58. package/lib/qa/coherence/flow-pool.mjs +1 -0
  59. package/lib/qa/coherence/graph.mjs +1 -0
  60. package/lib/qa/coherence/identity.mjs +1 -0
  61. package/lib/qa/coherence/ir.mjs +4 -0
  62. package/lib/qa/coherence/navigation.mjs +1 -0
  63. package/lib/qa/coherence/observe.mjs +2 -0
  64. package/lib/qa/coherence/offline.mjs +1 -0
  65. package/lib/qa/coherence/plan-join.mjs +1 -0
  66. package/lib/qa/coherence/provenance.mjs +2 -0
  67. package/lib/qa/coherence/report.mjs +1 -0
  68. package/lib/qa/coherence/representations.mjs +1 -0
  69. package/lib/qa/coherence/risk.mjs +2 -0
  70. package/lib/qa/coherence/structural-js.mjs +11 -0
  71. package/lib/qa/coherence/symbols.mjs +6 -0
  72. package/lib/qa/coherence/tanstack.mjs +4 -0
  73. package/lib/qa/coherence/views.mjs +1 -0
  74. package/lib/qa/coherence-planner.mjs +1 -0
  75. package/lib/qa/command-catalog.mjs +2 -0
  76. package/lib/qa/commands/dev.mjs +1 -0
  77. package/lib/qa/commands/fix.mjs +1 -0
  78. package/lib/qa/commands/impl/analysis.mjs +1 -0
  79. package/lib/qa/commands/impl/atlas.mjs +1 -0
  80. package/lib/qa/commands/impl/coherence.mjs +1 -0
  81. package/lib/qa/commands/impl/crud.mjs +2 -0
  82. package/lib/qa/commands/impl/doctor.mjs +1 -0
  83. package/lib/qa/commands/impl/experiments.mjs +1 -0
  84. package/lib/qa/commands/impl/fp.mjs +1 -0
  85. package/lib/qa/commands/impl/graph.mjs +1 -0
  86. package/lib/qa/commands/impl/invariants.mjs +1 -0
  87. package/lib/qa/commands/impl/lanes.mjs +7 -0
  88. package/lib/qa/commands/impl/ledger.mjs +11 -0
  89. package/lib/qa/commands/impl/loop.mjs +1 -0
  90. package/lib/qa/commands/impl/marathon.mjs +1 -0
  91. package/lib/qa/commands/impl/next.mjs +38 -0
  92. package/lib/qa/commands/impl/report.mjs +4 -0
  93. package/lib/qa/commands/impl/research.mjs +4 -0
  94. package/lib/qa/commands/impl/selftest.mjs +1 -0
  95. package/lib/qa/commands/impl/serve.mjs +1 -0
  96. package/lib/qa/commands/impl/shadow.mjs +1 -0
  97. package/lib/qa/commands/impl/stability.mjs +1 -0
  98. package/lib/qa/commands/impl/triage.mjs +1 -0
  99. package/lib/qa/commands/impl/validate.mjs +4 -0
  100. package/lib/qa/commands/impl/version.mjs +1 -0
  101. package/lib/qa/commands/impl/watch.mjs +1 -0
  102. package/lib/qa/commands/init.mjs +6 -0
  103. package/lib/qa/commands/measure.mjs +1 -0
  104. package/lib/qa/commands/run.mjs +1 -0
  105. package/lib/qa/commands/show.mjs +1 -0
  106. package/lib/qa/commands/verify.mjs +6 -0
  107. package/lib/qa/deepening.mjs +1 -0
  108. package/lib/qa/denominators.mjs +1 -0
  109. package/lib/qa/depth.mjs +4 -0
  110. package/lib/qa/doctor.mjs +2 -0
  111. package/lib/qa/experiments-default.mjs +1 -0
  112. package/lib/qa/fix-lanes.mjs +3 -0
  113. package/lib/qa/forced-states.mjs +1 -0
  114. package/lib/qa/fp.mjs +2 -0
  115. package/lib/qa/fs.mjs +6 -0
  116. package/lib/qa/graph-schema.mjs +2 -0
  117. package/lib/qa/graph.mjs +3 -0
  118. package/lib/qa/harness-plan.mjs +3 -0
  119. package/lib/qa/init.mjs +5 -0
  120. package/lib/qa/integrity.mjs +1 -0
  121. package/lib/qa/invariants.mjs +84 -0
  122. package/lib/qa/jcs.mjs +1 -0
  123. package/lib/qa/lanes.mjs +1 -0
  124. package/lib/qa/loop.mjs +7 -0
  125. package/lib/qa/marathon-coherence.mjs +1 -0
  126. package/lib/qa/marathon-phases.mjs +1 -0
  127. package/lib/qa/marathon-wait.mjs +3 -0
  128. package/lib/qa/marathon.mjs +5 -0
  129. package/lib/qa/mode-coverage.mjs +1 -0
  130. package/lib/qa/mutation-policy.mjs +1 -0
  131. package/lib/qa/pattern-ledger.mjs +3 -0
  132. package/lib/qa/pending.mjs +1 -0
  133. package/lib/qa/proc.mjs +4 -0
  134. package/lib/qa/recommend.mjs +1 -0
  135. package/lib/qa/release-manifest.mjs +1 -0
  136. package/lib/qa/report-html.mjs +178 -0
  137. package/lib/qa/reports.mjs +2 -0
  138. package/lib/qa/request-evidence.mjs +1 -0
  139. package/lib/qa/research.mjs +20 -0
  140. package/lib/qa/routing.mjs +1 -0
  141. package/lib/qa/runs.mjs +1 -0
  142. package/lib/qa/schema.mjs +1 -0
  143. package/lib/qa/serve.mjs +4 -0
  144. package/lib/qa/server.mjs +1 -0
  145. package/lib/qa/source-snapshot.mjs +3 -0
  146. package/lib/qa/stability.mjs +3 -0
  147. package/lib/qa/state-evidence.mjs +1 -0
  148. package/lib/qa/sweep-incremental.mjs +1 -0
  149. package/lib/qa/triage.mjs +2 -0
  150. package/lib/qa/verdict.mjs +1 -0
  151. package/lib/qa/watch.mjs +2 -0
  152. package/package.json +62 -0
  153. package/references/ATLAS-COVERAGE.md +231 -0
  154. package/references/atlas-v1.md +318 -0
  155. package/references/atlas.md +338 -0
  156. package/references/verify.crud.example.json +1 -0
  157. package/references/verify.impact.example.json +1 -0
  158. package/references/verify.shadow.example.json +1 -0
  159. package/schemas/agent-result.schema.json +135 -0
  160. package/schemas/analysis.schema.json +15 -0
  161. package/schemas/classify.schema.json +61 -0
  162. package/schemas/coherence-ir.schema.json +1585 -0
  163. package/schemas/coherence-observed.schema.json +1171 -0
  164. package/schemas/coherence-plan.schema.json +77 -0
  165. package/schemas/coherence-risk.schema.json +46 -0
  166. package/schemas/crud-observed.schema.json +118 -0
  167. package/schemas/experiment-execution.schema.json +82 -0
  168. package/schemas/graph-edge.schema.json +22 -0
  169. package/schemas/graph-node.schema.json +81 -0
  170. package/schemas/graph.schema.json +2493 -0
  171. package/schemas/identity-oracle.schema.json +19 -0
  172. package/schemas/inventory.schema.json +2167 -0
  173. package/schemas/lanes-plan.schema.json +52 -0
  174. package/schemas/next.schema.json +54 -0
  175. package/schemas/personas.schema.json +52 -0
  176. package/schemas/research.schema.json +192 -0
  177. package/schemas/run.schema.json +160 -0
  178. package/schemas/shadow-replay.schema.json +68 -0
  179. package/schemas/stability.schema.json +92 -0
  180. package/schemas/sweep.schema.json +239 -0
  181. package/schemas/v1/agent-result.schema.json +106 -0
  182. package/schemas/v1/run.schema.json +96 -0
  183. package/schemas/verify-crud.schema.json +37 -0
  184. package/schemas/verify-read-only.schema.json +37 -0
  185. package/templates/github-workflow.yml +80 -0
  186. package/templates/pre-commit.sample +22 -0
  187. package/verify.sh +385 -0
@@ -0,0 +1 @@
1
+ import S from"node:fs";import p from"node:path";import{EXIT as m}from"../../cli.mjs";import{writeText as b}from"../../fs.mjs";import{table as I}from"../../cli.mjs";import*as i from"../../atlas.mjs";import{buildCoverage as V,summaryLine as $,renderCoverageDoc as L,AtlasParseError as _,readProducerSources as w}from"../../atlas-coverage.mjs";const f=Object.freeze({1:"atlas-v1.md",2:"atlas.md"}),O=Object.freeze({1:i.KIND_ATLAS_V1,2:i.KIND_ATLAS_V2}),E=Object.freeze({1:i.ALSO_COVERS_V1,2:i.ALSO_COVERS_V2});function h(a,e){const c=S.readFileSync(p.join(a,"references",f[e]),"utf8"),n=S.readFileSync(p.join(a,"bin","classify.mjs"),"utf8");return V({atlasMd:c,classifySrc:n,producerSources:w(a),kindAtlas:O[e],alsoCovers:E[e],atlasIdForFn:o=>i.atlasIdFor({...o,atlasVersion:e}),classifyIdRemap:e===1?i.remapV2ToV1:o=>o,reportUnmappedProducers:e===2})}const D={implemented:"implemented",partial:"partial","deliberately-not-implemented":"deliberate",gap:"GAP","not-automatable":"n/a"};function N(a,e){a.say("",` ${e.id} \u2014 ${e.title}`,` ${e.rows.length} classes: ${e.counts.implemented} implemented, ${e.counts.partial} partial, ${e.counts["deliberately-not-implemented"]} deliberate, ${e.counts.gap} gap, ${e.counts["not-automatable"]} not-automatable`,"");const c=e.rows.map(n=>{const o=n.status==="implemented"?n.detectors.map(r=>`${r.mode}:${r.id}`).join(", "):n.status==="partial"?n.detectors.map(r=>`${r.mode}:${r.id}`).join(", ")+` (partial, missing ${n.missingModes.join("+")})`:n.status==="deliberately-not-implemented"?n.reason:n.status==="not-automatable"?n.note+(n.unexpectedDetectors?.length?` [detector exists: ${n.unexpectedDetectors.map(r=>`${r.mode}:${r.id}`).join(", ")}]`:""):`no detector (${n.missingModes.join("+")})`;return[n.id,`[${n.det}]`,D[n.status],n.class.length>60?n.class.slice(0,57)+"...":n.class,o.length>70?o.slice(0,67)+"...":o]});a.say(I(c))}async function R({parsed:a,out:e,skillRoot:c}){const n=a.flags.layer,o=a.flags.version,r=n!=null?Number(n):null,l=o!=null?Number(o):2;if(!i.ATLAS_VERSIONS.includes(l))return e.warn(` --version must be one of ${i.ATLAS_VERSIONS.join(", ")}, got ${JSON.stringify(o)}`),m.INVALID;let s;try{s=h(c,l)}catch(t){if(t instanceof _){e.warn(` references/${f[l]} does not parse: ${t.message}`);for(const u of t.problems)e.warn(` line ${u.line}: ${u.kind} (${u.detail})`);return m.INVALID}throw t}const A=r!=null?s.layers.filter(t=>t.n===r):s.layers;if(a.flags.write){const t=L(s);b(p.join(c,"references","ATLAS-COVERAGE.md"),t),e.say(` wrote references/ATLAS-COVERAGE.md (${$(s.overall)})`)}e.say("",` qa dev atlas \u2014 coverage of references/${f[l]} (atlas v${l}) by the detectors`);for(const t of A)N(e,t);const d=s.drift;if(e.say(""," reverse drift:"),!d.noAtlasId.length&&!d.unknownAtlasId.length)e.say(" none");else{for(const t of d.noAtlasId)e.say(` BUG ${t.kind} (${t.source}) -- produced, maps to no atlas id`);for(const t of d.unknownAtlasId)e.say(` BUG ${t.id} referenced by ${t.rule??t.kind} (${t.source}) -- not a row in references/${f[l]}`)}if(s.unlinkedDeliberate.length){e.say(""," deliberately-not-implemented entries not linked to one class:");for(const t of s.unlinkedDeliberate)e.say(` - ${t.check}`)}e.say("",` ${$(s.overall)}`,""),e.emit({ok:!0,atlasVersion:l,summary:$(s.overall),overall:s.overall,layers:A.map(t=>({id:t.id,title:t.title,counts:t.counts,rows:t.rows})),drift:s.drift,unlinkedDeliberate:s.unlinkedDeliberate});const y=!!a.flags.strict,g=d.noAtlasId.length+d.unknownAtlasId.length;return y&&g>0?m.FINDINGS:m.OK}export{R as run};
@@ -0,0 +1 @@
1
+ import m from"node:fs";import l from"node:path";import{EXIT as b,InvalidError as f,resolveRepo as U}from"../../cli.mjs";import{readJson as d,writeJson as A}from"../../fs.mjs";import{run as O}from"../../proc.mjs";import{evaluateMutationPolicy as E}from"../../mutation-policy.mjs";import{fanOut as J,fanIn as L,obligationUniverse as T,renderViews as V}from"../../coherence/views.mjs";import{linkCauses as _,renderCauses as z}from"../../coherence/causes.mjs";import{coherenceRisk as B,renderRisk as X,riskArtifactPath as G}from"../../coherence/risk.mjs";import{familyBlocks as H,renderFamilies as Q}from"../../coherence/family-report.mjs";import{listRunIds as Y,runsDir as Z}from"../../runs.mjs";function N(e,n){let r;try{r=Y(e)}catch{return null}if(!r.length)return null;const t=l.join(Z(e),r[r.length-1],"agent-result.json");if(!m.existsSync(t))return null;let a;try{a=d(t,{what:"agent-result.json"})}catch{return null}return a?.dimensions?.coherenceObligations?.status!=="measured"?null:(n.warn(` using coherence plan from latest run: ${t}`),t)}function ee(e){let n;try{n=d(l.join(e,"verify.crud.json"),{what:"verify.crud.json"})}catch{return[]}if(![2,3].includes(n?.schema_version))return[];const r=new Set;for(const t of n.flows??[])for(const a of t?.crosscheck?.targets??[])a?.route&&r.add(a.route);return[...r].sort()}function q(e,n){return typeof e=="string"&&/^\d+$/.test(e)?Number(e):n}async function ne(e,n){const r=l.join(e,".verify","inventory.json");if(!m.existsSync(r)&&((await O(process.execPath,[l.join(n,"bin","inventory.mjs"),e],{stdout:"capture",stderr:"capture"})).code!==0||!m.existsSync(r)))throw new f("coherence inventory could not be produced");return d(r,{what:"inventory.json"})}function te(e,n){const r=l.join(e,"verify.crud.json");if(!m.existsSync(r))throw new f("qa measure coherence refuses mutation: verify.crud.json is absent");let t;try{t=d(r,{what:"verify.crud.json"})}catch{throw new f("qa measure coherence refuses mutation: verify.crud.json is invalid")}const a=n.flags.lane??"public-runtime";if(a==="mock-contract")throw new f("qa measure coherence refuses mock-contract lane for runtime coherence");const i=E({command:"coherence",mutate:!!n.flags.mutate,base:n.flags.base,overrideNonLocal:!!n.flags["i-know-this-is-not-production"],lane:a,declaration:t,flowName:typeof n.flags.flow=="string"?n.flags.flow:null,maxMutations:q(n.flags["max-mutations"],20),cleanupCapable:!0,fixtureNamespace:"coherence-command"});if(!i.ok)throw new f(`qa measure coherence refuses mutation: ${i.reason}`);if(i.flows.some(u=>![2,3].includes(t.schema_version)||!u.crosscheck))throw new f("qa measure coherence refuses mutation: every selected flow needs a schema-v2-or-v3 crosscheck");const h=l.join(e,"verify.coherence-provenance.mjs");if(!m.existsSync(h))throw new f("qa measure coherence refuses mutation: no verify.coherence-provenance.mjs adapter");return{permit:i,adapter:h}}async function we({parsed:e,out:n,skillRoot:r}){const t=U(e);if(!m.existsSync(t))throw new f(`no such directory: ${t}`);if(!e.flags.mutate){const o=(await ne(t,r)).coherence;if(!o?.coverage?.counts)throw new f("inventory has no coherence projection");const s=o.coverage.counts,C=s.surfacesResolved??0,D=s.surfacesSuggested??s.surfacesDeclared??0,g=o.candidates??[],W=new Set(g.flatMap(c=>c.surfaceIds??[])).size;n.say("",` qa measure coherence ${t}`,` writes ${s.writesResolved} resolved / ${s.writeCandidates} discovered (${s.writesUnresolved} unresolved, ${s.writesNonWrite} non-write)`,` reads ${s.readsResolved} resolved / ${s.readCandidates} discovered (${s.readsUnresolved} unresolved)`,` surfaces ${C} with reads / ${D}`,` candidates ${g.length} (affected surfaces ${W})`),s.writersWithNoReaderSurface&&n.say(` unjoined writers ${s.writersWithNoReaderSurface}: ${(o.coverage.unjoinedWriterFamilies??[]).join(", ")} (no reader surface; static coherence is partial)`);const y={fanOut:J(o),fanIn:L(o),obligations:T(o)},M=new Set(g.map(c=>c.mutationId)),$=y.fanOut.filter(c=>M.has(c.mutationId));for(const c of $.slice(0,40)){const K=c.surfaces.map(v=>{const I=`${v.route}:${v.component??v.surfaceId}`;return v.covered===!0?I:v.covered==="unresolved"?`${I} (unresolved)`:`${I} (uncovered)`});n.say(` ${c.label??c.mutationId} [${c.family}] -> ${K.join(", ")}`)}$.length>40&&n.say(` ... ${$.length-40} more`),n.say(` static Cartesian estimate: ${y.obligations.count} (mutation x surface x principal x context; not the kernel obligation universe)`),e.flags.views&&n.say("",...V(y));const P=N(t,{warn:()=>{}});let k=null;if(P)try{k=d(P,{what:"agent-result.json"})?.dimensions?.coherenceObligations??null}catch{k=null}let w=null;try{w=d(l.join(t,".verify","coherence-observed.json"),{what:"coherence-observed.json"})}catch{w=null}const p=_({candidates:g,observed:w,surfaces:o.surfaces??[],mutations:o.mutations??[]});p.causes.length&&n.say("",...z(p));const F=ee(t),j=B({repo:t,coherence:o,observed:w,mandatoryRoutes:F});A(G(t),j),j.families.length&&n.say("",...X(j));const x=H({coherence:o,dimension:k,observed:w,causes:p});x.families.length&&n.say("",...Q(x));const S=o.coverage.status==="unmeasured"?b.INVALID:b.OK;return n.emit({ok:S===b.OK,exit:S,repo:t,coherence:o,views:y,causes:p,risk:j,families:x}),S}te(t,e);const a=l.join(t,".verify","coherence-observed.json"),i=["--repo",t,"--base",e.flags.base,"--mutate","--json",a,"--lane",e.flags.lane??"public-runtime","--max-mutations",String(q(e.flags["max-mutations"],20))];typeof e.flags.flow=="string"&&i.push("--flow",e.flags.flow),(typeof e.flags["flow-workers"]=="string"||typeof e.flags["flow-workers"]=="number")&&i.push("--flow-workers",String(e.flags["flow-workers"])),typeof e.flags.runid=="string"&&i.push("--runid",e.flags.runid),typeof e.flags["evidence-envelope"]=="string"&&i.push("--evidence-envelope",e.flags["evidence-envelope"]);const h=typeof e.flags.plan=="string"?e.flags.plan:N(t,n);h&&i.push("--plan",h);const u=await O(process.execPath,[l.join(r,"bin","coherence-runner.mjs"),...i],{stdout:e.json?"capture":"inherit",stderr:e.json?"capture":"inherit"});e.json&&(u.stdout&&process.stderr.write(u.stdout),u.stderr&&process.stderr.write(u.stderr));let R=null;try{R=d(a,{what:"coherence-observed.json"})}catch{}return n.emit({ok:u.code===b.OK,exit:u.code,repo:t,observed:R}),u.code}export{we as run};
@@ -0,0 +1,2 @@
1
+ import y from"node:fs";import P from"node:path";import{resolveRepo as M,UsageError as T,InvalidError as x,EXIT as d,table as q}from"../../cli.mjs";import{readJson as K,exists as z,acquireLock as G}from"../../fs.mjs";import{run as J}from"../../proc.mjs";import{newRunId as Z}from"../../runs.mjs";import{readBorrowedLease as X}from"../../auth/lease.mjs";import{validateMutationDeclaration as Y,loadMutationDeclaration as H,evaluateMutationPolicy as Q}from"../../mutation-policy.mjs";import{PROD_BASE_RE as xe,LOOPBACK_BASE_RE as be,unsafeBaseReason as Se}from"../../mutation-policy.mjs";const he=Y,R=e=>{if(!/^\d{4}-\d{2}-\d{2}$/.test(e??""))return!1;const t=new Date(`${e}T00:00:00Z`);return!Number.isNaN(t.getTime())&&t.toISOString().slice(0,10)===e};function W(e){let t=0;for(const r of String(e))t=t*31+r.charCodeAt(0)>>>0;return t%361-180}function me({min:e=null,max:t=null,override:r=null,now:n=new Date,salt:o=null}={}){const i=R(e)?e:null,u=R(t)?t:null;if(i&&u&&i>u)return null;if(r!=null)return R(r)&&(!i||r>=i)&&(!u||r<=u)?r:null;const f=o==null?n:new Date(n.getTime()+W(o)*864e5),c=`${f.getFullYear()}-${String(f.getMonth()+1).padStart(2,"0")}-${String(f.getDate()).padStart(2,"0")}`;return i&&c<i?i:u&&c>u?u:c}function ge(e,t={},{encodePath:r=!1}={}){if(e==null)return e;if(typeof e!="string")return null;const n=[...e.matchAll(/\{([^{}]*)\}/g)],o=e.replace(/\{[^{}]*\}/g,"");return/[{}]/.test(o)||n.some(([,i])=>!["fixtureId","sentinel"].includes(i)||t[i]==null||String(t[i]).trim()==="")||r&&n.some(([,i])=>i==="fixtureId"&&!/^[A-Za-z0-9_-]{1,128}$/.test(String(t[i])))?null:e.replace(/\{(fixtureId|sentinel)\}/g,(i,u)=>r?[".",".."].includes(String(t[u]))?encodeURIComponent(String(t[u])).replace(/\./g,"%252E"):encodeURIComponent(String(t[u])):String(t[u]))}function ee({mutate:e,base:t,declResult:r,overrideNonLocal:n=!1,lane:o="public-runtime",maxMutations:i=20,fixtureNamespace:u="compatibility-fixture",flowName:f=null}){const c=Q({command:"crud",mutate:e,base:t,overrideNonLocal:n,lane:o,maxMutations:i,fixtureNamespace:u,cleanupCapable:!0,flowName:f,declaration:r?.declaration??null,declarationReason:r?.reason??(r?null:"verify.crud.json is absent")});return c.ok?null:c.reason}function C(e,t){if(!t)return e.flows;const r=e.flows.filter(n=>n.name===t);if(!r.length)throw new T(`--flow "${t}" matches no flow in verify.crud.json (have: ${e.flows.map(n=>n.name).join(", ")||"none"})`);return r}function _(e,t){const r=e.personas??t.personas??["default"];return[...new Set([...r,...e.denied??[]])]}function we(e,t){return e.flatMap(r=>_(r,t).map(n=>({flow:r,persona:n})))}function ye(e,t,r){const n=[],o=`${e.name}:${t}`,i=r.reasons??{};return r.create===!1&&n.push({kind:"crud.create-failed",severity:"P1",at:o,route:e.route??null,detail:i.create??"create submit did not produce a successful write"}),r.create===!0&&r.read===!1&&n.push({kind:"crud.write-lost",severity:"P0",at:o,route:e.route??null,detail:"create returned a successful write, but the record was absent from the authoritative readback after reload. A 2xx is not persistence."}),r.update===!1&&n.push({kind:"crud.update-lost",severity:"P1",at:o,route:e.route??null,detail:i.update??"update was accepted but did not persist across a reload"}),r.delete===!1&&n.push({kind:"crud.delete-ineffective",severity:"P1",at:o,route:e.route??null,detail:i.delete??"delete was accepted but the record was still present after a reload"}),(e.denied??[]).includes(t)&&r.authorized===!1&&n.push({kind:"crud.unauthorized-allowed",severity:"P1",at:o,route:e.route??null,detail:`persona "${t}" is declared denied for flow "${e.name}", but its write was allowed`}),r.cleanup===!1&&n.push({kind:"crud.cleanup-failed",severity:"P1",at:o,route:e.route??null,detail:i.cleanup??"cleanup did not remove the record this run created -- verify and remove it by hand"}),n}function te(e,t){return t===d.INVALID?d.INVALID:!e||e.status==="invalid"?d.INVALID:(e.findings??[]).some(n=>n.severity==="P0"||n.severity==="P1")?d.FINDINGS:d.OK}function ne(e,t,r){const n=i=>({status:"invalid",runId:r.runId,base:r.base,lane:r.lane,flows:[],findings:[],invalid:{reason:i,gaps:[]},reason:i});if(t?.signal)return n(`child terminated by signal ${t.signal}`);if(t?.error)return n("child process failed to start");if(![0,1,2].includes(t?.code))return n(`child exited with unsupported code ${t?.code??"unknown"}`);if(!e)return n("child produced no CRUD observation");if(typeof e!="object"||Array.isArray(e)||!["measured","invalid"].includes(e.status)||!Array.isArray(e.flows)||!Array.isArray(e.findings))return n("child produced a malformed CRUD observation");if(e.runId!==r.runId||e.base!==r.base||e.lane!==r.lane)return n("CRUD observation identity does not match this invocation");const o=(e.findings??[]).some(i=>i.severity==="P0"||i.severity==="P1");return t.code===1&&!o?n("child reported findings exit without a P0/P1 finding"):t.code===0&&o?n("child reported clean exit with a P0/P1 finding"):e}async function $e({parsed:e,out:t,skillRoot:r}){const n=M(e);if(!z(n))throw new x(`no such directory: ${n}`);const o=!!e.flags.mutate,i=e.flags.base??null,u=!!e.flags["i-know-this-is-not-production"],f=e.flags.auth??null,c=e.flags["oracle-path"]??null,k=e.flags.launcher??null,N=e.flags.flow,p=typeof N=="string"?N:null,w=e.flags["max-mutations"];if(w!==void 0&&(typeof w!="string"||!/^\d+$/.test(w)))throw new x("--max-mutations must be a positive integer");const b=w===void 0?20:Number(w),S=Z(),j=`qa-${S}`,A=e.flags.lane??"public-runtime",B=e.flags["auth-lease-fd"],$=H(n),D=ee({mutate:o,base:i,declResult:$,overrideNonLocal:u,lane:A,maxMutations:b,fixtureNamespace:j,flowName:p});if(D==="no declared mutation flow matches this request scope"&&p&&$.ok&&C($.declaration,p),D)throw new x(`qa measure crud refuses to run: ${D}`);const L=$.declaration,O=C(L,p);t.say("",` qa measure crud ${n} --base ${i} --mutate`+(p?` --flow ${p}`:""),` ${O.length} flow(s), ${O.reduce((s,l)=>s+_(l,L).length,0)} declared flow/persona run(s), max ${b} mutation(s)`);const h=P.join(n,".verify","crud-observed.json"),U=G(n,"crud");let m,a=null;try{try{y.existsSync(h)&&y.unlinkSync(h)}catch{throw new x("cannot clear stale CRUD observation")}const s=P.join(r,"bin","crud-runner.mjs"),l=["--repo",n,"--base",i,"--mutate","--json",h,"--max-mutations",String(b),"--lane",A,"--fixture-namespace",j,"--runid",S];p&&l.push("--flow",p),f&&l.push("--auth",f),c&&l.push("--oracle-path",c),k&&l.push("--launcher",k),u&&l.push("--i-know-this-is-not-production");const E=X(B);E&&l.push("--auth-lease-fd","3"),m=await J(process.execPath,[s,...l],{stdout:e.json?"capture":"inherit",stderr:e.json?"capture":"inherit",fd3:E}),e.json&&(m.stdout&&process.stderr.write(m.stdout),m.stderr&&process.stderr.write(m.stderr));try{a=K(h)}catch(I){t.warn(` ${I.message}`)}if(a=ne(a,m,{runId:S,base:i,lane:A}),a.status==="invalid"){y.mkdirSync(P.dirname(h),{recursive:!0});const I=h+".invalid-"+process.pid;y.writeFileSync(I,JSON.stringify(a,null,2)+`
2
+ `),y.renameSync(I,h)}}finally{U()}const g=te(a,m.code);if(g===d.INVALID)return a?.coverage&&t.say(` coverage ${Object.entries(a.coverage).map(([s,l])=>`${s}=${l.measured}/${l.planned} measured,${l.unmeasured} unmeasured`).join(" ")}`),(a?.invalid?.reason||a?.reason)&&t.say(` INVALID report: ${a.invalid?.reason??a.reason}`),a?.invalid?.gaps?.length&&t.say(` invalid gaps: ${a.invalid.gaps.join(" | ")}`),t.say(""," INVALID exit 2 -- partial CRUD observations were retained; see coverage and invalid details above"),t.emit({ok:!1,exit:g,repo:n,base:i,observed:a}),d.INVALID;const v=a.findings??[],V=(a.flows??[]).map(s=>[s.name,s.persona,s.outcome.create,s.outcome.read,s.outcome.update,s.outcome.delete,s.outcome.reload_reconciled,s.outcome.navigation_reconciled,s.outcome.authorized,s.outcome.cleanup]);if(t.say("",q([["flow","persona","create","read","update","delete","reload","nav","auth","cleanup"],...V]),""),v.length){t.say(` ${v.length} finding(s):`);for(const s of v)t.say(` ${s.severity} ${s.kind} ${s.at} ${s.detail}`)}const F=g===d.OK?"CLEAN within measured scope":"GATING";return t.say("",` ${F} exit ${g} runId ${a.runId}`,` -> ${h}`,""),t.emit({ok:g===d.OK,exit:g,repo:n,base:i,runId:a.runId,attempts:a.attempts,counts:a.counts,coverage:a.coverage,flows:a.flows,findings:v}),g}export{be as LOOPBACK_BASE_RE,xe as PROD_BASE_RE,te as computeExit,me as dateValue,ye as findingsForOutcome,ge as interpolateTemplate,ne as observationForChild,_ as personasFor,ee as refusalReason,C as resolveFlows,$e as run,we as scheduledPersonaRuns,Se as unsafeBaseReason,he as validateDeclaration};
@@ -0,0 +1 @@
1
+ import{resolveRepo as x,EXIT as n}from"../../cli.mjs";import{runDoctor as $}from"../../doctor.mjs";const v={ok:"OK ",warn:"WARN",fail:"FAIL"};async function h({parsed:i,out:l,skillRoot:c}){const r=x(i),s=i.flags.base??null,o=!!i.flags.prod,a=!!i.flags.strict,e=await $(r,{skillRoot:c,base:s,prod:o,strict:a});l.say("",` qa verify --preflight ${r}`+(s?` --base ${s}`:""));for(const t of e.checks)l.say(` ${v[t.level]??t.level} ${t.id} ${t.detail}`),t.level!=="ok"&&t.fix&&l.say(` fix: ${t.fix}`);const f=e.exit===n.OK?"READY":e.exit===n.FINDINGS?"WARNINGS (--strict)":"BLOCKED";return l.say("",` ${f} exit ${e.exit} (${e.fails.length} fail, ${e.warns.length} warn)`),l.emit({ok:e.exit===n.OK,exit:e.exit,repo:r,base:s,prod:o,strict:a,checks:e.checks.map(m)}),e.exit}function m(i){return{id:i.id,level:i.level,detail:i.detail,fix:i.fix}}export{h as run};
@@ -0,0 +1 @@
1
+ import c from"node:path";import{resolveRepo as f,forwardFlags as p,InvalidError as i}from"../../cli.mjs";import{run as h}from"../../proc.mjs";import{exists as d}from"../../fs.mjs";import{requireLane as x}from"../../lanes.mjs";import{newRunId as w}from"../../runs.mjs";import{loadMutationDeclaration as g,evaluateMutationPolicy as b}from"../../mutation-policy.mjs";import{readBorrowedLease as j}from"../../auth/lease.mjs";const q=new Set(["mutate","lane","help","auth-lease-fd"]);async function I({parsed:e,out:u,skillRoot:m}){const t=f(e);if(!d(t))throw new i(`no such directory: ${t}`);const l=c.join(m,"bin","experiment-executor.mjs"),r=e.flags.lane?x(e.flags.lane,{command:"experiments"}):"public-runtime";if(e.flags.mutate){const n=g(t),s=b({command:"experiments",mutate:!0,base:e.flags.base??null,lane:r,declaration:n.declaration,declarationReason:n.reason,maxMutations:1,fixtureNamespace:`qa-experiment-${w()}`,cleanupCapable:!0});if(!s.ok)throw new i(`qa measure experiments refuses --mutate: ${s.reason}`)}const o=j(e.flags["auth-lease-fd"]),a=["--repo",t,"--lane",r,...p(e,q)];return e.flags.mutate&&a.push("--mutate"),e.json&&a.push("--json"),o&&a.push("--auth-lease-fd","3"),u.say("",` qa measure experiments ${t}`),(await h(process.execPath,[l,...a],{stdout:"inherit",stderr:"inherit",...o?{fd3:o}:{}})).code}export{I as run};
@@ -0,0 +1 @@
1
+ import y from"node:path";import{UsageError as c,InvalidError as u,table as x,resolveRepo as k}from"../../cli.mjs";import{loadFps as w,saveFps as v,addFp as q,addRuntimeFp as R,removeFp as E,fpStorePath as F,readSourceLine as j}from"../../fp.mjs";import{loadRun as Y,runRecords as A}from"../../runs.mjs";import{readJson as D}from"../../fs.mjs";function L(t,s){const d=y.join(s,".verify","loop.json"),i=D(d)?.outcomes??{},n=Object.keys(i);if(n.length){n.sort((e,r)=>i[r].waived+i[r].reverted-(i[e].waived+i[e].reverted)),t.say("",` fix-loop outcomes (${d})`,x([["rule","fixed","reverted","waived","blocked"],...n.map(e=>[e,i[e].fixed,i[e].reverted,i[e].waived,i[e].blocked])]));for(const e of n){const r=i[e];r.fixed===0&&r.waived+r.reverted>=3&&t.warn(` WARN ${e}: no real-repo fix evidence -- CLAUDE.md ladder says P3`)}}}async function S({parsed:t,out:s}){const d=t.positional[0],a=F();if(d==="list"||!d){const i=w(a);s.say("",` known false positives ${i.length} (${a})`,i.length?x([["id","rule","scope","note"],...i.map(e=>[e.id,e.rule,e.type==="runtime"?`${e.route} until ${e.expires.slice(0,10)}`:e.basename,e.note.slice(0,60)])]):"","");const n=t.positional[1];return n&&L(s,k({...t,positional:[n]})),s.emit({ok:!0,file:a,entries:i}),0}if(d==="rm"){const i=t.positional[1];if(!i)throw new c("qa fix fp rm <id>");const n=w(a),e=E(n,i);if(!e)throw new u(`no known false positive with id ${i}`);return v(n,a),s.say(` forgot ${e.id}`),s.emit({ok:!0,removed:e}),0}if(d==="add"){const i={...t,positional:t.positional.slice(1)},n=k(i),e=i.positional[1];if(!e)throw new c('qa fix fp add <repo> <fid> --note "why this is not a defect"');const r=typeof t.flags.note=="string"?t.flags.note:"";if(!r)throw new c("--note is required: say WHERE the guarantee comes from, so the memory stays checkable");const f=Y(n,t.flags.run),o=A(f).find(l=>l.fid===e||l.fid.startsWith(e));if(!o)throw new u(`fid ${e} not in run ${f.id}`);const p=w(a);if(!o.file){const l=typeof t.flags.evidence=="string"?t.flags.evidence:"",$=typeof t.flags.expires=="string"?t.flags.expires:"";if(!l)throw new c("--evidence is required for a runtime finding");if(!$)throw new c("--expires is required for a runtime finding");if(!f.sweep||f.meta.exit===2||f.sweep.status==="invalid"||f.sweep.integrity?.ok!==!0)throw new u("invalid or unmeasured runtime evidence cannot be adjudicated");let m;try{m=R(p,{record:o,repo:n,sweep:f.sweep,note:r,evidence:l,expires:$})}catch(b){throw new u(b.message)}return v(p,a),s.say("",` adjudicated ${m.id}`,` ${o.kind} ${o.route} ${o.role??"default"} ${o.at??"(page)"}`,` evidence: ${m.evidence}`,` expires: ${m.expires}`,""," Matching current evidence no longer gates. The raw runtime observation remains archived.",""),s.emit({ok:!0,entry:m,file:a}),0}const h=q(p,{rule:o.kind,file:o.file,anchor:o.anchor,note:r,repo:y.basename(n)});v(p,a);const g=j(n,o.file,o.line);return s.say("",` remembered ${h.id}`,` ${o.kind} ${o.file}:${o.line}`,g?` ${g.trim().slice(0,100)}`:"",` ${r}`,""," This labels the finding and ranks it last; it does not remove it. The durable fix is a",` \`verify-ignore: ${o.kind} -- ${r}\` comment above the line.`,""),s.emit({ok:!0,entry:h,file:a}),0}throw new c('qa fix fp add <repo> <fid> --note "..." [--evidence REF --expires YYYY-MM-DD] | qa fix fp list | qa fix fp rm <id>')}export{S as run};
@@ -0,0 +1 @@
1
+ import E from"node:fs";import h from"node:path";import{createHash as O}from"node:crypto";import{resolveRepo as q,UsageError as u,InvalidError as f,EXIT as x}from"../../cli.mjs";import{readJson as b}from"../../fs.mjs";import{shapes as G}from"../../reports.mjs";import{buildSyncGraph as J,loadTransitionGraph as K,toDot as P,toMermaid as R,toJson as T}from"../../graph.mjs";import{validateGraph as C,neighborhood as F,readJsonlStrict as k,NODE_KINDS as I}from"../../graph-schema.mjs";import{resolveRunId as M,runsDir as A}from"../../runs.mjs";const H=new Set(["dot","mermaid","json"]),U=new Set(["sync","transition"]);function S(e,r,s,n){if(e===void 0)return s;if(typeof e!="string"||!/^(?:0|[1-9]\d*)$/.test(e))throw new u(`--${r} must be an integer >= ${n}`);const o=Number(e);if(!Number.isSafeInteger(o)||o<n)throw new u(`--${r} must be an integer >= ${n}`);return o}async function X({parsed:e,out:r,repo:s}){const n=M(s,e.flags.run),o=h.join(A(s),n),t=h.join(o,"nodes.jsonl"),c=h.join(o,"edges.jsonl"),i=b(h.join(o,"run.json"),{required:!0,what:`run metadata for ${n}`})?.graph;if(!i||typeof i!="object"||!/^[0-9a-f]{64}$/.test(i["nodes.jsonl"]??"")||!/^[0-9a-f]{64}$/.test(i["edges.jsonl"]??""))throw new f(`graph for run ${n} has no valid archived node/edge digests`);for(const[d,D]of[["nodes.jsonl",t],["edges.jsonl",c]]){let v;try{v=O("sha256").update(E.readFileSync(D)).digest("hex")}catch(N){throw new f(`graph for run ${n} cannot read ${d}: ${N.message}`)}if(v!==i[d])throw new f(`graph for run ${n} failed archived ${d} digest validation`)}let l,m;try{l=k(t),m=k(c)}catch(d){throw new f(`graph for run ${n} is invalid: ${d.message}`)}if(l===null||m===null)throw new f(`no graph for run ${n} -- nodes.jsonl/edges.jsonl were not produced for this run`);const p=C({nodes:l,edges:m});if(!p.ok)throw new f(`graph for run ${n} failed integrity validation: ${p.problems.join("; ")}`);const g=e.flags.kind??null;if(g!==null&&!I.includes(g))throw new u(`--kind must be one of ${I.join(", ")} when --run is given (got "${g}")`);const j=S(e.flags.depth,"depth",1,0),y=S(e.flags.limit,"limit",200,1),w=e.flags.around??null;if(w!==null&&!l.some(d=>d.recordId===w))throw new u(`--around must name a node in run ${n}`);const a=F({nodes:l,edges:m},{kind:g,around:w,depth:j,limit:y});return r.say("",` qa show graph --run ${n}`+(e.flags.around?` --around ${e.flags.around}`:""),` nodes ${a.nodes.returned}/${a.nodes.total}${a.nodes.truncated?" (truncated)":""} \xB7 edges ${a.edges.returned}/${a.edges.total}${a.edges.truncated?" (truncated)":""}`),r.emit({ok:!0,run:n,kind:g,around:w,depth:j,limit:y,...a}),x.OK}async function nn({parsed:e,out:r}){const s=q(e);if(e.flags.run)return X({parsed:e,out:r,repo:s});const n=e.flags.format??"json";if(!H.has(n))throw new u(`--format must be one of dot, mermaid, json (got "${n}")`);const o=e.flags.kind??"sync";if(!U.has(o))throw new u(`--kind must be sync or transition (got "${o}") when --run is not given`);let t;if(o==="sync"){const $=h.join(s,".verify","inventory.json"),i=b($,{required:!0,shape:G.inventory,what:"inventory.json"});t=J(i)}else t=K(s),t.measured||r.warn(" transition graph: unmeasured \u2014 no .verify/autonomous/edges.jsonl; run the autonomous pipeline first");const c=n==="dot"?P(t,{title:`${o}-graph`}):n==="mermaid"?R(t):T(t);return r.say(c),r.emit({ok:!0,kind:o,format:n,measured:t.measured,nodeCount:t.nodes.length,edgeCount:t.edges.length,graph:t}),0}export{nn as run};
@@ -0,0 +1 @@
1
+ import E from"node:fs";import v from"node:path";import{createHash as C}from"node:crypto";import{resolveRepo as K,table as S,UsageError as $,InvalidError as k,EXIT as p}from"../../cli.mjs";import{readJson as I,writeJson as g,writeText as H,exists as M}from"../../fs.mjs";import{listRunIds as _,loadRun as L,runsDir as x}from"../../runs.mjs";import{mineFromRuns as G,evaluatePolicy as U,proofsValid as V,applyApprove as J,applyRevoke as X,statusRows as Y,emptyPolicy as z,packManifestFor as B,generatePackSource as Q,DEFAULT_TEXT_FLOOR as R}from"../../invariants.mjs";const y=e=>v.join(e,".verify","invariants","candidates.json"),w=e=>v.join(e,"verify.invariants.json"),A=e=>v.join(e,".verify","rules","invariants.mjs"),T=e=>v.join(e,".verify","invariants","pack-manifest.json"),W=e=>!e||typeof e!="object"?"not an object":Array.isArray(e.enforced)?null:"enforced is not an array",Z=e=>!e||typeof e!="object"?"not an object":Array.isArray(e.candidates)?null:"candidates is not an array";function q(e){try{return"sha256:"+C("sha256").update(E.readFileSync(e)).digest("hex")}catch{return null}}function j(e){return I(w(e),{shape:W,what:"verify.invariants.json"})??z()}function N(e){return I(y(e),{shape:Z,what:".verify/invariants/candidates.json"})}function ee(e){return I(T(e),{shape:t=>!t||!Array.isArray(t.ids)?"ids is not an array":null,what:"pack-manifest.json"})}function O(e,t){const s={},a=new Set;for(const r of t)for(const o of r.proofs??[]){const c=`${o.runId}:${o.file}`;a.has(c)||(a.add(c),s[c]=q(v.join(x(e),o.runId,o.file)))}return s}function ne(e,t){return e.map(s=>({...s,proofs:s.supportRuns.map(a=>{const r="sweep.json",o=q(v.join(x(t),a,r));return{runId:a,file:r,sha256:o??""}})}))}async function te(e,t){const s=_(e),a=s.map(n=>{try{const l=L(e,n);return{id:n,sweep:l.sweep}}catch{return{id:n,sweep:null}}}),r=a.filter(n=>n.sweep);if(!r.length)throw new k(`no archived runtime run under ${x(e)} carries a sweep.json -- run \`qa verify ${e} --base URL ...\` (a runtime run) first, then \`qa measure invariants mine ${e}\``);const{candidates:o,runsConsidered:c}=G(a,{textFloor:R}),f=ne(o,e),i={schemaVersion:1,minedAt:new Date().toISOString(),runsConsidered:c,textFloor:R,candidates:f};g(y(e),i);const d=f.filter(n=>n.support>=2&&n.counterexamples===0).length;return t.say("",` invariants mine ${f.length} candidate(s) over ${r.length} runtime run(s) (of ${s.length} archived)`,` ${d} candidate(s) already meet support>=2 & counterexamples=0 and can be reviewed`,"",S([["id","kind","route","support","counterex"],...f.slice(0,40).map(n=>[n.id,n.kind,n.route,n.support,n.counterexamples])]),f.length>40?` ... ${f.length-40} more in ${y(e)}`:"",` -> ${y(e)}`,""),t.emit({ok:!0,...i}),p.OK}function b(e,t,s){const a=e[t];if(typeof a=="string")return a;if(a===!0)return s.shift()}async function se(e,t,s){const a=t.positional.slice(2),r=Object.prototype.hasOwnProperty.call(t.flags,"approve"),o=Object.prototype.hasOwnProperty.call(t.flags,"revoke");if(r===o)throw new $("qa measure invariants review requires exactly one of --approve ID or --revoke ID");if(o){const u=b(t.flags,"revoke",a);if(!u)throw new $("--revoke needs an invariant id");const h=j(e),{policy:D,present:F}=X(h,u);return g(w(e),D),s.say("",F?` revoked ${u} -- removed from ${w(e)}`:` ${u} was not enforced; nothing changed`,""),s.emit({ok:!0,action:"revoke",id:u,present:F,enforced:D.enforced.length}),p.OK}const c=b(t.flags,"approve",a);if(!c)throw new $("--approve needs an invariant id");const f=b(t.flags,"by",a)??null,i=N(e);if(!i)throw new k(`no mined candidates at ${y(e)} -- run \`qa measure invariants mine ${e}\` first`);const d=i.candidates.find(u=>u.id===c);if(!d){const u=i.candidates.slice(0,5).map(h=>h.id).join(", ");throw new k(`invariant candidate "${c}" not found in ${y(e)}${u?` (have: ${u}${i.candidates.length>5?", ...":""})`:""}`)}const n=j(e);if(n.enforced.some(u=>u.id===c))return s.say("",` ${c} is already enforced -- revoke it first to re-approve`,""),s.emit({ok:!1,action:"approve",id:c,reason:"already-enforced"}),p.FINDINGS;const l=O(e,[d]),m=U(d,l);if(!m.ok){const u=m.reason==="tampered"?`proof mismatch: ${m.mismatches.map(h=>`${h.runId}/${h.file} (${h.reason})`).join("; ")}`:m.reason==="insufficient-support"?`support ${d.support} < 2 distinct runs`:`${d.counterexamples} counterexample run(s) present`;return s.warn(` invariants review: refused to approve ${c} -- ${u}`),s.emit({ok:!1,action:"approve",id:c,reason:m.reason,mismatches:m.mismatches,candidate:d}),m.reason==="tampered"?p.INVALID:p.FINDINGS}const P=J(n,d,{by:f});return g(w(e),P),s.say("",` approved ${c} -- ${d.statement}`,` support ${d.support}, counterexamples ${d.counterexamples}, by ${f??"(unspecified)"}`,` -> ${w(e)} (run \`qa measure invariants enforce ${e}\` to regenerate the runtime pack)`,""),s.emit({ok:!0,action:"approve",id:c,record:P.enforced.find(u=>u.id===c)}),p.OK}async function ae(e,t){const s=j(e),a=O(e,s.enforced),r=[],o=[];for(const i of s.enforced){const d=V(i.proofs,a);d.ok?r.push(i):o.push({id:i.id,mismatches:d.mismatches})}const c=Q(r);H(A(e),c);const f=B(r,{skipped:o});return g(T(e),f),t.say("",` invariants enforce ${r.length}/${s.enforced.length} enforced invariant(s) baked into ${A(e)}`,o.length?` ${o.length} skipped -- archived proof no longer validates (pruned or tampered run); re-approve after re-mining:`:"",...o.map(i=>` ${i.id} ${i.mismatches.map(d=>`${d.runId}/${d.file}: ${d.reason}`).join("; ")}`),""),t.emit({ok:o.length===0,enforced:r.map(i=>i.id),skipped:o,pack:A(e)}),o.length?p.FINDINGS:p.OK}async function re(e,t){const s=N(e),a=j(e),o=ee(e)?.ids??[],c=[...s?.candidates??[],...a.enforced],f=O(e,c),i=Y({candidates:s?.candidates??[],policy:a,packIds:o,currentDigests:f});if(!i.length)return t.say("",` no invariants known -- run \`qa measure invariants mine ${e}\` first`,""),t.emit({ok:!0,rows:[]}),p.OK;const d=i.reduce((n,l)=>(n[l.state]=(n[l.state]??0)+1,n),{});return t.say("",` invariants status ${i.length} known -- ${Object.entries(d).map(([n,l])=>`${l} ${n}`).join(", ")}`,"",S([["id","state","kind","route","support","counterex","proofs","runtime-checked"],...i.map(n=>[n.id,n.state,n.kind,n.route,n.support,n.counterexamples,n.proofsOk===null?"-":n.proofsOk?"ok":"MISMATCH",n.runtimeChecked?"yes":"no (see header)"])]),""),t.emit({ok:!0,rows:i}),p.OK}async function le({parsed:e,out:t}){const s=e.positional[0];if(!s)throw new $("qa measure invariants needs a subcommand: mine | review | enforce | status");const a={...e,positional:e.positional.slice(1)},r=K(a);if(!M(r))throw new k(`repo not found: ${r}`);switch(s){case"mine":return te(r,t);case"review":return se(r,e,t);case"enforce":return ae(r,t);case"status":return re(r,t);default:throw new $(`qa measure invariants: unknown subcommand "${s}" -- mine | review | enforce | status`)}}export{le as run};
@@ -0,0 +1,7 @@
1
+ import V from"node:fs";import d from"node:path";import se from"node:crypto";import{resolveRepo as re,UsageError as J,InvalidError as c,EXIT as p}from"../../cli.mjs";import{readJson as P,writeJson as M,writeText as B,readText as ie,exists as O,rmrf as X}from"../../fs.mjs";import{run as C}from"../../proc.mjs";import{listRunIds as ae,loadRun as U,runRecords as Y}from"../../runs.mjs";import{BlockLedger as z}from"../../block-ledger.mjs";import{buildBrief as oe}from"./next.mjs";import{classesFrom as ce,packBatches as le,emptyPlan as de,transition as q,boardMarkdown as fe,boardHeaderLine as N}from"../../fix-lanes.mjs";import{HARNESS_PATHS as K}from"../../loop.mjs";const H=e=>d.join(e,".verify","lanes"),W=e=>d.join(H(e),"plan.json"),F=e=>d.join(e,".verify","board.md");function D(e){return P(W(e),{shape:t=>t?.classes?null:"classes missing",what:"lanes/plan.json"})}function j(e,t){M(W(e),t)}function E(e,t){const r=d.join(e,".verify","ledger.md");B(F(e),fe(t,{ledgerMd:O(r)?ie(r):null}))}function ue(e){const t=new Set(["open","merged","blocked"]);return e.batches.find(r=>r.some(s=>!t.has(e.classes[s].status)))??null}async function g(e,t,{capture:r=!0}={}){const s=process.env.QA_ALLOW_TEST_HOOKS==="1"?process.env.QA_LANES_GIT:null,n=s?await C(process.execPath,[s,...t],{cwd:e,stdout:"capture",stderr:"capture"}):await C("git",t,{cwd:e,stdout:r?"capture":"inherit",stderr:"capture"});return{code:n.code,stdout:n.stdout??"",stderr:n.stderr??""}}function he(e){const t=P(d.join(e,".verify","stability.json"));return new Set((t?.needsReproduction??[]).map(r=>r.fid))}function we(e){const t=P(d.join(e,".verify","loop.json"));if(!t)return new Set;const r=z.from(t);return new Set(r.activeAt(Number.MAX_SAFE_INTEGER))}function ge(e,t){const r=t.filter(n=>e.fids.includes(n.fid)),s=r.find(n=>n.fid===e.id)??r[0];return{key:e.id,phase:e.phase,kind:e.kind,severity:e.severity,at:e.at,fids:e.fids,routes:e.routes,files:e.files,count:e.fids.length,sample:s,atlas:e.atlas}}function Z({repo:e,skillRoot:t,run:r,c:s,records:n,plan:o}){const{md:h}=oe({repo:e,skillRoot:t,run:r,group:ge(s,n),records:n,base:o.base,inventory:r.inventory}),a=[`# lane ${s.id} \u2014 ${s.kind} (${s.severity}) \u2014 ${s.fids.length} finding(s) on ${s.routes.length} route(s)`,"",`worktree: ${s.worktree}`,`branch: ${s.branch}`,`files this class resolves to: ${s.files.length?s.files.join(", "):"(unresolved -- read the findings and decide; say which file in the commit body)"}`,s.component?`shared component: ${s.component}`:"","","## contract","","- one root-cause fix, in this worktree only, touching the files above (callers listed in the brief are allowed)","- never edit bin/classify.mjs, bin/probe.js, bin/sweep.mjs or any detector; a false positive gets `verify-ignore: <rule> -- <proof>` in source",`- when done: \`qa fix gate ${s.id} ${e}\` (exit 0 = every finding gone and no regression), then \`qa fix close ${s.id} ${e}\``,"- a red gate reverts this worktree; the second attempt gets the first attempt attached","","## findings","",...s.fids.map(w=>{const i=n.find(l=>l.fid===w);return i?`- ${w} ${i.route??i.file??""}${i.line?":"+i.line:""} ${i.at&&i.at!=="(page)"?i.at+" ":""}${(i.detail??"").slice(0,160)}`:`- ${w}`}),"","## the finding brief (as qa show next would write it for the top finding)","",h];if(s.attempts.length){a.push("","## previous attempts","");for(const[w,i]of s.attempts.entries())a.push(`### attempt ${w+1}`,"","```",i.diffstat??"(no diff)","```","","gate:","","```",i.gateSummary??"","```","");a.push("## docs lookup","","(the session fills this before dispatching the retry: Context7 for the library named above, web search if that misses)","")}return a.filter(w=>w!==null).join(`
2
+ `)}const me=e=>K.some(t=>e===t||e.startsWith(t+"/"));async function G(e){const t=await g(e,["status","--porcelain"]);if(t.code!==0)throw new c(`git status failed for ${e}: ${t.stderr.trim()||t.stdout.trim()||`exit ${t.code}`}`);return t.stdout.split(`
3
+ `).filter(Boolean).filter(r=>!me(r.slice(3).replace(/\/$/,"")))}async function Q(e,t,r){const s=await g(e,["rev-parse",t]);if(s.code!==0||!s.stdout.trim())throw new c(`git rev-parse ${t} failed for ${r}: ${s.stderr.trim()||s.stdout.trim()||`exit ${s.code}`}`);return s.stdout.trim()}async function R(e,t,r,s,n){const o=await Q(e,t,s);if(o!==r)throw new c(`lane ${s} ${n} HEAD ${o} does not match recorded main HEAD ${r}; refusing to reuse it`)}function $e(e){const t=[];let r=null;for(const s of e.split(`
4
+ `))s.startsWith("worktree ")?(r&&t.push(r),r={path:s.slice(9),branch:null}):r&&s.startsWith("branch ")&&(r.branch=s.slice(7));return r&&t.push(r),t}async function pe(e,t,r){const s=d.join(H(e),t.id,"tree"),n=t.branch??`qa/${t.id}`,o=`refs/heads/${n}`,h=await g(e,["worktree","list","--porcelain"]);if(h.code!==0)throw new c(`git worktree list failed for ${t.id}: ${h.stderr.trim()||h.stdout.trim()}`);const a=$e(h.stdout),w=a.find(u=>d.resolve(e,u.path)===d.resolve(s));if(w){if(w.branch!==o)throw new c(`lane ${t.id} worktree is checked out on ${w.branch??"(detached)"}, expected ${o}; refusing to overwrite it`);if(!O(s))throw new c(`lane ${t.id} registered worktree path ${s} is missing; refusing to reuse it`);const u=await G(s);if(u.length)throw new c(`lane ${t.id} worktree is dirty outside .verify/.claude/.codex; refusing to reuse it: ${u.join(", ")}`);return await R(s,"HEAD",r,t.id,"worktree"),{tree:s,branch:n}}const i=a.find(u=>u.branch===o);if(i){const u=d.resolve(e,i.path),v=d.parse(u);if(v.base!=="tree"||d.basename(v.dir)!==t.id||d.basename(d.dirname(v.dir))!=="lanes"||d.basename(d.dirname(d.dirname(v.dir)))!==".verify")throw new c(`lane ${t.id} branch ${n} is already checked out at ${i.path}, outside its lane path; refusing to reuse or overwrite it`);if(!O(u))throw new c(`lane ${t.id} prior worktree path ${u} is missing; refusing to reuse it`);const $=await g(e,["rev-parse","--git-common-dir"]),f=await g(u,["rev-parse","--git-common-dir"]);if($.code!==0||f.code!==0||d.resolve(e,$.stdout.trim())!==d.resolve(u,f.stdout.trim()))throw new c(`lane ${t.id} branch ${n} is checked out in a different repository; refusing to reuse it`);const m=await G(u);if(m.length)throw new c(`lane ${t.id} prior worktree is dirty outside .verify/.claude/.codex; refusing to reuse it: ${m.join(", ")}`);return await R(u,"HEAD",r,t.id,"prior worktree"),{tree:u,branch:n}}if(O(s))throw new c(`lane ${t.id} path ${s} exists but is not a registered worktree; refusing to overwrite it`);const l=await g(e,["show-ref","--verify","--quiet",o]);if(l.code!==0&&l.code!==1)throw new c(`git show-ref failed for ${t.id}: ${l.stderr.trim()||l.stdout.trim()}`);l.code===0&&await R(e,n,r,t.id,"branch");const y=l.code===0?["worktree","add",s,n]:["worktree","add",s,"-b",n,r],k=await g(e,y);if(k.code!==0)throw new c(`git worktree add failed for ${t.id}: ${k.stderr.trim()||k.stdout.trim()}`);const I=await G(s);if(I.length)throw new c(`lane ${t.id} new worktree is dirty outside .verify/.claude/.codex; refusing to use it: ${I.join(", ")}`);return await R(s,"HEAD",r,t.id,"new worktree"),{tree:s,branch:n}}async function be({repo:e,parsed:t,out:r,skillRoot:s}){if(!ae(e).length)throw new c("no archived run to plan from; run qa run <repo> (or qa verify --base) first");const n=U(e,t.flags.run);if(n.meta.exit===p.INVALID)throw new c(`run ${n.id} exited 2 (INVALID): nothing in it is a finding`);const o=t.flags.lanes?Number(t.flags.lanes):4;if(!Number.isInteger(o)||o<1)throw new J("--lanes must be a positive integer");const h=Y(n),a=D(e),w=a&&a.run===n.id&&a.baseHead?a.baseHead:await Q(e,"main",n.id,"main");let i,l;if(a&&a.run===n.id)i=a.baseHead?a:{...a,baseHead:w},l=a.excluded;else{const f=ce({records:h,inventory:n.inventory,blocked:we(e),ghosts:he(e)});l=f.excluded;const m=n.meta.args??[],T=m.includes("--base")?m[m.indexOf("--base")+1]:null,_=m.filter((x,A)=>!(x==="--base"||m[A-1]==="--base")&&x!=="--ratchet"&&!x.startsWith("/"));if(i={...de({run:n.id,base:T,flags:_,baselineCounts:n.meta.counts??{total:f.classes.reduce((x,A)=>x+A.fids.length,0)},classes:f.classes,batches:le(f.classes,{lanes:o}),excluded:l}),baseHead:w},a){const x=new Set(["open","merged","blocked","retry","conflict"]),A=[];for(const[S,b]of Object.entries(a.classes))x.has(b.status)&&(i.classes[S]?i.classes[S]={...i.classes[S],status:b.status,pr:b.pr,branch:b.branch,worktree:b.worktree,attempts:b.attempts,gatedAt:b.gatedAt,lastGate:b.lastGate,conflictFiles:b.conflictFiles}:A.push({id:S,status:b.status,pr:b.pr??null,kind:b.kind}));A.length&&(i.dropped=A.sort((S,b)=>S.id.localeCompare(b.id)),r.warn(` ${A.length} class(es) from the previous plan (run ${a.run}) have no counterpart in run ${n.id} and were dropped: ${A.map(S=>`${S.id} [${S.status}${S.pr?" "+S.pr:""}]`).join(", ")} -- their PRs (if any) are still open on GitHub; close or merge them by hand`))}if(!f.classes.length)return j(e,i),E(e,i),r.say("",` qa fix plan ${e}`,"",` nothing to plan from run ${n.id}: ${l.length} finding(s) excluded (${[...new Set(l.map(x=>x.reason))].sort().join(", ")||"none"}), 0 classes`,""),r.emit({ok:!0,repo:e,run:n.id,classes:0,excluded:l}),p.OK}const y=ue(i);if(!y)return j(e,i),E(e,i),r.say("",` qa fix plan ${e}`,"",` ${N(i)}`," every batch settled; nothing left to plan",""),r.emit({ok:!0,repo:e,run:n.id,batches:i.batches.length,classes:Object.keys(i.classes).length,board:F(e)}),p.OK;const k=y.filter(f=>i.classes[f].status==="queued");if(k.length){const f=await Q(e,"main",n.id,"main");if(f!==i.baseHead)throw new c(`main HEAD ${f} changed since this lane plan recorded ${i.baseHead}; re-baseline before opening queued lanes`);const m=await G(e);if(m.length)throw new c(`main is dirty outside .verify/.claude/.codex; a lane worktree branches off main, so commit or stash first: ${m.join(", ")}`)}let I=0;for(const f of k){const m=i.classes[f],{tree:T,branch:_}=await pe(e,m,i.baseHead);i=q(i,f,"fixing",{worktree:T,branch:_}),B(d.join(H(e),f,"brief.md"),Z({repo:e,skillRoot:s,run:n,c:i.classes[f],records:h,plan:i})),j(e,i),E(e,i),I++}j(e,i),E(e,i);const u=i.batches.indexOf(y)+1,v=new Set(["open","merged","blocked"]),$=y.filter(f=>!v.has(i.classes[f].status));return r.say("",` qa fix plan ${e}`,"",` ${N(i)}`,` batch ${u} of ${i.batches.length}: ${I} new lane(s) opened, ${$.length} active current-batch lane(s) -- one brief each in ${H(e)}/<class>/brief.md`,` dispatch one fixer per lane; each ends with qa fix gate <class> then qa fix close <class>; then run qa run ${e} again`,""),r.emit({ok:!0,repo:e,run:n.id,batch:y,batches:i.batches.length,classes:Object.keys(i.classes).length,opened:I,active:$.length,board:F(e)}),p.FINDINGS}async function ye(e,t,r){const s=process.env.QA_ALLOW_TEST_HOOKS==="1"?process.env.QA_LANES_QA:null,n=await C(process.execPath,[s??d.join(e,"bin","qa.mjs"),...t],{cwd:r,stdout:"capture",stderr:"inherit"});let o=null;try{o=JSON.parse(n.stdout)}catch{}return{code:n.code,json:o}}async function ke(e,t){const r=process.env.QA_ALLOW_TEST_HOOKS==="1"?process.env.QA_LANES_GH:null,s=r?await C(process.execPath,[r,...t],{cwd:e,stdout:"capture",stderr:"capture"}):await C("gh",t,{cwd:e,stdout:"capture",stderr:"capture"});return{code:s.code,stdout:(s.stdout??"").trim(),stderr:(s.stderr??"").trim()}}const ve=K.flatMap(e=>["-e",e]),xe=K.map(e=>`:(exclude)${e}`);async function Se(e){await g(e,["checkout","--","."]),await g(e,["clean","-fd",...ve])}async function Ae(e){return(await g(e,["diff","--stat"])).stdout.trim()||"(no diff)"}async function ee(e){const t=await g(e,["diff","HEAD"]);return se.createHash("sha256").update(t.stdout).digest("hex")}const L=e=>d.join(H(e),"gate.lock");async function je(e,t){const r=Date.now()+18e5;for(;;)try{V.mkdirSync(d.dirname(L(e)),{recursive:!0}),V.writeFileSync(L(e),JSON.stringify({pid:process.pid,at:new Date().toISOString()}),{flag:"wx"});break}catch(s){if(s?.code!=="EEXIST")throw s;const n=P(L(e));if(!n||!Ee(n.pid)){X(L(e));continue}if(Date.now()>r)throw new c(`gate lock held by pid ${n.pid} for 30 minutes; remove ${L(e)} if that process is gone`);await new Promise(o=>setTimeout(o,2e3))}try{return await t()}finally{X(L(e))}}function Ee(e){if(!Number.isInteger(e))return!1;try{return process.kill(e,0),!0}catch(t){return t?.code==="EPERM"}}function te(e,t,r){const s=e?.classes?.[t];if(!s)throw new c(`no class "${t}" in ${W(r)}`);return s}async function ne({repo:e,id:t,out:r,skillRoot:s}){let n=D(e);if(!n)throw new c("no lanes plan; run qa fix plan first");const o=te(n,t,e);if(!o.worktree||!O(o.worktree))throw new c(`class ${t} has no worktree (status ${o.status})`);const h=["verify",o.worktree,...n.base?["--base",n.base]:[],...n.flags,"--ratchet","--no-archive","--json"],a=await je(e,()=>ye(s,h,o.worktree));if(a.code===p.INVALID||!a.json)return r.warn(` gate could not run for ${t} (exit ${a.code}); the worktree is left as is`),p.INVALID;const w=new Set((a.json.findings??[]).map($=>$.fid)),i=o.fids.filter($=>w.has($)),l=a.json.counts?.total??0,y=l>(n.baselineCounts?.total??1/0),k=i.length?`${i.length} of ${o.fids.length} finding(s) survived: ${i.join(", ")}`:y?`total rose ${n.baselineCounts.total} -> ${l} (RATCHET)`:`clean: ${o.fids.length} of ${o.fids.length} gone, total ${l}`;if(!i.length&&!y)return n=q(n,t,"gate",{gatedAt:await ee(o.worktree),lastGate:k}),j(e,n),E(e,n),r.say("",` gate ${t}: PASS -- ${k}`,` next: qa fix close ${t} ${e}`,""),r.emit({ok:!0,id:t,summary:k}),p.OK;const I={attempt:o.attempts.length+1,diffstat:await Ae(o.worktree),gateExit:a.code,gateSummary:k,regressed:y,at:new Date().toISOString()};await Se(o.worktree);const u=[...o.attempts,I],v=u.length>=2?"blocked":"retry";if(n=q(n,t,v,{attempts:u}),v==="blocked"){const $=d.join(e,".verify","loop.json"),f=P($)??{startedAt:new Date().toISOString(),iterations:[]},m=z.from(f);for(const[T,_]of u.entries())for(const x of o.fids)m.block(x,_.regressed?"regression":"no-change",T);M($,{...f,...m.toJSON()})}else{const $=U(e,n.run);B(d.join(H(e),t,"brief.md"),Z({repo:e,skillRoot:s,run:$,c:n.classes[t],records:Y($),plan:n}))}return j(e,n),E(e,n),r.say("",` gate ${t}: FAIL -- ${k}`,` worktree reverted; status ${v}${v==="retry"?` (fill the docs lookup in ${d.join(H(e),t,"brief.md")} and dispatch once more)`:" (two attempts attached; fids entered the block ledger)"}`,""),r.emit({ok:!1,id:t,summary:k,status:v,attempts:u.length}),p.FINDINGS}async function Ie({repo:e,id:t,out:r}){let s=D(e);if(!s)throw new c("no lanes plan; run qa fix plan first");const n=te(s,t,e);if(n.status!=="gate"||!n.gatedAt)throw new c(`class ${t}: no passing gate recorded (status ${n.status}); run qa fix gate ${t} ${e} first`);if(await ee(n.worktree)!==n.gatedAt)throw new c(`class ${t}: the worktree changed after its passing gate; run qa fix gate ${t} ${e} again`);const o=`fix(qa): ${n.kind} \u2014 closes ${n.fids.length} of ${s.baselineCounts?.total??n.fids.length} findings on ${n.routes.length} route(s)`,h=[`Class ${t} (${n.severity}), ${n.kind}${n.component?` in ${n.component}`:""}.`,"",`Routes: ${n.routes.join(", ")||"(none)"}`,`Findings closed: ${n.fids.join(", ")}`,"",`Gate: ${n.lastGate}`,"",`Lane brief: .verify/lanes/${t}/brief.md (not committed).`].join(`
5
+ `);await g(n.worktree,["add","-A","--",".",...xe]);const a=await g(n.worktree,["commit","-q","-m",o,"-m",h]);if(a.code!==0&&!/nothing to commit/.test(a.stdout+a.stderr))throw new c(`commit failed for ${t}: ${a.stderr}`);const w=await g(n.worktree,["push","-u","origin",n.branch]);if(w.code!==0)throw new c(`push failed for ${t}: ${w.stderr}`);const i=await ke(n.worktree,["pr","create","--head",n.branch,"--title",o,"--body",h]);if(i.code!==0)throw new c(`gh pr create failed for ${t}: ${i.stderr}`);const l=i.stdout.split(`
6
+ `).find(y=>/^https?:\/\//.test(y))??i.stdout;return s=q(s,t,"open",{pr:l}),j(e,s),E(e,s),r.say("",` closed ${t}: ${l}`,` ${N(s)}`,""),r.emit({ok:!0,id:t,pr:l}),p.OK}async function Oe({repo:e,out:t,skillRoot:r}){let s=D(e);if(!s)throw new c("no lanes plan");const n=Object.values(s.classes).filter(a=>a.status==="open"&&a.worktree&&O(a.worktree));let o=0;const h=[];for(const a of n){if(await g(a.worktree,["fetch","-q","origin","main"]),(await g(a.worktree,["rebase","origin/main"])).code!==0){const l=(await g(a.worktree,["diff","--name-only","--diff-filter=U"])).stdout.trim().split(`
7
+ `).filter(Boolean);await g(a.worktree,["rebase","--abort"]),s=q(s,a.id,"conflict",{conflictFiles:l}),o++,t.say(` ${a.id}: CONFLICT on ${l.join(", ")||"(unknown files)"} -- resolve in ${a.worktree}, then qa fix gate ${a.id} ${e} and qa fix close ${a.id} ${e}`);continue}j(e,s);const i=await ne({repo:e,id:a.id,out:t,skillRoot:r});s=D(e),i===p.OK?(await g(a.worktree,["push","--force-with-lease","origin",a.branch]),s=q(s,a.id,"open",{pr:a.pr})):i===p.INVALID&&(h.push(a.id),t.warn(` ${a.id}: gate could not run after rebase (exit 2); lane left as is`))}return j(e,s),E(e,s),t.say("",` rebased ${n.length} open lane(s), ${o} conflict(s)${h.length?`, ${h.length} could not gate`:""}`,` ${N(s)}`,""),h.length?(t.emit({ok:!1,rebased:n.length,conflicts:o,invalid:h}),p.INVALID):(t.emit({ok:o===0,rebased:n.length,conflicts:o}),o?p.FINDINGS:p.OK)}async function He({repo:e,out:t}){const r=D(e);if(!r)throw new c("no lanes plan");return E(e,r),t.say("",` ${N(r)}`,` -> ${F(e)}`,""),t.emit({ok:!0,board:F(e),header:N(r)}),p.OK}async function Be({parsed:e,out:t,skillRoot:r}){const s=e.positional[0];if(!["plan","gate","close","rebase","board"].includes(s))throw new J("usage: qa fix plan|gate|close|rebase|board <repo> ...");const n=re({...e,positional:s==="gate"||s==="close"?e.positional.slice(2):e.positional.slice(1)});if(!O(n))throw new c(`no such directory: ${n}`);if(s==="plan")return be({repo:n,parsed:e,out:t,skillRoot:r});if(s==="gate")return ne({repo:n,id:e.positional[1],out:t,skillRoot:r});if(s==="close")return Ie({repo:n,id:e.positional[1],out:t});if(s==="rebase")return Oe({repo:n,out:t,skillRoot:r});if(s==="board")return He({repo:n,out:t})}export{F as boardPath,ue as currentBatch,H as lanesDir,D as loadPlan,W as planPath,Be as run,j as savePlan,E as writeBoard};
@@ -0,0 +1,11 @@
1
+ import v from"node:path";import{resolveRepo as U,table as b,InvalidError as E}from"../../cli.mjs";import{readJson as k,exists as N,writeText as D}from"../../fs.mjs";import{shapes as A,normRoute as x}from"../../reports.mjs";import{listRunIds as R,loadRun as M,runRecords as S}from"../../runs.mjs";import{fpKey as C}from"../../fp.mjs";const I=/^L\d*\??$/;function L(e){return!e.file||!e.kind||!e.anchor||I.test(e.anchor)?null:C(e.kind,v.basename(e.file),e.anchor)}function O(e){const i=e.length;if(i>=2){const t=e[i-2],f=e[i-1];if(t!=null&&f==="waived")return"waived";if(t!=null&&f===null)return"fixed"}if(i===2&&e[0]==="live"&&e[1]==="live")return"persists";if(i>=3){let t=0;for(const f of e)if(t=f==="live"?t+1:0,t>=3)return"persists"}return e[i-1]!=null&&e.slice(0,i-1).every(t=>t==null)?"new":null}function z(e){const i=new Map,t=[];let f=0;for(const a of[...e].sort()){const u=R(a);if(t.push({repo:a,runs:u.length}),f+=u.length,!u.length)continue;const m=u.map(p=>M(a,p)),o=new Map;m.forEach((p,l)=>{for(const n of S(p)){const r=L(n);if(!r)continue;let s=o.get(r);s||(s={rule:n.kind,states:new Array(u.length).fill(null)},o.set(r,s)),s.states[l]!=="live"&&(s.states[l]=n.waived?"waived":"live")}});for(const[p,{rule:l,states:n}]of o){let r=i.get(l);r||(r={filed:new Set,fixed:new Set,waived:new Set,persists:new Set,repos:new Set},i.set(l,r)),r.filed.add(p),r.repos.add(a);const s=O(n);s==="fixed"?r.fixed.add(p):s==="waived"?r.waived.add(p):s==="persists"&&r.persists.add(p)}}const d=[...i.entries()].map(([a,u])=>({rule:a,filed:u.filed.size,fixed:u.fixed.size,waived:u.waived.size,persists:u.persists.size,reposSeen:u.repos.size})).sort((a,u)=>u.waived+u.persists-(a.waived+a.persists)||a.rule.localeCompare(u.rule)),$=d.filter(a=>a.fixed===0&&a.waived+a.persists>=5).map(a=>`${a.rule}: no real-repo precision evidence -- belongs at P3 (CLAUDE.md ladder)`);return{runsTotal:f,reposTotal:e.length,perRepo:t,rows:d,warnings:$}}function P(e){const i=e?.sweep;return i?i.status==="invalid"||i.integrity?.ok===!1:!1}const J="needs-reproduction (runtime integrity failed)",W="unmeasured (runtime integrity failed)",_=e=>e.includes("[")||e.startsWith(":");function F(e,i){const t=e.split("/"),f=i.split("/");return t.length===f.length&&t.every((d,$)=>_(d)||d===f[$])}function K({inventory:e,research:i,run:t,journeys:f}){const d=e.routes??[],$=(f??[]).map(l=>x(l.start??"/")),a=l=>$.some(n=>F(l,n)),u=P(t),m=new Map,o=new Map;if(t){for(const n of t.sweep?.routes??[]){if(String(n.route).startsWith("("))continue;const r=x(n.route),s=m.get(r)??{cells:0,reached:0};s.cells++,!n.redirectedTo&&!n.skipped&&s.reached++,m.set(r,s)}for(const n of S(t)){if(n.waived)continue;const r=n.subject??n.route;if(!r)continue;const s=o.get(r)??[];s.push(n),o.set(r,s)}const l=new Map(d.map(n=>[n.file,x(n.path)]));for(const n of S(t)){if(n.waived||!n.file)continue;const r=l.get(n.file);if(!r)continue;const s=o.get(r)??[];s.push(n),o.set(r,s)}}const p=d.map(l=>{const n=x(l.path),r=i?.routes?.find(h=>x(h.path)===n),s=m.get(n),c=o.get(n)??[],w=h=>h.severity==="P0"||h.severity==="P1",j=c.filter(h=>w(h)&&!(u&&!h.file)),g=u?c.filter(h=>w(h)&&!h.file).length:0,T=/[:[]/.test(l.path);let y;return t?j.length?y=`bug:${j[0].fid}`:u?y=g?J:W:s&&s.reached?y="clean":T?y=a(n)?"journeyed":"untested (dynamic: needs a journey)":s&&!s.reached?y="unreached":y=t.meta.mode==="static"?"static-only":"untested":y="untested",{route:l.path,file:l.file,entities:l.entities??[],mutations:(l.mutations??[]).length,journey:a(n)?"yes":r?.draftJourney?"draft":(l.mutations??[]).length?"NEEDED":"-",swept:s?`${s.reached}/${s.cells}`:"-",findings:c.length,gating:j.length,withheld:g,status:y,dynamic:T}});return{rows:p,count:p.length,expected:e.counts?.routes??null,runtimeUntrusted:u}}function q(e){const i=`| route | entities | mutations | journey | swept | findings | status |
2
+ |---|---|---|---|---|---|---|
3
+ `,t=e.rows.map(d=>`| ${d.route} | ${d.entities.join(", ")||"-"} | ${d.mutations} | ${d.journey} | ${d.swept} | ${d.findings} | ${d.status} |`).join(`
4
+ `),f=e.runtimeUntrusted?`
5
+ **The runtime evidence in this run failed its own integrity gate: it did not measure the application.** The observations are kept above, but no route status is derived from them \u2014 they are unmeasured, not bugs, and not clean.
6
+ `:"";return`# Ledger
7
+
8
+ ${e.count} rows; inventory.counts.routes = ${e.expected}${e.count===e.expected?"":" **MISMATCH**"}
9
+ ${f}
10
+ ${i}${t}
11
+ `}async function Y({parsed:e,out:i}){if(e.flags.across){const c=[e.flags.across,...e.positional].map(g=>v.resolve(process.cwd(),g)),w=z(c),j=w.rows.map(g=>[g.rule,g.filed,g.fixed,g.waived,g.persists,g.reposSeen]);return i.say("",` ${w.runsTotal} runs over ${w.reposTotal} repos`,"",b([["rule","filed","fixed","waived","persists","repos"],...j]),...w.warnings.length?["",...w.warnings.map(g=>` WARN ${g}`)]:[],""),i.emit({ok:!0,runsTotal:w.runsTotal,reposTotal:w.reposTotal,rows:w.rows,warnings:w.warnings}),0}const t=U(e),f=v.join(t,".verify","inventory.json"),d=k(f,{shape:A.inventory,what:"inventory.json"});if(!d)throw new E(`no inventory at ${f}; run \`qa verify ${t}\` or \`qa init ${t} --study\` first`);const $=k(v.join(t,".verify","research.json")),a=R(t).length?M(t,e.flags.run??"latest"):null;let u=null;const m=[v.join(t,"verify.journeys.mjs"),v.join(t,".verify","journeys.mjs")].find(N);if(m)try{u=(await import(m+"?t="+Date.now())).default}catch(c){i.warn(` journeys file failed to load: ${c.message}`),u=[]}const o=K({inventory:d,research:$,run:a,journeys:u});if(o.count!==o.expected)throw new E(`ledger has ${o.count} rows but inventory.counts.routes is ${o.expected}; the inventory is inconsistent with itself`);const p=q(o);D(v.join(t,".verify","ledger.md"),p);const l=o.rows.filter(c=>c.status==="clean").length,n=o.rows.filter(c=>c.status.startsWith("bug:")).length,r=o.rows.filter(c=>/^untested|unreached|static-only|unmeasured|needs-reproduction/.test(c.status)).length,s=`${l}/${o.count} clean, ${n} with gating findings, ${r} not measured`+(o.runtimeUntrusted?" (runtime integrity FAILED: the run did not measure the app, so no route status comes from it)":"")+(a?` (run ${a.id})`:" (no runtime run yet)");return i.say("",` ledger ${o.count} rows = inventory.counts.routes ${o.expected}`,` ${s}`,"",b([["route","ent","mut","journey","swept","find","status"],...o.rows.slice(0,60).map(c=>[c.route,c.entities.length,c.mutations,c.journey,c.swept,c.findings,c.status])]),o.rows.length>60?` ... ${o.rows.length-60} more in .verify/ledger.md`:"",` -> ${v.join(t,".verify","ledger.md")}`,""),i.emit({ok:!0,count:o.count,expected:o.expected,checkpoint:s,runtimeUntrusted:o.runtimeUntrusted,rows:o.rows}),0}export{J as NEEDS_REPRODUCTION,W as UNMEASURED_RUNTIME,z as buildAcrossLedger,K as buildLedger,O as classifyFate,q as ledgerMarkdown,F as routeMatchesJourneyStart,Y as run,P as runtimeEvidenceUntrusted};
@@ -0,0 +1 @@
1
+ import{resolveRepo as m,forwardFlags as f,InvalidError as e,rejectRemovedFlags as s}from"../../cli.mjs";import{exists as a}from"../../fs.mjs";import{runLoop as c}from"../../loop.mjs";const l=new Set(["fixer","fixer-protocol","max","commit","fixer-timeout","help","no-archive"]);async function x({parsed:o,out:t,skillRoot:i}){if(s(o,{command:"run loop",flags:["fixer","fixer-protocol","fixer-timeout","measure-only"]}),o.flags.commit)throw new e("qa run loop: --commit was retired; the host agent owns source commits");if(o.flags.max!==void 0)throw new e("qa run loop: --max was retired; the host handoff is one bounded gate");const r=m(o);if(!a(r))throw new e(`no such directory: ${r}`);const n=f(o,l);return c({repo:r,skillRoot:i,verifyArgs:n,out:t})}export{x as run};
@@ -0,0 +1 @@
1
+ import{resolveRepo as R,forwardFlags as q,UsageError as r,InvalidError as c,rejectRemovedFlags as E}from"../../cli.mjs";import{exists as L}from"../../fs.mjs";import{runMarathon as O,loadState as U,statusOf as W,formatStatus as j}from"../../marathon.mjs";import{acquireMarathonLock as C}from"../../marathon-wait.mjs";const D=new Set(["base","serve","hours","fixer","commit","fixer-protocol","fixer-timeout","measure-only","no-states","no-dark","stability-runs","checkpoint-minutes","resume","fresh","lanes","lane","flow-workers","dry-run","keep-serving","help","status","once"]);function i(n,s,a){const t=n.flags[s];if(t===void 0)return a;if(t===!0)throw new r(`--${s} needs a value`);const o=Number(t);if(!Number.isFinite(o)||o<=0)throw new r(`--${s} must be a positive number, got "${t}"`);return o}async function H({parsed:n,out:s,skillRoot:a}){if(E(n,{command:"run",flags:["fixer","fixer-protocol","fixer-timeout","measure-only"]}),n.flags.commit)throw new c("qa run: --commit was retired; the host agent owns source commits");const t=R(n);if(!L(t))throw new c(`no such directory: ${t}`);if(n.flags.status){const e=W(U(t));return s.say("",` qa run --status ${t}`,"",j(e,t),""),s.emit(e?{ok:!0,repo:t,...e}:{ok:!1,repo:t,reason:"no marathon state"}),e?e.running||e.finished?0:1:2}const o=!!n.flags.serve,l=typeof n.flags.base=="string"?n.flags.base:null;if(o&&l)throw new r("pass only one of --base / --serve");const u=i(n,"hours",10),g="widths"in n.flags||"width"in n.flags,m=!n.flags["no-states"],h=!n.flags["no-dark"],w=i(n,"stability-runs",3),k=i(n,"checkpoint-minutes",45),v=!!n.flags.resume,y=!!n.flags.fresh,b=i(n,"lanes",4),x=typeof n.flags.lane=="string"?n.flags.lane:null;if(n.flags.lane===!0)throw new r("--lane needs a value");const F=Math.max(1,Math.min(4,Math.trunc(i(n,"flow-workers",1)))),f=!!n.flags["dry-run"],M=!!n.flags["keep-serving"],G=!!n.flags.once,S=!!n.flags["i-know-this-is-not-production"],$=q(n,D),N=f?()=>{}:C(t);try{return await O({repo:t,skillRoot:a,out:s,flags:{serve:o,baseFlag:l,hours:u,widthsGiven:g,states:m,dark:h,stabilityRuns:w,checkpointMinutes:k,resume:v,fresh:y,lanes:b,dryRun:f,keepServing:M,extraFlags:$,overrideNonLocal:S,laneFlag:x,flowWorkers:F,once:G,baseGiven:"base"in n.flags,statesGiven:"no-states"in n.flags||"states"in n.flags,darkGiven:"no-dark"in n.flags||"dark"in n.flags,laneGiven:"lane"in n.flags,flowWorkersGiven:"flow-workers"in n.flags}})}finally{N()}}export{H as run};
@@ -0,0 +1,38 @@
1
+ import g from"node:path";import{resolveRepo as S,EXIT as y,REDACTED as I}from"../../cli.mjs";import{loadRun as N,runRecords as R}from"../../runs.mjs";import{triage as A,loadRoutePriorities as C}from"../../triage.mjs";import{atlasIdFor as E,hintFor as K,atlasEntry as L}from"../../atlas.mjs";import{readJson as T,writeJson as D,writeText as O}from"../../fs.mjs";import{denominatorsFor as V}from"../../denominators.mjs";import{loadPatterns as P,matchesForKind as W,guardThreshold as q}from"../../pattern-ledger.mjs";import{recommendFor as z}from"../../recommend.mjs";function B(s){return s.lane??"source"}function J(s,o=new Set){for(const a of s.groups)if(!a.fids.every(t=>o.has(t)))return a;return null}function G(s,o=new Set){const a=s.groups.filter(t=>!t.fids.every(i=>o.has(i)));return a.length>0&&a.every(t=>t.allKnownFp)}function M(s=[]){const o=new Set(["--prod","--dark","--mutate","--no-warm","--no-journeys","--leak-check"]),a=new Set(["--width","--widths","--parallel","--height","--delay","--settle","--nav-timeout","--warm-timeout","--text-floor","--integrity-share","--lane","--launcher","--oracle-path"]),t=new Set(["--states","--exercise"]),i=[];for(let r=0;r<s.length;r++){const u=s[r],h=String(u).split("=",1)[0];o.has(h)?i.push(u):a.has(h)?(i.push(u),!String(u).includes("=")&&s[r+1]!=null&&i.push(s[++r])):t.has(h)&&(i.push(u),!String(u).includes("=")&&s[r+1]!=null&&!String(s[r+1]).startsWith("--")&&i.push(s[++r]))}return i}function X({repo:s,skillRoot:o,run:a,group:t,records:i,base:r,inventory:u,patterns:h=P()}){typeof r=="string"&&r.includes(I)&&(r=null);const p=i.filter(n=>t.fids.includes(n.fid)),e=t.sample,f=E(e),{callers:l,example:c}=z(e,u),m=L(o,f,e.atlasVersion),$=K(e),v=e.file?`${e.file}:${e.line}`:`${e.route??"(page)"}${e.at&&e.at!=="(page)"?" at "+e.at:""}`,d=t.routes,w=M(a?.meta?.args??a?.args??[]),b=e.phase==="static"||e.phase==="sync"?`node ${g.join(o,"bin",e.phase==="sync"?"inventory.mjs":"classify.mjs")} ${s}`:`node ${g.join(o,"bin","sweep.mjs")} --repo ${s} --base ${r??"<BASE>"} --routes ${(e.subject??e.route??"/").replace(/^\(.*?:\s*|\)$/g,"")}`+(w.length?` ${w.join(" ")}`:"")+(e.phase==="state"&&!w.includes("--states")?" --states":e.phase==="mutate"&&!w.includes("--mutate")?" --mutate":""),k=e.phase==="static"?`// verify-ignore: ${e.kind} -- <where the guarantee comes from>`:e.phase==="sync"?"// verify-ignore: sync-risk -- <why nothing renders this resource>":null,x=W(h,e.kind).slice(0,3).map(n=>({description:n.description,snippet:n.snippet,count:n.count,repos:n.repos.length,guardCandidate:n.repos.length>=q()})),j={run:a.id,fid:e.fid,fids:t.fids,phase:e.phase,kind:e.kind,severity:e.severity,where:v,file:e.file??null,line:e.line??null,route:e.route??null,at:e.at??null,routes:d,files:t.files,count:t.count,leverage:t.leverage,determinism:t.determinism,sharedSurface:t.sharedSurface,detail:e.detail,atlas:f,atlasText:m?.text??null,hint:$,repro:b,waiver:k,knownFp:e.knownFp??null,staleRoutes:e.staleRoutes??null,endpoint:e.endpoint??null,hook:e.hook??null,callers:l.length?l:null,workingExample:c,priorFixes:x,members:p.slice(0,25).map(n=>({fid:n.fid,route:n.route,file:n.file,line:n.line,at:n.at,detail:n.detail}))},F=[`# Fix next: ${e.severity} ${e.kind}`,"",`- **run**: ${a.id}`,`- **fid**: ${e.fid}${t.fids.length>1?` (+${t.fids.length-1} in this group)`:""}`,`- **phase**: ${e.phase} \xB7 **determinism**: ${t.determinism}${t.sharedSurface?" \xB7 shared surface":""}`,`- **where**: ${v}`,d.length?`- **routes affected** (${d.length}): ${d.slice(0,12).join(", ")}${d.length>12?", \u2026":""}`:null,t.files.length>1?`- **files** (${t.files.length}): ${t.files.slice(0,8).join(", ")}`:null,e.staleRoutes?.length?`- **blast radius**: ${e.staleRoutes.join(", ")}`:null,e.endpoint?`- **endpoint**: ${e.endpoint}${e.hook?` \xB7 hook ${e.hook}`:""}`:null,"","## What was measured","",e.detail,"",f?`## Atlas ${f}${m?` \u2014 ${m.text} (detection ${m.detection})`:""}`:null,$?`
2
+ ## Likely fix
3
+
4
+ ${$}
5
+
6
+ ${l.length?`Fix the root cause: ${l.length} caller(s) are listed below in "Callers". One guard in the shared function beats N at N call sites.`:"Fix the root cause: grep every caller first. One guard in the shared function beats N at N call sites."}`:null,l.length?`
7
+ ## Callers
8
+
9
+ Every route whose import graph reaches this mutation:
10
+
11
+ ${l.map(n=>`- ${n.route}${n.file?` (${n.file})`:""}`).join(`
12
+ `)}`:null,c?`
13
+ ## A working example in this repo
14
+
15
+ \`${c.hook??`${c.file}:${c.line}`}\` writes "${c.entity}" and correctly invalidates it, and is not itself flagged. Copy its invalidation, not its layout.
16
+
17
+ ${c.file}:${c.line}`:null,"","## Reproduce","","```",b,"```","",k?`## If this is a proven false positive
18
+
19
+ Put the proof in the source, within 6 lines above the flagged line:
20
+
21
+ \`\`\`
22
+ ${k}
23
+ \`\`\`
24
+
25
+ Waived findings stay counted and listed; there is no silent bucket.`:'## If this is a proven false positive\n\nA runtime finding is measured, not inferred. Confirm it with `qa verify --runs N` before disputing it; a ghost is listed under "needs reproduction" and never gates.',"",x.length?`## You've fixed this before
26
+
27
+ `+x.map(n=>`- ${n.description} \u2014 seen ${n.count}\xD7 across ${n.repos} repo${n.repos===1?"":"s"}`+(n.guardCandidate?' **candidate for a permanent rule** (SKILL.md "The loop that ends the clicking" \u2014 promote it to classify.mjs/probe.js instead of fixing it by hand again)':"")+(n.snippet?`
28
+ \`\`\`
29
+ ${n.snippet.split(`
30
+ `).join(`
31
+ `)}
32
+ \`\`\``:"")).join(`
33
+ `):null,x.length?"":null,"## Then","","1. Commit that one fix.","2. Re-run the gate with `--ratchet`: total findings must not increase. A fix that trades one finding for two is reverted, not argued.","3. Run the selftests if any detector was touched (it should not have been).","",p.length>1?`## Members
34
+
35
+ `+p.slice(0,25).map(n=>`- ${n.fid} ${n.file?n.file+":"+n.line:n.route+(n.at&&n.at!=="(page)"?" at "+n.at:"")} \u2014 ${String(n.detail).slice(0,100)}`).join(`
36
+ `):null].filter(n=>n!==null).join(`
37
+ `)+`
38
+ `;return{json:j,md:F}}async function ie({parsed:s,out:o,skillRoot:a}){const t=S(s),i=N(t,s.flags.run),r=B(i.meta);if(i.meta.exit===y.INVALID)return o.warn(` run ${i.id} exited 2: INVALID. Fix the harness, not the app; nothing in it is a finding.`),o.emit({ok:!1,run:i.id,lane:r,phase:null,exit:2,next:null,reason:"invalid"}),y.INVALID;const u=(i.meta.args??[]).includes("--prod"),h=R(i),p=A({records:h,inventory:i.inventory,sweep:i.sweep,classify:i.classify,prod:u,priorities:C(t)}),e=T(g.join(t,".verify","loop.json")),f=new Set(e?.blocked??[]);if(G(p,f))return o.say("",` only known false positives remain in run ${i.id}; waive them in source with verify-ignore (see qa fix fp list)`,""),o.emit({ok:!0,run:i.id,lane:r,phase:null,next:null,reason:"known-fps-only",groups:p.groups.filter(d=>d.allKnownFp).map(d=>({kind:d.kind,files:d.files,fids:d.fids}))}),y.OK;const l=J(p,f);if(!l){o.say("",` nothing to fix in run ${i.id}`+(f.size?` (${f.size} blocked)`:""),"");const d=p.groups.length===0?"no-findings":"blocked";return o.emit({ok:!0,run:i.id,lane:r,phase:null,next:null,reason:d,blocked:[...f]}),y.OK}const c=(i.meta.args??[])[(i.meta.args??[]).indexOf("--base")+1]??null,{json:m,md:$}=X({repo:t,skillRoot:a,run:i,group:l,records:h,base:(i.meta.args??[]).includes("--base")?c:null,inventory:i.inventory});D(g.join(t,".verify","next.json"),m),O(g.join(t,".verify","next.md"),$);const v=V({phase:l.sample.phase,group:l,classify:i.classify,inventory:i.inventory,sweep:i.sweep});return o.say("",$,` -> ${g.join(t,".verify","next.md")}`),o.emit({ok:!0,run:i.id,lane:r,phase:l.sample.phase,next:m,brief:g.join(t,".verify","next.md"),group:{count:l.count,kind:l.kind,routes:l.routes,files:l.files,determinism:m.determinism},denominators:v}),y.FINDINGS}export{X as buildBrief,G as onlyKnownFpsLeft,J as pickNext,ie as run,M as runtimeReproFlags};
@@ -0,0 +1,4 @@
1
+ import f from"node:path";import{createHash as I}from"node:crypto";import{resolveRepo as y,InvalidError as k,UsageError as j,table as T}from"../../cli.mjs";import{readJson as b,readText as P,exists as x,writeText as R}from"../../fs.mjs";import{loadRun as w,runRecords as q,trajectory as E,listRuns as J,diffRuns as C,resolveValidRunId as N}from"../../runs.mjs";import{triage as O,loadRoutePriorities as S}from"../../triage.mjs";import{assertSameLane as G}from"../../lanes.mjs";async function K(r){const{parsed:e}=r,o=["ci","diff","runs"].includes(e.command)?e.command:["diff","runs"].includes(e.positional[0])?e.positional[0]:null;return o&&e.command==="report"&&(r={...r,parsed:{...e,positional:e.positional.slice(1)}}),o==="runs"?L(r):o==="diff"?M(r):o==="ci"||e.flags.format&&e.flags.format!=="html"?U(r):H(r)}function D(r){const e=r.meta.evidence?.coherence;if(!e?.path||!/^[A-Za-z0-9._-]+$/.test(e.path))return null;const o=f.join(r.dir,e.path);if(!x(o))return{status:"invalid",problems:["linked coherence artifact missing"]};const u=P(o);if(I("sha256").update(u).digest("hex")!==e.sha256)return{status:"invalid",problems:["linked coherence digest mismatch"]};try{return{status:"linked",path:e.path,report:JSON.parse(u)}}catch{return{status:"invalid",problems:["linked coherence artifact malformed"]}}}async function H({parsed:r,out:e}){const[{buildSyncGraph:o},{renderReport:u},{coherenceReportModel:l}]=await Promise.all([import("../../graph.mjs"),import("../../report-html.mjs"),import("../../coherence/report.mjs")]),t=y(r),s=w(t,r.flags.run),c=(s.meta.args??[]).includes("--prod"),a=O({records:q(s),inventory:s.inventory,sweep:s.sweep,classify:s.classify,prod:c,priorities:S(t)}),n=E(t),d=s.inventory?o(s.inventory):{measured:!1,nodes:[],edges:[]},i=b(f.join(t,".verify","stability.json")),h=f.join(t,".verify","ledger.md"),m=x(h)?P(h):null,p=b(f.join(t,".verify","lanes","plan.json")),A=l({linked:D(s),inventory:s.inventory}),v=u({repo:t,run:s,triage:a,trajectory:n,syncGraph:d,stability:i,ledgerMd:m,lanesPlan:p,coherence:A}),g=r.flags.out,$=g?f.isAbsolute(g)?g:f.join(t,g):f.join(t,".verify","report.html");return R($,v),e.say("",` qa show report run ${s.id} (${s.meta.mode}, exit ${s.meta.exit})`,` ${a.counts.findings} findings (${a.groups.length} groups) P0:${a.counts.P0} P1:${a.counts.P1} P2:${a.counts.P2} P3:${a.counts.P3}`,` -> ${$} (${v.length} bytes)`,""),e.emit({ok:!0,run:s.id,out:$,bytes:v.length,counts:a.counts}),0}async function L({parsed:r,out:e}){const o=y(r),u=J(o);if(!u.length)return e.say(""," no archived runs; `qa verify` writes one per invocation",""),e.emit({ok:!0,runs:[],trajectory:[]}),0;const l=[["run","mode","exit","total","P0","P1","P2","P3","reached","integrity"]];for(const t of u)l.push([t.id,t.mode??"?",t.exit??"?",t.counts?.total??"?",t.counts?.P0??0,t.counts?.P1??0,t.counts?.P2??0,t.counts?.P3??0,t.summary?`${t.summary.routesReached}/${t.summary.routesRequested}`:"-",t.integrity?t.integrity.ok?"ok":"FAILED":"-"]);return e.say("",T(l),""),e.emit({ok:!0,runs:u,trajectory:E(o)}),0}async function M({parsed:r,out:e}){const o=y(r);let u=r.flags.to,l=[];if(!u||u==="latest"){const i=N(o,{});if(!i.id)throw new k("no archived run has passed its own integrity gate -- nothing can serve as a diff baseline; run `qa show runs` to see what is on disk");u=i.id,l=i.skipped}const t=w(o,u);let s=r.flags.from,c=[];if(!s||s==="previous"){const i=N(o,{before:u});if(!i.id)throw new k("no earlier archived run has passed its own integrity gate -- nothing to compare against");s=i.id,c=i.skipped}const a=w(o,s);G(a.meta.lane,t.meta.lane,{operation:"diff"});const n=C(a,t),d=i=>`${i.severity} ${i.kind} ${i.file?i.file+":"+i.line:(i.route??"")+(i.at&&i.at!=="(page)"?" at "+i.at.slice(0,40):"")}`;return e.say("",` qa show diff ${a.id} (${a.meta.mode}) -> ${t.id} (${t.meta.mode})`),l.length&&e.say(` NOTE skipped ${l.length} run(s) that failed their own integrity gate to pick "to": ${l.join(", ")}`),c.length&&e.say(` NOTE skipped ${c.length} run(s) that failed their own integrity gate to pick "from": ${c.join(", ")}`),n.comparable||e.say(` NOTE different modes (${n.modes.join(" vs ")}): the two runs used different instruments, so "new" below includes everything only the runtime half can see`),e.say(` fixed ${n.counts.fixed} \xB7 new ${n.counts.new} \xB7 persisting ${n.counts.persisting} \xB7 unmeasured ${n.counts.unmeasured}`),n.unmeasured.length&&e.say(" NOTE the later run did not measure the route/persona/viewport these were observed on, so they are neither fixed nor confirmed"),n.fixed.length&&e.say(""," fixed",...n.fixed.slice(0,20).map(i=>" "+d(i))),n.unmeasured.length&&e.say(""," unmeasured (not measured again)",...n.unmeasured.slice(0,20).map(i=>" "+d(i)+(i.width?` @${i.width}px`:""))),n.new.length&&e.say(""," new",...n.new.slice(0,20).map(i=>" "+d(i))),e.say(""),e.emit({ok:!0,...n,skippedInvalidRuns:{to:l,from:c}}),0}async function U({parsed:r,out:e,skillRoot:o}){const{buildSarif:u,buildGithubCommands:l,buildJunit:t}=await import("../../ci.mjs"),s=y(r),c=typeof r.flags.format=="string"?r.flags.format:null;if(!c)throw new j("--format sarif|github|junit is required");if(!["sarif","github","junit"].includes(c))throw new j(`--format must be one of sarif, github, junit (got "${c}")`);const a=typeof r.flags.out=="string"?r.flags.out:null,n=w(s,r.flags.run),d=q(n),i=(n.meta.args??[]).includes("--prod"),h=O({records:d,inventory:n.inventory,sweep:n.sweep,classify:n.classify,prod:i,priorities:S(s)});let m;if(c==="sarif"){const p=b(f.join(o,"package.json"))?.version??"0.0.0";m=JSON.stringify(u({records:d,inventory:n.inventory,informationUri:"https://github.com/ArkashJ/Automated_QA",version:p,skillRoot:o,toolName:"frontend-verify"}),null,2)+`
2
+ `}else if(c==="github"){const p=l(d);m=(p.length?p.join(`
3
+ `):"")+`
4
+ `}else m=t({groups:h.groups});return a?(R(f.resolve(process.cwd(),a),m),e.say(` qa show ci wrote ${c} (${m.length} bytes) to ${a} \u2014 run ${n.id}, exit ${n.meta.exit}`),e.emit({ok:n.meta.exit===0,format:c,file:a,run:n.id,exit:n.meta.exit,counts:h.counts})):process.stdout.write(m),n.meta.exit}export{K as run,U as runCi,M as runDiff,L as runRuns};
@@ -0,0 +1,4 @@
1
+ import r from"node:path";import{resolveRepo as I,InvalidError as m}from"../../cli.mjs";import{readJson as w,writeJson as g,writeText as d,exists as k}from"../../fs.mjs";import{shapes as x}from"../../reports.mjs";import{run as R}from"../../proc.mjs";import{listRunIds as D,loadRun as J,newRunId as M}from"../../runs.mjs";import{buildLedger as C,ledgerMarkdown as P}from"./ledger.mjs";import{buildResearch as F,researchMarkdown as H,generateJourneysDraft as q}from"../../research.mjs";import{buildHarnessPlan as Q,harnessPlanMarkdown as W}from"../../harness-plan.mjs";import{buildAnalysis as A,analysisMarkdown as E}from"../../analysis.mjs";async function V({parsed:h,out:a,skillRoot:j}){const e=I(h);if(!k(e))throw new m(`no such directory: ${e}`);const u=r.join(e,".verify","inventory.json");let t=w(u,{shape:x.inventory,what:"inventory.json"});if(!t){a.say("",` no inventory at ${u}; running \`node bin/inventory.mjs ${e}\` first`);const o=await R(process.execPath,[r.join(j,"bin","inventory.mjs"),e],{stdout:"capture",stderr:"capture"});if(o.code!==0)throw new m(`inventory could not be generated (exit ${o.code}): ${(o.stderr||o.stdout||"").trim().slice(-2e3)||"unknown error"}`);t=w(u,{required:!0,shape:x.inventory,what:"inventory.json"})}const f=M(),n=F(e,{inventory:t,runId:f});n.harness=Q({root:e,research:n,inventory:t,skillRoot:j}),g(r.join(e,".verify","research.json"),n);const b=D(e).length?J(e,"latest"):null;let y=null;const p=[r.join(e,"verify.journeys.mjs"),r.join(e,".verify","journeys.mjs")].find(k);if(p)try{y=(await import(p+"?t="+Date.now())).default}catch(o){a.warn(` journeys file failed to load: ${o.message}`),y=[]}const s=C({inventory:t,research:n,run:b,journeys:y});if(s.count!==s.expected)throw new m(`ledger has ${s.count} rows but inventory.counts.routes is ${s.expected}; the inventory is inconsistent with itself`);d(r.join(e,".verify","ledger.md"),P(s)),d(r.join(e,".verify","journeys.draft.mjs"),q(n,f)),d(r.join(e,".verify","research.md"),H(n,s)+`
2
+ `+W(n.harness));let i=null;h.flags.analysis&&(i=A(e,{inventory:t,research:n}),g(r.join(e,".verify","analysis.json"),i),d(r.join(e,".verify","analysis.md"),E(i)));const v=n.routes.filter(o=>o.draftJourney),c=v.length,l=v.filter(o=>o.journeyReady).length,$=c-l;return a.say("",` qa init --study ${e}`,"",` ${n.counts.routes} routes; ledger ${s.count} rows (expected ${s.expected})`,` forms found on ${n.counts.routesWithForms} routes; ${n.counts.routesWithMutations} mutation routes -> ${c} draft journeys`,c?` ${l} ready for review (every field + submit resolved from source), ${$} need a human or a playwright-cli session`:"",` principals: ${n.counts.principals}${n.principals.rolesFile?` (${n.principals.rolesFile.path} present)`:" (no verify.roles.json)"}`,` mock layer: ${n.mocks.detected?n.mocks.kinds.join(", "):"none detected"}${n.mocks.detected?" \u2014 a run against mocks tests the mocks":""}`,` contract drift candidates: ${n.counts.contractDriftCandidates}`,n.harness.workItems.length?` harness handoff: ${n.harness.workItems.slice(0,3).map(o=>`${o.entity??"unresolved"} (${o.impact.score})`).join(", ")}`:" harness handoff: no mutation source found","",` -> ${r.join(e,".verify","research.json")}`,` -> ${r.join(e,".verify","research.md")}`,` -> ${r.join(e,".verify","ledger.md")}`,` -> ${r.join(e,".verify","journeys.draft.mjs")}`,...i?[` -> ${r.join(e,".verify","analysis.json")}`,` -> ${r.join(e,".verify","analysis.md")}`]:[],c?`
3
+ review the draft, then promote with:
4
+ cp ${r.join(e,".verify","journeys.draft.mjs")} ${r.join(e,"verify.journeys.mjs")}`:"",""),a.emit({ok:!0,repo:e,runId:f,counts:{...n.counts,journeysReady:l,journeysNeedHuman:$},ledger:{count:s.count,expected:s.expected},principals:n.principals,mocks:n.mocks,contractDrift:n.contractDrift,residueQuestions:n.residueQuestions,harness:n.harness,analysis:i}),0}export{V as run};
@@ -0,0 +1 @@
1
+ import h from"node:fs";import I from"node:os";import p from"node:path";import{EXIT as f,UsageError as g,SELFTEST_SUITES as $}from"../../cli.mjs";import{run as S}from"../../proc.mjs";import{cargoPhaseAvailability as N}from"../../agent-availability.mjs";async function D({parsed:e,out:n,skillRoot:i}){const o=[...$],u=o.filter(t=>!["runtime","atlas","e2e"].includes(t)),r=o.filter(t=>e.flags[t]);if(+!!e.flags.all+ +!!e.flags.fast+ +!!r.length>1)throw new g("choose --all, --fast, or individual suites; do not combine them");const d=r.length?r:e.flags.all?o:e.flags.fast?u:["static","cli"],a=Number(e.flags.parallel??4);if(!Number.isInteger(a)||a<1)throw new g("--parallel must be a positive integer");const s=[];for(const t of d){if(["agent","tournament","e2e"].includes(t)){const l=N();if(l.status!=="measured"){s.push({suite:t,skipped:l.reason});continue}}const b=p.join(i,t==="runtime"?"selftest.sh":`selftest-${t}.sh`);if(s.push(await k(t==="cli"?"cli:e2e":t,"bash",[b],n,i)),t==="cli"){const l=h.readdirSync(p.join(i,"test")).filter(m=>m.endsWith(".test.mjs")).sort();if(!l.length)throw new g("no CLI tests found");const y=p.join(I.tmpdir(),`qa-selftest-${process.pid}-${t}.tap`);s.push(await k(t,process.execPath,["--test",`--test-concurrency=${a}`,"--test-reporter=spec","--test-reporter-destination=stdout","--test-reporter=tap",`--test-reporter-destination=${y}`,...l.map(m=>p.join(i,"test",m))],n,i,y))}}const c=s.filter(t=>t.code!=null&&t.code!==0);n.say("",...s.map(t=>` ${t.skipped?"skip":t.code===0?"ok ":"FAIL"} ${t.suite}${t.skipped?" ("+t.skipped+")":" exit "+t.code+" "+t.seconds+"s"}`),"");const w=s.some(t=>t.skipped||![0,1].includes(t.code))?f.INVALID:c.length?f.FINDINGS:f.OK;return n.emit({ok:w===f.OK,results:s}),w}function v(e){let n;try{n=h.readFileSync(e,"utf8")}catch{return null}const i=n.match(/^# skipped (\d+)$/m);if(i)return Number(i[1]);const o=n.match(/# (SKIP|skip)\b/g);return o?o.length:0}async function k(e,n,i,o,u,r=null){o.say("",` --- ${e}`);const d=Date.now(),a=await S(n,i,{cwd:u,stdout:o.json?2:"inherit",stderr:"inherit",env:{PLAYWRIGHT_HOME:process.env.PLAYWRIGHT_HOME??p.join(u,".playwright")}}),s={suite:e,code:a.code,seconds:Math.round((Date.now()-d)/1e3)};if(r){const c=v(r);try{h.rmSync(r,{force:!0})}catch{}c===null?s.skipped="test report was not written, so the skip count is unknown":c>0&&(s.skipped=`${c} test(s) skipped -- not measured`)}return s}export{D as run};
@@ -0,0 +1 @@
1
+ import{resolveRepo as m,EXIT as o,InvalidError as $,UsageError as i}from"../../cli.mjs";import{exists as v}from"../../fs.mjs";import{startServer as g,stopServer as w,statusServer as h}from"../../serve.mjs";const P=new Set(["prod","port","build-cmd","start-cmd","timeout","stop","status","help"]);function u(s,e,t){const n=s.flags[e];if(n===void 0)return t;if(n===!0)throw new i(`--${e} needs a value`);const a=Number(n);if(!Number.isFinite(a))throw new i(`--${e} must be a number, got "${n}"`);return a}function d(s,e,t){const n=s.flags[e];if(n===void 0)return t;if(n===!0)throw new i(`--${e} needs a value`);return n}async function x({parsed:s,out:e}){const t=m(s);if(!v(t))throw new $(`no such directory: ${t}`);if(s.flags.stop&&s.flags.status)throw new i("pass only one of --stop / --status");return s.flags.stop?b(t,e):s.flags.status?S(t,e):y(t,s,e)}async function y(s,e,t){const n=!!e.flags.prod,a=u(e,"port",3e3),p=d(e,"build-cmd",null),c=d(e,"start-cmd",null),f=u(e,"timeout",180);t.say("",` qa run serve ${s} (${n?"prod":"dev"}, port ${a})`);const r=await g(s,{prod:n,port:a,buildCmd:p,startCmd:c,timeoutMs:f*1e3});if(!r.ok)return t.warn(` could not start: ${r.reason}`),r.logPath&&t.warn(` log: ${r.logPath}`),t.emit({ok:!1,reason:r.reason,logPath:r.logPath??null}),r.exit??o.INVALID;const l=`qa verify ${s}${r.mode==="prod"?" --prod":""} --base ${r.base}`;return t.say("",` serving ${r.base} pid ${r.pid} (${r.mode}, ${r.pm}, ${Math.round(r.elapsedMs)}ms to live)`,` log ${r.logPath}`,"",` next: ${l}`),t.emit({ok:!0,base:r.base,pid:r.pid,port:r.port,mode:r.mode,pm:r.pm,startedAt:r.startedAt,logPath:r.logPath,next:l}),o.OK}async function b(s,e){const t=await w(s);return e.say("",t.stopped?` stopped pid ${t.pid} (was ${t.base})`:` nothing to stop: ${t.reason}`),e.emit({ok:!0,...t}),o.OK}async function S(s,e){const t=await h(s);if(!t.started)return e.say("",` not running (${t.reason})`),e.emit({ok:!0,started:!1,live:!1,reason:t.reason}),o.FINDINGS;const n=!!t.reachable;return e.say("",` ${n?"live":"down"} ${t.state.base} pid ${t.state.pid} (${t.pidAlive?"alive":"dead"}) type=${t.serverType??"unknown"}`,...t.evidence?.length?[` evidence: ${t.evidence.join("; ")}`]:[]),e.emit({ok:!0,started:!0,live:n,pidAlive:t.pidAlive,serverType:t.serverType??"unknown",status:t.status??null,state:t.state}),n?o.OK:o.FINDINGS}export{x as run};
@@ -0,0 +1 @@
1
+ import s from"node:path";import{UsageError as f,InvalidError as p,EXIT as r}from"../../cli.mjs";import{run as j}from"../../proc.mjs";import{exists as I,writeJson as N}from"../../fs.mjs";import{FORBIDDEN as A}from"../../../../bin/shadow-replay.mjs";function L(e,t){if(t)return s.isAbsolute(t)?t:s.join(e,t);const c=s.join(e,"verify.shadow.json");return I(c)?c:s.join(e,".verify","shadow.json")}async function V({parsed:e,out:t,skillRoot:c}){const u=e.positional[0];if(u!=="replay")throw new f(`qa measure shadow needs a subcommand; only "replay" is implemented (got ${u?`"${u}"`:"none"})`);const g=e.positional[1];if(!g)throw new f("missing <repo> -- qa measure shadow replay <repo> --trace FILE --base URL --isolated");const o=s.resolve(process.cwd(),g);if(!I(o))throw new p(`no such directory: ${o}`);const w=e.flags.base;if(!w)throw new f("--base URL is required (the isolated target to replay against)");if(!e.flags.isolated)throw new p("qa measure shadow replay refuses without --isolated -- replay only ever runs against an explicitly isolated target");const d=e.flags.trace;if(!d)throw new f("--trace FILE is required (a collected trace from bin/shadow-collector.mjs)");const y=s.isAbsolute(d)?d:s.join(o,d);if(!I(y))throw new p(`no such trace file: ${y}`);const b=L(o,e.flags.policy),$=s.join(c,"bin","shadow-replay.mjs"),m=[w,"--isolated","--trace",y,"--policy",b,"--repo",o];if(e.flags.ids&&m.push("--ids",e.flags.ids),e.flags.auth){const h=s.isAbsolute(e.flags.auth)?e.flags.auth:s.join(o,e.flags.auth);m.push("--auth",h)}t.say("",` qa measure shadow replay ${o} --base ${w}`);const i=await j(process.execPath,[$,...m],{stdout:"capture",stderr:"capture"});i.stderr&&i.stderr.trim()&&t.warn(i.stderr.trim());const n=s.join(o,".verify","autonomous","shadow-replay.json");let a;try{a=JSON.parse(i.stdout)}catch{const h={status:"invalid",replayed:[],skipped:[],drift:[],privacy:{raw_values:!1},reason:"shadow-replay adapter produced no parseable report"};return N(n,h),t.say(""," INVALID exit 2",` -> ${n}`),t.emit({ok:!1,exit:r.INVALID,dir:n,...h}),r.INVALID}if(A.test(JSON.stringify(a)))throw new p("shadow replay report failed the privacy check -- refusing to write it");N(n,a);const l=[r.OK,r.FINDINGS,r.INVALID].includes(i.code)?i.code:r.INVALID,v=l===r.INVALID?"INVALID":l===r.FINDINGS?"GATING":"CLEAN";return t.say("",` status ${a.status} replayed ${a.replayed?.length??0} skipped ${a.skipped?.length??0} drift ${a.drift?.length??0}`,"",` ${v} exit ${l}`,` -> ${n}`),t.emit({ok:l===r.OK,exit:l,dir:n,...a}),l}export{V as run};
@@ -0,0 +1 @@
1
+ import"node:fs";import a from"node:path";import{resolveRepo as E,forwardFlags as q,UsageError as v,InvalidError as P,EXIT as m}from"../../cli.mjs";import{run as N}from"../../proc.mjs";import{acquireLock as k,exists as G,readJson as R,writeJson as L}from"../../fs.mjs";import{shapes as U}from"../../reports.mjs";import{newRunId as C}from"../../runs.mjs";import{classify as F,formatStability as J}from"../../stability.mjs";import{requireLane as K}from"../../lanes.mjs";import{readBorrowedLease as T}from"../../auth/lease.mjs";const V=new Set(["base","runs","warmup","parallel","lane","help","auth-lease-fd"]);async function st({parsed:t,out:i,skillRoot:x}){const r=E(t);if(!G(r))throw new P(`no such directory: ${r}`);const f=t.flags.base;if(!f)throw new v("--base URL is required (the running app to sweep, N times, serially)");const e=Number(t.flags.runs??3);if(!Number.isInteger(e)||e<1)throw new v("--runs must be a positive integer");const g=t.flags.lane?K(t.flags.lane,{command:"stability"}):"public-runtime",O=q(t,V),p=T(t.flags["auth-lease-fd"]),y=()=>({stdout:t.json?"capture":"inherit",stderr:t.json?"capture":"inherit",...p?{fd3:p}:{}}),j=a.join(x,"bin","sweep.mjs"),w=C(),l=a.join(r,".verify","stability",w),b=s=>["--repo",r,"--base",f,"--lane",g,"--json",s,"--parallel","1",...O,...p?["--auth-lease-fd","3"]:[]];i.say("",` qa verify ${r} --base ${f} --runs ${e}`+(t.flags.warmup?" (+warmup)":""));const S=k(r,"stability"),$=[];try{if(t.flags.warmup){i.say(" warmup (uncounted)...");const s=a.join(l,"warmup.json"),n=await N(process.execPath,[j,...b(s)],y());t.json&&(n.stdout&&process.stderr.write(n.stdout),n.stderr&&process.stderr.write(n.stderr))}for(let s=1;s<=e;s++){i.say(` run ${s}/${e} ...`);const n=a.join(l,`run-${s}.json`),u=await N(process.execPath,[j,...b(n)],y());t.json&&(u.stdout&&process.stderr.write(u.stdout),u.stderr&&process.stderr.write(u.stderr));let h=null;try{h=R(n,{shape:U.sweep,what:`run ${s} sweep.json`})}catch(D){i.warn(` run ${s}: ${D.message}`),h=null}$.push({exit:u.code,report:h})}}finally{S()}const o=F($,{n:e}),d=o.valid?"ok":"invalid",c=o.valid?o.gating?m.FINDINGS:m.OK:m.INVALID,I={id:w,at:new Date().toISOString(),repo:r,base:f,lane:g,n:e,status:d,exit:c,...o};L(a.join(l,"result.json"),I),L(a.join(r,".verify","stability.json"),I);const A=d==="invalid"?"INVALID":o.gating?"GATING":"CLEAN";return i.say("",J(o),"",` ${A} exit ${c}`,` -> ${l}`),i.emit({ok:c===m.OK,exit:c,status:d,id:w,dir:l,...o}),c}export{st as run};
@@ -0,0 +1 @@
1
+ import{resolveRepo as d}from"../../cli.mjs";import{loadRun as u,runRecords as f}from"../../runs.mjs";import{triage as p,formatTriage as h,loadRoutePriorities as k,loadAgentResult as y,coherenceObligationsLine as v,coherenceRiskLine as R}from"../../triage.mjs";import{readJson as x}from"../../fs.mjs";import{riskArtifactPath as $}from"../../coherence/risk.mjs";async function A({parsed:o,out:s}){const r=d(o),i=u(r,o.flags.run),m=(i.meta.args??[]).includes("--prod"),t=p({records:f(i),inventory:i.inventory,sweep:i.sweep,classify:i.classify,prod:m,priorities:k(r)}),a=y(r),l=v(a);let n=null;try{n=x($(r),{what:"coherence-risk.json"})}catch{n=null}const c=R(n);return s.say("",` qa show run ${i.id} (${i.meta.mode}, exit ${i.meta.exit})`,"",h(t,{limit:Number(o.flags.limit??30)||30}),...l?["",` ${l}`]:[],...c?[` ${c}`]:[],""),s.emit({ok:!0,run:i.id,mode:i.meta.mode,exit:i.meta.exit,counts:t.counts,denominators:t.denominators,tells:t.tells,gating:t.gating,coherenceObligations:a?.dimensions?.coherenceObligations??null,coherenceRisk:n??null,groups:t.groups.map(e=>({key:e.key,phase:e.phase,kind:e.kind,severity:e.severity,count:e.count,routes:e.routes,files:e.files,at:e.at,determinism:e.determinism,sharedSurface:e.sharedSurface,leverage:e.leverage,fids:e.fids,sample:{detail:e.sample.detail,file:e.sample.file,line:e.sample.line,route:e.sample.route}})),waived:t.waived.map(e=>({fid:e.fid,kind:e.kind,file:e.file,line:e.line}))}),0}export{A as run};
@@ -0,0 +1,4 @@
1
+ import w from"node:fs";import c from"node:path";import{resolveRepo as A,EXIT as b,InvalidError as C,table as N}from"../../cli.mjs";import{exists as x}from"../../fs.mjs";import{listRunIds as T,resolveRunId as E,runsDir as F}from"../../runs.mjs";import{validate as y}from"../../schema.mjs";import{validateCoherenceIrIdentities as D,validateCoherenceObservedIdentities as L,validateCoherencePlanIdentities as J,validateTypedCrudDeclaration as K}from"../../coherence/identity.mjs";import{validateMutationDeclaration as P}from"../../mutation-policy.mjs";import{analysisViolations as _}from"../../analysis.mjs";const q=[["inventory","inventory.json","inventory.schema.json"],["classify","classify.json","classify.schema.json"],["sweep","sweep.json","sweep.schema.json"],["research","research.json","research.schema.json"],["analysis","analysis.json","analysis.schema.json"],["stability","stability.json","stability.schema.json"],["next","next.json","next.schema.json"],["crud-observed","crud-observed.json","crud-observed.schema.json"],["coherence-observed","coherence-observed.json","coherence-observed.schema.json"],["coherence-risk","coherence-risk.json","coherence-risk.schema.json"],["shadow-replay",c.join("autonomous","shadow-replay.json"),"shadow-replay.schema.json"],["experiment-execution",c.join("autonomous","experiment-execution.json"),"experiment-execution.schema.json"],["lanes-plan",c.join("lanes","plan.json"),"lanes-plan.schema.json"],["verify-crud","verify.crud.json","verify-crud.schema.json","repo"],["personas","verify.roles.json","personas.schema.json","repo"],["verify-read-only","verify.read-only.json","verify-read-only.schema.json","repo"]],G=[["inventory","inventory.json","inventory.schema.json"],["classify","classify.json","classify.schema.json"],["sweep","sweep.json","sweep.schema.json"],["run","run.json","run.schema.json"],["agent-result","agent-result.json","agent-result.schema.json"]];function p(r,s){const i=c.join(r,s),t=w.readFileSync(i,"utf8");return JSON.parse(t)}function H(r,s,i,t){try{if(r==="analysis")return _(t,s).map(n=>({path:"(references)",message:n}));if(r==="inventory"&&s?.coherence?.schemaVersion===4){const n=y(p(i,"coherence-ir.schema.json"),s.coherence);if(n.length)return n.map(o=>({...o,path:`coherence.${o.path}`}));const a=y(p(i,"graph.schema.json"),s.coherence.graph);if(a.length)return a.map(o=>({...o,path:`coherence.graph.${o.path}`}));D(s.coherence)}else if(r==="inventory"&&s?.coherence?.schemaVersion===3){const n=y(p(i,"coherence-ir.schema.json"),s.coherence);if(n.length)return n.map(a=>({...a,path:`coherence.${a.path}`}))}else if(r==="verify-crud"){const n=P(s);if(!n.ok)return[{path:"entities",message:n.reason}];K(s)}else if(r==="coherence-observed")L(s);else if(r==="agent-result"){const n=s?.dimensions?.coherenceObligations;if(n?.status==="measured"){const a=y(p(i,"coherence-plan.schema.json"),n.value);if(a.length)return a.map(o=>({...o,path:`dimensions.coherenceObligations.value.${o.path}`}));J(n.value)}}}catch(n){return[{path:"(identity)",message:String(n?.message??n)}]}return[]}function M(r,s){const i=s?.coherence?.schemaVersion;return r==="inventory"&&[1,2,3].includes(i)?`coherence IR v${i}: legacy, readable, never current proof`:r==="coherence-observed"&&[1,2].includes(s?.schemaVersion)?`coherence observed v${s.schemaVersion}: legacy, readable, never current proof`:null}function S(r,s,i,t,n){if(!x(r))return{status:"absent",violations:[]};let a;try{a=JSON.parse(w.readFileSync(r,"utf8"))}catch(l){return{status:"error",violations:[{path:"(file)",message:`unreadable or not valid JSON: ${String(l.message??l).split(`
2
+ `)[0]}`}]}}const o=[...y(s,a),...H(i,a,t,n)],f=M(i,a);return{status:o.length?"violations":f?"legacy":"ok",violations:o,note:f}}async function se({parsed:r,out:s,skillRoot:i}){const t=A(r);if(!x(t))throw new C(`no such directory: ${t}`);const n=c.join(i,"schemas"),a=c.join(t,".verify"),o=[];for(const[e,u,j,g="verify"]of q){const v=c.join(g==="repo"?t:a,u);let m;try{const h=p(n,j);m=S(v,h,e,n,t)}catch(h){m={status:"error",violations:[{path:"(schema)",message:`could not load ${j}: ${String(h.message??h).split(`
3
+ `)[0]}`}]}}o.push({label:e,path:v,...m})}const f=typeof r.flags.run=="string"?r.flags.run:null;let l=null;if(f||T(t).length){l=E(t,f);const e=c.join(F(t),l);for(const[u,j,g]of G){const v=c.join(e,j);let m;try{const h=p(n,g);m=S(v,h,u,n,t)}catch(h){m={status:"error",violations:[{path:"(schema)",message:`could not load ${g}: ${String(h.message??h).split(`
4
+ `)[0]}`}]}}o.push({label:`run:${l}:${u}`,path:v,...m})}}const V={ok:"ok",absent:"absent",legacy:"LEGACY",violations:"VIOLATIONS",error:"ERROR"},O=o.map(e=>[c.relative(t,e.path),V[e.status],e.status==="violations"?`${e.violations.length} violation(s)`:e.status==="error"?e.violations[0]?.message??"error":e.note??""]);s.say("",` qa dev validate ${t}`+(l?` (archived run ${l})`:""),"",N([["artifact","status","detail"],...O]));for(const e of o)if(e.violations.length){s.say("",` ${c.relative(t,e.path)}:`);for(const u of e.violations.slice(0,30))s.say(` ${u.path} ${u.message}`);e.violations.length>30&&s.say(` ... and ${e.violations.length-30} more`)}const $=o.filter(e=>e.status==="violations"||e.status==="error"),I=o.filter(e=>e.status!=="absent"),R=o.filter(e=>e.status==="ok"),k=o.filter(e=>e.status==="legacy"),d=$.length?b.INVALID:b.OK;return s.say("",` ${$.length?"INVALID":"OK"} exit ${d} (${R.length}/${I.length} present artifacts current and valid, ${k.length} legacy non-proof, ${$.length} invalid, ${o.length-I.length} absent)`,""),s.emit({ok:d===b.OK,exit:d,repo:t,run:l,results:o.map(e=>({artifact:e.label,path:e.path,status:e.status,violations:e.violations,note:e.note??null}))}),d}export{se as run};
@@ -0,0 +1 @@
1
+ import m from"node:path";import{readJson as s}from"../../fs.mjs";import{git as a}from"../../proc.mjs";async function p({out:e,skillRoot:n}){const r=s(m.join(n,"package.json"))??{},t=a(n,["rev-parse","--short","HEAD"]),o={name:r.name??"automated-qa",version:r.version??"0.0.0",commit:t.code===0?t.stdout.trim():null,node:process.versions.node,root:n};return e.say(`${o.name} ${o.version}${o.commit?" ("+o.commit+")":""} node ${o.node} ${o.root}`),e.emit(o),0}export{p as run};
@@ -0,0 +1 @@
1
+ import R from"node:path";import{resolveRepo as E,UsageError as T,InvalidError as x,EXIT as m}from"../../cli.mjs";import{exists as S}from"../../fs.mjs";import{run as G}from"../../proc.mjs";import{listRunIds as y,loadRun as w,diffRuns as N}from"../../runs.mjs";import{watchRepo as j,createScheduler as O,DEFAULT_IGNORE as b,formatDiff as q}from"../../watch.mjs";async function U({parsed:f,out:t,skillRoot:I}){const e=E(f);if(!S(e))throw new x(`no such directory: ${e}`);const s=Number(f.flags.debounce??400);if(!Number.isFinite(s)||s<0)throw new T("--debounce must be a non-negative number of milliseconds");const c=!!f.flags.once,$=R.join(I,"bin","qa.mjs");let l=0;async function v(){l++;const o=y(e),u=o.length?o[o.length-1]:null;t.say("",` --- change detected: running the gate (cycle ${l}) ---`);const n=await G(process.execPath,[$,"verify",e],{stdout:"capture",stderr:"capture"});n.stdout&&t.say(n.stdout.replace(/\n+$/,"")),n.stderr&&t.warn(n.stderr.replace(/\n+$/,""));let p=null,h=" (first archived run -- nothing to diff against yet)";try{const r=y(e),a=r.length?r[r.length-1]:null;a&&u&&a!==u?(p=N(w(e,u),w(e,a)),h=q(p)):a||(h=" (the gate did not archive a run -- see its output above)")}catch(r){t.warn(` diff skipped: ${r.message}`)}return t.say("",h,"",` gate exit ${n.code}`),t.emit({ok:n.code===m.OK,exit:n.code,cycle:l,repo:e,diff:p}),n.code}const d=O({debounceMs:s,once:c,run:v}),g=j(e,{ignore:b,onChange:()=>d.notify()});t.say("",` qa run watch ${e}`+(c?" --once":""),` ignoring: ${b.join(", ")}`,` watching for changes (debounce ${s}ms)...`+(c?"":" ctrl-C to stop"));const i=()=>{t.say(""," qa run watch: stopping..."),g.stop(),d.stop()};process.on("SIGINT",i),process.on("SIGTERM",i);try{const o=await d.done;return c?o:m.OK}finally{process.off("SIGINT",i),process.off("SIGTERM",i),g.stop()}}export{U as run};
@@ -0,0 +1,6 @@
1
+ import k from"node:fs";import a from"node:path";import{resolveRepo as w,InvalidError as u}from"../cli.mjs";import{exists as f,readJson as m,writeText as d,readText as y}from"../fs.mjs";import{git as g}from"../proc.mjs";import{STOP_HOOK_SNIPPET as v,verifyignoreContent as j,rolesContent as $,crudContent as x,crudFlows as b,principalScaffold as P}from"../init.mjs";async function D(r){const{parsed:t,out:e,skillRoot:n}=r;if(t.flags.study||t.flags.analysis){const{run:h}=await import("./impl/research.mjs");return h({...r,parsed:{...t,command:"research"}})}const i=w(t);if(!f(i))throw new u(`no such directory: ${i}`);const o=t.flags,c=o.roles||o.workflow||o.hook||o.ignore||o.all,p={roles:o.all||o.roles||!c,workflow:o.all||o.workflow||!c,hook:o.all||o.hook||!c,ignore:o.all||o.ignore||!c,crud:!!o.crud};e.say("",` qa init ${i}`);const l=[];return p.roles&&l.push(S(i,e)),p.crud&&l.push(_(i,e)),p.ignore&&(l.push(q(i,e)),l.push(R(i,e))),p.workflow&&l.push(T(i,n,e)),p.hook&&(E(e),l.push(I(n,e))),e.say(""),e.emit({ok:!0,repo:i,actions:l}),0}function s(r,t,e){r.say(` ${t.padEnd(14)} ${e}`)}function S(r,t){const e=a.join(r,"verify.roles.json");if(f(e))return s(t,"skipped-exists",e),{file:"verify.roles.json",action:"skipped-exists"};const n=m(a.join(r,".verify","research.json")),i=P(n);return d(e,$(n)),s(t,"created",e),(i.skipped||i.deduplicated)&&t.warn(` roles-source skipped ${i.skipped} unsafe principal name(s); deduplicated ${i.deduplicated} normalized collision(s); raw names were not written`),{file:"verify.roles.json",action:"created",principals:i.names.length||1,skippedPrincipals:i.skipped,deduplicatedPrincipals:i.deduplicated}}function _(r,t){const e=a.join(r,"verify.crud.json");if(f(e))return s(t,"skipped-exists",e),{file:"verify.crud.json",action:"skipped-exists"};const n=m(a.join(r,".verify","research.json")),i=b(n);if(!i.length)return s(t,"skipped-empty",`${e} (qa init --study found no mutation; run qa init <repo> --study first, or write the file by hand)`),{file:"verify.crud.json",action:"skipped-empty"};const o=m(a.join(r,"verify.roles.json"));return d(e,x(n,o)),s(t,"created",`${e} (${i.length} flow(s), every one production:false -- this file opts the repo into writes on a loopback base)`),{file:"verify.crud.json",action:"created",flows:i.length}}function q(r,t){const e=a.join(r,".verifyignore");return f(e)?(s(t,"skipped-exists",e),{file:".verifyignore",action:"skipped-exists"}):(d(e,j()),s(t,"created",e),{file:".verifyignore",action:"created"})}function C(r){return g(r,["rev-parse","--is-inside-work-tree"]).code!==0?!1:g(r,["check-ignore","-q",".verify/__qa-init-ignore-probe__"]).code===0}function R(r,t){const e=a.join(r,".gitignore"),n=".verify/";if(C(r))return s(t,"skipped-exists",`${e} (already ignores ${n})`),{file:".gitignore",action:"skipped-exists"};if(!f(e))return d(e,n+`
2
+ `),s(t,"created",e),{file:".gitignore",action:"created"};const i=y(e);if(i.split(`
3
+ `).some(p=>p.trim()===n||p.trim()===".verify"))return s(t,"skipped-exists",`${e} (already ignores ${n})`),{file:".gitignore",action:"skipped-exists"};const c=i.length&&!i.endsWith(`
4
+ `)?`
5
+ `:"";return k.appendFileSync(e,`${c}${n}
6
+ `),s(t,"appended",`${e} (+ ${n})`),{file:".gitignore",action:"appended"}}function T(r,t,e){const n=a.join(r,".github","workflows","frontend-verify.yml");if(f(n))return s(e,"skipped-exists",n),{file:".github/workflows/frontend-verify.yml",action:"skipped-exists"};const i=a.join(t,"templates","github-workflow.yml");let o;try{o=y(i)}catch(c){throw new u(`template missing: ${i} (${c.code??c.message})`)}return d(n,o),s(e,"created",n),{file:".github/workflows/frontend-verify.yml",action:"created"}}function E(r){r.say(""," Stop-hook snippet (paste into ~/.claude/settings.json, merged into your existing hooks):","",v,"")}function I(r,t){const e=a.join(r,"templates","pre-commit.sample");let n;try{n=y(e)}catch(i){throw new u(`template missing: ${e} (${i.code??i.message})`)}return t.say(" pre-commit sample (copy to .git/hooks/pre-commit and chmod +x \u2014 not written for you):","",n),{file:"templates/pre-commit.sample",action:"printed"}}export{D as run};
@@ -0,0 +1 @@
1
+ import{UsageError as t}from"../cli.mjs";const s=["coherence","crud","experiments","shadow","invariants"];async function a(n){return import(`./impl/${n}.mjs`)}async function d(n){const{parsed:i}=n,o=i.positional[0];if(!s.includes(o))throw new t(`qa measure needs a kind: one of ${s.join(", ")} (got ${o?`"${o}"`:"none"})`);const r=o,e=i.positional.slice(1);return(await a(r)).run({...n,parsed:{...i,command:r,positional:e}})}export{d as run};
@@ -0,0 +1 @@
1
+ import*as n from"./impl/marathon.mjs";import*as e from"./impl/loop.mjs";import*as m from"./impl/watch.mjs";import*as i from"./impl/serve.mjs";const s={marathon:n,loop:e,watch:m,serve:i},p=new Set(Object.keys(s));async function c(a){const{parsed:t}=a;let r="marathon",o=t.positional;return p.has(o[0])&&(r=o[0],o=o.slice(1)),s[r].run({...a,parsed:{...t,command:r,positional:o}})}export{c as run};
@@ -0,0 +1 @@
1
+ import*as n from"./impl/triage.mjs";import*as i from"./impl/next.mjs";import*as m from"./impl/ledger.mjs";import*as d from"./impl/graph.mjs";import*as p from"./impl/report.mjs";import*as c from"./impl/analysis.mjs";const s=new Set(["report","ci","diff","runs"]),a={triage:n,next:i,ledger:m,graph:d,analysis:c},f=new Set([...Object.keys(a),...s]);function l(o){return s.has(o)?p:a[o]}async function M(o){const{parsed:e}=o;let t="triage",r=e.positional;return f.has(r[0])&&(t=r[0],r=r.slice(1)),l(t).run({...o,parsed:{...e,command:t,positional:r}})}export{M as run};
@@ -0,0 +1,6 @@
1
+ import k from"node:fs";import oe from"node:os";import l from"node:path";import{resolveRepo as ie,forwardFlags as ue,rejectRemovedFlags as ce,EXIT as q,InvalidError as V}from"../cli.mjs";import{run as C,which as le}from"../proc.mjs";import{acquireLock as de,exists as F,readJson as fe,writeText as z}from"../fs.mjs";import{archiveRun as me,newRunId as H}from"../runs.mjs";import{openPending as U,snapshotInput as X,snapshotInputs as pe}from"../pending.mjs";import{selectBinary as he,verifyBinary as ye}from"../release-manifest.mjs";import{validateAgentResult as Y,exitFor as M,unmeasured as R,invalid as O,gateCompleted as ge,bindAgentIdentity as ve}from"../agent-result.mjs";import{canonicalize as Q}from"../jcs.mjs";import{triage as we,formatTriage as be,loadRoutePriorities as je}from"../triage.mjs";import{modeCoverage as Se}from"../mode-coverage.mjs";import{depthReport as ke,formatDepth as $e,artifactSubstance as xe,formatArtifacts as Ae}from"../depth.mjs";import{requireLane as Ie}from"../lanes.mjs";import{kernelPhaseAvailability as Pe}from"../agent-availability.mjs";import{tryReuseGraph as Fe}from"../autonomous-cache.mjs";import{loadPersonas as Ne,validatePersonas as De}from"../auth/personas.mjs";import{readBorrowedLease as Oe}from"../auth/lease.mjs";import{loadMutationDeclaration as Le}from"../mutation-policy.mjs";import{checkInputDeclarations as qe}from"../doctor.mjs";function Re(e){const t=l.join(e,".verify","autonomous"),r=[],a=(o,i)=>{try{r.push({name:i,text:k.readFileSync(o,"utf8")})}catch{}};let n;try{n=k.readdirSync(t,{withFileTypes:!0})}catch{return r}for(const o of n){const i=l.join(t,o.name);if(o.isDirectory()){let f=[];try{f=k.readdirSync(i)}catch{continue}for(const u of f)a(l.join(i,u),`${o.name}/${u}`)}else a(i,o.name)}return r}const Ee=new Set(["no-archive","keep","help","autonomous","no-autonomous","link-adapters","preflight","runs","auth-lease-fd"]),Z=new Set(["crud","shadow","experiments","coherence"]);function Te(e){const t=e.flags["link-adapters"];if(!t||t===!0)return[];const r=String(t).split(",").map(n=>n.trim()).filter(Boolean),a=r.filter(n=>!Z.has(n));if(a.length)throw new V(`--link-adapters: unknown adapter(s) ${a.join(", ")} (known: ${[...Z].join(", ")})`);return r}function ee(e){const t=e.find(n=>n.name==="verify.roles");if(!t)return;let r;try{r=JSON.parse(k.readFileSync(t.path,"utf8"))}catch{throw new V("snapshotted verify.roles.json is not valid JSON")}const a=De(r);if(!a.ok)throw new V(`snapshotted verify.roles.json has invalid persona declarations:
2
+ ${a.problems.join(`
3
+ `)}`)}function te(e,t,r){const a=pe(e,r),n=Ne(t);n.file&&a.push(X(e,"verify.roles",JSON.stringify(n.declaration)+`
4
+ `));const o=Le(t);if(F(o.path)){if(!o.ok)throw new V(o.reason);a.push(X(e,"verify.crud",o.raw))}return a.sort((i,f)=>i.name.localeCompare(f.name))}async function ft(e){const{parsed:t,out:r,skillRoot:a}=e;if(t.flags.preflight){const{run:S}=await import("./impl/doctor.mjs");return S({...e,parsed:{...t,command:"doctor"}})}if(t.flags.runs!==void 0){const{run:S}=await import("./impl/stability.mjs");return S({...e,parsed:{...t,command:"stability"}})}const n=ie(t);if(!F(n))throw new V(`no such directory: ${n}`);const o=qe(n,a).find(S=>S.level==="fail");if(o)throw new V(`preflight ${o.id}: ${o.detail}${o.fix?` -- ${o.fix}`:""}`);ce(t,{command:"verify"});const i=ue(t,Ee),f=Oe(t.flags["auth-lease-fd"]);f&&i.push("--auth-lease-fd","3");const u=t.flags.base??null,A=!!t.flags.prod,g=u?"runtime":"static",v=t.flags.lane?Ie(t.flags.lane,{command:"verify"}):g==="runtime"?"public-runtime":"source",w=!!t.flags.autonomous,b=!!t.flags["no-autonomous"];let m=null;try{m=Se({skillRoot:a,mode:g})}catch{m=null}const c=l.join(n,".verify","sweep.json"),N=_(c),B=u?de(n,"verify"):()=>{};let $;try{r.say("",` qa verify ${n}`+(u?` --base ${u}`:" (static)")),$=await C("bash",[l.join(a,"verify.sh"),n,...i],{stdout:t.json?"capture":"inherit",stderr:t.json?"capture":"inherit",fd3:f})}finally{B()}t.json&&($.stdout&&process.stderr.write($.stdout),$.stderr&&process.stderr.write($.stderr));const p=$.code;let d=null,j=null,y=R("not-requested"),s=null,h=p;if(t.flags["no-archive"]){const S=H(),I=U(n,S);try{const D=g==="runtime"&&F(c)&&_(c)!==N,T=l.join(n,".verify","inventory.json"),G=l.join(n,".verify","classify.json"),J=te(I.dir,n,{inventory:F(T)?T:null,classify:F(G)?G:null,...D?{sweep:c}:{}});ee(J),y=p===q.INVALID?null:await re({skillRoot:a,repo:n,runId:S,mode:g,lane:v,inputs:J,out:r})}catch(D){r.warn(` agent skipped: ${D.message}`),y=O("load-failed")}finally{I.discard()}h=M({agentStatus:y?.status??"unmeasured",agentRequired:w,gateExit:p})}else{const S=H(),I=U(n,S);let D=!1;try{const T=g==="runtime"&&F(c)&&_(c)!==N,G=l.join(n,".verify","inventory.json"),J=l.join(n,".verify","classify.json"),P=te(I.dir,n,{inventory:F(G)?G:null,classify:F(J)?J:null,...T?{sweep:c}:{}});ee(P);const K=await Ve({parsed:t,skillRoot:a,repo:n,base:u,gateExit:p,inputs:P,out:r});y=p===q.INVALID?null:await re({skillRoot:a,repo:n,runId:S,mode:g,lane:v,inputs:P,out:r,emitGraph:K?.needsGraph===!0,graphBase:K?.base??null}),y!==null&&Y(y).ok&&z(l.join(I.dir,"agent-result.json"),Q(y)),s=await Be({plan:K,skillRoot:a,repo:n,inputs:P,agent:y,out:r});const se={inventory:P.some(x=>x.name==="inventory"),classify:P.some(x=>x.name==="classify"),sweep:P.some(x=>x.name==="sweep")};d=me(n,{id:S,dir:I.dir,mode:g,args:i,exit:p,produced:se,keep:Number(t.flags.keep??50)||50,extra:{lane:v,runId:S,agent:y,autonomous:s},link:Te(t),graphDir:s?.status==="measured"?s.dir:null});const ae=d.records;if(j=we({records:ae,inventory:d.reports.inventory,sweep:d.reports.sweep,classify:d.reports.classify,prod:A,priorities:je(n)}),!ge(p)&&P.length===0)I.discard(),d=null,j=null;else if(d.dir=I.publish(),D=!0,s?.status==="measured"&&P.some(x=>x.path.includes(`${l.sep}.pending-`))){const x=await ne({skillRoot:a,repo:n});x.ok||(s={status:"invalid",reason:`autonomous manifest invalid after publication (exit ${x.code})`},d.meta.autonomous=s,z(l.join(d.dir,"run.json"),Q(d.meta)))}h=M({agentStatus:y?.status??"unmeasured",agentRequired:w,gateExit:p}),s?.status==="invalid"&&(h=q.INVALID),r.say("",` ${D?"archived":"archive discarded (gate produced nothing)"} ${D?d.dir:""}`,"",j?be(j,{limit:12}):"")}catch(T){D||I.discard(),r.warn(` archive skipped: ${T.message}`),d=null,j=null,y=O("load-failed"),h=M({agentStatus:y.status,agentRequired:w,gateExit:p})}}m&&r.say("",` detectors ${m.line}`);const L=ke({mode:g,flags:{...t.flags,autonomous:b?!1:s?.status==="measured"}});r.say("",$e(L));const E=xe(Re(n));E.total&&r.say("",Ae(E));const W=h===q.OK?"PASS":h===q.FINDINGS?"FAIL":"INVALID";return r.say("",` ${W} exit ${h}`),r.emit({ok:h===q.OK,exit:h,verdict:W,mode:g,lane:v,repo:n,run:d?.id??null,dir:d?.dir??null,gate:{exit:p},agent:y,autonomous:s,counts:j?.counts??null,denominators:j?.denominators??null,tells:j?.tells??null,top:j?j.groups.slice(0,10).map(Me):null}),h}async function Ve({parsed:e,skillRoot:t,repo:r,base:a,gateExit:n,inputs:o=[],out:i}){if(e.flags["no-autonomous"])return{status:"unmeasured",reason:"--no-autonomous"};if(n===q.INVALID)return null;const f=!!e.flags.autonomous,u=(m,c)=>(i.warn(` autonomous phase ${f?"FAILED":"skipped"}: ${c}`),f||i.warn(" (the gate itself is unaffected; pass --autonomous to make this a hard failure)"),{status:f?"invalid":"unmeasured",reason:m}),A=Pe(t);if(A.status!=="measured")return u(A.reason,`${A.reason}, so the Rust kernel could not build`);const g=o.find(m=>m.name==="verify.roles")?.path??null,v=o.find(m=>m.name==="verify.crud")?.path??null,w=a?["--base",a]:[];for(const m of["inventory","classify","sweep"]){const c=o.find(N=>N.name===m)?.path;c&&w.push("--"+m,c)}w.push(...g?["--roles",g]:["--no-roles"]),w.push(...v?["--verify-crud",v]:["--no-crud"]);const b=await Fe({skillRoot:t,repo:r,agentFlags:w});return b.hit?i.say("",` autonomous phase: reusing graph reduction for identical inputs (content fingerprint ${b.contentFingerprint.slice(0,12)})`):i.say("",` autonomous phase: inputs changed (${b.reason}), reducing`),{status:"pending",required:f,cache:b,needsGraph:!b.hit,base:a}}async function Be({plan:e,skillRoot:t,repo:r,inputs:a=[],agent:n,out:o}){if(!e||e.status!=="pending")return e;const i=(u,A)=>(o.warn(" autonomous phase "+(e.required?"FAILED":"skipped")+": "+A),e.required||o.warn(" (the gate itself is unaffected; pass --autonomous to make this a hard failure)"),{status:e.required?"invalid":"unmeasured",reason:u});if(e.needsGraph&&n?.status!=="measured"){const u=n?.reason??"no-binary";return i(u,"the combined Rust kernel did not produce graph artifacts ("+u+")")}if(!a.some(u=>u.path.includes(`${l.sep}.pending-`))){const u=await ne({skillRoot:t,repo:r});if(!u.ok)return i("autonomous manifest invalid (exit "+u.code+")","the artifact manifest did not validate (exit "+u.code+")")}const f=l.join(r,".verify","autonomous");return o.say(" autonomous artifacts -> "+f+" (manifest valid)"),{status:"measured",dir:f,cache:e.cache.hit?{hit:!0,contentFingerprint:e.cache.contentFingerprint,inputFingerprint:e.cache.inputFingerprint,manifestPath:e.cache.manifestPath}:{hit:!1,reason:e.cache.reason}}}async function ne({skillRoot:e,repo:t}){const r=await C(process.execPath,[l.join(e,"bin","verify-artifacts.mjs"),l.join(t,".verify","autonomous","run-manifest.json")],{stdout:"capture",stderr:"capture"});return{ok:r.code===0,code:r.code}}function Ge(e){const t=String(e?.stderr??"").trim().split(`
5
+ `).slice(-3).join(" | ")||"<empty>";return`exit=${e?.code??2} signal=${e?.signal??"none"} timedOut=${e?.timedOut===!0} spawnError=${e?.error?.code??"none"} stdoutBytes=${Buffer.byteLength(String(e?.stdout??""))} stderr=${t}`}function Je(e){if(e?.error)return{status:"unmeasured",reason:"no-binary"};try{return{status:"ok",parsed:JSON.parse(e?.stdout)}}catch{return{status:"invalid",reason:"malformed-output"}}}function Ce(e,t=process.platform){const r=t==="win32"?"qa-agent.exe":"qa-agent";for(const a of String(e??"").split(/\r?\n/))try{const n=JSON.parse(a);if(n.reason==="compiler-artifact"&&n.target?.name==="qa-agent"&&typeof n.executable=="string"&&l.basename(n.executable)===r)return n.executable}catch{}return null}function Ke(e){const t=k.mkdtempSync(l.join(oe.tmpdir(),"qa-agent-"));try{const r=l.join(t,l.basename(e));return k.copyFileSync(e,r),k.chmodSync(r,448),{path:r,cleanup:()=>k.rmSync(t,{recursive:!0,force:!0})}}catch(r){throw k.rmSync(t,{recursive:!0,force:!0}),r}}async function re({skillRoot:e,repo:t,runId:r,mode:a,lane:n,inputs:o,out:i,emitGraph:f=!1,graphBase:u=null}){const A=l.join(e,"agent","release-manifest.json");let g;try{g=fe(A,{required:!0,what:"release manifest"})}catch{return R("no-binary")}const v=he({skillRoot:e,platform:process.platform,arch:process.arch,manifest:g});let w,b,m;if(v.status==="ok"){const s=ye(v.path,v.sha256);if(!s.ok)return O("digest-mismatch");w=v.path,b=s.actual,m=v.protocolVersion}else{if(!le("cargo"))return R(v.reason);const h=l.join(e,"agent","Cargo.toml"),L=await C("cargo",["build","--message-format=json","--manifest-path",h],{stdout:"capture",stderr:"capture"});if(L.code!==0)return i.warn(` agent: cargo build failed (unmeasured): ${String(L.stderr).split(`
6
+ `).slice(-3).join(" | ")}`),R("no-binary");const E=Ce(L.stdout);if(!E||!F(E))return R("no-binary");w=E,m=1}const c=["--repo",t,"--run-id",r,"--lane",n,"--mode",a,"--emit-result"];f&&(u&&c.push("--base",u),c.push("--emit-graph","--out-dir",l.join(t,".verify","autonomous")));for(const s of o)s.name==="inventory"&&c.push("--inventory",s.path),s.name==="classify"&&c.push("--classify",s.path),s.name==="sweep"&&c.push("--sweep",s.path),s.name==="verify.roles"&&c.push("--roles",s.path),s.name==="verify.crud"&&c.push("--verify-crud",s.path);o.some(s=>s.name==="verify.roles")||c.push("--no-roles"),o.some(s=>s.name==="verify.crud")||c.push("--no-crud");let N=w,B=null;if(v.status!=="ok")try{const s=Ke(w);N=s.path,B=s.cleanup;const{createHash:h}=await import("node:crypto");b=h("sha256").update(k.readFileSync(N)).digest("hex")}catch{return B?.(),R("no-binary")}let $;try{$=await C(N,c,{stdout:"capture",stderr:"capture",env:{FRONTEND_VERIFY_SKILL_ROOT:e}})}finally{B?.()}const p=Je($);if(p.status!=="ok")return i.warn(` agent: ${p.status==="unmeasured"?"could not launch (unmeasured)":"unparseable output (invalid)"}: ${Ge($)}`),p.status==="unmeasured"?R(p.reason):O(p.reason);const d=p.parsed;for(const s of o){const h=d.inputs?.find?.(L=>L.name===s.name);if(!h||h.sha256!==s.sha256)return O("digest-mismatch")}if(b&&d.binary&&d.binary.sha256!==b)return O("digest-mismatch");d.binary={sha256:b,protocolVersion:m};const j=ve({runId:r,mode:a,lane:n},d);if(j.length)return i.warn(` agent: identity-mismatch: ${j.join("; ")}`),O("schema-invalid");const y=Y(d);return y.ok?d:(i.warn(` agent: schema-invalid: ${y.problems.slice(0,3).join("; ")}`),O("schema-invalid"))}function _(e){try{return k.statSync(e).mtimeMs}catch{return null}}function Me(e){return{key:e.key,phase:e.phase,kind:e.kind,severity:e.severity,count:e.count,routes:e.routes,files:e.files,at:e.at,determinism:e.determinism,fids:e.fids}}export{Ge as agentProcessDiagnostic,Ce as cargoAgentExecutable,Je as classifyAgentSpawn,ft as run,Ke as stageAgentExecutable,ee as validateRolesSnapshot};
@@ -0,0 +1 @@
1
+ const l=Object.freeze([Object.freeze({index:0,name:"baseline",flags:Object.freeze([]),cells:1,minutes:0}),Object.freeze({index:1,name:"forced states",flags:Object.freeze(["--states"]),cells:1,minutes:15}),Object.freeze({index:2,name:"forced states + dark mode",flags:Object.freeze(["--states","--dark"]),cells:2,minutes:30}),Object.freeze({index:3,name:"forced states + dark + control exercising",flags:Object.freeze(["--states","--dark","--exercise"]),cells:2,minutes:60}),Object.freeze({index:4,name:"the full cell matrix",flags:Object.freeze(["--states","--dark","--exercise","--widths","390,768,1440"]),cells:3,minutes:120})]);function i({states:s=!1,dark:r=!1,exercise:t=!1}={}){return s?r?t?3:2:1:0}function u({current:s,remainingMs:r,widthsGiven:t}){const n=s+1;if(n>=l.length||t&&n===l.length-1)return null;const e=l[n];if(r<e.minutes*6e4)return null;const f=t?e.flags.filter((a,c)=>a!=="--widths"&&e.flags[c-1]!=="--widths"):[...e.flags];return{...e,flags:f}}export{l as DEPTH_LADDER,i as initialDepth,u as nextDepth};
@@ -0,0 +1 @@
1
+ import{cellsOf as d}from"./reports.mjs";function u(t,r,s,o){const n=Math.max(0,Math.min(r,s));return{unit:t,status:"measured",affected:n,measured:s,source:o}}function i(t,r,s){return{unit:t,status:"unmeasured",reason:r,source:s}}function p({phase:t,group:r,classify:s,inventory:o,sweep:n}){const m=r?.files?.length??0,a=r?.routes?.length??0,f=r?.count??0;if(t==="static")return[s?u("files",m,s.files??s.counts?.files??0,"classify"):i("files","no-classify-report","classify")];if(t==="sync"){const e=[];return e.push(o?u("mutations",m,o.counts?.mutations??0,"inventory"):i("mutations","no-inventory-report","inventory")),o&&e.push(u("routes",a,o.counts?.routes??o.routes?.length??0,"inventory")),e}if(["runtime","state","dark","leak"].includes(t)){const e=[];return e.push(n?u("cells",f,d(n).length,"sweep"):i("cells","no-sweep-report","sweep")),n&&e.push(u("routes",a,n.summary?.routesReached??0,"sweep")),e}if(["journey","mutate"].includes(t)){const e=[];return e.push(n?u("attempts",f,d(n).length,"sweep"):i("attempts","no-sweep-report","sweep")),n&&e.push(u("routes",a,n.summary?.routesReached??0,"sweep")),e}const c=r?.sample?.denominator;return c&&typeof c=="object"&&Number.isInteger(c.measured)?[u(c.unit??"occurrences",c.affected??f,c.measured,c.source??String(t??"adapter"))]:[i("occurrences","no-producer-denominator",String(t??"unknown"))]}export{p as denominatorsFor};
@@ -0,0 +1,4 @@
1
+ const c=Object.freeze([Object.freeze({id:"runtime",flag:"--base URL",weight:4,buys:"runtime invariants (53 of 74 detector classes need a running app)",on:e=>!!e.base}),Object.freeze({id:"graph",flag:"--autonomous",weight:2,buys:"the Rust kernel graph (nodes.jsonl/edges.jsonl/run-manifest.json)",on:e=>e.autonomous!==!1}),Object.freeze({id:"widths",flag:"--widths 390,1440",weight:2,buys:"the responsive matrix (L8 defects exist only at a width you did not test)",on:e=>typeof e.widths=="string"&&e.widths.includes(",")}),Object.freeze({id:"states",flag:"--states",weight:2,buys:"the empty / error / forbidden / malformed / slow branches",on:e=>!!e.states}),Object.freeze({id:"dark",flag:"--dark",weight:1,buys:"the dark-mode palette pass",on:e=>!!e.dark}),Object.freeze({id:"exercise",flag:"--exercise",weight:1,buys:"clicking every enabled control and grading whether it does anything",on:e=>!!e.exercise})]);function h({flags:e={},mode:s="static"}={}){const r=[],t=[];for(const n of c)(n.on(e)?r:t).push(n);const o=r.reduce((n,i)=>n+i.weight,0),a=c.reduce((n,i)=>n+i.weight,0);return{mode:s,measured:r,missing:t,score:o,total:a,next:u(e,t)}}function u(e,s){if(!s.length)return null;if(s.length>=3)return{command:`qa run <repo> ${e.base?`--base ${e.base}`:"--serve"}`,why:"climbs every rung below on its own, resumable, and drives the fix loop"};const r=s.map(t=>t.flag).join(" ");return{command:`qa verify <repo> ${e.base?`--base ${e.base} `:""}${r}`.replace(/\s+/g," ").trim(),why:`adds ${s.map(t=>t.id).join(", ")}`}}function f(e,{indent:s=" "}={}){const r=[];if(r.push(`${s}depth ${e.score} of ${e.total} \u2014 measured: ${e.measured.map(t=>t.id).join(" \xB7 ")||"nothing beyond the static scan"}`),e.missing.length){r.push(`${s} NOT measured, and therefore not clean:`);for(const t of e.missing)r.push(`${s} ${t.flag.padEnd(20)} ${t.buys}`)}return e.next&&(r.push(`${s}next ${e.next.command}`),r.push(`${s} (${e.next.why})`)),r.join(`
2
+ `)}const l=Object.freeze({});function p(e){const s=[],r=[];for(const{name:t,text:o}of e)(d(t,o)?r:s).push({name:t,needs:l[t]??null});return{substantive:s,hollow:r,total:e.length}}function d(e,s){const r=String(s??"").trim();if(!r)return!0;if(e.endsWith(".jsonl"))return r.split(`
3
+ `).filter(Boolean).length===0;let t;try{t=JSON.parse(r)}catch{return!1}if(t&&t.status==="unmeasured")return!0;const o=new Set(["schema_version","schemaVersion","run_id","runId","generated_at","generatedAt","version","status","reason","projection"]),a=Object.entries(t).filter(([n])=>!o.has(n));return a.length?a.every(([,n])=>n===null||Array.isArray(n)&&n.length===0||n&&typeof n=="object"&&Object.keys(n).length===0):!0}function g(e,{indent:s=" ",limit:r=6}={}){if(!e.total)return"";const t=[`${s}artifacts ${e.substantive.length} of ${e.total} carry evidence; ${e.hollow.length} are honest \`unmeasured\` markers`],o=e.hollow.filter(n=>n.needs).slice(0,r);for(const n of o)t.push(`${s} ${n.name.padEnd(28)} needs ${n.needs}`);const a=e.hollow.filter(n=>n.needs).length-o.length;return a>0&&t.push(`${s} ... and ${a} more (qa dev validate <repo> lists every one)`),t.join(`
4
+ `)}export{l as ARTIFACT_NEEDS,c as AXES,p as artifactSubstance,h as depthReport,g as formatArtifacts,f as formatDepth};
@@ -0,0 +1,2 @@
1
+ import y from"node:fs";import I from"node:os";import a from"node:path";import{createRequire as M}from"node:module";import{fileURLToPath as _}from"node:url";import{exists as m,readLock as F}from"./fs.mjs";import{run as J,runSync as q,which as E,gitInfo as H}from"./proc.mjs";import{probe as W}from"./server.mjs";import{CARGO_MISSING as N,kernelPhaseAvailability as G}from"./agent-availability.mjs";import{loadPersonas as U}from"./auth/personas.mjs";import{loadMutationDeclaration as B}from"./mutation-policy.mjs";import{validate as K}from"./schema.mjs";const T=1440*60*1e3,A=a.resolve(a.dirname(_(import.meta.url)),"../..");function i(e,t,r={}){return{id:e,level:"ok",detail:t,fix:null,...r}}function g(e,t,r,o={}){return{id:e,level:"warn",detail:t,fix:r,...o}}function l(e,t,r,o={}){return{id:e,level:"fail",detail:t,fix:r,...o}}function Y(e){let t;try{t=y.statSync(e)}catch(r){return r&&r.code==="ENOENT"?l("repo",`no such directory: ${e}`,"pass the repository path (absolute is safest)"):l("repo",`${e} is unreadable (${r.code??r.message})`,"check permissions")}return t.isDirectory()?i("repo",e):l("repo",`not a directory: ${e}`,"pass a repository directory")}function V(e=process.versions.node){const t=Number(String(e).split(".")[0]);return!Number.isFinite(t)||t<18?l("node",`node ${e} is too old`,"install node >= 18"):i("node",`node ${e}`)}function z(e){const t=F(e);return t&&!t.stale?l("lock",`another qa run holds the lock (pid ${t.pid}, ${t.command}, since ${t.at})`,"wait for it to finish, or remove .verify/qa.lock if that process is gone"):i("lock",t?"a stale lock is present and will be reclaimed":"no lock held")}async function X(e,t,{spawnFn:r=J}={}){const o=await r(process.execPath,[a.join(t,"bin","inventory.mjs"),e,"--stdout"],{stdout:"capture",stderr:"capture",timeoutMs:12e4});if(o.code!==0)return l("routes",`inventory could not run (exit ${o.code}): ${(o.stderr||"").trim().slice(0,300)||"(no output)"}`,"run `node bin/inventory.mjs <repo>` directly and read the error");let n;try{n=JSON.parse(o.stdout)}catch(c){return l("routes",`inventory produced invalid JSON: ${c.message}`,"run bin/inventory.mjs directly and inspect its output")}const s=n?.counts?.routes??0;return s?i("routes",`${s} route(s)`,{count:s,inventory:n}):l("routes","zero routes found",'no supported router convention matched. Looked for: Next app (app/**/page.tsx), Next pages (pages/**/*.tsx), react-router / route-config literals (<Route path=> or { path:, element|Component|lazy }), TanStack-style src/routes/**, Remix or React Router 7 flat routes (app/routes/**), SvelteKit (src/routes/**/+page.svelte), Astro (src/pages/**/*.astro), Nuxt or unplugin-vue-router (pages/**/*.vue), and Expo Router (app/** or src/app/**, needs the expo-router dependency). If the app is in a subdirectory of a monorepo, point <repo> at that subdirectory. If it builds its route array from a variable rather than a literal, the matcher cannot see it. To unblock a run on a router none of these cover, commit a <repo>/verify.routes.json: an array of "/path" strings, or of { "path": "/x", "file": "src/pages/X.tsx" } so the declared route also gets source attribution',{inventory:n})}async function Q(e,{probeFn:t=W,timeoutMs:r=8e3,prod:o=!1}={}){const n=await t(e,{timeoutMs:r});return n.reachable?o&&n.serverType==="dev"?l("base",`--prod was passed but ${e} looks like a dev server (${n.evidence.join("; ")||"no dev tell recorded"})`,'serve a production build \u2014 SKILL.md "Sweep a production build, not the dev server"',{probe:n}):i("base",`${e} reachable, status ${n.status}, serverType ${n.serverType}`,{probe:n}):l("base",`--base ${e} is unreachable (${(n.evidence||[]).join("; ")||"no response"})`,"start the server before running against --base",{probe:n})}function Z(e,{skillRoot:t=A}={}){const r=a.join(e,"verify.roles.json"),o=a.join(e,".verify","roles.json"),n=m(r)?r:m(o)?o:null;if(!n)return i("roles","no verify.roles.json (single default role owning everything)",{present:!1});let s;try{s=U(e)}catch(p){const d=String(p?.message??"").endsWith(": not valid JSON");return l("roles",`${a.relative(e,n)} ${d?"is not valid JSON":"has invalid persona declarations"}`,d?"fix the JSON":'remove credentials and use categorical persona names; see SKILL.md "Roles" and the host-harness launcher',{present:!0})}const c=C(n,"personas.schema.json",t);if(c)return l("roles",`${a.relative(e,n)} has invalid persona declarations at ${c}`,"fix the JSON to match schemas/personas.schema.json",{present:!0});const h=s.personas.map(p=>p.name);return h.length?i("roles",`${h.length} role(s): ${h.join(", ")}`,{present:!0,names:h}):l("roles",`${a.relative(e,n)} has no roles`,"add at least one role",{present:!0})}function C(e,t,r){let o,n;try{o=JSON.parse(y.readFileSync(e,"utf8")),n=JSON.parse(y.readFileSync(a.join(r,"schemas",t),"utf8"))}catch{return"(root)"}let s=n;if(t==="verify-crud.schema.json"&&Array.isArray(n.oneOf)){const c=o?.schema_version,h=n.oneOf.find(p=>p?.properties?.schema_version?.const===c);h&&(s={...n,...h},delete s.oneOf)}return K(s,o)[0]?.path??null}function ee(e,{skillRoot:t=A}={}){const r=B(e);if(!m(r.path))return i("crud","no verify.crud.json (mutation measurement disabled)",{present:!1});if(!r.ok)return l("crud","verify.crud.json has invalid CRUD declarations","fix verify.crud.json using schemas/verify-crud.schema.json and references/verify.crud.example.json",{present:!0});const o=C(r.path,"verify-crud.schema.json",t);return o?l("crud",`verify.crud.json has invalid CRUD declarations at ${o}`,"fix verify.crud.json using schemas/verify-crud.schema.json and references/verify.crud.example.json",{present:!0}):i("crud","verify.crud.json declarations are valid",{present:!0})}function te(e,t=A){return[Z(e,{skillRoot:t}),ee(e,{skillRoot:t})]}function re(){if(process.platform==="win32")return[];const e=q("ps",["-axo","pid,command"]);return e.code!==0?[]:e.stdout.split(`
2
+ `).slice(1)}const ne=/\b(?:next\s+(?:dev|start)|webpack(?:-dev-server|\s+serve)|turbo\s+(?:dev|start|serve))\b|(?<![\w/.-])vite(?![\w.-])(?!\s+build)/i,oe=/\b(?:next\s+build|vite\s+build|webpack(?=\s|$)(?:\s+(?:--watch|watch|build|--config\b)[^\n]*)?|turbo\s+(?:run\s+)?(?:build|watch)|tsc\s+--watch|esbuild[^\n]*--watch)\b/i;function se({psFn:e=re,ownPid:t=process.pid}={}){if(process.platform==="win32")return i("build-process","skipped on win32");const r=[];for(const o of e()){const n=o.match(/^\s*(\d+)\s+(.*)$/);if(!n)continue;const s=Number(n[1]),c=n[2];s!==t&&(/qa\.mjs/.test(c)||!ne.test(c)&&oe.test(c)&&r.push({pid:s,cmd:c}))}return r.length?g("build-process",`a build process appears to be running: ${r.map(o=>`pid ${o.pid} ${o.cmd.slice(0,80)}`).join("; ")}`,'finish or stop the build/watch process before sweeping \u2014 it can mutate assets or compete with the measured source (SKILL.md "Run order")'):i("build-process","no build process detected")}function ie(e,t,r){if(!t)return i("auth-domains","no --base; nothing to compare cookie domains against");let o;try{o=new URL(t)}catch{return i("auth-domains","base is not a URL; skipped")}const n=o.hostname,s=[],c=r?.names?.length?ae(e):null;if(c)for(const[d,f]of Object.entries(c))f?.auth?s.push([d,a.isAbsolute(f.auth)?f.auth:a.join(e,f.auth)]):m(a.join(e,".verify","auth.json"))&&s.push(["default",a.join(e,".verify","auth.json")]);if(!s.length)return i("auth-domains","no auth state to check");const h=d=>{const f=String(d??"").replace(/^\./,"").toLowerCase();return f===n||n.endsWith("."+f)},p=[];for(const[d,f]of s){let v;try{v=JSON.parse(y.readFileSync(f,"utf8"))}catch{continue}const j=Array.isArray(v?.cookies)?v.cookies:[],w=Array.isArray(v?.origins)?v.origins:[];if(!j.length&&!w.length)continue;const x=j.some(b=>h(b.domain)),S=w.some(b=>{try{return new URL(b.origin).origin===o.origin}catch{return!1}});if(!x&&!S){const b=[...new Set(j.map(u=>u.domain).filter(Boolean))].slice(0,3).join(", ")||"none";p.push(`${d} (cookies for ${b})`)}}return p.length?l("auth-domains",`${p.length} auth state(s) hold no cookie for base host "${n}": ${p.slice(0,4).join("; ")}${p.length>4?"; \u2026":""}`,`use --base with the host the cookies were minted for (e.g. http://localhost:PORT) or regenerate the states for "${n}" -- otherwise every route bounces to login and the run is INVALID`):i("auth-domains",`${s.length} auth state(s) carry cookies for "${n}"`)}function ae(e){const t=[a.join(e,"verify.roles.json"),a.join(e,".verify","roles.json")].find(m);if(!t)return null;try{return JSON.parse(y.readFileSync(t,"utf8"))}catch{return null}}function ue(e,{now:t=Date.now(),maxAgeMs:r=7*T}={}){const o=a.join(e,".verify","auth.json");if(!m(o))return i("auth-age","no .verify/auth.json yet");let n;try{n=y.statSync(o)}catch{return i("auth-age","no .verify/auth.json yet")}const s=Math.round((t-n.mtimeMs)/T);return t-n.mtimeMs>r?g("auth-age",`.verify/auth.json is ${s} day(s) old`,"delete it and capture fresh state by hand, or provision it through the host-harness launcher (--lane real-integration)"):i("auth-age",`.verify/auth.json is ${s} day(s) old`)}function ce(e,{gitInfoFn:t=H}={}){const r=t(e);return r.isRepo?r.dirty>0?g("git",`${r.dirty} uncommitted change(s) \u2014 only a failure for qa run loop, which needs a clean tree`,"commit or stash before running qa run loop"):i("git","clean working tree"):i("git","not a git repo (only a problem for qa run loop)")}function le(e,t){const r=m(a.join(e,"verify.journeys.mjs"))||m(a.join(e,".verify","journeys.mjs")),o=t?.counts?.mutations??0;return!r&&o>0?g("journeys",`${o} mutation(s) found but no verify.journeys.mjs \u2014 mutation routes are not driven by the sweep`,"run qa init --study to draft journeys, then promote .verify/journeys.draft.mjs to verify.journeys.mjs"):i("journeys",r?"verify.journeys.mjs present":"no mutations to journey yet")}function pe(e,t){const r=e?.counts?.mutations??0;return!t||!r?i("mutate-on-load",r?"no --base given yet":"no mutations found in this app's source"):g("mutate-on-load",`${r} mutation(s) exist in this app's source -- a sweep without --mutate submits none of its own, but cannot stop the APP from writing during a normal page load (a get-or-create on mount is the shape that bites)`,`point --base at an environment you are willing to have mutated, --mutate or not; see SKILL.md "The harness's own writes are opt-in; the app's are not"`)}function he(e,t){const r=a.join(e,".verify","research.json");if(!m(r))return i("principals","no research.json yet");let o;try{o=JSON.parse(y.readFileSync(r,"utf8"))}catch{return i("principals","research.json unreadable, skipping")}const n=o?.principals,s=Array.isArray(n)?n:Array.isArray(n?.names)?n.names:[];return s.length>1&&!t?.present?g("principals",`research found ${s.length} principals (${s.slice(0,6).join(", ")}) but no verify.roles.json`,'write verify.roles.json \u2014 SKILL.md "Roles"'):i("principals",s.length?`${s.length} principal(s), roles file present`:"single principal")}function fe({whichFn:e=E,strict:t=!1,skillRoot:r=null}={}){const o=e("cargo");if(!o){const n=r?G(r,{cargoAvailabilityFn:()=>({status:"unmeasured",reason:N})}):{status:"unmeasured",reason:N};if(n.status==="measured")return i("cargo","verified prebuilt Rust kernel");const s=`${n.reason} \u2014 the Rust kernel (graph, coherence obligation plan, experiment queue, triage route priority) cannot run`;return t?l("cargo",s,"install rustup (https://rustup.rs), or accept an unmeasured kernel by dropping --strict"):g("cargo",`${s} \u2014 only needed for the autonomous (Rust) phase`,"install rustup if you plan to run the autonomous phase")}return i("cargo",`cargo at ${o}`)}function de(e,t,{extraRoots:r=[]}={}){const o=[e,a.join(t,".playwright"),process.cwd(),...process.env.PLAYWRIGHT_HOME?[a.resolve(process.env.PLAYWRIGHT_HOME)]:[],...r];for(const n of o)for(const s of["playwright","@playwright/test"])try{return M(a.join(n,"package.json")).resolve(s),{root:n,pkg:s}}catch{}return null}function me(){const e=[];return process.env.PLAYWRIGHT_BROWSERS_PATH&&e.push(process.env.PLAYWRIGHT_BROWSERS_PATH),process.platform==="darwin"?e.push(a.join(I.homedir(),"Library","Caches","ms-playwright")):process.platform==="win32"?e.push(a.join(process.env.LOCALAPPDATA||"","ms-playwright")):e.push(a.join(I.homedir(),".cache","ms-playwright")),e}function ye({dirs:e}={}){for(const t of e??me())if(!(!t||!m(t)))try{const r=y.readdirSync(t).find(o=>/^chromium/i.test(o));if(r)return a.join(t,r)}catch{}return null}function ge(e,t,{playwrightOpts:r,chromiumOpts:o}={}){const n=de(e,t,r),s=ye(o);if(!n||!s){const c=[!n&&"the playwright package",!s&&"the chromium binary"].filter(Boolean).join(" and ");return g("playwright",`${c} not found; the runtime sweep can self-install on first use`,"npm i -D @playwright/test && npx playwright install chromium (or let the sweep install it)")}return i("playwright",`playwright package under ${n.root} (${n.pkg}), chromium at ${s}`)}function $(e,t){const r=e.filter(s=>s.level==="fail"),o=e.filter(s=>s.level==="warn"),n=r.length?2:o.length&&t?1:0;return{checks:e,fails:r,warns:o,exit:n}}async function Pe(e,t={}){const{skillRoot:r,base:o=null,prod:n=!1,strict:s=!1,probeFn:c,spawnFn:h,timeoutMs:p,nodeVersion:d,now:f,psFn:v,whichFn:j,gitInfoFn:w,authMaxAgeMs:x,playwrightOpts:S,chromiumOpts:b}=t,u=[],L=Y(e);if(u.push(L),L.level==="fail")return $(u,s);const O=V(d);if(u.push(O),O.level==="fail")return $(u,s);u.push(z(e));const[k,P]=te(e,r);if(u.push(k,P),[k,P].some(D=>D.level==="fail"))return $(u,s);const R=await X(e,r,{spawnFn:h});return u.push(R),o&&u.push(await Q(o,{probeFn:c,timeoutMs:p,prod:n})),o&&u.push(ie(e,o,k)),u.push(ge(e,r,{playwrightOpts:S,chromiumOpts:b})),u.push(se({psFn:v})),u.push(ue(e,{now:f,maxAgeMs:x})),u.push(ce(e,{gitInfoFn:w})),u.push(le(e,R.inventory)),u.push(pe(R.inventory,o)),u.push(he(e,k)),u.push(fe({whichFn:j,strict:s,skillRoot:r})),$(u,s)}export{T as DAY_MS,ue as checkAuthAge,ie as checkAuthDomains,Q as checkBase,se as checkBuildProcess,fe as checkCargo,ee as checkCrud,ce as checkGitDirty,te as checkInputDeclarations,le as checkJourneys,z as checkLock,pe as checkMutateOnLoad,V as checkNode,ge as checkPlaywright,Y as checkRepo,Z as checkRoles,he as checkRolesVsPrincipals,X as checkRoutes,ye as findChromium,de as findPlaywright,Pe as runDoctor};
@@ -0,0 +1 @@
1
+ function d(e,n){if(typeof e!="number"||!Number.isFinite(e)||e<=0)throw new RangeError(`defaultQueue: familyCost for family "${n}" must be a positive finite number, got ${JSON.stringify(e)}`)}function l({routePriority:e,families:n,inventory:f={},metadata:i={}}){if(!e||typeof e!="object"||typeof e.status!="string")throw new TypeError("defaultQueue: routePriority must be a discriminated {status, ...} dimension");if(e.status!=="measured")return{status:e.status,reason:e.reason};if(!Array.isArray(n)||n.length===0)throw new TypeError("defaultQueue: families must be a non-empty array of {name, cost}");for(const t of n)d(t?.cost,t?.name);const m=new Set((f.routes??[]).map(t=>t.path)),c=!!i.production,a=[];for(const t of e.value?.items??[]){const{route:o,confidence:r,expectedInformation:s}=t;if(typeof r!="number"||r<0||r>1)throw new RangeError(`defaultQueue: route "${o}" confidence must be in [0,1], got ${JSON.stringify(r)}`);if(typeof s!="number"||Math.abs(s-(1-r))>1e-9)throw new RangeError(`defaultQueue: route "${o}" expectedInformation must equal exactly 1 - confidence, never a second source of truth`);for(const u of n)a.push({route:o,family:u.name,expectedInformation:s,familyCost:u.cost,score:s/u.cost,mutation:m.has(o),production:c})}return a.sort((t,o)=>o.score-t.score||t.route.localeCompare(o.route)||t.family.localeCompare(o.family)),{status:"measured",items:a}}export{l as defaultQueue};
@@ -0,0 +1,3 @@
1
+ import{groupFindings as m,rankGroups as $}from"./triage.mjs";import{sevRank as f}from"./reports.mjs";const p=Object.freeze(["queued","fixing","gate","open","merged","retry","blocked","conflict"]);function x(n){return String(n).toLowerCase().replace(/[^a-z0-9]+/g,"-").replace(/^-+|-+$/g,"").slice(0,60)||"class"}function g(n,t){if(n.files.length)return{files:[...n.files].sort(),component:null,unresolved:!1};const c=new Map((t?.routes??[]).map(s=>[s.path,s])),l=n.routes.map(s=>c.get(s)).filter(Boolean);if(!l.length)return{files:[],component:null,unresolved:!0};if(l.length===1){const s=l[0].file??null;return s?{files:[s],component:null,unresolved:!1}:{files:[],component:null,unresolved:!0}}let o=new Set(l[0].files??[]);for(const s of l.slice(1))o=new Set((s.files??[]).filter(e=>o.has(e)));const r=[...o].filter(s=>!l.some(e=>e.file===s)).sort();return r.length?{files:r,component:r[0],unresolved:!1}:{files:[],component:null,unresolved:!0}}function y({records:n,inventory:t,blocked:c=new Set,ghosts:l=new Set}){const o=[],r=[];for(const e of n)e.waived?o.push({fid:e.fid,reason:"waived"}):e.knownFp?o.push({fid:e.fid,reason:"known-fp"}):c.has(e.fid)?o.push({fid:e.fid,reason:"blocked"}):l.has(e.fid)?o.push({fid:e.fid,reason:"ghost"}):r.push(e);return o.sort((e,i)=>e.fid.localeCompare(i.fid)),{classes:$(m(r)).map(e=>{const{files:i,component:a,unresolved:u}=g(e,t);return{id:e.sample.fid,kind:e.kind,phase:e.phase,severity:e.severity,at:e.at??null,fids:[...e.fids].sort(),routes:e.routes,files:i,component:a,atlas:e.atlas??null,unresolved:u,status:"queued",attempts:[],pr:null,worktree:null,branch:null}}),excluded:o}}function S(n,{lanes:t=4}={}){const c=[...n].sort((r,s)=>f(r.severity)-f(s.severity)||s.fids.length-r.fids.length||r.id.localeCompare(s.id)),l=[],o=new Set;for(;o.size<c.length;){const r=[],s=new Set;for(const e of c)if(!(o.has(e.id)||r.length>=t)){if(e.unresolved){if(r.length===0){r.push(e.id),o.add(e.id);break}continue}if(!e.files.some(i=>s.has(i))){r.push(e.id),o.add(e.id);for(const i of e.files)s.add(i)}}if(!r.length)break;l.push(r)}return l}function j({run:n,base:t,flags:c,baselineCounts:l,classes:o,batches:r,excluded:s}){const e={};for(const i of[...o].sort((a,u)=>a.id.localeCompare(u.id)))e[i.id]=i;return{schema_version:1,run:n,base:t??null,flags:[...c],baselineCounts:l,batches:r,classes:e,excluded:s,updatedAt:null}}const h=new Set(["fixing","gate","retry","conflict"]);function C(n){return Object.values(n.classes).filter(t=>h.has(t.status)).map(t=>t.id)}function O(n){return Object.values(n.classes).every(t=>t.status!=="queued"&&!h.has(t.status))}function A(n,t,c,l={}){if(!p.includes(c))throw new Error(`unknown status "${c}"; expected one of ${p.join(", ")}`);const o=n.classes[t];if(!o)throw new Error(`no class "${t}" in the plan`);const r=new Date().toISOString();return{...n,updatedAt:r,classes:{...n.classes,[t]:{...o,...l,status:c,updatedAt:r}}}}const d=new Set(["open","merged"]);function v(n){const t=Object.values(n.classes),c=l=>t.filter(o=>o.status===l).length;return{classes:t.length,lanesOpen:t.filter(l=>["fixing","gate","retry","conflict"].includes(l.status)).length,closed:t.filter(l=>d.has(l.status)).reduce((l,o)=>l+o.fids.length,0),total:n.baselineCounts?.total??t.reduce((l,o)=>l+o.fids.length,0),open:c("open"),merged:c("merged"),blocked:c("blocked"),retry:c("retry"),conflict:c("conflict")}}function b(n){const t=v(n);return`classes ${t.classes} \xB7 lanes open ${t.lanesOpen} \xB7 findings closed ${t.closed} of ${t.total} \xB7 PRs open ${t.open} \xB7 merged ${t.merged} \xB7 blocked ${t.blocked} \xB7 retry ${t.retry} \xB7 conflict ${t.conflict}`}function E(n,{ledgerMd:t=null}={}){const c=["# qa fix board","",`run ${n.run} \xB7 ${b(n)}`,""];c.push("| class | kind | sev | status | closed/total | routes | attempts | PR |","|---|---|---|---|---|---|---|---|");const l=Object.values(n.classes).sort((s,e)=>f(s.severity)-f(e.severity)||s.id.localeCompare(e.id));for(const s of l){const e=d.has(s.status)?s.fids.length:0;c.push(`| ${s.id} | ${s.kind} | ${s.severity} | ${s.status} | ${e}/${s.fids.length} | ${s.routes.join(", ")} | ${s.attempts.length} | ${s.pr??""} |`)}const o=n.excluded??[];if(o.length){const s={};for(const e of o)s[e.reason]=(s[e.reason]??0)+1;c.push("",`excluded ${o.length} (${Object.keys(s).sort().map(e=>`${e} ${s[e]}`).join(", ")})`)}const r=new Map;for(const s of l)for(const e of s.routes){const i=r.get(e)??{classes:0,closed:0};i.classes++,d.has(s.status)&&i.closed++,r.set(e,i)}if(t)for(const s of t.split(`
2
+ `)){const e=/^\|\s*(\/[^|]*?)\s*\|/.exec(s);e&&!r.has(e[1])&&r.set(e[1],{classes:0,closed:0})}if(r.size){c.push("","## routes","");for(const[s,e]of[...r.entries()].sort((i,a)=>i[0].localeCompare(a[0])))c.push(`- ${s}: ${e.classes} classes, ${e.closed} closed`)}return c.push("",`batches: ${n.batches.map((s,e)=>`${e+1}[${s.join(", ")}]`).join(" ")}`,""),c.join(`
3
+ `)}export{p as STATUSES,C as activeClasses,O as allBatchesSettled,v as boardCounts,b as boardHeaderLine,E as boardMarkdown,y as classesFrom,j as emptyPlan,S as packBatches,x as slug,A as transition};
@@ -0,0 +1 @@
1
+ import h from"node:fs";import c from"node:path";import{validate as f}from"./schema.mjs";const p=Object.freeze({error:500,forbidden:403}),d="verify.read-only.json",k=/(^|\/)(auth|login|logout|session|sessions|signin|sign-in|token|refresh|csrf|me)(\/|$|\?)/i;function v(a,i){const r=c.join(c.resolve(a),d);if(!h.existsSync(r))return{status:"absent",paths:new Set,reason:null};let t;try{t=JSON.parse(h.readFileSync(r,"utf8"))}catch{return{status:"invalid",paths:new Set,reason:`${d} is not valid JSON`}}let o;try{o=JSON.parse(h.readFileSync(c.join(i,"schemas","verify-read-only.schema.json"),"utf8"))}catch(n){return{status:"invalid",paths:new Set,reason:`verify-read-only.schema.json is unreadable: ${n.code??"error"}`}}const s=f(o,t);return s.length?{status:"invalid",paths:new Set,reason:`${d} does not validate: ${s[0].path} ${s[0].message}`}:{status:"declared",paths:new Set(t.read_only_posts.map(n=>n.path)),reason:null}}function y(a,i,r=null){const t=i instanceof Set?i:new Set;return(a?.apiTargets??(a?.apiPaths??[]).map(s=>({method:"GET",path:s}))).filter(s=>s&&typeof s.path=="string"&&!k.test(s.path)&&(!r||!s.origin||s.origin===r)&&(/^(GET|HEAD)$/i.test(s.method??"GET")||t.has(s.path)))}function b(a,i,r=null){const t=new Set,o=[],s=[];for(const n of a??[]){if(String(n?.route??"").startsWith("(")||n.redirectedTo)continue;const e=n.role+"\0"+n.route;t.has(e)||(t.add(e),y(n,i,r).length?o.push(n):s.push({route:n.route,role:n.role??null,reason:`no interceptable data request was observed on this route -- no GET/HEAD, and no POST path declared read-only in ${d} -- so no forced state could ever reach it`}))}return{cells:o,ineligible:s}}function u(a,i,r){return[{kind:"state.not-intercepted",severity:"P2",at:"all",detail:`${a} of ${i} ${r} -- the forced state never reached the app, so nothing here was measured. Findings for this route are withheld rather than reported as clean.`}]}function S(a,i=a.length){const r=a.filter(e=>!e.crashed),t=[];for(const e of r)e.leaksRaw&&t.push({kind:"state.leaks-raw",severity:"P2",at:e.kind,detail:`raw payload or stack text is visible in the DOM under ${e.kind}`});const o=e=>e.forcedHits??e.intercepted,s=r.some(e=>Number.isFinite(o(e)));let n=[];if(s){if(n=r.filter(e=>!Number.isFinite(o(e))||o(e)<1),n.length===r.length)return t.push(...u(n.length,i,"forced responses matched zero requests")),t}else{const e=new Set(r.map(l=>l.fingerprint));if(r.length>=3&&e.size===1)return t.push(...u(i,i,"deliberately different responses produced byte-identical pages")),t}for(const e of r.filter(l=>!n.includes(l))){if(e.kind==="slow"){!e.hasSpinner&&e.textLen<40&&t.push({kind:"state.slow-blank",severity:"P1",at:e.kind,detail:"while its data was in flight this route rendered neither content nor a loading indicator -- a blank screen is what a slow network looks like to the user"});continue}e.textLen<40?t.push({kind:"state.blank",severity:"P1",at:e.kind,detail:`forcing ${e.kind} left the page blank (${e.textLen} chars of text) -- a broken API renders nothing at all, with no way for the user to tell what happened`}):e.hasSpinner&&(e.kind==="error"||e.kind==="forbidden")?t.push({kind:"state.stuck-spinner",severity:"P2",at:e.kind,detail:`a visible loading indicator persisted through the bounded ${e.observationMs??1200}ms observation deadline after the request failed with ${p[e.kind]} -- recovery was not observed within the deadline`}):(e.kind==="error"||e.kind==="malformed")&&!e.saysError?t.push({kind:"state.silent-failure",severity:"P1",at:e.kind,detail:"the request failed and the surface says nothing about it -- it renders as though the data arrived, which is how a user acts on values that are not there"}):e.kind==="forbidden"&&!e.saysDenied&&!e.saysError?t.push({kind:"state.silent-failure",severity:"P1",at:e.kind,detail:"403 rendered with no denial or error message -- the user cannot tell an empty result from one they are not allowed to see"}):e.kind==="empty"&&e.status!=="unmeasured"&&!e.saysEmpty&&t.push({kind:"state.no-empty-state",severity:"P2",at:e.kind,detail:"an empty response renders no empty state -- an empty list and a failed fetch look identical to the user"})}return n.length&&t.push({kind:"state.not-intercepted",severity:"P2",at:"all",detail:`${n.length} forced state(s) intercepted no targeted requests and are unmeasured; findings from intercepted states are retained.`}),t}export{d as READ_ONLY_DECLARATION,y as forcedStateTargets,S as judgeForcedStateColumns,v as loadReadOnlyPosts,b as planForcedStateCells};