feat(runtime): consume governed Activity Core closes

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a06ba0-10aa-7ea0-b20a-4f3fac39efe9
This commit is contained in:
tegwick 2026-09-04 19:54:07 +02:00
parent 0e6d795aa4
commit d00ffcb402
22 changed files with 1218 additions and 148 deletions

View file

@ -34,11 +34,12 @@
|---|---|---|---|
| Instance manifest | `manifest.py` | extends `.kaizen/schedule.yml`; `validate` CLI | unchanged contract; tenant onboarding |
| Tool profiles | `profiles.py` | `green-commit-only`, `blue-mail-triage` registry | additional named profiles as needed |
| Task intake | `intake.py` + `taskspec.py` | issue-core GET/PATCH poll+claim; JSON task-file for local dev | NATS when activity-core migrates |
| Task intake | `ops_run_client.py` + `taskspec.py` | Activity Core claim/lease/close; JSON task-file for local dev; issue-core compatibility | event-driven wakeup if needed |
| Persona | `persona.py` | `kaizen-agentic schedule prepare` (ADR-005) | unchanged, plus phase-memory profile hook |
| Session | `adapter.py` | `AgenticClaudeCodeAdapter` (cwd-pinned, profile allow-list) | + hosted adapters |
| Orchestration | `runner.py` | profile/budget → transaction → session → grant acceptance → metrics → hub | activity-core intake |
| Orchestration | `runner.py` + `claim_loop.py` | profile → transaction → session → grant acceptance → metrics → durable close | readiness and recovery drills |
| Metrics | `metrics.py` | compatibility `.kaizen/metrics`; durable external ledger for granted runs | kaizen-owned projection |
| Close evidence | `close_outbox.py` | enqueue-before-close; exact replay; conflict quarantine | operational retention policy |
| Mail lane | `mailscan.py` | deterministic credentialed pre-step outside the session | pattern generalizes to other credentialed pre-steps |
| Hub reporting | `hub.py` | REST progress event + task close + token event | unchanged |

View file

@ -1,6 +1,6 @@
# Close-evidence outbox
Status: **durable core implemented, not connected to the live claim loop**.
Status: **active for profiled Activity Core terminal closes**.
The outbox preserves one bounded Activity Core completion or failure intent
after repository work. Replaying an outbox entry calls only the queue close
@ -21,9 +21,10 @@ outside target checkouts. Outbox directories are mode `0700`; lock and JSON
files are mode `0600`.
An entry is keyed by the digest of the bounded Activity Core `run_id` and
repository `transaction_id`. Re-enqueuing the identical intent returns its
existing pending or delivered state. Reusing that identity for a different
action, result, error, or reopen decision is a conflict and is refused.
repository `transaction_id` and retains the original worker identity.
Re-enqueuing the identical intent returns its existing pending or delivered
state. Reusing that identity for a different worker, action, result, error, or
reopen decision is a conflict and is refused.
The v1 close intent contains:
@ -41,7 +42,8 @@ Enqueue and retry-state updates use a same-directory temporary file, file
`fsync`, atomic replace, and directory `fsync` under a process-safe `flock`.
The retry attempt is durable before the delivery callback begins.
Normal callback return marks the entry delivered and atomically moves it from
Normal callback return, including Activity Core `close_disposition=applied` or
`reconciled`, marks the entry delivered and atomically moves it from
`pending/` to `delivered/`. An ordinary exception retains it in `pending/` with
only the exception class and a generic failure marker; exception text is not
persisted. Process interrupts propagate; the already-recorded attempt remains
@ -49,21 +51,29 @@ pending. Invalid, oversized, or directory/state-mismatched records are moved
intact to `quarantine/` with a bounded reason sidecar and are never delivered
automatically.
A network timeout may occur after Activity Core accepted a close. Retrying the
same close intent can therefore repeat the queue mutation request, but cannot
repeat repository workload. Activity Core remains authoritative for terminal
row state and must provide idempotent or reconcilable close semantics.
Activity Core refusal codes `not_found`, `wrong_owner`, `expired_lease`,
`state_conflict`, `evidence_conflict`, and `terminal_conflict` are conclusive,
not transient. Such entries move to quarantine rather than retrying forever.
An unclassified HTTP failure or transport outage remains pending.
## Activation gates
A network timeout may occur after Activity Core accepted a close. The worker
replays pending close-only work before it claims another row. Retrying the same
intent can repeat the queue request, but cannot repeat repository workload;
Activity Core reconciles the exact normalized terminal repeat.
The live claim loop still returns an in-memory failure when close delivery
fails. Connecting it to this outbox requires:
## Operator controls
1. the lease-bound repository transaction and accepted-result envelope;
2. enqueue-before-close using the same run and transaction identities;
3. Activity Core review of repeat close behavior for already-terminal rows;
4. startup/periodic replay that never calls workload code; and
5. operator status, quarantine inspection, and replay controls.
```bash
rein-aharness close-outbox status
rein-aharness close-outbox replay
rein-aharness close-outbox replay --limit 10
```
Until those gates are implemented and reviewed, no production close behavior
changes.
`status` and `replay` return non-zero while pending or quarantined material
exists, making them suitable for readiness/recovery checks. Quarantine content
and its bounded reason sidecar remain under the private state path for explicit
operator inspection; it is never silently deleted or treated as delivered.
Profile-absent compatibility closes are not yet outboxed. Granted rows without
an authoritative `harness_profile_ref` are refused before execution so they
cannot cross that weaker path.

View file

@ -42,10 +42,10 @@ must never fall back to either.
An unmatched route fails terminally. Most matched executor failures set
`reopen=true`; an unexpected exception also reopens. Activity Core applies its
attempt limit. The harness now records a repository transaction identity, but
Activity Core does not yet carry an accepted repository grant or reconcile a
response-lost terminal close, so a retry after ambiguous completion remains a
T03 gate.
attempt limit. Grant-bearing work now requires an authoritative profile and
therefore cannot enter this registry. Profiled terminal closes use durable
response-loss reconciliation; legacy close delivery remains a T04 migration
constraint rather than a grant-bearing path.
## Scheduled definitions

View file

@ -9,7 +9,7 @@ Does **not** require issue-core or Forgejo for FI / Binky-style ops.
```text
activity-core emit_tasks
→ INSERT ops_run (open, optional harness_profile_ref) + activity_task_spawn
→ INSERT ops_run (open, optional profile + repository_grant) + task_spawn
rein-aharness claim-loop
@ -17,15 +17,18 @@ rein-aharness claim-loop
harness_profile_ref present? ─ yes → Glas ExecutionRequest → gateway
│ no
└─→ select_approach(labels, definition, hint) → legacy executor
POST complete | fail(+reopen)
validate granted repository result + persist external metrics
enqueue exact terminal close → POST complete | fail
replay pending close evidence before another claim
```
`harness_profile_ref` is authoritative under ACT-ADR-006. A profiled row never
consults `approach_hint`, `select_approach`, or a legacy executor, including
after profile resolution refusal. The claim worker sends the complete
`GatewayResult` to Activity Core; Activity Core persists its allowlisted
`ExecutionEvidence` and drops direct tool output/error. Profile-absent rows keep
the legacy coexistence path until the migration flag is enabled upstream.
after profile resolution refusal. The claim worker sends bounded
`ExecutionEvidence`; direct tool output/error and unknown provider fields are
not persisted in its close outbox. A separately typed grant activates commit,
path, clean-state, metadata, and remote-ref acceptance plus external metrics.
Profile-absent rows keep the legacy coexistence path, but cannot carry a grant.
## Environment
@ -87,6 +90,10 @@ rein-aharness poll --source=ops-run
rein-aharness claim-loop
rein-aharness claim-loop --once --verbose
# Required close evidence and recovery
rein-aharness close-outbox status
rein-aharness close-outbox replay
# Dry-run: claim then fail+reopen without domain work
rein-aharness poll --source=ops-run --dry-run
```

View file

@ -1,7 +1,7 @@
# Repository grant contract
Status: **v1 enabled for local `TaskSpec` files; queued/profiled carriage is
not yet available**.
Status: **v1 enabled for local `TaskSpec` files and profiled Activity Core
rows**.
`repository_grant` is the explicit authority envelope for a bounded local Git
mutation. It is separate from task prose, labels, organizational attribution,
@ -53,16 +53,18 @@ Granted runs require durable external metrics and refuse `--no-metrics`. Their
metrics are written outside the checkout only after acceptance, so a successful
run remains clean. See [external-metrics.md](external-metrics.md).
Activity Core and issue-core adapters do not populate `repository_grant`.
Queued/profiled runs therefore cannot claim repository acceptance under this
contract. Existing grant-absent direct and compatibility runs retain their
legacy `HEAD`-changed behavior during migration.
Activity Core revision `b63131e` added strict definition admission and separate
queue carriage. The claim client parses that exact v1 field, refuses malformed
authority, and requires `harness_profile_ref` whenever a queued grant is
present. Profiled success applies the same repository acceptance policy and
external metrics contract as a local granted run.
Queued/profiled execution may use this contract only after:
1. an authoritative Activity Core/profile field carries the reviewed grant;
2. required close evidence durably records the grant, transaction, and accepted
result identities.
Before sending a profiled terminal result, the worker durably records the exact
run, transaction, worker, action, and bounded result. Response-lost closes are
replayed without repeating workload code and Activity Core distinguishes exact
reconciliation from terminal, evidence, owner, state, and lease conflicts.
Grant-absent compatibility runs retain their legacy result behavior during
migration.
Task descriptions, labels, `execution_refs`, consuming-repo defaults, and
profile absence must never synthesize a grant.

View file

@ -32,10 +32,10 @@ rein-aharness claim-loop
`TaskExecutorWorkflow` in activity-core stays a stub; execution lives here.
Activity Core does not yet emit the versioned `repository_grant` required by
the lease-bound transaction path. The harness must not infer one from labels,
task prose, `execution_refs`, or target-repo lookup. See
**`docs/repository-grant.md`** for the parsed-but-not-enabled v1 contract.
Activity Core revision `b63131e` emits a separately typed versioned
`repository_grant`. The harness parses it strictly, requires an authoritative
profile for queued grants, and never infers authority from labels, task prose,
`execution_refs`, or target-repo lookup. See **`docs/repository-grant.md`**.
---