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.
- package/LICENSE +22 -0
- package/NOTICE +9 -0
- package/README.md +100 -0
- package/SKILL.md +65 -0
- package/agent/prebuilt/darwin-universal/qa-agent +0 -0
- package/agent/prebuilt/linux-arm64/qa-agent +0 -0
- package/agent/prebuilt/linux-x64/qa-agent +0 -0
- package/agent/release-manifest.json +31 -0
- package/bin/autonomous-cache.mjs +2 -0
- package/bin/bench-generate.mjs +232 -0
- package/bin/bench-graph-query.mjs +2 -0
- package/bin/bench-score.mjs +10 -0
- package/bin/classify.mjs +15 -0
- package/bin/coherence-playwright.mjs +1 -0
- package/bin/coherence-runner.mjs +6 -0
- package/bin/crud-runner.mjs +12 -0
- package/bin/experiment-executor.mjs +10 -0
- package/bin/experiment-runner.mjs +3 -0
- package/bin/inventory.mjs +17 -0
- package/bin/probe.js +879 -0
- package/bin/qa.mjs +16 -0
- package/bin/rules.mjs +5 -0
- package/bin/run-benchmarks.mjs +2 -0
- package/bin/runtime-packs.mjs +3 -0
- package/bin/semantic-explorer.mjs +3 -0
- package/bin/shadow-collector.mjs +3 -0
- package/bin/shadow-replay.mjs +6 -0
- package/bin/soak-marathon.mjs +15 -0
- package/bin/sweep.mjs +43 -0
- package/bin/validate-priors.mjs +3 -0
- package/bin/verify-artifacts.mjs +3 -0
- package/bin/verify-manifest.mjs +2 -0
- package/install-skill.sh +45 -0
- package/lib/qa/agent-availability.mjs +1 -0
- package/lib/qa/agent-result.mjs +1 -0
- package/lib/qa/analysis.mjs +5 -0
- package/lib/qa/atlas-coverage.mjs +11 -0
- package/lib/qa/atlas-v1.mjs +1 -0
- package/lib/qa/atlas.mjs +2 -0
- package/lib/qa/auth/coordinator.mjs +1 -0
- package/lib/qa/auth/launcher.mjs +3 -0
- package/lib/qa/auth/lease.mjs +1 -0
- package/lib/qa/auth/oracle.mjs +1 -0
- package/lib/qa/auth/personas.mjs +3 -0
- package/lib/qa/autonomous-cache.mjs +3 -0
- package/lib/qa/block-ledger.mjs +1 -0
- package/lib/qa/capture-report.mjs +63 -0
- package/lib/qa/ci.mjs +11 -0
- package/lib/qa/cli.mjs +538 -0
- package/lib/qa/coherence/analyze.mjs +1 -0
- package/lib/qa/coherence/cache-key.mjs +6 -0
- package/lib/qa/coherence/causes.mjs +1 -0
- package/lib/qa/coherence/concurrency.mjs +1 -0
- package/lib/qa/coherence/contracts.mjs +1 -0
- package/lib/qa/coherence/entity-aliases.mjs +1 -0
- package/lib/qa/coherence/enums.mjs +1 -0
- package/lib/qa/coherence/family-report.mjs +1 -0
- package/lib/qa/coherence/flow-pool.mjs +1 -0
- package/lib/qa/coherence/graph.mjs +1 -0
- package/lib/qa/coherence/identity.mjs +1 -0
- package/lib/qa/coherence/ir.mjs +4 -0
- package/lib/qa/coherence/navigation.mjs +1 -0
- package/lib/qa/coherence/observe.mjs +2 -0
- package/lib/qa/coherence/offline.mjs +1 -0
- package/lib/qa/coherence/plan-join.mjs +1 -0
- package/lib/qa/coherence/provenance.mjs +2 -0
- package/lib/qa/coherence/report.mjs +1 -0
- package/lib/qa/coherence/representations.mjs +1 -0
- package/lib/qa/coherence/risk.mjs +2 -0
- package/lib/qa/coherence/structural-js.mjs +11 -0
- package/lib/qa/coherence/symbols.mjs +6 -0
- package/lib/qa/coherence/tanstack.mjs +4 -0
- package/lib/qa/coherence/views.mjs +1 -0
- package/lib/qa/coherence-planner.mjs +1 -0
- package/lib/qa/command-catalog.mjs +2 -0
- package/lib/qa/commands/dev.mjs +1 -0
- package/lib/qa/commands/fix.mjs +1 -0
- package/lib/qa/commands/impl/analysis.mjs +1 -0
- package/lib/qa/commands/impl/atlas.mjs +1 -0
- package/lib/qa/commands/impl/coherence.mjs +1 -0
- package/lib/qa/commands/impl/crud.mjs +2 -0
- package/lib/qa/commands/impl/doctor.mjs +1 -0
- package/lib/qa/commands/impl/experiments.mjs +1 -0
- package/lib/qa/commands/impl/fp.mjs +1 -0
- package/lib/qa/commands/impl/graph.mjs +1 -0
- package/lib/qa/commands/impl/invariants.mjs +1 -0
- package/lib/qa/commands/impl/lanes.mjs +7 -0
- package/lib/qa/commands/impl/ledger.mjs +11 -0
- package/lib/qa/commands/impl/loop.mjs +1 -0
- package/lib/qa/commands/impl/marathon.mjs +1 -0
- package/lib/qa/commands/impl/next.mjs +38 -0
- package/lib/qa/commands/impl/report.mjs +4 -0
- package/lib/qa/commands/impl/research.mjs +4 -0
- package/lib/qa/commands/impl/selftest.mjs +1 -0
- package/lib/qa/commands/impl/serve.mjs +1 -0
- package/lib/qa/commands/impl/shadow.mjs +1 -0
- package/lib/qa/commands/impl/stability.mjs +1 -0
- package/lib/qa/commands/impl/triage.mjs +1 -0
- package/lib/qa/commands/impl/validate.mjs +4 -0
- package/lib/qa/commands/impl/version.mjs +1 -0
- package/lib/qa/commands/impl/watch.mjs +1 -0
- package/lib/qa/commands/init.mjs +6 -0
- package/lib/qa/commands/measure.mjs +1 -0
- package/lib/qa/commands/run.mjs +1 -0
- package/lib/qa/commands/show.mjs +1 -0
- package/lib/qa/commands/verify.mjs +6 -0
- package/lib/qa/deepening.mjs +1 -0
- package/lib/qa/denominators.mjs +1 -0
- package/lib/qa/depth.mjs +4 -0
- package/lib/qa/doctor.mjs +2 -0
- package/lib/qa/experiments-default.mjs +1 -0
- package/lib/qa/fix-lanes.mjs +3 -0
- package/lib/qa/forced-states.mjs +1 -0
- package/lib/qa/fp.mjs +2 -0
- package/lib/qa/fs.mjs +6 -0
- package/lib/qa/graph-schema.mjs +2 -0
- package/lib/qa/graph.mjs +3 -0
- package/lib/qa/harness-plan.mjs +3 -0
- package/lib/qa/init.mjs +5 -0
- package/lib/qa/integrity.mjs +1 -0
- package/lib/qa/invariants.mjs +84 -0
- package/lib/qa/jcs.mjs +1 -0
- package/lib/qa/lanes.mjs +1 -0
- package/lib/qa/loop.mjs +7 -0
- package/lib/qa/marathon-coherence.mjs +1 -0
- package/lib/qa/marathon-phases.mjs +1 -0
- package/lib/qa/marathon-wait.mjs +3 -0
- package/lib/qa/marathon.mjs +5 -0
- package/lib/qa/mode-coverage.mjs +1 -0
- package/lib/qa/mutation-policy.mjs +1 -0
- package/lib/qa/pattern-ledger.mjs +3 -0
- package/lib/qa/pending.mjs +1 -0
- package/lib/qa/proc.mjs +4 -0
- package/lib/qa/recommend.mjs +1 -0
- package/lib/qa/release-manifest.mjs +1 -0
- package/lib/qa/report-html.mjs +178 -0
- package/lib/qa/reports.mjs +2 -0
- package/lib/qa/request-evidence.mjs +1 -0
- package/lib/qa/research.mjs +20 -0
- package/lib/qa/routing.mjs +1 -0
- package/lib/qa/runs.mjs +1 -0
- package/lib/qa/schema.mjs +1 -0
- package/lib/qa/serve.mjs +4 -0
- package/lib/qa/server.mjs +1 -0
- package/lib/qa/source-snapshot.mjs +3 -0
- package/lib/qa/stability.mjs +3 -0
- package/lib/qa/state-evidence.mjs +1 -0
- package/lib/qa/sweep-incremental.mjs +1 -0
- package/lib/qa/triage.mjs +2 -0
- package/lib/qa/verdict.mjs +1 -0
- package/lib/qa/watch.mjs +2 -0
- package/package.json +62 -0
- package/references/ATLAS-COVERAGE.md +231 -0
- package/references/atlas-v1.md +318 -0
- package/references/atlas.md +338 -0
- package/references/verify.crud.example.json +1 -0
- package/references/verify.impact.example.json +1 -0
- package/references/verify.shadow.example.json +1 -0
- package/schemas/agent-result.schema.json +135 -0
- package/schemas/analysis.schema.json +15 -0
- package/schemas/classify.schema.json +61 -0
- package/schemas/coherence-ir.schema.json +1585 -0
- package/schemas/coherence-observed.schema.json +1171 -0
- package/schemas/coherence-plan.schema.json +77 -0
- package/schemas/coherence-risk.schema.json +46 -0
- package/schemas/crud-observed.schema.json +118 -0
- package/schemas/experiment-execution.schema.json +82 -0
- package/schemas/graph-edge.schema.json +22 -0
- package/schemas/graph-node.schema.json +81 -0
- package/schemas/graph.schema.json +2493 -0
- package/schemas/identity-oracle.schema.json +19 -0
- package/schemas/inventory.schema.json +2167 -0
- package/schemas/lanes-plan.schema.json +52 -0
- package/schemas/next.schema.json +54 -0
- package/schemas/personas.schema.json +52 -0
- package/schemas/research.schema.json +192 -0
- package/schemas/run.schema.json +160 -0
- package/schemas/shadow-replay.schema.json +68 -0
- package/schemas/stability.schema.json +92 -0
- package/schemas/sweep.schema.json +239 -0
- package/schemas/v1/agent-result.schema.json +106 -0
- package/schemas/v1/run.schema.json +96 -0
- package/schemas/verify-crud.schema.json +37 -0
- package/schemas/verify-read-only.schema.json +37 -0
- package/templates/github-workflow.yml +80 -0
- package/templates/pre-commit.sample +22 -0
- package/verify.sh +385 -0
package/bin/qa.mjs
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import f from"node:path";import{fileURLToPath as L,pathToFileURL as O}from"node:url";import{parseArgs as V,makeOutput as y,HELP as p,EXIT as n,UsageError as S}from"../lib/qa/cli.mjs";import{commandCatalog as k,groupedCatalog as q,nearest as R,searchCatalog as v,RETIRED_COMMANDS as h}from"../lib/qa/command-catalog.mjs";const w=f.resolve(f.dirname(L(import.meta.url)),".."),D=f.join(w,"lib","qa","commands");function a({json:o,command:i,error:s,exit:m=n.INVALID}){o?process.stdout.write(JSON.stringify({ok:!1,command:i,error:s,exit:m})+`
|
|
3
|
+
`):process.stderr.write(`qa${i?` ${i}`:""}: ${s}
|
|
4
|
+
`)}function x(o){const i=o.indexOf("--");return o.slice(0,i<0?o.length:i).some(s=>s==="--json"||s.startsWith("--json="))}function E(o,i){return i.length?i.map(({name:s,title:m,group:u})=>`${s.padEnd(13)} ${u}
|
|
5
|
+
${m}`).join(`
|
|
6
|
+
`):`No commands match "${o}".`}function b(o){return o.length?o.join(" "):""}async function C(o){if(Number(process.versions.node.split(".")[0])<18)return process.stderr.write(`qa: node ${process.versions.node} is too old; 18 or newer is required
|
|
7
|
+
`),n.INVALID;let s;try{s=V(o)}catch(e){return a({json:x(o),error:e.message}),n.INVALID}const m=y(s.json),u=s.command===null&&!!s.flags.version,r=s.command??(s.flags.version?"version":"help");let d;try{d=k({commandsDir:D,help:p})}catch(e){return a({json:s.json,error:`could not discover commands: ${e.message}`}),n.INVALID}const j=new Set(d.map(({name:e})=>e)),g=e=>R(e,j);function N(e){const t=h[e],l=`qa ${e} was renamed; use qa ${t}`;return s.json?process.stdout.write(JSON.stringify({ok:!1,command:e,error:l,exit:n.INVALID,renamed:t})+`
|
|
8
|
+
`):process.stderr.write(`qa: ${l}
|
|
9
|
+
`),n.INVALID}if(!u&&Object.hasOwn(h,r))return N(r);if(r==="help"||s.flags.help){const e=r==="help"?s.flags.phases?"phases":b(s.positional):r,t=s.flags.search;if(t!==void 0){const c=v(d,p,t);return s.json?process.stdout.write(JSON.stringify({ok:!0,query:t,commands:c})+`
|
|
10
|
+
`):process.stdout.write(E(t,c)+`
|
|
11
|
+
`),n.OK}if(e&&Object.hasOwn(h,e))return N(e);if(e&&e!=="phases"&&!Object.hasOwn(p,e)){const c=g(e.split(" ")[0]),A=`unknown help topic "${e}"${c?`; did you mean "${c}"?`:""}`;return a({json:s.json,command:"help",error:A}),n.INVALID}const l=p[e]??p[""];if(s.json){const c=e?{ok:!0,topic:e,help:l}:{ok:!0,commands:d,groups:q(d)};process.stdout.write(JSON.stringify(c)+`
|
|
12
|
+
`)}else process.stdout.write(l+`
|
|
13
|
+
`);return n.OK}if(!u&&!j.has(r)){const e=g(r);return a({json:s.json,error:`unknown command "${r}"${e?`; did you mean "${e}"?`:""}`}),n.INVALID}const $=f.join(D,`${u?"dev":r}.mjs`);let I;try{I=await import(O($).href)}catch(e){return a({json:s.json,command:r,error:`could not load command: ${e?.message??e}`}),n.INVALID}if(typeof I.run!="function")return a({json:s.json,command:r,error:`command module ${r} exports no run()`}),n.INVALID;try{const e=await I.run({parsed:s,out:m,skillRoot:w,name:r});return[n.OK,n.FINDINGS,n.INVALID].includes(e)?e:n.INVALID}catch(e){if(e instanceof S)return s.json?a({json:!0,command:r,error:e.message}):a({json:!1,command:r,error:`${e.message}
|
|
14
|
+
Run qa help ${r} for usage.`}),n.INVALID;const t=[n.FINDINGS,n.INVALID].includes(e?.exit)?e.exit:n.INVALID;return a({json:!1,command:r,error:String(e?.message??e),exit:t}),s.json&&a({json:!0,command:r,error:String(e?.message??e),exit:t}),process.env.QA_DEBUG&&process.stderr.write((e?.stack??"")+`
|
|
15
|
+
`),t}}C(process.argv.slice(2)).then(o=>{process.exitCode=o},o=>{process.stderr.write(`qa: ${o?.stack??o}
|
|
16
|
+
`),process.exitCode=n.INVALID});
|
package/bin/rules.mjs
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import d from"node:fs";import o from"node:path";import{pathToFileURL as S}from"node:url";const i=Object.freeze({STATIC:"static",RUNTIME:"runtime",BOTH:"both"}),k=new Set(Object.values(i)),A=/^[A-Za-z0-9][A-Za-z0-9._:@/-]{0,127}$/,w=/(?:api[_-]?key|token|secret|password|passwd|bearer|sk_(?:live|test)|pk_(?:live|test))/i,E="<redacted>";function h(e,r){return e===void 0?r:typeof e=="string"&&A.test(e)&&!w.test(e)?e:E}function g(e,r){return{id:h(r?.id,h(e.replace(/\.mjs$/,""),"unknown")),version:h(r?.version,"unversioned"),phase:h(r?.phase,i.STATIC)}}function j(e,r){const a=g(e,r),s=r?.phase===void 0?i.STATIC:r.phase;if(typeof s!="string"||!k.has(s))throw new Error("invalid phase; expected static, runtime, or both");if((s===i.STATIC||s===i.BOTH)&&typeof r?.run!="function")throw new Error(`missing run({ root }) for ${s} phase`);if((s===i.RUNTIME||s===i.BOTH)&&typeof r?.runtime!="function")throw new Error(`missing runtime(context) for ${s} phase`);return{...a,phase:s,run:r.run}}const p=o.resolve(process.argv[2]??"."),y=o.join(p,".verify","rules"),I=d.existsSync(y)?d.readdirSync(y).filter(e=>e.endsWith(".mjs")).sort():[],c=[],f=[],l=[];for(const e of I){const r=o.join(y,e),a=o.relative(p,r);let s=g(e);try{const u=await import(S(r).href),t=u.default??u;s=g(e,t);const n=j(e,t);if(n.phase===i.RUNTIME){c.push({id:n.id,version:n.version,phase:n.phase,file:a,status:"skipped",selected:!1,skipReason:"phase-runtime"});continue}const v=await n.run({root:p}),T=Array.isArray(v)?v:v?.findings??[];for(const m of T)f.push({...m,pack:n.id,packVersion:n.version,packPhase:n.phase,evidenceLevel:"static"});c.push({id:n.id,version:n.version,phase:n.phase,file:a,status:"loaded",selected:!0,findings:T.length})}catch(u){const t={kind:"rule-pack.infrastructure",...s,file:a,error:String(u).split(`
|
|
3
|
+
`)[0]};l.push(t),c.push({id:t.id,version:t.version,phase:t.phase,file:a,status:"error",selected:!1,error:t.error})}}const $={version:"1",generated:new Date().toISOString(),packs:c,findings:f,infrastructureErrors:l};d.mkdirSync(o.join(p,".verify"),{recursive:!0}),d.writeFileSync(o.join(p,".verify","rule-packs.json"),JSON.stringify($,null,2)+`
|
|
4
|
+
`),d.writeFileSync(o.join(p,".verify","rule-pack-findings.json"),JSON.stringify(f,null,2)+`
|
|
5
|
+
`);const b=c.filter(e=>e.selected).length,O=c.filter(e=>e.status==="skipped").length;console.log(` rule packs ${c.length} discovered, ${b} selected, ${O} skipped, ${f.length} findings`),l.length&&console.error(` rule packs ${l.length} infrastructure error(s) -- see .verify/rule-packs.json`),process.exit(l.length?2:f.some(e=>e.severity==="P0"||e.severity==="P1")?1:0);export{i as RulePackPhase};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import I from"node:fs";import c from"node:path";import{fileURLToPath as P}from"node:url";const v=process.argv.slice(2),u=(e,n)=>{const i=v.indexOf(e);return i===-1?n:v[i+1]},O=P(new URL("../benchmarks/manifest.json",import.meta.url)),y=c.resolve(u("--manifest",O)),l=u("--results",null),f=u("--oracle",null),r=e=>{console.error(`INVALID BENCHMARK: ${e}`),process.exit(2)},m=(e,n)=>{try{return JSON.parse(I.readFileSync(e,"utf8"))}catch(i){r(`cannot read ${n}: ${i.message}`)}},t=m(y,"manifest");(!Array.isArray(t.fixtures)||t.fixtures.length<2)&&r("manifest needs at least two fixtures"),t.fixtures.some(e=>typeof e?.name!="string"||e.hidden_seed!==!0||!Number.isInteger(e.oracle_findings)||e.oracle_findings<0)&&r("fixtures need hidden judge-only oracle metadata"),(!t.execution?.fresh_fixture||!t.execution?.reset_history||!t.execution?.proposer_cannot_read_seeds||!t.execution?.oracle_is_judge_only)&&r("isolation requirements are incomplete");const N=["precision","recall","calibration_error","invalid_run_detection","reproduction_rate","time_to_counterexample","cost_per_accepted_finding"];(!t.gates||typeof t.gates!="object")&&r("manifest.gates is required");for(const e of N)(!Number.isFinite(t.gates[e])||t.gates[e]<0)&&r(`gate ${e} must be non-negative`);const j=new Map(t.fixtures.map(e=>[e.name,e]));let d=t.fixtures.map(e=>({name:e.name,oracle_findings:e.oracle_findings}));if(f){const e=m(c.resolve(f),"oracle");(!e?.judge_only||!Array.isArray(e.fixtures))&&r("external oracle must be judge_only with fixture rows"),d=e.fixtures}const o=new Map(d.map(e=>[e.name,e]));d.some(e=>!j.has(e.name)||!Number.isInteger(e.oracle_findings)||e.oracle_findings<0)&&r("oracle contains an unknown fixture or invalid finding count"),(d.length!==t.fixtures.length||t.fixtures.some(e=>!o.has(e.name)))&&r("oracle must contain exactly one row per manifest fixture");const a=l?m(c.resolve(l),"results"):t.fixtures.map(e=>({fixture:e.name,predicted_findings:o.get(e.name)?.oracle_findings??0,reproduced:(o.get(e.name)?.oracle_findings??0)>0,invalid_run_detected:!0,confidence:(o.get(e.name)?.oracle_findings??0)>0?.9:.05,time_to_counterexample:(o.get(e.name)?.oracle_findings??0)>0?12:0,cost:(o.get(e.name)?.oracle_findings??0)>0?6:2}));(!Array.isArray(a)||a.length!==t.fixtures.length)&&r("results need exactly one observation per fixture"),a.some(e=>!j.has(e?.fixture)||!Number.isInteger(e.predicted_findings)||e.predicted_findings<0||typeof e.reproduced!="boolean"||typeof e.invalid_run_detected!="boolean"||!Number.isFinite(e.confidence)||!Number.isFinite(e.time_to_counterexample)||e.time_to_counterexample<0||!Number.isFinite(e.cost))&&r("malformed benchmark observation");let _=0,g=0,s=0,w=0,p=0,x=0,A=0,M=0;for(const e of a){const n=o.get(e.fixture).oracle_findings>0,i=e.predicted_findings>0;i&&(g++,p+=e.predicted_findings),n&&(s++,M+=e.time_to_counterexample),n&&i&&_++,n&&i&&e.reproduced&&w++,A+=Math.abs(e.confidence-(n?1:0)),x+=e.cost}const k={precision:g?_/g:1,recall:s?_/s:1,calibration_error:A/a.length,invalid_run_detection:a.every(e=>e.invalid_run_detected)?1:0,reproduction_rate:s?w/s:1,time_to_counterexample:s?M/s:0,cost_per_accepted_finding:p?x/p:x},h={};for(const e of N){const n=t.gates[e];if(n===void 0)continue;const i=k[e],F=e==="calibration_error"||e==="time_to_counterexample"||e==="cost_per_accepted_finding";h[e]={actual:i,threshold:n,pass:F?i<=n:i>=n}}const b=Object.entries(h).filter(([,e])=>!e.pass).map(([e])=>e);console.log(JSON.stringify({schema_version:1,benchmark_id:t.benchmark_id??"unnamed",status:b.length?"failed":"passed",valid:!0,manifest:y,results:l?c.resolve(l):"deterministic-control-observations",fixtures:t.fixtures.map(({name:e,hidden_seed:n})=>({name:e,hidden_seed:n})),oracle:{judge_only:t.execution.oracle_is_judge_only,external:!!f,hidden_seed_count:t.fixtures.length},metrics:k,gates:t.gates,checks:h,failed_gates:b},null,2)),process.exit(b.length?1:0);
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import m from"node:fs";import p from"node:path";import{pathToFileURL as T}from"node:url";const o=Object.freeze({STATIC:"static",RUNTIME:"runtime",BOTH:"both"}),k=new Set(Object.values(o)),A=/^[A-Za-z0-9][A-Za-z0-9._:@/-]{0,127}$/,v=/(?:api[_-]?key|token|secret|password|passwd|bearer|sk_(?:live|test)|pk_(?:live|test))/i,E="<redacted>";function f(t,r){return t===void 0?r:typeof t=="string"&&A.test(t)&&!v.test(t)?t:E}function d(t,r){return{id:f(r?.id,f(t.replace(/\.mjs$/,""),"unknown")),version:f(r?.version,"unversioned"),phase:f(r?.phase,o.STATIC)}}function S(t,r){const a=d(t,r),e=r?.phase===void 0?o.STATIC:r.phase;if(typeof e!="string"||!k.has(e))throw new Error("invalid phase; expected static, runtime, or both");if((e===o.STATIC||e===o.BOTH)&&typeof r?.run!="function")throw new Error(`missing run({ root }) for ${e} phase`);if((e===o.RUNTIME||e===o.BOTH)&&typeof r?.runtime!="function")throw new Error(`missing runtime(context) for ${e} phase`);return{...a,phase:e,runtime:r.runtime}}async function R(t){const r=p.join(t,".verify","rules");if(!m.existsSync(r))return{packs:[],skipped:[],errors:[],infrastructureErrors:[]};const a=[],e=[],i=[];for(const s of m.readdirSync(r).filter(n=>n.endsWith(".mjs")).sort()){const n=p.relative(t,p.join(r,s));let h=d(s);try{const u=await import(T(p.join(r,s)).href),l=u.default??u;h=d(s,l);const c=S(s,l);if(c.phase===o.STATIC){e.push({id:c.id,version:c.version,phase:c.phase,file:n,status:"skipped",selected:!1,skipReason:"phase-static"});continue}a.push({...c,file:n,status:"loaded",selected:!0})}catch(u){i.push({kind:"rule-pack.infrastructure",...h,file:n,error:String(u).split(`
|
|
2
|
+
`)[0]})}}return{packs:a,skipped:e,errors:i,infrastructureErrors:i}}async function b(t,r){const a=[];for(const e of t.packs)if(!e.disabled)try{const i=await e.runtime(r),s=Array.isArray(i)?i:i?.findings??[];for(const n of s)a.push({...n,pack:e.id,packVersion:e.version,packPhase:e.phase,evidenceLevel:"runtime"})}catch(i){e.disabled=!0,e.status="error";const s={kind:"rule-pack.infrastructure",stage:"runtime",id:e.id,version:e.version,phase:e.phase,file:e.file,error:String(i).split(`
|
|
3
|
+
`)[0]};e.error=s.error,t.errors.some(n=>n.id===s.id&&n.stage===s.stage)||t.errors.push(s)}return a}export{o as RulePackPhase,R as loadRuntimePacks,b as runRuntimePacks};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import m from"node:fs";const l=process.argv[2];l||(console.error("report input required"),process.exit(2));let r;try{r=JSON.parse(m.readFileSync(l,"utf8"))}catch(e){console.error(`invalid report: ${String(e).split(`
|
|
3
|
+
`)[0]}`),process.exit(2)}const s=e=>{console.error(`invalid report: ${e}`),process.exit(2)};(!r||typeof r!="object"||Array.isArray(r))&&s("expected an object"),r.routes!==void 0&&!Array.isArray(r.routes)&&s("routes must be an array"),r.inventory!==void 0&&(!r.inventory||typeof r.inventory!="object"||Array.isArray(r.inventory))&&s("inventory must be an object"),r.inventory?.routes!==void 0&&!Array.isArray(r.inventory.routes)&&s("inventory.routes must be an array"),r.sweep!==void 0&&(!r.sweep||typeof r.sweep!="object"||Array.isArray(r.sweep))&&s("sweep must be an object"),r.roles!==void 0&&!Array.isArray(r.roles)&&s("roles must be an array"),r.sweep?.roles!==void 0&&!Array.isArray(r.sweep.roles)&&s("sweep.roles must be an array"),r.feature_flags!==void 0&&!Array.isArray(r.feature_flags)&&s("feature_flags must be an array"),r.featureFlags!==void 0&&!Array.isArray(r.featureFlags)&&s("featureFlags must be an array"),r.observed_branches!==void 0&&!Array.isArray(r.observed_branches)&&s("observed_branches must be an array"),r.branches!==void 0&&!Array.isArray(r.branches)&&s("branches must be an array");const a=e=>typeof e=="string"&&e.startsWith("/")?e.split(/[?#]/,1)[0].replace(/\/\d+(?=\/|$)/g,"/:id"):null,u=(e,o)=>{e.some(t=>!t||typeof t!="object"||Array.isArray(t))&&s(`${o} entries must be objects`)},y=(e,o)=>{e.some(t=>!t||typeof t!="string"&&(typeof t!="object"||Array.isArray(t)))&&s(`${o} entries must be strings or objects`)};y(r.roles??[],"roles"),y(r.sweep?.roles??[],"sweep.roles"),u(r.routes??[],"routes"),u(r.inventory?.routes??[],"inventory.routes");const n=Array.isArray(r.observed_branches)?r.observed_branches:Array.isArray(r.branches)?r.branches:[];u(n,"observed branches");const A=[...new Set([...(r.routes??[]).map(e=>a(e.route??e.path)),...(r.inventory?.routes??[]).map(e=>a(e.path)),...n.map(e=>a(e.route))].filter(Boolean))].sort(),v=[...new Set((r.roles??r.sweep?.roles??[]).map(e=>typeof e=="string"?e:e.name).filter(Boolean))].sort(),h=[...new Set((r.feature_flags??r.featureFlags??["default"]).filter(e=>typeof e=="string"))].sort(),x=["2xx","4xx","5xx"],p=[...new Set([...v,...n.map(e=>e.role).filter(e=>typeof e=="string"&&e)])].sort(),d=[...new Set(["default",...h,...n.map(e=>e.feature_flag).filter(e=>typeof e=="string"&&e)])].sort(),j=[...new Set(["json",...n.map(e=>e.storage_shape).filter(e=>typeof e=="string"&&e)])].sort(),S=[...new Set(["read",...n.map(e=>e.prior_action).filter(e=>typeof e=="string"&&e)])].sort(),b=e=>[e.role,e.route,e.feature_flag,e.storage_shape,e.response_class,e.prior_action].join("|"),w=new Set(n.map(e=>b({role:e.role??"default",route:a(e.route)??"/",feature_flag:e.feature_flag??"default",storage_shape:e.storage_shape??"json",response_class:e.response_class??"2xx",prior_action:e.prior_action??"read"}))),i=[];for(const e of A)for(const o of p.length?p:["default"])for(const t of d.length?d:["default"])for(const g of j)for(const _ of S)for(const f of x){const c={role:o,route:e,feature_flag:t,storage_shape:g,response_class:f,prior_action:_};i.push({state:c,utility:(f==="5xx"?3:f==="4xx"?2:1)*(o==="default"?1:1.2),observed:w.has(b(c))})}i.sort((e,o)=>o.utility-e.utility||e.state.route.localeCompare(o.state.route));const $=n.filter(e=>e.success===!0&&a(e.route)).map(e=>({name:`observed-${a(e.route).replace(/[^a-z0-9]+/gi,"-").replace(/^-|-$/g,"")||"root"}`,route:a(e.route),role:typeof e.role=="string"?e.role:"default",source:"observed-branch"}));console.log(JSON.stringify({schema_version:1,source:l,observed_identifiers_only:!0,observed_branch_count:n.length,generated:i.slice(0,200),frontier_count:i.length,frontier:i.slice(0,50),journey_candidates:$},null,2));
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import n from"node:fs";import a from"node:crypto";const s=process.argv[2];s||(console.error("trace input required"),process.exit(2));let t;try{t=JSON.parse(n.readFileSync(s,"utf8"))}catch(e){console.error(`invalid trace: ${String(e).split(`
|
|
3
|
+
`)[0]}`),process.exit(2)}const o=/(authorization|cookie|set-cookie|bearer|token|secret|password|api[_-]?key|email|phone|ssn|user[_-]?id)/i,c=JSON.stringify(t);o.test(c)&&(console.error("trace rejected by privacy policy"),process.exit(1));const i=e=>typeof e=="string"?e.split("?")[0].replace(/\/([0-9a-f]{8,}|\d+)(?=\/|$)/gi,"/:id").replace(/\/[A-Za-z0-9_-]{16,}(?=\/|$)/g,"/:id"):null,p=Array.isArray(t)?t:Array.isArray(t.events)?t.events:[t],r=p.map(e=>({route_template:i(e.route??e.url),endpoint_template:i(e.endpoint??e.path),method:e.method??null,response_class:Number.isFinite(e.status)?`${Math.floor(e.status/100)}xx`:e.response_class??null,timing_bucket_ms:Number.isFinite(e.duration_ms)?e.duration_ms<100?100:e.duration_ms<500?500:e.duration_ms<2e3?2e3:5e3:null,transition_index:Number.isInteger(e.transition_index)?e.transition_index:null})),l=a.createHash("sha256").update(JSON.stringify(r)).digest("hex");console.log(JSON.stringify({schema_version:1,accepted:!0,raw_values:!1,retention_days:7,tenant_partitioned:!0,schema_fingerprint:`sha256:${l}`,traces:r},null,2));
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import k from"node:fs";import l from"node:path";import{createRequire as H}from"node:module";import{fileURLToPath as L,pathToFileURL as M}from"node:url";const W=/(authorization|cookie|set-cookie|bearer|token|secret|password|api[_-]?key|email|phone|ssn|user[_-]?id)/i;function j(t){return typeof t=="string"?t.split("?")[0].replace(/\/([0-9a-f]{8,}|\d+)(?=\/|$)/gi,"/:id").replace(/\/[A-Za-z0-9_-]{16,}(?=\/|$)/g,"/:id"):null}function I(t){return Number.isFinite(t)?t<100?100:t<500?500:t<2e3?2e3:5e3:null}function J(t){return Number.isFinite(t)?`${Math.floor(t/100)}xx`:null}function E(t){return/prod|production/i.test(String(t??""))}function q(t,e){return t==null?!0:!Array.isArray(e)||!e.length?!1:e.some(n=>typeof n=="string"&&n.length>0&&(t===n||t.startsWith(n)))}function U({target:t,isolated:e,policy:n}){return e?E(t)?{ok:!1,reason:"replay target must be explicitly isolated and non-production"}:!n||typeof n!="object"?{ok:!1,reason:"no shadow replay policy available"}:n.replay?.isolated!==!0||n.replay?.production!==!1?{ok:!1,reason:"policy does not declare replay.isolated === true and replay.production === false"}:{ok:!0}:{ok:!1,reason:"replay target must be explicitly isolated and non-production"}}function z(t,e){const n=[];for(const o of t)o.route_template!=null&&!q(o.route_template,e.allow_routes)&&n.push({kind:"route",template:o.route_template}),o.endpoint_template!=null&&!q(o.endpoint_template,e.allow_endpoints)&&n.push({kind:"endpoint",template:o.endpoint_template});return n}function B(t,{ids:e={}}={}){const n=[],o=new Map,c=[],y=[...t].sort((r,p)=>(r.transition_index??0)-(p.transition_index??0));for(const r of y){const p={route_template:r.route_template??null,endpoint_template:r.endpoint_template??null,method:r.method??null,transition_index:r.transition_index??null};if(r.method&&r.method!=="GET"){n.push({...p,reason:"non-get"});continue}if(!r.route_template){n.push({...p,reason:"no-route-template"});continue}let f=r.route_template;if(f.includes(":id")){const u=e[f];if(!u){n.push({...p,reason:"unresolved-id"});continue}f=f.replace(":id",u)}const s=`${r.transition_index??0}\0${r.route_template}`;o.has(s)||(o.set(s,{transition_index:r.transition_index??0,route_template:r.route_template,resolvedRoute:f,expectations:[]}),c.push(s)),o.get(s).expectations.push({endpoint_template:r.endpoint_template??null,expected_class:r.response_class??null,expected_bucket:r.timing_bucket_ms??null})}return{steps:c.map(r=>o.get(r)).filter(r=>r.expectations.length>0),skipped:n}}function Y(t,e){return!t||!e||t===e?{drift:!1,gating:!1}:{drift:!0,gating:t==="2xx"&&(e==="4xx"||e==="5xx")}}function V(t){const e={};if(!t||typeof t!="string")return e;for(const n of t.split(",")){const o=n.indexOf("=");o<=0||(e[n.slice(0,o).trim()]=n.slice(o+1).trim())}return e}function d(t){return{status:"invalid",replayed:[],skipped:[],drift:[],privacy:{raw_values:!1},reason:t}}const D=t=>t?.chromium?t:t?.default?.chromium?t.default:null;async function Z(t){for(const e of["playwright","@playwright/test"])try{const n=H(l.join(t,"package.json")),o=D(await import(M(n.resolve(e)).href));if(o)return o}catch{}return null}async function K(t){const e=l.dirname(l.dirname(L(import.meta.url))),n=[t,l.join(e,".playwright"),process.cwd(),...process.env.PLAYWRIGHT_HOME?[l.resolve(process.env.PLAYWRIGHT_HOME)]:[]];for(const o of n){const c=await Z(o);if(c)return c}try{const o=D(await import("playwright"));if(o)return o}catch{}return null}async function Q(t,e,{repo:n,authPath:o,navTimeoutMs:c}){const y=await K(n);if(!y)return{report:d("playwright not reachable (checked repo, <skill>/.playwright, cwd, PLAYWRIGHT_HOME) -- install with `npm i -D @playwright/test && npx playwright install chromium`"),code:2};let x;try{x=await y.chromium.launch()}catch(g){return{report:d("could not launch chromium -- "+String(g).split(`
|
|
3
|
+
`)[0]),code:2}}const r=(()=>{try{return new URL(t).origin}catch{return null}})(),p=[],f=[],s=[...e.skipped];let u=0;try{const g=await x.newContext(o&&k.existsSync(o)?{storageState:o}:{}),m=await g.newPage();for(const h of e.steps){const S=new Map,O=[],R=i=>{S.set(i.url(),Date.now())},a=i=>{try{const F=new URL(i.url());if(F.origin!==r)return;const w=S.get(i.url());O.push({method:i.request().method(),pathname:F.pathname,status:i.status(),duration_ms:w?Date.now()-w:null})}catch{}};m.on("request",R),m.on("response",a);let A=!1;try{await m.goto(t.replace(/\/$/,"")+h.resolvedRoute,{waitUntil:"domcontentloaded",timeout:c}),await m.waitForLoadState("load",{timeout:c}).catch(()=>{}),await m.waitForTimeout(500)}catch{A=!0}m.off("request",R),m.off("response",a);for(const i of h.expectations){if(A){s.push({route_template:h.route_template,endpoint_template:i.endpoint_template,method:"GET",transition_index:h.transition_index,reason:"navigation-failed"});continue}const F=new Set,w=O.find(($,P)=>$.method==="GET"&&!F.has(P)&&j($.pathname)===i.endpoint_template),T=!!w,N=T?J(w.status):null,G=T?I(w.duration_ms):null;if(p.push({route_template:h.route_template,endpoint_template:i.endpoint_template,method:"GET",transition_index:h.transition_index,expected_class:i.expected_class,observed_class:N,expected_bucket:i.expected_bucket,observed_bucket:G,observed:T}),T){const{drift:$,gating:P}=Y(i.expected_class,N);$&&(f.push({endpoint_template:i.endpoint_template,method:"GET",expected_class:i.expected_class,observed_class:N,expected_bucket:i.expected_bucket,observed_bucket:G}),P&&u++)}}}await g.close()}finally{await x.close()}const b={status:"measured",replayed:p,skipped:s,drift:f,privacy:{raw_values:!1}},v=u>0?1:0;return{report:b,code:v}}function _(t,e,n){const o=t.indexOf("--"+e);return o>=0?t[o+1]:n}function X(t,e){return t.includes("--"+e)}async function C(){const t=process.argv.slice(2),e=t[0]&&!t[0].startsWith("--")?t[0]:"",n=X(t,"isolated"),o=_(t,"trace");o||((!n||E(e))&&(console.error("replay target must be explicitly isolated and non-production"),process.exit(2)),console.log(JSON.stringify({accepted:!0,target:e})),process.exit(0));const c=l.resolve(_(t,"repo",process.cwd())),y=_(t,"policy"),x=_(t,"ids"),r=_(t,"auth"),p=_(t,"out"),f=Number(_(t,"nav-timeout","30000")),s=(a,A)=>{const i=JSON.stringify(a,null,2);W.test(JSON.stringify(a))&&(console.error("shadow-replay: report failed the privacy check -- refusing to write it"),process.exit(2)),p?k.writeFileSync(l.resolve(p),i+`
|
|
4
|
+
`):console.log(i),process.exit(A)};let u;try{u=JSON.parse(k.readFileSync(l.resolve(o),"utf8"))}catch(a){s(d(`could not read/parse --trace ${o}: ${String(a).split(`
|
|
5
|
+
`)[0]}`),2);return}const b=Array.isArray(u?.traces)?u.traces:Array.isArray(u)?u:null;if(!b){s(d("trace has no traces[] array"),2);return}if(!n){s(d("replay target must be explicitly isolated and non-production"),2);return}if(E(e)){s(d("replay target must be explicitly isolated and non-production"),2);return}if(!y){s(d("no --policy given and no shadow replay policy could be located"),2);return}let v;try{v=JSON.parse(k.readFileSync(l.resolve(y),"utf8"))}catch(a){s(d(`could not read/parse --policy ${y}: ${String(a).split(`
|
|
6
|
+
`)[0]}`),2);return}const g=U({target:e,isolated:n,policy:v});if(!g.ok){s(d(g.reason),2);return}const m=z(b,v);if(m.length){s(d(`trace uses templates not covered by policy allow lists: ${m.slice(0,5).map(a=>`${a.kind}:${a.template}`).join(", ")}`),2);return}const h=B(b,{ids:V(x)});if(h.steps.length===0){s({...d("zero replayable entries"),skipped:h.skipped},2);return}const S=r?l.isAbsolute(r)?r:l.join(c,r):null,{report:O,code:R}=await Q(e,h,{repo:c,authPath:S,navTimeoutMs:f});s(O,R)}const tt=L(import.meta.url),et=(()=>{try{return!!process.argv[1]&&k.realpathSync(l.resolve(process.argv[1]))===k.realpathSync(tt)}catch{return!1}})();et&&C().catch(t=>{console.error("shadow-replay: "+(t?.stack??t)),process.exit(2)});export{W as FORBIDDEN,I as bucketOf,U as checkPolicy,z as checkTraceAllowed,J as classOf,Y as evaluateDrift,E as isProdLike,V as parseIds,B as planReplay,q as prefixAllowed,j as templateOf};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import o from"node:fs";import P from"node:os";import t from"node:path";import C from"node:http";import{spawn as N,spawnSync as F}from"node:child_process";import{fileURLToPath as G}from"node:url";const w=t.resolve(t.dirname(G(import.meta.url)),".."),u=process.argv.slice(2),n={hours:2,checkpoint:5,out:null};for(let e=0;e<u.length;e++){const r=u[e];if(!["--hours","--checkpoint-minutes","--out"].includes(r)||!u[e+1])throw new Error("usage: node bin/soak-marathon.mjs [--hours 2] [--checkpoint-minutes 5] [--out NEW_DIRECTORY]");const l=u[++e];r==="--out"?n.out=t.resolve(l):n[r==="--hours"?"hours":"checkpoint"]=Number(l)}if(![n.hours,n.checkpoint].every(e=>Number.isFinite(e)&&e>0))throw new Error("hours and checkpoint-minutes must be positive finite numbers");const s=n.out??o.mkdtempSync(t.join(P.tmpdir(),"qa-marathon-soak-"));n.out&&o.mkdirSync(s);const i=t.join(s,"fixture");o.mkdirSync(t.join(i,"app"),{recursive:!0}),o.writeFileSync(t.join(i,".gitignore"),`.verify/
|
|
3
|
+
`),o.writeFileSync(t.join(i,"package.json"),JSON.stringify({name:"qa-soak-fixture",private:!0})),o.writeFileSync(t.join(i,"next.config.mjs"),`// Router convention marker for the source fixture; served by the isolated HTTP server.
|
|
4
|
+
export default {};
|
|
5
|
+
`),o.writeFileSync(t.join(i,"app","page.tsx"),`export default function Home() { return <main><h1>Verification fixture</h1><p>A read-only page for repeated runtime measurement.</p></main>; }
|
|
6
|
+
`);for(const e of[["init","-q"],["add","."],["-c","user.name=QA Fixture","-c","user.email=fixture@example.invalid","commit","-qm","fixture: read-only soak target"]]){const r=F("git",["-C",i,...e],{encoding:"utf8"});if(r.status!==0)throw new Error(`fixture git initialization failed: ${r.stderr}`)}const H=`<!doctype html><html lang="en"><head><meta name="viewport" content="width=device-width, initial-scale=1"><title>Verification fixture</title><style>
|
|
7
|
+
html{color-scheme:light dark}body{font:18px/1.6 system-ui;margin:0;padding:32px}main{max-width:760px;margin:auto}a{display:inline-block;min-height:44px;padding:12px}
|
|
8
|
+
</style></head><body><main><h1>Verification fixture</h1>
|
|
9
|
+
<p>This isolated read-only application exercises the real verification pipeline over a wall-clock budget. It contains no credentials, external services, writes, authentication, or shared application data.</p>
|
|
10
|
+
<p>Repeated browser visits provide fresh observations of a stable page. Static and runtime artifacts remain separate, and every result carries its measured denominator.</p>
|
|
11
|
+
<nav aria-label="Main"><a href="/">Home</a></nav></main></body></html>`;let v=0;const a=C.createServer((e,r)=>{if(v++,e.method!=="GET"&&e.method!=="HEAD"){r.writeHead(405),r.end();return}r.writeHead(e.url==="/"?200:404,{"Content-Type":"text/html","Cache-Control":"no-store"}),r.end(e.method==="HEAD"?"":H)});await new Promise((e,r)=>{a.once("error",r),a.listen(0,"127.0.0.1",e)});const d=`http://127.0.0.1:${a.address().port}`,h=o.openSync(t.join(s,"marathon.log"),"w",384),S=Date.now(),O=[t.join(w,"bin","qa.mjs"),"marathon",i,"--base",d,"--hours",String(n.hours),"--checkpoint-minutes",String(n.checkpoint),"--widths","390,1440","--parallel","1","--no-install","--fresh"],c=N(process.execPath,O,{cwd:w,stdio:["ignore",h,h],detached:process.platform!=="win32"});o.closeSync(h);let x=!1;function f(e){try{process.platform==="win32"?c.kill(e):process.kill(-c.pid,e)}catch{}}const m=()=>{x=!0,f("SIGTERM")};process.on("SIGINT",m),process.on("SIGTERM",m);const D=setTimeout(()=>f("SIGKILL"),n.hours*36e5+12e4);o.writeFileSync(t.join(s,"process.json"),JSON.stringify({driverPid:process.pid,marathonPid:c.pid,repo:i,base:d,startedAt:new Date(S).toISOString(),requestedHours:n.hours},null,2)),process.stdout.write(`Soak running: ${s}
|
|
12
|
+
PID ${c.pid}; ${n.hours} hours; ${d}
|
|
13
|
+
`);let g;try{g=await new Promise(e=>{c.once("error",r=>e({code:2,error:r.message})),c.once("exit",(r,l)=>e({code:r,signal:l}))})}finally{clearTimeout(D),f("SIGKILL"),a.closeAllConnections(),await new Promise(e=>a.close(e)),process.off("SIGINT",m),process.off("SIGTERM",m)}const j=Date.now()-S;function y(e){try{return JSON.parse(o.readFileSync(e,"utf8"))}catch{return null}}const k=y(t.join(i,".verify","marathon.json")),p=t.join(i,".verify","runs"),b=o.existsSync(p)?o.readdirSync(p).map(e=>({id:e,meta:y(t.join(p,e,"run.json")),sweep:y(t.join(p,e,"sweep.json"))})).filter(e=>e.meta):[],I=b.filter(e=>e.meta.mode==="runtime"&&e.sweep),R=b.filter(e=>e.meta.graph?.["nodes.jsonl"]&&e.meta.graph?.["edges.jsonl"]),L=(k?.commandLog??[]).filter(e=>e.cmd?.startsWith("qa verify ")),q=n.hours*60>n.checkpoint*2?3:1,T={fullBudget:j>=n.hours*36e5-1e3,cleanExit:g.code===0&&!x,finalized:k?.phase==="done",repeatedRuntime:I.length>=q,graphProduced:R.length>0,serverClosed:!a.listening,coordinatorLockReleased:!o.existsSync(t.join(i,".verify","marathon.lock"))},E={requestedHours:n.hours,elapsedMs:j,result:g,checks:T,requests:v,runtimeRuns:I.length,graphRuns:R.length,verifyCalls:L.length,fixture:i,artifactsRetained:!0,unmeasured:["mutation/fixture cleanup (read-only target)","authenticated personas","business invariants","production-scale memory/load"]};o.writeFileSync(t.join(s,"summary.json"),JSON.stringify(E,null,2)+`
|
|
14
|
+
`),process.stdout.write(JSON.stringify(E,null,2)+`
|
|
15
|
+
`),process.exitCode=Object.values(T).every(Boolean)?0:2;
|
package/bin/sweep.mjs
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from"node:fs";import os from"node:os";import path from"node:path";import{spawnSync}from"node:child_process";import{createRequire}from"node:module";import{createHash,randomUUID}from"node:crypto";import{pathToFileURL}from"node:url";import{loadRuntimePacks,runRuntimePacks}from"./runtime-packs.mjs";import{requireLane,laneRequiresOracle}from"../lib/qa/lanes.mjs";import{loadPersonas}from"../lib/qa/auth/personas.mjs";import{createPersonaCoordinator}from"../lib/qa/auth/coordinator.mjs";import{restoreSessionStorage}from"../lib/qa/auth/launcher.mjs";import{findPlaywright}from"../lib/qa/doctor.mjs";import{gradeIntegrity,surfaceFingerprint}from"../lib/qa/integrity.mjs";import{cellsOf,routeCounts}from"../lib/qa/reports.mjs";import{writeCaptureReport}from"../lib/qa/capture-report.mjs";import{forcedStateTargets,judgeForcedStateColumns,loadReadOnlyPosts,planForcedStateCells}from"../lib/qa/forced-states.mjs";import{hasConcreteRepeat}from"../lib/qa/request-evidence.mjs";import{routeAdapter,routingModeFromInventory}from"../lib/qa/routing.mjs";import{emptyInjectionHitErrorState,semanticStateSignature}from"../lib/qa/state-evidence.mjs";import{cellIdentity,journalReplay,nextWorkers,percentile,serverIdentityFrom}from"../lib/qa/sweep-incremental.mjs";import{cleanupRequestAllowed,evaluateMutationPolicy,journeyMutationMetadata,loadMutationDeclaration,requestAllowed,unsafeBaseReason,isBeaconPath,idpBootstrapAllowed}from"../lib/qa/mutation-policy.mjs";const argv=process.argv.slice(2),arg=(e,t)=>{const r=argv.indexOf("--"+e);return r>=0?argv[r+1]:t},flag=e=>argv.includes("--"+e),BASE=(arg("base")??"http://localhost:3000").replace(/\/$/,""),WIDTH=Number(arg("width","1280")),WIDTHS=[...new Set(String(arg("widths",String(WIDTH))).split(",").map(e=>Number(e.trim())).filter(e=>e>0))],HEIGHT=Number(arg("height","900")),SETTLE_MS=Number(arg("settle","1200")),CELL_DELAY_MS=Number(arg("delay","0")),REPO=path.resolve(arg("repo",".")),INVENTORY=arg("inventory",path.join(REPO,".verify","inventory.json")),JSON_OUT=arg("json",path.join(REPO,".verify","sweep.json")),CAPTURE=flag("capture"),CAPTURE_FULL=flag("capture-full"),CAPTURE_DIR=arg("capture-dir",path.join(REPO,".verify","capture")),CAPTURE_MASK='input[type="password"],[data-qa-redact]',captureSlug=(e,t,r)=>`${e||"default"}__${(t==="/"?"root":t).replace(/[^a-zA-Z0-9/_-]/g,"-").replace(/\//g,"_").replace(/^_/,"")}__${r}w`.replace(/_{2,}/g,"__").slice(0,180);function borrowedAuthLease(){if(!flag("auth-lease-fd"))return null;const e=arg("auth-lease-fd",null);if(!/^\d+$/.test(e)||Number(e)<3)throw new Error("--auth-lease-fd must name an inherited descriptor >= 3");const t=JSON.parse(fs.readFileSync(Number(e),"utf8"));if(!t||typeof t.dir!="string"||typeof t.signature!="string"||Object.keys(t).sort().join(",")!=="dir,signature")throw new Error("auth lease descriptor must contain exactly dir and signature");return t}let BORROWED_AUTH_LEASE=null;try{BORROWED_AUTH_LEASE=borrowedAuthLease()}catch(e){console.error("sweep: "+e.message),process.exit(2)}const JOURNAL_PATH=JSON_OUT+".journal.jsonl",FLUSH_EVERY=Math.max(1,Number(arg("flush-every","10"))||10),FLUSH_INTERVAL_MS=15e3;let JOURNAL_REPLAYED=0;if(flag("resume")&&fs.existsSync(JOURNAL_PATH))try{const e=fs.statSync(JOURNAL_PATH),t=fs.existsSync(JSON_OUT),r=t?fs.statSync(JSON_OUT):null;if(!r||r.mtimeMs<e.mtimeMs){const n=t?JSON.parse(fs.readFileSync(JSON_OUT,"utf8")):null,o=fs.readFileSync(JOURNAL_PATH,"utf8").split(`
|
|
3
|
+
`).filter(Boolean).map(f=>{try{return JSON.parse(f)}catch{return null}}).filter(Boolean),{report:a,count:s}=journalReplay(n,o);fs.mkdirSync(path.dirname(JSON_OUT),{recursive:!0});const h=JSON_OUT+".tmp-replay-"+process.pid;fs.writeFileSync(h,JSON.stringify(a,null,2)),fs.renameSync(h,JSON_OUT),JOURNAL_REPLAYED=s}}catch{}(arg("auth")||arg("login")||arg("login-user")||arg("login-pass")||arg("login-path"))&&(console.error("sweep: --auth/--login/--login-user/--login-pass/--login-path were removed -- use the host-harness launcher, invoked by persona name (see verify.auth-launcher.mjs / README auth section)"),process.exit(2));const LANE=arg("lane")?requireLane(arg("lane"),{command:"sweep"}):"public-runtime",JOURNEY_MAX_MUTATIONS=Number(arg("max-mutations","1")),JOURNEY_FIXTURE_NAMESPACE=`qa-sweep-${randomUUID()}`,LAUNCHER_PATH=arg("launcher",path.join(REPO,"verify.auth-launcher.mjs")),LAUNCHER_TIMEOUT_MS=Number(arg("launcher-timeout","60000"));(!Number.isSafeInteger(LAUNCHER_TIMEOUT_MS)||LAUNCHER_TIMEOUT_MS<1e3||LAUNCHER_TIMEOUT_MS>3e5)&&(console.error("sweep: --launcher-timeout must be an integer between 1000 and 300000 milliseconds"),process.exit(2));const ORACLE_PATH=arg("oracle-path","/__qa/identity"),PROBE=path.join(path.dirname(new URL(import.meta.url).pathname),"probe.js"),wait=e=>new Promise(t=>setTimeout(t,e)),AUTH_ROUTE=/\b(login|signin|sign-in|auth)\b/,NAV_MS=Number(arg("nav-timeout","60000"));(!Number.isFinite(NAV_MS)||NAV_MS<=0)&&(console.error(`sweep: --nav-timeout must be a positive number of milliseconds (got ${JSON.stringify(arg("nav-timeout","60000"))})`),process.exit(2));const WARM_MS=Number(arg("warm-timeout","90000")),digest=e=>createHash("sha256").update(e).digest("hex"),stable=e=>Array.isArray(e)?e.map(stable):e&&typeof e=="object"?Object.fromEntries(Object.keys(e).sort().map(t=>[t,stable(e[t])])):e,identity=e=>digest(JSON.stringify(stable(e))),fileIdentity=e=>{try{return"sha256:"+digest(fs.readFileSync(e))}catch{return"missing"}},PUBLIC_BASE=(()=>{try{const e=new URL(BASE);return e.username="",e.password="",e.search="",e.hash="",e.toString().replace(/\/$/,"")}catch{return BASE.replace(/[?#].*$/,"").replace(/\/\//,"//")}})(),journeyFiles=[path.join(REPO,"verify.journeys.mjs"),path.join(REPO,".verify","journeys.mjs")],journeyFile=journeyFiles.find(e=>fs.existsSync(e));async function loadJourneys(){if(!journeyFile||flag("no-journeys"))return{file:journeyFile,journeys:[],error:null};try{const e=await import(pathToFileURL(journeyFile).href),t=e.default??e.journeys??[];return Array.isArray(t)?{file:journeyFile,journeys:t,error:null}:{file:journeyFile,journeys:[],error:"journeys export must be an array"}}catch(e){return{file:journeyFile,journeys:[],error:String(e).split(`
|
|
4
|
+
`)[0].slice(0,200)}}}function writePreflightInvalid(e,t){const r={status:"invalid",base:PUBLIC_BASE,atlasVersion:2,width:WIDTH,started:new Date().toISOString(),lane:LANE,routes:[],unreached:[],roles:[],summary:{},infrastructureErrors:[{phase:e,reason:t}]};fs.mkdirSync(path.dirname(JSON_OUT),{recursive:!0}),fs.writeFileSync(JSON_OUT,JSON.stringify(r,null,2))}function journeyPermit(e,t,r){return evaluateMutationPolicy({command:"sweep-journey",mutate:flag("mutate"),base:BASE,overrideNonLocal:flag("i-know-this-is-not-production"),lane:LANE,declaration:r.declaration,declarationReason:r.reason,flowName:t.flow,maxMutations:JOURNEY_MAX_MUTATIONS,fixtureNamespace:JOURNEY_FIXTURE_NAMESPACE,cleanupCapable:typeof e.cleanup=="function"})}const JOURNEY_LOAD=await loadJourneys(),JOURNEY_METADATA=new Map;for(const e of JOURNEY_LOAD.journeys)try{JOURNEY_METADATA.set(e,journeyMutationMetadata(e))}catch(t){writePreflightInvalid("journey-mutate",String(t.message??t).split(`
|
|
5
|
+
`)[0].slice(0,200)),process.exit(2)}const JOURNEY_PERMITS=new Map;if(flag("mutate")&&!JOURNEY_LOAD.error){const e=loadMutationDeclaration(REPO),t=JOURNEY_LOAD.journeys.filter(r=>JOURNEY_METADATA.get(r)?.mutates);for(const r of t){const n=journeyPermit(r,JOURNEY_METADATA.get(r),e);n.ok||(writePreflightInvalid("journey-mutate",n.reason),process.exit(2)),JOURNEY_PERMITS.set(r,n)}}const unwrap=e=>e?.chromium?e:e?.default?.chromium?e.default:null,SKILL_ROOT=path.dirname(path.dirname(new URL(import.meta.url).pathname));function playwrightHome(){if(process.env.PLAYWRIGHT_HOME)return process.env.PLAYWRIGHT_HOME;try{return fs.accessSync(SKILL_ROOT,fs.constants.W_OK),path.join(SKILL_ROOT,".playwright")}catch{const e=process.env.XDG_CACHE_HOME||path.join(os.homedir(),".cache");return path.join(e,"qa","playwright")}}const SHARED=playwrightHome(),READ_ONLY=loadReadOnlyPosts(REPO,SKILL_ROOT);READ_ONLY.status==="invalid"&&(console.error("sweep: "+READ_ONLY.reason),process.exit(2));const isDeclaredReadOnlyPost=e=>{if(!READ_ONLY.paths.size||!/^POST$/i.test(e.method))return!1;try{return READ_ONLY.paths.has(new URL(e.url,BASE).pathname)}catch{return!1}};async function tryLoad(e){const t=findPlaywright(e,SKILL_ROOT);if(!t)return null;try{const r=createRequire(path.join(t.root,"package.json"));return unwrap(await import(pathToFileURL(r.resolve(t.pkg)).href))}catch{return null}}function run(e,t,r){return spawnSync(e,t,{cwd:r,stdio:"inherit",encoding:"utf8"}).status===0}function installInto(e){return fs.mkdirSync(e,{recursive:!0}),fs.existsSync(path.join(e,"package.json"))||fs.writeFileSync(path.join(e,"package.json"),JSON.stringify({name:"frontend-verify-runtime",private:!0},null,2)+`
|
|
6
|
+
`),console.error(`
|
|
7
|
+
installing @playwright/test into `+e+` (one time)
|
|
8
|
+
`),run("npm",["install","--no-audit","--no-fund","--save-dev","@playwright/test"],e)?(console.error(`
|
|
9
|
+
ensuring the chromium binary is present (shared cache)
|
|
10
|
+
`),run("npx",["--yes","playwright","install","chromium"],e),!0):!1}async function loadPlaywright(){const e=path.resolve(arg("repo",".")),t=await tryLoad(e);if(t)return t;flag("no-install")&&(console.error(`Playwright not found and --no-install was passed.
|
|
11
|
+
npm i -D @playwright/test && npx playwright install chromium`),process.exit(2));const r=arg("install")==="repo"?e:SHARED;installInto(r)||(console.error(`Automatic install failed. Install manually:
|
|
12
|
+
npm i -D @playwright/test && npx playwright install chromium`),process.exit(2));const n=await tryLoad(e);if(n)return n;console.error("Installed Playwright but could not load it from "+r),process.exit(2)}const ROUTING_MODE=routingModeFromInventory(INVENTORY),{routeToUrl,urlToRoute}=routeAdapter(BASE,ROUTING_MODE),dynamicSkipped=[];function routesToSweep(){const e=arg("routes");if(e){const t=e.split(",").map(r=>r.trim()).filter(Boolean);if(fs.existsSync(INVENTORY))try{const r=new Set(JSON.parse(fs.readFileSync(INVENTORY,"utf8")).routes.map(o=>o.path)),n=t.filter(o=>!r.has(o));if(n.length===t.length&&r.size){const o=[...r].slice(0,8).join(", ");console.error(`sweep: --routes matched none of the ${r.size} inventoried route(s): ${n.join(", ")}`),console.error(` inventoried routes include: ${o}${r.size>8?", ...":""}`),console.error(" a route the inventory missed can still be swept -- name it alongside one that exists."),process.exit(2)}n.length&&console.error(`sweep: --routes named ${n.length} route(s) not in the inventory (sweeping them anyway): ${n.join(", ")}`)}catch{}return t}if(fs.existsSync(INVENTORY)){const r=JSON.parse(fs.readFileSync(INVENTORY,"utf8")).routes.map(o=>o.path),n=o=>/[:[]/.test(o);return dynamicSkipped.push(...r.filter(n)),r.filter(o=>!n(o))}return["/"]}function routeKey(e){const t=String(e||"").split("?")[0].split("#")[0];return t.length>1?t.replace(/\/+$/,"")||"/":t||"/"}function routeQueryCounts(){const e=new Map;try{const t=JSON.parse(fs.readFileSync(INVENTORY,"utf8"));for(const r of t.routes)e.set(routeKey(r.path),(r.queries??[]).length)}catch{}return e}const ROUTE_QUERIES=routeQueryCounts(),REQUEST_LOG_CAP=40,ID_UUID=/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i,ID_NUMERIC=/^\d+$/,ID_HEX_LONG=/^[0-9a-f]{9,}$/i,ID_TOKEN_LONG=/^[A-Za-z0-9_-]{17,}$/,ID_LOOKS_LIKE_WORD=/^[a-z-]+$/,ID_PREFIXED_NUM=/^[a-z]{2,12}-\d{2,}$/;function pathTemplate(e){return String(e||"/").split("/").map(t=>t&&(ID_UUID.test(t)||ID_NUMERIC.test(t)||ID_HEX_LONG.test(t)||ID_PREFIXED_NUM.test(t)||ID_TOKEN_LONG.test(t)&&!ID_LOOKS_LIKE_WORD.test(t)?":id":t)).join("/")}function statusClassOf(e){const t=Number(e);return!Number.isFinite(t)||t<=0?"failed":t>=500?"5xx":t>=400?"4xx":t>=300?"3xx":"2xx"}async function serverAlive(){for(const e of[0,1])try{return await fetch(BASE+"/",{signal:AbortSignal.timeout(2e4)}),!0}catch{e||await wait(2e3)}return!1}const playwright=await loadPlaywright(),routes=routesToSweep();routes.length||(console.error("sweep: no routes to visit. Run inventory.mjs first, or pass --routes /,/dashboard."),process.exit(2));const probeSrc=fs.readFileSync(PROBE,"utf8").replace(/^\s*\/\/[^\n]*\n/gm,"").trim(),probeFn=eval("("+probeSrc+")"),PROD=flag("prod"),DARK=flag("dark"),THEME_STORAGE_KEY=String(arg("theme-storage-key","theme"));/^[A-Za-z][A-Za-z0-9_.-]{0,127}$/.test(THEME_STORAGE_KEY)||(console.error("sweep: --theme-storage-key must be a safe localStorage key"),process.exit(2));const EXERCISE=(()=>{if(!flag("exercise"))return 0;const e=arg("exercise"),t=Number(e);return e&&!String(e).startsWith("--")&&Number.isFinite(t)&&t>0?t:40})(),COMPILE_SENSITIVE=/^perf\.(lcp|long-task|layout-shift|layout-thrash)$/;function graded(e){if(PROD)return e;for(const t of e.findings??[])COMPILE_SENSITIVE.test(t.kind)&&(t.severity="P3",t.detail+=" -- measured without --prod; against a dev server this is the compiler, not the app");return e}let MUTATE_PERMIT=null;if(flag("mutate")){const e=o=>{const a={base:BASE,atlasVersion:2,started:new Date().toISOString(),lane:LANE,routes:[],unreached:[],roles:[],summary:{},infrastructureErrors:[{phase:"mutate",reason:o}]};fs.mkdirSync(path.dirname(JSON_OUT),{recursive:!0}),fs.writeFileSync(JSON_OUT,JSON.stringify(a,null,2)),console.error("sweep: --mutate refused -- "+o),process.exit(2)},t=loadMutationDeclaration(REPO);t.ok||e(t.reason);const r=unsafeBaseReason(BASE,{overrideNonLocal:flag("i-know-this-is-not-production")});r&&e(r);const n=evaluateMutationPolicy({command:"sweep",mutate:!0,base:BASE,overrideNonLocal:flag("i-know-this-is-not-production"),lane:LANE,declaration:t.declaration,maxMutations:Number(arg("max-mutations","20")),cleanupCapable:!1,fixtureNamespace:"sweep-"+randomUUID().slice(0,8)});n.ok&&(MUTATE_PERMIT=n)}let browser;try{browser=await playwright.chromium.launch()}catch(e){writePreflightInvalid("browser-launch",String(e).split(`
|
|
13
|
+
`)[0].slice(0,200)),console.error("sweep: could not launch chromium -- "+String(e).split(`
|
|
14
|
+
`)[0]),console.error(" npx playwright install chromium"),process.exit(2)}async function makeContext({statePath:e=null,headers:t=null,session:r=null}={},{serviceWorkers:n=null}={}){const o=await browser.newContext({viewport:{width:WIDTH,height:HEIGHT},...n?{serviceWorkers:n}:{},...typeof e=="string"&&fs.existsSync(e)||e&&typeof e=="object"?{storageState:e}:{},...t&&Object.keys(t).length?{extraHTTPHeaders:t}:{}});o.setDefaultTimeout(NAV_MS),o.setDefaultNavigationTimeout(NAV_MS),await restoreSessionStorage(o,r);for(const a of INIT_SCRIPTS)await o.addInitScript(a);return o}const INIT_SCRIPTS=[];INIT_SCRIPTS.push(()=>{const e=[];Object.defineProperty(window,"__fv_net",{get:()=>e,configurable:!0});const t=(o,a)=>{if(!(typeof a!="string"||a.length>2e6))try{const s=JSON.parse(a),h=Array.isArray(s)?s:["data","items","results","rows","records"].map(f=>s&&s[f]).find(Array.isArray);h&&(o.arrayLen=h.length)}catch{}},r=window.fetch;window.fetch=async function(...o){const a=String(o[0]?.url??o[0]??""),s=(o[1]?.method??o[0]?.method??"GET").toUpperCase(),h=performance.now();try{const f=await r.apply(this,o),m={url:a,method:s,status:f.status,ok:f.ok,start:h,end:performance.now(),via:"fetch"};return e.push(m),f.ok&&(f.headers.get("content-type")??"").includes("json")&&f.clone().text().then(u=>t(m,u)).catch(()=>{}),f}catch(f){throw e.push({url:a,method:s,status:0,ok:!1,error:String(f),aborted:!!(f&&f.name==="AbortError"),start:h,end:performance.now(),via:"fetch"}),f}};const n=window.XMLHttpRequest;if(n){const o=n.prototype.open,a=n.prototype.send;n.prototype.open=function(s,h,...f){return this.__fv={url:String(h),method:String(s).toUpperCase()},o.call(this,s,h,...f)},n.prototype.send=function(...s){const h=this.__fv;return h&&(h.start=performance.now(),this.addEventListener("loadend",()=>{const f={...h,status:this.status,ok:this.status>=200&&this.status<400,end:performance.now(),via:"xhr"};e.push(f);try{f.ok&&(this.responseType===""||this.responseType==="text")&&t(f,this.responseText)}catch{}})),a.apply(this,s)}}}),INIT_SCRIPTS.push(()=>{const e={longTasks:0,longestTaskMs:0,cls:0,lcpMs:null,thrashReads:0};Object.defineProperty(window,"__fv_perf",{get:()=>e,configurable:!0});const t=[];e.flush=()=>t.forEach(([c,T])=>c.takeRecords().forEach(T));const r=(c,T)=>{try{const w=new PerformanceObserver(p=>p.getEntries().forEach(T));w.observe({type:c,buffered:!0}),t.push([w,T])}catch{}};r("longtask",c=>{e.longTasks++,e.longestTaskMs=Math.max(e.longestTaskMs,Math.round(c.duration))});let n=performance.now();const o=()=>{const c=performance.now(),T=c-n;T>50&&e.longTasks===0&&(e.longTasks=1,e.longestTaskMs=Math.max(e.longestTaskMs,Math.round(T))),n=c,setTimeout(o,0)};setTimeout(o,0);let a=null,s=null,h=0;r("layout-shift",c=>{if(c.hadRecentInput)return;const T=c.startTime;s===null||T-s>1e3||T-a>5e3?(a=T,h=c.value):h+=c.value,s=T,e.cls=Math.max(e.cls,h)}),r("largest-contentful-paint",c=>{e.lcpMs=Math.round(c.startTime)});let f=!1;const m=(c,T)=>{const w=c[T];w&&(c[T]=function(...p){return f=!0,w.apply(this,p)})};["setAttribute","appendChild","insertBefore","removeChild","remove"].forEach(c=>m(Element.prototype,c));const u=Element.prototype.getBoundingClientRect;Element.prototype.getBoundingClientRect=function(...c){return f&&(e.thrashReads++,f=!1),u.apply(this,c)};const y=()=>{f=!1,requestAnimationFrame(y)};requestAnimationFrame(y)}),EXERCISE&&INIT_SCRIPTS.push(()=>{const e={count:0};Object.defineProperty(window,"__fv_mut",{get:()=>e,configurable:!0});const t=()=>{if(!document.documentElement){requestAnimationFrame(t);return}new MutationObserver(r=>{e.count+=r.length}).observe(document.documentElement,{childList:!0,subtree:!0,attributes:!0,characterData:!0})};t()});const{personas:PERSONAS}=loadPersonas(REPO),underPrefix=(e,t)=>e===t||e.startsWith(t.replace(/\/$/,"")+"/"),roleOwns=(e,t)=>e.owns.some(r=>underPrefix(t,r))&&!e.excludes.some(r=>underPrefix(t,r)),PERSONA_STATE=new Map;let AUTH_COORDINATOR=null,IDENTITIES=null;if(laneRequiresOracle(LANE)){const e=r=>createPersonaCoordinator({repo:REPO,runId:randomUUID(),signature:r?.signature??LANE,launcherPath:LAUNCHER_PATH,base:BASE,personas:PERSONAS,oraclePath:ORACLE_PATH,timeoutMs:LAUNCHER_TIMEOUT_MS,lease:r});try{AUTH_COORDINATOR=e(BORROWED_AUTH_LEASE)}catch(r){if(!BORROWED_AUTH_LEASE||r?.code!=="ENOENT")throw r;console.error(`sweep: borrowed auth lease ${BORROWED_AUTH_LEASE.dir} is gone -- provisioning under a fresh lease`),BORROWED_AUTH_LEASE=null,AUTH_COORDINATOR=e(null)}const t=await AUTH_COORDINATOR.provision(PERSONAS.map(r=>r.name));t.status!=="measured"&&(console.error(`sweep: persona "${t.persona??"unknown"}" could not be provisioned (${t.status}${t.reason?": "+t.reason:""}) -- required for lane ${LANE}`),AUTH_COORDINATOR.release(),process.exit(2)),IDENTITIES=[];for(const[r,n]of t.states)PERSONA_STATE.set(r,{statePath:n.statePath,headers:n.headers,session:n.session??null}),n.archivable&&IDENTITIES.push(n.archivable)}else for(const e of PERSONAS)PERSONA_STATE.set(e.name,{statePath:null,headers:null,session:null});const primaryState=PERSONA_STATE.get(PERSONAS[0].name)??{statePath:null,headers:null,session:null};let context=await makeContext(primaryState);const report={base:PUBLIC_BASE,atlasVersion:2,width:WIDTH,started:new Date().toISOString(),lane:LANE,routes:[],unreached:[],roles:[],summary:{}};IDENTITIES&&(report.identities=IDENTITIES);const RUNTIME_PACKS=await loadRuntimePacks(REPO);RUNTIME_PACKS.errors.length&&(report.infrastructureErrors=RUNTIME_PACKS.errors,writeReport({force:!0}),console.error("sweep: rule-pack infrastructure error(s) -- see "+JSON_OUT),process.exit(2));let currentRole="default";const sevOf=e=>e.severity??"P2";let journaledRouteCount=0,cellsSinceFlush=0,lastFlushAt=Date.now();function writeReport(e={}){const t=!!e.force;if(report.routes.length>journaledRouteCount){const o=report.routes.slice(journaledRouteCount),a=o.map(s=>JSON.stringify({cell:s.cell??null,record:s})+`
|
|
15
|
+
`).join("");try{fs.mkdirSync(path.dirname(JOURNAL_PATH),{recursive:!0}),fs.appendFileSync(JOURNAL_PATH,a)}catch{}cellsSinceFlush+=o.length,journaledRouteCount=report.routes.length}if(!(t||cellsSinceFlush>=FLUSH_EVERY||Date.now()-lastFlushAt>=FLUSH_INTERVAL_MS))return;fs.mkdirSync(path.dirname(JSON_OUT),{recursive:!0});const n=JSON_OUT+".tmp-"+process.pid;if(fs.writeFileSync(n,JSON.stringify(report,null,2)),fs.renameSync(n,JSON_OUT),cellsSinceFlush=0,lastFlushAt=Date.now(),t){try{fs.rmSync(JOURNAL_PATH,{force:!0})}catch{}journaledRouteCount=report.routes.length}}const measured=new Set;function attachListeners(e){const t=[],r=[],n=[],o=new Set,a=(u,y,c)=>u+" "+y+" "+c,s=(u,y,c,T,w={})=>t.push({kind:u,severity:y,detail:c,at:T??"(page)",...w}),h={errors:0,lastError:null,aborted:0},f={exercising:!1};e.on("console",u=>{const y=u.text();if(u.type()==="warning"&&/preloaded using link preload but not used/.test(y))return s("perf.unused-preload","P3",y.slice(0,200));if(u.type()==="error"&&(h.errors++,h.lastError=y.slice(0,250),!/Failed to load resource/.test(y))){if(/ResizeObserver loop/.test(y))return s("render.resize-observer-loop","P2",y.slice(0,200));if(/ChunkLoadError|Loading chunk \d+ failed/.test(y))return s("delivery.chunk-load","P0",y.slice(0,200));if(/Hydration failed|did not match|Text content does not match|#418|#423|#425/.test(y))return s("render.hydration-mismatch","P0",y.slice(0,250));if(/Content Security Policy|Refused to (load|execute|connect)|Trusted Types?|TrustedHTML/.test(y))return s("security.csp-violation","P1",y.slice(0,200));if(!PROD&&/^Warning: |validateDOMNesting|unique "key" prop|Cannot update a component|Function components cannot be given refs|Fast Refresh/.test(y))return s("console.error","P3",y.slice(0,250)+" -- React dev diagnostic, measured without --prod");s("console.error","P1",y.slice(0,250))}}),e.on("pageerror",u=>{h.errors++,h.lastError=String(u).slice(0,250),s("console.pageerror","P0",String(u).slice(0,250))}),e.on("requestfailed",u=>{const y=u.failure()?.errorText??"unknown";if(/ERR_ABORTED/.test(y)){h.aborted++;return}const c=new URL(u.url()),T=c.origin===new URL(BASE).origin;s(T?"network.requestfailed":"network.requestfailed.external",T?"P1":"P3",u.method()+" "+c.pathname+" -- "+y)}),e.on("request",u=>r.push({url:u.url(),method:u.method(),type:u.resourceType(),start:Date.now(),end:null}));const m=new Set;return e.on("response",async u=>{const y=r.find(p=>p.url===u.url()&&p.end===null);y&&(y.end=Date.now());const c=new URL(u.url());if(c.origin!==new URL(BASE).origin)return;const T=u.request().headers(),w=c.searchParams.has("_rsc")||T["next-router-prefetch"]==="1";n.push({method:u.request().method(),pathname:c.pathname,status:u.status(),prefetch:w,exercising:f.exercising,start:y?y.start:null,end:y?y.end:Date.now()}),u.status()>=400&&(o.add(a(u.request().method(),c.pathname,u.status())),s("network.http-"+u.status(),u.status()>=500?"P0":"P1",u.request().method()+" "+c.pathname+" -> "+u.status()));try{const p=(await u.allHeaders())["set-cookie"];if(!p)return;for(const l of p.split(`
|
|
16
|
+
`)){if(!/samesite=none/i.test(l)||/;\s*secure/i.test(l))continue;const A=l.split("=")[0].trim();m.has(A)||(m.add(A),s("security.cookie-samesite-none-insecure","P1",'cookie "'+A+'" set with SameSite=None and no Secure on '+c.pathname+" -- browsers reject it; whatever it carries is silently absent"))}}catch{}}),{findings:t,requests:r,responses:n,reported:o,netKey:a,push:s,counters:h,marks:f}}async function mergeInPage(e,t){let r=[];try{r=await e.evaluate(()=>(window.__fv_net??[]).map(o=>({...o})))}catch{}const n=new Set(t.requests.map(o=>o.url));for(const o of r){let a=o.url,s=o.url;try{const f=new URL(o.url,BASE);a=f.href,s=f.pathname}catch{}const h=t.netKey(o.method,s,o.status);t.reported.has(h)||(o.status>=400?(t.reported.add(h),t.push("network.http-"+o.status,o.status>=500?"P0":"P1",o.method+" "+s+" -> "+o.status+" (seen in-page)")):!o.ok&&o.status===0&&o.aborted?(t.reported.add(h),t.counters.aborted++):!o.ok&&o.status===0&&(t.reported.add(h),t.push("network.requestfailed","P1",o.method+" "+s+" -- "+(o.error??"failed")+" (seen in-page)"))),n.has(a)||(t.requests.push({url:a,method:o.method,type:"fetch",start:o.start,end:o.end,inPageOnly:!0}),(o.status>0||!o.ok&&o.status===0&&!o.aborted)&&t.responses.push({method:o.method,pathname:s,status:o.status,start:o.start,end:o.end,prefetch:new URL(a).searchParams.has("_rsc")}))}return r}const settleAndLand=async(e,t)=>(await e.waitForLoadState("load",{timeout:15e3}).catch(()=>{}),await e.waitForFunction(()=>{const n=document.querySelector("#root,#__next,[data-reactroot],main")||document.body;return n&&(n.innerText||"").trim().length>1},void 0,{timeout:8e3}).catch(()=>{}),await e.waitForTimeout(SETTLE_MS),urlToRoute(e.url())??t),EXERCISE_SELECTOR="button,a[href],[role=button],[role=tab],[role=menuitem],summary,input[type=checkbox],input[type=radio],select",EXERCISE_DESTRUCTIVE=/delete|remove|destroy|pay|purchase|buy|send|submit|sign ?out|log ?out|archive|unsubscribe|transfer|reset password/i;async function discoverControls(e,t){return e.evaluate(({budget:r,SELECTOR:n,DESTRUCTIVE_SRC:o})=>{const a=l=>{if(!l||l.nodeType!==1)return!1;const A=getComputedStyle(l);if(A.display==="none"||A.visibility==="hidden"||Number(A.opacity)<.02)return!1;const E=l.getBoundingClientRect();return E.width>0&&E.height>0},s=l=>{const A=l.getAttribute("aria-label");if(A&&A.trim())return A.trim();const E=l.getAttribute("aria-labelledby");if(E){const L=E.split(/\s+/).map(N=>{const S=document.getElementById(N);return S?S.innerText:""}).join(" ").trim();if(L)return L}if(l.labels&&l.labels.length){const L=[...l.labels].map(N=>N.innerText).join(" ").trim();if(L)return L}const k=(l.innerText||"").trim();if(k)return k;const O=l.getAttribute("title");if(O&&O.trim())return O.trim();const v=l.getAttribute("alt");return v&&v.trim()?v.trim():l.value?String(l.value).trim():""},h=l=>[...l.classList].sort().join("."),f=l=>{const A=l.getAttribute&&l.getAttribute("data-testid");if(A)return"[data-testid="+A+"]";if(l.id)return"#"+l.id;const E=l.className&&String(l.className).split(/\s+/)[0]||"";return l.tagName.toLowerCase()+(E?"."+E:"")},m=new RegExp(o,"i"),u=l=>{const A=l.tagName.toLowerCase();if(A==="input"&&["submit","image"].includes((l.getAttribute("type")||"").toLowerCase()))return!0;if(A==="button"){const E=(l.getAttribute("type")||"").toLowerCase();if(E==="submit"||!E&&l.closest("form"))return!0}return!1},y=l=>{const A=l.getAttribute("aria-current");return A&&A!=="false"?!0:l.getAttribute("aria-pressed")==="true"||l.getAttribute("aria-selected")==="true"},c=[...document.querySelectorAll(n)].filter(a),T={destructive:0,submit:0,external:0,disabled:0,budget:0,duplicate:0},w=new Set,p=[];for(const l of c){if(l.disabled||l.getAttribute("aria-disabled")==="true"){T.disabled++;continue}if(u(l)){T.submit++;continue}const A=s(l);if(m.test(A)){T.destructive++;continue}const E=l.tagName.toLowerCase();if(E==="a"){if(l.hasAttribute("download")){T.external++;continue}let v=!0;try{v=new URL(l.getAttribute("href"),location.href).origin!==location.origin}catch{v=!0}if(v){T.external++;continue}}const k=h(l),O=E+"|"+A+"|"+k;if(w.has(O)){T.duplicate++;continue}if(w.add(O),p.length>=r){T.budget++;continue}p.push({tag:E,name:A,classes:k,at:f(l),current:y(l),testid:l.getAttribute("data-testid")||null,elementId:l.id||null})}return{candidates:p,skipped:T}},{budget:t,SELECTOR:EXERCISE_SELECTOR,DESTRUCTIVE_SRC:EXERCISE_DESTRUCTIVE.source})}async function waitControlVisible(e,t,r){const n=Date.now()+r;for(;;){const o=await e.evaluateHandle(({d:s,SELECTOR:h})=>{const f=w=>{if(!w||w.nodeType!==1)return!1;const p=getComputedStyle(w);if(p.display==="none"||p.visibility==="hidden"||Number(p.opacity)<.02)return!1;const l=w.getBoundingClientRect();return l.width>0&&l.height>0},m=w=>{const p=w.getAttribute("aria-label");if(p&&p.trim())return p.trim();const l=w.getAttribute("aria-labelledby");if(l){const O=l.split(/\s+/).map(v=>{const L=document.getElementById(v);return L?L.innerText:""}).join(" ").trim();if(O)return O}if(w.labels&&w.labels.length){const O=[...w.labels].map(v=>v.innerText).join(" ").trim();if(O)return O}const A=(w.innerText||"").trim();if(A)return A;const E=w.getAttribute("title");if(E&&E.trim())return E.trim();const k=w.getAttribute("alt");return k&&k.trim()?k.trim():w.value?String(w.value).trim():""},u=w=>[...w.classList].sort().join("."),y=w=>w.length===1?w[0]:null,c=[...document.querySelectorAll(h)].filter(f);if(s.testid){const w=y(c.filter(p=>p.getAttribute("data-testid")===s.testid));if(w)return w}if(s.elementId){const w=y(c.filter(p=>p.id===s.elementId));if(w)return w}const T=c.filter(w=>w.tagName.toLowerCase()===s.tag);return T.find(w=>m(w)===s.name&&u(w)===s.classes)||y(T.filter(w=>s.name&&m(w)===s.name||s.classes&&u(w)===s.classes))||null},{d:t,SELECTOR:EXERCISE_SELECTOR}),a=o.asElement();if(a)return a;if(await o.dispose().catch(()=>{}),Date.now()>=n)return null;await wait(100)}}async function snapshotControl(e,t,r){const n=e.url(),[o,a,s,h,f]=await Promise.all([e.evaluate(()=>window.__fv_mut?window.__fv_mut.count:0).catch(()=>0),e.evaluate(()=>document.querySelectorAll('dialog[open],[role="dialog"],[aria-modal="true"]').length).catch(()=>0),e.evaluate(()=>{const m=document.activeElement;if(!m||m===document.body)return null;const u=m.getAttribute&&m.getAttribute("data-testid");if(u)return"[data-testid="+u+"]";if(m.id)return"#"+m.id;const y=m.className&&String(m.className).split(/\s+/)[0]||"";return m.tagName.toLowerCase()+(y?"."+y:"")}).catch(()=>null),t.evaluate(m=>({expanded:m.getAttribute("aria-expanded"),pressed:m.getAttribute("aria-pressed"),selected:m.getAttribute("aria-selected"),checked:m.getAttribute("aria-checked")??("checked"in m?String(m.checked):null)})).catch(()=>null),t.evaluate(m=>document.activeElement===m).catch(()=>!1)]);return{url:n,dom:o,dialogs:a,active:s,aria:h,selfFocused:f,requests:r.requests.length,errors:r.counters.errors,lastError:r.counters.lastError}}function controlChanged(e,t){const r=!t.selfFocused&&e.active!==t.active;return e.url!==t.url||t.dom>e.dom||t.dialogs!==e.dialogs||r||t.requests>e.requests||t.errors>e.errors||JSON.stringify(e.aria)!==JSON.stringify(t.aria)}async function waitForControlEffect(e,t,r,n,o=800){const a=Date.now()+o;let s=await snapshotControl(e,t,r);for(;!controlChanged(n,s)&&Date.now()<a;)await wait(Math.min(50,a-Date.now())),s=await snapshotControl(e,t,r);return s}async function coveringOverlays(e){return e.evaluate(()=>{const t=window.innerWidth,r=window.innerHeight;if(!t||!r)return 0;let n=0;for(const o of document.elementsFromPoint(Math.floor(t/2),Math.floor(r/2))){const a=getComputedStyle(o);if(a.position!=="fixed"&&a.position!=="absolute"||a.pointerEvents==="none")continue;const s=o.getBoundingClientRect();s.width>=t*.8&&s.height>=r*.8&&n++}return n}).catch(()=>0)}function journalExerciseProgress(e,t,r){const n=`sweep: exercise ${e} ${t}/${r} controls completed`;console.error(n);try{fs.appendFileSync(JOURNAL_PATH,JSON.stringify({progress:{phase:"exercise",route:e,completed:t,total:r,at:new Date().toISOString()}})+`
|
|
17
|
+
`)}catch{}}async function exerciseCell(e,t,r,n){const o=await discoverControls(e,n),a=[];let s=0,h=0,f=0,m=0,u=0;const y=async()=>{await e.goto(routeToUrl(t),{waitUntil:"domcontentloaded",timeout:NAV_MS}).catch(()=>{}),await settleAndLand(e,t)};for(const c of o.candidates){const T=await waitControlVisible(e,c,1500);if(!T){a.push({kind:"interact.never-appeared",severity:"P2",at:c.at,detail:'control "'+(c.name||c.tag)+'" ('+c.tag+") matched at discovery time but never became visible again within 1500ms"}),journalExerciseProgress(t,++s,o.candidates.length);continue}await T.scrollIntoViewIfNeeded().catch(()=>{});const w=await snapshotControl(e,T,r),p=await coveringOverlays(e);let l=!1;try{await T.click({timeout:2e3})}catch{l=!0}if(l){a.push({kind:"interact.not-clickable",severity:"P2",at:c.at,detail:'control "'+(c.name||c.tag)+'" ('+c.tag+") appeared but did not become clickable within 2000ms -- an element may be covering it, or it never became actionable"}),await T.dispose().catch(()=>{}),journalExerciseProgress(t,++s,o.candidates.length);continue}h++;const A=await waitForControlEffect(e,T,r,w);A.errors>w.errors?(m++,a.push({kind:"interact.click-error",severity:"P1",at:c.at,detail:'clicking "'+(c.name||c.tag)+'" ('+c.tag+") raised a console error/pageerror: "+(A.lastError??"(no message captured)")+" -- this is the class nobody measures: a handler that throws"})):controlChanged(w,A)||(c.current?u++:(f++,a.push({kind:"interact.inert-control",severity:"P2",at:c.at,detail:'clicking "'+(c.name||c.tag)+'" ('+c.tag+") produced no observable effect -- no URL change, DOM mutation, request, dialog, focus move, or aria state change"})));const E=A.dialogs>w.dialogs||await coveringOverlays(e)>p;E&&(await e.keyboard.press("Escape").catch(()=>{}),await wait(200)),(A.url!==w.url||E&&await coveringOverlays(e)>p)&&await y(),await T.dispose().catch(()=>{}),journalExerciseProgress(t,++s,o.candidates.length)}return{findings:a,summary:{controls:o.candidates.length,completed:s,clicked:h,inert:f,alreadyCurrent:u,errors:m,skipped:o.skipped}}}let documentLevel429Count=0;const ordinal=e=>e+(e%100>=11&&e%100<=13?"th":{1:"st",2:"nd",3:"rd"}[e%10]??"th");async function sweepRoute(e,t=WIDTHS[0]){const r=await context.newPage();t!==WIDTH&&await r.setViewportSize({width:t,height:HEIGHT}).catch(()=>{});const n=[];r.on("framenavigated",i=>{if(i!==r.mainFrame())return;const d=urlToRoute(i.url());d!==null&&n.push(d)});const o=attachListeners(r),{push:a}=o;let s={findings:[],metrics:{}},h=null,f=!1,m=null,u=null,y=null;try{CELL_DELAY_MS>0&&await wait(CELL_DELAY_MS);const i=await r.goto(routeToUrl(e),{waitUntil:"domcontentloaded",timeout:NAV_MS});i&&i.status()===429?(documentLevel429Count++,documentLevel429Count>1?a("route.status-429","P2","route returned HTTP 429 -- the "+ordinal(documentLevel429Count)+" 429 this sweep has produced. A real user does not generate this request volume in minutes, so repeated 429s across a run read as the SWEEP tripping the target's own rate limiter rather than a defect this route has for real users. Slow the sweep down with --delay MS and re-run before treating this as an app defect."):a("route.status-429","P0","route returned HTTP 429 (first of this run)")):i&&i.status()>=400&&a("route.status-"+i.status(),"P0","route returned HTTP "+i.status());const d=await settleAndLand(r,e),b=e.replace(/\/$/,"")||"/";if(d!==b&&(h=d,report.unreached.push({route:b,width:t,landedOn:d,role:currentRole,reason:AUTH_ROUTE.test(d)?"redirected to auth -- no role in verify.roles.json reached this route":"redirected"})),h||await r.evaluate(()=>{const I=(document.body&&document.body.innerText||"").slice(0,600).toLowerCase();return/\b(404|not found|page not found|does(n't| not) exist|no such|couldn't find|could not find)\b/.test(I)}).catch(()=>!1)&&(a("route.not-found-shell","P1","route rendered a not-found surface -- if this path has a dynamic segment, the sweep used an id that exists in no fixture, so nothing below measures the real route"),h=d+" (not-found)"),!h&&!AUTH_ROUTE.test(d)){const I=[...new Set(n)].filter(x=>x!==d);I.length>=2&&a("route.indirect-landing","P3","this cell reached "+d+" after passing through "+I.length+" other path(s) first: "+I.join(" -> ")+" -> "+d+" -- a single redirect is normal, a chain like this is worth naming")}h?s={findings:[],metrics:{},skipped:"redirected to "+d}:measured.has(d+"@"+t)?s={findings:[],metrics:{},skipped:"destination already measured at this width: "+d}:(measured.add(d+"@"+t),s=graded(await probeFn(r)));const R=await r.evaluate(()=>{const I=((()=>{const P=document.querySelector('main,[role="main"],#main,#content');if(P&&(P.innerText||"").trim().length>0)return P.innerText;const U=document.body;if(!U)return"";const M=U.cloneNode(!0);for(const D of M.querySelectorAll('nav,header,aside,footer,[role="navigation"],[role="banner"],[role="contentinfo"]'))D.remove();const j=(M.innerText||"").trim();return j.length>0?j:U.innerText||""})()||"").replace(/\s+/g," ").trim(),x=[...document.querySelectorAll('h1,h2,h3,button,a,[role="button"],form,table,[role="dialog"],main')].slice(0,24).map(P=>{const U=P.getBoundingClientRect();return{tag:P.tagName.toLowerCase(),text:(P.innerText||P.getAttribute("aria-label")||"").replace(/\s+/g," ").trim().slice(0,120),classes:[...P.classList].sort().slice(0,8),box:{x:Math.round(U.x),y:Math.round(U.y),w:Math.round(U.width),h:Math.round(U.height)}}});return{textLen:(document.body&&document.body.innerText||"").replace(/\s+/g," ").trim().length,text:I,anchors:x}}).catch(()=>null);if(R){const{text:_,...I}=R;m={...I,fingerprint:surfaceFingerprint(_)}}if(EXERCISE&&!s.skipped&&!h){o.marks.exercising=!0;try{const _=await exerciseCell(r,e,o,EXERCISE);u=_.summary,s.findings=[...s.findings??[],..._.findings]}finally{o.marks.exercising=!1}}}catch(i){a("route.unreachable","P0",String(i).split(`
|
|
18
|
+
`)[0].slice(0,200)),f=!0}await mergeInPage(r,o);const c=await runRuntimePacks(RUNTIME_PACKS,{page:r,route:e,role:currentRole,width:t,base:BASE});RUNTIME_PACKS.errors.length&&(report.infrastructureErrors=RUNTIME_PACKS.errors,writeReport({force:!0}),console.error("sweep: rule-pack infrastructure error(s) -- see "+JSON_OUT),process.exit(2)),c.length&&(s.findings=[...s.findings??[],...c]);const T=/^(render\.empty|render\.stuck-loading|render\.hydration|console\.pageerror|delivery\.chunk-load|value\.leak)/;if(![...o.findings,...s.findings??[]].some(i=>T.test(i.kind))&&!h&&!f)for(const i of o.findings){const d=Number((i.kind.match(/^network\.http-(\d+)$/)??[])[1]);!d||d>=500||(i.severity="P2",i.detail+=" -- the page still rendered and satisfied every invariant afterwards, so the app handled this; P1 only if a surface actually broke")}!h&&!f&&(ROUTE_QUERIES.get(routeKey(e))??0)>0&&(y="mounted-query-reachability-unmeasured");const p=o.requests.filter(i=>(i.type==="xhr"||i.type==="fetch")&&i.end),l=i=>{try{return!new URL(i.url,BASE).searchParams.has("_rsc")}catch{return!0}};p.sort((i,d)=>i.start-d.start);let A=1,E=1;for(let i=1;i<p.length;i++)p[i].start>=p[i-1].end-5&&p[i].start-p[i-1].end<400?(A++,E=Math.max(E,A)):A=1;E>=3&&a("network.waterfall","P3",E+" data requests formed a potentially serialized chain -- timing alone cannot prove dependency; inspect initiators before treating this as a waterfall");const k=new Map;for(const i of o.responses){const d=pathTemplate(i.pathname),b=statusClassOf(i.status),R=i.method+" "+d+" "+b,_=Number.isFinite(i.start)&&Number.isFinite(i.end)?Math.max(0,i.end-i.start):null,I=k.get(R)??{method:i.method,pathTemplate:d,statusClass:b,count:0,msTotal:0,msCount:0};I.count++,_!=null&&(I.msTotal+=_,I.msCount++),k.set(R,I)}const O=[...k.values()].map(i=>({method:i.method,pathTemplate:i.pathTemplate,statusClass:i.statusClass,count:i.count,ms:i.msCount?Math.round(i.msTotal/i.msCount):null}));O.sort((i,d)=>d.count-i.count);const v=Math.max(0,O.length-REQUEST_LOG_CAP),L=O.slice(0,REQUEST_LOG_CAP);for(const i of O)i.statusClass==="5xx"&&a("network.endpoint-5xx","P1",i.method+" "+i.pathTemplate+" answered 5xx "+i.count+" time(s) on this cell -- a template-level aggregate, distinct from the per-response network.http-5xx finding(s) above");const N=new Map;for(const i of o.responses){if(i.prefetch||i.exercising)continue;const d=i.method+" "+pathTemplate(i.pathname),b=N.get(d)??[];b.push(i.pathname),N.set(d,b)}for(const[i,d]of N){if(!hasConcreteRepeat(d))continue;const b=i.indexOf(" ");a("network.duplicate-request","P2",i.slice(0,b)+" "+i.slice(b+1)+" fired "+d.length+" time(s) during this single passive page load -- inspect request initiators to distinguish retries, prefetching, polling, and redundant fetches")}let S=null;if(CAPTURE){const i=captureSlug(currentRole,e,t)+".png";try{fs.mkdirSync(CAPTURE_DIR,{recursive:!0}),await r.screenshot({path:path.join(CAPTURE_DIR,i),fullPage:CAPTURE_FULL,mask:[r.locator(CAPTURE_MASK)],animations:"disabled",caret:"hide"}),S={file:i,fullPage:CAPTURE_FULL}}catch(d){S={file:null,unmeasured:"screenshot failed: "+String(d).split(`
|
|
19
|
+
`)[0].slice(0,160)}}}return await r.close(),{record:{route:e,width:t,redirectedTo:h,findings:[...o.findings,...s.findings??[]],...S?{screenshot:S}:{},metrics:{...s.metrics??{},aborted:o.counters.aborted},navigationHistory:[...new Set(n)],apiRequests:p.length,apiPaths:[...new Set(p.filter(l).map(i=>{try{return new URL(i.url,BASE).pathname}catch{return null}}).filter(Boolean))],apiTargets:[...new Map(p.filter(l).filter(i=>/^(GET|HEAD)$/i.test(i.method)||isDeclaredReadOnlyPost(i)).map(i=>{try{const d=new URL(i.url,BASE);return[i.method+" "+d.origin+d.pathname,{method:i.method.toUpperCase(),path:d.pathname,origin:d.origin}]}catch{return null}}).filter(Boolean)).values()],requests:L,requestsTruncated:v,...m??{},...s.skipped?{skipped:s.skipped}:{},...u?{exercise:u}:{},...y?{dataTraffic:y}:{}},unreachable:f}}const PARALLEL_ARG=String(arg("parallel","1")).trim().toLowerCase(),PARALLEL_AUTO=PARALLEL_ARG==="auto",PARALLEL_MAX=6,PARALLEL=PARALLEL_AUTO?PARALLEL_MAX:Math.max(1,Math.min(8,Number(PARALLEL_ARG)||1));let TARGET_WORKERS=PARALLEL_AUTO?PROD?2:1:PARALLEL;report.concurrency={mode:PARALLEL_AUTO?"auto":"fixed",requested:PARALLEL_ARG,min:1,max:PARALLEL_AUTO?PARALLEL_MAX:PARALLEL,final:TARGET_WORKERS,windows:[]};const THROTTLED_CELLS=new Set;let concurrencyBaselineP95=null,concurrencyWindowSamples=[];const CONCURRENCY_WINDOW_SIZE=5;function concurrencyRecordSample(e,t,r,n){if(!PARALLEL_AUTO||(concurrencyWindowSamples.push({key:e,navMs:t,had429:r,had5xx:n}),concurrencyWindowSamples.length<CONCURRENCY_WINDOW_SIZE))return;const o=concurrencyWindowSamples;concurrencyWindowSamples=[];const a=percentile(o.map(m=>m.navMs),95);concurrencyBaselineP95==null&&(concurrencyBaselineP95=a);const s=o.filter(m=>m.had429).length/o.length,h=o.filter(m=>m.had5xx).length/o.length,f=nextWorkers({p95Ms:a,rate429:s,rate5xx:h},{workers:TARGET_WORKERS,baselineP95Ms:concurrencyBaselineP95,min:1,max:PARALLEL_MAX});if(f.reason==="429"||f.reason==="5xx-rate")for(const m of o)(m.had429||m.had5xx)&&THROTTLED_CELLS.add(m.key);TARGET_WORKERS=f.workers,report.concurrency.windows.push({cells:o.length,p95Ms:Math.round(a),rate429:Number(s.toFixed(3)),rate5xx:Number(h.toFixed(3)),workers:TARGET_WORKERS}),report.concurrency.final=TARGET_WORKERS}async function httpWarm(e){const t=Date.now();let r=0;for(const n of e)try{await(await fetch(routeToUrl(n),{signal:AbortSignal.timeout(WARM_MS)})).arrayBuffer(),r++}catch{}report.warm={requested:e.length,compiled:r,seconds:Math.round((Date.now()-t)/1e3)}}async function swWarm(e){const t=await e.newPage();try{await t.goto(routeToUrl("/"),{waitUntil:"load",timeout:NAV_MS}).catch(()=>{});const r=()=>t.evaluate(async()=>{if(!("serviceWorker"in navigator))return"none";let o=await navigator.serviceWorker.getRegistrations().catch(()=>[]);const a=Date.now()+1e3;for(;!o.length&&Date.now()<a;)await new Promise(s=>setTimeout(s,50)),o=await navigator.serviceWorker.getRegistrations().catch(()=>[]);if(!o.length)return"none";try{await Promise.race([navigator.serviceWorker.ready,new Promise(s=>setTimeout(s,8e3))])}catch{return"none"}return navigator.serviceWorker.controller?"claimed":"registered-not-controlling"}).catch(()=>"unknown");let n=await r();return n==="registered-not-controlling"&&(await t.reload({waitUntil:"load",timeout:NAV_MS}).catch(()=>{}),n=await r()),n}finally{await t.close()}}async function currentServiceWorkerState(e){let t=null,r="page creation",n="unknown",o=null,a=!1;try{if(t=await e.newPage(),r="goto",await t.goto(routeToUrl("/"),{waitUntil:"load",timeout:NAV_MS}),r="landed origin",new URL(t.url()).origin!==new URL(BASE).origin)throw new Error("cross-origin redirect");r="evaluate",n=await t.evaluate(async()=>{if(!("serviceWorker"in navigator))return"unavailable";try{const s=await navigator.serviceWorker.getRegistrations();return Array.isArray(s)?s.length?navigator.serviceWorker.controller?"claimed":"registered-not-controlling":"none":"unknown"}catch{return"unknown"}})}catch(s){o=r==="landed origin"?"service worker probe redirected to a different origin":`service worker probe ${r} failed: ${String(s).split(`
|
|
20
|
+
`)[0].slice(0,160)}`,n="unknown"}finally{if(t)try{await t.close()}catch(s){o="service worker probe close failed: "+String(s).split(`
|
|
21
|
+
`)[0].slice(0,160),n="unknown",a=!0,await e.close().catch(()=>{})}}return{status:n,reason:o,fatal:a}}function runtimeRulesIdentity(){const e=path.join(REPO,".verify","rules");let t=[];try{t=fs.readdirSync(e).filter(r=>r.endsWith(".mjs")).sort().map(r=>({file:digest(r),content:fileIdentity(path.join(e,r))}))}catch{}return identity({engine:fileIdentity(new URL(import.meta.url).pathname),probe:fileIdentity(PROBE),custom:t})}const SOURCE_EXCLUDES=new Set([".git",".verify","node_modules","vendor",".venv","venv",".next",".nuxt",".svelte-kit","dist","build","out","target","coverage",".cache",".turbo",".parcel-cache",".pytest_cache","__pycache__"]),SOURCE_EXT=/\.(?:astro|bash|c|cc|cjs|cpp|cs|css|env|fs|fsx|go|gql|graphql|h|hpp|html?|java|js|jsonc?|jsx|kt|kts|less|lock|mdx|mjs|php|prisma|py|rb|rs|sass|scss|sh|sql|svelte|swift|toml|tsx?|vue|xml|ya?ml|zsh)$/i,SOURCE_NAMES=/^(?:Dockerfile|Makefile|Procfile|Gemfile|Rakefile|Podfile|\.env(?:\..*)?|\.npmrc|\.yarnrc|\.babelrc|\.eslintrc|\.prettierrc)$/i,SOURCE_FILE_LIMIT=1e4,SOURCE_DIR_LIMIT=2e4,SOURCE_BYTE_LIMIT=128*1024*1024;function boundedSourceIdentity(){const e=createHash("sha256");let t=!0,r=null,n=0,o=0,a=0;const s=m=>{t&&(t=!1,r=m)},h=m=>SOURCE_EXT.test(m)||SOURCE_NAMES.test(m),f=(m,u="")=>{if(!t)return;if(n>=SOURCE_DIR_LIMIT){s("directory-budget");return}n++;let y;try{y=fs.readdirSync(m,{withFileTypes:!0}).sort((c,T)=>c.name.localeCompare(T.name))}catch{s("directory-read-error");return}for(const c of y){if(!t)break;const T=u?path.join(u,c.name):c.name;if(c.isDirectory()){SOURCE_EXCLUDES.has(c.name)||f(path.join(m,c.name),T);continue}if(c.isSymbolicLink()){SOURCE_EXCLUDES.has(c.name)||s("source-symlink");continue}if(!c.isFile()||!h(c.name))continue;if(o>=SOURCE_FILE_LIMIT){s("file-budget");break}o++;const w=path.join(REPO,T);let p;try{p=fs.openSync(w,"r");const l=fs.fstatSync(p);if(a+l.size>SOURCE_BYTE_LIMIT){s("byte-budget");continue}e.update(digest(T)),e.update(":"+l.size+":"+(l.mode&511)+":");const A=Buffer.allocUnsafe(64*1024);let E=0;for(;E<l.size;){const O=fs.readSync(p,A,0,Math.min(A.length,l.size-E),E);if(O<=0)throw new Error("short source read");e.update(A.subarray(0,O)),E+=O}const k=fs.fstatSync(p);(k.size!==l.size||k.mtimeMs!==l.mtimeMs)&&s("source-changed-during-scan"),a+=l.size}catch{s("file-read-error")}finally{if(p!=null)try{fs.closeSync(p)}catch{s("file-read-error")}}if(!t)break}};return f(REPO),e.update(":files="+o+":directories="+n+":bytes="+a),{complete:t,reason:r,digest:e.digest("hex")}}function sourceFailure(e,t,r={}){return{complete:!1,reason:e,digest:identity({kind:"incomplete",reason:e,bounded:t.digest,...r})}}function gitResult(e,t="utf8"){return spawnSync("git",["-C",REPO,...e],{encoding:t,maxBuffer:SOURCE_BYTE_LIMIT,stdio:["ignore","pipe","ignore"]})}function targetSourceIdentity(){const e=boundedSourceIdentity();if(!e.complete)return sourceFailure(e.reason,e);const t=gitResult(["rev-parse","--verify","HEAD"]);if(t.status!==0||!String(t.stdout??"").trim())return e;const r=gitResult(["diff","--binary","HEAD","--","."],"buffer");return r.status!==0||!Buffer.isBuffer(r.stdout)?sourceFailure("git-diff-incomplete",e):{complete:!0,reason:null,digest:identity({kind:"git",head:String(t.stdout).trim(),workingTree:digest(r.stdout),relevantFiles:e.digest})}}function buildResumeSignature(e){const t=targetSourceIdentity();return{source:t,signature:"sha256:"+identity({schema:3,lane:LANE,mode:"runtime",baseTarget:digest(BASE),routes:[...routes].sort(),viewportHeight:HEIGHT,modes:{prod:PROD,states:flag("states")?arg("states")??!0:!1,mutate:flag("mutate"),leakCheck:flag("leak-check"),warm:!flag("no-warm"),journeys:!flag("no-journeys"),parallel:PARALLEL_ARG,dark:DARK,exercise:EXERCISE},timeouts:{settleMs:SETTLE_MS,navigationMs:NAV_MS,warmMs:WARM_MS},inventory:fileIdentity(INVENTORY),targetSource:{complete:t.complete,reason:t.reason,digest:t.digest},roles:e.map(r=>({name:r.name,owns:[...r.owns],excludes:[...r.excludes],policy:identity(r.expect)})),runtimeRules:runtimeRulesIdentity()})}}function resumeRejection(e){return!e||typeof e!="object"?"malformed":typeof e.route!="string"||e.route.startsWith("(")?"synthetic":e.findings?.some(t=>t.kind==="route.not-found-shell")?"not-found":e.redirectedTo?"redirected":e.unreachable||e.findings?.some(t=>t.kind==="route.unreachable")?"unreachable":e.skipped||e.synthetic?"skipped":typeof e.role!="string"||!Number.isFinite(e.width)?"missing-key":!Array.isArray(e.findings)||!e.metrics||typeof e.metrics!="object"||!Number.isFinite(e.textLen)||typeof e.fingerprint!="string"||!e.fingerprint?"missing-evidence":null}function resumeRecords(){const e=new Map;if(!report.resume.requested)return e;if(!fs.existsSync(JSON_OUT))return report.resume.reason="report-missing",e;try{const t=JSON.parse(fs.readFileSync(JSON_OUT,"utf8")),r=Array.isArray(t.routes)?t.routes:[];if(!RESUME_SOURCE.complete)return report.resume.rejected=r.length,report.resume.reason="source-identity-incomplete",e;if(t.resume?.sourceIdentity?.complete!==!0)return report.resume.rejected=r.length,report.resume.reason="prior-source-identity-incomplete",e;if(typeof t.resume?.signature!="string")return report.resume.rejected=r.length,report.resume.reason="signature-missing",e;if(t.resume.signature!==RESUME_SIGNATURE)return report.resume.rejected=r.length,report.resume.reason="signature-mismatch",e;if(SERVER_IDENTITY.value==null)return report.resume.rejected=r.length,report.resume.reason="server-identity-unavailable",e;const n=t.resume?.serverIdentity?.value??null;if(n==null||n!==SERVER_IDENTITY.value)return report.resume.rejected=r.length,report.resume.reason="server-identity-changed",e;report.resume.compatible=!0,report.resume.reason="compatible";for(const o of r){const a=resumeRejection(o);if(a){report.resume.rejected++,report.resume.rejectedReasons[a]=(report.resume.rejectedReasons[a]??0)+1;continue}const s=o.role+" "+o.route+" @"+o.width;if(e.has(s)){report.resume.rejected++,report.resume.rejectedReasons.duplicate=(report.resume.rejectedReasons.duplicate??0)+1;continue}e.set(s,{...o,reused:!0,reusedFrom:t.started??null})}return report.resume.reused=e.size,e}catch{return report.resume.reason="report-invalid",e}}const ROLES=PERSONAS;report.roles=ROLES.map(e=>({name:e.name,owns:e.owns,excludes:e.excludes}));const RESUME_IDENTITY=buildResumeSignature(ROLES),RESUME_SIGNATURE=RESUME_IDENTITY.signature,RESUME_SOURCE=RESUME_IDENTITY.source;async function computeServerIdentity(){try{const e=await fetch(BASE+"/",{signal:AbortSignal.timeout(WARM_MS)}),t={};for(const[n,o]of e.headers)t[n]=o;const r=await e.text();return serverIdentityFrom(t,r)}catch{return{value:null,signals:[]}}}const SERVER_IDENTITY=await computeServerIdentity();report.resume={requested:flag("resume"),compatible:flag("resume")?!1:null,reused:0,rejected:0,reason:flag("resume")?"report-missing":"not-requested",rejectedReasons:{},sourceIdentity:{complete:RESUME_SOURCE.complete,...RESUME_SOURCE.reason?{reason:RESUME_SOURCE.reason}:{}},signature:RESUME_SIGNATURE,serverIdentity:{value:SERVER_IDENTITY.value,signals:SERVER_IDENTITY.signals},journalReplayed:JOURNAL_REPLAYED};const RESUMED=resumeRecords();writeReport({force:!0}),flag("no-warm")||await httpWarm(routes);let records=[],nextIdx=0,roleRoutes=[];const WORKER_IDLE_POLL_MS=200;async function sweepWorker(e){for(;;){if(nextIdx>=roleRoutes.length)return;if(e>TARGET_WORKERS){await wait(WORKER_IDLE_POLL_MS);continue}const t=nextIdx++;if(t>=roleRoutes.length)return;const{route:r,width:n}=roleRoutes[t];await serverAlive()||(report.aborted="server unreachable at cell "+(t+1)+"/"+roleRoutes.length+" ("+r+" @"+n+"px, role "+currentRole+") -- partial results kept; rerun with --resume to continue from here",report.routes=records.concat(roleRecords.filter(Boolean)),writeReport({force:!0}),console.error("sweep: "+report.aborted),process.exit(2));const o=Date.now();let{record:a,unreachable:s}=await sweepRoute(r,n);if(s){const f=await sweepRoute(r,n);f.unreachable||(a=f.record,a.flaky=!0,a.findings.push({kind:"route.flaky",severity:"P2",at:"(page)",detail:"first visit failed, retry succeeded -- intermittent"}))}a.role=currentRole;const h=currentRole+" "+r+" @"+n;if(a.cell=cellIdentity({role:currentRole,route:r,width:n,theme:"light",exerciseMode:EXERCISE||null,sourceIdentity:RESUME_SOURCE.digest??null,serverIdentity:SERVER_IDENTITY.value}),PARALLEL_AUTO){const f=Date.now()-o,m=a.findings.some(y=>y.kind==="route.status-429"||y.kind==="network.http-429"),u=(a.requests??[]).some(y=>y.statusClass==="5xx")||a.findings.some(y=>y.kind.startsWith("network.http-5")||y.kind.startsWith("route.status-5"));concurrencyRecordSample(h,f,m,u),THROTTLED_CELLS.has(h)&&(a.findings=[],a.unmeasured="throttled",delete a.textLen,delete a.fingerprint,delete a.anchors,report.unreached.push({route:r,width:n,landedOn:null,role:currentRole,reason:"throttled -- sweep concurrency tripped the target, re-measure with fewer workers"}))}roleRecords[t]=a,report.routes=records.concat(roleRecords.filter(Boolean)),writeReport()}}let roleRecords=[];for(const e of ROLES){currentRole=e.name;const t=routes.filter(f=>roleOwns(e,f)).flatMap(f=>WIDTHS.map(m=>({route:f,width:m}))),r=f=>e.name+" "+f.route+" @"+f.width,n=t.filter(f=>RESUMED.has(r(f)));if(roleRoutes=t.filter(f=>!RESUMED.has(r(f))),records.push(...n.map(f=>RESUMED.get(r(f)))),!t.length){console.error('sweep: role "'+e.name+'" owns no routes -- check `owns` in verify.roles.json');continue}if(!roleRoutes.length)continue;const o=PERSONA_STATE.get(e.name)??{statePath:null,headers:null,session:null},a=e.name===PERSONAS[0].name?context:await makeContext(o),s=context;context=a,measured.clear();const h=await swWarm(a);(report.serviceWorker??={})[e.name]=h,roleRecords=new Array(roleRoutes.length),nextIdx=0,await Promise.all(Array.from({length:Math.min(PARALLEL,roleRoutes.length)},(f,m)=>sweepWorker(m+1))),records.push(...roleRecords.filter(Boolean)),roleRecords=[],report.routes=records,writeReport({force:!0}),context=s,a!==s&&await a.close()}if(currentRole="default",report.routes=records,journeyFile&&!flag("no-journeys")){const e=JOURNEY_LOAD.journeys,t=loadMutationDeclaration(REPO),r=await currentServiceWorkerState(context),n=r.status;(report.serviceWorker??={}).journey=n;let o=null;if(n!=="none"&&(o=r.reason??`service worker state "${n}" makes the protected journey surface unmeasured because service workers are blocked`),r.fatal){report.status="invalid";for(const a of e)report.routes.push({route:"(journey: "+(a.name??"unnamed")+")",metrics:{},findings:[],unmeasured:o});report.infrastructureErrors??=[],report.infrastructureErrors.push({phase:"journey-context",reason:o}),writeReport({force:!0}),AUTH_COORDINATOR&&AUTH_COORDINATOR.release(),await browser.close().catch(()=>{}),process.exit(2)}JOURNEY_LOAD.error&&report.routes.push({route:"(journeys)",metrics:{},findings:[{kind:"journey.load-failed",severity:"P1",at:journeyFile,detail:JOURNEY_LOAD.error}]});for(const a of e){const s=JOURNEY_METADATA.get(a)??journeyMutationMetadata(a),h=t.ok&&a.name?[...new Set(t.declaration.flows.filter(f=>f.journey===a.name).flatMap(f=>f.personas??t.declaration.personas??[]))]:[];h.length||h.push(PERSONAS[0].name);for(const f of h){const m=PERSONA_STATE.get(f),u={route:"(journey: "+(a.name??"unnamed")+")",role:f,width:WIDTH,viewport:{width:WIDTH,height:HEIGHT},metrics:{},findings:[]};if(s.mutates&&!flag("mutate")){!t.ok||!t.declaration.flows.some(R=>R.name===s.flow)?(u.unmeasured=t.reason??`no declared mutation flow named "${s.flow}"`,report.status="invalid",report.infrastructureErrors??=[],report.infrastructureErrors.push({phase:"journey-mutate",reason:u.unmeasured})):u.unmeasured="mutating journey skipped: --mutate was not passed",report.routes.push(u);continue}if(!m){u.unmeasured=`persona state unavailable for declared journey actor "${f}" -- is it declared in verify.roles.json?`,report.status="invalid",report.routes.push(u),report.infrastructureErrors??=[],report.infrastructureErrors.push({phase:"journey-context",reason:u.unmeasured});continue}const y=o;if(y){report.status="invalid",u.unmeasured=y,report.routes.push(u),report.infrastructureErrors??=[],report.infrastructureErrors.push({phase:"journey-context",reason:y});continue}let c,T=!1;try{c=await makeContext({...m,statePath:f===PERSONAS[0].name?await context.storageState():m.statePath},{serviceWorkers:"block"}),await c.exposeBinding("__qaReportServiceWorkerRegistrationAttempt",()=>{T=!0}),await c.addInitScript(()=>{if(!navigator.serviceWorker?.register)return;const R=navigator.serviceWorker.register.bind(navigator.serviceWorker);navigator.serviceWorker.register=(..._)=>(globalThis.__qaReportServiceWorkerRegistrationAttempt(),R(..._))})}catch(R){u.unmeasured="live browser storage state could not seed the protected journey context: "+String(R).split(`
|
|
22
|
+
`)[0].slice(0,160),report.status="invalid",report.routes.push(u),report.infrastructureErrors??=[],report.infrastructureErrors.push({phase:"journey-context",reason:u.unmeasured});continue}const w=new Set(c.pages()),p=await c.newPage(),l=attachListeners(p);let A={findings:[],metrics:{}},E=null,k=null,O=!1,v=!1,L=null,N=!1,S=!0;const i=R=>{if(!v)return E??="cleanup target can only be reserved during cleanup",!1;const _=cleanupRequestAllowed(k,{method:"DELETE",pathname:R,cleanupPath:L??R,flowName:s.flow});return _.ok?(L??=R,!0):(E??=_.reason,!1)},d=async R=>{const _=R.request(),I=_.method().toUpperCase(),x=_.url();let C;try{const U=new URL(x);if(U.origin!==new URL(BASE).origin)return idpBootstrapAllowed(t?.declaration,{url:x,method:I,headers:_.headers()})?R.continue():_.redirectedFrom()?(E??="identity bootstrap redirect left its declared origin/path",R.abort()):["POST","PUT","PATCH","DELETE"].includes(I)?(E??="write request must stay on the declared base origin",R.abort()):R.continue();C=U.pathname}catch{return E??="write request URL is invalid",R.abort()}if(!["POST","PUT","PATCH","DELETE"].includes(I))return R.continue();if(!S)return E??="browser write attempted after its journey completed",R.abort();if(!v&&isBeaconPath(t?.declaration,C))return R.continue();if(!k)return E??="mutating journey requests require mutates:true, a named flow, and shared mutation policy approval",R.abort();if(v&&L===null)return E??="cleanup target must be reserved before its DELETE request",R.abort();const P=(v?cleanupRequestAllowed:requestAllowed)(k,{method:I,url:x,base:BASE,cleanupPath:L,flowName:s.flow});return P.ok?v?(N=!0,R.continue()):(O=!0,R.continue()):(E??=P.reason,R.abort())};try{s.mutates&&(k=JOURNEY_PERMITS.get(a)??journeyPermit(a,s,t),k.ok||(E=k.reason)),await c.route("**/*",d),E||(await p.goto(routeToUrl(a.start??"/"),{waitUntil:"domcontentloaded",timeout:NAV_MS}),await settleAndLand(p,a.start??"/"),T&&(E="service worker registration attempted on the protected journey surface; result is unmeasured"),E||await a.run(p,{base:BASE,settle:()=>p.waitForTimeout(SETTLE_MS)}),await p.waitForTimeout(SETTLE_MS),T&&(E??="service worker registration attempted during the protected journey; result is unmeasured"),E||(A=graded(await probeFn(p))))}catch(R){const _=(a.name??"journey")+": "+String(R).split(`
|
|
23
|
+
`)[0].slice(0,200);l.push("journey.failed","P0",_,"(journey)",{failureClass:classifyJourneyFailure(_)})}finally{if(O&&k?.cleanup?.required)if(typeof a.cleanup!="function")E??="accepted journey write requires a cleanup function, but cleanup is absent";else{v=!0;try{const R=await a.cleanup(p,{base:BASE,settle:()=>p.waitForTimeout(SETTLE_MS),reserveCleanup:i});L===null?E??="accepted journey write cleanup target was not reserved":N?R!==!0&&R?.ok!==!0&&(E??="accepted journey write cleanup is unmeasured or failed"):E??="accepted journey write cleanup DELETE was not observed"}catch(R){E??="accepted journey write cleanup failed: "+String(R).split(`
|
|
24
|
+
`)[0].slice(0,160)}finally{v=!1,L=null}}}await mergeInPage(p,l);const b={...u,findings:[...l.findings,...A.findings],metrics:A.metrics};/service worker registration/i.test(E??"")&&(b.unmeasured=E),S=!1;for(const R of c.pages().filter(_=>!w.has(_)))try{await R.close()}catch(_){E??="browser page close failure: "+String(_).split(`
|
|
25
|
+
`)[0].slice(0,160)}if(c.pages().some(R=>!w.has(R))&&(E??="browser page close failure left a journey page open"),E){try{await c.close()}catch(R){E+="; browser context close failure: "+String(R).split(`
|
|
26
|
+
`)[0].slice(0,160)}report.status="invalid",report.routes.push(b),report.infrastructureErrors??=[],report.infrastructureErrors.push({phase:"journey-mutate",reason:E}),writeReport({force:!0}),AUTH_COORDINATOR&&AUTH_COORDINATOR.release(),await context.close().catch(()=>{}),await browser.close().catch(()=>{}),process.exit(2)}report.routes.push(b);try{await c.close()}catch(R){report.status="invalid",report.infrastructureErrors??=[],report.infrastructureErrors.push({phase:"journey-context",reason:"browser context close failure: "+String(R).split(`
|
|
27
|
+
`)[0].slice(0,160)})}}}}function classifyJourneyFailure(e){const t=String(e).toLowerCase();return/locator|selector|strict mode|no element|waiting for/.test(t)?"selector-missing":/login|sign.?in|unauthori[sz]|forbidden|permission|access denied|403/.test(t)?"auth-gate":/pageerror|console\.error|typeerror|referenceerror|is not a function|cannot read/.test(t)?"client-exception":/timeout|timed out|exceeded/.test(t)?"route-timeout":/readonly|read-only|mutation|write|csrf|blocked|not allowed/.test(t)?"mutation-blocked":/navigation|url|redirect|route|path/.test(t)?"navigation-drift":"unknown"}const STATE_KINDS={empty:{status:200,body:"[]"},error:{status:500,body:'{"error":"internal"}'},forbidden:{status:403,body:'{"error":"forbidden"}'},malformed:{status:200,body:'{"unexpected":true}'},slow:{status:200,body:null,delayMs:6e3}},STATES=(()=>{if(!flag("states"))return[];const e=arg("states");return(!e||e.startsWith("--")?Object.keys(STATE_KINDS):e.split(",").map(r=>r.trim())).filter(r=>STATE_KINDS[r])})();if(STATES.length){const e=new URL(BASE).origin,{cells:t,ineligible:r}=planForcedStateCells(report.routes,READ_ONLY.paths,e);report.states={routes:new Set(t.map(n=>n.route)).size,kinds:STATES,cells:t.length*STATES.length,...READ_ONLY.status==="declared"?{declaredReadOnlyPosts:READ_ONLY.paths.size}:{},...r.length?{ineligible:r}:{}},r.length&&console.log(`
|
|
28
|
+
NOT MEASURED `+r.length+" route/persona pair(s) are outside the forced-state matrix: "+r.slice(0,6).map(n=>n.route).join(", ")+" -- "+r[0].reason);for(const n of t){const o=PERSONA_STATE.get(n.role);if(!o){report.routes.push({route:"(states: "+n.route+")",role:n.role,width:n.width,metrics:{},findings:[],unmeasured:"persona state unavailable for forced-state replay"}),writeReport({force:!0});continue}let a=null;try{a=n.role===PERSONAS[0].name?context:await makeContext(o);const s=forcedStateTargets(n,READ_ONLY.paths,e),h=[];for(const u of STATES){const y=STATE_KINDS[u],c=await a.newPage();try{await c.setViewportSize({width:n.width,height:HEIGHT}),await c.addInitScript(({targets:w,spec:p})=>{window.__fv_forcedHits=0;const l=S=>{try{return new URL(S,location.origin)}catch{return null}},A=(S,i)=>{const d=l(i);return d?w.some(b=>b.method===S&&b.path===d.pathname&&(b.origin?b.origin===d.origin:d.origin===location.origin)):!1},E=S=>new Promise(i=>setTimeout(i,S)),k=async S=>{try{const i=JSON.parse(await S.clone().text());if(Array.isArray(i))return"[]";const d=["data","items","results","rows","records"].find(b=>Array.isArray(i?.[b]));return d?JSON.stringify({...i,[d]:[]}):null}catch{return null}},O=S=>new Response(S,{status:p.status,headers:{"content-type":"application/json"}}),v=window.fetch.bind(window);window.fetch=async(S,i)=>{const d=typeof S=="string"||S instanceof URL?String(S):S&&S.url||"",b=(i?.method??S?.method??"GET").toUpperCase();if(!A(b,d))return v(S,i);if(p.delayMs)return window.__fv_forcedHits++,await E(p.delayMs),v(S,i);if(p.kind==="empty"){const R=await v(S,i),_=await k(R);return _===null?R:(window.__fv_forcedHits++,O(_))}return window.__fv_forcedHits++,O(p.body)};const L=XMLHttpRequest.prototype.open,N=XMLHttpRequest.prototype.send;XMLHttpRequest.prototype.open=function(S,i,...d){return this.__fv_method=String(S).toUpperCase(),this.__fv_async=d[0]!==!1,this.__fv_empty=p.kind==="empty"&&A(this.__fv_method,i),this.__fv_forced=p.kind!=="empty"&&A(this.__fv_method,i),this.__fv_forcedBody=p.body,L.call(this,S,i,...d)},XMLHttpRequest.prototype.send=function(...S){if(this.__fv_empty){let i=!1;return this.addEventListener("readystatechange",()=>{if(i||this.readyState!==4)return;let d=null;try{d=this.responseText}catch{try{d=JSON.stringify(this.response)}catch{}}let b=null;try{const R=JSON.parse(d);if(Array.isArray(R))b="[]";else{const _=["data","items","results","rows","records"].find(I=>Array.isArray(R?.[I]));_&&(b=JSON.stringify({...R,[_]:[]}))}}catch{}b!==null&&(i=!0,window.__fv_forcedHits++,Object.defineProperty(this,"responseText",{configurable:!0,get:()=>b}),Object.defineProperty(this,"response",{configurable:!0,get:()=>this.responseType==="json"?JSON.parse(b):b}))},!0),N.apply(this,S)}if(!this.__fv_forced)return N.apply(this,S);if(p.delayMs){if(!this.__fv_async)return this.__fv_forced=!1,N.apply(this,S);window.__fv_forcedHits++,setTimeout(()=>N.apply(this,S),p.delayMs);return}window.__fv_forcedHits++,Object.defineProperty(this,"status",{get:()=>p.status}),Object.defineProperty(this,"statusText",{get:()=>({200:"OK",403:"Forbidden",500:"Internal Server Error"})[p.status]??""}),Object.defineProperty(this,"responseText",{get:()=>this.__fv_forcedBody??""}),Object.defineProperty(this,"response",{get:()=>this.__fv_forcedBody??""}),Object.defineProperty(this,"readyState",{get:()=>4}),this.getAllResponseHeaders=()=>`content-type: application/json\r
|
|
29
|
+
`,this.getResponseHeader=i=>String(i).toLowerCase()==="content-type"?"application/json":null,setTimeout(()=>{this.onreadystatechange?.(),this.dispatchEvent(new Event("readystatechange")),this.onload?.(),this.dispatchEvent(new Event("load")),this.onloadend?.(),this.dispatchEvent(new Event("loadend"))},0)}},{targets:s,spec:{...y,kind:u}});let T=null;try{await c.goto(routeToUrl(n.route),{waitUntil:"domcontentloaded",timeout:NAV_MS}),await settleAndLand(c,n.route),y.delayMs&&await c.waitForTimeout(1500);const w=async()=>c.evaluate(()=>{const l=document.querySelector('main,[role="main"],#root,#__next')||document.body,A=S=>{if(!S)return!1;const i=getComputedStyle(S),d=S.getBoundingClientRect();return i.display!=="none"&&i.visibility!=="hidden"&&Number(i.opacity)>=.02&&d.width>0&&d.height>0},E=[...l.querySelectorAll('[role="progressbar"],[aria-busy="true"],.animate-spin,[data-loading="true"],[class*="skeleton" i]')].some(A),k=[...document.querySelectorAll('[role="alert"],[aria-live="assertive"],[data-error],[data-testid*="error" i]')].filter(A).map(S=>S.innerText||S.textContent||"").join(" "),O=[l.innerText||"",k].join(" ").replace(/\s+/g," ").trim(),v=O.toLowerCase();let L=5381;for(let S=0;S<Math.min(O.length,400);S++)L=(L*33^O.charCodeAt(S))>>>0;const N=[...document.querySelectorAll('h1,h2,h3,button,a,[role="button"],form,table,[role="dialog"],main')].slice(0,24).map(S=>{const i=S.getBoundingClientRect();return{tag:S.tagName.toLowerCase(),text:(S.innerText||S.getAttribute("aria-label")||"").replace(/\s+/g," ").trim().slice(0,120),classes:[...S.classList].sort().slice(0,8),box:{x:Math.round(i.x),y:Math.round(i.y),w:Math.round(i.width),h:Math.round(i.height)}}});return{textLen:O.length,fingerprint:L.toString(36),semanticEvidence:O,anchors:N,intercepted:window.__fv_forcedHits??0,viewportWidth:innerWidth,saysError:/\b(error|went wrong|failed|unable to|try again|couldn.t load|problem)\b/.test(v),saysEmpty:/\b(no |none|empty|nothing|0 results|not found yet|get started|add your first)\b/.test(v),saysDenied:/\b(denied|forbidden|not authoriz|no access|permission)\b/.test(v),hasSpinner:E,leaksRaw:/\{"(error|unexpected)"|\bTypeError\b|\bundefined is not\b|at [A-Za-z]+ \(http/.test(O)}}),p=[await w()];for(let l=0;l<4;l++)await c.waitForTimeout(300),p.push(await w());T={...p.at(-1),hasSpinner:p.every(l=>l.hasSpinner),spinnerSamples:p.filter(l=>l.hasSpinner).length,observationMs:1200},T.semanticSignature=semanticStateSignature(T.semanticEvidence),delete T.semanticEvidence}catch(w){T={crashed:String(w).split(`
|
|
30
|
+
`)[0].slice(0,160)}}h.push({kind:u,...T})}finally{await c.close()}}for(const u of h)!u.crashed&&!u.intercepted&&(u.status="unmeasured",u.reason="no targeted request was intercepted for this forced state");const f=h.filter(u=>!u.crashed&&u.intercepted>0);if(emptyInjectionHitErrorState(f)){const u=f.find(y=>y.kind==="empty");u.status="unmeasured",u.reason="forced empty response rendered error semantics -- the endpoint envelope is unknown, so the empty path was not measured"}const m=judgeForcedStateColumns(h,STATES.length);report.routes.push({route:"(states: "+n.route+")",role:n.role,width:n.width,metrics:{},findings:m,columns:h}),writeReport({force:!0})}finally{a&&a!==context&&await a.close()}}}if(DARK){const e=new Set,t=report.routes.filter(n=>!n.route.startsWith("(")&&!n.redirectedTo),r=[];for(const n of t){const o=n.role+"\0"+n.route;e.has(o)||(e.add(o),r.push(n))}report.dark={requested:!0,seeded:!0,storageKey:THEME_STORAGE_KEY,toggled:0,cells:r.length,unmeasured:0};for(const n of r){const o=PERSONA_STATE.get(n.role);if(!o){report.dark.unmeasured+=1,report.routes.push({route:"(dark: "+n.route+")",role:n.role,width:n.width,metrics:{},findings:[],unmeasured:`persona state unavailable for dark-mode replay as "${n.role}"`}),writeReport({force:!0});continue}const a=n.role===PERSONAS[0].name?context:await makeContext(o);try{const s=await a.newPage();await s.setViewportSize({width:n.width,height:HEIGHT}).catch(()=>{}),await s.emulateMedia({colorScheme:"dark"}).catch(()=>{}),await s.addInitScript(m=>{try{window.localStorage.setItem(m,"dark")}catch{}},THEME_STORAGE_KEY);const h=[];let f="light";try{await s.goto(routeToUrl(n.route),{waitUntil:"domcontentloaded",timeout:NAV_MS}),await settleAndLand(s,n.route);const m=s.locator('[aria-label*="theme" i], [data-theme-toggle], button:has-text("Dark")').first();let u=!1;await m.count().catch(()=>0)&&(await m.waitFor({state:"visible",timeout:2e3}).catch(()=>{}),await m.isVisible().catch(()=>!1)&&(await m.click({timeout:2e3}).catch(()=>{}),u=!0,await s.waitForTimeout(300))),u&&report.dark.toggled++;const y=await s.evaluate(()=>{const w=document.createElement("canvas");w.width=w.height=1;const p=w.getContext("2d",{willReadFrequently:!0}),l=N=>{if(!p||!globalThis.CSS?.supports?.("color",N))return null;p.clearRect(0,0,1,1),p.fillStyle=N,p.fillRect(0,0,1,1);const[S,i,d,b]=p.getImageData(0,0,1,1).data;return b===255?{r:S,g:i,b:d}:null},A=N=>(.2126*N.r+.7152*N.g+.0722*N.b)/255,E=N=>{for(let S=N;S;S=S.parentElement){const i=l(getComputedStyle(S).backgroundColor);if(i)return i}return null},k=document.documentElement,v=(" "+(k.getAttribute("class")||"")+" ").indexOf(" dark ")>=0||k.getAttribute("data-theme")==="dark",L=getComputedStyle(k).colorScheme||"";return{htmlDarkSignal:v,colorSchemeDark:/dark/.test(L),mediaDark:matchMedia("(prefers-color-scheme: dark)").matches,bodyLum:(()=>{const N=E(document.body||k);return N?A(N):null})()}});y.bodyLum===null&&report.dark.unmeasured++;const c=y.htmlDarkSignal||y.colorSchemeDark||y.mediaDark&&y.bodyLum!==null&&y.bodyLum<.6;if(f=c?"dark":"light",!c&&y.bodyLum!==null&&y.bodyLum>=.6&&h.push({kind:"theme.tokens-not-applied",severity:"P2",at:"(page)",detail:"seeding localStorage.theme=dark (and clicking the theme control when present) did not change the root -- no dark class or data-theme, color-scheme is not dark, and the background did not darken. Dark mode never engaged."}),c){await s.emulateMedia({colorScheme:"light"}).catch(()=>{});const w=await s.evaluate(()=>{const p=document.createElement("canvas");p.width=p.height=1;const l=p.getContext("2d",{willReadFrequently:!0}),A=d=>{if(!l||!globalThis.CSS?.supports?.("color",d))return null;l.clearRect(0,0,1,1),l.fillStyle=d,l.fillRect(0,0,1,1);const[b,R,_,I]=l.getImageData(0,0,1,1).data;return I===255?{r:b,g:R,b:_}:null},E=d=>(.2126*d.r+.7152*d.g+.0722*d.b)/255,k=d=>{const b=getComputedStyle(d);if(b.display==="none"||b.visibility==="hidden"||Number(b.opacity)<.02)return!1;const R=d.getBoundingClientRect();return R.width>0&&R.height>0},O=d=>{const b=d.getAttribute&&d.getAttribute("data-testid");if(b)return"[data-testid="+b+"]";if(d.id)return"#"+d.id;const R=d.className&&String(d.className).split(/\s+/)[0]||"";return d.tagName.toLowerCase()+(R?"."+R:"")},v=[];for(const d of document.querySelectorAll("body *")){if(v.length>=10)break;if(!k(d))continue;const b=getComputedStyle(d),R=A(b.backgroundColor);if(!R)continue;const _=A(b.color);_&&E(R)>.85&&E(_)<.15&&v.push({el:d,bg:b.backgroundColor,fg:b.color})}if(!v.length)return[];const L=document.documentElement,N=L.getAttribute("class"),S=L.getAttribute("data-theme"),i=document.createElement("style");i.textContent="*,*::before,*::after{transition:none!important}",(document.head||L).appendChild(i);try{L.classList.remove("dark"),S==="dark"&&L.setAttribute("data-theme","light");const d=[];for(const b of v){const R=getComputedStyle(b.el);R.backgroundColor===b.bg&&R.color===b.fg&&d.push(O(b.el))}return d}finally{N===null?L.removeAttribute("class"):L.setAttribute("class",N),S===null?L.removeAttribute("data-theme"):L.setAttribute("data-theme",S),i.remove()}});w.length&&h.push({kind:"theme.light-hardcoded",severity:"P2",at:w[0],detail:w.length+" element(s) with a near-white background and near-black text while the root is dark (capped at 10): "+w.join(", ")})}}catch(m){h.push({kind:"theme.check-failed",severity:"P2",at:"(page)",detail:String(m).split(`
|
|
31
|
+
`)[0].slice(0,200)})}report.routes.push({route:"(dark: "+n.route+")",role:n.role,width:n.width,metrics:{},findings:h,cell:cellIdentity({role:n.role,route:n.route,width:n.width,theme:f,exerciseMode:EXERCISE||null,sourceIdentity:RESUME_SOURCE.digest??null,serverIdentity:SERVER_IDENTITY.value})}),writeReport({force:!0}),await s.close()}finally{a!==context&&await a.close().catch(()=>{})}}}if(flag("mutate")&&fs.existsSync(INVENTORY)){const t=(JSON.parse(fs.readFileSync(INVENTORY,"utf8")).syncRisks??[]).filter(r=>r.endpoint&&r.route&&(r.staleRoutes??[]).length&&!/[:[]/.test(r.route));for(const r of t)report.routes.push({route:"(mutate: "+r.route+")",metrics:{},findings:[],skipped:"generic mutation replay disabled until actual browser requests can be scoped by full origin; cover this risk with a declared journey or qa measure crud"});t.length&&(report.status="invalid",report.infrastructureErrors??=[],report.infrastructureErrors.push({phase:"mutate",reason:"generic mutation replay disabled because actual browser requests cannot be safely authorized by full origin"}))}if(flag("leak-check")&&routes.length>=2){const e=await context.newPage(),t=await context.newCDPSession(e),r=async()=>{await t.send("HeapProfiler.collectGarbage");const{usedSize:s}=await t.send("Runtime.getHeapUsage");return s};await e.goto(routeToUrl(routes[0]),{waitUntil:"load"}).catch(()=>{});const n=await r();for(let s=0;s<5;s++)for(const h of routes.slice(0,4))await e.goto(routeToUrl(h),{waitUntil:"load"}).catch(()=>{});await e.goto(routeToUrl(routes[0]),{waitUntil:"load"}).catch(()=>{});const o=await r(),a=Math.round((o-n)/1048576*10)/10;report.leak={beforeMB:Math.round(n/1048576),afterMB:Math.round(o/1048576),growthMB:a},a>8&&report.routes.push({route:"(navigation loop)",metrics:{},findings:[{kind:"perf.memory-leak",severity:"P1",at:"(page)",detail:a+"MB retained after GC across 20 navigations -- detached nodes or un-removed listeners"}]}),await e.close()}try{await browser.close()}catch(e){report.status="invalid",report.infrastructureErrors??=[],report.infrastructureErrors.push({phase:"browser-close",reason:String(e).split(`
|
|
32
|
+
`)[0].slice(0,200)})}const measuredRouteWidths=new Set(report.routes.filter(e=>!e.redirectedTo&&e.textLen!=null).map(e=>e.route+"@"+e.width));report.unreached=report.unreached.filter(e=>!measuredRouteWidths.has(e.route+"@"+e.width));const all=report.routes.flatMap(e=>e.findings.map(t=>({...t,route:e.route}))),bySev=all.reduce((e,t)=>(e[sevOf(t)]=(e[sevOf(t)]??0)+1,e),{}),byKind=all.reduce((e,t)=>(e[t.kind]=(e[t.kind]??0)+1,e),{}),counts=routeCounts(cellsOf(report),dynamicSkipped),ownedByNoRole=routes.filter(e=>!ROLES.some(t=>roleOwns(t,e))),contrast=report.routes.reduce((e,t)=>{const r=Number(t.metrics?.contrastEligible)||0,n=Math.min(r,Math.max(0,Number(t.metrics?.contrastUnmeasured)||0));return e.eligible+=r,e.unmeasured+=n,e.measured+=r-n,e},{eligible:0,measured:0,unmeasured:0});if(report.measurement={producer:{name:"sweep",atlasVersion:2,rulesDigest:"sha256:"+digest(fs.readFileSync(new URL(import.meta.url)))},routes:[...routes].sort(),personas:PERSONAS.map(e=>e.name).sort(),viewports:[...WIDTHS].sort((e,t)=>e-t),modes:{runtime:{prod:PROD,exercise:EXERCISE,leakCheck:flag("leak-check")},state:{states:[...STATES].sort()},dark:{dark:DARK},journey:{journeys:!flag("no-journeys"),mutate:flag("mutate")},pack:{enabled:RUNTIME_PACKS.packs.length>0}},packs:RUNTIME_PACKS.packs.map(({id:e,version:t,phase:r})=>({id:e,version:t,phase:r})).sort((e,t)=>e.id.localeCompare(t.id))},report.summary={routes:report.routes.length,routesRequested:counts.routesRequested,routesReached:counts.routesReached,cellsRequested:counts.cellsRequested,cellsReached:counts.cellsReached,routesUnreached:report.unreached.length,routesUnowned:ownedByNoRole.length,routesDynamicSkipped:counts.routesDynamicSkipped,routesInInventory:counts.routesInInventory,roles:ROLES.map(e=>e.name),findings:all.length,contrast,...bySev},ownedByNoRole.length&&(report.unowned=ownedByNoRole),dynamicSkipped.length&&(report.dynamicSkipped=dynamicSkipped.map(e=>({route:e,reason:"dynamic segment needs a real id; cover it with an app-owned journey (verify.journeys.mjs)"}))),report.integrity=gradeIntegrity({cells:report.routes.filter(e=>!e.route.startsWith("(")),unreached:report.unreached,isAuthRoute:e=>AUTH_ROUTE.test(e),textFloor:Number(arg("text-floor","40")),share:Number(arg("integrity-share","0.25")),aborted:report.aborted}),report.finished=new Date().toISOString(),writeReport({force:!0}),console.log(`
|
|
33
|
+
`+PUBLIC_BASE+" "+counts.routesRequested+" route(s), "+counts.cellsRequested+" cell(s) at "+WIDTHS.join(",")+"px"+(ROLES.length>1?" as "+ROLES.map(e=>e.name).join(", "):"")),report.resume.requested&&console.log(" resume: "+(report.resume.compatible?"compatible":"fresh")+" \xB7 "+report.resume.reused+" reused \xB7 "+report.resume.rejected+" rejected \xB7 "+report.resume.reason),PARALLEL_AUTO){const e=PROD?2:1,t=report.concurrency.windows.filter(r=>r.rate429>0||r.rate5xx>=.05).length;console.log(" parallel: auto ("+e+" \u2192 "+report.concurrency.final+" workers, "+t+" throttled window(s))")}if(report.warm&&console.log(" warm-up: "+report.warm.compiled+"/"+report.warm.requested+" routes precompiled in "+report.warm.seconds+"s"),report.serviceWorker&&console.log(" service worker: "+Object.entries(report.serviceWorker).map(([e,t])=>e+"="+t).join(", ")),EXERCISE){const e=report.routes.filter(r=>r.exercise),t=e.reduce((r,n)=>(r.controls+=n.exercise.controls,r.clicked+=n.exercise.clicked,r.inert+=n.exercise.inert,r.errors+=n.exercise.errors,r.alreadyCurrent+=n.exercise.alreadyCurrent??0,r),{controls:0,clicked:0,inert:0,errors:0,alreadyCurrent:0});console.log(" exercise: "+e.length+" cell(s), "+t.controls+" control(s) considered, "+t.clicked+" clicked, "+t.inert+" inert, "+t.alreadyCurrent+" already in the requested state (not graded), "+t.errors+" click-error(s)")}if(report.unreached.length){console.log(`
|
|
34
|
+
NOT MEASURED `+report.unreached.length+" of "+counts.cellsRequested+" cells -- redirected, so nothing below covers them:");for(const e of report.unreached.slice(0,8))console.log(" "+e.route+" -> "+e.landedOn+" (as "+e.role+")");report.unreached.length>8&&console.log(" ... and "+(report.unreached.length-8)+" more in "+JSON_OUT),console.log(" Give each one an owning role in verify.roles.json, or accept them as out of scope.")}ownedByNoRole.length&&console.log(`
|
|
35
|
+
UNOWNED `+ownedByNoRole.length+" route(s) match no role in verify.roles.json and were never visited: "+ownedByNoRole.slice(0,6).join(", ")),console.log(`
|
|
36
|
+
`+all.length+" findings "+Object.entries(bySev).map(([e,t])=>e+":"+t).join(" ")+`
|
|
37
|
+
`),console.log(" contrast "+contrast.eligible+" eligible \xB7 "+contrast.measured+" measured \xB7 "+contrast.unmeasured+" unmeasured");for(const[e,t]of Object.entries(byKind).sort((r,n)=>n[1]-r[1])){const r=all.find(n=>n.kind===e);console.log(" "+String(t).padStart(4)+" "+sevOf(r)+" "+e);for(const n of all.filter(o=>o.kind===e).slice(0,3))console.log(" "+n.route+" "+n.detail.slice(0,130));t>3&&console.log(" ... and "+(t-3)+" more")}if(console.log(" -> "+JSON_OUT),CAPTURE){const e=writeCaptureReport(report,CAPTURE_DIR),t=(report.routes??[]).filter(n=>n.screenshot?.file).length,r=(report.routes??[]).filter(n=>n.screenshot&&!n.screenshot.file).length;e?(console.log(` -> ${e} (${t} screenshot(s)${r?`, ${r} not captured`:""})`),console.log(" these images may contain application data -- review before sharing")):console.error(t||r?" capture: screenshots were taken but the index could not be written":" capture: no cell produced a screenshot")}report.leak&&console.log(`
|
|
38
|
+
heap after GC across navigation: `+report.leak.beforeMB+"MB -> "+report.leak.afterMB+"MB (+"+report.leak.growthMB+"MB)");const g=report.integrity;console.log(`
|
|
39
|
+
integrity `+g.cellsMeasured+"/"+g.cellsPlanned+" cells measured \xB7 "+g.bouncedToLogin+" bounced to login \xB7 "+g.cellsBelowTextFloor+" below the text floor \xB7 "+g.distinctSurfaces+" distinct surfaces"+(g.lanes.length>1?" across "+g.lanes.length+" role/width lanes":"")+" \xB7 widest cluster "+g.maxRoutesPerFingerprint+" route(s) per fingerprint");for(const e of g.lanes.filter(t=>t.collapsed))console.log(" lane "+e.role+" @"+e.width+"px rendered "+e.distinct+" distinct surface(s) over "+e.cells+" cells -- read its findings as one page");if(report.status==="invalid"&&(AUTH_COORDINATOR&&AUTH_COORDINATOR.release(),console.error(`
|
|
40
|
+
INVALID browser or protected journey finalization failed; see infrastructureErrors.
|
|
41
|
+
`),process.exit(2)),!g.ok){AUTH_COORDINATOR&&AUTH_COORDINATOR.release(),console.error(`
|
|
42
|
+
INVALID this run did not measure the app:`);for(const e of g.failed)console.error(" - "+e);console.error(` Do not merge it, do not average it, and do not read "no findings" from it.
|
|
43
|
+
`),process.exit(2)}console.log(""),AUTH_COORDINATOR&&AUTH_COORDINATOR.release(),process.exit((bySev.P0??0)+(bySev.P1??0)>0?1:0);
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import a from"node:fs";const n=process.argv[2];n||(console.error("prior input required"),process.exit(2));let t;try{t=JSON.parse(a.readFileSync(n,"utf8"))}catch(r){console.error(`invalid prior: ${String(r).split(`
|
|
3
|
+
`)[0]}`),process.exit(2)}const p=new Set(["schema_version","signature","frequency","calibration","provenance_class","priors"]),f=/(password|secret|bearer|token|cookie|authorization|payload|email|user|path|repo|source)/i,l=/^[A-Za-z0-9+/=_-]{32,}$/;let o=null;const s=(r,e="")=>{if(!o){if(e&&(f.test(e)||!p.has(e)&&e!=="")){o=`forbidden field: ${e}`;return}if(typeof r=="string"&&(/@/.test(r)||/\/Users\/|[A-Za-z]:\\/i.test(r)||l.test(r))){o="private or high-entropy value";return}if(Array.isArray(r))return r.forEach(i=>s(i,e));if(r&&typeof r=="object")return Object.entries(r).forEach(([i,c])=>s(c,i))}};s(t),o&&(console.error(`prior rejected by privacy policy: ${o}`),process.exit(1)),console.log(JSON.stringify({schema_version:1,valid:!0,advisory_only:!0,fields_checked:Object.keys(t)},null,2));
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import a from"node:fs";import r from"node:path";import s from"node:crypto";import{validateManifestFile as l}from"./verify-manifest.mjs";const{report:t,manifestDir:o}=l(process.argv[2]);if(t.valid&&o){const n=r.join(o,"artifact-attestation.json"),i=r.join(o,`.artifact-attestation.${process.pid}.${s.randomBytes(8).toString("hex")}.tmp`);try{const e=`${JSON.stringify(t,null,2)}
|
|
3
|
+
`;a.writeFileSync(i,e,{flag:"wx",mode:384}),a.renameSync(i,n)}catch{try{a.unlinkSync(i)}catch{}t.status="invalid",t.valid=!1,t.reasons.push("attestation_write_failed"),t.outputIntegrity.status="invalid"}}console.log(JSON.stringify(t,null,2)),process.exit(t.valid?0:2);
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import c from"node:fs";import r from"node:path";import x from"node:crypto";import{fileURLToPath as C}from"node:url";import{canonicalize as H}from"../lib/qa/jcs.mjs";const I=/^[a-f0-9]{64}$/,z=/^sha256:[a-f0-9]{64}$/,D=C(import.meta.url),O=Object.freeze({status:"unmeasured",mismatched:[],missing:[]}),j=t=>t!==null&&typeof t=="object"&&!Array.isArray(t),d=t=>typeof t=="string"&&t.trim().length>0,S=(t,e)=>e===t||e.startsWith(`${t}${r.sep}`),n=(t,e)=>{t.includes(e)||t.push(e)};function N(t,e,i){const p=t?.[e];return p===null?{valid:!0,status:"unavailable"}:typeof p=="string"&&p.length>0&&(!i||i.test(p))?{valid:!0,status:"available"}:{valid:!1,status:"unavailable"}}function R(t,e){const i=r.isAbsolute(e)?r.resolve(e):r.resolve(t,e);if(!S(t,i)||i===t)return{safe:!1,exists:!1};const p=r.relative(t,i);let s=t;for(const l of p.split(r.sep)){s=r.join(s,l);let _;try{_=c.lstatSync(s)}catch(m){return m?.code==="ENOENT"?{safe:!0,exists:!1,resolved:s}:{safe:!1,exists:!1}}if(_.isSymbolicLink()){try{s=c.realpathSync(s)}catch{return{safe:!1,exists:!1}}if(!S(t,s))return{safe:!1,exists:!1}}}return{safe:!0,exists:!0,resolved:s}}function $(t,e){const i=t.output_digests;if(!j(i))return{...O};const p=Array.isArray(t.outputs)?t.outputs:[],s=[],l=[],_=new Set,m=(a,u)=>{if(!d(a)||a.includes("\0")||r.isAbsolute(a)||r.win32.isAbsolute(a)||a.split(/[\\/]+/).includes("..")){n(s,a);return}const f=r.normalize(a);if(_.has(f))return;if(_.add(f),!d(u)||!z.test(u)){n(s,a);return}if(!e){n(l,a);return}const v=r.resolve(e,f);if(!S(e,v)||v===e){n(s,a);return}let b;try{b=c.lstatSync(v)}catch{n(l,a);return}if(b.isSymbolicLink()||!b.isFile()){n(l,a);return}let A;try{A=x.createHash("sha256").update(c.readFileSync(v)).digest("hex")}catch{n(l,a);return}`sha256:${A}`!==u&&n(s,a)};for(const[a,u]of Object.entries(i))a!=="run-manifest.json"&&m(a,u);for(const a of p)a==="run-manifest.json"||!d(a)||_.has(r.normalize(a))||n(l,a);return{status:s.length===0&&l.length===0?"measured":"stale",mismatched:s,missing:l}}function T(t,e){return t.schema_version===1&&d(t.run_id)&&t.status==="complete"&&d(t.input_fingerprint)&&d(t.config_fingerprint)&&d(t.repo_root)&&r.isAbsolute(t.repo_root)&&d(t.generated_at)&&Number.isFinite(Date.parse(t.generated_at))&&d(t.label)&&(t.base===null||typeof t.base=="string")&&d(t.widths)&&typeof t.states=="boolean"&&typeof t.mutate=="boolean"&&Array.isArray(t.warnings)&&t.warnings.every(i=>typeof i=="string")&&Array.isArray(t.input_hashes)&&Array.isArray(t.outputs)&&Number.isSafeInteger(t.graph_nodes)&&t.graph_nodes>=0&&Number.isSafeInteger(t.graph_edges)&&t.graph_edges>=0&&typeof t.confidence=="number"&&Number.isFinite(t.confidence)&&t.confidence>=0&&t.confidence<=1&&e.every(i=>i.valid)}function F(t,e,i,p=O){const s=t.length===0;return{schema_version:1,status:s?"valid":"invalid",valid:s,reasons:t,counts:e,inputIntegrity:{status:t.some(l=>l.startsWith("input_")||l.startsWith("repo_"))?"invalid":"verified",checkedCount:e.inputsChecked},outputIntegrity:{status:s?"unmeasured":"invalid",reason:"expected_output_hashes_unavailable",checkedCount:e.outputsChecked},output_freshness:p,attestation:{repoCommit:{status:i[0]?.status??"unavailable"},skillHash:{status:i[1]?.status??"unavailable"},playwrightBuild:{status:i[2]?.status??"unavailable"}}}}function U(t){const e=[],i={outputsDeclared:0,outputsChecked:0,inputsDeclared:0,inputsChecked:0},p=r.resolve(t??".verify/autonomous/run-manifest.json");let s;try{if(!c.lstatSync(p).isFile())throw new Error("not a regular file");s=JSON.parse(c.readFileSync(p,"utf8"))}catch(o){return n(e,o instanceof SyntaxError?"manifest_json_invalid":"manifest_unreadable"),{report:F(e,i,[]),manifestDir:null}}if(!j(s))return n(e,"manifest_schema_invalid"),{report:F(e,i,[]),manifestDir:null};const l=[N(s,"repo_commit",/^[a-f0-9]{40,64}$/i),N(s,"skill_hash",/^[a-f0-9]{32}$/i),N(s,"playwright_build")];T(s,l)||n(e,"manifest_schema_invalid");const _=Array.isArray(s.outputs)?s.outputs:[],m=Array.isArray(s.content_hashes)?s.content_hashes:null,w=m??(Array.isArray(s.input_hashes)?s.input_hashes:[]),a=o=>{const y=c.readFileSync(o);if(!m)return x.createHash("sha256").update(y).digest("hex");let h;try{h=JSON.parse(y.toString("utf8"))}catch{return x.createHash("sha256").update(y).digest("hex")}if(h&&typeof h=="object"&&!Array.isArray(h))for(const g of["generated","started","finished","runId","cache"])delete h[g];return x.createHash("sha256").update(H(h)).digest("hex")};i.outputsDeclared=_.length,i.inputsDeclared=w.length,_.length===0&&n(e,"outputs_empty");let u=null,f=null;try{u=c.realpathSync(r.dirname(p))}catch{n(e,"manifest_run_root_invalid")}if(d(s.repo_root)&&r.isAbsolute(s.repo_root))try{if(f=c.realpathSync(s.repo_root),!c.statSync(f).isDirectory())throw new Error("not a directory")}catch{n(e,"repo_root_invalid"),f=null}if(u&&f){const o=r.relative(f,u);(!S(f,u)||o.split(r.sep)[0]!==".verify")&&n(e,"manifest_repo_scope_invalid")}const v=new Set;for(const o of _){if(!d(o)||o.includes("\0")||r.isAbsolute(o)||r.win32.isAbsolute(o)||o.split(/[\\/]+/).includes("..")){n(e,"output_path_invalid");continue}const y=r.normalize(o);if(v.has(y)){n(e,"output_duplicate");continue}if(v.add(y),!u)continue;const h=r.resolve(u,y);if(!S(u,h)||h===u){n(e,"output_path_invalid");continue}let g;try{g=c.lstatSync(h)}catch{n(e,"output_missing");continue}if(g.isSymbolicLink()){n(e,"output_symlink");continue}if(!g.isFile()){n(e,"output_not_regular");continue}try{if(!S(u,c.realpathSync(h))){n(e,"output_path_invalid");continue}}catch{n(e,"output_unreadable");continue}i.outputsChecked+=1}const b=new Set;for(const o of w){if(!Array.isArray(o)||o.length!==2||!d(o[0])||!(o[1]==="missing"||typeof o[1]=="string"&&I.test(o[1]))){n(e,"input_hash_tuple_invalid");continue}const[y,h]=o;if(!f)continue;const g=r.isAbsolute(y)?r.resolve(y):r.resolve(f,y),E=r.normalize(g);if(b.has(E)){n(e,"input_duplicate");continue}b.add(E);const k=R(f,g);if(!k.safe){n(e,"input_path_invalid");continue}if(h==="missing"){k.exists?n(e,"input_replaced"):i.inputsChecked+=1;continue}if(!k.exists){n(e,"input_missing");continue}try{if(!c.statSync(k.resolved).isFile()){n(e,"input_not_regular");continue}a(k.resolved)!==h?n(e,"input_hash_mismatch"):i.inputsChecked+=1}catch{n(e,"input_unreadable")}}const A=$(s,u);return A.status==="stale"&&n(e,"output_freshness_stale"),{report:F(e,i,l,A),manifestDir:u}}function W(){const{report:t}=U(process.argv[2]);console.log(JSON.stringify(t,null,2)),process.exit(t.valid?0:2)}const L=(()=>{try{return!!process.argv[1]&&c.realpathSync(r.resolve(process.argv[1]))===c.realpathSync(D)}catch{return!1}})();L&&W();export{U as validateManifestFile};
|
package/install-skill.sh
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
script_path="${BASH_SOURCE[0]}"
|
|
5
|
+
while [ -L "$script_path" ]; do
|
|
6
|
+
script_dir="$(cd "$(dirname "$script_path")" && pwd -P)"
|
|
7
|
+
target="$(readlink "$script_path")"
|
|
8
|
+
case "$target" in
|
|
9
|
+
/*) script_path="$target" ;;
|
|
10
|
+
*) script_path="$script_dir/$target" ;;
|
|
11
|
+
esac
|
|
12
|
+
done
|
|
13
|
+
script_dir="$(cd "$(dirname "$script_path")" && pwd -P)"
|
|
14
|
+
skill_root="$script_dir"
|
|
15
|
+
|
|
16
|
+
if command -v brew >/dev/null 2>&1; then
|
|
17
|
+
brew_root="$(brew --prefix qa 2>/dev/null || true)"
|
|
18
|
+
if [ -f "$brew_root/libexec/SKILL.md" ]; then
|
|
19
|
+
skill_root="$brew_root/libexec"
|
|
20
|
+
fi
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
[ -f "$skill_root/SKILL.md" ] || {
|
|
24
|
+
echo "frontend-verify skill not found beside the installer" >&2
|
|
25
|
+
exit 2
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
linked=0
|
|
29
|
+
for agent_root in "$HOME/.claude" "$HOME/.agents" "$HOME/.codex" "$HOME/.cursor"; do
|
|
30
|
+
[ -d "$agent_root" ] || continue
|
|
31
|
+
skills="$agent_root/skills"
|
|
32
|
+
link="$skills/frontend-verify"
|
|
33
|
+
mkdir -p "$skills"
|
|
34
|
+
if [ -e "$link" ] || [ -L "$link" ]; then
|
|
35
|
+
current="$(readlink "$link" 2>/dev/null || true)"
|
|
36
|
+
[ "$current" = "$skill_root" ] && continue
|
|
37
|
+
echo "Preserved existing $link; move it aside to install this skill." >&2
|
|
38
|
+
continue
|
|
39
|
+
fi
|
|
40
|
+
ln -s "$skill_root" "$link"
|
|
41
|
+
echo "Linked $link"
|
|
42
|
+
linked=$((linked + 1))
|
|
43
|
+
done
|
|
44
|
+
|
|
45
|
+
[ "$linked" -gt 0 ] || echo "No new agent skill links were needed."
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import u from"node:fs";import i from"node:path";import{which as c}from"./proc.mjs";import{selectBinary as m,verifyBinary as f}from"./release-manifest.mjs";const p="cargo not found";function l(){return c("cargo")?{status:"measured",reason:null}:{status:"unmeasured",reason:p}}function b(s,{platform:a=process.platform,arch:t=process.arch,cargoAvailabilityFn:n=l}={}){const r=n();if(r.status==="measured")return{...r,source:"cargo"};try{const o=JSON.parse(u.readFileSync(i.join(s,"agent","release-manifest.json"),"utf8")),e=m({skillRoot:s,platform:a,arch:t,manifest:o});return e.status!=="ok"?{status:"unmeasured",reason:`${r.reason}; ${e.reason}`}:f(e.path,e.sha256).ok?{status:"measured",reason:null,source:"prebuilt"}:{status:"unmeasured",reason:`${r.reason}; digest-mismatch`}}catch{return{status:"unmeasured",reason:`${r.reason}; no-binary`}}}export{p as CARGO_MISSING,l as cargoPhaseAvailability,b as kernelPhaseAvailability};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import $ from"node:fs";import{validate as v}from"./schema.mjs";import{validateCoherencePlanIdentities as O}from"./coherence/identity.mjs";import{classifyCoherencePlan as A}from"./coherence/plan-join.mjs";const l=1,f=1,N=Object.freeze(["no-sweep-input","no-binary","incompatible-protocol","unsupported-platform","not-requested","malformed-output","schema-invalid","digest-mismatch","load-failed","graph-integrity-failed","no-coherence-input","legacy-coherence-input"]),p=Object.freeze(["static","runtime"]),b=Object.freeze(["source","public-runtime","mock-contract","real-integration","mfa-integration"]);function P(t){return{status:"measured",value:t}}function T(t){return{status:"unmeasured",reason:t}}function V(t){return{status:"invalid",reason:t}}function R(t,n){if(!Array.isArray(t))throw new TypeError("collection: items must be an array");if(!Number.isInteger(n))throw new TypeError("collection: total must be an integer \u2014 an unknown total is not guessed");const e=t.length;if(e>n)throw new RangeError(`collection: returned ${e} exceeds total ${n}`);return{items:t,returned:e,total:n,truncated:e<n}}function h(t,n){if(n===null||typeof n!="object"||Array.isArray(n))throw new TypeError(`dimension "${t}" must be a discriminated object, got ${JSON.stringify(n)}`);if(!["measured","unmeasured","invalid"].includes(n.status))throw new TypeError(`dimension "${t}" has no valid status`)}function w(t){const n=Object.entries(t??{});for(const[e,i]of n)h(e,i);return n.some(([,e])=>e.status==="invalid")?"invalid":n.some(([,e])=>e.status==="measured")?"measured":"unmeasured"}const y=/^[0-9a-f]{64}$/,I=JSON.parse($.readFileSync(new URL("../../schemas/coherence-plan.schema.json",import.meta.url),"utf8"));function _(t){const n=[],e=r=>n.push(r);if(t===null||typeof t!="object")return{ok:!1,problems:["result is not an object"]};if(t.schemaVersion!==l&&e(`schemaVersion must be ${l}`),t.protocolVersion!==f&&e(`protocolVersion must be ${f}`),(typeof t.runId!="string"||!t.runId)&&e("runId must be a non-empty string"),p.includes(t.mode)||e(`mode must be one of ${p.join(", ")}`),b.includes(t.lane)||e(`lane must be one of ${b.join(", ")}`),(!t.binary||!y.test(t.binary.sha256??""))&&e("binary.sha256 must be 64 lowercase hex characters"),t.binary&&t.binary.protocolVersion!==f&&e("binary.protocolVersion mismatch"),!Array.isArray(t.inputs))e("inputs must be an array");else for(const[r,o]of t.inputs.entries())typeof o?.name!="string"&&e(`inputs[${r}].name must be a string`),y.test(o?.sha256??"")||e(`inputs[${r}].sha256 must be 64 lowercase hex characters`),(!Number.isInteger(o?.bytes)||o.bytes<0)&&e(`inputs[${r}].bytes must be a non-negative integer`);Array.isArray(t.limitations)||e("limitations must be an array");const i=t.dimensions;if(i===null||typeof i!="object")return e("dimensions must be an object"),{ok:!1,problems:n};for(const[r,o]of Object.entries(i)){try{h(r,o)}catch(s){e(s.message);continue}o.status!=="measured"&&!N.includes(o.reason)&&e(`dimension "${r}" reason ${JSON.stringify(o.reason)} is not one of the typed reasons`),o.status==="measured"&&o.value===void 0&&e(`dimension "${r}" is measured with no value`)}const c=i.routePriority;if(c?.status==="measured"){for(const r of g("routePriority",c.value))e(r);for(const[r,o]of(c.value?.items??[]).entries()){const s=o?.confidence,d=o?.expectedInformation;typeof s!="number"||s<0||s>1?e(`routePriority.items[${r}].confidence must be in [0,1]`):(typeof d!="number"||Math.abs(d-(1-s))>1e-9)&&e(`routePriority.items[${r}].expectedInformation must equal 1 - confidence`)}}const a=i.coherenceObligations;if(a?.status==="measured")try{if(A(a.value)!=="current")throw new TypeError("legacy-coherence-plan-non-proof");const r=v(I,a.value);for(const o of r)e(`dimensions.coherenceObligations.value.${o.path}: ${o.message}`);r.length||O(a.value)}catch(r){e(`dimensions.coherenceObligations.value: ${String(r?.message??r)}`)}for(const[r,o]of Object.entries(i))if(r!=="routePriority"&&o?.status==="measured"&&o.value&&typeof o.value=="object"&&Array.isArray(o.value.items))for(const s of g(r,o.value))e(s);let u=null;try{u=w(i)}catch(r){e(r.message)}return u!==null&&t.status!==u&&e(`status ${JSON.stringify(t.status)} disagrees with the status derived from its dimensions (${u})`),{ok:n.length===0,problems:n}}function g(t,n){const e=[];return Array.isArray(n?.items)?(Number.isInteger(n.returned)||e.push(`${t}.returned must be an integer`),Number.isInteger(n.total)||e.push(`${t}.total must be an integer`),typeof n.truncated!="boolean"&&e.push(`${t}.truncated must be a boolean`),Number.isInteger(n.returned)&&Number.isInteger(n.total)&&n.returned>n.total&&e.push(`${t}: returned ${n.returned} exceeds total ${n.total}`),Number.isInteger(n.returned)&&n.returned!==n.items.length&&e.push(`${t}.returned ${n.returned} disagrees with items.length ${n.items.length}`),e):(e.push(`${t}.items must be an array`),e)}function k(t,n){const e=[];for(const i of["runId","mode","lane"])n?.[i]!==t?.[i]&&e.push(`agent result ${i} ${JSON.stringify(n?.[i])} does not match the ${i} this run passed it (${JSON.stringify(t?.[i])})`);return e}function x(t){return t===0||t===1}function J({agentStatus:t,agentRequired:n,gateExit:e}){return!x(e)||t==="invalid"||n&&t!=="measured"?m:e}const m=2;export{f as AGENT_PROTOCOL_VERSION,l as AGENT_SCHEMA_VERSION,b as LANES,p as MODES,N as REASONS,k as bindAgentIdentity,R as collection,w as deriveStatus,J as exitFor,x as gateCompleted,V as invalid,P as measured,T as unmeasured,_ as validateAgentResult};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import k from"node:fs";import y from"node:path";import{createHash as A}from"node:crypto";const d=(o,n)=>`${o}-${A("sha256").update(n).digest("hex").slice(0,12)}`,C=(o,n)=>({file:n,verified:typeof n=="string"&&k.existsSync(y.join(o,n))}),m=o=>[...new Set(o.filter(Boolean))].sort();function S(o){return{id:d("REQ",o.path),status:"unresolved",text:`No independent business requirement was supplied for ${o.path}; observed source behavior is not promoted to intent.`}}function M(o,n){return(o?.flows??[]).find(i=>i.declared_endpoint===n.endpoint)??null}function T(o,{inventory:n,research:i,run:f=null,declaration:h=null}={}){const u=[...n.routes??[]].sort((e,t)=>String(e.path).localeCompare(String(t.path))),x=u.map(e=>({id:d("EV",`${e.path}:${e.file}`),kind:"source",fact:`route ${e.path} is owned by ${e.file}`,sources:[C(o,e.file)],uses:[`feature:${e.path}`],confidence:"high"})),w=u.map(e=>({id:d("FEAT",e.path),route:e.path,owner:e.file,personas:m((i?.principals?.detected??[]).map(t=>t.name)),requirements:[S(e)],expectedBehavior:"source-derived candidate only; no independent requirement established",evidence:[d("EV",`${e.path}:${e.file}`)],surfaces:[e.path],producers:(e.mutations??[]).map(t=>({action:`${t.verb??"?"} ${t.endpoint??"?"}`,source:t.file??e.file})),consumers:m((e.entities??[]).flatMap(t=>n.matrix?.[t]??[])),consistencyRules:(e.mutations??[]).map(t=>`refresh every surface rendering ${t.writes??"the changed record"}`),rbac:{personas:m((i?.principals?.detected??[]).map(t=>t.name)),status:"source-derived ownership only; runtime visibility is unmeasured"},coverage:{verifiedAppearances:[],inferredConnections:[],unmeasuredBehavior:["business requirement","runtime visibility","post-mutation readback"]}})),l=new Map;for(const e of u)for(const t of e.entities??[]){const c=l.get(t)??[];c.push(e.path),l.set(t,c)}const a=[];for(const e of u)for(const t of e.mutations??[]){const c=m([...n.matrix?.[t.writes]??[],...l.get(t.writes)??[]]);for(const v of c)a.push({from:e.path,to:v,entity:t.writes??"unknown",evidence:d("EV",`${e.path}:${e.file}`),certainty:t.writes?"source-derived":"inferred"})}a.sort((e,t)=>`${e.from}:${e.to}:${e.entity}`.localeCompare(`${t.from}:${t.to}:${t.entity}`));const p=u.flatMap(e=>(e.mutations??[]).map(t=>{const c=a.filter(g=>g.from===e.path&&g.entity===t.writes).map(g=>g.to),v=t.endpoint?2:1,b=Math.max(1,m(c).length);return{id:d("RISK",`${e.path}:${t.file}:${t.line}`),route:e.path,mutation:`${t.verb??"?"} ${t.endpoint??"?"}`,likelihood:v,impact:b,score:v*b,rationale:"heuristic prioritization from statically observed write fan-out; not a probability",evidence:[d("EV",`${e.path}:${e.file}`)]}})).sort((e,t)=>t.score-e.score||e.id.localeCompare(t.id)),s=u.flatMap(e=>(e.mutations??[]).map(t=>{const c=M(h,t);return{id:d("MUT",`${e.path}:${t.file}:${t.line}`),route:e.path,endpoint:t.endpoint??null,authorization:c?"declared flow; execution still requires --mutate and policy checks":"unavailable: no matching declared mutation flow",preconditions:c?["loopback/non-production target","positive shared budget","isolated fixture namespace"]:[],expectedChange:"unresolved: an independent requirement or app-owned assertion is required",authoritativeReadback:c?.crosscheck?"declared crosscheck":"unavailable",downstreamInvariants:[],cleanup:c?.cleanup_required===!1?"not declared":c?"declared cleanup required":"unavailable",status:c?"declared-not-executed":"unavailable",evidence:[d("EV",`${e.path}:${e.file}`)]}})),r=u.map(e=>({id:d("STORY",e.path),feature:d("FEAT",e.path),route:e.path,riskIds:p.filter(t=>t.route===e.path).map(t=>t.id),requirementIds:[d("REQ",e.path)],actions:["open route","observe only declared/app-owned assertions"],expected:"unresolved without an independent requirement",disposition:"unmeasured",reason:"analysis creates no executable assertion or runtime observation"})),$=r.length,q=r.map(e=>({checkId:e.id,sourceRevision:n.cache?.identity??n.cache?.sourceDigest??"unknown",runId:f?.id??null,result:"unmeasured",assertions:[],evidence:[],gap:e.reason})),E=(n.syncRisks??[]).map((e,t)=>({id:d("FIX",`${e.file??""}:${e.line??t}:${e.kind??""}`),priority:e.severity??"P2",finding:e.kind??"source sync risk",status:"unverified",retest:`qa show analysis ${o}`,evidence:[]})),R={scope:`source:${n.cache?.identity??n.cache?.sourceDigest??"unknown"}`,declared:$,executable:0,attempted:0,passed:0,failed:0,unmeasured:$,invalid:0,exclusions:0,rule:"Each planned story is exactly one mutually exclusive disposition."},I=["flowchart LR",...a.map((e,t)=>` N${t}[${e.from}] -->|${e.entity}| T${t}[${e.to}]`)].join(`
|
|
2
|
+
`),j=a.length?a.map(e=>`${e.from} --${e.entity}--> ${e.to}`).join(`
|
|
3
|
+
`):"(no source-derived dependency edges)";return{schemaVersion:1,source:{inventoryIdentity:n.cache?.identity??null,sourceDigest:n.cache?.sourceDigest??null,researchRunId:i?.runId??null},evidence:x,features:w,context:{routes:u.map(e=>e.path),twoHopEdges:a},graphs:{mermaid:I,ascii:j},risks:p,stories:r,mutationContracts:s,proof:q,fixQueue:E,beforeAfter:{before:"no linked execution evidence",after:"retest each fix queue item through its linked executable check when one exists",unresolvedRisks:p.map(e=>e.id)},coverage:R}}function B(o){const n=["# QA analysis (source-derived, not execution proof)","",`Scope: ${o.coverage.scope}`,"","## Evidence ledger","","| id | fact | confidence |","| --- | --- | --- |"];for(const i of o.evidence)n.push(`| ${i.id} | ${i.fact} | ${i.confidence} |`);n.push("","## Feature coverage matrix","","| feature | surfaces | producers | consumers | consistency | RBAC | coverage |","| --- | --- | --- | --- | --- | --- | --- |");for(const i of o.features)n.push(`| ${i.route} | ${i.surfaces.join(", ")||"-"} | ${i.producers.map(f=>f.action).join("; ")||"-"} | ${i.consumers.join(", ")||"-"} | ${i.consistencyRules.join("; ")||"-"} | ${i.rbac.status} | verified ${i.coverage.verifiedAppearances.length}; inferred ${i.coverage.inferredConnections.length}; unmeasured ${i.coverage.unmeasuredBehavior.join(", ")} |`);n.push("","## Context graph","","```mermaid",o.graphs.mermaid,"```","","```text",o.graphs.ascii,"```","","## Risks","","| id | score | rationale |","| --- | ---: | --- |");for(const i of o.risks)n.push(`| ${i.id} | ${i.score} | ${i.rationale} |`);n.push("","## Proof and coverage","",`Declared ${o.coverage.declared}; executable ${o.coverage.executable}; attempted ${o.coverage.attempted}; passed ${o.coverage.passed}; failed ${o.coverage.failed}; unmeasured ${o.coverage.unmeasured}; invalid ${o.coverage.invalid}.`,"","Every pass requires an executed assertion. This artifact contains source-derived candidates and explicit gaps, not invented outcomes.","","## Fix queue","");for(const i of o.fixQueue)n.push(`- ${i.id}: ${i.priority} ${i.finding} (${i.status}; retest: ${i.retest})`);return o.fixQueue.length||n.push("- No source findings were promoted to a fix claim."),n.join(`
|
|
4
|
+
`)+`
|
|
5
|
+
`}function D(o,n){const i=s=>Array.isArray(n?.[s])?n[s]:[],f=s=>new Set(i(s).map(r=>r.id)),h=f("evidence"),u=f("features"),x=f("risks"),w=f("stories"),l=[],a=(s,r,$)=>{r.has(s)||l.push(`${$}:${s}`)};for(const s of i("evidence"))for(const r of s.sources??[])(!r.verified||!k.existsSync(y.join(o,r.file)))&&l.push(`source:${r.file}`);for(const s of i("features"))for(const r of s.evidence??[])a(r,h,"feature-evidence");for(const s of i("risks"))for(const r of s.evidence??[])a(r,h,"risk-evidence");for(const s of i("mutationContracts"))for(const r of s.evidence??[])a(r,h,"mutation-evidence");for(const s of i("stories")){a(s.feature,u,"story-feature");for(const r of s.riskIds??[])a(r,x,"story-risk")}for(const s of i("proof"))a(s.checkId,w,"proof-story");const p=n?.coverage??{};return p.declared!==p.passed+p.failed+p.unmeasured+p.invalid+p.exclusions&&l.push("coverage-reconciliation"),l}export{B as analysisMarkdown,D as analysisViolations,T as buildAnalysis};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import N from"node:fs";import E from"node:path";const O=/^\|\s*(\d+\.\d+)\s*\|\s*(.+?)\s*\|\s*([A-Z+]+)\s*\|\s*$/,T=/^##\s+L(\d+)\s+—\s+(.+?)\s*$/,v=/^##\s+Evaluated and deliberately not implemented/i,G=/^##\s+/,H=/(\d+\.\d+)/,z=/^\s*\|\s*([\d.]+)\s*\|/,K=/^(?!0\d)\d+\.(?!0\d)\d+$/;class q extends Error{constructor(e){super(`references/atlas.md has ${e.length} structural problem(s): `+e.map(t=>`line ${t.line}: ${t.kind} (${t.detail})`).join("; ")),this.name="AtlasParseError",this.problems=e,this.exit=2}}function B(n){const e=n.split(`
|
|
2
|
+
`),t=[],o=[],s=[],l=new Map;let r=null,a=!1,u=!1;for(let f=0;f<e.length;f++){const i=f+1,c=e[f].replace(/\r$/,""),d=c.match(T);if(d){r={n:Number(d[1]),id:"L"+d[1],title:d[2].replace(/\*\*/g,""),rows:[]},t.push(r),a=!1;continue}if(v.test(c)){a=!0,u=!1,r=null;continue}if(G.test(c)){r=null,a=!1;continue}if(!a){const y=c.match(z);if(y){const D=y[1];if(!K.test(D)){s.push({kind:"malformed-coordinate",line:i,detail:D});continue}const j=c.match(O);if(!j){s.push({kind:"malformed-coordinate",line:i,detail:c.trim()});continue}if(!r){s.push({kind:"row-outside-layer",line:i,detail:"L"+D});continue}const k="L"+D;if(l.has(k)){s.push({kind:"duplicate-id",line:i,detail:`${k} first seen on line ${l.get(k)}`});continue}if(l.set(k,i),Number(D.split(".")[0])!==r.n){s.push({kind:"prefix-mismatch",line:i,detail:`${k} under ${r.id}`});continue}r.rows.push({id:k,num:D,class:j[2].replace(/\*\*/g,"").replace(/\s*†\s*$/,"").trim(),det:j[3]})}continue}const m=c.match(/^\|\s*(.+?)\s*\|\s*(.+?)\s*\|\s*$/);if(!m)continue;const h=m[1].replace(/`/g,"").trim(),A=m[2].trim();if(/^Check$/i.test(h)&&!u){u=!0;continue}if(h===""||/^:?-+:?$/.test(h))continue;const _=h.match(H),b=/runtime half/i.test(h+" "+A)?"R":/static half/i.test(h+" "+A)?"S":null;o.push({check:h,reason:A,id:_?"L"+_[1]:null,mode:b})}if(s.length)throw new q(s);return{layers:t,deliberate:o}}function P(n){const e=[],t=/\badd\(/g;let o;for(;o=t.exec(n);){const s=o.index+o[0].length-1;let l=0,r=-1;for(let d=s;d<n.length;d++){const m=n[d];if(m==="(")l++;else if(m===")"&&(l--,l===0)){r=d;break}}if(r===-1)continue;const a=n.slice(s+1,r),u=a.match(/^\s*'([^']+)'/);if(!u)continue;const f=[...a.matchAll(/'(L\d+\.\d+)'/g)],i=f[f.length-1],c=n.slice(0,o.index).split(`
|
|
3
|
+
`).length;e.push({rule:u[1],atlas:i?i[1]:null,line:c})}return e}const w=/'([a-z][a-z0-9_]*(?:\.[a-z0-9_-]+)+)'/g;function W(n){let e="",t=0;const o=n.length;for(;t<o;){const s=n[t],l=n[t+1];if(s==="/"&&l==="/"){for(;t<o&&n[t]!==`
|
|
4
|
+
`;)t++;continue}if(s==="/"&&l==="*"){for(t+=2;t<o&&!(n[t]==="*"&&n[t+1]==="/");)n[t]===`
|
|
5
|
+
`&&(e+=`
|
|
6
|
+
`),t++;t+=2;continue}if(s==="'"||s==='"'||s==="`"){const r=s;for(e+=s,t++;t<o;){if(n[t]==="\\"){e+=n[t]+(n[t+1]??""),t+=2;continue}if(e+=n[t],n[t]===r){t++;break}t++}continue}e+=s,t++}return e}function Y(n){let e="",t=0;for(;t<n.length;){const o=n[t];if(o!=="'"&&o!=='"'&&o!=="`"){e+=n[t++];continue}for(e+=" ",t++;t<n.length;){if(n[t]==="\\"){e+=" ",t+=2;continue}if(n[t]===o){e+=" ",t++;break}e+=n[t]===`
|
|
7
|
+
`?`
|
|
8
|
+
`:" ",t++}}return e}function U(n,e){const t=W(n),o=Y(t),s=new Map,l=u=>{const f=u.endsWith("-"),i=u;s.has(i)||s.set(i,{kind:u,prefix:f,source:e,count:0}),s.get(i).count++},r=/\b(?:([A-Za-z_$][\w$]*(?:\s*\.\s*[A-Za-z_$][\w$]*)*)\s*\.\s*)?(push|add)\s*\(/g;let a;for(;a=r.exec(o);){const u=r.lastIndex-1;let f=0,i=-1;for(let b=u;b<o.length;b++){const y=o[b];if(y==="(")f++;else if(y===")"&&--f===0){i=b;break}}if(i<0)continue;const c=a[1]?.replace(/\s/g,"")??null,d=t.slice(u+1,i),m=o.slice(u+1,i),h=c==null||/(?:^|\.)findings$/.test(c),A=d.match(/^\s*'([a-z][a-z0-9_]*(?:\.[a-z0-9_-]+)+)'\s*,\s*'P[0-3]'/)?.[1]??null;if(!h&&!A&&!/\bfindings\s*:/.test(m)){r.lastIndex=i+1;continue}const _=d.match(/^\s*([^,]*)/)?.[1]??"";if(c==null||A)for(const b of _.matchAll(w))l(b[1]);for(const b of d.matchAll(/\bkind:\s*'([a-z][a-z0-9_]*(?:\.[a-z0-9_-]+)+)'/g))l(b[1]);if(/\bkind\s*[,}]/.test(m)&&/\bseverity\s*:/.test(m)){const b=[...t.slice(0,a.index).matchAll(/\b(?:const|let)\s+kind\s*=\s*([^;]{1,400});/g)];for(const y of(b.at(-1)?.[1]??"").matchAll(w))l(y[1])}r.lastIndex=i+1}for(const u of t.matchAll(/\bkind\s*:\s*([^,\n}]+)[^{}]{0,1200}\bseverity\s*:/g))if(o.slice(u.index,u.index+4)==="kind")for(const f of u[1].matchAll(w))s.has(f[1])||l(f[1]);return[...s.values()]}function rt(n){const e=[],t=o=>{if(N.existsSync(o))for(const s of N.readdirSync(o,{withFileTypes:!0}).sort((l,r)=>l.name.localeCompare(r.name))){const l=E.join(o,s.name);s.isDirectory()?t(l):/\.(?:mjs|js)$/.test(s.name)&&e.push(l)}};return t(E.join(n,"bin")),t(E.join(n,"lib")),e.sort().map(o=>({source:E.relative(n,o).split(E.sep).join("/"),src:N.readFileSync(o,"utf8")}))}const R=Object.freeze({"sync.no-invalidation":"no-invalidation","sync.partial-invalidation":"partial-invalidation"}),L=new Set(Object.keys(R));function Z(n){const e=new Map;if(!n||typeof n!="object")return e;for(const[t,o]of Object.entries(n)){const s=Array.isArray(o)?o:[o],l=/^L?\d+\.\d+$/.test(t);for(const r of s)if(typeof r=="string"){if(l){const a=t.startsWith("L")?t:"L"+t;e.has(r)||e.set(r,new Set),e.get(r).add(a)}else if(/^L?\d+\.\d+$/.test(r)){const a=r.startsWith("L")?r:"L"+r;e.has(t)||e.set(t,new Set),e.get(t).add(a)}}}return e}function F({classifyRules:n,kindAtlas:e,alsoCovers:t,atlasIdForFn:o,producerKinds:s}){const l=new Map,r=new Map,a=(i,c,d)=>{c&&(i.has(c)||i.set(c,[]),i.get(c).push(d))};for(const i of n)a(l,i.atlas,{source:"bin/classify.mjs",id:i.rule});for(const[i,c]of Object.entries(R))a(l,(e??{})[i],{source:"bin/inventory.mjs (syncRisks, via reports.mjs)",id:c});const u=new Map;for(const i of s)u.has(i.kind)||u.set(i.kind,[]),u.get(i.kind).includes(i.source)||u.get(i.kind).push(i.source);for(const[i,c]of Object.entries(e??{}))if(!L.has(i))for(const d of u.get(i)??[])a(r,c,{source:d,id:i});const f=Z(t);for(const[i,c]of f)if(!(L.has(i)||!u.has(i)))for(const d of c)for(const m of u.get(i))a(r,d,{source:m+" (also-covers)",id:i});for(const i of s){if(!i.prefix)continue;const c=o({kind:i.kind+"000"});c&&a(r,c,{source:i.source,id:i.kind+"*"})}return{sIndex:l,rIndex:r}}function J({classifyRules:n,producerKinds:e,atlasIdForFn:t,layers:o}){const s=new Set(o.flatMap(a=>a.rows.map(u=>u.id))),l=[],r=[];for(const a of e){const u=a.prefix?a.kind+"000":a.kind;t({kind:u})||l.push({source:a.source,kind:a.kind})}for(const a of n){if(!a.atlas){l.push({source:"bin/classify.mjs",kind:a.rule,line:a.line});continue}s.has(a.atlas)||r.push({source:"bin/classify.mjs",id:a.atlas,rule:a.rule,line:a.line})}return{noAtlasId:l,unknownAtlasId:r}}function X(n,e){const t=new Set(e.flatMap(s=>s.rows.map(l=>l.id))),o=[];for(const[s,l]of Object.entries(n??{}))t.has(l)||o.push({source:"lib/qa/atlas.mjs KIND_ATLAS",id:l,kind:s});return o}const Q={G:"generated journey \u2014 derived from the route\xD7entity matrix (route\xD7role sweep or a journey), not a standing rule",H:"human intent \u2014 the irreducible part; one question per entity, not automatable from source or the DOM alone",A:"architecture-dependent \u2014 applies only if this repo chose that architecture; a configuration question, not a missing rule"};function V(n,e,t,o){const s=n.det;if(s==="G"||s==="H"||s==="A"){const d=[...(e.get(n.id)??[]).map(m=>({mode:"S",...m})),...(t.get(n.id)??[]).map(m=>({mode:"R",...m}))];return{id:n.id,class:n.class,det:s,status:"not-automatable",note:Q[s]??s,unexpectedDetectors:d}}const l=s.split("+").filter(d=>d==="S"||d==="R"),r=e.get(n.id)??[],a=t.get(n.id)??[],u={S:r,R:a},f=l.filter(d=>u[d].length===0),i=l.flatMap(d=>u[d].map(m=>({mode:d,...m})));if(f.length===0)return{id:n.id,class:n.class,det:s,status:"implemented",detectors:i,missingModes:[]};if(f.length<l.length)return{id:n.id,class:n.class,det:s,status:"partial",detectors:i,missingModes:f};const c=o.filter(d=>d.id===n.id);return c.length?{id:n.id,class:n.class,det:s,status:"deliberately-not-implemented",reason:c.map(d=>`${d.check}: ${d.reason}`).join(" | "),missingModes:f}:{id:n.id,class:n.class,det:s,status:"gap",missingModes:f}}function ct({atlasMd:n,classifySrc:e,probeSrc:t,sweepSrc:o,producerSources:s,kindAtlas:l,alsoCovers:r,atlasIdForFn:a,classifyIdRemap:u=i=>i,reportUnmappedProducers:f=!0}){const{layers:i,deliberate:c}=B(n),d=P(e).map(p=>p.atlas?{...p,atlas:u(p.atlas)}:p).filter(p=>p.atlas!==null),h=(s??[{source:"bin/probe.js",src:t??""},{source:"bin/sweep.mjs",src:o??""}]).flatMap(({src:p,source:g})=>U(p,g)),{sIndex:A,rIndex:_}=F({classifyRules:d,kindAtlas:l,alsoCovers:r,atlasIdForFn:a,producerKinds:h}),b=J({classifyRules:d,producerKinds:h,atlasIdForFn:a,layers:i});f||(b.noAtlasId=[]);const y=X(l,i);b.unknownAtlasId.push(...y);const D=new Set(c.filter(p=>p.id).map(p=>p.id)),j=c.filter(p=>!p.id),k=i.map(p=>{const g=p.rows.map(C=>V(C,A,_,c)),x={implemented:0,partial:0,"deliberately-not-implemented":0,gap:0,"not-automatable":0};for(const C of g)x[C.status]++;const I=g.length-x["not-automatable"];return{id:p.id,n:p.n,title:p.title,rows:g,counts:x,srTotal:I}}),M=new Set(["G","H","A"]),$={fullClass:{implemented:0,partial:0,deliberate:0,gap:0,total:0},modeObligation:{implemented:0,total:0},notAutomatable:{total:0,byDet:{G:0,H:0,A:0}}};for(const p of k)for(const g of p.rows){if(M.has(g.det)){$.notAutomatable.total++,$.notAutomatable.byDet[g.det]++;continue}const x=g.det.split("+").filter(I=>I==="S"||I==="R");$.fullClass.total++,$.modeObligation.total+=x.length,$.modeObligation.implemented+=x.length-(g.missingModes?.length??x.length),g.status==="implemented"?$.fullClass.implemented++:g.status==="partial"?$.fullClass.partial++:g.status==="deliberately-not-implemented"?$.fullClass.deliberate++:$.fullClass.gap++}return{layers:k,overall:$,drift:b,deliberate:c,linkedDeliberateIds:D,unlinkedDeliberate:j}}function tt(n){const e=n.fullClass,t=n.modeObligation,o=n.notAutomatable;return`${e.implemented} of ${e.total} S/R classes fully implemented (${e.partial} partial, ${e.deliberate} deliberate, ${e.gap} gaps); ${t.implemented} of ${t.total} mode obligations met; ${o.total} not-automatable (G ${o.byDet.G}, H ${o.byDet.H}, A ${o.byDet.A})`}const S=n=>String(n??"").replace(/\|/g,"\\|").replace(/\n/g," ");function et(n){return n.status==="implemented"?n.detectors.map(e=>`${e.mode}:${e.id}`).join(", "):n.status==="partial"?`${n.detectors.map(t=>`${t.mode}:${t.id}`).join(", ")} (partial \u2014 missing ${n.missingModes.join("+")})`:n.status==="deliberately-not-implemented"?n.reason:n.status==="not-automatable"?n.note+(n.unexpectedDetectors?.length?` (but a detector exists: ${n.unexpectedDetectors.map(e=>`${e.mode}:${e.id}`).join(", ")} \u2014 atlas Det may be stale)`:""):`no detector for ${n.missingModes.join("+")}`}function dt(n){const e=[];e.push("# Atlas coverage"),e.push(""),e.push("Generated by `qa dev atlas --write` from `references/atlas.md` and the detectors"),e.push("(`bin/classify.mjs`, every JavaScript producer under `bin/` and `lib/`, and"),e.push("`lib/qa/atlas.mjs`). Do not edit by hand \u2014 it will be overwritten on the next"),e.push("`--write`."),e.push(""),e.push(tt(n.overall)),e.push("");for(const t of n.layers){e.push(`## ${t.id} \u2014 ${t.title}`),e.push(""),e.push(`${t.rows.length} classes: ${t.counts.implemented} implemented, ${t.counts.partial} partial, ${t.counts["deliberately-not-implemented"]} deliberate, ${t.counts.gap} gap, ${t.counts["not-automatable"]} not-automatable.`),e.push(""),e.push("| id | det | status | class | detail |"),e.push("|---|---|---|---|---|");for(const o of t.rows)e.push(`| ${o.id} | ${o.det} | ${o.status} | ${S(o.class)} | ${S(et(o))} |`);e.push("")}if(e.push("## Reverse drift"),e.push(""),!n.drift.noAtlasId.length&&!n.drift.unknownAtlasId.length)e.push("None today.");else{if(n.drift.noAtlasId.length){e.push("Finding kinds with no atlas id:"),e.push("");for(const t of n.drift.noAtlasId)e.push(`- \`${t.kind}\` (${t.source})`);e.push("")}if(n.drift.unknownAtlasId.length){e.push("Detectors referencing an atlas id that is not a row in `references/atlas.md`:"),e.push("");for(const t of n.drift.unknownAtlasId)e.push(`- \`${t.id}\` from \`${t.rule??t.kind}\` (${t.source})`);e.push("")}}if(n.unlinkedDeliberate.length){e.push("## Deliberately not implemented, not linked to one class"),e.push("");for(const t of n.unlinkedDeliberate)e.push(`- **${S(t.check)}** \u2014 ${S(t.reason)}`);e.push("")}return e.join(`
|
|
9
|
+
`)+`
|
|
10
|
+
`}const nt=/^##\s+(L\d+)\s+—/,st=/^\|\s*(L\d+\.\d+)\s*\|\s*([A-Z+]+)\s*\|\s*([a-z-]+)\s*\|\s*[^|]*\|\s*(.*?)\s*\|\s*$/,it=new Set(["G","H","A"]),ot=/^(\d+) of (\d+) S\/R classes fully implemented \((\d+) partial, (\d+) deliberate, (\d+) gaps\); (\d+) of (\d+) mode obligations met; (\d+) not-automatable \(G (\d+), H (\d+), A (\d+)\)$/m;function ut(n){const e=n.split(`
|
|
11
|
+
`),t={};let o=null;const s={fullClass:{implemented:0,partial:0,deliberate:0,gap:0,total:0},modeObligation:{implemented:0,total:0},notAutomatable:{total:0,byDet:{G:0,H:0,A:0}}};for(const a of e){const u=a.match(nt);if(u){o=u[1],t[o]={implemented:0,partial:0,"deliberately-not-implemented":0,gap:0,"not-automatable":0};continue}const f=a.match(st);if(!f||!o)continue;const[,,i,c,d]=f;if(t[o][c]!==void 0&&t[o][c]++,it.has(i)){s.notAutomatable.total++,s.notAutomatable.byDet[i]++;continue}const m=i.split("+").filter(h=>h==="S"||h==="R");if(s.fullClass.total++,s.modeObligation.total+=m.length,c==="implemented")s.fullClass.implemented++,s.modeObligation.implemented+=m.length;else if(c==="partial"){s.fullClass.partial++;const h=d.match(/missing ([SR+]+)/),A=h?h[1].split("+").length:0;s.modeObligation.implemented+=m.length-A}else c==="deliberately-not-implemented"?s.fullClass.deliberate++:s.fullClass.gap++}const l=n.match(ot),r=l?{fullClass:{implemented:Number(l[1]),total:Number(l[2]),partial:Number(l[3]),deliberate:Number(l[4]),gap:Number(l[5])},modeObligation:{implemented:Number(l[6]),total:Number(l[7])},notAutomatable:{total:Number(l[8]),byDet:{G:Number(l[9]),H:Number(l[10]),A:Number(l[11])}}}:null;return{perLayer:t,overall:s,claimed:r}}export{q as AtlasParseError,ct as buildCoverage,F as buildDetectorIndex,X as checkKindAtlasIds,V as classifyRow,B as parseAtlasMd,P as parseClassifyRules,ut as parseCoverageDocCounts,U as parseProducerKinds,rt as readProducerSources,dt as renderCoverageDoc,J as reverseDrift,W as stripComments,tt as summaryLine};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
const e=Object.freeze({"console.error":"L4.41","console.pageerror":"L4.41","render.resize-observer-loop":"L4.41","render.dev-overlay":"L4.41","render.hydration-mismatch":"L4.39","delivery.chunk-load":"L1.5","security.csp-violation":"L7.77","security.cookie-samesite-none-insecure":"L7.78","network.requestfailed":"L2.20","network.waterfall":"L2.15","value.leak":"L5.46","render.empty":"L4.34","render.stuck-loading":"L4.37","render.empty-list-no-state":"L4.36","data.rendered-zero-of-n":"L2.22","verify.no-data-traffic":"L2.10","layout.h-scroll":"L8.79","a11y.tap-target":"L8.81","a11y.unlabeled-control":"L9.88","interact.click-occluded":"L6.64","perf.long-task":"L10.93","perf.layout-shift":"L10.94","perf.lcp":"L10.95","perf.layout-thrash":"L10.96","perf.memory-leak":"L10.97","perf.unused-preload":"L10.101","route.flaky":"L1.10","route.not-found-shell":"L11.104","route.unreachable":"L1.3","sync.stale-after-write":"L3.35","sync.no-invalidation":"L3.23","sync.partial-invalidation":"L3.24","mutate.write-lost":"L3.25","mutate.write-rejected":"L6.66","state.silent-failure":"L4.35","state.no-empty-state":"L4.36","state.stuck-spinner":"L4.37","state.blank":"L4.35","state.slow-blank":"L4.34","state.leaks-raw":"L5.55","state.not-intercepted":"L11.101","journey.failed":"L11.107","journey.load-failed":"L11.105","theme.tokens-not-applied":"L8.84","theme.light-hardcoded":"L8.84","render.broken-image":"L8.83","render.virtualized-empty":"L4.44","layout.truncated-no-title":"L5.56","layout.fixed-height-clip":"L8.86","interact.disabled-not-conveyed":"L6.67","value.raw-enum":"L5.57","a11y.no-focus-ring":"L9.91","a11y.keyboard-trap":"L9.89","a11y.focus-order":"L9.90","a11y.contrast":"L9.87","layout.clipped-text":"L8.80","interact.never-appeared":"L6.69","interact.not-clickable":"L6.70","interact.inert-control":"L6.71","interact.click-error":"L6.72","network.endpoint-5xx":"L2.23","network.duplicate-request":"L2.24","network.request-count-below-declared":"L2.25","network.requestfailed.external":"L2.26","route.indirect-landing":"L1.11","theme.check-failed":"L11.111","mutate.failed":"L11.112"}),t=Object.freeze({"value.leak":["L5.47","L5.48","L5.49","L5.50","L5.51"],"perf.layout-shift":["L4.45"]}),r=Object.freeze([["network.http-","L2.11"],["route.status-","L1.6"]]);export{t as ALSO_COVERS_V1,e as KIND_ATLAS_V1,r as PREFIX_V1};
|