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

84 lines
5.4 KiB
Markdown

# 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](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`.