LearnNewsExamplesServices
Frontmatter
id16486
titleThe activation decision has no closed contract: nothing can refuse a mutation for lack of proof
stateClosed
labels
enhancementaitesting
assigneesneo-opus-ada
createdAtAug 4, 2026, 10:03 AM
updatedAtAug 4, 2026, 12:39 PM
githubUrlhttps://github.com/neomjs/neo/issues/16486
authorneo-opus-ada
commentsCount0
parentIssue16448
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 4, 2026, 12:39 PM

The activation decision has no closed contract: nothing can refuse a mutation for lack of proof

Closed Backlog/active-chunk-12 enhancementaitesting
neo-opus-ada
neo-opus-ada commented on Aug 4, 2026, 10:03 AM

Refs #16452

Context

Split out of #16452 per pull-request-workflow.md §9.1, which requires an honest delivered close target and offers exactly this escape: "add the honest delivered leaf close target or split/file the narrow ticket". #16452 asks for the activation kernel to be the only mutation path, enforced. Enforcement needs two halves — a contract that can refuse, and wiring that routes every mutation through it. This leaf is the first half. #16452 stays open and owns the second.

Not a micro-ticket and not an A+FU split of one deliverable: the contract's vocabulary and decision semantics are the part a reviewer must be free to change, and they are much cheaper to change before a deploy path depends on them than after.

The Problem

ai/scripts/maintenance/redeployPreflight.mjs decides correctly and then returns. It leaves no artifact behind, so nothing downstream can distinguish:

  • the preflight ran and passed, from
  • the preflight was never run at all

Both present as "no refusal happened". A mutation path that simply does not call it therefore inherits no protection — which is what makes a reachable guard a bypassable one, the exact distinction #16055 established and #16452 exists to close.

The incident behind this whole line of work was a destructive path that proceeded because nothing positively said no. Absence of proof has to be the refusal.

The Architectural Reality

  • redeployPreflight.mjs:18 already gates on a "verified, non-empty, restorable" bundle, and evaluateRedeployPreconditions is already a pure truth table with IO held at the edges. The shape to match exists; what is missing is a durable output.
  • REDEPLOY_PREFLIGHT_DECISION carries several PROCEED_* variants, and they are not interchangeable for this purpose. PROCEED_INITIALIZING authorizes a declared first install — it asserts the opposite of what an activation receipt must: that there is nothing to restore. Only a RESTORABLE verdict can carry an activation.
  • The selection sub (#16450) hands over a bound stageReceiptId. This leaf must treat it as an opaque identifier — the binding check is a comparison, and reaching into that module's internals would be a layering violation and a false dependency.

The Fix

A pure, IO-free decision module: ai/services/shared/activationReceipt.mjs, sibling to captureReceipt.mjs.

Target Surface Source of Authority Proposed Behavior Fallback / Error Semantics Docs Evidence
decision vocabulary this ticket authorize | refuse, frozen No value means "undecided" module JSDoc closure test over generated cross-product
refusal reasons this ticket Frozen, distinct per cause Unknown cause is impossible by construction module JSDoc every refusal's reason asserted in-vocabulary
receipt binding this ticket Binds stageReceiptId and resolved container config Mismatch on either refuses module JSDoc spec per binding
ordering this ticket Pre-mutation check precedes freshness Post-mutation receipt refuses as such, never as stale module JSDoc spec pins the precedence

The receipt binds the resolved CONTAINER CONFIG, not the image digest. docker compose freezes healthcheck, command and env into the container at create time, so an activation that rebuilds images at the target revision and leaves containers in place moves the code and leaves the contract behind. Measured on neo-local-canonical after PR #16465 merged: image revision label current, merged code present inside the image, merged contract present in the checkout — and the running container's healthcheck predating the change, because the container was created before it. A digest-bound receipt is satisfied by exactly that half-applied state.

Acceptance Criteria

  • The decision function returns only authorize or refuse, proven over a generated input cross-product rather than a hand-written case list — an enumerated list proves only that the enumerated cases are closed, which is where a third state hides.
  • The authorizing set is asserted exactly and by name, so a newly-authorizing combination fails loudly instead of shifting a count.
  • Every refusal carries a reason from the frozen vocabulary; no ad-hoc strings, no nulls.
  • A missing, malformed, or non-object receipt refuses.
  • A non-RESTORABLE verdict refuses, and PROCEED_INITIALIZING is witnessed as one of them.
  • A receipt observed at or after the first mutation refuses as receipt-not-pre-mutation, including at the exact instant, and including when it is also stale — the precedence is pinned, because a staleness-first report makes the operator fix "re-run the preflight", which mints a fresh receipt that is still post-mutation.
  • A stale receipt refuses, and so does a future-dated one.
  • A stageReceiptId mismatch refuses; a resolved-container-config mismatch refuses.
  • Mutation-verified: introducing a third decision value fails the closure test.

Out of Scope

  • Wiring. Emitting receipts from redeployPreflight and consuming them in the reference deploy transaction stay with #16452, which is what "enforced" means there.
  • The docker inspect digest helper. targetConfigDigest is consumed as an opaque string here.
  • Preflight soundness. #16404 owns whether the non-emptiness judgement is correct; this leaf enforces where authority lives, not the quality of its input.
  • Selection policy#16451.

Avoided Traps

  • Accepting any PROCEED_* as sufficient. PROCEED_INITIALIZING would authorize mutating a plane with no bundle.
  • Binding the image digest. Satisfied by the half-applied state measured above; it admits the third state through the binding rather than through the decision.
  • A hand-written closure test. It proves the author's imagination is closed, not the function.
  • Reporting staleness before pre-mutation ordering. See AC above — the "obvious" order produces a second attempt that authorizes an already-touched plane.

Related

Origin Session ID: eeacb603-97f1-4241-9b2f-3a542cab6d2c

Retrieval Hint: query_raw_memories("activation receipt closure two states never three container config not image digest pre-mutation before freshness")