First production caller of the schema-v2 writer chain. Every
`sf headless triage --apply` invocation now emits four gate_run trace
events with surface=headless, runControl=supervised, permissionProfile=
high, traceId=flowId — making the gates visible in `status uok --json`
with coverageStatus: "ok" (or fail/manual-attention on reject paths).
Gates emitted, in order:
1. trusted-agent-source-gate — fires on the trust precondition:
pass: both triage-decider and rubber-duck are SF-shipped built-ins
fail: missing-agent OR non-builtin source OR untrusted custom runner
(covers all three pre-dispatch refusal paths so operators see the
failure in status uok, not just in the journal)
2. triage-plan-validation-gate — fires on the strict-parse contract:
pass: parseTriagePlanStrict returns a valid plan covering expectedIds
fail: missing marker / bad yaml / unknown id / outcome-required field missing
3. triage-apply-review-gate — fires on the rubber-duck verdict:
pass: rubber-duck: agree → apply phase proceeds
fail: rubber-duck disagreed → clean pause, no mutations
manual-attention: rubber-duck subagent failed to complete
4. triage-apply-mutation-gate — fires after applyTriagePlan:
pass: every approved mutation landed
fail: any rejected mutation
manual-attention: zero approved mutations (all decisions were "fix")
Includes counts in extra: resolvedCount, rejectedCount, pendingFixCount.
Reader-side fixes (codex review follow-up on slice 3a):
- getDistinctGateIds (sf-db-gates.js) now UNIONs trace-event IDs with
quality_gates DB IDs instead of returning trace IDs early when any
exist. The old behavior silently hid slice-scoped DB-only gates the
moment a flow-scoped trace landed.
- getGateMeta (headless-uok-status.ts) now reads BOTH trace events and
DB row, then picks whichever has the later evaluatedAt. Tie-break
prefers trace (flow-scoped gates with no quality_gates FK row are
trace-only). Old behavior preferred trace whenever surface was set,
regardless of timestamp.
Live verification: ran `sf headless triage --apply` 4 times against the
operator's environment (rubber-duck is a project-level override).
trusted-agent-source-gate now shows in `sf headless status uok --json`
with total: 4, fail: 4, coverageStatus: "ok" — proving the schema-v2
metadata round-trips through the trace events and reaches the
classifier.
Tests:
- headless-triage-uok-gates.test.ts (3 new tests): agree path emits
3 pass gates with v2 metadata; disagree path emits review fail;
unknown-id path emits validation fail with no review gate.
- Existing test suites adjusted for the GateMetadataRow →
GateRunContextRow rename (classifier helpers renamed consistently
across .ts source and the .mjs test mirror).
- Full SF + headless apply: 1681/1681.
Still legacy in production (slice 3b targets these next):
- phases-pre-dispatch.js gates: resource-version-guard, pre-dispatch-
health-gate, planning-flow-gate. None of these pass uokContext yet.
- phases-unit.js gates: unit-verification-gate, plan-gate.
- plan-slice.js: Q3/Q4/Q5/Q6/Q7/Q8 seed gates.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>