Implement hard capture gate and lifecycle state machine #49

Merged
lost-rob0t merged 17 commits from agent/issue-7-lifecycle into main 2026-07-18 07:01:18 +00:00
lost-rob0t commented 2026-07-18 06:47:26 +00:00 (Migrated from github.com)

What changed

  • Added LifecycleActor, a Pykka ThreadingActor that is the sole owner of capture lifecycle transitions.
  • Added a thread-safe CaptureGate required around capture, preflight, and persistence operations.
  • Added generation epochs, cooperative cancellation permits, in-flight operation tracking, and stale-generation rejection.
  • Added a persistence commit barrier shared with stop/fault invalidation so the final generation check and irreversible commit have deterministic ordering.
  • Added an asynchronous private Pykka preflight actor so a stop command remains responsive while session, encryption, and policy checks are running.
  • Added serialized, deterministic, and idempotent start, pause, resume, stop, and fault commands.
  • Added a bounded stop barrier that independently cancels queued and in-flight work, waits for gate and pipeline quiescence, and clears volatile buffers.
  • Added sanitized lifecycle audit events and fail-closed behavior for audit, cancellation, preflight, quiescence, buffer-clear, actor, and shutdown failures.
  • Extended the lifecycle domain model for OFF -> FAULTED, STARTING -> STOPPING, and explicit startup opt-in.
  • Added lifecycle documentation and the mandatory integration rule: concrete capture and persistence operations must execute inside the gate callback, not inspect state and act later.

Security behavior

  • A new process starts off with no active generation.
  • Startup enters capture only when the current validated configuration explicitly enables it.
  • off, starting, stopping, and faulted reject capture and persistence callbacks before invocation.
  • paused rejects new capture while allowing already-redacted current-generation persistence.
  • Stop/fault invalidates the generation before pipeline cancellation begins.
  • Stale or late preflight results cannot reopen recording.
  • An uncooperative preflight can delay cleanup only until the finite stop deadline; timeout leaves the gate closed in faulted.
  • No runtime lifecycle state is persisted, so daemon restart cannot silently resume a prior recording session.

TDD evidence

Observed red result:

The first lifecycle design executed preflight synchronously in LifecycleActor. A regression test sent stop while preflight was deliberately blocked; the stop command timed out because the actor inbox could not process it. Preflight was moved to a private bounded Pykka actor, after which stop immediately invalidated the starting generation and late success was ignored.

Focused green result:

  • 35 lifecycle and hard-gate unit/security tests passed.
  • Ruff formatting and linting passed against the Python 3.14 target.
  • Strict Pyright passed with zero errors.
  • Tests cover startup opt-in, preflight faults, pause semantics, concurrent idempotent commands, cancellation ordering, stop during preflight, persistence rejection, finite quiescence, sanitized audit events, actor ownership, registry cleanup, and restart behavior.

Python 3.14 CI validation

Final workflow run #38 passed:

  • Ubuntu 22.04: formatting, Ruff lint, ShellCheck, strict Pyright, unit tests, contract tests, security tests, and security scans passed.
  • Ubuntu 24.04: formatting, Ruff lint, ShellCheck, strict Pyright, unit tests, contract tests, security tests, and security scans passed.
  • Independent eight-mode failure propagation passed.

Architecture boundary

This issue implements the Pykka lifecycle/control plane. It does not implement the ZeroMQ capture data plane; that remains issue #8. The lifecycle gate and generation contracts added here are the authority that issue #8 must enforce at every pipeline stage.

Closes #7

## What changed - Added `LifecycleActor`, a Pykka `ThreadingActor` that is the sole owner of capture lifecycle transitions. - Added a thread-safe `CaptureGate` required around capture, preflight, and persistence operations. - Added generation epochs, cooperative cancellation permits, in-flight operation tracking, and stale-generation rejection. - Added a persistence commit barrier shared with stop/fault invalidation so the final generation check and irreversible commit have deterministic ordering. - Added an asynchronous private Pykka preflight actor so a stop command remains responsive while session, encryption, and policy checks are running. - Added serialized, deterministic, and idempotent start, pause, resume, stop, and fault commands. - Added a bounded stop barrier that independently cancels queued and in-flight work, waits for gate and pipeline quiescence, and clears volatile buffers. - Added sanitized lifecycle audit events and fail-closed behavior for audit, cancellation, preflight, quiescence, buffer-clear, actor, and shutdown failures. - Extended the lifecycle domain model for `OFF -> FAULTED`, `STARTING -> STOPPING`, and explicit startup opt-in. - Added lifecycle documentation and the mandatory integration rule: concrete capture and persistence operations must execute inside the gate callback, not inspect state and act later. ## Security behavior - A new process starts `off` with no active generation. - Startup enters capture only when the current validated configuration explicitly enables it. - `off`, `starting`, `stopping`, and `faulted` reject capture and persistence callbacks before invocation. - `paused` rejects new capture while allowing already-redacted current-generation persistence. - Stop/fault invalidates the generation before pipeline cancellation begins. - Stale or late preflight results cannot reopen recording. - An uncooperative preflight can delay cleanup only until the finite stop deadline; timeout leaves the gate closed in `faulted`. - No runtime lifecycle state is persisted, so daemon restart cannot silently resume a prior recording session. ## TDD evidence **Observed red result:** The first lifecycle design executed preflight synchronously in `LifecycleActor`. A regression test sent stop while preflight was deliberately blocked; the stop command timed out because the actor inbox could not process it. Preflight was moved to a private bounded Pykka actor, after which stop immediately invalidated the starting generation and late success was ignored. **Focused green result:** - 35 lifecycle and hard-gate unit/security tests passed. - Ruff formatting and linting passed against the Python 3.14 target. - Strict Pyright passed with zero errors. - Tests cover startup opt-in, preflight faults, pause semantics, concurrent idempotent commands, cancellation ordering, stop during preflight, persistence rejection, finite quiescence, sanitized audit events, actor ownership, registry cleanup, and restart behavior. ## Python 3.14 CI validation Final workflow run #38 passed: - Ubuntu 22.04: formatting, Ruff lint, ShellCheck, strict Pyright, unit tests, contract tests, security tests, and security scans passed. - Ubuntu 24.04: formatting, Ruff lint, ShellCheck, strict Pyright, unit tests, contract tests, security tests, and security scans passed. - Independent eight-mode failure propagation passed. ## Architecture boundary This issue implements the Pykka lifecycle/control plane. It does not implement the ZeroMQ capture data plane; that remains issue #8. The lifecycle gate and generation contracts added here are the authority that issue #8 must enforce at every pipeline stage. Closes #7
Sign in to join this conversation.
No description provided.