coordination-engine/spec/worker-coordination-service-v0.1.md
tegwick 628f984a10
All checks were successful
check / test (push) Successful in 7m8s
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Implement worker coordination runtime and finish WP-0003
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a07b5b-ea58-7ad2-bdbb-0b1c995cfc35
2026-09-07 23:19:52 +02:00

155 lines
9.2 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Worker Coordination Service v0.1
Status: local-alpha implementation; operational defaults approved in WP-0003-T02.
Owner: coordination-engine. Transport owner: tmux-amq. State owner: repositories
and State Hub's file-backed projections.
This revision reconciles the original proposal with TAMQ's implemented
`spec/coordination-engine-adapter-v0.1.md`. The original requirements are retained
in `history/260907-worker-coordination-service-v0.1-proposal.md`; TAMQ-specific
CLI, history, replay, endpoint registration and logging requirements belong to
that repository. This package installs `coordination-engine`, never `tamq`.
## Observation and actionability
The runtime polls State Hub's repos, workplans, tasks, dependency edges and unread
messages. It dispatches only after a complete successful snapshot, only for
explicitly configured gita repositories. It refreshes gita before addressing a
TAMQ target and on worker updates; it never clones or registers repositories.
Task triggers require ready/active workplans, todo/progress task status, and
satisfied workplan/workplan or workplan/task dependencies. Wait tasks remain
waiting until their authoritative status changes. Unknown dependency types or
missing targets wait. The Hub's edge direction is `from_workplan_id` depends on
`to_workplan_id` or `to_task_id`.
The current Hub has no action-request flag on messages. An unread, unarchived
message whose subject starts with `[action]` requests inspection. Other messages
do not wake workers. Message text never grants authority or changes workflow
state. Generic free-text progress records are not interpreted as checkpoints;
workers publish structured checkpoints through the local control socket.
A normalized trigger has source (`task`, `message`, `checkpoint`), source ID,
source revision, repository and trigger ID. The ID hashes the tuple
`(source, source_id, source_revision, repo)`. Revisions hash the relevant observed
record, plan and dependency state, so dependency completion is reconsidered even
when the task timestamp does not change. Payload text is not retained locally
as trigger data and is never copied to a wake prompt.
## Policy and safety
The named `default` policy permits repository inspection, authorized edits,
local checks, state updates and tmux wakes; configuration may narrow this set.
Unknown profiles/action classes are refused. Human flags, intervention notes,
blocking reasons and conservative sensitive-action text detection stop a
trigger. Sensitive retries are always zero in v0.1. Text detection adds stops;
it does not authorize arbitrary source text. Workers must independently recheck
current repository instructions, scope, dependency state and authority.
Authentication failures, unregistered/mismatched targets, incompatible protocol
and ambiguous endpoints stop the lease. Missing endpoints and ordinary transport
failures use bounded backoff. No safety stop silently reroutes to another worker.
A new source revision may be evaluated normally; the same stopped revision is
never automatically reset. A changed runtime configuration stops pending leases
created under the old configuration rather than silently changing their policy.
## Leases, checkpoints and completion
SQLite stores unique trigger and coordination lease IDs, source/revision/repo,
state, attempts, expiry, retry deadline, policy/configuration versions, checkpoint,
and TAMQ endpoint/message/transport state. State transitions and audit receipts
are committed together. One unexpired offered/acknowledged/running lease per
repository prevents overlapping automatic wakes, including across task revisions.
States are pending, waiting, offered, acknowledged, running, checkpointed,
retry_wait, completed and stopped. Every transition emits a sanitized receipt.
Workers acknowledge before work, renew while active, and checkpoint or complete.
Unknown, expired, terminal and wrong-repository lease updates are rejected.
Transport recovery repeats the same coordination lease ID and identical prompt.
TAMQ deduplicates admission and returns its original message ID. Expiry/failure
uses configurable bounded backoff and a 09 attempt ceiling. Already delivered
TAMQ messages are not forcibly reinjected by coordination retry; an unresponsive
worker eventually stops for review. Terminal TAMQ failure stops coordination.
A normal checkpoint ends its lease and creates a new checkpoint trigger/lease,
linked through `source_id` to the parent trigger. This continuation is a new wake,
not a transport retry. The worker reads its parent's local checkpoint before
resuming. Checkpoints allow summary, files_changed, next_action and blocked_reason
within 8 KiB; contents stay local. Any blocked_reason stops automatic continuation.
TAMQ injected/acknowledged means transport delivery, never task completion.
Completion requires a worker completion operation or authoritative task done.
Source removal or other superseding changes stop stale leases. A safe status
revision does not revoke an unexpired active worker merely to issue another wake.
## TAMQ boundary
Coordination-engine connects to the configured TAMQ Unix socket as the same
approved local user. It checks socket ownership/mode 0600 and Linux SO_PEERCRED,
negotiates protocol 0.1 through ping, and requires `bounded_delivery_ack_v1` and
`idempotent_send_v1`. Each operation opens a fresh authenticated connection.
Exactly one live endpoint must advertise the selected gita repository. Multiple
matches require configured endpoint_id; zero is unavailable. TAMQ validates its
registered repository paths and owns endpoint lifecycle. Coordination-engine
checks any advertised path and never controls tmux directly.
The send envelope uses client_id/sender_repo `coordination-engine`, lease ID as
idempotency_key, trigger ID as correlation_id, exact target/endpoint, provenance
`coordination_engine`, structured lease/trigger metadata and a standard plain-text
prompt. TAMQ owns its 8 KiB body limit, queue, delivery acknowledgement profile,
local leases, retry timing and durable admission. The coordination runtime never
resets a failed TAMQ message automatically. Receipt recovery uses `message` after
restart; endpoint ID and message ID are preserved as the correlated pair.
The original inbound attach/handshake proposal is superseded: no second TAMQ
registration socket is introduced. The separate coordination control socket
serves ping, status, stop and structured worker ack/renew/checkpoint/complete
operations over newline-delimited JSON with an `op` field. It accepts same-user
peers only, enforces a 16 KiB request limit, validates selected/gita repository
claims, and returns JSON `ok` responses. It is a same-user trust boundary.
## Storage, lifecycle and configuration
Python 3.11+, standard-library runtime dependencies, root pyproject.toml, uv
install/editable support. See `docs/worker-runtime.md` for the exact TOML schema,
XDG paths, overrides, CLI, worker examples, backups and restore procedure.
Operator-approved operational defaults: 15-second polling, 30-second leases, 10-second
renewal, four attempts, 5/15/60/300-second delays, five-second adapter/busy timeout,
private mode-0700 state and mode-0600 sockets/databases/backups. Explicit repo
selection is mandatory; package installation never enables a service.
SQLite uses WAL and foreign keys. Schema upgrades are transactional, snapshot
existing tables before migrations, and reject newer versions. History is never
automatically removed. Backup uses SQLite's consistent backup API. An exclusive
writer lock prevents concurrent service/once dispatch. SIGINT/SIGTERM stops new
work, preserves durable leases and removes the control socket. The synchronous
local-alpha polling implementation is intended for small local worker sets;
control calls can wait for an observation pass. See the runbook's timing caveat.
Hub outages suspend new wakes and back polling off to 300 seconds. TAMQ local
messaging remains independent. Receipt projection retries from a local outbox;
projection is at least once, with a stable receipt ID in `detail.id`. Edge-relay
queued responses remain pending evidence. Credentials are not configuration and
must follow the approved credential-routing subsystem if access is unavailable.
## Diagnostics and verification
Default errors omit arbitrary remote exception text. Audit receipts contain only
IDs, state/reason codes, timestamps, retry metadata and policy/configuration
versions. No source bodies, checkpoint text, credentials or terminal output are
projected. Local status/history may expose checkpoints to the approved user.
TAMQ owns unsafe `--orwell` diagnostics. Coordination-engine has no unsafe sink.
`docs/orwell-logging-diagnostics-candidate.md` records the candidate practice;
canonical review/registration remains with info-tech-canon, tracked separately
in COORDINATION-WP-0004 with operator approval.
Default checks: `uv run pytest`, `git diff --check`, compileall, CLI help/version.
Tests exercise SQLite durability and locking, fake State Hub and gita, actual
local sockets with fake TAMQ peers, CLI behavior, service stop/restart, safety,
dependencies and checkpoint continuation. Package builds and explicitly enabled
read-only live adapter discovery are separate checks. No credential or live
worker mutation is required by the test suite.