Neutron N6 — Desktop Mutation Host Flow (Security + Architecture Brief)
Status
D1 — READ-ONLY AUTHORITATIVE MUTATION REVIEW TRANSPORT: IMPLEMENTED AND MERGED
DESKTOP MUTATION D2 IMPLEMENTED AND MERGED
D3 IMPLEMENTED AND MERGED (PR #522)
DL IMPLEMENTED AND MERGED (PR #525)
DESKTOP MUTATION DURABLE HOST STATE PREREQUISITE IMPLEMENTED AND MERGED (PR #528)
DESKTOP MUTATION D4 IMPLEMENTED AND MERGED (PR #534)
D5 SLICE 1 — AUTHORITATIVE STATUS RECOVERY: IMPLEMENTED AND MERGED (PR #537)
D5 SLICE 2 — DESKTOP RECONNECT AND AUTHORITATIVE STATUS RECOVERY UX: IMPLEMENTED AND MERGED (PR #540)
DESKTOP MUTATION D5 COMPLETE
Verification retry is a separate post-D5 capability, intentloom.neutron.mutation.verification.retry.v1. It re-runs Slice 4 verification for an already applied transaction and does not Apply, approve, or Undo. Undo and rollback execution remain unauthorized.
This document is the canonical threat-reviewed design for the first safe Desktop mutation flow. D1 (read-only review payload transport) is implemented (PR #516, merge ca87a2532d1e4965655f97d39efb21fc8ad36437; implementation head 25d0f6391ebc3079a755c2cbe0f492ef868a6578). D2 (Desktop exact mutation review UI) is implemented (PR #519, merge 2d3dfed9296ee998a458379db81ac5c0e1fa9e67; implementation head 74acec00d07afcf8c5a69e8ba667d091fcedbfa7). D3 (host approval-intent protocol + security tests) is implemented (PR #522, merge f629c2ef8b4c8cb962bf502eec07c1bad20dfc3a; implementation head e36a74b8bc09698b70621da31e37f7c09f6b6917). D3 adds in-process host approval issuance only; it does not authorize D4–D5, production mutation UI, mutating mutation RPC, Approve/Apply buttons, an N4 mutation tool, Undo, or any change to mutationAllowed. DL (legacy fake Approved Apply removal) is implemented and merged (PR #525). Desktop durableStateDirectory wiring is implemented and merged (PR #528, merge be1f0201e968846765f7efa731560886a8b50032; implementation head a3eb8c96b5012283107eb361c14fecd69025e22e). That prerequisite is not itself Approve & Apply. D4 (combined host Approve & Apply) is implemented and merged (PR #534, merge 0da5612e45d99454eb765cb370a058187ef47f94; implementation branch feat/neutron-desktop-approve-and-apply; starting main 67c0a58d850d9d566177fd920bc8b3fd8da654c4; implementation head 42d8810308326bb69e29ac499962626fe42ea393, which is not the merge SHA). D5 Slice 1 (read-only intentloom.neutron.mutation.status.get.v1) is implemented and merged (PR #537, merge 866c96ab6fd1a1265dfc46a8840117baaaebd0b6; branch feat/neutron-desktop-mutation-status-recovery; starting main d62b10a67051fc0f5f1ccb4d8a863c69bca0b1c8; initial head 0793cdc1624049dd477be07bc85d9fb6c5039816; final audited head c366d9f7d8d27f2a6f92b328ee775e1ceab53381, which is not the merge SHA). It D5 is complete. Slice 2 reconnect UX is merged (PR #540, merge bb9255a79229d9a64d611ce644c0f75caaec74bf; final audited head a0c029f62c30008dde416f5f93847fca45279e0d). Post-D5 verification retry is a separate operation and does not change the D5 security model. Undo remains unauthorized.
Evidence baseline: origin/main @ be1f0201e968846765f7efa731560886a8b50032 (2026-09-28; PR #528 merged). Tracked tree clean at handoff start.
Authoritative implementation and tests remain truth. Related:
NEUTRON_RUNTIME_ROADMAP.md§N5.5–§N6NEUTRON_MUTATION_ROUTING_BRIEF.mdNEUTRON_MUTATION_SLICE3_SECURITY_REVIEW.mdNEUTRON_N6_DESKTOP_READONLY_BRIEF.mdNEUTRON_N5_EXECUTABLE_TASK_GRAPH_BRIEF.mdADR-0042ADR-0053
0. Maintainer recommendation (not an implementation grant)
| Decision | Verdict |
|---|---|
| Current mutation slice | D4 merged (PR #534). D5 complete (Slice 1 PR #537, Slice 2 PR #540). Post-D5 verification retry is a separate verification-only operation. Undo is not authorized. |
| Approval issuer | Trusted daemon/application host only. Desktop submits a typed human intent. Host re-fetches the review bundle and issues NeutronMutationApproval itself. |
| Public RPC shape | B — one host approveAndApply action (merged) plus read-only review.get, read-only status.get, and post-D5 verification.retry (durable verification metadata only; zero project-write authority). No separate public Approve RPC. |
| Desktop button copy | Approve & Apply is implemented on the exact mutation review and is enabled only for an eligible/current review. |
approvalToken | Host-only. Public result validation rejects approvalToken and other authority/content-bearing fields. Diagnostics are redacted. |
| Durable authority | Reuse Slice 3.1 store and project lock. Do not add a Desktop approval database. |
| Apply engine | Reuse applyApprovedNeutronGraphMutation → applyApprovedNeutronMutation → declared-path Approved Apply → Slice 4 verification. |
N4 / mutationAllowed | Unchanged. Seven read-only tools. Literal false. |
| Undo | Out of first Desktop mutation scope. |
| Legacy fake Approved Apply | DL implemented and merged (PR #525). Fabricated applied: true and synthetic rollback evidence are removed from production Desktop. Neutron must never reuse a fake Apply path. |
| This document | D1–D5 are merged. Verification retry is the separate post-D5 slice in this implementation. Undo remains unauthorized. |
1. Baseline
| Item | Evidence |
|---|---|
Expected main after PR #511 | Merged Slice 5.1 handoff |
Actual origin/main | 1a8abac9dce73cbdddb12d035416691b6360cde6 |
| Latest merge | PR #511 docs(neutron): handoff for Mutation Slice 5.1 |
| Prior correction | PR #510 Slice 5.1 implementation b36d836c05591599f2526f0d3f5302e7edce8f5b |
| Tracked tree at brief start | Clean main fast-forwarded to origin/main |
| Unrelated scratch | None present; nothing preserved or deleted |
If this brief is read against a tree where #511 is not merged, stop: DESKTOP MUTATION HOST FLOW BRIEF BLOCKED: SLICE 5.1 HANDOFF NOT MERGED.
2. Current mutation foundation (Slices 1–5.1)
Implementation and tests are authoritative.
| Slice | What exists | Authority |
|---|---|---|
| 1 | NeutronMutationProposal, host-shaped NeutronMutationApproval, preflight envelopes | Types only |
| 2 | preflightNeutronMutation semantic authorization | Eligible/rejected; zero writes |
| 2.5 | Content-bound NeutronMutationReviewArtifact, canonical planDigest, declared-path sync | Review artifact is not approval |
| 3 | Host-only applyApprovedNeutronMutation | One claimed transaction; declared-path Apply |
| 3.1 | Durable approval/transaction store + exclusive project lock | Production requires store or durableStateDirectory |
| 4 | Independent post-Apply verification; sanitized rollback projection | applied ≠ verified; no Apply retry |
| 5 | N5 candidate → host materialization → payload store; applyApprovedNeutronGraphMutation | Host composition after separately issued approval |
| 5.1 | Stale materialization fail-closed; production capability clamp | Candidate A cannot rebind onto project B |
Invariants that this Desktop design must not weaken:
NeutronRuntimeSession.mutationAllowedis typedfalse.- N4 catalog is seven read-only tools.
assertReadOnlyTooldenies writes. - Proposal validators reject
approved,grantedApprovals,approvalToken, and related authority keys. - Approval source is
local-interactive. Token format isapproved:<proposalDigest>(binding string, not a high-entropy secret). - Model
grantedApprovalscannot authorize Neutron mutation. The inner Approved Apply string"atomic-commit-approval"is attached only by the trusted Apply adapter (NEUTRON_MUTATION_INNER_APPLY_APPROVAL). - Hidden
.aif/manifest.lock.jsonand.aif/source-map.jsonmust not appear unless declared (NEUTRON_HIDDEN_GENERATED_METADATA_PATHS). - Graph-linked Apply calls
detectNeutronGraphStalenessbeforeapplyApprovedNeutronMutation. - There is no production
issueNeutronMutationApprovalhelper today. Tests construct the approval record. Desktop must not become that issuer.
3. Current Desktop foundation (N6 Slices 1–5)
| Slice | What exists | Mutation? |
|---|---|---|
| 1 | Named Neutron session RPCs + Desktop Neutron view | No |
| 2 | Bounded N3 context + N4 tool activity on the completed turn snapshot | No |
| 3 | Graph get / one-wave execute / cancel | No |
| 4 | Authoritative result/evidence/provenance UX | No |
| 5 | Read-only NeutronMutationProposal panel | Paths + digests only |
Current Desktop review is intentionally insufficient for real approval:
- Session viewmodel carries a single
mutationProposalornull. selectSessionMutationProposaluses array-first (length === 1) orambiguous→null. That is acceptable while Apply is impossible.NeutronMutationProposalPanelshows ids, class, plan/baseline/proposal digests, and paths only viaDiffViewer.- Copy is Mutation not authorized / No Approve control / No Apply control.
NeutronGraphSnapshotdoes not projectmutationProposals[].- Review artifact bytes and
GeneratedFile[]stay in the host payload store. desktopClient.neutronRequest(request: object)plusinvoke_neutron_requestforwards one of seven allowlisted read-only methods. That hole must not grow to mutation.
Daemon Neutron capabilities are classified read-only. Spawn (packages/daemon/src/bin.ts) creates createNeutronSessionRuntime with an Ollama adapter. Desktop-owned and SEA production launches now inject a host-controlled durableStateDirectory through --neutron-mutation-state-dir. The flag remains optional for non-Desktop read-only daemon launches. Payload store is still in-process memory (createMemoryNeutronGraphMutationPayloadStore). This is not Apply.
4. Critical principle
Desktop is presentation and user-intent capture.
Desktop must not:
- fabricate approval or Apply success;
- generate
approvalToken,approvalDigest, orapprovalId; - recompute security-sensitive digests as authority;
- hold mutation authority independently;
- write project files directly;
- call a provider/model to approve;
- call generic shell;
- regenerate reviewed content;
- silently retry Apply.
Actual authority stays in trusted host/application boundaries:
Neutron task graph
→ host-materialized authoritative proposal + review artifact + payload
→ Desktop displays exact review information (read-only transport)
→ human explicitly selects proposalId and clicks Approve & Apply
→ Desktop sends typed intent (identities + reviewed artifact digest)
→ authenticated daemon / application host
1. re-fetches host-held bundle
2. revalidates session/graph/project currentness
3. issues bound NeutronMutationApproval (token stays host-side)
4. durable Slice 3.1 claim + project lock
5. applyApprovedNeutronGraphMutation
6. applyApprovedNeutronMutation
7. declared-path Approved Apply engine
8. Slice 4 verification
9. persist structured result
→ Desktop displays authoritative result/evidence from host stateDo not invent a parallel Desktop mutation engine, a second approval store, or an N4 applyApprovedTransaction tool.
5. Existing authority chain (reuse, do not fork)
Verified chain for a later implementation:
Desktop UI
→ typed desktopClient operation (narrow; not generic request: object)
→ dedicated Tauri command allowlist
→ authenticated local daemon (session token, no wildcard methods)
→ versioned JSON-RPC
→ application host operation
→ host-issued NeutronMutationApproval
→ applyApprovedNeutronGraphMutation
(payload-store lookup, authoritative source only,
detectNeutronGraphStaleness)
→ applyApprovedNeutronMutation
(parse envelope, canonical realpath, durable store,
exclusive claim, lock, pre-write validate)
→ executeTrustedDeclaredPathApply
→ evaluateApprovedApplyPlan + executeApprovedApplyPlan
→ synchronizeGeneratedFiles (declared-paths-only)
→ Slice 4 independent verification
→ durable transaction record (ids, digests, statuses; no token/bodies)Forbidden substitutes:
intentloom.project.approvedApply.v1as the Neutron Desktop path- App.tsx fabricated
applied: true - model output,
expectedOutput, or preview proposal as Apply bytes - renderer-supplied approval record
- Desktop filesystem or shell commands
- N4 mutation tool
- adoption Apply (
applyProjectAdoption) or workspaceapprovedBy
Adoption Approve/Apply is a pattern analogue (host-issued bound approval, explicit human click, daemon mutating classification), not the Neutron writer.
6. Threat model
6.1 Approval spoofing
| Threat | Required control |
|---|---|
Model says approved: true | Keep proposal/candidate rejection of the key. Host ignores it. |
UI sends approved: true | Request schema forbids authority flags. Host issues approval itself. |
| Malicious renderer JS | Treat renderer as untrusted. Intent only. Host recomputes facts. |
| Replayed old approval | Slice 3.1 one-use claim. Combined approveAndApply so unused approvals are not exported. |
Forged approvalId | Host allocates ids. Client-supplied approval ids are rejected. |
| Copied proposal digest | Digest is review-visible by design. Unforgeability is architectural (host-held record), not a MAC. |
| Old Desktop session | Bind sessionId/projectId/root; daemon root equality; cancelled session fails closed. |
| Modified RPC payload | Schema validation; host re-fetches bundle; extra paths rejected; exact path-set equality. |
approvalToken is approved:<proposalDigest>. It is not a secret. The control is “never accept a client/model approval record,” not token secrecy. Desktop still must not receive the token, because presence in viewmodels/logs trains a fake-authority path.
6.2 TOCTOU
Windows between proposal display, click, daemon receipt, durable claim, and first write:
| Change | Control |
|---|---|
| Project files change | Review-time fingerprint + write-time project-stale / graph stale |
| Symlink / root change | Canonical realpath containment; root equality |
| Profile / checkpoint change | detectNeutronGraphStaleness before graph-linked Apply |
| Graph becomes stale | Slice 5.1 currentness + Slice 5 graph stale reject |
| Approval expires | Host rechecks approvalValidUntil / plan expiresAt before issue and before write |
| Session cancelled | Existing cancellation mapping; cancelled-before-write |
Desktop click is never a capability. Both review-time currentness and write-time currentness are mandatory.
6.3 Replay
Double-click, network retry, daemon reconnect, Desktop refresh, app restart, duplicate RPC, and restored-tree replay of an old approval are handled by:
- single host approveAndApply;
- Slice 3.1 exclusive claim (
wx+ fsync); - applied replay returns the prior terminal result without a second write;
- client idempotency keys are optional UX, not authority.
An old approval after the tree is restored must not write again. Slice 3.1 already treats consumed/applied records as non-reusable.
6.4 Multiple Desktop windows/processes
Desktop/Tauri locks are not authoritative. Two windows on the same project must serialize through the Slice 3.1 durable project lock (durableStateDirectory lock file keyed by canonical realpath). Lock conflict returns a structured lock-conflict / claim conflict, not a second write.
6.5 Crash
| Moment | Required durable outcome |
|---|---|
| After intent, before host approval | No approval, no write |
| After approval, before claim | Combined operation: approval is not exported; crash looks like unused. Do not persist a free-floating approval in D4. |
| After claim, before write | claimed / failed-before-write / in-flight → fail closed; no second writer |
| During write | Existing Apply rollback / failed-needs-reconciliation |
| After write, before verification | applied + verification pending/incomplete; resume verification only |
| After verification, before Desktop response | Durable record is truth; status.get recovers it |
6.6 Stale proposal (Slice 5.1)
Materialization already requires accepted stale report and attempt fingerprint == current project fingerprint. Desktop Apply must keep that gate. Candidate bytes generated against project A cannot be approved onto project B.
Preview expectedOutput remains non-authoritative. source !== "authoritative" is preview-not-authoritative.
6.7 Secret leakage
Desktop viewmodels, review RPC, status RPC, Apply result, logs, and evidence must not contain:
approvalToken- raw previous file bodies /
previousContent - hidden context, prompts, or model reasoning
- secret-like path excerpts beyond existing N6 redaction
Review content may include proposed bytes and current on-disk bytes for the declared path set only, through the explicit review endpoint, bounded by Slice 2.5 limits (256 files, 1 MiB/file, 8 MiB aggregate).
7. Approval creation
Decision: a real NeutronMutationApproval is created only inside the trusted application host during approveAndApply.
It must not be created in:
- model output or N5 scheduler;
- Desktop renderer;
- N4 tool router;
- generic RPC payload from an arbitrary caller;
- a client-supplied approval JSON blob.
Preferred sequence:
- Desktop submits typed human approval intent (identity fields only; see §8 /
NeutronMutationApprovalIntent). - Host loads the authoritative bundle from the session payload store by
proposalId. - Host revalidates currentness, binding, expiry, cancellation, and artifact digest.
- Host constructs the approval (ids, digests,
approvalSource: "local-interactive",approvingActorhost-assigned, tokenapproved:<proposalDigest>,reviewArtifactDigestrequired). - Host immediately claims and Applies. Token never appears on the wire.
D3 (merged): the internal host issuer (issueNeutronMutationApprovalFromIntent, NeutronSessionRuntime.issueMutationApproval) constructs NeutronMutationApproval in-process from authoritative review state. Tests exercise the factory. D3 itself adds no public Approve-only RPC and no production Desktop mutating control. D4 (implemented and merged, PR #534) is the production caller that combines approval with Apply inside one trusted-host operation. There is still no public Approve-only RPC.
approvingActor is assigned by the host (for example desktop-local-interactive) from the authenticated daemon session. The renderer does not supply actor identity as authority.
8. Desktop approval request
Narrow typed intent (NeutronMutationApprovalIntent). D3 merged fields:
| Field | Role |
|---|---|
schemaVersion | Intent schema URN |
protocolVersion | Existing protocol compatibility |
action | Literal request-host-approval (not approval) |
root | Bound to daemon canonical root |
sessionId | Current Neutron session |
projectId | Current project |
graphId | Current graph identity |
proposalId | Explicit authoritative proposal selection |
Optional later hardening (not required for D1): host-issued reviewViewNonce from review.get, short TTL, single-use with approveAndApply. Security still depends on host re-fetch + revalidation, not the nonce.
Rejected client fields (strict validation): approvalToken, approvalDigest, approvalId, proposalDigest, reviewArtifactDigest, projectStateDigest, planDigest, grantedApprovals, approved, authorized, mutationAllowed, files, content, paths, changedPaths, proposedContent, currentContent, previousContent, expiresAt, expiry, approvalValidUntil, approvedAt, mutationClass, approvingActor, approvalSource, filesToApply, taskId, and any other unexpected key.
Host resolves authoritative proposal, artifact, files, digests, root, expiry, and mutation class from the payload store.
9. Human review boundary
Paths-only N6 Slice 5 was acceptable because Apply was impossible. Real approval requires the human to understand the bytes that will be written.
Desktop must show, before enabling Approve & Apply:
- selected project / canonical root;
- proposal identity (
proposalId, session/project/graph/task); - mutation class
approved-transaction-apply; - exact changed paths;
- create / update / no-op classification (delete is not in the current writer);
- exact content diff or other safe rendering of reviewed bytes;
proposalDigest,planDigest,reviewArtifactDigest;- baseline
projectStateDigest/ fingerprint; - stale / current status and warnings;
- expiry;
- test/evidence state if already present (usually empty before Apply).
A filename list is insufficient. Preview/model prose cannot substitute for the host payload.
If multiple authoritative proposals exist, Desktop lists them and requires an explicit proposalId selection. No array-first Apply.
10. Exact reviewed bytes
Do not reconstruct bytes from model output, expectedOutput, or a second model call.
Source of truth: host-held NeutronGraphMutationReviewBundle (proposal + artifact + GeneratedFile[]) in the session payload store populated by materializeNeutronGraphMutationReview.
D1 adds a read-only review RPC that copies declared-path proposed bytes from that store. No mutation authority in the review method.
If the bundle is missing (daemon restart, cancelled session, never materialized), return structured proposal-not-found. Do not rematerialize from the model.
Do not persist raw review bodies into the Slice 3.1 durable approval store. That store is ids/digests/statuses. File bodies stay in process memory for the review window. Restart before Apply fail-closes; the user re-runs the graph to obtain a new current proposal.
11. Diff architecture
Reuse DiffViewer (apps/desktop/src/design/components/code/DiffViewer.tsx). Today Neutron feeds it path names with kind: "add". Future review UI must feed real hunks.
Per path, host review payload supplies:
| Field | Source |
|---|---|
| exact path | artifact changedPaths / file binding |
| proposed bytes | payload-store GeneratedFile.content |
| proposed digest | artifact fileBindings[].contentDigest |
| current bytes | read-only disk read at review time, or absent |
| classification | missing → create; equal → no-op; different → update |
Diff display is not the Apply byte source. Apply continues to use the host payload store. Desktop-edited buffers, if any, are ignored.
Delete is unsupported in NeutronMutationProposalCandidateFile (path + content only) and in executeTrustedDeclaredPathApply (create/update/no-op). A later delete capability needs its own security review.
12. Approval binding
Host-issued approval must bind existing canonical facts:
proposalId,proposalDigestreviewArtifactDigest(required for Apply; not optional in this flow)planDigest,projectStateDigestroot,projectId,sessionIdgraphId/taskIdwhen the proposal carries them- exact
changedPaths mutationClass: approved-transaction-applyapprovalSource: local-interactiveapprovedAt,approvalValidUntil
Do not weaken Slice 1–5.1 invariants. Do not omit reviewArtifactDigest. Do not accept approvals without content-bound artifacts.
13. Desktop must never see approvalToken
Decision: raw approvalToken remains inside the trusted host.
Safe Desktop fields after a later Apply:
approvalId- status / failureCode
- expiry (if still relevant)
- proposal / artifact / transaction identities and digests
Existing Apply/verification validators already reject approvalToken on public results. Keep that rule on every new RPC and viewmodel.
14. New protocol methods
Naming follows current Neutron RPC style (intentloom.neutron.<area>.<verb>.v1 in packages/protocol/src/jsonrpc.ts).
| Conceptual method | Classification | Authority |
|---|---|---|
intentloom.neutron.mutation.review.get.v1 | read-only | Display exact reviewed bytes |
intentloom.neutron.mutation.approveAndApply.v1 | mutating | Host issues approval, claims, Applies, verifies |
intentloom.neutron.mutation.status.get.v1 | read-only | Durable/authoritative recovery |
intentloom.neutron.mutation.verification.retry.v1 | read-only | Re-run Slice 4 verification for an applied transaction. Not Apply. |
Names are design targets. Implementation may keep these exact strings.
14.1 Option A — separate Approve then Apply
Mirrors existing-project adoption (adoption.approve.v1 + adoption.apply.v1).
| Pros | Cons |
|---|---|
| Matches adoption UX | Creates a free-floating Neutron approval |
| Can test issuance alone via public RPC | Wider TOCTOU; replay of unused approval |
| User could delay Apply | Slice 3.1 store tracks claimed transactions, not unused approvals; A would add a new unused-approval persistence surface |
14.2 Option B — single approveAndApply (recommended)
One authenticated host action performs:
- resolve authoritative review bundle;
- revalidate project/session/graph;
- issue approval (token host-local);
- durable claim + project lock;
- existing graph/current Apply;
- persist result;
- Slice 4 verification.
| Pros | Cons |
|---|---|
| No exported unused approval | Cannot “approve now, apply later” |
| Smaller TOCTOU and replay window | Combined failure taxonomy must stay structured |
| Matches the intended button | D4 merged the combined RPC and Desktop control (PR #534) |
Fits current applyApprovedNeutronGraphMutation input shape | Cancellation during the combined call needs the existing before-claim / after-claim split |
Recommendation: B. Safer and simpler for the first Desktop mutation slice. D3 merged tests the internal issuer without publishing Approve-only RPC.
Do not automatically retry on stale, lock-conflict, or verification failure.
15. Recommended transaction UX
Single explicit user action: Approve & Apply.
That label is the confirmation of mutation for ordinary declared-path generated-file transactions. Do not use Continue / Accept / Done.
Host atomicity is authority-level (one operation, claim-then-write), not a promise that the OS cannot crash mid-write. Crash semantics stay on Slice 3.1 / Slice 4.
16. Staleness before approval
Before issuing approval the host revalidates:
- project fingerprint vs proposal
projectStateDigest; - Slice 5.1 currentness (attempt fingerprint == current);
- graph stale kinds: project / checkpoint / profile;
- session binding and cancellation;
- proposal identity and
reviewArtifactDigest; - payload
source === "authoritative"; - expiry.
If stale: structured stale result. No approval. No write. No automatic regenerate/rebase/rerun.
17. Staleness after approval
Desktop approval does not replace Slice 3 pre-write checks.
applyApprovedNeutronMutation already revalidates immediately before the first write (digest, expiry, cancellation, containment, path set, live project-state, optional graph stale). Keep both layers:
review-time currentness → may issue approval
write-time currentness → may enter executeTrustedDeclaredPathApply18. Durable approval state
Reuse Slice 3.1. Do not design a second Desktop approval database.
Production Apply already fails closed without an injected NeutronMutationApprovalStore or host durableStateDirectory. Memory store is test-only.
D4 prerequisite: Desktop-spawned daemon must own a host-controlled directory (application-private neutron-mutation-state beside the existing runtime token/socket — not project source, not .aif generated adapters). Lifecycle: created with the Desktop app-data tree, not deleted when the daemon endpoint is reclaimed, not world-writable on Unix (0700). Wiring is implemented and merged (PR #528). It is not itself D4. No second approval database and no second lock. Merged D4 reuses Slice 3.1. Production Desktop composition does not fall back to a memory store. Missing durable host state fails closed.
Durable records store ids, digests, states, and sanitized results. No token, no file bodies, no prompts.
19. Project lock
Reuse the Slice 3.1 durable exclusive project lock when durableStateDirectory is set. Canonical realpath key. Fail-fast conflict.
withCanonicalProjectRootLock is in-process and insufficient across two Desktop windows / two daemon processes.
N5 leases serialize task attempts, not project files.
20. Apply result
Desktop must render structured host status. Never collapse to Success/Failed.
Minimum distinguishable outcomes:
| Outcome | Meaning |
|---|---|
approval-rejected / invalid intent | Host refused to issue approval |
stale / graph-stale / project-stale | Currentness failed; no write |
preview-not-authoritative | Preview/expectedOutput is not Apply |
proposal-not-found | No host bundle |
claim-conflict / lock-conflict / approval-consumed | Another actor or replay |
cancelled-before-write | Cancelled before first write |
transaction-failed | Writer failed; see rollback flags |
applied + verification pending | Bytes written; verify not finished |
verified | Applied and independent verification matched |
verification-failed | Applied, verification mismatched |
reconciliation-required | Unknown/incomplete crash or rollback |
Use existing NeutronMutationApplyStatus / NeutronMutationApplyFailureCode / NeutronMutationVerificationStatus plus graph-linked failure codes. Do not invent a second enum that collapses them.
21. Applied vs verified
Preserve Slice 4. If bytes were written and verification fails, Desktop shows Applied + Verification failed, not Apply failed, and does not offer automatic Apply retry.
Optional later control: Retry verification (read-only retryNeutronMutationVerification). Never label it Retry Apply. This brief designs the label only; it does not authorize the control.
22. Crash / reconnect UX
After Desktop reconnect, status comes from host durable state (and, while the same daemon process lives, in-memory review bundles). Desktop must not infer status from whether an RPC response arrived.
| Host state | Desktop |
|---|---|
| Bundle present, no claim | Review required / unused |
claimed / executing | In flight; do not offer a second Apply |
applied + pending verification | Applied; verification pending |
applied + verified | Verified |
failed-before-write | Failed before write; consumed |
failed-needs-reconciliation | Reconciliation required |
| Bundle missing after restart, no durable record | Proposal not found; re-run graph |
23. Verification recovery
Slice 4 already allows read-only verification resume. If Desktop later exposes it, the control is Retry verification. Out of this authorization.
24. Reconciliation UX
Read-only recovery information for incomplete rollback, unknown crash, or verification mismatch:
- affected paths;
- safe digests;
- state /
reconciliationRequired; - rollback summary (
attempted/completed/verified, not raw bodies); - guidance to a human/operator.
No automatic repair. Host rollback execution / Undo remains separately unauthorized.
25. Undo
Keep out of the first Desktop mutation slice. Do not map rollback evidence to a clickable Undo. Rollback write execution needs its own authority/security review.
26. Legacy fake Approved Apply path
DL implemented and merged (PR #525).
The former apps/desktop/src/App.tsx onApprovePlan path opened ApprovedApplyModal, injected ["atomic-commit-approval"], did not call intentloom.project.approvedApply.v1, and after 600 ms fabricated applied: true plus previousContent: "// previous snapshot content".
That production composition is removed. ApprovedApplyModal.tsx no longer exists. WorkspaceContent no longer mounts it. App.tsx no longer holds Apply-plan state, grant injection, timeout-delayed success, or synthetic rollback bodies. Neutron sources still must not import the removed modal.
The daemon method intentloom.project.approvedApply.v1 is unchanged and remains unwired from Desktop spawn. DL does not activate it and does not add approveAndApply.
DL does not perform Neutron mutation. The later, separately authorized D4 path is the merged host operation. The legacy daemon method stays unwired. Fail closed outside that dedicated path.
27. Tauri security
Current Neutron bridge: invoke_neutron_request + is_neutron_method exact match of seven session/graph methods. No wildcards. Capabilities: core:default + dialog:allow-open. No FS/shell/HTTP plugins. CSP default-src 'self'. Commands run on main.
Do not add mutation methods to invoke_neutron_request. Do not add a generic invoke proxy or arbitrary daemon method string.
Recommended dedicated commands:
| Tauri command | RPC | State |
|---|---|---|
get_neutron_mutation_review | review.get.v1 | Implemented with D1 |
approve_and_apply_neutron_mutation | approveAndApply.v1 | Implemented with D4; not on invoke_neutron_request |
get_neutron_mutation_status | status.get.v1 | D5 merged. Read-only. Not on invoke_neutron_request |
retry_neutron_mutation_verification | verification.retry.v1 | Post-D5. Verification only. Not on invoke_neutron_request |
desktopClient grows typed methods that construct params internally. The renderer does not pass a free-form request: object for mutation.
No arbitrary filesystem command. Folder dialog remains the existing select_project_root.
Origin/window: keep commands on the default main window capability. Do not expose mutation commands to untrusted additional windows if any are added later.
28. Daemon security
Keep the authenticated local daemon as the host boundary.
Existing controls to reuse:
- session token (≥32 chars) on every envelope (
packages/daemon/src/index.ts); - protocol parse + schema validation;
- root/session/project binding on Neutron methods;
- canonical root enforcement where other mutating handlers already use it;
- mutating vs read-only capability classification (see adoption Apply).
Prerequisites before D4 (identify, do not implement here):
- Wire
durableStateDirectoryin Desktop daemon spawn (implemented and merged, PR #528). - Classify
approveAndApplyasmutating; review/status asread-only. - Reject externally supplied approval records.
- Redact tokens from results, logs, and errors (existing
redactApprovalTokenhelpers). - Bind mutation calls to the same canonical root as the session.
- Keep payload-store lookup inside the daemon process that materialized the graph; missing bundle fails closed.
Current daemon security is sufficient for D1 read-only review if the new method is authenticated, root-bound, schema-validated, and classified read-only. It is not sufficient for Apply until (1)–(5) exist.
29. Desktop process trust
Assume the renderer is easier to compromise than the application host.
Renderer requests are intent, not mutation authority. Host always re-fetches the bundle and recomputes/revalidates authoritative facts. Desktop-computed diffs, shortened digests, and disabled buttons are UX only.
30. Multiple proposals
selectSessionMutationProposal array-first / ambiguous is not an Apply selector.
D1/D2 must list host-held authoritative proposals for the session/graph (proposalId + safe evidence). The human selects proposalId. Approve without an explicit selection is invalid.
Preview proposals remain non-authoritative and cannot be approved.
31. Proposal expiry
Display expiresAt / currentness. If expired, disable Approve & Apply. Host still rechecks. UI disabling is never security.
32. Cancellation
Reuse existing session/graph cancellation and Apply AbortSignal mapping.
| Phase | Behavior |
|---|---|
| Before click | No host mutation |
| During approval request, before claim | cancelled-before-write; no approval export |
| After claim, before write | Existing Slice 3.1 before-write failure |
| During transaction | Existing writer/rollback semantics |
| During verification | Cancel does not un-apply |
| After applied | Renderer cancel must not erase the mutation |
Client Promise abort is not success (same as N6 graph cancel).
33. Button semantics
Final wording for the first mutating control: Approve & Apply.
Not Continue, Accept, Done, Apply (alone), or Approve (alone). The combined host action and the label must match.
Disabled reasons (expired, stale, no selection, in-flight, already applied) must be explicit status text, not color-only (React a11y: glyph + word).
34. Confirmation design
Decision: do not add universal second-confirmation friction.
The explicit Approve & Apply click is the human approval for ordinary declared-path generated-file transactions inside existing bounds (max 256 files, 1 MiB/file, 8 MiB aggregate, realpath containment).
Risk-based extra confirmation is justified only when existing governance already treats the case as higher risk. For this mutation class:
- delete is unsupported → reject, do not confirm;
- undeclared hidden metadata → fail closed (Slice 2.5/4);
- extra / escaped paths → fail closed, no override checkbox;
- many files within the existing hard cap → show the full review, not a second modal by default.
If a later capability adds deletes, symlink retargeting, or writes outside generated-file declared paths, that is a new security review — not a generic “type APPLY” checkbox invented here.
Foundation scaffold’s applyConfirmed checkbox is a different domain (scaffold apply) and is not copied onto Neutron by default.
35. Sensitive paths
Reuse existing path/profile mechanisms; do not invent uncontrolled exceptions.
| Mechanism | Role |
|---|---|
Session/profile allowedPaths + proposal capability clamp (Slice 5.1) | Who may propose |
Canonical path + realpath containment | Where writes may land |
| Exact path-set equality | No extra path at Apply |
| Hidden metadata denylist unless declared | No silent .aif widening |
N4 trustedRoot string equality | Insufficient alone; Apply already uses realpath |
If a proposal includes high-risk paths that fail those controls: reject. If it passes: existing host-issued approval is sufficient. Do not add a parallel “allow sensitive path” exception flag.
36. Network
After a review artifact exists, approval and Apply are host-local.
No provider/model call between human approval and Apply. That prevents the model from changing the reviewed transaction.
N2/Ollama remains available for new graph turns, which produce new proposal identities, not mutations of the reviewed bundle.
37. Evidence
Desktop-visible safe evidence (status and result surfaces):
transactionId,proposalIdapprovalId(not token)reviewArtifactDigest,planDigest,proposalDigest- changed / created / updated / unchanged paths
applied, verification status,verificationEvidenceDigestreconciliationRequired, rollback status summary- graph/task/attempt identities from existing graph apply evidence
No bodies except on the explicit review content endpoint. No previousContent. No approvalToken. Byte-check records may include expected/actual digests only.
38. Audit / logging
Log identities, statuses, and digests. No tokens, prompts, raw secret bodies, or reasoning traces. Reuse existing redaction helpers on error messages.
Conceptual future metrics (docs only; do not add production instrumentation from this brief): approveAndApply counts by structured status, stale rejects, lock conflicts, verification mismatches. No payload bodies.
39. No N4 mutation tool
First Desktop mutation flow calls a host application RPC.
Keep N4’s seven read-only tools: inspect, doctor, memorySearch, timeline, conformance, securityAudit, projectDiff.
Do not design applyApprovedTransaction as model-callable. mutationAttempted stays false on proposal nodes.
40. mutationAllowed
Keep model-facing mutationAllowed: false on NeutronRuntimeSession.
Desktop human host mutation is outside model session permission. Do not change the N1 contract merely to enable Desktop Apply.
41. Implementation slices
D1, D2, D3, DL, the durable-state prerequisite, D4, and D5 Slice 1 are implemented and merged. D5 Slice 2 is implemented on branch awaiting maintainer review. D5 as a whole is not complete. Adjustments after audit: add DL as a D4 prerequisite; keep D3 as host issuer tests without public Approve-only RPC; D1 remains first.
DL — Legacy fake Approved Apply isolation/removal
Implemented and merged — PR #525 (fix/desktop-legacy-approved-apply-isolation); starting main ea03f6e3e5cf441eeb25e87f673d6c1e2dc45256; final head d66bb641ec62b1d6ec5eb57d0258f58b2166abc7; merge 21c7bbfb4daa389382fafd191b470b64cfed2b20. Production Desktop no longer fabricates Apply success or synthetic rollback evidence. Neutron remains unable to reach the removed path. D4 is not authorized by this slice.
D1 — Read-only review payload protocol + daemon endpoint
Implemented and merged — PR #516 (feat/neutron-desktop-mutation-review-transport); starting main d8d312f2c9e2dac9f7375a2f79ab572d4e75bb7d; final head 25d0f6391ebc3079a755c2cbe0f492ef868a6578; merge ca87a2532d1e4965655f97d39efb21fc8ad36437.
Transport exact reviewed bytes from the host payload store to Desktop via intentloom.neutron.mutation.review.list.v1 and intentloom.neutron.mutation.review.get.v1 (read-only). Tauri: list_neutron_mutation_reviews, get_neutron_mutation_review. Desktop client: listNeutronMutationReviews, getNeutronMutationReview. Generic Neutron invoke does not gain arbitrary mutation-review dispatch. Proposed bytes only from NeutronGraphMutationPayloadStore / authoritative NeutronGraphMutationReviewBundle — never reconstructed from preview, model output, or Desktop bodies. In-memory payload store: daemon restart without payload fails closed (persistence not implemented). Compatibility correction: resolveDaemonProjectRoot honors enforceCanonicalRoots === false for Neutron session/review dispatch; production daemon remains enforceCanonicalRoots: true; Slice 5.1 attemptFingerprint === currentFingerprint unchanged.
No approval. No Apply.
D2 — Desktop exact mutation review UI
Implemented and merged — PR #519 (feat/neutron-desktop-mutation-review-ui); starting main 4ce587259080d8dee8e0ebe843fc7a02899d52cc; final head 74acec00d07afcf8c5a69e8ba667d091fcedbfa7; merge 2d3dfed9296ee998a458379db81ac5c0e1fa9e67. Do not record the first branch head 759d910dab9261066283b3fd53923f0e0917cdac as the final D2 head.
Render diffs/classification/digests/expiry/currentness over the D1 viewmodel. Still no approval or Apply. Explicit multi-proposal selection UX without enabling mutation: if multiple authoritative proposals exist, none is silently selected; get uses explicit proposalId. If one proposal exists, Desktop may present it directly while preserving explicit proposal identity. Host currentness (current / stale / expired / cancelled) is rendered as-is; Desktop does not rebase, regenerate, convert stale to current, or rerun a model. Exact review distinguishes "alpha" from "alpha\n", "alpha\n" from "alpha\n\n", and "" from "\n" via presentation marker No newline at end of file without mutating authoritative D1 strings (EOF correction 5ccd169dec03deba294f9ed907c7266db1823d1e). A later test-helper CodeQL correction (74acec00d07afcf8c5a69e8ba667d091fcedbfa7) did not change production review semantics. Renderer review state clears when root, session, project, or graph scope changes. Secret-like paths expose safe status only.
D2 did not add Approve, Apply, transaction mutation, D4 approveAndApply, D5 reconnect/status flow, DL cleanup, N4 mutation tool, or mutationAllowed change.
D3 — Host approval-intent protocol + security tests
Implemented and merged (PR #522, merge f629c2ef8b4c8cb962bf502eec07c1bad20dfc3a; implementation head e36a74b8bc09698b70621da31e37f7c09f6b6917). Typed NeutronMutationApprovalIntent, strict validation, internal issuer issueNeutronMutationApprovalFromIntent, production composition NeutronSessionRuntime.issueMutationApproval, adversarial tests in tests/neutron-mutation-approval-intent.test.ts, and isolated canonical Apply tests via applyApprovedNeutronGraphMutation. Approval is derived only from host-held NeutronGraphMutationPayloadStore / NeutronGraphMutationReviewBundle (D1 binding, Slice 2.5 artifact, Slice 5/5.1 currentness). Host assigns approvalSource: local-interactive, approvingActor: desktop-local-interactive. Token format remains approved:<proposalDigest> (binding string, not a high-entropy secret); raw token never appears in publicNeutronMutationApprovalIssueFacts. Lifetime: 30 minutes maximum, capped by plan expiresAt when earlier. Duplicate intent may reuse stable approval identity; Slice 3.1 one-use claim governs Apply replay. Issuance is deterministic host logic with no model/provider call. No production Desktop button. No intentloom.neutron.mutation.approve.v1. No approveAndApply. Those limits describe D3. Merged D4 is recorded below and does not add a public Approve-only RPC.
D4 — Approve & Apply host operation
Implemented and merged — PR #534 (feat/neutron-desktop-approve-and-apply); starting main 67c0a58d850d9d566177fd920bc8b3fd8da654c4; final implementation head 42d8810308326bb69e29ac499962626fe42ea393; merge 0da5612e45d99454eb765cb370a058187ef47f94 (2026-10-01). The implementation head is not the merge SHA.
Public operation: intentloom.neutron.mutation.approveAndApply.v1, classified mutating. Trusted flow:
Desktop exact review
→ explicit human Approve & Apply
→ dedicated Desktop/Tauri command approve_and_apply_neutron_mutation
→ authenticated daemon
→ trusted Neutron application host
→ resolve authoritative D1 review bundle
→ construct D3 approval in-process
→ canonical graph Apply
→ Slice 3 / 3.1 transaction, durable claim, replay protection, project lock
→ Slice 4 verification
→ sanitized public resultThe renderer sends only root, sessionId, projectId, graphId, and proposalId. It does not create a NeutronMutationApproval and does not supply proposed bytes, changed paths, review artifact digest, plan digest, approval fields, grant flags, or transaction authority. Exact Apply bytes come from the host-held proposal payload store and content-bound review artifact. An explicit proposalId is required.
There is no public intentloom.neutron.mutation.approve.v1 and no Apply-by-token RPC. The raw approvalToken stays host-only. Public result validation rejects it and other authority/content-bearing fields. Diagnostics are redacted. The method is not added to invoke_neutron_request.
Staleness checks run before approval and again immediately before the first write. A stale project fails closed. D4 consumes the existing host durableStateDirectory and Slice 3.1 claim, replay, transaction, and project lock. Missing durable state fails closed. Merged tests show a duplicate request does not write twice, concurrent callers cannot both mutate, and an existing project lock blocks another Apply.
applied stays distinct from verified. Verification failure after a write remains applied and does not reopen approval or retry Apply. The D4 path does not call a model after the human action. mutationAllowed remains literal false. N4 remains the seven read-only tools.
Desktop shows Approve & Apply only for an eligible/current review and keeps applied + verified, applied + verification failed, applied + reconciliation required or incomplete verification, and rejected before write distinct.
D5 — Authoritative result / verification / reconnect UX
D5 complete. Slice 1 merged (PR #537, merge 866c96ab6fd1a1265dfc46a8840117baaaebd0b6; final audited head c366d9f7d8d27f2a6f92b328ee775e1ceab53381). Slice 2 merged (PR #540, merge bb9255a79229d9a64d611ce644c0f75caaec74bf; final audited head a0c029f62c30008dde416f5f93847fca45279e0d). Verification retry is not part of D5. Undo remains unauthorized.
Slice 1 adds read-only intentloom.neutron.mutation.status.get.v1 on a dedicated Desktop/Tauri command (get_neutron_mutation_status). Daemon classification is read-only. The generic Neutron request surface was not widened. Lookup identity is root, sessionId, projectId, graphId, and proposalId. Optional transactionId only narrows the lookup. The renderer does not send approvalToken.
The canonical path is D4 Apply, durable transaction result persisted, Desktop response lost or host restarted, fresh runtime with the same durableStateDirectory, status.get, authoritative durable truth, and zero second Apply. Existing Slice 3.1 transaction records stay authoritative. The metadata-only proposal-index in that directory points at approvalId and transactionId. It is not a second transaction database and stores no raw approval token, file bodies, proposed content, previous content, or model prompt/reasoning.
The final head rejects a pointer unless its embedded identity exactly matches the requested identity. A self-integrity SHA digest is not host authentication. A validly encoded pointer under another identity path, or a pointer whose approvalId / transactionId does not match the loaded canonical record, fails closed as durable-status-corrupt.
Lookup outcomes are unknown, recorded, root-mismatch, project-mismatch, session-mismatch, graph-mismatch, transaction-mismatch, and durable-state-unavailable. Recorded facts use the canonical transactionState, Apply status, and verificationStatus values already defined for mutation transactions. applied stays independent of verification. A later external edit does not rewrite a historical applied/verified durable record.
Status recovery does not issue or claim approval, create a token, call Apply, write project source, retry Apply, take the mutation write lock, or invoke a provider. mutationAllowed remains literal false. N4 remains the seven read-only tools. No mutation or status tool was added to N4.
D5 Slice 2 is merged (PR #540). A lost Approve & Apply response stays an uncertain presentation state. After the existing daemon connection is authenticated again for the same root, session, project, and graph, Desktop calls read-only status.get once and renders that host result. Explicit Refresh status uses the same read. There is no polling loop, no second Apply, no new approval, and no model call. A scope change drops the unresolved identity.
Post-D5 verification recovery is intentloom.neutron.mutation.verification.retry.v1 on retry_neutron_mutation_verification. The trusted host reloads the same proposal-index and Slice 3.1 record, and only when applied is true and verification is verification-failed or verification-incomplete it runs the existing Slice 4 verifier and replaces verification metadata. Historical applied: true stays true. reconciliation-required is not retryable. The capability class is read-only because the taxonomy has no third class and this operation has zero project-write authority; the operation name is distinct from Apply. Persistence re-reads the Slice 3.1 record under the approval-record gate and compare-and-sets its digest, so a stale cross-process retry cannot replace a newer verified result. A process-local queue is not that safety boundary. Still unauthorized: Undo, rollback execution, a mutation history browser, an N4 mutation or verification tool, and Apply retry.
42. Security gates per slice
D1
| Gate | Rule |
|---|---|
| Allowed | Read host-held review bundle; return declared-path proposed bytes + current disk bytes for diff; list authoritative proposalIds |
| Forbidden | Approval issuance, claim, Apply, token, Desktop writes |
| Inputs | session/project/root/graph + optional proposalId |
| Outputs | Safe review viewmodel; stale / proposal-not-found / ambiguous listing |
| Tests | Binding, root equality, no token/previousContent, extra-path ignore, preview not authoritative, missing store, size bounds |
| Exit | Desktop can display exact reviewed bytes; fingerprint unchanged; Apply still impossible |
| Next grant | Explicit maintainer authorization for D2 (or combined D1+D2 docs/UI) |
D2
| Gate | Rule |
|---|---|
| Allowed | Review UI over D1 payload; disable expired/stale; explicit selection |
| Forbidden | Approve & Apply button that calls a mutating RPC; reuse of ApprovedApplyModal |
| Inputs | D1 viewmodel |
| Outputs | Human-readable diff + statuses |
| Tests | Paths-only is insufficient; multi-proposal requires click-to-select; Neutron still does not import legacy modal |
| Exit | Review UX ready; mutation still unauthorized |
| Next grant | D3 |
D3
| Gate | Rule |
|---|---|
| Allowed | Host factory for NeutronMutationApproval from intent; reject spoofed fields; tests may call existing Apply |
| Forbidden | Production Desktop mutating control; public Approve-only RPC; token in responses |
| Inputs | Typed intent fixture |
| Outputs | Host approval object in-process; public results still token-free |
| Tests | Section 43 adversarial cases that do not require Desktop chrome |
| Exit | Complete (D3 merged). Issuer rejects spoofed model/renderer fields; tests cover section 43 items that do not require Desktop chrome |
| Next grant | Explicit maintainer authorization for D4. DL and Desktop durableStateDirectory wiring are merged. D4 is not authorized by this brief |
D4
| Gate | Rule |
|---|---|
| Allowed | approveAndApply → host approval → Slice 3.1 claim/lock → graph Apply → Slice 4 |
| Forbidden | N4 tool, mutationAllowed: true, Undo, silent retry, client approval records, extending invoke_neutron_request |
| Inputs | Typed intent |
| Outputs | Structured Apply/verification result |
| Tests | Merged D4 coverage: exact bytes, spoof rejection, staleness, duplicate, concurrent, lock, no model call, applied vs verified. Reconnect remains D5 |
| Exit | Complete (D4 merged, PR #534). One human action mutates only through the canonical chain; token stays host-only |
| Next candidate | D5, only after a new explicit maintainer authorization. This row does not authorize D5 |
D5
| Gate | Rule |
|---|---|
| Allowed | Result/evidence/status recovery UX; optional design of Retry verification |
| Forbidden | Retry Apply, Undo, collapsing statuses |
| Inputs | D4 result + status.get |
| Outputs | Applied vs verified vs reconciliation copy |
| Tests | Response-lost-after-Apply, verification mismatch labeling |
| Exit | Complete. Slice 1 (PR #537) recovers durable truth. Slice 2 (PR #540) recovers it after reconnect. Verification retry is a separate post-D5 operation. Undo is not authorized |
| Next candidate | Undo / rollback execution, only under a new explicit grant |
DL
| Gate | Rule |
|---|---|
| Allowed | Remove fabricated Approved Apply success from production Desktop |
| Forbidden | Wiring Neutron to a stub; adding real Apply; leaving fake success reachable |
| Exit | No Desktop path fabricates applied: true without an authoritative host |
| Status | Implemented and merged (PR #525) |
| Next grant | Complete for DL. D4 later merged (PR #534). D5 Slice 1 later merged (PR #537). Slice 2 is implemented on branch awaiting maintainer review |
43. Adversarial tests
D3 merged (tests/neutron-mutation-approval-intent.test.ts) covers at least: spoofed authority fields; fake token/digests/bodies; approved: true, authorized, mutationAllowed; wrong proposalId; preview proposal; stale/expired/cancelled proposal; wrong root/project/session/graph; payload tampering; graph staleness; multiple authoritative proposals (exact proposalId, no first-wins); missing payload after restart; duplicate intent; one-use Apply replay via Slice 3.1; no model call; no public Approve RPC; no approveAndApply RPC; N4 still seven read-only tools.
D4 merged (tests/neutron-mutation-approve-and-apply.test.ts, tests/desktop-neutron-approve-apply-ui.test.ts) covers at least: exact reviewed bytes; renderer bytes ignored; spoofed authority rejected before write; stale project fail-closed; pre-write project-state revalidation; duplicate request does not write twice; concurrent callers cannot both mutate; an existing project lock blocks Apply; missing durable state fails closed; no model call on the Approve & Apply path; verification failure stays applied and is not retried; no public Approve RPC; no status.get; the method is absent from the generic Neutron command; N4 remains seven read-only tools; Approve & Apply is enabled only for a current review.
D5 Slice 1 merged (tests/neutron-mutation-status-recovery.test.ts, PR #537) covers response-lost recovery, applied versus verification, identity-bound index rejection, canonical pointer mismatch, and historical status after a later external edit.
D5 Slice 2 merged (PR #540, tests/desktop-neutron-mutation-recovery.test.ts) covers reconnect UX: uncertain transport presentation, one status.get after authenticated reconnect, and explicit Refresh status. Post-D5 verification retry is covered by tests/neutron-mutation-verification-retry.test.ts. Remain unauthorized: Undo, rollback execution UI, a mutation history browser, and two-window Desktop chrome walkthroughs beyond the focused UI tests.
DL tests (tests/desktop-legacy-approved-apply-isolation.test.ts, merged with PR #525) cover: no fabricated applied: true; no timeout mutation success; no synthetic previousContent; Neutron cannot reach ApprovedApplyModal; modal removed; no real Apply call; no mutating daemon RPC; no approveAndApply; mutationAllowed === false; N4 remains seven read-only tools.
44. Production metrics
Docs only. Do not touch production files in the brief PR. Future slices may count structured statuses without logging bodies or tokens.
45. Canonical document
This file: docs/roadmap/NEUTRON_N6_DESKTOP_MUTATION_HOST_BRIEF.md
46. Cross-links
Update pointers in DUTY_WATCH.md, PROJECT_STATE.md, NEUTRON_RUNTIME_ROADMAP.md, NEUTRON_N6_DESKTOP_READONLY_BRIEF.md, and NEUTRON_MUTATION_ROUTING_BRIEF.md. Mark D1, D2, D3, DL, and the Desktop durable-state prerequisite, D4 (PR #534), and D5 (Slice 1 PR #537, Slice 2 PR #540) complete and merged. Undo remains unauthorized. Verification retry is the separate post-D5 operation.
47. Status wording (normative)
Implementation and handoff PRs that cite this brief must repeat:
D1 IMPLEMENTED AND MERGED (PR #516)
DESKTOP MUTATION D2 IMPLEMENTED AND MERGED (PR #519)
D3 IMPLEMENTED AND MERGED (PR #522)
DL IMPLEMENTED AND MERGED (PR #525)
DESKTOP MUTATION DURABLE HOST STATE PREREQUISITE IMPLEMENTED AND MERGED (PR #528)
DESKTOP MUTATION D4 IMPLEMENTED AND MERGED (PR #534)
DESKTOP MUTATION D5 COMPLETE (Slice 1 PR #537, Slice 2 PR #540)
mutationAllowed remains literal false. N4 remains seven read-only tools.
48. Architecture decision
D1 (implemented): read-only authoritative mutation review payload transport from the authenticated daemon to Desktop (PR #516).
D2 (implemented): Desktop exact mutation review UI over D1 (PR #519). Read-only. No Approve/Apply.
D3 (implemented, PR #522): host approval-intent protocol and security tests. In-process issuer only. No production Approve/Apply.
DL (implemented, PR #525): legacy fake Approved Apply path removed from production Desktop. Does not add real Apply or wire intentloom.project.approvedApply.v1.
Desktop durable-state prerequisite (implemented and merged, PR #528): trusted Desktop-owned --neutron-mutation-state-dir / durableStateDirectory wiring for future Slice 3.1 host Apply. The native host derives <app_data>/neutron-mutation-state (Unix 0700; no Windows ACL claim beyond the code). The daemon validates the path and passes it into createNeutronSessionRuntime. The directory is not public. Not D4. No second store or lock.
D4 (implemented and merged, PR #534): one trusted-host Approve & Apply operation. Prerequisites merged before this slice: D1, D2, D3, DL, and Desktop durableStateDirectory wiring. D5 is complete (Slice 1 PR #537, Slice 2 PR #540). This document does not authorize Undo.
Rationale for sequencing (unchanged):
- Exact reviewed bytes live in the host payload store; D1 exposes them and D2 presents them without widening mutation authority.
- D3 proves the host issuer cannot be spoofed through intent fields; combined approveAndApply is the later authority slice, now merged as D4.
- Undo and any N4 mutation tool remain future grants. Slice 1 status recovery and Slice 2 reconnect UX are merged. Verification retry is the separate post-D5 operation.
Slice 1 is the merged read-only status recovery. Slice 2 is the merged Desktop reconnect presentation (PR #540). The durable-state prerequisite is merged and is not itself Approve & Apply. D4 is the merged host operation.
49. Out of scope (repeat)
D4 (merged, PR #534) delivered the bounded host Approve & Apply operation described above. Still out of scope, and not authorized by this document:
- verification retry UI
- Undo / host rollback execution
- N4 mutation tool
mutationAllowedchange- automatic Apply retry
- persisting unused approvals
- rematerializing payloads from the model after daemon restart
- a public Approve-only RPC or an Apply-by-token RPC