Skip to content

Neutron Mutation Routing — Maintainer Brief ​

Status ​

Slice 2 implemented — host-side semantic authorization and approved-transaction preflight for approved-transaction-apply. Session snapshots remain mutationAllowed: false. No N4 mutation route.

Slice 2.5 implemented (PR #494, merge a75edad5a8501480fa74ed3eb7b40e9ffdcd3f33). Slice 3 implemented (PR #497, merge 816b0ccacafc62c8a1e13a4dbdb452a4cd70a392) — host-only single approved transaction Apply. Slice 3.1 implemented (PR #500, merge cb93edb3f66d90d2522514ae4e9b0725796a0e46) — crash-safe durable approval/transaction state. Slice 4 implemented (PR #503, merge b5c9a9556648382343bd90cc5e2c277d0775cf32) — independent post-Apply verification and sanitized rollback evidence. Slice 5 implemented (PR #507, merge 0eb326986537f125abfefc573e6709824914b37c) — N5 proposal/review integration with host-only graph-linked Apply composition. Slice 5.1 correction (PR #510, merge b36d836c05591599f2526f0d3f5302e7edce8f5b) — fail-closed stale proposal materialization and end-to-end proposal capability clamping in production session composition. Mutation-routing Slice 5 proposal/review integration is security-corrected / complete for authorized scope. Desktop host Approve & Apply is the separately merged D4 operation. DESKTOP MUTATION D5 COMPLETE (PR #540). Post-D5 verification retry is verification-only. Host rollback execution remains unauthorized.

The Slice 3 security review remains the historical NO-GO for Apply on pre-2.5 contracts: NEUTRON_MUTATION_SLICE3_SECURITY_REVIEW.md.

Write tools, generic shell, host rollback execution / Undo, optional N3 Slice 5, and P4l17 remain unauthorized until a later explicit maintainer grant. Post-D5 verification retry is the separate verification-only operation and is not Apply. N6 Slices 1–5 read-only Desktop are implemented separately. Desktop mutation host D1 (read-only authoritative review transport; PR #516) is implemented; it does not expand mutation-routing write authority, Approve/Apply, or N4 tools. D2 implemented and merged (PR #519; exact review UX). D3 implemented and merged (PR #522; typed approval intent + in-process host issuer; no public Approve RPC; no Desktop mutation authority). DL implemented and merged (PR #525; legacy fake Desktop Approved Apply removed). Desktop durableStateDirectory wiring is implemented and merged (PR #528; that prerequisite is not itself D4). D4 implemented and merged (PR #534; intentloom.neutron.mutation.approveAndApply.v1; dedicated Tauri command; renderer sends identity fields only; mutationAllowed remains literal false). D5 Slice 1 implemented and merged (PR #537, merge 866c96ab6fd1a1265dfc46a8840117baaaebd0b6; final audited head c366d9f7d8d27f2a6f92b328ee775e1ceab53381): read-only intentloom.neutron.mutation.status.get.v1 recovers the durable Slice 3.1 outcome after a lost D4 response. DESKTOP MUTATION D5 COMPLETE (Slice 2 PR #540). Post-D5 verification retry is verification-only. Undo remains unauthorized. Canon: NEUTRON_N6_DESKTOP_MUTATION_HOST_BRIEF.md.

Authoritative roadmap gate: NEUTRON_RUNTIME_ROADMAP.md §N5–§N6 and this brief.


1. Current baseline ​

ItemEvidence
Current main SHAb36d836c05591599f2526f0d3f5302e7edce8f5b (main == origin/main after Slice 5.1 correction)
N5 Slice 5PR #453 head a6819a49fe3cf1adbda93ea835d96fdf1decfc52, merge 7a6e07c
N5 handoffPR #454 head 502629ffe7e5256a5fa4ad3b15b37c4760747240
N1–N5Runtime contracts, model adapter, context assembly, read-only tool router, executable task graph
mutationAllowedN1 NeutronRuntimeSession.mutationAllowed is typed false; validator rejects any other value
N4 cataloginspect, doctor, memorySearch, timeline, conformance, securityAudit, projectDiff
Mutation implementationSlice 1–2.5 contracts/preflight/artifact + Slice 3 host-only Apply + Slice 3.1 durable claim + Slice 4 verification evidence + Slice 5 N5 proposal/review integration

Capability summary used by this brief ​

SurfaceRole today
N4Fail-closed read-only router. definition.readOnly !== true → mutation-forbidden. Capabilities must be readOnly: true and allowNetwork: false.
N5One-wave scheduler with leases, retry, cancellation, stale project/checkpoint/profile detection, provenance. No Apply.
ADR-0053Approved Apply gate + executeApprovedApplyPlan → synchronizeGeneratedFiles.
Daemonintentloom.project.approvedApply.v1 exists; handler is optional and currently unwired in Desktop spawn.
DesktopLegacy fake Approved Apply path removed (DL merged PR #525). Daemon intentloom.project.approvedApply.v1 remains unwired from Desktop spawn. No production Desktop path fabricates Apply success.

2. Mutation-routing definition ​

Mutation routing is not “the model writes files.”

It is:

text
model / N5 subagent
  → typed mutation proposal (no bytes written)
  → affected-scope + policy/capability checks
  → review artifact
  → host-issued explicit human approval (bound record)
  → N4 mutation-class route for one already-approved transaction
  → canonical executeApprovedApplyPlan
  → synchronizeGeneratedFiles
  → verification + evidence + rollback metadata

Invariant (unchanged): model output, repeated success, subagent results, schedules, tool requests, or a model-supplied approved: true / grantedApprovals array never constitute mutation approval.

The router must never hand the model writeFile, rm, mkdir, chmod, arbitrary rename, unrestricted patch, shell, git, or dependency installation.


3. Existing mutation foundations ​

Implementation and tests are authoritative. Do not invent a second transaction engine.

3.1 Diff / proposal ​

PrimitiveLocationMutation?
diffProject@intentloom/application; N4 projectDiff; daemon intentloom.project.diff.v1No. applied: false; contents omitted in N4.
inspectProject / doctorProjectApplication + N4No. Doctor dryRun: true in N4.
Agent Workspace PlanpromoteWorkspaceConversationToProposal → .aif/proposals/<id>.jsonNo.
Agent Workspace ReviewreviewWorkspaceProposalNo.
Existing-project adoption planplanProjectAdoption / adoptProject proposalNo until apply.
ApprovedApplyPlanpackages/protocol/src/approved-apply.tsSchema only: planDigest, projectStateDigest, targetRoot, changedPaths, optional expiresAt.

Change representation for Neutron Apply: ApprovedApplyPlan + the GeneratedFile[] payload consumed by executeApprovedApplyPlan. N4 projectDiff stays read-only and must not become Apply.

3.2 Transaction / apply engines (do not replace) ​

EngineAPIWhat it writesUse for Neutron?
Generated-file transactionsynchronizeGeneratedFilesPlanned generated/metadata bytes; collision/path checks; staged write; rollback on failure; post-write consistencyYes — inner writer for Approved Apply
Approved ApplyevaluateApprovedApplyPlan + executeApprovedApplyPlanGate then sync; rollback evidence of previous bytesYes — canonical Neutron Apply (ADR-0053)
Adoption applyapplyProjectAdoptionDuty-watch/governance pack create ops + journalNo. Wrong artifact set (desktop pre-apply review).
Workspace applyapplyWorkspaceProposalRequires non-empty approvedBy, then calls adoptProjectNot general file mutation. Human-approval pattern only.
ScaffoldapplyProjectScaffold / rollbackProjectScaffoldInception/foundation scaffoldsNo.

Daemon method intentloom.project.approvedApply.v1 already types ApprovedApplyRequest → ApprovedApplyExecutionResult. Rust allowlist includes the method. Production Desktop does not currently inject options.approvedApply.

3.3 Approval infrastructure ​

RecordBindingGap for Neutron
ApprovedApplyRequest.grantedApprovalsString list; gate requires "atomic-commit-approval"Spoofable if the model or a tool argument supplies the list. Not a bound token.
ExistingProjectAdoptionApprovalapprovalId, approvalDigest, approvalToken, root, planDigest, projectFingerprint, approvalValidUntil, source local-interactiveStronger pattern. approved: true is host-written, not model-written. Not wired to Neutron.
Workspace approvedByNon-empty stringIdentity string only; no digest/expiry/scope.
Desktop modalHost click injects ["atomic-commit-approval"]Correct injection locus; current App success path is fake.

Reuse: host-issued bound approval (adoption-approval shape) + Approved Apply plan/request/result. Do not accept model-controlled grantedApprovals.

3.4 Evidence / verification / rollback ​

PrimitiveEvidence
Sync transaction resultstatus, created/updated/unchanged paths, rollbackCompleted, rollbackFailures, post-write consistency (docs/reference/GENERATED_FILES.md)
Approved Apply resultapplied, gateResult, optional rollbackEvidence (planDigest, targetRoot, previous bytes or null for creates)
N5 provenanceParent/child, attempt, capability, N3, N4 digests (neutron-scheduler-provenance.ts)
N5 staleProject fingerprint, checkpoint authority, profile fingerprint (detectNeutronGraphStaleness); rerunAttempted: false
Doctor / diff / conformance / securityExisting N4 read-only tools after Apply
Desktop stub rollbackPlaceholder "// previous snapshot content" — not evidence

3.5 Path / sandbox ​

ControlCurrent strength
N4 trustedRootSession root equality; rejects .. in tool root. No realpath.
N4 session bindInvocation root/sessionId must match session.
Sync noncanonicalPathPlanRejects non-canonical generated paths before write.
Sync pre-replaceComment: revalidate to narrow symlink substitution races.
Inspectioninspection-root-symlink diagnostic exists on inspect, not on N4 mutate (there is no N4 mutate).
Extension lock / scaffoldrealpath / reject symlinked roots.

Mutation raises the cost of N4’s string-equality root check. Slice work must reuse sync/canonicalization and add Neutron preflight realpath containment before Apply.


4. Proposal vs execution authority ​

Two distinct classes. The model cannot self-grant Apply.

4.1 Proposal capability (non-mutating) ​

Allows a role to:

  • describe a bounded change;
  • emit a structured ApprovedApplyPlan (digest, baseline fingerprint, root, changed paths, expiry);
  • identify affected files;
  • request that the host create a review/transaction record.

Does not write project bytes. May be an N5 task output or a future read-adjacent tool (proposePatch / createTransaction) that only persists a proposal under .aif/ if a later slice explicitly adds that store. Initial recommendation: proposal is structured N5/task output + host materialization, not a filesystem tool.

4.2 Apply capability (mutating) ​

Allows the host to execute one already-approved ApprovedApplyRequest through executeApprovedApplyPlan.

  • Not granted to any NEUTRON_DELEGATED_AGENT_ROLES value.
  • Not granted because a parent task or model tool-call asked for it.
  • N4 exposes at most one mutation-class operation initially: applyApprovedTransaction.
  • Payload is a host-held approval reference + plan digest, not file bytes invented at Apply time.

5. Approval contract ​

Do not accept generic approved: true from model-controlled input.

5.1 Required binding (Neutron approval record) ​

Host-issued after explicit human Approve. Fields (canonical names may match adoption approval where they already exist):

BindWhy
approvalId / approvalDigest / approvalTokenUnforgeable host secret; digest covers the bound facts
root / projectId / sessionIdSession and project containment
graphId / taskId (optional but recommended)N5 provenance
transactionId / planDigestExact proposal
changedPathsAffected-file scope
expectedMutationTypee.g. approved-apply-generated-sync
capabilityScopeProfile/delegation clamp snapshot
projectStateDigest / N5 project fingerprintBaseline
checkpoint / profile fingerprints when presentN5 stale dimensions
approvedAt / approvalValidUntilExpiry
approvingActor / approvalSource: local-interactiveHuman, not model

The Apply path receives this record from Desktop/CLI/daemon session state, never from tool argumentsJson.

5.2 Protocol gap ​

NeutronRuntimeSession.mutationAllowed is a literal false in protocol and validator. Enabling session-level mutation requires a versioned contract change (additive session field and/or new approval schema URN). This docs task does not change protocol.

Recommendation: keep session mutationAllowed: false for all model-facing snapshots. Apply authority lives on a host approval record, not on flipping the session boolean from model-visible state. If a later slice needs a session flag for UI, it must be host-authored and still insufficient without the bound approval.

grantedApprovals: ["atomic-commit-approval"] remains the inner Approved Apply gate string. Only the host adapter may attach it after validating the Neutron approval record.


6. Stale-state / TOCTOU ​

Approval at state A must not apply against state B.

evaluateApprovedApplyPlan compares projectStateDigest only whencurrentProjectStateDigest is passed. Expiry is optional. The engine does not compare filesToApply to changedPaths.

6.1 Mandatory Apply preflight (fail closed) ​

Revalidate all of:

  1. Project fingerprint / projectStateDigest (required, not optional).
  2. N5 detectNeutronGraphStaleness when the proposal came from a graph (project, checkpoint, profile). No automatic rerun.
  3. planDigest matches stored immutable proposal.
  4. Approval token valid, unconsumed, unexpired, same root/session.
  5. Effective capabilities still match approval capabilityScope.
  6. Session/task not cancelled; no terminal session state.
  7. filesToApply paths ⊆ approved changedPaths (set equality or strict subset per atomic policy; initial: exact set).
  8. Canonical root containment (realpath under selected root).
  9. No other active mutation transaction on the project.

On any mismatch: fail closed. Require re-review and a new approval. No automatic mutation against stale assumptions.


7. Capability model ​

Effective mutation authority:

text
host approval
  ∩ session root/sessionId
  ∩ profile.allowedCapabilities
  ∩ delegation.effectiveCapabilities
  ∩ task/node requiredCapabilities
  ∩ transaction changedPaths

AgentRoleCapabilities today: readOnly, allowedPaths, allowedTools, maxBudget, allowNetwork. There is no mutationAllowed on the role object.

7.1 Existing delegated roles ​

context-scout, feature-builder, test-engineer, reviewer, release-analyst.

Do not invent executor.

RoleProposeApproveApply
context-scoutNoNoNo
test-engineerNoNoNo
release-analystNoNoNo
reviewerNo (review artifact only)No — human only for initial slicesNo
feature-builderYes, if profile/task grant proposal classNoNo
Human / Desktop / CLI hostMaterialize reviewYesYes (after approval)

Read-only profiles (readOnly: true) cannot Apply. A child cannot gain proposal or Apply authority because a parent requested it. delegateTaskRole remains an intersect, never a union.


8. N4 integration ​

Keep the seven read-only definitions unchanged (readOnly: true, rejectMutationFlags, trustedRoot).

8.1 Additive mutation class ​

  • New tool name not in NEUTRON_READ_ONLY_TOOLS (requires protocol union extension in the same slice that adds the tool — not Slice 1 if Slice 1 is contracts-only).
  • readOnly: false / mutating: true.
  • Authorization: reject unless host approval record is present; do not use today’s assertReadOnlyTool global ban without a new branch.
  • Do not require capabilities.readOnly === true for the mutation tool; require a distinct permission class and still forbid allowNetwork.
  • Do not convert projectDiff into Apply.

Initial mutation catalog: one operation, applyApprovedTransaction.

No proposePatch tool in the first Apply-capable slice if proposal stays N5-structured output. If a later slice adds proposal persistence, that tool stays readOnly: true with respect to the project tree (or writes only a reviewed .aif proposal path under a typed capability — separately authorized).


9. N5 integration ​

Compose N5; do not bypass it.

  • A node may propose under proposal capability and finish completed with a proposal digest in the subagent result.
  • Graph aggregation remains observational. completed ≠ approved.
  • Scheduler must stop at the review boundary. No node Apply.
  • Leases stay per attempt; they do not serialize project mutation (see §14).
  • detectNeutronGraphStaleness + reconcileNeutronTaskGraphExecution run before Apply when the proposal is graph-linked.
  • Provenance must record proposer task/attempt, model/provider, tool envelopes, approval id, plan digest, apply result.
  • Retry of a proposal node follows existing N5 retry rules.
  • Retry of Apply is forbidden after mutation may have started (§14).

10. Tool design and prohibitions ​

Initial mutation routing operates only on canonical Approved Apply / GeneratedFile structures.

Forbidden unless a future separately reviewed typed capability exists:

writeFile, rm, mkdir, chmod, arbitrary rename, unrestricted patch, shell, git command, dependency installation, generic MCP write.

Conceptual names (proposePatch, createTransaction, stageApprovedChanges) map onto existing APIs; they are not a new writer.


11. Affected-file scope ​

Approval binds changedPaths.

Test: approved A.ts + B.ts; execution touches C.ts → reject the entire transaction (synchronizeGeneratedFiles is already all-or-nothing at the plan gate for collisions/paths; Neutron preflight must fail before write).

No silent partial widening. allowedPaths on the profile further intersects the approved set.

Implementation gap: executeApprovedApplyPlan does not check filesToApply ⊆ plan.changedPaths. applyBoundedExecutionChange checks outsideApprovedPaths before calling the engine. Neutron must enforce this in the mutation preflight (and should close the engine gap in the same implementation milestone).


12. Root containment ​

Reuse N4 session-root equality and raise it:

  • reject .., absolute roots ≠ session root, sibling projects;
  • realpath selected root and every apply path; reject symlink escape;
  • reuse noncanonicalPathPlan / sync symlink revalidation.

A containment bug at mutation severity is a security defect, not a UX miss.


13. Transaction atomicity ​

Proven today (synchronizeGeneratedFiles + tests / generated-files docs):

  • Pre-write collision, ownership, and path-security failures do not enter the transaction.
  • In-transaction failure attempts rollback of written destinations.
  • Success requires post-write byte/checksum consistency.
  • Incomplete rollback adds transaction-rollback-incomplete and listed project-relative paths; success is never reported.

Not proven / do not claim:

  • Cross-process crash-safe journal for the Approved Apply write engine (adoption journal is a different engine and is written after success). Slice 3.1 persists approval/transaction claim state crash-safely; it does not make executeApprovedApplyPlan itself a crash-atomic disk journal.
  • Desktop stub Apply atomicity (it does not write).

Slice 4 closed the failed-sync rollback-evidence gap: executeApprovedApplyPlan now attaches rollbackEvidence when synchronization fails. Neutron public evidence projects path/digest/status only (no raw previous bytes). Independent post-Apply verification still does not make the writer a crash-atomic disk journal.

Initial Neutron Apply is staged then commit via the existing sync engine: atomic relative to that engine’s rollback, best-effort if rollback is incomplete. Recovery evidence is the transaction diagnostics + any captured previous bytes.


14. Cancellation, expiry, replay, concurrency, retry ​

Cancellation ​

WhenRequired behavior
Before approvalDrop/cancel proposal; no write.
After approval, before ApplyInvalidate approval; no write.
During transactionFollow sync cancellation/rollback; no detached background Apply.
During verificationMutation already committed; record verification failure; do not silently roll back unless the user requests authorized rollback.

N5 session/node cancel already blocks new admission. Apply must observe the same AbortSignal / session cancelled state.

Expiry ​

ApprovedApplyPlan.expiresAt is optional. Neutron approval must have approvalValidUntil. Expired → no Apply, fresh review. Model cannot refresh approval.

Recommend a short bound (implementation default: ≤ 30 minutes, or the existing adoption approvalValidUntil policy if reused). Session timeout already fails N4 tools.

Replay / idempotency ​

No consumed-approval store exists today. Second Apply of the same approval must fail (one-shot) unless the transaction layer later proves safe idempotency for identical bytes + digest.

Stale baseline after a successful Apply will also fail closed (fingerprint changed). That is not a substitute for consumption tracking (replay against a restored tree would otherwise succeed).

Concurrent mutation ​

N5 leases serialize task attempts, not project files.

executeApprovedApplyPlan has no project lock.

Initial rule: one active mutation transaction per project. Concurrent approved transactions on the same or independent files wait or fail closed. Finer-grained locking is out of scope until the transaction layer proves it.

Retry ​

  • Failed preflight (no write started): host may retry preflight.
  • No automatic N5 retry of Apply after mutation may have started.
  • Partial/failed transaction: reconcile (rollbackCompleted / transaction-rollback-incomplete) before any new Apply.
  • N5 maxAttempts 2 must not wrap the mutation tool.

15. Verification after mutation ​

A successful write is not sufficient.

After applied: true, run typed existing operations only (no arbitrary commands):

  1. Result fingerprint ≠ baseline (or equals expected post-apply digest if supplied).
  2. Affected paths ⊆ approved set (diff/doctor change list).
  3. Optional diffProject vs approved plan (bounded, same as N4).
  4. Optional doctorProject (read-only).
  5. Conformance / security list only if the approval listed them.

The model may receive structured verification evidence. It does not decide that an unauthorized write was acceptable. Policy stays outside the model.


16. Rollback ​

Prefer the existing sync + Approved Apply rollback evidence. Do not add a second backup system.

QuestionAnswer from current code
Snapshot before Apply?Per-file previous bytes captured in executeApprovedApplyPlan before sync; plus sync’s own staging backups.
Restore affected files?Sync rollback on failure; rollbackEvidence on success for later human revert.
Automatic rollback on mid-transaction failure?Yes, via sync; incomplete rollback is evidenced, not silent.
User rollback after successful Apply?Evidence exists; no Neutron-authorized “rollback tool” yet. Future slice: host-only revert using rollbackEvidence, same approval class.
Who authorizes rollback?Human/host, same as Apply. Model cannot roll back to hide a write.

Slice 4 closed the failed-sync rollbackEvidence omission. Neutron operator evidence uses path, digest, existedBefore, restored, and rollback status — not raw previous file bodies.

U1 host preflight confirmed the boundary for a later user Undo, which is a different operation from failed-Apply recovery. Previous file bodies exist only inside the ephemeral Apply rollbackEvidence. After a successful Apply the Slice 3.1 record keeps digests, not those bytes. An updated path is therefore not eligible (undo-source-unavailable). A verified created path can be eligible for a future delete while the current bytes still match the stored post-Apply digest. U1 does not persist snapshots, does not execute Undo, and does not rewrite historical applied: true. U2 would persist a trusted pre-Apply snapshot for new mutations only. U3 would add a separate host Undo authorization and execute one transaction. Those slices are not authorized.


17. Provenance / audit ​

Every mutation must answer:

QuestionSource
Who approved?Approval approvingActor / source
Project / session / task?Session + N5 node
Model / provider?N2 adapter capability on the proposing attempt
Proposal digest / files / baseline?Plan + fingerprints
Transaction / capabilities?Apply request + clamp snapshot
When approved / applied?Approval + execution timestamps
What changed?Sync path lists + rollback evidence
Verification?Typed post-apply results
Rollback available/performed?Evidence + transaction diagnostics

No hidden reasoning. N5 provenance digests stay; Apply adds approval and transaction digests.


18. Protocol / daemon implications ​

Existing daemon method is sufficient transport for Apply: intentloom.project.approvedApply.v1.

Still required before Desktop N6 can render a real loop:

ArtifactAction
Neutron approval schema + validatorNew (Slice 1)
Proposal / review viewmodelNew or reuse adoption viewmodel fields
Session mutationAllowedKeep false for model snapshots; do not treat as Apply grant
NEUTRON_READ_ONLY_TOOLSUnchanged until the Apply tool slice
Apply resultReuse ApprovedApplyExecutionResult
PathsProject-relative only; no loose command strings

Do not expose filesystem commands. Desktop must call the daemon, not a webview stub.


19. N6 Desktop implications (do not implement) ​

N6 is unauthorized. The mutation contract N6 will need:

  • proposed changes, affected files, diff;
  • role/capabilities, provider/model, tools used;
  • stale warning, approval scope, Approve / Reject;
  • Apply state, verification, rollback availability.

Roadmap §N6 first milestone is still read-only Neutron (session, inspect, plan, evidence). Approved Apply is a “separate threat-reviewed stage.”

Desktop today: allowlisted approvedApply RPC; modal injects the inner approval string; Apply is stubbed in App.tsx. N6 must not ship that stub as a mutation path.


20. packages/neutron-runtime decision ​

N5 deferred the package until an N6 consumer or cross-package pressure. Re-evaluation:

FactImplication
22 neutron-scheduler-*.ts modulesFile-count trigger from the N5 brief is already exceeded
Canonical Apply lives in @intentloom/applicationapproved-apply-engine, synchronizeGeneratedFiles
Desktop must not import @intentloom/applicationAlready true; Desktop uses protocol + daemon
Daemon already has Approved Apply RPCNo new package needed to carry mutation
Creating a runtime package nowLarge migration; mixes scheduler + transaction writer

Decision: keep mutation routing in @intentloom/application (neutron-mutation-*.ts modules + existing approved-apply files). Do not create packages/neutron-runtime during mutation slices.

Re-introduce the package at the N6 boundary only if Desktop needs a runtime-shaped client that is not the daemon protocol. Mutation routing is not that justification.


21. Threat model ​

ThreatMitigation
Prompt injection asks the model to writeNo write tools; Apply not in model catalog; proposal ≠ approval
Capability escalationIntersect clamp; child cannot widen; read-only roles denied
Approval spoofingHost-issued token/digest; ignore model grantedApprovals / approved
Approval replayConsume approval; one-shot Apply
Stale approvalMandatory fingerprint + N5 stale + expiry
Affected-file wideningExact/subset path check before write; fail whole transaction
Path traversalN4 .. + session root + sync noncanonical paths
Symlink escaperealpath preflight; sync symlink revalidation
Transaction tamperingImmutable plan digest; Apply by reference
Partial writeExisting sync rollback; evidence on incomplete rollback
Duplicate ApplyConsumed approval + changed baseline
Retry after partial writeNo automatic Apply retry; reconcile first
Concurrent conflicting ApplyOne active mutation per project
Cancelled Apply continuingAbort + session cancel; no background Apply
Malicious model patchHuman review of exact paths/bytes; digest bind
Dependency/script injectionNo install/shell tools
Secret exfiltration via mutationN3 secret-like path exclusion; approval still required for any write
Tampered verificationTyped existing ops only; host displays raw results
Rollback failuretransaction-rollback-incomplete + listed paths; no success

22. Implementation slices ​

Derived from gaps above. Slices 1–5 are implemented. Desktop host Approve & Apply is merged as D4. DESKTOP MUTATION D5 COMPLETE (PR #540). Post-D5 verification retry is verification-only. Host rollback execution remain unauthorized. Desktop host-flow brief: NEUTRON_N6_DESKTOP_MUTATION_HOST_BRIEF.md. See NEUTRON_MUTATION_SLICE3_SECURITY_REVIEW.md for the historical pre-2.5 Apply blockers.

Slice 1 — contracts and validators only (implemented) ​

Neutron approval / proposal / Apply-preflight schemas and validators. Session snapshots remain mutationAllowed: false. No router mutation tool. No Apply. Unblocks N6 from inventing a second DTO. See §30.

Slice 2 — router authorization + approved-transaction preflight, no Apply (implemented) ​

Mutation permission class and preflightNeutronMutation return eligible or rejected diagnostics only. Zero project writes. See §31.

Slice 2.5 — content-bound review artifact + declared-path Apply contract (implemented) ​

Prerequisite for Slice 3. Bind exact per-path content digests into proposalDigest / computed planDigest. Canonical path-set helper. Host-held review artifact for bytes. Declared-path mode of the canonical writer so undeclared .aif metadata cannot widen scope. Tests for byte-swap fail-closed. No Apply RPC, no N4 write tool, mutationAllowed remains false.

Slice 3 — single approved transaction Apply (implemented) ​

Host-triggered Apply only (not an N4 mutation tool). After Slice 2.5: lock, claim/consume, final pre-write checks, declared-path executeApprovedApplyPlan. No N5 automatic retry. Original Slice 3 used in-process memory authority. See §32.

Slice 3.1 — crash-safe durable approval/transaction state (implemented) ​

Replace production in-memory claim/replay state with a host-controlled durable store. Claim, executing, applied, and failed-needs-reconciliation survive process restart. Same approval cannot become unused after crash. See §32.1.

Slice 4 — verification + rollback evidence (implemented) ​

Independent post-Apply byte/path verification, observed project-state digests, sanitized rollback evidence on failed sync, and durable verification status. applied and verification status stay distinct. Doctor/Diff/ Conformance/Security Audit are not Slice 4 health gates (diffProject does not prove reviewed-byte equality). Desktop verification UX is deferred. See §32.2.

Slice 5 — N5 proposal/review integration (implemented; Slice 5.1 corrected) ​

Feature-builder proposal in graph results; stop at review; provenance includes approval/apply; no node-level Apply. Post-merge gaps closed in Slice 5.1 (PR #510). See §32.3 and §32.4.


Slice 1 — mutation/approval contracts + validators only.

Rationale: protocol and approval binding are the unsafe gaps (mutationAllowed literal false; spoofable grantedApprovals; optional digest). Wiring Apply before those contracts would repeat the Desktop stub failure mode (UI “success” without a bound token).


24. Mutation-routing exit gates ​

  • No Apply without explicit valid host approval
  • Approval bound to exact transaction/proposal digest
  • Stale baseline rejects Apply
  • Affected scope cannot widen
  • Root + symlink containment
  • Read-only roles denied
  • Approval not improperly replayable
  • Cancellation respected; no detached Apply
  • Concurrent conflict: one active mutation per project
  • Apply uses executeApprovedApplyPlan / synchronizeGeneratedFiles only
  • No generic filesystem write or shell
  • Post-apply typed verification
  • Evidence/provenance complete
  • Rollback behavior proven (including incomplete rollback diagnostics)
  • Deterministic fixtures; cross-platform CI; full pnpm verify

25. N6 sequencing ​

N6 READ-ONLY MAY BEGIN IN PARALLEL AFTER Slice 1 contracts.

Evidence:

  • Roadmap §N6 first Desktop Neutron flow is read-only and already has N1–N5 contracts.
  • Mutation Apply UI is a later threat-reviewed stage.
  • Slice 1 prevents Desktop from inventing a second approval/plan DTO.
  • Full mutation slices are not required to render discuss/inspect/plan.

Do not begin N6 in this task. Do not treat the current Approved Apply stub as the N6 Apply path.

Alternative rejected: defer all Desktop until mutation complete — that blocks the documented read-only Neutron Workspace for no containment benefit. Alternative rejected: start N6 before Slice 1 — Desktop would fork schemas.


26. Human-in-the-loop ​

Initial mutation routing requires explicit human approval. No “auto-approve after tests pass.” Future policy automation needs a separate authorization.


27. Open decisions (non-blocking for Slice 1) ​

#DecisionRecommendationBlocker for
1Flip NeutronRuntimeSession.mutationAllowedKeep false on model-facing sessions; host approval is the grantSlice 3
2Persist proposals under .aif/ vs in-memory reviewInjectable host-held payload store; not model-path .aif filesSlice 5
3packages/neutron-runtimeKeep in application; revisit at N6N6
4User rollback tool after successHost-only revert remains unauthorized; Slice 4 shipped evidence onlylater
5Exact vs subset changedPathsExact set for Slice 3Slice 3

28. Recommendation ​

MUTATION SLICE 5 SECURITY-CORRECTED — DESKTOP APPLY UNAUTHORIZED

Slice 5 N5 proposal/review integration is implemented; Slice 5.1 (PR #510) closed stale proposal materialization and production capability-threading gaps discovered after Slice 5 merge. Do not start Desktop Approve/Apply UX, Desktop verification UX, host rollback execution / Undo, an N4 mutation tool, autonomous execution, or automatic Apply retries from this brief. The Slice 3 security review remains the historical record of why unmodified Slice 2 Apply was unsafe. Next first action: none from automation — await explicit maintainer authorization.


29. Explicit non-goals (this brief) ​

  • Implementing mutation routing or write tools
  • Generic shell
  • Changing project bytes through Neutron
  • N6 Desktop implementation
  • Optional N3 Slice 5
  • Returning to P4l
  • Creating packages/neutron-runtime
  • Auto-approval policies

30. Slice 1 implementation record ​

Authorized contracts-only slice. Ownership stays in @intentloom/protocol and @intentloom/validator. No packages/neutron-runtime.

DecisionRecord
Proposal vs ApprovedApplyPlanEnvelope (NeutronMutationProposal) wrapping ApprovedApplyPlan. No second changedPaths / digest / root representation.
Bound approvalNew NeutronMutationApproval using the adoption token/digest pattern (approved:<proposalDigest>, digest of unsigned facts). Not a grantedApprovals array.
Mutation classNarrow approved-transaction-apply only.
mutationAllowedUnchanged literal false on NeutronRuntimeSession.
Digestssha256:<64 lowercase hex> over code-point-canonical JSON. Paths: normalizeStoredPath, unique, code-point sort.
PreflightTyped request/result only. Semantic evaluation (live fingerprint, consumption, symlink realpath, lock) is Slice 2.
Structural vs semanticValidators throw closed on schema/binding errors. Result reasons are reserved for later semantic decisions.
SymlinkStructural path checks do not claim containment against symlink escape.
N4 / ApplyNo mutation tool. No executeApprovedApplyPlan.
N6 read-onlyContract gate satisfied for a separate brief. Not authorized here.

31. Slice 2 implementation record ​

Authorized semantic-preflight slice. Ownership stays in @intentloom/application (neutron-mutation-*.ts). Slice 1 protocol and validator contracts are reused. No packages/neutron-runtime. No daemon RPC. No Desktop UI.

DecisionRecord
AuthorizationHost-only approved-transaction-apply permission class. Model output, approved: true, grantedApprovals, and read-only/delegated roles cannot authorize. Successful preflight does not grant Apply or flip mutationAllowed.
Semantic checksStructural validity, mutation class, proposal/plan/project-state digests, host approval source local-interactive, token/digest binding, expiry via injected clock, root/project/session/task/graph binding, exact affected-path set, capability, cancellation, injected replay checker, realpath containment.
evaluateApprovedApplyPlanNot reused. That gate treats grantedApprovals as authorization, which Neutron forbids.
ContainmentCanonical realpath of the project root; nearest existing ancestor for missing targets; reject symlink escape, traversal, absolute paths, and filesystem errors. No lexical-only fallback.
Path scopeApproval changedPaths cannot widen the canonical proposal/plan set. Extra approved paths → approval-scope-mismatch. Unapproved plan targets → affected-path-mismatch.
ReplayInjected isApprovalConsumed only. No durable consumption store.
Mutation lockObservational type only (NeutronMutationLockObserver). Slice 2 does not acquire a lock and does not claim concurrency safety.
Zero-writeEligible preflight leaves project fingerprint and target bytes unchanged. Modules do not import executeApprovedApplyPlan or synchronizeGeneratedFiles.
TOCTOUSlice 3 must repeat project-state digest, path containment, approval validity/consumption, exact affected scope, and project lock immediately before the first write.
mutationAllowedUnchanged literal false.
N4Read-only catalog unchanged. classifyNeutronMutationRoute reports applyAuthorized: false. No applyApprovedTransaction tool.

32. Slice 3 implementation record ​

Authorized host-only Apply slice. Ownership stays in @intentloom/application (neutron-mutation-apply*.ts). Slice 1–2.5 contracts are reused. No packages/neutron-runtime. No daemon RPC. No Desktop Approve/Apply UI.

DecisionRecord
Call pathHost applyApprovedNeutronMutation → parse envelope → exclusive realpath lock → atomic claim → final pre-write validation → executing → trusted declared-path executeApprovedApplyPlan → persist terminal result → release lock.
AuthorizationHost kind + Slice 2.5 reviewArtifactDigest required. Envelope grantedApprovals, approved, and mutationAllowed are rejected. Inner atomic-commit-approval is injected only by the trusted adapter after host checks pass.
Claim / lock orderLock first, then claim. Stronger than claim-before-lock: a second project writer cannot claim while the lock is held. Cancel before claim leaves no durable state.
Approval lifecycleissued (absent) → claimed → executing → applied, or failed-before-write / failed-needs-reconciliation. In-process injectable store; default is a process-global memory map. No consumer-repo .aif JSON. Process crash leaves claims empty; in-flight work is unknown until reconcile.
ReplayApplied transaction identity (transactionId, approvalId, reviewArtifactDigest, planDigest) returns the prior terminal result without a second write. Consumed/applied/claimed approvals cannot be reused to mutate.
Mutation lockExclusive in-process lock keyed by canonical project realpath. Bound to transactionId. Conflict fails closed with no wait. Released on every terminal path this process still owns.
WritersyncMode: "declared-paths-only". Undeclared .aif/manifest.lock.json and .aif/source-map.json are not written.
applied: trueOnly after canonical transaction success. Engine rollback evidence and previous file bodies are not copied into the Neutron result. Approval token is redacted from results, errors, and diagnostics.
mutationAllowed / N4Unchanged literal false. Seven read-only tools. No applyApprovedTransaction catalog entry.
Next unauthorized slicesDesktop Approve/Apply UX; Desktop verification UX; host rollback execution / Undo; N4 mutation tool; autonomous retry.

32.1 Slice 3.1 durable authority ​

Slice 3 shipped with an in-process Map as the production default store. That lost claimed/executing/applied state on restart, so a used approval could look unused. Slice 3.1 (PR #500, merge cb93edb3f66d90d2522514ae4e9b0725796a0e46) replaces that default.

DecisionRecord
Production storeapplyApprovedNeutronMutation requires an injected NeutronMutationApprovalStore or host durableStateDirectory. Missing both fails closed (mutation-state-unknown). createMemoryNeutronMutationApprovalStore remains test-only.
Persistence locationHost-controlled directory (approvals/ + locks/). Not model-supplied. Not among user project source files. Not .aif generated-adapter metadata.
AtomicityExclusive create (wx + fsync) for first claim. Transitions use temp + fsync + rename under a fail-fast directory gate. Corrupt, partial, checksum-invalid, or version-unsupported records fail closed and never look unused.
Claim / lock orderClaim persisted first, then lock. A crash before claim leaves the approval unused and does not plant a stale project lock. Two processes cannot both observe the same approval as unclaimed.
Restartapplied returns the prior terminal result without a second write. executing becomes mutation-state-unknown with reconciliationRequired: true and stays consumed. failed-before-write and failed-needs-reconciliation remain non-reusable.
Mutation lockDurable exclusive lock file when durableStateDirectory is set (canonical realpath key, transaction ownership, fail-fast conflict). Process-local Map lock remains only for injected memory-store tests. The host process model is not proven single-process.
SecretsDurable records store ids and digests, not raw approvalToken, file bodies, or model prompts. Schema version is validated on load.
Next unauthorized slicesUnchanged from §32.

32.2 Slice 4 verification evidence ​

Slice 4 (PR #503, merge b5c9a9556648382343bd90cc5e2c277d0775cf32) adds independent post-Apply verification. It does not expand mutation authority.

DecisionRecord
Call pathCanonical Apply → persist write outcome (applied or failed) with pending verification if needed → independent filesystem reads → persist terminal verification. Crash after Apply resumes read-only verification only.
Status separationapplied is the write outcome. Verification status is verified, verification-failed, verification-incomplete, or reconciliation-required. Writer success never implies verified: true. Committed writes never become applied: false.
Byte verificationFresh read of each reviewed path; digestGeneratedFileContent(actual) === reviewed contentDigest.
Path verificationCanonical exactNeutronMutationPathSetsEqual over approved vs accounted paths. Hidden .aif/manifest.lock.json and .aif/source-map.json must not appear unless approved.
Project statedigestNeutronMutationObservedState over inspect fingerprint plus approved/hidden path content. Pre-Apply digest is captured before writes and reused for rollback checks.
Health checksByte, write-set, observed digest, and transaction consistency only. Doctor / Diff / Conformance / Security Audit are not Slice 4 gates (diffProject does not prove reviewed-byte equality).
Rollback evidenceFailed executeApprovedApplyPlan keeps rollbackEvidence. Neutron projection: path, digest, existedBefore, restored, status. Raw previous bytes stay internal to the writer.
Rollback verificationAfter failed write, postRollbackProjectStateDigest === preApplyProjectStateDigest independently verifies restoration. Incomplete rollback sets reconciliationRequired.
Durable stateVerification fields live on the existing transaction record. Corrupt verification fails closed without erasing applied write outcome.
RetryRead-only verification may resume. Apply never retries automatically.
SecretsSerialized evidence excludes approvalToken, file bodies, previousContent, prompts, and model reasoning.
Desktop / N4 / N5No Desktop Approve/Apply or verification UX. N4 catalog unchanged. Slice 5 may later reference transactionId / evidenceDigest; this slice does not attach N5 provenance.
Next unauthorized slicesDesktop Approve/Apply UX; Desktop verification UX; host rollback execution / Undo; N4 mutation tool; autonomous retry.

32.3 Slice 5 N5 proposal/review integration ​

Slice 5 (PR #507, merge 0eb326986537f125abfefc573e6709824914b37c) connects N5 graph execution to existing review/Apply without a second scheduler, Apply engine, or verification framework. Handoff #508 and finalize #509 recorded merge SHAs; a post-merge audit found two bounded gaps corrected in Slice 5.1 (§32.4).

DecisionRecord
CandidateStrict NeutronMutationProposalCandidate (urn:intentloom:schema:neutron-mutation-proposal-candidate:1). Files + optional bounded sources only. Authority fields (approved, approvalToken, planDigest, projectStateDigest, …) are rejected. Arbitrary prose is not a proposal.
Authoritative attemptOnly the last live completed attempt of a permitted feature-builder node may materialize. Stale, failed, cancelled, and timed-out attempts cannot. expectedOutput seed is preview-only. After Slice 5.1: materialization requires graph stale report accepted, authoritative attempt fingerprint equals current project fingerprint, and proposal projectStateDigest from that attempt fingerprint (§32.4).
CapabilityAdditive mutation-proposal. Intersection of node, parent, session, profile, and ceiling grants via neutronNodeMayPropose. Does not mean Apply, write, or mutationAllowed. Unpermitted roles cannot propose. After Slice 5.1: production session runtime threads profile and ceiling through the full execute/materialize path (§32.4). Parent cannot widen child authority.
MaterializationHost materializeNeutronGraphMutationReview computes path/content/plan/proposal/artifact digests (host-computed) and stores exact GeneratedFile[] in an injectable host payload store. Content-bound review artifact. Fail closed when stale (§32.4).
Review boundaryGraph/node completed is not approved/applied/verified. Scheduler stops at review; never calls Apply. Default session execution does not auto-materialize unless the host grants mutation-proposal.
Graph-linked ApplyHost-only composition: applyApprovedNeutronGraphMutation — store lookup → detectNeutronGraphStaleness → existing applyApprovedNeutronMutation → Slice 4 verification evidence linkage. Stale project/checkpoint/profile rejects with no write. No Apply retry.
RetryN5 may retry a proposal node before materialization. After an authoritative artifact exists, a new attempt is a new proposal identity. Apply failure is not an N5 retry. No automatic rebase/rerun/regeneration on stale proposal rejection.
ProvenanceAdditive NeutronGraphMutationProposalEvidence / Apply evidence (ids, digests, paths, statuses). No file bodies, tokens, prompts, or reasoning. Node task state is not rewritten after later Apply.
mutationAllowed / N4Unchanged literal false. Seven read-only tools. No applyApprovedTransaction.
Next unauthorized slicesDesktop Approve/Apply UX; Desktop verification UX; host rollback execution / Undo; N4 mutation tool; autonomous graph runner; automatic Apply retry.

32.4 Slice 5.1 stale proposal and capability clamp correction ​

Slice 5.1 (PR #510, merge b36d836c05591599f2526f0d3f5302e7edce8f5b; starting main c604c985c1476e17d281821f1fcd53e067957aaf) is an implementation correction only. It does not expand mutation authority.

DecisionRecord
Gap A — stale bindOriginal Slice 5 could materialize after reconciliation already reported stale project, rebinding candidate bytes from state A onto post-wave fingerprint B. Now: accept stale report; require authoritative attempt fingerprint == current project fingerprint; derive proposal baseline from attempt fingerprint. No proposal/review artifact/payload-store entry when stale.
Gap B — capabilityneutronNodeMayPropose already intersected node/parent/session/profile/ceiling; production composition omitted profile/ceiling threading. Now: createNeutronSessionRuntime → … → collectNeutronGraphMutationCandidates → neutronNodeMayPropose receives configured profile and ceiling.
UnchangedmutationAllowed === false; proposal node mutationAttempted === false; N4 catalog; Desktop read-only proposal review; human/host approval; applyApprovedNeutronMutation; Slice 4 verification; no autonomous mutation; no generic shell.