activity-core/docs/custodian-bounded-operations.md
tegwick fed119a17c feat: prepare pinned Custodian worker bundle and required input admission
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a15374-f59f-7a31-86d2-d23b5d2ed181
2026-10-19 15:09:27 +02:00

5.4 KiB

Custodian bounded operations

Daily security-flow and weekly allocation definitions remain disabled. They use the existing ACT-ADR-007 bounded-operation stage and Temporal schedule ownership, with one fixed owner command per run. They emit no agent task, ops_run or model invocation. The former harness.agent-dev-local@1.1.1 pin is incompatible: its green-commit-only tools forbid Python, its sandbox denies Hub egress, and its operational readiness is unverified.

Trusted operator bindings

Environment variable Required contract
ACTIVITY_CORE_CUSTODIAN_ROOT Absolute pinned owner source bundle, including owner scripts and dependencies.
ACTIVITY_CORE_CUSTODIAN_FLEET_ROOT Absolute accepted repository snapshot/mount root, without credential directories.
ACTIVITY_CORE_CUSTODIAN_STATE_ROOT Precreated private directory, worker-owned, mode 0700, outside source and fleet roots; retained/backed up by executor owner.
ACTIVITY_CORE_CUSTODIAN_REPOS Daily flow's explicit comma-separated canonical repo slugs, each with workplans under fleet root.
ACTIVITY_CORE_CUSTODIAN_HUB Daily flow's explicitly admitted HTTP(S) base URL without embedded credentials, query or fragment.
ACTIVITY_CORE_CUSTODIAN_ALLOCATION_REQUEST Weekly executor-owned request JSON within private state, binding real inputs, fixed evaluation time and retained output.

These are trusted deployment configuration, never definition command parameters. Missing bindings, traversal, symlinks, unbound inputs and output/input aliases are refused before execution. Weekly inputs remain under accepted roots and output stays in private state. The state root must be separate: when fleet root is /home/worsch, its ~/.local/state default is not accepted. Provision a separate retained mount and precreate its security-flow subdirectory.

Keep paths/request files protected from concurrent untrusted writers and mount source read-only. Checks are not a sandbox or credential-isolation boundary. Owner code and dependencies need accepted revision/digest. The adapter uses the worker Python and installs nothing. Daily decision sync scans decision logs throughout the fleet root; reporting scans the explicit repository list. Bind the root to approved decision-sync scope, not an arbitrary home directory.

Execution and receipts

Daily: worker Python runs security_flow_daily.py with fixed --root, --repos, --hub, --state-dir, and --apply only when explicitly declared. Owner locking and checkpoints govern duplicate messages. The adapter suppresses stdout/stderr, requires a refreshed valid report.json, and retains only SHA-256, exit status, apply flag and category counts. No message bodies enter its receipt.

Weekly: worker Python runs weekly_allocation_report.py --request with the bound request. The owner's immutable retention and identical retry semantics remain, including unchanged mtime on identical retries. The adapter executes a private snapshot of validated request bytes, preserving relative-path resolution; pointer changes cannot redirect that run. Freeze each request across retries; use a new retained path for revised evidence. The receipt contains report hash and recommendation count, without inventing measurements.

Both use fixed argv, no shell, at most 240 seconds within a 300-second Temporal activity timeout, and one attempt. Subprocess timeout terminates the process group including helpers. Nonzero exits and missing/invalid reports fail; raw command errors are not persisted. Checkpointed writes can precede failure: inspect owner state before retry. No parallel scheduler/retry loop is added.

The existing workflow retains the compact result in activity context and sends it through the declared State Hub progress evidence sink. This replaces ops_run close evidence for these routines. Command success does not prove sink delivery or owner acknowledgment; verify sink outcomes and owner artifacts separately.

Remaining acceptance

ACTIVITY-WP-0047-T02/T03 retain daily activation/first fire; CUST-WP-0071-T05 retains weekly acceptance after T04. Runtime owner must verify placement, scoped mounts, Python/dependencies, retained state/backup, admitted Hub connectivity and exact owner command behavior. No bindings or schedule installation are established by source tests. Exercise stale/missed/failed-run detection, ambiguous failure recovery, delivery and owner acknowledgment. Misfires skip; recovery requires a named owner. Weekly collection and recommendation upserts also remain. Retain the first unattended activity, report, sink and owner receipts.

tests/test_custodian_operations.py uses temporary fixture commands or mocked dispatch. It never invokes the real security command, Hub or schedule activation.

Concrete delivery options and unresolved owner choices are recorded in custodian-runtime-binding-proposal.md.

Daily execution requires ACTIVITY_CORE_CUSTODIAN_INPUT_POLICY and ACTIVITY_CORE_CUSTODIAN_FRESHNESS_RECEIPTS, a protected JSON array of existing Repo Manager receipts. Admission verifies exact complete snapshot contents and fresh per-repo Forge observations before starting subprocesses. The existing-worker candidate configures this gate; missing policy or receipts refuse execution before subprocess. Native freshness noops rely on protected Repo Manager issuer custody: the issuer checks HEAD against its last successful commit before emitting forge_head_matches_last_success.