Commit Graph
2 Commits
Author SHA1 Message Date
GodopuandClaude Opus 5 d87674679d docs(issue-5): correct the phase 2b operations note and the stale references
The important correction is an honesty one, raised independently by two
reviewers. The note claimed startup recovery runs in the subscriber. It does
not: nothing in production imports the gate or the recovery function, and there
is no per-job opt-in path, so the flag is currently a no-op in the live loop.
Phase 2b delivers a verified library and gate; wiring it in belongs to phase 3,
and the note now says so and lists what wiring requires.

Also records the checks that are now mandatory rather than caller-dependent,
that the rollback switch disables auto-completion too, that backend keys match
exactly, that bookkeeping may touch one field, that settling is conditional on
the intent actually sent, and why the intent's identity travels in the payload
rather than relying on the transport sequence.

Fixes three stale references: task.md named the pre-move path for finalize, the
loop package description still described itself as shadow-only, and a comment in
mqtt_common still pointed at the engine's original location.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UrDNJkHmm18uu7n7JwV5Qd
2026-09-06 22:26:56 +09:00
GodopuandClaude Opus 5 2532e78453 feat(issue-5): single CAS transition boundary for every job-status write
engine.transition is now the only writer of job status. It implements the target
table in state-transitions.md §3: claim, non-terminal metadata, explicit
terminals, idempotent re-receipt, refusal of conflicting or late signals with an
audit entry, stale-attempt rejection, and deadline timeout.

Two write paths become one. update_job_status merged fields unconditionally;
pick_pending hand-rolled a correct pending->running swap plus its own audit
mirror. Neither was wrong alone, but a rule added to one silently missed the
other, which is the shape that produced the phase 1 transport bugs. Both keep
their signatures and their preserved legacy bodies behind MAM_ENGINE_CAS=0.

A merged `status` value is routed to the request that owns it, so a raw field
write can no longer slip past the table. Getting that wrong first was caught by
the characterization tests: a metadata merge was overwriting the caller's status
with the pre-merge value.

Lock discipline is now enforced, not described. The lock covers a decision and
one record write; audit mirroring is queued and flushed after release, carrying a
monotonic transition_seq so ordering survives the move. A test watches for
subprocess, socket and sleep calls while the lock is held and asserts none.

Structural correction to my own plan, caught by a phase 1 guard: the plan put the
engine in lib_py/loop, but delegation would then have had to import a sibling,
which is exactly what mixes trees under a frozen loop. The engine belongs with
the registry writers, so it lives in lib_py/delegation and loop consumes it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UrDNJkHmm18uu7n7JwV5Qd
2026-09-06 18:53:22 +09:00