Repository navigation
Cloud refusal for the remaining mefor-coord writers: handoff, lane, owner_log, tombstone, hygiene report - #2437
Conversation
In a cloud VM --git-common-dir resolves to the clone's own .git, so seat.ps1, mail.ps1, alloc.ps1 and claim.ps1 would "succeed" into a mefor-coord directory no other session reads. Each now prints one stderr line and exits 3 when CLAUDE_CODE_REMOTE=true. seat.ps1 exempts its two hook paths, -Record and -Prompt, which must exit 0. The SessionStart declare-prompt hook prints a one-line cloud-Builder notice instead of the prompt, stamps nothing, and still exits 0. tests/conftest.py drops an inherited CLAUDE_CODE_REMOTE so the existing pwsh suites keep testing the local path inside a cloud VM. New tests/test_coord_cloud_refusal.py pairs each refusal with a control that takes the normal path.
It hardcoded .venv\Scripts\python.exe, so it failed on a Claude Code cloud VM even with pwsh installed. It now uses .venv/bin/python off Windows, judged by [Environment]::OSVersion so Windows PowerShell 5.1 (no $IsWindows) still reads as Windows, and defaults -Python to python3 there. The install line is unchanged, so both parity tests hold. tests/test_worktree_ensure_venv.py drops its Windows-only gate and builds the fixture's site-packages path per platform, so the ubuntu legs, which carry pwsh, drive the bin/python branch.
Section 5 gains one rule: a Claude Code cloud session is a Builder in its own session, on MEFORORG/MessageFoundry alone and never with a second repository attached, because a multi-repo session loads no project hooks. It opens its PR over REST, since the proxy refuses GraphQL, and hands it to the Lander with the qa label and a PR comment, not mail. Evidence: probe PR 2432 and wiki note 20261018T215116801Z-5lqiqo.
The ubuntu tooling leg errored in the fixture: the template's commit detached background maintenance, whose .git/objects/maintenance.lock vanished while copytree walked it. The template now sets maintenance.auto=false and gc.auto=0, and the per-test copy skips *.lock files.
handoff.ps1, lane.ps1, owner_log.py, tombstone-retired-backlog-allocator.ps1 and hygiene-report.ps1 now print the same one stderr line as PR 2434's four scripts and exit 3 under CLAUDE_CODE_REMOTE=true, path overrides included. Inline copies, not a shared helper: the set spans PowerShell and Python, fixtures copy scripts file by file, and an override bypasses a resolver. tests/test_coord_cloud_refusal.py adds a refusal and a control per script, and a census that fails until every script naming mefor-coord is guarded or classified (user-scope hook, clone-local, reader, mention).
…epeats The /simplify pass: the script label is derived from its path, owner_log.py runs from a cwd with no clone and one remote flag, the text check of the Python copy goes (the behavioural tests already run without pwsh), and the GUARDED comment states the reasons that hold.
CLAUDE.md no longer says every writer refuses: a clone-local lock or cache is still written, and the census says which. handoff.ps1 documents exit 3, the param comments in handoff.ps1 and lane.ps1 name the refusal they mean, and the census tests bytes, so a non-UTF-8 script cannot crash it.
…' into claude/coord-cloud-refusal-rest
|
Lander: this PR now CONFLICTS with main. Engine PR 2434, the PR it was stacked on, squash-merged at 22:23:24Z (d18836b), and GitHub retargeted this PR to main. Please merge main into the branch, or rebase it onto main, then post a QA line that names the new head. That counts as the handover. The Lander enqueues once the required contexts are green. |
…fusal-rest # Conflicts: # CLAUDE.md # tests/test_coord_cloud_refusal.py
|
Brought up to date with
|
|
Lander: enqueued at 283fbd1, pinned. The QA line in the body (code-review at xhigh, tag printed) covers 277913f. The head merges main by a hand resolution, which the Lander reviewed by remerge-diff: it takes this PR's side throughout, and nothing from main is lost. The open findings are filed as vault #3894 in open vault PR 2550 (ledger pass 12), graded as changing no behaviour against main and weakening no control. That grade is a Lander decision under the owner's Driver delegation. All 9 live required contexts are SUCCESS on this head. |
Follow-up to PR 2434, closing the finding its xhigh review left open: other writers into
<git-common-dir>/mefor-coordstill succeeded silently in a Claude Code cloud VM, where that directory belongs to the VM's own clone and no other session reads it.Stacked on PR 2434 (base
claude/xenodochial-euclid-bb7971, merged in atf9e7726e6). Land 2434 first. GitHub retargets this one tomainwhen that branch is deleted.What changed
Five more scripts print PR 2434's refusal line, byte for byte with only the script name changed, and exit 3 when
CLAUDE_CODE_REMOTEistrue. Each refuses before it reads or writes anything, and also refuses when a path override is passed (the same rulemail.ps1follows for-MailRoot):scripts/coord/handoff.ps1handoffs/(every verb)-CoordDirscripts/coord/lane.ps1lanes/(every verb)scripts/coord/owner_log.pyowner-log/OWNER-ITEMS.mdMEFOR_OWNER_LOGscripts/coord/tombstone-retired-backlog-allocator.ps1alloc/backlog/-GitCommonDirscripts/worktree/hygiene-report.ps1hygiene/-OutDirowner_log.pylower-cases the value before comparing, to match PowerShell's case-insensitive-eq.handoff.ps1documents exit 3 next to its 0/1/2. The CLAUDE.md cloud rule now says "at least" and notes that clone-local locks and caches are still written.Why inline copies and not one shared resolver
The brief asked for a shared resolver if the fixtures allowed one. I decided against it, for these reasons:
alloc.ps1,claim.ps1and the tombstone script dot-source nothing. About ten fixtures copyalloc.ps1orclaim.ps1alone (for exampletest_coord_alloc_floor.py,test_claim_shared_registry.py), so adding a dot-source would break them.mail-key.ps1could hold the guard for the four scripts that load it (seat, mail, lane, handoff). That would leave half the copies in a helper and half inline.-CoordDir,-MailRootandMEFOR_OWNER_LOG.owner_log.pyis Python and could not share a PowerShell helper anyway.Two things in
tests/test_coord_cloud_refusal.pykeep the copies equal instead:.ps1/.py/.shunderscripts/coord,scripts/hooksandscripts/worktreethat namesmefor-coordis either guarded or classified.Inventory: writers into mefor-coord, and why each is or is not guarded
install-coordination.ps1wires and which never load in a cloud session:announce-session.ps1,collision_gate.ps1,mail-drain.ps1(andmail-claim.ps1through it),wiki-write-prompt.ps1,seat-record.ps1(throughseat.ps1 -Record),session-context.ps1.mail-watch.ps1is retired from the wiring.lock.ps1, the mutexworktree/new.ps1andspawn.ps1take, andoverlap.ps1's cache of its own answer.claim-reconcile.ps1 -Applywrites throughclaim.ps1 -Release.seat-declare-promptis guarded by PR 2434.precompact-reprimeonly reads.role-card-inject,usage-headroom-injectandshell-guard-dispatchwrite nothing under mefor-coord.scripts/wiki/write.ps1is in another repository and is not changed here. It already refuses a state root that does not exist, and a fresh cloud clone has nomefor-coordunless something creates one.Checks run
ruff check .andruff format --check .: clean.ci.ymllegs (linux engine, win32 engine,testson linux,scripts/asvs): clean.scripts/coord/owner_log.pyis also clean under--strict.tests/test_coord_cloud_refusal.py39 passed, every Windows-gated control included. In the earlier run of the touched suites, 1201 passed: handoff report/pointer/clear, lane statement, alloc tombstone, hygiene report, worktree remove, CLAUDE.md citations/settings contract, tooling partition, ci tooling gate, cp1252, not-deployed, crypto inventory, licence header, glyph. After the review fixes, 264 passed in a narrower rerun.tests/tooling_manifest.txtis unchanged (the test file was already listed by PR 2434).QA
QA: code-review skill at level
xhigh(skill tag printed:xhigh effort -> 10 inline angles -> dedup (no verify) -> sweep -> <=15 findings), againstdocs/REVIEW-STANDARDS.md. It reported 10 findings, fixed 6 in277913f2f, and skipped 4:hygiene-report.ps1refusal still names mefor-coord when-OutDiris given. Skipped because the brief requires the line to stay identical. The comment above the guard gives the real reason.overlap.ps1,claim-adjudicate.ps1andfleet.ps1return a confident empty answer in a cloud VM. This is outside the writer scope and is proposed as a follow-up.scripts/worktreecopy in the template, and_owner_logrepeating_run'ssubprocess.runkeywords./simplifyran first (b827f5eeb).