diff --git a/bin/fm-remote-home-seed.sh b/bin/fm-remote-home-seed.sh index d1a434f1f24..e5bad4779bb 100755 --- a/bin/fm-remote-home-seed.sh +++ b/bin/fm-remote-home-seed.sh @@ -22,6 +22,7 @@ # # Known provisioning failure rolls the registry back. SSH status 255 preserves # the route and any newly scaffolded brief because completion is unknown and a same-route rerun converges. +# The remote charter copy replaces the parent's local parent-channel and steering-inbox paths with the remote home paths the route actually uses. set -eu SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" @@ -147,11 +148,18 @@ REG_EXISTED=0 [ -f "$REG" ] && { cp "$REG" "$TMP/registry.before"; REG_EXISTED=1; } # Keep the parent charter as its durable source, but publish a remote copy whose -# status path is the remote append-only relay log rather than a local Mac path. +# parent-channel and steering-inbox paths are remote-home paths rather than local +# parent-home paths. The steering-inbox path below has to stay the exact inbox +# bin/fm-remote-secondmate-control.sh's cmd_send writes records into and rings +# the doorbell for (its CONTROL_STATE); a charter naming any other path leaves +# the mate reading an inbox nothing delivers to. PARENT_STATUS="$STATE/$ID.status" REMOTE_STATUS="$REMOTE_HOME/state/parent-replies.status" +PARENT_INBOX="$STATE/$ID.inbox" +REMOTE_INBOX="$REMOTE_HOME/state/parent-route/$ID.inbox" while IFS= read -r line || [ -n "$line" ]; do - printf '%s\n' "${line//"$PARENT_STATUS"/"$REMOTE_STATUS"}" + line=${line//"$PARENT_STATUS"/"$REMOTE_STATUS"} + printf '%s\n' "${line//"$PARENT_INBOX"/"$REMOTE_INBOX"}" done < "$BRIEF" > "$TMP/charter.remote" PROJECTS_CSV= diff --git a/docs/remote-secondmates.md b/docs/remote-secondmates.md index bf8f044e0e4..46c811226d2 100644 --- a/docs/remote-secondmates.md +++ b/docs/remote-secondmates.md @@ -179,7 +179,8 @@ FM_HOME= bin/fm-send.sh fm- '' ``` The [`fm-send.sh` header](../bin/fm-send.sh) owns the exact delivery-status contract. -A routed request is delivered as a durable record in the remote home's steering inbox plus a best-effort doorbell, never by typing the payload into the pane; exit 0 means the record durably exists. +A routed request is delivered as a durable record in the remote home's `state/parent-route/.inbox` steering inbox plus a best-effort doorbell, never by typing the payload into the pane; exit 0 means the record durably exists. +The seeded remote charter names that same host-local inbox, so the mate acts on the record the doorbell rang for instead of an unreachable inbox path in the parent's home; re-running `fm-remote-home-seed.sh` republishes that charter into an existing home. Every remote transport attempt is bounded by `FM_SEND_REMOTE_BUDGET`; that header owns the setting's default and validation contract. An unconfirmed SSH transport (exit 255) is retried identically once, while a budget expiry is not retried because completion is unknown; either outcome preserves this ordinary reply-bearing request's pending-reply expectation for the record that may have landed. If delivery remains unconfirmed, only the exact `FM_PENDING_REPLY_EXISTING_CORR=` resend command printed by `fm-send` is safe to run later because it preserves the request body and lets the remote enqueue deduplicate onto the same record; a plain rerun mints a different correlation and is not idempotent. diff --git a/tests/fm-remote-secondmate-lifecycle-e2e.test.sh b/tests/fm-remote-secondmate-lifecycle-e2e.test.sh index be7022401bb..15d0deeb3aa 100755 --- a/tests/fm-remote-secondmate-lifecycle-e2e.test.sh +++ b/tests/fm-remote-secondmate-lifecycle-e2e.test.sh @@ -659,6 +659,23 @@ assert_present "$REMOTE_HOME/.fm-secondmate-home" "remote provisioning did not p assert_present "$REMOTE_HOME/projects/alpha/.git" "remote provisioning did not clone the project on that host" assert_grep "$REMOTE_HOME/state/parent-replies.status" "$REMOTE_HOME/data/charter.md" "remote charter did not use its append-only reply log" assert_no_grep "$PARENT/state/ios.status" "$REMOTE_HOME/data/charter.md" "remote charter retained the inaccessible local status path" +assert_grep "$REMOTE_HOME/state/parent-route/ios.inbox" "$REMOTE_HOME/data/charter.md" "remote charter did not use its host-local steering inbox" +assert_no_grep "$PARENT/state/ios.inbox" "$REMOTE_HOME/data/charter.md" "remote charter retained the inaccessible local steering inbox" +# A home seeded before the charter carried its host-local steering inbox is +# corrected by reseeding the same route, not by patching the home. +remote_inbox_line=$(grep -F "$REMOTE_HOME/state/parent-route/ios.inbox" "$REMOTE_HOME/data/charter.md" | head -n 1) +old_inbox_line=${remote_inbox_line//"$REMOTE_HOME/state/parent-route/ios.inbox"/"$PARENT/state/ios.inbox"} +while IFS= read -r line || [ -n "$line" ]; do + printf '%s\n' "${line//"$REMOTE_HOME/state/parent-route/ios.inbox"/"$PARENT/state/ios.inbox"}" +done < "$REMOTE_HOME/data/charter.md" > "$TMP_ROOT/charter.old-seed" +cp "$TMP_ROOT/charter.old-seed" "$REMOTE_HOME/data/charter.md" +assert_grep "$old_inbox_line" "$REMOTE_HOME/data/charter.md" "the old-seed fixture did not restore the parent steering inbox" +FM_SECONDMATE_CHARTER='Own iOS delivery on the build Mac.' \ + FM_SECONDMATE_SCOPE='iOS implementation and Xcode validation' \ + remote_env "$ROOT/bin/fm-remote-home-seed.sh" ios remote-mac "$REMOTE_ROOT" "$REMOTE_HOME" alpha >/dev/null \ + || fail "reseeding the existing remote route failed" +assert_grep "$remote_inbox_line" "$REMOTE_HOME/data/charter.md" "reseeding did not republish the host-local steering inbox" +assert_no_grep "$PARENT/state/ios.inbox" "$REMOTE_HOME/data/charter.md" "reseeding kept the old charter's inaccessible steering inbox" if FM_SECONDMATE_CHARTER='Own iOS delivery on the build Mac.' \ FM_SECONDMATE_SCOPE='iOS implementation and Xcode validation' \ remote_env "$ROOT/bin/fm-remote-home-seed.sh" ios remote-mac "$REMOTE_ROOT" "$TMP_ROOT/other-home" alpha \