ACT-ADR-007 accepted · accepted-1 activity-core reviewed 2026-08-23generated from canonical source — do not edit

Code-registered bounded operations are the only local mutation exception

Source: activity-core · docs/adr/adr-007-bounded-operations.md · b72fdb5452bff51a867a0316edb994723b35f268

Review due: 2027-02-23

Status

Accepted (2026-08-23) for ACTIVITY-WP-0035.

Context

The Event Bridge principle says activity-core answers when, what, and where and does not execute domain work. Production nevertheless contains three useful operations whose complete outcome is a small scheduled maintenance action:

  • ingest at most three Repo Manager-selected immutable sources into SBOM Nexus;
  • prune Forgejo package versions under live-image protection; and
  • invoke the fixed CNPG Option A backup tool for an explicit target list.

Historically the latter two ran through a generic shell context resolver. That made a mutating subprocess look like a read and left extension policy to convention. Removing the operations would recreate bespoke cron; accepting arbitrary shell would make activity-core a general executor.

Decision

1. Bounded operations are a narrow implementation exception

The governing when/what/where responsibility does not gain a general “how.” A bounded operation is allowed only when the operation itself is the declared automation outcome and all admission requirements below are code-reviewable.

The initial allowlist is exactly:

  1. sbom_nexus_ingest
  2. forgejo_package_prune
  3. cnpg_option_a_backup

Adding an operation requires updating the code-owned registry, this ADR (or a successor), tests, credential route, and evidence contract. A string in a definition cannot register an operation.

2. Admission is fail closed during file sync

Each registry entry declares:

  • source type and query;
  • whether mutation intent is apply or dry_run and that it is explicit;
  • fixed or maximum target bounds;
  • canonical implementation and allowed configuration;
  • idempotency and Temporal retry semantics;
  • maximum execution timeout;
  • credential owner/route; and
  • mandatory non-secret evidence mode.

Unknown shell queries are refused unless separately registered as read-only. Known operation queries with missing, malformed, or over-limit safety fields are refused before database projection or Temporal schedule reconciliation.

3. Context resolution is read-only

The workflow first resolves and freezes context. Mutations then run in an explicit bounded-operation stage and merge only normalized outcomes into the snapshot before evidence and rule/instruction evaluation.

SBOM selection remains a read in the context phase; its fixed selection is the input to the operation stage. Package prune and backup have no discovery read inside activity-core and bind a pending marker until their operation completes.

4. Retry behavior is operation-specific

  • SBOM ingest uses stable per-run/per-repository idempotency keys and heartbeat checkpoints, so Activity retries resume the frozen batch.
  • Forgejo prune and CNPG backup have no activity-core-verifiable remote idempotency receipt. Their operation activity therefore has one Temporal attempt; a failure remains visible for operator reconciliation rather than risking an automatic second mutation.

This does not preclude future safe retries after the platform tools expose a durable operation receipt.

5. Evidence is mandatory and bounded

Every operation must produce an allowlisted summary through a configured report/evidence sink. Raw subprocess output, tokens, provider payloads, archive URLs, kubeconfigs, and credential material are not evidence.

Rejected alternatives

  • Keep mutating shell resolvers. Rejected because resolution should be a replayable read and the generic dispatcher hides mutation admission.
  • Generic command activity. Rejected because command text/path from a definition is remote code execution by configuration.
  • Move every operation to a rein. Rejected for these fixed platform operations; it adds an agent execution constellation without judgement or repository work. Operations that exceed this ADR's bounds do belong there.
  • Remove all local operations. Rejected because it recreates scattered cron and loses Temporal/evidence guarantees for established maintenance.

Consequences

  • Definition parsing gains a central operation-policy validator.
  • The workflow gains an explicit operation stage.
  • The generic shell resolver becomes read-only.
  • Existing definitions migrate without widening targets or permissions.
  • The registry is intentionally small and architectural review is required to expand it.