fix: producers that ran before heartbeats were switched on, and three stored verdicts that had rotted - #18
Conversation
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 2 minutes Limit details: You’ve used the included review currently available. Your 66 included PR review attempts over the past 7 days set your current allowance at 1 review per hour. Your organization has reached its usage spending cap. Adjust your spending cap in the billing tab. How can I continue?Wait for the limit to reset, then comment An organization admin can change what happens after included review limits in Billing. How do review limits work?CodeRabbit enforces per-developer PR review limits within each organization. For paid Pro and Pro+ reviews, CodeRabbit uses a developer's included PR review attempts over the past 7 days to set the current hourly allowance. At typical activity levels, the full plan allowance applies. Higher sustained activity can lower the allowance until earlier attempts leave the 7-day window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (10)
Comment |
…icating #18 CI caught a real defect in the first version of this change: the reach selftest asserted a floor against the LIVE ledger, which holds 41 rows here and 14 on a clean runner. It passed locally and failed CI — a machine-local assertion wearing the clothes of a correctness test, which is the shape this repo's skip discipline exists to catch. Fixed by isolation rather than by skipping, per .verify-floor.json's own rule ("Moving a test back below the ceiling is the preferred way to lower it — fix what made it machine-dependent, rather than agreeing to check less"): * MECHANISM assertions now run on a SYNTHETIC ledger, so they execute on every machine: a kind-based capability is reported with its named reason and entry requirement rather than as absence; context forwarding reaches it; and absent, empty or WRONG context still fails closed. * THE DRIFT GUARD is now code vs code. `DIRECT_ENTRY` is hoisted to `direct_entry()` so the dispatcher agreement is assertable with no ledger at all — drift would otherwise land unnoticed precisely on the clean runner where a ledger-dependent check can only skip. * REACH SHRINKAGE IS DELIBERATELY NOT CHECKED HERE. #18 already added `capability_firing_monitor.advisor_reach` with a declared baseline and an `advisor_reach_regression` defect. A second copy would be a parallel inventory. Said so in the code, at the point someone would add it back. Net: no new skip is spent (the one PREREQUISITE mark on a bare runner is the pre-existing front-door section), and CI now checks MORE than before. Also fixed: the synthetic-ledger assertion originally required not_applicable to equal exactly the two fixtures. `capabilities.load` seeds KNOWN_DECLARATIONS into any ledger it reads, so that was a test coupled to an unrelated constant — containment plus a denominator computed from the ledger instead. Break->revert, all three discriminating: emptying the dispatcher map fails on the task-type disagreement; removing reason-reporting fails with "came back as SILENCE, not as named non-matches"; letting context be assumed instead of fail-closed fails on the wrong-value case. verify.py: 353 passed, 0 failed, 0 skipped, 81/81 selftests, 5/5 gates. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…icating #18 CI caught a real defect in the first version of this change: the reach selftest asserted a floor against the LIVE ledger, which holds 41 rows here and 14 on a clean runner. It passed locally and failed CI — a machine-local assertion wearing the clothes of a correctness test, which is the shape this repo's skip discipline exists to catch. Fixed by isolation rather than by skipping, per .verify-floor.json's own rule ("Moving a test back below the ceiling is the preferred way to lower it — fix what made it machine-dependent, rather than agreeing to check less"): * MECHANISM assertions now run on a SYNTHETIC ledger, so they execute on every machine: a kind-based capability is reported with its named reason and entry requirement rather than as absence; context forwarding reaches it; and absent, empty or WRONG context still fails closed. * THE DRIFT GUARD is now code vs code. `DIRECT_ENTRY` is hoisted to `direct_entry()` so the dispatcher agreement is assertable with no ledger at all — drift would otherwise land unnoticed precisely on the clean runner where a ledger-dependent check can only skip. * REACH SHRINKAGE IS DELIBERATELY NOT CHECKED HERE. #18 already added `capability_firing_monitor.advisor_reach` with a declared baseline and an `advisor_reach_regression` defect. A second copy would be a parallel inventory. Said so in the code, at the point someone would add it back. Net: no new skip is spent (the one PREREQUISITE mark on a bare runner is the pre-existing front-door section), and CI now checks MORE than before. Also fixed: the synthetic-ledger assertion originally required not_applicable to equal exactly the two fixtures. `capabilities.load` seeds KNOWN_DECLARATIONS into any ledger it reads, so that was a test coupled to an unrelated constant — containment plus a denominator computed from the ledger instead. Break->revert, all three discriminating: emptying the dispatcher map fails on the task-type disagreement; removing reason-reporting fails with "came back as SILENCE, not as named non-matches"; letting context be assumed instead of fail-closed fails on the wrong-value case. verify.py: 353 passed, 0 failed, 0 skipped, 81/81 selftests, 5/5 gates. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… stored verdicts that had rotted
Every capability had a declared trigger, caller, heartbeat and recurrence fixture. That proved the
pathways exist. Driving 25 productive tasks through them found four defects where a pathway existed
and still recorded nothing, plus four line-number citations pointing at the wrong lines.
1. TWO PRODUCERS RAN ABOVE THE HEARTBEAT EXPORT, so their heartbeats were no-ops.
`production_heartbeat` / `daily_heartbeat` return False unless ORCH_CAPABILITY_HEARTBEATS=1 is in
the CHILD's environment, and only orchestrate.sh exports it. The export sat 19-22 lines BELOW:
* `frontend_verify.py --doctor` -- the frontend-verifier capability's ONLY tick caller, so
flipping ORCH_FRONTEND_VERIFY_START_BROWSER=1 would have recorded nothing and read as "the
switch didn't help";
* `capacity.py` -- making windowed-capacity-policy's declared cadence ("every tick,
capacity.build at the top of the tick") false; its evidence came only from later in-process
callers.
The active-mode validate+export block moves above all producers, preserving the invariant inside
it (validate lifecycle truth FIRST, then enable heartbeats -- otherwise a producer emits evidence
outside a validated contract). Three grep-able anchors added.
Durable countermeasure in capability_activation_audit: `emits_heartbeat` (a definition is not a
call, which is why capabilities.py may stay above the export), `shell_heartbeat_gate` (pure;
before / after / DEFERRED, the third because a one-line shell function is defined at the top and
called throughout, so its position says nothing), `heartbeat_env_gate` (publishes suppressed
COUNT next to invocations_after, so a zero is readable), and defect `heartbeat_env_suppressed`.
Verified both directions on real code: against origin/main's orchestrate.sh it reports
['capacity.py', 'frontend_verify.py']; against the fixed file, [] with 39 post-export invocations.
This is a new dimension, not a duplicate: `heartbeat_reachable` reported frontend-verifier
REACHABLE (correctly -- the call is reachable) and the firing monitor reported NEVER FIRED (also
correctly). Both were right and neither could see the cause.
2. `feature-scan` HAD NO PRODUCER AT ALL. It and feature-reflection-cli both declare
feature_scan.py:scan, and the module credited only the second -- so the daily cadence step ran
and feature-scan read "never fired" forever. Unrecorded, not dormant, and indistinguishable from
dormant in the monitor. Both rows are now credited at the path both declare. Whether two rows
should describe one code path is left open: merging or retiring one is a lifecycle decision
needing its own evidence, not an invented heartbeat.
3. ORCH_RUNTIME_AC_ALLOW_COMMANDS WAS NEVER HOLDING WHAT ITS RECORD SAID. The stored criterion said
it gated both agent-authored command/non_regression checks AND the template-built
deliberate_break command, and that the flag had to be SPLIT before it could be turned on. Running
a real deliberate_break spec both ways gave identical results -- local_verify executed and
returned a real verdict with the flag OFF. The split already existed. So deliberate-break-verifier
sat in the "held switches" bucket on a frozen diagnosis.
The split is now NAMED: COMMAND_EXEC_GATED_TYPES + command_execution_gated(), consumed by both
the runtime gate and the recurrence fixture -- one shared name, because a matching pair of
literals drifts. No new gate was added for deliberate_break's agent-authored test_cmd: a
default-OFF gate over a path that works today would CREATE a latch. What screens it is recorded
instead (shlex.split, shell=False, shell-marker rejected). switch_review's flag -> capability map
remapped to runtime-ac-checks; pointing the "ON but silent" arm at a capability the switch cannot
influence made that review unable to say anything true about either.
4. LINE-NUMBER CITATIONS ROT, so they are banned and the ban is enforced. Four found, all wrong:
orchestrate.sh:133/:152 (actually 171/190), runtime_ac.py:955 (off by one, and the claim built on
it was false), the firing monitor's "line ~152", and a ledger note. All replaced with anchors.
capability_recurrence_check._selftest now rejects any file.py:NNN citation in SWITCH_ON_CRITERIA
and requires every cited `ORCH-ANCHOR: <name>` to be DEFINED on its own comment line, with a
floor of >=3 citations so the rule cannot pass vacuously.
Its own first version was circular -- `"ORCH-ANCHOR: x" in tree_text` passed a citation to a
nonexistent anchor, because the criteria table's own source is part of the tree. Only the
break->revert found that.
5. ORCH_EXPLORATION_MODE was correctly diagnosed as "waiting for the review, not the data", and the
review had never been run. Ran it: keep_epsilon_greedy / epsilon_still_preferred -- gates ARE met
(727 instrumented runs, 65 direct exploration outcomes, ready_tasks 5/9, zero-cell 16.2%) and
Thompson-hybrid improves simulated challenger quality and direct exploration outcomes not BOTH.
A DECISION, not a hold. `_check_thompson` now runs the review and reports its recommendation, so
the prose criterion is gone and the switch re-raises itself if the verdict flips.
6. ADVISOR REACH is now measured against a declared floor. The advisor can name 6 of 41 capabilities
from free text (5 by task_type-shaped matcher + offload via a hardcoded map); the other 35 fail
closed by design. Reach had already SHRUNK in silence -- docs-drift-fix-agent and
adversarial-review still hold learned association observations from when they were reachable.
`advisor_reach` publishes the set against ADVISOR_REACH_BASELINE and raises
`advisor_reach_regression` on a shrink. REPORT-ONLY, and it must stay that way: widening
TASK_SIGNALS or loosening a matcher to move this number would corrupt the learned associations.
Every row also carries advisor_reachable now, because "the advisor never recommended it" has two
causes with opposite fixes -- no demand, versus the front door structurally cannot name it.
Recurrence replay 41/46 -> 42/46, by replacing a fixture that asserted a nonexistent blocker.
Activation audit 41/41 can fire. `python3 verify.py`: 354 passed, 0 skipped, 5 of 5 gates green,
and identical from a copy of the tree at a different path (path-independence, since the exec mirror
was deliberately left unsynced by a concurrent session -- that gap is the owner's circuit breaker).
Break->revert, all reverted byte-identical: the command-exec split at the predicate; the same split
drifting at the call site; the anchor-existence rule; and the ordering check against origin/main's
real orchestrate.sh.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
3d8f90e to
d5fed55
Compare
… end (#20) * fix(advisor): the front door was a thin caller, not a structural dead end `capability_advisor` could name 6 of 41 capabilities from free text, and the other 35 came back as ABSENCE — no entry, no reason, indistinguishable from "nothing else exists". That was read as a structural limit. It is not. `capabilities._matches_trigger` matches a `{"kind": k, "name": n}` matcher against a SAME-NAMED FIELD THE CALLER SUPPLIES, and says so itself: "Adding a new trigger kind is then a caller-side change, not an edit here." The advisor was passing a three-field trigger (repository/task_type/lane) and no kind fields. Supply the context a caller actually has and those capabilities match immediately — proven in the selftest for `closer_gate`/`adversarial-review`. Three changes, no matcher rewritten and no task-signal widened (widening keywords to move the number would corrupt the very associations this feeds): 1. ONE CONSTANT. `DIRECT_ENTRY` was a one-entry literal whose comment claimed it mirrored `dispatcher.TASK_TYPE_CAPABILITY`. It did not — so the dispatcher routed `runtime_ac` to `runtime-ac-checks` while the advisor named `deliberate-break-verifier` for the same work. The two halves of the system disagreed about one task type. Now derived from the dispatcher's map. 2. CONTEXT IS FORWARDED (`context=`, and `--context` on the CLI), confined to kinds the ledger actually declares. Absent context still FAILS CLOSED. 3. THE WHOLE DENOMINATOR. Every non-match now reports its named reason from `_matches_trigger` plus an `entry_requirement` derived from its own matcher ("entered at ci_workflow 'maint-87-docs-drift-fix-agent'"). Those reasons were already computed and thrown away; discarding them is what turned 35 capabilities into silence. Reach: 6 -> 7 from free text alone, 6 -> 12 with real caller context, 41 of 41 now accounted for with a reason. `_selftest_reach` pins it, because `adversarial-review` and `docs-drift-fix-agent` both carry advisory match history proving they were once reachable and dropped out unnoticed when their matchers were tightened. Reach had never been measured, so it fell in silence. Break->revert: reverting DIRECT_ENTRY fails on the dispatcher disagreement; removing the reason-reporting fails with all 39 unaccounted capabilities named. verify.py: 353 passed, 0 failed, 0 skipped, 81/81 selftests, 5/5 gates. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> * fix(advisor): make the reach guard machine-independent, and stop duplicating #18 CI caught a real defect in the first version of this change: the reach selftest asserted a floor against the LIVE ledger, which holds 41 rows here and 14 on a clean runner. It passed locally and failed CI — a machine-local assertion wearing the clothes of a correctness test, which is the shape this repo's skip discipline exists to catch. Fixed by isolation rather than by skipping, per .verify-floor.json's own rule ("Moving a test back below the ceiling is the preferred way to lower it — fix what made it machine-dependent, rather than agreeing to check less"): * MECHANISM assertions now run on a SYNTHETIC ledger, so they execute on every machine: a kind-based capability is reported with its named reason and entry requirement rather than as absence; context forwarding reaches it; and absent, empty or WRONG context still fails closed. * THE DRIFT GUARD is now code vs code. `DIRECT_ENTRY` is hoisted to `direct_entry()` so the dispatcher agreement is assertable with no ledger at all — drift would otherwise land unnoticed precisely on the clean runner where a ledger-dependent check can only skip. * REACH SHRINKAGE IS DELIBERATELY NOT CHECKED HERE. #18 already added `capability_firing_monitor.advisor_reach` with a declared baseline and an `advisor_reach_regression` defect. A second copy would be a parallel inventory. Said so in the code, at the point someone would add it back. Net: no new skip is spent (the one PREREQUISITE mark on a bare runner is the pre-existing front-door section), and CI now checks MORE than before. Also fixed: the synthetic-ledger assertion originally required not_applicable to equal exactly the two fixtures. `capabilities.load` seeds KNOWN_DECLARATIONS into any ledger it reads, so that was a test coupled to an unrelated constant — containment plus a denominator computed from the ledger instead. Break->revert, all three discriminating: emptying the dispatcher map fails on the task-type disagreement; removing reason-reporting fails with "came back as SILENCE, not as named non-matches"; letting context be assumed instead of fail-closed fails on the wrong-value case. verify.py: 353 passed, 0 failed, 0 skipped, 81/81 selftests, 5/5 gates. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> --------- Co-authored-by: Tim Stranske <tim@stranskemo.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
… opened (#21) Two modules were publishing different "advisor reach" figures for the same front door — `capability_activation_audit.advisor_reach` said 5, `capability_advisor` said 7 — with no statement of which population each covered. Two disagreeing inventories of the same thing is how a parallel inventory starts, which is the defect both modules were written to prevent. They disagreed for a good reason that had just expired. #18 excluded `offload` from the reach baseline with an explicit rationale: it was reachable only through a HARDCODED map inside `advise()`, and "a hardcode cannot shrink quietly — deleting it IS a diff." That was correct when written. #20, merged the same day, replaced the hardcode with `capability_advisor.direct_entry()` DERIVED from `dispatcher.TASK_TYPE_CAPABILITY` — so dropping an entry from the dispatcher's map now narrows the front door with no diff in either module. The exemption that was right for a literal is exactly wrong for a derivation. * `advisor_reach` now reads the direct-entry map from `capability_advisor` — one source, consumed by both, instead of a second list here. * The derived set gets its OWN baseline (`ADVISOR_DIRECT_ENTRY_BASELINE`), so it can grow freely and cannot shrink without appearing in a diff. Declared and derived reach stay separate, because #18's reasoning for separating them holds; only the "no baseline needed" part died. * `total_reachable_count` publishes 7 next to declared 5 and direct-only 2, so the numbers reconcile in the output rather than in someone's head. * Fixed a pointer in `capability_advisor.py` that named `capability_firing_monitor.advisor_reach`; it lives in `capability_activation_audit`. A comment pointing at a symbol that does not exist is the rot #18 spent four citations cleaning up. Break->revert: emptying `direct_entry()` must regress the derived baseline (it does, asserted with the map patched out); neutering `direct_entry_regressed` fails the new assertion. verify.py: 366 passed, 0 failed, 0 skipped, 81/81 selftests, 5/5 gates. Co-authored-by: Tim Stranske <tim@stranskemo.com> Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
What this is
Every capability already had a declared trigger, caller, heartbeat and recurrence fixture, and
capability_firing_monitor.pywatched for silence. That proved the pathways exist. This PR iswhat happened when 25 productive tasks were actually driven through them: four defects where the
pathway existed and still recorded nothing, and four line-number citations pointing at the wrong
lines.
Measured, not asserted: heartbeats were driven against an isolated copy of the ledger
(
ORCH_CAPABILITIES_PATH) with the real Brain (ORCH_LOCAL_RUNTIMEuntouched), each task in itsown subprocess, ledger diffed after each. 38 of 41 capabilities are now confirmed to fire from
real work — 25 in production from the live hourly tick, 22 from tasks driven here, 13 of them with
zero prior production events. The 3 that never fired each carry a measured cause and a criterion.
A concurrent session was writing advisory
matchevents into the same shared ledger. Every counthere filters
metadata.source == "capability_advisor"and non-nullmetadata.skill; their 8-eventburst (epoch 1787417784–1787417795) is accounted for separately and counted as a firing nowhere.
1. Two producers ran above the heartbeat export, so their heartbeats were no-ops
production_heartbeat/daily_heartbeatreturnFalseunlessORCH_CAPABILITY_HEARTBEATS=1is inthe child process's environment, and only
orchestrate.shexports it. The export sat below twoproducers:
frontend_verify.py --doctor …frontend-verifierORCH_FRONTEND_VERIFY_START_BROWSER=1would have recorded nothing and read as "the switch didn't help".capacity.pywindowed-capacity-policyThe active-mode block that validates lifecycle truth and exports the flag moves above all
producers, preserving the ordering invariant inside it (validate first, then enable — otherwise a
producer emits evidence outside a validated contract). Three grep-able anchors added:
ORCH-ANCHOR: heartbeat-export,heartbeat-producers,frontend-verify-doctor.Durable countermeasure (in
capability_activation_audit, extending the module that already owns"could it fire?"):
emits_heartbeat(path)— a definition is not a call, which is whycapabilities.pymaylegitimately stay above the export. Handled by mechanism, not an exemption list.
shell_heartbeat_gate(text)— pure;before/after/deferred. The third bucket existsbecause a one-line shell function (
_gh_gate() { … gh_capacity.py …; }) is defined at the top andcalled throughout, so its position says nothing. My first implementation called it
beforeandinvented a defect; the bucket is the fix.
heartbeat_env_gate()— publishessuppressed_modulesnext toinvocations_after, so a zerois readable.
suppressed: []withinvocations_after: 0is a broken parse, not a clean ordering.heartbeat_env_suppressed, applied entry-class agnostically.test_capabilities.test_no_tick_producer_runs_above_the_heartbeat_export, carrying itsown positive control on synthetic text.
Verified both directions on real code. Against
origin/main'sorchestrate.shthe check reportssuppressed: ['capacity.py', 'frontend_verify.py']; against the fixed file,[]with 39 post-exportinvocations.
Why this is not a duplicate of anything.
heartbeat_reachablereportedfrontend-verifierreachable — correctly, the call is reachable. The firing monitor reported never fired — also
correctly. Both were right and neither could see the cause. Reachability and enablement are different
questions and the second had no owner.
2.
feature-scanhad no producer at allfeature-scanandfeature-reflection-cliboth declarefeature_scan.py:scan, and the modulecredited only the second. So the daily cadence step ran,
feature-reflection-cliaccrued invocations,and
feature-scanreadnever firedforever — unrecorded, not dormant, and indistinguishablefrom dormant in the monitor. Both rows are now credited at the path both declare.
Deliberately not fixed here: whether two rows should describe one code path. Merging or retiring
one is a lifecycle decision needing its own evidence, and inventing a heartbeat is the wrong way to
make it.
3.
ORCH_RUNTIME_AC_ALLOW_COMMANDSwas never holding what its record saidThe stored criterion said the flag gated both agent-authored
command/non_regressionchecksand the template-built
deliberate_breakcommand, and that the actionable step was to splitthe flag. I ran a real
deliberate_breakspec throughrun_verificationwithallow_command_checksFalseandTrue: identical results —local_verifyexecuted, mutated acandidate, ran the test, returned a real verdict, both times. The split already existed.
So
deliberate-break-verifiersat in the "held switches" bucket on a frozen diagnosis, not ablocker.
The split is now named:
COMMAND_EXEC_GATED_TYPES+command_execution_gated()atORCH-ANCHOR: runtime-ac-command-exec-gate, consumed by both the runtime gate and the recurrencefixture — one shared name, because a matching pair of literals drifts and a name cannot.
No new gate was added for
deliberate_break's agent-authoredtest_cmd. A default-OFF gate overa path that works today would create a latch, not remove one. What screens that payload is recorded
instead:
local_verifyruns it viashlex.split,shell=False, after_has_shell_markerhasrejected every shell control character. The flag correctly stays off for what it does hold.
switch_review.SWITCH_CAPABILITYremappedORCH_RUNTIME_AC_ALLOW_COMMANDS → runtime-ac-checks;pointing the "ON but silent" arm at a capability the switch cannot influence made that review unable
to say anything true about either one.
4. Line-number citations rot, so they are banned and the ban is enforced
Four found, all pointing somewhere wrong:
SWITCH_ON_CRITERIA[…START_BROWSER]:orchestrate.sh:133/:152SWITCH_ON_CRITERIA[…ALLOW_COMMANDS]:runtime_ac.py:955shlex.split(check['command'])capability_firing_monitor.pyheader: "(line ~152)"frontend-verifier:orchestrate.sh:133All replaced with anchors. Enforced in
capability_recurrence_check._selftest: noSWITCH_ON_CRITERIAvalue may contain afile.py:NNNcitation, and every citedORCH-ANCHOR: <name>must be defined on its own comment line in the tree, with a floor of ≥3citations so the rule cannot pass vacuously.
That assertion's own first version was circular — a plain
"ORCH-ANCHOR: x" in tree_texttestpassed a citation to a nonexistent anchor, because the criteria table's own source is part of the
tree. The check satisfied itself, in three lines. Only the break→revert found it.
5.
ORCH_EXPLORATION_MODE: a decision, not a holdIts criterion correctly said the blocker was the review, not the data — and the review had never
been run. Ran it:
keep_epsilon_greedy/epsilon_still_preferred. Its evidence gates are met(727 instrumented runs, 65 direct exploration outcomes, ready_tasks 5/9, zero-cell 16.2%) and
Thompson-hybrid improves simulated challenger quality and direct exploration outcomes — but not
both. So ε-greedy is kept on merit.
_check_thompsonnow runs the review and reports its recommendation, so the prose criterion isgone and the switch re-raises itself if that verdict ever flips. README updated (it still said
"pending the weekly evidence gate's recommendation").
Net re-triage: 5 held switches → 3 (range-lane genuinely data-gated — today's preview reports
dispatch_preview: [], 0 eligible candidates, read from the artifact its own criterion names, not acalendar; strategy-experiments no demand; frontend operator policy), 1 decided, 1 never held.
6. Advisor reach is now measured against a declared floor
The advisor can name 6 of 41 capabilities from free text — 5 by declaration
(
{"field": "task_type", …}) plusoffloadthrough a hardcoded direct-entry map. The other 35 failclosed by design, because
_matches_triggeris handed{repository, task_type, lane}and akind-shaped matcher cannot match it.
Reach had already shrunk in silence:
docs-drift-fix-agentandadversarial-reviewstill holdlearned association observations from when they were reachable; each was later retuned to a
kind-shaped matcher and dropped out of the front door with nothing reporting it — a latched gate
inside the measuring instrument.
advisor_reach()publishes the reachable set againstADVISOR_REACH_BASELINEand raisesadvisor_reach_regressionon a shrink. Report-only, and it must stay that way. WideningTASK_SIGNALSor loosening a matcher to move this number would corrupt the learned associations —explicitly not done.
Every audit row now also carries
advisor_reachable, because "the advisor never recommended it" hastwo causes with opposite fixes: no demand, versus the front door structurally cannot name it.
Verification
Recurrence went 41/46 → 42/46 by replacing a fixture that asserted a blocker which does not
exist, proven by running the code — not by bending one to match.
Path-independence instead of a mirror re-run. A concurrent session had deliberately left
~/.codex/orchestrator-mirrorunsynced, and that gap is the owner's only circuit breaker between anagent's change and the dispatcher that dispatches agents — so it was not touched. The same guarantee
was obtained by re-running the verdict from a copy of the tree at a different path: identical
354/354, 41/41, 42/46, 5/5.
Break→revert, all reverted byte-identical:
COMMAND_EXEC_GATED_TYPESgainingdeliberate_break→ predicate assertion fails.runtime assertion fails with
SKIPinstead ofUNSAFE. This is what the shared name buys.SWITCH_ON_CRITERIAentry citing a nonexistent anchor → anchor rule fails (and caught its owncircular first draft).
origin/main's realorchestrate.sh→ reports both realsuppressed producers.
.verify-floor.json353 → 354: exactly one new test. No ceiling moved and nothing new isskipped — the new checks read source files rather than a populated ledger, so they run on any
machine. Measured on the merge result, per that file's own note. Hand-edited rather than via
--update-floorso the note history survives.Not in this PR, on purpose
capability_advisor.pyis untouched — PR fix(advisor): every association count names its population (FM5) #17 is open against that file alone. The reachmeasurement lives in the activation audit and imports the advisor read-only.
ORCH_FRONTEND_VERIFY_START_BROWSERstays the owner's call; this onlymakes flipping it measurable.
accumulate: neither has a queue, and neither can auto-open a gate or auto-widen a matcher.
roles.pychange, soARCHITECTURE.md+orchestrator-loop.svgare untouched by design.The 41-row expected-vs-actual table, with a named cause and a criterion for every non-firing row,
lives in the gitignored evidence record
Code/Audits/Orchestrator/2026-08-22-trigger-pathways-end-to-end.md.🤖 Generated with Claude Code