Repository navigation
feat(cli): add autonomous delivery coordinator - #102768
blue9694100-del wants to merge 7 commits into
Conversation
andrexibiza
left a comment
There was a problem hiding this comment.
Reviewed exact head 68f3515c56a406e8efbb64107735bf93ff6247be against its direct/current main base 63279301bcbdc185c1b07b98a9312eb0c862f26d (18 files, +5650/-4, 7 commits). I also traced the public CLI path, the two state machines, async-delegation persistence/finalization, the acceptance gate, current hosted checks, and the adjacent durable-delegation / ownership work. There is useful machinery here worth preserving — especially the frozen contract/hash gate, stage-report validation, CAS transitions, post-stage repository checks, the create-task race closure, and the Windows-safe PID probe — but the advertised autonomous delivery contract is not yet connected safely enough to merge.
BLOCKER 1 — the public hermes delivery path never reaches the coordinator that contains Git/CI/deploy/runtime acceptance.
hermes_cli/subcommands/delivery.py constructs only FeatureDeliveryRunner (optionally with ProfileStageExecutor). That runner's own contract is explicit that DELIVERED means the feature-delivery gate passed and does not mean merged, pushed, deployed, or released; after acceptance passes, _run_acceptance() transitions directly to FeatureDeliveryState.DELIVERED.
The only implementation in this PR that actually has DELIVERING -> DEPLOYING -> ACCEPTANCE_RUNTIME -> DONE is hermes_cli/autonomous_coordinator.py::CoordinatorRunner, but I cannot find a production construction/import of that runner anywhere in the PR. hermes_cli.autonomous_coordinator is imported by its tests; completion_callback= is likewise passed only by tests. So the CLI-wired feature and the advertised feature are currently two disconnected state machines.
Required fix: establish one canonical runtime owner for the full workflow and wire the public command to it with concrete stage/action adapters. If the intended deliverable is only the deterministic developer/tester/acceptance gate, scope the PR and docs to that instead and remove the dead autonomous/Git/CI/deploy surface. Please do not leave a second durable state machine in core as speculative infrastructure.
BLOCKER 2 — effectful delivery/deploy failures collapse indeterminate into FAIL, which makes automatic retry unsafe.
CoordinatorEvent.status can represent only PASS | FAIL | RUNNING | BLOCKED. CoordinatorRunner.run() catches every action exception and deliberately converts it to FAIL (# Action failures are ordinary repair/retry inputs, not owner stops). For DEPLOYING, a failure increments deployment_retries, clears operation_id, and dispatches the deployment again; a DELIVERING failure routes back through repair and can later deliver again.
That is not sufficient for Git/PR/deployment side effects. A push, PR creation, release, or deployment can succeed remotely and then lose its acknowledgement. At that boundary the outcome is unknown, not failed, and a blind retry can duplicate or compound the side effect. The coordinator needs a typed settlement state (not_started / committed / failed / indeterminate is one workable shape), durable operation receipts/idempotency keys, and a reconciliation path that observes external truth before any retry. This is the same safety class as #102530/#102511: once side effects may have happened, owner/receipt loss must not be rewritten into retry permission.
There is a second authority problem on the proposed async bridge: CoordinatorRunner.completion_callback() accepts result["coordinator_event"]. In the integration test, the delegated runner itself returns a PASS coordinator event and commit. If this is wired directly, worker output can mint the transition evidence that advances the authority state. That should instead consume host-owned, run-bound evidence. #102390 is complementary here: it is explicitly separating worker completion prose from trusted verification receipts. #68499 is also complementary: lifecycle completion and logical task outcome must remain distinct.
BLOCKER 3 — the new continuation callback is process-memory state, not durable recovery state.
tools/async_delegation.py stores completion_callback in the in-memory delegation record and pops/calls it during _finalize(). _persist_dispatch() deliberately persists only task/routing fields into async_delegations; the callable is not and cannot be persisted. After a process restart, the durable delegation/completion row can survive while the continuation is gone. There is currently no production reconciliation bridge from a restored async completion to AutonomousTaskStateStore, and (per blocker 1) no production callback registration in the first place.
resume_all() therefore does not close this gap: redispatching an operation with the same coordinator operation_id is not equivalent to reconciling the durable delegation that may already have completed. Before this can own autonomous side effects, persist a stable correlation such as (task_id, operation_id, delegation_id, expected stage) and make startup/recovery consume the existing durable completion ledger idempotently. #82704 (completion-route ownership), #98349 (finalization must always settle), and #101550 (restart ownership/drain) are adjacent pieces to compose with, not duplicate.
BLOCKER 4 — generic Hermes delivery code contains a project-specific repository authority policy.
VesselMindRepositoryLock.expected is hard-coded to github.com/jasoncheungcn/ceramic-ai-designer-h5, and _lock_repository() activates that special policy based on project/repository strings. That is a concrete external-project policy embedded in a generic core coordinator. The current AGENTS.md direction is to keep Hermes' core narrow and extend generic infrastructure rather than special-case products/projects.
If repository lineage is a real delivery invariant, make it a generic contract field/policy resolved from the task/workspace (for example approved remote identity + target branch), validate it before mutation, and test the generic rule. The VesselMind-specific name/remote should live in the consuming task/config, not in hermes_cli core.
Interlocks / landing order
- #102117 is actively relocating
hermes_cli/main.pyandhermes_cli/kanban_db.pyownership on this same63279301bcbbase. This PR touches both current god-file owners. If #102117 lands first, forward-port the parser registration and idempotency fix into the extracted owners; do not re-grow the facades. This PR is complementary to that decomposition, not a reason to bypass it. - #102390's run-bound completion evidence should inform the acceptance/completion boundary here; worker claims must not become acceptance authority.
- #102530/#102511 defines the sibling unknown-after-effect/ownership-transfer problem for cron. Delivery/deploy needs the same fail-closed settlement semantics.
- #68499's lifecycle-vs-outcome split and #82704/#98349's durable completion semantics are adjacent contracts; this coordinator should consume those semantics rather than invent a parallel weaker terminal model.
Verification gate: exact-head CI is not green yet. The head's CI, Docker, and Nix workflow runs are all action_required and contain zero executed jobs. The immediately prior coordinator head (fb7daef019bd0a3770087e16b1c461758e445923) also has only action_required hosted runs, while the first five commits have no workflow runs attached. The reported local 271 passed is useful evidence, but it is not an exact-hosted green receipt. Every surviving commit still needs repository CI evidence before this is merge-ready.
I would keep the deterministic contract/gate work and the race/Windows fixes, then collapse the architecture to one wired state owner with durable receipt/reconciliation semantics. Once those blockers are fixed, this is worth another exact-head review; the core idea is useful, but autonomous delivery is exactly where false-green and blind-retry semantics have to be impossible.
What does this PR do?
Adds a durable autonomous delivery coordinator that advances a feature task through developer, tester, acceptance, Git/CI/deploy, and runtime-acceptance stages without requiring manual continue messages between normal stage completions. Recoverable failures return to the appropriate repair stage, while true owner-stop conditions remain blocked for explicit input.
The implementation reuses the existing feature-delivery runner, profile stage executor, Kanban durability, and async delegation queue. Async completion callbacks run only after the durable completion event is published.
Related Issue
No linked issue.
Type of Change
Changes Made
How to Test
scripts/run_tests.sh -j 4 tests/hermes_cli/test_autonomous_coordinator.py tests/hermes_cli/test_feature_delivery.py tests/hermes_cli/test_feature_delivery_gate.py tests/hermes_cli/test_feature_delivery_profiles.py tests/hermes_cli/test_feature_delivery_resume.py tests/hermes_cli/test_feature_delivery_runner.py tests/hermes_cli/test_feature_delivery_unblock.py tests/hermes_cli/test_profile_stage_executor.py tests/tools/test_async_delegation.py -q.mainandgit diff --check origin/main...HEAD.Checklist
Code
Documentation & Housekeeping
cli-config.yaml.exampleis N/A; no config key was added.CONTRIBUTING.md/AGENTS.mdupdates are N/A.Screenshots / Logs
Focused local validation: 271 passed, 0 failed, 1 macOS-only skip. Ruff, diff-check, and secret scan passed.