Skip to content

fix(launcher): claude-pmoves shorthand had no MCP creds — they were never duplicates - #2484

Merged
POWERFULMOVES merged 1 commit into
mainfrom
fix/claude-pmoves-delegate
Aug 8, 2026
Merged

fix(launcher): claude-pmoves shorthand had no MCP creds — they were never duplicates#2484
POWERFULMOVES merged 1 commit into
mainfrom
fix/claude-pmoves-delegate

Conversation

@POWERFULMOVES

Copy link
Copy Markdown
Owner

What

WS2 item 3 was enumerated as "two divergent claude-pmoves.sh — pick the authoritative one." Reading both first, per the plan: they were never duplicates. They share a name and do different, mutually exclusive things.

pmoves/scripts/ (642 B) deploy/provision/ (6523 B)
loads the shared env file
passes --mcp-config=<roster>
selects --agent

Each ends in its own exec claude, so you could have a PMOVES agent or working MCP creds, never both.

Why it matters

make -C pmoves claude-pmoves (Makefile:4936) calls the agent-selecting one:

claude-pmoves: ## Launch Claude Code with PMOVES delivery-agent context
	@bash $(CURDIR)/scripts/claude-pmoves.sh delivery-agent

So the documented way to launch the delivery agent came up with every cred-dependent MCP empty — which is the exact failure the provisioning script's header says it exists to prevent:

Nothing sources env.shared into that env, so cred-dependent MCPs start empty and their tools never surface — the CLI ends up without "access to all".

Reached by the path most likely to be used.

Fix

pmoves/scripts/claude-pmoves.sh becomes a thin delegate. It keeps its positional-agent shorthand and forwards --agent "$AGENT" "$@" to the provisioning launcher.

Passthrough verified rather than assumed:

  • all three exec claude paths in the launcher forward "$@" verbatim (lines 127 / 130 / 134)
  • the launcher consumes no positionals — no getopts, no shift, no $1 handling

So --agent rides through in every branch, including the two degraded ones.

Neither UI changes. claude-pmoves control-agent still works and now inherits creds. deploy/provision/claude-pmoves.sh on its own is still the plain no-agent launch. Nothing is deletedcrush-pmoves.sh and install-claude-pmoves-command.sh, which reference the provisioning script, are untouched.

If the launcher is ever missing, this degrades to the previous behavior (agent loads, creds don't) with a warning rather than failing outright. The point is that the missing half becomes visible instead of silent — that's how this was invisible in the first place.

Verification

$ bash -n pmoves/scripts/claude-pmoves.sh
CLEAN

Reference check before touching anything:

scripts/claude-pmoves      -> pmoves/Makefile:4936            (only caller)
provision/claude-pmoves    -> crush-pmoves.sh,
                              install-claude-pmoves-command.sh

End-to-end: make -C pmoves claude-pmoves should now start the delivery agent and surface the cred-dependent MCP tools. Previously it started the agent with them empty.

Note for the coordination plan

Picking one and deleting the other — the item as written — would have removed a working capability either way. The "divergence" was a name collision hiding a missing composition. Worth correcting in the enumeration so the next reader doesn't retry the delete.

🤖 Generated with Claude Code

… the real launcher

The two claude-pmoves.sh files were never duplicates. They share a name and do
different, mutually exclusive things:

                                  pmoves/scripts   deploy/provision
  loads the shared env file            no                yes
  passes --mcp-config=<roster>         no                yes
  selects --agent                      yes               no

Each ended in its own `exec claude`, so you could have a PMOVES agent OR working
MCP creds, never both. `make -C pmoves claude-pmoves` (Makefile:4936) calls the
agent-selecting one, so the documented way to launch the delivery agent came up
with every cred-dependent MCP empty — the exact failure the provisioning script
was written to prevent, reached by the path most likely to be used.

Fix: pmoves/scripts/claude-pmoves.sh becomes a thin delegate. It keeps its
positional-agent shorthand and forwards `--agent "$AGENT" "$@"` to
deploy/provision/claude-pmoves.sh, which loads the shared env and the normalized
roster. All three of that script's exec paths forward "$@" verbatim (lines
127/130/134) and it consumes no positionals, so the passthrough is clean.

Neither UI changes. `claude-pmoves control-agent` still works and now inherits
creds; `deploy/provision/claude-pmoves.sh` on its own is still the no-agent
launch. Nothing deleted — both entry points stay valid, and crush-pmoves.sh plus
install-claude-pmoves-command.sh (which reference the provisioning one) are
untouched.

If the launcher is missing, this degrades to the previous behavior — agent
loads, creds do not — with a warning, rather than failing outright. The point is
that the missing half becomes visible instead of silent.

Verified: bash -n clean.

WS2 item 3. Note for the coordination plan: this item was enumerated as "two
divergent claude-pmoves.sh — pick the authoritative one". Picking one would have
deleted a working capability; the divergence was a name collision hiding a
missing composition.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

@POWERFULMOVES, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 52 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 27d34ef1-811c-496c-890f-71a617e69d96

📥 Commits

Reviewing files that changed from the base of the PR and between 22c78fb and 2a240ca.

📒 Files selected for processing (1)
  • pmoves/scripts/claude-pmoves.sh

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

POWERFULMOVES added a commit that referenced this pull request Aug 8, 2026
…d-mount class

Two corrections to the handoff doc, made before the enumeration propagates.

RETRACTION. An earlier revision added hf-mcp-server.yml to the sibling-submodule
build list. Wrong. Its context is ../../pmoves/services/hf-mcp-server — a
two-level relative path back INTO pmoves/services/, i.e. a first-party service.
My matcher keyed on a leading `../`, which is not sufficient: the first path
segment has to be checked against .gitmodules. Corrected list is 7 registered
submodules / 8 services, which is z890's original number plus n8n. This is the
same class of error as the "13 of 15" figure it was meant to fix — pattern-
matching a path shape instead of resolving it.

ADDITION. The enumeration covers build contexts only, and those are the less
dangerous half. Five BIND-MOUNT sources also come from submodules
(PMOVES-supabase x4, PMOVES-n8n x1). A missing build context fails loudly; a
missing bind source does not — Docker CREATES it as a directory, so a file mount
silently becomes a directory mount and the container then fails on its own
config. That is the class that took services down on this node, and it is the
reason item 4 is a runbook rather than a code fix.

Also records item 3's outcome: the two claude-pmoves.sh were never duplicates.
One selects an agent and loads nothing; the other loads env + the MCP roster and
selects no agent. Each ends in its own `exec claude`, so they are mutually
exclusive, and `make -C pmoves claude-pmoves` took the path with no creds.
Picking one — the item as written — would have deleted a working capability
either way. Fixed by delegation in #2484.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
POWERFULMOVES added a commit that referenced this pull request Aug 8, 2026
…lindness claim

Review pass on the claim + handoff. All six findings were valid; none were
cosmetic.

TTL. The claim had no expiry, so an abandoned branch would reserve these items
indefinitely with no way for another node to tell when reclaiming is safe.
Added: TTL 72h, expires 2026-08-11T13:00:00Z.

OWNERSHIP COUNT. The entry said "five items claimed" while the body enumerated
items 2-5 and the next paragraph assigned item 1 to z890 — ambiguous in exactly
the file that exists to prevent collisions. Now states WS2 has five items and
4090 reserves FOUR of them, with item 1 named as z890's.

PROVENANCE. "Everything re-verified via git show against origin/main" was not
true of the whole document: it also carries GitHub Actions run results and node
runtime observations, neither of which git show can produce. Now split three
ways — repository content via git show, CI behavior via gh api on run records,
node runtime state via docker inspect / git submodule status on THIS node only
(single-node observation, not a fleet claim).

CI-BLINDNESS CLAIM WAS WRONG. The doc said CI cannot catch the submodule gap
because workflows check out with `submodules: recursive`. They do not check out
submodules at all (validate-dockerfile-paths-ratchet.yml:54,
validate-composes-ratchet.yml:41). They pass because
validate_dockerfile_paths.py explicitly excludes sibling-submodule targets —
"external repos the ratchet can't statically check" — and because
validate-composes never inspects bind sources. A known, accepted blind spot
rather than an accidental one. That is a better argument for the runbook, not a
worse one, and it is now the one the doc makes.

TRIGGER ROW. Correction 3 said `issue_comment` and `push` were both correct and
only `pull_request_review` was wrong. `push` resolves from the PUSHED ref, so
that was half-right; my first fix left the other half standing. Now a three-row
table with the two halves' provenance distinguished — the pull_request_review
correction is empirical from runs 31257145963 / 31257247543, the push correction
is Actions semantics from review and is stated as behavior rather than as
something measured here.

Plus: item 3's outcome recorded (the two claude-pmoves.sh were never duplicates,
fixed by delegation in #2484), and MD040 language identifiers on three fenced
blocks.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
POWERFULMOVES added a commit that referenced this pull request Aug 8, 2026
…n the enumeration (#2482)

* docs(ws2): claim the tooling-audit workstream + correct three items in the enumeration

z890's coordination plan assigns Workstream 2 to this node by affinity. This
claims it — bounded to five enumerated items, not to "tooling" as an area — and
records the corrections the enumeration needs before either node builds on it.

Corrections, all verified against origin/main @ 22c78fb:

  (a) 2 leaking copies, not 4 drifting implementations. Both shell scripts
      already carry the #2473 buildx fix (pmoves-disk-cleanup.sh:52,
      docker-fleet-cleanup.sh:43). Only infra.mk:89 and
      ci-expedition/SKILL.md:69 lack it.

  (b) 4090's own earlier "13 of 15 fork services" figure was WRONG — it
      double-counted services re-declared across the generated split overlays.
      Correct count is 7 unique sibling-context builds in docker-compose.yml,
      plus n8n, plus hf-mcp-server.yml which the handed-over list missed.

  (c) The ci-expedition fix is narrower than "PR head, not default". In that
      row `issue_comment` and `push` are correct; only `pull_request_review`
      is wrong. Surgical edit, not a row rewrite.

  Plus: the four .worktrees/* skill copies are checkouts of the same tracked
  file, so fixing main fixes them all — one item off the list.

Evidence for (c) is empirical, from the #2479 fix rather than from docs: run
31257145963 (PR head pre-fix, main already fixed) ran the OLD workflow and
failed at checkout; run 31257247543 (head rebased) ran the NEW workflow and went
green. Default-branch resolution predicts the opposite.

Not claimed and deliberately left alone: infra.mk:89 (z890's declared sub-fix),
WS1/WS3/WS4. Recommends Mavis-5090 for WS4-B, since the "rooms pull portals"
model is their open OpenRoom slice-2 claim.

The handoff doc also carries a WS3-relevant finding: AGNOTE4482PHI.t1.md was
damaged three times in a row by stale-base merges (#2437 markers, #2450 deleting
six entries, a corrupted duplicate). A deletion is a valid diff, so nothing
catches it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs(ws2): retract hf-mcp-server from the submodule list, add the bind-mount class

Two corrections to the handoff doc, made before the enumeration propagates.

RETRACTION. An earlier revision added hf-mcp-server.yml to the sibling-submodule
build list. Wrong. Its context is ../../pmoves/services/hf-mcp-server — a
two-level relative path back INTO pmoves/services/, i.e. a first-party service.
My matcher keyed on a leading `../`, which is not sufficient: the first path
segment has to be checked against .gitmodules. Corrected list is 7 registered
submodules / 8 services, which is z890's original number plus n8n. This is the
same class of error as the "13 of 15" figure it was meant to fix — pattern-
matching a path shape instead of resolving it.

ADDITION. The enumeration covers build contexts only, and those are the less
dangerous half. Five BIND-MOUNT sources also come from submodules
(PMOVES-supabase x4, PMOVES-n8n x1). A missing build context fails loudly; a
missing bind source does not — Docker CREATES it as a directory, so a file mount
silently becomes a directory mount and the container then fails on its own
config. That is the class that took services down on this node, and it is the
reason item 4 is a runbook rather than a code fix.

Also records item 3's outcome: the two claude-pmoves.sh were never duplicates.
One selects an agent and loads nothing; the other loads env + the MCP roster and
selects no agent. Each ends in its own `exec claude`, so they are mutually
exclusive, and `make -C pmoves claude-pmoves` took the path with no creds.
Picking one — the item as written — would have deleted a working capability
either way. Fixed by delegation in #2484.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs(ws2): add claim TTL, split evidence provenance, correct the CI-blindness claim

Review pass on the claim + handoff. All six findings were valid; none were
cosmetic.

TTL. The claim had no expiry, so an abandoned branch would reserve these items
indefinitely with no way for another node to tell when reclaiming is safe.
Added: TTL 72h, expires 2026-08-11T13:00:00Z.

OWNERSHIP COUNT. The entry said "five items claimed" while the body enumerated
items 2-5 and the next paragraph assigned item 1 to z890 — ambiguous in exactly
the file that exists to prevent collisions. Now states WS2 has five items and
4090 reserves FOUR of them, with item 1 named as z890's.

PROVENANCE. "Everything re-verified via git show against origin/main" was not
true of the whole document: it also carries GitHub Actions run results and node
runtime observations, neither of which git show can produce. Now split three
ways — repository content via git show, CI behavior via gh api on run records,
node runtime state via docker inspect / git submodule status on THIS node only
(single-node observation, not a fleet claim).

CI-BLINDNESS CLAIM WAS WRONG. The doc said CI cannot catch the submodule gap
because workflows check out with `submodules: recursive`. They do not check out
submodules at all (validate-dockerfile-paths-ratchet.yml:54,
validate-composes-ratchet.yml:41). They pass because
validate_dockerfile_paths.py explicitly excludes sibling-submodule targets —
"external repos the ratchet can't statically check" — and because
validate-composes never inspects bind sources. A known, accepted blind spot
rather than an accidental one. That is a better argument for the runbook, not a
worse one, and it is now the one the doc makes.

TRIGGER ROW. Correction 3 said `issue_comment` and `push` were both correct and
only `pull_request_review` was wrong. `push` resolves from the PUSHED ref, so
that was half-right; my first fix left the other half standing. Now a three-row
table with the two halves' provenance distinguished — the pull_request_review
correction is empirical from runs 31257145963 / 31257247543, the push correction
is Actions semantics from review and is stated as behavior rather than as
something measured here.

Plus: item 3's outcome recorded (the two claude-pmoves.sh were never duplicates,
fixed by delegation in #2484), and MD040 language identifiers on three fenced
blocks.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
@POWERFULMOVES
POWERFULMOVES merged commit a193591 into main Aug 8, 2026
20 checks passed
@POWERFULMOVES
POWERFULMOVES deleted the fix/claude-pmoves-delegate branch August 8, 2026 14:25
POWERFULMOVES added a commit that referenced this pull request Aug 9, 2026
…reign RELEASE lines (#2498)

* docs(agnote): WS2 RELEASE + lane sweep — evidence for owners, no foreign RELEASE lines

Two entries.

RELEASE — WS2 (z890's coordination plan) is complete inside its 72h TTL. Eight
PRs merged: #2482 claim+handoff+corrections, #2483 ci-expedition skill, #2484
claude-pmoves delegation, #2485 submodule gap runbook, #2486 up-* inventory,
#2488 validate-command-anchors, #2494 first-contact + guard routing table, #2495
Danger Room handoff to SPARK. pmoves/mk/infra.mk untouched throughout — z890's
#2480, no collision.

The entry records what the audit found BEYOND its enumerated items, because that
is the reusable part: a gate can advertise coverage it does not have (three
separate instances, each caught by review rather than by me); the always-loaded
orientation file misdirects first contact; the guard's own routing table has two
dead roads; and patterns.yaml is the pattern worth generalizing while
pre-tool.sh duplicates 5 of its entries minus the affordance.

It also records four corrections to my own prior work — the wrong 13-of-15
figure, the retracted hf-mcp-server entry, the dangerous first up-* retire list,
and the yt-cookies pair that was never a duplicate. A closeout that only lists
wins is not a closeout.

NOTE — lane sweep. 115 CLAIM against 119 RELEASE. Four Mavis lanes verified
shipped and missing only a RELEASE (harness v0 #2437/#2443/#2450, multi-fork
follow-ups #2477, OpenRoom slice 2, creative-pipeline v0). Four older lanes
still open with no PR cited and nothing found merged, now 8-10 days. Mine that
are correctly still open: #2446 draft, #2468 held for review, and the SPARK
handoff awaiting its CLAIM.

ZERO RELEASE lines written on another agent's behalf — verified in the diff.
Those lanes are Mavis's to close under Village Rule; this records evidence so
they can close from it rather than from memory. Kept distinct from the
KIMI-SPARK / CRUSH stale claims, which need a release OR re-claim — different
category, and conflating them would make the ping inaccurate.

Verified: make -C pmoves validate-command-anchors passes.

Note for follow-up: dogfooding this entry surfaced a real false-positive
generator in MAKE_CITE_RE — `-C \S+` swallows a closing backtick, so prose that
backticks "make -C pmoves" alone captures the following word as a target. Fixed
separately, not folded in here.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs(agnote): state the counting method, and stop treating merge as acceptance

Three review findings, all correct, all mine.

1. THE TOTALS WERE NOT REPRODUCIBLE. I published "115 CLAIM against 119 RELEASE"
   with no method attached. Recounting three ways on the same file:

     anchored bullet rows      115 / 119   <- what I published
     token anywhere in text    284 / 221   (prose mentions)
     timestamped, no anchor    116 / 119

   and the 2026-08-07 sweep reported 121 / 115 on a fourth. None of these is
   wrong; a bare number with no pattern is. The entry now states the exact regex,
   lists what the other methods give, and says to treat the ratio as a rough
   signal rather than a metric — the per-lane table is the checkable part.

2. MERGE IS NOT RUNTIME ACCEPTANCE. I listed OpenRoom slice 2 under "verified
   shipped, missing only a RELEASE" on the strength of #2437 merging. That lane
   was claimed against six handoff priorities with room-level acceptance, and
   #2437 is scaffold plus iframe wiring. Merging it does not demonstrate the
   rooms render.

3. Same for line 1723: it covers three deliverables including fork-side
   consumers, and #2477 merging in PMOVES.AI says nothing about whether the fork
   consumers landed.

Both are now "merged, acceptance unverified" rather than ready-to-release, with
the reasoning stated so the owner closes from runtime evidence instead of from
my table.

This is precisely the error the register exists to prevent, made by the sweep
that exists to catch it — which is worth leaving visible rather than quietly
correcting. Every gate I shipped this week was weaker than advertised until
something proved it could say no; this one was a bookkeeping claim that had not
been asked to reproduce itself.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
POWERFULMOVES added a commit that referenced this pull request Aug 14, 2026
…P creds silently empty

`install-claude-pmoves-command.sh` installs the command by two routes: an rc
function holding the launcher's absolute path, and a ~/.local/bin/claude-pmoves
symlink for shells that don't source the rc. The two behaved differently.

    ROOT="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")/../.." && pwd)"

Via the rc function BASH_SOURCE[0] is the real path and ROOT is the repo. Via the
PATH symlink BASH_SOURCE[0] is ~/.local/bin/claude-pmoves, so dirname/../.. is
$HOME — and both the shared env file and .claude/mcp.json miss.

It failed as a WARN, not an error: the launcher printed the warning and exec'd
claude anyway, producing a session that looks healthy with every cred-dependent
MCP dark. Same failure class as #2484, different entrypoint.

Two changes:

1. Walk symlinks before deriving ROOT. Pure-bash loop handling relative link
   targets, no readlink -f dependency.

2. Validate ROOT against a marker file instead of trusting the computation. The
   sibling launchers already do this — crush-pmoves tests
   $candidate/pmoves/Makefile, pmoves-mini tests
   $candidate/pmoves/tools/mini_cli.py, and both reject a failing candidate.
   That validation, not their path arithmetic, is why they survived the same
   trap. A launcher whose whole job is loading repo-relative config must not
   proceed when it cannot find the repo, so this now hard-exits with a fix hint.
   Adds a PMOVES_REPO_ROOT override as the escape hatch.

Verified on B850 (Knuckles), all four routes:

  PATH symlink route          loaded 373 vars   (was: 0, two WARNs)
  real-path route             loaded 373 vars
  PMOVES_REPO_ROOT override   honored
  bogus root (=/tmp)          exit 1 + actionable error

The surrounding launcher design was audited against the Claude Code docs in the
same pass and is correct as written: --mcp-config is variadic (<configs...>) so
the `=` form is required; omitting --strict-mcp-config is what keeps the per-node
.mcp.json merged; ${VAR} in the roster does resolve from Claude Code's own process
env, which is why sourcing the shared env before exec is the right mechanism; and
.claude/mcp.json is not a scope Claude reads on its own, so the explicit
--mcp-config really is required. ROOT derivation was the only defect.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
POWERFULMOVES added a commit that referenced this pull request Aug 15, 2026
…P creds silently empty

`install-claude-pmoves-command.sh` installs the command by two routes: an rc
function holding the launcher's absolute path, and a ~/.local/bin/claude-pmoves
symlink for shells that don't source the rc. The two behaved differently.

    ROOT="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")/../.." && pwd)"

Via the rc function BASH_SOURCE[0] is the real path and ROOT is the repo. Via the
PATH symlink BASH_SOURCE[0] is ~/.local/bin/claude-pmoves, so dirname/../.. is
$HOME — and both the shared env file and .claude/mcp.json miss.

It failed as a WARN, not an error: the launcher printed the warning and exec'd
claude anyway, producing a session that looks healthy with every cred-dependent
MCP dark. Same failure class as #2484, different entrypoint.

Two changes:

1. Walk symlinks before deriving ROOT. Pure-bash loop handling relative link
   targets, no readlink -f dependency.

2. Validate ROOT against a marker file instead of trusting the computation. The
   sibling launchers already do this — crush-pmoves tests
   $candidate/pmoves/Makefile, pmoves-mini tests
   $candidate/pmoves/tools/mini_cli.py, and both reject a failing candidate.
   That validation, not their path arithmetic, is why they survived the same
   trap. A launcher whose whole job is loading repo-relative config must not
   proceed when it cannot find the repo, so this now hard-exits with a fix hint.
   Adds a PMOVES_REPO_ROOT override as the escape hatch.

Verified on B850 (Knuckles), all four routes:

  PATH symlink route          loaded 373 vars   (was: 0, two WARNs)
  real-path route             loaded 373 vars
  PMOVES_REPO_ROOT override   honored
  bogus root (=/tmp)          exit 1 + actionable error

The surrounding launcher design was audited against the Claude Code docs in the
same pass and is correct as written: --mcp-config is variadic (<configs...>) so
the `=` form is required; omitting --strict-mcp-config is what keeps the per-node
.mcp.json merged; ${VAR} in the roster does resolve from Claude Code's own process
env, which is why sourcing the shared env before exec is the right mechanism; and
.claude/mcp.json is not a scope Claude reads on its own, so the explicit
--mcp-config really is required. ROOT derivation was the only defect.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
POWERFULMOVES added a commit that referenced this pull request Aug 15, 2026
…CP creds silently empty (#2538)

* fix(launcher): claude-pmoves via PATH symlink resolved ROOT=$HOME, MCP creds silently empty

`install-claude-pmoves-command.sh` installs the command by two routes: an rc
function holding the launcher's absolute path, and a ~/.local/bin/claude-pmoves
symlink for shells that don't source the rc. The two behaved differently.

    ROOT="$(cd "$(dirname "${BASH_SOURCE[0]:-$0}")/../.." && pwd)"

Via the rc function BASH_SOURCE[0] is the real path and ROOT is the repo. Via the
PATH symlink BASH_SOURCE[0] is ~/.local/bin/claude-pmoves, so dirname/../.. is
$HOME — and both the shared env file and .claude/mcp.json miss.

It failed as a WARN, not an error: the launcher printed the warning and exec'd
claude anyway, producing a session that looks healthy with every cred-dependent
MCP dark. Same failure class as #2484, different entrypoint.

Two changes:

1. Walk symlinks before deriving ROOT. Pure-bash loop handling relative link
   targets, no readlink -f dependency.

2. Validate ROOT against a marker file instead of trusting the computation. The
   sibling launchers already do this — crush-pmoves tests
   $candidate/pmoves/Makefile, pmoves-mini tests
   $candidate/pmoves/tools/mini_cli.py, and both reject a failing candidate.
   That validation, not their path arithmetic, is why they survived the same
   trap. A launcher whose whole job is loading repo-relative config must not
   proceed when it cannot find the repo, so this now hard-exits with a fix hint.
   Adds a PMOVES_REPO_ROOT override as the escape hatch.

Verified on B850 (Knuckles), all four routes:

  PATH symlink route          loaded 373 vars   (was: 0, two WARNs)
  real-path route             loaded 373 vars
  PMOVES_REPO_ROOT override   honored
  bogus root (=/tmp)          exit 1 + actionable error

The surrounding launcher design was audited against the Claude Code docs in the
same pass and is correct as written: --mcp-config is variadic (<configs...>) so
the `=` form is required; omitting --strict-mcp-config is what keeps the per-node
.mcp.json merged; ${VAR} in the roster does resolve from Claude Code's own process
env, which is why sourcing the shared env before exec is the right mechanism; and
.claude/mcp.json is not a scope Claude reads on its own, so the explicit
--mcp-config really is required. ROOT derivation was the only defect.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(launcher): fix the OTHER two launchers too, and guard the class with a test

Review of the first cut found the fix was scoped to one of three files carrying
the same code. Addresses all four findings.

1. TWO MIRRORS WERE STILL BROKEN (the important one)
   deploy/provision/crush-pmoves.sh and pmoves/scripts/claude-pmoves.sh carried
   the identical unvalidated `dirname "$BASH_SOURCE"` root derivation. Anyone
   symlinking either onto PATH reproduced the exact bug this branch fixes. Worse,
   the original commit message justified itself by citing "the siblings already
   validate" — pointing at pmoves/scripts/crush-pmoves (which does) while its
   deploy/provision twin (which does not) sat one directory away. A rationale
   that was never checked is as dangerous as a claim that was never checked.

   All three now carry a byte-identical walk.

2. PMOVES_REPO_ROOT COLLIDED WITH AN EXISTING CONSUMER
   pmoves/services/creator-operator/config.py:15 already reads PMOVES_REPO_ROOT.
   A developer exporting it to point creator-operator at a container path would
   silently redirect the launcher — and, because the new marker check then fails,
   hard-exit a launcher whose own location resolved perfectly. Renamed to
   PMOVES_LAUNCHER_ROOT.

3. HARD EXIT REMOVED A DOCUMENTED DEGRADED PATH
   The file's own header advertises `alias claude=/path/to/claude-pmoves.sh`.
   After a repo move the PATH symlink dangles, ROOT resolves empty, and the gate
   made `claude` itself unusable on that node — worse than the credless launch it
   replaced. It also printed an empty path in the error. Now: an explicit,
   existing PMOVES_ENV_SHARED is honoured with a warning about the skipped
   roster; only the no-override case hard-fails, and unresolved roots print
   `<unresolved>` rather than nothing.

4. CDPATH COULD CORRUPT THE RESOLVED PATH
   `dirname` yields a bare relative path when the script is invoked relatively,
   and `cd` consults CDPATH for such arguments — jumping elsewhere AND echoing
   the destination, embedding a newline in the captured directory. Now
   `CDPATH= cd -P -- ...` throughout.

GUARD: deploy/provision/tests/test-launcher-root-resolution.sh
   Fixing one file and missing two is the failure this branch is about, so the
   drift is now detectable rather than trusted to review. The test asserts the
   symlink walk is byte-identical across all three launchers, that none
   reintroduces PMOVES_REPO_ROOT (comments excluded — the files legitimately name
   it to explain the avoidance), and that each resolves correctly when invoked
   through both an absolute symlink and a relative symlink chain.

   12/12 pass. Negative control: reverting one launcher to the old walk fails
   with "walk DIFFERS ... fix all three together", so the guard is proven to
   catch the thing it exists for.

Verified live on B850 through the installed PATH symlink: 373 vars loaded; still
373 under a hostile CDPATH; still 373 with PMOVES_REPO_ROOT=/tmp exported (no
longer hijacks); PMOVES_LAUNCHER_ROOT=/tmp exits 1 with an actionable message.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
POWERFULMOVES added a commit that referenced this pull request Aug 16, 2026
…sured not assumed

The handoff and the mesh plan have read as open work since 2026-08-08 while four of
the five enumerated items shipped and were released on 2026-08-09. A reader arriving
at either document had no way to tell.

Adds a STATUS block to the handoff with per-item state re-measured against
origin/main @ 017de53 today, rather than carried forward from the release text:

  item 2 (ci-expedition trigger row)  DONE  #2483
  item 3 (two claude-pmoves.sh)       DONE  #2484
  item 4 (up-* sprawl)                DONE  #2486 + #2523 — now 88/88, was 89/88
  item 5 (submodule build gap)        DONE  #2485
  item 1 (cleanup de-duplication)     PARTIAL — leak closed, 3 impls remain

Two figures in the record were wrong and are corrected here rather than repeated:
the retirement list is 8 both-zero targets, not 11, and the merged inventory flags
two of those eight as load-bearing. Mesh Stage C's "no such file exists" correction
is itself now stale — the value-engine spec is open as PR #2516, so Stage C is
blocked on a merge rather than on an authorship gap.

Stage A's figures are re-measured and hold (98 MB beats, 126 MB CATACLYSM, 38 PNGs),
but the table under-scopes the job: the tracked surface is 75 blobs over 1 MiB
totalling 259 MB, and the largest single file appears in no row.

Method note recorded in the doc: git cherry reported four of these branches as
unmerged because they were squash-merged, which rewrites patch-ids. File contents in
origin/main settled it. The register conflict this PR carried was resolved
append-only — the diff against main is 22/0, 159/0, 149/0, zero deletions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
POWERFULMOVES added a commit that referenced this pull request Aug 18, 2026
…sured not assumed

The handoff and the mesh plan have read as open work since 2026-08-08 while four of
the five enumerated items shipped and were released on 2026-08-09. A reader arriving
at either document had no way to tell.

Adds a STATUS block to the handoff with per-item state re-measured against
origin/main @ 017de53 today, rather than carried forward from the release text:

  item 2 (ci-expedition trigger row)  DONE  #2483
  item 3 (two claude-pmoves.sh)       DONE  #2484
  item 4 (up-* sprawl)                DONE  #2486 + #2523 — now 88/88, was 89/88
  item 5 (submodule build gap)        DONE  #2485
  item 1 (cleanup de-duplication)     PARTIAL — leak closed, 3 impls remain

Two figures in the record were wrong and are corrected here rather than repeated:
the retirement list is 8 both-zero targets, not 11, and the merged inventory flags
two of those eight as load-bearing. Mesh Stage C's "no such file exists" correction
is itself now stale — the value-engine spec is open as PR #2516, so Stage C is
blocked on a merge rather than on an authorship gap.

Stage A's figures are re-measured and hold (98 MB beats, 126 MB CATACLYSM, 38 PNGs),
but the table under-scopes the job: the tracked surface is 75 blobs over 1 MiB
totalling 259 MB, and the largest single file appears in no row.

Method note recorded in the doc: git cherry reported four of these branches as
unmerged because they were squash-merged, which rewrites patch-ids. File contents in
origin/main settled it. The register conflict this PR carried was resolved
append-only — the diff against main is 22/0, 159/0, 149/0, zero deletions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
POWERFULMOVES added a commit that referenced this pull request Aug 18, 2026
… lane-review claim (#2481)

* docs(coord): 4090 tooling-audit handoff + mesh live-layer plan + z890 lane-review claim

Post-13-PR coherence pass (z890 side), three coordination artifacts:

- handoffs/tooling-audit-4090-drift-enumeration-2026-08-08.md — hands the
  ops/tooling drift audit to 4090 (node affinity: Archon/compose/fork/CI depth).
  Enumerates the 4-place cleanup drift, ci-expedition SKILL.md:29 pull_request_review
  row error, two divergent claude-pmoves.sh (6523 vs 642 B), ~87 up-* target sprawl,
  and the sibling-submodule build gap (13/15 fork services, invisible in diffs).
  All figures verified locally.

- specs/mesh-live-collaboration-layer-plan-2026-08-08.md — mesh/JuiceFS reframed as
  the live collaboration layer, NOT a privacy purge. Repo stays public; history-rewrite
  premise dropped. Stage A large-binary offload actionable; Stages B/C are follow-ups.

- AGNOTE4482PHI.t1.md — z890 CLAIM+RELEASE for the cleanup + coordination + lane-review
  work; pings KIMI-SPARK (VSS) + CRUSH (cipher Phase B) stale claims for release/re-claim;
  DROPs the z890 voice/#2317 pickup (left available); records WS1 worktree cleanup outcome
  (20 removed, main freed, 2 held with cause).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(agnote): resolve the 3 control items — sso-apps dup-of-main, z890-reconciled removed, crawl→Archon

Operator decisions recorded:
- pmoves-sso-apps: no relocation — survivors duplicate main; unique artifacts
  were already lost in the earlier partial remove. Dead remnant left for manual delete.
- pmoves-z890-reconciled: removed (only local CHIT secrets-manifest regen drift).
- archon.crawl.*: delegated to Archon (has live NATS visibility) to observe
  request/result subjects and decide retire-vs-keep.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* docs(coord): address 3 Codex threads — build-gap count, up-* call sites, dead spec path

All three were correct, including the one the trim classifier labelled
false-positive. That is the third time the classifier has mislabelled a real
Codex finding, so every thread was read by hand rather than trusted to the
badge.

T1 — "13 of 15" fork services (classified false-positive; it is real)

The figure double-counted the same services re-declared across split overlays.
Verified unique sibling-context builds: 7 submodules / 8 services. Left the old
number visible with an inline correction rather than silently replacing it, so
the audit owner knows which number to stop chasing. Codex's pointer to the
existing correction at AGNOTE4482PHI.t1.md:1787 was accurate.

T2 — up-* "consolidate/retire superseded siblings"

`up-core-*` is a dependency chain, not three siblings: up-core-gpu invokes
up-core-capable (Makefile:2567) which invokes up-core-hardened (:2562). Codex's
line references check out exactly. Retiring the "superseded" sibling would break
the canonical capable and GPU bring-up roads.

This is the same trap the first pass fell into — up-core-hardened scored zero
references because the grep excluded makefiles to skip *definitions*, and
excluded *call sites* with them. Reframed the section as inventory candidates
with the call-site caveat stated, and flagged up-cipher-nobuild as a keeper
regardless of count (it is the existing workaround for item 5's build gap).

T3 — value-engine-domino-v0-spec-2026-08-07.md does not exist

Confirmed: no such file at this commit, no renamed equivalent, nothing in a
repo-wide filename or content search. The thesis exists only in a node-local
memory that whoever picks up Stage C cannot read. Rewritten so writing that spec
is Stage C's first task rather than a reference it leans on — a dead path is
worse than no path, because it reads as though requirements exist somewhere.

Corrections are attributed inline as 4090-at-merge rather than folded into
z890's text, since the surrounding documents are theirs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs(coord): address 9 CodeRabbit threads — stale figures marked, contracts named, register append-only

All nine checked against the tree. One resolved a live disagreement between two
reviewers by finding both were right.

up-* count: 89 AND 88 are both correct

CodeRabbit said 88, this doc said 89. `grep -c '^up-[a-z0-9-]*:'` gives 89
definitions; `sort -u` gives 88 names. The gap is `up-openroom`, defined TWICE
in pmoves/Makefile (:1279 and :1335) with byte-identical recipes — make emits an
overriding-recipe warning and the second wins. Behaviourally harmless, but it is
now the single safest item on the consolidation list, and it only surfaced
because two counts disagreed.

Cleanup drift is historical — leak count is 0

The table read as live state. Two copies already carried #2473 when it was
written, infra.mk was fixed by z890 in #2480, and the ci-expedition copy shipped
with item 2. Marked historical with the current status stated, so the audit
owner does not re-audit a closed leak.

claude-pmoves.sh — keep both, do not pick one

CodeRabbit verified the two are not duplicates: deploy/provision loads
env.shared + the MCP roster, pmoves/scripts provides positional-agent selection.
"Pick the authoritative one ... or delete" would have lost a capability either
way. Reworded to delegate-not-delete.

Sibling-build list — dropped tokenism-ui

The section contradicted the corrected list added earlier in the same document.
tokenism-ui's context is a nested `./`, not a sibling submodule path, so it
builds fine from a worktree. Now restates the verified 7 + n8n = 8 explicitly in
both places so they cannot drift apart again.

Two OPEN DECISION markers, because a plan that reads executable and is not is
worse than one that says so

  - Stage A: git-lfs pointer vs remove-from-tree are not interchangeable — they
    differ in clone behaviour, CI cost, offline-node availability and rollback.
    Stage A cannot start until each table row names one, plus bootstrap/restore.
  - Stage B: "git holds source, mesh holds generated" is split-brain until
    writer, update trigger, atomic publish, read-only status, and divergence
    repair are named. docs-reconcile-check has no notion of a mesh copy, so it
    cannot detect divergence — extending it is Stage B work, not an assumption.

Stage A inventory made reproducible: audit commands, observation date, and an
explicit inclusion rule (tracked files only, submodules out of scope). Same
unreproducible-number defect the WS2 register sweep was called out for.

archon.crawl.* status recorded as PARKED + delegated to Archon, with the note
that .claude/context/nats-subjects.md:1776-1777 still carries no status marker
and belongs to whoever executes the retirement — this plan does not pre-empt it.

Register: appended an UPDATE row rather than editing z890's entry

CodeRabbit asked for supersession markers and missing branch/PR metadata on an
append-only file. Both delivered as a new signed 4090 row carrying the corrected
figures, the ownership correction (4090 claimed items 2-5, not 1-5; item 1 was
z890's), and the branch/PR identifiers the original omitted. z890's text is
byte-unchanged: 4 insertions, 0 deletions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

* docs(coord): close the WS2 handoff record — 4 of 5 items shipped, measured not assumed

The handoff and the mesh plan have read as open work since 2026-08-08 while four of
the five enumerated items shipped and were released on 2026-08-09. A reader arriving
at either document had no way to tell.

Adds a STATUS block to the handoff with per-item state re-measured against
origin/main @ 017de53 today, rather than carried forward from the release text:

  item 2 (ci-expedition trigger row)  DONE  #2483
  item 3 (two claude-pmoves.sh)       DONE  #2484
  item 4 (up-* sprawl)                DONE  #2486 + #2523 — now 88/88, was 89/88
  item 5 (submodule build gap)        DONE  #2485
  item 1 (cleanup de-duplication)     PARTIAL — leak closed, 3 impls remain

Two figures in the record were wrong and are corrected here rather than repeated:
the retirement list is 8 both-zero targets, not 11, and the merged inventory flags
two of those eight as load-bearing. Mesh Stage C's "no such file exists" correction
is itself now stale — the value-engine spec is open as PR #2516, so Stage C is
blocked on a merge rather than on an authorship gap.

Stage A's figures are re-measured and hold (98 MB beats, 126 MB CATACLYSM, 38 PNGs),
but the table under-scopes the job: the tracked surface is 75 blobs over 1 MiB
totalling 259 MB, and the largest single file appears in no row.

Method note recorded in the doc: git cherry reported four of these branches as
unmerged because they were squash-merged, which rewrites patch-ids. File contents in
origin/main settled it. The register conflict this PR carried was resolved
append-only — the diff against main is 22/0, 159/0, 149/0, zero deletions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant