fix(issue-6): fail-closed registry timeout and TUI turn handshake

- Introduce finalize_unfinished in multi-agent-mux-delegate-job for fail-closed atomic transitions
- Enforce CAS dispatch_token and attempt_id pinning across engine, CLI, and lifecycle scripts
- Integrate Herdr wait_for_agent_idle turn handshake to prevent prompt loss and race conditions
- Resolve bash 3.2 set -u empty array expansion compatibility in lib.sh
- Preserve Issue #5 R2 contract in CAS transition engine (engine.py, publish_event.py, service.py)
- Add comprehensive regression suites (test_issue6_fail_closed.py, test_issue6_turn_handshake.py)
- Bump lockstep framework and skills to v4.4.1 across lib.sh, eager.py, VERSIONS.md, 8 SKILL.md
- Synchronize documentation (IMPROVEMENTS.md, MESSAGING.md, state-transitions.md, task.md, phase5-e2e.md)
- Unanimous PASS achieved across 4 reviewer team leaders (Claude, Codex, OpenCode, Grok)

Closes #6
This commit is contained in:
Godopu committed 2026-09-09 14:02:47 +09:00
1 parent 7b761be7b8
commit ec61511699
51 files changed
+3477 -293

No files matched your search

@@ -240,7 +240,7 @@ def broker_config_from_env(overrides: Optional[Dict[str, Any]] = None) -> Broker
for any key it specifies with a non-None value.
"""
cfg = BrokerConfig(
host=os.environ.get("MQTT_BROKER", "broker.hivemq.com"),
host=os.environ.get("MQTT_BROKER") or os.environ.get("MQTT_HOST", "broker.hivemq.com"),
port=_env_int("MQTT_PORT", 1883),
tls=_env_bool("MQTT_TLS", False),
username=os.environ.get("MQTT_USERNAME") or None,
@@ -428,12 +428,16 @@ def transition_job_status(job_id: str, registry_dir: str = DEFAULT_REGISTRY_DIR,
from lib_py.delegation import engine # same package: no sibling hop (see __init__)
request = "merge"
expected_dispatch_token = fields.pop("expected_dispatch_token", None)
expected_attempt = fields.pop("expected_attempt", None)
if "status" in fields:
request = engine.STATUS_TO_REQUEST.get(fields["status"], "merge")
if request != "merge":
fields = {k: v for k, v in fields.items() if k != "status"}
return engine.transition(job_id, request, registry_dir=registry_dir,
fields=fields, source=_source)
fields=fields, source=_source,
expected_dispatch_token=expected_dispatch_token,
expected_attempt=expected_attempt)
def _update_job_status_legacy(job_id: str, registry_dir: str = DEFAULT_REGISTRY_DIR, **fields: Any) -> Dict[str, Any]: