diff --git a/docs/remote.ja.md b/docs/remote.ja.md index 9e7bc532..362679e3 100644 --- a/docs/remote.ja.md +++ b/docs/remote.ja.md @@ -18,7 +18,7 @@ agmsg はローカルファーストであり、メッセージは1台のマシ `add` の時点でストアに既に存在するメッセージはローカルに留まる。バスへの接続は「これから先を共有する」ことであり、「過去ログを恒久的な git 履歴に公開する」ことではない。過去分も共有したい場合(マシン移行・バックアップ)は `--include-history` を付ける。 -これで完了。以後 `send.sh` は新しいメッセージをバックグラウンドでバスへ push し、Stop フックの inbox チェック(および `inbox.sh`)は読み取り前にリモートのメッセージを pull するため、環境をまたぐメッセージもローカルとまったく同じ配信経路で届く。 +これで完了。以後 `send.sh` は新しいメッセージをバスへ push する。Unix ではバックグラウンドで実行する。Windows/MSYS では、管理されたシェルがコマンド終了時にバックグラウンドの子プロセスを終了させることがあるため、フォアグラウンドで実行する。したがって Windows での `send.sh` 成功は、イベントが bus へ到達したことまでを意味する。他の環境でもフォアグラウンド送信を強制するには `AGMSG_REMOTE_PUSH_SYNC=1`、明示的にバックグラウンド送信へ戻すには `0` を設定する。Stop フックの inbox チェック(および `inbox.sh`)は読み取り前にリモートのメッセージを pull するため、環境をまたぐメッセージもローカルとまったく同じ配信経路で届く。 ## コマンド diff --git a/docs/remote.md b/docs/remote.md index b72ffcc8..05f96fdc 100644 --- a/docs/remote.md +++ b/docs/remote.md @@ -31,10 +31,14 @@ bus means "share from now on", not "publish my backlog into a permanent git history". Pass `--include-history` to also export the pre-existing backlog (machine migration, backup). -That's it. `send.sh` now pushes new messages to the bus in the background, -and the Stop-hook inbox check (and `inbox.sh`) pulls remote messages before -reading, so cross-environment messages arrive through the exact same -delivery path as local ones. +That's it. `send.sh` now pushes new messages to the bus. On Unix the push runs +in the background. On Windows/MSYS it runs in the foreground because managed +shells may terminate background descendants when the command returns; a +successful `send.sh` therefore confirms that the event reached the bus. +Set `AGMSG_REMOTE_PUSH_SYNC=1` to force foreground delivery elsewhere, or `0` +to opt into background delivery explicitly. The Stop-hook inbox check (and +`inbox.sh`) pulls remote messages before reading, so cross-environment messages +arrive through the exact same delivery path as local ones. ## Commands diff --git a/scripts/lib/sync.sh b/scripts/lib/sync.sh index 229e10f4..1cd8a8ee 100755 --- a/scripts/lib/sync.sh +++ b/scripts/lib/sync.sh @@ -169,12 +169,18 @@ sync_mark_read() { } # A read is local UX state, but it must reach the bus without requiring the -# caller to remember a separate remote.sh push. Match send.sh's best-effort -# behavior; tests and callers that need deterministic completion can set -# AGMSG_REMOTE_PUSH_SYNC=1. +# caller to remember a separate remote.sh push. Managed Windows shells can +# tear down descendants when inbox/check-inbox exits, so match send.sh's +# platform default there. AGMSG_REMOTE_PUSH_SYNC explicitly overrides it. sync_push_best_effort() { + local push_sync="${AGMSG_REMOTE_PUSH_SYNC:-}" sync_configured || return 0 - if [ "${AGMSG_REMOTE_PUSH_SYNC:-}" = "1" ]; then + if [ -z "${AGMSG_REMOTE_PUSH_SYNC+x}" ]; then + case "${MSYSTEM:-$(uname -s 2>/dev/null || true)}" in + MINGW*|MSYS*|CYGWIN*|CLANGARM*) push_sync=1 ;; + esac + fi + if [ "$push_sync" = "1" ]; then bash "$(_sync_scripts_dir)/remote.sh" push --quiet >/dev/null 2>&1 || true else (bash "$(_sync_scripts_dir)/remote.sh" push --quiet >/dev/null 2>&1 || true) & diff --git a/scripts/send.sh b/scripts/send.sh index 04ab26f5..ca55d495 100755 --- a/scripts/send.sh +++ b/scripts/send.sh @@ -90,15 +90,25 @@ if ! printf '%s ' "$INSERT" | agmsg_sqlite "$DB" fi -# Remote transport (ADR 0005): export + push in the background so send -# returns at local speed. A failed or skipped push is caught up by the next -# push/pull cycle; the message is already durable in the local store. -# AGMSG_REMOTE_PUSH_SYNC=1 pushes in the foreground instead — for callers -# that must not leave a writer racing behind them (tests tearing down the -# store, scripts that exit immediately after send). +# Remote transport (ADR 0005): Unix shells export + push in the background so +# send returns at local speed. Managed Windows shells can tear down descendants +# as soon as the command exits, stranding a uuid-bearing row before it reaches +# the writer file. Default to foreground push on MSYS/Git Bash so "Sent" means +# the event reached the bus. AGMSG_REMOTE_PUSH_SYNC explicitly overrides the +# platform default: 1 = foreground, any other value = background. if [ -f "$(agmsg_storage_dir)/remote.conf" ]; then - if [ "${AGMSG_REMOTE_PUSH_SYNC:-}" = "1" ]; then - bash "$SCRIPT_DIR/remote.sh" push --quiet >/dev/null 2>&1 || true + REMOTE_PUSH_SYNC="${AGMSG_REMOTE_PUSH_SYNC:-}" + if [ -z "${AGMSG_REMOTE_PUSH_SYNC+x}" ]; then + case "${MSYSTEM:-$(uname -s 2>/dev/null || true)}" in + MINGW*|MSYS*|CYGWIN*|CLANGARM*) REMOTE_PUSH_SYNC=1 ;; + esac + fi + + if [ "$REMOTE_PUSH_SYNC" = "1" ]; then + if ! bash "$SCRIPT_DIR/remote.sh" push --quiet; then + echo "Error: message saved locally, but remote bus push failed. Run remote.sh push to retry." >&2 + exit 1 + fi else (bash "$SCRIPT_DIR/remote.sh" push --quiet >/dev/null 2>&1 || true) & fi diff --git a/tests/test_remote_sync.bats b/tests/test_remote_sync.bats index f1141ec1..091b81d1 100644 --- a/tests/test_remote_sync.bats +++ b/tests/test_remote_sync.bats @@ -153,6 +153,26 @@ sqlite_mem_db() { [ "$status" -ne 0 ] } +@test "inbox: MSYS waits for its read receipt to reach the bus" { + connect_both + bash "$SCRIPTS/send.sh" testteam alice bob "windows read receipt" >/dev/null + in_b "$SCRIPTS/remote.sh" pull + + # Delay the receipt push so an async implementation would return before the + # bare bus contains the message_read event. + hook="$TEST_SKILL_DIR/bus.git/hooks/pre-receive" + printf '%s\n' '#!/usr/bin/env bash' 'sleep 2' > "$hook" + chmod +x "$hook" + + run env -u AGMSG_REMOTE_PUSH_SYNC MSYSTEM=MINGW64 AGMSG_STORAGE_PATH="$ENV_B" \ + bash "$SCRIPTS/inbox.sh" testteam bob + [ "$status" -eq 0 ] + [[ "$output" =~ "windows read receipt" ]] + + run git -C "$TEST_SKILL_DIR/bus.git" grep -q '"type":"message_read"' HEAD -- + [ "$status" -eq 0 ] +} + @test "sync: a read receipt does not block later messages from the same env (#16)" { connect_both bash "$SCRIPTS/send.sh" testteam alice bob "first message" >/dev/null @@ -485,6 +505,25 @@ sqlite_mem_db() { done } +@test "send: MSYS defaults to foreground push before returning" { + connect_both + + # Make the push observably slower than send.sh's local insert. An async + # implementation returns before this hook finishes; the managed-shell-safe + # MSYS default must wait until the event is present on the bus. + hook="$TEST_SKILL_DIR/bus.git/hooks/pre-receive" + printf '%s\n' '#!/usr/bin/env bash' 'sleep 2' > "$hook" + chmod +x "$hook" + + run env -u AGMSG_REMOTE_PUSH_SYNC MSYSTEM=MINGW64 \ + bash "$SCRIPTS/send.sh" testteam alice bob "windows durable push" + [ "$status" -eq 0 ] + [[ "$output" =~ "Sent to bob" ]] + + run git -C "$TEST_SKILL_DIR/bus.git" grep -q "windows durable push" HEAD -- + [ "$status" -eq 0 ] +} + # --- subscriber registry (ADR 0006) --- @test "subscribe: writes the registry entry to the bus and pushes it (#ADR-0006)" {