railiance-platform/docs/task-execution-plan-compiler.md
codex 59d1441cf8 fix(execution): retain scoped cleanup after uncertain outcomes
Assistant: codex
Assistant-Model: gpt-6-astra
Assistant-Session: 01a140c4-354a-7823-8f0d-8f717397f786
2026-10-16 01:51:53 +02:00

7.6 KiB

Owner-derived typed metadata plan compiler

Prepared source component for RPF-WP-0035-T12, with completed bounded source preparation RPF-WP-ADHOC-2026-10-11-T02. This component does not authorize, dispatch, authenticate or mint anything. CCR38 stays identity/self-only.

OwnerConfig is independently installed by the Platform owner. It is never parsed from an HTTP body, CheckRequest or a service-supplied configuration. It fixes the Platform repository, Secrets source repository, owner-admitted local catalogue directory and exact CCR/catalogue filenames and reviewed source hashes. The closed source set contains this compiler, existing credential-change validator, CCR, its one reader HCL artifact, catalogue entry and the actual Secrets catalogue loader/package/errors/redactor files. Arbitrary additional files are refused. Paths/hashes are public provenance, not credentials or authority. Source authenticity and admitted runtime/continuous exclusive source custody remain external gates; before/after filesystem hashes detect observed changes, not adversarial same-UID swap-and-restore or arbitrary mutation of Python memory.

The compiler reuses validate_ccr, applier_readiness_blockers, apply_blockers, generated_policy_hcl and auth_payload. It only accepts freshly approved, confirmed production workload-kv-read CCRs whose reviewed HCL matches generated read-only policy, with existing netkingdom OIDC or Kubernetes auth mount. Already applied/verified/active CCRs refuse rather than silently become replay authority. No other lane module, mount enable/config, KV read/write, token/SecretID delivery, AppRole mutation, caller HCL/JSON body, paths or command argv can be compiled.

The catalogue comes from a separately owner-pinned local Secrets catalogue file, loaded by secrets_engine.catalog.validate_entry executed directly from the verified source snapshot, together with its pinned package/errors/redactor. Each compilation uses temporary private module names and removes them on success or failure. Canonical application imports remain untouched. Cached modules and timestamp-based bytecode cannot substitute different code for the verified bytes; new Secrets Engine imports outside the closed source set refuse. This is source consistency within the admitted Python runtime, not a Python security sandbox. Its catalogue id, existing mount, logical path, fields, production stage and approval CCR reference must match the owner CCR. Engine-managed mounts and auth are refused: existing delivery-auth metadata can be carried, but is never applied or authenticated by this action. This is not a new credential-grants catalogue. The exact thirteen-field CatalogEntry projection matches build_action_request.context.catalog_target; its digest uses sorted compact UTF8 JSON with ensure_ascii=False, matching the actual ExecutionContract helper. Actual Forgejo CCR42 is in_flight and no matching Secrets catalogue exists. Neither is invented. Positive tests only make explicitly synthetic approved/pilot copies of existing public owner fixtures in temporary directories; they grant nothing.

Concrete interfaces

Compiler.proposal() locally derives metadata: action execute-ccr, kind workload-kv-read, catalogue id, CCR bytes digest, exact catalogue-target projection and digest, and six ordered operation method/path/payload hashes. It is preparation, not an admission or a complete ExecutionContract. Order matches the proposed Access package: GET/PUT/GET ACL, then GET/PUT/GET OIDC/Kubernetes role. GET payload is empty; PUT JSON bytes are deterministic, sorted compact ASCII JSON containing generated HCL or auth_payload, with no caller-selected body.

Compiler.compile(request) accepts exactly that metadata, operation UUID and the opaque served canonical PDP request digest sha256:<64 lowercase hex>. It compares the exact owner-derived proposal and returns a frozen serialized Plan. It does not recompute Access canonicalization, strip the prefix, validate a PDP or accept an unsigned digest as authority. The authenticated mediator must establish correspondence with exact full CheckRequest/claim/consume bytes independently. Plan content digest is explicitly bare64hex for Journal.Binding.plan_digest; request and catalogue/payload hashes retain their required sha256 prefix.

Compiler.before_call(plan, ordinal) accepts exact integer0..5, recompiles the immutable plan and source bundle, re-derives fresh owner payload bytes and compares again before returning one frozen local Call. A forged Plan constructor, extra field, body hash/path/method/ordinal substitution, source drift or readiness change refuses. Call.call_digest is the bare hash of exact ordinal/kind/method/path/payload hash for the durable journal. The future mediator must validate this call immediately before persisting its journal intent and dispatching these same bytes; this API has no native callback, no backend opener and no credential.

Enforced integration gates still missing

The compiler only binds its closed local component request. It does not construct or approve the complete SECRETS-EXEC-20261011-v1 descriptor. The mediator must compare the exact owner-derived CCR/catalogue/six operations and full independently pinned source bundle, authoritative grant CCR/hash, origin/cluster, approved native before/after state expectations, deadline and TTL/use ceilings against locally admitted inputs. Caller-selected grant/baseline/estate/configuration cannot supply these. Missing authoritative inputs or whole-descriptor correspondence must refuse before protected backend effects. Expected native baseline/readback normalization, conflict handling and source/time enforcement spanning revalidation-to-dispatch remain unimplemented; source comparison alone is not native atomic CAS.

The proposed Access package still hard-refuses owner admission. No initiator projection or verified/member/MFA flags are produced here; existing readiness refusal remains. Actual identity/PIP/representation/service/PDP/claim/consume, Journal admission/replay/unknown state handling, independent provider confirmation, restricted native adapter and whole-request/source/deadline enforcement must be integrated. The proposed task token ceilings are120s/12uses, with at least one cleanup use; this component records limits, it does not enforce native expiry/uses or provide a hard elapsed timer. Trusted owner runtime must bound parser/import/ filesystem/compiler work by remaining action budget before any actual call. Token custody/revoke, temporary object removal, owner-private recovery after unknown and truthful native readback remain gates. The 2026-10-16 source cleanup journal and fixed driver are documented in task-execution-cleanup.md; they preserve unknown outcomes but are not yet bound into this compiler or a production mediator. General lifecycle/apply/exec/default migration and SecretsT06/RPF35T02/T12 cannot close from this source component.

Validation:45 compiler tests and 15 journal tests pass, covering real owner K8s/OIDC body generation, exact catalogue unicode digest interoperability, refusal of alternate action/kind/ path/body/ordinal, re-pinned root policy, nonapproved/unconfirmed/replayed CCR, engine-managed catalogue, forged immutable plan, source drift/duplicates and symlink/hardlink/writable source. No native action, credential/Secret access or owner checkout writes.

October 13 correction: a regression first demonstrated that a cached validator could replace the hashed source. Additional cases cover cached dependencies, invalid catalogue acceptance through a substituted validator, and private-module cleanup on both success and refusal. The October 11 v2 review remains evidence for its original bytes only; it does not attest this corrected revision.