Exhaustion detection is armed on 2 of 8 profiles, and the default profile is not one of them #479
Open
opened 2026-09-10 23:21:03 +02:00 by ltms
·
3 comments
No Branch/Tag Specified
main
worker/fleetd-612-unita-87807e-1
worker/612-b3-mcpwirings-da2b58-3
worker/612-b2-cb185-176d3a-2
worker/612-b1-completion-457459-1
worker/612-agaps-73a926-2
worker/608-sleeps-3a64ff-3
worker/621-b4520b-1
worker/618-b83894-2
worker/fleetd-615-e05481-5
worker/lead-autocompact-5f1ab2-3
worker/fleetd-613-f85deb-3
worker/fleetd-608-flaky-nudge-test-d0c2d1-3
worker/lead-context-gauge-ad404f-1
worker/gauge-wiring-9158c1-4
worker/redeploy-slowstart-ead0e5-5
worker/charter-bytes-13668c-6
worker/rollover-outcome-291483-2
worker/589-f64303-2
worker/593-1a8025-5
worker/589-fcd2aa-1
worker/568-9fdaa2-3
worker/571-attempted-outcome-5739f7-2
worker/581-completionresolver-cas-sites-0542b7-6
worker/562-loop-health-wiring-test-99611c-5
worker/562-surface-loop-health-7df5cc-4
worker/575-waiter-cleanup-sites-62ad80-1
worker/572-answer-lock-release-46a9ae-5
worker/567-probe-channel-leak-a38fc5-6
worker/551-record-before-send-7cbf56-1
worker/561-listener-fanout-survives-a-throw-61d538-2
worker/555-redeploy-main-flow-seam-65c2f5-2
worker/556-injector-owns-registration-e027a5-1
worker/552-post-restart-mktemp-abort-bc2672-4
worker/553-onstatus-completion-leak-0da881-2
worker/550-shasum-linux-196132-1
worker/538-loop-dies-on-error-4a5eeb-6
worker/426-health-coverage-ef1fd4-4
worker/504-failed-reported-clean-3cfd66-3
worker/537-capturedlog-close-e4c437-2
worker/459-broken-link-targets-cadc17-5
worker/535-appender-leak-fe74c1-1
worker/512-part2-shutdown-detection-434701-9
worker/529-logger-level-sweep-2a5533-8
worker/528-drain-gate-call-site-5de83d-7
charter/forge-mcp-vs-token
worker/521-swap-guard-unpinned-28e931-5
worker/519-probe-test-harness-d25ab8-4
worker/525-logger-level-leak-1b4eb0-6
worker/518-fleetmcp-resolver-wiring-8ef96c-1
worker/512-drain-complete-line-7edd71-3
worker/517-abort-branch-and-jar-id-41b641-2
worker/500-9e52c9-3
worker/509-4912f4-2
worker/511-9a4b23-1
worker/493-479f45-2
worker/505-03f8b2-1
worker/492-followup-detect-unclear
worker/501-a31fa0-7
worker/498-451d1c-5
worker/494-1015ce-2
worker/492-209647-1
worker/489-001902-2
worker/480-relative-handover-path-906323-1
worker/480-b-handover-skill-45bf1f-5
worker/474-followup-source-pin-f54a55-17
worker/474-charter-check-on-reload-f54a55-17
worker/466-quarantine-repeatcount-report
worker/393-opencode-skill-seeding-71854b-13
worker/469-canonical-tool-names-2a472a-16
worker/466-quarantine-escalation-5ae9c1-15
worker/446-hot-exhausted-pattern-0af580-6
worker/464-charter-tool-name-guard-a85635-12
worker/463-listfleet-default-fails-open-f1c76c-11
worker/458-invariant-5-by-purpose-862f9a-10
worker/439-coordinator-row-gate-bc032a-8
worker/449-herdr-protocol-576015-4
worker/450-abstract-spawn-599e1c-5
worker/437-ack-refuses-177d91-1
worker/444-placement-window-feb56a-2
worker/440-helddurable-derived-d462d7-13
worker/425-rework-placement-resolve-c58ba1-9
worker/421-lead-peek-held-msgs-cdbad2-10
worker/435-fixed-policy-cap-fe11de-12
worker/422-gate-state-observability-9e79d6-11
worker/431-memberregistry-live-readers-cdbad2-10
worker/424-architect-slot-hot-038b41-7
worker/422-model-gate-spawn-c29f48-6
worker/425-default-profile-live-f55534-8
worker/415-coverage-wording-2cbf9c-5
worker/416-3ad1da-1
worker/418-588283-3
worker/deterministic-stamp-race-409-3cb7b6-10
worker/armed-reads-live-config-404-ed931f-9
worker/reply-peer-refusal-391-5a34bd-7
worker/models-allowlist-aa9e9b-3
worker/ttl-stamp-race-399-f1122f-8
worker/scrub-receipt-400-316b3e-5
worker/exhaustion-detection-395-105105-6
worker/scrub-abort-394-316b3e-5
fix/scrub-uid-abort
worker/task-scrub-517574-2
worker/t386-clock-bd5b78-4
worker/t384-scrub-813790-5
worker/t381-cc-748314-2
worker/t373-336973-2
worker/t365-3920c5-3
worker/t358-6e989b-1
worker/t355-8b321c-1
worker/fleetd-369-hermetic-git-tests-e8b19a-3
worker/fleetd-368-stale-lead-binding-f5682e-2
worker/fleetd-360-deploy-units-0d3793-1
worker/359-dead-lead-tabs-f1253b-4
worker/362-worktree-skills-c03e51-3
worker/361-coord-visibility-655144-1
362-plugin-visibility-and-drift
worker/errscan-bed2ca-2
worker/amqp-log-identity-bed2ca-2
worker/withdefaults-guard-561704
worker/sleepguard-82076d-1
worker/fd334-9ee1b6-5
worker/fd348-f1ab27-4
worker/fd335-a71c35-1
worker/fd342-174a17-2
worker/fd345-490d0f-3
worker/fleetd-337-5ec7d4-21
worker/fleetd-341-af5a6b-24
worker/fleetd-339-5ca0a2-23
worker/fleetd-338-83a4a1-22
worker/fleetd-333-281f46-18
worker/fleetd-329-11bdbb-16
worker/fleetd-330-2770fb-17
worker/fix-326-50506e-15
worker/fix-324-3e9bbf-14
worker/fix-323-b8287d-13
worker/fix-316b-bd0860-11
worker/fix-318-76ca36-9
worker/fix-317-486aec-8
worker/fix-315-ce47c5-6
worker/fix-307-275890-6
worker/fix-308-b4f664-7
worker/fix-309-ec3939-8
worker/fix-310-7a3974-9
worker/fix-302-52ad0e-9
worker/fix-298-ce1acb-8
worker/fix-297-66bd11-7
worker/fix-296-104622-6
worker/fix-293-bare-closetab-eb22b5-3
worker/fix-280-gone-ask-lapse-bca98e-2
worker/fix-290-reapidle-guard-coverage-9b0dd1-1
worker/fix-285-trust-seed-8f3565-10
worker/fix-284-backend-error-seat-85912c-11
worker/fix-282-chained-ask-e6d0bb-8
worker/fix-283-teardown-leaks-f40dfa-9
worker/fix-281-pin-handler-actions-4921ac-7
worker/audit-rendezvous-lifecycle-d072ae-2
worker/audit-health-placement-1a2476-6
worker/audit-teardown-exits-e207a5-3
worker/audit-launcher-asymmetry-27e370-4
worker/audit-rest-authz-6ca53c-5
worker/investigate-275-abandon-asking-fdef52-8
worker/fix-274-worktree-leak-b0095d-7
worker/fix-273-exhausted-pattern-9665b5-6
worker/fleetd-267-model-check-bd8068-1
worker/fleetd-131-archunit-18b834-7
worker/fleetd-266-sshagent-rename-a014ff-6
worker/fleetd-184-uid-claim-8e1f31-4
worker/fleetd-184-warn-b381ee-10
worker/fleetd-184-docs-be1d12-9
worker/fleetd-257-9bf010-7
worker/fleetd-103-23a113-6
worker/fleetd-247-342356-5
worker/fleetd-116-04dea8-4
worker/fleetd-252-a830e0-3
worker/fleetd-111-7e8673-9
worker/fleetd-155c-f8ef4b-8
worker/fleetd-176-b928ca-3
worker/fleetd-249-7a7878-2
worker/cb248-composition-root-b-9acdf7-15
worker/cb148-envrc-default-fa6c82-12
worker/cb201-unit5-wiring-6c12e6-8
worker/cb241-fallback-echo-1175e9-11
worker/cb149-trust-dialog-2392a5-9
worker/cb134-148-overlay-visible-c9b986-10
worker/cb234-session-id-keyed-04e1fc-1
worker/cb201-unit3-nudge-abdf5c-6
worker/cb201-unit2-policy-c1102c-5
worker/cb201-unit4-outcome-a13bfa-7
worker/cb201-unit1-classifier-91b9b1-4
worker/cb201-227-refine-831980-3
worker/cb175-model-readback-0f085f-1
worker/cb222-charter-tmpdir-17f013-1
worker/cb226-architect-slot-race-cd3aa8-3
worker/cb224-worktree-root-group-024523-2
worker/cb-123-role-demotion-c600f7-2
worker/cb-219-opencode-roots-1f677e-1
worker/cb214-claude-session-id-b9eab4-4
worker/cb213-zdotdir-wrong-process-dd6de4-3
worker/cb211-exhaustion-classification-9546e0-2
worker/cb137-ambiguous-task-4df3d8-4
worker/cb209-agentsessionid-4dfdb6-2
worker/cb185-hostenvnames-2692b5-3
worker/cb206-opencode-sqlite-128718-2
worker/cb185-worktree-group-fc0c99-1
worker/cb-137-ask-ticket-e7760c-2
worker/cb-172-broker-uri-d36ae4-4
worker/cb-175-model-readback-76ead6-3
worker/cb-161-pane-ancestry-293510-1
worker/cb-164-rebase-885863-8
worker/cb-164-empty-scrape-false-success-1a80af-3
fix/cb-197-ticket-ttl-from-completion
worker/cb-189-remote-url-coverage-4692f3-1
worker/cb-185-blockers-027756-4
worker/cb-192-gap-log-11b631-2
worker/cb-633-fix-5f4396-3
worker/cb185-router-d6436d-3
worker/cb185-router-routing-gaps-9e9d33-3
worker/cb185-paneids-992586-2
worker/cb-633-allow-list-union-ed374b-1
worker/cb-157-credential-in-remote-url-496e44-2
worker/cb-641-health-herdr-evidence-8f1f54-6
worker/cb-640-health-msg-evidence-99c9cd-1
worker/cb-642-fleets-status-skill-bbbc40-5
cb-634-ide-mcp
worker/lead-comms-wiring-c014b9-7
worker/lead-mailbox-c19577-6
worker/autocompact-window-82bc2f-5
worker/cb-634-probe-18056f-4
worker/cb635-broker-urienv
worker/cb-632-config-retry-8e0efa-7
lead/cb-622e-claude-md
lead/cb-622-followup
worker/cb-622a-165dff-1
lead/cb-622d-opencode-mount
worker/cb-622b-717c67-2
worker/cb-622c-ab7759-3
worker/cb-617b2-20ca4b-3
worker/cb-617a-5c2f4a-1
worker/cb596-4e49ef-3
worker/cb586-10500c-1
worker/cb-606-b9343a-25
worker/cb604-1445f8-24
worker/cb582-477374-21
worker/cb584-8c2281-22
worker/cb600-e6b9a9-20
worker/cb602-ce257f-19
worker/cb601-b42837-18
worker/cb598-6c7ba7-17
worker/cb599-740fe4-16
worker/cb597-282224-15
worker/cb590fix-185e9a-10
worker/cb528-recovery-race
worker/cb594-96bead-8
worker/cb590-916766-2
worker/cb527-997d99-3
worker/cb592-env-leak-3cbf9c-1
worker/cb588-async-ticket-nudge-3218f7-5
worker/cb578b-9dcb13-6
worker/cb581-d24826-5
worker/m2-u5-ef8c42-15
worker/cb578a-516499-2
worker/cb576-01a04b-17
worker/cb579-lead-tab-acba06-20
worker/cb580-terminal-health-ed6058-21
worker/cb577-f36fdc-18
worker/cb573b-3db06f-16
worker/cb568c-f36fdc-18
worker/cb568-drop-cause-c3ac1c
worker/cb575-cancelled-notification-c3ac1c
worker/m4-sol-a2cbec-3
worker/cb574-async-ask-c3ac1c
worker/cb573-health-model-8ca857-14
worker/cb572-unknown-target-7f2e35-13
worker/u4-700706-9
worker/u3-b9fcb6-6
worker/u2-ef5b68-4
worker/u1-469dce-1-clean
worker/u1-469dce-1
worker/cb-564-health-events-70cf7e-2
worker/cb-565-recycle-drops-role-98e58f-3
worker/cb-563-missing-reply-df2866-1
worker/cb-562-readiness-gate-silent-6c23c9-3
worker/cb-560-architect-presence-da8155-1
worker/cb-561-architect-silent-off-a71cab-2
worker/cb-548-bind-architect-slot-fe1b8c-1
worker/parity-overlay-settings-5fb711-1
secrets-central-store
cb-559-hot-key-correction
cb-557-fleet-role-pools
worker/cb-553-maxload-explicit-spawn-305ee3-6
worker/cb-551-idle-lead-heartbeat-f1633c-1
worker/cb-544-drain-preserves-worktree-925fad-3
worker/cb-552-docs-sync-1cb9cf-4
worker/cb-548-rendezvous-guard-rebased
worker/cb-548-rendezvous-guard-116b53-10
worker/cb-548-authz-v2-586df6-8
worker/cb-548-authz-264363-5
salvage/cb-528b-codex-home
salvage/cb-528a-codex-launcher
CB-518-primary-flow
feature/peer-launcher-spi
cb-103-injector
v1.1.0
v1.0.0
Labels
Clear labels
blocked
needs-live-proof
ready-to-delegate
silent-default
Cannot start until something else lands. The body says what.
Merged and green, but never shown working on the running daemon. Not the same as done.
Scope, files and acceptance criteria are written. A worker can be briefed from the body alone.
A feature that compiles, passes tests, and ships turned off. Nine recurrences and counting.
No Label
Milestone
No items
No Milestone
Projects
Clear projects
No project
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: fleet/fleetd#479
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What I measured
On the live daemon (pid 52482, jar
82ebc1cc7047, started 04:17:59 on49a404d):The cause is in the config, not the code.
exhaustedPatternis set on exactly two profiles:Line 153 is under
sol:(starts at :133), line 175 underterra:(starts at :155). Control: thesame file lists 8 profiles at
profiles:(:9), so the grep read a real file and found two ofeight.
Why this matters more than a coverage number
The default profile is
sonnet, and it is one of the six with detection off. So isopus,which the lead itself runs on.
When a subscription limit hits a profile with no
exhaustedPattern, the whole chain that wasbuilt for this case does nothing:
BackendQuarantinenever escalates and never setsfree: 0usageLimitFixWarning, so nothing names the model to turn offfleet_profilesreports the profile as normally availableThe operator asked for exactly this behaviour in their own words: "runtime model limit
monitoring, off when subscription limit reach and on when the limit lifted." Parts 1 and 2 of
that ask are shipped and live —
models.allowis a central list,enabled: falseis honoured atspawn and in placement, and the boot log confirms
model gate (fleetd #422): armed (models: block present; 0 models currently turned off). Part 3 is the monitoring, and today it watches the twoOpenAI profiles and none of the Claude ones.
So the feature is not missing. It is armed on the profiles least likely to be the problem, and
absent on the two that carry the lead and the default worker.
Why this is a fleetd ticket and not just a config edit
Adding two lines to
fleetd.yamlwould fix today's fleet and leave the defect in place, becausenothing tells anyone the gap exists. Three things are worth deciding:
reports it precisely.
exhaustedPatternis per-profile with no default. That may be right — the string differsby backend. But a per-profile key with no default and no coverage warning is the shape that
produced this: six profiles were added and nobody noticed the key was missing on any of them.
← Settled by #415; do not reopen.
exhaustedPatternis DEFERRED, so adding it needs a restart. That is fine, but it meansthe fix cannot be applied during an outage, which is the moment someone would reach for it.
Worth stating in the warning text if we keep it deferred.
Scope
Decide and implement how the daemon reports and handles a profile with no exhaustion detection.
I am not choosing the answer; both of these are defensible and the PR body should say which and why:
the shape of
modelGateCoverageLine. ← VOID: already shipped.profile. ← VOID: settled by #415.
Whatever you pick, do not silently change what
exhaustionDetectionArmedreports withoutchanging what it measures. The two must stay the same read. ← still holds.
Acceptance criteria
exhaustedPatternis reported as such. It must fail againsttoday's code — paste the failure.
inverted condition passes a one-sided test; #469's own M5 cell showed that.
the way
FleetdUsageLimitFixWarningTestdoes, plus a source-text test pinning the call site inFleetd.main. ReadFleetdConfigRefWiringTestfirst and follow it. Do not use thebuild-your-own-wiring pattern: five
Fleetd.maincall sites have now survived a mutationbattery here (#446 M5/M7, #466 M1, #474 M2) precisely because that pattern cannot prove
mainchose the wiring. ← VOID as written (nothing to build), but the source-text rule still
applies to whatever call site you do add.
exhaustedPatternstays deferred, and if it does, whether the operator is toldthat a restart is needed.
from a piped tail. Baseline is 1634 on
49a404d.subscription limit, so do not imply you did.
Not in scope
Editing
fleetd.yaml. It is gitignored, it is the live config, and a write to it was refused bythe operator's command classifier. If your change means the live config should gain lines, say
which lines and let the operator paste them.
Measured and filed by the lead. Related: #466 (the quarantine escalation this feeds), #411 (two
profiles sharing a
tokenEnvbut quarantining under different credential ids), #415 (the committhat settles the default question).
Second host, and it changes which option in the Scope section I would pick.
What the fleet01 lead measured on their host
Reported to me over the coordinator channel. I did not run these commands — this is their measurement on their host, not mine. I am recording it because it separates two things my own host cannot separate.
Three layers, and the second is the one I had not thought about.
Layer 1 — nothing is armed. 0 of 4 profiles, against a control of 4 that proves the search works. Worse than the 2 of 8 here.
Layer 2 — the field that would report it does not exist on their jar. Their
fleet_profilesreturns nomodelGateArmed, noexhaustionDetectionArmed, no quarantined map. Their jar predates all three fields. So on my host the gap is visible if you look; on theirs, the instrument that would show the gap is missing. They put it better than I would: I knew my daemon predated the gate because the key was absent, and they get the same signal, but for them it means the diagnostic is missing rather than only the arming.Layer 3 — their default profile is the one their own config documents as recurrently exhausting. Their
fleet_profilesreportsdefault: "xf", and their config at line 245 records that xf's credential "sits on a backend-exhaustion quarantine … Add xf here once that quarantine stops recurring." So the most likely profile to hit a limit is the default, has nothing armed to detect it, and has no field that could report it.Why this changes the scope
The ticket offered two options: report the gap (a startup line in the shape of
modelGateCoverageLine), or report it and add a per-backend default. I left the choice open. I now think the reporting half is the more valuable one, and here is the argument, which is not the one I would have made yesterday.A status field is only readable by a caller whose daemon already has the field. That is circular in exactly the case you need it: a host running an older jar gets a silent, well-formed response with the field simply absent, and absence of a field reads as "nothing to report" rather than "this daemon cannot tell you". fleet01's host is that case today.
A startup log line does not have that property. It lands in
fleetd.outat boot, an operator or a peer lead can read it without any API contract, and its absence from a boot log is itself informative — it dates the jar. So the log line degrades honestly on an old jar and the status field does not.That is not an argument against the field. Ship both. It is an argument that the log line is the part that must not be dropped for scope, which is the opposite of how I would normally rank a log against a structured field.
One thing this does not change
Do not add a default
exhaustedPatternin order to make fleet01's host report armed. A pattern that never matches is indistinguishable from no pattern at all, and arming detection that cannot fire is worse than leaving it visibly off — it converts a legible gap into a false receipt. Their fix is config plus a rebuild, and both are their operator's to time; they said explicitly they are recording it rather than acting on it.Correction to my own framing above
My original text said this is "armed on the profiles least likely to be the problem." On my host that is fair —
solandterraare armed andsonnetis the default. Read across both hosts it is too weak: fleet01's arming is not merely misdirected, it is absent, and their default is the documented offender. The general statement is thatexhaustedPatternhas no default, no coverage report, and no way for a caller to tell "off" from "this daemon cannot say" — and the third of those is the new one.I have to correct this ticket's premise. The startup line I asked for already exists, and it already reports the gap precisely. I filed a ticket asking for shipped work. Here is what is actually left.
What I measured, on my own host, this boot
Control: 27 INFO lines from that boot, so the filter read a real range.
The first line names the mechanism, says
partial, and enumerates both sets by name — including the six unconfigured profiles. That is better than themodelGateCoverageLine-shaped thing I proposed to add. So acceptance criterion 3 as written is void: there is nothing to build there.The honest failure is mine. I measured the state (
fleet_profiles) and the config (grep exhaustedPattern), concluded the gap was unreported, and never read the third channel — the daemon's own boot log — which reports it exactly. The fleet01 lead made the same mistake in the same direction on their host and caught it first; that is what made me look.Two things this ticket did surface, and both are real
1. There are two detectors of this shape, not one. I only looked at exhaustion.
errorPattern(fleetd #201 Unit 5) is its sibling and I had not examined it at all.2. The sibling already answers the design question I claimed to leave open — in the opposite direction to the argument I made. I wrote above: "Do not add a default
exhaustedPattern… a pattern that never matches is indistinguishable from no pattern." ButerrorPatternalready has a built-in default, and it is not in config:FleetConfig.java:562-565— "errorPattern stays null when unset/blank (opt-in) — same rule as exhaustedPattern". So the config half is identical for both.built-in default for all profiles, andFleetd.java:967names "backend-error classification againstCompletionResolver's built-in". So the fallback lives in the resolver, not in the config record.exhaustedPatternhas no such fallback.Fleetd.java:912says it plainly: "a profile with none configured really does have the classification off."So the two siblings made opposite choices about the same question, and neither the code nor this ticket says why. That is the actual open question, and it is better than the one I filed:
I lean toward the difference being deliberate for exactly that reason, which is my original argument. But it is now an argument against a shipped sibling, not against a hypothetical, so it needs to be made on the evidence rather than asserted. Whoever takes this should read
CompletionResolver's built-in pattern first and say whether an exhaustion equivalent could be written safely.fleet01's caveat, which matters if anyone builds on the log line
Their host has a decoy log file.
fleetd-run/fleetd.outexists, is 27 lines and 4007 bytes, is dated five days before the running daemon started, and containsfleetd listening on 127.0.0.1:8765— startup lines of exactly the right shape. The running daemon's stdout and stderr are both a socket; it runs under a systemd user unit, so the real log isjournalctl --user -u fleetd.service. Nothing infleetd.outnames a pid or a jar.Their measurement, not mine. The consequence generalises:
"Absent from the boot log, therefore this jar predates the feature" is unsound unless the log is first proven to cover that boot. My own host is fine —
redeploy-fleetd.sh --checkreportslog path check: script and plist agree, and I anchor reads to a timestamp from the current boot. That is a property of my launchd setup, not of fleetd. Any ticket or runbook that says "read the boot log" must name the stream and say how to date it.Revised scope
Criterion 3 (build a startup line) is void — already shipped. What is left:
exhaustedPatterndefault question above, against theerrorPatternprecedent. Name the reason either way; that reason is the deliverable.partial (… not configured: [… sonnet …])anddefault: sonnetare two facts in two places, and nothing joins them. Joining them is cheap and is the one report that does not exist.The config arming on both hosts stays the operator's, and it is still out of scope here.
The design question in my previous comment is already decided and shipped, as #415. Do not re-litigate it. The fleet01 lead found it; I verified every claim below in my own tree before writing this.
#415 is the answer, and it is in my jar
git cat-file -t be123d0→commit. My last fetch was 04:13:30 today, and the commit is dated2026-09-10, so it is inside my fetch window.
Verified in the file rather than from the commit message — note the path is
inject/, notmsg/:inject/CompletionResolver.java:84—BACKEND_ERROR = Pattern.compile("(?i)\\bAPI Error\\s*:"):609—return configured != null ? configured : BACKEND_ERROR;:716—public enum UnsetMeaning:743—public static String coverage(String patternKey, UnsetMeaning unsetMeaning, Set<String> allProfiles, …)Control: 822 lines in the file, so it was read.
So the built-in has been there all along, on both hosts. What differed between our two boot
lines was never two designs — it was one wording bug and its fix. #415's own rationale states it:
"
coverage()measured pattern coverage (how many profiles set a key) but its 'off' wording readas feature state. That is false for
errorPattern: an unseterrorPatternstill runs theclassification against the built-in
BACKEND_ERRORpattern."My previous comment asked whoever takes this to "read
CompletionResolver's built-in patternfirst and say whether an exhaustion equivalent could be written safely." Replace that with:
read #415's rationale — it already contains the argument I was preparing to have.
#415 also encodes the forcing function, and it is the antidote to a shape we keep hitting
UnsetMeaningis a required parameter ofcoverage()with no defaulted overload(
:743is the only signature). So a future third pattern key cannot compile without stating whatunset means for it.
That is worth naming because it is the exact inverse of the
Fleetd.mainshape this repo has nowhit five times (#446 M5/M7, #466 M1, #474 M2). There, extracting a helper created a new uncovered
decision and every metric went up, so nothing pointed at it. Here, the extraction creates a new
decision and refuses to compile until it is answered. The difference is only whether the new
parameter is required or defaulted — a one-word choice made at the time of extraction. Same
mechanism as
Authz.permitsbeing a default-less switch overAction.A correction that weakens my own ranking argument
I argued above that a startup log line is better than a status field because "the log degrades
honestly on an old jar and the status field does not." That is too strong, and fleet01
supplied the counterexample from their own host — against their own earlier point.
Their stale jar printed
backend-error classification: off (no profile has an errorPattern configured). That line was false: the built-in was matching every target the whole time. Soan old jar's log line can say something confidently wrong, which is worse than silence, because it
terminates the search. It did exactly that — it read as an answer and was forwarded to me as one.
It is also the precise dual of the thing I argued against earlier in this ticket. I said: do not
give
exhaustedPatterna default, because a pattern that never matches is indistinguishable fromno pattern. Their jar had the other half — a pattern that does match, reported as off.
The ranking still holds, but on a different and better property, which is theirs:
#415 corrected the wording in place, and every host that rebuilds gets the fix with no API
contract to renegotiate. That is why the log line is the part not to drop for scope — not because
logs degrade honestly, because they do not.
What is still genuinely missing — unchanged by any of the above
Nothing joins the coverage line to the default-profile selection. #415 fixed what the line
means; it did not make anything correlate coverage with which profile a spawn actually lands on.
not configured: [gx, local, local-direct, opus, sonnet, xf]anddefault: sonnet.xfrecurrently exhausts,defaultisxf, andnothing armed.
Two facts, two places, no single output that puts them next to each other. That is the whole
remaining finding, and it is the same on both hosts.
Revised scope, superseding both earlier comments
deserves more than INFO. This is the deliverable.
exhaustedPatterndefault question — closed by #415; read its rationale, do not reopen.the boot (fleet01's host has a stale
fleetd.outthat fails by looking right).Config arming on both hosts remains the operator's.