feat(issue-50): create --onboard waits for the onboarding job to end (A)
The handshake only said the agent saw the prompt (#23). create_session.sh now waits, after `started`, for the onboarding job to reach a terminal status and makes that its own result, so the orchestrator learns it from create's exit (MULTI_AGENT_RULES §6) instead of guessing from herdr's agent_status: - lib_py/delegation/wrapper.py: `wait-terminal` (completed|error|cancelled, `timeout` after --timeout, `missing` when the record is gone; stops with its caller via --watch-pid) and `onboard-exit` (completed 0, error or cancelled 44, timeout or missing 45). bash branches on the printed answer. - lib.sh: delegate_wait_terminal / delegate_onboard_exit, thin calls. - create_session.sh: `onboarding completed: job <id> (<n>s)` on stdout, or `last_visible_status: onboarding-<status>` on the row, the reason on stderr and `exit 44` / `exit 45` after `trap - EXIT` - the session is kept, as for a missing handshake. The "no started" path (error, exit 0 before) now ends with 44. MAM_ONBOARD_WAIT_S (default 600, floor 1) bounds the wait. - worktree_prepare.sh: 44/45 pass through with "session and worktree kept". - create SKILL.md exit table (44, 45) and the knob; the #42 inventory follows. - tests/conftest.py: --onboard is the default, so ~60 sandbox create tests reach the new wait. The mock herdr now acts as the agent for create's onboarding prompt - it runs the publish commands the prompt names through the real publish_event.py - and MAM_ONBOARD_WAIT_S=3 caps a test that turns that off (MAM_MOCK_ONBOARD_HANDSHAKE=0 -> 44, `started` -> 45). The delegate-job wrapper's prompt has another shape and is never answered. - tests/test_issue50_onboard.py: the decision tables, create in the sandbox (0 / 44 / 45, session kept, row note, trap window), the knob's integer rule, worktree_prepare's pass-through. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
40293841e7
commit
c29c5b2142
9 files changed
+367
-14
No files matched your search
@@ -281,6 +281,12 @@ def mam_sandbox(tmp_path, monkeypatch, request):
|
||||
# Create-specific on purpose: MAM_INJECT_ACK_GRACE_S is the delegate-job
|
||||
# wrapper's knob and its own tests assert on the value they pass.
|
||||
monkeypatch.setenv("MAM_ONBOARD_ACK_S", "3")
|
||||
# Issue #50: create now waits for the onboarding job to end as well. The
|
||||
# mock herdr acts as the agent for create's onboarding prompt (it runs the
|
||||
# publish commands the prompt names, through the real publish_event.py),
|
||||
# so the wait ends at once; this cap is for a test that turns that off
|
||||
# (MAM_MOCK_ONBOARD_HANDSHAKE=0 / started) and expects 44 / 45.
|
||||
monkeypatch.setenv("MAM_ONBOARD_WAIT_S", "3")
|
||||
monkeypatch.delenv("HERDR_SESSION_NAME", raising=False)
|
||||
monkeypatch.delenv("HERDR_SERVER_NAME", raising=False)
|
||||
monkeypatch.delenv("HERDR_WORKSPACE_ID", raising=False)
|
||||
@@ -356,6 +362,27 @@ if os.path.exists(state_file):
|
||||
pass
|
||||
time.sleep(0.02)
|
||||
|
||||
def _onboard_handshake(text):
|
||||
# Issue #50: create's onboarding prompt names the publish commands it
|
||||
# expects the agent to run. This fake agent runs them - through the real
|
||||
# publish_event.py, as a real agent would - so create's wait for the
|
||||
# onboarding to end is satisfied. MAM_MOCK_ONBOARD_HANDSHAKE=0 publishes
|
||||
# nothing (create exits 44: no started), `started` publishes only the
|
||||
# first (create exits 45: never completed). The delegate-job wrapper's
|
||||
# prompt has another shape and is never answered here.
|
||||
import re
|
||||
import subprocess
|
||||
mode = os.environ.get("MAM_MOCK_ONBOARD_HANDSHAKE", "1")
|
||||
if mode == "0" or "On start run:" not in text:
|
||||
return
|
||||
for label in ("On start run:", "On success run:"):
|
||||
m = re.search(r"^" + re.escape(label) + r"\\s+(.+?)\\.?\\s*$", text, re.M)
|
||||
if m:
|
||||
subprocess.run(["bash", "-c", m.group(1)], stdout=subprocess.DEVNULL,
|
||||
stderr=subprocess.DEVNULL, timeout=120)
|
||||
if mode == "started":
|
||||
break
|
||||
|
||||
# Record the command call
|
||||
state.setdefault("calls", []).append(sys.argv[1:])
|
||||
try:
|
||||
@@ -659,6 +686,7 @@ elif cmd1 == "pane":
|
||||
sys.exit(1)
|
||||
pane_id = args[2]
|
||||
text = args[3]
|
||||
_onboard_handshake(text)
|
||||
found = False
|
||||
for a_name, data in state.get("agents", {}).items():
|
||||
if data.get("pane_id") == pane_id or a_name == pane_id:
|
||||
@@ -1147,6 +1175,7 @@ elif cmd1 == "agent":
|
||||
target_agent = agents[matched_k]
|
||||
target_agent["sent_text"] = target_agent.get("sent_text", "") + text
|
||||
target_agent["buffer"] = target_agent.get("buffer", "") + "\\n" + text + "\\n\\nesc to interrupt"
|
||||
_onboard_handshake(text)
|
||||
if wait_flag:
|
||||
p_res = state.get("prompt_wait_result")
|
||||
if p_res is None:
|
||||
|
||||
Reference in new issue
Block a user