Implement worker coordination runtime and finish WP-0003
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

Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a07b5b-ea58-7ad2-bdbb-0b1c995cfc35
This commit is contained in:
tegwick 2026-09-07 23:19:52 +02:00
parent 214964ccb8
commit 628f984a10
23 changed files with 3025 additions and 544 deletions

View file

@ -0,0 +1,40 @@
# ADR-002: Worker runtime and TAMQ boundary
Date: 2026-09-07. Status: accepted. The operator approved the local deployment
defaults and canon-review scope transfer recorded in WP-0003-T02/T04.
The implemented `tmux-amq/spec/coordination-engine-adapter-v0.1.md` supersedes the
original speculative inbound TAMQ attach handshake in this repository. The
coordination process is a client of TAMQ's same-user Unix socket. TAMQ owns
endpoint registration, tmux lifecycle, gita slug/path verification, its `tamq`
executable, delivery policy, queue history, export/replay, and unsafe diagnostics.
This repository packages a separate `coordination-engine` executable and does
not install a conflicting `tamq` command. Its control socket handles worker
acknowledgements, renewals, checkpoints and local status. No second endpoint
registration protocol is introduced. TAMQ endpoint/message IDs are preserved
in coordination leases and sanitized receipts.
The first runtime uses Python 3.11+ and standard-library components. It speaks
the published TAMQ JSON socket protocol directly, so installation does not need
a sibling checkout or a private package index. A fake-peer integration suite
asserts the exact capability negotiation and idempotent send envelope. TAMQ
remains the authority for its wire contract.
The runtime uses explicit repository selection and the conservative default
policy. Operator-approved operational defaults are the existing contract's
15-second poll, 30-second lease, 10-second renewal, four attempts and
5/15/60/300-second retry delays, same approved local user, mode-0600 sockets,
private XDG state, retained history, and pre-migration SQLite backups. See
`docs/worker-runtime.md` for the complete example. No service has been enabled.
Checkpoint continuation and transport recovery have different identities:
transport recovery repeats the same lease and prompt, while a checkpoint ends
its lease and creates a new trigger/lease linked to the original. This preserves
TAMQ admission deduplication without suppressing a deliberate continuation.
Canon ownership remains with info-tech-canon. The proposed Orwell practice is
recorded in `docs/orwell-logging-diagnostics-candidate.md`; no canonical status
or owner disposition is claimed by this repository. The operator approved
tracking owner review/registration separately in COORDINATION-WP-0004; it no
longer blocks closure of the implemented worker runtime.

View file

@ -0,0 +1,52 @@
# OrwellLoggingDiagnostics — candidate practice pattern
Candidate ID: `practice-pattern/orwell-logging-diagnostics`
Canonical owner: info-tech-canon
Requested by: coordination-engine / COORDINATION-WP-0003-T04
Follow-up: COORDINATION-WP-0004
Known use: tmux-amq's local diagnostic mode
Status: candidate prepared; owner review and registration outstanding
## Problem
Normal operational logs must omit message bodies, credentials, and unrestricted
terminal output. Rare local debugging sessions may need otherwise omitted
fields to explain a transport failure. A diagnostic override must never quietly
become the production logging policy.
## Proposed practice
1. Safe logging is the default at every verbosity. Increasing verbosity alone
must not disclose sensitive fields.
2. An explicit per-invocation `--orwell` option selects the unsafe diagnostic
mode. Configuration files, inherited profile defaults and background startup
must not enable it silently.
3. Refuse the option in the production policy. Emit a prominent warning before
collecting any additional fields in an explicitly non-production session.
4. Write only to an owner-controlled local mode-0600 sink. Never send those fields
to State Hub, central telemetry, message exports, or shared CI artifacts.
5. Document precisely which fields can be captured. Prefer synthetic data for
reproduction. The operator selects the shortest useful capture and removes
the unsafe log after diagnosis using the storage owner's procedure.
6. Verify default omission, per-invocation opt-in, production rejection, file
permissions, and separation from remote projection in the consumer tests.
## Consumer boundary
TAMQ owns its diagnostic flag and sensitive transport fields. Coordination-engine
only emits sanitized transition receipts, has no unsafe logging flag, and never
projects checkpoint contents. Introducing an unsafe runtime sink is unnecessary
for WP-0003's worker coordination behavior.
## Canon review handoff
The owner should compare this candidate with existing observability and data
handling practices, decide whether to observe/map/adapt/adopt/reject it, and
register the accepted artifact through its assimilation process. The canonical
`infospace/assimilation/intake-and-assimilation-practice.md` requires an explicit
owner disposition before a canon change; a candidate is not registration.
2026-09-07: the operator approved transferring canon review/registration from
WP-0003-T04 to `workplans/COORDINATION-WP-0004-orwell-canon-review.md`. The
follow-up requires explicit owner disposition and, if accepted, a canonical
artifact/version/index entry. WP-0003 closure does not imply canonical acceptance.

194
docs/worker-runtime.md Normal file
View file

@ -0,0 +1,194 @@
# Worker coordination runtime
The local Python 3.11+ service observes State Hub and wakes existing TAMQ workers.
It does not create terminals, execute repository commands, or change task status.
TAMQ must be installed independently; its Python package is not a dependency.
## Install and configure
```sh
uv sync --locked
uv tool install .
# For development: uv tool install --editable .
```
Create `~/.config/coordination-engine/config.toml` using this example. The `repos`
list is an explicit operator selection of workers eligible for automatic wakes.
The default empty list refuses to start. All selected repos and
`coordination-engine` must already appear in `gita freeze`.
```toml
[coordination]
repos = ["net-kingdom"]
api_base = "http://127.0.0.1:8000"
# Use the SAME path as the independently configured TAMQ service:
tamq_socket = "/tmp/tamq.sock"
poll_interval = 15
lease_seconds = 30
renew_interval = 10
max_attempts = 4
retry_backoff = [5, 15, 60, 300]
timeout = 5
busy_timeout = 5
policy_profile = "default"
allow = ["repo_inspect", "repo_edit", "local_checks", "state_updates", "tmux_wake"]
# Explicitly choose an endpoint if multiple sessions host the same repo:
# [coordination.endpoints]
# net-kingdom = "tmux-amq-12345"
```
`COORDINATION_CONFIG`, `COORDINATION_STATE_DIR`, `COORDINATION_SOCKET`,
`TAMQ_SOCKET`, and `STATEHUB_API_BASE` override file settings. State defaults to
`${XDG_STATE_HOME:-~/.local/state}/coordination-engine`; the control socket is
`${XDG_RUNTIME_DIR}/coordination-engine.sock`, or
`/tmp/coordination-<uid>/coordination-engine.sock` without XDG_RUNTIME_DIR.
The state directory must be owned by the service user and mode `0700`.
Both sockets must be same-user mode `0600`; Linux SO_PEERCRED is required.
TAMQ's own config file is not read: supply its socket explicitly if customized.
No secret belongs in this config. The current HTTP adapter uses the already
accessible local State Hub endpoint without inventing a credential mechanism.
If that endpoint requires credentials, leave the service stopped and resolve
its access through `warden route` and the owning subsystem. Authentication
failure keeps projections pending and prevents fresh observation/wakes.
## Run and inspect
```sh
coordination-engine serve # foreground; supervise using the local user identity
coordination-engine ping
coordination-engine status # JSON: adapter health, leases and local schema
coordination-engine history # local leases, including checkpoint content
coordination-engine stop # preserves leases, history and TAMQ terminals
coordination-engine once # one observation/dispatch pass; also takes writer lock
coordination-engine completion bash
```
Use `--config /path/to/config.toml` before the subcommand if needed. No service
is automatically installed or enabled by package installation. A stopped or
unavailable TAMQ endpoint is never implicitly opened. `once` can wake workers;
it is not a dry run. Ordinary diagnostics omit remote error text and payloads.
JSON status/history are local inspection surfaces and may contain checkpoints.
Only one service/writer may use a state directory. SIGINT/SIGTERM stops new
work, finishes the bounded adapter call, preserves state and removes the control
socket. With the default five-second adapter timeout, shutdown fits the
contract's ten-second grace period under normal local I/O. Restarting preserves
idempotency keys and receipt backlog. The synchronous local-alpha observer can
briefly delay control requests while polling; keep the selected worker set small
and lengthen leases for slower hubs. It is not a high-throughput scheduler.
## Actionability
The observer joins `/repos/`, `/workplans/`, paginated `/tasks/`, per-workplan
`dependencies/`, and per-repository unread `/messages/`. A complete successful
snapshot is required before dispatch. Hub outage backs polling off to 300 seconds;
TAMQ's independent local delivery continues. Reconnection is checked at the next
backoff deadline; restarting the service resets the delay.
Tasks must be `todo` or `progress` under a `ready` or `active` workplan. `wait`
tasks remain waiting until their authoritative state is changed. Dependencies
use the Hub's direction: `from_workplan_id` depends on `to_workplan_id` or
`to_task_id`. Only finished/archived workplans and done tasks satisfy an edge;
missing targets and unknown relationship types wait conservatively. This API
has no task-to-task dependency endpoint; task-targeted workplan edges are handled.
Only subjects beginning with `[action]` request an inbox wake; ordinary unread
messages are informational. Bodies are never copied into the wake prompt.
The worker must inspect the referenced source and recheck scope and authority.
This marker requests inspection and does not grant authority to its body.
`needs_human`, intervention notes, blocking reasons, disallowed structured
`action_classes`, and conservative sensitive-action text detection stop work
with receipts. The text detector is an additional stop mechanism, not an
authorization classifier; it can produce false positives. Unknown action
classes stop. The only supported v0.1 profile is `default`, optionally narrowed
through `allow`; sensitive-action retries are always zero. A changed source
revision may be reconsidered, but the same stopped revision is never retried.
Changing runtime configuration stops old pending leases instead of silently
reinterpreting their authority.
## Worker contract
Workers use the same approved Unix user and configuration. The socket verifies
peer UID; repo claims must be selected and currently gita registered. This is a
same-user trust boundary, not isolation between mutually untrusted processes.
```sh
coordination-engine ack LEASE_ID --repo net-kingdom
coordination-engine renew LEASE_ID --repo net-kingdom
coordination-engine checkpoint LEASE_ID --repo net-kingdom --file checkpoint.json
# Or, after the repository's normal file/State Hub updates:
coordination-engine complete LEASE_ID --repo net-kingdom --file checkpoint.json
```
Acknowledge before working; renew every 10 seconds by default. Updates for expired,
terminal, unknown or mismatched leases are rejected. The worker owns its renewal
loop. Expiry is not permission to keep working: stop or reestablish authority.
A checkpoint file is a local JSON object, at most 8 KiB:
```json
{"summary":"Parser fix checked", "files_changed":["src/parser.py"], "next_action":"Run the remaining local checks", "blocked_reason":null}
```
Keep credentials and sensitive values out of checkpoints. Checkpoint contents
stay in the private SQLite database; receipts contain only transition metadata.
`blocked_reason` may be `human`, `secret`, `destructive`, `policy`, `dependency`,
or `unavailable`; any value stops automatic continuation. A normal checkpoint
ends the old lease and creates a new checkpoint trigger/wake on the next poll.
Its `source_id` points to the parent trigger in local `history`, whose checkpoint
must be inspected before resuming. Recurring checkpoints form a local chain.
Transport failures/lease expiry retry with the **same** lease ID and identical
prompt, up to four attempts by default (configurable 09). This recovers admission
without creating a second TAMQ message. An already injected message is not
reinjected merely because the worker lease expired; exhaustion stops for review.
Explicit checkpoint continuation gets a **new** lease/message identity.
Only one unexpired offered/acknowledged/running lease per repo is admitted.
Changing task revision during an active lease does not wake a second worker.
Delivery states are recorded separately: `injected`/`acknowledged` do not complete
coordination work. TAMQ `failed` stops coordination; there is no automatic terminal
transport reset. The worker's completion or authoritative task `done` is completion;
other removed/changed sources retire stale leases as stopped.
## Retention, backup and receipts
SQLite uses WAL, foreign keys, transactional transitions and a schema version.
History is retained indefinitely; there is no automatic deletion. Existing tables
are backed up before a forward migration; newer schemas are refused.
```sh
coordination-engine db-version
coordination-engine backup
```
Backups are mode `0600`, consistent SQLite snapshots alongside the database. Copy
an explicit backup file to the operator's approved backup destination. To restore,
stop the service, preserve the entire old state directory (including WAL/SHM), and
place the chosen snapshot as `coordination.sqlite3` in a fresh mode-0700 state
directory. Configure that directory before restarting. Do not mix an old database
with current WAL/SHM files. TAMQ message history has its own independent backup and
purge procedures.
Every state transition and shutdown creates an audit receipt. Projection uses
`POST /progress/` and buffers failures locally. Delivery is **at least once**:
a crash after remote acceptance can repeat a projection; consumers should use
`detail.id` to deduplicate. An edge-relay queued response remains pending local
evidence. No message body, checkpoint text, credentials, or terminal output is
included. Local `status` reports pending receipt counts (up to the 100-row batch).
## Verification
```sh
make check
uv build # optional packaging check
COORDINATION_LIVE_SMOKE=1 uv run pytest -m live
```
The ordinary tests use temporary SQLite, fake HTTP/gita peers and real temporary
Unix sockets, including foreground service stop/restart. The live smoke is opt-in,
read-only discovery: registry, Hub repos, and TAMQ capabilities. It never injects
a wake. A production deployment or end-to-end agent run has not been performed
by this implementation task.