fleetd #247: CAS seedTrustDialog against the operator's own live Claude Code #261
Closed
agent
wants to merge 0 commits from
worker/fleetd-247-342356-5 into main
pull from: worker/fleetd-247-342356-5
merge into: fleet:main
fleet:main
fleet:worker/fleetd-612-unita-87807e-1
fleet:worker/612-b3-mcpwirings-da2b58-3
fleet:worker/612-b2-cb185-176d3a-2
fleet:worker/612-b1-completion-457459-1
fleet:worker/612-agaps-73a926-2
fleet:worker/608-sleeps-3a64ff-3
fleet:worker/621-b4520b-1
fleet:worker/618-b83894-2
fleet:worker/fleetd-615-e05481-5
fleet:worker/lead-autocompact-5f1ab2-3
fleet:worker/fleetd-613-f85deb-3
fleet:worker/fleetd-608-flaky-nudge-test-d0c2d1-3
fleet:worker/lead-context-gauge-ad404f-1
fleet:worker/gauge-wiring-9158c1-4
fleet:worker/redeploy-slowstart-ead0e5-5
fleet:worker/charter-bytes-13668c-6
fleet:worker/rollover-outcome-291483-2
fleet:worker/589-f64303-2
fleet:worker/593-1a8025-5
fleet:worker/589-fcd2aa-1
fleet:worker/568-9fdaa2-3
fleet:worker/571-attempted-outcome-5739f7-2
fleet:worker/581-completionresolver-cas-sites-0542b7-6
fleet:worker/562-loop-health-wiring-test-99611c-5
fleet:worker/562-surface-loop-health-7df5cc-4
fleet:worker/575-waiter-cleanup-sites-62ad80-1
fleet:worker/572-answer-lock-release-46a9ae-5
fleet:worker/567-probe-channel-leak-a38fc5-6
fleet:worker/551-record-before-send-7cbf56-1
fleet:worker/561-listener-fanout-survives-a-throw-61d538-2
fleet:worker/555-redeploy-main-flow-seam-65c2f5-2
fleet:worker/556-injector-owns-registration-e027a5-1
fleet:worker/552-post-restart-mktemp-abort-bc2672-4
fleet:worker/553-onstatus-completion-leak-0da881-2
fleet:worker/550-shasum-linux-196132-1
fleet:worker/538-loop-dies-on-error-4a5eeb-6
fleet:worker/426-health-coverage-ef1fd4-4
fleet:worker/504-failed-reported-clean-3cfd66-3
fleet:worker/537-capturedlog-close-e4c437-2
fleet:worker/459-broken-link-targets-cadc17-5
fleet:worker/535-appender-leak-fe74c1-1
fleet:worker/512-part2-shutdown-detection-434701-9
fleet:worker/529-logger-level-sweep-2a5533-8
fleet:worker/528-drain-gate-call-site-5de83d-7
fleet:charter/forge-mcp-vs-token
fleet:worker/521-swap-guard-unpinned-28e931-5
fleet:worker/519-probe-test-harness-d25ab8-4
fleet:worker/525-logger-level-leak-1b4eb0-6
fleet:worker/518-fleetmcp-resolver-wiring-8ef96c-1
fleet:worker/512-drain-complete-line-7edd71-3
fleet:worker/517-abort-branch-and-jar-id-41b641-2
fleet:worker/500-9e52c9-3
fleet:worker/509-4912f4-2
fleet:worker/511-9a4b23-1
fleet:worker/493-479f45-2
fleet:worker/505-03f8b2-1
fleet:worker/492-followup-detect-unclear
fleet:worker/501-a31fa0-7
fleet:worker/498-451d1c-5
fleet:worker/494-1015ce-2
fleet:worker/492-209647-1
fleet:worker/489-001902-2
fleet:worker/480-relative-handover-path-906323-1
fleet:worker/480-b-handover-skill-45bf1f-5
fleet:worker/474-followup-source-pin-f54a55-17
fleet:worker/474-charter-check-on-reload-f54a55-17
fleet:worker/466-quarantine-repeatcount-report
fleet:worker/393-opencode-skill-seeding-71854b-13
fleet:worker/469-canonical-tool-names-2a472a-16
fleet:worker/466-quarantine-escalation-5ae9c1-15
fleet:worker/446-hot-exhausted-pattern-0af580-6
fleet:worker/464-charter-tool-name-guard-a85635-12
fleet:worker/463-listfleet-default-fails-open-f1c76c-11
fleet:worker/458-invariant-5-by-purpose-862f9a-10
fleet:worker/439-coordinator-row-gate-bc032a-8
fleet:worker/449-herdr-protocol-576015-4
fleet:worker/450-abstract-spawn-599e1c-5
fleet:worker/437-ack-refuses-177d91-1
fleet:worker/444-placement-window-feb56a-2
fleet:worker/440-helddurable-derived-d462d7-13
fleet:worker/425-rework-placement-resolve-c58ba1-9
fleet:worker/421-lead-peek-held-msgs-cdbad2-10
fleet:worker/435-fixed-policy-cap-fe11de-12
fleet:worker/422-gate-state-observability-9e79d6-11
fleet:worker/431-memberregistry-live-readers-cdbad2-10
fleet:worker/424-architect-slot-hot-038b41-7
fleet:worker/422-model-gate-spawn-c29f48-6
fleet:worker/425-default-profile-live-f55534-8
fleet:worker/415-coverage-wording-2cbf9c-5
fleet:worker/416-3ad1da-1
fleet:worker/418-588283-3
fleet:worker/deterministic-stamp-race-409-3cb7b6-10
fleet:worker/armed-reads-live-config-404-ed931f-9
fleet:worker/reply-peer-refusal-391-5a34bd-7
fleet:worker/models-allowlist-aa9e9b-3
fleet:worker/ttl-stamp-race-399-f1122f-8
fleet:worker/scrub-receipt-400-316b3e-5
fleet:worker/exhaustion-detection-395-105105-6
fleet:worker/scrub-abort-394-316b3e-5
fleet:fix/scrub-uid-abort
fleet:worker/task-scrub-517574-2
fleet:worker/t386-clock-bd5b78-4
fleet:worker/t384-scrub-813790-5
fleet:worker/t381-cc-748314-2
fleet:worker/t373-336973-2
fleet:worker/t365-3920c5-3
fleet:worker/t358-6e989b-1
fleet:worker/t355-8b321c-1
fleet:worker/fleetd-369-hermetic-git-tests-e8b19a-3
fleet:worker/fleetd-368-stale-lead-binding-f5682e-2
fleet:worker/fleetd-360-deploy-units-0d3793-1
fleet:worker/359-dead-lead-tabs-f1253b-4
fleet:worker/362-worktree-skills-c03e51-3
fleet:worker/361-coord-visibility-655144-1
fleet:362-plugin-visibility-and-drift
fleet:worker/errscan-bed2ca-2
fleet:worker/amqp-log-identity-bed2ca-2
fleet:worker/withdefaults-guard-561704
fleet:worker/sleepguard-82076d-1
fleet:worker/fd334-9ee1b6-5
fleet:worker/fd348-f1ab27-4
fleet:worker/fd335-a71c35-1
fleet:worker/fd342-174a17-2
fleet:worker/fd345-490d0f-3
fleet:worker/fleetd-337-5ec7d4-21
fleet:worker/fleetd-341-af5a6b-24
fleet:worker/fleetd-339-5ca0a2-23
fleet:worker/fleetd-338-83a4a1-22
fleet:worker/fleetd-333-281f46-18
fleet:worker/fleetd-329-11bdbb-16
fleet:worker/fleetd-330-2770fb-17
fleet:worker/fix-326-50506e-15
fleet:worker/fix-324-3e9bbf-14
fleet:worker/fix-323-b8287d-13
fleet:worker/fix-316b-bd0860-11
fleet:worker/fix-318-76ca36-9
fleet:worker/fix-317-486aec-8
fleet:worker/fix-315-ce47c5-6
fleet:worker/fix-307-275890-6
fleet:worker/fix-308-b4f664-7
fleet:worker/fix-309-ec3939-8
fleet:worker/fix-310-7a3974-9
fleet:worker/fix-302-52ad0e-9
fleet:worker/fix-298-ce1acb-8
fleet:worker/fix-297-66bd11-7
fleet:worker/fix-296-104622-6
fleet:worker/fix-293-bare-closetab-eb22b5-3
fleet:worker/fix-280-gone-ask-lapse-bca98e-2
fleet:worker/fix-290-reapidle-guard-coverage-9b0dd1-1
fleet:worker/fix-285-trust-seed-8f3565-10
fleet:worker/fix-284-backend-error-seat-85912c-11
fleet:worker/fix-282-chained-ask-e6d0bb-8
fleet:worker/fix-283-teardown-leaks-f40dfa-9
fleet:worker/fix-281-pin-handler-actions-4921ac-7
fleet:worker/audit-rendezvous-lifecycle-d072ae-2
fleet:worker/audit-health-placement-1a2476-6
fleet:worker/audit-teardown-exits-e207a5-3
fleet:worker/audit-launcher-asymmetry-27e370-4
fleet:worker/audit-rest-authz-6ca53c-5
fleet:worker/investigate-275-abandon-asking-fdef52-8
fleet:worker/fix-274-worktree-leak-b0095d-7
fleet:worker/fix-273-exhausted-pattern-9665b5-6
fleet:worker/fleetd-267-model-check-bd8068-1
fleet:worker/fleetd-131-archunit-18b834-7
fleet:worker/fleetd-266-sshagent-rename-a014ff-6
fleet:worker/fleetd-184-uid-claim-8e1f31-4
fleet:worker/fleetd-184-warn-b381ee-10
fleet:worker/fleetd-184-docs-be1d12-9
fleet:worker/fleetd-257-9bf010-7
fleet:worker/fleetd-103-23a113-6
fleet:worker/fleetd-116-04dea8-4
fleet:worker/fleetd-252-a830e0-3
fleet:worker/fleetd-111-7e8673-9
fleet:worker/fleetd-155c-f8ef4b-8
fleet:worker/fleetd-176-b928ca-3
fleet:worker/fleetd-249-7a7878-2
fleet:worker/cb248-composition-root-b-9acdf7-15
fleet:worker/cb148-envrc-default-fa6c82-12
fleet:worker/cb201-unit5-wiring-6c12e6-8
fleet:worker/cb241-fallback-echo-1175e9-11
fleet:worker/cb149-trust-dialog-2392a5-9
fleet:worker/cb134-148-overlay-visible-c9b986-10
fleet:worker/cb234-session-id-keyed-04e1fc-1
fleet:worker/cb201-unit3-nudge-abdf5c-6
fleet:worker/cb201-unit2-policy-c1102c-5
fleet:worker/cb201-unit4-outcome-a13bfa-7
fleet:worker/cb201-unit1-classifier-91b9b1-4
fleet:worker/cb201-227-refine-831980-3
fleet:worker/cb175-model-readback-0f085f-1
fleet:worker/cb222-charter-tmpdir-17f013-1
fleet:worker/cb226-architect-slot-race-cd3aa8-3
fleet:worker/cb224-worktree-root-group-024523-2
fleet:worker/cb-123-role-demotion-c600f7-2
fleet:worker/cb-219-opencode-roots-1f677e-1
fleet:worker/cb214-claude-session-id-b9eab4-4
fleet:worker/cb213-zdotdir-wrong-process-dd6de4-3
fleet:worker/cb211-exhaustion-classification-9546e0-2
fleet:worker/cb137-ambiguous-task-4df3d8-4
fleet:worker/cb209-agentsessionid-4dfdb6-2
fleet:worker/cb185-hostenvnames-2692b5-3
fleet:worker/cb206-opencode-sqlite-128718-2
fleet:worker/cb185-worktree-group-fc0c99-1
fleet:worker/cb-137-ask-ticket-e7760c-2
fleet:worker/cb-172-broker-uri-d36ae4-4
fleet:worker/cb-175-model-readback-76ead6-3
fleet:worker/cb-161-pane-ancestry-293510-1
fleet:worker/cb-164-rebase-885863-8
fleet:worker/cb-164-empty-scrape-false-success-1a80af-3
fleet:fix/cb-197-ticket-ttl-from-completion
fleet:worker/cb-189-remote-url-coverage-4692f3-1
fleet:worker/cb-185-blockers-027756-4
fleet:worker/cb-192-gap-log-11b631-2
fleet:worker/cb-633-fix-5f4396-3
fleet:worker/cb185-router-d6436d-3
fleet:worker/cb185-router-routing-gaps-9e9d33-3
fleet:worker/cb185-paneids-992586-2
fleet:worker/cb-633-allow-list-union-ed374b-1
fleet:worker/cb-157-credential-in-remote-url-496e44-2
fleet:worker/cb-641-health-herdr-evidence-8f1f54-6
fleet:worker/cb-640-health-msg-evidence-99c9cd-1
fleet:worker/cb-642-fleets-status-skill-bbbc40-5
fleet:cb-634-ide-mcp
fleet:worker/lead-comms-wiring-c014b9-7
fleet:worker/lead-mailbox-c19577-6
fleet:worker/autocompact-window-82bc2f-5
fleet:worker/cb-634-probe-18056f-4
fleet:worker/cb635-broker-urienv
fleet:worker/cb-632-config-retry-8e0efa-7
fleet:lead/cb-622e-claude-md
fleet:lead/cb-622-followup
fleet:worker/cb-622a-165dff-1
fleet:lead/cb-622d-opencode-mount
fleet:worker/cb-622b-717c67-2
fleet:worker/cb-622c-ab7759-3
fleet:worker/cb-617b2-20ca4b-3
fleet:worker/cb-617a-5c2f4a-1
fleet:worker/cb596-4e49ef-3
fleet:worker/cb586-10500c-1
fleet:worker/cb-606-b9343a-25
fleet:worker/cb604-1445f8-24
fleet:worker/cb582-477374-21
fleet:worker/cb584-8c2281-22
fleet:worker/cb600-e6b9a9-20
fleet:worker/cb602-ce257f-19
fleet:worker/cb601-b42837-18
fleet:worker/cb598-6c7ba7-17
fleet:worker/cb599-740fe4-16
fleet:worker/cb597-282224-15
fleet:worker/cb590fix-185e9a-10
fleet:worker/cb528-recovery-race
fleet:worker/cb594-96bead-8
fleet:worker/cb590-916766-2
fleet:worker/cb527-997d99-3
fleet:worker/cb592-env-leak-3cbf9c-1
fleet:worker/cb588-async-ticket-nudge-3218f7-5
fleet:worker/cb578b-9dcb13-6
fleet:worker/cb581-d24826-5
fleet:worker/m2-u5-ef8c42-15
fleet:worker/cb578a-516499-2
fleet:worker/cb576-01a04b-17
fleet:worker/cb579-lead-tab-acba06-20
fleet:worker/cb580-terminal-health-ed6058-21
fleet:worker/cb577-f36fdc-18
fleet:worker/cb573b-3db06f-16
fleet:worker/cb568c-f36fdc-18
fleet:worker/cb568-drop-cause-c3ac1c
fleet:worker/cb575-cancelled-notification-c3ac1c
fleet:worker/m4-sol-a2cbec-3
fleet:worker/cb574-async-ask-c3ac1c
fleet:worker/cb573-health-model-8ca857-14
fleet:worker/cb572-unknown-target-7f2e35-13
fleet:worker/u4-700706-9
fleet:worker/u3-b9fcb6-6
fleet:worker/u2-ef5b68-4
fleet:worker/u1-469dce-1-clean
fleet:worker/u1-469dce-1
fleet:worker/cb-564-health-events-70cf7e-2
fleet:worker/cb-565-recycle-drops-role-98e58f-3
fleet:worker/cb-563-missing-reply-df2866-1
fleet:worker/cb-562-readiness-gate-silent-6c23c9-3
fleet:worker/cb-560-architect-presence-da8155-1
fleet:worker/cb-561-architect-silent-off-a71cab-2
fleet:worker/cb-548-bind-architect-slot-fe1b8c-1
fleet:worker/parity-overlay-settings-5fb711-1
fleet:secrets-central-store
fleet:cb-559-hot-key-correction
fleet:cb-557-fleet-role-pools
fleet:worker/cb-553-maxload-explicit-spawn-305ee3-6
fleet:worker/cb-551-idle-lead-heartbeat-f1633c-1
fleet:worker/cb-544-drain-preserves-worktree-925fad-3
fleet:worker/cb-552-docs-sync-1cb9cf-4
fleet:worker/cb-548-rendezvous-guard-rebased
fleet:worker/cb-548-rendezvous-guard-116b53-10
fleet:worker/cb-548-authz-v2-586df6-8
fleet:worker/cb-548-authz-264363-5
fleet:salvage/cb-528b-codex-home
fleet:salvage/cb-528a-codex-launcher
fleet:CB-518-primary-flow
fleet:feature/peer-launcher-spi
fleet:cb-103-injector
No Reviewers
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#261
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 "worker/fleetd-247-342356-5"
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?
fleetd #247 — CAS seedTrustDialog against the operator's own live Claude Code
The bug (confirmed facts from the brief, not re-derived)
TRUST_JSON_LOCK(fleetd #149) only serialisesseedTrustDialogcalls made inside this one JVM.It cannot reach a writer outside it — and on this host that writer is the operator's own live
Claude Code:
configDiris routinely set to the operator's ownCLAUDE_CONFIG_DIR, so the filefleetd writes on every claude-code spawn is the config of the very session doing the spawning. A
plain read-modify-write there is a routine lost update on every spawn, not a rare collision:
fleetd reads v1, the operator's session writes v2, fleetd's
ATOMIC_MOVElands v3 built from v1 —atomic, but v2 is gone.
What changed
fleetd/src/main/java/dev/ltms/fleet/member/ClaudeCodeLauncher.java:seedTrustDialognow runs a bounded (5 attempts,MAX_TRUST_JSON_CAS_ATTEMPTS)compare-and-swap loop inside
TRUST_JSON_LOCK: read the target's bytes, build the update, thenimmediately before the
ATOMIC_MOVEre-read the target's bytes and compare with what theupdate was built from. A mismatch discards the attempt and rebuilds from the fresh bytes.
up on. This is deliberate (commented in the code): a member that starts unseeded shows the trust
dialog and fails to reach an injectable state — visible, logged, recoverable. Writing a stale
copy over the operator's live config would be silent and not recoverable. Fail toward the
recoverable outcome.
~/.claude.json(
configDirnull/blank) — the only path that hits the operator's home file, and the default.trustJsonCasTestHook(package-visible, no-op in production): a seam fired once per CASattempt, between the read and the final compare, so tests can deterministically simulate a
racing external writer instead of depending on real thread timing (mirrors why
writeAtomicallyis package-visible rather than private — see its javadoc).
between the final re-read and the
ATOMIC_MOVEitself is still lost, because there is noOS-level CAS on a plain file — and that the file at risk is the lead's own config file
(
<configDir>/.claude.json), not some unrelated process's, so this is a routine spawn-time race,not a rare one.
fleetd/src/test/java/dev/ltms/fleet/member/ClaudeCodeLauncherTest.java— 3 new tests, all goingthrough the real
seedTrustDialog/spawn()path viatrustJsonCasTestHook:seedTrustDialogRetriesAndPreservesAConcurrentExternalWritersChange— the acceptance-criteriontest: an external writer changes the file once, between fleetd's read and write; asserts the
external writer's change survives in the final file, alongside fleetd's own retried entry.
seedTrustDialogWritesNothingAndWarnsWhenCasRetriesAreExhausted— an external writer changes thefile on every attempt; asserts fleetd writes nothing (file left exactly as the external
writer last left it, no
hasTrustDialogAcceptedfor our cwd) and a WARN naming the cwd/file islogged.
seedTrustDialogWarnsEveryTimeItTargetsTheDefaultClaudeJson— asserts the loud-default WARNfires when
configDiris unset.All new fixtures use
@TempDirfor bothconfigDir/worktreeand (where relevant) a redirecteduser.home, per the project's.claude.json-safety rule;noFixtureSeededTheDefaultClaudeJson(the existing guard from fleetd #258) still passes — untouched and not weakened.
Proof the CAS is a real guard (per the brief's required recipe)
seedTrustDialog(changedif (!Arrays.equals(before, atMove))toif (false && !Arrays.equals(before, atMove))— i.e."just do the move as before"). Re-ran
ClaudeCodeLauncherTest. Both new race tests failed,verbatim:
ClaudeCodeLauncher.javafrom acpcopy taken before the experimental edit; confirmedwith a plain
diffagainst that backup that the restore was byte-for-byte exact (no leftoverexperiment changes made it into the commit).
Build (all commands run unpiped, full output read)
mvn compile(after the CAS change) —BUILD SUCCESS.mvn test-compile(after adding tests) —BUILD SUCCESS.mvn test -Dtest=ClaudeCodeLauncherTest(before the removal experiment) —Tests run: 105, Failures: 0, Errors: 0, Skipped: 0/BUILD SUCCESS.mvn test -Dtest=ClaudeCodeLauncherTest(with the CAS check removed) —Tests run: 105, Failures: 2, Errors: 0, Skipped: 0/BUILD FAILURE(verbatim failure textabove).
mvn test -Dtest=ClaudeCodeLauncherTest—Tests run: 105, Failures: 0, Errors: 0, Skipped: 0/BUILD SUCCESS.mvn clean install—Tests run: 1260, Failures: 0, Errors: 0, Skipped: 0/BUILD SUCCESS. (main was 1257 green;this PR adds exactly 3 tests.)
Safety
Every new fixture uses
@TempDirforconfigDir/worktree, and the one default-path testredirects
user.hometo a@TempDirin atry/finally. No test wrote to any real.claude.json.The existing
noFixtureSeededTheDefaultClaudeJsonguard (fleetd #258) ran and passed, unmodified.Scope note
Out of scope, not investigated: nothing else in
ClaudeCodeLauncher.javawas touched beyondseedTrustDialog's javadoc/body and the two new supporting members (MAX_TRUST_JSON_CAS_ATTEMPTS,parseTrustJsonOrEmpty,trustJsonCasTestHook).Branch:
worker/fleetd-247-342356-5Worktree root:
/Users/dai.ha/LTMS/.bridged-worktrees/63b767-5Files changed:
fleetd/src/main/java/dev/ltms/fleet/member/ClaudeCodeLauncher.java,fleetd/src/test/java/dev/ltms/fleet/member/ClaudeCodeLauncherTest.javaMerged to
mainas34480ce. Full build on the merge commit: 1261 tests, 0 failures, BUILD SUCCESS (main was 1258; this adds exactly the 3 claimed).I read the whole diff rather than taking the report on trust. Notes below, including two things you got right that I want on the record and one I checked separately.
The retry with no backoff is correct, and I want to say why
My first reaction was that five attempts in a tight loop with no sleep is really one attempt — they all run within microseconds, so a busy writer beats all five.
That reasoning is wrong here, and the design is right. The conflicting write is a point event, not a stream. The operator's Claude Code session saves its config now and then, not continuously. By the time your re-read sees different bytes, that write has already landed. So attempt 2 reads the settled file and succeeds. A sleep would only add latency to the spawn path while holding
TRUST_JSON_LOCK.The fail-closed choice is the right one
Exhausting the retries writes nothing. That is what I asked for and you built it, with the reasoning in the code rather than only in the PR. An unseeded member shows the trust dialog and fails to reach an injectable state: visible, logged, recoverable. A stale write over the operator's live config is silent and not recoverable. Fail toward the recoverable outcome.
The javadoc is honest about the residual window
That is exactly right and exactly what should be written down. A future reader will otherwise assume the CAS closed the race and stop looking.
Your "warn once" question — your reading was correct
You interpreted it as one WARN per occurrence, not once per JVM. That is what I meant. Every occurrence is a real write to a real person's home config file. Do not throttle it.
What I checked that you could not
You cannot see
fleetd/fleetd.yaml— it is gitignored. So you could not know whether the new default-path WARN would fire in practice. I checked. It will not, on this host:local,local-directopus,sonnetgx,sol,terra,xfEvery
claude-codeprofile has aconfigDir. The four without one are allopencode, andopencodenever callsseedTrustDialog. So the WARN guards a future misconfiguration rather than a live one — which is a good reason to keep it, not a reason to drop it.That table also confirms the ticket's premise.
opusandsonnetboth point at/Users/dai.ha/.ccs/instances/ltms, which is the lead's own live config directory. The race you fixed is on every ordinary spawn of those two profiles, not an exotic case.The
trustJsonCasTestHookseamStatic mutable state in production code is a cost, and I weighed it. It buys a deterministic test of a real race through the real
spawn()path, instead of a thread-timing test that passes by luck.writeAtomicallywas already made package-visible for the same reason, so this follows an existing decision rather than inventing one. The javadoc says a test must restore it in afinally. Kept.One check I could not run
ide_diagnosticswas unavailable — the fleetd project is not currently open in IntelliJ, and I was not going to change the operator's IDE state to run it. So this was gated onmvn clean installalone. Saying so rather than implying a check I did not run.Verified separately
The full test run touched no real config. After the build,
~/.claude.jsonstill holds 18 projects, 0 junit temp keys,oauthAccountpresent. The #258 guardnoFixtureSeededTheDefaultClaudeJsonran unmodified and passed.Good work, and the honest caveat about the "warn once" wording is the kind of thing that makes a report worth reading.
Pull request closed