agentiqa 1.1.58 → 1.1.59
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/dist/cli.js +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -8,7 +8,7 @@ ${n.message}`),r?new YV.VercelOidcTokenError(r):n}return t}function Am(){let t=(
|
|
|
8
8
|
`),n=e.match(/credential\s+['"`]([^'"`]+)['"`]|\bas\s+['"`]([^'"`]+)['"`]|['"`]([^'"`\s]+@[^'"`\s]+)['"`]/i);return n?n[1]??n[2]??n[3]:e.match(/[\w.+-]+@[\w.-]+\.\w+/)?.[0]}function p1(t){let e=t.credentialRole?.trim();return e||aw([t])}function h1(t){if(t.length<=1)return t;let e=t.map(c=>c.credentialRole?.trim()).filter(c=>!!c),n=t.map(c=>p1(c)).filter(c=>!!c);if(new Set(n.map(c=>c.toLowerCase())).size>1)return t;let s=e[0],i=u1(t),o=s??aw(t),a=o?` as '${o}'`:"";return[{text:i?`Sign in with ${i}${a}`:`Log in${a}`,type:"setup",authRole:"login",...s?{credentialRole:s}:{}}]}function f1(t){let e=[];for(let n=0;n<t.length;){if(t[n].authRole!=="login"){e.push(t[n]),n++;continue}let r=n+1;for(;r<t.length&&t[r].authRole==="login";)r++;e.push(...h1(t.slice(n,r))),n=r}return e}function nl(t,e){return e?.authModeHint==="under_test"?{steps:t,authMode:"under_test"}:t.some(n=>n.authRole)?{steps:f1(t),authMode:"precondition"}:{steps:t}}var Lr="CREDENTIAL_ROLE_NOT_CONFIGURED";function Ls(t){if(typeof t!="string")return null;let e=t.trim();return e.length>0?e:null}function Hn(t,e){let n=Ls(t);if(!n)return{requested:null,canonical:null,state:"not_configured"};let r=n.toLocaleLowerCase(),s=e.map(Ls).find(i=>i?.toLocaleLowerCase()===r)??null;return s?{requested:n,canonical:s,state:"configured"}:{requested:n,canonical:null,state:"not_configured"}}function cs(t){return t.authRole!=="login"?null:Ls(t.credentialRole)}function _d(t,e){let n=new Map;for(let r of e){if(!Number.isInteger(r.stepIndex)||r.stepIndex<0||r.stepIndex>=t.steps.length)throw new Error(`Credential role override has invalid step index: ${r.stepIndex}`);if(n.has(r.stepIndex))throw new Error(`Duplicate credential role override for step index ${r.stepIndex}`);if(t.steps[r.stepIndex].authRole!=="login")throw new Error(`Credential role override must target a login step: ${r.stepIndex}`);let i=Ls(r.credentialRole);if(!i)throw new Error(`Credential role override must be non-empty: ${r.stepIndex}`);n.set(r.stepIndex,i)}return{...t,steps:t.steps.map((r,s)=>{let i=n.get(s);return i?{...r,credentialRole:i}:r})}}var vn="CREDENTIAL_POOL_EXHAUSTED",yo="Project settings \u2192 Project credentials";function wd(t){let e=(t??[]).filter(s=>typeof s=="string"&&s.trim()),n=e.map(s=>`"${s.trim()}"`).join(", ");return`Every shared account for ${e.length===1?`role ${n} is`:`roles ${n} are`} currently in use by another run. Wait for a run to finish, or ask a project admin to add another account to ${e.length===1?"that role":"those roles"} in ${yo}.`}function lw(t,e){if(t!==409)return null;let n;try{n=JSON.parse(e)}catch{return null}if(!n||n.code!==vn)return null;let r=Array.isArray(n.names)?n.names.filter(s=>typeof s=="string"):[];return{error:typeof n.error=="string"?n.error:wd(r),names:r}}function cw(t,e){let n=new Map;for(let r of e)r?.name&&r.login&&n.set(r.name.trim().toLowerCase(),{name:r.name,secret:r.secret,login:r.login});for(let r of t){if(!r?.name)continue;let s=typeof r.login=="string"?r.login.trim():"";n.set(r.name.trim().toLowerCase(),{name:r.name,secret:r.secret,...s?{login:s}:{}})}return Array.from(n.values())}function vo(t,e){let n=new Set((e??[]).filter(i=>typeof i=="string").map(i=>i.trim().toLowerCase())),r=[],s=new Set;for(let i of t??[]){if(typeof i!="string")continue;let o=i.trim().toLowerCase();!o||n.has(o)||s.has(o)||(s.add(o),r.push(i))}return r}var m1=new Set(["password","username","email","login","user"]);function bo(t){return m1.has(t.trim().toLowerCase())}var dw="A-Za-z0-9@._+-";function g1(t){return t.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function y1(t,e="i"){return new RegExp(`(?<![${dw}])${g1(t)}(?![${dw}])`,e)}function Ed(t,e){let n=new Set;if(!t)return n;let r=[...e].filter(i=>typeof i=="string"&&i.trim().length>0).sort((i,o)=>o.trim().length-i.trim().length),s=t;for(let i of r){let o=i.trim(),a=o.toLowerCase();if(n.has(a))continue;let l=y1(o,"gi"),c=!1;s=s.replace(l,d=>(c=!0,"\0".repeat(d.length))),c&&n.add(a)}return n}function sl(t,e){if(!t)return null;let n=new Map;for(let s of e){if(typeof s!="string")continue;let i=s.trim();if(!i||bo(i))continue;let o=i.toLowerCase();n.has(o)||n.set(o,i)}let r=Ed(t,[...n.values()]);return r.size!==1?null:n.get([...r][0])??null}var v1=[{re:/credential\s+['"`]([^'"`]+)['"`]/gi,quoted:!0},{re:/\bas\s+['"`]([^'"`]+)['"`]/gi,quoted:!0},{re:/['"`]([^'"`\s]+@[^'"`\s]+)['"`]/g,quoted:!0},{re:/[\w.+-]+@[\w.-]+\.\w+/g,quoted:!1}];function Td(t){return(t.steps??[]).filter(e=>bd(e))}function If(t){let e=[],n=new Set;for(let r of Td(t)){let s=cs(r);if(s){let o=s.toLowerCase();n.has(o)||(n.add(o),e.push(s));continue}let i=r.text??"";if(i)for(let o of v1){o.re.lastIndex=0;let a;for(;(a=o.re.exec(i))!==null;){let l=(a[1]??a[0]??"").trim();if(!l||!o.quoted&&bo(l))continue;let c=l.toLowerCase();n.has(c)||(n.add(c),e.push(l))}}}return e}var rl=(t,e=[])=>({ok:!0,satisfiedBy:t,referenced:e,missing:[],message:""});function uw(t,e){if(t.authMode==="under_test")return rl("auth_under_test");let n=Td(t);if(n.length===0)return rl("no_signin_step");let r=(e??[]).filter(u=>typeof u=="string"&&u.trim().length>0),s=new Set(r.map(u=>u.trim().toLowerCase())),i=If(t),o=i.filter(u=>!s.has(u.trim().toLowerCase())),a=n.map(cs).filter(u=>u!==null),l=new Set(a.map(u=>u.toLowerCase())),c=o.some(u=>l.has(u.toLowerCase()));if(a.length>0)return o.length===0?rl("name_match",i):{ok:!1,satisfiedBy:null,referenced:i,missing:o,message:Sd(o),...c?{code:Lr}:{}};if(i.length>0&&o.length<i.length)return rl("name_match",i);let d=n.map(u=>u.text??"").join(`
|
|
9
9
|
`);return sl(d,r)!==null?rl("step_reference",i):{ok:!1,satisfiedBy:null,referenced:i,missing:o,message:Sd(o)}}function Sd(t){let e=(t??[]).filter(s=>typeof s=="string"&&s.trim());if(e.length===0)return`This plan declares a sign-in step but does not name the credential it uses, so there is no name to look up against your stored credentials. Add the login this plan needs in ${yo}, and name the credential in the sign-in step so the plan says which one it wants. On a shared project each teammate stores their own login \u2014 nobody else can see it.`;let n=e.map(s=>`"${s.trim()}"`).join(", "),r=e.length===1?"credential":"credentials";return`This plan signs in with the ${r} ${n}, which you have not stored. Add your own in ${yo} using the same ${r==="credential"?"name":"names"} \u2014 the name is only the key the plan looks up, the secret stays yours. On a shared project each teammate stores their own login \u2014 nobody else can see it.`}var bn=t=>t.trim().toLowerCase();function pw(t,e){let n=[],r=new Set;for(let d of e??[]){if(typeof d!="string")continue;let u=bn(d);!u||r.has(u)||(r.add(u),n.push(d))}if(n.length===0)return{required:[],optional:[]};let s=(t??[]).filter(d=>!!d);if(s.length===0)return{required:[],optional:n};let i=n.filter(d=>!bo(d)),o=new Set,a=new Set;for(let d of s){if(d.authMode==="under_test")continue;let u=new Set(If(d).map(y=>bn(y))),f=Td(d).filter(y=>cs(y)===null).map(y=>y.text??"").join(`
|
|
10
10
|
`),p=(d.steps??[]).filter(y=>!bd(y)&&Tf(y.text)).map(y=>y.text??"").join(`
|
|
11
|
-
`),h=Ed(f,i),m=Ed(p,i),g=!1;for(let y of n){let E=bn(y);u.has(E)||h.has(E)?(o.add(E),g=!0):m.has(E)&&(a.add(E),g=!0)}if(!g&&u.size===0)for(let y of n)a.add(bn(y))}let l=n.filter(d=>o.has(bn(d))),c=n.filter(d=>!o.has(bn(d))&&a.has(bn(d)));return{required:l,optional:c}}function b1(t){let e=t;return!e||e.code!==vn?null:Array.isArray(e.names)?e.names.filter(n=>typeof n=="string"):[]}async function hw(t){let e=t.scope?.required??[],n=t.scope?.optional??[],r=t.maxAttempts??3,s=new Set(n.map(l=>bn(l))),i=new Set(e.map(l=>bn(l))),o=l=>{let c=new Set(l.map(d=>bn(d.name)));return n.filter(d=>!c.has(bn(d)))},a=vo([...e,...n],[]);for(let l=0;l<r;l++){if(a.length===0)return e.length>0?{ok:!1,refused:[...e]}:{ok:!0,granted:[],skipped:[...n]};try{let c=a.filter(u=>s.has(bn(u))&&!i.has(bn(u))),d=await t.lease(a,c);return{ok:!0,granted:d,skipped:o(d)}}catch(c){let d=b1(c);if(d===null)throw c;let u=new Set(d.map(h=>bn(h))),f=e.filter(h=>u.has(bn(h)));if(f.length>0)return{ok:!1,refused:f};let p=a.filter(h=>!u.has(bn(h)));if(p.length===a.length)return e.length>0?{ok:!1,refused:[...e]}:{ok:!0,granted:[],skipped:[...n]};a=p}}return e.length>0?{ok:!1,refused:[...e]}:{ok:!0,granted:[],skipped:[...n]}}var Af={GROUNDED_EXPECTATIONS:{key:"GROUNDED_EXPECTATIONS",envVars:["AGENTIQA_GROUNDED_EXPECTATIONS","AGENTIQA_EXPERIMENT_GROUNDED_EXPECTATIONS"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"When on, run_complete batch-judges provisionally-passing steps for expectation drift (heal cosmetic, fail-closed critical/judge-unavailable) instead of trusting the model grade verbatim.",designDoc:"docs/plans/2026-07-07-grounded-expectations-phase-b-design.md",status:"active",added:"2026-07-07",notes:"GRADUATED 2026-07-21 (default on): prod ran env-ON (AGENTIQA_EXPERIMENT_GROUNDED_EXPECTATIONS=1 on the orchestrator) since 2026-07-10 with zero grounded-attributable false-FAIL; nightly regressions in the window were all infra/render flake. This flip normalizes code to the already-live prod behavior \u2014 remove the orchestrator env vars (both namespaces) once this reaches each environment."},LOOP_VISION_ESCALATION_CHAT:{key:"LOOP_VISION_ESCALATION_CHAT",envVars:["AGENTIQA_LOOP_VISION_ESCALATION_CHAT","AGENTIQA_EXPERIMENT_LOOP_VISION_ESCALATION_CHAT"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Extends loop-detection vision-supervisor escalation to the Coordinator/chat lane; off disables it for chat only (the master LOOP_VISION_ESCALATION still governs the Runner/Explorer lanes).",designDoc:"docs/plans/2026-07-07-loop-detection-vision-supervisor-design.md",status:"active",added:"2026-07-07",notes:"Staged default-OFF originally, flipped default-ON once baked (AG-6995) \u2014 reads via killSwitchDisabled today."},PIN_PAGE_GROUNDING:{key:"PIN_PAGE_GROUNDING",envVars:["AGENTIQA_PIN_PAGE_GROUNDING","AGENTIQA_EXPERIMENT_PIN_PAGE_GROUNDING"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Runner-lane pin page-grounding: a criterion's pinned expectedValue must appear in the literal full-page a11y text at verify-evidence capture (parroted grade notes no longer suffice). Detection runs in shadow (telemetry) unless VALUE_GROUNDING_ENFORCE is on; '0' disables detection AND the forced full-snapshot capture entirely.",designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-07-10"},ISSUE_QUOTE_GROUNDING:{key:"ISSUE_QUOTE_GROUNDING",envVars:["AGENTIQA_ISSUE_QUOTE_GROUNDING","AGENTIQA_EXPERIMENT_ISSUE_QUOTE_GROUNDING"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Chat-lane hallucinated-quote gate on report_issue: a quoted literal asserted as visible must appear in the freshly captured full a11y snapshot. Detection runs in shadow (telemetry) unless VALUE_GROUNDING_ENFORCE is on; '0' disables detection AND the forced full-snapshot capture entirely.",designDoc:"packages/engine-core/src/negativeStateEvidence.ts",status:"active",added:"2026-07-10"},VALUE_GROUNDING_ENFORCE:{key:"VALUE_GROUNDING_ENFORCE",envVars:["AGENTIQA_VALUE_GROUNDING_ENFORCE","AGENTIQA_EXPERIMENT_VALUE_GROUNDING_ENFORCE"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Flips both value-grounding gates (PIN_PAGE_GROUNDING, ISSUE_QUOTE_GROUNDING) from shadow telemetry (would_fail / would_bounce diag logs) to enforcement: pin absence records a real per-step oracle failure that fails run_complete; a hallucinated visible quote rejects the report_issue filing.",designDoc:"packages/engine-core/src/killSwitch.ts",status:"active",added:"2026-07-10",notes:"Shadow-first rollout: two adversarial review rounds each surfaced false-fail classes on a hard-fail gate, so enforcement waited on a staging soak. PARKED 2026-07-21 (steering decision, Alex): VERIFY_REOBSERVE_WITHHOLD (graduated default-ON) strictly dominates this hard-fail path \u2014 it closes the same blind-verdict/pin-absence hole with a soft-withhold that never manufactures the false-FAIL classes both review rounds surfaced. Never graduate the enforcement; the oracle_failure recording branch is a deletion candidate. Detection stays: PIN_PAGE_GROUNDING + ISSUE_QUOTE_GROUNDING (default-ON) keep their shadow would_fail/would_bounce telemetry. Remove the staging orchestrator env var (AGENTIQA_EXPERIMENT_VALUE_GROUNDING_ENFORCE) \u2014 the force-ON soak is moot.",graduation:{status:"parked",gate:"Retired in favor of VERIFY_REOBSERVE_WITHHOLD (soft-withhold successor). Do not graduate; delete the enforcement branch once the successor has a clean prod window.",evidence:"F2b eval proof: enforce cannot catch the abstain{snapshot_incremental} class (identical RED both legs); panel + review-round history of false-fail classes on the hard-fail path",owner:"steering (Alex)",review:"2026-08-15"}},RESUME_INPUT_ASK_USER:{key:"RESUME_INPUT_ASK_USER",envVars:["AGENTIQA_RESUME_INPUT_ASK_USER","AGENTIQA_EXPERIMENT_RESUME_INPUT_ASK_USER"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Coordinator turn-continuity: when on, a child Explorer's ask_user that blocks on a MISSING INPUT (a file to upload, a path/value \u2014 not an email/generic async wait) arms a resumable `input_wait` pause that persists the halted child's OBJECTIVE; the next user turn resumes that SAME objective (re-attaching session attachments) instead of falling through to free re-decomposition, and re-asks for the SAME objective when the required file is still absent. Off = byte-identical to today (missing-input ask_user arms no pause; the email-wait / generic-wait paths are unchanged).",designDoc:"docs/plans/2026-07-20-paused-task-resume-design.md",status:"active",added:"2026-07-20",notes:"GRADUATED 2026-07-21 (default on) via the bound-eval arm of its gate: chat/paused-task-resume green 2/2 replicates on origin/staging (pause armed on the missing-file ask_user; resume carried the SAME objective tokens (upload + filename) with an explicit no-re-plan prompt; text-only reply correctly re-asked for the same file) + the earlier recorded GRADUATION-PASS on the lio replay (asess_1784568697674). Organic staging soak was vacuous (organic chat never hits a missing-input upload ask_user \u2014 0 input_wait events in 12 sessions), so the eval arm is the gate per the evidence-count doctrine. Renderer attachment-drop discriminator stays a SEPARATE open item (PostHog coordinator_started.has_attachments). Remove the staging orchestrator env var once this reaches staging."},VERIFY_REOBSERVE_WITHHOLD:{key:"VERIFY_REOBSERVE_WITHHOLD",envVars:["AGENTIQA_VERIFY_REOBSERVE_WITHHOLD","AGENTIQA_EXPERIMENT_VERIFY_REOBSERVE_WITHHOLD"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Runner-lane grade-time re-observe + soft-withhold (batch.verify-never-blind fix). When a pinned STRICT verify criterion cannot be grounded at grade time \u2014 the snapshot is not full (abstain snapshot_incremental/missing/thin) or the value is absent from a full a11y snapshot (would_fail) \u2014 the runtime takes ONE fresh forced-full a11y re-capture and re-grounds before accepting the verdict. Groundable after re-observe (value was present but the grade-time capture was imageless/incremental) \u2192 the model verdict stands; still ungroundable \u2192 the step is SOFT-withheld to a 'warning'+note (NON-confident, never a hard fail, never routed through the VALUE_GROUNDING_ENFORCE oracle_failure path). Closes the blind-verdict hole (F2a canvas + F2b incremental) WITHOUT manufacturing false-FAILs on values present-to-user but absent from the a11y outline (virtualized/scrolled-off rows).",designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-07-21",notes:"GRADUATED 2026-07-21 (default on), same-day evidence gate approved by Alex in lieu of a calendar soak: (1) staging replay-soak on the lio fixture (proj_mqzn31900, 3 sequential replicates of the only expectedValue-pinned plan) \u2014 zero false-withholds across 6 present-pin observations, withholds only on genuinely-absent values, surgical parity (grounded and no-pin verdicts untouched, check-only plans fully inert), no failing run laundered to pass, no material latency delta; (2) the grounded-rescue + Regression A/B legs banked by the fix-gate eval run (F2b re-observe\u2192grounded\u2192pass, healthy batch zero-fire, below-fold DOM value grounds). Product semantics approved by Alex: pinned-strict TRUE-MISMATCH fails also downgrade to warning (observed-value note preserved) until typed-match (AG-7753) restores precise typed fails. The two evals (runner-verify-blind-canvas / -incremental) remain the regression gate: fix OFF must reproduce, ON must resolve. Independent of VALUE_GROUNDING_ENFORCE \u2014 when off, that flag behaves unchanged. Remove the staging orchestrator env var once this reaches staging."},BLOCKED_RUN_DRAFT_PLAN:{key:"BLOCKED_RUN_DRAFT_PLAN",envVars:["AGENTIQA_BLOCKED_RUN_DRAFT_PLAN","AGENTIQA_EXPERIMENT_BLOCKED_RUN_DRAFT_PLAN"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'A loop-blocked Explorer child keeps its work: when the loop detector stops a NON-discovery child before it could file assistant_v2_report, the fallback result carries a reconstructed `draftTestCase` built from the natural-language stepText/intent of the interactions it actually performed (observation-only tools excluded; any step text repeated 3+ times \u2014 the stuck target the detector fired on \u2014 dropped whole; capped at 30 steps; suppressed under 2 steps). Without it the Coordinator emits `tested_areas[].draft_steps: []`, FindingsCheckpointCard filters the area out of "What was just tested", and the product offers NO way to save a run that got blocked (issue #2024: the self-agent signup\u2192first-run journey dead-ended at "Save the completed run as a new test case" for two consecutive nights). Off restores the pre-#2024 behaviour \u2014 a blocked child reports no plan and the run is unsaveable. Verdicts are untouched either way: the run stays blocked, the recommendation stays do_not_ship, and the area is surfaced as partial with its not-tested reason by the existing incomplete-child coverage backfill.',designDoc:"packages/engine-core/src/ExplorerRuntime.ts",status:"active",added:"2026-08-02"},VERIFY_GATED_DONE:{key:"VERIFY_GATED_DONE",envVars:["AGENTIQA_VERIFY_GATED_DONE","AGENTIQA_EXPERIMENT_VERIFY_GATED_DONE"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Cross-checks a 'done'/'passed' claim against deterministic completion-time oracle failures (wait-style); off restores trusting the model's completion claim verbatim.",designDoc:"docs/plans/2026-07-06-verification-gated-done-design.md",status:"active",added:"2026-07-06"},VERIFY_CONFLICT_RECONCILE:{key:"VERIFY_CONFLICT_RECONCILE",envVars:["AGENTIQA_VERIFY_CONFLICT_RECONCILE","AGENTIQA_EXPERIMENT_VERIFY_CONFLICT_RECONCILE"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'Downgrades a verification-conflict force-FAIL to a step-level warning (run not failed) when every plan criterion on the conflicted step passed with a substantiated note AND the only unresolved oracle is an agent-invented wait literal absent from the plan; off restores the unconditional bounce-then-fail-closed (false-negative on incident asess_1784155719395_h62evpmp). Since 2026-08-01 this switch is the UMBRELLA kill for the whole agent-invented-literal family: =0 also disables VERIFY_AGENT_LITERAL_ABSTAIN and VERIFY_AGENT_LITERAL_ABSTAIN_ACTION_SHAPED, so the documented "restores the unconditional force-fail" promise stays true.',designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-07-16"},VERIFY_AGENT_LITERAL_ABSTAIN:{key:"VERIFY_AGENT_LITERAL_ABSTAIN",envVars:["AGENTIQA_VERIFY_AGENT_LITERAL_ABSTAIN","AGENTIQA_EXPERIMENT_VERIFY_AGENT_LITERAL_ABSTAIN"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Last valve of the verify-conflict cascade (after M1 reconcile + M2 withhold): a step whose ONLY unresolved oracle is a wait literal the AGENT invented (absent from the whole saved plan, substring) is degraded to a step-level WARNING instead of force-FAILing the run \u2014 so the synthesized 'Verification step not satisfied' issue is not filed either. TWO LANES ON SEPARATE SWITCHES since 2026-08-01 round 3: THIS flag enforces lane 'grounded_criteria' (default ON) and is also the parent kill for lane 'action_shaped', which has its OWN switch VERIFY_AGENT_LITERAL_ABSTAIN_ACTION_SHAPED (shipped default-OFF/shadow 2026-08-01, GRADUATED to default ON 2026-08-02 on its census \u2014 set it to 0 to put that lane back in shadow without touching this one). Shared preconditions of both lanes: an invented literal, no M1 grounded negative, no model-graded negative on the step (neither a 'failed' step nor a 'failed' criterion is ever softened), and no prior downgrade of the step by another valve (step_already_downgraded \u2014 the same screen M2 carries). Lane 'grounded_criteria': the step has >=1 PLAN-AUTHORED criterion, every one came back graded pass, and every one carries FRESH GROUNDING from this run (an engine-written groundingObservation for this run/step, or an observed/expectedValue the engine finds whole-token on the step's OWN retained full frame) \u2014 ungrounded model grades keep the force-fail, because this gate exists to catch fabricated passes and is not weakened for ungrounded grading. Strictly weaker than VERIFY_CONFLICT_RECONCILE, which needs a substantiation note on every criterion and produces a clean PASS; this one can never produce anything better than an honest amber and is one-directional (RED->AMBER only). NOT covered, deliberately: a PLAN-GROUNDED literal the app renders differently (staging run_b501d461's '50 EUR' vs '\u20AC50.00 net') refuses in BOTH lanes \u2014 opening that lane would re-decide four separate M1/M2 refusals and needs a page-side equivalence read; tracked as follow-up. Detection always runs: off logs verify_conflict_reconcile:agent_literal_abstain with enforced:false PLUS a verify_conflict_reconcile:agent_literal_would_abstain census row, and changes nothing; every refusal logs verify_conflict_reconcile:agent_literal_abstain_inapplicable with its reason AND its lane ('none' for the screens that run before lane selection). VERIFY_CONFLICT_RECONCILE=0 is an UMBRELLA kill for this valve too (both address the agent-invented-literal class, and that switch is documented as restoring the unconditional force-fail).",designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-08-01"},VERIFY_AGENT_LITERAL_ABSTAIN_ACTION_SHAPED:{key:"VERIFY_AGENT_LITERAL_ABSTAIN_ACTION_SHAPED",envVars:["AGENTIQA_VERIFY_AGENT_LITERAL_ABSTAIN_ACTION_SHAPED","AGENTIQA_EXPERIMENT_VERIFY_AGENT_LITERAL_ABSTAIN_ACTION_SHAPED"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"GRADUATED 2026-08-02 (default ON) \u2014 lane 'action_shaped' of the agent-literal abstain valve, shipped SHADOW on 2026-08-01 (round-3 steering split) and flipped on its own census one release later, the #1893/#1895 shadow-first precedent. The lane covers a conflicted verify step with ZERO plan-authored criteria (an ACTION step mislabeled 'verify' whose saved plan object carries text/type only: prod asess_1785560226558_33t8nuos step 4; staging run_b501d461 step 2), where the timed-out wait literal is the agent's own scaffolding rather than anybody's assertion; ON, such a step degrades to a step-level WARNING exactly as lane 'grounded_criteria' does \u2014 never a pass, and the synthesized 'Verification step not satisfied' issue is not filed either. GRADUATION EVIDENCE (verify_conflict_reconcile:agent_literal_would_abstain census, the shadow lane's whole product): 2026-08-01 evening staging replay \u2014 2 instances, invented literal 'How can I help you today?' on a Lio conversation-UX plan, wouldBecome:'warning' 2/2; 2026-08-02 prod batch asess_1785646213123_5xqheb4g \u2014 invented literal 'Ich kann', currentStatus:'passed', wouldBecome:'warning' on a step the run falsely failed. ZERO censused instances where the abstain would have been harmful (i.e. no row where the withdrawn oracle was the only thing catching a real defect), which is exactly the population question the round-2 review raised \u2014 53% of e2e/evals verify steps are criteria-less and the runner SYNTHESIZES a strict criterion from the verify text, so criteria-less is not unasserted, and the census is what shows the enforced lane is not converting real detections into green runs. The model's own negatives remain hard refusals in this lane (action_shaped_step_graded_failed / action_shaped_criterion_graded_failed), so the synthesized criterion still decides when it is graded failed. KILL-SWITCH SEMANTICS UNCHANGED: an explicit AGENTIQA_VERIFY_AGENT_LITERAL_ABSTAIN_ACTION_SHAPED=0 (or the AGENTIQA_EXPERIMENT_ spelling) restores the byte-identical pre-graduation SHADOW behavior \u2014 the classifier still reaches the same 'abstain' verdict, logs verify_conflict_reconcile:agent_literal_abstain with enforced:false plus the verify_conflict_reconcile:agent_literal_would_abstain census row, and mutates NO verdict and suppresses NO issue. Parented by VERIFY_AGENT_LITERAL_ABSTAIN (=0 kills both lanes) and by VERIFY_CONFLICT_RECONCILE (=0 is the family umbrella kill and silences the diags entirely). killSwitch resolves the no-env value from this defaultState (#1729), so graduation = defaultState 'on' AND the key listed in INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire). Acceptance suite: packages/engine-core/src/__tests__/RunnerRuntime.agentLiteralAbstain.test.ts (B1/B3 at the DEFAULT \u2192 amber; B1k/B3k/D1 with =0 \u2192 byte-identical red + census).",designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-08-01"},VERIFY_PLAN_LITERAL_TYPED_EQUIVALENCE:{key:"VERIFY_PLAN_LITERAL_TYPED_EQUIVALENCE",envVars:["AGENTIQA_VERIFY_PLAN_LITERAL_TYPED_EQUIVALENCE","AGENTIQA_EXPERIMENT_VERIFY_PLAN_LITERAL_TYPED_EQUIVALENCE"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Lane B1 of the plan-literal equivalence design (docs/plans/2026-08-02-plan-literal-equivalence-design.md; prod Lio 2026-08-02). SHADOW, DEFAULT OFF since the 2026-08-02 round-5 steering de-scope \u2014 the same shadow-first shape as lane B2 below, VERIFY_AGENT_LITERAL_ABSTAIN_ACTION_SHAPED and #1893/#1895. WHY IT SHIPS OFF RATHER THAN LIVE: THREE CONSECUTIVE adversarial rounds each found a NEW way the page-side scan reads a value that is not on the page (round 2 F2 node-locality/F3 literal ambiguity, round 3 the ten boundary shapes, round 4 D1 contested marker/D2 letter-bearing symbol, round 5 the DE-thousandths and group-separator-truncation holes below). Every round's holes were closed and every round found more; a reader whose defect-discovery rate has not yet flattened does not get to withdraw a grounded negative on a default fleet. WHAT =1 ENABLES: TYPED EQUIVALENCE = PRESENCE at the ONE page read of the verify-conflict cascade (M1 reconcileVerifyConflictsFromSnapshot). When the raw whole-token pinPresentInPage read of a PLAN-AUTHORED wait literal comes back ABSENT, the snapshot is re-read with a deterministic typed scan. SIX PROPERTIES DEFINE THE SCAN, each pinned by acceptance cases through the production wrapper pinEquivalentInPage: (a) NODE-LOCAL \u2014 the unit of scan is a SINGLE a11y node (the raw snapshot's own lines plus flattenAriaSnapshot(...).lines), never the space-joined 'spaced' or separator-free 'stream' haystacks pinPresentInPage uses; amount and currency marker must share one node, because a join turns the newline that separates two nodes into a legal gap char and FABRICATES composites present on neither (measured on five realistic aria shapes, 2026-08-02 round 2 defect F2). (b) UNAMBIGUOUS LITERAL \u2014 the AUTHORED side must carry exactly ONE surviving numeric reading; a both-locales-legal form with a meaningful fraction ('1,500' -> {1500, 1.5}) is refused outright, because under INTERSECT semantics it would equate a 1000x-different page value (round 2 defect F3). (b2) UNAMBIGUOUS PAGE OPERAND (round 5) \u2014 the PAGE atom must ALSO carry exactly one surviving reading after the structural group validation numericCandidates already applies. Under INTERSECT semantics a multi-reading page atom equated a 1000x-different value in the OTHER direction: a DE page rendering 'Rechnungsbetrag 7.890 EUR' (= 7890) reads {7.89, 7890} and satisfied the literal '7.89 EUR'; likewise '2.750'/'2.75 EUR' and '9.990'/'9.99 EUR'. Refusing multi-reading page atoms costs nothing real: 'EUR 50.00 net' is DE-invalid as a thousands group ('.00' is not three digits), so the ordinary renderings stay singletons and still fold. (c) BOUNDARY-GUARDED, MAXIMAL amount runs \u2014 a number-like run may not touch a letter, a digit, a '.'/',' or a '%'/'/'/'-' on either side, so 'EUR 50k', 'EUR 1.5B', 'EUR 50,00abc', 'EUR 50% off', 'EUR 50/Monat' and 'EUR 50-60' all read as NO amount rather than as 50/1.5 (round 3; ten measured false matches, the scaled-magnitude ones off by 1000x-1e9). (c2) NO TRUNCATED GROUP RUN (round 5) \u2014 the boundary class of (c) is a CHARACTER class, and the group separators the world actually renders money with are not in it: the Swiss apostrophe U+2019 and the space family (U+0020/U+00A0/U+202F/U+2009). 'Gesamtsumme: CHF 50\u2019000.00' therefore split into the runs '50' and '000.00', the first of which claimed the adjacent CHF and matched the literal '50 CHF' \u2014 a value 1000x below what the page states. A run adjacent, across group separators only, to another DIGIT is a TRUNCATION of a larger grouped number and yields no atom at all. (d) TYPED IDENTITY \u2014 the literal must parse as an amount+currency composite scalar ('50 EUR' / 'EUR 50' / '\u20AC50' / '50,00 \u20AC' \u2014 recognizeAmountCurrency, which rejects anything carrying another word) or a bare number, and the page must carry a MONEY ATOM with the IDENTICAL ISO-4217 code (symbol<->code via the frozen gazetteer; the ambiguous $ and \xA5 never canonicalize) whose numeric candidate set INTERSECTS the literal's under the existing dual-locale numericCandidates (US 1,234.56 == DE 1.234,56, exact equality, no epsilon). Currency adjacency is horizontal-whitespace-only, so 'EUR 12.00 shipping, 50 items' never reads as EUR 50. FOUND, WITH =1 => the literal counts as PRESENT and takes the EXISTING clean-pass rail (M1 'cleared'), with its OWN decision reason literal_equivalent_in_snapshot and its OWN client note \u2014 the whole-token rail's note ('was present when the run re-checked') is FALSE of this population by construction and is not reused (round 2 defect F4B); no new verdict, no extra browser action. NOT found => today's grounded_negative stands. NO fuzzy matching, NO LLM, NO string similarity: a shape the closed tables cannot canonicalize is not 'probably the same value'. THE CLASS IT WOULD CLOSE: a plan that writes money the way a human writes it while the product renders it the way a locale renders it ('50 EUR' vs 'EUR 50.00 net') produced a force-FAIL plus a HIGH-severity 'Verification step not satisfied' issue in the customer's project while every criterion of the step passed \u2014 that incident still force-fails on a default fleet. DEFAULT OFF = SHADOW, NOT INERT: detection runs in full and the caller censuses verify_conflict_reconcile:literal_typed_equivalence {enforced:false} PLUS a dedicated row verify_conflict_reconcile:literal_typed_equivalence_would_clear {stepIndex, literal, matchType, currency, matched, nodeText, basis, wouldBecome:'passed'}, and mutates NOTHING \u2014 the M1 decision map keeps its grounded_negative, would_fail is logged exactly as with the lane absent, the force-fail and the HIGH-severity issue stand byte-identically, and no capture kind changes. TWO GATES, NOT ONE (round 5 invariance fix): the mutation requires BOTH this switch AND a live VERIFY_CONFLICT_RECONCILE_SNAPSHOT. The decision map is populated even when M1 is in shadow, and its grounded_negative is read UNGATED by two downstream firewalls (M2 and the agent-literal abstain valve) \u2014 so before the fix an opt-in =1 reached THROUGH a disabled M1 and changed verdicts on a build whose operator had switched M1 off. VERIFY_CONFLICT_RECONCILE=0 is the family umbrella kill and silences the census too. Runner lane only (RunnerRuntime run_complete, second escalation).",designDoc:"docs/plans/2026-08-02-plan-literal-equivalence-design.md",status:"active",added:"2026-08-02",graduation:{status:"gated",gate:"NOT graduable as built \u2014 FOUR blockers. THE STANDING ONE: three consecutive adversarial rounds each found a fresh class of page-side false match (round 2 F2/F3, round 3 the ten boundary shapes, round 4 D1/D2, round 5 the two below) and the discovery rate has not flattened, so graduation needs a round that finds nothing new, not merely the closure of the last round's list. (1) PAGE-SIDE SINGLETON-READING SEMANTICS. Round 5 measured the DE-thousandths hole in the direction the round-2 F3 guard does not cover: the AUTHORED side is now required to be unambiguous, but the PAGE side was left multi-reading on the argument that 'the page is not the operand the author controls' \u2014 and under INTERSECT that is exactly what lets a page rendering 7.890 (DE seven-thousand-eight-hundred-ninety) satisfy the literal '7.89 EUR'. The singleton rule shipped in this PR closes the measured shapes; what it does NOT yet have is a systematic argument that the page operand is single-reading everywhere the fold fires, which is what a graduation needs. (2) GROUP-SEPARATOR TRUNCATION. The boundary class of property (c) is a character class and cannot see that a run it accepted is a FRAGMENT of a grouped number: 'CHF 50\u2019000.00' cleared '50 CHF' because U+2019 and the space family are not boundary chars. Closed for the apostrophe and the space family in this PR; the general form (any group separator any locale renders, including the ones no test has enumerated) is not proven closed. (3) SUBJECT BINDING (F1). Node-locality is an AUTHORSHIP guard, not a PLACE one: an equal value on an UNRELATED card clears the presence wait, demonstrated on the lio-mirror/chat-price-format trap where the distractor renders \u20AC7.89 and the anchor \u20AC78.90 \u2014 B1 turns a genuine red into a green there. The census carries nodeText so a clear is auditable post-hoc, but auditability is not a gate; the lane cannot enforce until a subject-binding design exists (bind the matched node to the step's target, or refuse when the page carries more than one atom satisfying the literal). (4) STAGING AND PROD CENSUS. verify_conflict_reconcile:literal_typed_equivalence_would_clear plus the paired literal_typed_equivalence_refused rows, showing the fold fires on genuine locale renderings of the value the step is about and not on pages where the product is actually wrong about the number.",evidence:"an adversarial round that finds NO new page-side false-match class; a systematic (not case-by-case) argument for page-operand single-readingness and for group-separator closure; a subject-binding design with its own acceptance suite that reddens the chat-price-format trap; and a staging+prod literal_typed_equivalence_would_clear / _refused census sized against the population it would clear",owner:"steering (Alex)",review:"2026-08-09"}},VERIFY_PLAN_LITERAL_CRITERIA_DEGRADE:{key:"VERIFY_PLAN_LITERAL_CRITERIA_DEGRADE",envVars:["AGENTIQA_VERIFY_PLAN_LITERAL_CRITERIA_DEGRADE","AGENTIQA_EXPERIMENT_VERIFY_PLAN_LITERAL_CRITERIA_DEGRADE"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Lane B2 of the plan-literal equivalence design (docs/plans/2026-08-02-plan-literal-equivalence-design.md; prod Lio 2026-08-02, wait literal 'Munich' on a German page rendering 'M\xFCnchen'). SHADOW, DEFAULT OFF since the 2026-08-02 round-3 steering de-scope \u2014 the same shadow-first shape as VERIFY_AGENT_LITERAL_ABSTAIN_ACTION_SHAPED and #1893/#1895. WHAT =1 ENABLES: the ONE deliberately scoped-out lane of VERIFY_AGENT_LITERAL_ABSTAIN \u2014 a PLAN-GROUNDED wait literal that B1's typed equivalence could NOT reconcile stops refusing at the literal_plan_grounded provenance screen WHEN the step has >=1 PLAN-AUTHORED criterion, falls through to lane 'grounded_criteria', which applies its FULL, unchanged contract (every authored criterion graded, every one passed, every one seen by the ENGINE on the step's OWN retained frame via criterionValueSeenOnOwnFrame) and degrades the step to a step-level WARNING under the pre-cut reason plan_literal_not_rendered_as_written and the pre-cut client note ('the run waited for this exact wording and did not find it \u2014 the page may present the same value differently'). =1 ALSO arms lane B2's own full-snapshot request at dispatch (verifyWaitCarriesDegradableLiteral), without which the honesty condition has no frame to read and the lane is inert. NEVER a pass: the outcome is the one-directional RED->AMBER the valve already owns, so run.status can go from failed to passed only in the sense that no step is failed, and the synthesized 'Verification step not satisfied' issue is not filed. DEFAULT OFF = SHADOW, NOT INERT: detection runs in full (the provenance screen no longer reads the switch), the classifier reaches the same verdict, the caller logs verify_conflict_reconcile:agent_literal_abstain with enforced:false PLUS a dedicated census row verify_conflict_reconcile:plan_literal_would_degrade {stepIndex, stepText, literal, timeoutMs, reason, provenance, lane, criteriaCount, currentStatus, wouldBecome:'warning', flag}, and mutates NOTHING \u2014 the step keeps its status, the conflict stays in failingConflicts, the force-fail and the HIGH-severity issue stand byte-identically, and NO capture kind changes. The shadow census UNDERCOUNTS BY CONSTRUCTION: with the lane off no full frame is bought, so most of the population refuses one screen earlier and is visible as agent_literal_abstain_inapplicable {reason:'plan_literal_criteria_not_page_grounded', provenance:'plan_grounded', enforced:false} rather than as a would-degrade row; read the two together to size the population. WHAT STAYS RED under =1, unchanged: a criteria-LESS step (lane 'action_shaped' still refuses every plan-grounded literal pre-lane with literal_plan_grounded, lane:'none'), any step the model graded failed or with a failed criterion, any step another valve already marked down, any step whose authored criteria are ungraded or not page-grounded, and any M1 grounded negative (the firewall is untouched \u2014 which is why B1 must clear the equivalence case at M1 rather than here). Parented by VERIFY_AGENT_LITERAL_ABSTAIN (=0 kills this too) and by VERIFY_CONFLICT_RECONCILE (=0 is the family umbrella kill and silences the diags entirely). Runner lane only.",designDoc:"docs/plans/2026-08-02-plan-literal-equivalence-design.md",status:"active",added:"2026-08-02",graduation:{status:"gated",gate:"NOT graduable as built \u2014 three blockers, all found by the round-2/round-3 adversarial verification of this PR and none of them a soak question. (1) HONESTY-NEEDLE REDESIGN. criterionValueSeenOnOwnFrame, the condition that is supposed to pay for the amber, accepts a MODEL-CONTROLLED value: it looks for the criterion's own `observed`/`expectedValue` on the step's frame, and a probe showed ANY on-page token the model chooses to emit buys the amber. 'the engine saw the asserted value' is therefore closer to 'the model quoted something from the page', which is not corroboration of an authored claim; the lane cannot enforce until that predicate is re-designed to read a value the model does not control. (2) F5 BLAST RADIUS. B2 is inert without a full frame, so it requests one at DISPATCH \u2014 before the wait's outcome is known \u2014 which also promotes SUCCEEDING waits to full and widens _domGroundableCandidates (the live predicate-basis candidate set) for steps B2 never touches. Enabling B2 is therefore also a capture-kind change for a wider population and must be re-measured as one. (3) M1-CANARY RE-BASELINE. The late-render canary's failed->warning baseline was recorded with B2 ENFORCING; that decision has to be re-taken against the default-OFF world before the flag can flip. On top of the three: a staging AND prod census of verify_conflict_reconcile:plan_literal_would_degrade plus the plan_literal_criteria_not_page_grounded refusals, showing the lane fires on genuine rendering mismatches (Munich/M\xFCnchen) and not on steps where the product is actually wrong.",evidence:"staging/prod verify_conflict_reconcile:plan_literal_would_degrade census rows + the paired agent_literal_abstain_inapplicable {reason:plan_literal_criteria_not_page_grounded} refusals (population sizing), a re-designed honesty predicate with its own acceptance suite, an F5 capture-kind impact measurement over a full runner-eval pass, and the re-taken m1-canary late-render baseline",owner:"steering (Alex)",review:"2026-08-09"}},VERIFY_LITERAL_PROVENANCE_STEP_SCOPED:{key:"VERIFY_LITERAL_PROVENANCE_STEP_SCOPED",envVars:["AGENTIQA_VERIFY_LITERAL_PROVENANCE_STEP_SCOPED","AGENTIQA_EXPERIMENT_VERIFY_LITERAL_PROVENANCE_STEP_SCOPED"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"GRADUATED 2026-08-03 (default ON) \u2014 STEP-SCOPED LITERAL PROVENANCE, the third provenance class of the agent-literal abstain valve (prod Lio 2026-08-02/03, 5 hits in 24h). Shipped SHADOW on 2026-08-03 and flipped on its own REPLAY-BASED census one release later, the same shadow-first path VERIFY_AGENT_LITERAL_ABSTAIN_ACTION_SHAPED took on 2026-08-02 (#1893/#1895 precedent). GRADUATION EVIDENCE (2026-08-03, ag-rehearsal prod->staging mirror replay, 18 runs over 6 plans: 16 census runs at =0 plus a 2-run enforcing A/B; VERDICT.md at https://s.assets.agentiqa.com/provenance-graduation-2026-08-03/): (1) COUNT AND SHAPE \u2014 exactly 2 verify_conflict_reconcile:agent_literal_would_rescue rows in the 16 shadow runs, both the known '50 EUR' hit (chat-knowledge-qa tp_0370f61d run14 and asst-knowledge-qa run11), both step 2, both planMatchLocation step_3:criterion_1:check, both currentStatus passed -> wouldBecome warning, on runs the build falsely FAILED. (2) NO OVER-TRIGGER \u2014 zero provenance rows on the other 4 plans across 8 runs, and the 4 plan_grounded refusals were retained as authored, so the population did not creep into the typed-match equivalence track's half. (3) ENFORCING A/B \u2014 run14 (=0): step 2 failed, run failed, would_rescue censused; run18 (=1): step 2 warning, run PASSED, enforced:true and no census row, i.e. the rescue lands as the one-directional RED->AMBER and nothing else moved. (4) PROD RECOMPUTE \u2014 all 5 documented prod hits recomputed under the step-scoped read: 4 remap to agent_invented_plan_coincident at exactly the documented locations; the 5th (m77p3hky) became agent_invented after lio edited that plan at 2026-08-03T09:01:10Z, i.e. it left the class by an authoring change, not by a read change. THE CLASS: the valve's provenance screen asks 'did the AUTHOR write this literal?' against the WHOLE plan document (title + every step's text/criteria, loose substring). When an INVENTED wait literal COINCIDENTALLY appears somewhere else in the plan \u2014 the plan title, or (in all 5 measured hits) a NEIGHBOURING step's criterion \u2014 the screen returns plan_grounded, the criteria-LESS lane 'action_shaped' refuses before lane selection with agent_literal_abstain_inapplicable {reason:'literal_plan_grounded', lane:'none'}, and the action step keeps its force-FAIL while every criterion of its paired verify step passed. Measured: '50 EUR' (asess_1785644983561_n5worrfv step 2, matched step 3's criterion; app renders '\u20AC50 (net)'), 'Munich' (o7hvcun7 step 2, matched step 3's criterion; app renders 'M\xFCnchen'), the German prompt of step 2 (5xqheb4g step 4, matched step 2's TEXT), 'QA Base \u2014 Draft Completion (do not modify)' (m77p3hky step 4, matched step 5's criterion), '50 EUR' again on the 08-03 build (r3ghgy68 step 2). All five failing steps carry ZERO plan-authored criteria, so lane B2's escape hatch (which needs >=1 criterion) cannot reach any of them. WHAT =1 ENABLES: the provenance read is SCOPED TO THE FAILING STEP'S OWN authored text (buildStepPlanHaystack \u2014 its instruction plus each of its criteria's check/expectedValue) with the SAME loose lowercased-substring matcher as before, and splits the old plan_grounded population in two \u2014 literal ON the step's own text stays plan_grounded and every existing refusal is byte-identical; literal absent from the step but present ELSEWHERE in the plan becomes the NEW provenance agent_invented_plan_coincident, which falls through the plan-grounded screen into ordinary lane selection and is then decided by each lane's UNCHANGED contract (lane 'action_shaped': no model-graded negative, no prior downgrade, no M1 grounded negative; lane 'grounded_criteria': every authored criterion graded, passed and engine-grounded this run via criterionGroundedThisRun \u2014 the invented-literal bar, since a literal the author did not write on this step is not an authored claim about it). Literal absent from the plan entirely is agent_invented, unchanged. The two reads bracket the population EXACTLY: plan_grounded + agent_invented_plan_coincident is precisely the old plan_grounded set (the locator falls back to 'plan_document' for the join-boundary residue), so no literal can be reclassified into the default-ON agent_invented path by this change. OUTCOME CONTRACT UNCHANGED: the only mutation is the valve's one-directional RED->AMBER step-level WARNING, never a pass (#2002 doctrine \u2014 a coincidence buys a warning, never green), and the M1 grounded-negative firewall plus both model-negative screens still refuse. KILL-SWITCH SEMANTICS (=0 under either spelling) RESTORE THE BYTE-IDENTICAL PRE-GRADUATION SHADOW, NOT AN INERT PATH: detection always runs, the classifier reaches the same verdict, the caller logs verify_conflict_reconcile:agent_literal_abstain {enforced:false} PLUS a dedicated census row verify_conflict_reconcile:agent_literal_would_rescue {stepIndex, stepText, literal, timeoutMs, reason, provenance:'agent_invented_plan_coincident', planMatchLocation, lane, criteriaCount, currentStatus, wouldBecome:'warning', flag} and mutates NOTHING \u2014 the step keeps its status, the conflict stays in failingConflicts, the force-fail and the synthesized 'Verification step not satisfied' issue stand byte-identically. So an operator who kills the class keeps the measurement. NO capture kind changes in EITHER state (this valve reads no page and calls no model; lane B2's full-frame force is untouched because it is gated on waitLiteralAuthoredOnStep, which this class fails by definition). ONE TELEMETRY DELTA, stated rather than hidden: a coincident literal that reaches a lane reports that lane's reason (e.g. action_shaped_criterion_graded_failed) instead of the pre-2026-08-03 literal_plan_grounded, and its lane field is no longer 'none' \u2014 the census got more specific when the read was scoped, independently of enforcement. The parents are unchanged \u2014 VERIFY_AGENT_LITERAL_ABSTAIN=0 kills both lanes and VERIFY_CONFLICT_RECONCILE=0 is the family umbrella kill that silences the census too. killSwitch resolves the no-env value from this defaultState (#1729), so graduation = defaultState 'on' AND the key listed in INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire). Acceptance suite: packages/engine-core/src/__tests__/RunnerRuntime.agentLiteralAbstain.test.ts \u2014 B2 (the prod shape verbatim), P2, P3d, P9 leg (a) and P10 leg (b) at the DEFAULT -> amber, never a pass; B2k, P3, P5b, P9 leg (b) and P10 leg (a) with =0 -> byte-identical red + census; P1/P1e pin that the own-step (genuinely authored) read never moves; P4 pins the agent_invented path byte-identical; P8 pins that a model-graded negative is still refused; P9 is asserted in BOTH states because the coincident/agent_invented split is what makes this switch reach the population at all (agent_invented is held by VERIFY_AGENT_LITERAL_ABSTAIN, not by this key). Runner lane only.",designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-08-03"},VERIFY_NOTE_REFUTES_CRITERION:{key:"VERIFY_NOTE_REFUTES_CRITERION",envVars:["AGENTIQA_VERIFY_NOTE_REFUTES_CRITERION","AGENTIQA_EXPERIMENT_VERIFY_NOTE_REFUTES_CRITERION"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"SELF-CONTRADICTION GATE \u2014 ships ENFORCING (default ON) on a measured population. THE DEFECT (false-PASS, the direction that destroys customer trust): a criterion recorded passed:true while its OWN note states the opposite, inside a single verdict record, and the run reported PASSED. TWO measured instances, one on PROD. (1) PROD run_35ffd2b2 / asess_..._eobjjo88, lio's 2026-08-04 04:14Z batch on engine 8741da063, plan 'Lio Assistant - conversation UX' step 3: STRICT criterion 'The answer addresses the question about cleaning-services suppliers (it mentions at least one supplier or a count)' booked passed with note 'The assistant addresses the topic of cleaning-services suppliers, stating it couldn't find specific info but offering to help narrow down the search.' The SAME underlying lio behaviour correctly FAILED a sibling run in the same batch (8vc9g930), so the false pass did not merely mis-grade a criterion \u2014 it ABSORBED a real customer-visible defect our own product had already proven elsewhere. (2) MIRROR run_9bec8ba8 (c13) step 3, plan tp_3d5427b9 'asst-negotiation-email': STRICT criterion 'An email draft is shown with an editable subject and body (an email composer, not just prose in the chat)' booked passed with note 'An email draft was shown in a composer-like UI, but the elements were not programmatically editable (not contenteditable).' Ground truth settled by two independent methods across five sibling replicates: a contenteditable DOM query returned only the chat composer (b13) and an actually-attempted type_text_at into subject and body FAILED (f14, f15). NO existing gate caught either: in c13 step 3 predicate_basis_verify was inconclusive/dom_not_resolvable, typed_atom_floor abstained on no_structured_observed, criteria_containment and ungrounded_pass were silent. WHAT =1 ENABLES: every graded criterion still reported passed after pin substantiation is read by detectCriterionSelfContradiction (packages/engine-core/src/criterionSelfContradiction.ts) and, on a `contradicted` verdict, has `passed` flipped to false \u2014 the SAME single mutation substantiatePinnedCriterion makes for an uncited pin, so the outcome is decided by the ONE existing chokepoint deriveStepStatusFromCriteria: a STRICT criterion FAILS its step, a non-strict one WARNS it. No new status, no new vocabulary, no note rewrite (the criterion's own note already says why, in the grader's own words). ONE-DIRECTIONAL: only ever reached for a grade that is still passed, so it can never rescue a failure and never makes anything green. WHY FAIL RATHER THAN WITHHOLD for a strict criterion: a withhold means 'we could not confirm', and this is not that \u2014 the note is the grader's own AFFIRMATIVE statement that the checked property is ABSENT, i.e. evidence AGAINST the claim rather than missing evidence, and in both measured cases an independent method proved the fail correct. TWO LANES, because the class has two shapes and one mechanism cannot reach both. LANE A: the note negates a term the CHECK ITSELF USES, scoped \u2014 an explicit negator from a closed list, and a >=4-char non-stopword subject token of the check's own main clause inside that negator's 60-char forward window (the mirror c13 shape, negatedTerm 'editable'). LANE B: the note reports the app FAILED TO PRODUCE what the check REQUIRES, IN WORDS THE CHECK NEVER USES \u2014 a closed NOT-FOUND list (couldn't/could not/did not/unable to + find|locate|retrieve|provide|identify|return|surface; 'no results|matches|information|data|details|suppliers|records'; 'not found'; 'found no|nothing|none') against a closed PRESENCE-REQUIREMENT verb list in the check (mentions|shows|displays|contains|includes|lists|states|reports|identifies|names|cites|returns|surfaces|provides|presents|specifies). The PROD case is lane B and lane A provably cannot see it: 'couldn't find specific info' and 'mentions at least one supplier or a count' share NO surface term, and the check's 'or' screens lane A out anyway. NO LLM, NO FUZZY SCORING, NO NETWORK \u2014 two strings the caller already holds, so shadow and enforcement are the same computation and OFF costs nothing. FOUR SCREENS, EACH KILLING A MEASURED FALSE POSITIVE of the naive version (a bare 'does the note contain a negation' scan fires on 7 of the mirror corpus's 526 passed criteria and only ONE is the defect; THREE of the six false positives are on STRICT criteria, so a naive gate shipped enforcing would have turned three HONEST passes into hard failures \u2014 the one direction this gate may never take): check_disjunctive (a09 \u2014 check 'mentions the tracking number QACORE-TRACK-0001 and/or the carrier DHL', note 'Tracking number ... was not mentioned'; DHL WAS mentioned so the disjunction is satisfied and the note refutes nothing; also e05); check_carries_negation, read on the check's MAIN CLAUSE (f01/e01 \u2014 check '\"Final Sign-off\" has not started', note 'Final Sign-off is Not Started', where the note CONFIRMS a check whose own subject is negative; also a11/b11 \u2014 check '... (webshops, private expensing) is not permitted' vs note 'you must not use private expense reimbursement'); GLOSS STRIPPING of quoted spans and parenthetical asides from BOTH texts before anything is read (a08 \u2014 check and note both name the plan title 'QA Base \u2014 Submission Checks (do not modify)' and the negation lives INSIDE THE TARGET NAME, the exact class stripTargetLiteralFromProse exists for; and f13/f16 \u2014 note 'Actual text: \"Payment terms of 60 days end of month instead of 30 days\"', a page quotation rather than the grader's own claim); and CONTRASTIVE EXCLUSION \u2014 'instead of' / 'rather than' / 'but' / 'however' contrast, they do not negate, and 'instead of' was exactly f13/f16's naive hit. CENSUS THAT PAYS FOR ENFORCING: 2 fires over 698 passed criteria, both of them the known defects, ZERO honest passes moved \u2014 1 of 168 in lio's PROD 04:14Z batch (eobjjo88, lane B), where an independent hand audit of the same batch found exactly that one note-contradicted pass so the detector matches the audit, and 1 of 530 in the 46-run prod-parity mirror (c13, lane A). KNOWN BOUNDARIES, stated rather than hidden: a check whose main clause carries any negator abstains wholesale (a genuine self-contradiction there is missed \u2014 f01/e01/a11/b11 are four measured reasons to keep it); a negation the grader writes INSIDE a quotation abstains, because transcription cannot be told from assertion there; morphology is not modelled, so 'mentions' in the check vs 'mentioned' in the note abstains. =0 UNDER EITHER SPELLING IS SHADOW, NOT INERT: detection still runs, the caller logs note_refutes_criterion:would_fail {stepIndex, criterionIndex, lane, reason, negatedTerm, strict, check, note, currentStatus, wouldBecome, decision, enforce:false, mutated:false, flag} and mutates NOTHING, so an operator who kills the class keeps the measurement. A SECOND census row, emitted in BOTH states, sizes the SUPPRESSED population: note_refutes_criterion:suppressed fires when a note DID carry an explicit negator and a screen declined it \u2014 the row a graduation or rollback review reads to see whether the screens are over- or under-refusing. Notes with nothing negative in them are silent, because they carry no signal and would drown it. Acceptance suite: packages/engine-core/src/__tests__/criterionSelfContradiction.test.ts (both prod/mirror defects verbatim, all eight measured honest passes as regression pins, and a mutation guard per screen) + RunnerRuntime.noteContradictsCriterion.test.ts (the cascade: strict -> step failed, non-strict -> step warning, =0 -> byte-identical pass + census). Runner lane only. See docs/VERDICT-GATES.md.",designDoc:"docs/plans/2026-08-04-criterion-self-contradiction-design.md",status:"active",added:"2026-08-04"},VERIFY_STRICT_PASS_SUBSTANTIATION:{key:"VERIFY_STRICT_PASS_SUBSTANTIATION",envVars:["AGENTIQA_VERIFY_STRICT_PASS_SUBSTANTIATION","AGENTIQA_EXPERIMENT_VERIFY_STRICT_PASS_SUBSTANTIATION"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"SHADOW, DEFAULT OFF \u2014 the SECOND shape of the 2026-08-04 false-PASS class: a STRICT criterion booking a pass with NO substantiation at all. Measured on PROD in run_783593d9 (mirror label e13) step 3, where the same plan-13 editability criterion recorded passed:true with note, observed AND groundingObservation ALL absent \u2014 and where all 10 strict passes in that run carried zero criterion-level substantiation. WHY IT IS SHADOW AND NOT ENFORCING, with the number: requiring substantiation to book a strict pass would flip legitimate passes EN MASSE, which is precisely the harm the sibling VERIFY_NOTE_REFUTES_CRITERION gate exists to prevent. 104 of 446 strict passes in the 46-run prod-parity mirror (23.3%) carry no substantiation of any kind, spread over 21 of its 47 runs, and those runs are dominated by HONEST passes (a06, a07, a09, a11, a12, a14, b06, b07, b10, b12, b16 are all honest-pass rows in the rehearsal SCORE.md). On lio's PROD 2026-08-04 04:14Z batch the same count is 79 of 143 strict passes. An independent audit of that batch corroborates the weakness is SYSTEMIC rather than a verdict bug: only 47 of 168 passes carry a distinct page literal at all \u2014 62 are prose self-echo, 59 ungrounded, and 56 already carry ungrounded_pass:would_withhold. Enforcing a substantiation requirement would therefore touch about a third of ALL passes, so the fix belongs at the SOURCE (what the grader is required to transcribe) and is its own track, not a verdict change to make alongside a false-pass fix. IT EXTENDS THE pin_substantiation FAMILY RATHER THAN DUPLICATING IT: substantiatePinnedCriterion already flips a passing criterion whose note does not cite its pin, but returns the grade VERBATIM when planCriterion.expectedValue is empty \u2014 which is every PROSE criterion, including this one. strictPassLacksSubstantiation (packages/engine-core/src/criterionSelfContradiction.ts) is exactly that abstention made countable, on the same 'the grade's evidence is its note' doctrine. WHAT THE DEFAULT-OFF STATE DOES: detection always runs and the caller emits strict_pass_unsubstantiated:would_withhold {stepIndex, criterionIndex, check, currentStatus, wouldBecome:'warning', decision:'abstain', enforce, mutated:false, flag} while mutating NOTHING \u2014 no status write, no note, byte-identical verdicts. WHAT =1 WOULD ENABLE is deliberately NOT WIRED in this PR: the flag currently selects only the census row's `enforce` field, so setting it to 1 changes no verdict. Graduating this class requires the source fix plus its own census, not an env flip. Runner lane only. See docs/VERDICT-GATES.md.",designDoc:"docs/plans/2026-08-04-criterion-self-contradiction-design.md",status:"active",added:"2026-08-04",graduation:{status:"gated",gate:"NOT graduable as built, and deliberately so. Enforcing it would demote 104 of 446 strict passes in the mirror corpus (23.3%, 21 of 47 runs) and 79 of 143 in lio's prod 04:14Z batch, overwhelmingly on runs that are honest passes \u2014 a mass false-fail, i.e. the opposite defect. The blocker is not a soak: the SOURCE of the weakness is that a strict prose criterion is not required to transcribe anything (only 47 of 168 prod passes carry a distinct page literal; 62 are prose self-echo). Graduation requires (1) the grading contract changed so a strict pass must carry a transcribed page value, (2) a re-census showing the unsubstantiated population has collapsed, and (3) a decision on the residual population that legitimately cannot transcribe (visual/layout criteria).",evidence:"a strict_pass_unsubstantiated:would_withhold census on staging AND prod after the grading-contract change, showing the population collapsed rather than merely moved, plus the ungrounded_pass:would_withhold overlap so the two are not double-counted",owner:"steering (Alex)",review:"2026-08-11"}},VERIFY_CONFLICT_NEGATION_BAIL_WITHHOLD:{key:"VERIFY_CONFLICT_NEGATION_BAIL_WITHHOLD",envVars:["AGENTIQA_VERIFY_CONFLICT_NEGATION_BAIL_WITHHOLD","AGENTIQA_EXPERIMENT_VERIFY_CONFLICT_NEGATION_BAIL_WITHHOLD"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"FALSE-FAIL REMOVAL \u2014 ships ENFORCING (default ON) because it only ever runs RED->AMBER and removes a verdict the run's own record contradicted. THE DEFECT: mirror run_86de13b1 (b05) step 3 of plan tp_4ce9dd2d 'chat-item-search' was recorded status:failed \u2014 and the RUN failed with it \u2014 while ALL FOUR of its criteria were passed:true, each carrying a groundingObservation quoting the page ('Inacopia Kopierpapier Elite A4 90g 500Bl' visible, price '7,89 \u20AC', card 'B\xFCrowelt Schmidt GmbH & Co. KG'). The client summary read 'Failed: verification for step 3 ... did not pass \u2014 the expected content never appeared on the page and was not confirmed on a re-check, even after a retry', which the run's own step_results contradict. Rate 1 of 5 replicates; the trigger is the model choosing to scroll back and re-verify, and this plan is seq 4 in lio's nightly batch. MECHANISM: the oracle chain declined in sequence and the LEGACY verify-conflict force-fail owned the verdict BY DEFAULT \u2014 verify_conflict_reconcile:inapplicable reason=negation_marker literal='Inacopia ...', then verify_conflict_withhold:inapplicable reason=negation_marker (M2 is OFF on prod anyway), then verify_conflict_reconcile:agent_literal_abstain_inapplicable lane=none provenance=plan_grounded reason=literal_plan_grounded. M1's negation_marker bail is triggered by lio's own DEFENSIVE criterion wording ('those do not count', 'their presence or absence must not be asserted') and is CORRECT \u2014 a PRESENCE clear is unsound for an assertion that something is GONE, and clearing there would launder a genuine failure into a pass \u2014 so the bail itself is untouched by this flag. The bug is the inference that followed it: 'M1 could not clear this' is an ABSENCE OF PROOF, not proof of failure. WHAT =1 ENABLES: a new rung between the agent-literal abstain valve and the legacy force-fail. For a step in failingConflicts whose M1 decision reason is EXACTLY negation_marker, whose status is still passed, and which carries >=1 graded PLAN-AUTHORED criterion with ALL of them passed, the oracle failure is cleared and the step lands in the WARNING band with an honest client note (clientNegationBailWithheldNote \u2014 'could not be re-checked automatically because this step is worded as a negative or absence condition. Every check the plan wrote for this step passed; the re-check itself is reported as unconfirmed rather than passed or failed'), on exactly the machinery M1's stale-scope rung and M2's withhold already use. Because the conflict leaves failingConflicts, the synthesized 'Verification step not satisfied' issue is not filed for it either, and terminationReason stays 'completed'. NEVER A PASS and never a silent green: the outcome is the one-directional RED->AMBER the valve family already owns. NARROW BY CONSTRUCTION \u2014 every condition is a refusal the class needs: no other M1 bail reason qualifies; authorship is read via the SAME `unauthored` mark deriveStepStatusFromCriteria honours, so a fabricated grade can neither buy nor block the amber; and a step the model itself graded negative, or that another valve already marked down, refuses \u2014 so this can never rescue a model-reported failure. =0 UNDER EITHER SPELLING IS SHADOW, NOT INERT: the eligibility computation still runs and the caller logs verify_conflict_negation_bail:would_withhold {stepIndex, stepText, literal, criteriaCount, currentStatus, wouldBecome:'warning', decision:'withhold', enforce:false, mutated:false, flag} while the force-fail, the failed step status, the client summary and the synthesized issue all stand byte-identically. Reads no page and calls no model, so both states cost the same. Acceptance suite: packages/engine-core/src/__tests__/RunnerRuntime.negationBailWithhold.test.ts (the b05 shape verbatim -> warning + run not failed at the default; =0 -> byte-identical failed + census; and the refusals: a non-negation_marker bail, a model-graded failed criterion, an already-downgraded step, a step with zero authored criteria, and a step whose only passing grades are `unauthored`). Runner lane only. See docs/VERDICT-GATES.md.",designDoc:"docs/plans/2026-08-04-criterion-self-contradiction-design.md",status:"active",added:"2026-08-04"},VERIFY_RECONCILE_CLEAN_PASS:{key:"VERIFY_RECONCILE_CLEAN_PASS",envVars:["AGENTIQA_VERIFY_RECONCILE_CLEAN_PASS","AGENTIQA_EXPERIMENT_VERIFY_RECONCILE_CLEAN_PASS"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Presentation of a VERIFY_CONFLICT_RECONCILE-reconciled verify step: on, the reconciled step is a clean PASS and the reconciliation is recorded only in the structured verification_conflict_reconciled diag event (no engine-jargon note on the user-facing step); off restores the legacy step-level WARNING plus the explanatory note. Independent of VERIFY_CONFLICT_RECONCILE, which decides WHETHER a conflict reconciles at all \u2014 this only changes how an already-reconciled step is surfaced.",designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-07-22"},VERIFY_PRESENCE_WAIT_FLOOR:{key:"VERIFY_PRESENCE_WAIT_FLOOR",envVars:["AGENTIQA_VERIFY_PRESENCE_WAIT_FLOOR","AGENTIQA_EXPERIMENT_VERIFY_PRESENCE_WAIT_FLOOR"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'Raises a verify-step presence oracle wait (wait_for_element on a `verify` plan step) to a minimum budget (20s) so a slow-rendering but PRESENT element \u2014 e.g. Miro\'s Templates carousel "Blank board" card, which paints several seconds after the dashboard is otherwise interactive \u2014 is not falsely failed by the 5s default wait budget (staging step-5 login/dashboard flake, sessions asess_1784961071757_cqwxon46 / asess_1784960734951_4t0zaxp1, where the next action successfully CLICKED "Blank board"). Floor-only: never lowers a larger model-supplied timeout; off restores the model-supplied / 5s-default budget. Fail-closed preserved \u2014 a genuinely-absent element still times out at the larger budget and records the same oracle failure, so no false-PASS is introduced. Applied in RunnerRuntime.raiseVerifyPresenceWaitBudget (Runner/test-plan lane only; Explorer/Coordinator have no plan steps).',designDoc:"packages/engine-core/src/verifyPresenceWaitBudget.ts",status:"active",added:"2026-07-25"},ABSENCE_AWARE_VERIFY:{key:"ABSENCE_AWARE_VERIFY",envVars:["AGENTIQA_ABSENCE_AWARE_VERIFY","AGENTIQA_EXPERIMENT_ABSENCE_AWARE_VERIFY"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Absence-assertion verify oracle (issue_742c9da8), a BEFORE\u2192AFTER differential. A verify step that asserts a NEGATIVE (the target is GONE) is verified with the presence-only wait_for_element, whose legitimate timeout on the correctly-absent, PLAN-GROUNDED literal is recorded as an oracle failure and force-FAILS the run at run_complete (canReconcileVerificationConflict refuses the plan-grounded literal \u2014 correct for a PRESENCE assertion, wrong for an absence one). Detection ALWAYS runs (shadow): for a conflicted verify step whose step text / any criterion check asserts THIS TARGET's absence (checkTextAssertsAbsence \u2014 the target literal is stripped first, then an EXPLICIT absence lexeme must survive; a negation inside the target NAME or an incidental 'not' cannot route it) and whose unresolved oracle is a wait-style action with a captured target literal, it classifies CONFIRMED vs ABSTAIN and emits an `absence_verify_oracle:shadow` diag. CONFIRMED requires the wait-literal's OWN before\u2192after transition: (a) GENUINELY ABSENT from a FULL, substantive, NON-canvas, non-load-failure AFTER snapshot retained for that step (never the model's note), AND (b) pinPresentInPage-TRUE in an EARLIER full+whole-page+substantive+same-origin BEFORE snapshot (the presence ledger \u2014 reusing the retained full-snapshot maps), AND every graded criterion passed substantiated. The weak container-noun positive anchor is DROPPED as the load-proof (before-presence + whole-page liveness replaces it); a load-failure/retry interstitial AFTER page is REJECTED (snapshotShowsLoadFailure). When this flag is ON it ENFORCES: a CONFIRMED absence reconciles the conflict to a PASS (via the VERIFY_CONFLICT_RECONCILE clean-pass rail), and an ABSTAIN (no full snapshot / canvas / target still present / load-failure after / NO before-presence \u2014 the target was never shown present / unsubstantiated criteria) soft-withholds the step to a WARNING (never a hard fail, never a clean pass). Off leaves every verdict byte-identical (the plan-grounded absence timeout still force-FAILs) with the shadow diag only. Positive (presence) assertions are untouched (checkTextAssertsAbsence false \u2192 inapplicable \u2192 the existing timeout-fails behavior). Runner lane only (RunnerRuntime run_complete).",designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-07-25",notes:"GRADUATED 2026-07-26 (default ON) \u2014 the FIRST live trust-verdict graduation. Enforcement (reconcile-to-pass on CONFIRMED / soft-withhold-to-warning on ABSTAIN) is now the no-env default; detection had run in shadow since 2026-07-25 (absence_verify_oracle:shadow diag) \u2014 the PIN_PAGE_GROUNDING / VERIFY_REOBSERVE_WITHHOLD shadow-first precedent. GRADUATION EVIDENCE: the kind-agnostic graduation benchmark (#1857, e2e/benchmark/) returned GATE=GO on the absence corpus (4 false-FAILs fixed \u2192 PASS, 0 regressions, 0 new false-PASS, 0 marginal LLM cost) and is adversarially proven able to say NO-GO; a fresh-build re-confirm held (confirmed-absence\u2192PASS fixes #1816 / unconfirmable\u2192WARNING / still-present\u2192no false-PASS; the full engine-core suite is byte-identical ON vs OFF except the graduated verdicts). HARD CONSTRAINT (unchanged): confirm fires ONLY on the wait-literal's OWN before\u2192after transition (present in an earlier same-origin full+substantive+non-canvas snapshot, absent from the non-load-failure after one) read from real page snapshots \u2014 never the model's note nor a disjoint absence lexeme \u2014 so a hallucinated 'it's gone', a never-loaded presence target (compound presence+absence), and a silently-blank list echoing the container noun all abstain to WARNING rather than confirming; no false-PASS is reintroduced. killSwitch resolves the no-env value from this defaultState (#1729), so graduation = defaultState 'on' AND ABSENCE_AWARE_VERIFY listed in INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire); an explicit AGENTIQA_ABSENCE_AWARE_VERIFY=0 still restores the byte-identical pre-graduation force-FAIL behavior. Read site: packages/engine-core/src/RunnerRuntime.ts (absenceAwareVerifyEnabled). Acceptance tests: packages/engine-core/src/__tests__/RunnerRuntime.absenceVerifyFalseFail.test.ts (end-to-end, now green for the right reason) + RunnerRuntime.absenceVerifyHardening.test.ts. Claim runner.absence-assertion-verify is now bound green."},GROUNDED_STATE_VERIFIER:{key:"GROUNDED_STATE_VERIFIER",envVars:["AGENTIQA_GROUNDED_STATE_VERIFIER","AGENTIQA_EXPERIMENT_GROUNDED_STATE_VERIFIER"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Grounded-state verifier \u2014 S1 escalation gate + shadow instrumentation (Tier-2 vision-extraction cost sizing; design docs/plans/2026-07-25-grounded-state-verifier-design.md). SHADOW-ONLY / MEASUREMENT-ONLY: this slice makes NO model call and NEVER alters a verdict, verdict input, or any other diagnostic. When on, the runner's Tier-1 deterministic pin page-grounding oracle (checkPinPageGrounding), at each GROUNDABILITY abstain on a step carrying a countable expectedValue pin \u2014 canvas_dominant, a non-full/incremental a11y snapshot, or a thin/missing snapshot (NOT the transient/ephemeral-text abstain) \u2014 consults the surface-agnostic capture-groundability signal (captureModeGroundsAbsence, keyed on captureMode, NOT a surface-name check) and emits a structured `verifier_escalated` diag {stepIndex, reason, assertionKind, captureMode, wouldNeedTier2:true} for a capture a vision extractor could ground (the genuine Tier-2 candidate), or `verifier_escalation_abstained` {\u2026, wouldNeedTier2:false, floor:'inconclusive'} when even vision cannot ground it (the fail-closed floor \u2014 abstain, never escalate-and-guess). Off \u21D2 byte-identical to today: the escalation diags are not emitted and nothing else changes. Sizes S2's per-verify vision-extraction cost by measuring how often and WHERE Tier-1 abstains on countable state assertions.",designDoc:"docs/plans/2026-07-25-grounded-state-verifier-design.md",status:"active",added:"2026-07-25",notes:"S1 of the grounded-state verifier (measurement slice). Default OFF; SHADOW-ONLY and verdict-inert even when ON \u2014 this slice only emits verifier_escalated / verifier_escalation_abstained shadow diags at the checkPinPageGrounding abstain points, makes no model call, and changes no verdict. The escalation decision keys on captureModeGroundsAbsence (capture fidelity / checkPinPageGrounding outcome), NOT on canvasDominant/surface identity, so a non-canvas vision-groundable surface escalates through the same path (spec AC-7). Read site: packages/engine-core/src/RunnerRuntime.ts (groundedStateVerifierEnabled \u2192 maybeEmitVerifierEscalation, called from checkPinPageGrounding); pure logic in packages/engine-core/src/groundedStateVerifier.ts. Acceptance tests: packages/engine-core/src/__tests__/RunnerRuntime.groundedStateVerifier.test.ts (wiring, flag-OFF byte-identical) + groundedStateVerifier.test.ts (pure groundability keying). S2 (prompt-only extractor + deterministic comparator) is the next slice and adds the actual Tier-2 call behind this same flag.",graduation:{status:"gated",gate:"S1 is measurement-only (no verdict change), so it graduates by FEEDING S2, not by flipping default-on: a staging shadow soak of verifier_escalated / verifier_escalation_abstained sizes the Tier-1-abstain-on-countable-state rate (per reason + captureMode) that S2 (prompt-only vision extractor + deterministic comparator, same flag) is built against. The flag advances to a real verdict path only under S2+ with its own verdict-parity shadow-soak; S1 alone never flips default-on.",evidence:"staging verifier_escalated / verifier_escalation_abstained diag events (escalation rate + reason/captureMode breakdown) + the engine-core RunnerRuntime.groundedStateVerifier + groundedStateVerifier unit suites",owner:"steering (Alex)",review:"2026-08-08"}},GROUNDED_STATE_EXTRACT:{key:"GROUNDED_STATE_EXTRACT",envVars:["AGENTIQA_GROUNDED_STATE_EXTRACT","AGENTIQA_EXPERIMENT_GROUNDED_STATE_EXTRACT"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Grounded-state verifier \u2014 S2 prompt-only vision EXTRACTOR + deterministic presence COMPARATOR (design docs/plans/2026-07-25-grounded-state-verifier-design.md). The sibling flag to GROUNDED_STATE_VERIFIER (S1's free measurement stays independently runnable). SHADOW-ONLY: when on AND a stateExtractor is wired, each S1 escalation candidate (a Tier-1 checkPinPageGrounding abstain on a vision-groundable capture carrying a PRESENCE assertion, deduped one-call-per-step) gets ONE no-task-stake vision extraction at run_complete that enumerates what is on screen into a FIXED schema (objects/text/labels/counts) \u2014 NEVER a verdict (AC-2: the prompt receives no assertion outcome and no pass/fail framing). A DETERMINISTIC comparator then decides presence of the plan-text-derived target against that extraction, reproducibly from the logged extraction + target without re-calling the model (AC-3), and the runtime LOGS the would-be verdict + its PARITY vs the driver's current grade (grounded_state_extract diag). This slice changes NO live verdict. FAIL-CLOSED (Decision 7): any missing image / extractor abstain / non-answer / error / timeout / ambiguous or thin comparison \u2192 INCONCLUSIVE, never a pass. Off \u21D2 zero extraction calls, no candidate collection, byte-identical behavior (AC-6). Cost: one extraction call per escalated presence step, hard-capped (fork E).",designDoc:"docs/plans/2026-07-25-grounded-state-verifier-design.md",status:"active",added:"2026-07-25",notes:"S2 of the grounded-state verifier (prompt-only extractor + deterministic presence comparator). Default OFF; SHADOW-FIRST \u2014 even when ON it only computes and LOGS the would-be presence verdict and its parity vs the driver grade (grounded_state_extract / _start / _done diags), makes at most ONE extraction model call per escalated step (fork-E hard cap), and changes NO verdict or stepResult. Sibling to GROUNDED_STATE_VERIFIER so S1 measurement runs without paying S2 cost. Requires deps.stateExtractor wired (getStateExtractor in apps/execution-engine/src/buildDeps.ts) \u2014 absent \u21D2 inert. Read site: packages/engine-core/src/RunnerRuntime.ts (groundedStateExtractEnabled \u2192 candidate recording in maybeEmitVerifierEscalation, consumed by runGroundedStateExtractions at run_complete); extractor + comparator in packages/engine-core/src/groundedStateExtractor.ts. Acceptance tests: packages/engine-core/src/__tests__/groundedStateExtractor.test.ts (pure prompt/parser/comparator/parity \u2014 AC-2/AC-3) + RunnerRuntime.groundedStateExtract.test.ts (wiring, shadow-no-mutation, fail-closed, one-call cap, seam guard, flag-OFF byte-identical). Binds claim verify.grounded-state-extract-then-compare. S3 (GROUNDED_STATE_DIFFERENTIAL) adds the before\u2192after differential for absence + modification.",graduation:{status:"gated",gate:"S2 is shadow-first (no verdict change). Graduation gates the PRESENCE case only, and only after: (1) a staging verdict-parity shadow soak of grounded_state_extract shows the extract-then-compare would-verdict matching the driver grade on DOM-groundable controls and DISAGREEing (would_fail on a driver-passed step) on the canvas false-pass fixtures (project_canvas_direct_draw_test) that the driver self-grade lets through today; (2) the extractor accuracy on the canvas presence fixtures clears the fork-G bar (else spec the fine-tuned extractor first). Advancing to a LIVE verdict path is a separate step from flipping this flag to shadow-on.",evidence:"staging grounded_state_extract / _start / _done diag events (would-verdict + parity + inconclusive-rate breakdown) + the engine-core groundedStateExtractor + RunnerRuntime.groundedStateExtract unit suites + the claim verify.grounded-state-extract-then-compare",owner:"steering (Alex)",review:"2026-08-15"}},GROUNDED_STATE_DIFFERENTIAL:{key:"GROUNDED_STATE_DIFFERENTIAL",envVars:["AGENTIQA_GROUNDED_STATE_DIFFERENTIAL","AGENTIQA_EXPERIMENT_GROUNDED_STATE_DIFFERENTIAL"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Grounded-state verifier \u2014 S3 BEFORE\u2192AFTER DIFFERENTIAL (absence \u2282 update) for ABSENCE + MODIFICATION (design docs/plans/2026-07-25-grounded-state-verifier-design.md). The sibling flag to GROUNDED_STATE_VERIFIER (S1 measurement) / GROUNDED_STATE_EXTRACT (S2 single-after presence). SHADOW-ONLY: when on AND a stateExtractor is wired, each S1 escalation candidate (a Tier-1 checkPinPageGrounding abstain on a vision-groundable capture carrying an ABSENCE or MODIFICATION assertion, deduped one-call-per-step) runs the S2 no-task-stake vision extraction on BOTH the persisted baseline (before, resolveBaselineMessage) and verify (after, resolveEvidenceMessage) frames \u2014 each with S2's IDENTICAL constant task-blind prompt (AC-2: task-blind on BOTH frames, no assertion / no expected value / no pass/fail framing) \u2014 and a DETERMINISTIC differential comparator decides the verdict from the two extractions + the plan-text-derived target, reproducibly without re-calling the model (AC-3). ABSENCE: target present-before \u2227 absent-after \u2192 would_pass (confirmed_absent); still present-after \u2192 would_fail; before-presence unestablished / after unreadable \u2192 inconclusive. MODIFICATION: a count that changed to the expected value, or a crisp new value that appeared (before-absent + after-present) \u2192 would_pass; unresolvable \u2192 inconclusive. The runtime LOGS the would-be differential verdict + its PARITY vs the driver grade (grounded_state_differential diag); this slice changes NO live verdict. FAIL-CLOSED (Decision 7): any missing-before / unreadable / extractor abstain / error / timeout / ambiguous or unresolvable comparison \u2192 INCONCLUSIVE, never a pass. Canvas is IN scope (the extractor is vision; before/after frames exist via blind-double-read), identified by the plan DESCRIPTOR (fork D1) with an ambiguous match abstaining to inconclusive \u2014 never re-identifying an anonymous object. Off \u21D2 zero candidate collection, zero extraction calls, byte-identical behavior (AC-6). Cost: at most TWO extraction calls per escalated differential step (before + after, fork-E bounded call budget), hard-capped, batched under a deadline.",designDoc:"docs/plans/2026-07-25-grounded-state-verifier-design.md",status:"active",added:"2026-07-25",notes:'S3 of the grounded-state verifier (before\u2192after differential; absence \u2282 update). Default OFF; SHADOW-FIRST \u2014 even when ON it only computes and LOGS the would-be differential verdict and its parity vs the driver grade (grounded_state_differential / _start / _done diags), makes at most TWO extraction model calls per escalated step (before + after; fork-E bounded budget, hard-capped, batched under a deadline \u2192 timeout inconclusive), and changes NO verdict or stepResult. Sibling to GROUNDED_STATE_VERIFIER (S1) / GROUNDED_STATE_EXTRACT (S2). Requires deps.stateExtractor wired (getStateExtractor in apps/execution-engine/src/buildDeps.ts, reused per-frame) \u2014 absent \u21D2 inert. Read site: packages/engine-core/src/RunnerRuntime.ts (groundedStateDifferentialEnabled \u2192 differential-candidate recording in maybeEmitVerifierEscalation, consumed by runGroundedStateDifferentials at run_complete); pure differential comparator in packages/engine-core/src/groundedStateDifferential.ts (reuses S2 groundedStateExtractor). Acceptance tests: packages/engine-core/src/__tests__/groundedStateDifferential.test.ts (pure differential \u2014 absence/modification/fail-closed, #1816 RED\u2192GREEN comparator soak hook + green-guard, AC-2/AC-3) + RunnerRuntime.groundedStateDifferential.test.ts (wiring, before+after two-frame extraction, shadow-no-mutation, fail-closed on missing-before/unreadable/timeout, one-differential-per-step cap, canvas descriptor-match, flag-OFF byte-identical). Binds claim verify.grounded-state-extract-then-compare. S4 (GROUNDED_STATE_INCONCLUSIVE) maps verifier-abstain \u2192 the inconclusive floor. Trust-layer Slice 1 (routing-gap fix) ALSO gates a STATIC single-frame COUNT comparator on this same flag: a count-intent assertion ("exactly 3 shapes" \u2014 authored factKind:count or an NL count check) escalates a count candidate and, at run_complete, runGroundedStateCounts runs ONE task-blind extraction + the deterministic compareCount (extracted 4 \u2260 expected 3 \u2192 grounded_state_count would_fail(after_count_mismatch); 3 = 3 \u2192 would_pass(confirmed_count)); shadow-only, fail-closed, one-call-per-step. Acceptance: RunnerRuntime.groundedStateCount.test.ts + the compareCount/checkTextAssertsCount cases in groundedStateDifferential.test.ts; end-to-end firewall proof eval runner-verify-count-canvas.',graduation:{status:"gated",gate:"S3 is shadow-first (no verdict change). Graduation gates the ABSENCE + MODIFICATION differential and only after: (1) a staging verdict-parity shadow soak of grounded_state_differential shows the #1816 reproduced-RED absence scenario would flip RED\u2192GREEN under the differential (confirmed_absent \u2192 would_pass on a driver-failed step) WITHOUT regressing the paired green-guard fixture (target-still-present \u2192 would_fail / driver-pass preserved); (2) new MODIFICATION fixtures (count + value-appearance) shadow-soak clean; (3) the extractor accuracy on the canvas absence/modification fixtures clears the fork-G bar. Advancing to a LIVE verdict path is a separate step from flipping this flag to shadow-on.",evidence:"staging grounded_state_differential / _start / _done diag events (would-verdict + parity + inconclusive-rate breakdown, #1816 RED\u2192GREEN) + the engine-core groundedStateDifferential + RunnerRuntime.groundedStateDifferential unit suites + the claim verify.grounded-state-extract-then-compare",owner:"steering (Alex)",review:"2026-08-22"}},GROUNDED_STATE_INCONCLUSIVE:{key:"GROUNDED_STATE_INCONCLUSIVE",envVars:["AGENTIQA_GROUNDED_STATE_INCONCLUSIVE","AGENTIQA_EXPERIMENT_GROUNDED_STATE_INCONCLUSIVE"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Grounded-state verifier \u2014 S4 FAIL-CLOSED INCONCLUSIVE FLOOR wiring (Decision 7 + Fork F2; design docs/plans/2026-07-25-grounded-state-verifier-design.md). The sibling flag to GROUNDED_STATE_VERIFIER (S1) / GROUNDED_STATE_EXTRACT (S2) / GROUNDED_STATE_DIFFERENTIAL (S3). SHADOW-ONLY: when on, each verifier ABSTAIN \u2014 the S1 escalation fail-closed floor (verifier_escalation_abstained, a capture even a vision extractor cannot ground) and every S2 presence / S3 differential `inconclusive` outcome (thin/unreadable capture, extractor refusal/error/timeout, before-presence unestablished, after unreadable, ambiguous match, unresolved count) \u2014 is ALSO mapped to the typed inconclusive floor and LOGGED (grounded_state_inconclusive diag): the DISTINCT `verifier_inconclusive:{reason}` sub-reason + the interim `warning` step status (Fork F2 \u2014 a NEUTRAL 'couldn't tell', NOT an alarm-amber defect per project_warning_display_semantics) + the verifierInconclusive marker + migratesTo:'inconclusive'. This slice mutates NO stepResult and changes NO verdict \u2014 it computes/LOGS the would-be floor mapping only. HONESTY FLOORS: an abstain NEVER maps to `passed` (VERIFIER_INCONCLUSIVE_STEP_STATUS is warning/inconclusive, never passed), and a genuine would_pass/would_fail is never floored (inconclusiveFloorForVerdict returns null on a non-inconclusive verdict; the wiring only fires on the S2/S3 abstain paths + the S1 abstained floor). ONE migration point: flip VERIFIER_INCONCLUSIVE_STEP_STATUS (groundedStateInconclusive.ts) from `warning` to step-level `inconclusive` when Layered-Hybrid Phase-1 lands that status in the step enum. Off \u21D2 zero mapping, zero logs, byte-identical (AC-6).",designDoc:"docs/plans/2026-07-25-grounded-state-verifier-design.md",status:"active",added:"2026-07-25",notes:"S4 of the grounded-state verifier (fail-closed inconclusive floor; interim warning-with-distinct-sub-reason, Fork F2). Default OFF; SHADOW-FIRST \u2014 even when ON it only computes and LOGS the would-be inconclusive floor mapping (grounded_state_inconclusive diag), makes NO model call (pure wiring of S1/S2/S3 abstain outcomes), and changes NO verdict or stepResult. Sibling to GROUNDED_STATE_VERIFIER (S1) / GROUNDED_STATE_EXTRACT (S2) / GROUNDED_STATE_DIFFERENTIAL (S3); the abstains it maps only exist when those slices run, so S4 is additive telemetry on top of them. Read site: packages/engine-core/src/RunnerRuntime.ts (groundedStateInconclusiveEnabled \u2192 maybeLogInconclusiveFloor, called at the S1 verifier_escalation_abstained floor in maybeEmitVerifierEscalation + the S2/S3 logInconclusive abstain choke points in runGroundedStateExtractions / runGroundedStateDifferentials); pure mapping in packages/engine-core/src/groundedStateInconclusive.ts (the ONE migration point VERIFIER_INCONCLUSIVE_STEP_STATUS). Acceptance tests: packages/engine-core/src/__tests__/groundedStateInconclusive.test.ts (pure \u2014 each abstain reason \u2192 typed sub-reason \u2192 interim warning-never-passed, distinct-from-product-warning, migration seam, would_pass/would_fail never floored) + RunnerRuntime.groundedStateInconclusive.test.ts (wiring \u2014 flag-OFF byte-identical, shadow-no-mutation, floor logged per S1/S2/S3 abstain, no floor on a would_pass step). Binds claim verify.grounded-state-extract-then-compare. S5 broadens the groundability surface + fine-tune trigger.",graduation:{status:"gated",gate:"S4 is shadow-first (no verdict change) and the INTERIM F2 mapping (warning-with-distinct-sub-reason). Graduation to a LIVE floor is a SEPARATE, later step from flipping this flag shadow-on and requires: (1) a staging shadow soak of grounded_state_inconclusive confirming the abstain\u2192floor breakdown (source / reason / rate) is sane and that no would_pass/would_fail is ever floored (the honesty invariants hold in the field); (2) the S2/S3 verdict-parity soaks having graduated their live-verdict paths (an inconclusive floor is only meaningful once the extract-then-compare verdicts gate); (3) the UI rendering the verifier_inconclusive sub-reason as a NEUTRAL could-not-tell (not alarm-amber). The clean F2\u2192F1 migration (flip VERIFIER_INCONCLUSIVE_STEP_STATUS warning\u2192inconclusive at the ONE point) lands when Layered-Hybrid Phase-1 ships the step-level inconclusive status.",evidence:"staging grounded_state_inconclusive diag events (abstain\u2192floor mapping: source/reason/sub-reason/interim-status breakdown, honesty-invariant field check) + the engine-core groundedStateInconclusive + RunnerRuntime.groundedStateInconclusive unit suites + the claim verify.grounded-state-extract-then-compare",owner:"steering (Alex)",review:"2026-08-29"}},GROUNDED_STATE_FINETUNE_METRIC:{key:"GROUNDED_STATE_FINETUNE_METRIC",envVars:["AGENTIQA_GROUNDED_STATE_FINETUNE_METRIC","AGENTIQA_EXPERIMENT_GROUNDED_STATE_FINETUNE_METRIC"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Grounded-state verifier \u2014 S5 FINE-TUNE TRIGGER METRIC (Fork G: DEFINE the trigger, do NOT fine-tune; design docs/plans/2026-07-25-grounded-state-verifier-design.md). The final sibling flag to GROUNDED_STATE_VERIFIER (S1) / GROUNDED_STATE_EXTRACT (S2) / GROUNDED_STATE_DIFFERENTIAL (S3) / GROUNDED_STATE_INCONCLUSIVE (S4). SHADOW-ONLY: when on AND an S2/S3 shadow batch runs, the runtime ACCUMULATES per-SURFACE outcome counts from the extract-then-compare batches \u2014 extractor-abstain (the prompt-only extractor produced no facts), comparator-inconclusive (facts extracted but no definite verdict), and parity agree/disagree \u2014 bucketed by the escalation reason \u2192 surface class (canvas_dominant\u2192canvas, thin\u2192image_or_svg, incremental/non_full\u2192partial_capture; a DIAGNOSTIC label over the already-recorded reason, NOT a surface-name gate \u2014 the escalation gate stays keyed on captureModeGroundsAbsence, Fork A1). After both batches it LOGS the evaluated fine-tune trigger report against the STATED bar (grounded_state_finetune_metric diag): per surface the extractor-abstain rate, inconclusive rate, disagreement rate, and a would-trigger-fine-tune flag (Fork-G graduation signal, ADVISORY). This makes the prompt-only\u2192dedicated/fine-tuned graduation a DATA read; prompt-only stays v1 (Decision 6) \u2014 this slice invests in NO model, mutates NO stepResult, and changes NO verdict. The metric only has samples to fold when S2 and/or S3 also run. Off \u21D2 zero accumulation, zero logs, byte-identical (AC-6). The STATED bar: per surface, after live graduation, extractor-abstain rate > 0.20 OR inconclusive rate > 0.40 over \u2265 50 escalated extract-then-compares triggers a dedicated/fine-tuned extractor FOR THAT SURFACE (parity-disagreement is reported but NOT a trigger input \u2014 a high disagreement can be the extractor CATCHING driver false-passes, the desired signal).",designDoc:"docs/plans/2026-07-25-grounded-state-verifier-design.md",status:"active",added:"2026-07-25",notes:"S5 of the grounded-state verifier (fine-tune trigger metric; Fork G define-not-fine-tune) and the LAST vision-tier slice. Default OFF; SHADOW-ONLY \u2014 even when ON it only accumulates the per-surface outcome breakdown from the S2/S3 shadow batches and LOGS the evaluated trigger report (grounded_state_finetune_metric diag), makes NO model call (pure metric over existing S2/S3 outcomes), and changes NO verdict or stepResult. Sibling to GROUNDED_STATE_VERIFIER (S1) / GROUNDED_STATE_EXTRACT (S2) / GROUNDED_STATE_DIFFERENTIAL (S3) / GROUNDED_STATE_INCONCLUSIVE (S4); the outcomes it folds only exist when S2/S3 run, so S5 is additive telemetry on top of them. The companion BROADEN half of S5 (SVG/image coverage, spec AC-7 generalized) needed NO gate change \u2014 the S1/S2/S3 path already keys on captureModeGroundsAbsence (Fork A1), so broadening is a fixture/coverage add, proven by the RunnerRuntime.groundedStateBroaden test. Read site: packages/engine-core/src/RunnerRuntime.ts (groundedStateFineTuneMetricEnabled \u2192 the run_complete accumulator threaded into runGroundedStateExtractions / runGroundedStateDifferentials, emitted by emitFineTuneTriggerMetric); pure metric + STATED bar in packages/engine-core/src/groundedStateFineTuneTrigger.ts (FINETUNE_TRIGGER_BAR). Acceptance tests: packages/engine-core/src/__tests__/groundedStateFineTuneTrigger.test.ts (pure surface mapping / fold / rates / bar evaluation) + RunnerRuntime.groundedStateFineTuneMetric.test.ts (wiring \u2014 per-surface fold from S2/S3, flag-OFF byte-identical, shadow-no-mutation) + RunnerRuntime.groundedStateBroaden.test.ts (AC-7 generalized: a non-canvas SVG/image surface escalates + extracts + compares through the identical path). Binds claim verify.grounded-state-extract-then-compare. COMPLETES the S1\u2013S5 vision-tier build; remaining work is soak + graduation flips (Alex).",graduation:{status:"gated",gate:"S5 is shadow-first (no verdict change) and DEFINES the Fork-G fine-tune trigger \u2014 it does not fine-tune. The metric graduates by FEEDING the fine-tune decision, not by flipping default-on: a staging shadow soak of grounded_state_finetune_metric measures each surface (canvas / image_or_svg / partial_capture) extractor-abstain + inconclusive rate against the STATED bar (FINETUNE_TRIGGER_BAR: abstain > 0.20 OR inconclusive > 0.40 over \u2265 50 escalated steps). Investing in a dedicated/fine-tuned extractor for a surface is triggered ONLY when that surface clears the bar AFTER the S2/S3 extract-then-compare has graduated live on it (Decision 6: prompt-only ships regardless). This flag alone never flips default-on.",evidence:"staging grounded_state_finetune_metric diag events (per-surface extractor-abstain / inconclusive / disagreement rate + would-trigger evaluation vs FINETUNE_TRIGGER_BAR) + the engine-core groundedStateFineTuneTrigger + RunnerRuntime.groundedStateFineTuneMetric + RunnerRuntime.groundedStateBroaden unit suites + the claim verify.grounded-state-extract-then-compare",owner:"steering (Alex)",review:"2026-09-05"}},GROUNDED_STATE_NL_TRIGGER:{key:"GROUNDED_STATE_NL_TRIGGER",envVars:["AGENTIQA_GROUNDED_STATE_NL_TRIGGER","AGENTIQA_EXPERIMENT_GROUNDED_STATE_NL_TRIGGER"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Groundability-contract Slice 1 \u2014 un-inert the grounded-state verifier for PINLESS criteria (design docs/plans/2026-07-26-groundability-contract-design.md). The Tier-1 pin page-grounding oracle (checkPinPageGrounding) early-returns when a criterion carries no expectedValue pin, so a real/canvas plan's natural-language {check,strict} assertion never reached the S1 escalation path \u2014 the grounded-state verifier was provably INERT on exactly the canvas surfaces (Miro/Figma) it was built for. When on, a PINLESS verify step whose RUNTIME fact-kind (deriveVerifyAssertionKind) is a non-default state-change kind \u2014 `absence` or `modification` \u2014 routes through the SAME capture-groundability abstain\u2192escalate logic the pinned path uses (maybeEmitVerifierEscalation \u2192 buildVerifierEscalation), keyed on the IDENTICAL captureModeGroundsAbsence gate (a capture even a vision extractor cannot ground abstains to the fail-closed inconclusive floor, never escalate-and-guess). F2 ANTI-FLOOD: the DEFAULT `presence` kind is EXCLUDED \u2014 deriveVerifyAssertionKind defaults to presence for everything, so escalating on presence would flood every pinless ungroundable step; only absence/modification (which the classifier returns deliberately) escalate in Slice 1 (authored count/value are Slice 2). ESCALATION-TRIGGER ONLY: matchType/comparison/expectedValue-as-comparator are untouched, this is shadow/verdict-inert like S1\u2013S5, and it emits nothing on its own \u2014 the escalation diag / candidate still requires GROUNDED_STATE_VERIFIER / _EXTRACT / _DIFFERENTIAL / _INCONCLUSIVE. Off \u21D2 byte-identical: the pinless early-return stands, zero new telemetry, zero behavior change (AC-6).",designDoc:"docs/plans/2026-07-26-groundability-contract-design.md",status:"active",added:"2026-07-25",notes:"Groundability-contract Slice 1 (PR-A). Default OFF; SHADOW-ONLY and verdict-inert even when ON \u2014 it only relocates the S1 escalation TRIGGER for pinless criteria off the expectedValue pin onto the existing runtime fact-kind classifier (deriveVerifyAssertionKind). NO schema change (migration-safe). The escalate-vs-abstain disposition stays keyed on the exact captureModeGroundsAbsence gate reused from the pinned path (GUARDRAIL 1: no pinless bypass). Read site: packages/engine-core/src/RunnerRuntime.ts (groundedStateNlTriggerEnabled \u2192 maybeEmitPinlessNlEscalation, called at the checkPinPageGrounding pinless early-return; routes through the shared maybeEmitVerifierEscalation). Acceptance tests: packages/engine-core/src/__tests__/RunnerRuntime.groundedStateNlTrigger.test.ts (pinless absence/modification escalates on ungroundable canvas/thin/non_full; groundable-mode capture abstains to the inconclusive floor NOT escalate; presence pinless never escalates \u2014 F2 anti-flood; flag-OFF byte-identical; pinned path unchanged). OVERLAP NOTE: ABSENCE_AWARE_VERIFY (#1818, classifyAbsenceVerifyConflict / wait-oracle literal) is a DISTINCT pin-independent absence route \u2014 both are shadow/verdict-inert, so absence carries a DOUBLE shadow signal; do not double-count in soak evidence. Slice 2 adds authored count/value kinds.",graduation:{status:"gated",gate:"Slice 1 is measurement-only (no verdict change): it graduates by FEEDING the same S2/S3 extract-then-compare shadow soak \u2014 a staging shadow soak of verifier_escalated / verifier_escalation_abstained on PINLESS absence/modification steps (canvas/real plans) sizes the escalation rate the vision tier is built against, previously unmeasurable because pinless steps never escalated. The flag advances to a real verdict path only under S2+ with its own verdict-parity shadow-soak; Slice 1 alone never flips default-on. Slice 2 (authored count/value kinds) is a separate gated slice.",evidence:"staging verifier_escalated / verifier_escalation_abstained diag events on pinless absence/modification steps (escalation rate + reason/captureMode breakdown, de-duplicated against the ABSENCE_AWARE_VERIFY absence shadow signal) + the engine-core RunnerRuntime.groundedStateNlTrigger unit suite",owner:"steering (Alex)",review:"2026-08-15"}},GROUNDED_STATE_FACTKIND_AUTHORED:{key:"GROUNDED_STATE_FACTKIND_AUTHORED",envVars:["AGENTIQA_GROUNDED_STATE_FACTKIND_AUTHORED","AGENTIQA_EXPERIMENT_GROUNDED_STATE_FACTKIND_AUTHORED"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Groundability-contract Slice 2 \u2014 consume the MODEL-authored `factKind` as source-of-truth for the grounded-state escalation trigger's assertion shape (design docs/plans/2026-07-26-groundability-contract-design.md). Builds on Slice 1 (GROUNDED_STATE_NL_TRIGGER, pinless escalation on runtime absence/modification). The new OPTIONAL `factKind` enum (value|count|presence|absence|modification|relation) on TestPlanV2Criterion is authored by the generation model (all three producer schemas) as a deliberate typed choice (spawn_agent authMode/authSurfaceKind precedent). When on, a criterion carrying an AUTHORED factKind (a) OVERRIDES the runtime deriveVerifyAssertionKind inference (mapped 6\u21923 for the escalation branch, Fork F1: absence\u2192absence, modification\u2192modification, value/count/presence/relation\u2192presence) AND (b) LIFTS Slice 1's F2 anti-flood filter for that step so ANY authored kind escalates \u2014 authoring IS the deliberate groundable-fact signal \u2014 still gated by the IDENTICAL captureModeGroundsAbsence floor (a capture even a vision extractor cannot ground abstains to the fail-closed inconclusive floor, NEVER escalate-and-guess), routed per collapsed kind (absence/modification \u2192 S3 differential candidate; value/count/presence/relation \u2192 S2 presence-family extraction candidate). This fixes the count/canvas case (authored count \u2192 escalates to S2 extraction) WITHOUT the runtime-presence flood. UNAUTHORED criteria keep Slice 1's runtime path EXACTLY (only absence/modification escalate; the runtime `presence` default does NOT). matchType/comparison/expectedValue-as-comparator UNTOUCHED; SHADOW-ONLY / verdict-inert like S1\u2013S5; it emits nothing on its own \u2014 the escalation diag / candidate still requires GROUNDED_STATE_VERIFIER / _EXTRACT / _DIFFERENTIAL / _INCONCLUSIVE. Off (or factKind absent) \u21D2 byte-identical: the field is unread, Slice 1's runtime behavior stands (AC-6). The field may be authored + persisted with this flag OFF (harmless, unread).",designDoc:"docs/plans/2026-07-26-groundability-contract-design.md",status:"active",added:"2026-07-25",notes:"Groundability-contract Slice 2 (PR-B). Default OFF; SHADOW-ONLY and verdict-inert even when ON. Read sites: packages/engine-core/src/RunnerRuntime.ts (groundedStateFactKindAuthoredEnabled \u2192 authoredFactKind, consumed by deriveVerifyAssertionKind override + the pinless F2-filter lift in maybeEmitPinlessNlEscalation). Field: packages/shared-types/src/index.ts (FactKind + TestPlanV2Criterion.factKind); producer schemas: coordinatorToolDefs.ts test_plan_criteria_schema, agentToolDefs.ts criteria_schema, runnerToolDefs.ts step_with_criteria_schema (thin inline); generation steer: planStepGuidance.ts buildCriteriaFactKindGuidance; 6\u21923 mapping + type-guard: groundedStateVerifier.ts factKindToAssertionKind / isAuthoredFactKind. DROP-SITES registered (else a plain step-text edit silently wipes the authored field \u2014 the matchType bug): renderer finalizeCriterion (apps/desktop-next/.../testPlanStepsSerde.ts) + server carryCriterionPins (apps/web-next/lib/testPlanSaveNormalize.ts) \u2014 both preserve factKind on an UNCHANGED check, drop it on an EDITED check (\u2192 runtime fallback, safe). Acceptance tests: packages/engine-core/src/__tests__/RunnerRuntime.groundedStateFactKindAuthored.test.ts (authored count/presence on ungroundable canvas \u2192 S2 extraction candidate; authored absence \u2192 S3 differential candidate; authored + degraded capture \u2192 inconclusive floor, no escalate-and-guess; flag OFF \u2192 byte-identical field-unread Slice-1 behavior; field absent \u2192 Slice-1 behavior) + drop-site round-trip tests in testPlanStepsSerde.test.ts / testPlanSaveNormalize.test.ts. The MANDATORY real-runtime generation eval + the qa-box claim verify.groundability-contract-authored are Slice-2b (separate follow-on), NOT this PR. OVERLAP NOTE: composes with GROUNDED_STATE_NL_TRIGGER (Slice 1) via the shared maybeEmitPinlessNlEscalation helper.",graduation:{status:"gated",gate:"Slice 2 is measurement-only (no verdict change): it graduates by FEEDING the same S2/S3 extract-then-compare shadow soak, now widened to authored count/value/presence/relation kinds (the count/canvas case Slice 1 could not reach because runtime presence does not escalate). It advances to a real verdict path only under S2+ with its own verdict-parity shadow-soak; Slice 2 alone never flips default-on. Requires the Slice-2b generation eval (real ExplorerRuntime/CoordinatorRuntime authoring the correct factKind) to gate the authoring quality before any graduation.",evidence:"staging verifier_escalated / verifier_escalation_abstained diag events on PINLESS authored-factKind steps (escalation rate + factKind/reason/captureMode breakdown) + the engine-core RunnerRuntime.groundedStateFactKindAuthored unit suite; graduation additionally blocked on the Slice-2b real-runtime generation eval (authoring-distribution baseline)",owner:"steering (Alex)",review:"2026-08-15"}},GROUNDED_STATE_FACTKIND_TYPING_PASS:{key:"GROUNDED_STATE_FACTKIND_TYPING_PASS",envVars:["AGENTIQA_GROUNDED_STATE_FACTKIND_TYPING_PASS","AGENTIQA_EXPERIMENT_GROUNDED_STATE_FACTKIND_TYPING_PASS"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Groundability-contract Slice 2b \u2014 MODEL-authored focused second-pass factKind typing that FIXES the realistic-authoring gap (design docs/plans/2026-07-26-groundability-contract-design.md). Slice 2 (GROUNDED_STATE_FACTKIND_AUTHORED) consumes an authored `factKind`, but the live ExplorerRuntime authors it on ~0% of criteria produced by a realistic multimodal explore (screenshots + long trace + coordinator\u2192explorer spawn = the load that suppresses a deeply-nested OPTIONAL enum) \u2014 MEASURED 0/8 on e2e/evals/plans/groundability-factkind-explored.ts, and the three natural schema fixes (imperative guidance, required field, field reorder) ALL stayed 0%. But a FOCUSED low-load typing call over JUST the drafted check-texts types factKind at 100% (probe 78/78, real gemini-3-flash-preview). When on, AFTER the Explorer's draftTestCase is finalized (assistant_v2_report accept seam, past every re-prompt gate), a SEPARATE model-authored pass (packages/engine-core/src/factKindTypingPass.ts runFactKindTypingPass) collects the verify criteria that LACK an authored factKind, makes ONE batched Gemini call (the session's own model + the REAL buildCriteriaFactKindGuidance the producer schema ships; generateText + Output.object, thinkingBudget:0) that returns {index,factKind}[], and writes the kind back onto each criterion IN PLACE. It is MODEL-authored (a real Gemini call), NOT the rejected deterministic runtime factKind inference. NO OVERWRITE: only a MISSING factKind is filled (an already-authored kind is skipped at collect + double-guarded at write-back). FAIL-SAFE: any error/empty/timeout/invalid-kind leaves factKind UNSET (falls through to runtime inference) \u2014 never crashes authoring, never writes a garbage kind. SHADOW-SAFE: the written factKind is read ONLY by the separately flag-gated GROUNDED_STATE_FACTKIND_AUTHORED verifier (default-OFF), so verdicts are byte-identical whether or not this pass ran. Off \u21D2 the caller never invokes the module: ZERO new model calls, byte-identical authoring (AC-6).",designDoc:"docs/plans/2026-07-26-groundability-contract-design.md",status:"active",added:"2026-07-26",notes:"Groundability-contract Slice 2b (the realistic-authoring FIX; a flag-gated prototype \u2014 adding a model call to the authoring flow is Alex's architecture/graduation call). Default OFF; when ON adds ONE auxiliary Gemini call per COMPLETED explore that produced untyped verify criteria (batched, text-only, cost-isolated via emitAuxiliaryLlmUsage \u2014 not a billable step). Read site: packages/engine-core/src/ExplorerRuntime.ts assistant_v2_report accept seam (killSwitchEnabled('GROUNDED_STATE_FACTKIND_TYPING_PASS') \u2192 runFactKindTypingPass over draftTestCase.steps, mutating criteria in place BEFORE the report message is persisted/emitted, so the authored factKind rides both the saved plan and the diag). Pure module core (collectUntypedVerifyCriteria / buildFactKindTypingPrompt / mapTypesByIndex / applyFactKindTypes) + the single generateText seam. Unit suite: packages/engine-core/src/__tests__/factKindTypingPass.test.ts (ON fills missing factKind from a mocked typing response; OFF = no call / byte-identical; already-authored factKind never overwritten; error/empty response \u2192 factKind stays unset, fail-safe). RE-MEASURE: e2e/evals/plans/groundability-factkind-explored.ts COMMITTED_BASELINE carries the with-typing-pass authored-rate alongside the without (0/8).",graduation:{status:"gated",gate:"The realistic-authoring re-measure (groundability-factkind-explored with GROUNDED_STATE_FACTKIND_TYPING_PASS ON) shows the authored-factKind rate on the real explore path jump from ~0% to high (target near the probe 100%, \u2265~85%) with CORRECT kinds, AND the engine-core factKindTypingPass unit suite green (fill / no-overwrite / fail-safe / off-byte-identical). Graduation to any verdict path additionally requires GROUNDED_STATE_FACTKIND_AUTHORED (the consumer) to graduate under its own shadow-soak \u2014 this pass only PRODUCES the field.",evidence:"e2e/evals/plans/groundability-factkind-explored.ts with-typing-pass measuredAt entry (authored-rate + kind-correctness) + the engine-core factKindTypingPass unit suite + factkind_typing_pass diag events",owner:"steering (Alex)",review:"2026-08-15"}},GROUNDED_STATE_UNIFIED:{key:"GROUNDED_STATE_UNIFIED",envVars:["AGENTIQA_GROUNDED_STATE_UNIFIED","AGENTIQA_EXPERIMENT_GROUNDED_STATE_UNIFIED"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Trust-layer Slice 2 \u2014 PER-SNAPSHOT UNIFICATION (design docs/plans/2026-07-26-trust-layer-slice-2-per-snapshot-unification-design.md; north star docs/plans/2026-07-26-trust-layer-verification-architecture.md). Collapses the three Slice-1 per-kind run_complete batches (runGroundedStateCounts / runGroundedStateExtractions / runGroundedStateDifferentials \u2014 each re-extracting once PER step/kind) into ONE task-blind extraction PER SNAPSHOT feeding ONE typed-comparator dispatch, and introduces the verdict SPECTRUM (Verified / Assessed / Inconclusive) + semantic matchType. SHADOW-ONLY: when on AND a stateExtractor is wired, it consumes the SAME escalation candidates the per-kind batches do (its flag is OR-ed into the maybeEmitVerifierEscalation candidate-recording seam), groups them by shared captured AFTER-frame (groupCandidatesBySnapshot \u2014 verify-steps sharing a frame with no intervening state-mutating action share ONE extraction; differential candidates additionally share ONE run-start baseline extraction), runs ONE extraction per unique frame, and for each candidate runs its typed comparator (compareCount / comparePresence / compareAbsenceDifferential / compareModificationDifferential) \u2014 or, for a matchType:'semantic' criterion, the task-blind concept classifier (deps.conceptClassifier) \u2014 against that shared extraction. It LOGS grounded_state_unified (band + shadowVerdict + parity vs the driver grade) + grounded_state_unified_start/_done (the extraction-vs-comparison counts that PROVE 1-extraction-per-snapshot); it mutates NO stepResult and changes NO verdict. Part B is a REFACTOR that must be verdict-PARITY with the three per-kind paths: the unified would-verdict equals what the retired per-kind batch logged (same evidence resolution + same target derivation + same deterministic comparator on the same extraction). The verdict spectrum: a deterministic comparator would_pass/would_fail \u2192 Verified (the no-false-positive guarantee); a low-confidence semantic result \u2192 Assessed (the SLOT only \u2014 the independent reasoned judge is a LATER slice); no confident extraction/classification / missing image / extractor abstain / error / timeout / unresolvable comparison / unavailable semantic classifier \u2192 the fail-closed Inconclusive floor, NEVER a pass. Task-blindness preserved: the extraction prompt (buildExtractionQuestion) carries observation targets only and the semantic classifier carries a concept + a neutral observation rendering \u2014 NEVER the expected values or pass/fail framing. It runs ALONGSIDE the per-kind batches (its own flag) so the shadow soak can prove parity BEFORE the per-kind methods are physically retired. Off \u21D2 zero candidate consumption here, zero extraction calls, byte-identical (the per-kind paths, if their flags are on, are untouched) (AC-6). Cost: #snapshots (+1 shared before, if any differential) extraction calls per run \u2014 strictly \u2264 the sum of the three per-kind batches, and 1 for N verify-steps on one screen.",designDoc:"docs/plans/2026-07-26-trust-layer-slice-2-per-snapshot-unification-design.md",status:"active",added:"2026-07-26",notes:"Trust-layer Slice 2 (per-snapshot unification). Default OFF; SHADOW-FIRST \u2014 even when ON it only computes + LOGS the would-be verdict-spectrum result and its parity vs the driver grade (grounded_state_unified / _start / _done diags), makes #snapshots (+1 shared before) extraction calls plus one concept-classifier call per semantic candidate (both cost-isolated Flash-model seams via emitAuxiliaryLlmUsage, hard-capped, batched under the same deadline as the per-kind batches), and changes NO verdict or stepResult. Sibling to GROUNDED_STATE_EXTRACT (S2) / GROUNDED_STATE_DIFFERENTIAL (S3) \u2014 it consumes the SAME candidate maps and reuses the SAME comparators, so its would-verdicts are byte-parity with the three per-kind batches (the Slice-2 acceptance gate). Requires deps.stateExtractor wired; a matchType:'semantic' criterion additionally requires deps.conceptClassifier (absent \u21D2 that candidate floors to Inconclusive). Read site: packages/engine-core/src/RunnerRuntime.ts (groundedStateUnifiedEnabled \u2192 the OR-ed candidate recording in maybeEmitVerifierEscalation + collectUnifiedCandidates + runGroundedStateUnified at run_complete); pure core (snapshot grouping / verdict spectrum / semantic mapping) in packages/engine-core/src/groundedStateUnified.ts. Acceptance tests: packages/engine-core/src/__tests__/groundedStateUnified.test.ts (pure \u2014 grouping, spectrum mapping, semantic mapping) + RunnerRuntime.groundedStateUnified.test.ts (wiring \u2014 verdict-PARITY vs the three per-kind batches, ONE extraction for N\u22653 verify-steps on one snapshot, semantic success\u2192Verified / ambiguous\u2192Assessed / error\u2192Verified-FAIL, shadow-no-mutation, flag-OFF byte-identical). Evals: e2e/evals/plans/trust-unified-parity.ts (runner-trust-unified-parity), trust-unified-batching.ts (runner-trust-unified-batching), trust-unified-semantic.ts (runner-trust-unified-semantic). The three per-kind batch methods are RETAINED in this slice as the parity oracle; their physical removal is the graduation follow-up. Binds claim verify.grounded-state-extract-then-compare.",graduation:{status:"gated",gate:"Slice 2 is shadow-first (no verdict change). Graduation gates on: (1) a staging verdict-PARITY shadow soak of grounded_state_unified vs the per-kind grounded_state_count / _extract / _differential diags showing ZERO would-verdict drift across the count / presence / absence / modification fixtures; (2) grounded_state_unified_done confirming extractions == #snapshots (+ shared before), NOT #comparisons (the batching win) in the field; (3) the semantic-matchType Assessed slot behaving (confident \u2192 Verified, ambiguous \u2192 Assessed, contradiction \u2192 Verified-FAIL) at an acceptable concept-classifier accuracy. ONLY after parity is proven do the three per-kind batch methods get physically retired (a separate refactor PR) and does advancing to a LIVE verdict path get considered \u2014 both separate steps from flipping this flag shadow-on.",evidence:"staging grounded_state_unified / _start / _done diag events (spectrum verdict + parity + extraction-vs-comparison counts) cross-checked against the per-kind diags for parity + the engine-core groundedStateUnified + RunnerRuntime.groundedStateUnified unit suites + the trust-unified-parity / -batching / -semantic evals + the claim verify.grounded-state-extract-then-compare",owner:"steering (Alex)",review:"2026-09-05"}},INTERACTION_CLEARS_PRESENCE_ORACLE:{key:"INTERACTION_CLEARS_PRESENCE_ORACLE",envVars:["AGENTIQA_INTERACTION_CLEARS_PRESENCE_ORACLE","AGENTIQA_EXPERIMENT_INTERACTION_CLEARS_PRESENCE_ORACLE"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Interaction-clears-presence-oracle (rank-2 of the step-5 slow-render flake wave, sibling to VERIFY_PRESENCE_WAIT_FLOOR's rank-1 budget raise). A verify-step PRESENCE wait oracle (wait_for_element) that STILL times out after the raised budget records an unresolved oracle failure that verify-gated-done only lets a fresh successful wait_for_element/run_js clear \u2014 a screenshot or a successful click is refused and the plan-grounded literal blocks canReconcileVerificationConflict \u2014 so a genuinely-present-but-slow element force-FAILs (staging sessions asess_1784961071757_cqwxon46 / asess_1784960734951_4t0zaxp1, where the NEXT step's click_at('Blank board') SUCCEEDED and navigated). When on, a subsequent SUCCESSFUL deterministic click_at (or a type_text_at/set_focused_input_value write) on the current verify step or the one immediately preceding it (lookback 1) whose ENGINE-RESOLVED element identity (clickTarget.accessibleName/textContent for a ref/coordinate click, clickedElement.textContent for a label click, or typedIntoField \u2014 the resolved field's accessible name \u2014 for a type write; never the model's narration or the typed VALUE) whole-token-substantiates (citationSubstantiates \u2014 the SAME contiguous-token machinery pinPresentInPage uses, NOT substring) the failed wait's captured target literal CLEARS that step's wait-oracle failure \u2014 engine-observed, un-hallucinable proof of presence, strictly stronger than the screenshot the gate already refuses. HARD CONSTRAINTS (no false-PASS): whole-token resolved-name match only, gated by a SIGNIFICANCE floor (the wait literal must carry a \u22654-char token OR \u22652 tokens \u2014 a bare common single short token like \"ok\"/\"3\" whole-token-matches an unrelated control name too easily, so it can never clear); the #1476 real-hit guard (a coordinate no-op-success \u2014 noObservedEffect side channel \u2014 and a non-interactive pixel landing whose accessibleName merely mirrors a container's textContent are BOTH rejected, so a click that reports success but hit nothing cannot launder a miss); a type only ever clears when it genuinely resolved+focused a named field (typedIntoField populated \u2014 a blind write carries no identity); scope = wait-style presence oracle only (isVerificationOracleAction, NEVER the plan-derived pin-page-grounding oracle) on a Runner verify step; an ABSENCE-intent verify step (checkTextAssertsAbsence over the step text + criteria) is NEVER cleared \u2014 a successful interaction DISPROVES an absence assertion, so clearing would manufacture a pass on a real absence-violation bug; fail-closed (a genuinely-absent element cannot be successfully interacted with, and an ambiguous / non-matching / insignificant-literal / stale-beyond-lookback / absence-intent identity does NOT clear \u2014 the force-fail stands). SHADOW-FIRST: off (default) leaves every verdict identical (the force-fail stands) and only emits an `interaction_clears_presence_oracle:would_clear` diag of what it WOULD have cleared, so a staging soak can confirm it fires only on genuine presence before the flip; on makes the clear live (`\u2026:cleared`).",designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-07-25",notes:"Default OFF in code; detection runs in shadow always (interaction_clears_presence_oracle:would_clear diag), the actual clear is gated \u2014 the ABSENCE_AWARE_VERIFY / PIN_PAGE_GROUNDING shadow-first precedent. killSwitch resolves the no-env value from this defaultState (#1729), so GRADUATING = flip defaultState to 'on' AND add INTERACTION_CLEARS_PRESENCE_ORACLE to INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire). Read site: packages/engine-core/src/RunnerRuntime.ts (interactionClearsPresenceOracleEnabled \u2192 maybeInteractionClearsPresenceOracle, called from the browser-action dispatch after recordOffPlanResolvedClick). Resolved-identity sources: label click \u2192 response.clickedElement.text (clickByLabel only populates it after resolving exactly one clickable control by name AND clicking without error \u2014 un-hallucinable); ref/coordinate click \u2192 response.clickTarget.accessibleName/text, admitted ONLY when isInteractiveClickTarget(clickTarget) so a pixel that landed on a text container cannot launder via the accessibleName\u2192textContent fallback; type write (type_text_at / set_focused_input_value) \u2192 response.typedIntoField (getFocusedFieldName \u2014 the accessible name of the field the write actually resolved+focused; the typed VALUE is never an identity). SIGNIFICANCE floor (waitLiteralHasSignificantTokens over the same tokenizeCitation basis the match uses): a bare common single short token (a status word, a lone digit) whole-token-matches an unrelated resolved name too easily, so the wait literal must carry a \u22654-char token OR \u22652 tokens or it never clears. Lookback is intentionally tight (1 step) to keep a stale REAL miss from an earlier step from being laundered by a same-named element that appears much later; a soak may widen it. Absence-intent verify steps are excluded via checkTextAssertsAbsence (a successful interaction disproves an absence assertion \u2192 would be a false-PASS); a suppressed match emits interaction_clears_presence_oracle:absence_skip. Acceptance tests: packages/engine-core/src/__tests__/RunnerRuntime.interactionClearsPresenceOracle.test.ts (flag-OFF shadow-only + byte-identical for both click and type, flag-ON label + ref + type clear, type resolved-different-field reject, type-with-no-resolved-field reject, significance-floor bare-one-token reject, #1476 no-op reject, non-interactive container-text reject, non-matching-element reject, whole-token-not-substring, genuinely-absent still-fails, absence-intent step NEVER cleared, pin-page-grounding scope guard, cross-step lookback bound).",graduation:{status:"gated",gate:"Staging shadow soak clean (interaction_clears_presence_oracle:would_clear fires ONLY where a successful click genuinely resolved+acted on an element whose engine-resolved identity whole-token-matches the failed wait literal \u2014 never on a #1476 coordinate no-op, a non-interactive container-text mirror, an absence-intent verify step, or a stale failure beyond the 1-step lookback) AND the engine-core interaction-clears-presence-oracle detection-inversion passes (flag OFF \u2192 the timed-out presence step still force-FAILs; flag ON \u2192 a matching successful click clears it to a PASS, a no-op/non-matching/absent/absence-intent element still FAILs)",evidence:"staging interaction_clears_presence_oracle:would_clear / :cleared diag events (clear rate + resolvedVia/literal breakdown) + the RunnerRuntime.interactionClearsPresenceOracle unit suite + the step-5 slow-render rehearsal replay (asess_1784961071757_cqwxon46 / asess_1784960734951_4t0zaxp1)",owner:"steering (Alex)",review:"2026-08-01"}},CANVAS_STRATEGY:{key:"CANVAS_STRATEGY",envVars:["AGENTIQA_CANVAS_STRATEGY","AGENTIQA_EXPERIMENT_CANVAS_STRATEGY"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Enables the canvas-app (Miro/Figma/spreadsheet) per-turn strategy prompt injection and the coordinate-action tool-result caveat; off drops both so a false canvas classification cannot alter targeting guidance.",designDoc:"docs/plans/2026-07-06-canvas-capability-design.md",status:"active",added:"2026-07-06"},SCREENSHOT_DIRECT_UPLOAD:{key:"SCREENSHOT_DIRECT_UPLOAD",envVars:["AGENTIQA_SCREENSHOT_DIRECT_UPLOAD","AGENTIQA_EXPERIMENT_SCREENSHOT_DIRECT_UPLOAD"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'Analytics-sink screenshot upload path: on, the sink fetches a presigned R2 PUT URL from /api/analytics/presign-screenshot and PUTs the PNG straight to R2 (bytes never transit web-next); "0" forces the legacy base64 /api/analytics/upload-image route. On any presign/PUT failure the sink falls back to the legacy route per-screenshot regardless of this switch.',designDoc:"packages/engine-core/src/sinks/RemoteAnalyticsSink.ts",status:"active",added:"2026-07-19"},SCOPE_SAME_ORIGIN_LINKS:{key:"SCOPE_SAME_ORIGIN_LINKS",envVars:["AGENTIQA_SCOPE_SAME_ORIGIN_LINKS","AGENTIQA_EXPERIMENT_SCOPE_SAME_ORIGIN_LINKS"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:`Scope-gate valve for page-authored links: a clicked href on a scoped ORIGIN whose path falls outside every scope prefix is allowed (reason same_origin_link, with a scope-drift diag) instead of hard-blocked as an "external link". Cross-origin targets and model-typed navigate URLs keep the hard block. Prevents an LLM-guessed scope prefix from blocking the app's own navigation (assertqa /why-assertqa false 404, 2026-08-05); "0" restores the unconditional prefix block.`,designDoc:"docs/plans/2026-08-05-scope-observed-url-grounding.md",status:"active",added:"2026-08-05"},SCOPE_SPAWN_GROUNDING:{key:"SCOPE_SPAWN_GROUNDING",envVars:["AGENTIQA_SCOPE_SPAWN_GROUNDING","AGENTIQA_EXPERIMENT_SCOPE_SPAWN_GROUNDING"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'Deterministic scope-authoring grounding at the spawn boundary: a same-origin spawn scope entry that no child ever visited (observed-URL ledger, runtime-observed only) and the user never supplied is bounced back to the Coordinator ONCE with the observed-URL list to re-author from; an insisted repeat proceeds fail-open. Also stops checkpoint surfaces with unobserved same-origin URLs from being persisted into the durable AppMap. Pure set-membership on parsed URLs \u2014 no label matching or slug heuristics; "0" disables both the bounce and the AppMap filter.',designDoc:"docs/plans/2026-08-05-scope-observed-url-grounding.md",status:"active",added:"2026-08-05"},SAME_GOAL_ABORT:{key:"SAME_GOAL_ABORT",envVars:["AGENTIQA_SAME_GOAL_ABORT","AGENTIQA_EXPERIMENT_SAME_GOAL_ABORT"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Escalates consecutive milestone-free supervisor REDIRECT verdicts into an abort-then-block, bounding a stuck same-goal step; off leaves the supervisor redirecting until the iteration budget runs out.",designDoc:"docs/plans/2026-07-06-canvas-capability-design.md",status:"active",added:"2026-07-06"},TARGET_CONTAINMENT:{key:"TARGET_CONTAINMENT",envVars:["AGENTIQA_TARGET_CONTAINMENT","AGENTIQA_EXPERIMENT_TARGET_CONTAINMENT"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'Keeps the agent within the app-under-test origin at every navigation chokepoint; off makes every boundary check return "allow" (pre-containment behavior).',designDoc:"packages/engine-core/src/BasePlaywrightService.ts",status:"active",added:"2026-07-05"},INCIDENTAL_EXTERNAL_LOGIN_RECOVERY:{key:"INCIDENTAL_EXTERNAL_LOGIN_RECOVERY",envVars:["AGENTIQA_INCIDENTAL_EXTERNAL_LOGIN_RECOVERY","AGENTIQA_EXPERIMENT_INCIDENTAL_EXTERNAL_LOGIN_RECOVERY"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Necessity-gated split of the third-party-IdP boundary, in BOTH lanes: with NO login credentials configured, an external login reached on a DIFFERENT registrable domain than the app under test AND judged task-INCIDENTAL is RECOVERED \u2014 the service returns the page to the app (popup close \u2192 history back \u2192 pre-navigation app URL \u2192 app origin, every leg bounded at 5s), injects an agent-visible note that names the host, forbids re-following it AND warns that the return trip reset the page's in-page state, and the run continues. Unattended (RunnerRuntime.startRun) that replaces terminating the run as exploration_blocked (staging asess_1785402865447_25cimyx8: a \"check all the buttons\" run followed a social footer link to instagram.com/accounts/login and lost 49 recorded actions); in the interactive CHAT lane (Coordinator/Explorer, and the runner's own sendMessage) it replaces the ask-user credentials pause (staging asess_1785434018769_usq42s45: the self-agent's inner \"Direct task\" Coordinator run blocked on the same host with reason:'interactive_session'). TWO LAYERS: the registrable-domain rule is only the fail-closed floor; before any recovery the service consults the NECESSITY judge (externalLoginNecessityJudge.ts, Layer 2 of the uncertain-boundary class, engine-owned Flash model, at most ONE call per external host per run) with the active plan step \u2014 or, in the chat lane, the turn's task/objective text (BaseRuntime.necessityContextText). FAIL-SAFE onto the pre-existing terminal for `required`, `uncertain`, confidence < 0.8, a judge error, and NO judge wired at all (desktop / no engine key \u21D2 the whole feature is inert) \u2014 so a human who genuinely needs to hand over SSO credentials still gets the pause, and only logins the task does not need stop interrupting them. Bounded at 3 recoveries per run via ONE counter shared by both lanes; a 4th hit, a recovery that lands back on an external login, or a SAME-registrable-domain login (the app's own SSO) keeps today's terminal block verbatim. GRADUATED default-ON 2026-07-30 (gates G1-G4): with NO env both lanes now recover a confidently-`incidental` login and the judge is consulted in the would-recover branch. An explicit `=0` under either spelling restores the pre-graduation terminal verbatim in BOTH lanes and spends NO judge call, while the decision is still computed and emitted as `incidental_external_login` diagnostics (with `wouldRecover`, the domain-signal-only upper bound across BOTH lanes \u2014 pair it with `unattended`; plus the necessity verdict/confidence/reason whenever a judge ran). Recovery reasons are lane-labeled (`incidental` unattended / `chat_incidental` interactive); the pre-2026-07-30 `interactive_session` terminal reason is retired. Never fires when credentials are configured (the pre-existing suppress) and never sees a same-product cross-environment escape (targetContainment runs first).",designDoc:"docs/plans/2026-07-06-uncertain-boundary-escalation-design.md",status:"active",added:"2026-07-30",notes:"GRADUATED 2026-07-30 (default ON) \u2014 graduation gate v2 (PR #1969), four attestations; the first graduation whose effect is to REMOVE a whole-run terminal rather than add a gate. killSwitch resolves the no-env value from this defaultState (#1729), so graduation = defaultState 'on' AND INCIDENTAL_EXTERNAL_LOGIN_RECOVERY listed in INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire); an explicit AGENTIQA_INCIDENTAL_EXTERNAL_LOGIN_RECOVERY=0 (or the AGENTIQA_EXPERIMENT_ spelling) still restores the byte-identical pre-graduation terminal \u2014 `exploration_blocked` unattended, the ask-user credentials pause in chat \u2014 with ZERO judge calls. GRADUATION EVIDENCE. G1 (necessity-discrimination eval PAIR, deterministic + local): e2e/evals/plans/incidental-external-login-recovery.ts \u2014 6 legs, both necessity directions, 2/2 green with the REAL gemini-3-flash judge (incidental @0.90 / required @1.00 on the SAME external host) and 1/1 green with the deterministic stub judge. G2 (suites): engine-core incidentalExternalLogin (L1 decision matrix incl. the necessity fold-in AND the chat lane: judge-incidental recovers as `chat_incidental`, required/uncertain/unjudged keep the pause, shared cap), BasePlaywrightService.incidentalExternalLogin (L2 wiring over a real headless browser and synthetic hosts, stubbed judge through the production DI seam, both directions x both lanes + per-host cache + no-judge fail-safe + one shared counter + the forced-OFF parity cases, which are now built by forcing `=0` and never by deleting the key), RunnerRuntime.unattendedRunLifecycle (the necessity-context channel: plan step in a run, task text in chat, undefined when blank), externalLoginNecessityJudge (judge fail-safe). G3 (bounded live staging validation, flag force-ON via the staging orchestrator env): self-agent.yml GH run 30572972048 (2026-07-30, the SECOND G3 attempt) \u2014 3/3 PASSED including ci-first-plan end-to-end (signup -> inner directed run completed -> Save -> re-run of the saved plan). The FIRST attempt failed informatively: the inner run is an assistant_v2 Coordinator session, so the recovery was lane-gated OFF and the run blocked exactly as before the flag existed \u2014 that is what motivated the chat-lane extension and forced the re-run. G4 (telemetry): a LIVE unattended-lane recovery on the saved-plan re-run \u2014 `incidental_external_login {route:'popup_closed', reason:'incidental', idpHost:'www.instagram.com', outcome:'recovered', returnedTo:'https://agentiqa.com/en/pricing', unattended:true, recoveryCount:1, pageStateReset:false, necessityReason:\u2026}` in asess_1785438775291_qqz9uae4 \u2014 a real judge, a real third-party login wall, the popup route; 0 recovery loops, 0 nav-timeout fallbacks, 0 misfires. HONEST RESIDUAL \u2014 the CHAT lane is live-UNEXERCISED: in that run the external site showed the inner run no login wall at all (it redirected to X instead \u2014 a nondeterministic third party), so no live `chat_incidental` recovery was observed. Chat-lane behavior is pinned by HERMETIC evidence only (the G1 eval's chat leg plus the L1/L2 chat-lane cases), which is the one non-live leg of this graduation. SCENARIO-19 CONSEQUENCE: agent-core/oauth-idp-redirect-no-creds-pause used to pass for TWO independent reasons (the flag was staged OFF, and its prompt IS the task-REQUIRED SSO shape); post-graduation only the second holds, so that nightly scenario is now a live assertion that the necessity judge answers `required` on it (G1 measured confidence 1.00 on exactly that shape). The staging orchestrator force-ON override (AGENTIQA_EXPERIMENT_INCIDENTAL_EXTERNAL_LOGIN_RECOVERY=1, hand-set for G3) is redundant once this ships and is scheduled for removal \u2014 see docs/FLAGS-STATE.md. STAGING-FIRST: merging to staging makes this default-ON on the STAGING engine only; PROD stays OFF until a later staging->main release carries it (a natural staged soak). Read site: packages/engine-core/src/BasePlaywrightService.ts (recoverIncidentalExternalLoginIfNeeded). Binds claim agent.incidental-external-login-recovery."},PIN_SUBSTANTIATION:{key:"PIN_SUBSTANTIATION",envVars:["AGENTIQA_PIN_SUBSTANTIATION","AGENTIQA_EXPERIMENT_PIN_SUBSTANTIATION"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'Flips a criterion the model graded "passed" to failed when the pinned expected value is not substantiated; off returns the model grade verbatim (pins still render, enforcement is off).',designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-07-07"},PIN_CROSS_STEP_SUBSTANTIATION:{key:"PIN_CROSS_STEP_SUBSTANTIATION",envVars:["AGENTIQA_PIN_CROSS_STEP_SUBSTANTIATION","AGENTIQA_EXPERIMENT_PIN_CROSS_STEP_SUBSTANTIATION"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:`FIX 3 (extends FIX 2 to the cross-STEP misattribution class). Before Amendment 5a synthesizes a strict pinned criterion as "never graded \u2192 unconfirmed \u2192 fail", it searches EARLIER step entries for an ORPHAN grade of the IDENTICAL check \u2014 graded passed=true, bound to NONE of its own step's plan criteria, and whose observed value substantiates THIS pin's expectedValue (the SAME substantiatePinnedCriterion gate). A match means the grading LLM misfiled the pin under a PRECEDING/action step (prod asess_1785211658727: step 2's 3 strict pins graded inside step 1, an action step), so the pin was really evaluated \u2014 the substantiated pass is relocated to its own slot instead of false-failing the step. Off restores FIX 2 behavior verbatim (no cross-step search). The rescue is SCOPED to misattributed grades, not a blanket 'a passing grade exists elsewhere': (1) only orphan grades qualify \u2014 a step's OWN bound verdict is never borrowed, so a still-passing earlier verify cannot mask a later regression; (2) a contradicting FAILING grade for the same check anywhere blocks the rescue; (3) only a STRICTLY EARLIER step's grade qualifies, so a later step's page state is never laundered backward onto an earlier assertion (a backward misattribution stays fail-closed); (4) the caller only rescues a pin unique among plan verify criteria. Outside that envelope the pre-existing fail-closed synthesis stands. EVIDENCE-EPOCH DISPOSITION (steering 2026-07-28): 'strictly earlier' does not mean 'same page state', so a rescue is only a silent PASS when NO state-changing plan step (type action/setup) sits STRICTLY BETWEEN the rescuing entry and the pin's own step \u2014 the confirmed prod shape (verify step k's grades misfiled under the immediately preceding action step k-1) has nothing in between, so it keeps its full PASS rescue. A STALE-FORWARD rescue (an intervening action/setup broke the epoch, so the observation may predate a regression) is instead SOFT-WITHHELD to a step-level 'warning' \u2014 the criterion carries the substantiated pass noted 'substantiated by an earlier-step grade; not re-verified at this step' and the step is demoted passed->warning (never a hard fail, never a clean pass; run status is untouched since only 'failed' steps downgrade a run). This is the ABSENCE_AWARE_VERIFY-ABSTAIN / GROUNDING_EPOCH_FIDELITY withhold rail: the criterion stays passed:true on purpose, because flipping it would route deriveStepStatusFromCriteria to a hard 'failed' on a strict criterion (warning-cap-only). Every accepted rescue emits RunnerRuntime log pin_cross_step_substantiated {stepIndex, sourceStep, expected, orphanCandidates, corpusSize, reason} for prod frequency/provenance, where reason is 'same-epoch-rescue' (PASS) or 'stale-forward-warning' (withheld). SHARED BINDER (round 4): the orphan test (guard 1) READS the already-computed per-entry bindings from bindGradesToPlanSlots \u2014 the ONE authoritative two-pass binder that also drives FIX 2's cross-entry union and the reported criteria results \u2014 and never re-derives them. An earlier revision ran its own SEQUENTIAL bindGradeToPlanIdx loop, which diverges from the two-pass binder wherever a rephrased grade's positional fallback would steal a slot a later grade matches by exact text: the authoritative binder books that grade onto its own step (non-orphan) while the sequential mirror leaves it unbound (orphan) and thus eligible to rescue another step's pin \u2014 an EMERGENT false-PASS reachable only with PIN_CROSS_STEP_SUBSTANTIATION and CRITERION_BIND_RESIDUAL both on, which each flag's own tests miss. Pinned by a 4-cell flag-matrix test. PER-CRITERION FLOOR (round 4): the rescue pushes a synthetic passed result, which switched OFF the whole-step zero-grade 'unsubstantiated verify' floor (that floor tests criteriaResults.length === 0) for the step's OTHER criteria \u2014 and Amendment 5a itself only covers PINNED strict criteria, so a plain strict criterion beside a rescued pin was adjudicated by nothing and rode through on the reported 'passed'. On steps where a rescue fired, each plan criterion that is in neither the FIX 2 union nor Amendment 5a's coverage AND has no matching grade anywhere in the cross-step corpus now synthesizes its own failure (strict) or warning (strict:false), emitting pin_cross_step_sibling_unsubstantiated {stepIndex, check, strict, rescuedPinsOnStep}. Scoped to rescue-touched steps so no untouched verdict moves, and gated on 'ungraded ANYWHERE' so the whole-step misattribution the rescue tolerates is not re-punished.`,designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-07-28"},CHECK_TEXT_ATOM_PIN:{key:"CHECK_TEXT_ATOM_PIN",envVars:["AGENTIQA_CHECK_TEXT_ATOM_PIN","AGENTIQA_EXPERIMENT_CHECK_TEXT_ATOM_PIN"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"run_complete grade-time atom pinning: a strict verify criterion whose check text pins exactly one URL literal (http(s):// or bare localhost) and carries no expectedValue has that literal promoted to an effective expectedValue so the pin-substantiation / grounding machinery fires on it, plus a deterministic URL-host floor that fails the criterion closed (naming both hosts) when the evidence observes a different-host URL and preserves the pass on a scheme-only / trailing-slash difference \u2014 regardless of what the substantiation/drift judge decided. Off leaves a bare {check, strict} URL criterion ungraded past the model self-grade (pre-feature behavior).",designDoc:"docs/plans/2026-07-19-ag7727-run-fidelity-fixes-design.md",status:"active",added:"2026-07-19"},TYPED_MATCH:{key:"TYPED_MATCH",envVars:["AGENTIQA_TYPED_MATCH","AGENTIQA_EXPERIMENT_TYPED_MATCH"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Routes pin-substantiation's observed-vs-expected comparison through the typed match-comparator registry keyed on a criterion's `matchType` (country ISO-3166 fold DE\u2261Germany, locale numeric equality); a typed match keeps a pass the brittle literal compare would have flipped, a typed mismatch (wrong country) flips closed. Off (or an absent/`literal` matchType) restores the pure literal `citationSubstantiates` behavior verbatim \u2014 inert until a criterion carries a matchType, so this only removes a false-fail class, never changes an untyped verdict (AG-7753).",designDoc:"docs/plans/2026-07-20-typed-match-comparator-design.md",status:"active",added:"2026-07-20"},COUNTRY_EQUIV_RESCUE:{key:"COUNTRY_EQUIV_RESCUE",envVars:["AGENTIQA_COUNTRY_EQUIV_RESCUE","AGENTIQA_EXPERIMENT_COUNTRY_EQUIV_RESCUE"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'C2 country-equivalence rescue in run_complete pin substantiation: when an already model-PASSED strict criterion\'s literal citation fails, the citation is retried with every recognized country surface form on BOTH sides folded to its ISO-3166 alpha-3, so an address that differs ONLY by country NAME vs CODE ("\u2026H\xF6henkirchen Germany" pin vs "\u2026H\xF6henkirchen DE" observed, prod Lio tp_5a2a7f4c step 9) substantiates instead of brittle-failing. Narrowly scoped: the rescue runs only for a country-SHAPED pin \u2014 `matchType: \'country\'`, or (untyped/literal/text-normalized/presence) a pin whose TERMINAL token is a full country NAME or alpha-3; a bare terminal alpha-2 pin ("Dover, DE") and every number/currency/url/semantic matchType are excluded. The fold itself only rewrites a country NAME anywhere, and a country CODE only as an uppercase token in the terminal country slot (never before a US zip), with subdivision/unit collisions (CA/NL/GB/CH/IE/PL/PT/SE/IN/CAN/NOR/\u2026) restricted to a sole-token read. That extra ambiguity is FOLD-SCOPED (`FOLD_AMBIGUOUS_ALPHA2`/`FOLD_AMBIGUOUS_ALPHA3`): the shared gazetteer read by citedCountry/compareTyped \u2014 i.e. the typed-atom floor and the predicate-basis typed comparator, neither of which this switch gates \u2014 is untouched BY THIS FLAG, verified by a differential sweep of the whole gazetteer. Off restores the pure literal `citationSubstantiates` verdict, so this flag only ever removes a false-FAIL class and nothing this flag contributes survives turning it off. NOTE (AG-8212, 2026-07-29/30) \u2014 that is a statement about THIS flag, not about the comparator stack as a whole: BOTH free-text country reads have since gained their own ungated guard (the same eight collision-prone alpha-2 codes are now gated in `citedCountry` \u2014 the observed scan, direction 1 \u2014 and in `authoredCountryRead` \u2014 the typed-atom floor\'s check-text read, direction 2 \u2014 so a bare code buried in prose resolves a country only when a full NAME corroborates it or when it is the sole token), which this switch does not gate and cannot revert. The fold path and both fold-ambiguity sets remain pre-C2 byte-identical. Split out of TYPED_MATCH (which stays inert on untyped criteria) because this rescue fires on criteria that carry NO matchType.',designDoc:"docs/plans/2026-07-20-typed-match-comparator-design.md",status:"active",added:"2026-07-28"},TYPED_ATOM_FLOOR:{key:"TYPED_ATOM_FLOOR",envVars:["AGENTIQA_TYPED_ATOM_FLOOR","AGENTIQA_EXPERIMENT_TYPED_ATOM_FLOOR"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"AG-7753 Phase 2 strict-unpinned typed-atom floor. run_complete detection ALWAYS runs (shadow): for a strict verify criterion the model graded passed that carries NO expectedValue and whose check text names exactly one recognized typed atom (country ISO-3166 / currency ISO-4217 / number \u2014 the generalization of #1685's URL host floor), the page-read observed value (a11y snapshot, else the structured `observed` grade field; disagreement \u2192 abstain) is compared to the recognized expectation via compareTyped. On a same-type MISMATCH (France where Germany expected) it emits a shadow `typed_atom_floor:would_fail` diag; when this flag is ON it ENFORCES \u2014 flipping the criterion passed\u2192false (only-fails, never originates a pass) and emitting `typed_atom_floor:fire`. Off leaves every verdict byte-identical (shadow diag only). URL stays owned by CHECK_TEXT_ATOM_PIN \u2014 a URL-bearing check is not recognized here.",designDoc:"docs/plans/2026-07-20-typed-match-comparator-design.md",status:"active",added:"2026-07-20",notes:"GRADUATED 2026-07-21 (default on) via the evidence-count doctrine: detection-inversion PROVEN twice \u2014 deterministically in RunnerRuntime.typedAtomFloor.test.ts (33 assertions) and live in the runner/typed-atom-country-floor L3 eval (qa-exhaustive 29861680933: seeded wrong-country step correctly failed) after the self-testing fixture deploy was unblocked (productionBranch was pinned to main). Shadow soak was clean but thin (organic staging plans carry no recognizable unpinned atoms \u2014 vacuous-soak class). Enforcement only-fails a same-type mismatch, never originates a pass. Distinct from TYPED_MATCH (Phase 1, default ON, pinned-criterion fold) and CHECK_TEXT_ATOM_PIN (#1685, URL host floor). Runner lane only (RunnerRuntime run_complete)."},COMPLETION_EVIDENCE_FLOOR:{key:"COMPLETION_EVIDENCE_FLOOR",envVars:["AGENTIQA_COMPLETION_EVIDENCE_FLOOR","AGENTIQA_EXPERIMENT_COMPLETION_EVIDENCE_FLOOR"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Completion-evidence floor for the CHAT/CLI objective lane (the ungrounded-ship seam VERIFY_GATED_DONE misses: it fires only on a failed wait-oracle in the last 5 actions, so a fabricated value with no failing wait ships silently). Detection ALWAYS runs (shadow) at ExplorerRuntime.handleReport on a terminal `completed` report: for an evidence-bearing objective (find/copy/extract a named value) it grounds the agent's TYPED CLAIM (the optional `extractedValues` field on the assistant_v2_report payload; free-text is never parsed): a claimed value found nowhere in the runtime a11y-snapshot corpus nor in runtime_observed revealed facts (whole-token `citationSubstantiates` \u2014 free-form; `compareTyped`'s closed grammar does NOT apply) emits a shadow `completion_grounding:would_demote` diag; a completed extraction objective with NO typed claim is shadow-only signal (reason no_typed_claim) and is NEVER enforced. Label-presence in the corpus never substantiates a value (v1.1). When this flag is ON it ENFORCES the typed-claim-fabrication path only \u2014 attaching an Explorer `VerificationConflict` with the NEW source `completion_ungrounded` (own card copy; NO terminal blockKind) that the existing verification-conflict rail (`applyVerificationConflictFindings` / `focusedTaskVerdictRecommendation`) demotes to do_not_ship in BOTH Coordinator producers. ABSTAINS (never demotes) when the objective is not evidence-bearing, the target is undecidable, or the observed corpus is thin/absent (canvas/off-DOM/dynamic value not in the snapshot) \u2014 demote-on-absence must abstain on any evidence-availability gap (blind-double-read precedent). Off leaves every verdict byte-identical (shadow diag only). Distinct from TYPED_ATOM_FLOOR (runner lane, closed-grammar value mismatch); this is the chat-lane grounding net (absence of any observed evidence, not value-correctness).",designDoc:"docs/plans/2026-07-21-completion-evidence-floor-design.md",status:"active",added:"2026-07-21",notes:"Default OFF in code; detection runs in shadow always (would_demote diag), enforcement is gated \u2014 the PIN_PAGE_GROUNDING / TYPED_ATOM_FLOOR shadow-first precedent. Single Explorer-level hook (handleReport) so both Coordinator verdict producers surface it (the cross-producer parity bug class, PR #643). Chat/Explorer lane only (assistant_v2_report). v1.1: enforcement requires a typed extractedValues claim proven absent from corpus+facts (provable fabrication); strict-extraction recognizer with common-UI-word stoplist (generic read/find objectives abstain); no-typed-claim path stays shadow-only so the soak measures claim-population rate. Grounds fabrication of a claimed value, NOT mis-selection of a real-but-wrong on-page value.",graduation:{status:"gated",gate:"Staging shadow soak clean (would_demote fires on the wandering-maze fabricate-and-ship replicates, zero would_demote on legitimately-shipping value-extraction controls) AND the engine-core completion-evidence-floor detection-inversion passes (floor OFF \u2192 fabricate-and-ship rides through as ship, ON \u2192 do_not_ship on the identical input in BOTH producers)",evidence:"staging completion_grounding:would_demote diag events + the engine-core completion-evidence-floor unit suite + the loop-detection/wandering-self-stop flag-ON graduation run",owner:"steering (Alex)",review:"2026-07-23"}},SETUP_NOTE_GROUNDING:{key:"SETUP_NOTE_GROUNDING",envVars:["AGENTIQA_SETUP_NOTE_GROUNDING","AGENTIQA_EXPERIMENT_SETUP_NOTE_GROUNDING"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Setup/action step-note grounding \u2014 surface S-A of the substantiation-fidelity (\"honest words\") track. Detection ALWAYS runs (shadow) at RunnerRuntime run_complete, after substantiation + the typed-atom floor: for each setup/action step (verify steps out of scope) graded passed/warning whose NOTE names a completed UI action (a closed past-tense/gerund verb lexicon \u2014 entered/typed/filled/submitted/clicked/selected/uploaded/dragged/\u2026) it checks the step's own planStepIndex-stamped action-tool window; a note asserting an action with ZERO grounding action tool calls in-window (the census shape asess_1784714866561_jhc34ht9 \u2014 pre-authed login steps graded green with 'Entered the email'/'Submitted the login form' notes and no type_*/click ever fired) emits a shadow `narration_fidelity:would_flag{surface:'setup_note', stepIndex, claim, missing_action, toolCallsInWindow}`. ABSTAINS (never flags) on a verify/read-only step, a note with no action verb, a non-terminal grade, or a window carrying ANY grounding action tool \u2014 ambiguity always abstains (closed allowlist). When this flag is ON it ENFORCES honest note substitution \u2014 the fabricated action clause is replaced with an honest precondition note; the step's verdict/status is UNTOUCHED (never a hard fail \u2014 the precondition was met, just not by the asserted action). Off leaves every stepResults note byte-identical (shadow diag only). Distinct from TYPED_ATOM_FLOOR (verify-criterion value mismatch) and COMPLETION_EVIDENCE_FLOOR (chat-lane claimed-value fabrication); this grounds SETUP/ACTION step NARRATION against the action log.",designDoc:"docs/plans/2026-07-22-substantiation-fidelity-design.md",status:"active",added:"2026-07-22",notes:"Default OFF in code; detection runs in shadow always (would_flag diag), enforcement (honest note substitution) is gated \u2014 the COMPLETION_EVIDENCE_FLOOR / TYPED_ATOM_FLOOR shadow-first precedent. Single RunnerRuntime run_complete pass (mirrors the typed-atom floor shadow). Deterministic \u2014 ~zero marginal LLM cost (reads the already-collected stepResults notes + the planStepIndex-stamped action-message log). P0 of the substantiation-fidelity track (S-A); S-B/S-C/S-D are separate per-phase flags.",graduation:{status:"gated",gate:"Staging shadow soak clean (narration_fidelity:would_flag{surface:'setup_note'} fires on the census-shaped reproduced-RED fixture, zero would_flag on legitimately-honest action notes whose tool call fired) AND the engine-core setupNoteGrounding detection-inversion passes (rip the pass out \u2192 the fixture stops flagging; enforce-ON rewrites the note, verdict untouched, on the identical input)",evidence:"staging narration_fidelity:would_flag diag events + the engine-core setupNoteGrounding + RunnerRuntime.setupNoteGrounding unit suites + the runner/narration-ghost-setup reproduced-RED eval",owner:"steering (Alex)",review:"2026-07-29"}},GROUNDING_EPOCH_FIDELITY:{key:"GROUNDING_EPOCH_FIDELITY",envVars:["AGENTIQA_GROUNDING_EPOCH_FIDELITY","AGENTIQA_EXPERIMENT_GROUNDING_EPOCH_FIDELITY"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Criterion grounding-epoch fidelity \u2014 surface S-B of the substantiation-fidelity (\"honest words\") track. Detection ALWAYS runs (shadow) at RunnerRuntime run_complete, after substantiation + grounding + the typed-atom floor: for each verify-step criterion substantiated as PASSED it compares the criterion's grounding epoch (the monotonic capture/generation index at which its pinned value was actually groundable in a full page snapshot) against the step's OWN verify-time epoch. A value groundable ONLY in a STRICTLY-LATER capture \u2014 a subsequent step's / a different entity's snapshot \u2014 is cross-entity-deferred grounding (the census asess_1784714866561_jhc34ht9 step 6: base-draft pins pin_page_grounding:would_fail at the step's own epoch, then matched off the duplicate-draft (e674/e731) and submitted-request (e1337) snapshots, all past the base-draft epoch) and emits a shadow `narration_fidelity:would_flag{surface:'grounding_epoch', stepIndex, criterion, groundingEpoch, stepEpoch, epochDriftRefs}`. ABSTAINS (never flags) on a value grounded at/before the step's own epoch (honest same-epoch grounding, or a legitimately-earlier carried observation), an explicitly carried-forward observation with recorded provenance, a criterion that did not pass, or an UNDETERMINED epoch (a missing epoch never manufactures a flag). When this flag is ON it ENFORCES via the existing VERIFY_REOBSERVE_WITHHOLD soft-withhold rail: the cross-entity-grounded criterion is treated as UNCONFIRMED and routed to a warning (never a red fail \u2014 the values may be correct; the fix removes the false EVIDENCE ATTRIBUTION, not the pass). Off leaves every verdict byte-identical (shadow diag only). Distinct from PIN_PAGE_GROUNDING (value ABSENCE at grade time) and SETUP_NOTE_GROUNDING (setup/action step NARRATION); this grounds a verify criterion's EVIDENCE-EPOCH provenance.",designDoc:"docs/plans/2026-07-22-substantiation-fidelity-design.md",status:"active",added:"2026-07-23",notes:"Default OFF in code; detection runs in shadow always (would_flag diag), enforcement (soft-withhold to warning via VERIFY_REOBSERVE_WITHHOLD) is gated \u2014 the COMPLETION_EVIDENCE_FLOOR / SETUP_NOTE_GROUNDING shadow-first precedent. Single RunnerRuntime run_complete pass (mirrors the setup-note-grounding shadow). Deterministic \u2014 ~zero marginal LLM cost (reconstructs each step's own capture epoch + each pinned value's grounding epoch from the retained per-generation full snapshots collected during the run). P1 of the substantiation-fidelity track (S-B); S-A (SETUP_NOTE_GROUNDING) shipped P0, S-C/S-D are separate flags.",graduation:{status:"gated",gate:"Staging shadow soak clean (narration_fidelity:would_flag{surface:'grounding_epoch'} fires on the census-shaped A\u2192duplicate-B reproduced-RED fixture, zero would_flag on same-epoch honest grounding controls) AND the engine-core groundingEpochFidelity detection-inversion passes (rip the drift out \u2192 the fixture stops flagging; enforce-ON soft-withholds the criterion to warning, never red, on the identical input)",evidence:"staging narration_fidelity:would_flag diag events + the engine-core groundingEpochFidelity + RunnerRuntime.groundingEpoch unit suites + the runner/narration-grounding-epoch reproduced-RED eval",owner:"steering (Alex)",review:"2026-07-30"}},NOTE_CONTRADICTION_FLOOR:{key:"NOTE_CONTRADICTION_FLOOR",envVars:["AGENTIQA_NOTE_CONTRADICTION_FLOOR","AGENTIQA_EXPERIMENT_NOTE_CONTRADICTION_FLOOR"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Terminal-error verdict-honesty Layer A \u2014 the deterministic note-contradiction floor (run_27825c90). Detection ALWAYS runs (pure module) at RunnerRuntime run_complete, LAST \u2014 after every fail gate: for a step the model graded `passed` carrying a passed criterion whose OWN note/groundingObservation text contains a conservative error marker (error/failed/failure/timeout/timed out/aborted/exception/went wrong, word-boundary) that the criterion's own check/expectedValue (or its step text) does NOT license, it caps that step to `warning` with an explanatory note. A marker GOVERNED BY A NEGATOR in the same clause (no/not/never/without/none/zero/didn't/no longer/free of/\u2026 \u2014 see NEGATOR_RE) is BENIGN and never caps ('No error appeared', 'verified no timeout occurred'), so a note that negates every marker is skipped; a note that re-asserts an error after negating one ('no error at first, then Error: timeout appeared') still caps. ONE-DIRECTIONAL \u2014 only caps a passed step (never rescues/escalates a model-failed grade; the criterion result stays passed, the STEP status is soft-withheld like VERIFY_REOBSERVE_WITHHOLD). Check-text licensing is dumb-string (accepts the documented `verify NO error` negation blind spot on the CHECK \u2014 Layer B handles it semantically). This flag ALSO gates the deterministic report_issue verdict-bearing fold (decision 6): a high/medium-severity `logical` issue filed during an otherwise-clean `passed` run caps the run at `warning`. When ON it ENFORCES; off \u21D2 `note_contradiction:would_cap` / `report_issue_contradiction:would_cap` diags and every verdict byte-identical. Distinct from Layer B (TERMINAL_ERROR_FLOOR, a model terminal-screen read). Ships default-ON (the trap eval's warning-cap signature only greens with both floors live).",designDoc:"docs/plans/2026-07-23-terminal-error-verdict-honesty-design.md",status:"active",added:"2026-07-23",notes:"Ships default-ON from inception (not graduated from an off default) \u2014 registered in killSwitchDefaultState.test.ts INTENTIONALLY_GRADUATED. Deterministic (~zero marginal LLM cost \u2014 scans the already-collected stepResults notes + grounding observations). Pure logic in packages/engine-core/src/noteContradictionFloor.ts; applied at RunnerRuntime run_complete AFTER all fail gates so it only ever touches a still-passed step. Runner lane only. Layer A of the terminal-error wave; TERMINAL_ERROR_FLOOR is Layer B."},TERMINAL_ERROR_FLOOR:{key:"TERMINAL_ERROR_FLOOR",envVars:["AGENTIQA_TERMINAL_ERROR_FLOOR","AGENTIQA_EXPERIMENT_TERMINAL_ERROR_FLOOR"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Terminal-error verdict-honesty Layer B \u2014 one unconditional terminal-screen model read per plan run (run_27825c90). At RunnerRuntime run_complete (LAST, after every fail gate) the terminal capture plus the plan's step texts are sent through the existing `deps.blindReader` seam, asked whether an app error/failure state is visible that no plan step asserts. The terminal frame is selected DELIBERATELY from the in-memory `_screenshots` ledger (NOT R2 \u2014 works in the anonymous eval lane where imageStorage is null): the LAST full-frame capture (crops and post-upload shots are skipped; each push site is kind-tagged), abstaining when none exists or when it predates the last browser action (stale). Licensing is SEMANTIC (the reader sees the step texts, so it handles negation like `verify NO error is shown`). An unlicensed visible error \u21D2 cap the run at `warning` by demoting the highest-index PASSED step (via capRunToWarning \u2014 never a skipped/failed step; when a sibling floor already capped the terminal step, the quote is appended, not double-capped) and quote the error text in the step note + run summary. Reader unavailable / no full-frame / stale frame / non-answer / licensed error \u21D2 abstain no-op (never a new false-fail). ONE-DIRECTIONAL (only caps a still-passed step; the binary run status never flips \u2014 `warning` is the step-level/derived aggregate). When ON it ENFORCES; off \u21D2 `terminal_error:would_cap` shadow, no cap. This is the ONLY layer that fires when the model never transcribes the banner into any note (the incident shape). Distinct from Layer A (NOTE_CONTRADICTION_FLOOR, deterministic note scan). Ships default-ON.",designDoc:"docs/plans/2026-07-23-terminal-error-verdict-honesty-design.md",status:"active",added:"2026-07-23",notes:"Ships default-ON from inception \u2014 registered in killSwitchDefaultState.test.ts INTENTIONALLY_GRADUATED. Cost \u2248 one cheap Flash vision call per plan run (the cost-isolated blindReader model, same as BLIND_DOUBLE_READ). Detection (the read) runs whenever a reader + final capture are available so the switch shadows (`terminal_error:would_cap`) when off; a later cost-driven change could guard the call on the flag. Pure prompt/interpretation in packages/engine-core/src/terminalErrorFloor.ts. Runner lane only. Layer B of the terminal-error wave; NOTE_CONTRADICTION_FLOOR is Layer A + the report_issue fold."},BLIND_DOUBLE_READ:{key:"BLIND_DOUBLE_READ",envVars:["AGENTIQA_BLIND_DOUBLE_READ","AGENTIQA_EXPERIMENT_BLIND_DOUBLE_READ"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"When on, run_complete independently re-reads the persisted evidence screenshot for each pinned criterion that survived substantiation as passed (a blind LLM read that never sees the expected value) and flips the pass to failed when the observed value does not match the pin; abstains (no-op) on missing image / R2 read failure / reader non-answer / batch timeout. Off makes zero extra LLM calls and leaves every verdict unchanged.",designDoc:"docs/plans/2026-07-15-blind-double-read-design.md",status:"active",added:"2026-07-15",notes:"Default OFF in code; forced ON in staging via the orchestrator env (AGENTIQA_EXPERIMENT_BLIND_DOUBLE_READ=1) \u2014 the exact GROUNDED_EXPECTATIONS precedent. Prod enablement is a later explicit flip after nightly baselines. Runner lane only (RunnerRuntime run_complete).",graduation:{status:"gated",gate:"HARDENED 2026-07-21: BDR must actually FIRE (reads>0) and FLIP on runner/blind-read-conflation-trap \u2014 not merely avoid errors. BLOCKED on the evidence-resolution gap: on the conflation trap BDR abstained missing_image (resolveEvidenceMessage found no hasScreenshot+planStepIndex message \u2014 the model graded from the inline tool-result snapshot, which is never persisted as a screenshot message) and the seeded false-pass shipped (qa-exhaustive 29861680933). Fix = guarantee a planStepIndex-stamped verify screenshot (STEP_MARKER_FOLD stamping path is the natural vehicle \u2014 same root as the run-detail evidence-fidelity gap) or broaden resolveEvidenceMessage fallback. Plus: no new false-FAIL class on nightly runner baselines (MET as of 07-21; abstains acceptable). tp_00b325d5 confirm-path re-verified 07-21 (2 reads/2 confirms/0 flips).",evidence:"runner/blind-read-{parroting,conflation}-trap eval verdicts (conflation must flip) + blind_double_read diag events + nightly runner baselines",owner:"steering (Alex)",review:"2026-07-28"}},NEVER_GRADED_RETRY:{key:"NEVER_GRADED_RETRY",envVars:["AGENTIQA_NEVER_GRADED_RETRY","AGENTIQA_EXPERIMENT_NEVER_GRADED_RETRY"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:'When on (and a criterionRegrader is wired at engine boot), run_complete re-asks the grader ONCE for a STRICT pinned required criterion that NO stepResults entry graded (genuine model-omission \u2014 absent from the FIX 2 cross-entry union) before Amendment 5a synthesizes its "never graded \u2192 unconfirmed \u2192 fail". The returned grade is fed through the SAME substantiatePinnedCriterion gate, so only a re-grade that independently CITES the pinned value rescues the pin to passed; a returned FAIL, an unsubstantiated pass, or an abstain (no image / read failure / non-answer / error / batch timeout) leaves the "never graded" fail byte-identical. Cap ONE retry per pin, no loops. Off makes zero extra LLM calls and leaves every verdict unchanged.',designDoc:"docs/plans/2026-07-27-never-graded-retry-design.md",status:"active",added:"2026-07-27",notes:"Default OFF in code; force ON in staging via the orchestrator env (AGENTIQA_EXPERIMENT_NEVER_GRADED_RETRY=1) \u2014 the BLIND_DOUBLE_READ / GROUNDED_EXPECTATIONS shadow-first precedent. Prod enablement is a later explicit flip after a benchmark + staging soak. Runner lane only (RunnerRuntime run_complete, buildRunnerDeps.getCriterionRegrader \u2014 the SAME cost-isolated Flash model as blindReader; child Runners on the web-coordinator lane do not receive it yet, mirroring blindReader's own coordinator-forward gap). COMPOSES with (does not regress) PR #1884 FIX 2: the retry fires ONLY on pins genuinely absent from the cross-entry union FIX 2 computes, i.e. exactly the omission FIX 2 deliberately left as a fail. killSwitch resolves the no-env value from this defaultState (#1729), so GRADUATING = flip defaultState to 'on' AND add NEVER_GRADED_RETRY to INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire). Read site: packages/engine-core/src/RunnerRuntime.ts (neverGradedRetryEnabled \u2192 runNeverGradedRetries). Acceptance tests: packages/engine-core/src/__tests__/RunnerRuntime.neverGradedRetry.test.ts.",graduation:{status:"gated",gate:"FIRST SLICE = build + unit (this PR): shadow behind the default-OFF flag, byte-identical when off, fail-closed-safe (retry never manufactures a pass; a genuinely-ungraded-after-retry pin still fails). GRADUATION (later, separate slices) needs: (1) a kind-agnostic graduation benchmark (e2e/benchmark/, the ABSENCE_AWARE_VERIFY #1857 precedent) showing the Lio-s2 omission false-FAIL class is rescued to PASS with 0 new false-PASS and 0 regressions on the runner corpus, adversarially proven able to say NO-GO; (2) a staging shadow/force-ON soak measuring re-grade fire rate + rescue vs abstain vs still-fail; (3) parity when BLIND_DOUBLE_READ is also on; (4) gate-review sign-off (Alex).",evidence:"never_graded_retry:{start,rescue,abstain,still_fail} diag events + the graduation benchmark verdict + a staging soak on re-grade outcomes",owner:"steering (Alex)",review:"2026-08-03"}},EVIDENCE_FIDELITY:{key:"EVIDENCE_FIDELITY",envVars:["AGENTIQA_EVIDENCE_FIDELITY","AGENTIQA_EXPERIMENT_EVIDENCE_FIDELITY"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"P0 of the evidence-fidelity ('honest pictures') design: makes full_page_screenshot honest. When on, BasePlaywrightService.fullPageScreenshot climbs a capture ladder (document_full \u2192 cdp_full \u2192 scroll_stitch \u2192 viewport_degraded) so an inner-overflow layout (asklio line-items, where the document is viewport-height but the scrollable content lives in an inner overflow:auto container) is captured at its true extent instead of a silent viewport crop; the ONLY non-full path stamps EnvState.captureMode 'viewport_degraded' and emits the full_page_capture:degraded marker \u2014 never a silent lie. Off is byte-identical to today EXCEPT the geometry measurement + a shadow degradation marker still run (detect-only) so staging can measure the lie's live frequency before the capture behavior flips. EnvState.captureMode is set in BOTH states.",designDoc:"docs/plans/2026-07-22-verify-evidence-fidelity-design.md",status:"active",added:"2026-07-22",notes:"Default OFF in code; force ON in staging via the orchestrator env (AGENTIQA_EXPERIMENT_EVIDENCE_FIDELITY=1) \u2014 the GROUNDED_EXPECTATIONS / BLIND_DOUBLE_READ precedent. Prod flip is a later explicit step after nightly baselines + a shadow soak on the degradation marker. killSwitch resolves the no-env value from this defaultState (#1729), so GRADUATING = flip defaultState to 'on' AND add EVIDENCE_FIDELITY to INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire). Read sites: (P0) packages/engine-core/src/BasePlaywrightService.ts fullPageScreenshot \u2192 the honest capture ladder; (P1) packages/engine-core/src/RunnerRuntime.ts \u2192 per settled static-verify batch, capture ONE canonical honest full_page_screenshot, derive planStepIndex-stamped per-criterion crops (region resolved off the graded node; uncropped fallback), and persist a StepEvidenceRef on each TestPlanV2StepResult/CriterionResult at run_complete. Flag OFF is byte-identical to today EXCEPT the P0 shadow degradation marker: no canonical capture, no refs. P1 supersedes the run-detail findStepEvidenceIndex heuristic on runs that carry refs (legacy no-ref runs fall back). P2 (flip BLIND_DOUBLE_READ on the planStepIndex-stamped evidence P1 now produces) is the remaining follow-up gated on this.",graduation:{status:"gated",gate:"P0/P1 reproduced-RED evals green with the flag ON (runner-fullpage-honesty capture-mode/height/marker facts; degraded-abstain unit; P1 wrong-region + stamping-integrity) AND a staging shadow soak on the full_page_capture:degraded marker showing the expected live frequency with NO capture regression on batch.verify-never-blind",evidence:"full_page_capture:degraded diag events (staging shadow soak) + the engine-core capture-honesty integration test + the captureFidelity unit suite + the batch.verify-never-blind regression",owner:"steering (Alex)",review:"2026-07-29"}},PER_ACTION_BILLED_STEPS:{key:"PER_ACTION_BILLED_STEPS",envVars:["AGENTIQA_PER_ACTION_BILLED_STEPS","AGENTIQA_EXPERIMENT_PER_ACTION_BILLED_STEPS"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Decouples the billed step count from the LLM-iteration count (W1 of the batched-actions design, Decision 6). When on, the main-loop agent_step llm_usage event carries an additive billedUnits integer = executed browser actions + verify captures this turn (a marker-only signal_step turn earns 0); ingest sums it into run_billing.step_count. Off omits the field entirely, so ingest bills one step per agent_step event \u2014 byte-identical behavior AND billing to pre-change. The only intended billing delta when on (today, one-action-per-turn) is that marker-only iterations bill 0 instead of 1; a batched turn executing k actions will bill k (W3).",designDoc:"docs/plans/2026-07-18-batched-actions-design.md",status:"active",added:"2026-07-18",notes:"GRADUATED 2026-07-21 (default on): staging run_billing parity check over the force-ON window (since 07-18) held exactly \u2014 116/116 flag-ON runs with step_count == executed actions + verify captures, 99/99 marker-only iterations billed 0; billing.batched-steps-parity unit lane green. Billing substrate for the batched-actions family (W1). Read site: packages/engine-core/src/billedUnits.ts (perActionBilledStepsEnabled). Remove the staging orchestrator env var once this reaches staging."},STEP_MARKER_FOLD:{key:"STEP_MARKER_FOLD",envVars:["AGENTIQA_STEP_MARKER_FOLD","AGENTIQA_EXPERIMENT_STEP_MARKER_FOLD"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Phase 1 of the batched-actions design (Decision 3): when on, the RunnerRuntime run-mode prompt instructs the model to emit the signal_step boundary marker TOGETHER with the signaled step's FIRST action in the SAME turn \u2014 but ONLY for setup/action steps; VERIFY steps keep the separate marker turn so evidence is never captured blind. This collapses the ~18.7% of runner iterations that are marker-only today. HARD COUPLING: inert unless PER_ACTION_BILLED_STEPS is ALSO enabled \u2014 a folded turn under iteration-billing would bill 1 where marker+action should bill 2, breaking billing parity. The code guard (stepMarkerFold.ts::stepMarkerFoldEnabled = STEP_MARKER_FOLD && PER_ACTION_BILLED_STEPS) makes the fold prompt byte-identical to pre-change whenever PER_ACTION_BILLED_STEPS is off, so this flag alone changes nothing. No engine dispatch change: the multi-call loop already runs signal_step before the folded action in order (both in one generation the model saw the same screen), and billedUnits (W1) already counts marker+action as 1 billed unit.",designDoc:"docs/plans/2026-07-18-batched-actions-design.md",status:"active",added:"2026-07-18",notes:"Default OFF in code. Prompt-only change (no engine dispatch or verify-flow change). Read site: packages/engine-core/src/stepMarkerFold.ts (stepMarkerFoldEnabled), consumed in RunnerRuntime.buildRunnerPrompt run-mode signal_step cadence directive. The AND-coupling with PER_ACTION_BILLED_STEPS lives in code, not just here: flipping STEP_MARKER_FOLD=1 while PER_ACTION_BILLED_STEPS stays off is a no-op.",graduation:{status:"gated",gate:"Default-off soak on staging, then a staging flip (with PER_ACTION_BILLED_STEPS on) proves per-step verdict counts identical to OFF and marker-only iterations drop from ~18.7% to <7% (batch.step-attribution-preserved)",evidence:"batch.step-attribution-preserved unit lane + staging marker-only-iteration % soak measurement",owner:"steering (Alex)",review:"2026-07-25"}},WARNING_CRITERION_BIND:{key:"WARNING_CRITERION_BIND",envVars:["AGENTIQA_WARNING_CRITERION_BIND","AGENTIQA_EXPERIMENT_WARNING_CRITERION_BIND"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'RETIRED 2026-08-04 by the typed criterion reference. It gated the render tiers of the TEXT binder \u2014 strip the platform-appended " (warning only)" suffix, resolve {{unique}}/{{timestamp}} \u2014 so a failing warning-only grade could bind by text and keep its authored strict:false instead of being forced strict:true by the ordinal fallback, plus the C1 structural gate that decided when an ORDINAL-bound failing grade could keep an explicit strict:false. A grade now names its criterion by index (`criteriaResults[].criterionIndex`), so there is no text tier to gate and no ordinal fallback to fail closed against: a bound reference simply takes the criterion authored flag, an unbound one takes strict:true. Setting this env does nothing. Entry kept for provenance only; the switch has no read site.',designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"deprecated",added:"2026-07-16"},CRITERION_BIND_RESIDUAL:{key:"CRITERION_BIND_RESIDUAL",envVars:["AGENTIQA_CRITERION_BIND_RESIDUAL","AGENTIQA_EXPERIMENT_CRITERION_BIND_RESIDUAL"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"RETIRED 2026-08-04 by the typed criterion reference. It chose between the two-pass binder (text bindings claim their slot first, then a positional residual pass) and the sequential one whose ordinal fallback could steal a slot a later grade matched by exact text \u2014 a grade-ORDER lottery over identical observations. Both passes are gone: a grade states which criterion it graded, so no slot is ever assigned by position and the order the model lists its grades in decides nothing. Setting this env does nothing. Entry kept for provenance only; the switch has no read site.",designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"deprecated",added:"2026-07-28"},CRITERIA_CONTAINMENT:{key:"CRITERIA_CONTAINMENT",envVars:["AGENTIQA_CRITERIA_CONTAINMENT","AGENTIQA_EXPERIMENT_CRITERIA_CONTAINMENT"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"GRADUATED 2026-08-03 (default ON) \u2014 flipped OFF\u2192ON because the class it contains is LIVE ON PROD and is what the verdict-replay gate is red on. Closed-world containment on run_complete: a step's verdict is decided ONLY by criteria the PLAN authored. A graded criterion result that binds to NO authored slot of its step (the grading model invented it) is still persisted \u2014 marked `unauthored: true` for forensics \u2014 but `deriveStepStatusFromCriteria` skips it, so a fabricated strict failure can no longer decide a step. Live prod-shaped defect (canary tp_vrgate_offer_upload_retry step 13, ~2/9 replicates): the step authors exactly 2 criteria (Description \"Montage und Einweisung\", Unit \"St\xFCck\"), both graded PASS with correct grounding, and the model additionally graded a THIRD criterion \u2014 `Order line 2 Quantity is \"St\xFCck\"`, strict:true, observed \"1\" \u2014 that exists in neither the plan nor the DB; that invented failure hard-failed the run. Detection ALWAYS runs, in BOTH states: at the default the engine excludes and logs `criteria_containment:excluded` {stepIndex, criterionText, strict, observed, wouldFlipStep, authoredCriteria, gradedCriteria}; with an explicit `=0` under either spelling every verdict is byte-identical to pre-graduation staging and the engine logs `criteria_containment:would_exclude` with the SAME payload \u2014 so an operator who kills the class keeps the measurement. `wouldFlipStep` was the graduation evidence and remains the incidence metric \u2014 it is true only where the exclusion actually moves the step status, so a fabricated PASSING grade stays pure telemetry in either state. THREE DELIBERATE NARROWINGS keep this rescue-polarity gate from opening a false-PASS channel: (1) it fires only on a step that AUTHORED \u22651 criterion \u2014 grades filed under a criteria-less action/setup step are the cross-step MISFILE class, where today's fail-closed treatment of the unbound grade is the only adjudication that failure gets; and (2) only when EVERY authored criterion of that entry received a grade, so an unbound grade can never be excluded while an authored slot went unadjudicated (it would then plausibly BE that slot's grade under a heavy rephrase). NARROWING (2b) \u2014 TEXT-JUSTIFIED COVERAGE, added 2026-08-03 after adversarial verification of PR #2153 found a LAUNDERING vector: (2) counted a slot as graded no matter HOW it was covered, and `bindGradesToPlanSlots` PASS 2 assigns leftover slots in GRADE ORDER, so a FABRICATED grade emitted BEFORE a rephrased real one takes that real criterion's slot, coverage reads complete, and the grade left unbound (and excluded) is the REAL one. Reproduced by execution on head 2dc7691f4: criteria [Description strict, Unit strict] + grades [fabricated \"Order line 2 Quantity looks plausible\" PASS, Description exact PASS, Unit REPHRASED FAIL observed \"kg\"] booked the run `passed` at the graduated default versus `failed` at `=0`, on 7 of 8 realistic rephrase forms (reworded, units added, whitespace, case, smart quotes, DE/EN, truncated; only the platform's own \" (warning only)\" suffix still bound by text), with the diag itself reporting wouldFlipStep:true. The replay gate could NOT see it \u2014 the exclusion consumes exactly one unit of surplus, so (C1) 2<=2, (C2) overreach 0 and (B) were all green and buildReport returned ok:true on a laundered run. `evaluateCriteriaContainmentEligibility` therefore refuses exclusion whenever ANY authored slot was covered by PASS 2's positional residual rather than a PASS 1 `matchedByText` binding: a positional binding is ordinal-only evidence (the C1 strict-honoring gate already refuses to trust it for the strict flag, and containment is strictly stronger since it DELETES the grade). The refusal is entry-wide rather than per-slot \u2014 an honest reorder is indistinguishable from slot theft without reading grade text \u2014 so both keep the fail-closed treatment, and the engine logs `criteria_containment:refused` {stepIndex, reason:'positional-only-coverage', criterionText, strict, observed, wouldFlipStep, positionalSlots, authoredCriteria, gradedCriteria} in BOTH flag states, where wouldFlipStep:true means laundering was PREVENTED. ACCEPTED COST, pinned by test: a genuine fabrication sitting beside an honest positional binding is no longer contained \u2014 a false FAIL, never a false PASS. Both frozen incident runs bind BOTH authored slots by exact text, so the graduation class is untouched (asserted end-to-end through handleToolCall and through the replay gate's own buildReport: `=0` -> ok:false with plan_verdict_regression x2 + criteria_over_grading x2; default -> ok:true, engineContainedResults 2, containmentOverreachSteps 0). Identification reuses the ONE authoritative binder \u2014 `bindGradesToPlanSlots` per-entry output (text tiers incl. the platform's \" (warning only)\" rewrite, then residual positional) \u2014 never a new text comparison: measured over 76 persisted corpus runs, 58 of 60 text-unbound results were exactly that legal rewrite, so a text-equality containment rule would be ~97% false positives. A THIRD narrowing closes the DUPLICATE-READ false-PASS channel (review round 2026-07-29): the binder claims slots EXCLUSIVELY, so a SECOND grade of the SAME authored criterion binds to nothing and unguarded containment would discard it \u2014 and when the two reads contradict ([passed:true, then passed:false observed \"\u20AC35.00\"] for one authored total) the discarded one is the FAILING one, passing the step on a record whose \"unauthored\" criterionText is byte-identical to the authored check. Before excluding, `findDuplicateReadCriterionIdx` re-runs the binder's own text tiers over ALL authored criteria with the claimed set IGNORED; a match means duplicate READ, containment REFUSES to exclude, the grade keeps its flag-OFF effect (the step can still fail \u2014 the correct polarity for a contradicting observation), and the engine logs `criteria_containment:duplicate_read` {stepIndex, criterionIndex, criterionText, passed, wouldHaveExcluded} in BOTH flag states. RESIDUAL: identification is a TEXT relation, so a REPHRASED contradicting second read (\"Der Gesamtbetrag lautet \u2026\") matches no tier and is still excluded under the flag \u2014 the shadow soak's would_exclude population must be reviewed for that shape before graduation. That residual is the DUPLICATE shape (one authored criterion graded TWICE, the paraphrased second read dropped) and is DISTINCT from the positional-slot-theft class narrowing (2b) closes, where the authored criterion is graded exactly ONCE and that single grade is discarded. GATE HALF (detector, no verdict effect): `criteria_over_grading` in e2e/scripts/verdict-replay-gate.mjs, PR #1934, split in two at this graduation because the gate reads a PERSISTED array in which the excluded result is still present. (C1) counts only the results that still DECIDE \u2014 `criteriaResults.filter((cr) => cr.unauthored !== true).length <= step.criteria.length` \u2014 so a contained fabrication is no longer a violation while an UNMARKED surplus is the pre-graduation violation verbatim (identical on every legacy run and under `=0`, where nothing is marked). (C2) `criteria_containment_overreach` polices the OPPOSITE direction: the engine may exclude at most `persisted - authored`, so marking more than the surplus \u2014 containment removing a criterion the plan really authored \u2014 is a hard gate failure. The gate deliberately CONSUMES this mark rather than re-deriving a binding of its own: a second, independently-drifting binder in the gate is exactly how a runtime fix and its gate come to disagree, and it is (C2) that makes trusting the mark safe, since the mark is only ever believed up to a cardinality the gate verifies for itself. Equivalence is pinned by e2e/scripts/criteria-containment-gate-equivalence.test.ts (the gate's counted set === the set `deriveStepStatusFromCriteria` reduces over, asserted on the two frozen prod-shaped runs). NO RESCUE \u2014 THE ENGINE BOUNCES INSTEAD (2026-08-04). The filter alone is STRUCTURALLY INERT on the production shape: `deriveStepStatusIgnoringAuthorship` returns a reported `failed` VERBATIM before it looks at any criterion, so when the model stamps the step `failed` BECAUSE of the criterion it invented, `derive(failed, all) === derive(failed, contained) === failed` and the exclusion moves nothing. Live proof \u2014 staging replicate run_d5b6f130-734f-4f57-8067-d43aa617c7ae, plan tp_vrgate_offer_upload_retry step 13: 2 authored criteria both PASS with grounding, plus a third graded `Order line 2 Quantity is 4.2` (strict, observed 1, NO groundingObservation) which is step 12's line-1 quantity copied across lines; the target is a STATIC self-testing fixture (apps/self-testing/app/lio-mirror/offer-upload-retry/page.tsx hardcodes Quantity 1) and the sibling replicate run_eccf41c3-9e09-4493-9e25-d6ae44b93c60 graded all 14 steps identically and PASSED, so there is zero app non-determinism. THREE SUCCESSIVE DESIGNS TRIED TO RESCUE THAT VERDICT and adversarial verification defeated each: (1) co-occurrence read as attribution \u2014 a failing unauthored grade merely CO-EXISTING with the step's failure rescued it, laundering 8/8 genuine non-criterion failures (timeout, error banner, HTTP 500, thrown exception, blocked navigation, disabled control, credential rejection, unobservable browser); (2) an ALLOWLIST of recognized genuine-failure signals \u2014 everything unenumerated was invisible by construction; (3) a positive execution attestation PLUS an attribution requirement \u2014 which fell too, because the attribution absorbed the excluded grade's own note/observed while BOTH that note and the step note are written by the same model in the same run_complete call, so one coherent narration made its own cause 'accounted for' (proven end-to-end: a genuine HTTP 500 with a fabricated grade whose note echoed the 500 booked passed/passed, and one coherent narration made all 8 laundering fixtures attributable). The lesson is structural: EVIDENCE THE MODEL AUTHORED CANNOT VALIDATE THE MODEL'S OWN REPORT, and every rescue reduced to trusting some model-written field. So the engine no longer rewrites a verdict at all. Instead `run_complete:unauthored_criterion_grades_rejected` BOUNCES the malformed call ONCE \u2014 naming the offending step indexes and the exact fabricated criterion texts, and instructing the model to resubmit grading ONLY the criteria the plan authored \u2014 mirroring the shipped `run_complete:incomplete_step_results_rejected` bounce (#2207). Bounded to one rejection per run (`_unauthoredGradeRunCompleteRejections`, reset in startRun beside its sibling counter) and placed immediately after the grade map, BEFORE every cascade adjudicator, so a bounce discards no gate's work. On the resubmission \u2014 or if the model repeats the fabrication and is not bounced again (`run_complete:unauthored_criterion_grades_repeated`) \u2014 the unauthored grades stay excluded from the derivation and marked, and WHATEVER THE MODEL BOOKS STANDS: a `failed` is never turned into a `passed`, at the step level or the run level. The bounce fires on ANY unauthored-and-unrelated grade regardless of its `passed` value, deliberately: whether a fabrication 'matters' is itself a model-written field, and reading one to decide is the exact error the three rescues each made in a different disguise. FORGERY GUARD (kept from the rework, now used to avoid a WRONG bounce): `excludedGradeRelatesToAuthoredCriterion` relates an excluded grade to an authored criterion over four deterministic tiers \u2014 the binder's own tiers (`findDuplicateReadCriterionIdx`), COSMETIC equality (`canonicalizeCheckTextForRelatedness`: NFKC, NBSP/space family, curly/angle/prime quotes, dash family, case, terminal punctuation, currency marks, locale number separators so 1.234,50 equals 1234.50), TRUNCATION (prefix either direction, floored at 12 canonical chars), and SUBJECT-TERM identity (`significantSubjectTokens`, shared with the note-contradiction lane). A related grade is a REPHRASED second read of a criterion the plan really does define \u2014 `findDuplicateReadCriterionIdx`'s documented residual \u2014 so the model is NOT asked to drop it, and it keeps its flag-OFF effect of failing the step. Reported on the containment diag as `relatesToAuthoredCriterion`. DIAGNOSTIC SEAM (verdict-neutral, kept on its own merits): `BaseRuntime.executeAction` calls `onBrowserActionError`, whose base implementation is an empty method RunnerRuntime never overrode, so an errored click_at / type_text_at / navigate left NO step-attributable trace anywhere in the codebase. `_stepActionErrors` now records both error shapes \u2014 the generic `status:'error'` via the overridden hook and the informative `status:'ok'` carrying `error`/`valueMismatch:true` at the dispatch seam \u2014 bounded per step, emitted as `step_action_error` and surfaced as a per-step count on the containment diag. NOTHING READS IT TO DECIDE A STATUS. GATE HALF (C3) `criteria_containment_inert`: C1 stops counting a surplus result the moment it is marked, so a MARKED-but-still-deciding fabrication went silent \u2014 gate run 30889770637 printed 'steps that graded more criteria than authored: 0' beside engineContainedResults: 1 on a run that booked failed on a criterion nobody wrote, and that false zero was read as proof the class was fixed. C3 reds when some excluded result FAILED, every counted result PASSED, and the step still books `failed`; `strictInvariant.containmentInertSteps` is printed on the same summary line as the over-graded count so the two can never be read apart again. C3 is now the whole visibility mechanism rather than a backstop to a rescue, and it fires on the incident shape by design.",designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-07-29",notes:"GRADUATED 2026-08-03 (default ON), ahead of the 2026-08-12 review date, because the containment class is NOT a hypothetical: it is red on release PR #2149 AND on the `main` nightly (GH run 30781168843, build 0555cc09a = the build prod is serving), so the pre-graduation default is what is booking false `failed` verdicts on prod TODAY. killSwitch resolves the no-env value from this defaultState (#1729), so graduation = defaultState 'on' AND CRITERIA_CONTAINMENT listed in INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire); an explicit AGENTIQA_CRITERIA_CONTAINMENT=0 (or the AGENTIQA_EXPERIMENT_ spelling) restores the byte-identical pre-graduation SHADOW \u2014 detection still runs, `criteria_containment:would_exclude` still fires, and NO verdict moves. STAGING-FIRST: merging to staging makes this default-ON on the STAGING engine only; PROD stays OFF until a later staging->main release carries it (a natural staged soak). Rides on the binder flags: with WARNING_CRITERION_BIND=0 or CRITERION_BIND_RESIDUAL=0 the binder is the sequential one, which leaves grades unbound in shapes that are NOT surplus \u2014 narrowing (2) is what keeps containment inert there rather than excluding a legitimately-graded criterion. Runner lane only (RunnerRuntime run_complete). Scope is step-status derivation: the rescue-eligibility gates that read `criteriaResults.every(passed)` (absence oracle, VERIFY_REOBSERVE_WITHHOLD, verify-conflict reconcile) still see the full persisted array, so a fabricated failing result can still BLOCK a rescue \u2014 the fail-closed direction, left deliberately. OPEN RESIDUAL CARRIED THROUGH THE FLIP, stated rather than hidden: the REPHRASED-DUPLICATE shape (a paraphrased second read of an authored criterion) matches no text tier, is therefore indistinguishable from a fabrication, and IS excluded at the default \u2014 a false-PASS channel the duplicate-read guard does not close and which the new (C2) gate check does NOT catch either (a rephrased duplicate consumes exactly one unit of surplus, so the exclusion is within budget). The pre-flip gate asked for a grade-by-grade shadow-soak review of that population; no such soak was run before this flip, which is the one piece of the original graduation bar that is being WAIVED rather than met, and it is why this flip is Alex's call and not a mechanical one.",graduation:{status:"gated",gate:'GRADUATED 2026-08-03 (default ON) with ONE part of this bar explicitly waived \u2014 kept as `gated` rather than deleted because the waived part is a real open false-PASS channel that still needs closing. MET: (1) the fabrication class reproduces on the canary at the documented location \u2014 tp_vrgate_offer_upload_retry step 13 authors exactly 2 criteria and the model graded 3, frozen from TWO real staging runs (run_11eeceb1-3bfc-4810-a17f-e7c23170c12e, run_81ef70ae-a3f9-4673-ba52-95378ca477d3, both 2026-08-03, both booking `failed` against a `passed` baseline on the invented `Order line 2 Quantity is "2"`, strict:true, passed:false, observed "1") into packages/engine-core/src/__tests__/fixtures/criteriaContainment.overGradedRuns.json; (2) the #1934 `criteria_over_grading` detector agrees on exactly those steps \u2014 it is what turned the gate red \u2014 and its 76-run corpus measurement recorded 2 true hits / 0 false positives, both this same defect; (3) the detector now also polices the exclusion in the opposite direction (`criteria_containment_overreach`), so over-exclusion beyond the surplus is a permanent CI check rather than a one-off review. WAIVED: the grade-by-grade shadow-soak review of the `would_exclude` population for the REPHRASED-DUPLICATE shape (a paraphrased second read of an authored criterion, which the text tiers cannot distinguish from a fabrication and which the duplicate-read guard does NOT catch). No soak was run; the flip was driven by the class being live on prod and blocking a release. That residual is the remaining work behind this entry.',evidence:"engine `criteria_containment:excluded` / `:would_exclude` / `:duplicate_read` diag events + the RunnerRuntime.criteriaContainment unit suite incl. the two frozen prod-shaped runs (claim verify.containment.authored-criteria-only) + the replay-gate `criteria_over_grading` / `criteria_containment_overreach` checks + e2e/scripts/criteria-containment-gate-equivalence.test.ts (runtime filter === gate filter)",owner:"steering (Alex)",review:"2026-08-12"}},PLAN_OBEDIENCE:{key:"PLAN_OBEDIENCE",envVars:["AGENTIQA_PLAN_OBEDIENCE","AGENTIQA_EXPERIMENT_PLAN_OBEDIENCE"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Enforces the active plan step: rejects off-plan label clicks, records deviations, and lets run_complete fail on them; off makes the pre-check, deviation recording, and verdict gate all inert (pre-#1294 behavior).",designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-07-07"},LOOP_VISION_ESCALATION:{key:"LOOP_VISION_ESCALATION",envVars:["AGENTIQA_LOOP_VISION_ESCALATION","AGENTIQA_EXPERIMENT_LOOP_VISION_ESCALATION"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Master gate (Runner/Explorer lanes) for consulting the vision supervisor once before an ambiguous screenshot-blind force-block terminates the run; off keeps the deterministic hard-block.",designDoc:"docs/plans/2026-07-07-loop-detection-vision-supervisor-design.md",status:"active",added:"2026-07-07"},LOOP_VISION_DIFFERENTIAL:{key:"LOOP_VISION_DIFFERENTIAL",envVars:["AGENTIQA_LOOP_VISION_DIFFERENTIAL","AGENTIQA_EXPERIMENT_LOOP_VISION_DIFFERENTIAL"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Phase-2 differential-grant refinement of loop-vision escalation: grants 2..K need a concrete task-unit delta and the per-step ceiling rises to 12; off reverts to Phase-1 (absolute judgment, 4-grant ceiling).",designDoc:"docs/plans/2026-07-09-loop-vision-differential-extension-design.md",status:"active",added:"2026-07-09"},CANVAS_PIXEL_PROGRESS:{key:"CANVAS_PIXEL_PROGRESS",envVars:["AGENTIQA_CANVAS_PIXEL_PROGRESS","AGENTIQA_EXPERIMENT_CANVAS_PIXEL_PROGRESS"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Canvas-aware loop progress: on a canvas-dominant screen (a <canvas> covers >=40% of the viewport) the LoopDetector novel_screen milestone is driven by a coarse screenshot pixel-diff hash (own NOVEL_SCREEN_BUDGET) instead of the static DOM/a11y hash, and the loop-vision differential delta gate accepts a qualitative delta_evidence string when countable task units are unavailable. Off drops both so a canvas build behaves exactly as before (structural breaker climbs on the static DOM hash, delta gate stays countable-only).",designDoc:"docs/plans/2026-07-14-canvas-chat-failure-class-design.md",status:"active",added:"2026-07-14"},LOOP_URL_NOVELTY_REARM:{key:"LOOP_URL_NOVELTY_REARM",envVars:["AGENTIQA_LOOP_URL_NOVELTY_REARM","AGENTIQA_EXPERIMENT_LOOP_URL_NOVELTY_REARM"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Re-arms the structural loop breaker in the chat/explorer (assistant_v2) lane. URL-ONLY (narrow-safe): only the novel-URL seen-set, its NOVEL_URL_BUDGET=40/turn budget, and actionsSinceProgress become TURN-scoped (resetForNewStep no longer wipes them), so a recycled URL revisited across many declared steps stops re-counting as novel and actionsSinceProgress climbs to the 30-action structural threshold. The novel-REF and novel-SCREEN budgets stay PER-DECLARED-STEP in both states \u2014 turn-scoping them was reverted because it starved a legit long single-stable-URL SPA turn producing genuinely-new screen content each action (force-block ~action 53). A genuinely-new URL each step still resets (multi-page wizards unaffected); a wander that also mints novel screens escapes this deterministic re-arm and the run backstop is the net. Off restores the per-step reset (recycled URLs re-count as novel forever, unbudgeted novel_url) \u2014 today's prod behavior. Runner (test_run) lane never opts in.",designDoc:"docs/plans/2026-07-20-loop-safety-rearm-and-backstop-design.md",status:"active",added:"2026-07-20"},RUN_PROGRESS_BACKSTOP:{key:"RUN_PROGRESS_BACKSTOP",envVars:["AGENTIQA_RUN_PROGRESS_BACKSTOP","AGENTIQA_EXPERIMENT_RUN_PROGRESS_BACKSTOP"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'Judge-gated run backstop in the chat/explorer (assistant_v2) runLoop: a wall-clock (12 min, primary), iteration (200), and cumulative prompt+completion billed-token (6M) ceiling \u2014 all well above p99 legit chat turns and below the 300-iteration explorer child cap. At a ceiling breach the loop-vision progress judge (the same fail-closed SupervisorService judge the loop breaker uses, temp-0 / thinkingBudget-0) is consulted ONCE with a goal-anchored progress question, rather than blind-terminating: "progressing" grants a BOUNDED extension (each ceiling raised by one window) up to a hard cap of RUN_BACKSTOP_MAX_EXTENSIONS=2 (worst case ~36 min), after which the run terminates regardless of the judge; "wandering" terminates with a judge-confirmed "not getting closer to the objective" message; any no-signal case (no judge wired in this lane, judge error / timeout / unparseable) fails CLOSED to a terminate with a neutral "hit the safety limit" message. Ends with blockKind=backstop / endKind=run_backstop, NOT loop_block, so it is not counted toward the session structural-loop cap. No interactive ask and no cross-turn state (the escalate\u2192ask_user ladder was removed; the judge consult is synchronous and re-derived per breach). Off removes all three ceilings (only bound remains iteration<=maxIterations=300, ~2.5h). Never a crash \u2014 the emit path fails open while the judge fails closed. Runner (test_run) lane never opts in.',designDoc:"docs/plans/2026-07-20-loop-safety-rearm-and-backstop-design.md",status:"active",added:"2026-07-20"},CLICK_AT_INTERACTIVE_DESCEND:{key:"CLICK_AT_INTERACTIVE_DESCEND",envVars:["AGENTIQA_CLICK_AT_INTERACTIVE_DESCEND","AGENTIQA_EXPERIMENT_CLICK_AT_INTERACTIVE_DESCEND"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"A coordinate click_at/double_click_at whose point resolves to a NON-interactive element descends to an interactive descendant within 16px and clicks it via locator, plus a no-effect advisory on a zero-mutation same-URL click; off restores the raw mouse.click(x,y) with no advisory.",designDoc:"docs/plans/2026-07-11-click-at-interactive-descend-design.md",status:"active",added:"2026-07-11"},LABEL_CLICK_TWIN_COLLAPSE:{key:"LABEL_CLICK_TWIN_COLLAPSE",envVars:["AGENTIQA_LABEL_CLICK_TWIN_COLLAPSE","AGENTIQA_EXPERIMENT_LABEL_CLICK_TWIN_COLLAPSE"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'BOTH twin-collapse kinds in `resolveClickableByLabel`, plus the actionable-click stage they feed (`pickActionableTwin` / `probeClickTopmost`). (1) Same-destination: a click_at/double_click_at label ties at the best rank across <= 3 elements that all resolve to ONE absolute http(s) href which is not the current page, opened in the same browsing context (equal `target`), each inside a landmark, whose OUTERMOST landmarks span >= 2 distinct region kinds including a header or a footer \u2014 the header-nav / footer-nav twin, e.g. "Security" on the pricing page. (2) Containment: the tied candidates nest (`<a href><button>Log in</button></a>`), so the innermost one is clicked. Either way the collapsed control is clicked only after it is proven topmost where it will be clicked; if nothing can be, nothing is clicked. Off restores the pre-fix behavior \u2014 any rank tie returns `ambiguous` and nothing is clicked. It does NOT gate the diagnostics on the no-click paths: the enriched ambiguity note (landmark / visibility / href hints) and the already-on-this-page note for twins that link to the current URL.',designDoc:"packages/engine-core/src/BasePlaywrightService.ts",status:"active",added:"2026-08-02",notes:'Root-cause fix for the #2024 blocked-loop class: the ambiguous no-op pushed the agent onto snapshot refs, which `_snapshotForAI` (refs: "interactable") never mints for an auto-hidden `fixed` header \u2014 so both recovery paths were dead and LoopDetector force-blocked on screen_cycling.'},LOOP_BLOCK_ATTRIBUTION:{key:"LOOP_BLOCK_ATTRIBUTION",envVars:["AGENTIQA_LOOP_BLOCK_ATTRIBUTION","AGENTIQA_EXPERIMENT_LOOP_BLOCK_ATTRIBUTION"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:`Evidence-gated loop-block finding attribution: when a force-block's dominant repeated click target is a non-interactive element or a no-op self-anchor, suppress the false "Page appears stuck" auto-issue instead of filing it. Off restores the old unconditional filing (subject only to the AG-6107/AG-6490 gates).`,designDoc:"docs/plans/2026-07-11-loop-block-finding-attribution-design.md",status:"active",added:"2026-07-11"},CLICK_AFFORDANCE_CAPTURE:{key:"CLICK_AFFORDANCE_CAPTURE",envVars:["AGENTIQA_CLICK_AFFORDANCE_CAPTURE","AGENTIQA_EXPERIMENT_CLICK_AFFORDANCE_CAPTURE"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Phase-2 of loop-block attribution: captures click-handler affordance evidence onto clickTarget (page-context inline/React/Vue signals + a CDP addEventListener probe on the rare non-interactive branch) so a dominant non-interactive target with a real-but-dead handler files a 'Custom control appears unresponsive' issue instead of being suppressed agent-side. Off \u21D2 no affordance field emitted \u21D2 the custom-control classifier branch can never fire (falls back to the #1477 status quo); it does NOT re-enable filing on a bare non-interactive target.",designDoc:"docs/plans/2026-07-11-custom-control-unresponsive-detection-design.md",status:"active",added:"2026-07-11"},CLICK_EFFECT_SIGNAL:{key:"CLICK_EFFECT_SIGNAL",envVars:["AGENTIQA_CLICK_EFFECT_SIGNAL","AGENTIQA_EXPERIMENT_CLICK_EFFECT_SIGNAL"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:`Post-click effect signal on EVERY click path (coordinate, retargeted, ref and label \u2014 click_at and double_click_at), SHADOW-FIRST. After the click the engine waits up to a 500ms settle window for the DomObserver to record any DOM/text/attribute mutation (returning the instant one lands), then compares the URL and the auto-accepted-dialog tally. Any movement \u21D2 an effect was observed. No movement \u21D2 the click STILL reports SUCCESS (a click that legitimately changes nothing must never become an error) and the feature's only output is agent STEER. THREE STATES, not two: no env (default) = SHADOW \u2014 detection runs and emits the click_effect:would_signal diag, but no advisory, no effectObserved metadata and no prompt-byte change reach the model; =1 = ENFORCE \u2014 the hedged re-observe advisory rides the successful tool result, effectObserved lands on the ToolCallResult side channel, the wait_for_element "Do NOT retry" coaching softens to its effect-aware wording and the runner prompt gains the re-observe-after-corrective-action line, with a click_effect:signaled diag; =0 = FULLY OFF \u2014 not even the probe runs, so there is zero added latency and click results plus every prompt byte are identical to pre-change behavior. Targets the 2026-07-29 vrgate false-FAIL (run_b72f5099-cee2-4063-b109-1961c55c9898), where a click_at on a rendered React button ~1.3s after a Next.js client navigation reported success while the onClick never fired (painted but not yet hydrated), the agent trusted the success, and the plan FAILED for a defect that did not exist. EXECUTOR-SIDE ONLY: it changes no verdict path, writes no step/criterion/run status, and adds no gate \u2014 it changes what the AGENT is told, and the agent still grades. Fail-safe by construction: an in-page probe that cannot run (CSP, cross-origin frame, document destroyed mid-navigation) yields NO verdict rather than a false "no effect"; navigation/dialog evidence is evaluated BEFORE the mutation counters so the one case where the probe reliably dies is the case the URL alone already proves; canvas-dominant surfaces and native property-only controls (checkbox/radio/select/text input \u2014 where a spurious "re-perform once" would TOGGLE the control back) are advisory-suppressed; and the pre-existing #1476 dead-coordinate peek keeps its exact timing and semantics. Canvas surfaces additionally skip the PROBE (not just the advisory) once a capture has classified the page as canvas-dominant, so a whiteboard/design flow never pays the settle window per click for a verdict that is suppressed on arrival; those clicks are counted as click_effect:probe_skipped. A SECOND probe skip covers the unobservable case: in SHADOW, where the diag is the feature's only output, a platform with no BasePlaywrightService.diagLog wired skips the probe entirely rather than pay the settle window for a measurement nothing can read (ENFORCE always probes \u2014 its output is agent-visible behavior, not telemetry).`,designDoc:"packages/engine-core/src/clickEffectSignal.ts",status:"active",added:"2026-07-29",notes:"Default OFF (shadow). Detection runs in shadow always unless explicitly =0 \u2014 the PIN_PAGE_GROUNDING / INTERACTION_CLEARS_PRESENCE_ORACLE shadow-first precedent \u2014 because a shadow that skipped the settle wait would measure a DIFFERENT detector than the one enforcement ships, and its numbers would not predict the ON behavior. The one deliberate non-identity in shadow is therefore TIMING, not tool-result bytes: a click that has mutated nothing yet pays up to 500ms of settle before the state capture that follows it (an effective click returns on the first in-page read and pays one evaluate round-trip). Stated plainly because it is a real, if small, behavior delta in the DEFAULT state \u2014 the post-click screenshot/snapshot of a no-effect click is taken up to 500ms later than before, which is more settled, not less faithful. AGENTIQA_CLICK_EFFECT_SIGNAL=0 removes even that and restores exact timing parity. killSwitch resolves the no-env value from this defaultState (#1729), so GRADUATING = flip defaultState to 'on' AND add CLICK_EFFECT_SIGNAL to INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire). Read sites: packages/engine-core/src/BasePlaywrightService.ts (probeClickEffect / resolveClickEffect on clickAt, clickByRef, clickByLabel), packages/engine-core/src/waitToolCoaching.ts (waitNotFoundError picks the coaching variant), packages/engine-core/src/tools/browserTools.ts (getFailureHandlingPrompt), packages/engine-core/src/RunnerRuntime.ts (buildRunnerPrompt re-observe line); pure decision logic in packages/engine-core/src/clickEffectSignal.ts. Acceptance tests: clickEffectSignal.test.ts (pure verdict matrix incl. every suppression and the unknown-probe degrade), BasePlaywrightService.clickEffectSignal.test.ts (per-path wiring + shadow/enforce/off + the canvas-memo skip + degraded-note preservation on the ref path), clickEffectPromptParity.test.ts (flag-OFF prompt BYTE-parity across all four prompt surfaces). The 500ms settle cost is bounded on canvas-dominant surfaces by the SessionState.lastCaptureCanvasDominant memo: captureState records canvasDominant on every capture and probeClickEffect returns null (emitting click_effect:probe_skipped, reason canvas-memo) while it is set, because the canvas verdict was suppressed anyway. It is bounded a second way by the OBSERVER check: in shadow, where the diag is the only output, probeClickEffect returns null before the settle window when this.diagLog is unwired \u2014 an unobservable measurement is pure latency. Enforce is exempt (its verdict is agent-visible behavior). Acceptance: the shadow/enforce no-observer pair in BasePlaywrightService.clickEffectSignal.test.ts.",graduation:{status:"gated",gate:'SOAK SOURCE \u2014 read this first. click_effect:* rides BasePlaywrightService.diagLog, which was wired ONLY in DesktopPlaywrightService until the CloudPlaywrightService sink resolver landed (2026-07-30), so before that commit desktop runs were the ONLY source and a census of 0 from a cloud/staging replay lane is NON-EVIDENCE (the events were structurally unemittable there, not absent). Any soak reading must therefore be taken from engine builds that carry the cloud diagLog wiring; older cloud replays cannot be counted, and in shadow an unwired lane now skips the probe outright so it contributes no fires by construction. Staging shadow soak on click_effect:would_signal establishes (a) the no-effect rate per click path, (b) that the fires are dominated by genuinely ineffective clicks rather than by the known effect-free-but-legitimate classes, and (c) that no page class fires it continuously. FOUR NAMED REQUIREMENTS, all of which must be answered before the flag moves past shadow. (1) BLOCKER \u2014 THE NON-IDEMPOTENT SUBMIT. `<button>` and `<a>` are deliberately NOT in the property-only suppression set, so a form submit whose only feedback is a server round-trip (no spinner, no optimistic DOM write) reads as no-effect and, under enforce, receives the "re-perform the action ONCE" advisory. That directly contradicts the failure-handling prompt already shipped in browserTools.getFailureHandlingPrompt, which names "status: ok with url unchanged and no visible DOM change" as a SIGN OF AN IN-FLIGHT WRITE and instructs "do NOT re-click ... Re-clicking the same button while a write is in flight is a no-op for the user and burns retry budget" \u2014 and on a non-idempotent endpoint a second submit is not merely wasted, it can double-charge, double-book or double-post. The soak must show ZERO enforce-mode advisories on non-idempotent submits (classify fires by targetTag/targetRole plus the accessible name against a submit-shaped vocabulary, and cross-check each against pendingRequests at the moment of the fire), OR enforce must first gain a submit-shaped suppression (e.g. withhold the advisory whenever a same-origin request was in flight at probe time, or whenever the target is a submit-shaped control) \u2014 either outcome, and NEITHER may be waived. (2) IFRAME BLIND SPOT. Every counter read (peek / waitForMutation / flush) goes through page.evaluate, which runs in the MAIN frame only, and a top-document MutationObserver does not cross an iframe boundary \u2014 so a click whose whole effect renders inside an embedded frame (payment element, third-party booking/chat widget, embedded editor preview) reads as no-effect however well it worked. There is no target shape to suppress on, so iframe-hosted effects are a KNOWN-LEGITIMATE fire class the soak must be able to account for and subtract, exactly like property-only controls, downloads, clipboard and focus-only clicks; it is enumerated in the clickEffectSignal.ts header for the same reason. (3) SHADOW IS NOT INERT \u2014 READ THE SOAK ACCORDINGLY. The probe delays the post-click captureState by up to the full 500ms settle window on any click that has mutated nothing yet, INCLUDING in the default shadow state. Captured screenshots/snapshots on those clicks are therefore of a MORE SETTLED page than pre-change, so a shadow-vs-baseline comparison that shows different captured content on no-effect clicks is expected and is not evidence of a detector defect; only the =0 state is timing-identical to pre-change. (4) HYDRATION EMPIRICAL CRITERION. The soak must answer "what would run_b72f5099-cee2-4063-b109-1961c55c9898 have produced?" \u2014 i.e. for clicks landing inside a post-navigation hydration window, what fraction show mutationCount > 0 or attrCount > 0 (some other script mutated the page, so the detector stays SILENT and would not have rescued the incident) versus both counters at 0 (the detector fires and the advisory would have reached the agent). Both counters are already in the click_effect payload, so the query is: filter click_effect:would_signal to fires within ~2s of a navigation, then bucket on (mutationCount > 0 || attrCount > 0). A silent-dominated result means this feature does not fix its own motivating incident and enforcement is not justified on that basis. Enforcement additionally needs an inversion showing the advisory does not induce a harmful second click on a toggle.',evidence:"staging click_effect:would_signal diag events FROM AN ENGINE BUILD THAT CARRIES THE CLOUD diagLog WIRING (rate + clickPath/suppressReason/targetTag/targetRole breakdown, plus the mutationCount/attrCount split inside the post-navigation hydration window and the submit-shaped-target cross-check against in-flight same-origin requests) + click_effect:probe_skipped counts for the canvas-memo skips + the engine-core clickEffectSignal / BasePlaywrightService.clickEffectSignal / clickEffectPromptParity unit suites + apps/execution-engine/__tests__/CloudPlaywrightService.diagLog.test.ts for the cloud emit path itself",owner:"steering (Alex)",review:"2026-08-08"}},HALT_CONCLUDING_TURN:{key:"HALT_CONCLUDING_TURN",envVars:["AGENTIQA_HALT_CONCLUDING_TURN","AGENTIQA_EXPERIMENT_HALT_CONCLUDING_TURN"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"ONE final, tool-restricted turn offered to the runner when the MACHINERY (loop detector / vision supervisor / run backstop) halts a saved-plan run that still has status=running AND the deterministic unresponsive-control window holds \u2014 i.e. exactly the runs that end today with terminationReason=supervisor_halted and NO run_complete, hence no verdict at all, narrowed to the halt shape the change was measured against. SCOPE GATE: the turn is offered only when hasUnresponsiveControlEvidence() is true (a dominant repeated click target, noObservedEffect on EVERY one of those clicks, a non-canvas surface, no committed 2xx/3xx write \u2014 a model-free predicate that consults no LLM output). Every OTHER machinery halt keeps its current behavior: no extra turn, no extra LLM call, status=blocked + supervisor_halted. The gate is checked BEFORE the single-use latch, so a halt without that evidence does not consume the run's one attempt, and a `concluding_turn:not_offered_no_evidence` diag records each suppression so the gate's live fire rate is measurable. The turn is offered run_complete ONLY (the _editOnly idiom, RUNNER_TOOL_SET_CONCLUDE_ONLY), so it cannot act on the browser, cannot navigate, and cannot extend the loop: it is a single generateText call with no iteration, and it is offered at most once per run (_concludingTurnUsed). Its directive is NEUTRAL \u2014 it names no finding and no failure shape, asks for each step to be graded only on observed evidence, and tells the model to mark an unsubstantiable step 'skipped' rather than assert a verdict; an earlier revision spelled the dead-control conclusion out, which asked the model to ratify what the gate already believed. The halted path also performs NO cross-run writes: maybeCaptureProjectProfile and maybeMigratePlanFromCapture are skipped while _concludingTurnActive is true, because both end at isRunAuthenticated \u2014 routinely TRUE on a dead-CTA halt, where the login worked and the CTA did not \u2014 and a halted run must not persist its browser state or rewrite the customer's saved plan. Motivation, measured 2026-08-04 \u2014 READ THE PROVENANCE BEFORE QUOTING THE NUMBER (corrected 2026-08-06). It is run r1 of an UNCOMMITTED model-reliability harness (e2e/benchmark/model-reliability/, never merged to staging): its own execution-engine, fixtures on the deployed self-testing app, engine = staging @ fb89690cf, all 46 verification gates forced off as AGENTIQA_EXPERIMENT_<FLAG>=0 (PLAN_OBEDIENCE and CLICK_EFFECT_SIGNAL among them) and LLM_CACHE=0 \u2014 NOT the runner eval lane, which was not run that day. In it the two DEAD-CTA fixtures (a button that is present, whose click reports success, and which does nothing) produced 12/12 runs ending supervisor_halted/loop_block with ZERO run_complete, ZERO report_issue and ZERO agent-declared blocks. Those 12 are google:gemini-3-flash-preview \xD75 per fixture plus google:gemini-3.5-flash \xD71 per fixture: the second arm is n=1 per fixture and there was no Anthropic arm. THE CLASS IS MODEL-DEPENDENT, so 12/12 is not a property of the defect \u2014 a rerun the same day on the same code and fixtures (r2, 2026-08-04T20:11Z) had gemini-3.6-flash booking `failed` on 9 of 10 dead-CTA runs; only r1 reached this text. The halt is not the agent refusing to grade; it is the machinery taking the turn away before a verdict can be booked. MEASUREMENT STATUS: the post-merge L3 on the real lane (runner/seeded-broken, 2026-08-06 @ bdbfa8e30, 12 branch + 6 control, incumbent model) fired this gate 0/12 \u2014 GRADUATE-NO. Every eligible halt was suppressed by the scope gate (concluding_turn:not_offered_no_evidence) because the fixture's semantic `button` classifies as app-defect / non-interactive-target rather than custom-control-unresponsive, and its click has a real DOM effect so no noObservedEffect is ever sampled. The mechanism is unreachable on the very corpus that motivated it; see docs/VERDICT-GATES.md and PR #2246. GUARD RAILS, all pinned by tests: (a) a status=passed booked from this turn is REJECTED and the run falls back to today's blocked terminal unchanged \u2014 the agent must never be able to declare success out of a halt, and the halt itself is evidence against the claim; (b) the turn is NOT offered when blockKind==='agent' (the agent already concluded via exploration_blocked \u2014 nothing to second-guess); (c) exactly one turn, one LLM call, no tool loop, so a runaway loop is extended by that bounded turn and nothing more; (d) anything unusable (no run_complete, a throw, a stop mid-flight) falls back to the unchanged blocked terminal; (e) the scope gate above \u2014 a machinery halt with no unresponsive-control evidence gets no turn at all, in any blockKind. WHAT OFF (=0) DOES AND DOES NOT COVER \u2014 read this before using it as a kill switch. OFF removes the TURN: no extra turn, no extra LLM call, no extra token spend, and no run can be moved from blocked to failed by this mechanism. OFF does NOT change the terminal VOCABULARY: the `control_unresponsive` reason written by the blocked-terminal branch in startRun is UNCONDITIONAL and not gated by this flag, because it comes from `hasUnresponsiveControlEvidence` \u2014 a deterministic, model-free predicate that consults no LLM output \u2014 and it exists to stop blaming the agent for a dead control in the customer's own app, which is a separate improvement from giving a halted run a verdict. So with the flag OFF a halted run still terminates `control_unresponsive` rather than `supervisor_halted` whenever that evidence holds; to suppress the vocabulary too, turn off its input (`AGENTIQA_CLICK_EFFECT_SIGNAL=0` captures no `noObservedEffect`, so the predicate returns false and the terminal falls back to `supervisor_halted`). docs/VERDICT-GATES.md states the same split.",designDoc:"docs/VERDICT-GATES.md",status:"active",added:"2026-08-04",notes:"Default ON. Polarity is the reason: this gate can only move a run from blocked (NO verdict) to failed (a verdict the model booked), never toward a pass \u2014 the passed arm is refused in code, so there is no path by which it manufactures a false pass, which is the asymmetry docs/VERDICT-GATES.md guards hardest. Its worst case is a false FAIL on a run that today produces no verdict at all, and 'blocked' was never a pass either. Billing is NEUTRAL, and the ORDER is what makes it so: `handleRunComplete` reads `_concludingTurnActive` and stamps the non-billable reason (control_unresponsive when the deterministic unresponsive-control window holds, else supervisor_halted) BEFORE the first persist \u2014 because that upsert IS the billing trigger. It PUTs the entity-sync route, which fires commitRunUsage on any terminal + billable row, and `committed_at` is written in exactly one place in apps/web-next and only ever to NOW(); there is no un-commit path, so a later discardRunUsage no-ops with `already_committed`. An earlier revision corrected the reason with a second upsert AFTER handleRunComplete had already persisted 'completed'; that shipped the money before the correction and was replaced by this single-mechanism ordering (PR #2246 review finding 1). The same ordering is what makes a throwing upsert safe: the surviving in-memory reason is already non-billable, so startRun's finally cannot finalize a charge (finding 2). Read site: packages/engine-core/src/RunnerRuntime.ts#attemptConcludingRunComplete (called from startRun, immediately before the blocked terminal write) plus the `_concludingTurnActive` branch in #handleRunComplete. The turn's own LLM call is metered as auxiliary spend via emitAuxiliaryLlmUsage on every exit path (booked, declined, passed-refused) \u2014 it is a full-trace prompt plus up to 8192 output tokens, so leaving it unmetered hid the feature's entire cost (findings 3+4). Scope, directive neutrality and blast radius were narrowed in review: the turn fires only on the deterministic unresponsive-control halt shape (so every other machinery halt is untouched), its prompt names no finding, and the halted path skips the two cross-run writers handleRunComplete otherwise performs. Acceptance tests: RunnerRuntime.concludingTurn.test.ts (offer conditions incl. the scope gate across every blockKind, the neutral directive, the no-cross-run-writes pins, the passed refusal, the single-turn bound, the restricted tool set, flag-off parity), RunnerRuntime.controlUnresponsiveTerminal.test.ts (the terminal vocabulary + the browser_unobservable-style regression pins), RunnerRuntime.concludingTurnBilling.test.ts (the first-persist reason and the throwing-upsert path), RunnerRuntime.concludingTurnMetering.test.ts (the aux emit on every exit) and RunStatusBanner.controlUnresponsive.test.ts (the renderer copy + the booked-verdict line). MEASUREMENT STATUS 2026-08-06: post-merge L3 on runner/seeded-broken @ bdbfa8e30 (12 branch + 6 control) gave rescue 0/12 \u2014 GRADUATE-NO, with every eligible halt suppressed by the scope gate; the concluding_turn:not_offered_no_evidence diag remains the source of the live fire rate and is the number to read before revisiting this default."},REVISION_CRITERIA_CARRYOVER:{key:"REVISION_CRITERIA_CARRYOVER",envVars:["AGENTIQA_REVISION_CRITERIA_CARRYOVER","AGENTIQA_EXPERIMENT_REVISION_CRITERIA_CARRYOVER"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Backstop that carries prior-draft criteria forward when a plan revision strips ALL criteria across ALL steps; off lets a total criteria-strip through.",designDoc:"packages/engine-core/src/revisionCriteriaCarryOver.ts",status:"active",added:"2026-07-07"},CREDENTIAL_GUARD:{key:"CREDENTIAL_GUARD",envVars:["AGENTIQA_CREDENTIAL_GUARD","AGENTIQA_EXPERIMENT_CREDENTIAL_GUARD"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Credential-fabrication guard: injects the login-prohibition prompt wording and fires the false-premise report_issue gate; off omits both (pre-feature behavior).",designDoc:"packages/engine-core/src/testingEmailPolicy.ts",status:"active",added:"2026-07-06"},CREDENTIAL_BINDING:{key:"CREDENTIAL_BINDING",envVars:["AGENTIQA_CREDENTIAL_BINDING","AGENTIQA_EXPERIMENT_CREDENTIAL_BINDING"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Runner deterministic credential-step binding (v1.1): when a plan step unambiguously references exactly one non-generic stored credential AND the model's own type_project_credential_at pick was a generic field word, the mismatched credentialName is overridden to the referenced one (credential_binding_override diag) \u2014 a non-generic pick is never rewritten; and when a credential fill is followed within a 3-action adjacency window by a 401/403 while the step-referenced credential is untried and was not the last one filled, ONE report_issue/exploration_blocked per run is deflected toward that specific credential. A genuine 401 of the step's own credential, an unrelated stray 401, or a step naming no credential all proceed to the report. Off restores the model's free credential pick and no auth-failure nudge (pre-feature behavior).",designDoc:"docs/plans/2026-07-19-ag7727-run-fidelity-fixes-design.md",status:"active",added:"2026-07-19"},EMAIL_CODE_PROVENANCE:{key:"EMAIL_CODE_PROVENANCE",envVars:["AGENTIQA_EMAIL_CODE_PROVENANCE","AGENTIQA_EXPERIMENT_EMAIL_CODE_PROVENANCE"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Verification-code write provenance gate: a write the model TYPED as fieldPurpose='verification_code' is refused unless its value equals a whole code token in an email actually fetched via check_email this run (subject + text + html-as-text, token-equality not raw substring). Off passes the write through ungrounded (pre-feature behavior), so a guessed or fabricated code can mutate the page into a false success state.",designDoc:"packages/engine-core/src/emailVerificationGate.ts",status:"active",added:"2026-07-12"},VERBATIM_INPUT_PIN:{key:"VERBATIM_INPUT_PIN",envVars:["AGENTIQA_VERBATIM_INPUT_PIN","AGENTIQA_EXPERIMENT_VERBATIM_INPUT_PIN"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Verbatim user-input payload pinning: committed text writes carry a source-action id, field identity, and model-authored purpose role; an exact match to a configured login-email identity is forced to credential_identifier from typed project state. On a source-bound run, a text-entry step's verbatimInput is kept ONLY when its exact source action resolves uniquely and has inputRole=user_supplied_payload; duplicate, credential/generated/other, missing, conflicting, and ambiguous bindings are omitted. The value must also provenance-match user chat/attachment text. Legacy source-less producers retain the corpus-only gate. RunnerRuntime types the accepted pin verbatim (slot wins over prose). Off restores the prior write-tool contract (inputRole optional, legacy descriptions) and disables capture/attach plus runner consumption.",designDoc:"docs/plans/2026-07-13-verbatim-input-payload-design.md",status:"active",added:"2026-07-13"},VERIFY_ORACLE_FIDELITY:{key:"VERIFY_ORACLE_FIDELITY",envVars:["AGENTIQA_VERIFY_ORACLE_FIDELITY","AGENTIQA_EXPERIMENT_VERIFY_ORACLE_FIDELITY"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Verify-oracle fidelity (verbatim-input Phase 2): a deterministic post-generation pass at both producer gates (CoordinatorRuntime.handleSaveTestPlan + the Explorer draft path), after pinVerbatimInputSteps, that restores user-authored acceptance criteria the LLM summarized away. It captures verification check-bullets under a high-precision verification-heading line (r2: heading ends with ':' and its core matches a verification PHRASE whole, not merely contains a strong token; each bullet captured unless action-imperative-shaped) plus a bounded single-paragraph anti-false-pass lookback, tests each against the persisted verify-side content (verify-step texts + criteria checks + expectedValue pins) by literal-atom containment (quoted spans + numerals/ranges with EN/RU 0\u201320 spelled-form equivalence; atom-free \u2192 normalized-token containment \u2265 0.6), and APPENDS a verify step for every uncovered item carrying the user's bullet verbatim as both the step text AND a compiled criterion ({check, strict:true}, r2/F7 \u2014 so the appended step passes validateDraftPlanSteps and never hits the runner's criteria-less synthesis path). Append-only (never edits/deletes an existing step), fail-open (any error \u2192 save unchanged). Off restores pure LLM-compliance generation (summarized oracles persist as-is). Emits an oracle_fidelity diag {bullets, covered, appended, lookbackCaptured, looseBullets, disabled} regardless of flag state (looseBullets = diag-only recall telemetry, never appends).",designDoc:"docs/plans/2026-07-15-verify-oracle-fidelity-design.md",status:"active",added:"2026-07-15"},TYPE_NEWLINE_SAFE:{key:"TYPE_NEWLINE_SAFE",envVars:["AGENTIQA_TYPE_NEWLINE_SAFE","AGENTIQA_EXPERIMENT_TYPE_NEWLINE_SAFE"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'Multi-line text typing: embedded newlines are entered as Shift+Enter soft line breaks (never a raw Enter keypress) so a multi-line prompt does not premature-submit an Enter-to-send composer (e.g. Miro/Slack chat sidekicks). Off restores the raw keyboard.type(text) behavior where each "\\n" fires an Enter keypress. Applies to all four text-typing paths (typeTextAt / typeByRef / typeByLabel / setFocusedInputValue fallback); pressEnter still appends one trailing Enter regardless.',designDoc:"packages/engine-core/src/typeMultiline.ts",status:"active",added:"2026-07-13"},CANVAS_TYPE_GROUNDING:{key:"CANVAS_TYPE_GROUNDING",envVars:["AGENTIQA_CANVAS_TYPE_GROUNDING","AGENTIQA_EXPERIMENT_CANVAS_TYPE_GROUNDING"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Canvas typed-text grounding: after a coordinate type_text_at whose focus is NOT a text-editable DOM element on a canvas-dominant screen, verify the text actually landed \u2014 a11y/DOM positive check on the reused post-action snapshot, else a pixel route gated by an ambient-animation pre-check (two pre-type full-frame captures a settle apart). If the board is proven static (pre-captures byte-identical) a full-frame before/after compare decides: any change = landed (off-clip renders included), byte-identical = confident negative \u2192 explicit failure (metadata.error + canvasTypeVerification='text_not_found') with a switch-strategy hint. If the board is self-animating (pre-captures differ) the outcome is uncertain_animated: behavior byte-identical to flag-off, recorded via a canvas_type_verify diag event. Every uncertain outcome is fail-open (behavior unchanged); \u22643 verification screenshots per qualifying action. Off restores the pre-feature bare-success canvas type (no verification screenshots, no a11y check).",designDoc:"docs/plans/2026-07-14-canvas-chat-failure-class-design.md",status:"active",added:"2026-07-14"},RESULT_FIRST_DISCOVERY:{key:"RESULT_FIRST_DISCOVERY",envVars:["AGENTIQA_EXPERIMENT_RESULT_FIRST_DISCOVERY"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"env-const-disabled",gates:'Result-first discovery (#373): auto-approve discovered scope and run high-risk areas immediately; off restores the "question-first" scope-approval checkpoint that waits.',designDoc:"packages/engine-core/src/resultFirstDiscovery.ts",status:"active",added:"2026-06-01",notes:"Single spelling: reads ONLY AGENTIQA_EXPERIMENT_RESULT_FIRST_DISCOVERY (no bare AGENTIQA_ spelling). NOT migrated to killSwitchDisabled \u2014 that would add the bare spelling and change behavior. Also has a per-session config opt-out."},SCOPE_PROVENANCE:{key:"SCOPE_PROVENANCE",envVars:["AGENTIQA_EXPERIMENT_SCOPE_PROVENANCE"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"env-const-disabled",gates:"Scope-provenance-aware plan selection: user-enumerated medium/low areas still run in the first pass; off falls back to the legacy risk-only split.",designDoc:"packages/engine-core/src/resultFirstDiscovery.ts",status:"active",added:"2026-07-01",notes:"Single spelling: reads ONLY AGENTIQA_EXPERIMENT_SCOPE_PROVENANCE. NOT migrated to killSwitch (would add a bare spelling)."},MEMORY_WRITEBACK:{key:"MEMORY_WRITEBACK",envVars:["AGENTIQA_EXPERIMENT_MEMORY_WRITEBACK"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"env-const-enabled",gates:"Verified-memory writeback: offers the save_verified_memory tool and activates the durable write path; off leaves writeback inert.",designDoc:"docs/plans/2026-07-04-verified-memory-writeback-design.md",status:"active",added:"2026-07-04",notes:"Single spelling: reads ONLY AGENTIQA_EXPERIMENT_MEMORY_WRITEBACK. NOT migrated to killSwitchEnabled (would add a bare spelling). Also has a per-session config force-on for evals.",graduation:{status:"gated",gate:"Model-elicitation quality gate from the 2026-07-04 verified-memory-writeback design doc closes",evidence:"design-doc gate + eval force-on runs",owner:"steering (Alex)",review:"2026-08-01"}},FAST_START_PROMPT:{key:"FAST_START_PROMPT",envVars:["AGENTIQA_EXPERIMENT_FAST_START_PROMPT"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"is-experiment-enabled",gates:"Explorer prompt variant: emits the fast-start initial prompt instead of the standard one.",designDoc:"packages/engine-core/src/ExplorerRuntime.ts",status:"active",added:"2026-06-01",notes:'Single spelling via isExperimentEnabled (exact "1"). NOT a killSwitch flag \u2014 no bare AGENTIQA_ spelling honored.',graduation:{status:"parked",gate:"No rollout intent; delete registry entry + code path if still unused by 2026-09-01"}},MINIMAL_INITIAL_CONTEXT:{key:"MINIMAL_INITIAL_CONTEXT",envVars:["AGENTIQA_EXPERIMENT_MINIMAL_INITIAL_CONTEXT"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"is-experiment-enabled",gates:"Explorer context variant: sends a minimal initial context to the model instead of the full one.",designDoc:"packages/engine-core/src/ExplorerRuntime.ts",status:"active",added:"2026-06-01",notes:'Single spelling via isExperimentEnabled (exact "1"). NOT a killSwitch flag \u2014 no bare AGENTIQA_ spelling honored.',graduation:{status:"parked",gate:"No rollout intent; delete registry entry + code path if still unused by 2026-09-01"}},STEERING_VETO:{key:"STEERING_VETO",envVars:["AGENTIQA_STEERING_VETO"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!1,read:"env-direct",gates:"Supervisor steering veto: at a redirect, refuses the recent loop-set of action fingerprints for a bounded TTL/budget; off passes those calls through.",designDoc:"packages/engine-core/src/steeringVeto.ts",status:"active",added:"2026-06-01",notes:'Reads ONLY the bare AGENTIQA_STEERING_VETO (via direct process.env access, "!== 0"). It has NO AGENTIQA_EXPERIMENT_ spelling, so the orchestrator passthrough cannot flip it in a cloud pod (cloud-unreachable \u2014 known limitation). NOT migrated to killSwitchDisabled: that would add the EXPERIMENT_ spelling and change behavior/reachability.'},AUTH_DISABLE_EMAIL_VERIFICATION:{key:"AUTH_DISABLE_EMAIL_VERIFICATION",envVars:["AUTH_DISABLE_EMAIL_VERIFICATION"],polarity:"1-enables",defaultState:"off",surfaces:["web-next-node"],cloudForwarded:!1,read:"env-direct",gates:"On-prem escape hatch: disables signup email verification; IGNORED (fail-safe) on a hosted Vercel deploy where VERCEL/VERCEL_ENV is present.",designDoc:"apps/web-next/lib/auth-flags.ts",status:"active",added:"2026-06-01",notes:"Not AGENTIQA_-prefixed. Static process.env member access (edge-safe). Hosted-platform sentinel forces it off on Vercel.",graduation:{status:"permanent",gate:"On-prem operational escape hatch, not an experiment \u2014 never graduates; forced off on hosted Vercel"}},RUN_DISPATCH_MUTEX:{key:"RUN_DISPATCH_MUTEX",envVars:["RUN_DISPATCH_MUTEX"],polarity:"1-enables",defaultState:"off",surfaces:["web-next-edge"],cloudForwarded:!1,read:"env-direct",gates:"Per-plan run-dispatch mutual exclusion (AG-8221): when on, a new run of a plan that already has a non-terminal run either supersedes a ZOMBIE predecessor (no activity for RUN_DISPATCH_ZOMBIE_MS, default 600s \u2014 a measured floor, see packages/shared-types/src/runDispatch.ts) or is rejected 409 plan_run_active behind a LIVE one. Off = SHADOW: the same scan and decision run and log run_dispatch_mutex:would_supersede/would_block, but every caller is told to proceed and nothing is written.",designDoc:"packages/shared-types/src/runDispatch.ts",status:"active",added:"2026-07-30",notes:"Not AGENTIQA_-prefixed. Static process.env member access via runDispatchMutexEnforced() in apps/web-next/lib/run-dispatch-mutex.ts (single read point, edge-safe). The engine only relays the decision \u2014 it reads no flag \u2014 so this one env var is the whole gate. Companion tuning knob RUN_DISPATCH_ZOMBIE_MS overrides the zombie threshold; it is a threshold, not a behavior gate, so it is deliberately not a registry entry.",graduation:{status:"gated",gate:"Shadow diags from the Lio daily bursts show would_block/would_supersede firing only on genuine overlaps, with zero would_supersede against a run that later produced a verdict (a superseded-live event is the dangerous direction and blocks graduation).",evidence:"run_dispatch_mutex:would_* lines from web-next logs, cross-checked against the superseded runs' final status in app.test_plan_runs.",owner:"Alex",review:"2026-08-13"}},ORG_ENTITLEMENT_ENABLED:{key:"ORG_ENTITLEMENT_ENABLED",envVars:["ORG_ENTITLEMENT_ENABLED"],polarity:"0-disables",defaultState:"on",surfaces:["web-next-node"],cloudForwarded:!1,read:"env-direct",gates:'Company-plan org entitlement (O0+): when on, an active OrgMembership resolves the billing subject to the org and its plan wins over the personal plan in all entitlement gates; "0" forces the personal subject everywhere (org rows become inert).',designDoc:"docs/plans/2026-07-11-company-plan-access-control-design.md",status:"active",added:"2026-07-11",notes:"Not AGENTIQA_-prefixed. Static process.env member access via isOrgEntitlementEnabled() in apps/web-next/lib/billing-subject.ts (single read point). Dark by data until an Organization row exists \u2014 with zero orgs the flag has no observable effect."},ORG_PROJECT_SHARING:{key:"ORG_PROJECT_SHARING",envVars:["ORG_PROJECT_SHARING"],polarity:"0-disables",defaultState:"on",surfaces:["web-next-edge","web-next-node"],cloudForwarded:!1,read:"env-direct",gates:"Org project sharing (Company plan): when on, `projectScopedGrantFor` lets a member of the OWNER's active Company-tier org read AND write the shared child entities of a project whose owner set `app.projects.org_shared = true` (test-plans, test-plan-runs, batch-runs, issues, labels, app-map), and widens the owner's own child queries from user-scoped to project-scoped so the owner sees what teammates ran and edited. \"0\" collapses every project grant to owner-only \u2014 reads re-narrow to the caller's own rows and writes re-gate to the owner \u2014 with no redeploy and no data change. NEVER gates credentials (plaintext secrets are excluded from the share by design), memory, journal, or Project Files.",designDoc:"docs/plans/2026-08-03-org-project-sharing-design.md",status:"active",added:"2026-08-03",notes:"Not AGENTIQA_-prefixed, so NOT cloud-forwarded: the orchestrator forwards only AGENTIQA_EXPERIMENT_* into engine session pods, and this flag is read exclusively in web-next (Vercel env), never in the engine \u2014 same shape as ORG_ENTITLEMENT_ENABLED. Static process.env member access via isOrgProjectSharingEnabled() in apps/web-next/lib/org-read-grant.ts (single read point; the sync-entity routes are edge functions, so the read must stay a static literal member access). Dark by data twice over: org_shared defaults false, and a true toggle still grants nobody unless the owner is an active member of an enterprise-tier org \u2014 so with zero shared projects the flag has no observable effect. ORG_ENTITLEMENT_ENABLED=0 also collapses this grant, because the Company-org half resolves through resolveBillingSubject."},PROJECT_RUN_LOCK:{key:"PROJECT_RUN_LOCK",envVars:["PROJECT_RUN_LOCK"],polarity:"1-enables",defaultState:"off",surfaces:["web-next-edge"],cloudForwarded:!1,read:"env-direct",gates:"Per-PROJECT run concurrency lock (P1.6): at most ONE live run OR batch per project at a time, across ALL identities \u2014 the owner, any member of the owner's Company org on a shared project, and ServiceKey CLI runs alike. Enforced at the two server seams that create the rows: the new-row path of `PUT /api/sync/entities/test-plan-runs/[id]` and of `PUT /api/sync/entities/batch-runs/[id]`. A refused start gets 409 `{code:'PROJECT_RUN_IN_PROGRESS', runId, kind, startedAt, startedByLabel}` naming who holds the project and since when \u2014 never 403 (a 403 on an entity write trips the SPA signOut trap, HC1) and never a 200 no-op. Liveness is GREATEST(run.updated_at, run_billing.last_step_at) for a single run and the freshest of the batch row and ALL its legs for a batch, with a 90-minute silence TTL; a holder past the TTL stops blocking and is opportunistically reaped to status='cancelled' (+ termination_reason='stale_lock_reap' on runs, which bills zero). NEVER blocks an update to an EXISTING row \u2014 only the creation of a new one \u2014 nor a leg of an already-admitted batch. The exemption is EXISTENCE, not status: until 2026-08-03 any PUT whose `status` was already terminal skipped the lock outright, which let a refused caller INSERT itself as a brand-new terminal row simply by finishing (measured web bypass) \u2014 so a first PUT that is already terminal is now admitted whenever nothing holds the project (the desktop late-sync case) and refused only while another run/batch of that project is live. \"0\" removes the lock entirely: both seams revert to an unguarded upsert with no reap and no 409. RETIRED 2026-08-04 \u2014 now default OFF, \"1\" restores it. Its rationale opened with \"the thing two concurrent runs collide on is the TARGET APPLICATION and the credentials used to reach it, and in this product both are properties of the PROJECT\": the credential half was never true (app.project_credentials has carried user_id since its first migration and the read path has always been WHERE user_id = caller), and the data half was declined by the customer it was built for (\"two accounts will operate on different data and test cases in 95-99% of cases \u2026 we're fine not handling that case\"). The credential POOL contends on the ACCOUNT \u2014 the resource that is actually shared \u2014 and hands a distinct one to each caller instead of refusing the second run. What made keeping it untenable rather than merely redundant: the lock is active ONLY on org-shared projects, exactly where the pool lives, so it gated the one scenario the feature exists for \u2014 and did so AFTER the caller had already leased an account, leaving that slot held until teardown. Nothing is deleted: the transaction, the reap and the 409 contract are the shape a per-account guard needs, and lib/credential-pool.ts already reuses them.",designDoc:"docs/plans/2026-08-03-org-project-sharing-design.md",status:"active",added:"2026-08-03",notes:"Not AGENTIQA_-prefixed, so NOT cloud-forwarded: the orchestrator forwards only AGENTIQA_EXPERIMENT_* into engine session pods, and this flag is read exclusively in web-next (Vercel env), never in the engine \u2014 same shape as ORG_PROJECT_SHARING. Static process.env member access via projectRunLockEnabled() in apps/web-next/lib/project-run-lock.ts (single read point; both seams are edge functions, so the read must stay a static literal member access). Scope is deliberately the PROJECT, not the plan and not the org: credentials and default_url are project-scoped, so two runs of DIFFERENT plans in one project drive the same account against the same environment \u2014 which the pre-existing per-plan mutex RUN_DISPATCH_MUTEX (AG-8221) does not cover \u2014 while an org-wide lock would serialize unrelated products that merely share a payer. The two locks are complementary and use deliberately different staleness thresholds because their safe directions are opposite (AG-8221 may KILL a predecessor, so it wants the smallest value above the observed silence distribution: 600s; this lock only REFUSES a newcomer, so it wants a value above the longest legitimate LIFETIME: the 90-minute sandbox ceiling, overridable via PROJECT_RUN_LOCK_TTL_MS). RETIRED 2026-08-04 (default OFF): with the parent off, PROJECT_RUN_LOCK_UNSHARED cannot revive it either \u2014 pinned by projectRunLockApplies returning false for BOTH shapes, so a stray child flag left in some environment is inert. Fail-open on any error \u2014 a guard that refuses customers' runs because its own bookkeeping broke is worse than the overlap it prevents.",graduation:{status:"permanent",gate:"RETIRED 2026-08-04, not parked: this does not graduate back to default-ON. The credential pool contends on the ACCOUNT \u2014 the resource two concurrent runs actually share \u2014 while this lock refused the whole PROJECT, was active only where the pool lives (org-shared projects), and consumed a leased account before refusing. Kept as `=1` for the one case it would still answer: whole-project serialisation for a plan that signs in with nothing, on a project whose data two runs would genuinely corrupt. Nobody has asked for that; the customer it was built for declined it explicitly.",evidence:"a project that needs whole-project serialisation despite per-account leasing \u2014 none known",owner:"steering (Alex)",review:"2026-11-04"}},PROJECT_RUN_LOCK_UNSHARED:{key:"PROJECT_RUN_LOCK_UNSHARED",envVars:["PROJECT_RUN_LOCK_UNSHARED"],polarity:"1-enables",defaultState:"off",surfaces:["web-next-edge"],cloudForwarded:!1,read:"env-direct",gates:"Extends the PROJECT_RUN_LOCK enforcement (above) from org-SHARED projects to NON-shared, single-owner projects. OFF (default): a project with no live org share is never locked and both seams behave exactly as they do today \u2014 no probe, no reap, no 409. ON: the same one-live-run-or-batch-per-project rule applies to every project regardless of sharing. Parented by PROJECT_RUN_LOCK (=0 disables both halves).",designDoc:"docs/plans/2026-08-03-org-project-sharing-design.md",status:"active",added:"2026-08-03",graduation:{status:"gated",gate:"NOT graduable until concurrent solo runs are either migrated or accepted as breakage. MEASURED ON PROD 2026-08-03: over the trailing 90 days, 50 pairs of SAME-USER runs in ONE project genuinely overlapped in time \u2014 8 distinct users, 8 distinct projects, every pair with BOTH rows terminal (so real concurrency, not a zombie artefact), 35 pairs overlapping >60s and 10 >5 minutes, and every single pair origin='desktop'. Flipping this ON today would start answering 409 to a workflow eight people use. Graduation therefore needs, in order: (1) a decision on whether concurrent desktop runs in one project are legitimate (the desktop app drives a LOCAL browser, so the collision domain argument is weaker there than for cloud runs) \u2014 if they are, the flag needs a per-origin carve-out rather than a flip; (2) if they are not, a client-side surface that explains the refusal before the user starts the second run, since the CLI/SPA today learn about it only from the 409; (3) a shadow census from the shared-project half showing the refusal rate and that no refusal was a false positive. Note the lock cannot be graduated by soak alone: with the shared half dark by data (no org_shared projects on prod), the shared half produces no evidence about the solo population.",evidence:"a product decision on concurrent same-project desktop runs (the measured 50-pair / 8-user population); client-side pre-start surfacing so a refusal is not first learned from a 409; and a shared-project refusal census with zero false positives",owner:"steering (Alex)",review:"2026-08-17"},notes:"Same read discipline as its parent: static process.env member access via projectRunLockUnsharedEnabled() in apps/web-next/lib/project-run-lock.ts, web-next-edge only, never cloud-forwarded. Exists as a separate key rather than a mode on PROJECT_RUN_LOCK because the two populations have opposite evidence: the shared-project case has ZERO current population and ships enforcing, while the solo case has a measured live population and must ship dark. One flag cannot carry two defaults."},PROJECT_CREDENTIAL_POOL:{key:"PROJECT_CREDENTIAL_POOL",envVars:["PROJECT_CREDENTIAL_POOL"],polarity:"0-disables",defaultState:"on",surfaces:["web-next-node"],cloudForwarded:!1,read:"env-direct",gates:"Project credential pool (2026-08-04): a project may hold SEVERAL ACCOUNTS \u2014 each a LOGIN/PASSWORD PAIR \u2014 under one role NAME (`scope='project'` rows of `app.project_credentials`, keyed `(project_id, name, slot)`), provisioned by the project owner or an `OrgMembership.role='admin'` of the owner's org. A caller holding no PERSONAL credential of that name LEASES one for the life of an engine session and releases it at teardown; the lease returns the account's real LOGIN alongside its secret and the seed carries both, because `credential.name` is otherwise the login the agent types into the email field \u2014 a role with no login would be typed AS the login (corrected 2026-08-04 after the first merge shipped role/password, which was one login with N passwords); a lease older than the staleness cutoff is reaped so a lost release cannot wedge a slot (default 5400000 ms = 90 minutes, the sandbox wall-clock ceiling and deliberately the same number as PROJECT_RUN_LOCK_DEFAULT_TTL_MS; overridable via `CREDENTIAL_LEASE_TTL_MS`, whose invalid or non-positive values fall back to the default rather than disabling reaping \u2014 a pool that never reaps would wedge permanently after one lost release). Personal rows always win, so a caller with their own `admin` consumes no shared account and behaves exactly as before. `GET /api/sync/entities/credentials` gains pool NAMES \u2014 never secrets, because it is called on every pre-flight and every seed build; the ONLY route that returns a pool secret is `POST /api/projects/{id}/credential-pool/lease`, and only for the slots it just granted to that lease. An exhausted role answers 409 `{code:'CREDENTIAL_POOL_EXHAUSTED', names}`, which both clients re-tag as the existing `CREDENTIAL_PREFLIGHT_REFUSED` so the refusal lands before a run row or engine session exists. FAIL-CLOSED, unlike the sibling PROJECT_RUN_LOCK: a session seeded without its credential burns a full run to fail at the login step. \"0\" hides the pool everywhere \u2014 list returns personal rows only, leasing 404s, the settings section renders empty \u2014 with no redeploy and no data change. Dark by data: a project with no `scope='project'` row behaves exactly as today.",designDoc:"docs/plans/2026-08-04-project-credential-pool-design.md",status:"active",added:"2026-08-04",notes:"Not AGENTIQA_-prefixed, so NOT cloud-forwarded: read exclusively in web-next (Vercel env), never in the engine \u2014 same shape as ORG_PROJECT_SHARING and PROJECT_RUN_LOCK. Single read point: credentialPoolEnabled() in apps/web-next/lib/credential-pool.ts, as a static process.env member access. NODE surface, not edge: provisioning encrypts and leasing decrypts with node:crypto, which the edge runtime does not provide \u2014 the routes originally declared runtime='edge', which compiled, type-checked and passed all 20 DB-evaluated tests (they import the handlers directly under Node) and then answered 500 on the first live request. RELATIONSHIP TO PROJECT_RUN_LOCK: that lock models the PROJECT as the contended resource and refuses a second run outright; the pool models the ACCOUNT, which is what two concurrent runs actually collide on, and hands out a distinct one per caller. The run lock is the degenerate case of a pool of size one, and on a project WITH a pool it is the coarser instrument \u2014 it will refuse a run for which a free account exists. Note the run lock's own rationale cites 'credentials are project-scoped', which was never true: app.project_credentials has carried user_id since its first migration and the read path has always been WHERE user_id = caller. Recorded scope cut (design P6): v1 returns the leased secret to the CALLER'S OWN CLIENT, exactly as their own credential already travels, so a member can read a shared secret out of their client; closing that needs server-side seeding (P1.5), which touches the engine image (Pipeline B). There is no lease heartbeat: every normal exit releases explicitly, so the TTL covers only a release that never arrived (closed tab, killed CI job, crashed pod), and it errs long because the failure directions are asymmetric \u2014 a slot wrongly held for an hour costs one refusal, while a slot wrongly freed hands the same account to two live agents, the exact failure the pool exists to prevent. The narrowed HC3 invariant the suite pins: a caller may receive a secret only when it is their own row, OR it is a pool row of a project they may access AND they hold a live lease on that exact slot \u2014 no caller ever receives another MEMBER'S personal row, under any grant, on any route."},ORG_GRANT_DEBIT_AT_FINALIZE:{key:"ORG_GRANT_DEBIT_AT_FINALIZE",envVars:["ORG_GRANT_DEBIT_AT_FINALIZE"],polarity:"1-enables",defaultState:"off",surfaces:["web-next-edge"],cloudForwarded:!1,read:"env-direct",gates:"Moves the org run-grant debit from analytics-ingest to finalize-run (2026-07-30). At ingest neither termination_reason nor the unit's final length is known, so the grant counter over-debited every non-billable termination, could never apply the per-unit min_billable_steps floor, could not refund, and double-debited AG-8220 duplicate pairs \u2014 measured on lio as +1041 / -437 / net +615 units (+24.6 credits) of customer-visible over-consumption against the invoice-authoritative getOrgUsage. When ON, ingest debits nothing and /api/billing/finalize-run/[id] recomputes the whole billing UNIT against getOrgUsage's own math (same unit keying incl. the chat/explore fan-out collapse, same per-(unit,byok) floor, same billable-termination filter) and applies only the DELTA against run_billing.grant_debited_units, claimed atomically per billing unit (one non-interactive Neon transaction behind a pg_advisory_xact_lock on (org, user, unit_key)) \u2014 which makes the debit idempotent (a double-finalize settles 0, and so does a concurrent one), refundable (a non-billable unit recomputes to 0 and the units are credited back), and safe across the flip. ON also activates the in-flight HOLD in the org cap gate (getOrgInFlightHeldUnits): unfinalized runs' not-yet-debited steps are subtracted from remainingUnits, so the orchestrator's per-dispatch admission check keeps decrementing continuously instead of standing still for the whole duration of a run. OFF = SHADOW: the ingest debit continues with byte-identical org_run_grant writes; finalize additionally computes the would-be total, stamps run_billing.grant_shadow_units, and logs grant_debit:finalize_shadow \u2014 the soak corpus that gates graduation.",designDoc:"docs/plans/2026-07-23-ag7872-org-run-grants-design.md",status:"active",added:"2026-07-30",notes:"Not AGENTIQA_-prefixed. Static process.env member access via isGrantDebitAtFinalizeEnabled() in apps/web-next/lib/org-grant-debit.ts (single read point, edge-safe \u2014 read from two Edge routes and the cap gate). Dark by data for every org without run-grant rows: debitOrgGrants/creditOrgGrants match no active grant and issue zero UPDATEs, and personal subjects short-circuit before any query. TRANSITION PROTOCOL (why the flip cannot double-debit): the debit is derived from run_billing.grant_debited_units, so that ledger has to be accurate for HISTORY as well as for new rows. Two things make it so, and both are load-bearing: (a) the 20260730120000 migration BACKFILLS every org-stamped pre-migration row to its step_count \u2014 the amount the ingest boundary actually debited, since /api/analytics/ingest passed the same billedStepIncrement to debitOrgGrants that recordRunStep added to step_count \u2014 and without that backfill a chat session still alive at the flip would recompute alreadyDebited=0 and re-debit its whole history; (b) under the flag OFF the ingest path records what it debits into the same column. A run ingested pre-flip and finalized post-flip therefore settles only trueUnits - alreadyDebited, covered by the flag-flip and post-sweep no-op tests in apps/web-next/lib/org-grant-debit.db.test.ts. EXACTLY-ONCE: the recompute-and-claim is a single non-interactive Neon transaction guarded by a pg_advisory_xact_lock keyed on (org, user, unit_key), because the ledger IS the idempotence guard and a read-modify-write across HTTP round trips let N concurrent finalizes of one unit each claim the whole total (measured 5x on an explore fan-out, 2x on a double finalize; regression suite apps/web-next/lib/org-grant-debit.race.db.test.ts). Historical drift accrued BEFORE the flip is NOT self-correcting (those units never finalize again) and is returned separately by scripts/true-up-org-grant.ts, which is operator-run and never automatic.",graduation:{status:"gated",gate:"Graduate only when the shadow corpus shows the finalize-time recomputation agreeing with getOrgUsage. The corpus is a plain SQL query over run_billing \u2014 compare grant_shadow_units against the unit's summed grant_debited_units, grouped by termination_reason \u2014 NOT scraped logs. Graduation criteria: (a) for billable terminations the shadow total matches the unit's floored getOrgUsage total exactly; (b) every non-billable termination shows a NEGATIVE delta of exactly what ingest debited (the refund the current boundary cannot make); (c) no unit shows a positive delta unexplained by the floor. Then, in this ORDER: (1) run scripts/true-up-org-grant.ts --apply per grant-mode org to return the historical drift (lio: -615 units / -24.6 credits as of 2026-07-30); (2) only then flip to 1 in the web-next Vercel env. The two commute \u2014 the migration's backfill keeps the ledger accurate for history either way \u2014 but sweeping FIRST makes the flip a provable no-op on everything already settled: the sweep rewrites each unit's ledger to its true total, so that unit's next settlement computes a delta of exactly 0 and the flip cannot move a historical unit at all. Flip-first is a correct fallback if the sweep has to wait, not the default. The sweep is operator-run and never automatic.",evidence:"run_billing.grant_shadow_units vs summed grant_debited_units per unit (durable, queryable soak corpus) + the grant_debit:finalize_shadow finalize logs; the unit + live-DB suites in apps/web-next/lib/org-grant-debit.test.ts, org-grant-debit.db.test.ts (per-termination-class debit, chat/explore folding, double-finalize idempotence, AG-8220 duplicate pair, flag-flip no-double-debit, in-flight hold) and org-grant-debit.race.db.test.ts (exactly-once under a 5-way concurrent fan-out over 20 iterations, concurrent double refund, no-active-grant reconcile); scripts/true-up-org-grant.ts dry run per org",owner:"steering (Alex)",review:"2026-08-13"}},EXTENSION_PROFILE_PERSISTENCE:{key:"EXTENSION_PROFILE_PERSISTENCE",envVars:[],polarity:"1-enables",defaultState:"on",surfaces:["desktop-main","desktop-renderer"],cloudForwarded:!1,read:"compile-const",gates:"Saves/restores a per-project Chrome profile (cookies/storage) across sessions so login can be skipped on replay; a false value skips profile persistence.",designDoc:"apps/desktop-next/src/renderer/featureFlags.ts",status:"active",added:"2026-06-01",notes:"Hardcoded TypeScript boolean (`= true`), no env var \u2014 a compile-time gate flipped by editing the const. Defined twice: apps/desktop-next/src/renderer/featureFlags.ts and apps/desktop-next/src/main/computerUse/DesktopPlaywrightService.ts (the main-process copy is the one actually consumed)."},TRANSIENT_ENV_RETRY:{key:"TRANSIENT_ENV_RETRY",envVars:["AGENTIQA_TRANSIENT_ENV_RETRY","AGENTIQA_EXPERIMENT_TRANSIENT_ENV_RETRY"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:`Transient-environment retry-once policy (RunnerRuntime / test-plan runs only, v1 \u2014 never ExplorerRuntime/chat). Motivating incident: staging run run_9fff560a \u2014 the app's POST to its own API failed transiently (console: CORS block + AxiosError "Network Error"), the agent waited 2\xD730s, then filed report_issue and blocked after ONE attempt; later probing showed the API healthy. When on, a report_issue (or the ensuing exploration_blocked) whose failure is a DETERMINISTIC transient-environment stall \u2014 BOTH a stall/timeout symptom (a wait/wait_for_element in the evidence window, or explicit stall/network phrasing; never a content/assertion mismatch) AND an environment signature in the captured EventDigest (any failedRequests, a console/page error matching the network class \u2014 CORS / "Network Error" / AxiosError / net::ERR_ / "fetch failed" \u2014 or an explicit 5xx write) \u2014 is BOUNCED once with a structured instruction to repeat the triggering action and report only if it reproduces, tracked per step index. The second matching failure at the SAME step passes through and the runtime (not the model) stamps the issue evidence JSON with attempts:2, both attempt timestamps, both EventDigest snapshots, sets category='environment', and appends 'Reproduced on retry \u2014 2 attempts.' to the description. Bounds: exactly 1 retry per step, max 2 retried steps per run; a further environment stall after the budget is spent passes through with attempts:1 and the note 'Environment degraded \u2014 not retried (budget exhausted).'. A run-mode prompt nudge asks the agent to retry proactively; the deterministic gate is the backstop (never trust LLM compliance). Off \u21D2 every environment failure files immediately on the first attempt, byte-identical to pre-change behavior. Non-environment failures are never retried regardless of this flag.`,designDoc:"docs/plans/2026-07-23-transient-env-retry-once-design.md",status:"active",added:"2026-07-23",notes:"Ships default-ON from inception (not graduated from an off default) \u2014 the policy is a strict reduction of a confirmed false-block class (one transient blip \u2192 a blocked run + a filed non-defect issue), so there is no pre-change OFF behavior to preserve. Registered in killSwitchDefaultState.test.ts INTENTIONALLY_GRADUATED (the tripwire acknowledging the default-ON state deliberately). Deterministic classifier + state machine in packages/engine-core/src/transientEnvRetry.ts; the kill-switch read + wiring live at packages/engine-core/src/RunnerRuntime.ts handleReportIssue / handleBlocked. Runner lane only."},PREDICATE_BASIS_COMPILE:{key:"PREDICATE_BASIS_COMPILE",envVars:["AGENTIQA_PREDICATE_BASIS_COMPILE","AGENTIQA_EXPERIMENT_PREDICATE_BASIS_COMPILE"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:`Trust-layer predicate basis \u2014 Slice B AUTOFORMALIZATION COMPILER (design docs/plans/2026-07-26-trust-layer-predicate-basis-design.md; north star docs/plans/2026-07-26-trust-layer-verification-architecture.md). "Compile-don't-parse": at AUTHORING an LLM COMPILES a natural-language assertion into a typed Slice-A logical form (Predicate) over named observables \u2014 autoformalization (utterance \u2192 logical form \u2192 executor \u2192 denotation) \u2014 NOT a verdict. Slice A shipped the schema + generic deterministic executor; Slice B produces the forms the executor denotes. TASK-BLIND: the compiler compiles the assertion's MEANING (including any pinned expected value the assertion text itself carries) and never sees the observed screen or pass/fail \u2014 the firewall keeps the LLM out of the DECISION (Slice A's deterministic executor owns pass/fail). GRAMMAR-CONSTRAINED \u2192 MECHANICAL ABSTAIN: the model may only emit an in-grammar kind (count/delta/absence/modification/presence/typed) or the explicit not_groundable escape; compileToPredicate then STRICTLY validates the chosen kind's operands, so an inherently-subjective assertion ("the agent responds correctly", "looks clean") or an in-grammar kind with missing/invalid operands routes to a first-class ABSTAIN (AbstainNode) \u2014 NEVER a forced/invalid form. The grammar's expressiveness IS the verifiable/subjective boundary. v1 = structured output + strict schema validation + a groundability decision; full constrained decoding (PICARD) is a later refinement (TODO). GRADUATED default-ON 2026-07-27 (alongside PREDICATE_BASIS_VERIFY, which is the live consumer that routes a compiled form to a step result); the emergency kill-switch is retained and byte-identical WHEN FORCED OFF: the seam (runAssertionCompile / runRedundantCompile) short-circuits to \`disabled\` (ZERO model calls) when AGENTIQA_PREDICATE_BASIS_COMPILE=0 \u2014 and with COMPILE forced off but VERIFY on the compiler returns a disabled result \u2192 ABSTAIN (safe, never a manufactured verdict). Cost when on: one auxiliary structured-output call per compiled assertion (cost-isolated via emitAuxiliaryLlmUsage \u2014 not a billable step), thinkingBudget:0, hard-capped by a per-call timeout; fail-closed (error/empty/timeout/out-of-grammar \u2192 ABSTAIN).`,designDoc:"docs/plans/2026-07-26-trust-layer-predicate-basis-design.md",status:"active",added:"2026-07-26",notes:"GRADUATED 2026-07-27 (default ON) \u2014 the authoring-side ARc compiler that feeds the LIVE verify wiring (PREDICATE_BASIS_VERIFY, graduated in the same PR). Turn-on = BOTH flags ON: with VERIFY on, runPredicateBasisVerify calls runRedundantCompile, which rides THIS flag; on \u21D2 the compiler produces the logical forms the deterministic executor denotes. Slice B (authoring-side compiler) + Slice C (ARc redundant-compile: compile k\xD7 \u2192 ABSTAIN on disagreement/degeneration, attacking the factKind-wrong-kind gap) are the compile consumers of this flag. GRADUATION EVIDENCE: the flagship live-replay benchmark (PR #1879) returned GATE=GO \u2014 zero-regression on the prod Lio (semantic-reference) + Miro (canvas) corpora, the firewall intact (a groundable contradiction FAILS; nothing ungroundable is ever a hard PASS), the vision/canvas path abstains, and the #1876 DOM-reachability fix live-confirmed. TASK-BLIND / FIREWALL (unchanged): the compiler compiles the assertion's MEANING (never sees the observed screen nor pass/fail); Slice A's deterministic executor owns the decision. GRAMMAR-CONSTRAINED \u2192 MECHANICAL ABSTAIN: an inherently-subjective or out-of-grammar assertion routes to a first-class ABSTAIN (AbstainNode), never a forced/invalid form. Off (explicit AGENTIQA_PREDICATE_BASIS_COMPILE=0) is byte-identical to pre-graduation: runAssertionCompile / runRedundantCompile short-circuit to `disabled` with ZERO model calls, and with COMPILE off but VERIFY on the compiler returns a disabled result \u2192 ABSTAIN (safe, never a manufactured verdict) \u2014 the emergency kill-switch still works. killSwitch resolves the no-env value from this defaultState (#1729), so graduation = defaultState 'on' AND PREDICATE_BASIS_COMPILE listed in INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire). Read site: packages/engine-core/src/predicateBasis/compile.ts (runAssertionCompile) + packages/engine-core/src/predicateBasis/redundantCompile.ts (runRedundantCompile). Cost when on: one auxiliary structured-output call per compiled assertion, cost-isolated via emitAuxiliaryLlmUsage (not a billable step), thinkingBudget:0, hard-capped, fail-closed (error/empty/timeout/out-of-grammar \u2192 ABSTAIN). Acceptance tests: packages/engine-core/src/__tests__/predicateBasisCompile.test.ts + predicateBasisRedundantCompile.test.ts. Compile-correctness benchmark: e2e/benchmark/compileCorrectness.ts. Binds claims verify.predicate-basis-compile-correctness + verify.predicate-basis-redundant-compile. STAGING-FIRST: merging to staging makes this default-ON on the STAGING engine only; PROD stays OFF until a later staging\u2192main release carries it (a natural staged soak). PREDICATE_BASIS_CONSENSUS (Slice D vision-consensus) stays default-OFF \u2014 vision-consensus is safe ONLY as abstain, not turn-on-ready (the P2 dense-abstain finding)."},PREDICATE_BASIS_CONSENSUS:{key:"PREDICATE_BASIS_CONSENSUS",envVars:["AGENTIQA_PREDICATE_BASIS_CONSENSUS","AGENTIQA_EXPERIMENT_PREDICATE_BASIS_CONSENSUS"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:'Trust-layer predicate basis \u2014 Slice D: (1) CONSENSUS EXTRACTION/RESOLUTION and (2) the SEMANTIC-TOLERANCE FALLBACK TIER (design docs/plans/2026-07-26-trust-layer-predicate-basis-design.md \u2014 \xA7 three buckets bucket-2 canvas "task-blind extraction + deterministic comparison + consensus-or-abstain", \xA7 the @reference resolution consensus/abstain, \xA7 "Semantic as a fallback tier, not a peer primitive"; north star hard-part #1 "the extractor itself can be wrong: miscount a canvas, misread"). CONSENSUS: task-blindness removes confirmation bias but NOT perception error (the model genuinely miscounts a canvas / mis-resolves "the cart total"); the no-false-positive mitigation is N INDEPENDENT task-blind extractions must AGREE, else ABSTAIN \u2014 one mechanism serving BOTH the vision/canvas path AND the semantic-reference resolution risk. SEMANTIC-TOLERANCE: run the DETERMINISTIC executor FIRST; invoke the task-blind concept classifier ONLY when the deterministic comparison cannot decide (tolerance:semantic OR an inconclusive comparison) \u2014 confident semantic = Verified, ambiguous = Assessed (a lean + confidence, never a grounded badge, never a manufactured pass \u2014 the bucket-3 Assessed slot). SHADOW / behavior-neutral even when ON: NOT wired into any live RunnerRuntime verdict path \u2014 the two drivers are consumed only by the consensus benchmark + unit tests. Off \u21D2 ZERO extractor/classifier calls, byte-identical; Slice-A deterministic parity is UNAFFECTED (the semantic tier COMPOSES the untouched evaluate, only reached on an inconclusive result).',designDoc:"docs/plans/2026-07-26-trust-layer-predicate-basis-design.md",status:"active",added:"2026-07-26",notes:"Trust-layer predicate basis Slice D (consensus extraction/resolution + semantic-tolerance fallback tier). Default OFF; SHADOW / behavior-neutral even when ON \u2014 the two drivers produce would-be results and are wired into NO live verdict path (consumers: the consensus benchmark + unit tests). Read sites: packages/engine-core/src/predicateBasis/consensusExtract.ts (runConsensusExtraction \u2192 killSwitchEnabled('PREDICATE_BASIS_CONSENSUS'); zero extractor calls when off) + packages/engine-core/src/predicateBasis/semanticTolerance.ts (runSemanticTolerance \u2192 killSwitchEnabled('PREDICATE_BASIS_CONSENSUS'); zero classifier calls when off). CONSENSUS-EXTRACT: a PURE reducer (reduceNumericConsensus / reduceCategoricalConsensus \u2014 N observations \u2192 accept-on-agreement / ABSTAIN(extraction_disagreement) on numeric-spread-beyond-tolerance or different presence/text; fail-closed extraction_error when no usable sample) + a pure per-frame read (readObservable over count-of / presence-of / value-at) + the thin flag-gated driver over the EXISTING StateExtractor seam (reused, not re-implemented \u2014 N independent samples). SEMANTIC-TOLERANCE: a PURE reducer (resolveSemanticTier \u2014 deterministic-first; decided deterministic \u2192 Verified with no classifier call; inconclusive + eligible + injected classifier \u2192 semanticToSpectrum: confident match/contradiction \u2192 Verified, ambiguous \u2192 Assessed, abstain \u2192 the Inconclusive floor) + the flag-gated driver that COMPOSES the untouched Slice-A evaluate (Slice-A parity byte-identical by construction). Reuses the Slice-2 concept-classifier seam (ConceptClassifier / semanticToSpectrum / renderObservation from groundedStateUnified.ts). Deliberately NOT re-exported from predicateBasis/index.ts (imported directly). Acceptance tests: packages/engine-core/src/__tests__/predicateBasisConsensus.test.ts (deterministic \u2014 injected observations for the consensus reducer; injected classifier RESULT for the semantic tier; flag-OFF zero-calls; fail-closed error\u2192abstain). Consensus benchmark: e2e/benchmark/consensusExtraction.ts (deterministic reducer/tier self-validation always runs; the live N-extraction + classifier measurement is PENDING until a keyed run \u2014 Slice-1/B/C precedent, no fabricated number). Binds claim verify.predicate-basis-consensus-extraction. Slice E (abstain-rate as a first-class COVERAGE metric on a real-assertion corpus) is BUILT: packages/engine-core/src/predicateBasis/coverage.ts (aggregateCoverage \u2014 a PURE reducer folding pipeline-routed assertion outcomes into the Verified/Assessed/Inconclusive distribution + abstain rate + abstain-origin/per-kind breakdown), wired into NO live verdict path (no new flag; the live measurement reuses THIS flag + PREDICATE_BASIS_COMPILE), claim verify.predicate-basis-coverage-abstain-rate, benchmark e2e/benchmark/coverage.ts (deterministic layers real; live full-pipeline number PENDING until keyed). This COMPLETES the predicate-basis v1 build; the remaining work is the KEYED phase (live LLM measurements) + the prod Miro+Lio final gate.",graduation:{status:"gated",gate:"Slice D is shadow / behavior-neutral (two drivers, wired into NO verdict path). Graduation gates on: (1) the consensus benchmark run WITH a model key showing consensus ABSTAINS on genuinely disagreeing/noisy N task-blind extractions and ACCEPTS on agreement (the miscount-a-canvas / mis-resolve-a-reference no-false-positive mechanism), plus a measured abstain-rate on the vision/canvas + semantic-reference corpus; (2) the semantic-tolerance tier measured deterministic-first (a deterministic-decidable case never consulting the classifier) with confident\u2192Verified / ambiguous\u2192Assessed; (3) the engine-core predicateBasisConsensus unit suite green. This is the DEFERRED consensus-extraction that gates ANY vision-path live graduation; the FINAL gate is the prod Miro (canvas) + Lio (semantic-reference) benchmark.",evidence:"e2e/benchmark/artifacts/benchmark-consensus-extraction.{json,md} (consensus abstain/accept + abstain-rate + semantic-tier bands; PENDING until a keyed run) + the engine-core predicateBasisConsensus unit suite + the claim verify.predicate-basis-consensus-extraction",owner:"steering (Alex)",review:"2026-09-05"}},PREDICATE_BASIS_VERIFY:{key:"PREDICATE_BASIS_VERIFY",envVars:["AGENTIQA_PREDICATE_BASIS_VERIFY","AGENTIQA_EXPERIMENT_PREDICATE_BASIS_VERIFY"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Trust-layer predicate basis \u2014 Slice F LIVE VERIFY WIRING (the FIRST non-shadow slice; design docs/plans/2026-07-26-trust-layer-predicate-basis-design.md \xA7 three buckets; north star docs/plans/2026-07-26-trust-layer-verification-architecture.md \xA7 the verdict spectrum). Slices A\u2013E built the basis as SHADOW machinery wired into NO verdict path; this is the first wiring that lets a compiled logical form FEED A STEP RESULT (the readiness step toward turn-on). When ON, a verify step's assertion routes through the basis: runRedundantCompile (ARc: compile k\xD7 \u2192 ABSTAIN on disagreement/degeneration) \u2192 classifyVerifyBucket \u2192 the deterministic executor / semantic-tolerance tier \u2192 a verdict in the SPECTRUM (Verified-PASS/FAIL | Assessed | Inconclusive) that feeds the step result. SCOPED to the SAFE paths \u2014 everything else ABSTAINS, never manufactures a verdict: DOM-groundable (deterministic executor over structured extraction) \u2192 Verified-PASS/FAIL LIVE (a grounded contradiction FAILS \u2014 the no-false-positive core); confident-semantic (semantic-tolerance, deterministic-first, task-blind classifier) \u2192 Verified, ambiguous \u2192 Assessed (non-blocking WARNING band, never a hard pass/fail); VISION/CANVAS (bucket 2) \u2192 ABSTAIN (Inconclusive/WARNING), NOT enforced \u2014 the mandatory dense-abstain guard from the P2 finding (consensus does NOT eliminate correlated-miscount false-confirms on dense canvases, so vision must NOT produce a live PASS/FAIL yet; a count tally is treated as vision-grounded \u2192 ABSTAIN unless the caller asserts domGrounded); not-groundable / compile-abstain / consensus-abstain \u2192 Inconclusive/Assessed, NEVER a manufactured pass. FIREWALL INVARIANT: a groundable contradiction MUST FAIL; nothing ungroundable is EVER a hard PASS \u2014 verifyStepAction encodes the DEFAULT consequence policy (only a Verified-FAIL gates; a Verified-PASS confirms but never manufactures/overrides a pass; Assessed is a non-blocking warning; Inconclusive leaves the existing verdict). GRADUATED default-ON 2026-07-27 (alongside PREDICATE_BASIS_COMPILE) after the flagship live-replay gate; the emergency kill-switch is retained and BYTE-IDENTICAL WHEN FORCED OFF (non-negotiable): explicit AGENTIQA_PREDICATE_BASIS_VERIFY=0 makes runPredicateBasisVerify short-circuit to disabled with ZERO compile/executor/classifier calls and the RunnerRuntime enforcement pass early-return before any effect. The compile itself rides PREDICATE_BASIS_COMPILE (also graduated), so full turn-on = both flags ON (with VERIFY on but COMPILE forced off the compiler returns a disabled result \u2192 ABSTAIN, safe). STAGING-FIRST: default-ON reaches the STAGING engine on merge; PROD stays OFF until a later staging\u2192main release carries it.",designDoc:"docs/plans/2026-07-26-trust-layer-predicate-basis-design.md",status:"active",added:"2026-07-27",notes:"GRADUATED 2026-07-27 (default ON) \u2014 Slice F LIVE verify wiring, the FIRST live trust-verdict on the predicate basis (turned on alongside PREDICATE_BASIS_COMPILE per Alex's explicit approval after the flagship live-replay gate). When on (and a predicateBasisCompiler is wired at engine boot \u2014 buildDeps.getPredicateBasisCompiler, present whenever a Google key is set), a verify step's assertion routes through the basis: runRedundantCompile (ARc, rides PREDICATE_BASIS_COMPILE) \u2192 classifyVerifyBucket \u2192 the deterministic executor / semantic-tolerance tier \u2192 a SPECTRUM verdict feeding the step result. SCOPED to the SAFE paths \u2014 everything else ABSTAINS, never manufactures a verdict: DOM-groundable \u2192 Verified-PASS/FAIL LIVE (a grounded contradiction FAILS \u2014 the no-false-positive core); confident-semantic \u2192 Verified, ambiguous \u2192 Assessed (non-blocking WARNING); VISION/CANVAS \u2192 ABSTAIN (Inconclusive), NOT enforced \u2014 the mandatory P2 dense-abstain guard (a count tally is vision-grounded \u2192 ABSTAIN unless the caller asserts domGrounded); not-groundable / compile-abstain \u2192 Inconclusive, NEVER a manufactured pass. FIREWALL INVARIANT (unchanged): only a Verified-FAIL gates; a Verified-PASS confirms but never manufactures/overrides a pass; Assessed warns; Inconclusive leaves the existing verdict. GRADUATION EVIDENCE: the flagship live-replay benchmark (PR #1879) returned GATE=GO \u2014 zero-regression on the prod Lio (semantic-reference) + Miro (canvas) corpora, the firewall intact, canvas-abstain intact, and the #1876 DOM-reachability fix live-confirmed; the engine-core predicateBasisVerifyWiring + RunnerRuntime.predicateBasisVerify unit suites are green (flag-OFF byte-identical parity, safe-path enforcement, vision-canvas ABSTAIN, the firewall). BYTE-IDENTICAL WHEN FORCED OFF (emergency kill-switch retained): explicit AGENTIQA_PREDICATE_BASIS_VERIFY=0 restores the pre-graduation behavior \u2014 runPredicateBasisVerify short-circuits to disabled with ZERO compile/executor/classifier calls and the RunnerRuntime enforcement pass early-returns before any effect. killSwitch resolves the no-env value from this defaultState (#1729), so graduation = defaultState 'on' AND PREDICATE_BASIS_VERIFY listed in INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire). Read sites: packages/engine-core/src/predicateBasis/verifyWiring.ts (runPredicateBasisVerify) + packages/engine-core/src/RunnerRuntime.ts (predicateBasisVerifyEnabled / the run_complete enforcement pass). Acceptance tests: packages/engine-core/src/__tests__/predicateBasisVerifyWiring.test.ts + RunnerRuntime.predicateBasisVerify.test.ts. E2E proof: e2e/benchmark/predicateBasisVerify.ts. Standing live gate post-graduation: e2e/scenarios/22-verify/01-predicate-basis-dom-enforce.ts asserts the pbv-ON DOM-enforce + canvas-abstain behavior by DEFAULT now (default-ON engine) in the nightly qa-exhaustive path. Binds claim verify.predicate-basis-verify-wiring. STAGING-FIRST: merging to staging makes pbv default-ON on the STAGING engine ONLY (staging deploys from staging); PROD stays OFF until a later staging\u2192main release carries it \u2014 a natural staged soak. VISION-path live PASS/FAIL stays DEFERRED behind PREDICATE_BASIS_CONSENSUS (still default-OFF) regardless of this flag \u2014 vision-consensus is safe ONLY as abstain, not turn-on-ready (the P2 finding)."},ELEMENT_STATE_GROUNDING:{key:"ELEMENT_STATE_GROUNDING",envVars:["AGENTIQA_ELEMENT_STATE_GROUNDING","AGENTIQA_EXPERIMENT_ELEMENT_STATE_GROUNDING"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:'Trust-layer predicate basis \u2014 ELEMENT-STATE grounding (residual track P2, slice 1: BUILD + SHADOW; design docs/plans/2026-07-27-element-state-grounding-design.md). Closes a groundability gap in the DOM-verify path (routeDomVerify / domExtractionSource): the deterministic a11y-outline extraction grounds PRESENCE / ABSENCE / COUNT / MODIFICATION but STRIPS element STATE \u2014 a `- button "Submit" [disabled]` line\'s `[disabled]`, an `aria-checked` / `[selected]` / `[expanded]` marker, and an input\'s inline value \u2014 so an assertion like "the Submit button is enabled" is NOT DOM-groundable and falls through to flaky model-vision grading (the Lio submission s8 flake: a weak vision-only read of button-enabled that varied run-to-run). The a11y snapshot (Playwright _snapshotForAI) DETERMINISTICALLY carries this state; this track parses it into a new `state-of` observable + `element-state` predicate the Slice-A executor denotes over, so enabled/disabled/checked/selected/expanded/value assertions ground deterministically. FIREWALL PRESERVED: a state that is NOT resolvable from the a11y outline (target element not found, dimension not applicable to the role, mixed/unreadable value) ABSTAINS (Inconclusive) \u2014 never force-fails on unresolvable, never manufactures a pass; enforcement stays force-fail-only (only a grounded CONTRADICTION \u2192 would_fail gates). SLICE 1 = SHADOW / behavior-neutral even when ON: the machinery (extraction + NL parser + mapping + the flag-gated driver runElementStateGrounding) is wired into NO live RunnerRuntime verdict path \u2014 consumed only by unit tests (+ a later graduation benchmark). Off \u21D2 ZERO element-state extraction / parse / evaluate, byte-identical (the new observable/extraction runs only under this flag); the existing pbv DOM/count/absence/modification paths are UNTOUCHED. Graduation (benchmark + gate-review + Alex\'s default-on flip + live wiring) is a LATER slice, mirroring the pbv arc.',designDoc:"docs/plans/2026-07-27-element-state-grounding-design.md",status:"active",added:"2026-07-27",notes:"Residual track P2 (element-state grounding) slice 1 \u2014 BUILD + SHADOW behind a default-OFF killSwitch, mirroring the predicate-basis Slice A\u2013E / Slice-D CONSENSUS shadow precedent (pure machinery + flag-gated driver, wired into NO live verdict path). Read site: packages/engine-core/src/predicateBasis/elementState.ts (runElementStateGrounding \u2192 killSwitchEnabled('ELEMENT_STATE_GROUNDING'); zero element-state work when off). PURE machinery (also in elementState.ts, always safe to call, exercised in the unit suite): extractElementStatesFromAriaSnapshot (per-a11y-line state parse keyed on Playwright's exact role\u2192dimension applicability \u2014 kAriaDisabledRoles / kAriaCheckedRoles / kAriaSelectedRoles / kAriaExpandedRoles \u2014 so absence-of-marker on an applicable role reads as false, and a non-applicable role ABSTAINS rather than guessing), parseElementStateAssertion (deterministic NL \u2192 {ref, dimension, expected}; returns null when no state vocabulary is present so it is additive), elementStateQueryToLogicalForm (\u2192 the `element-state` node), and evaluateElementState (in executor.ts, the Slice-A generic executor's new case \u2014 resolves the target element in frames.after.elementStates, reads the dimension, denotes PASS on confirm / FAIL on a grounded contradiction / INCONCLUSIVE on any unresolvable \u2014 target absent, dimension N/A, mixed/ambiguous). GROUNDABLE STATES: enabled/disabled (fully both directions \u2014 the Lio s8 case), checked/unchecked, selected, expanded (PASS on [expanded] + FAIL on a collapsed-assertion contradiction; absence ABSTAINS \u2014 collapsed vs non-expandable is not distinguishable in the outline), and input value (normalized-equality; typed/locale-tolerant value comparison is a follow-up routing to compareTyped). Acceptance tests: packages/engine-core/src/__tests__/predicateBasisElementState.test.ts (extraction, parser, mapping, executor firewall, flag-OFF zero-work / byte-identical, registry-presence freshness canary). killSwitch resolves the no-env value from this defaultState (#1729) \u2014 GRADUATING = flip defaultState to 'on' AND add ELEMENT_STATE_GROUNDING to INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire); slice 1 stays OFF.",graduation:{status:"gated",gate:"Slice 1 is BUILD + SHADOW (pure machinery + a flag-gated driver, wired into NO verdict path). Graduation gates on the LATER slice: (1) an element-state graduation-benchmark run showing enabled/disabled/checked/selected/expanded/value ground deterministically from the a11y outline (a grounded contradiction \u2192 would_fail FIXES the Lio-s8-class vision flake; an unresolvable state \u2192 ABSTAIN, no false-PASS / no false-FAIL) and adversarially proven able to say NO-GO; (2) wiring runElementStateGrounding into the RunnerRuntime run_complete enforcement pass (force-fail-only, alongside applyPredicateBasisVerify); (3) the engine-core predicateBasisElementState unit suite green + flag-OFF byte-identical; then Alex flips the default-on (and lists it in INTENTIONALLY_GRADUATED). Mirrors the pbv graduation arc (shadow \u2192 benchmark \u2192 gate-review \u2192 default-on).",evidence:"e2e/benchmark element-state corpus (grounded PASS/FAIL vs ABSTAIN; Lio-s8 vision flake fixed; PENDING until the graduation slice) + the engine-core predicateBasisElementState unit suite + the (later) claim verify.element-state-grounding",owner:"steering (Alex)",review:"2026-08-15"}},VERIFY_CONFLICT_RECONCILE_SNAPSHOT:{key:"VERIFY_CONFLICT_RECONCILE_SNAPSHOT",envVars:["AGENTIQA_VERIFY_CONFLICT_RECONCILE_SNAPSHOT","AGENTIQA_EXPERIMENT_VERIFY_CONFLICT_RECONCILE_SNAPSHOT"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Verify-conflict DETERMINISTIC snapshot reconcile \u2014 M1 of the verify-oracle reconcile design (docs/plans/2026-07-28-verify-oracle-reconcile-design.md; GH #1907, root cause of #1813). WHAT IT DECIDES: at the SECOND verification-conflict escalation ONLY (the run_complete pass that would otherwise force-FAIL; the first bounce is untouched, and the bounce alone already resolves ~73% of oracle failures), the runtime takes ONE forced-full a11y snapshot for the whole run and re-checks each conflicted step's recorded wait literal (`_verifyOracleFailureDetails[step].literal`) against it with the SAME whole-token `pinPresentInPage` matcher, under the `waitLiteralHasSignificantTokens` significance floor. ZERO LLM calls \u2014 the model is not in the loop, so a lie about a genuinely broken wait fails constructively. THREE OUTCOMES: (a) literal significant AND present in the fresh snapshot \u2192 CLEARED: the oracle failure is removed from the ledger, the step's own already-passed criteria grades stand, the step passes, and the step is TAINTED via noteStepReopenedForReverify (the runtime consumed a re-observation it demanded, so predicate-basis-verify must not enforce over a basis it cannot attribute \u2014 the same invariant the bounce path carries); (b) literal significant and ABSENT from the fresh snapshot \u2192 GROUNDED NEGATIVE: the hard fail stands and the step is permanently INELIGIBLE for the VERIFY_CONFLICT_WITHHOLD warning band; (c) INAPPLICABLE \u2014 which is the only input VERIFY_CONFLICT_WITHHOLD (M2) ever sees \u2014 for no literal / insignificant literal / capture failed / budget already spent / the plan-derived `pin_page_grounding` oracle (excluded WHOLESALE \u2014 a plan-pin failure is a deterministic contradiction, not an absence of confirmation) / **any negation-or-absence marker on the step text or a criterion check** (`stepCarriesNegationOrAbsenceMarker`, reason `negation_marker`: a presence-clear is INVERTED for an assertion that the target is gone, and the absence lane's routing lexicon is narrow BY DESIGN, so this deliberately broader screen \u2014 not/no/none/without/missing/gone/empty/removed/deleted/closes/hidden/disappear*/vanish*/clear*/away/left-the-list/moves-to-trash/ceases-to/count-drops/zero-rows/fewer/struck-through/back-to-default/\u2026 plus a minimal DE/FR set, read over prose with only the target literal's OCCURRENCES removed (round-3: the earlier strip also deleted every \u22654-char TOKEN of the literal from the whole check, so a destination name like 'Deleted Items' silently disarmed the screen) \u2014 keeps every such step ineligible. HONEST LIMIT: this is a best-effort keyword screen over open-ended NL and completeness is unreachable; a phrasing it misses stays eligible and can be presence-cleared. The guarantee for absence assertions is the ABSENCE_AWARE_VERIFY lane, which decides with evidence rather than vocabulary) / **a literal that is not PLAN-GROUNDED whole-token with \u22652 tokens** (`waitLiteralGroundsSnapshotClear`, reason `literal_not_plan_grounded`: a CLEAR is only sound when the literal is the AUTHOR's expectation, read with the same `pinPresentInPage` matcher against the step text + criteria check/expectedValue \u2014 a model-invented literal that whole-token-matches unrelated chrome on another screen proves nothing, and a one-token literal is never distinctive enough even when the plan does contain it. KNOWN BOUNDARY, documented not fixed (round-3): this gate reads AUTHORSHIP, never PLACE \u2014 an AUTHORED literal that appears in unrelated chrome (a nav item, a help-sidebar link, the header of an Error 500 page) CLEARS exactly as it would in the region the step names; page-state and container binding are the next slice, and until then such clears are only visible in the `verify_conflict_reconcile:would_clear` census the graduation soak reads). Every inapplicable decision is logged per step as `verify_conflict_reconcile:inapplicable` with its reason. DETECTION ALWAYS RUNS (shadow): with the flag OFF the same decision is computed against the RETAINED full-snapshot corpus (`_fullSnapshotByStep`) \u2014 NO fresh capture, so OFF stays byte-identical including ZERO extra browser actions \u2014 and logged as `verify_conflict_reconcile:would_clear` / `:would_fail` with `basis:'retained_snapshot'` and `enforced:false`; ON logs the same events with `basis:'fresh_snapshot'` and `enforced:true`. Exactly ONE reconcile snapshot per run (guarded); a second escalation after the budget is spent decides INAPPLICABLE rather than re-enforcing off a stale page. NOT COVERED BY THIS FLAG (deferred slices, see the design \xA7 4): dropping `wait_for_element` from VERIFY_EVIDENCE_ACTIONS (own flag `VERIFY_EVIDENCE_REQUIRE_CAPTURE`, needs a shadow taint-rate measurement first), and still-running-vs-terminal recognition (a nested run still in flight at run end reads as a grounded negative and correctly stays red in this slice \u2014 the honest known gap). Runner lane only (RunnerRuntime run_complete).",designDoc:"docs/plans/2026-07-28-verify-oracle-reconcile-design.md",status:"active",added:"2026-07-28",notes:"M1 of the verify-oracle reconcile track (design docs/plans/2026-07-28-verify-oracle-reconcile-design.md), default OFF. Fixes the residual verify-conflict FALSE-FAIL that `canReconcileVerificationConflict` deliberately refuses: a PLAN-GROUNDED wait literal (repro run_5a6f0681 step 2, plan tp_vmlio_d1360115, literal 'QA Base \u2014 Draft Completion (do not modify)'; asess_1785247451027 step 1 is the same shape). That refusal is CORRECT as a static rule \u2014 a plan-grounded literal is a real must-pass presence oracle \u2014 so the fix is not to relax the rule but to consult the PAGE one more time: the element rendered late, and a fresh forced-full snapshot proves it. THREE FACTUAL CORRECTIONS to #1907 this design records (staging code, not the issue's reading): (a) evidence is NOT recorded on a timed-out oracle \u2014 the `_verifyEvidenceStepIndexes` add sits in the SUCCESS branch, so a timeout does not satisfy the evidence gate; (b) 'criteria are advisory' is too broad \u2014 `deriveStepStatusFromCriteria` does derive from grades; the real root is that the verify-conflict force-fail OVERRIDES already-passed criteria because `oracleConflictReason` never sees stepResults; (c) an existing valve (`canReconcileVerificationConflict`) already reconciles the non-plan-grounded case to a full PASS \u2014 the residual false-fail is exactly its plan-grounded refusal branch, not the whole mechanism. Read site: packages/engine-core/src/RunnerRuntime.ts (verifyConflictReconcileSnapshotEnabled \u2192 reconcileVerifyConflictsFromSnapshot, called from the second verification-conflict escalation in handleRunComplete). Acceptance tests: packages/engine-core/src/__tests__/RunnerRuntime.verifyConflictReconcile.test.ts (B-R1 repro form + B-C1..B-C9 counter-probes + the flag-OFF parity / one-snapshot-per-run invariants). killSwitch resolves the no-env value from this defaultState (#1729) \u2014 graduating means flipping defaultState to 'on' AND listing the key in INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts.",graduation:{status:"gated",gate:"Graduation gates on the LIVE harness, not on units: Chin's ci-first-plan `tp_c0dcd706` run \xD710 against staging with the flag ON \u2014 baseline is 0/10, the target is \u22658/10 on the step-9 mode (post-login redirect), with the cleared-vs-would_fail split reported from the `verify_conflict_reconcile:*` diags. The step-17 mode (nested run still in flight at run end) is EXPECTED to stay red in this slice; if it turns green the significance threshold is leaky and that is a bug to investigate, NOT evidence to graduate on. Plus: a staging shadow soak of `verify_conflict_reconcile:would_clear` / `:would_fail` sizing the cleared rate and confirming no would_clear fires on a genuinely-absent target, and the engine-core verifyConflictReconcile unit suite green with flag-OFF parity over the full runner corpus.",evidence:"staging `verify_conflict_reconcile:would_clear` / `:would_fail` diag events (cleared rate, basis, per-step literals) + the tp_c0dcd706 \xD710 harness pass rate per failure mode + packages/engine-core/src/__tests__/RunnerRuntime.verifyConflictReconcile.test.ts",owner:"steering (Alex)",review:"2026-08-15"}},VERIFY_CONFLICT_WITHHOLD:{key:"VERIFY_CONFLICT_WITHHOLD",envVars:["AGENTIQA_VERIFY_CONFLICT_WITHHOLD","AGENTIQA_EXPERIMENT_VERIFY_CONFLICT_WITHHOLD"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Verify-conflict RESIDUAL WARNING policy \u2014 M2 of the verify-oracle reconcile design (docs/plans/2026-07-28-verify-oracle-reconcile-design.md). Strictly downstream of VERIFY_CONFLICT_RECONCILE_SNAPSHOT (M1): it sees ONLY the conflicts M1 could not decide (no captured literal, an insignificant literal, a failed capture, or the reconcile budget already spent) and NEVER a step M1 read as a GROUNDED NEGATIVE \u2014 a target the runtime looked for and did not find always stays a hard FAIL. When ON, such a residual conflict is soft-withheld to a step-level WARNING ('could not be independently confirmed on this run') instead of force-FAILing the run. ALL SEVEN conditions must hold or the existing force-fail stands: (1) the unresolved oracle is a wait-style action, never the plan-derived `pin_page_grounding` oracle; (2) every graded criterion on the step passed with a non-empty substantiation note (the same bar `canReconcileVerificationConflict` uses) AND the step result is still 'passed' with no other deterministic negative on it (no reobserve-withhold, no upstream floor having already flipped it) ; (3) \u22651 STRICT criterion carries an engine-corroborated concrete value \u2014 its `observed` (or, absent that, its pinned `expectedValue`) is whole-token present in the retained page corpus via `pinPresentInPage`; (4) the wait literal's significant tokens are corroborated by the `observed`/note corpus of some passed criterion (this is what links 'what we waited for' to 'what was confirmed', and it is why a short/frequent literal like 'ok' or '3' can never withhold \u2014 the significance floor rejects it); (5) the step is NOT absence-intent (`checkTextAssertsAbsence` \u2014 that shape belongs to ABSENCE_AWARE_VERIFY, which owns its own confirm/abstain ladder) and, since that lexicon is narrow by design, ALSO carries no negation-or-absence marker at all under the broader M1 screen (`stepCarriesNegationOrAbsenceMarker`, reason `negation_marker`) \u2014 a warning there would withdraw a force-fail the engine has no evidence to withdraw; (6) \u2014 folded into (2) \u2014 no other deterministic negative on the step; (7) the step was OBSERVED AGAIN after the conflict bounce (a runtime capture-sequence watermark taken at the bounce; a verbatim resubmit of pre-bounce grades never withholds). DETECTION ALWAYS RUNS (shadow): `verify_conflict_withhold:would_withhold` (and a reasoned `:inapplicable`) is logged with `enforced:` reflecting the flag, so OFF is byte-identical with telemetry only. KNOWN, DELIBERATE SIDE EFFECTS when enforcing (asserted in CI, not accidents): withdrawing the forced conflict fail leaves `terminationReason='completed'`, which makes the run BILLABLE; and no 'Potential Issue Detected' card is synthesized for a withheld step \u2014 one fewer false issue. Runner lane only (RunnerRuntime run_complete).",designDoc:"docs/plans/2026-07-28-verify-oracle-reconcile-design.md",status:"active",added:"2026-07-28",notes:"M2 of the verify-oracle reconcile track, default OFF and inert unless M1's decision for the step is INAPPLICABLE. The honesty floor is the point: a withheld step is a WARNING \u2014 never a pass, never a fail \u2014 so this flag can only ever move a run out of a false RED into an honest AMBER, and it can never manufacture a green. The grounded-negative exclusion is the hard invariant (design \xA7 3): M1 having actually read the page and not found the target is exactly the evidence that the step is genuinely broken, so those never enter this path. Measured coverage of conditions (2)-(4) on the design's corpus is 32/33 steps; the one exception (staging ck-cli_test 2026-07-28 step 17, a genuinely failed criterion) correctly stays red. Read site: packages/engine-core/src/RunnerRuntime.ts (verifyConflictWithholdEnabled \u2192 classifyVerifyConflictWithhold, applied at the second verification-conflict escalation after the M1 pass). Acceptance tests: packages/engine-core/src/__tests__/RunnerRuntime.verifyConflictReconcile.test.ts (B-C2/B-C3/B-C5/B-C6 keep the reds red; B-C8 asserts the billing invariant deliberately). killSwitch resolves the no-env value from this defaultState (#1729) \u2014 graduating means flipping defaultState to 'on' AND listing the key in INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts.",graduation:{status:"gated",gate:"Graduation gates on M1 graduating FIRST (M2 is only meaningful over M1's inapplicable residue), then on a staging shadow soak of `verify_conflict_withhold:would_withhold` showing (a) no would_withhold on a step any deterministic oracle reads as a negative, (b) the withheld population is dominated by genuinely unconfirmable captures rather than real failures \u2014 sampled and hand-adjudicated \u2014 and (c) an explicit product decision on the billing consequence (withdrawing the forced fail makes the run terminate 'completed' and therefore BILLABLE) plus the warning-display semantics review (a withheld step must read as a neutral 'could not confirm', not as an alarm-amber defect).",evidence:"staging `verify_conflict_withhold:would_withhold` / `:inapplicable` diag events (rate + hand-adjudicated sample of the withheld population) + the tp_c0dcd706 harness residue after M1 + packages/engine-core/src/__tests__/RunnerRuntime.verifyConflictReconcile.test.ts",owner:"steering (Alex)",review:"2026-08-15"}},RUN_COMPLETENESS_ALL_STATUSES:{key:"RUN_COMPLETENESS_ALL_STATUSES",envVars:["AGENTIQA_RUN_COMPLETENESS_ALL_STATUSES","AGENTIQA_EXPERIMENT_RUN_COMPLETENESS_ALL_STATUSES"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Run-completeness guard for NON-passed claims. The guard (cascade stage 15: bounce-once, then fail-closed with placeholder rows and a summary prefix) was gated on `status === 'passed'`, so a `run_complete{status:'failed'}` bypassed it ENTIRELY \u2014 the model could declare a run failed part-way and every unexecuted step was silently absent from the verdict. When ON the guard evaluates on EVERY claimed terminal status, and the fail-closed branch additionally discloses the FULL unexecuted-step set (every plan step with no reported result, not just the `required` verify subset the bounce keys on) as `skipped`+`synthesized` rows plus a coverage clause in the run summary. It cannot launder a failure into a pass (the only status write in this branch is \u2192 `failed`) and it cannot invent a fail on a complete run (it is unreachable when every required step is reported). OFF restores the byte-identical pre-change behavior: guard on `passed` only, no coverage clause, no unexecuted rows.",designDoc:"docs/plans/2026-08-04-run-completeness-guard-all-statuses-design.md",status:"active",added:"2026-08-04",notes:"Ships default-ON from inception \u2014 there is no pre-change OFF behavior worth preserving: the gated-on-`passed` condition is a pure false-signal + coverage-loss defect measured at prod parity on the lio mirror (2026-08-03, 46 runs, https://s.assets.agentiqa.com/lio-prod-parity-2026-08-03/SCORE.md finding 1). Repro run `run_04a6292b-f968-47e5-a1ee-4013f26afce1` (plan tp_2d051c76 `workflow-approval`, session asess_1785788803738_bf4wmgv2) booked `failed` with 10 of 29 steps in step_results, 7/7 criteria passed / 0 failed / 0 never-graded, and the client-visible summary was the model's own mid-run narration \"Test run in progress. Step 10 verified.\" The same gate is live on the prod build, so lio's nightly CLI batch can hit it. Direction is strictly information-adding: the run status can only move `passed`\u2192`failed` (unchanged from the pre-change branch) and the new rows are `skipped` \u2014 never a pass, never a fail, the same trust-layer rule as `synthesizePartialStepResults`. Read site: packages/engine-core/src/RunnerRuntime.ts (`runCompletenessAllStatusesEnabled` \u2192 the stage-15 guard in handleRunComplete; `getUnexecutedStepIndexes` computes the disclosed set). Acceptance tests: packages/engine-core/src/__tests__/RunnerRuntime.runCompletenessAllStatuses.test.ts (fixture built from the measured run). killSwitch resolves the no-env value from this defaultState (#1729) \u2014 this entry is listed in INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts so the no-delta proof acknowledges the default-ON state deliberately; an explicit AGENTIQA_RUN_COMPLETENESS_ALL_STATUSES=0 is the emergency kill switch that restores the bypass."},ZERO_GRADE_VERIFY_BOUNCE:{key:"ZERO_GRADE_VERIFY_BOUNCE",envVars:["AGENTIQA_ZERO_GRADE_VERIFY_BOUNCE","AGENTIQA_EXPERIMENT_ZERO_GRADE_VERIFY_BOUNCE"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Zero-grade verify bounce (cascade stage 12c). The whole-step zero-grade floor (`run_complete:verify_step_unsubstantiated`) refuses a verify step reported `passed` with NONE of its plan-printed criteria graded \u2014 correctly \u2014 but until this flag it did so SILENTLY, deriving `failed` (strict) / `warning` (non-strict) with no chance to correct, the only sibling gate without the one-bounce self-correction contract. When ON the first offending `run_complete` is rejected once (`run_complete:verify_step_unsubstantiated_rejected`, response status `ungraded_verify_criteria`) naming each offending step and its full printed criteria range 1..N; a repeat offense takes the floor's derivation exactly as before, with a de-jargonized client note recording that a resubmission was already asked for. The trigger keys on the POST-synthetics `unsubstantiatedVerifySteps` collection, so a step the FIX 3 cross-step rescue or Amendment 5a fail-closed synthesis already adjudicated never bounces. The bounce writes no step or run status and mutates nothing. OFF restores the byte-identical pre-change behavior: immediate silent derivation with the original note/summary text.",designDoc:"docs/plans/2026-08-06-lio-terminal-verdicts-and-zero-grade-bounce-design.md",status:"active",added:"2026-08-06",notes:"Ships default-ON from inception (precedent: RUN_COMPLETENESS_ALL_STATUSES \u2014 the bounce writes no status, so there is no verdict-polarity change to soak; `=0` restores today's silent derivation byte-identically). Measured trigger: Lio's first post-#2238 prod nightly (2026-08-06, 15 runs, proj_b6424373, gemini-3-flash-preview) \u2014 3/15 runs whose substance passed were reported FAILED because the model submitted step 1 `passed` with `criteriaResults: []` and the floor derived `failed` from the strict criterion, vs 0/30 on the 08-04/08-05 pre-#2238 baseline; the #2238 exclusion counters (unauthored/step-mismatch/out-of-range) were all zero, i.e. the typed reference did not drop the grades \u2014 the model stopped emitting them. Bounce doctrine (one bounce per class per run, then fail-closed, NEVER a rescue/rewrite) is the `_unauthoredGradeRunCompleteRejections` doctrine; worst-case chain incomplete \u2192 zero-grade \u2192 over-grading \u2192 conflict is ~4 extra run_complete turns, the same accepted profile as the pre-existing bounces. Read site: packages/engine-core/src/RunnerRuntime.ts (`zeroGradeVerifyBounceEnabled` \u2192 the stage-12c bounce in handleRunComplete; the counter is `_zeroGradeVerifyRejections`). Acceptance tests: packages/engine-core/src/__tests__/RunnerRuntime.zeroGradeVerifyBounce.test.ts. killSwitch resolves the no-env value from this defaultState (#1729) \u2014 this entry is listed in INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts so the no-delta proof acknowledges the default-ON state deliberately; an explicit AGENTIQA_ZERO_GRADE_VERIFY_BOUNCE=0 is the emergency kill switch. Known residual carried, not fixed (AG-8153): the floor and this bounce cover `type === 'verify'` steps only \u2014 zero-graded strict criteria on action/setup steps remain a false-PASS class."}},lde=Object.values(Af);var _1=["run","run the test","run the test plan","run test plan"];function fw(t){let e=t.toLowerCase().trim();return _1.includes(e)}function mw(t){return t.replace(/\s+/g," ").trim()}var il="plan_run_active";var Us="PROJECT_RUN_IN_PROGRESS";function Rf(t){return`This project already has a ${t.kind==="batch"?"batch":"run"} in progress, started by ${t.startedByLabel} at ${t.startedAt}. Only one run or batch can be active per project at a time \u2014 wait for it to finish, or stop it first.`}function vi(t,e){if(t!==409)return null;let n;try{n=JSON.parse(e)}catch{return null}return!n||n.code!==Us?null:{error:typeof n.error=="string"?n.error:"",code:Us,runId:typeof n.runId=="string"?n.runId:"",kind:n.kind==="batch"?"batch":"run",startedAt:typeof n.startedAt=="string"?n.startedAt:"",startedByLabel:typeof n.startedByLabel=="string"?n.startedByLabel:""}}var ds=class extends Error{reason=Us;holderRunId;holderKind;startedAt;startedByLabel;body;constructor(e){super(e.error.trim()||Rf({kind:e.kind,startedByLabel:e.startedByLabel,startedAt:e.startedAt})),this.name="ProjectRunInProgressError",this.holderRunId=e.runId,this.holderKind=e.kind,this.startedAt=e.startedAt,this.startedByLabel=e.startedByLabel,this.body=e}};function Id(t){if(t instanceof ds)return t;if(!t||typeof t!="object")return null;let e=t;if(e.reason===Us){let n=e.body;if(n&&n.code===Us)return new ds(n)}return e.code!==Us?null:new ds({error:typeof e.message=="string"?e.message:"",code:Us,runId:"",kind:"run",startedAt:"",startedByLabel:""})}function Fs(t){let e=t.indexOf(":");return e===-1?{provider:"google",modelName:t}:{provider:t.slice(0,e),modelName:t.slice(e+1)}}var or="google:gemini-3-flash-preview",gw="google:gemini-3-flash-preview",us=["credential_identifier","user_supplied_payload","generated_test_value","other"];var Ad=[{name:"Green",hex:"#4ade80"},{name:"Blue",hex:"#60a5fa"},{name:"Purple",hex:"#a78bfa"},{name:"Amber",hex:"#fbbf24"},{name:"Red",hex:"#f87171"},{name:"Cyan",hex:"#22d3ee"},{name:"Pink",hex:"#f472b6"},{name:"Slate",hex:"#94a3b8"}];function ve(t){return`${t}_${crypto.randomUUID()}`}function ol(t,e){return typeof process>"u"||!process.env?!1:process.env[`AGENTIQA_${t}`]===e||process.env[`AGENTIQA_EXPERIMENT_${t}`]===e}function yw(t){return Af[t]?.defaultState}function ke(t){return ol(t,"0")?!0:ol(t,"1")?!1:yw(t)==="off"}function Ge(t){return ol(t,"1")?!0:ol(t,"0")?!1:yw(t)==="on"}function vw(t){return ol(t,"0")}function al(){return Ge("VALUE_GROUNDING_ENFORCE")}function xf(t){return`${t}_${Date.now()}_${Math.random().toString(36).slice(2,9)}`}function bw(t){switch(t){case"message":return"msg";case"tool_call":return"tool";case"llm_usage":return"llm";case"supervisor_verdict":return"sv";case"agent_lifecycle":return"lc";case"user_action":return"ua";case"session_start":case"session_end":case"turn_start":case"turn_end":return"sl";case"log":case"pageSnapshot.structuralStrip":return"diag";default:return"evt"}}var _o=class{apiUrl;fetchFn;sessions=new Map;queues=new Map;timer=null;isUploading=!1;inFlight=new Set;eventIds=new WeakMap;directUploadFailedSessions=new Set;BATCH_SIZE=25;FLUSH_INTERVAL=6e4;MAX_PAYLOAD_BYTES=35e5;auth;constructor(e,n,r=globalThis.fetch.bind(globalThis)){this.apiUrl=e,this.fetchFn=r,this.auth=typeof n=="string"?{kind:"bearer",token:n}:n,this.timer=setInterval(()=>this.flushAll(),this.FLUSH_INTERVAL)}buildAuthHeaders(e,n=!1){let r={"Content-Type":"application/json"};if(this.auth.kind==="bearer")return r.Authorization=`Bearer ${this.auth.token}`,r;if(n&&this.auth.bearerFallback)return r.Authorization=`Bearer ${this.auth.bearerFallback}`,r;r["x-admin-service-key"]=this.auth.serviceKey;let s=e?.userId??this.auth.fallbackUserId;return s&&(r["x-user-id"]=s),r}hasBearerFallback(){return this.auth.kind==="service"&&!!this.auth.bearerFallback}emit(e){let n=e.sessionId;if(this.eventIds.has(e)||this.eventIds.set(e,xf(bw(e.kind))),e.kind==="session_start"&&e.sessionMeta&&this.sessions.set(n,{...e.sessionMeta,desktopSessionId:n,status:"active",startedAt:new Date(e.ts).toISOString()}),e.kind==="session_end"){let s=this.sessions.get(n);s&&(s.status=e.status??"completed",s.endedAt=new Date(e.ts).toISOString())}if(this.isProviderLocationUnsupportedEvent(e)){let s=this.sessions.get(n);s&&(s.terminalErrorClass="provider_location_unsupported")}!n&&!this.sessions.has("")&&this.sessions.set("",{desktopSessionId:"global",projectId:"_global",status:"active",startedAt:new Date(e.ts).toISOString()});let r=this.queues.get(n);r||(r=[],this.queues.set(n,r)),r.push(e),r.length>=this.BATCH_SIZE&&this.flushSession(n),e.kind==="session_end"&&this.flushSession(n)}async flush(){await this.flushAll()}destroy(){this.timer&&(clearInterval(this.timer),this.timer=null),this.flushAll()}async flushAll(){let e=[];for(let r of this.queues.keys()){let s=this.flushSession(r);s&&e.push(s)}let n=Array.from(this.inFlight);await Promise.allSettled([...e,...n])}flushSession(e){let n=this.sessions.get(e),r=this.queues.get(e);if(!n||!r||r.length===0)return null;let s=r.splice(0),i=this.uploadWithRetry(n,s).catch(o=>{let a=s.filter(l=>l.kind==="session_end");if(a.length>0){let l=this.queues.get(n.desktopSessionId);l&&l.unshift(...a)}console.error(`[RemoteAnalyticsSink] Failed to upload ${s.length} events:`,o.message)});return this.inFlight.add(i),i.finally(()=>{this.inFlight.delete(i)}),i}async uploadWithRetry(e,n,r=3){let s;for(let i=1;i<=r;i++)try{await this.upload(e,n);return}catch(o){s=o;let a=o?.status;if(a!==void 0&&a>=400&&a<500)throw o;if(i<r){let l=Math.min(1e3*Math.pow(3,i-1),9e3);await new Promise(c=>setTimeout(c,l))}}throw s}async upload(e,n){let r=await this.mapEvents(e.desktopSessionId,n);await this.postIngest(e,r)}async postIngest(e,n,r=!1){if(n.length===0)return;let s=JSON.stringify({session:{...e},events:n});if(s.length>this.MAX_PAYLOAD_BYTES&&n.length>1){let l=Math.floor(n.length/2);await this.postIngest(e,n.slice(0,l),r),await this.postIngest(e,n.slice(l),r);return}let i;try{i=await this.fetchFn(`${this.apiUrl}/api/analytics/ingest`,{method:"POST",headers:this.buildAuthHeaders(e,r),body:s})}catch(l){throw new Error(`analytics upload network error: ${l?.message??String(l)}`)}if(i.ok)return;if((i.status===401||i.status===403)&&!r&&this.hasBearerFallback()){console.warn(`[RemoteAnalyticsSink] service-key auth got ${i.status} for session ${e.desktopSessionId} \u2014 retrying with bearer fallback (AG-169)`),await this.postIngest(e,n,!0);return}if(i.status===413){if(n.length>1){let l=Math.floor(n.length/2);await this.postIngest(e,n.slice(0,l),r),await this.postIngest(e,n.slice(l),r);return}console.warn(`[RemoteAnalyticsSink] Dropping single oversized event (${Math.round(s.length/1024)} KB)`);return}let o=await i.text().catch(()=>`HTTP ${i.status}`);if(o.includes("FUNCTION_PAYLOAD_TOO_LARGE")||o.includes("Request Entity Too Large")){if(n.length>1){let l=Math.floor(n.length/2);await this.postIngest(e,n.slice(0,l),r),await this.postIngest(e,n.slice(l),r);return}console.warn(`[RemoteAnalyticsSink] Dropping single oversized event (${Math.round(s.length/1024)} KB)`);return}(i.status===401||i.status===403)&&console.error(`[RemoteAnalyticsSink] auth_failed status=${i.status} authKind=${this.auth.kind} sessionId=${e.desktopSessionId} userId=${e.userId??"(unset)"} body=${o.slice(0,200)}`);let a=new Error(o);throw a.status=i.status,a}async mapEvents(e,n){let r=[];for(let s of n){let i={timestamp:new Date(s.ts).toISOString(),childId:mo(s.childId)},o=this.eventIds.get(s)??xf(bw(s.kind));switch(s.kind){case"message":r.push({...i,id:o,eventType:"message",role:s.role,messageText:s.text,toolName:s.actionName,toolArgs:s.actionArgs,url:s.url});break;case"tool_call":{let a;s.screenshotBase64&&(a=await this.uploadScreenshot(e,s.screenshotBase64)),r.push({...i,id:o,eventType:"tool_call",toolName:s.toolName,toolArgs:s.args,toolResult:s.result,screenshotUrl:a,url:s.url,stepIndex:s.stepIndex,actionMetadata:{durationMs:s.durationMs,tokenCount:s.tokenCount}});break}case"llm_usage":r.push({...i,id:o,eventType:"llm_usage",toolName:s.model,promptTokens:s.promptTokens,completionTokens:s.completionTokens,totalTokens:s.totalTokens,runId:s.runId,callKind:s.callKind,keySource:s.keySource,llmProvider:s.llmProvider,billedUnits:s.billedUnits,actionMetadata:{durationMs:s.durationMs,finishReason:s.finishReason,tokenCount:s.tokenCount,messageCount:s.messageCount,systemPromptHash:s.systemPromptHash,lastToolResults:s.lastToolResults,chosenActions:s.chosenActions,textResponse:s.textResponse,cachedInputTokens:s.cachedInputTokens,planStepIndex:s.planStepIndex,planStepType:s.planStepType}});break;case"supervisor_verdict":r.push({...i,id:o,eventType:"supervisor_verdict",actionType:s.verdict,actionMetadata:{verdict:s.verdict,message:s.message,iteration:s.iteration,actionLogSize:s.actionLogSize,stepText:s.stepText,progress:s.progress??null,differential:s.differential??null}});break;case"agent_lifecycle":r.push({...i,id:o,eventType:"agent_lifecycle",actionType:s.event,actionMetadata:{event:s.event,iteration:s.iteration,details:s.details}});break;case"user_action":r.push({...i,id:o,eventType:"user_action",actionType:s.action,actionTargetId:s.targetId,actionMetadata:s.metadata});break;case"session_start":case"session_end":case"turn_start":case"turn_end":r.push({...i,id:o,eventType:"user_action",actionType:s.kind,actionTargetId:s.sessionId,actionMetadata:s.sessionMeta?{...s.sessionMeta}:{status:s.status,...s.kind==="session_end"&&s.endKind?{endKind:s.endKind}:{}}});break;case"log":r.push({...i,id:o,eventType:"diagnostic",actionType:s.level,actionMetadata:{source:s.source,msg:s.msg,...s.data}});break;case"pageSnapshot.structuralStrip":r.push({...i,id:o,eventType:"diagnostic",actionType:s.kind,actionMetadata:{originalLen:s.originalLen,finalLen:s.finalLen,droppedNodeCount:s.droppedNodeCount,foldedRunCount:s.foldedRunCount,capHit:s.capHit}});break;default:{console.warn(`[RemoteAnalyticsSink] dropping unmapped DiagnosticEvent kind=${s.kind}`);break}}}return r}isProviderLocationUnsupportedEvent(e){return e.kind!=="log"?!1:Dr([e.msg,e.source,typeof e.data=="object"&&e.data?JSON.stringify(e.data):""].join(`
|
|
11
|
+
`),h=Ed(f,i),m=Ed(p,i),g=!1;for(let y of n){let E=bn(y);u.has(E)||h.has(E)?(o.add(E),g=!0):m.has(E)&&(a.add(E),g=!0)}if(!g&&u.size===0)for(let y of n)a.add(bn(y))}let l=n.filter(d=>o.has(bn(d))),c=n.filter(d=>!o.has(bn(d))&&a.has(bn(d)));return{required:l,optional:c}}function b1(t){let e=t;return!e||e.code!==vn?null:Array.isArray(e.names)?e.names.filter(n=>typeof n=="string"):[]}async function hw(t){let e=t.scope?.required??[],n=t.scope?.optional??[],r=t.maxAttempts??3,s=new Set(n.map(l=>bn(l))),i=new Set(e.map(l=>bn(l))),o=l=>{let c=new Set(l.map(d=>bn(d.name)));return n.filter(d=>!c.has(bn(d)))},a=vo([...e,...n],[]);for(let l=0;l<r;l++){if(a.length===0)return e.length>0?{ok:!1,refused:[...e]}:{ok:!0,granted:[],skipped:[...n]};try{let c=a.filter(u=>s.has(bn(u))&&!i.has(bn(u))),d=await t.lease(a,c);return{ok:!0,granted:d,skipped:o(d)}}catch(c){let d=b1(c);if(d===null)throw c;let u=new Set(d.map(h=>bn(h))),f=e.filter(h=>u.has(bn(h)));if(f.length>0)return{ok:!1,refused:f};let p=a.filter(h=>!u.has(bn(h)));if(p.length===a.length)return e.length>0?{ok:!1,refused:[...e]}:{ok:!0,granted:[],skipped:[...n]};a=p}}return e.length>0?{ok:!1,refused:[...e]}:{ok:!0,granted:[],skipped:[...n]}}var Af={GROUNDED_EXPECTATIONS:{key:"GROUNDED_EXPECTATIONS",envVars:["AGENTIQA_GROUNDED_EXPECTATIONS","AGENTIQA_EXPERIMENT_GROUNDED_EXPECTATIONS"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"When on, run_complete batch-judges provisionally-passing steps for expectation drift (heal cosmetic, fail-closed critical/judge-unavailable) instead of trusting the model grade verbatim.",designDoc:"docs/plans/2026-07-07-grounded-expectations-phase-b-design.md",status:"active",added:"2026-07-07",notes:"GRADUATED 2026-07-21 (default on): prod ran env-ON (AGENTIQA_EXPERIMENT_GROUNDED_EXPECTATIONS=1 on the orchestrator) since 2026-07-10 with zero grounded-attributable false-FAIL; nightly regressions in the window were all infra/render flake. This flip normalizes code to the already-live prod behavior \u2014 remove the orchestrator env vars (both namespaces) once this reaches each environment."},LOOP_VISION_ESCALATION_CHAT:{key:"LOOP_VISION_ESCALATION_CHAT",envVars:["AGENTIQA_LOOP_VISION_ESCALATION_CHAT","AGENTIQA_EXPERIMENT_LOOP_VISION_ESCALATION_CHAT"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Extends loop-detection vision-supervisor escalation to the Coordinator/chat lane; off disables it for chat only (the master LOOP_VISION_ESCALATION still governs the Runner/Explorer lanes).",designDoc:"docs/plans/2026-07-07-loop-detection-vision-supervisor-design.md",status:"active",added:"2026-07-07",notes:"Staged default-OFF originally, flipped default-ON once baked (AG-6995) \u2014 reads via killSwitchDisabled today."},PIN_PAGE_GROUNDING:{key:"PIN_PAGE_GROUNDING",envVars:["AGENTIQA_PIN_PAGE_GROUNDING","AGENTIQA_EXPERIMENT_PIN_PAGE_GROUNDING"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Runner-lane pin page-grounding: a criterion's pinned expectedValue must appear in the literal full-page a11y text at verify-evidence capture (parroted grade notes no longer suffice). Detection runs in shadow (telemetry) unless VALUE_GROUNDING_ENFORCE is on; '0' disables detection AND the forced full-snapshot capture entirely.",designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-07-10"},ISSUE_QUOTE_GROUNDING:{key:"ISSUE_QUOTE_GROUNDING",envVars:["AGENTIQA_ISSUE_QUOTE_GROUNDING","AGENTIQA_EXPERIMENT_ISSUE_QUOTE_GROUNDING"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Chat-lane hallucinated-quote gate on report_issue: a quoted literal asserted as visible must appear in the freshly captured full a11y snapshot. Detection runs in shadow (telemetry) unless VALUE_GROUNDING_ENFORCE is on; '0' disables detection AND the forced full-snapshot capture entirely.",designDoc:"packages/engine-core/src/negativeStateEvidence.ts",status:"active",added:"2026-07-10"},VALUE_GROUNDING_ENFORCE:{key:"VALUE_GROUNDING_ENFORCE",envVars:["AGENTIQA_VALUE_GROUNDING_ENFORCE","AGENTIQA_EXPERIMENT_VALUE_GROUNDING_ENFORCE"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Flips both value-grounding gates (PIN_PAGE_GROUNDING, ISSUE_QUOTE_GROUNDING) from shadow telemetry (would_fail / would_bounce diag logs) to enforcement: pin absence records a real per-step oracle failure that fails run_complete; a hallucinated visible quote rejects the report_issue filing.",designDoc:"packages/engine-core/src/killSwitch.ts",status:"active",added:"2026-07-10",notes:"Shadow-first rollout: two adversarial review rounds each surfaced false-fail classes on a hard-fail gate, so enforcement waited on a staging soak. PARKED 2026-07-21 (steering decision, Alex): VERIFY_REOBSERVE_WITHHOLD (graduated default-ON) strictly dominates this hard-fail path \u2014 it closes the same blind-verdict/pin-absence hole with a soft-withhold that never manufactures the false-FAIL classes both review rounds surfaced. Never graduate the enforcement; the oracle_failure recording branch is a deletion candidate. Detection stays: PIN_PAGE_GROUNDING + ISSUE_QUOTE_GROUNDING (default-ON) keep their shadow would_fail/would_bounce telemetry. Remove the staging orchestrator env var (AGENTIQA_EXPERIMENT_VALUE_GROUNDING_ENFORCE) \u2014 the force-ON soak is moot.",graduation:{status:"parked",gate:"Retired in favor of VERIFY_REOBSERVE_WITHHOLD (soft-withhold successor). Do not graduate; delete the enforcement branch once the successor has a clean prod window.",evidence:"F2b eval proof: enforce cannot catch the abstain{snapshot_incremental} class (identical RED both legs); panel + review-round history of false-fail classes on the hard-fail path",owner:"steering (Alex)",review:"2026-08-15"}},RESUME_INPUT_ASK_USER:{key:"RESUME_INPUT_ASK_USER",envVars:["AGENTIQA_RESUME_INPUT_ASK_USER","AGENTIQA_EXPERIMENT_RESUME_INPUT_ASK_USER"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Coordinator turn-continuity: when on, a child Explorer's ask_user that blocks on a MISSING INPUT (a file to upload, a path/value \u2014 not an email/generic async wait) arms a resumable `input_wait` pause that persists the halted child's OBJECTIVE; the next user turn resumes that SAME objective (re-attaching session attachments) instead of falling through to free re-decomposition, and re-asks for the SAME objective when the required file is still absent. Off = byte-identical to today (missing-input ask_user arms no pause; the email-wait / generic-wait paths are unchanged).",designDoc:"docs/plans/2026-07-20-paused-task-resume-design.md",status:"active",added:"2026-07-20",notes:"GRADUATED 2026-07-21 (default on) via the bound-eval arm of its gate: chat/paused-task-resume green 2/2 replicates on origin/staging (pause armed on the missing-file ask_user; resume carried the SAME objective tokens (upload + filename) with an explicit no-re-plan prompt; text-only reply correctly re-asked for the same file) + the earlier recorded GRADUATION-PASS on the lio replay (asess_1784568697674). Organic staging soak was vacuous (organic chat never hits a missing-input upload ask_user \u2014 0 input_wait events in 12 sessions), so the eval arm is the gate per the evidence-count doctrine. Renderer attachment-drop discriminator stays a SEPARATE open item (PostHog coordinator_started.has_attachments). Remove the staging orchestrator env var once this reaches staging."},VERIFY_REOBSERVE_WITHHOLD:{key:"VERIFY_REOBSERVE_WITHHOLD",envVars:["AGENTIQA_VERIFY_REOBSERVE_WITHHOLD","AGENTIQA_EXPERIMENT_VERIFY_REOBSERVE_WITHHOLD"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Runner-lane grade-time re-observe + soft-withhold (batch.verify-never-blind fix). When a pinned STRICT verify criterion cannot be grounded at grade time \u2014 the snapshot is not full (abstain snapshot_incremental/missing/thin) or the value is absent from a full a11y snapshot (would_fail) \u2014 the runtime takes ONE fresh forced-full a11y re-capture and re-grounds before accepting the verdict. Groundable after re-observe (value was present but the grade-time capture was imageless/incremental) \u2192 the model verdict stands; still ungroundable \u2192 the step is SOFT-withheld to a 'warning'+note (NON-confident, never a hard fail, never routed through the VALUE_GROUNDING_ENFORCE oracle_failure path). Closes the blind-verdict hole (F2a canvas + F2b incremental) WITHOUT manufacturing false-FAILs on values present-to-user but absent from the a11y outline (virtualized/scrolled-off rows).",designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-07-21",notes:"GRADUATED 2026-07-21 (default on), same-day evidence gate approved by Alex in lieu of a calendar soak: (1) staging replay-soak on the lio fixture (proj_mqzn31900, 3 sequential replicates of the only expectedValue-pinned plan) \u2014 zero false-withholds across 6 present-pin observations, withholds only on genuinely-absent values, surgical parity (grounded and no-pin verdicts untouched, check-only plans fully inert), no failing run laundered to pass, no material latency delta; (2) the grounded-rescue + Regression A/B legs banked by the fix-gate eval run (F2b re-observe\u2192grounded\u2192pass, healthy batch zero-fire, below-fold DOM value grounds). Product semantics approved by Alex: pinned-strict TRUE-MISMATCH fails also downgrade to warning (observed-value note preserved) until typed-match (AG-7753) restores precise typed fails. The two evals (runner-verify-blind-canvas / -incremental) remain the regression gate: fix OFF must reproduce, ON must resolve. Independent of VALUE_GROUNDING_ENFORCE \u2014 when off, that flag behaves unchanged. Remove the staging orchestrator env var once this reaches staging."},BLOCKED_RUN_DRAFT_PLAN:{key:"BLOCKED_RUN_DRAFT_PLAN",envVars:["AGENTIQA_BLOCKED_RUN_DRAFT_PLAN","AGENTIQA_EXPERIMENT_BLOCKED_RUN_DRAFT_PLAN"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'A loop-blocked Explorer child keeps its work: when the loop detector stops a NON-discovery child before it could file assistant_v2_report, the fallback result carries a reconstructed `draftTestCase` built from the natural-language stepText/intent of the interactions it actually performed (observation-only tools excluded; any step text repeated 3+ times \u2014 the stuck target the detector fired on \u2014 dropped whole; capped at 30 steps; suppressed under 2 steps). Without it the Coordinator emits `tested_areas[].draft_steps: []`, FindingsCheckpointCard filters the area out of "What was just tested", and the product offers NO way to save a run that got blocked (issue #2024: the self-agent signup\u2192first-run journey dead-ended at "Save the completed run as a new test case" for two consecutive nights). Off restores the pre-#2024 behaviour \u2014 a blocked child reports no plan and the run is unsaveable. Verdicts are untouched either way: the run stays blocked, the recommendation stays do_not_ship, and the area is surfaced as partial with its not-tested reason by the existing incomplete-child coverage backfill.',designDoc:"packages/engine-core/src/ExplorerRuntime.ts",status:"active",added:"2026-08-02"},VERIFY_GATED_DONE:{key:"VERIFY_GATED_DONE",envVars:["AGENTIQA_VERIFY_GATED_DONE","AGENTIQA_EXPERIMENT_VERIFY_GATED_DONE"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Cross-checks a 'done'/'passed' claim against deterministic completion-time oracle failures (wait-style); off restores trusting the model's completion claim verbatim.",designDoc:"docs/plans/2026-07-06-verification-gated-done-design.md",status:"active",added:"2026-07-06"},VERIFY_CONFLICT_RECONCILE:{key:"VERIFY_CONFLICT_RECONCILE",envVars:["AGENTIQA_VERIFY_CONFLICT_RECONCILE","AGENTIQA_EXPERIMENT_VERIFY_CONFLICT_RECONCILE"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'Downgrades a verification-conflict force-FAIL to a step-level warning (run not failed) when every plan criterion on the conflicted step passed with a substantiated note AND the only unresolved oracle is an agent-invented wait literal absent from the plan; off restores the unconditional bounce-then-fail-closed (false-negative on incident asess_1784155719395_h62evpmp). Since 2026-08-01 this switch is the UMBRELLA kill for the whole agent-invented-literal family: =0 also disables VERIFY_AGENT_LITERAL_ABSTAIN and VERIFY_AGENT_LITERAL_ABSTAIN_ACTION_SHAPED, so the documented "restores the unconditional force-fail" promise stays true.',designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-07-16"},VERIFY_AGENT_LITERAL_ABSTAIN:{key:"VERIFY_AGENT_LITERAL_ABSTAIN",envVars:["AGENTIQA_VERIFY_AGENT_LITERAL_ABSTAIN","AGENTIQA_EXPERIMENT_VERIFY_AGENT_LITERAL_ABSTAIN"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Last valve of the verify-conflict cascade (after M1 reconcile + M2 withhold): a step whose ONLY unresolved oracle is a wait literal the AGENT invented (absent from the whole saved plan, substring) is degraded to a step-level WARNING instead of force-FAILing the run \u2014 so the synthesized 'Verification step not satisfied' issue is not filed either. TWO LANES ON SEPARATE SWITCHES since 2026-08-01 round 3: THIS flag enforces lane 'grounded_criteria' (default ON) and is also the parent kill for lane 'action_shaped', which has its OWN switch VERIFY_AGENT_LITERAL_ABSTAIN_ACTION_SHAPED (shipped default-OFF/shadow 2026-08-01, GRADUATED to default ON 2026-08-02 on its census \u2014 set it to 0 to put that lane back in shadow without touching this one). Shared preconditions of both lanes: an invented literal, no M1 grounded negative, no model-graded negative on the step (neither a 'failed' step nor a 'failed' criterion is ever softened), and no prior downgrade of the step by another valve (step_already_downgraded \u2014 the same screen M2 carries). Lane 'grounded_criteria': the step has >=1 PLAN-AUTHORED criterion, every one came back graded pass, and every one carries FRESH GROUNDING from this run (an engine-written groundingObservation for this run/step, or an observed/expectedValue the engine finds whole-token on the step's OWN retained full frame) \u2014 ungrounded model grades keep the force-fail, because this gate exists to catch fabricated passes and is not weakened for ungrounded grading. Strictly weaker than VERIFY_CONFLICT_RECONCILE, which needs a substantiation note on every criterion and produces a clean PASS; this one can never produce anything better than an honest amber and is one-directional (RED->AMBER only). NOT covered, deliberately: a PLAN-GROUNDED literal the app renders differently (staging run_b501d461's '50 EUR' vs '\u20AC50.00 net') refuses in BOTH lanes \u2014 opening that lane would re-decide four separate M1/M2 refusals and needs a page-side equivalence read; tracked as follow-up. Detection always runs: off logs verify_conflict_reconcile:agent_literal_abstain with enforced:false PLUS a verify_conflict_reconcile:agent_literal_would_abstain census row, and changes nothing; every refusal logs verify_conflict_reconcile:agent_literal_abstain_inapplicable with its reason AND its lane ('none' for the screens that run before lane selection). VERIFY_CONFLICT_RECONCILE=0 is an UMBRELLA kill for this valve too (both address the agent-invented-literal class, and that switch is documented as restoring the unconditional force-fail).",designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-08-01"},VERIFY_AGENT_LITERAL_ABSTAIN_ACTION_SHAPED:{key:"VERIFY_AGENT_LITERAL_ABSTAIN_ACTION_SHAPED",envVars:["AGENTIQA_VERIFY_AGENT_LITERAL_ABSTAIN_ACTION_SHAPED","AGENTIQA_EXPERIMENT_VERIFY_AGENT_LITERAL_ABSTAIN_ACTION_SHAPED"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"GRADUATED 2026-08-02 (default ON) \u2014 lane 'action_shaped' of the agent-literal abstain valve, shipped SHADOW on 2026-08-01 (round-3 steering split) and flipped on its own census one release later, the #1893/#1895 shadow-first precedent. The lane covers a conflicted verify step with ZERO plan-authored criteria (an ACTION step mislabeled 'verify' whose saved plan object carries text/type only: prod asess_1785560226558_33t8nuos step 4; staging run_b501d461 step 2), where the timed-out wait literal is the agent's own scaffolding rather than anybody's assertion; ON, such a step degrades to a step-level WARNING exactly as lane 'grounded_criteria' does \u2014 never a pass, and the synthesized 'Verification step not satisfied' issue is not filed either. GRADUATION EVIDENCE (verify_conflict_reconcile:agent_literal_would_abstain census, the shadow lane's whole product): 2026-08-01 evening staging replay \u2014 2 instances, invented literal 'How can I help you today?' on a Lio conversation-UX plan, wouldBecome:'warning' 2/2; 2026-08-02 prod batch asess_1785646213123_5xqheb4g \u2014 invented literal 'Ich kann', currentStatus:'passed', wouldBecome:'warning' on a step the run falsely failed. ZERO censused instances where the abstain would have been harmful (i.e. no row where the withdrawn oracle was the only thing catching a real defect), which is exactly the population question the round-2 review raised \u2014 53% of e2e/evals verify steps are criteria-less and the runner SYNTHESIZES a strict criterion from the verify text, so criteria-less is not unasserted, and the census is what shows the enforced lane is not converting real detections into green runs. The model's own negatives remain hard refusals in this lane (action_shaped_step_graded_failed / action_shaped_criterion_graded_failed), so the synthesized criterion still decides when it is graded failed. KILL-SWITCH SEMANTICS UNCHANGED: an explicit AGENTIQA_VERIFY_AGENT_LITERAL_ABSTAIN_ACTION_SHAPED=0 (or the AGENTIQA_EXPERIMENT_ spelling) restores the byte-identical pre-graduation SHADOW behavior \u2014 the classifier still reaches the same 'abstain' verdict, logs verify_conflict_reconcile:agent_literal_abstain with enforced:false plus the verify_conflict_reconcile:agent_literal_would_abstain census row, and mutates NO verdict and suppresses NO issue. Parented by VERIFY_AGENT_LITERAL_ABSTAIN (=0 kills both lanes) and by VERIFY_CONFLICT_RECONCILE (=0 is the family umbrella kill and silences the diags entirely). killSwitch resolves the no-env value from this defaultState (#1729), so graduation = defaultState 'on' AND the key listed in INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire). Acceptance suite: packages/engine-core/src/__tests__/RunnerRuntime.agentLiteralAbstain.test.ts (B1/B3 at the DEFAULT \u2192 amber; B1k/B3k/D1 with =0 \u2192 byte-identical red + census).",designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-08-01"},VERIFY_PLAN_LITERAL_TYPED_EQUIVALENCE:{key:"VERIFY_PLAN_LITERAL_TYPED_EQUIVALENCE",envVars:["AGENTIQA_VERIFY_PLAN_LITERAL_TYPED_EQUIVALENCE","AGENTIQA_EXPERIMENT_VERIFY_PLAN_LITERAL_TYPED_EQUIVALENCE"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Lane B1 of the plan-literal equivalence design (docs/plans/2026-08-02-plan-literal-equivalence-design.md; prod Lio 2026-08-02). SHADOW, DEFAULT OFF since the 2026-08-02 round-5 steering de-scope \u2014 the same shadow-first shape as lane B2 below, VERIFY_AGENT_LITERAL_ABSTAIN_ACTION_SHAPED and #1893/#1895. WHY IT SHIPS OFF RATHER THAN LIVE: THREE CONSECUTIVE adversarial rounds each found a NEW way the page-side scan reads a value that is not on the page (round 2 F2 node-locality/F3 literal ambiguity, round 3 the ten boundary shapes, round 4 D1 contested marker/D2 letter-bearing symbol, round 5 the DE-thousandths and group-separator-truncation holes below). Every round's holes were closed and every round found more; a reader whose defect-discovery rate has not yet flattened does not get to withdraw a grounded negative on a default fleet. WHAT =1 ENABLES: TYPED EQUIVALENCE = PRESENCE at the ONE page read of the verify-conflict cascade (M1 reconcileVerifyConflictsFromSnapshot). When the raw whole-token pinPresentInPage read of a PLAN-AUTHORED wait literal comes back ABSENT, the snapshot is re-read with a deterministic typed scan. SIX PROPERTIES DEFINE THE SCAN, each pinned by acceptance cases through the production wrapper pinEquivalentInPage: (a) NODE-LOCAL \u2014 the unit of scan is a SINGLE a11y node (the raw snapshot's own lines plus flattenAriaSnapshot(...).lines), never the space-joined 'spaced' or separator-free 'stream' haystacks pinPresentInPage uses; amount and currency marker must share one node, because a join turns the newline that separates two nodes into a legal gap char and FABRICATES composites present on neither (measured on five realistic aria shapes, 2026-08-02 round 2 defect F2). (b) UNAMBIGUOUS LITERAL \u2014 the AUTHORED side must carry exactly ONE surviving numeric reading; a both-locales-legal form with a meaningful fraction ('1,500' -> {1500, 1.5}) is refused outright, because under INTERSECT semantics it would equate a 1000x-different page value (round 2 defect F3). (b2) UNAMBIGUOUS PAGE OPERAND (round 5) \u2014 the PAGE atom must ALSO carry exactly one surviving reading after the structural group validation numericCandidates already applies. Under INTERSECT semantics a multi-reading page atom equated a 1000x-different value in the OTHER direction: a DE page rendering 'Rechnungsbetrag 7.890 EUR' (= 7890) reads {7.89, 7890} and satisfied the literal '7.89 EUR'; likewise '2.750'/'2.75 EUR' and '9.990'/'9.99 EUR'. Refusing multi-reading page atoms costs nothing real: 'EUR 50.00 net' is DE-invalid as a thousands group ('.00' is not three digits), so the ordinary renderings stay singletons and still fold. (c) BOUNDARY-GUARDED, MAXIMAL amount runs \u2014 a number-like run may not touch a letter, a digit, a '.'/',' or a '%'/'/'/'-' on either side, so 'EUR 50k', 'EUR 1.5B', 'EUR 50,00abc', 'EUR 50% off', 'EUR 50/Monat' and 'EUR 50-60' all read as NO amount rather than as 50/1.5 (round 3; ten measured false matches, the scaled-magnitude ones off by 1000x-1e9). (c2) NO TRUNCATED GROUP RUN (round 5) \u2014 the boundary class of (c) is a CHARACTER class, and the group separators the world actually renders money with are not in it: the Swiss apostrophe U+2019 and the space family (U+0020/U+00A0/U+202F/U+2009). 'Gesamtsumme: CHF 50\u2019000.00' therefore split into the runs '50' and '000.00', the first of which claimed the adjacent CHF and matched the literal '50 CHF' \u2014 a value 1000x below what the page states. A run adjacent, across group separators only, to another DIGIT is a TRUNCATION of a larger grouped number and yields no atom at all. (d) TYPED IDENTITY \u2014 the literal must parse as an amount+currency composite scalar ('50 EUR' / 'EUR 50' / '\u20AC50' / '50,00 \u20AC' \u2014 recognizeAmountCurrency, which rejects anything carrying another word) or a bare number, and the page must carry a MONEY ATOM with the IDENTICAL ISO-4217 code (symbol<->code via the frozen gazetteer; the ambiguous $ and \xA5 never canonicalize) whose numeric candidate set INTERSECTS the literal's under the existing dual-locale numericCandidates (US 1,234.56 == DE 1.234,56, exact equality, no epsilon). Currency adjacency is horizontal-whitespace-only, so 'EUR 12.00 shipping, 50 items' never reads as EUR 50. FOUND, WITH =1 => the literal counts as PRESENT and takes the EXISTING clean-pass rail (M1 'cleared'), with its OWN decision reason literal_equivalent_in_snapshot and its OWN client note \u2014 the whole-token rail's note ('was present when the run re-checked') is FALSE of this population by construction and is not reused (round 2 defect F4B); no new verdict, no extra browser action. NOT found => today's grounded_negative stands. NO fuzzy matching, NO LLM, NO string similarity: a shape the closed tables cannot canonicalize is not 'probably the same value'. THE CLASS IT WOULD CLOSE: a plan that writes money the way a human writes it while the product renders it the way a locale renders it ('50 EUR' vs 'EUR 50.00 net') produced a force-FAIL plus a HIGH-severity 'Verification step not satisfied' issue in the customer's project while every criterion of the step passed \u2014 that incident still force-fails on a default fleet. DEFAULT OFF = SHADOW, NOT INERT: detection runs in full and the caller censuses verify_conflict_reconcile:literal_typed_equivalence {enforced:false} PLUS a dedicated row verify_conflict_reconcile:literal_typed_equivalence_would_clear {stepIndex, literal, matchType, currency, matched, nodeText, basis, wouldBecome:'passed'}, and mutates NOTHING \u2014 the M1 decision map keeps its grounded_negative, would_fail is logged exactly as with the lane absent, the force-fail and the HIGH-severity issue stand byte-identically, and no capture kind changes. TWO GATES, NOT ONE (round 5 invariance fix): the mutation requires BOTH this switch AND a live VERIFY_CONFLICT_RECONCILE_SNAPSHOT. The decision map is populated even when M1 is in shadow, and its grounded_negative is read UNGATED by two downstream firewalls (M2 and the agent-literal abstain valve) \u2014 so before the fix an opt-in =1 reached THROUGH a disabled M1 and changed verdicts on a build whose operator had switched M1 off. VERIFY_CONFLICT_RECONCILE=0 is the family umbrella kill and silences the census too. Runner lane only (RunnerRuntime run_complete, second escalation).",designDoc:"docs/plans/2026-08-02-plan-literal-equivalence-design.md",status:"active",added:"2026-08-02",graduation:{status:"gated",gate:"NOT graduable as built \u2014 FOUR blockers. THE STANDING ONE: three consecutive adversarial rounds each found a fresh class of page-side false match (round 2 F2/F3, round 3 the ten boundary shapes, round 4 D1/D2, round 5 the two below) and the discovery rate has not flattened, so graduation needs a round that finds nothing new, not merely the closure of the last round's list. (1) PAGE-SIDE SINGLETON-READING SEMANTICS. Round 5 measured the DE-thousandths hole in the direction the round-2 F3 guard does not cover: the AUTHORED side is now required to be unambiguous, but the PAGE side was left multi-reading on the argument that 'the page is not the operand the author controls' \u2014 and under INTERSECT that is exactly what lets a page rendering 7.890 (DE seven-thousand-eight-hundred-ninety) satisfy the literal '7.89 EUR'. The singleton rule shipped in this PR closes the measured shapes; what it does NOT yet have is a systematic argument that the page operand is single-reading everywhere the fold fires, which is what a graduation needs. (2) GROUP-SEPARATOR TRUNCATION. The boundary class of property (c) is a character class and cannot see that a run it accepted is a FRAGMENT of a grouped number: 'CHF 50\u2019000.00' cleared '50 CHF' because U+2019 and the space family are not boundary chars. Closed for the apostrophe and the space family in this PR; the general form (any group separator any locale renders, including the ones no test has enumerated) is not proven closed. (3) SUBJECT BINDING (F1). Node-locality is an AUTHORSHIP guard, not a PLACE one: an equal value on an UNRELATED card clears the presence wait, demonstrated on the lio-mirror/chat-price-format trap where the distractor renders \u20AC7.89 and the anchor \u20AC78.90 \u2014 B1 turns a genuine red into a green there. The census carries nodeText so a clear is auditable post-hoc, but auditability is not a gate; the lane cannot enforce until a subject-binding design exists (bind the matched node to the step's target, or refuse when the page carries more than one atom satisfying the literal). (4) STAGING AND PROD CENSUS. verify_conflict_reconcile:literal_typed_equivalence_would_clear plus the paired literal_typed_equivalence_refused rows, showing the fold fires on genuine locale renderings of the value the step is about and not on pages where the product is actually wrong about the number.",evidence:"an adversarial round that finds NO new page-side false-match class; a systematic (not case-by-case) argument for page-operand single-readingness and for group-separator closure; a subject-binding design with its own acceptance suite that reddens the chat-price-format trap; and a staging+prod literal_typed_equivalence_would_clear / _refused census sized against the population it would clear",owner:"steering (Alex)",review:"2026-08-09"}},VERIFY_PLAN_LITERAL_CRITERIA_DEGRADE:{key:"VERIFY_PLAN_LITERAL_CRITERIA_DEGRADE",envVars:["AGENTIQA_VERIFY_PLAN_LITERAL_CRITERIA_DEGRADE","AGENTIQA_EXPERIMENT_VERIFY_PLAN_LITERAL_CRITERIA_DEGRADE"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Lane B2 of the plan-literal equivalence design (docs/plans/2026-08-02-plan-literal-equivalence-design.md; prod Lio 2026-08-02, wait literal 'Munich' on a German page rendering 'M\xFCnchen'). SHADOW, DEFAULT OFF since the 2026-08-02 round-3 steering de-scope \u2014 the same shadow-first shape as VERIFY_AGENT_LITERAL_ABSTAIN_ACTION_SHAPED and #1893/#1895. WHAT =1 ENABLES: the ONE deliberately scoped-out lane of VERIFY_AGENT_LITERAL_ABSTAIN \u2014 a PLAN-GROUNDED wait literal that B1's typed equivalence could NOT reconcile stops refusing at the literal_plan_grounded provenance screen WHEN the step has >=1 PLAN-AUTHORED criterion, falls through to lane 'grounded_criteria', which applies its FULL, unchanged contract (every authored criterion graded, every one passed, every one seen by the ENGINE on the step's OWN retained frame via criterionValueSeenOnOwnFrame) and degrades the step to a step-level WARNING under the pre-cut reason plan_literal_not_rendered_as_written and the pre-cut client note ('the run waited for this exact wording and did not find it \u2014 the page may present the same value differently'). =1 ALSO arms lane B2's own full-snapshot request at dispatch (verifyWaitCarriesDegradableLiteral), without which the honesty condition has no frame to read and the lane is inert. NEVER a pass: the outcome is the one-directional RED->AMBER the valve already owns, so run.status can go from failed to passed only in the sense that no step is failed, and the synthesized 'Verification step not satisfied' issue is not filed. DEFAULT OFF = SHADOW, NOT INERT: detection runs in full (the provenance screen no longer reads the switch), the classifier reaches the same verdict, the caller logs verify_conflict_reconcile:agent_literal_abstain with enforced:false PLUS a dedicated census row verify_conflict_reconcile:plan_literal_would_degrade {stepIndex, stepText, literal, timeoutMs, reason, provenance, lane, criteriaCount, currentStatus, wouldBecome:'warning', flag}, and mutates NOTHING \u2014 the step keeps its status, the conflict stays in failingConflicts, the force-fail and the HIGH-severity issue stand byte-identically, and NO capture kind changes. The shadow census UNDERCOUNTS BY CONSTRUCTION: with the lane off no full frame is bought, so most of the population refuses one screen earlier and is visible as agent_literal_abstain_inapplicable {reason:'plan_literal_criteria_not_page_grounded', provenance:'plan_grounded', enforced:false} rather than as a would-degrade row; read the two together to size the population. WHAT STAYS RED under =1, unchanged: a criteria-LESS step (lane 'action_shaped' still refuses every plan-grounded literal pre-lane with literal_plan_grounded, lane:'none'), any step the model graded failed or with a failed criterion, any step another valve already marked down, any step whose authored criteria are ungraded or not page-grounded, and any M1 grounded negative (the firewall is untouched \u2014 which is why B1 must clear the equivalence case at M1 rather than here). Parented by VERIFY_AGENT_LITERAL_ABSTAIN (=0 kills this too) and by VERIFY_CONFLICT_RECONCILE (=0 is the family umbrella kill and silences the diags entirely). Runner lane only.",designDoc:"docs/plans/2026-08-02-plan-literal-equivalence-design.md",status:"active",added:"2026-08-02",graduation:{status:"gated",gate:"NOT graduable as built \u2014 three blockers, all found by the round-2/round-3 adversarial verification of this PR and none of them a soak question. (1) HONESTY-NEEDLE REDESIGN. criterionValueSeenOnOwnFrame, the condition that is supposed to pay for the amber, accepts a MODEL-CONTROLLED value: it looks for the criterion's own `observed`/`expectedValue` on the step's frame, and a probe showed ANY on-page token the model chooses to emit buys the amber. 'the engine saw the asserted value' is therefore closer to 'the model quoted something from the page', which is not corroboration of an authored claim; the lane cannot enforce until that predicate is re-designed to read a value the model does not control. (2) F5 BLAST RADIUS. B2 is inert without a full frame, so it requests one at DISPATCH \u2014 before the wait's outcome is known \u2014 which also promotes SUCCEEDING waits to full and widens _domGroundableCandidates (the live predicate-basis candidate set) for steps B2 never touches. Enabling B2 is therefore also a capture-kind change for a wider population and must be re-measured as one. (3) M1-CANARY RE-BASELINE. The late-render canary's failed->warning baseline was recorded with B2 ENFORCING; that decision has to be re-taken against the default-OFF world before the flag can flip. On top of the three: a staging AND prod census of verify_conflict_reconcile:plan_literal_would_degrade plus the plan_literal_criteria_not_page_grounded refusals, showing the lane fires on genuine rendering mismatches (Munich/M\xFCnchen) and not on steps where the product is actually wrong.",evidence:"staging/prod verify_conflict_reconcile:plan_literal_would_degrade census rows + the paired agent_literal_abstain_inapplicable {reason:plan_literal_criteria_not_page_grounded} refusals (population sizing), a re-designed honesty predicate with its own acceptance suite, an F5 capture-kind impact measurement over a full runner-eval pass, and the re-taken m1-canary late-render baseline",owner:"steering (Alex)",review:"2026-08-09"}},VERIFY_LITERAL_PROVENANCE_STEP_SCOPED:{key:"VERIFY_LITERAL_PROVENANCE_STEP_SCOPED",envVars:["AGENTIQA_VERIFY_LITERAL_PROVENANCE_STEP_SCOPED","AGENTIQA_EXPERIMENT_VERIFY_LITERAL_PROVENANCE_STEP_SCOPED"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"GRADUATED 2026-08-03 (default ON) \u2014 STEP-SCOPED LITERAL PROVENANCE, the third provenance class of the agent-literal abstain valve (prod Lio 2026-08-02/03, 5 hits in 24h). Shipped SHADOW on 2026-08-03 and flipped on its own REPLAY-BASED census one release later, the same shadow-first path VERIFY_AGENT_LITERAL_ABSTAIN_ACTION_SHAPED took on 2026-08-02 (#1893/#1895 precedent). GRADUATION EVIDENCE (2026-08-03, ag-rehearsal prod->staging mirror replay, 18 runs over 6 plans: 16 census runs at =0 plus a 2-run enforcing A/B; VERDICT.md at https://s.assets.agentiqa.com/provenance-graduation-2026-08-03/): (1) COUNT AND SHAPE \u2014 exactly 2 verify_conflict_reconcile:agent_literal_would_rescue rows in the 16 shadow runs, both the known '50 EUR' hit (chat-knowledge-qa tp_0370f61d run14 and asst-knowledge-qa run11), both step 2, both planMatchLocation step_3:criterion_1:check, both currentStatus passed -> wouldBecome warning, on runs the build falsely FAILED. (2) NO OVER-TRIGGER \u2014 zero provenance rows on the other 4 plans across 8 runs, and the 4 plan_grounded refusals were retained as authored, so the population did not creep into the typed-match equivalence track's half. (3) ENFORCING A/B \u2014 run14 (=0): step 2 failed, run failed, would_rescue censused; run18 (=1): step 2 warning, run PASSED, enforced:true and no census row, i.e. the rescue lands as the one-directional RED->AMBER and nothing else moved. (4) PROD RECOMPUTE \u2014 all 5 documented prod hits recomputed under the step-scoped read: 4 remap to agent_invented_plan_coincident at exactly the documented locations; the 5th (m77p3hky) became agent_invented after lio edited that plan at 2026-08-03T09:01:10Z, i.e. it left the class by an authoring change, not by a read change. THE CLASS: the valve's provenance screen asks 'did the AUTHOR write this literal?' against the WHOLE plan document (title + every step's text/criteria, loose substring). When an INVENTED wait literal COINCIDENTALLY appears somewhere else in the plan \u2014 the plan title, or (in all 5 measured hits) a NEIGHBOURING step's criterion \u2014 the screen returns plan_grounded, the criteria-LESS lane 'action_shaped' refuses before lane selection with agent_literal_abstain_inapplicable {reason:'literal_plan_grounded', lane:'none'}, and the action step keeps its force-FAIL while every criterion of its paired verify step passed. Measured: '50 EUR' (asess_1785644983561_n5worrfv step 2, matched step 3's criterion; app renders '\u20AC50 (net)'), 'Munich' (o7hvcun7 step 2, matched step 3's criterion; app renders 'M\xFCnchen'), the German prompt of step 2 (5xqheb4g step 4, matched step 2's TEXT), 'QA Base \u2014 Draft Completion (do not modify)' (m77p3hky step 4, matched step 5's criterion), '50 EUR' again on the 08-03 build (r3ghgy68 step 2). All five failing steps carry ZERO plan-authored criteria, so lane B2's escape hatch (which needs >=1 criterion) cannot reach any of them. WHAT =1 ENABLES: the provenance read is SCOPED TO THE FAILING STEP'S OWN authored text (buildStepPlanHaystack \u2014 its instruction plus each of its criteria's check/expectedValue) with the SAME loose lowercased-substring matcher as before, and splits the old plan_grounded population in two \u2014 literal ON the step's own text stays plan_grounded and every existing refusal is byte-identical; literal absent from the step but present ELSEWHERE in the plan becomes the NEW provenance agent_invented_plan_coincident, which falls through the plan-grounded screen into ordinary lane selection and is then decided by each lane's UNCHANGED contract (lane 'action_shaped': no model-graded negative, no prior downgrade, no M1 grounded negative; lane 'grounded_criteria': every authored criterion graded, passed and engine-grounded this run via criterionGroundedThisRun \u2014 the invented-literal bar, since a literal the author did not write on this step is not an authored claim about it). Literal absent from the plan entirely is agent_invented, unchanged. The two reads bracket the population EXACTLY: plan_grounded + agent_invented_plan_coincident is precisely the old plan_grounded set (the locator falls back to 'plan_document' for the join-boundary residue), so no literal can be reclassified into the default-ON agent_invented path by this change. OUTCOME CONTRACT UNCHANGED: the only mutation is the valve's one-directional RED->AMBER step-level WARNING, never a pass (#2002 doctrine \u2014 a coincidence buys a warning, never green), and the M1 grounded-negative firewall plus both model-negative screens still refuse. KILL-SWITCH SEMANTICS (=0 under either spelling) RESTORE THE BYTE-IDENTICAL PRE-GRADUATION SHADOW, NOT AN INERT PATH: detection always runs, the classifier reaches the same verdict, the caller logs verify_conflict_reconcile:agent_literal_abstain {enforced:false} PLUS a dedicated census row verify_conflict_reconcile:agent_literal_would_rescue {stepIndex, stepText, literal, timeoutMs, reason, provenance:'agent_invented_plan_coincident', planMatchLocation, lane, criteriaCount, currentStatus, wouldBecome:'warning', flag} and mutates NOTHING \u2014 the step keeps its status, the conflict stays in failingConflicts, the force-fail and the synthesized 'Verification step not satisfied' issue stand byte-identically. So an operator who kills the class keeps the measurement. NO capture kind changes in EITHER state (this valve reads no page and calls no model; lane B2's full-frame force is untouched because it is gated on waitLiteralAuthoredOnStep, which this class fails by definition). ONE TELEMETRY DELTA, stated rather than hidden: a coincident literal that reaches a lane reports that lane's reason (e.g. action_shaped_criterion_graded_failed) instead of the pre-2026-08-03 literal_plan_grounded, and its lane field is no longer 'none' \u2014 the census got more specific when the read was scoped, independently of enforcement. The parents are unchanged \u2014 VERIFY_AGENT_LITERAL_ABSTAIN=0 kills both lanes and VERIFY_CONFLICT_RECONCILE=0 is the family umbrella kill that silences the census too. killSwitch resolves the no-env value from this defaultState (#1729), so graduation = defaultState 'on' AND the key listed in INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire). Acceptance suite: packages/engine-core/src/__tests__/RunnerRuntime.agentLiteralAbstain.test.ts \u2014 B2 (the prod shape verbatim), P2, P3d, P9 leg (a) and P10 leg (b) at the DEFAULT -> amber, never a pass; B2k, P3, P5b, P9 leg (b) and P10 leg (a) with =0 -> byte-identical red + census; P1/P1e pin that the own-step (genuinely authored) read never moves; P4 pins the agent_invented path byte-identical; P8 pins that a model-graded negative is still refused; P9 is asserted in BOTH states because the coincident/agent_invented split is what makes this switch reach the population at all (agent_invented is held by VERIFY_AGENT_LITERAL_ABSTAIN, not by this key). Runner lane only.",designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-08-03"},VERIFY_NOTE_REFUTES_CRITERION:{key:"VERIFY_NOTE_REFUTES_CRITERION",envVars:["AGENTIQA_VERIFY_NOTE_REFUTES_CRITERION","AGENTIQA_EXPERIMENT_VERIFY_NOTE_REFUTES_CRITERION"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"SELF-CONTRADICTION GATE \u2014 ships ENFORCING (default ON) on a measured population. THE DEFECT (false-PASS, the direction that destroys customer trust): a criterion recorded passed:true while its OWN note states the opposite, inside a single verdict record, and the run reported PASSED. TWO measured instances, one on PROD. (1) PROD run_35ffd2b2 / asess_..._eobjjo88, lio's 2026-08-04 04:14Z batch on engine 8741da063, plan 'Lio Assistant - conversation UX' step 3: STRICT criterion 'The answer addresses the question about cleaning-services suppliers (it mentions at least one supplier or a count)' booked passed with note 'The assistant addresses the topic of cleaning-services suppliers, stating it couldn't find specific info but offering to help narrow down the search.' The SAME underlying lio behaviour correctly FAILED a sibling run in the same batch (8vc9g930), so the false pass did not merely mis-grade a criterion \u2014 it ABSORBED a real customer-visible defect our own product had already proven elsewhere. (2) MIRROR run_9bec8ba8 (c13) step 3, plan tp_3d5427b9 'asst-negotiation-email': STRICT criterion 'An email draft is shown with an editable subject and body (an email composer, not just prose in the chat)' booked passed with note 'An email draft was shown in a composer-like UI, but the elements were not programmatically editable (not contenteditable).' Ground truth settled by two independent methods across five sibling replicates: a contenteditable DOM query returned only the chat composer (b13) and an actually-attempted type_text_at into subject and body FAILED (f14, f15). NO existing gate caught either: in c13 step 3 predicate_basis_verify was inconclusive/dom_not_resolvable, typed_atom_floor abstained on no_structured_observed, criteria_containment and ungrounded_pass were silent. WHAT =1 ENABLES: every graded criterion still reported passed after pin substantiation is read by detectCriterionSelfContradiction (packages/engine-core/src/criterionSelfContradiction.ts) and, on a `contradicted` verdict, has `passed` flipped to false \u2014 the SAME single mutation substantiatePinnedCriterion makes for an uncited pin, so the outcome is decided by the ONE existing chokepoint deriveStepStatusFromCriteria: a STRICT criterion FAILS its step, a non-strict one WARNS it. No new status, no new vocabulary, no note rewrite (the criterion's own note already says why, in the grader's own words). ONE-DIRECTIONAL: only ever reached for a grade that is still passed, so it can never rescue a failure and never makes anything green. WHY FAIL RATHER THAN WITHHOLD for a strict criterion: a withhold means 'we could not confirm', and this is not that \u2014 the note is the grader's own AFFIRMATIVE statement that the checked property is ABSENT, i.e. evidence AGAINST the claim rather than missing evidence, and in both measured cases an independent method proved the fail correct. TWO LANES, because the class has two shapes and one mechanism cannot reach both. LANE A: the note negates a term the CHECK ITSELF USES, scoped \u2014 an explicit negator from a closed list, and a >=4-char non-stopword subject token of the check's own main clause inside that negator's 60-char forward window (the mirror c13 shape, negatedTerm 'editable'). LANE B: the note reports the app FAILED TO PRODUCE what the check REQUIRES, IN WORDS THE CHECK NEVER USES \u2014 a closed NOT-FOUND list (couldn't/could not/did not/unable to + find|locate|retrieve|provide|identify|return|surface; 'no results|matches|information|data|details|suppliers|records'; 'not found'; 'found no|nothing|none') against a closed PRESENCE-REQUIREMENT verb list in the check (mentions|shows|displays|contains|includes|lists|states|reports|identifies|names|cites|returns|surfaces|provides|presents|specifies). The PROD case is lane B and lane A provably cannot see it: 'couldn't find specific info' and 'mentions at least one supplier or a count' share NO surface term, and the check's 'or' screens lane A out anyway. NO LLM, NO FUZZY SCORING, NO NETWORK \u2014 two strings the caller already holds, so shadow and enforcement are the same computation and OFF costs nothing. FOUR SCREENS, EACH KILLING A MEASURED FALSE POSITIVE of the naive version (a bare 'does the note contain a negation' scan fires on 7 of the mirror corpus's 526 passed criteria and only ONE is the defect; THREE of the six false positives are on STRICT criteria, so a naive gate shipped enforcing would have turned three HONEST passes into hard failures \u2014 the one direction this gate may never take): check_disjunctive (a09 \u2014 check 'mentions the tracking number QACORE-TRACK-0001 and/or the carrier DHL', note 'Tracking number ... was not mentioned'; DHL WAS mentioned so the disjunction is satisfied and the note refutes nothing; also e05); check_carries_negation, read on the check's MAIN CLAUSE (f01/e01 \u2014 check '\"Final Sign-off\" has not started', note 'Final Sign-off is Not Started', where the note CONFIRMS a check whose own subject is negative; also a11/b11 \u2014 check '... (webshops, private expensing) is not permitted' vs note 'you must not use private expense reimbursement'); GLOSS STRIPPING of quoted spans and parenthetical asides from BOTH texts before anything is read (a08 \u2014 check and note both name the plan title 'QA Base \u2014 Submission Checks (do not modify)' and the negation lives INSIDE THE TARGET NAME, the exact class stripTargetLiteralFromProse exists for; and f13/f16 \u2014 note 'Actual text: \"Payment terms of 60 days end of month instead of 30 days\"', a page quotation rather than the grader's own claim); and CONTRASTIVE EXCLUSION \u2014 'instead of' / 'rather than' / 'but' / 'however' contrast, they do not negate, and 'instead of' was exactly f13/f16's naive hit. CENSUS THAT PAYS FOR ENFORCING: 2 fires over 698 passed criteria, both of them the known defects, ZERO honest passes moved \u2014 1 of 168 in lio's PROD 04:14Z batch (eobjjo88, lane B), where an independent hand audit of the same batch found exactly that one note-contradicted pass so the detector matches the audit, and 1 of 530 in the 46-run prod-parity mirror (c13, lane A). KNOWN BOUNDARIES, stated rather than hidden: a check whose main clause carries any negator abstains wholesale (a genuine self-contradiction there is missed \u2014 f01/e01/a11/b11 are four measured reasons to keep it); a negation the grader writes INSIDE a quotation abstains, because transcription cannot be told from assertion there; morphology is not modelled, so 'mentions' in the check vs 'mentioned' in the note abstains. =0 UNDER EITHER SPELLING IS SHADOW, NOT INERT: detection still runs, the caller logs note_refutes_criterion:would_fail {stepIndex, criterionIndex, lane, reason, negatedTerm, strict, check, note, currentStatus, wouldBecome, decision, enforce:false, mutated:false, flag} and mutates NOTHING, so an operator who kills the class keeps the measurement. A SECOND census row, emitted in BOTH states, sizes the SUPPRESSED population: note_refutes_criterion:suppressed fires when a note DID carry an explicit negator and a screen declined it \u2014 the row a graduation or rollback review reads to see whether the screens are over- or under-refusing. Notes with nothing negative in them are silent, because they carry no signal and would drown it. Acceptance suite: packages/engine-core/src/__tests__/criterionSelfContradiction.test.ts (both prod/mirror defects verbatim, all eight measured honest passes as regression pins, and a mutation guard per screen) + RunnerRuntime.noteContradictsCriterion.test.ts (the cascade: strict -> step failed, non-strict -> step warning, =0 -> byte-identical pass + census). Runner lane only. See docs/VERDICT-GATES.md.",designDoc:"docs/plans/2026-08-04-criterion-self-contradiction-design.md",status:"active",added:"2026-08-04"},VERIFY_STRICT_PASS_SUBSTANTIATION:{key:"VERIFY_STRICT_PASS_SUBSTANTIATION",envVars:["AGENTIQA_VERIFY_STRICT_PASS_SUBSTANTIATION","AGENTIQA_EXPERIMENT_VERIFY_STRICT_PASS_SUBSTANTIATION"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"SHADOW, DEFAULT OFF \u2014 the SECOND shape of the 2026-08-04 false-PASS class: a STRICT criterion booking a pass with NO substantiation at all. Measured on PROD in run_783593d9 (mirror label e13) step 3, where the same plan-13 editability criterion recorded passed:true with note, observed AND groundingObservation ALL absent \u2014 and where all 10 strict passes in that run carried zero criterion-level substantiation. WHY IT IS SHADOW AND NOT ENFORCING, with the number: requiring substantiation to book a strict pass would flip legitimate passes EN MASSE, which is precisely the harm the sibling VERIFY_NOTE_REFUTES_CRITERION gate exists to prevent. 104 of 446 strict passes in the 46-run prod-parity mirror (23.3%) carry no substantiation of any kind, spread over 21 of its 47 runs, and those runs are dominated by HONEST passes (a06, a07, a09, a11, a12, a14, b06, b07, b10, b12, b16 are all honest-pass rows in the rehearsal SCORE.md). On lio's PROD 2026-08-04 04:14Z batch the same count is 79 of 143 strict passes. An independent audit of that batch corroborates the weakness is SYSTEMIC rather than a verdict bug: only 47 of 168 passes carry a distinct page literal at all \u2014 62 are prose self-echo, 59 ungrounded, and 56 already carry ungrounded_pass:would_withhold. Enforcing a substantiation requirement would therefore touch about a third of ALL passes, so the fix belongs at the SOURCE (what the grader is required to transcribe) and is its own track, not a verdict change to make alongside a false-pass fix. IT EXTENDS THE pin_substantiation FAMILY RATHER THAN DUPLICATING IT: substantiatePinnedCriterion already flips a passing criterion whose note does not cite its pin, but returns the grade VERBATIM when planCriterion.expectedValue is empty \u2014 which is every PROSE criterion, including this one. strictPassLacksSubstantiation (packages/engine-core/src/criterionSelfContradiction.ts) is exactly that abstention made countable, on the same 'the grade's evidence is its note' doctrine. WHAT THE DEFAULT-OFF STATE DOES: detection always runs and the caller emits strict_pass_unsubstantiated:would_withhold {stepIndex, criterionIndex, check, currentStatus, wouldBecome:'warning', decision:'abstain', enforce, mutated:false, flag} while mutating NOTHING \u2014 no status write, no note, byte-identical verdicts. WHAT =1 WOULD ENABLE is deliberately NOT WIRED in this PR: the flag currently selects only the census row's `enforce` field, so setting it to 1 changes no verdict. Graduating this class requires the source fix plus its own census, not an env flip. Runner lane only. See docs/VERDICT-GATES.md.",designDoc:"docs/plans/2026-08-04-criterion-self-contradiction-design.md",status:"active",added:"2026-08-04",graduation:{status:"gated",gate:"NOT graduable as built, and deliberately so. Enforcing it would demote 104 of 446 strict passes in the mirror corpus (23.3%, 21 of 47 runs) and 79 of 143 in lio's prod 04:14Z batch, overwhelmingly on runs that are honest passes \u2014 a mass false-fail, i.e. the opposite defect. The blocker is not a soak: the SOURCE of the weakness is that a strict prose criterion is not required to transcribe anything (only 47 of 168 prod passes carry a distinct page literal; 62 are prose self-echo). Graduation requires (1) the grading contract changed so a strict pass must carry a transcribed page value, (2) a re-census showing the unsubstantiated population has collapsed, and (3) a decision on the residual population that legitimately cannot transcribe (visual/layout criteria).",evidence:"a strict_pass_unsubstantiated:would_withhold census on staging AND prod after the grading-contract change, showing the population collapsed rather than merely moved, plus the ungrounded_pass:would_withhold overlap so the two are not double-counted",owner:"steering (Alex)",review:"2026-08-11"}},VERIFY_CONFLICT_NEGATION_BAIL_WITHHOLD:{key:"VERIFY_CONFLICT_NEGATION_BAIL_WITHHOLD",envVars:["AGENTIQA_VERIFY_CONFLICT_NEGATION_BAIL_WITHHOLD","AGENTIQA_EXPERIMENT_VERIFY_CONFLICT_NEGATION_BAIL_WITHHOLD"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"FALSE-FAIL REMOVAL \u2014 ships ENFORCING (default ON) because it only ever runs RED->AMBER and removes a verdict the run's own record contradicted. THE DEFECT: mirror run_86de13b1 (b05) step 3 of plan tp_4ce9dd2d 'chat-item-search' was recorded status:failed \u2014 and the RUN failed with it \u2014 while ALL FOUR of its criteria were passed:true, each carrying a groundingObservation quoting the page ('Inacopia Kopierpapier Elite A4 90g 500Bl' visible, price '7,89 \u20AC', card 'B\xFCrowelt Schmidt GmbH & Co. KG'). The client summary read 'Failed: verification for step 3 ... did not pass \u2014 the expected content never appeared on the page and was not confirmed on a re-check, even after a retry', which the run's own step_results contradict. Rate 1 of 5 replicates; the trigger is the model choosing to scroll back and re-verify, and this plan is seq 4 in lio's nightly batch. MECHANISM: the oracle chain declined in sequence and the LEGACY verify-conflict force-fail owned the verdict BY DEFAULT \u2014 verify_conflict_reconcile:inapplicable reason=negation_marker literal='Inacopia ...', then verify_conflict_withhold:inapplicable reason=negation_marker (M2 is OFF on prod anyway), then verify_conflict_reconcile:agent_literal_abstain_inapplicable lane=none provenance=plan_grounded reason=literal_plan_grounded. M1's negation_marker bail is triggered by lio's own DEFENSIVE criterion wording ('those do not count', 'their presence or absence must not be asserted') and is CORRECT \u2014 a PRESENCE clear is unsound for an assertion that something is GONE, and clearing there would launder a genuine failure into a pass \u2014 so the bail itself is untouched by this flag. The bug is the inference that followed it: 'M1 could not clear this' is an ABSENCE OF PROOF, not proof of failure. WHAT =1 ENABLES: a new rung between the agent-literal abstain valve and the legacy force-fail. For a step in failingConflicts whose M1 decision reason is EXACTLY negation_marker, whose status is still passed, and which carries >=1 graded PLAN-AUTHORED criterion with ALL of them passed, the oracle failure is cleared and the step lands in the WARNING band with an honest client note (clientNegationBailWithheldNote \u2014 'could not be re-checked automatically because this step is worded as a negative or absence condition. Every check the plan wrote for this step passed; the re-check itself is reported as unconfirmed rather than passed or failed'), on exactly the machinery M1's stale-scope rung and M2's withhold already use. Because the conflict leaves failingConflicts, the synthesized 'Verification step not satisfied' issue is not filed for it either, and terminationReason stays 'completed'. NEVER A PASS and never a silent green: the outcome is the one-directional RED->AMBER the valve family already owns. NARROW BY CONSTRUCTION \u2014 every condition is a refusal the class needs: no other M1 bail reason qualifies; authorship is read via the SAME `unauthored` mark deriveStepStatusFromCriteria honours, so a fabricated grade can neither buy nor block the amber; and a step the model itself graded negative, or that another valve already marked down, refuses \u2014 so this can never rescue a model-reported failure. =0 UNDER EITHER SPELLING IS SHADOW, NOT INERT: the eligibility computation still runs and the caller logs verify_conflict_negation_bail:would_withhold {stepIndex, stepText, literal, criteriaCount, currentStatus, wouldBecome:'warning', decision:'withhold', enforce:false, mutated:false, flag} while the force-fail, the failed step status, the client summary and the synthesized issue all stand byte-identically. Reads no page and calls no model, so both states cost the same. Acceptance suite: packages/engine-core/src/__tests__/RunnerRuntime.negationBailWithhold.test.ts (the b05 shape verbatim -> warning + run not failed at the default; =0 -> byte-identical failed + census; and the refusals: a non-negation_marker bail, a model-graded failed criterion, an already-downgraded step, a step with zero authored criteria, and a step whose only passing grades are `unauthored`). Runner lane only. See docs/VERDICT-GATES.md.",designDoc:"docs/plans/2026-08-04-criterion-self-contradiction-design.md",status:"active",added:"2026-08-04"},VERIFY_RECONCILE_CLEAN_PASS:{key:"VERIFY_RECONCILE_CLEAN_PASS",envVars:["AGENTIQA_VERIFY_RECONCILE_CLEAN_PASS","AGENTIQA_EXPERIMENT_VERIFY_RECONCILE_CLEAN_PASS"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Presentation of a VERIFY_CONFLICT_RECONCILE-reconciled verify step: on, the reconciled step is a clean PASS and the reconciliation is recorded only in the structured verification_conflict_reconciled diag event (no engine-jargon note on the user-facing step); off restores the legacy step-level WARNING plus the explanatory note. Independent of VERIFY_CONFLICT_RECONCILE, which decides WHETHER a conflict reconciles at all \u2014 this only changes how an already-reconciled step is surfaced.",designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-07-22"},VERIFY_PRESENCE_WAIT_FLOOR:{key:"VERIFY_PRESENCE_WAIT_FLOOR",envVars:["AGENTIQA_VERIFY_PRESENCE_WAIT_FLOOR","AGENTIQA_EXPERIMENT_VERIFY_PRESENCE_WAIT_FLOOR"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'Raises a verify-step presence oracle wait (wait_for_element on a `verify` plan step) to a minimum budget (20s) so a slow-rendering but PRESENT element \u2014 e.g. Miro\'s Templates carousel "Blank board" card, which paints several seconds after the dashboard is otherwise interactive \u2014 is not falsely failed by the 5s default wait budget (staging step-5 login/dashboard flake, sessions asess_1784961071757_cqwxon46 / asess_1784960734951_4t0zaxp1, where the next action successfully CLICKED "Blank board"). Floor-only: never lowers a larger model-supplied timeout; off restores the model-supplied / 5s-default budget. Fail-closed preserved \u2014 a genuinely-absent element still times out at the larger budget and records the same oracle failure, so no false-PASS is introduced. Applied in RunnerRuntime.raiseVerifyPresenceWaitBudget (Runner/test-plan lane only; Explorer/Coordinator have no plan steps).',designDoc:"packages/engine-core/src/verifyPresenceWaitBudget.ts",status:"active",added:"2026-07-25"},ABSENCE_AWARE_VERIFY:{key:"ABSENCE_AWARE_VERIFY",envVars:["AGENTIQA_ABSENCE_AWARE_VERIFY","AGENTIQA_EXPERIMENT_ABSENCE_AWARE_VERIFY"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Absence-assertion verify oracle (issue_742c9da8), a BEFORE\u2192AFTER differential. A verify step that asserts a NEGATIVE (the target is GONE) is verified with the presence-only wait_for_element, whose legitimate timeout on the correctly-absent, PLAN-GROUNDED literal is recorded as an oracle failure and force-FAILS the run at run_complete (canReconcileVerificationConflict refuses the plan-grounded literal \u2014 correct for a PRESENCE assertion, wrong for an absence one). Detection ALWAYS runs (shadow): for a conflicted verify step whose step text / any criterion check asserts THIS TARGET's absence (checkTextAssertsAbsence \u2014 the target literal is stripped first, then an EXPLICIT absence lexeme must survive; a negation inside the target NAME or an incidental 'not' cannot route it) and whose unresolved oracle is a wait-style action with a captured target literal, it classifies CONFIRMED vs ABSTAIN and emits an `absence_verify_oracle:shadow` diag. CONFIRMED requires the wait-literal's OWN before\u2192after transition: (a) GENUINELY ABSENT from a FULL, substantive, NON-canvas, non-load-failure AFTER snapshot retained for that step (never the model's note), AND (b) pinPresentInPage-TRUE in an EARLIER full+whole-page+substantive+same-origin BEFORE snapshot (the presence ledger \u2014 reusing the retained full-snapshot maps), AND every graded criterion passed substantiated. The weak container-noun positive anchor is DROPPED as the load-proof (before-presence + whole-page liveness replaces it); a load-failure/retry interstitial AFTER page is REJECTED (snapshotShowsLoadFailure). When this flag is ON it ENFORCES: a CONFIRMED absence reconciles the conflict to a PASS (via the VERIFY_CONFLICT_RECONCILE clean-pass rail), and an ABSTAIN (no full snapshot / canvas / target still present / load-failure after / NO before-presence \u2014 the target was never shown present / unsubstantiated criteria) soft-withholds the step to a WARNING (never a hard fail, never a clean pass). Off leaves every verdict byte-identical (the plan-grounded absence timeout still force-FAILs) with the shadow diag only. Positive (presence) assertions are untouched (checkTextAssertsAbsence false \u2192 inapplicable \u2192 the existing timeout-fails behavior). Runner lane only (RunnerRuntime run_complete).",designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-07-25",notes:"GRADUATED 2026-07-26 (default ON) \u2014 the FIRST live trust-verdict graduation. Enforcement (reconcile-to-pass on CONFIRMED / soft-withhold-to-warning on ABSTAIN) is now the no-env default; detection had run in shadow since 2026-07-25 (absence_verify_oracle:shadow diag) \u2014 the PIN_PAGE_GROUNDING / VERIFY_REOBSERVE_WITHHOLD shadow-first precedent. GRADUATION EVIDENCE: the kind-agnostic graduation benchmark (#1857, e2e/benchmark/) returned GATE=GO on the absence corpus (4 false-FAILs fixed \u2192 PASS, 0 regressions, 0 new false-PASS, 0 marginal LLM cost) and is adversarially proven able to say NO-GO; a fresh-build re-confirm held (confirmed-absence\u2192PASS fixes #1816 / unconfirmable\u2192WARNING / still-present\u2192no false-PASS; the full engine-core suite is byte-identical ON vs OFF except the graduated verdicts). HARD CONSTRAINT (unchanged): confirm fires ONLY on the wait-literal's OWN before\u2192after transition (present in an earlier same-origin full+substantive+non-canvas snapshot, absent from the non-load-failure after one) read from real page snapshots \u2014 never the model's note nor a disjoint absence lexeme \u2014 so a hallucinated 'it's gone', a never-loaded presence target (compound presence+absence), and a silently-blank list echoing the container noun all abstain to WARNING rather than confirming; no false-PASS is reintroduced. killSwitch resolves the no-env value from this defaultState (#1729), so graduation = defaultState 'on' AND ABSENCE_AWARE_VERIFY listed in INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire); an explicit AGENTIQA_ABSENCE_AWARE_VERIFY=0 still restores the byte-identical pre-graduation force-FAIL behavior. Read site: packages/engine-core/src/RunnerRuntime.ts (absenceAwareVerifyEnabled). Acceptance tests: packages/engine-core/src/__tests__/RunnerRuntime.absenceVerifyFalseFail.test.ts (end-to-end, now green for the right reason) + RunnerRuntime.absenceVerifyHardening.test.ts. Claim runner.absence-assertion-verify is now bound green."},GROUNDED_STATE_VERIFIER:{key:"GROUNDED_STATE_VERIFIER",envVars:["AGENTIQA_GROUNDED_STATE_VERIFIER","AGENTIQA_EXPERIMENT_GROUNDED_STATE_VERIFIER"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Grounded-state verifier \u2014 S1 escalation gate + shadow instrumentation (Tier-2 vision-extraction cost sizing; design docs/plans/2026-07-25-grounded-state-verifier-design.md). SHADOW-ONLY / MEASUREMENT-ONLY: this slice makes NO model call and NEVER alters a verdict, verdict input, or any other diagnostic. When on, the runner's Tier-1 deterministic pin page-grounding oracle (checkPinPageGrounding), at each GROUNDABILITY abstain on a step carrying a countable expectedValue pin \u2014 canvas_dominant, a non-full/incremental a11y snapshot, or a thin/missing snapshot (NOT the transient/ephemeral-text abstain) \u2014 consults the surface-agnostic capture-groundability signal (captureModeGroundsAbsence, keyed on captureMode, NOT a surface-name check) and emits a structured `verifier_escalated` diag {stepIndex, reason, assertionKind, captureMode, wouldNeedTier2:true} for a capture a vision extractor could ground (the genuine Tier-2 candidate), or `verifier_escalation_abstained` {\u2026, wouldNeedTier2:false, floor:'inconclusive'} when even vision cannot ground it (the fail-closed floor \u2014 abstain, never escalate-and-guess). Off \u21D2 byte-identical to today: the escalation diags are not emitted and nothing else changes. Sizes S2's per-verify vision-extraction cost by measuring how often and WHERE Tier-1 abstains on countable state assertions.",designDoc:"docs/plans/2026-07-25-grounded-state-verifier-design.md",status:"active",added:"2026-07-25",notes:"S1 of the grounded-state verifier (measurement slice). Default OFF; SHADOW-ONLY and verdict-inert even when ON \u2014 this slice only emits verifier_escalated / verifier_escalation_abstained shadow diags at the checkPinPageGrounding abstain points, makes no model call, and changes no verdict. The escalation decision keys on captureModeGroundsAbsence (capture fidelity / checkPinPageGrounding outcome), NOT on canvasDominant/surface identity, so a non-canvas vision-groundable surface escalates through the same path (spec AC-7). Read site: packages/engine-core/src/RunnerRuntime.ts (groundedStateVerifierEnabled \u2192 maybeEmitVerifierEscalation, called from checkPinPageGrounding); pure logic in packages/engine-core/src/groundedStateVerifier.ts. Acceptance tests: packages/engine-core/src/__tests__/RunnerRuntime.groundedStateVerifier.test.ts (wiring, flag-OFF byte-identical) + groundedStateVerifier.test.ts (pure groundability keying). S2 (prompt-only extractor + deterministic comparator) is the next slice and adds the actual Tier-2 call behind this same flag.",graduation:{status:"gated",gate:"S1 is measurement-only (no verdict change), so it graduates by FEEDING S2, not by flipping default-on: a staging shadow soak of verifier_escalated / verifier_escalation_abstained sizes the Tier-1-abstain-on-countable-state rate (per reason + captureMode) that S2 (prompt-only vision extractor + deterministic comparator, same flag) is built against. The flag advances to a real verdict path only under S2+ with its own verdict-parity shadow-soak; S1 alone never flips default-on.",evidence:"staging verifier_escalated / verifier_escalation_abstained diag events (escalation rate + reason/captureMode breakdown) + the engine-core RunnerRuntime.groundedStateVerifier + groundedStateVerifier unit suites",owner:"steering (Alex)",review:"2026-08-08"}},GROUNDED_STATE_EXTRACT:{key:"GROUNDED_STATE_EXTRACT",envVars:["AGENTIQA_GROUNDED_STATE_EXTRACT","AGENTIQA_EXPERIMENT_GROUNDED_STATE_EXTRACT"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Grounded-state verifier \u2014 S2 prompt-only vision EXTRACTOR + deterministic presence COMPARATOR (design docs/plans/2026-07-25-grounded-state-verifier-design.md). The sibling flag to GROUNDED_STATE_VERIFIER (S1's free measurement stays independently runnable). SHADOW-ONLY: when on AND a stateExtractor is wired, each S1 escalation candidate (a Tier-1 checkPinPageGrounding abstain on a vision-groundable capture carrying a PRESENCE assertion, deduped one-call-per-step) gets ONE no-task-stake vision extraction at run_complete that enumerates what is on screen into a FIXED schema (objects/text/labels/counts) \u2014 NEVER a verdict (AC-2: the prompt receives no assertion outcome and no pass/fail framing). A DETERMINISTIC comparator then decides presence of the plan-text-derived target against that extraction, reproducibly from the logged extraction + target without re-calling the model (AC-3), and the runtime LOGS the would-be verdict + its PARITY vs the driver's current grade (grounded_state_extract diag). This slice changes NO live verdict. FAIL-CLOSED (Decision 7): any missing image / extractor abstain / non-answer / error / timeout / ambiguous or thin comparison \u2192 INCONCLUSIVE, never a pass. Off \u21D2 zero extraction calls, no candidate collection, byte-identical behavior (AC-6). Cost: one extraction call per escalated presence step, hard-capped (fork E).",designDoc:"docs/plans/2026-07-25-grounded-state-verifier-design.md",status:"active",added:"2026-07-25",notes:"S2 of the grounded-state verifier (prompt-only extractor + deterministic presence comparator). Default OFF; SHADOW-FIRST \u2014 even when ON it only computes and LOGS the would-be presence verdict and its parity vs the driver grade (grounded_state_extract / _start / _done diags), makes at most ONE extraction model call per escalated step (fork-E hard cap), and changes NO verdict or stepResult. Sibling to GROUNDED_STATE_VERIFIER so S1 measurement runs without paying S2 cost. Requires deps.stateExtractor wired (getStateExtractor in apps/execution-engine/src/buildDeps.ts) \u2014 absent \u21D2 inert. Read site: packages/engine-core/src/RunnerRuntime.ts (groundedStateExtractEnabled \u2192 candidate recording in maybeEmitVerifierEscalation, consumed by runGroundedStateExtractions at run_complete); extractor + comparator in packages/engine-core/src/groundedStateExtractor.ts. Acceptance tests: packages/engine-core/src/__tests__/groundedStateExtractor.test.ts (pure prompt/parser/comparator/parity \u2014 AC-2/AC-3) + RunnerRuntime.groundedStateExtract.test.ts (wiring, shadow-no-mutation, fail-closed, one-call cap, seam guard, flag-OFF byte-identical). Binds claim verify.grounded-state-extract-then-compare. S3 (GROUNDED_STATE_DIFFERENTIAL) adds the before\u2192after differential for absence + modification.",graduation:{status:"gated",gate:"S2 is shadow-first (no verdict change). Graduation gates the PRESENCE case only, and only after: (1) a staging verdict-parity shadow soak of grounded_state_extract shows the extract-then-compare would-verdict matching the driver grade on DOM-groundable controls and DISAGREEing (would_fail on a driver-passed step) on the canvas false-pass fixtures (project_canvas_direct_draw_test) that the driver self-grade lets through today; (2) the extractor accuracy on the canvas presence fixtures clears the fork-G bar (else spec the fine-tuned extractor first). Advancing to a LIVE verdict path is a separate step from flipping this flag to shadow-on.",evidence:"staging grounded_state_extract / _start / _done diag events (would-verdict + parity + inconclusive-rate breakdown) + the engine-core groundedStateExtractor + RunnerRuntime.groundedStateExtract unit suites + the claim verify.grounded-state-extract-then-compare",owner:"steering (Alex)",review:"2026-08-15"}},GROUNDED_STATE_DIFFERENTIAL:{key:"GROUNDED_STATE_DIFFERENTIAL",envVars:["AGENTIQA_GROUNDED_STATE_DIFFERENTIAL","AGENTIQA_EXPERIMENT_GROUNDED_STATE_DIFFERENTIAL"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Grounded-state verifier \u2014 S3 BEFORE\u2192AFTER DIFFERENTIAL (absence \u2282 update) for ABSENCE + MODIFICATION (design docs/plans/2026-07-25-grounded-state-verifier-design.md). The sibling flag to GROUNDED_STATE_VERIFIER (S1 measurement) / GROUNDED_STATE_EXTRACT (S2 single-after presence). SHADOW-ONLY: when on AND a stateExtractor is wired, each S1 escalation candidate (a Tier-1 checkPinPageGrounding abstain on a vision-groundable capture carrying an ABSENCE or MODIFICATION assertion, deduped one-call-per-step) runs the S2 no-task-stake vision extraction on BOTH the persisted baseline (before, resolveBaselineMessage) and verify (after, resolveEvidenceMessage) frames \u2014 each with S2's IDENTICAL constant task-blind prompt (AC-2: task-blind on BOTH frames, no assertion / no expected value / no pass/fail framing) \u2014 and a DETERMINISTIC differential comparator decides the verdict from the two extractions + the plan-text-derived target, reproducibly without re-calling the model (AC-3). ABSENCE: target present-before \u2227 absent-after \u2192 would_pass (confirmed_absent); still present-after \u2192 would_fail; before-presence unestablished / after unreadable \u2192 inconclusive. MODIFICATION: a count that changed to the expected value, or a crisp new value that appeared (before-absent + after-present) \u2192 would_pass; unresolvable \u2192 inconclusive. The runtime LOGS the would-be differential verdict + its PARITY vs the driver grade (grounded_state_differential diag); this slice changes NO live verdict. FAIL-CLOSED (Decision 7): any missing-before / unreadable / extractor abstain / error / timeout / ambiguous or unresolvable comparison \u2192 INCONCLUSIVE, never a pass. Canvas is IN scope (the extractor is vision; before/after frames exist via blind-double-read), identified by the plan DESCRIPTOR (fork D1) with an ambiguous match abstaining to inconclusive \u2014 never re-identifying an anonymous object. Off \u21D2 zero candidate collection, zero extraction calls, byte-identical behavior (AC-6). Cost: at most TWO extraction calls per escalated differential step (before + after, fork-E bounded call budget), hard-capped, batched under a deadline.",designDoc:"docs/plans/2026-07-25-grounded-state-verifier-design.md",status:"active",added:"2026-07-25",notes:'S3 of the grounded-state verifier (before\u2192after differential; absence \u2282 update). Default OFF; SHADOW-FIRST \u2014 even when ON it only computes and LOGS the would-be differential verdict and its parity vs the driver grade (grounded_state_differential / _start / _done diags), makes at most TWO extraction model calls per escalated step (before + after; fork-E bounded budget, hard-capped, batched under a deadline \u2192 timeout inconclusive), and changes NO verdict or stepResult. Sibling to GROUNDED_STATE_VERIFIER (S1) / GROUNDED_STATE_EXTRACT (S2). Requires deps.stateExtractor wired (getStateExtractor in apps/execution-engine/src/buildDeps.ts, reused per-frame) \u2014 absent \u21D2 inert. Read site: packages/engine-core/src/RunnerRuntime.ts (groundedStateDifferentialEnabled \u2192 differential-candidate recording in maybeEmitVerifierEscalation, consumed by runGroundedStateDifferentials at run_complete); pure differential comparator in packages/engine-core/src/groundedStateDifferential.ts (reuses S2 groundedStateExtractor). Acceptance tests: packages/engine-core/src/__tests__/groundedStateDifferential.test.ts (pure differential \u2014 absence/modification/fail-closed, #1816 RED\u2192GREEN comparator soak hook + green-guard, AC-2/AC-3) + RunnerRuntime.groundedStateDifferential.test.ts (wiring, before+after two-frame extraction, shadow-no-mutation, fail-closed on missing-before/unreadable/timeout, one-differential-per-step cap, canvas descriptor-match, flag-OFF byte-identical). Binds claim verify.grounded-state-extract-then-compare. S4 (GROUNDED_STATE_INCONCLUSIVE) maps verifier-abstain \u2192 the inconclusive floor. Trust-layer Slice 1 (routing-gap fix) ALSO gates a STATIC single-frame COUNT comparator on this same flag: a count-intent assertion ("exactly 3 shapes" \u2014 authored factKind:count or an NL count check) escalates a count candidate and, at run_complete, runGroundedStateCounts runs ONE task-blind extraction + the deterministic compareCount (extracted 4 \u2260 expected 3 \u2192 grounded_state_count would_fail(after_count_mismatch); 3 = 3 \u2192 would_pass(confirmed_count)); shadow-only, fail-closed, one-call-per-step. Acceptance: RunnerRuntime.groundedStateCount.test.ts + the compareCount/checkTextAssertsCount cases in groundedStateDifferential.test.ts; end-to-end firewall proof eval runner-verify-count-canvas.',graduation:{status:"gated",gate:"S3 is shadow-first (no verdict change). Graduation gates the ABSENCE + MODIFICATION differential and only after: (1) a staging verdict-parity shadow soak of grounded_state_differential shows the #1816 reproduced-RED absence scenario would flip RED\u2192GREEN under the differential (confirmed_absent \u2192 would_pass on a driver-failed step) WITHOUT regressing the paired green-guard fixture (target-still-present \u2192 would_fail / driver-pass preserved); (2) new MODIFICATION fixtures (count + value-appearance) shadow-soak clean; (3) the extractor accuracy on the canvas absence/modification fixtures clears the fork-G bar. Advancing to a LIVE verdict path is a separate step from flipping this flag to shadow-on.",evidence:"staging grounded_state_differential / _start / _done diag events (would-verdict + parity + inconclusive-rate breakdown, #1816 RED\u2192GREEN) + the engine-core groundedStateDifferential + RunnerRuntime.groundedStateDifferential unit suites + the claim verify.grounded-state-extract-then-compare",owner:"steering (Alex)",review:"2026-08-22"}},GROUNDED_STATE_INCONCLUSIVE:{key:"GROUNDED_STATE_INCONCLUSIVE",envVars:["AGENTIQA_GROUNDED_STATE_INCONCLUSIVE","AGENTIQA_EXPERIMENT_GROUNDED_STATE_INCONCLUSIVE"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Grounded-state verifier \u2014 S4 FAIL-CLOSED INCONCLUSIVE FLOOR wiring (Decision 7 + Fork F2; design docs/plans/2026-07-25-grounded-state-verifier-design.md). The sibling flag to GROUNDED_STATE_VERIFIER (S1) / GROUNDED_STATE_EXTRACT (S2) / GROUNDED_STATE_DIFFERENTIAL (S3). SHADOW-ONLY: when on, each verifier ABSTAIN \u2014 the S1 escalation fail-closed floor (verifier_escalation_abstained, a capture even a vision extractor cannot ground) and every S2 presence / S3 differential `inconclusive` outcome (thin/unreadable capture, extractor refusal/error/timeout, before-presence unestablished, after unreadable, ambiguous match, unresolved count) \u2014 is ALSO mapped to the typed inconclusive floor and LOGGED (grounded_state_inconclusive diag): the DISTINCT `verifier_inconclusive:{reason}` sub-reason + the interim `warning` step status (Fork F2 \u2014 a NEUTRAL 'couldn't tell', NOT an alarm-amber defect per project_warning_display_semantics) + the verifierInconclusive marker + migratesTo:'inconclusive'. This slice mutates NO stepResult and changes NO verdict \u2014 it computes/LOGS the would-be floor mapping only. HONESTY FLOORS: an abstain NEVER maps to `passed` (VERIFIER_INCONCLUSIVE_STEP_STATUS is warning/inconclusive, never passed), and a genuine would_pass/would_fail is never floored (inconclusiveFloorForVerdict returns null on a non-inconclusive verdict; the wiring only fires on the S2/S3 abstain paths + the S1 abstained floor). ONE migration point: flip VERIFIER_INCONCLUSIVE_STEP_STATUS (groundedStateInconclusive.ts) from `warning` to step-level `inconclusive` when Layered-Hybrid Phase-1 lands that status in the step enum. Off \u21D2 zero mapping, zero logs, byte-identical (AC-6).",designDoc:"docs/plans/2026-07-25-grounded-state-verifier-design.md",status:"active",added:"2026-07-25",notes:"S4 of the grounded-state verifier (fail-closed inconclusive floor; interim warning-with-distinct-sub-reason, Fork F2). Default OFF; SHADOW-FIRST \u2014 even when ON it only computes and LOGS the would-be inconclusive floor mapping (grounded_state_inconclusive diag), makes NO model call (pure wiring of S1/S2/S3 abstain outcomes), and changes NO verdict or stepResult. Sibling to GROUNDED_STATE_VERIFIER (S1) / GROUNDED_STATE_EXTRACT (S2) / GROUNDED_STATE_DIFFERENTIAL (S3); the abstains it maps only exist when those slices run, so S4 is additive telemetry on top of them. Read site: packages/engine-core/src/RunnerRuntime.ts (groundedStateInconclusiveEnabled \u2192 maybeLogInconclusiveFloor, called at the S1 verifier_escalation_abstained floor in maybeEmitVerifierEscalation + the S2/S3 logInconclusive abstain choke points in runGroundedStateExtractions / runGroundedStateDifferentials); pure mapping in packages/engine-core/src/groundedStateInconclusive.ts (the ONE migration point VERIFIER_INCONCLUSIVE_STEP_STATUS). Acceptance tests: packages/engine-core/src/__tests__/groundedStateInconclusive.test.ts (pure \u2014 each abstain reason \u2192 typed sub-reason \u2192 interim warning-never-passed, distinct-from-product-warning, migration seam, would_pass/would_fail never floored) + RunnerRuntime.groundedStateInconclusive.test.ts (wiring \u2014 flag-OFF byte-identical, shadow-no-mutation, floor logged per S1/S2/S3 abstain, no floor on a would_pass step). Binds claim verify.grounded-state-extract-then-compare. S5 broadens the groundability surface + fine-tune trigger.",graduation:{status:"gated",gate:"S4 is shadow-first (no verdict change) and the INTERIM F2 mapping (warning-with-distinct-sub-reason). Graduation to a LIVE floor is a SEPARATE, later step from flipping this flag shadow-on and requires: (1) a staging shadow soak of grounded_state_inconclusive confirming the abstain\u2192floor breakdown (source / reason / rate) is sane and that no would_pass/would_fail is ever floored (the honesty invariants hold in the field); (2) the S2/S3 verdict-parity soaks having graduated their live-verdict paths (an inconclusive floor is only meaningful once the extract-then-compare verdicts gate); (3) the UI rendering the verifier_inconclusive sub-reason as a NEUTRAL could-not-tell (not alarm-amber). The clean F2\u2192F1 migration (flip VERIFIER_INCONCLUSIVE_STEP_STATUS warning\u2192inconclusive at the ONE point) lands when Layered-Hybrid Phase-1 ships the step-level inconclusive status.",evidence:"staging grounded_state_inconclusive diag events (abstain\u2192floor mapping: source/reason/sub-reason/interim-status breakdown, honesty-invariant field check) + the engine-core groundedStateInconclusive + RunnerRuntime.groundedStateInconclusive unit suites + the claim verify.grounded-state-extract-then-compare",owner:"steering (Alex)",review:"2026-08-29"}},GROUNDED_STATE_FINETUNE_METRIC:{key:"GROUNDED_STATE_FINETUNE_METRIC",envVars:["AGENTIQA_GROUNDED_STATE_FINETUNE_METRIC","AGENTIQA_EXPERIMENT_GROUNDED_STATE_FINETUNE_METRIC"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Grounded-state verifier \u2014 S5 FINE-TUNE TRIGGER METRIC (Fork G: DEFINE the trigger, do NOT fine-tune; design docs/plans/2026-07-25-grounded-state-verifier-design.md). The final sibling flag to GROUNDED_STATE_VERIFIER (S1) / GROUNDED_STATE_EXTRACT (S2) / GROUNDED_STATE_DIFFERENTIAL (S3) / GROUNDED_STATE_INCONCLUSIVE (S4). SHADOW-ONLY: when on AND an S2/S3 shadow batch runs, the runtime ACCUMULATES per-SURFACE outcome counts from the extract-then-compare batches \u2014 extractor-abstain (the prompt-only extractor produced no facts), comparator-inconclusive (facts extracted but no definite verdict), and parity agree/disagree \u2014 bucketed by the escalation reason \u2192 surface class (canvas_dominant\u2192canvas, thin\u2192image_or_svg, incremental/non_full\u2192partial_capture; a DIAGNOSTIC label over the already-recorded reason, NOT a surface-name gate \u2014 the escalation gate stays keyed on captureModeGroundsAbsence, Fork A1). After both batches it LOGS the evaluated fine-tune trigger report against the STATED bar (grounded_state_finetune_metric diag): per surface the extractor-abstain rate, inconclusive rate, disagreement rate, and a would-trigger-fine-tune flag (Fork-G graduation signal, ADVISORY). This makes the prompt-only\u2192dedicated/fine-tuned graduation a DATA read; prompt-only stays v1 (Decision 6) \u2014 this slice invests in NO model, mutates NO stepResult, and changes NO verdict. The metric only has samples to fold when S2 and/or S3 also run. Off \u21D2 zero accumulation, zero logs, byte-identical (AC-6). The STATED bar: per surface, after live graduation, extractor-abstain rate > 0.20 OR inconclusive rate > 0.40 over \u2265 50 escalated extract-then-compares triggers a dedicated/fine-tuned extractor FOR THAT SURFACE (parity-disagreement is reported but NOT a trigger input \u2014 a high disagreement can be the extractor CATCHING driver false-passes, the desired signal).",designDoc:"docs/plans/2026-07-25-grounded-state-verifier-design.md",status:"active",added:"2026-07-25",notes:"S5 of the grounded-state verifier (fine-tune trigger metric; Fork G define-not-fine-tune) and the LAST vision-tier slice. Default OFF; SHADOW-ONLY \u2014 even when ON it only accumulates the per-surface outcome breakdown from the S2/S3 shadow batches and LOGS the evaluated trigger report (grounded_state_finetune_metric diag), makes NO model call (pure metric over existing S2/S3 outcomes), and changes NO verdict or stepResult. Sibling to GROUNDED_STATE_VERIFIER (S1) / GROUNDED_STATE_EXTRACT (S2) / GROUNDED_STATE_DIFFERENTIAL (S3) / GROUNDED_STATE_INCONCLUSIVE (S4); the outcomes it folds only exist when S2/S3 run, so S5 is additive telemetry on top of them. The companion BROADEN half of S5 (SVG/image coverage, spec AC-7 generalized) needed NO gate change \u2014 the S1/S2/S3 path already keys on captureModeGroundsAbsence (Fork A1), so broadening is a fixture/coverage add, proven by the RunnerRuntime.groundedStateBroaden test. Read site: packages/engine-core/src/RunnerRuntime.ts (groundedStateFineTuneMetricEnabled \u2192 the run_complete accumulator threaded into runGroundedStateExtractions / runGroundedStateDifferentials, emitted by emitFineTuneTriggerMetric); pure metric + STATED bar in packages/engine-core/src/groundedStateFineTuneTrigger.ts (FINETUNE_TRIGGER_BAR). Acceptance tests: packages/engine-core/src/__tests__/groundedStateFineTuneTrigger.test.ts (pure surface mapping / fold / rates / bar evaluation) + RunnerRuntime.groundedStateFineTuneMetric.test.ts (wiring \u2014 per-surface fold from S2/S3, flag-OFF byte-identical, shadow-no-mutation) + RunnerRuntime.groundedStateBroaden.test.ts (AC-7 generalized: a non-canvas SVG/image surface escalates + extracts + compares through the identical path). Binds claim verify.grounded-state-extract-then-compare. COMPLETES the S1\u2013S5 vision-tier build; remaining work is soak + graduation flips (Alex).",graduation:{status:"gated",gate:"S5 is shadow-first (no verdict change) and DEFINES the Fork-G fine-tune trigger \u2014 it does not fine-tune. The metric graduates by FEEDING the fine-tune decision, not by flipping default-on: a staging shadow soak of grounded_state_finetune_metric measures each surface (canvas / image_or_svg / partial_capture) extractor-abstain + inconclusive rate against the STATED bar (FINETUNE_TRIGGER_BAR: abstain > 0.20 OR inconclusive > 0.40 over \u2265 50 escalated steps). Investing in a dedicated/fine-tuned extractor for a surface is triggered ONLY when that surface clears the bar AFTER the S2/S3 extract-then-compare has graduated live on it (Decision 6: prompt-only ships regardless). This flag alone never flips default-on.",evidence:"staging grounded_state_finetune_metric diag events (per-surface extractor-abstain / inconclusive / disagreement rate + would-trigger evaluation vs FINETUNE_TRIGGER_BAR) + the engine-core groundedStateFineTuneTrigger + RunnerRuntime.groundedStateFineTuneMetric + RunnerRuntime.groundedStateBroaden unit suites + the claim verify.grounded-state-extract-then-compare",owner:"steering (Alex)",review:"2026-09-05"}},GROUNDED_STATE_NL_TRIGGER:{key:"GROUNDED_STATE_NL_TRIGGER",envVars:["AGENTIQA_GROUNDED_STATE_NL_TRIGGER","AGENTIQA_EXPERIMENT_GROUNDED_STATE_NL_TRIGGER"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Groundability-contract Slice 1 \u2014 un-inert the grounded-state verifier for PINLESS criteria (design docs/plans/2026-07-26-groundability-contract-design.md). The Tier-1 pin page-grounding oracle (checkPinPageGrounding) early-returns when a criterion carries no expectedValue pin, so a real/canvas plan's natural-language {check,strict} assertion never reached the S1 escalation path \u2014 the grounded-state verifier was provably INERT on exactly the canvas surfaces (Miro/Figma) it was built for. When on, a PINLESS verify step whose RUNTIME fact-kind (deriveVerifyAssertionKind) is a non-default state-change kind \u2014 `absence` or `modification` \u2014 routes through the SAME capture-groundability abstain\u2192escalate logic the pinned path uses (maybeEmitVerifierEscalation \u2192 buildVerifierEscalation), keyed on the IDENTICAL captureModeGroundsAbsence gate (a capture even a vision extractor cannot ground abstains to the fail-closed inconclusive floor, never escalate-and-guess). F2 ANTI-FLOOD: the DEFAULT `presence` kind is EXCLUDED \u2014 deriveVerifyAssertionKind defaults to presence for everything, so escalating on presence would flood every pinless ungroundable step; only absence/modification (which the classifier returns deliberately) escalate in Slice 1 (authored count/value are Slice 2). ESCALATION-TRIGGER ONLY: matchType/comparison/expectedValue-as-comparator are untouched, this is shadow/verdict-inert like S1\u2013S5, and it emits nothing on its own \u2014 the escalation diag / candidate still requires GROUNDED_STATE_VERIFIER / _EXTRACT / _DIFFERENTIAL / _INCONCLUSIVE. Off \u21D2 byte-identical: the pinless early-return stands, zero new telemetry, zero behavior change (AC-6).",designDoc:"docs/plans/2026-07-26-groundability-contract-design.md",status:"active",added:"2026-07-25",notes:"Groundability-contract Slice 1 (PR-A). Default OFF; SHADOW-ONLY and verdict-inert even when ON \u2014 it only relocates the S1 escalation TRIGGER for pinless criteria off the expectedValue pin onto the existing runtime fact-kind classifier (deriveVerifyAssertionKind). NO schema change (migration-safe). The escalate-vs-abstain disposition stays keyed on the exact captureModeGroundsAbsence gate reused from the pinned path (GUARDRAIL 1: no pinless bypass). Read site: packages/engine-core/src/RunnerRuntime.ts (groundedStateNlTriggerEnabled \u2192 maybeEmitPinlessNlEscalation, called at the checkPinPageGrounding pinless early-return; routes through the shared maybeEmitVerifierEscalation). Acceptance tests: packages/engine-core/src/__tests__/RunnerRuntime.groundedStateNlTrigger.test.ts (pinless absence/modification escalates on ungroundable canvas/thin/non_full; groundable-mode capture abstains to the inconclusive floor NOT escalate; presence pinless never escalates \u2014 F2 anti-flood; flag-OFF byte-identical; pinned path unchanged). OVERLAP NOTE: ABSENCE_AWARE_VERIFY (#1818, classifyAbsenceVerifyConflict / wait-oracle literal) is a DISTINCT pin-independent absence route \u2014 both are shadow/verdict-inert, so absence carries a DOUBLE shadow signal; do not double-count in soak evidence. Slice 2 adds authored count/value kinds.",graduation:{status:"gated",gate:"Slice 1 is measurement-only (no verdict change): it graduates by FEEDING the same S2/S3 extract-then-compare shadow soak \u2014 a staging shadow soak of verifier_escalated / verifier_escalation_abstained on PINLESS absence/modification steps (canvas/real plans) sizes the escalation rate the vision tier is built against, previously unmeasurable because pinless steps never escalated. The flag advances to a real verdict path only under S2+ with its own verdict-parity shadow-soak; Slice 1 alone never flips default-on. Slice 2 (authored count/value kinds) is a separate gated slice.",evidence:"staging verifier_escalated / verifier_escalation_abstained diag events on pinless absence/modification steps (escalation rate + reason/captureMode breakdown, de-duplicated against the ABSENCE_AWARE_VERIFY absence shadow signal) + the engine-core RunnerRuntime.groundedStateNlTrigger unit suite",owner:"steering (Alex)",review:"2026-08-15"}},GROUNDED_STATE_FACTKIND_AUTHORED:{key:"GROUNDED_STATE_FACTKIND_AUTHORED",envVars:["AGENTIQA_GROUNDED_STATE_FACTKIND_AUTHORED","AGENTIQA_EXPERIMENT_GROUNDED_STATE_FACTKIND_AUTHORED"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Groundability-contract Slice 2 \u2014 consume the MODEL-authored `factKind` as source-of-truth for the grounded-state escalation trigger's assertion shape (design docs/plans/2026-07-26-groundability-contract-design.md). Builds on Slice 1 (GROUNDED_STATE_NL_TRIGGER, pinless escalation on runtime absence/modification). The new OPTIONAL `factKind` enum (value|count|presence|absence|modification|relation) on TestPlanV2Criterion is authored by the generation model (all three producer schemas) as a deliberate typed choice (spawn_agent authMode/authSurfaceKind precedent). When on, a criterion carrying an AUTHORED factKind (a) OVERRIDES the runtime deriveVerifyAssertionKind inference (mapped 6\u21923 for the escalation branch, Fork F1: absence\u2192absence, modification\u2192modification, value/count/presence/relation\u2192presence) AND (b) LIFTS Slice 1's F2 anti-flood filter for that step so ANY authored kind escalates \u2014 authoring IS the deliberate groundable-fact signal \u2014 still gated by the IDENTICAL captureModeGroundsAbsence floor (a capture even a vision extractor cannot ground abstains to the fail-closed inconclusive floor, NEVER escalate-and-guess), routed per collapsed kind (absence/modification \u2192 S3 differential candidate; value/count/presence/relation \u2192 S2 presence-family extraction candidate). This fixes the count/canvas case (authored count \u2192 escalates to S2 extraction) WITHOUT the runtime-presence flood. UNAUTHORED criteria keep Slice 1's runtime path EXACTLY (only absence/modification escalate; the runtime `presence` default does NOT). matchType/comparison/expectedValue-as-comparator UNTOUCHED; SHADOW-ONLY / verdict-inert like S1\u2013S5; it emits nothing on its own \u2014 the escalation diag / candidate still requires GROUNDED_STATE_VERIFIER / _EXTRACT / _DIFFERENTIAL / _INCONCLUSIVE. Off (or factKind absent) \u21D2 byte-identical: the field is unread, Slice 1's runtime behavior stands (AC-6). The field may be authored + persisted with this flag OFF (harmless, unread).",designDoc:"docs/plans/2026-07-26-groundability-contract-design.md",status:"active",added:"2026-07-25",notes:"Groundability-contract Slice 2 (PR-B). Default OFF; SHADOW-ONLY and verdict-inert even when ON. Read sites: packages/engine-core/src/RunnerRuntime.ts (groundedStateFactKindAuthoredEnabled \u2192 authoredFactKind, consumed by deriveVerifyAssertionKind override + the pinless F2-filter lift in maybeEmitPinlessNlEscalation). Field: packages/shared-types/src/index.ts (FactKind + TestPlanV2Criterion.factKind); producer schemas: coordinatorToolDefs.ts test_plan_criteria_schema, agentToolDefs.ts criteria_schema, runnerToolDefs.ts step_with_criteria_schema (thin inline); generation steer: planStepGuidance.ts buildCriteriaFactKindGuidance; 6\u21923 mapping + type-guard: groundedStateVerifier.ts factKindToAssertionKind / isAuthoredFactKind. DROP-SITES registered (else a plain step-text edit silently wipes the authored field \u2014 the matchType bug): renderer finalizeCriterion (apps/desktop-next/.../testPlanStepsSerde.ts) + server carryCriterionPins (apps/web-next/lib/testPlanSaveNormalize.ts) \u2014 both preserve factKind on an UNCHANGED check, drop it on an EDITED check (\u2192 runtime fallback, safe). Acceptance tests: packages/engine-core/src/__tests__/RunnerRuntime.groundedStateFactKindAuthored.test.ts (authored count/presence on ungroundable canvas \u2192 S2 extraction candidate; authored absence \u2192 S3 differential candidate; authored + degraded capture \u2192 inconclusive floor, no escalate-and-guess; flag OFF \u2192 byte-identical field-unread Slice-1 behavior; field absent \u2192 Slice-1 behavior) + drop-site round-trip tests in testPlanStepsSerde.test.ts / testPlanSaveNormalize.test.ts. The MANDATORY real-runtime generation eval + the qa-box claim verify.groundability-contract-authored are Slice-2b (separate follow-on), NOT this PR. OVERLAP NOTE: composes with GROUNDED_STATE_NL_TRIGGER (Slice 1) via the shared maybeEmitPinlessNlEscalation helper.",graduation:{status:"gated",gate:"Slice 2 is measurement-only (no verdict change): it graduates by FEEDING the same S2/S3 extract-then-compare shadow soak, now widened to authored count/value/presence/relation kinds (the count/canvas case Slice 1 could not reach because runtime presence does not escalate). It advances to a real verdict path only under S2+ with its own verdict-parity shadow-soak; Slice 2 alone never flips default-on. Requires the Slice-2b generation eval (real ExplorerRuntime/CoordinatorRuntime authoring the correct factKind) to gate the authoring quality before any graduation.",evidence:"staging verifier_escalated / verifier_escalation_abstained diag events on PINLESS authored-factKind steps (escalation rate + factKind/reason/captureMode breakdown) + the engine-core RunnerRuntime.groundedStateFactKindAuthored unit suite; graduation additionally blocked on the Slice-2b real-runtime generation eval (authoring-distribution baseline)",owner:"steering (Alex)",review:"2026-08-15"}},GROUNDED_STATE_FACTKIND_TYPING_PASS:{key:"GROUNDED_STATE_FACTKIND_TYPING_PASS",envVars:["AGENTIQA_GROUNDED_STATE_FACTKIND_TYPING_PASS","AGENTIQA_EXPERIMENT_GROUNDED_STATE_FACTKIND_TYPING_PASS"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Groundability-contract Slice 2b \u2014 MODEL-authored focused second-pass factKind typing that FIXES the realistic-authoring gap (design docs/plans/2026-07-26-groundability-contract-design.md). Slice 2 (GROUNDED_STATE_FACTKIND_AUTHORED) consumes an authored `factKind`, but the live ExplorerRuntime authors it on ~0% of criteria produced by a realistic multimodal explore (screenshots + long trace + coordinator\u2192explorer spawn = the load that suppresses a deeply-nested OPTIONAL enum) \u2014 MEASURED 0/8 on e2e/evals/plans/groundability-factkind-explored.ts, and the three natural schema fixes (imperative guidance, required field, field reorder) ALL stayed 0%. But a FOCUSED low-load typing call over JUST the drafted check-texts types factKind at 100% (probe 78/78, real gemini-3-flash-preview). When on, AFTER the Explorer's draftTestCase is finalized (assistant_v2_report accept seam, past every re-prompt gate), a SEPARATE model-authored pass (packages/engine-core/src/factKindTypingPass.ts runFactKindTypingPass) collects the verify criteria that LACK an authored factKind, makes ONE batched Gemini call (the session's own model + the REAL buildCriteriaFactKindGuidance the producer schema ships; generateText + Output.object, thinkingBudget:0) that returns {index,factKind}[], and writes the kind back onto each criterion IN PLACE. It is MODEL-authored (a real Gemini call), NOT the rejected deterministic runtime factKind inference. NO OVERWRITE: only a MISSING factKind is filled (an already-authored kind is skipped at collect + double-guarded at write-back). FAIL-SAFE: any error/empty/timeout/invalid-kind leaves factKind UNSET (falls through to runtime inference) \u2014 never crashes authoring, never writes a garbage kind. SHADOW-SAFE: the written factKind is read ONLY by the separately flag-gated GROUNDED_STATE_FACTKIND_AUTHORED verifier (default-OFF), so verdicts are byte-identical whether or not this pass ran. Off \u21D2 the caller never invokes the module: ZERO new model calls, byte-identical authoring (AC-6).",designDoc:"docs/plans/2026-07-26-groundability-contract-design.md",status:"active",added:"2026-07-26",notes:"Groundability-contract Slice 2b (the realistic-authoring FIX; a flag-gated prototype \u2014 adding a model call to the authoring flow is Alex's architecture/graduation call). Default OFF; when ON adds ONE auxiliary Gemini call per COMPLETED explore that produced untyped verify criteria (batched, text-only, cost-isolated via emitAuxiliaryLlmUsage \u2014 not a billable step). Read site: packages/engine-core/src/ExplorerRuntime.ts assistant_v2_report accept seam (killSwitchEnabled('GROUNDED_STATE_FACTKIND_TYPING_PASS') \u2192 runFactKindTypingPass over draftTestCase.steps, mutating criteria in place BEFORE the report message is persisted/emitted, so the authored factKind rides both the saved plan and the diag). Pure module core (collectUntypedVerifyCriteria / buildFactKindTypingPrompt / mapTypesByIndex / applyFactKindTypes) + the single generateText seam. Unit suite: packages/engine-core/src/__tests__/factKindTypingPass.test.ts (ON fills missing factKind from a mocked typing response; OFF = no call / byte-identical; already-authored factKind never overwritten; error/empty response \u2192 factKind stays unset, fail-safe). RE-MEASURE: e2e/evals/plans/groundability-factkind-explored.ts COMMITTED_BASELINE carries the with-typing-pass authored-rate alongside the without (0/8).",graduation:{status:"gated",gate:"The realistic-authoring re-measure (groundability-factkind-explored with GROUNDED_STATE_FACTKIND_TYPING_PASS ON) shows the authored-factKind rate on the real explore path jump from ~0% to high (target near the probe 100%, \u2265~85%) with CORRECT kinds, AND the engine-core factKindTypingPass unit suite green (fill / no-overwrite / fail-safe / off-byte-identical). Graduation to any verdict path additionally requires GROUNDED_STATE_FACTKIND_AUTHORED (the consumer) to graduate under its own shadow-soak \u2014 this pass only PRODUCES the field.",evidence:"e2e/evals/plans/groundability-factkind-explored.ts with-typing-pass measuredAt entry (authored-rate + kind-correctness) + the engine-core factKindTypingPass unit suite + factkind_typing_pass diag events",owner:"steering (Alex)",review:"2026-08-15"}},GROUNDED_STATE_UNIFIED:{key:"GROUNDED_STATE_UNIFIED",envVars:["AGENTIQA_GROUNDED_STATE_UNIFIED","AGENTIQA_EXPERIMENT_GROUNDED_STATE_UNIFIED"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Trust-layer Slice 2 \u2014 PER-SNAPSHOT UNIFICATION (design docs/plans/2026-07-26-trust-layer-slice-2-per-snapshot-unification-design.md; north star docs/plans/2026-07-26-trust-layer-verification-architecture.md). Collapses the three Slice-1 per-kind run_complete batches (runGroundedStateCounts / runGroundedStateExtractions / runGroundedStateDifferentials \u2014 each re-extracting once PER step/kind) into ONE task-blind extraction PER SNAPSHOT feeding ONE typed-comparator dispatch, and introduces the verdict SPECTRUM (Verified / Assessed / Inconclusive) + semantic matchType. SHADOW-ONLY: when on AND a stateExtractor is wired, it consumes the SAME escalation candidates the per-kind batches do (its flag is OR-ed into the maybeEmitVerifierEscalation candidate-recording seam), groups them by shared captured AFTER-frame (groupCandidatesBySnapshot \u2014 verify-steps sharing a frame with no intervening state-mutating action share ONE extraction; differential candidates additionally share ONE run-start baseline extraction), runs ONE extraction per unique frame, and for each candidate runs its typed comparator (compareCount / comparePresence / compareAbsenceDifferential / compareModificationDifferential) \u2014 or, for a matchType:'semantic' criterion, the task-blind concept classifier (deps.conceptClassifier) \u2014 against that shared extraction. It LOGS grounded_state_unified (band + shadowVerdict + parity vs the driver grade) + grounded_state_unified_start/_done (the extraction-vs-comparison counts that PROVE 1-extraction-per-snapshot); it mutates NO stepResult and changes NO verdict. Part B is a REFACTOR that must be verdict-PARITY with the three per-kind paths: the unified would-verdict equals what the retired per-kind batch logged (same evidence resolution + same target derivation + same deterministic comparator on the same extraction). The verdict spectrum: a deterministic comparator would_pass/would_fail \u2192 Verified (the no-false-positive guarantee); a low-confidence semantic result \u2192 Assessed (the SLOT only \u2014 the independent reasoned judge is a LATER slice); no confident extraction/classification / missing image / extractor abstain / error / timeout / unresolvable comparison / unavailable semantic classifier \u2192 the fail-closed Inconclusive floor, NEVER a pass. Task-blindness preserved: the extraction prompt (buildExtractionQuestion) carries observation targets only and the semantic classifier carries a concept + a neutral observation rendering \u2014 NEVER the expected values or pass/fail framing. It runs ALONGSIDE the per-kind batches (its own flag) so the shadow soak can prove parity BEFORE the per-kind methods are physically retired. Off \u21D2 zero candidate consumption here, zero extraction calls, byte-identical (the per-kind paths, if their flags are on, are untouched) (AC-6). Cost: #snapshots (+1 shared before, if any differential) extraction calls per run \u2014 strictly \u2264 the sum of the three per-kind batches, and 1 for N verify-steps on one screen.",designDoc:"docs/plans/2026-07-26-trust-layer-slice-2-per-snapshot-unification-design.md",status:"active",added:"2026-07-26",notes:"Trust-layer Slice 2 (per-snapshot unification). Default OFF; SHADOW-FIRST \u2014 even when ON it only computes + LOGS the would-be verdict-spectrum result and its parity vs the driver grade (grounded_state_unified / _start / _done diags), makes #snapshots (+1 shared before) extraction calls plus one concept-classifier call per semantic candidate (both cost-isolated Flash-model seams via emitAuxiliaryLlmUsage, hard-capped, batched under the same deadline as the per-kind batches), and changes NO verdict or stepResult. Sibling to GROUNDED_STATE_EXTRACT (S2) / GROUNDED_STATE_DIFFERENTIAL (S3) \u2014 it consumes the SAME candidate maps and reuses the SAME comparators, so its would-verdicts are byte-parity with the three per-kind batches (the Slice-2 acceptance gate). Requires deps.stateExtractor wired; a matchType:'semantic' criterion additionally requires deps.conceptClassifier (absent \u21D2 that candidate floors to Inconclusive). Read site: packages/engine-core/src/RunnerRuntime.ts (groundedStateUnifiedEnabled \u2192 the OR-ed candidate recording in maybeEmitVerifierEscalation + collectUnifiedCandidates + runGroundedStateUnified at run_complete); pure core (snapshot grouping / verdict spectrum / semantic mapping) in packages/engine-core/src/groundedStateUnified.ts. Acceptance tests: packages/engine-core/src/__tests__/groundedStateUnified.test.ts (pure \u2014 grouping, spectrum mapping, semantic mapping) + RunnerRuntime.groundedStateUnified.test.ts (wiring \u2014 verdict-PARITY vs the three per-kind batches, ONE extraction for N\u22653 verify-steps on one snapshot, semantic success\u2192Verified / ambiguous\u2192Assessed / error\u2192Verified-FAIL, shadow-no-mutation, flag-OFF byte-identical). Evals: e2e/evals/plans/trust-unified-parity.ts (runner-trust-unified-parity), trust-unified-batching.ts (runner-trust-unified-batching), trust-unified-semantic.ts (runner-trust-unified-semantic). The three per-kind batch methods are RETAINED in this slice as the parity oracle; their physical removal is the graduation follow-up. Binds claim verify.grounded-state-extract-then-compare.",graduation:{status:"gated",gate:"Slice 2 is shadow-first (no verdict change). Graduation gates on: (1) a staging verdict-PARITY shadow soak of grounded_state_unified vs the per-kind grounded_state_count / _extract / _differential diags showing ZERO would-verdict drift across the count / presence / absence / modification fixtures; (2) grounded_state_unified_done confirming extractions == #snapshots (+ shared before), NOT #comparisons (the batching win) in the field; (3) the semantic-matchType Assessed slot behaving (confident \u2192 Verified, ambiguous \u2192 Assessed, contradiction \u2192 Verified-FAIL) at an acceptable concept-classifier accuracy. ONLY after parity is proven do the three per-kind batch methods get physically retired (a separate refactor PR) and does advancing to a LIVE verdict path get considered \u2014 both separate steps from flipping this flag shadow-on.",evidence:"staging grounded_state_unified / _start / _done diag events (spectrum verdict + parity + extraction-vs-comparison counts) cross-checked against the per-kind diags for parity + the engine-core groundedStateUnified + RunnerRuntime.groundedStateUnified unit suites + the trust-unified-parity / -batching / -semantic evals + the claim verify.grounded-state-extract-then-compare",owner:"steering (Alex)",review:"2026-09-05"}},INTERACTION_CLEARS_PRESENCE_ORACLE:{key:"INTERACTION_CLEARS_PRESENCE_ORACLE",envVars:["AGENTIQA_INTERACTION_CLEARS_PRESENCE_ORACLE","AGENTIQA_EXPERIMENT_INTERACTION_CLEARS_PRESENCE_ORACLE"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Interaction-clears-presence-oracle (rank-2 of the step-5 slow-render flake wave, sibling to VERIFY_PRESENCE_WAIT_FLOOR's rank-1 budget raise). A verify-step PRESENCE wait oracle (wait_for_element) that STILL times out after the raised budget records an unresolved oracle failure that verify-gated-done only lets a fresh successful wait_for_element/run_js clear \u2014 a screenshot or a successful click is refused and the plan-grounded literal blocks canReconcileVerificationConflict \u2014 so a genuinely-present-but-slow element force-FAILs (staging sessions asess_1784961071757_cqwxon46 / asess_1784960734951_4t0zaxp1, where the NEXT step's click_at('Blank board') SUCCEEDED and navigated). When on, a subsequent SUCCESSFUL deterministic click_at (or a type_text_at/set_focused_input_value write) on the current verify step or the one immediately preceding it (lookback 1) whose ENGINE-RESOLVED element identity (clickTarget.accessibleName/textContent for a ref/coordinate click, clickedElement.textContent for a label click, or typedIntoField \u2014 the resolved field's accessible name \u2014 for a type write; never the model's narration or the typed VALUE) whole-token-substantiates (citationSubstantiates \u2014 the SAME contiguous-token machinery pinPresentInPage uses, NOT substring) the failed wait's captured target literal CLEARS that step's wait-oracle failure \u2014 engine-observed, un-hallucinable proof of presence, strictly stronger than the screenshot the gate already refuses. HARD CONSTRAINTS (no false-PASS): whole-token resolved-name match only, gated by a SIGNIFICANCE floor (the wait literal must carry a \u22654-char token OR \u22652 tokens \u2014 a bare common single short token like \"ok\"/\"3\" whole-token-matches an unrelated control name too easily, so it can never clear); the #1476 real-hit guard (a coordinate no-op-success \u2014 noObservedEffect side channel \u2014 and a non-interactive pixel landing whose accessibleName merely mirrors a container's textContent are BOTH rejected, so a click that reports success but hit nothing cannot launder a miss); a type only ever clears when it genuinely resolved+focused a named field (typedIntoField populated \u2014 a blind write carries no identity); scope = wait-style presence oracle only (isVerificationOracleAction, NEVER the plan-derived pin-page-grounding oracle) on a Runner verify step; an ABSENCE-intent verify step (checkTextAssertsAbsence over the step text + criteria) is NEVER cleared \u2014 a successful interaction DISPROVES an absence assertion, so clearing would manufacture a pass on a real absence-violation bug; fail-closed (a genuinely-absent element cannot be successfully interacted with, and an ambiguous / non-matching / insignificant-literal / stale-beyond-lookback / absence-intent identity does NOT clear \u2014 the force-fail stands). SHADOW-FIRST: off (default) leaves every verdict identical (the force-fail stands) and only emits an `interaction_clears_presence_oracle:would_clear` diag of what it WOULD have cleared, so a staging soak can confirm it fires only on genuine presence before the flip; on makes the clear live (`\u2026:cleared`).",designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-07-25",notes:"Default OFF in code; detection runs in shadow always (interaction_clears_presence_oracle:would_clear diag), the actual clear is gated \u2014 the ABSENCE_AWARE_VERIFY / PIN_PAGE_GROUNDING shadow-first precedent. killSwitch resolves the no-env value from this defaultState (#1729), so GRADUATING = flip defaultState to 'on' AND add INTERACTION_CLEARS_PRESENCE_ORACLE to INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire). Read site: packages/engine-core/src/RunnerRuntime.ts (interactionClearsPresenceOracleEnabled \u2192 maybeInteractionClearsPresenceOracle, called from the browser-action dispatch after recordOffPlanResolvedClick). Resolved-identity sources: label click \u2192 response.clickedElement.text (clickByLabel only populates it after resolving exactly one clickable control by name AND clicking without error \u2014 un-hallucinable); ref/coordinate click \u2192 response.clickTarget.accessibleName/text, admitted ONLY when isInteractiveClickTarget(clickTarget) so a pixel that landed on a text container cannot launder via the accessibleName\u2192textContent fallback; type write (type_text_at / set_focused_input_value) \u2192 response.typedIntoField (getFocusedFieldName \u2014 the accessible name of the field the write actually resolved+focused; the typed VALUE is never an identity). SIGNIFICANCE floor (waitLiteralHasSignificantTokens over the same tokenizeCitation basis the match uses): a bare common single short token (a status word, a lone digit) whole-token-matches an unrelated resolved name too easily, so the wait literal must carry a \u22654-char token OR \u22652 tokens or it never clears. Lookback is intentionally tight (1 step) to keep a stale REAL miss from an earlier step from being laundered by a same-named element that appears much later; a soak may widen it. Absence-intent verify steps are excluded via checkTextAssertsAbsence (a successful interaction disproves an absence assertion \u2192 would be a false-PASS); a suppressed match emits interaction_clears_presence_oracle:absence_skip. Acceptance tests: packages/engine-core/src/__tests__/RunnerRuntime.interactionClearsPresenceOracle.test.ts (flag-OFF shadow-only + byte-identical for both click and type, flag-ON label + ref + type clear, type resolved-different-field reject, type-with-no-resolved-field reject, significance-floor bare-one-token reject, #1476 no-op reject, non-interactive container-text reject, non-matching-element reject, whole-token-not-substring, genuinely-absent still-fails, absence-intent step NEVER cleared, pin-page-grounding scope guard, cross-step lookback bound).",graduation:{status:"gated",gate:"Staging shadow soak clean (interaction_clears_presence_oracle:would_clear fires ONLY where a successful click genuinely resolved+acted on an element whose engine-resolved identity whole-token-matches the failed wait literal \u2014 never on a #1476 coordinate no-op, a non-interactive container-text mirror, an absence-intent verify step, or a stale failure beyond the 1-step lookback) AND the engine-core interaction-clears-presence-oracle detection-inversion passes (flag OFF \u2192 the timed-out presence step still force-FAILs; flag ON \u2192 a matching successful click clears it to a PASS, a no-op/non-matching/absent/absence-intent element still FAILs)",evidence:"staging interaction_clears_presence_oracle:would_clear / :cleared diag events (clear rate + resolvedVia/literal breakdown) + the RunnerRuntime.interactionClearsPresenceOracle unit suite + the step-5 slow-render rehearsal replay (asess_1784961071757_cqwxon46 / asess_1784960734951_4t0zaxp1)",owner:"steering (Alex)",review:"2026-08-01"}},CANVAS_STRATEGY:{key:"CANVAS_STRATEGY",envVars:["AGENTIQA_CANVAS_STRATEGY","AGENTIQA_EXPERIMENT_CANVAS_STRATEGY"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Enables the canvas-app (Miro/Figma/spreadsheet) per-turn strategy prompt injection and the coordinate-action tool-result caveat; off drops both so a false canvas classification cannot alter targeting guidance.",designDoc:"docs/plans/2026-07-06-canvas-capability-design.md",status:"active",added:"2026-07-06"},SCREENSHOT_DIRECT_UPLOAD:{key:"SCREENSHOT_DIRECT_UPLOAD",envVars:["AGENTIQA_SCREENSHOT_DIRECT_UPLOAD","AGENTIQA_EXPERIMENT_SCREENSHOT_DIRECT_UPLOAD"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'Analytics-sink screenshot upload path: on, the sink fetches a presigned R2 PUT URL from /api/analytics/presign-screenshot and PUTs the PNG straight to R2 (bytes never transit web-next); "0" forces the legacy base64 /api/analytics/upload-image route. On any presign/PUT failure the sink falls back to the legacy route per-screenshot regardless of this switch.',designDoc:"packages/engine-core/src/sinks/RemoteAnalyticsSink.ts",status:"active",added:"2026-07-19"},SCOPE_SAME_ORIGIN_LINKS:{key:"SCOPE_SAME_ORIGIN_LINKS",envVars:["AGENTIQA_SCOPE_SAME_ORIGIN_LINKS","AGENTIQA_EXPERIMENT_SCOPE_SAME_ORIGIN_LINKS"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:`Scope-gate valve for page-authored links: a clicked href on a scoped ORIGIN whose path falls outside every scope prefix is allowed (reason same_origin_link, with a scope-drift diag) instead of hard-blocked as an "external link". Cross-origin targets and model-typed navigate URLs keep the hard block. Prevents an LLM-guessed scope prefix from blocking the app's own navigation (assertqa /why-assertqa false 404, 2026-08-05); "0" restores the unconditional prefix block.`,designDoc:"docs/plans/2026-08-05-scope-observed-url-grounding.md",status:"active",added:"2026-08-05"},SCOPE_SPAWN_GROUNDING:{key:"SCOPE_SPAWN_GROUNDING",envVars:["AGENTIQA_SCOPE_SPAWN_GROUNDING","AGENTIQA_EXPERIMENT_SCOPE_SPAWN_GROUNDING"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'Deterministic scope-authoring grounding at the spawn boundary: a same-origin spawn scope entry that no child ever visited (observed-URL ledger, runtime-observed only) and the user never supplied is bounced back to the Coordinator ONCE with the observed-URL list to re-author from; an insisted repeat proceeds fail-open. Also stops checkpoint surfaces with unobserved same-origin URLs from being persisted into the durable AppMap. Pure set-membership on parsed URLs \u2014 no label matching or slug heuristics; "0" disables both the bounce and the AppMap filter.',designDoc:"docs/plans/2026-08-05-scope-observed-url-grounding.md",status:"active",added:"2026-08-05"},SAME_GOAL_ABORT:{key:"SAME_GOAL_ABORT",envVars:["AGENTIQA_SAME_GOAL_ABORT","AGENTIQA_EXPERIMENT_SAME_GOAL_ABORT"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Escalates consecutive milestone-free supervisor REDIRECT verdicts into an abort-then-block, bounding a stuck same-goal step; off leaves the supervisor redirecting until the iteration budget runs out.",designDoc:"docs/plans/2026-07-06-canvas-capability-design.md",status:"active",added:"2026-07-06"},TARGET_CONTAINMENT:{key:"TARGET_CONTAINMENT",envVars:["AGENTIQA_TARGET_CONTAINMENT","AGENTIQA_EXPERIMENT_TARGET_CONTAINMENT"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'Keeps the agent within the app-under-test origin at every navigation chokepoint; off makes every boundary check return "allow" (pre-containment behavior).',designDoc:"packages/engine-core/src/BasePlaywrightService.ts",status:"active",added:"2026-07-05"},INCIDENTAL_EXTERNAL_LOGIN_RECOVERY:{key:"INCIDENTAL_EXTERNAL_LOGIN_RECOVERY",envVars:["AGENTIQA_INCIDENTAL_EXTERNAL_LOGIN_RECOVERY","AGENTIQA_EXPERIMENT_INCIDENTAL_EXTERNAL_LOGIN_RECOVERY"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Necessity-gated split of the third-party-IdP boundary, in BOTH lanes: with NO login credentials configured, an external login reached on a DIFFERENT registrable domain than the app under test AND judged task-INCIDENTAL is RECOVERED \u2014 the service returns the page to the app (popup close \u2192 history back \u2192 pre-navigation app URL \u2192 app origin, every leg bounded at 5s), injects an agent-visible note that names the host, forbids re-following it AND warns that the return trip reset the page's in-page state, and the run continues. Unattended (RunnerRuntime.startRun) that replaces terminating the run as exploration_blocked (staging asess_1785402865447_25cimyx8: a \"check all the buttons\" run followed a social footer link to instagram.com/accounts/login and lost 49 recorded actions); in the interactive CHAT lane (Coordinator/Explorer, and the runner's own sendMessage) it replaces the ask-user credentials pause (staging asess_1785434018769_usq42s45: the self-agent's inner \"Direct task\" Coordinator run blocked on the same host with reason:'interactive_session'). TWO LAYERS: the registrable-domain rule is only the fail-closed floor; before any recovery the service consults the NECESSITY judge (externalLoginNecessityJudge.ts, Layer 2 of the uncertain-boundary class, engine-owned Flash model, at most ONE call per external host per run) with the active plan step \u2014 or, in the chat lane, the turn's task/objective text (BaseRuntime.necessityContextText). FAIL-SAFE onto the pre-existing terminal for `required`, `uncertain`, confidence < 0.8, a judge error, and NO judge wired at all (desktop / no engine key \u21D2 the whole feature is inert) \u2014 so a human who genuinely needs to hand over SSO credentials still gets the pause, and only logins the task does not need stop interrupting them. Bounded at 3 recoveries per run via ONE counter shared by both lanes; a 4th hit, a recovery that lands back on an external login, or a SAME-registrable-domain login (the app's own SSO) keeps today's terminal block verbatim. GRADUATED default-ON 2026-07-30 (gates G1-G4): with NO env both lanes now recover a confidently-`incidental` login and the judge is consulted in the would-recover branch. An explicit `=0` under either spelling restores the pre-graduation terminal verbatim in BOTH lanes and spends NO judge call, while the decision is still computed and emitted as `incidental_external_login` diagnostics (with `wouldRecover`, the domain-signal-only upper bound across BOTH lanes \u2014 pair it with `unattended`; plus the necessity verdict/confidence/reason whenever a judge ran). Recovery reasons are lane-labeled (`incidental` unattended / `chat_incidental` interactive); the pre-2026-07-30 `interactive_session` terminal reason is retired. Never fires when credentials are configured (the pre-existing suppress) and never sees a same-product cross-environment escape (targetContainment runs first).",designDoc:"docs/plans/2026-07-06-uncertain-boundary-escalation-design.md",status:"active",added:"2026-07-30",notes:"GRADUATED 2026-07-30 (default ON) \u2014 graduation gate v2 (PR #1969), four attestations; the first graduation whose effect is to REMOVE a whole-run terminal rather than add a gate. killSwitch resolves the no-env value from this defaultState (#1729), so graduation = defaultState 'on' AND INCIDENTAL_EXTERNAL_LOGIN_RECOVERY listed in INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire); an explicit AGENTIQA_INCIDENTAL_EXTERNAL_LOGIN_RECOVERY=0 (or the AGENTIQA_EXPERIMENT_ spelling) still restores the byte-identical pre-graduation terminal \u2014 `exploration_blocked` unattended, the ask-user credentials pause in chat \u2014 with ZERO judge calls. GRADUATION EVIDENCE. G1 (necessity-discrimination eval PAIR, deterministic + local): e2e/evals/plans/incidental-external-login-recovery.ts \u2014 6 legs, both necessity directions, 2/2 green with the REAL gemini-3-flash judge (incidental @0.90 / required @1.00 on the SAME external host) and 1/1 green with the deterministic stub judge. G2 (suites): engine-core incidentalExternalLogin (L1 decision matrix incl. the necessity fold-in AND the chat lane: judge-incidental recovers as `chat_incidental`, required/uncertain/unjudged keep the pause, shared cap), BasePlaywrightService.incidentalExternalLogin (L2 wiring over a real headless browser and synthetic hosts, stubbed judge through the production DI seam, both directions x both lanes + per-host cache + no-judge fail-safe + one shared counter + the forced-OFF parity cases, which are now built by forcing `=0` and never by deleting the key), RunnerRuntime.unattendedRunLifecycle (the necessity-context channel: plan step in a run, task text in chat, undefined when blank), externalLoginNecessityJudge (judge fail-safe). G3 (bounded live staging validation, flag force-ON via the staging orchestrator env): self-agent.yml GH run 30572972048 (2026-07-30, the SECOND G3 attempt) \u2014 3/3 PASSED including ci-first-plan end-to-end (signup -> inner directed run completed -> Save -> re-run of the saved plan). The FIRST attempt failed informatively: the inner run is an assistant_v2 Coordinator session, so the recovery was lane-gated OFF and the run blocked exactly as before the flag existed \u2014 that is what motivated the chat-lane extension and forced the re-run. G4 (telemetry): a LIVE unattended-lane recovery on the saved-plan re-run \u2014 `incidental_external_login {route:'popup_closed', reason:'incidental', idpHost:'www.instagram.com', outcome:'recovered', returnedTo:'https://agentiqa.com/en/pricing', unattended:true, recoveryCount:1, pageStateReset:false, necessityReason:\u2026}` in asess_1785438775291_qqz9uae4 \u2014 a real judge, a real third-party login wall, the popup route; 0 recovery loops, 0 nav-timeout fallbacks, 0 misfires. HONEST RESIDUAL \u2014 the CHAT lane is live-UNEXERCISED: in that run the external site showed the inner run no login wall at all (it redirected to X instead \u2014 a nondeterministic third party), so no live `chat_incidental` recovery was observed. Chat-lane behavior is pinned by HERMETIC evidence only (the G1 eval's chat leg plus the L1/L2 chat-lane cases), which is the one non-live leg of this graduation. SCENARIO-19 CONSEQUENCE: agent-core/oauth-idp-redirect-no-creds-pause used to pass for TWO independent reasons (the flag was staged OFF, and its prompt IS the task-REQUIRED SSO shape); post-graduation only the second holds, so that nightly scenario is now a live assertion that the necessity judge answers `required` on it (G1 measured confidence 1.00 on exactly that shape). The staging orchestrator force-ON override (AGENTIQA_EXPERIMENT_INCIDENTAL_EXTERNAL_LOGIN_RECOVERY=1, hand-set for G3) is redundant once this ships and is scheduled for removal \u2014 see docs/FLAGS-STATE.md. STAGING-FIRST: merging to staging makes this default-ON on the STAGING engine only; PROD stays OFF until a later staging->main release carries it (a natural staged soak). Read site: packages/engine-core/src/BasePlaywrightService.ts (recoverIncidentalExternalLoginIfNeeded). Binds claim agent.incidental-external-login-recovery."},PIN_SUBSTANTIATION:{key:"PIN_SUBSTANTIATION",envVars:["AGENTIQA_PIN_SUBSTANTIATION","AGENTIQA_EXPERIMENT_PIN_SUBSTANTIATION"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'Flips a criterion the model graded "passed" to failed when the pinned expected value is not substantiated; off returns the model grade verbatim (pins still render, enforcement is off).',designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-07-07"},PIN_CROSS_STEP_SUBSTANTIATION:{key:"PIN_CROSS_STEP_SUBSTANTIATION",envVars:["AGENTIQA_PIN_CROSS_STEP_SUBSTANTIATION","AGENTIQA_EXPERIMENT_PIN_CROSS_STEP_SUBSTANTIATION"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:`FIX 3 (extends FIX 2 to the cross-STEP misattribution class). Before Amendment 5a synthesizes a strict pinned criterion as "never graded \u2192 unconfirmed \u2192 fail", it searches EARLIER step entries for an ORPHAN grade of the IDENTICAL check \u2014 graded passed=true, bound to NONE of its own step's plan criteria, and whose observed value substantiates THIS pin's expectedValue (the SAME substantiatePinnedCriterion gate). A match means the grading LLM misfiled the pin under a PRECEDING/action step (prod asess_1785211658727: step 2's 3 strict pins graded inside step 1, an action step), so the pin was really evaluated \u2014 the substantiated pass is relocated to its own slot instead of false-failing the step. Off restores FIX 2 behavior verbatim (no cross-step search). The rescue is SCOPED to misattributed grades, not a blanket 'a passing grade exists elsewhere': (1) only orphan grades qualify \u2014 a step's OWN bound verdict is never borrowed, so a still-passing earlier verify cannot mask a later regression; (2) a contradicting FAILING grade for the same check anywhere blocks the rescue; (3) only a STRICTLY EARLIER step's grade qualifies, so a later step's page state is never laundered backward onto an earlier assertion (a backward misattribution stays fail-closed); (4) the caller only rescues a pin unique among plan verify criteria. Outside that envelope the pre-existing fail-closed synthesis stands. EVIDENCE-EPOCH DISPOSITION (steering 2026-07-28): 'strictly earlier' does not mean 'same page state', so a rescue is only a silent PASS when NO state-changing plan step (type action/setup) sits STRICTLY BETWEEN the rescuing entry and the pin's own step \u2014 the confirmed prod shape (verify step k's grades misfiled under the immediately preceding action step k-1) has nothing in between, so it keeps its full PASS rescue. A STALE-FORWARD rescue (an intervening action/setup broke the epoch, so the observation may predate a regression) is instead SOFT-WITHHELD to a step-level 'warning' \u2014 the criterion carries the substantiated pass noted 'substantiated by an earlier-step grade; not re-verified at this step' and the step is demoted passed->warning (never a hard fail, never a clean pass; run status is untouched since only 'failed' steps downgrade a run). This is the ABSENCE_AWARE_VERIFY-ABSTAIN / GROUNDING_EPOCH_FIDELITY withhold rail: the criterion stays passed:true on purpose, because flipping it would route deriveStepStatusFromCriteria to a hard 'failed' on a strict criterion (warning-cap-only). Every accepted rescue emits RunnerRuntime log pin_cross_step_substantiated {stepIndex, sourceStep, expected, orphanCandidates, corpusSize, reason} for prod frequency/provenance, where reason is 'same-epoch-rescue' (PASS) or 'stale-forward-warning' (withheld). SHARED BINDER (round 4): the orphan test (guard 1) READS the already-computed per-entry bindings from bindGradesToPlanSlots \u2014 the ONE authoritative two-pass binder that also drives FIX 2's cross-entry union and the reported criteria results \u2014 and never re-derives them. An earlier revision ran its own SEQUENTIAL bindGradeToPlanIdx loop, which diverges from the two-pass binder wherever a rephrased grade's positional fallback would steal a slot a later grade matches by exact text: the authoritative binder books that grade onto its own step (non-orphan) while the sequential mirror leaves it unbound (orphan) and thus eligible to rescue another step's pin \u2014 an EMERGENT false-PASS reachable only with PIN_CROSS_STEP_SUBSTANTIATION and CRITERION_BIND_RESIDUAL both on, which each flag's own tests miss. Pinned by a 4-cell flag-matrix test. PER-CRITERION FLOOR (round 4): the rescue pushes a synthetic passed result, which switched OFF the whole-step zero-grade 'unsubstantiated verify' floor (that floor tests criteriaResults.length === 0) for the step's OTHER criteria \u2014 and Amendment 5a itself only covers PINNED strict criteria, so a plain strict criterion beside a rescued pin was adjudicated by nothing and rode through on the reported 'passed'. On steps where a rescue fired, each plan criterion that is in neither the FIX 2 union nor Amendment 5a's coverage AND has no matching grade anywhere in the cross-step corpus now synthesizes its own failure (strict) or warning (strict:false), emitting pin_cross_step_sibling_unsubstantiated {stepIndex, check, strict, rescuedPinsOnStep}. Scoped to rescue-touched steps so no untouched verdict moves, and gated on 'ungraded ANYWHERE' so the whole-step misattribution the rescue tolerates is not re-punished.`,designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-07-28"},CHECK_TEXT_ATOM_PIN:{key:"CHECK_TEXT_ATOM_PIN",envVars:["AGENTIQA_CHECK_TEXT_ATOM_PIN","AGENTIQA_EXPERIMENT_CHECK_TEXT_ATOM_PIN"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"run_complete grade-time atom pinning: a strict verify criterion whose check text pins exactly one URL literal (http(s):// or bare localhost) and carries no expectedValue has that literal promoted to an effective expectedValue so the pin-substantiation / grounding machinery fires on it, plus a deterministic URL-host floor that fails the criterion closed (naming both hosts) when the evidence observes a different-host URL and preserves the pass on a scheme-only / trailing-slash difference \u2014 regardless of what the substantiation/drift judge decided. Off leaves a bare {check, strict} URL criterion ungraded past the model self-grade (pre-feature behavior).",designDoc:"docs/plans/2026-07-19-ag7727-run-fidelity-fixes-design.md",status:"active",added:"2026-07-19"},TYPED_MATCH:{key:"TYPED_MATCH",envVars:["AGENTIQA_TYPED_MATCH","AGENTIQA_EXPERIMENT_TYPED_MATCH"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Routes pin-substantiation's observed-vs-expected comparison through the typed match-comparator registry keyed on a criterion's `matchType` (country ISO-3166 fold DE\u2261Germany, locale numeric equality); a typed match keeps a pass the brittle literal compare would have flipped, a typed mismatch (wrong country) flips closed. Off (or an absent/`literal` matchType) restores the pure literal `citationSubstantiates` behavior verbatim \u2014 inert until a criterion carries a matchType, so this only removes a false-fail class, never changes an untyped verdict (AG-7753).",designDoc:"docs/plans/2026-07-20-typed-match-comparator-design.md",status:"active",added:"2026-07-20"},COUNTRY_EQUIV_RESCUE:{key:"COUNTRY_EQUIV_RESCUE",envVars:["AGENTIQA_COUNTRY_EQUIV_RESCUE","AGENTIQA_EXPERIMENT_COUNTRY_EQUIV_RESCUE"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'C2 country-equivalence rescue in run_complete pin substantiation: when an already model-PASSED strict criterion\'s literal citation fails, the citation is retried with every recognized country surface form on BOTH sides folded to its ISO-3166 alpha-3, so an address that differs ONLY by country NAME vs CODE ("\u2026H\xF6henkirchen Germany" pin vs "\u2026H\xF6henkirchen DE" observed, prod Lio tp_5a2a7f4c step 9) substantiates instead of brittle-failing. Narrowly scoped: the rescue runs only for a country-SHAPED pin \u2014 `matchType: \'country\'`, or (untyped/literal/text-normalized/presence) a pin whose TERMINAL token is a full country NAME or alpha-3; a bare terminal alpha-2 pin ("Dover, DE") and every number/currency/url/semantic matchType are excluded. The fold itself only rewrites a country NAME anywhere, and a country CODE only as an uppercase token in the terminal country slot (never before a US zip), with subdivision/unit collisions (CA/NL/GB/CH/IE/PL/PT/SE/IN/CAN/NOR/\u2026) restricted to a sole-token read. That extra ambiguity is FOLD-SCOPED (`FOLD_AMBIGUOUS_ALPHA2`/`FOLD_AMBIGUOUS_ALPHA3`): the shared gazetteer read by citedCountry/compareTyped \u2014 i.e. the typed-atom floor and the predicate-basis typed comparator, neither of which this switch gates \u2014 is untouched BY THIS FLAG, verified by a differential sweep of the whole gazetteer. Off restores the pure literal `citationSubstantiates` verdict, so this flag only ever removes a false-FAIL class and nothing this flag contributes survives turning it off. NOTE (AG-8212, 2026-07-29/30) \u2014 that is a statement about THIS flag, not about the comparator stack as a whole: BOTH free-text country reads have since gained their own ungated guard (the same eight collision-prone alpha-2 codes are now gated in `citedCountry` \u2014 the observed scan, direction 1 \u2014 and in `authoredCountryRead` \u2014 the typed-atom floor\'s check-text read, direction 2 \u2014 so a bare code buried in prose resolves a country only when a full NAME corroborates it or when it is the sole token), which this switch does not gate and cannot revert. The fold path and both fold-ambiguity sets remain pre-C2 byte-identical. Split out of TYPED_MATCH (which stays inert on untyped criteria) because this rescue fires on criteria that carry NO matchType.',designDoc:"docs/plans/2026-07-20-typed-match-comparator-design.md",status:"active",added:"2026-07-28"},TYPED_ATOM_FLOOR:{key:"TYPED_ATOM_FLOOR",envVars:["AGENTIQA_TYPED_ATOM_FLOOR","AGENTIQA_EXPERIMENT_TYPED_ATOM_FLOOR"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"AG-7753 Phase 2 strict-unpinned typed-atom floor. run_complete detection ALWAYS runs (shadow): for a strict verify criterion the model graded passed that carries NO expectedValue and whose check text names exactly one recognized typed atom (country ISO-3166 / currency ISO-4217 / number \u2014 the generalization of #1685's URL host floor), the page-read observed value (a11y snapshot, else the structured `observed` grade field; disagreement \u2192 abstain) is compared to the recognized expectation via compareTyped. On a same-type MISMATCH (France where Germany expected) it emits a shadow `typed_atom_floor:would_fail` diag; when this flag is ON it ENFORCES \u2014 flipping the criterion passed\u2192false (only-fails, never originates a pass) and emitting `typed_atom_floor:fire`. Off leaves every verdict byte-identical (shadow diag only). URL stays owned by CHECK_TEXT_ATOM_PIN \u2014 a URL-bearing check is not recognized here.",designDoc:"docs/plans/2026-07-20-typed-match-comparator-design.md",status:"active",added:"2026-07-20",notes:"GRADUATED 2026-07-21 (default on) via the evidence-count doctrine: detection-inversion PROVEN twice \u2014 deterministically in RunnerRuntime.typedAtomFloor.test.ts (33 assertions) and live in the runner/typed-atom-country-floor L3 eval (qa-exhaustive 29861680933: seeded wrong-country step correctly failed) after the self-testing fixture deploy was unblocked (productionBranch was pinned to main). Shadow soak was clean but thin (organic staging plans carry no recognizable unpinned atoms \u2014 vacuous-soak class). Enforcement only-fails a same-type mismatch, never originates a pass. Distinct from TYPED_MATCH (Phase 1, default ON, pinned-criterion fold) and CHECK_TEXT_ATOM_PIN (#1685, URL host floor). Runner lane only (RunnerRuntime run_complete)."},COMPLETION_EVIDENCE_FLOOR:{key:"COMPLETION_EVIDENCE_FLOOR",envVars:["AGENTIQA_COMPLETION_EVIDENCE_FLOOR","AGENTIQA_EXPERIMENT_COMPLETION_EVIDENCE_FLOOR"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Completion-evidence floor for the CHAT/CLI objective lane (the ungrounded-ship seam VERIFY_GATED_DONE misses: it fires only on a failed wait-oracle in the last 5 actions, so a fabricated value with no failing wait ships silently). Detection ALWAYS runs (shadow) at ExplorerRuntime.handleReport on a terminal `completed` report: for an evidence-bearing objective (find/copy/extract a named value) it grounds the agent's TYPED CLAIM (the optional `extractedValues` field on the assistant_v2_report payload; free-text is never parsed): a claimed value found nowhere in the runtime a11y-snapshot corpus nor in runtime_observed revealed facts (whole-token `citationSubstantiates` \u2014 free-form; `compareTyped`'s closed grammar does NOT apply) emits a shadow `completion_grounding:would_demote` diag; a completed extraction objective with NO typed claim is shadow-only signal (reason no_typed_claim) and is NEVER enforced. Label-presence in the corpus never substantiates a value (v1.1). When this flag is ON it ENFORCES the typed-claim-fabrication path only \u2014 attaching an Explorer `VerificationConflict` with the NEW source `completion_ungrounded` (own card copy; NO terminal blockKind) that the existing verification-conflict rail (`applyVerificationConflictFindings` / `focusedTaskVerdictRecommendation`) demotes to do_not_ship in BOTH Coordinator producers. ABSTAINS (never demotes) when the objective is not evidence-bearing, the target is undecidable, or the observed corpus is thin/absent (canvas/off-DOM/dynamic value not in the snapshot) \u2014 demote-on-absence must abstain on any evidence-availability gap (blind-double-read precedent). Off leaves every verdict byte-identical (shadow diag only). Distinct from TYPED_ATOM_FLOOR (runner lane, closed-grammar value mismatch); this is the chat-lane grounding net (absence of any observed evidence, not value-correctness).",designDoc:"docs/plans/2026-07-21-completion-evidence-floor-design.md",status:"active",added:"2026-07-21",notes:"Default OFF in code; detection runs in shadow always (would_demote diag), enforcement is gated \u2014 the PIN_PAGE_GROUNDING / TYPED_ATOM_FLOOR shadow-first precedent. Single Explorer-level hook (handleReport) so both Coordinator verdict producers surface it (the cross-producer parity bug class, PR #643). Chat/Explorer lane only (assistant_v2_report). v1.1: enforcement requires a typed extractedValues claim proven absent from corpus+facts (provable fabrication); strict-extraction recognizer with common-UI-word stoplist (generic read/find objectives abstain); no-typed-claim path stays shadow-only so the soak measures claim-population rate. Grounds fabrication of a claimed value, NOT mis-selection of a real-but-wrong on-page value.",graduation:{status:"gated",gate:"Staging shadow soak clean (would_demote fires on the wandering-maze fabricate-and-ship replicates, zero would_demote on legitimately-shipping value-extraction controls) AND the engine-core completion-evidence-floor detection-inversion passes (floor OFF \u2192 fabricate-and-ship rides through as ship, ON \u2192 do_not_ship on the identical input in BOTH producers)",evidence:"staging completion_grounding:would_demote diag events + the engine-core completion-evidence-floor unit suite + the loop-detection/wandering-self-stop flag-ON graduation run",owner:"steering (Alex)",review:"2026-07-23"}},SETUP_NOTE_GROUNDING:{key:"SETUP_NOTE_GROUNDING",envVars:["AGENTIQA_SETUP_NOTE_GROUNDING","AGENTIQA_EXPERIMENT_SETUP_NOTE_GROUNDING"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Setup/action step-note grounding \u2014 surface S-A of the substantiation-fidelity (\"honest words\") track. Detection ALWAYS runs (shadow) at RunnerRuntime run_complete, after substantiation + the typed-atom floor: for each setup/action step (verify steps out of scope) graded passed/warning whose NOTE names a completed UI action (a closed past-tense/gerund verb lexicon \u2014 entered/typed/filled/submitted/clicked/selected/uploaded/dragged/\u2026) it checks the step's own planStepIndex-stamped action-tool window; a note asserting an action with ZERO grounding action tool calls in-window (the census shape asess_1784714866561_jhc34ht9 \u2014 pre-authed login steps graded green with 'Entered the email'/'Submitted the login form' notes and no type_*/click ever fired) emits a shadow `narration_fidelity:would_flag{surface:'setup_note', stepIndex, claim, missing_action, toolCallsInWindow}`. ABSTAINS (never flags) on a verify/read-only step, a note with no action verb, a non-terminal grade, or a window carrying ANY grounding action tool \u2014 ambiguity always abstains (closed allowlist). When this flag is ON it ENFORCES honest note substitution \u2014 the fabricated action clause is replaced with an honest precondition note; the step's verdict/status is UNTOUCHED (never a hard fail \u2014 the precondition was met, just not by the asserted action). Off leaves every stepResults note byte-identical (shadow diag only). Distinct from TYPED_ATOM_FLOOR (verify-criterion value mismatch) and COMPLETION_EVIDENCE_FLOOR (chat-lane claimed-value fabrication); this grounds SETUP/ACTION step NARRATION against the action log.",designDoc:"docs/plans/2026-07-22-substantiation-fidelity-design.md",status:"active",added:"2026-07-22",notes:"Default OFF in code; detection runs in shadow always (would_flag diag), enforcement (honest note substitution) is gated \u2014 the COMPLETION_EVIDENCE_FLOOR / TYPED_ATOM_FLOOR shadow-first precedent. Single RunnerRuntime run_complete pass (mirrors the typed-atom floor shadow). Deterministic \u2014 ~zero marginal LLM cost (reads the already-collected stepResults notes + the planStepIndex-stamped action-message log). P0 of the substantiation-fidelity track (S-A); S-B/S-C/S-D are separate per-phase flags.",graduation:{status:"gated",gate:"Staging shadow soak clean (narration_fidelity:would_flag{surface:'setup_note'} fires on the census-shaped reproduced-RED fixture, zero would_flag on legitimately-honest action notes whose tool call fired) AND the engine-core setupNoteGrounding detection-inversion passes (rip the pass out \u2192 the fixture stops flagging; enforce-ON rewrites the note, verdict untouched, on the identical input)",evidence:"staging narration_fidelity:would_flag diag events + the engine-core setupNoteGrounding + RunnerRuntime.setupNoteGrounding unit suites + the runner/narration-ghost-setup reproduced-RED eval",owner:"steering (Alex)",review:"2026-07-29"}},GROUNDING_EPOCH_FIDELITY:{key:"GROUNDING_EPOCH_FIDELITY",envVars:["AGENTIQA_GROUNDING_EPOCH_FIDELITY","AGENTIQA_EXPERIMENT_GROUNDING_EPOCH_FIDELITY"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Criterion grounding-epoch fidelity \u2014 surface S-B of the substantiation-fidelity (\"honest words\") track. Detection ALWAYS runs (shadow) at RunnerRuntime run_complete, after substantiation + grounding + the typed-atom floor: for each verify-step criterion substantiated as PASSED it compares the criterion's grounding epoch (the monotonic capture/generation index at which its pinned value was actually groundable in a full page snapshot) against the step's OWN verify-time epoch. A value groundable ONLY in a STRICTLY-LATER capture \u2014 a subsequent step's / a different entity's snapshot \u2014 is cross-entity-deferred grounding (the census asess_1784714866561_jhc34ht9 step 6: base-draft pins pin_page_grounding:would_fail at the step's own epoch, then matched off the duplicate-draft (e674/e731) and submitted-request (e1337) snapshots, all past the base-draft epoch) and emits a shadow `narration_fidelity:would_flag{surface:'grounding_epoch', stepIndex, criterion, groundingEpoch, stepEpoch, epochDriftRefs}`. ABSTAINS (never flags) on a value grounded at/before the step's own epoch (honest same-epoch grounding, or a legitimately-earlier carried observation), an explicitly carried-forward observation with recorded provenance, a criterion that did not pass, or an UNDETERMINED epoch (a missing epoch never manufactures a flag). When this flag is ON it ENFORCES via the existing VERIFY_REOBSERVE_WITHHOLD soft-withhold rail: the cross-entity-grounded criterion is treated as UNCONFIRMED and routed to a warning (never a red fail \u2014 the values may be correct; the fix removes the false EVIDENCE ATTRIBUTION, not the pass). Off leaves every verdict byte-identical (shadow diag only). Distinct from PIN_PAGE_GROUNDING (value ABSENCE at grade time) and SETUP_NOTE_GROUNDING (setup/action step NARRATION); this grounds a verify criterion's EVIDENCE-EPOCH provenance.",designDoc:"docs/plans/2026-07-22-substantiation-fidelity-design.md",status:"active",added:"2026-07-23",notes:"Default OFF in code; detection runs in shadow always (would_flag diag), enforcement (soft-withhold to warning via VERIFY_REOBSERVE_WITHHOLD) is gated \u2014 the COMPLETION_EVIDENCE_FLOOR / SETUP_NOTE_GROUNDING shadow-first precedent. Single RunnerRuntime run_complete pass (mirrors the setup-note-grounding shadow). Deterministic \u2014 ~zero marginal LLM cost (reconstructs each step's own capture epoch + each pinned value's grounding epoch from the retained per-generation full snapshots collected during the run). P1 of the substantiation-fidelity track (S-B); S-A (SETUP_NOTE_GROUNDING) shipped P0, S-C/S-D are separate flags.",graduation:{status:"gated",gate:"Staging shadow soak clean (narration_fidelity:would_flag{surface:'grounding_epoch'} fires on the census-shaped A\u2192duplicate-B reproduced-RED fixture, zero would_flag on same-epoch honest grounding controls) AND the engine-core groundingEpochFidelity detection-inversion passes (rip the drift out \u2192 the fixture stops flagging; enforce-ON soft-withholds the criterion to warning, never red, on the identical input)",evidence:"staging narration_fidelity:would_flag diag events + the engine-core groundingEpochFidelity + RunnerRuntime.groundingEpoch unit suites + the runner/narration-grounding-epoch reproduced-RED eval",owner:"steering (Alex)",review:"2026-07-30"}},NOTE_CONTRADICTION_FLOOR:{key:"NOTE_CONTRADICTION_FLOOR",envVars:["AGENTIQA_NOTE_CONTRADICTION_FLOOR","AGENTIQA_EXPERIMENT_NOTE_CONTRADICTION_FLOOR"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Terminal-error verdict-honesty Layer A \u2014 the deterministic note-contradiction floor (run_27825c90). Detection ALWAYS runs (pure module) at RunnerRuntime run_complete, LAST \u2014 after every fail gate: for a step the model graded `passed` carrying a passed criterion whose OWN note/groundingObservation text contains a conservative error marker (error/failed/failure/timeout/timed out/aborted/exception/went wrong, word-boundary) that the criterion's own check/expectedValue (or its step text) does NOT license, it caps that step to `warning` with an explanatory note. A marker GOVERNED BY A NEGATOR in the same clause (no/not/never/without/none/zero/didn't/no longer/free of/\u2026 \u2014 see NEGATOR_RE) is BENIGN and never caps ('No error appeared', 'verified no timeout occurred'), so a note that negates every marker is skipped; a note that re-asserts an error after negating one ('no error at first, then Error: timeout appeared') still caps. ONE-DIRECTIONAL \u2014 only caps a passed step (never rescues/escalates a model-failed grade; the criterion result stays passed, the STEP status is soft-withheld like VERIFY_REOBSERVE_WITHHOLD). Check-text licensing is dumb-string (accepts the documented `verify NO error` negation blind spot on the CHECK \u2014 Layer B handles it semantically). This flag ALSO gates the deterministic report_issue verdict-bearing fold (decision 6): a high/medium-severity `logical` issue filed during an otherwise-clean `passed` run caps the run at `warning`. When ON it ENFORCES; off \u21D2 `note_contradiction:would_cap` / `report_issue_contradiction:would_cap` diags and every verdict byte-identical. Distinct from Layer B (TERMINAL_ERROR_FLOOR, a model terminal-screen read). Ships default-ON (the trap eval's warning-cap signature only greens with both floors live).",designDoc:"docs/plans/2026-07-23-terminal-error-verdict-honesty-design.md",status:"active",added:"2026-07-23",notes:"Ships default-ON from inception (not graduated from an off default) \u2014 registered in killSwitchDefaultState.test.ts INTENTIONALLY_GRADUATED. Deterministic (~zero marginal LLM cost \u2014 scans the already-collected stepResults notes + grounding observations). Pure logic in packages/engine-core/src/noteContradictionFloor.ts; applied at RunnerRuntime run_complete AFTER all fail gates so it only ever touches a still-passed step. Runner lane only. Layer A of the terminal-error wave; TERMINAL_ERROR_FLOOR is Layer B."},TERMINAL_ERROR_FLOOR:{key:"TERMINAL_ERROR_FLOOR",envVars:["AGENTIQA_TERMINAL_ERROR_FLOOR","AGENTIQA_EXPERIMENT_TERMINAL_ERROR_FLOOR"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Terminal-error verdict-honesty Layer B \u2014 one unconditional terminal-screen model read per plan run (run_27825c90). At RunnerRuntime run_complete (LAST, after every fail gate) the terminal capture plus the plan's step texts are sent through the existing `deps.blindReader` seam, asked whether an app error/failure state is visible that no plan step asserts. The terminal frame is selected DELIBERATELY from the in-memory `_screenshots` ledger (NOT R2 \u2014 works in the anonymous eval lane where imageStorage is null): the LAST full-frame capture (crops and post-upload shots are skipped; each push site is kind-tagged), abstaining when none exists or when it predates the last browser action (stale). Licensing is SEMANTIC (the reader sees the step texts, so it handles negation like `verify NO error is shown`). An unlicensed visible error \u21D2 cap the run at `warning` by demoting the highest-index PASSED step (via capRunToWarning \u2014 never a skipped/failed step; when a sibling floor already capped the terminal step, the quote is appended, not double-capped) and quote the error text in the step note + run summary. Reader unavailable / no full-frame / stale frame / non-answer / licensed error \u21D2 abstain no-op (never a new false-fail). ONE-DIRECTIONAL (only caps a still-passed step; the binary run status never flips \u2014 `warning` is the step-level/derived aggregate). When ON it ENFORCES; off \u21D2 `terminal_error:would_cap` shadow, no cap. This is the ONLY layer that fires when the model never transcribes the banner into any note (the incident shape). Distinct from Layer A (NOTE_CONTRADICTION_FLOOR, deterministic note scan). Ships default-ON.",designDoc:"docs/plans/2026-07-23-terminal-error-verdict-honesty-design.md",status:"active",added:"2026-07-23",notes:"Ships default-ON from inception \u2014 registered in killSwitchDefaultState.test.ts INTENTIONALLY_GRADUATED. Cost \u2248 one cheap Flash vision call per plan run (the cost-isolated blindReader model, same as BLIND_DOUBLE_READ). Detection (the read) runs whenever a reader + final capture are available so the switch shadows (`terminal_error:would_cap`) when off; a later cost-driven change could guard the call on the flag. Pure prompt/interpretation in packages/engine-core/src/terminalErrorFloor.ts. Runner lane only. Layer B of the terminal-error wave; NOTE_CONTRADICTION_FLOOR is Layer A + the report_issue fold."},BLIND_DOUBLE_READ:{key:"BLIND_DOUBLE_READ",envVars:["AGENTIQA_BLIND_DOUBLE_READ","AGENTIQA_EXPERIMENT_BLIND_DOUBLE_READ"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"When on, run_complete independently re-reads the persisted evidence screenshot for each pinned criterion that survived substantiation as passed (a blind LLM read that never sees the expected value) and flips the pass to failed when the observed value does not match the pin; abstains (no-op) on missing image / R2 read failure / reader non-answer / batch timeout. Off makes zero extra LLM calls and leaves every verdict unchanged.",designDoc:"docs/plans/2026-07-15-blind-double-read-design.md",status:"active",added:"2026-07-15",notes:"Default OFF in code; forced ON in staging via the orchestrator env (AGENTIQA_EXPERIMENT_BLIND_DOUBLE_READ=1) \u2014 the exact GROUNDED_EXPECTATIONS precedent. Prod enablement is a later explicit flip after nightly baselines. Runner lane only (RunnerRuntime run_complete).",graduation:{status:"gated",gate:"HARDENED 2026-07-21: BDR must actually FIRE (reads>0) and FLIP on runner/blind-read-conflation-trap \u2014 not merely avoid errors. BLOCKED on the evidence-resolution gap: on the conflation trap BDR abstained missing_image (resolveEvidenceMessage found no hasScreenshot+planStepIndex message \u2014 the model graded from the inline tool-result snapshot, which is never persisted as a screenshot message) and the seeded false-pass shipped (qa-exhaustive 29861680933). Fix = guarantee a planStepIndex-stamped verify screenshot (STEP_MARKER_FOLD stamping path is the natural vehicle \u2014 same root as the run-detail evidence-fidelity gap) or broaden resolveEvidenceMessage fallback. Plus: no new false-FAIL class on nightly runner baselines (MET as of 07-21; abstains acceptable). tp_00b325d5 confirm-path re-verified 07-21 (2 reads/2 confirms/0 flips).",evidence:"runner/blind-read-{parroting,conflation}-trap eval verdicts (conflation must flip) + blind_double_read diag events + nightly runner baselines",owner:"steering (Alex)",review:"2026-07-28"}},NEVER_GRADED_RETRY:{key:"NEVER_GRADED_RETRY",envVars:["AGENTIQA_NEVER_GRADED_RETRY","AGENTIQA_EXPERIMENT_NEVER_GRADED_RETRY"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:'When on (and a criterionRegrader is wired at engine boot), run_complete re-asks the grader ONCE for a STRICT pinned required criterion that NO stepResults entry graded (genuine model-omission \u2014 absent from the FIX 2 cross-entry union) before Amendment 5a synthesizes its "never graded \u2192 unconfirmed \u2192 fail". The returned grade is fed through the SAME substantiatePinnedCriterion gate, so only a re-grade that independently CITES the pinned value rescues the pin to passed; a returned FAIL, an unsubstantiated pass, or an abstain (no image / read failure / non-answer / error / batch timeout) leaves the "never graded" fail byte-identical. Cap ONE retry per pin, no loops. Off makes zero extra LLM calls and leaves every verdict unchanged.',designDoc:"docs/plans/2026-07-27-never-graded-retry-design.md",status:"active",added:"2026-07-27",notes:"Default OFF in code; force ON in staging via the orchestrator env (AGENTIQA_EXPERIMENT_NEVER_GRADED_RETRY=1) \u2014 the BLIND_DOUBLE_READ / GROUNDED_EXPECTATIONS shadow-first precedent. Prod enablement is a later explicit flip after a benchmark + staging soak. Runner lane only (RunnerRuntime run_complete, buildRunnerDeps.getCriterionRegrader \u2014 the SAME cost-isolated Flash model as blindReader; child Runners on the web-coordinator lane do not receive it yet, mirroring blindReader's own coordinator-forward gap). COMPOSES with (does not regress) PR #1884 FIX 2: the retry fires ONLY on pins genuinely absent from the cross-entry union FIX 2 computes, i.e. exactly the omission FIX 2 deliberately left as a fail. killSwitch resolves the no-env value from this defaultState (#1729), so GRADUATING = flip defaultState to 'on' AND add NEVER_GRADED_RETRY to INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire). Read site: packages/engine-core/src/RunnerRuntime.ts (neverGradedRetryEnabled \u2192 runNeverGradedRetries). Acceptance tests: packages/engine-core/src/__tests__/RunnerRuntime.neverGradedRetry.test.ts.",graduation:{status:"gated",gate:"FIRST SLICE = build + unit (this PR): shadow behind the default-OFF flag, byte-identical when off, fail-closed-safe (retry never manufactures a pass; a genuinely-ungraded-after-retry pin still fails). GRADUATION (later, separate slices) needs: (1) a kind-agnostic graduation benchmark (e2e/benchmark/, the ABSENCE_AWARE_VERIFY #1857 precedent) showing the Lio-s2 omission false-FAIL class is rescued to PASS with 0 new false-PASS and 0 regressions on the runner corpus, adversarially proven able to say NO-GO; (2) a staging shadow/force-ON soak measuring re-grade fire rate + rescue vs abstain vs still-fail; (3) parity when BLIND_DOUBLE_READ is also on; (4) gate-review sign-off (Alex).",evidence:"never_graded_retry:{start,rescue,abstain,still_fail} diag events + the graduation benchmark verdict + a staging soak on re-grade outcomes",owner:"steering (Alex)",review:"2026-08-03"}},EVIDENCE_FIDELITY:{key:"EVIDENCE_FIDELITY",envVars:["AGENTIQA_EVIDENCE_FIDELITY","AGENTIQA_EXPERIMENT_EVIDENCE_FIDELITY"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"P0 of the evidence-fidelity ('honest pictures') design: makes full_page_screenshot honest. When on, BasePlaywrightService.fullPageScreenshot climbs a capture ladder (document_full \u2192 cdp_full \u2192 scroll_stitch \u2192 viewport_degraded) so an inner-overflow layout (asklio line-items, where the document is viewport-height but the scrollable content lives in an inner overflow:auto container) is captured at its true extent instead of a silent viewport crop; the ONLY non-full path stamps EnvState.captureMode 'viewport_degraded' and emits the full_page_capture:degraded marker \u2014 never a silent lie. Off is byte-identical to today EXCEPT the geometry measurement + a shadow degradation marker still run (detect-only) so staging can measure the lie's live frequency before the capture behavior flips. EnvState.captureMode is set in BOTH states.",designDoc:"docs/plans/2026-07-22-verify-evidence-fidelity-design.md",status:"active",added:"2026-07-22",notes:"Default OFF in code; force ON in staging via the orchestrator env (AGENTIQA_EXPERIMENT_EVIDENCE_FIDELITY=1) \u2014 the GROUNDED_EXPECTATIONS / BLIND_DOUBLE_READ precedent. Prod flip is a later explicit step after nightly baselines + a shadow soak on the degradation marker. killSwitch resolves the no-env value from this defaultState (#1729), so GRADUATING = flip defaultState to 'on' AND add EVIDENCE_FIDELITY to INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire). Read sites: (P0) packages/engine-core/src/BasePlaywrightService.ts fullPageScreenshot \u2192 the honest capture ladder; (P1) packages/engine-core/src/RunnerRuntime.ts \u2192 per settled static-verify batch, capture ONE canonical honest full_page_screenshot, derive planStepIndex-stamped per-criterion crops (region resolved off the graded node; uncropped fallback), and persist a StepEvidenceRef on each TestPlanV2StepResult/CriterionResult at run_complete. Flag OFF is byte-identical to today EXCEPT the P0 shadow degradation marker: no canonical capture, no refs. P1 supersedes the run-detail findStepEvidenceIndex heuristic on runs that carry refs (legacy no-ref runs fall back). P2 (flip BLIND_DOUBLE_READ on the planStepIndex-stamped evidence P1 now produces) is the remaining follow-up gated on this.",graduation:{status:"gated",gate:"P0/P1 reproduced-RED evals green with the flag ON (runner-fullpage-honesty capture-mode/height/marker facts; degraded-abstain unit; P1 wrong-region + stamping-integrity) AND a staging shadow soak on the full_page_capture:degraded marker showing the expected live frequency with NO capture regression on batch.verify-never-blind",evidence:"full_page_capture:degraded diag events (staging shadow soak) + the engine-core capture-honesty integration test + the captureFidelity unit suite + the batch.verify-never-blind regression",owner:"steering (Alex)",review:"2026-07-29"}},PER_ACTION_BILLED_STEPS:{key:"PER_ACTION_BILLED_STEPS",envVars:["AGENTIQA_PER_ACTION_BILLED_STEPS","AGENTIQA_EXPERIMENT_PER_ACTION_BILLED_STEPS"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Decouples the billed step count from the LLM-iteration count (W1 of the batched-actions design, Decision 6). When on, the main-loop agent_step llm_usage event carries an additive billedUnits integer = executed browser actions + verify captures this turn (a marker-only signal_step turn earns 0); ingest sums it into run_billing.step_count. Off omits the field entirely, so ingest bills one step per agent_step event \u2014 byte-identical behavior AND billing to pre-change. The only intended billing delta when on (today, one-action-per-turn) is that marker-only iterations bill 0 instead of 1; a batched turn executing k actions will bill k (W3).",designDoc:"docs/plans/2026-07-18-batched-actions-design.md",status:"active",added:"2026-07-18",notes:"GRADUATED 2026-07-21 (default on): staging run_billing parity check over the force-ON window (since 07-18) held exactly \u2014 116/116 flag-ON runs with step_count == executed actions + verify captures, 99/99 marker-only iterations billed 0; billing.batched-steps-parity unit lane green. Billing substrate for the batched-actions family (W1). Read site: packages/engine-core/src/billedUnits.ts (perActionBilledStepsEnabled). Remove the staging orchestrator env var once this reaches staging."},STEP_MARKER_FOLD:{key:"STEP_MARKER_FOLD",envVars:["AGENTIQA_STEP_MARKER_FOLD","AGENTIQA_EXPERIMENT_STEP_MARKER_FOLD"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Phase 1 of the batched-actions design (Decision 3): when on, the RunnerRuntime run-mode prompt instructs the model to emit the signal_step boundary marker TOGETHER with the signaled step's FIRST action in the SAME turn \u2014 but ONLY for setup/action steps; VERIFY steps keep the separate marker turn so evidence is never captured blind. This collapses the ~18.7% of runner iterations that are marker-only today. HARD COUPLING: inert unless PER_ACTION_BILLED_STEPS is ALSO enabled \u2014 a folded turn under iteration-billing would bill 1 where marker+action should bill 2, breaking billing parity. The code guard (stepMarkerFold.ts::stepMarkerFoldEnabled = STEP_MARKER_FOLD && PER_ACTION_BILLED_STEPS) makes the fold prompt byte-identical to pre-change whenever PER_ACTION_BILLED_STEPS is off, so this flag alone changes nothing. No engine dispatch change: the multi-call loop already runs signal_step before the folded action in order (both in one generation the model saw the same screen), and billedUnits (W1) already counts marker+action as 1 billed unit.",designDoc:"docs/plans/2026-07-18-batched-actions-design.md",status:"active",added:"2026-07-18",notes:"Default OFF in code. Prompt-only change (no engine dispatch or verify-flow change). Read site: packages/engine-core/src/stepMarkerFold.ts (stepMarkerFoldEnabled), consumed in RunnerRuntime.buildRunnerPrompt run-mode signal_step cadence directive. The AND-coupling with PER_ACTION_BILLED_STEPS lives in code, not just here: flipping STEP_MARKER_FOLD=1 while PER_ACTION_BILLED_STEPS stays off is a no-op.",graduation:{status:"gated",gate:"Default-off soak on staging, then a staging flip (with PER_ACTION_BILLED_STEPS on) proves per-step verdict counts identical to OFF and marker-only iterations drop from ~18.7% to <7% (batch.step-attribution-preserved)",evidence:"batch.step-attribution-preserved unit lane + staging marker-only-iteration % soak measurement",owner:"steering (Alex)",review:"2026-07-25"}},WARNING_CRITERION_BIND:{key:"WARNING_CRITERION_BIND",envVars:["AGENTIQA_WARNING_CRITERION_BIND","AGENTIQA_EXPERIMENT_WARNING_CRITERION_BIND"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'RETIRED 2026-08-04 by the typed criterion reference. It gated the render tiers of the TEXT binder \u2014 strip the platform-appended " (warning only)" suffix, resolve {{unique}}/{{timestamp}} \u2014 so a failing warning-only grade could bind by text and keep its authored strict:false instead of being forced strict:true by the ordinal fallback, plus the C1 structural gate that decided when an ORDINAL-bound failing grade could keep an explicit strict:false. A grade now names its criterion by index (`criteriaResults[].criterionIndex`), so there is no text tier to gate and no ordinal fallback to fail closed against: a bound reference simply takes the criterion authored flag, an unbound one takes strict:true. Setting this env does nothing. Entry kept for provenance only; the switch has no read site.',designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"deprecated",added:"2026-07-16"},CRITERION_BIND_RESIDUAL:{key:"CRITERION_BIND_RESIDUAL",envVars:["AGENTIQA_CRITERION_BIND_RESIDUAL","AGENTIQA_EXPERIMENT_CRITERION_BIND_RESIDUAL"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"RETIRED 2026-08-04 by the typed criterion reference. It chose between the two-pass binder (text bindings claim their slot first, then a positional residual pass) and the sequential one whose ordinal fallback could steal a slot a later grade matched by exact text \u2014 a grade-ORDER lottery over identical observations. Both passes are gone: a grade states which criterion it graded, so no slot is ever assigned by position and the order the model lists its grades in decides nothing. Setting this env does nothing. Entry kept for provenance only; the switch has no read site.",designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"deprecated",added:"2026-07-28"},CRITERIA_CONTAINMENT:{key:"CRITERIA_CONTAINMENT",envVars:["AGENTIQA_CRITERIA_CONTAINMENT","AGENTIQA_EXPERIMENT_CRITERIA_CONTAINMENT"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"GRADUATED 2026-08-03 (default ON) \u2014 flipped OFF\u2192ON because the class it contains is LIVE ON PROD and is what the verdict-replay gate is red on. Closed-world containment on run_complete: a step's verdict is decided ONLY by criteria the PLAN authored. A graded criterion result that binds to NO authored slot of its step (the grading model invented it) is still persisted \u2014 marked `unauthored: true` for forensics \u2014 but `deriveStepStatusFromCriteria` skips it, so a fabricated strict failure can no longer decide a step. Live prod-shaped defect (canary tp_vrgate_offer_upload_retry step 13, ~2/9 replicates): the step authors exactly 2 criteria (Description \"Montage und Einweisung\", Unit \"St\xFCck\"), both graded PASS with correct grounding, and the model additionally graded a THIRD criterion \u2014 `Order line 2 Quantity is \"St\xFCck\"`, strict:true, observed \"1\" \u2014 that exists in neither the plan nor the DB; that invented failure hard-failed the run. Detection ALWAYS runs, in BOTH states: at the default the engine excludes and logs `criteria_containment:excluded` {stepIndex, criterionText, strict, observed, wouldFlipStep, authoredCriteria, gradedCriteria}; with an explicit `=0` under either spelling every verdict is byte-identical to pre-graduation staging and the engine logs `criteria_containment:would_exclude` with the SAME payload \u2014 so an operator who kills the class keeps the measurement. `wouldFlipStep` was the graduation evidence and remains the incidence metric \u2014 it is true only where the exclusion actually moves the step status, so a fabricated PASSING grade stays pure telemetry in either state. THREE DELIBERATE NARROWINGS keep this rescue-polarity gate from opening a false-PASS channel: (1) it fires only on a step that AUTHORED \u22651 criterion \u2014 grades filed under a criteria-less action/setup step are the cross-step MISFILE class, where today's fail-closed treatment of the unbound grade is the only adjudication that failure gets; and (2) only when EVERY authored criterion of that entry received a grade, so an unbound grade can never be excluded while an authored slot went unadjudicated (it would then plausibly BE that slot's grade under a heavy rephrase). NARROWING (2b) \u2014 TEXT-JUSTIFIED COVERAGE, added 2026-08-03 after adversarial verification of PR #2153 found a LAUNDERING vector: (2) counted a slot as graded no matter HOW it was covered, and `bindGradesToPlanSlots` PASS 2 assigns leftover slots in GRADE ORDER, so a FABRICATED grade emitted BEFORE a rephrased real one takes that real criterion's slot, coverage reads complete, and the grade left unbound (and excluded) is the REAL one. Reproduced by execution on head 2dc7691f4: criteria [Description strict, Unit strict] + grades [fabricated \"Order line 2 Quantity looks plausible\" PASS, Description exact PASS, Unit REPHRASED FAIL observed \"kg\"] booked the run `passed` at the graduated default versus `failed` at `=0`, on 7 of 8 realistic rephrase forms (reworded, units added, whitespace, case, smart quotes, DE/EN, truncated; only the platform's own \" (warning only)\" suffix still bound by text), with the diag itself reporting wouldFlipStep:true. The replay gate could NOT see it \u2014 the exclusion consumes exactly one unit of surplus, so (C1) 2<=2, (C2) overreach 0 and (B) were all green and buildReport returned ok:true on a laundered run. `evaluateCriteriaContainmentEligibility` therefore refuses exclusion whenever ANY authored slot was covered by PASS 2's positional residual rather than a PASS 1 `matchedByText` binding: a positional binding is ordinal-only evidence (the C1 strict-honoring gate already refuses to trust it for the strict flag, and containment is strictly stronger since it DELETES the grade). The refusal is entry-wide rather than per-slot \u2014 an honest reorder is indistinguishable from slot theft without reading grade text \u2014 so both keep the fail-closed treatment, and the engine logs `criteria_containment:refused` {stepIndex, reason:'positional-only-coverage', criterionText, strict, observed, wouldFlipStep, positionalSlots, authoredCriteria, gradedCriteria} in BOTH flag states, where wouldFlipStep:true means laundering was PREVENTED. ACCEPTED COST, pinned by test: a genuine fabrication sitting beside an honest positional binding is no longer contained \u2014 a false FAIL, never a false PASS. Both frozen incident runs bind BOTH authored slots by exact text, so the graduation class is untouched (asserted end-to-end through handleToolCall and through the replay gate's own buildReport: `=0` -> ok:false with plan_verdict_regression x2 + criteria_over_grading x2; default -> ok:true, engineContainedResults 2, containmentOverreachSteps 0). Identification reuses the ONE authoritative binder \u2014 `bindGradesToPlanSlots` per-entry output (text tiers incl. the platform's \" (warning only)\" rewrite, then residual positional) \u2014 never a new text comparison: measured over 76 persisted corpus runs, 58 of 60 text-unbound results were exactly that legal rewrite, so a text-equality containment rule would be ~97% false positives. A THIRD narrowing closes the DUPLICATE-READ false-PASS channel (review round 2026-07-29): the binder claims slots EXCLUSIVELY, so a SECOND grade of the SAME authored criterion binds to nothing and unguarded containment would discard it \u2014 and when the two reads contradict ([passed:true, then passed:false observed \"\u20AC35.00\"] for one authored total) the discarded one is the FAILING one, passing the step on a record whose \"unauthored\" criterionText is byte-identical to the authored check. Before excluding, `findDuplicateReadCriterionIdx` re-runs the binder's own text tiers over ALL authored criteria with the claimed set IGNORED; a match means duplicate READ, containment REFUSES to exclude, the grade keeps its flag-OFF effect (the step can still fail \u2014 the correct polarity for a contradicting observation), and the engine logs `criteria_containment:duplicate_read` {stepIndex, criterionIndex, criterionText, passed, wouldHaveExcluded} in BOTH flag states. RESIDUAL: identification is a TEXT relation, so a REPHRASED contradicting second read (\"Der Gesamtbetrag lautet \u2026\") matches no tier and is still excluded under the flag \u2014 the shadow soak's would_exclude population must be reviewed for that shape before graduation. That residual is the DUPLICATE shape (one authored criterion graded TWICE, the paraphrased second read dropped) and is DISTINCT from the positional-slot-theft class narrowing (2b) closes, where the authored criterion is graded exactly ONCE and that single grade is discarded. GATE HALF (detector, no verdict effect): `criteria_over_grading` in e2e/scripts/verdict-replay-gate.mjs, PR #1934, split in two at this graduation because the gate reads a PERSISTED array in which the excluded result is still present. (C1) counts only the results that still DECIDE \u2014 `criteriaResults.filter((cr) => cr.unauthored !== true).length <= step.criteria.length` \u2014 so a contained fabrication is no longer a violation while an UNMARKED surplus is the pre-graduation violation verbatim (identical on every legacy run and under `=0`, where nothing is marked). (C2) `criteria_containment_overreach` polices the OPPOSITE direction: the engine may exclude at most `persisted - authored`, so marking more than the surplus \u2014 containment removing a criterion the plan really authored \u2014 is a hard gate failure. The gate deliberately CONSUMES this mark rather than re-deriving a binding of its own: a second, independently-drifting binder in the gate is exactly how a runtime fix and its gate come to disagree, and it is (C2) that makes trusting the mark safe, since the mark is only ever believed up to a cardinality the gate verifies for itself. Equivalence is pinned by e2e/scripts/criteria-containment-gate-equivalence.test.ts (the gate's counted set === the set `deriveStepStatusFromCriteria` reduces over, asserted on the two frozen prod-shaped runs). NO RESCUE \u2014 THE ENGINE BOUNCES INSTEAD (2026-08-04). The filter alone is STRUCTURALLY INERT on the production shape: `deriveStepStatusIgnoringAuthorship` returns a reported `failed` VERBATIM before it looks at any criterion, so when the model stamps the step `failed` BECAUSE of the criterion it invented, `derive(failed, all) === derive(failed, contained) === failed` and the exclusion moves nothing. Live proof \u2014 staging replicate run_d5b6f130-734f-4f57-8067-d43aa617c7ae, plan tp_vrgate_offer_upload_retry step 13: 2 authored criteria both PASS with grounding, plus a third graded `Order line 2 Quantity is 4.2` (strict, observed 1, NO groundingObservation) which is step 12's line-1 quantity copied across lines; the target is a STATIC self-testing fixture (apps/self-testing/app/lio-mirror/offer-upload-retry/page.tsx hardcodes Quantity 1) and the sibling replicate run_eccf41c3-9e09-4493-9e25-d6ae44b93c60 graded all 14 steps identically and PASSED, so there is zero app non-determinism. THREE SUCCESSIVE DESIGNS TRIED TO RESCUE THAT VERDICT and adversarial verification defeated each: (1) co-occurrence read as attribution \u2014 a failing unauthored grade merely CO-EXISTING with the step's failure rescued it, laundering 8/8 genuine non-criterion failures (timeout, error banner, HTTP 500, thrown exception, blocked navigation, disabled control, credential rejection, unobservable browser); (2) an ALLOWLIST of recognized genuine-failure signals \u2014 everything unenumerated was invisible by construction; (3) a positive execution attestation PLUS an attribution requirement \u2014 which fell too, because the attribution absorbed the excluded grade's own note/observed while BOTH that note and the step note are written by the same model in the same run_complete call, so one coherent narration made its own cause 'accounted for' (proven end-to-end: a genuine HTTP 500 with a fabricated grade whose note echoed the 500 booked passed/passed, and one coherent narration made all 8 laundering fixtures attributable). The lesson is structural: EVIDENCE THE MODEL AUTHORED CANNOT VALIDATE THE MODEL'S OWN REPORT, and every rescue reduced to trusting some model-written field. So the engine no longer rewrites a verdict at all. Instead `run_complete:unauthored_criterion_grades_rejected` BOUNCES the malformed call ONCE \u2014 naming the offending step indexes and the exact fabricated criterion texts, and instructing the model to resubmit grading ONLY the criteria the plan authored \u2014 mirroring the shipped `run_complete:incomplete_step_results_rejected` bounce (#2207). Bounded to one rejection per run (`_unauthoredGradeRunCompleteRejections`, reset in startRun beside its sibling counter) and placed immediately after the grade map, BEFORE every cascade adjudicator, so a bounce discards no gate's work. On the resubmission \u2014 or if the model repeats the fabrication and is not bounced again (`run_complete:unauthored_criterion_grades_repeated`) \u2014 the unauthored grades stay excluded from the derivation and marked, and WHATEVER THE MODEL BOOKS STANDS: a `failed` is never turned into a `passed`, at the step level or the run level. The bounce fires on ANY unauthored-and-unrelated grade regardless of its `passed` value, deliberately: whether a fabrication 'matters' is itself a model-written field, and reading one to decide is the exact error the three rescues each made in a different disguise. FORGERY GUARD (kept from the rework, now used to avoid a WRONG bounce): `excludedGradeRelatesToAuthoredCriterion` relates an excluded grade to an authored criterion over four deterministic tiers \u2014 the binder's own tiers (`findDuplicateReadCriterionIdx`), COSMETIC equality (`canonicalizeCheckTextForRelatedness`: NFKC, NBSP/space family, curly/angle/prime quotes, dash family, case, terminal punctuation, currency marks, locale number separators so 1.234,50 equals 1234.50), TRUNCATION (prefix either direction, floored at 12 canonical chars), and SUBJECT-TERM identity (`significantSubjectTokens`, shared with the note-contradiction lane). A related grade is a REPHRASED second read of a criterion the plan really does define \u2014 `findDuplicateReadCriterionIdx`'s documented residual \u2014 so the model is NOT asked to drop it, and it keeps its flag-OFF effect of failing the step. Reported on the containment diag as `relatesToAuthoredCriterion`. DIAGNOSTIC SEAM (verdict-neutral, kept on its own merits): `BaseRuntime.executeAction` calls `onBrowserActionError`, whose base implementation is an empty method RunnerRuntime never overrode, so an errored click_at / type_text_at / navigate left NO step-attributable trace anywhere in the codebase. `_stepActionErrors` now records both error shapes \u2014 the generic `status:'error'` via the overridden hook and the informative `status:'ok'` carrying `error`/`valueMismatch:true` at the dispatch seam \u2014 bounded per step, emitted as `step_action_error` and surfaced as a per-step count on the containment diag. NOTHING READS IT TO DECIDE A STATUS. GATE HALF (C3) `criteria_containment_inert`: C1 stops counting a surplus result the moment it is marked, so a MARKED-but-still-deciding fabrication went silent \u2014 gate run 30889770637 printed 'steps that graded more criteria than authored: 0' beside engineContainedResults: 1 on a run that booked failed on a criterion nobody wrote, and that false zero was read as proof the class was fixed. C3 reds when some excluded result FAILED, every counted result PASSED, and the step still books `failed`; `strictInvariant.containmentInertSteps` is printed on the same summary line as the over-graded count so the two can never be read apart again. C3 is now the whole visibility mechanism rather than a backstop to a rescue, and it fires on the incident shape by design.",designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-07-29",notes:"GRADUATED 2026-08-03 (default ON), ahead of the 2026-08-12 review date, because the containment class is NOT a hypothetical: it is red on release PR #2149 AND on the `main` nightly (GH run 30781168843, build 0555cc09a = the build prod is serving), so the pre-graduation default is what is booking false `failed` verdicts on prod TODAY. killSwitch resolves the no-env value from this defaultState (#1729), so graduation = defaultState 'on' AND CRITERIA_CONTAINMENT listed in INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire); an explicit AGENTIQA_CRITERIA_CONTAINMENT=0 (or the AGENTIQA_EXPERIMENT_ spelling) restores the byte-identical pre-graduation SHADOW \u2014 detection still runs, `criteria_containment:would_exclude` still fires, and NO verdict moves. STAGING-FIRST: merging to staging makes this default-ON on the STAGING engine only; PROD stays OFF until a later staging->main release carries it (a natural staged soak). Rides on the binder flags: with WARNING_CRITERION_BIND=0 or CRITERION_BIND_RESIDUAL=0 the binder is the sequential one, which leaves grades unbound in shapes that are NOT surplus \u2014 narrowing (2) is what keeps containment inert there rather than excluding a legitimately-graded criterion. Runner lane only (RunnerRuntime run_complete). Scope is step-status derivation: the rescue-eligibility gates that read `criteriaResults.every(passed)` (absence oracle, VERIFY_REOBSERVE_WITHHOLD, verify-conflict reconcile) still see the full persisted array, so a fabricated failing result can still BLOCK a rescue \u2014 the fail-closed direction, left deliberately. OPEN RESIDUAL CARRIED THROUGH THE FLIP, stated rather than hidden: the REPHRASED-DUPLICATE shape (a paraphrased second read of an authored criterion) matches no text tier, is therefore indistinguishable from a fabrication, and IS excluded at the default \u2014 a false-PASS channel the duplicate-read guard does not close and which the new (C2) gate check does NOT catch either (a rephrased duplicate consumes exactly one unit of surplus, so the exclusion is within budget). The pre-flip gate asked for a grade-by-grade shadow-soak review of that population; no such soak was run before this flip, which is the one piece of the original graduation bar that is being WAIVED rather than met, and it is why this flip is Alex's call and not a mechanical one.",graduation:{status:"gated",gate:'GRADUATED 2026-08-03 (default ON) with ONE part of this bar explicitly waived \u2014 kept as `gated` rather than deleted because the waived part is a real open false-PASS channel that still needs closing. MET: (1) the fabrication class reproduces on the canary at the documented location \u2014 tp_vrgate_offer_upload_retry step 13 authors exactly 2 criteria and the model graded 3, frozen from TWO real staging runs (run_11eeceb1-3bfc-4810-a17f-e7c23170c12e, run_81ef70ae-a3f9-4673-ba52-95378ca477d3, both 2026-08-03, both booking `failed` against a `passed` baseline on the invented `Order line 2 Quantity is "2"`, strict:true, passed:false, observed "1") into packages/engine-core/src/__tests__/fixtures/criteriaContainment.overGradedRuns.json; (2) the #1934 `criteria_over_grading` detector agrees on exactly those steps \u2014 it is what turned the gate red \u2014 and its 76-run corpus measurement recorded 2 true hits / 0 false positives, both this same defect; (3) the detector now also polices the exclusion in the opposite direction (`criteria_containment_overreach`), so over-exclusion beyond the surplus is a permanent CI check rather than a one-off review. WAIVED: the grade-by-grade shadow-soak review of the `would_exclude` population for the REPHRASED-DUPLICATE shape (a paraphrased second read of an authored criterion, which the text tiers cannot distinguish from a fabrication and which the duplicate-read guard does NOT catch). No soak was run; the flip was driven by the class being live on prod and blocking a release. That residual is the remaining work behind this entry.',evidence:"engine `criteria_containment:excluded` / `:would_exclude` / `:duplicate_read` diag events + the RunnerRuntime.criteriaContainment unit suite incl. the two frozen prod-shaped runs (claim verify.containment.authored-criteria-only) + the replay-gate `criteria_over_grading` / `criteria_containment_overreach` checks + e2e/scripts/criteria-containment-gate-equivalence.test.ts (runtime filter === gate filter)",owner:"steering (Alex)",review:"2026-08-12"}},PLAN_OBEDIENCE:{key:"PLAN_OBEDIENCE",envVars:["AGENTIQA_PLAN_OBEDIENCE","AGENTIQA_EXPERIMENT_PLAN_OBEDIENCE"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Enforces the active plan step: rejects off-plan label clicks, records deviations, and lets run_complete fail on them; off makes the pre-check, deviation recording, and verdict gate all inert (pre-#1294 behavior).",designDoc:"packages/engine-core/src/RunnerRuntime.ts",status:"active",added:"2026-07-07"},LOOP_VISION_ESCALATION:{key:"LOOP_VISION_ESCALATION",envVars:["AGENTIQA_LOOP_VISION_ESCALATION","AGENTIQA_EXPERIMENT_LOOP_VISION_ESCALATION"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Master gate (Runner/Explorer lanes) for consulting the vision supervisor once before an ambiguous screenshot-blind force-block terminates the run; off keeps the deterministic hard-block.",designDoc:"docs/plans/2026-07-07-loop-detection-vision-supervisor-design.md",status:"active",added:"2026-07-07"},LOOP_VISION_DIFFERENTIAL:{key:"LOOP_VISION_DIFFERENTIAL",envVars:["AGENTIQA_LOOP_VISION_DIFFERENTIAL","AGENTIQA_EXPERIMENT_LOOP_VISION_DIFFERENTIAL"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Phase-2 differential-grant refinement of loop-vision escalation: grants 2..K need a concrete task-unit delta and the per-step ceiling rises to 12; off reverts to Phase-1 (absolute judgment, 4-grant ceiling).",designDoc:"docs/plans/2026-07-09-loop-vision-differential-extension-design.md",status:"active",added:"2026-07-09"},CANVAS_PIXEL_PROGRESS:{key:"CANVAS_PIXEL_PROGRESS",envVars:["AGENTIQA_CANVAS_PIXEL_PROGRESS","AGENTIQA_EXPERIMENT_CANVAS_PIXEL_PROGRESS"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Canvas-aware loop progress: on a canvas-dominant screen (a <canvas> covers >=40% of the viewport) the LoopDetector novel_screen milestone is driven by a coarse screenshot pixel-diff hash (own NOVEL_SCREEN_BUDGET) instead of the static DOM/a11y hash, and the loop-vision differential delta gate accepts a qualitative delta_evidence string when countable task units are unavailable. Off drops both so a canvas build behaves exactly as before (structural breaker climbs on the static DOM hash, delta gate stays countable-only).",designDoc:"docs/plans/2026-07-14-canvas-chat-failure-class-design.md",status:"active",added:"2026-07-14"},LOOP_URL_NOVELTY_REARM:{key:"LOOP_URL_NOVELTY_REARM",envVars:["AGENTIQA_LOOP_URL_NOVELTY_REARM","AGENTIQA_EXPERIMENT_LOOP_URL_NOVELTY_REARM"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Re-arms the structural loop breaker in the chat/explorer (assistant_v2) lane. URL-ONLY (narrow-safe): only the novel-URL seen-set, its NOVEL_URL_BUDGET=40/turn budget, and actionsSinceProgress become TURN-scoped (resetForNewStep no longer wipes them), so a recycled URL revisited across many declared steps stops re-counting as novel and actionsSinceProgress climbs to the 30-action structural threshold. The novel-REF and novel-SCREEN budgets stay PER-DECLARED-STEP in both states \u2014 turn-scoping them was reverted because it starved a legit long single-stable-URL SPA turn producing genuinely-new screen content each action (force-block ~action 53). A genuinely-new URL each step still resets (multi-page wizards unaffected); a wander that also mints novel screens escapes this deterministic re-arm and the run backstop is the net. Off restores the per-step reset (recycled URLs re-count as novel forever, unbudgeted novel_url) \u2014 today's prod behavior. Runner (test_run) lane never opts in.",designDoc:"docs/plans/2026-07-20-loop-safety-rearm-and-backstop-design.md",status:"active",added:"2026-07-20"},RUN_PROGRESS_BACKSTOP:{key:"RUN_PROGRESS_BACKSTOP",envVars:["AGENTIQA_RUN_PROGRESS_BACKSTOP","AGENTIQA_EXPERIMENT_RUN_PROGRESS_BACKSTOP"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'Judge-gated run backstop in the chat/explorer (assistant_v2) runLoop: a wall-clock (12 min, primary), iteration (200), and cumulative prompt+completion billed-token (6M) ceiling \u2014 all well above p99 legit chat turns and below the 300-iteration explorer child cap. At a ceiling breach the loop-vision progress judge (the same fail-closed SupervisorService judge the loop breaker uses, temp-0 / thinkingBudget-0) is consulted ONCE with a goal-anchored progress question, rather than blind-terminating: "progressing" grants a BOUNDED extension (each ceiling raised by one window) up to a hard cap of RUN_BACKSTOP_MAX_EXTENSIONS=2 (worst case ~36 min), after which the run terminates regardless of the judge; "wandering" terminates with a judge-confirmed "not getting closer to the objective" message; any no-signal case (no judge wired in this lane, judge error / timeout / unparseable) fails CLOSED to a terminate with a neutral "hit the safety limit" message. Ends with blockKind=backstop / endKind=run_backstop, NOT loop_block, so it is not counted toward the session structural-loop cap. No interactive ask and no cross-turn state (the escalate\u2192ask_user ladder was removed; the judge consult is synchronous and re-derived per breach). Off removes all three ceilings (only bound remains iteration<=maxIterations=300, ~2.5h). Never a crash \u2014 the emit path fails open while the judge fails closed. Runner (test_run) lane never opts in.',designDoc:"docs/plans/2026-07-20-loop-safety-rearm-and-backstop-design.md",status:"active",added:"2026-07-20"},CLICK_AT_INTERACTIVE_DESCEND:{key:"CLICK_AT_INTERACTIVE_DESCEND",envVars:["AGENTIQA_CLICK_AT_INTERACTIVE_DESCEND","AGENTIQA_EXPERIMENT_CLICK_AT_INTERACTIVE_DESCEND"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"A coordinate click_at/double_click_at whose point resolves to a NON-interactive element descends to an interactive descendant within 16px and clicks it via locator, plus a no-effect advisory on a zero-mutation same-URL click; off restores the raw mouse.click(x,y) with no advisory.",designDoc:"docs/plans/2026-07-11-click-at-interactive-descend-design.md",status:"active",added:"2026-07-11"},LABEL_CLICK_TWIN_COLLAPSE:{key:"LABEL_CLICK_TWIN_COLLAPSE",envVars:["AGENTIQA_LABEL_CLICK_TWIN_COLLAPSE","AGENTIQA_EXPERIMENT_LABEL_CLICK_TWIN_COLLAPSE"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'BOTH twin-collapse kinds in `resolveClickableByLabel`, plus the actionable-click stage they feed (`pickActionableTwin` / `probeClickTopmost`). (1) Same-destination: a click_at/double_click_at label ties at the best rank across <= 3 elements that all resolve to ONE absolute http(s) href which is not the current page, opened in the same browsing context (equal `target`), each inside a landmark, whose OUTERMOST landmarks span >= 2 distinct region kinds including a header or a footer \u2014 the header-nav / footer-nav twin, e.g. "Security" on the pricing page. (2) Containment: the tied candidates nest (`<a href><button>Log in</button></a>`), so the innermost one is clicked. Either way the collapsed control is clicked only after it is proven topmost where it will be clicked; if nothing can be, nothing is clicked. Off restores the pre-fix behavior \u2014 any rank tie returns `ambiguous` and nothing is clicked. It does NOT gate the diagnostics on the no-click paths: the enriched ambiguity note (landmark / visibility / href hints) and the already-on-this-page note for twins that link to the current URL.',designDoc:"packages/engine-core/src/BasePlaywrightService.ts",status:"active",added:"2026-08-02",notes:'Root-cause fix for the #2024 blocked-loop class: the ambiguous no-op pushed the agent onto snapshot refs, which `_snapshotForAI` (refs: "interactable") never mints for an auto-hidden `fixed` header \u2014 so both recovery paths were dead and LoopDetector force-blocked on screen_cycling.'},LOOP_BLOCK_ATTRIBUTION:{key:"LOOP_BLOCK_ATTRIBUTION",envVars:["AGENTIQA_LOOP_BLOCK_ATTRIBUTION","AGENTIQA_EXPERIMENT_LOOP_BLOCK_ATTRIBUTION"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:`Evidence-gated loop-block finding attribution: when a force-block's dominant repeated click target is a non-interactive element or a no-op self-anchor, suppress the false "Page appears stuck" auto-issue instead of filing it. Off restores the old unconditional filing (subject only to the AG-6107/AG-6490 gates).`,designDoc:"docs/plans/2026-07-11-loop-block-finding-attribution-design.md",status:"active",added:"2026-07-11"},CLICK_AFFORDANCE_CAPTURE:{key:"CLICK_AFFORDANCE_CAPTURE",envVars:["AGENTIQA_CLICK_AFFORDANCE_CAPTURE","AGENTIQA_EXPERIMENT_CLICK_AFFORDANCE_CAPTURE"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Phase-2 of loop-block attribution: captures click-handler affordance evidence onto clickTarget (page-context inline/React/Vue signals + a CDP addEventListener probe on the rare non-interactive branch) so a dominant non-interactive target with a real-but-dead handler files a 'Custom control appears unresponsive' issue instead of being suppressed agent-side. Off \u21D2 no affordance field emitted \u21D2 the custom-control classifier branch can never fire (falls back to the #1477 status quo); it does NOT re-enable filing on a bare non-interactive target.",designDoc:"docs/plans/2026-07-11-custom-control-unresponsive-detection-design.md",status:"active",added:"2026-07-11"},CLICK_EFFECT_SIGNAL:{key:"CLICK_EFFECT_SIGNAL",envVars:["AGENTIQA_CLICK_EFFECT_SIGNAL","AGENTIQA_EXPERIMENT_CLICK_EFFECT_SIGNAL"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:`Post-click effect signal on EVERY click path (coordinate, retargeted, ref and label \u2014 click_at and double_click_at), SHADOW-FIRST. After the click the engine waits up to a 500ms settle window for the DomObserver to record any DOM/text/attribute mutation (returning the instant one lands), then compares the URL and the auto-accepted-dialog tally. Any movement \u21D2 an effect was observed. No movement \u21D2 the click STILL reports SUCCESS (a click that legitimately changes nothing must never become an error) and the feature's only output is agent STEER. THREE STATES, not two: no env (default) = SHADOW \u2014 detection runs and emits the click_effect:would_signal diag, but no advisory, no effectObserved metadata and no prompt-byte change reach the model; =1 = ENFORCE \u2014 the hedged re-observe advisory rides the successful tool result, effectObserved lands on the ToolCallResult side channel, the wait_for_element "Do NOT retry" coaching softens to its effect-aware wording and the runner prompt gains the re-observe-after-corrective-action line, with a click_effect:signaled diag; =0 = FULLY OFF \u2014 not even the probe runs, so there is zero added latency and click results plus every prompt byte are identical to pre-change behavior. Targets the 2026-07-29 vrgate false-FAIL (run_b72f5099-cee2-4063-b109-1961c55c9898), where a click_at on a rendered React button ~1.3s after a Next.js client navigation reported success while the onClick never fired (painted but not yet hydrated), the agent trusted the success, and the plan FAILED for a defect that did not exist. EXECUTOR-SIDE ONLY: it changes no verdict path, writes no step/criterion/run status, and adds no gate \u2014 it changes what the AGENT is told, and the agent still grades. Fail-safe by construction: an in-page probe that cannot run (CSP, cross-origin frame, document destroyed mid-navigation) yields NO verdict rather than a false "no effect"; navigation/dialog evidence is evaluated BEFORE the mutation counters so the one case where the probe reliably dies is the case the URL alone already proves; canvas-dominant surfaces and native property-only controls (checkbox/radio/select/text input \u2014 where a spurious "re-perform once" would TOGGLE the control back) are advisory-suppressed; and the pre-existing #1476 dead-coordinate peek keeps its exact timing and semantics. Canvas surfaces additionally skip the PROBE (not just the advisory) once a capture has classified the page as canvas-dominant, so a whiteboard/design flow never pays the settle window per click for a verdict that is suppressed on arrival; those clicks are counted as click_effect:probe_skipped. A SECOND probe skip covers the unobservable case: in SHADOW, where the diag is the feature's only output, a platform with no BasePlaywrightService.diagLog wired skips the probe entirely rather than pay the settle window for a measurement nothing can read (ENFORCE always probes \u2014 its output is agent-visible behavior, not telemetry).`,designDoc:"packages/engine-core/src/clickEffectSignal.ts",status:"active",added:"2026-07-29",notes:"Default OFF (shadow). Detection runs in shadow always unless explicitly =0 \u2014 the PIN_PAGE_GROUNDING / INTERACTION_CLEARS_PRESENCE_ORACLE shadow-first precedent \u2014 because a shadow that skipped the settle wait would measure a DIFFERENT detector than the one enforcement ships, and its numbers would not predict the ON behavior. The one deliberate non-identity in shadow is therefore TIMING, not tool-result bytes: a click that has mutated nothing yet pays up to 500ms of settle before the state capture that follows it (an effective click returns on the first in-page read and pays one evaluate round-trip). Stated plainly because it is a real, if small, behavior delta in the DEFAULT state \u2014 the post-click screenshot/snapshot of a no-effect click is taken up to 500ms later than before, which is more settled, not less faithful. AGENTIQA_CLICK_EFFECT_SIGNAL=0 removes even that and restores exact timing parity. killSwitch resolves the no-env value from this defaultState (#1729), so GRADUATING = flip defaultState to 'on' AND add CLICK_EFFECT_SIGNAL to INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire). Read sites: packages/engine-core/src/BasePlaywrightService.ts (probeClickEffect / resolveClickEffect on clickAt, clickByRef, clickByLabel), packages/engine-core/src/waitToolCoaching.ts (waitNotFoundError picks the coaching variant), packages/engine-core/src/tools/browserTools.ts (getFailureHandlingPrompt), packages/engine-core/src/RunnerRuntime.ts (buildRunnerPrompt re-observe line); pure decision logic in packages/engine-core/src/clickEffectSignal.ts. Acceptance tests: clickEffectSignal.test.ts (pure verdict matrix incl. every suppression and the unknown-probe degrade), BasePlaywrightService.clickEffectSignal.test.ts (per-path wiring + shadow/enforce/off + the canvas-memo skip + degraded-note preservation on the ref path), clickEffectPromptParity.test.ts (flag-OFF prompt BYTE-parity across all four prompt surfaces). The 500ms settle cost is bounded on canvas-dominant surfaces by the SessionState.lastCaptureCanvasDominant memo: captureState records canvasDominant on every capture and probeClickEffect returns null (emitting click_effect:probe_skipped, reason canvas-memo) while it is set, because the canvas verdict was suppressed anyway. It is bounded a second way by the OBSERVER check: in shadow, where the diag is the only output, probeClickEffect returns null before the settle window when this.diagLog is unwired \u2014 an unobservable measurement is pure latency. Enforce is exempt (its verdict is agent-visible behavior). Acceptance: the shadow/enforce no-observer pair in BasePlaywrightService.clickEffectSignal.test.ts.",graduation:{status:"gated",gate:'SOAK SOURCE \u2014 read this first. click_effect:* rides BasePlaywrightService.diagLog, which was wired ONLY in DesktopPlaywrightService until the CloudPlaywrightService sink resolver landed (2026-07-30), so before that commit desktop runs were the ONLY source and a census of 0 from a cloud/staging replay lane is NON-EVIDENCE (the events were structurally unemittable there, not absent). Any soak reading must therefore be taken from engine builds that carry the cloud diagLog wiring; older cloud replays cannot be counted, and in shadow an unwired lane now skips the probe outright so it contributes no fires by construction. Staging shadow soak on click_effect:would_signal establishes (a) the no-effect rate per click path, (b) that the fires are dominated by genuinely ineffective clicks rather than by the known effect-free-but-legitimate classes, and (c) that no page class fires it continuously. FOUR NAMED REQUIREMENTS, all of which must be answered before the flag moves past shadow. (1) BLOCKER \u2014 THE NON-IDEMPOTENT SUBMIT. `<button>` and `<a>` are deliberately NOT in the property-only suppression set, so a form submit whose only feedback is a server round-trip (no spinner, no optimistic DOM write) reads as no-effect and, under enforce, receives the "re-perform the action ONCE" advisory. That directly contradicts the failure-handling prompt already shipped in browserTools.getFailureHandlingPrompt, which names "status: ok with url unchanged and no visible DOM change" as a SIGN OF AN IN-FLIGHT WRITE and instructs "do NOT re-click ... Re-clicking the same button while a write is in flight is a no-op for the user and burns retry budget" \u2014 and on a non-idempotent endpoint a second submit is not merely wasted, it can double-charge, double-book or double-post. The soak must show ZERO enforce-mode advisories on non-idempotent submits (classify fires by targetTag/targetRole plus the accessible name against a submit-shaped vocabulary, and cross-check each against pendingRequests at the moment of the fire), OR enforce must first gain a submit-shaped suppression (e.g. withhold the advisory whenever a same-origin request was in flight at probe time, or whenever the target is a submit-shaped control) \u2014 either outcome, and NEITHER may be waived. (2) IFRAME BLIND SPOT. Every counter read (peek / waitForMutation / flush) goes through page.evaluate, which runs in the MAIN frame only, and a top-document MutationObserver does not cross an iframe boundary \u2014 so a click whose whole effect renders inside an embedded frame (payment element, third-party booking/chat widget, embedded editor preview) reads as no-effect however well it worked. There is no target shape to suppress on, so iframe-hosted effects are a KNOWN-LEGITIMATE fire class the soak must be able to account for and subtract, exactly like property-only controls, downloads, clipboard and focus-only clicks; it is enumerated in the clickEffectSignal.ts header for the same reason. (3) SHADOW IS NOT INERT \u2014 READ THE SOAK ACCORDINGLY. The probe delays the post-click captureState by up to the full 500ms settle window on any click that has mutated nothing yet, INCLUDING in the default shadow state. Captured screenshots/snapshots on those clicks are therefore of a MORE SETTLED page than pre-change, so a shadow-vs-baseline comparison that shows different captured content on no-effect clicks is expected and is not evidence of a detector defect; only the =0 state is timing-identical to pre-change. (4) HYDRATION EMPIRICAL CRITERION. The soak must answer "what would run_b72f5099-cee2-4063-b109-1961c55c9898 have produced?" \u2014 i.e. for clicks landing inside a post-navigation hydration window, what fraction show mutationCount > 0 or attrCount > 0 (some other script mutated the page, so the detector stays SILENT and would not have rescued the incident) versus both counters at 0 (the detector fires and the advisory would have reached the agent). Both counters are already in the click_effect payload, so the query is: filter click_effect:would_signal to fires within ~2s of a navigation, then bucket on (mutationCount > 0 || attrCount > 0). A silent-dominated result means this feature does not fix its own motivating incident and enforcement is not justified on that basis. Enforcement additionally needs an inversion showing the advisory does not induce a harmful second click on a toggle.',evidence:"staging click_effect:would_signal diag events FROM AN ENGINE BUILD THAT CARRIES THE CLOUD diagLog WIRING (rate + clickPath/suppressReason/targetTag/targetRole breakdown, plus the mutationCount/attrCount split inside the post-navigation hydration window and the submit-shaped-target cross-check against in-flight same-origin requests) + click_effect:probe_skipped counts for the canvas-memo skips + the engine-core clickEffectSignal / BasePlaywrightService.clickEffectSignal / clickEffectPromptParity unit suites + apps/execution-engine/__tests__/CloudPlaywrightService.diagLog.test.ts for the cloud emit path itself",owner:"steering (Alex)",review:"2026-08-08"}},HALT_CONCLUDING_TURN:{key:"HALT_CONCLUDING_TURN",envVars:["AGENTIQA_HALT_CONCLUDING_TURN","AGENTIQA_EXPERIMENT_HALT_CONCLUDING_TURN"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"ONE final, tool-restricted turn offered to the runner when the MACHINERY (loop detector / vision supervisor / run backstop) halts a saved-plan run that still has status=running AND the deterministic unresponsive-control window holds \u2014 i.e. exactly the runs that end today with terminationReason=supervisor_halted and NO run_complete, hence no verdict at all, narrowed to the halt shape the change was measured against. SCOPE GATE: the turn is offered only when hasUnresponsiveControlEvidence() is true (a dominant repeated click target, noObservedEffect on EVERY one of those clicks, a non-canvas surface, no committed 2xx/3xx write \u2014 a model-free predicate that consults no LLM output). Every OTHER machinery halt keeps its current behavior: no extra turn, no extra LLM call, status=blocked + supervisor_halted. The gate is checked BEFORE the single-use latch, so a halt without that evidence does not consume the run's one attempt, and a `concluding_turn:not_offered_no_evidence` diag records each suppression so the gate's live fire rate is measurable. The turn is offered run_complete ONLY (the _editOnly idiom, RUNNER_TOOL_SET_CONCLUDE_ONLY), so it cannot act on the browser, cannot navigate, and cannot extend the loop: it is a single generateText call with no iteration, and it is offered at most once per run (_concludingTurnUsed). Its directive is NEUTRAL \u2014 it names no finding and no failure shape, asks for each step to be graded only on observed evidence, and tells the model to mark an unsubstantiable step 'skipped' rather than assert a verdict; an earlier revision spelled the dead-control conclusion out, which asked the model to ratify what the gate already believed. The halted path also performs NO cross-run writes: maybeCaptureProjectProfile and maybeMigratePlanFromCapture are skipped while _concludingTurnActive is true, because both end at isRunAuthenticated \u2014 routinely TRUE on a dead-CTA halt, where the login worked and the CTA did not \u2014 and a halted run must not persist its browser state or rewrite the customer's saved plan. Motivation, measured 2026-08-04 \u2014 READ THE PROVENANCE BEFORE QUOTING THE NUMBER (corrected 2026-08-06). It is run r1 of an UNCOMMITTED model-reliability harness (e2e/benchmark/model-reliability/, never merged to staging): its own execution-engine, fixtures on the deployed self-testing app, engine = staging @ fb89690cf, all 46 verification gates forced off as AGENTIQA_EXPERIMENT_<FLAG>=0 (PLAN_OBEDIENCE and CLICK_EFFECT_SIGNAL among them) and LLM_CACHE=0 \u2014 NOT the runner eval lane, which was not run that day. In it the two DEAD-CTA fixtures (a button that is present, whose click reports success, and which does nothing) produced 12/12 runs ending supervisor_halted/loop_block with ZERO run_complete, ZERO report_issue and ZERO agent-declared blocks. Those 12 are google:gemini-3-flash-preview \xD75 per fixture plus google:gemini-3.5-flash \xD71 per fixture: the second arm is n=1 per fixture and there was no Anthropic arm. THE CLASS IS MODEL-DEPENDENT, so 12/12 is not a property of the defect \u2014 a rerun the same day on the same code and fixtures (r2, 2026-08-04T20:11Z) had gemini-3.6-flash booking `failed` on 9 of 10 dead-CTA runs; only r1 reached this text. The halt is not the agent refusing to grade; it is the machinery taking the turn away before a verdict can be booked. MEASUREMENT STATUS: the post-merge L3 on the real lane (runner/seeded-broken, 2026-08-06 @ bdbfa8e30, 12 branch + 6 control, incumbent model) fired this gate 0/12 \u2014 GRADUATE-NO. Every eligible halt was suppressed by the scope gate (concluding_turn:not_offered_no_evidence) because the fixture's semantic `button` classifies as app-defect / non-interactive-target rather than custom-control-unresponsive, and its click has a real DOM effect so no noObservedEffect is ever sampled. The mechanism is unreachable on the very corpus that motivated it; see docs/VERDICT-GATES.md and PR #2246. GUARD RAILS, all pinned by tests: (a) a status=passed booked from this turn is REJECTED and the run falls back to today's blocked terminal unchanged \u2014 the agent must never be able to declare success out of a halt, and the halt itself is evidence against the claim; (b) the turn is NOT offered when blockKind==='agent' (the agent already concluded via exploration_blocked \u2014 nothing to second-guess); (c) exactly one turn, one LLM call, no tool loop, so a runaway loop is extended by that bounded turn and nothing more; (d) anything unusable (no run_complete, a throw, a stop mid-flight) falls back to the unchanged blocked terminal; (e) the scope gate above \u2014 a machinery halt with no unresponsive-control evidence gets no turn at all, in any blockKind. WHAT OFF (=0) DOES AND DOES NOT COVER \u2014 read this before using it as a kill switch. OFF removes the TURN: no extra turn, no extra LLM call, no extra token spend, and no run can be moved from blocked to failed by this mechanism. OFF does NOT change the terminal VOCABULARY: the `control_unresponsive` reason written by the blocked-terminal branch in startRun is UNCONDITIONAL and not gated by this flag, because it comes from `hasUnresponsiveControlEvidence` \u2014 a deterministic, model-free predicate that consults no LLM output \u2014 and it exists to stop blaming the agent for a dead control in the customer's own app, which is a separate improvement from giving a halted run a verdict. So with the flag OFF a halted run still terminates `control_unresponsive` rather than `supervisor_halted` whenever that evidence holds; to suppress the vocabulary too, turn off its input (`AGENTIQA_CLICK_EFFECT_SIGNAL=0` captures no `noObservedEffect`, so the predicate returns false and the terminal falls back to `supervisor_halted`). docs/VERDICT-GATES.md states the same split.",designDoc:"docs/VERDICT-GATES.md",status:"active",added:"2026-08-04",notes:"Default ON. Polarity is the reason: this gate can only move a run from blocked (NO verdict) to failed (a verdict the model booked), never toward a pass \u2014 the passed arm is refused in code, so there is no path by which it manufactures a false pass, which is the asymmetry docs/VERDICT-GATES.md guards hardest. Its worst case is a false FAIL on a run that today produces no verdict at all, and 'blocked' was never a pass either. Billing is NEUTRAL, and the ORDER is what makes it so: `handleRunComplete` reads `_concludingTurnActive` and stamps the non-billable reason (control_unresponsive when the deterministic unresponsive-control window holds, else supervisor_halted) BEFORE the first persist \u2014 because that upsert IS the billing trigger. It PUTs the entity-sync route, which fires commitRunUsage on any terminal + billable row, and `committed_at` is written in exactly one place in apps/web-next and only ever to NOW(); there is no un-commit path, so a later discardRunUsage no-ops with `already_committed`. An earlier revision corrected the reason with a second upsert AFTER handleRunComplete had already persisted 'completed'; that shipped the money before the correction and was replaced by this single-mechanism ordering (PR #2246 review finding 1). The same ordering is what makes a throwing upsert safe: the surviving in-memory reason is already non-billable, so startRun's finally cannot finalize a charge (finding 2). Read site: packages/engine-core/src/RunnerRuntime.ts#attemptConcludingRunComplete (called from startRun, immediately before the blocked terminal write) plus the `_concludingTurnActive` branch in #handleRunComplete. The turn's own LLM call is metered as auxiliary spend via emitAuxiliaryLlmUsage on every exit path (booked, declined, passed-refused) \u2014 it is a full-trace prompt plus up to 8192 output tokens, so leaving it unmetered hid the feature's entire cost (findings 3+4). Scope, directive neutrality and blast radius were narrowed in review: the turn fires only on the deterministic unresponsive-control halt shape (so every other machinery halt is untouched), its prompt names no finding, and the halted path skips the two cross-run writers handleRunComplete otherwise performs. Acceptance tests: RunnerRuntime.concludingTurn.test.ts (offer conditions incl. the scope gate across every blockKind, the neutral directive, the no-cross-run-writes pins, the passed refusal, the single-turn bound, the restricted tool set, flag-off parity), RunnerRuntime.controlUnresponsiveTerminal.test.ts (the terminal vocabulary + the browser_unobservable-style regression pins), RunnerRuntime.concludingTurnBilling.test.ts (the first-persist reason and the throwing-upsert path), RunnerRuntime.concludingTurnMetering.test.ts (the aux emit on every exit) and RunStatusBanner.controlUnresponsive.test.ts (the renderer copy + the booked-verdict line). MEASUREMENT STATUS 2026-08-06: post-merge L3 on runner/seeded-broken @ bdbfa8e30 (12 branch + 6 control) gave rescue 0/12 \u2014 GRADUATE-NO, with every eligible halt suppressed by the scope gate; the concluding_turn:not_offered_no_evidence diag remains the source of the live fire rate and is the number to read before revisiting this default."},REVISION_CRITERIA_CARRYOVER:{key:"REVISION_CRITERIA_CARRYOVER",envVars:["AGENTIQA_REVISION_CRITERIA_CARRYOVER","AGENTIQA_EXPERIMENT_REVISION_CRITERIA_CARRYOVER"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Backstop that carries prior-draft criteria forward when a plan revision strips ALL criteria across ALL steps; off lets a total criteria-strip through.",designDoc:"packages/engine-core/src/revisionCriteriaCarryOver.ts",status:"active",added:"2026-07-07"},CREDENTIAL_GUARD:{key:"CREDENTIAL_GUARD",envVars:["AGENTIQA_CREDENTIAL_GUARD","AGENTIQA_EXPERIMENT_CREDENTIAL_GUARD"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Credential-fabrication guard: injects the login-prohibition prompt wording and fires the false-premise report_issue gate; off omits both (pre-feature behavior).",designDoc:"packages/engine-core/src/testingEmailPolicy.ts",status:"active",added:"2026-07-06"},CREDENTIAL_BINDING:{key:"CREDENTIAL_BINDING",envVars:["AGENTIQA_CREDENTIAL_BINDING","AGENTIQA_EXPERIMENT_CREDENTIAL_BINDING"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Runner deterministic credential-step binding (v1.1): when a plan step unambiguously references exactly one non-generic stored credential AND the model's own type_project_credential_at pick was a generic field word, the mismatched credentialName is overridden to the referenced one (credential_binding_override diag) \u2014 a non-generic pick is never rewritten; and when a credential fill is followed within a 3-action adjacency window by a 401/403 while the step-referenced credential is untried and was not the last one filled, ONE report_issue/exploration_blocked per run is deflected toward that specific credential. A genuine 401 of the step's own credential, an unrelated stray 401, or a step naming no credential all proceed to the report. Off restores the model's free credential pick and no auth-failure nudge (pre-feature behavior).",designDoc:"docs/plans/2026-07-19-ag7727-run-fidelity-fixes-design.md",status:"active",added:"2026-07-19"},EMAIL_CODE_PROVENANCE:{key:"EMAIL_CODE_PROVENANCE",envVars:["AGENTIQA_EMAIL_CODE_PROVENANCE","AGENTIQA_EXPERIMENT_EMAIL_CODE_PROVENANCE"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Verification-code write provenance gate: a write the model TYPED as fieldPurpose='verification_code' is refused unless its value equals a whole code token in an email actually fetched via check_email this run (subject + text + html-as-text, token-equality not raw substring). Off passes the write through ungrounded (pre-feature behavior), so a guessed or fabricated code can mutate the page into a false success state.",designDoc:"packages/engine-core/src/emailVerificationGate.ts",status:"active",added:"2026-07-12"},VERBATIM_INPUT_PIN:{key:"VERBATIM_INPUT_PIN",envVars:["AGENTIQA_VERBATIM_INPUT_PIN","AGENTIQA_EXPERIMENT_VERBATIM_INPUT_PIN"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Verbatim user-input payload pinning: committed text writes carry a source-action id, field identity, and model-authored purpose role; an exact match to a configured login-email identity is forced to credential_identifier from typed project state. On a source-bound run, a text-entry step's verbatimInput is kept ONLY when its exact source action resolves uniquely and has inputRole=user_supplied_payload; duplicate, credential/generated/other, missing, conflicting, and ambiguous bindings are omitted. The value must also provenance-match user chat/attachment text. Legacy source-less producers retain the corpus-only gate. RunnerRuntime types the accepted pin verbatim (slot wins over prose). Off restores the prior write-tool contract (inputRole optional, legacy descriptions) and disables capture/attach plus runner consumption.",designDoc:"docs/plans/2026-07-13-verbatim-input-payload-design.md",status:"active",added:"2026-07-13"},VERIFY_ORACLE_FIDELITY:{key:"VERIFY_ORACLE_FIDELITY",envVars:["AGENTIQA_VERIFY_ORACLE_FIDELITY","AGENTIQA_EXPERIMENT_VERIFY_ORACLE_FIDELITY"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Verify-oracle fidelity (verbatim-input Phase 2): a deterministic post-generation pass at both producer gates (CoordinatorRuntime.handleSaveTestPlan + the Explorer draft path), after pinVerbatimInputSteps, that restores user-authored acceptance criteria the LLM summarized away. It captures verification check-bullets under a high-precision verification-heading line (r2: heading ends with ':' and its core matches a verification PHRASE whole, not merely contains a strong token; each bullet captured unless action-imperative-shaped) plus a bounded single-paragraph anti-false-pass lookback, tests each against the persisted verify-side content (verify-step texts + criteria checks + expectedValue pins) by literal-atom containment (quoted spans + numerals/ranges with EN/RU 0\u201320 spelled-form equivalence; atom-free \u2192 normalized-token containment \u2265 0.6), and APPENDS a verify step for every uncovered item carrying the user's bullet verbatim as both the step text AND a compiled criterion ({check, strict:true}, r2/F7 \u2014 so the appended step passes validateDraftPlanSteps and never hits the runner's criteria-less synthesis path). Append-only (never edits/deletes an existing step), fail-open (any error \u2192 save unchanged). Off restores pure LLM-compliance generation (summarized oracles persist as-is). Emits an oracle_fidelity diag {bullets, covered, appended, lookbackCaptured, looseBullets, disabled} regardless of flag state (looseBullets = diag-only recall telemetry, never appends).",designDoc:"docs/plans/2026-07-15-verify-oracle-fidelity-design.md",status:"active",added:"2026-07-15"},TYPE_NEWLINE_SAFE:{key:"TYPE_NEWLINE_SAFE",envVars:["AGENTIQA_TYPE_NEWLINE_SAFE","AGENTIQA_EXPERIMENT_TYPE_NEWLINE_SAFE"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:'Multi-line text typing: embedded newlines are entered as Shift+Enter soft line breaks (never a raw Enter keypress) so a multi-line prompt does not premature-submit an Enter-to-send composer (e.g. Miro/Slack chat sidekicks). Off restores the raw keyboard.type(text) behavior where each "\\n" fires an Enter keypress. Applies to all four text-typing paths (typeTextAt / typeByRef / typeByLabel / setFocusedInputValue fallback); pressEnter still appends one trailing Enter regardless.',designDoc:"packages/engine-core/src/typeMultiline.ts",status:"active",added:"2026-07-13"},CANVAS_TYPE_GROUNDING:{key:"CANVAS_TYPE_GROUNDING",envVars:["AGENTIQA_CANVAS_TYPE_GROUNDING","AGENTIQA_EXPERIMENT_CANVAS_TYPE_GROUNDING"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-disabled",gates:"Canvas typed-text grounding: after a coordinate type_text_at whose focus is NOT a text-editable DOM element on a canvas-dominant screen, verify the text actually landed \u2014 a11y/DOM positive check on the reused post-action snapshot, else a pixel route gated by an ambient-animation pre-check (two pre-type full-frame captures a settle apart). If the board is proven static (pre-captures byte-identical) a full-frame before/after compare decides: any change = landed (off-clip renders included), byte-identical = confident negative \u2192 explicit failure (metadata.error + canvasTypeVerification='text_not_found') with a switch-strategy hint. If the board is self-animating (pre-captures differ) the outcome is uncertain_animated: behavior byte-identical to flag-off, recorded via a canvas_type_verify diag event. Every uncertain outcome is fail-open (behavior unchanged); \u22643 verification screenshots per qualifying action. Off restores the pre-feature bare-success canvas type (no verification screenshots, no a11y check).",designDoc:"docs/plans/2026-07-14-canvas-chat-failure-class-design.md",status:"active",added:"2026-07-14"},RESULT_FIRST_DISCOVERY:{key:"RESULT_FIRST_DISCOVERY",envVars:["AGENTIQA_EXPERIMENT_RESULT_FIRST_DISCOVERY"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"env-const-disabled",gates:'Result-first discovery (#373): auto-approve discovered scope and run high-risk areas immediately; off restores the "question-first" scope-approval checkpoint that waits.',designDoc:"packages/engine-core/src/resultFirstDiscovery.ts",status:"active",added:"2026-06-01",notes:"Single spelling: reads ONLY AGENTIQA_EXPERIMENT_RESULT_FIRST_DISCOVERY (no bare AGENTIQA_ spelling). NOT migrated to killSwitchDisabled \u2014 that would add the bare spelling and change behavior. Also has a per-session config opt-out."},SCOPE_PROVENANCE:{key:"SCOPE_PROVENANCE",envVars:["AGENTIQA_EXPERIMENT_SCOPE_PROVENANCE"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"env-const-disabled",gates:"Scope-provenance-aware plan selection: user-enumerated medium/low areas still run in the first pass; off falls back to the legacy risk-only split.",designDoc:"packages/engine-core/src/resultFirstDiscovery.ts",status:"active",added:"2026-07-01",notes:"Single spelling: reads ONLY AGENTIQA_EXPERIMENT_SCOPE_PROVENANCE. NOT migrated to killSwitch (would add a bare spelling)."},MEMORY_WRITEBACK:{key:"MEMORY_WRITEBACK",envVars:["AGENTIQA_EXPERIMENT_MEMORY_WRITEBACK"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"env-const-enabled",gates:"Verified-memory writeback: offers the save_verified_memory tool and activates the durable write path; off leaves writeback inert.",designDoc:"docs/plans/2026-07-04-verified-memory-writeback-design.md",status:"active",added:"2026-07-04",notes:"Single spelling: reads ONLY AGENTIQA_EXPERIMENT_MEMORY_WRITEBACK. NOT migrated to killSwitchEnabled (would add a bare spelling). Also has a per-session config force-on for evals.",graduation:{status:"gated",gate:"Model-elicitation quality gate from the 2026-07-04 verified-memory-writeback design doc closes",evidence:"design-doc gate + eval force-on runs",owner:"steering (Alex)",review:"2026-08-01"}},FAST_START_PROMPT:{key:"FAST_START_PROMPT",envVars:["AGENTIQA_EXPERIMENT_FAST_START_PROMPT"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"is-experiment-enabled",gates:"Explorer prompt variant: emits the fast-start initial prompt instead of the standard one.",designDoc:"packages/engine-core/src/ExplorerRuntime.ts",status:"active",added:"2026-06-01",notes:'Single spelling via isExperimentEnabled (exact "1"). NOT a killSwitch flag \u2014 no bare AGENTIQA_ spelling honored.',graduation:{status:"parked",gate:"No rollout intent; delete registry entry + code path if still unused by 2026-09-01"}},MINIMAL_INITIAL_CONTEXT:{key:"MINIMAL_INITIAL_CONTEXT",envVars:["AGENTIQA_EXPERIMENT_MINIMAL_INITIAL_CONTEXT"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"is-experiment-enabled",gates:"Explorer context variant: sends a minimal initial context to the model instead of the full one.",designDoc:"packages/engine-core/src/ExplorerRuntime.ts",status:"active",added:"2026-06-01",notes:'Single spelling via isExperimentEnabled (exact "1"). NOT a killSwitch flag \u2014 no bare AGENTIQA_ spelling honored.',graduation:{status:"parked",gate:"No rollout intent; delete registry entry + code path if still unused by 2026-09-01"}},STEERING_VETO:{key:"STEERING_VETO",envVars:["AGENTIQA_STEERING_VETO"],polarity:"0-disables",defaultState:"on",surfaces:["engine"],cloudForwarded:!1,read:"env-direct",gates:"Supervisor steering veto: at a redirect, refuses the recent loop-set of action fingerprints for a bounded TTL/budget; off passes those calls through.",designDoc:"packages/engine-core/src/steeringVeto.ts",status:"active",added:"2026-06-01",notes:'Reads ONLY the bare AGENTIQA_STEERING_VETO (via direct process.env access, "!== 0"). It has NO AGENTIQA_EXPERIMENT_ spelling, so the orchestrator passthrough cannot flip it in a cloud pod (cloud-unreachable \u2014 known limitation). NOT migrated to killSwitchDisabled: that would add the EXPERIMENT_ spelling and change behavior/reachability.'},AUTH_DISABLE_EMAIL_VERIFICATION:{key:"AUTH_DISABLE_EMAIL_VERIFICATION",envVars:["AUTH_DISABLE_EMAIL_VERIFICATION"],polarity:"1-enables",defaultState:"off",surfaces:["web-next-node"],cloudForwarded:!1,read:"env-direct",gates:"On-prem escape hatch: disables signup email verification; IGNORED (fail-safe) on a hosted Vercel deploy where VERCEL/VERCEL_ENV is present.",designDoc:"apps/web-next/lib/auth-flags.ts",status:"active",added:"2026-06-01",notes:"Not AGENTIQA_-prefixed. Static process.env member access (edge-safe). Hosted-platform sentinel forces it off on Vercel.",graduation:{status:"permanent",gate:"On-prem operational escape hatch, not an experiment \u2014 never graduates; forced off on hosted Vercel"}},RUN_DISPATCH_MUTEX:{key:"RUN_DISPATCH_MUTEX",envVars:["RUN_DISPATCH_MUTEX"],polarity:"1-enables",defaultState:"off",surfaces:["web-next-edge"],cloudForwarded:!1,read:"env-direct",gates:"Per-plan run-dispatch mutual exclusion (AG-8221): when on, a new run of a plan that already has a non-terminal run either supersedes a ZOMBIE predecessor (no activity for RUN_DISPATCH_ZOMBIE_MS, default 600s \u2014 a measured floor, see packages/shared-types/src/runDispatch.ts) or is rejected 409 plan_run_active behind a LIVE one. Off = SHADOW: the same scan and decision run and log run_dispatch_mutex:would_supersede/would_block, but every caller is told to proceed and nothing is written.",designDoc:"packages/shared-types/src/runDispatch.ts",status:"active",added:"2026-07-30",notes:"Not AGENTIQA_-prefixed. Static process.env member access via runDispatchMutexEnforced() in apps/web-next/lib/run-dispatch-mutex.ts (single read point, edge-safe). The engine only relays the decision \u2014 it reads no flag \u2014 so this one env var is the whole gate. Companion tuning knob RUN_DISPATCH_ZOMBIE_MS overrides the zombie threshold; it is a threshold, not a behavior gate, so it is deliberately not a registry entry.",graduation:{status:"gated",gate:"Shadow diags from the Lio daily bursts show would_block/would_supersede firing only on genuine overlaps, with zero would_supersede against a run that later produced a verdict (a superseded-live event is the dangerous direction and blocks graduation).",evidence:"run_dispatch_mutex:would_* lines from web-next logs, cross-checked against the superseded runs' final status in app.test_plan_runs.",owner:"Alex",review:"2026-08-13"}},ORG_ENTITLEMENT_ENABLED:{key:"ORG_ENTITLEMENT_ENABLED",envVars:["ORG_ENTITLEMENT_ENABLED"],polarity:"0-disables",defaultState:"on",surfaces:["web-next-node"],cloudForwarded:!1,read:"env-direct",gates:'Company-plan org entitlement (O0+): when on, an active OrgMembership resolves the billing subject to the org and its plan wins over the personal plan in all entitlement gates; "0" forces the personal subject everywhere (org rows become inert).',designDoc:"docs/plans/2026-07-11-company-plan-access-control-design.md",status:"active",added:"2026-07-11",notes:"Not AGENTIQA_-prefixed. Static process.env member access via isOrgEntitlementEnabled() in apps/web-next/lib/billing-subject.ts (single read point). Dark by data until an Organization row exists \u2014 with zero orgs the flag has no observable effect."},ORG_PROJECT_SHARING:{key:"ORG_PROJECT_SHARING",envVars:["ORG_PROJECT_SHARING"],polarity:"0-disables",defaultState:"on",surfaces:["web-next-edge","web-next-node"],cloudForwarded:!1,read:"env-direct",gates:"Org project sharing (Company plan): when on, `projectScopedGrantFor` lets a member of the OWNER's active Company-tier org read AND write the shared child entities of a project whose owner set `app.projects.org_shared = true` (test-plans, test-plan-runs, batch-runs, issues, labels, app-map), and widens the owner's own child queries from user-scoped to project-scoped so the owner sees what teammates ran and edited. \"0\" collapses every project grant to owner-only \u2014 reads re-narrow to the caller's own rows and writes re-gate to the owner \u2014 with no redeploy and no data change. NEVER gates credentials (plaintext secrets are excluded from the share by design), memory, journal, or Project Files.",designDoc:"docs/plans/2026-08-03-org-project-sharing-design.md",status:"active",added:"2026-08-03",notes:"Not AGENTIQA_-prefixed, so NOT cloud-forwarded: the orchestrator forwards only AGENTIQA_EXPERIMENT_* into engine session pods, and this flag is read exclusively in web-next (Vercel env), never in the engine \u2014 same shape as ORG_ENTITLEMENT_ENABLED. Static process.env member access via isOrgProjectSharingEnabled() in apps/web-next/lib/org-read-grant.ts (single read point; the sync-entity routes are edge functions, so the read must stay a static literal member access). Dark by data twice over: org_shared defaults false, and a true toggle still grants nobody unless the owner is an active member of an enterprise-tier org \u2014 so with zero shared projects the flag has no observable effect. ORG_ENTITLEMENT_ENABLED=0 also collapses this grant, because the Company-org half resolves through resolveBillingSubject."},PROJECT_RUN_LOCK:{key:"PROJECT_RUN_LOCK",envVars:["PROJECT_RUN_LOCK"],polarity:"1-enables",defaultState:"off",surfaces:["web-next-edge"],cloudForwarded:!1,read:"env-direct",gates:"Per-PROJECT run concurrency lock (P1.6): at most ONE live run OR batch per project at a time, across ALL identities \u2014 the owner, any member of the owner's Company org on a shared project, and ServiceKey CLI runs alike. Enforced at the two server seams that create the rows: the new-row path of `PUT /api/sync/entities/test-plan-runs/[id]` and of `PUT /api/sync/entities/batch-runs/[id]`. A refused start gets 409 `{code:'PROJECT_RUN_IN_PROGRESS', runId, kind, startedAt, startedByLabel}` naming who holds the project and since when \u2014 never 403 (a 403 on an entity write trips the SPA signOut trap, HC1) and never a 200 no-op. Liveness is GREATEST(run.updated_at, run_billing.last_step_at) for a single run and the freshest of the batch row and ALL its legs for a batch, with a 90-minute silence TTL; a holder past the TTL stops blocking and is opportunistically reaped to status='cancelled' (+ termination_reason='stale_lock_reap' on runs, which bills zero). NEVER blocks an update to an EXISTING row \u2014 only the creation of a new one \u2014 nor a leg of an already-admitted batch. The exemption is EXISTENCE, not status: until 2026-08-03 any PUT whose `status` was already terminal skipped the lock outright, which let a refused caller INSERT itself as a brand-new terminal row simply by finishing (measured web bypass) \u2014 so a first PUT that is already terminal is now admitted whenever nothing holds the project (the desktop late-sync case) and refused only while another run/batch of that project is live. \"0\" removes the lock entirely: both seams revert to an unguarded upsert with no reap and no 409. RETIRED 2026-08-04 \u2014 now default OFF, \"1\" restores it. Its rationale opened with \"the thing two concurrent runs collide on is the TARGET APPLICATION and the credentials used to reach it, and in this product both are properties of the PROJECT\": the credential half was never true (app.project_credentials has carried user_id since its first migration and the read path has always been WHERE user_id = caller), and the data half was declined by the customer it was built for (\"two accounts will operate on different data and test cases in 95-99% of cases \u2026 we're fine not handling that case\"). The credential POOL contends on the ACCOUNT \u2014 the resource that is actually shared \u2014 and hands a distinct one to each caller instead of refusing the second run. What made keeping it untenable rather than merely redundant: the lock is active ONLY on org-shared projects, exactly where the pool lives, so it gated the one scenario the feature exists for \u2014 and did so AFTER the caller had already leased an account, leaving that slot held until teardown. Nothing is deleted: the transaction, the reap and the 409 contract are the shape a per-account guard needs, and lib/credential-pool.ts already reuses them.",designDoc:"docs/plans/2026-08-03-org-project-sharing-design.md",status:"active",added:"2026-08-03",notes:"Not AGENTIQA_-prefixed, so NOT cloud-forwarded: the orchestrator forwards only AGENTIQA_EXPERIMENT_* into engine session pods, and this flag is read exclusively in web-next (Vercel env), never in the engine \u2014 same shape as ORG_PROJECT_SHARING. Static process.env member access via projectRunLockEnabled() in apps/web-next/lib/project-run-lock.ts (single read point; both seams are edge functions, so the read must stay a static literal member access). Scope is deliberately the PROJECT, not the plan and not the org: credentials and default_url are project-scoped, so two runs of DIFFERENT plans in one project drive the same account against the same environment \u2014 which the pre-existing per-plan mutex RUN_DISPATCH_MUTEX (AG-8221) does not cover \u2014 while an org-wide lock would serialize unrelated products that merely share a payer. The two locks are complementary and use deliberately different staleness thresholds because their safe directions are opposite (AG-8221 may KILL a predecessor, so it wants the smallest value above the observed silence distribution: 600s; this lock only REFUSES a newcomer, so it wants a value above the longest legitimate LIFETIME: the 90-minute sandbox ceiling, overridable via PROJECT_RUN_LOCK_TTL_MS). RETIRED 2026-08-04 (default OFF): with the parent off, PROJECT_RUN_LOCK_UNSHARED cannot revive it either \u2014 pinned by projectRunLockApplies returning false for BOTH shapes, so a stray child flag left in some environment is inert. Fail-open on any error \u2014 a guard that refuses customers' runs because its own bookkeeping broke is worse than the overlap it prevents.",graduation:{status:"permanent",gate:"RETIRED 2026-08-04, not parked: this does not graduate back to default-ON. The credential pool contends on the ACCOUNT \u2014 the resource two concurrent runs actually share \u2014 while this lock refused the whole PROJECT, was active only where the pool lives (org-shared projects), and consumed a leased account before refusing. Kept as `=1` for the one case it would still answer: whole-project serialisation for a plan that signs in with nothing, on a project whose data two runs would genuinely corrupt. Nobody has asked for that; the customer it was built for declined it explicitly.",evidence:"a project that needs whole-project serialisation despite per-account leasing \u2014 none known",owner:"steering (Alex)",review:"2026-11-04"}},PROJECT_RUN_LOCK_UNSHARED:{key:"PROJECT_RUN_LOCK_UNSHARED",envVars:["PROJECT_RUN_LOCK_UNSHARED"],polarity:"1-enables",defaultState:"off",surfaces:["web-next-edge"],cloudForwarded:!1,read:"env-direct",gates:"Extends the PROJECT_RUN_LOCK enforcement (above) from org-SHARED projects to NON-shared, single-owner projects. OFF (default): a project with no live org share is never locked and both seams behave exactly as they do today \u2014 no probe, no reap, no 409. ON: the same one-live-run-or-batch-per-project rule applies to every project regardless of sharing. Parented by PROJECT_RUN_LOCK (=0 disables both halves).",designDoc:"docs/plans/2026-08-03-org-project-sharing-design.md",status:"active",added:"2026-08-03",graduation:{status:"gated",gate:"NOT graduable until concurrent solo runs are either migrated or accepted as breakage. MEASURED ON PROD 2026-08-03: over the trailing 90 days, 50 pairs of SAME-USER runs in ONE project genuinely overlapped in time \u2014 8 distinct users, 8 distinct projects, every pair with BOTH rows terminal (so real concurrency, not a zombie artefact), 35 pairs overlapping >60s and 10 >5 minutes, and every single pair origin='desktop'. Flipping this ON today would start answering 409 to a workflow eight people use. Graduation therefore needs, in order: (1) a decision on whether concurrent desktop runs in one project are legitimate (the desktop app drives a LOCAL browser, so the collision domain argument is weaker there than for cloud runs) \u2014 if they are, the flag needs a per-origin carve-out rather than a flip; (2) if they are not, a client-side surface that explains the refusal before the user starts the second run, since the CLI/SPA today learn about it only from the 409; (3) a shadow census from the shared-project half showing the refusal rate and that no refusal was a false positive. Note the lock cannot be graduated by soak alone: with the shared half dark by data (no org_shared projects on prod), the shared half produces no evidence about the solo population.",evidence:"a product decision on concurrent same-project desktop runs (the measured 50-pair / 8-user population); client-side pre-start surfacing so a refusal is not first learned from a 409; and a shared-project refusal census with zero false positives",owner:"steering (Alex)",review:"2026-08-17"},notes:"Same read discipline as its parent: static process.env member access via projectRunLockUnsharedEnabled() in apps/web-next/lib/project-run-lock.ts, web-next-edge only, never cloud-forwarded. Exists as a separate key rather than a mode on PROJECT_RUN_LOCK because the two populations have opposite evidence: the shared-project case has ZERO current population and ships enforcing, while the solo case has a measured live population and must ship dark. One flag cannot carry two defaults."},PROJECT_CREDENTIAL_POOL:{key:"PROJECT_CREDENTIAL_POOL",envVars:["PROJECT_CREDENTIAL_POOL"],polarity:"0-disables",defaultState:"on",surfaces:["web-next-node"],cloudForwarded:!1,read:"env-direct",gates:"Project credential pool (2026-08-04): a project may hold SEVERAL ACCOUNTS \u2014 each a LOGIN/PASSWORD PAIR \u2014 under one role NAME (`scope='project'` rows of `app.project_credentials`, keyed `(project_id, name, slot)`), provisioned by the project owner or an `OrgMembership.role='admin'` of the owner's org. A caller holding no PERSONAL credential of that name LEASES one for the life of an engine session and releases it at teardown; the lease returns the account's real LOGIN alongside its secret and the seed carries both, because `credential.name` is otherwise the login the agent types into the email field \u2014 a role with no login would be typed AS the login (corrected 2026-08-04 after the first merge shipped role/password, which was one login with N passwords); a lease older than the staleness cutoff is reaped so a lost release cannot wedge a slot (default 5400000 ms = 90 minutes, the sandbox wall-clock ceiling and deliberately the same number as PROJECT_RUN_LOCK_DEFAULT_TTL_MS; overridable via `CREDENTIAL_LEASE_TTL_MS`, whose invalid or non-positive values fall back to the default rather than disabling reaping \u2014 a pool that never reaps would wedge permanently after one lost release). Personal rows always win, so a caller with their own `admin` consumes no shared account and behaves exactly as before. `GET /api/sync/entities/credentials` gains pool NAMES \u2014 never secrets, because it is called on every pre-flight and every seed build; the ONLY route that returns a pool secret is `POST /api/projects/{id}/credential-pool/lease`, and only for the slots it just granted to that lease. An exhausted role answers 409 `{code:'CREDENTIAL_POOL_EXHAUSTED', names}`, which both clients re-tag as the existing `CREDENTIAL_PREFLIGHT_REFUSED` so the refusal lands before a run row or engine session exists. FAIL-CLOSED, unlike the sibling PROJECT_RUN_LOCK: a session seeded without its credential burns a full run to fail at the login step. \"0\" hides the pool everywhere \u2014 list returns personal rows only, leasing 404s, the settings section renders empty \u2014 with no redeploy and no data change. Since 2026-08-06 the pool list route also reports the flag itself as `poolEnabled` (false on the disabled early return, before the grant check, since the flag is a property of the deployment and not of the caller): the settings page infers \"off\" from an empty pool it may not administer, but the NEW-PROJECT form has no project to inspect and so kept offering a Whole-team tab whose write could only 404 \u2014 clients read `poolEnabled` and fail OPEN when it is absent. Dark by data: a project with no `scope='project'` row behaves exactly as today.",designDoc:"docs/plans/2026-08-04-project-credential-pool-design.md",status:"active",added:"2026-08-04",notes:"Not AGENTIQA_-prefixed, so NOT cloud-forwarded: read exclusively in web-next (Vercel env), never in the engine \u2014 same shape as ORG_PROJECT_SHARING and PROJECT_RUN_LOCK. Single read point: credentialPoolEnabled() in apps/web-next/lib/credential-pool.ts, as a static process.env member access. NODE surface, not edge: provisioning encrypts and leasing decrypts with node:crypto, which the edge runtime does not provide \u2014 the routes originally declared runtime='edge', which compiled, type-checked and passed all 20 DB-evaluated tests (they import the handlers directly under Node) and then answered 500 on the first live request. RELATIONSHIP TO PROJECT_RUN_LOCK: that lock models the PROJECT as the contended resource and refuses a second run outright; the pool models the ACCOUNT, which is what two concurrent runs actually collide on, and hands out a distinct one per caller. The run lock is the degenerate case of a pool of size one, and on a project WITH a pool it is the coarser instrument \u2014 it will refuse a run for which a free account exists. Note the run lock's own rationale cites 'credentials are project-scoped', which was never true: app.project_credentials has carried user_id since its first migration and the read path has always been WHERE user_id = caller. Recorded scope cut (design P6): v1 returns the leased secret to the CALLER'S OWN CLIENT, exactly as their own credential already travels, so a member can read a shared secret out of their client; closing that needs server-side seeding (P1.5), which touches the engine image (Pipeline B). There is no lease heartbeat: every normal exit releases explicitly, so the TTL covers only a release that never arrived (closed tab, killed CI job, crashed pod), and it errs long because the failure directions are asymmetric \u2014 a slot wrongly held for an hour costs one refusal, while a slot wrongly freed hands the same account to two live agents, the exact failure the pool exists to prevent. The narrowed HC3 invariant the suite pins: a caller may receive a secret only when it is their own row, OR it is a pool row of a project they may access AND they hold a live lease on that exact slot \u2014 no caller ever receives another MEMBER'S personal row, under any grant, on any route."},ORG_GRANT_DEBIT_AT_FINALIZE:{key:"ORG_GRANT_DEBIT_AT_FINALIZE",envVars:["ORG_GRANT_DEBIT_AT_FINALIZE"],polarity:"1-enables",defaultState:"off",surfaces:["web-next-edge"],cloudForwarded:!1,read:"env-direct",gates:"Moves the org run-grant debit from analytics-ingest to finalize-run (2026-07-30). At ingest neither termination_reason nor the unit's final length is known, so the grant counter over-debited every non-billable termination, could never apply the per-unit min_billable_steps floor, could not refund, and double-debited AG-8220 duplicate pairs \u2014 measured on lio as +1041 / -437 / net +615 units (+24.6 credits) of customer-visible over-consumption against the invoice-authoritative getOrgUsage. When ON, ingest debits nothing and /api/billing/finalize-run/[id] recomputes the whole billing UNIT against getOrgUsage's own math (same unit keying incl. the chat/explore fan-out collapse, same per-(unit,byok) floor, same billable-termination filter) and applies only the DELTA against run_billing.grant_debited_units, claimed atomically per billing unit (one non-interactive Neon transaction behind a pg_advisory_xact_lock on (org, user, unit_key)) \u2014 which makes the debit idempotent (a double-finalize settles 0, and so does a concurrent one), refundable (a non-billable unit recomputes to 0 and the units are credited back), and safe across the flip. ON also activates the in-flight HOLD in the org cap gate (getOrgInFlightHeldUnits): unfinalized runs' not-yet-debited steps are subtracted from remainingUnits, so the orchestrator's per-dispatch admission check keeps decrementing continuously instead of standing still for the whole duration of a run. OFF = SHADOW: the ingest debit continues with byte-identical org_run_grant writes; finalize additionally computes the would-be total, stamps run_billing.grant_shadow_units, and logs grant_debit:finalize_shadow \u2014 the soak corpus that gates graduation.",designDoc:"docs/plans/2026-07-23-ag7872-org-run-grants-design.md",status:"active",added:"2026-07-30",notes:"Not AGENTIQA_-prefixed. Static process.env member access via isGrantDebitAtFinalizeEnabled() in apps/web-next/lib/org-grant-debit.ts (single read point, edge-safe \u2014 read from two Edge routes and the cap gate). Dark by data for every org without run-grant rows: debitOrgGrants/creditOrgGrants match no active grant and issue zero UPDATEs, and personal subjects short-circuit before any query. TRANSITION PROTOCOL (why the flip cannot double-debit): the debit is derived from run_billing.grant_debited_units, so that ledger has to be accurate for HISTORY as well as for new rows. Two things make it so, and both are load-bearing: (a) the 20260730120000 migration BACKFILLS every org-stamped pre-migration row to its step_count \u2014 the amount the ingest boundary actually debited, since /api/analytics/ingest passed the same billedStepIncrement to debitOrgGrants that recordRunStep added to step_count \u2014 and without that backfill a chat session still alive at the flip would recompute alreadyDebited=0 and re-debit its whole history; (b) under the flag OFF the ingest path records what it debits into the same column. A run ingested pre-flip and finalized post-flip therefore settles only trueUnits - alreadyDebited, covered by the flag-flip and post-sweep no-op tests in apps/web-next/lib/org-grant-debit.db.test.ts. EXACTLY-ONCE: the recompute-and-claim is a single non-interactive Neon transaction guarded by a pg_advisory_xact_lock keyed on (org, user, unit_key), because the ledger IS the idempotence guard and a read-modify-write across HTTP round trips let N concurrent finalizes of one unit each claim the whole total (measured 5x on an explore fan-out, 2x on a double finalize; regression suite apps/web-next/lib/org-grant-debit.race.db.test.ts). Historical drift accrued BEFORE the flip is NOT self-correcting (those units never finalize again) and is returned separately by scripts/true-up-org-grant.ts, which is operator-run and never automatic.",graduation:{status:"gated",gate:"Graduate only when the shadow corpus shows the finalize-time recomputation agreeing with getOrgUsage. The corpus is a plain SQL query over run_billing \u2014 compare grant_shadow_units against the unit's summed grant_debited_units, grouped by termination_reason \u2014 NOT scraped logs. Graduation criteria: (a) for billable terminations the shadow total matches the unit's floored getOrgUsage total exactly; (b) every non-billable termination shows a NEGATIVE delta of exactly what ingest debited (the refund the current boundary cannot make); (c) no unit shows a positive delta unexplained by the floor. Then, in this ORDER: (1) run scripts/true-up-org-grant.ts --apply per grant-mode org to return the historical drift (lio: -615 units / -24.6 credits as of 2026-07-30); (2) only then flip to 1 in the web-next Vercel env. The two commute \u2014 the migration's backfill keeps the ledger accurate for history either way \u2014 but sweeping FIRST makes the flip a provable no-op on everything already settled: the sweep rewrites each unit's ledger to its true total, so that unit's next settlement computes a delta of exactly 0 and the flip cannot move a historical unit at all. Flip-first is a correct fallback if the sweep has to wait, not the default. The sweep is operator-run and never automatic.",evidence:"run_billing.grant_shadow_units vs summed grant_debited_units per unit (durable, queryable soak corpus) + the grant_debit:finalize_shadow finalize logs; the unit + live-DB suites in apps/web-next/lib/org-grant-debit.test.ts, org-grant-debit.db.test.ts (per-termination-class debit, chat/explore folding, double-finalize idempotence, AG-8220 duplicate pair, flag-flip no-double-debit, in-flight hold) and org-grant-debit.race.db.test.ts (exactly-once under a 5-way concurrent fan-out over 20 iterations, concurrent double refund, no-active-grant reconcile); scripts/true-up-org-grant.ts dry run per org",owner:"steering (Alex)",review:"2026-08-13"}},EXTENSION_PROFILE_PERSISTENCE:{key:"EXTENSION_PROFILE_PERSISTENCE",envVars:[],polarity:"1-enables",defaultState:"on",surfaces:["desktop-main","desktop-renderer"],cloudForwarded:!1,read:"compile-const",gates:"Saves/restores a per-project Chrome profile (cookies/storage) across sessions so login can be skipped on replay; a false value skips profile persistence.",designDoc:"apps/desktop-next/src/renderer/featureFlags.ts",status:"active",added:"2026-06-01",notes:"Hardcoded TypeScript boolean (`= true`), no env var \u2014 a compile-time gate flipped by editing the const. Defined twice: apps/desktop-next/src/renderer/featureFlags.ts and apps/desktop-next/src/main/computerUse/DesktopPlaywrightService.ts (the main-process copy is the one actually consumed)."},TRANSIENT_ENV_RETRY:{key:"TRANSIENT_ENV_RETRY",envVars:["AGENTIQA_TRANSIENT_ENV_RETRY","AGENTIQA_EXPERIMENT_TRANSIENT_ENV_RETRY"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:`Transient-environment retry-once policy (RunnerRuntime / test-plan runs only, v1 \u2014 never ExplorerRuntime/chat). Motivating incident: staging run run_9fff560a \u2014 the app's POST to its own API failed transiently (console: CORS block + AxiosError "Network Error"), the agent waited 2\xD730s, then filed report_issue and blocked after ONE attempt; later probing showed the API healthy. When on, a report_issue (or the ensuing exploration_blocked) whose failure is a DETERMINISTIC transient-environment stall \u2014 BOTH a stall/timeout symptom (a wait/wait_for_element in the evidence window, or explicit stall/network phrasing; never a content/assertion mismatch) AND an environment signature in the captured EventDigest (any failedRequests, a console/page error matching the network class \u2014 CORS / "Network Error" / AxiosError / net::ERR_ / "fetch failed" \u2014 or an explicit 5xx write) \u2014 is BOUNCED once with a structured instruction to repeat the triggering action and report only if it reproduces, tracked per step index. The second matching failure at the SAME step passes through and the runtime (not the model) stamps the issue evidence JSON with attempts:2, both attempt timestamps, both EventDigest snapshots, sets category='environment', and appends 'Reproduced on retry \u2014 2 attempts.' to the description. Bounds: exactly 1 retry per step, max 2 retried steps per run; a further environment stall after the budget is spent passes through with attempts:1 and the note 'Environment degraded \u2014 not retried (budget exhausted).'. A run-mode prompt nudge asks the agent to retry proactively; the deterministic gate is the backstop (never trust LLM compliance). Off \u21D2 every environment failure files immediately on the first attempt, byte-identical to pre-change behavior. Non-environment failures are never retried regardless of this flag.`,designDoc:"docs/plans/2026-07-23-transient-env-retry-once-design.md",status:"active",added:"2026-07-23",notes:"Ships default-ON from inception (not graduated from an off default) \u2014 the policy is a strict reduction of a confirmed false-block class (one transient blip \u2192 a blocked run + a filed non-defect issue), so there is no pre-change OFF behavior to preserve. Registered in killSwitchDefaultState.test.ts INTENTIONALLY_GRADUATED (the tripwire acknowledging the default-ON state deliberately). Deterministic classifier + state machine in packages/engine-core/src/transientEnvRetry.ts; the kill-switch read + wiring live at packages/engine-core/src/RunnerRuntime.ts handleReportIssue / handleBlocked. Runner lane only."},PREDICATE_BASIS_COMPILE:{key:"PREDICATE_BASIS_COMPILE",envVars:["AGENTIQA_PREDICATE_BASIS_COMPILE","AGENTIQA_EXPERIMENT_PREDICATE_BASIS_COMPILE"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:`Trust-layer predicate basis \u2014 Slice B AUTOFORMALIZATION COMPILER (design docs/plans/2026-07-26-trust-layer-predicate-basis-design.md; north star docs/plans/2026-07-26-trust-layer-verification-architecture.md). "Compile-don't-parse": at AUTHORING an LLM COMPILES a natural-language assertion into a typed Slice-A logical form (Predicate) over named observables \u2014 autoformalization (utterance \u2192 logical form \u2192 executor \u2192 denotation) \u2014 NOT a verdict. Slice A shipped the schema + generic deterministic executor; Slice B produces the forms the executor denotes. TASK-BLIND: the compiler compiles the assertion's MEANING (including any pinned expected value the assertion text itself carries) and never sees the observed screen or pass/fail \u2014 the firewall keeps the LLM out of the DECISION (Slice A's deterministic executor owns pass/fail). GRAMMAR-CONSTRAINED \u2192 MECHANICAL ABSTAIN: the model may only emit an in-grammar kind (count/delta/absence/modification/presence/typed) or the explicit not_groundable escape; compileToPredicate then STRICTLY validates the chosen kind's operands, so an inherently-subjective assertion ("the agent responds correctly", "looks clean") or an in-grammar kind with missing/invalid operands routes to a first-class ABSTAIN (AbstainNode) \u2014 NEVER a forced/invalid form. The grammar's expressiveness IS the verifiable/subjective boundary. v1 = structured output + strict schema validation + a groundability decision; full constrained decoding (PICARD) is a later refinement (TODO). GRADUATED default-ON 2026-07-27 (alongside PREDICATE_BASIS_VERIFY, which is the live consumer that routes a compiled form to a step result); the emergency kill-switch is retained and byte-identical WHEN FORCED OFF: the seam (runAssertionCompile / runRedundantCompile) short-circuits to \`disabled\` (ZERO model calls) when AGENTIQA_PREDICATE_BASIS_COMPILE=0 \u2014 and with COMPILE forced off but VERIFY on the compiler returns a disabled result \u2192 ABSTAIN (safe, never a manufactured verdict). Cost when on: one auxiliary structured-output call per compiled assertion (cost-isolated via emitAuxiliaryLlmUsage \u2014 not a billable step), thinkingBudget:0, hard-capped by a per-call timeout; fail-closed (error/empty/timeout/out-of-grammar \u2192 ABSTAIN).`,designDoc:"docs/plans/2026-07-26-trust-layer-predicate-basis-design.md",status:"active",added:"2026-07-26",notes:"GRADUATED 2026-07-27 (default ON) \u2014 the authoring-side ARc compiler that feeds the LIVE verify wiring (PREDICATE_BASIS_VERIFY, graduated in the same PR). Turn-on = BOTH flags ON: with VERIFY on, runPredicateBasisVerify calls runRedundantCompile, which rides THIS flag; on \u21D2 the compiler produces the logical forms the deterministic executor denotes. Slice B (authoring-side compiler) + Slice C (ARc redundant-compile: compile k\xD7 \u2192 ABSTAIN on disagreement/degeneration, attacking the factKind-wrong-kind gap) are the compile consumers of this flag. GRADUATION EVIDENCE: the flagship live-replay benchmark (PR #1879) returned GATE=GO \u2014 zero-regression on the prod Lio (semantic-reference) + Miro (canvas) corpora, the firewall intact (a groundable contradiction FAILS; nothing ungroundable is ever a hard PASS), the vision/canvas path abstains, and the #1876 DOM-reachability fix live-confirmed. TASK-BLIND / FIREWALL (unchanged): the compiler compiles the assertion's MEANING (never sees the observed screen nor pass/fail); Slice A's deterministic executor owns the decision. GRAMMAR-CONSTRAINED \u2192 MECHANICAL ABSTAIN: an inherently-subjective or out-of-grammar assertion routes to a first-class ABSTAIN (AbstainNode), never a forced/invalid form. Off (explicit AGENTIQA_PREDICATE_BASIS_COMPILE=0) is byte-identical to pre-graduation: runAssertionCompile / runRedundantCompile short-circuit to `disabled` with ZERO model calls, and with COMPILE off but VERIFY on the compiler returns a disabled result \u2192 ABSTAIN (safe, never a manufactured verdict) \u2014 the emergency kill-switch still works. killSwitch resolves the no-env value from this defaultState (#1729), so graduation = defaultState 'on' AND PREDICATE_BASIS_COMPILE listed in INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire). Read site: packages/engine-core/src/predicateBasis/compile.ts (runAssertionCompile) + packages/engine-core/src/predicateBasis/redundantCompile.ts (runRedundantCompile). Cost when on: one auxiliary structured-output call per compiled assertion, cost-isolated via emitAuxiliaryLlmUsage (not a billable step), thinkingBudget:0, hard-capped, fail-closed (error/empty/timeout/out-of-grammar \u2192 ABSTAIN). Acceptance tests: packages/engine-core/src/__tests__/predicateBasisCompile.test.ts + predicateBasisRedundantCompile.test.ts. Compile-correctness benchmark: e2e/benchmark/compileCorrectness.ts. Binds claims verify.predicate-basis-compile-correctness + verify.predicate-basis-redundant-compile. STAGING-FIRST: merging to staging makes this default-ON on the STAGING engine only; PROD stays OFF until a later staging\u2192main release carries it (a natural staged soak). PREDICATE_BASIS_CONSENSUS (Slice D vision-consensus) stays default-OFF \u2014 vision-consensus is safe ONLY as abstain, not turn-on-ready (the P2 dense-abstain finding)."},PREDICATE_BASIS_CONSENSUS:{key:"PREDICATE_BASIS_CONSENSUS",envVars:["AGENTIQA_PREDICATE_BASIS_CONSENSUS","AGENTIQA_EXPERIMENT_PREDICATE_BASIS_CONSENSUS"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:'Trust-layer predicate basis \u2014 Slice D: (1) CONSENSUS EXTRACTION/RESOLUTION and (2) the SEMANTIC-TOLERANCE FALLBACK TIER (design docs/plans/2026-07-26-trust-layer-predicate-basis-design.md \u2014 \xA7 three buckets bucket-2 canvas "task-blind extraction + deterministic comparison + consensus-or-abstain", \xA7 the @reference resolution consensus/abstain, \xA7 "Semantic as a fallback tier, not a peer primitive"; north star hard-part #1 "the extractor itself can be wrong: miscount a canvas, misread"). CONSENSUS: task-blindness removes confirmation bias but NOT perception error (the model genuinely miscounts a canvas / mis-resolves "the cart total"); the no-false-positive mitigation is N INDEPENDENT task-blind extractions must AGREE, else ABSTAIN \u2014 one mechanism serving BOTH the vision/canvas path AND the semantic-reference resolution risk. SEMANTIC-TOLERANCE: run the DETERMINISTIC executor FIRST; invoke the task-blind concept classifier ONLY when the deterministic comparison cannot decide (tolerance:semantic OR an inconclusive comparison) \u2014 confident semantic = Verified, ambiguous = Assessed (a lean + confidence, never a grounded badge, never a manufactured pass \u2014 the bucket-3 Assessed slot). SHADOW / behavior-neutral even when ON: NOT wired into any live RunnerRuntime verdict path \u2014 the two drivers are consumed only by the consensus benchmark + unit tests. Off \u21D2 ZERO extractor/classifier calls, byte-identical; Slice-A deterministic parity is UNAFFECTED (the semantic tier COMPOSES the untouched evaluate, only reached on an inconclusive result).',designDoc:"docs/plans/2026-07-26-trust-layer-predicate-basis-design.md",status:"active",added:"2026-07-26",notes:"Trust-layer predicate basis Slice D (consensus extraction/resolution + semantic-tolerance fallback tier). Default OFF; SHADOW / behavior-neutral even when ON \u2014 the two drivers produce would-be results and are wired into NO live verdict path (consumers: the consensus benchmark + unit tests). Read sites: packages/engine-core/src/predicateBasis/consensusExtract.ts (runConsensusExtraction \u2192 killSwitchEnabled('PREDICATE_BASIS_CONSENSUS'); zero extractor calls when off) + packages/engine-core/src/predicateBasis/semanticTolerance.ts (runSemanticTolerance \u2192 killSwitchEnabled('PREDICATE_BASIS_CONSENSUS'); zero classifier calls when off). CONSENSUS-EXTRACT: a PURE reducer (reduceNumericConsensus / reduceCategoricalConsensus \u2014 N observations \u2192 accept-on-agreement / ABSTAIN(extraction_disagreement) on numeric-spread-beyond-tolerance or different presence/text; fail-closed extraction_error when no usable sample) + a pure per-frame read (readObservable over count-of / presence-of / value-at) + the thin flag-gated driver over the EXISTING StateExtractor seam (reused, not re-implemented \u2014 N independent samples). SEMANTIC-TOLERANCE: a PURE reducer (resolveSemanticTier \u2014 deterministic-first; decided deterministic \u2192 Verified with no classifier call; inconclusive + eligible + injected classifier \u2192 semanticToSpectrum: confident match/contradiction \u2192 Verified, ambiguous \u2192 Assessed, abstain \u2192 the Inconclusive floor) + the flag-gated driver that COMPOSES the untouched Slice-A evaluate (Slice-A parity byte-identical by construction). Reuses the Slice-2 concept-classifier seam (ConceptClassifier / semanticToSpectrum / renderObservation from groundedStateUnified.ts). Deliberately NOT re-exported from predicateBasis/index.ts (imported directly). Acceptance tests: packages/engine-core/src/__tests__/predicateBasisConsensus.test.ts (deterministic \u2014 injected observations for the consensus reducer; injected classifier RESULT for the semantic tier; flag-OFF zero-calls; fail-closed error\u2192abstain). Consensus benchmark: e2e/benchmark/consensusExtraction.ts (deterministic reducer/tier self-validation always runs; the live N-extraction + classifier measurement is PENDING until a keyed run \u2014 Slice-1/B/C precedent, no fabricated number). Binds claim verify.predicate-basis-consensus-extraction. Slice E (abstain-rate as a first-class COVERAGE metric on a real-assertion corpus) is BUILT: packages/engine-core/src/predicateBasis/coverage.ts (aggregateCoverage \u2014 a PURE reducer folding pipeline-routed assertion outcomes into the Verified/Assessed/Inconclusive distribution + abstain rate + abstain-origin/per-kind breakdown), wired into NO live verdict path (no new flag; the live measurement reuses THIS flag + PREDICATE_BASIS_COMPILE), claim verify.predicate-basis-coverage-abstain-rate, benchmark e2e/benchmark/coverage.ts (deterministic layers real; live full-pipeline number PENDING until keyed). This COMPLETES the predicate-basis v1 build; the remaining work is the KEYED phase (live LLM measurements) + the prod Miro+Lio final gate.",graduation:{status:"gated",gate:"Slice D is shadow / behavior-neutral (two drivers, wired into NO verdict path). Graduation gates on: (1) the consensus benchmark run WITH a model key showing consensus ABSTAINS on genuinely disagreeing/noisy N task-blind extractions and ACCEPTS on agreement (the miscount-a-canvas / mis-resolve-a-reference no-false-positive mechanism), plus a measured abstain-rate on the vision/canvas + semantic-reference corpus; (2) the semantic-tolerance tier measured deterministic-first (a deterministic-decidable case never consulting the classifier) with confident\u2192Verified / ambiguous\u2192Assessed; (3) the engine-core predicateBasisConsensus unit suite green. This is the DEFERRED consensus-extraction that gates ANY vision-path live graduation; the FINAL gate is the prod Miro (canvas) + Lio (semantic-reference) benchmark.",evidence:"e2e/benchmark/artifacts/benchmark-consensus-extraction.{json,md} (consensus abstain/accept + abstain-rate + semantic-tier bands; PENDING until a keyed run) + the engine-core predicateBasisConsensus unit suite + the claim verify.predicate-basis-consensus-extraction",owner:"steering (Alex)",review:"2026-09-05"}},PREDICATE_BASIS_VERIFY:{key:"PREDICATE_BASIS_VERIFY",envVars:["AGENTIQA_PREDICATE_BASIS_VERIFY","AGENTIQA_EXPERIMENT_PREDICATE_BASIS_VERIFY"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Trust-layer predicate basis \u2014 Slice F LIVE VERIFY WIRING (the FIRST non-shadow slice; design docs/plans/2026-07-26-trust-layer-predicate-basis-design.md \xA7 three buckets; north star docs/plans/2026-07-26-trust-layer-verification-architecture.md \xA7 the verdict spectrum). Slices A\u2013E built the basis as SHADOW machinery wired into NO verdict path; this is the first wiring that lets a compiled logical form FEED A STEP RESULT (the readiness step toward turn-on). When ON, a verify step's assertion routes through the basis: runRedundantCompile (ARc: compile k\xD7 \u2192 ABSTAIN on disagreement/degeneration) \u2192 classifyVerifyBucket \u2192 the deterministic executor / semantic-tolerance tier \u2192 a verdict in the SPECTRUM (Verified-PASS/FAIL | Assessed | Inconclusive) that feeds the step result. SCOPED to the SAFE paths \u2014 everything else ABSTAINS, never manufactures a verdict: DOM-groundable (deterministic executor over structured extraction) \u2192 Verified-PASS/FAIL LIVE (a grounded contradiction FAILS \u2014 the no-false-positive core); confident-semantic (semantic-tolerance, deterministic-first, task-blind classifier) \u2192 Verified, ambiguous \u2192 Assessed (non-blocking WARNING band, never a hard pass/fail); VISION/CANVAS (bucket 2) \u2192 ABSTAIN (Inconclusive/WARNING), NOT enforced \u2014 the mandatory dense-abstain guard from the P2 finding (consensus does NOT eliminate correlated-miscount false-confirms on dense canvases, so vision must NOT produce a live PASS/FAIL yet; a count tally is treated as vision-grounded \u2192 ABSTAIN unless the caller asserts domGrounded); not-groundable / compile-abstain / consensus-abstain \u2192 Inconclusive/Assessed, NEVER a manufactured pass. FIREWALL INVARIANT: a groundable contradiction MUST FAIL; nothing ungroundable is EVER a hard PASS \u2014 verifyStepAction encodes the DEFAULT consequence policy (only a Verified-FAIL gates; a Verified-PASS confirms but never manufactures/overrides a pass; Assessed is a non-blocking warning; Inconclusive leaves the existing verdict). GRADUATED default-ON 2026-07-27 (alongside PREDICATE_BASIS_COMPILE) after the flagship live-replay gate; the emergency kill-switch is retained and BYTE-IDENTICAL WHEN FORCED OFF (non-negotiable): explicit AGENTIQA_PREDICATE_BASIS_VERIFY=0 makes runPredicateBasisVerify short-circuit to disabled with ZERO compile/executor/classifier calls and the RunnerRuntime enforcement pass early-return before any effect. The compile itself rides PREDICATE_BASIS_COMPILE (also graduated), so full turn-on = both flags ON (with VERIFY on but COMPILE forced off the compiler returns a disabled result \u2192 ABSTAIN, safe). STAGING-FIRST: default-ON reaches the STAGING engine on merge; PROD stays OFF until a later staging\u2192main release carries it.",designDoc:"docs/plans/2026-07-26-trust-layer-predicate-basis-design.md",status:"active",added:"2026-07-27",notes:"GRADUATED 2026-07-27 (default ON) \u2014 Slice F LIVE verify wiring, the FIRST live trust-verdict on the predicate basis (turned on alongside PREDICATE_BASIS_COMPILE per Alex's explicit approval after the flagship live-replay gate). When on (and a predicateBasisCompiler is wired at engine boot \u2014 buildDeps.getPredicateBasisCompiler, present whenever a Google key is set), a verify step's assertion routes through the basis: runRedundantCompile (ARc, rides PREDICATE_BASIS_COMPILE) \u2192 classifyVerifyBucket \u2192 the deterministic executor / semantic-tolerance tier \u2192 a SPECTRUM verdict feeding the step result. SCOPED to the SAFE paths \u2014 everything else ABSTAINS, never manufactures a verdict: DOM-groundable \u2192 Verified-PASS/FAIL LIVE (a grounded contradiction FAILS \u2014 the no-false-positive core); confident-semantic \u2192 Verified, ambiguous \u2192 Assessed (non-blocking WARNING); VISION/CANVAS \u2192 ABSTAIN (Inconclusive), NOT enforced \u2014 the mandatory P2 dense-abstain guard (a count tally is vision-grounded \u2192 ABSTAIN unless the caller asserts domGrounded); not-groundable / compile-abstain \u2192 Inconclusive, NEVER a manufactured pass. FIREWALL INVARIANT (unchanged): only a Verified-FAIL gates; a Verified-PASS confirms but never manufactures/overrides a pass; Assessed warns; Inconclusive leaves the existing verdict. GRADUATION EVIDENCE: the flagship live-replay benchmark (PR #1879) returned GATE=GO \u2014 zero-regression on the prod Lio (semantic-reference) + Miro (canvas) corpora, the firewall intact, canvas-abstain intact, and the #1876 DOM-reachability fix live-confirmed; the engine-core predicateBasisVerifyWiring + RunnerRuntime.predicateBasisVerify unit suites are green (flag-OFF byte-identical parity, safe-path enforcement, vision-canvas ABSTAIN, the firewall). BYTE-IDENTICAL WHEN FORCED OFF (emergency kill-switch retained): explicit AGENTIQA_PREDICATE_BASIS_VERIFY=0 restores the pre-graduation behavior \u2014 runPredicateBasisVerify short-circuits to disabled with ZERO compile/executor/classifier calls and the RunnerRuntime enforcement pass early-returns before any effect. killSwitch resolves the no-env value from this defaultState (#1729), so graduation = defaultState 'on' AND PREDICATE_BASIS_VERIFY listed in INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire). Read sites: packages/engine-core/src/predicateBasis/verifyWiring.ts (runPredicateBasisVerify) + packages/engine-core/src/RunnerRuntime.ts (predicateBasisVerifyEnabled / the run_complete enforcement pass). Acceptance tests: packages/engine-core/src/__tests__/predicateBasisVerifyWiring.test.ts + RunnerRuntime.predicateBasisVerify.test.ts. E2E proof: e2e/benchmark/predicateBasisVerify.ts. Standing live gate post-graduation: e2e/scenarios/22-verify/01-predicate-basis-dom-enforce.ts asserts the pbv-ON DOM-enforce + canvas-abstain behavior by DEFAULT now (default-ON engine) in the nightly qa-exhaustive path. Binds claim verify.predicate-basis-verify-wiring. STAGING-FIRST: merging to staging makes pbv default-ON on the STAGING engine ONLY (staging deploys from staging); PROD stays OFF until a later staging\u2192main release carries it \u2014 a natural staged soak. VISION-path live PASS/FAIL stays DEFERRED behind PREDICATE_BASIS_CONSENSUS (still default-OFF) regardless of this flag \u2014 vision-consensus is safe ONLY as abstain, not turn-on-ready (the P2 finding)."},ELEMENT_STATE_GROUNDING:{key:"ELEMENT_STATE_GROUNDING",envVars:["AGENTIQA_ELEMENT_STATE_GROUNDING","AGENTIQA_EXPERIMENT_ELEMENT_STATE_GROUNDING"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:'Trust-layer predicate basis \u2014 ELEMENT-STATE grounding (residual track P2, slice 1: BUILD + SHADOW; design docs/plans/2026-07-27-element-state-grounding-design.md). Closes a groundability gap in the DOM-verify path (routeDomVerify / domExtractionSource): the deterministic a11y-outline extraction grounds PRESENCE / ABSENCE / COUNT / MODIFICATION but STRIPS element STATE \u2014 a `- button "Submit" [disabled]` line\'s `[disabled]`, an `aria-checked` / `[selected]` / `[expanded]` marker, and an input\'s inline value \u2014 so an assertion like "the Submit button is enabled" is NOT DOM-groundable and falls through to flaky model-vision grading (the Lio submission s8 flake: a weak vision-only read of button-enabled that varied run-to-run). The a11y snapshot (Playwright _snapshotForAI) DETERMINISTICALLY carries this state; this track parses it into a new `state-of` observable + `element-state` predicate the Slice-A executor denotes over, so enabled/disabled/checked/selected/expanded/value assertions ground deterministically. FIREWALL PRESERVED: a state that is NOT resolvable from the a11y outline (target element not found, dimension not applicable to the role, mixed/unreadable value) ABSTAINS (Inconclusive) \u2014 never force-fails on unresolvable, never manufactures a pass; enforcement stays force-fail-only (only a grounded CONTRADICTION \u2192 would_fail gates). SLICE 1 = SHADOW / behavior-neutral even when ON: the machinery (extraction + NL parser + mapping + the flag-gated driver runElementStateGrounding) is wired into NO live RunnerRuntime verdict path \u2014 consumed only by unit tests (+ a later graduation benchmark). Off \u21D2 ZERO element-state extraction / parse / evaluate, byte-identical (the new observable/extraction runs only under this flag); the existing pbv DOM/count/absence/modification paths are UNTOUCHED. Graduation (benchmark + gate-review + Alex\'s default-on flip + live wiring) is a LATER slice, mirroring the pbv arc.',designDoc:"docs/plans/2026-07-27-element-state-grounding-design.md",status:"active",added:"2026-07-27",notes:"Residual track P2 (element-state grounding) slice 1 \u2014 BUILD + SHADOW behind a default-OFF killSwitch, mirroring the predicate-basis Slice A\u2013E / Slice-D CONSENSUS shadow precedent (pure machinery + flag-gated driver, wired into NO live verdict path). Read site: packages/engine-core/src/predicateBasis/elementState.ts (runElementStateGrounding \u2192 killSwitchEnabled('ELEMENT_STATE_GROUNDING'); zero element-state work when off). PURE machinery (also in elementState.ts, always safe to call, exercised in the unit suite): extractElementStatesFromAriaSnapshot (per-a11y-line state parse keyed on Playwright's exact role\u2192dimension applicability \u2014 kAriaDisabledRoles / kAriaCheckedRoles / kAriaSelectedRoles / kAriaExpandedRoles \u2014 so absence-of-marker on an applicable role reads as false, and a non-applicable role ABSTAINS rather than guessing), parseElementStateAssertion (deterministic NL \u2192 {ref, dimension, expected}; returns null when no state vocabulary is present so it is additive), elementStateQueryToLogicalForm (\u2192 the `element-state` node), and evaluateElementState (in executor.ts, the Slice-A generic executor's new case \u2014 resolves the target element in frames.after.elementStates, reads the dimension, denotes PASS on confirm / FAIL on a grounded contradiction / INCONCLUSIVE on any unresolvable \u2014 target absent, dimension N/A, mixed/ambiguous). GROUNDABLE STATES: enabled/disabled (fully both directions \u2014 the Lio s8 case), checked/unchecked, selected, expanded (PASS on [expanded] + FAIL on a collapsed-assertion contradiction; absence ABSTAINS \u2014 collapsed vs non-expandable is not distinguishable in the outline), and input value (normalized-equality; typed/locale-tolerant value comparison is a follow-up routing to compareTyped). Acceptance tests: packages/engine-core/src/__tests__/predicateBasisElementState.test.ts (extraction, parser, mapping, executor firewall, flag-OFF zero-work / byte-identical, registry-presence freshness canary). killSwitch resolves the no-env value from this defaultState (#1729) \u2014 GRADUATING = flip defaultState to 'on' AND add ELEMENT_STATE_GROUNDING to INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts (the tripwire); slice 1 stays OFF.",graduation:{status:"gated",gate:"Slice 1 is BUILD + SHADOW (pure machinery + a flag-gated driver, wired into NO verdict path). Graduation gates on the LATER slice: (1) an element-state graduation-benchmark run showing enabled/disabled/checked/selected/expanded/value ground deterministically from the a11y outline (a grounded contradiction \u2192 would_fail FIXES the Lio-s8-class vision flake; an unresolvable state \u2192 ABSTAIN, no false-PASS / no false-FAIL) and adversarially proven able to say NO-GO; (2) wiring runElementStateGrounding into the RunnerRuntime run_complete enforcement pass (force-fail-only, alongside applyPredicateBasisVerify); (3) the engine-core predicateBasisElementState unit suite green + flag-OFF byte-identical; then Alex flips the default-on (and lists it in INTENTIONALLY_GRADUATED). Mirrors the pbv graduation arc (shadow \u2192 benchmark \u2192 gate-review \u2192 default-on).",evidence:"e2e/benchmark element-state corpus (grounded PASS/FAIL vs ABSTAIN; Lio-s8 vision flake fixed; PENDING until the graduation slice) + the engine-core predicateBasisElementState unit suite + the (later) claim verify.element-state-grounding",owner:"steering (Alex)",review:"2026-08-15"}},VERIFY_CONFLICT_RECONCILE_SNAPSHOT:{key:"VERIFY_CONFLICT_RECONCILE_SNAPSHOT",envVars:["AGENTIQA_VERIFY_CONFLICT_RECONCILE_SNAPSHOT","AGENTIQA_EXPERIMENT_VERIFY_CONFLICT_RECONCILE_SNAPSHOT"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Verify-conflict DETERMINISTIC snapshot reconcile \u2014 M1 of the verify-oracle reconcile design (docs/plans/2026-07-28-verify-oracle-reconcile-design.md; GH #1907, root cause of #1813). WHAT IT DECIDES: at the SECOND verification-conflict escalation ONLY (the run_complete pass that would otherwise force-FAIL; the first bounce is untouched, and the bounce alone already resolves ~73% of oracle failures), the runtime takes ONE forced-full a11y snapshot for the whole run and re-checks each conflicted step's recorded wait literal (`_verifyOracleFailureDetails[step].literal`) against it with the SAME whole-token `pinPresentInPage` matcher, under the `waitLiteralHasSignificantTokens` significance floor. ZERO LLM calls \u2014 the model is not in the loop, so a lie about a genuinely broken wait fails constructively. THREE OUTCOMES: (a) literal significant AND present in the fresh snapshot \u2192 CLEARED: the oracle failure is removed from the ledger, the step's own already-passed criteria grades stand, the step passes, and the step is TAINTED via noteStepReopenedForReverify (the runtime consumed a re-observation it demanded, so predicate-basis-verify must not enforce over a basis it cannot attribute \u2014 the same invariant the bounce path carries); (b) literal significant and ABSENT from the fresh snapshot \u2192 GROUNDED NEGATIVE: the hard fail stands and the step is permanently INELIGIBLE for the VERIFY_CONFLICT_WITHHOLD warning band; (c) INAPPLICABLE \u2014 which is the only input VERIFY_CONFLICT_WITHHOLD (M2) ever sees \u2014 for no literal / insignificant literal / capture failed / budget already spent / the plan-derived `pin_page_grounding` oracle (excluded WHOLESALE \u2014 a plan-pin failure is a deterministic contradiction, not an absence of confirmation) / **any negation-or-absence marker on the step text or a criterion check** (`stepCarriesNegationOrAbsenceMarker`, reason `negation_marker`: a presence-clear is INVERTED for an assertion that the target is gone, and the absence lane's routing lexicon is narrow BY DESIGN, so this deliberately broader screen \u2014 not/no/none/without/missing/gone/empty/removed/deleted/closes/hidden/disappear*/vanish*/clear*/away/left-the-list/moves-to-trash/ceases-to/count-drops/zero-rows/fewer/struck-through/back-to-default/\u2026 plus a minimal DE/FR set, read over prose with only the target literal's OCCURRENCES removed (round-3: the earlier strip also deleted every \u22654-char TOKEN of the literal from the whole check, so a destination name like 'Deleted Items' silently disarmed the screen) \u2014 keeps every such step ineligible. HONEST LIMIT: this is a best-effort keyword screen over open-ended NL and completeness is unreachable; a phrasing it misses stays eligible and can be presence-cleared. The guarantee for absence assertions is the ABSENCE_AWARE_VERIFY lane, which decides with evidence rather than vocabulary) / **a literal that is not PLAN-GROUNDED whole-token with \u22652 tokens** (`waitLiteralGroundsSnapshotClear`, reason `literal_not_plan_grounded`: a CLEAR is only sound when the literal is the AUTHOR's expectation, read with the same `pinPresentInPage` matcher against the step text + criteria check/expectedValue \u2014 a model-invented literal that whole-token-matches unrelated chrome on another screen proves nothing, and a one-token literal is never distinctive enough even when the plan does contain it. KNOWN BOUNDARY, documented not fixed (round-3): this gate reads AUTHORSHIP, never PLACE \u2014 an AUTHORED literal that appears in unrelated chrome (a nav item, a help-sidebar link, the header of an Error 500 page) CLEARS exactly as it would in the region the step names; page-state and container binding are the next slice, and until then such clears are only visible in the `verify_conflict_reconcile:would_clear` census the graduation soak reads). Every inapplicable decision is logged per step as `verify_conflict_reconcile:inapplicable` with its reason. DETECTION ALWAYS RUNS (shadow): with the flag OFF the same decision is computed against the RETAINED full-snapshot corpus (`_fullSnapshotByStep`) \u2014 NO fresh capture, so OFF stays byte-identical including ZERO extra browser actions \u2014 and logged as `verify_conflict_reconcile:would_clear` / `:would_fail` with `basis:'retained_snapshot'` and `enforced:false`; ON logs the same events with `basis:'fresh_snapshot'` and `enforced:true`. Exactly ONE reconcile snapshot per run (guarded); a second escalation after the budget is spent decides INAPPLICABLE rather than re-enforcing off a stale page. NOT COVERED BY THIS FLAG (deferred slices, see the design \xA7 4): dropping `wait_for_element` from VERIFY_EVIDENCE_ACTIONS (own flag `VERIFY_EVIDENCE_REQUIRE_CAPTURE`, needs a shadow taint-rate measurement first), and still-running-vs-terminal recognition (a nested run still in flight at run end reads as a grounded negative and correctly stays red in this slice \u2014 the honest known gap). Runner lane only (RunnerRuntime run_complete).",designDoc:"docs/plans/2026-07-28-verify-oracle-reconcile-design.md",status:"active",added:"2026-07-28",notes:"M1 of the verify-oracle reconcile track (design docs/plans/2026-07-28-verify-oracle-reconcile-design.md), default OFF. Fixes the residual verify-conflict FALSE-FAIL that `canReconcileVerificationConflict` deliberately refuses: a PLAN-GROUNDED wait literal (repro run_5a6f0681 step 2, plan tp_vmlio_d1360115, literal 'QA Base \u2014 Draft Completion (do not modify)'; asess_1785247451027 step 1 is the same shape). That refusal is CORRECT as a static rule \u2014 a plan-grounded literal is a real must-pass presence oracle \u2014 so the fix is not to relax the rule but to consult the PAGE one more time: the element rendered late, and a fresh forced-full snapshot proves it. THREE FACTUAL CORRECTIONS to #1907 this design records (staging code, not the issue's reading): (a) evidence is NOT recorded on a timed-out oracle \u2014 the `_verifyEvidenceStepIndexes` add sits in the SUCCESS branch, so a timeout does not satisfy the evidence gate; (b) 'criteria are advisory' is too broad \u2014 `deriveStepStatusFromCriteria` does derive from grades; the real root is that the verify-conflict force-fail OVERRIDES already-passed criteria because `oracleConflictReason` never sees stepResults; (c) an existing valve (`canReconcileVerificationConflict`) already reconciles the non-plan-grounded case to a full PASS \u2014 the residual false-fail is exactly its plan-grounded refusal branch, not the whole mechanism. Read site: packages/engine-core/src/RunnerRuntime.ts (verifyConflictReconcileSnapshotEnabled \u2192 reconcileVerifyConflictsFromSnapshot, called from the second verification-conflict escalation in handleRunComplete). Acceptance tests: packages/engine-core/src/__tests__/RunnerRuntime.verifyConflictReconcile.test.ts (B-R1 repro form + B-C1..B-C9 counter-probes + the flag-OFF parity / one-snapshot-per-run invariants). killSwitch resolves the no-env value from this defaultState (#1729) \u2014 graduating means flipping defaultState to 'on' AND listing the key in INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts.",graduation:{status:"gated",gate:"Graduation gates on the LIVE harness, not on units: Chin's ci-first-plan `tp_c0dcd706` run \xD710 against staging with the flag ON \u2014 baseline is 0/10, the target is \u22658/10 on the step-9 mode (post-login redirect), with the cleared-vs-would_fail split reported from the `verify_conflict_reconcile:*` diags. The step-17 mode (nested run still in flight at run end) is EXPECTED to stay red in this slice; if it turns green the significance threshold is leaky and that is a bug to investigate, NOT evidence to graduate on. Plus: a staging shadow soak of `verify_conflict_reconcile:would_clear` / `:would_fail` sizing the cleared rate and confirming no would_clear fires on a genuinely-absent target, and the engine-core verifyConflictReconcile unit suite green with flag-OFF parity over the full runner corpus.",evidence:"staging `verify_conflict_reconcile:would_clear` / `:would_fail` diag events (cleared rate, basis, per-step literals) + the tp_c0dcd706 \xD710 harness pass rate per failure mode + packages/engine-core/src/__tests__/RunnerRuntime.verifyConflictReconcile.test.ts",owner:"steering (Alex)",review:"2026-08-15"}},VERIFY_CONFLICT_WITHHOLD:{key:"VERIFY_CONFLICT_WITHHOLD",envVars:["AGENTIQA_VERIFY_CONFLICT_WITHHOLD","AGENTIQA_EXPERIMENT_VERIFY_CONFLICT_WITHHOLD"],polarity:"1-enables",defaultState:"off",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Verify-conflict RESIDUAL WARNING policy \u2014 M2 of the verify-oracle reconcile design (docs/plans/2026-07-28-verify-oracle-reconcile-design.md). Strictly downstream of VERIFY_CONFLICT_RECONCILE_SNAPSHOT (M1): it sees ONLY the conflicts M1 could not decide (no captured literal, an insignificant literal, a failed capture, or the reconcile budget already spent) and NEVER a step M1 read as a GROUNDED NEGATIVE \u2014 a target the runtime looked for and did not find always stays a hard FAIL. When ON, such a residual conflict is soft-withheld to a step-level WARNING ('could not be independently confirmed on this run') instead of force-FAILing the run. ALL SEVEN conditions must hold or the existing force-fail stands: (1) the unresolved oracle is a wait-style action, never the plan-derived `pin_page_grounding` oracle; (2) every graded criterion on the step passed with a non-empty substantiation note (the same bar `canReconcileVerificationConflict` uses) AND the step result is still 'passed' with no other deterministic negative on it (no reobserve-withhold, no upstream floor having already flipped it) ; (3) \u22651 STRICT criterion carries an engine-corroborated concrete value \u2014 its `observed` (or, absent that, its pinned `expectedValue`) is whole-token present in the retained page corpus via `pinPresentInPage`; (4) the wait literal's significant tokens are corroborated by the `observed`/note corpus of some passed criterion (this is what links 'what we waited for' to 'what was confirmed', and it is why a short/frequent literal like 'ok' or '3' can never withhold \u2014 the significance floor rejects it); (5) the step is NOT absence-intent (`checkTextAssertsAbsence` \u2014 that shape belongs to ABSENCE_AWARE_VERIFY, which owns its own confirm/abstain ladder) and, since that lexicon is narrow by design, ALSO carries no negation-or-absence marker at all under the broader M1 screen (`stepCarriesNegationOrAbsenceMarker`, reason `negation_marker`) \u2014 a warning there would withdraw a force-fail the engine has no evidence to withdraw; (6) \u2014 folded into (2) \u2014 no other deterministic negative on the step; (7) the step was OBSERVED AGAIN after the conflict bounce (a runtime capture-sequence watermark taken at the bounce; a verbatim resubmit of pre-bounce grades never withholds). DETECTION ALWAYS RUNS (shadow): `verify_conflict_withhold:would_withhold` (and a reasoned `:inapplicable`) is logged with `enforced:` reflecting the flag, so OFF is byte-identical with telemetry only. KNOWN, DELIBERATE SIDE EFFECTS when enforcing (asserted in CI, not accidents): withdrawing the forced conflict fail leaves `terminationReason='completed'`, which makes the run BILLABLE; and no 'Potential Issue Detected' card is synthesized for a withheld step \u2014 one fewer false issue. Runner lane only (RunnerRuntime run_complete).",designDoc:"docs/plans/2026-07-28-verify-oracle-reconcile-design.md",status:"active",added:"2026-07-28",notes:"M2 of the verify-oracle reconcile track, default OFF and inert unless M1's decision for the step is INAPPLICABLE. The honesty floor is the point: a withheld step is a WARNING \u2014 never a pass, never a fail \u2014 so this flag can only ever move a run out of a false RED into an honest AMBER, and it can never manufacture a green. The grounded-negative exclusion is the hard invariant (design \xA7 3): M1 having actually read the page and not found the target is exactly the evidence that the step is genuinely broken, so those never enter this path. Measured coverage of conditions (2)-(4) on the design's corpus is 32/33 steps; the one exception (staging ck-cli_test 2026-07-28 step 17, a genuinely failed criterion) correctly stays red. Read site: packages/engine-core/src/RunnerRuntime.ts (verifyConflictWithholdEnabled \u2192 classifyVerifyConflictWithhold, applied at the second verification-conflict escalation after the M1 pass). Acceptance tests: packages/engine-core/src/__tests__/RunnerRuntime.verifyConflictReconcile.test.ts (B-C2/B-C3/B-C5/B-C6 keep the reds red; B-C8 asserts the billing invariant deliberately). killSwitch resolves the no-env value from this defaultState (#1729) \u2014 graduating means flipping defaultState to 'on' AND listing the key in INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts.",graduation:{status:"gated",gate:"Graduation gates on M1 graduating FIRST (M2 is only meaningful over M1's inapplicable residue), then on a staging shadow soak of `verify_conflict_withhold:would_withhold` showing (a) no would_withhold on a step any deterministic oracle reads as a negative, (b) the withheld population is dominated by genuinely unconfirmable captures rather than real failures \u2014 sampled and hand-adjudicated \u2014 and (c) an explicit product decision on the billing consequence (withdrawing the forced fail makes the run terminate 'completed' and therefore BILLABLE) plus the warning-display semantics review (a withheld step must read as a neutral 'could not confirm', not as an alarm-amber defect).",evidence:"staging `verify_conflict_withhold:would_withhold` / `:inapplicable` diag events (rate + hand-adjudicated sample of the withheld population) + the tp_c0dcd706 harness residue after M1 + packages/engine-core/src/__tests__/RunnerRuntime.verifyConflictReconcile.test.ts",owner:"steering (Alex)",review:"2026-08-15"}},RUN_COMPLETENESS_ALL_STATUSES:{key:"RUN_COMPLETENESS_ALL_STATUSES",envVars:["AGENTIQA_RUN_COMPLETENESS_ALL_STATUSES","AGENTIQA_EXPERIMENT_RUN_COMPLETENESS_ALL_STATUSES"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Run-completeness guard for NON-passed claims. The guard (cascade stage 15: bounce-once, then fail-closed with placeholder rows and a summary prefix) was gated on `status === 'passed'`, so a `run_complete{status:'failed'}` bypassed it ENTIRELY \u2014 the model could declare a run failed part-way and every unexecuted step was silently absent from the verdict. When ON the guard evaluates on EVERY claimed terminal status, and the fail-closed branch additionally discloses the FULL unexecuted-step set (every plan step with no reported result, not just the `required` verify subset the bounce keys on) as `skipped`+`synthesized` rows plus a coverage clause in the run summary. It cannot launder a failure into a pass (the only status write in this branch is \u2192 `failed`) and it cannot invent a fail on a complete run (it is unreachable when every required step is reported). OFF restores the byte-identical pre-change behavior: guard on `passed` only, no coverage clause, no unexecuted rows.",designDoc:"docs/plans/2026-08-04-run-completeness-guard-all-statuses-design.md",status:"active",added:"2026-08-04",notes:"Ships default-ON from inception \u2014 there is no pre-change OFF behavior worth preserving: the gated-on-`passed` condition is a pure false-signal + coverage-loss defect measured at prod parity on the lio mirror (2026-08-03, 46 runs, https://s.assets.agentiqa.com/lio-prod-parity-2026-08-03/SCORE.md finding 1). Repro run `run_04a6292b-f968-47e5-a1ee-4013f26afce1` (plan tp_2d051c76 `workflow-approval`, session asess_1785788803738_bf4wmgv2) booked `failed` with 10 of 29 steps in step_results, 7/7 criteria passed / 0 failed / 0 never-graded, and the client-visible summary was the model's own mid-run narration \"Test run in progress. Step 10 verified.\" The same gate is live on the prod build, so lio's nightly CLI batch can hit it. Direction is strictly information-adding: the run status can only move `passed`\u2192`failed` (unchanged from the pre-change branch) and the new rows are `skipped` \u2014 never a pass, never a fail, the same trust-layer rule as `synthesizePartialStepResults`. Read site: packages/engine-core/src/RunnerRuntime.ts (`runCompletenessAllStatusesEnabled` \u2192 the stage-15 guard in handleRunComplete; `getUnexecutedStepIndexes` computes the disclosed set). Acceptance tests: packages/engine-core/src/__tests__/RunnerRuntime.runCompletenessAllStatuses.test.ts (fixture built from the measured run). killSwitch resolves the no-env value from this defaultState (#1729) \u2014 this entry is listed in INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts so the no-delta proof acknowledges the default-ON state deliberately; an explicit AGENTIQA_RUN_COMPLETENESS_ALL_STATUSES=0 is the emergency kill switch that restores the bypass."},ZERO_GRADE_VERIFY_BOUNCE:{key:"ZERO_GRADE_VERIFY_BOUNCE",envVars:["AGENTIQA_ZERO_GRADE_VERIFY_BOUNCE","AGENTIQA_EXPERIMENT_ZERO_GRADE_VERIFY_BOUNCE"],polarity:"1-enables",defaultState:"on",surfaces:["engine"],cloudForwarded:!0,read:"killSwitch-enabled",gates:"Zero-grade verify bounce (cascade stage 12c). The whole-step zero-grade floor (`run_complete:verify_step_unsubstantiated`) refuses a verify step reported `passed` with NONE of its plan-printed criteria graded \u2014 correctly \u2014 but until this flag it did so SILENTLY, deriving `failed` (strict) / `warning` (non-strict) with no chance to correct, the only sibling gate without the one-bounce self-correction contract. When ON the first offending `run_complete` is rejected once (`run_complete:verify_step_unsubstantiated_rejected`, response status `ungraded_verify_criteria`) naming each offending step and its full printed criteria range 1..N; a repeat offense takes the floor's derivation exactly as before, with a de-jargonized client note recording that a resubmission was already asked for. The trigger keys on the POST-synthetics `unsubstantiatedVerifySteps` collection, so a step the FIX 3 cross-step rescue or Amendment 5a fail-closed synthesis already adjudicated never bounces. The bounce writes no step or run status and mutates nothing. OFF restores the byte-identical pre-change behavior: immediate silent derivation with the original note/summary text.",designDoc:"docs/plans/2026-08-06-lio-terminal-verdicts-and-zero-grade-bounce-design.md",status:"active",added:"2026-08-06",notes:"Ships default-ON from inception (precedent: RUN_COMPLETENESS_ALL_STATUSES \u2014 the bounce writes no status, so there is no verdict-polarity change to soak; `=0` restores today's silent derivation byte-identically). Measured trigger: Lio's first post-#2238 prod nightly (2026-08-06, 15 runs, proj_b6424373, gemini-3-flash-preview) \u2014 3/15 runs whose substance passed were reported FAILED because the model submitted step 1 `passed` with `criteriaResults: []` and the floor derived `failed` from the strict criterion, vs 0/30 on the 08-04/08-05 pre-#2238 baseline; the #2238 exclusion counters (unauthored/step-mismatch/out-of-range) were all zero, i.e. the typed reference did not drop the grades \u2014 the model stopped emitting them. Bounce doctrine (one bounce per class per run, then fail-closed, NEVER a rescue/rewrite) is the `_unauthoredGradeRunCompleteRejections` doctrine; worst-case chain incomplete \u2192 zero-grade \u2192 over-grading \u2192 conflict is ~4 extra run_complete turns, the same accepted profile as the pre-existing bounces. Read site: packages/engine-core/src/RunnerRuntime.ts (`zeroGradeVerifyBounceEnabled` \u2192 the stage-12c bounce in handleRunComplete; the counter is `_zeroGradeVerifyRejections`). Acceptance tests: packages/engine-core/src/__tests__/RunnerRuntime.zeroGradeVerifyBounce.test.ts. killSwitch resolves the no-env value from this defaultState (#1729) \u2014 this entry is listed in INTENTIONALLY_GRADUATED in killSwitchDefaultState.test.ts so the no-delta proof acknowledges the default-ON state deliberately; an explicit AGENTIQA_ZERO_GRADE_VERIFY_BOUNCE=0 is the emergency kill switch. Known residual carried, not fixed (AG-8153): the floor and this bounce cover `type === 'verify'` steps only \u2014 zero-graded strict criteria on action/setup steps remain a false-PASS class."}},lde=Object.values(Af);var _1=["run","run the test","run the test plan","run test plan"];function fw(t){let e=t.toLowerCase().trim();return _1.includes(e)}function mw(t){return t.replace(/\s+/g," ").trim()}var il="plan_run_active";var Us="PROJECT_RUN_IN_PROGRESS";function Rf(t){return`This project already has a ${t.kind==="batch"?"batch":"run"} in progress, started by ${t.startedByLabel} at ${t.startedAt}. Only one run or batch can be active per project at a time \u2014 wait for it to finish, or stop it first.`}function vi(t,e){if(t!==409)return null;let n;try{n=JSON.parse(e)}catch{return null}return!n||n.code!==Us?null:{error:typeof n.error=="string"?n.error:"",code:Us,runId:typeof n.runId=="string"?n.runId:"",kind:n.kind==="batch"?"batch":"run",startedAt:typeof n.startedAt=="string"?n.startedAt:"",startedByLabel:typeof n.startedByLabel=="string"?n.startedByLabel:""}}var ds=class extends Error{reason=Us;holderRunId;holderKind;startedAt;startedByLabel;body;constructor(e){super(e.error.trim()||Rf({kind:e.kind,startedByLabel:e.startedByLabel,startedAt:e.startedAt})),this.name="ProjectRunInProgressError",this.holderRunId=e.runId,this.holderKind=e.kind,this.startedAt=e.startedAt,this.startedByLabel=e.startedByLabel,this.body=e}};function Id(t){if(t instanceof ds)return t;if(!t||typeof t!="object")return null;let e=t;if(e.reason===Us){let n=e.body;if(n&&n.code===Us)return new ds(n)}return e.code!==Us?null:new ds({error:typeof e.message=="string"?e.message:"",code:Us,runId:"",kind:"run",startedAt:"",startedByLabel:""})}function Fs(t){let e=t.indexOf(":");return e===-1?{provider:"google",modelName:t}:{provider:t.slice(0,e),modelName:t.slice(e+1)}}var or="google:gemini-3-flash-preview",gw="google:gemini-3-flash-preview",us=["credential_identifier","user_supplied_payload","generated_test_value","other"];var Ad=[{name:"Green",hex:"#4ade80"},{name:"Blue",hex:"#60a5fa"},{name:"Purple",hex:"#a78bfa"},{name:"Amber",hex:"#fbbf24"},{name:"Red",hex:"#f87171"},{name:"Cyan",hex:"#22d3ee"},{name:"Pink",hex:"#f472b6"},{name:"Slate",hex:"#94a3b8"}];function ve(t){return`${t}_${crypto.randomUUID()}`}function ol(t,e){return typeof process>"u"||!process.env?!1:process.env[`AGENTIQA_${t}`]===e||process.env[`AGENTIQA_EXPERIMENT_${t}`]===e}function yw(t){return Af[t]?.defaultState}function ke(t){return ol(t,"0")?!0:ol(t,"1")?!1:yw(t)==="off"}function Ge(t){return ol(t,"1")?!0:ol(t,"0")?!1:yw(t)==="on"}function vw(t){return ol(t,"0")}function al(){return Ge("VALUE_GROUNDING_ENFORCE")}function xf(t){return`${t}_${Date.now()}_${Math.random().toString(36).slice(2,9)}`}function bw(t){switch(t){case"message":return"msg";case"tool_call":return"tool";case"llm_usage":return"llm";case"supervisor_verdict":return"sv";case"agent_lifecycle":return"lc";case"user_action":return"ua";case"session_start":case"session_end":case"turn_start":case"turn_end":return"sl";case"log":case"pageSnapshot.structuralStrip":return"diag";default:return"evt"}}var _o=class{apiUrl;fetchFn;sessions=new Map;queues=new Map;timer=null;isUploading=!1;inFlight=new Set;eventIds=new WeakMap;directUploadFailedSessions=new Set;BATCH_SIZE=25;FLUSH_INTERVAL=6e4;MAX_PAYLOAD_BYTES=35e5;auth;constructor(e,n,r=globalThis.fetch.bind(globalThis)){this.apiUrl=e,this.fetchFn=r,this.auth=typeof n=="string"?{kind:"bearer",token:n}:n,this.timer=setInterval(()=>this.flushAll(),this.FLUSH_INTERVAL)}buildAuthHeaders(e,n=!1){let r={"Content-Type":"application/json"};if(this.auth.kind==="bearer")return r.Authorization=`Bearer ${this.auth.token}`,r;if(n&&this.auth.bearerFallback)return r.Authorization=`Bearer ${this.auth.bearerFallback}`,r;r["x-admin-service-key"]=this.auth.serviceKey;let s=e?.userId??this.auth.fallbackUserId;return s&&(r["x-user-id"]=s),r}hasBearerFallback(){return this.auth.kind==="service"&&!!this.auth.bearerFallback}emit(e){let n=e.sessionId;if(this.eventIds.has(e)||this.eventIds.set(e,xf(bw(e.kind))),e.kind==="session_start"&&e.sessionMeta&&this.sessions.set(n,{...e.sessionMeta,desktopSessionId:n,status:"active",startedAt:new Date(e.ts).toISOString()}),e.kind==="session_end"){let s=this.sessions.get(n);s&&(s.status=e.status??"completed",s.endedAt=new Date(e.ts).toISOString())}if(this.isProviderLocationUnsupportedEvent(e)){let s=this.sessions.get(n);s&&(s.terminalErrorClass="provider_location_unsupported")}!n&&!this.sessions.has("")&&this.sessions.set("",{desktopSessionId:"global",projectId:"_global",status:"active",startedAt:new Date(e.ts).toISOString()});let r=this.queues.get(n);r||(r=[],this.queues.set(n,r)),r.push(e),r.length>=this.BATCH_SIZE&&this.flushSession(n),e.kind==="session_end"&&this.flushSession(n)}async flush(){await this.flushAll()}destroy(){this.timer&&(clearInterval(this.timer),this.timer=null),this.flushAll()}async flushAll(){let e=[];for(let r of this.queues.keys()){let s=this.flushSession(r);s&&e.push(s)}let n=Array.from(this.inFlight);await Promise.allSettled([...e,...n])}flushSession(e){let n=this.sessions.get(e),r=this.queues.get(e);if(!n||!r||r.length===0)return null;let s=r.splice(0),i=this.uploadWithRetry(n,s).catch(o=>{let a=s.filter(l=>l.kind==="session_end");if(a.length>0){let l=this.queues.get(n.desktopSessionId);l&&l.unshift(...a)}console.error(`[RemoteAnalyticsSink] Failed to upload ${s.length} events:`,o.message)});return this.inFlight.add(i),i.finally(()=>{this.inFlight.delete(i)}),i}async uploadWithRetry(e,n,r=3){let s;for(let i=1;i<=r;i++)try{await this.upload(e,n);return}catch(o){s=o;let a=o?.status;if(a!==void 0&&a>=400&&a<500)throw o;if(i<r){let l=Math.min(1e3*Math.pow(3,i-1),9e3);await new Promise(c=>setTimeout(c,l))}}throw s}async upload(e,n){let r=await this.mapEvents(e.desktopSessionId,n);await this.postIngest(e,r)}async postIngest(e,n,r=!1){if(n.length===0)return;let s=JSON.stringify({session:{...e},events:n});if(s.length>this.MAX_PAYLOAD_BYTES&&n.length>1){let l=Math.floor(n.length/2);await this.postIngest(e,n.slice(0,l),r),await this.postIngest(e,n.slice(l),r);return}let i;try{i=await this.fetchFn(`${this.apiUrl}/api/analytics/ingest`,{method:"POST",headers:this.buildAuthHeaders(e,r),body:s})}catch(l){throw new Error(`analytics upload network error: ${l?.message??String(l)}`)}if(i.ok)return;if((i.status===401||i.status===403)&&!r&&this.hasBearerFallback()){console.warn(`[RemoteAnalyticsSink] service-key auth got ${i.status} for session ${e.desktopSessionId} \u2014 retrying with bearer fallback (AG-169)`),await this.postIngest(e,n,!0);return}if(i.status===413){if(n.length>1){let l=Math.floor(n.length/2);await this.postIngest(e,n.slice(0,l),r),await this.postIngest(e,n.slice(l),r);return}console.warn(`[RemoteAnalyticsSink] Dropping single oversized event (${Math.round(s.length/1024)} KB)`);return}let o=await i.text().catch(()=>`HTTP ${i.status}`);if(o.includes("FUNCTION_PAYLOAD_TOO_LARGE")||o.includes("Request Entity Too Large")){if(n.length>1){let l=Math.floor(n.length/2);await this.postIngest(e,n.slice(0,l),r),await this.postIngest(e,n.slice(l),r);return}console.warn(`[RemoteAnalyticsSink] Dropping single oversized event (${Math.round(s.length/1024)} KB)`);return}(i.status===401||i.status===403)&&console.error(`[RemoteAnalyticsSink] auth_failed status=${i.status} authKind=${this.auth.kind} sessionId=${e.desktopSessionId} userId=${e.userId??"(unset)"} body=${o.slice(0,200)}`);let a=new Error(o);throw a.status=i.status,a}async mapEvents(e,n){let r=[];for(let s of n){let i={timestamp:new Date(s.ts).toISOString(),childId:mo(s.childId)},o=this.eventIds.get(s)??xf(bw(s.kind));switch(s.kind){case"message":r.push({...i,id:o,eventType:"message",role:s.role,messageText:s.text,toolName:s.actionName,toolArgs:s.actionArgs,url:s.url});break;case"tool_call":{let a;s.screenshotBase64&&(a=await this.uploadScreenshot(e,s.screenshotBase64)),r.push({...i,id:o,eventType:"tool_call",toolName:s.toolName,toolArgs:s.args,toolResult:s.result,screenshotUrl:a,url:s.url,stepIndex:s.stepIndex,actionMetadata:{durationMs:s.durationMs,tokenCount:s.tokenCount}});break}case"llm_usage":r.push({...i,id:o,eventType:"llm_usage",toolName:s.model,promptTokens:s.promptTokens,completionTokens:s.completionTokens,totalTokens:s.totalTokens,runId:s.runId,callKind:s.callKind,keySource:s.keySource,llmProvider:s.llmProvider,billedUnits:s.billedUnits,actionMetadata:{durationMs:s.durationMs,finishReason:s.finishReason,tokenCount:s.tokenCount,messageCount:s.messageCount,systemPromptHash:s.systemPromptHash,lastToolResults:s.lastToolResults,chosenActions:s.chosenActions,textResponse:s.textResponse,cachedInputTokens:s.cachedInputTokens,planStepIndex:s.planStepIndex,planStepType:s.planStepType}});break;case"supervisor_verdict":r.push({...i,id:o,eventType:"supervisor_verdict",actionType:s.verdict,actionMetadata:{verdict:s.verdict,message:s.message,iteration:s.iteration,actionLogSize:s.actionLogSize,stepText:s.stepText,progress:s.progress??null,differential:s.differential??null}});break;case"agent_lifecycle":r.push({...i,id:o,eventType:"agent_lifecycle",actionType:s.event,actionMetadata:{event:s.event,iteration:s.iteration,details:s.details}});break;case"user_action":r.push({...i,id:o,eventType:"user_action",actionType:s.action,actionTargetId:s.targetId,actionMetadata:s.metadata});break;case"session_start":case"session_end":case"turn_start":case"turn_end":r.push({...i,id:o,eventType:"user_action",actionType:s.kind,actionTargetId:s.sessionId,actionMetadata:s.sessionMeta?{...s.sessionMeta}:{status:s.status,...s.kind==="session_end"&&s.endKind?{endKind:s.endKind}:{}}});break;case"log":r.push({...i,id:o,eventType:"diagnostic",actionType:s.level,actionMetadata:{source:s.source,msg:s.msg,...s.data}});break;case"pageSnapshot.structuralStrip":r.push({...i,id:o,eventType:"diagnostic",actionType:s.kind,actionMetadata:{originalLen:s.originalLen,finalLen:s.finalLen,droppedNodeCount:s.droppedNodeCount,foldedRunCount:s.foldedRunCount,capHit:s.capHit}});break;default:{console.warn(`[RemoteAnalyticsSink] dropping unmapped DiagnosticEvent kind=${s.kind}`);break}}}return r}isProviderLocationUnsupportedEvent(e){return e.kind!=="log"?!1:Dr([e.msg,e.source,typeof e.data=="object"&&e.data?JSON.stringify(e.data):""].join(`
|
|
12
12
|
`))}async uploadScreenshot(e,n){let r=xf("img"),s=this.sessions.get(e)??null;if(!ke("SCREENSHOT_DIRECT_UPLOAD")&&!this.directUploadFailedSessions.has(e)){let i=await this.uploadScreenshotDirect(e,s,r,n);if(i)return i;this.directUploadFailedSessions.add(e),console.warn(`[RemoteAnalyticsSink] direct screenshot upload failed for session ${e} \u2014 falling back to legacy base64 upload for this session`)}return this.uploadScreenshotLegacy(e,s,r,n)}async uploadScreenshotDirect(e,n,r,s){let i;try{let u=s.includes(",")?s.split(",")[1]:s;if(u=u.replace(/\s/g,""),u.length===0)return;i=Uint8Array.from(atob(u),f=>f.charCodeAt(0))}catch{return}let o=JSON.stringify({sessionId:e,eventId:r,sizeBytes:i.byteLength}),a=u=>this.fetchFn(`${this.apiUrl}/api/analytics/presign-screenshot`,{method:"POST",headers:this.buildAuthHeaders(n,u),body:o}),l;try{l=await a(!1),(l.status===401||l.status===403)&&this.hasBearerFallback()&&(l=await a(!0))}catch{return}if(!l.ok)return;let c=await l.json().catch(()=>null);if(!c||!c.putUrl||!c.publicUrl)return;let d;try{d=await this.fetchFn(c.putUrl,{method:"PUT",headers:{"content-type":"image/png"},body:i})}catch{return}if(d.ok)return c.publicUrl}async uploadScreenshotLegacy(e,n,r,s){let i=JSON.stringify({sessionId:e,eventId:r,imageBase64:s}),o=a=>this.fetchFn(`${this.apiUrl}/api/analytics/upload-image`,{method:"POST",headers:this.buildAuthHeaders(n,a),body:i});try{let a=await o(!1);if((a.status===401||a.status===403)&&this.hasBearerFallback()&&(console.warn(`[RemoteAnalyticsSink] upload-image service-key auth got ${a.status} for session ${e} \u2014 retrying with bearer fallback (AG-388)`),a=await o(!0)),!a.ok){let c=await a.text().catch(()=>`HTTP ${a.status}`);console.error(`[RemoteAnalyticsSink] upload-image failed status=${a.status} authKind=${this.auth.kind} sessionId=${e} userId=${n?.userId??"(unset)"} body=${c.slice(0,200)}`);return}let l=await a.json().catch(()=>null);if(!l||!l.url){console.warn(`[RemoteAnalyticsSink] upload-image returned no url (success=${l?.success??"(none)"}) for session ${e} \u2014 screenshot_url will be null`);return}return l.url}catch(a){console.error(`[RemoteAnalyticsSink] upload-image network error for session ${e}:`,a.message);return}}};function rt(t,e){return t.replace(/\{\{timestamp\}\}/g,String(e)).replace(/\{\{unique\}\}/g,w1(e))}function w1(t){let e="abcdefghijklmnopqrstuvwxyz",n="",r=t;for(;r>0;)n=e[r%26]+n,r=Math.floor(r/26);return n||"a"}var Rd=500;function xd(){return vw("CLICK_EFFECT_SIGNAL")?"off":Ge("CLICK_EFFECT_SIGNAL")?"enforce":"shadow"}function wo(){return xd()==="enforce"}var S1=new Set(["input","select","textarea","option"]),E1=new Set(["checkbox","radio","switch","textbox","searchbox","combobox","listbox","option","spinbutton","slider","menuitemcheckbox","menuitemradio"]);function T1(t){if(!t)return!1;let e=(t.tag??"").toLowerCase(),n=(t.role??"").toLowerCase();return S1.has(e)||E1.has(n)}var I1=`The click was dispatched but no page change was observed within ${Rd}ms (no DOM, text or attribute change, no navigation, no dialog). The target may not have been interactive yet \u2014 for example a freshly navigated page that is still hydrating, where a control is already painted but its handler is not attached. Re-check the element state in the latest screenshot/snapshot and re-perform the action ONCE if it did not take effect; do not assume it succeeded just because it was dispatched.`;function _w(t,e={}){return t.urlBefore!==t.urlAfter?{effectObserved:!0}:t.dialogsAfter>t.dialogsBefore?{effectObserved:!0}:t.mutationCount==null||t.attrCount==null?{}:t.mutationCount>0||t.attrCount>0?{effectObserved:!0}:T1(e.target)?{effectObserved:!1,suppressedBy:"property-only-control"}:e.canvasDominant?{effectObserved:!1,suppressedBy:"canvas"}:e.suppressAdvisory?{effectObserved:!1,suppressedBy:"caller-note"}:{effectObserved:!1,note:I1}}function ll(){return" When type='verify', the criteria array MUST contain at least one concrete outcome check. Never leave criteria empty or omit it on verify steps; the runner has nothing to assert against and may skip the check."}function kd(){return"Concrete check describing the expected OUTCOME. Focus on data created or changed during the test. When action steps use {{unique}} or {{timestamp}}, criteria must refer to the same token. Do not quote transient UI text from memory; describe the expected outcome and let the runner read the live screen. Always also set this criterion\u2019s factKind \u2014 the KIND of fact it asserts (count, presence, absence, modification, value, or relation)."}function Cd(){return"REQUIRED for verify steps. Include one or more concrete checks the runner can perform against the live screen. Omit for setup/action steps."+kf()}function kf(){return" Prefer exactly ONE criterion per verify step: when a step would assert several distinct outcomes, split them into separate sequential verify steps, each with its single check, rather than stacking multiple criteria on one step. A verify step's text and its single criterion's check must not repeat each other verbatim \u2014 the text states the intent, the criterion states the concrete outcome to confirm."}function Nd(){return'Optional. The exact literal this check pins \u2014 set it ONLY when the assertion checks for a specific value: a quoted string, an exact number or count, or an exact URL (e.g. check "Order ID is \\"TC-009\\"" \u2192 expectedValue "TC-009"). This is the machine-comparable expectation; the runner requires the observed value to match it verbatim. Omit it for vague/qualitative checks (e.g. "a success message is visible"). When revising an existing plan, keep every existing expectedValue pin in its criterion; never move a pinned value into step prose.'}function js(){return`Author this on EVERY verify criterion whenever the kind is clear \u2014 committing the right kind is what lets the grounded-state verifier confirm the RIGHT kind of state (especially on canvas/visual surfaces where a count or presence fact has no DOM literal to pin). It is a deliberate typed choice you commit to, NOT a re-description of the check text (like the auth enums, prefer authoring this over relying on the check wording). Pick the kind the check asserts:
|
|
13
13
|
- "count" \u2014 a quantity or "N of X" fact (e.g. "exactly 3 shapes on the board", "5 rows in the table"), especially countable canvas/visual facts with no exact string to pin.
|
|
14
14
|
- "absence" \u2014 something was REMOVED / is gone / no longer shown (e.g. "the banner is no longer visible", "the sticky note was deleted").
|