Trial 2 of this branch: cross-server delivery was restored (f7cee00), the
agent completed its onboarding and published started and completed - and
create_session still waited 180 s, called the onboarding unconfirmed, and
tried to write `error` over the completed job (refused by the registry).
delegate_wait_started required the started line to carry data.dispatch_token
(review R1: a stale attempt's started must not vouch for this one). But
publish_event only writes that field when its command line carries
--expect-dispatch-token, and create_session's hand-rolled onboarding command
never did - the wrapper's _pub_cmd does, which is why every wrapper-dispatched
agent's started has the token and no create_session onboarding ever could.
Five reviewers missed it because the fixture wrote the token into the started
line by hand: a fake producer that differs from the real one.
Fix, along the wrapper's existing mechanism rather than a new one:
* delegate_publish_cmd (lib.sh) builds the publish command for an agent, with
--expect-dispatch-token and --attempt-id, and create_session uses it. The
agent's own started now names the dispatch, and publish_event's CAS check
refuses a stale dispatch's event at the source. S7l pins the builder
argument-for-argument against the wrapper's _pub_cmd.
* delegate_started_recorded (lib.sh) is the one definition of "this
dispatch's started is on disk" - JSON-parsed, token-matched when a token is
given - used by delegate_wait_started and by the wrapper's _wait_started in
place of its regex. The wrapper keeps its status and subscriber routes
(remote agents); the shared part is the evidence contract.
* An already-terminal job is left alone when the wait expires (req. 4): no
error over a completed/error/cancelled record.
The fixture now registers the record through registry.py and publishes
started through the real publish_event.py; the only synthetic line left (S7h)
exists to pin key-order independence and says so. S7i pins the trial-2 defect
as the negative it is (old command shape -> started without token -> not
acknowledged); S7j runs the builder's command through the real producer and
is acknowledged on the first probe; S7n shows the wrapper accepting the same
real line and rejecting a stale token; S7m pins no-write-over-terminal. S7f
moves from "publish error" to "leave the terminal job alone", per req. 4.
Option B (verify data.hmac_sig with the job's auth_token, plus record-token
still equal) was weighed and not taken: verify_hmac returns True with no
auth_token - PoC mode, which the trial and every sandbox run in - so it proves
nothing there; in production hmac_sig authenticates the signer but does not
identify the dispatch, so R1 would rest on a current-state check that a stale
line plus an unrotated token passes; and it would diverge from the wrapper
unless the wrapper changed too. Token exposure is not new: every wrapper-
dispatched instruction already carries the dispatch token in the pane, and it
is a per-dispatch CAS nonce, not the HMAC secret.
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
multi-agent-mux-delegate-job 스킬
작업(Job)을 자율 에이전트(claude-code/hermes/agy/grok-build/codex/opencode/human)에게 위임하고 MQTT
이벤트 채널로 비동기 관찰하는 범용 에이전트 협업 스킬. 시작점은 SKILL.md.
- 프로토콜/스키마:
job-protocol.md - 브로커 PoC→운영 전환:
mqtt-broker-setup.md - 레지스트리 포맷/동시성:
registry.md - 참조 구현:
multi-agent-mux-delegate-job(bash wrapper),scripts/publish_event.py,scripts/job_subscriber.py,scripts/registry.py,scripts/mqtt_common.py - v4.3.0 이후: 위
scripts/*.py는 호환 shim이며 실제 구현은.agents/skills/lib_py/delegation/에 있다 (issue #5 phase 1). 스크립트 경로와 CLI 계약은 그대로이며,lib.sh헬퍼는python -m lib_py.delegation.cli를 호출한다. - 영구 감사 로그:
.mam/delegate_job_logs/<job_id>/(meta.json·events.ndjson·status.json) —multi-agent-mux-delegate-job logs <id>또는multi-agent-mux-delegate-job logs --list로 조회 (SKILL.md "Audit Logs" 참조)