@sema-agent/core 7.25.0 → 7.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. package/CHANGELOG.md +81 -0
  2. package/dist/core/arg-summary.d.ts +0 -7
  3. package/dist/core/arg-summary.js +2 -1
  4. package/dist/core/ask-origin.d.ts +84 -0
  5. package/dist/core/ask-origin.js +31 -3
  6. package/dist/core/auto-compaction.d.ts +5 -0
  7. package/dist/core/auto-compaction.js +7 -0
  8. package/dist/core/auto-mode-directive.d.ts +45 -0
  9. package/dist/core/auto-mode-directive.js +47 -0
  10. package/dist/core/checkpoint-store.d.ts +10 -1
  11. package/dist/core/engine-notice.d.ts +10 -0
  12. package/dist/core/gate-lanes.js +18 -10
  13. package/dist/core/governance-codes.d.ts +1 -1
  14. package/dist/core/governance-codes.js +3 -0
  15. package/dist/core/hooks.d.ts +3 -0
  16. package/dist/core/mcp-probe.d.ts +122 -0
  17. package/dist/core/mcp-probe.js +132 -0
  18. package/dist/core/memory-engine/engine.d.ts +6 -1
  19. package/dist/core/memory-engine/engine.js +30 -2
  20. package/dist/core/memory-engine/index.d.ts +1 -0
  21. package/dist/core/memory-engine/index.js +1 -0
  22. package/dist/core/memory-engine/shadowing.d.ts +58 -0
  23. package/dist/core/memory-engine/shadowing.js +40 -0
  24. package/dist/core/memory-engine/tools.d.ts +33 -1
  25. package/dist/core/memory-engine/tools.js +136 -31
  26. package/dist/core/memory-engine/types.d.ts +10 -0
  27. package/dist/core/memory.d.ts +60 -0
  28. package/dist/core/memory.js +47 -2
  29. package/dist/core/permission-rule-store.d.ts +51 -8
  30. package/dist/core/permission-rule-store.js +112 -65
  31. package/dist/core/reminder-mint.d.ts +41 -0
  32. package/dist/core/reminder-mint.js +55 -0
  33. package/dist/core/runner/compaction-restate.d.ts +9 -0
  34. package/dist/core/runner/compaction-restate.js +20 -0
  35. package/dist/core/runner/contracts.d.ts +18 -1
  36. package/dist/core/runner/denial-limit-arms.js +1 -1
  37. package/dist/core/runner/permission-rule-lanes.d.ts +5 -17
  38. package/dist/core/runner/permission-rule-lanes.js +5 -23
  39. package/dist/core/runner/prepare-ask-lane.js +2 -0
  40. package/dist/core/runner/prepare-context-lane.d.ts +4 -0
  41. package/dist/core/runner/prepare-context-lane.js +7 -2
  42. package/dist/core/runner/prepare-inherited-gate.d.ts +5 -1
  43. package/dist/core/runner/prepare-inherited-gate.js +4 -2
  44. package/dist/core/runner/prepare-memory.js +47 -6
  45. package/dist/core/runner/prepare-park-ask.js +1 -0
  46. package/dist/core/runner/prepare-policy-chain.js +8 -4
  47. package/dist/core/runner/prepare-suspend-saga.d.ts +2 -0
  48. package/dist/core/runner/prepare-suspend-saga.js +3 -1
  49. package/dist/core/runner/prepare-task.js +7 -4
  50. package/dist/core/runner/run-leg.js +9 -1
  51. package/dist/core/runner/run-recovery-lanes.js +2 -2
  52. package/dist/core/runner/run-terminal-adoption.js +2 -2
  53. package/dist/core/runner/run-turn-boundary.js +2 -2
  54. package/dist/core/store-contracts/permission-rule-write-contract.d.ts +20 -0
  55. package/dist/core/store-contracts/permission-rule-write-contract.js +77 -0
  56. package/dist/core/task-result.d.ts +11 -0
  57. package/dist/core/task-spec.d.ts +31 -0
  58. package/dist/core/tool-catalog-entries.js +3 -3
  59. package/dist/core/tool-policy.d.ts +38 -0
  60. package/dist/core/tool-policy.js +35 -24
  61. package/dist/core/types.d.ts +1 -1
  62. package/dist/index.d.ts +7 -2
  63. package/dist/index.js +6 -1
  64. package/dist/stores/file/permission-rule-store.js +48 -29
  65. package/dist/tools/fs/bash-lexer.d.ts +13 -0
  66. package/dist/tools/fs/bash-lexer.js +61 -0
  67. package/dist/tools/fs/bash-program-position.d.ts +32 -3
  68. package/dist/tools/fs/bash-program-position.js +33 -2
  69. package/dist/tools/fs/bash-readonly-classifier.d.ts +6 -4
  70. package/dist/tools/fs/bash-readonly-classifier.js +55 -18
  71. package/dist/tools/fs/shell-read-plan.js +9 -11
  72. package/package.json +1 -1
  73. package/test/export-surface.snapshot.json +29 -1
  74. package/wire-changes/core@7.25.1.json +64 -0
  75. package/wire-changes/core@7.26.0.json +695 -0
@@ -1,6 +1,6 @@
1
1
  import { DEFAULT_CHARS_PER_TOKEN, estimateContextTokens } from "../../internal/harness.js";
2
2
  import { clearStaleToolResults, createClearedProjectionLedger, dropEmptyFailureAssistants, editBudget, replayClearedProjection, resolveTriggerWindow } from "../context-edit.js";
3
- import { dropOrphanToolResults, guardBudget, insertTrimNotice, trimToBudget } from "../context-guard.js";
3
+ import { dropOrphanToolResults, guardBudget, insertTrimNotice, protectGitFrame, trimToBudget } from "../context-guard.js";
4
4
  import { AGGREGATE_MEDIA_BUDGET_BYTES, capAggregateMediaBytes } from "../media-byte-cap.js";
5
5
  import { capAggregateToolResults } from "../tool-result-budget.js";
6
6
  import { createOffloadPersist } from "../tool-result-store.js";
@@ -9,7 +9,7 @@ import { deliverEngineNotice } from "../types.js";
9
9
  import { applyGitFrameGuard } from "./git-status-frame.js";
10
10
  import { applyContextShrinkToReadFileState } from "../../tools/fs/index.js";
11
11
  export function prepareContextLane(input) {
12
- const { model, deps, sessionId, runId, hostTaskId, offloadStore, microCompactKnob, harness, batchHaltRef, gitStatusRef, requestLossyRef, trimPressureRef, readFileState } = input;
12
+ const { model, deps, sessionId, runId, hostTaskId, offloadStore, microCompactKnob, harness, batchHaltRef, gitStatusRef, postureFrameRef, requestLossyRef, trimPressureRef, readFileState } = input;
13
13
  const editAt = editBudget(model);
14
14
  const guardAt = guardBudget(model);
15
15
  const microCompact = buildMicroCompactState(deps, model, sessionId, runId, offloadStore, microCompactKnob);
@@ -85,6 +85,11 @@ export function prepareContextLane(input) {
85
85
  }
86
86
  },
87
87
  });
88
+ if (postureFrameRef.protectedText !== undefined) {
89
+ const kept = protectGitFrame(working, trimmed, guardAt, { protectedText: postureFrameRef.protectedText }, charsPerToken);
90
+ if (kept.action === "kept" || kept.action === "reinserted")
91
+ trimmed = kept.messages;
92
+ }
88
93
  const swept = dropOrphanToolResults(trimmed);
89
94
  if (swept.dropped.length > 0) {
90
95
  try {
@@ -44,7 +44,7 @@ import type { DelegationContentSafety } from "../memory-engine/delegation-proven
44
44
  import type { SessionPermissionRules } from "../session-policy-store.js";
45
45
  import type { SessionStore } from "../session.js";
46
46
  import { type OnAsk } from "../tool-policy.js";
47
- import type { RunnerDeps, RuntimeCaps, ShellGateDoctrine, TaskEvent, TaskSpec, ToolExecuteContext } from "../types.js";
47
+ import type { ApproverPosture, RunnerDeps, RuntimeCaps, ShellGateDoctrine, TaskEvent, TaskSpec, ToolExecuteContext } from "../types.js";
48
48
  import type { ActiveSkillScope } from "./active-skill-scope.js";
49
49
  import type { InheritedGate, Prepared, PrepareResume, RunInternals, ToolFaceSnapshot } from "./contracts.js";
50
50
  import { type StopForDenialLimit } from "./denial-limit-arms.js";
@@ -96,6 +96,8 @@ export interface PrepareInheritedGateInput {
96
96
  resolvedInteractionPosture: ToolExecuteContext["interactionPosture"];
97
97
  /** borrowed-readonly — the task's own auto-mode intent seat, screened and snapshotted before the first await. */
98
98
  autoModeSeat: boolean;
99
+ /** borrowed-readonly — the task's own approver-posture seat, screened and snapshotted before the first await. */
100
+ approverPostureSeat: ApproverPosture | undefined;
99
101
  /** borrowed-readonly — whether an execution env is wired at all (the full-shell predicate's first term). */
100
102
  handsEnabled: boolean;
101
103
  /** borrowed-readonly — the nested-usage sink the ctx forwards. */
@@ -192,6 +194,8 @@ export interface PrepareInheritedGateResult {
192
194
  inheritedParentConstraints: NonNullable<InheritedGate["parentConstraints"]>[number][] | undefined;
193
195
  /** owned — the auto-mode INTENT (own seat ∨ live chain ∨ checkpoint bit); the arming site judges the other two arms. */
194
196
  autoModeIntent: boolean;
197
+ /** owned — the resolved approver POSTURE (own seat, else the live chain's, else the checkpoint's); rendered, never enforced. */
198
+ approverPosture: ApproverPosture | undefined;
195
199
  /** owned — the seed ∩ live admitted org scopes (one side absent ⇒ the other; both absent ⇒ undefined). */
196
200
  inheritedAdmittedOrgScopes: readonly string[] | undefined;
197
201
  /** owned — the prior own org verdict: checkpoint ∩ same-session ref (either alone stands; neither ⇒ undefined). */
@@ -7,7 +7,7 @@ import { askApproverIdentity, durableMandateSourceOf } from "../tool-policy.js";
7
7
  import { attachRebuiltDenialTrackers, createDenialLimitStop } from "./denial-limit-arms.js";
8
8
  import { asThrownError } from "../thrown-value.js";
9
9
  export function prepareInheritedGate(input) {
10
- const { spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, autoModeSeat, handsEnabled, reportUsage, harnessRef, skillScope, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun } = input;
10
+ const { spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, autoModeSeat, approverPostureSeat, handsEnabled, reportUsage, harnessRef, skillScope, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun } = input;
11
11
  const liveInheritedGate = internals?.inheritedGate;
12
12
  const seedInheritedGate = resume?.seed.inheritedGate;
13
13
  const inheritedAncestorRules = (() => {
@@ -39,6 +39,7 @@ export function prepareInheritedGate(input) {
39
39
  : seedShellGate;
40
40
  const inheritedParentConstraints = attachRebuiltDenialTrackers(liveInheritedGate?.parentConstraints, deps.autoMode?.denialLimit);
41
41
  const autoModeIntent = autoModeSeat || liveInheritedGate?.autoModeRequested === true || seedInheritedGate?.autoModeRequested === true;
42
+ const approverPosture = [approverPostureSeat, liveInheritedGate?.approverPosture, seedInheritedGate?.approverPosture].find((w) => w !== undefined);
42
43
  const liveAdmittedOrg = liveInheritedGate?.admittedOrgScopes;
43
44
  const seedAdmittedOrg = seedInheritedGate?.admittedOrgScopes;
44
45
  const inheritedAdmittedOrgScopes = liveAdmittedOrg === undefined ? seedAdmittedOrg : seedAdmittedOrg === undefined ? liveAdmittedOrg : liveAdmittedOrg.filter((s) => seedAdmittedOrg.includes(s));
@@ -166,6 +167,7 @@ export function prepareInheritedGate(input) {
166
167
  ...(ancestorRules.length > 0 ? { ancestorRules } : {}),
167
168
  ...(effectiveShellGate !== "off" ? { shellGate: effectiveShellGate } : {}),
168
169
  ...(autoModeIntent ? { autoModeRequested: true } : {}),
170
+ ...(approverPosture !== undefined ? { approverPosture } : {}),
169
171
  ...(parentConstraints.length > 0 ? { parentConstraints } : {}),
170
172
  admittedOrgScopes: memoryAdmittedOrgScopesRef.current,
171
173
  ...(orgGovernedProvenance ? { orgAdmissionGoverned: true } : {}),
@@ -276,5 +278,5 @@ export function prepareInheritedGate(input) {
276
278
  ...(spec.enablePlanMode === true ? { enterPlanMode } : {}),
277
279
  };
278
280
  };
279
- return { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, autoModeIntent, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, ancestorSessionRulesRef, ancestorRulesNow, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx };
281
+ return { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, autoModeIntent, approverPosture, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, ancestorSessionRulesRef, ancestorRulesNow, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx };
280
282
  }
@@ -3,7 +3,9 @@ import { deliverEngineNotice } from "../types.js";
3
3
  import { admitMemoryScopes } from "../memory-admission.js";
4
4
  import { adoptLegacyRepoDirs, canonicalize, deriveRepoControlPlaneDir, deriveProjectControlDir, deriveProjectMemoryDir, deriveRepoMemoryDir, drainMemoryAnnouncements, enqueueMemoryAnnouncement, isContainedIn, lookupProjectIdHint, recordProjectIdHint, resolveMemoryEngineRoot } from "../memory-engine/layout.js";
5
5
  import { classifyScopePlanes, derivePersonalControlDir, derivePersonalMemoryDir, mergeHarvestReports, mergeInjections, needsDualRoots, parsedProjectPlane } from "../memory-engine/dual-root.js";
6
- import { normalizeMemorySpec } from "../memory.js";
6
+ import { memoryProjectScopeAbsentNotice, normalizeMemorySpec } from "../memory.js";
7
+ import { deriveSupersededSet } from "../memory-engine/consolidation.js";
8
+ import { shadowNamesOf } from "../memory-engine/shadowing.js";
7
9
  import { MEMORY_ANNOUNCEMENT_CAPTURE_OPTOUT_CODA, MEMORY_ANNOUNCEMENT_READONLY_CODA, MEMORY_CAPTURE_INDETERMINATE_NOTICE, MEMORY_CAPTURE_OPTOUT_NOTICE, MEMORY_PREFERENCE_DISCIPLINE, MEMORY_READONLY_NOTICE, MemoryEngine, entryFileHeadCarriesOrigin, memoryCaptureOptedOutNotice, memoryCaptureOptOutUnpersistedNotice, memoryConsolidationRecommendedNotice, memoryHarvestQuarantinedNotice, memoryHoldNotices, memoryRecallDisciplineSegment, memoryRestoreWithheldNotice, memorySessionPollutedNotice, pollutionContainmentCounts, } from "../memory-engine/engine.js";
8
10
  import { adjudicateCaptureOptOut, captureOptOutDeniedError, captureOptOutUnpersistedError } from "./memory-capture-optout.js";
9
11
  import { readSessionCaptureOptOut } from "../memory-engine/layout.js";
@@ -46,6 +48,9 @@ export async function prepareMemory(input) {
46
48
  memorySpec.scopes = outcome.scopes;
47
49
  memorySpec.writeScope = outcome.writeScope;
48
50
  }
51
+ if (memorySpec?.enabled === true && memorySpec.projectKey?.absent !== undefined) {
52
+ deliverEngineNotice(deps.onNotice, memoryProjectScopeAbsentNotice({ reason: memorySpec.projectKey.absent, taskRoot: taskRootPath, sessionId, writeScope: memorySpec.writeScope }));
53
+ }
49
54
  const useMemoryEngine = Boolean(memorySpec && memorySpec.enabled && deps.memoryBackend);
50
55
  const capturePosture = input.memoryCapturePolicy ?? "open";
51
56
  const captureEntitlement = input.captureEntitlement ?? { value: undefined, faulted: false };
@@ -367,11 +372,35 @@ export async function prepareMemory(input) {
367
372
  const personalEngine = personal.engine;
368
373
  const p = planes;
369
374
  await armCaptureOptOut(p.writePlane === "personal" ? personalEngine : projectEngine);
370
- const projectHandle = await projectEngine.materialize(p.project, p.writePlane === "project" ? memorySpec.writeScope : null, { adoptionRestricted: adoptionRestricted || captureOptedOut || captureIndeterminate, sessionId });
371
- materializedResidue.push(...planeScopes(p.project, p.writePlane === "project" ? memorySpec.writeScope : null));
372
- const personalHandle = await personalEngine.materialize(p.personal, p.writePlane === "personal" ? memorySpec.writeScope : null, { adoptionRestricted: adoptionRestricted || captureOptedOut || captureIndeterminate, sessionId });
373
- materializedResidue.push(...planeScopes(p.personal, p.writePlane === "personal" ? memorySpec.writeScope : null));
374
375
  const writeIsPersonal = p.writePlane === "personal";
376
+ const mounts = [
377
+ { engine: writeIsPersonal ? personalEngine : projectEngine, scopes: writeIsPersonal ? p.personal : p.project, write: p.writePlane === null ? null : memorySpec.writeScope },
378
+ { engine: writeIsPersonal ? projectEngine : personalEngine, scopes: writeIsPersonal ? p.project : p.personal, write: null },
379
+ ];
380
+ const mounted = [];
381
+ let shadowNames;
382
+ for (const m of mounts) {
383
+ const handle = await m.engine.materialize(m.scopes, m.write, {
384
+ adoptionRestricted: adoptionRestricted || captureOptedOut || captureIndeterminate,
385
+ sessionId,
386
+ ...(m.write === null && shadowNames !== undefined && shadowNames.size > 0 ? { shadowNames } : {}),
387
+ });
388
+ mounted.push(handle);
389
+ materializedResidue.push(...planeScopes(m.scopes, m.write));
390
+ if (m.write !== null) {
391
+ try {
392
+ const withheld = new Set(m.engine.readChallengeExclusions().keys());
393
+ const committed = await m.engine.listCommittedHeaders([m.write]);
394
+ const superseded = deriveSupersededSet(committed, { excluded: (id) => withheld.has(id) });
395
+ shadowNames = shadowNamesOf(committed, m.write, { excluded: (id) => withheld.has(id) || superseded.has(id) });
396
+ }
397
+ catch {
398
+ shadowNames = undefined;
399
+ }
400
+ }
401
+ }
402
+ const projectHandle = writeIsPersonal ? mounted[1] : mounted[0];
403
+ const personalHandle = writeIsPersonal ? mounted[0] : mounted[1];
375
404
  memoryMarkedEntriesServed = projectHandle.markedEntriesPresent === true || personalHandle.markedEntriesPresent === true;
376
405
  writeEngine = writeIsPersonal ? personalEngine : projectEngine;
377
406
  writeHandle = writeIsPersonal ? personalHandle : projectHandle;
@@ -385,6 +414,7 @@ export async function prepareMemory(input) {
385
414
  recordRetrieved: (ids) => projectEngine.recordRetrieved(ids),
386
415
  challengeExclusions: () => projectEngine.readChallengeExclusions(),
387
416
  enginePlaneProvenance: projectEngine.provenanceMode,
417
+ committedHeaders: () => projectEngine.listCommittedHeaders(planeScopes(p.project, p.writePlane === "project" ? memorySpec.writeScope : null)),
388
418
  },
389
419
  {
390
420
  backend: retrievalBackend(personal.backend, adoptionRestricted || captureOptedOut || captureIndeterminate || p.writePlane !== "personal"),
@@ -392,6 +422,7 @@ export async function prepareMemory(input) {
392
422
  recordRetrieved: (ids) => personalEngine.recordRetrieved(ids),
393
423
  challengeExclusions: () => personalEngine.readChallengeExclusions(),
394
424
  enginePlaneProvenance: personalEngine.provenanceMode,
425
+ committedHeaders: () => personalEngine.listCommittedHeaders(planeScopes(p.personal, p.writePlane === "personal" ? memorySpec.writeScope : null)),
395
426
  },
396
427
  ];
397
428
  harvestBoth = async () => {
@@ -430,6 +461,7 @@ export async function prepareMemory(input) {
430
461
  recordRetrieved: (ids) => personalEngine.recordRetrieved(ids),
431
462
  challengeExclusions: () => personalEngine.readChallengeExclusions(),
432
463
  enginePlaneProvenance: personalEngine.provenanceMode,
464
+ committedHeaders: () => personalEngine.listCommittedHeaders(planeScopes(memorySpec.scopes, memorySpec.writeScope)),
433
465
  },
434
466
  ];
435
467
  }
@@ -458,9 +490,17 @@ export async function prepareMemory(input) {
458
490
  recordRetrieved: (ids) => engine.recordRetrieved(ids),
459
491
  challengeExclusions: () => engine.readChallengeExclusions(),
460
492
  enginePlaneProvenance: engine.provenanceMode,
493
+ committedHeaders: () => engine.listCommittedHeaders(planeScopes(memorySpec.scopes, memorySpec.writeScope)),
461
494
  },
462
495
  ];
463
496
  }
497
+ for (const [scope, count] of [...(writeHandle.shadowedByScope ?? []), ...(readOnlyHandle?.shadowedByScope ?? [])]) {
498
+ if (count <= 0 || scope === memorySpec.writeScope)
499
+ continue;
500
+ const row = mountedScopeRows.find((r) => r.scope === scope);
501
+ if (row !== undefined)
502
+ row.shadowed = (row.shadowed ?? 0) + count;
503
+ }
464
504
  const foreignUnderRoot = [];
465
505
  if (input.writeToolsMounted && writeHandle.writeScope !== null && input.advertisedWritableDirs !== undefined) {
466
506
  const controlPlane = canonicalize(writeEngine.controlPlaneDir);
@@ -678,6 +718,7 @@ export async function prepareMemory(input) {
678
718
  contract: memorySpec.scopeContract === "v2" ? "v2" : "legacy",
679
719
  scopes: mountedScopeRows,
680
720
  writeScope: memorySpec.writeScope,
721
+ ...(memorySpec.projectKey !== undefined ? { projectKey: memorySpec.projectKey } : {}),
681
722
  };
682
723
  if (input.memoryProvenance !== "off") {
683
724
  const session = memoryEngineSession;
@@ -689,7 +730,7 @@ export async function prepareMemory(input) {
689
730
  plane.onTaintedDelivery = markDerived;
690
731
  }
691
732
  if (input.memorySearchToolsPlanned)
692
- memoryTools = createMemoryEngineTools({ planes: toolPlanes, sessionPollution: () => writeEngine.sessionPollution(sessionId), provenance: input.memoryProvenance });
733
+ memoryTools = createMemoryEngineTools({ planes: toolPlanes, sessionPollution: () => writeEngine.sessionPollution(sessionId), provenance: input.memoryProvenance, writeScope: memorySpec.writeScope });
693
734
  }
694
735
  catch (err) {
695
736
  if (typeof err.code === "string" &&
@@ -294,6 +294,7 @@ export function prepareParkAsk(input) {
294
294
  ...(realApproval !== undefined ? { requiresRealApproval: true } : {}),
295
295
  ...askCarryRowMembers(carry),
296
296
  ...ruleOffersOf(req.toolName, parkedArgs, {
297
+ ...(carry?.mandated === true ? { mandated: true } : {}),
297
298
  ...(realApproval !== undefined ? { requiresRealApproval: true } : {}),
298
299
  ...(shadowedRule !== undefined ? { persistedRuleShadowed: shadowedRule } : {}),
299
300
  ...(askDecisionReason !== undefined ? { decisionReason: askDecisionReason } : {}),
@@ -10,7 +10,8 @@ import { RETIRED_TOOL_NAMES } from "../tool-name-aliases.js";
10
10
  import { NAMESPACED_NAME_SHAPES, protocolOf } from "../protocol-table.js";
11
11
  import { emitTrace } from "../trace.js";
12
12
  import { createActiveSkillScopePolicy } from "./active-skill-scope.js";
13
- import { probeMandatedAsk } from "../ask-origin.js";
13
+ import { askMandateMember, probeMandatedAsk } from "../ask-origin.js";
14
+ const ancestorFacts = (ask) => ({ ...(ask ?? {}), ancestorResolved: true });
14
15
  import { headlessDenyAtFold, headlessDenyAtRecheck, inheritedAskCarry, judgeInheritedClassifier, lateAskSettlementObserver, settleDenialLimitFallback } from "./denial-limit-arms.js";
15
16
  import { askGrantShapeOf } from "./inherited-ask-grants.js";
16
17
  import { createPermissionRuleLanes, inheritedAskRuleEvidence, createRuleOffersOf } from "./permission-rule-lanes.js";
@@ -349,11 +350,12 @@ export async function preparePolicyChain(input) {
349
350
  toolName: creq.toolName,
350
351
  toolCallId: creq.toolCallId,
351
352
  args: editArgs,
352
- ...ruleOffersOf(creq.toolName, editArgs, { ...(re ?? {}), ancestorResolved: true }),
353
+ ...ruleOffersOf(creq.toolName, editArgs, ancestorFacts(re)),
353
354
  message: re.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
354
355
  ...askSourceIdentity(),
355
356
  ...riskAxesOf(creq.toolName),
356
357
  ...(re.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
358
+ ...askMandateMember(ancestorFacts(re)),
357
359
  ...(re.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: re.persistedRuleShadowed } : {}),
358
360
  ...inheritedAskCarry(judged, isLiveApproverSeat(onAskOf), ancestorTracker),
359
361
  ruleEvidence: inheritedAskEvidence,
@@ -424,11 +426,12 @@ export async function preparePolicyChain(input) {
424
426
  toolName: creq.toolName,
425
427
  toolCallId: creq.toolCallId,
426
428
  args: presentedArgs,
427
- ...ruleOffersOf(creq.toolName, presentedArgs, { ...(inheritedAsk.action === "ask" ? inheritedAsk : {}), ancestorResolved: true }),
429
+ ...ruleOffersOf(creq.toolName, presentedArgs, ancestorFacts(inheritedAsk.action === "ask" ? inheritedAsk : undefined)),
428
430
  message: inheritedAsk.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
429
431
  ...askSourceIdentity(),
430
432
  ...riskAxesOf(creq.toolName),
431
433
  ...(inheritedAsk.action === "ask" && inheritedAsk.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
434
+ ...askMandateMember(ancestorFacts(inheritedAsk.action === "ask" ? inheritedAsk : undefined)),
432
435
  ...(inheritedAsk.action === "ask" && inheritedAsk.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: inheritedAsk.persistedRuleShadowed } : {}),
433
436
  ...inheritedAskCarry(judged, isLiveApproverSeat(pc.onAsk), pc.autoMode?.denialTracking),
434
437
  ruleEvidence: inheritedAskEvidence,
@@ -493,11 +496,12 @@ export async function preparePolicyChain(input) {
493
496
  toolName: creq.toolName,
494
497
  toolCallId: creq.toolCallId,
495
498
  args: presentedArgs,
496
- ...ruleOffersOf(creq.toolName, presentedArgs, { ...(inheritedAsk.action === "ask" ? inheritedAsk : {}), ancestorResolved: true }),
499
+ ...ruleOffersOf(creq.toolName, presentedArgs, ancestorFacts(inheritedAsk.action === "ask" ? inheritedAsk : undefined)),
497
500
  message: inheritedAsk.message ?? `approval required for "${creq.toolName}" (inherited parent policy)`,
498
501
  ...askSourceIdentity(),
499
502
  ...riskAxesOf(creq.toolName),
500
503
  ...(inheritedAsk.action === "ask" && inheritedAsk.requiresRealApproval === true ? { requiresRealApproval: true } : {}),
504
+ ...askMandateMember(ancestorFacts(inheritedAsk.action === "ask" ? inheritedAsk : undefined)),
501
505
  ...(inheritedAsk.action === "ask" && inheritedAsk.persistedRuleShadowed !== undefined ? { persistedRuleShadowed: inheritedAsk.persistedRuleShadowed } : {}),
502
506
  ...inheritedAskCarry(judged, isLiveApproverSeat(pc.onAsk), pc.autoMode?.denialTracking),
503
507
  ruleEvidence: inheritedAskEvidence,
@@ -134,6 +134,8 @@ export interface PrepareSuspendSagaInput {
134
134
  inheritedShellGate: InheritedGate["shellGate"];
135
135
  /** borrowed-readonly — the auto-mode intent (seat ∨ live ∨ seed), carried while the latch is healthy. */
136
136
  autoModeIntent: boolean;
137
+ /** borrowed-readonly — the resolved approver posture (seat, else live, else seed), persisted verbatim. */
138
+ approverPosture: InheritedGate["approverPosture"];
137
139
  /** borrowed-readonly — the auto-mode decider, or undefined (the latch-health reading only). */
138
140
  /** borrowed-readonly — the inherited admitted org scopes, or undefined (copied onto the row). */
139
141
  inheritedAdmittedOrgScopes: readonly string[] | undefined;
@@ -79,7 +79,7 @@ function stampPlacementRootSessionId(placementRootResolved) {
79
79
  return placementValueOrAbsent(placementRootResolved);
80
80
  }
81
81
  export function prepareSuspendSaga(input) {
82
- const { gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, autoModeIntent, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter } = input;
82
+ const { gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, autoModeIntent, approverPosture, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter } = input;
83
83
  if (!gateMachineryActive)
84
84
  return { saga: undefined };
85
85
  const inFlightSpendMicroUsd = () => {
@@ -139,6 +139,7 @@ export function prepareSuspendSaga(input) {
139
139
  return ancestorRulesAtMint !== undefined ||
140
140
  inheritedShellGate !== undefined ||
141
141
  autoModeIntentCarried ||
142
+ approverPosture !== undefined ||
142
143
  inheritedAdmittedOrgScopes !== undefined ||
143
144
  ownOrgVerdictRef.current !== undefined ||
144
145
  orgGovernedProvenance ||
@@ -147,6 +148,7 @@ export function prepareSuspendSaga(input) {
147
148
  ...(ancestorRulesAtMint !== undefined ? { ancestorRules: structuredClone(ancestorRulesAtMint) } : {}),
148
149
  ...(inheritedShellGate !== undefined ? { shellGate: inheritedShellGate } : {}),
149
150
  ...(autoModeIntentCarried ? { autoModeRequested: true } : {}),
151
+ ...(approverPosture !== undefined ? { approverPosture } : {}),
150
152
  ...(inheritedAdmittedOrgScopes !== undefined ? { admittedOrgScopes: [...inheritedAdmittedOrgScopes] } : {}),
151
153
  ...(ownOrgVerdictRef.current !== undefined
152
154
  ? { ownAdmittedOrgScopes: [...ownOrgVerdictRef.current.scopes], ownAdmittedOrgWriteScope: ownOrgVerdictRef.current.writeScope }
@@ -9,6 +9,7 @@ import { hasSessionFork } from "../session.js";
9
9
  import { SEND_MESSAGE_TOOL_NAME } from "../../agents/send-message-tool.js";
10
10
  import { loadAnnounceOnceLedger } from "./announce-once-ledger.js";
11
11
  import { isValidReminderMark, mintReminderMark } from "../reminder-mint.js";
12
+ import { refuseInvalidApproverPosture } from "../auto-mode-directive.js";
12
13
  import { emitTrace } from "../trace.js";
13
14
  import { reservedCollisions, reservedFor } from "../../brain/request-params.js";
14
15
  import { STALL_CONNECT_MS, STALL_FIRST_TOKEN_MS, STALL_IDLE_MS, withBrainCallGuardrail } from "../../brain/timeout.js";
@@ -265,6 +266,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
265
266
  spec = doors.spec;
266
267
  const checkpointStore = resolveCheckpointStore(spec, deps), checkpointSeat = checkpointSeatOf(spec, checkpointStore);
267
268
  const autoModeSeat = refuseInvalidAutoModeSeat(spec);
269
+ const approverPostureSeat = refuseInvalidApproverPosture(spec);
268
270
  const { toolFaceSnapshot, promptProfile, lockedPreflight, resolvedInteractionPosture, microCompactKnob, resolvedRole, model, thinking, compModel, fableMitigations, memoryDelegationEvidence, memoryProvenance, memoryCapturePolicy, usageWindows, brainCallGuardrailRef, brainCallGuardrailMs } = doors;
269
271
  announceToolModelGate(deps.onNotice, model.id, doors.modelGate);
270
272
  const modelGateManifest = toolModelGateManifest(doors.modelGate);
@@ -376,7 +378,7 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
376
378
  let readDenyAdditionsNormalized = [];
377
379
  let handsLessResolvedFace;
378
380
  let handsLessResolvedWriteFace;
379
- const { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, autoModeIntent, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, ancestorSessionRulesRef, ancestorRulesNow, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx } = prepareInheritedGate({ spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, checkpointSeat, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, autoModeSeat, handsEnabled, reportUsage, harnessRef, skillScope, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun: () => { abortController.abort(); void harness.abort(); }, runtimeCaps: () => runtimeCaps, hooks: () => hooks, preToolUseObservational: () => preToolUseObservational, hookTimeoutMs: () => hookTimeoutMs, hookEnvFace: () => hookEnvFace, autoModeDecider: () => autoModeDecider, autoModeDenialTracking: () => autoModeDenialTracking, autoModeArming: () => autoModeArming, agentForkDenial: () => agentForkDenial, observersActive: () => observersActive, resolvedReadFace: () => resolvedReadFace, resolvedWriteFace: () => resolvedWriteFace, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized, handsLessResolvedFace: () => handsLessResolvedFace, handsLessResolvedWriteFace: () => handsLessResolvedWriteFace, centerAdoption: () => centerAdoptionRef.current, memoryEngineSession: () => memoryEngineSession });
381
+ const { liveInheritedGate, seedInheritedGate, inheritedAncestorRules, inheritedShellGate, inheritedParentConstraints, autoModeIntent, approverPosture: approverPostureResolved, inheritedAdmittedOrgScopes, priorOwnOrgVerdict, orgGovernedProvenance, effectiveShellGate, ownSessionRulesRef, ancestorSessionRulesRef, ancestorRulesNow, memoryAdmittedOrgScopesRef, ownOrgVerdictRef, orgAdmissionCheckpointState, faceCheckpointSection, faceCheckpointState, writeFaceCheckpointSection, f012CheckpointState, frozenOnAsk, hookEnvSource, notifyOwnHookCrash, frozenOnQuestion, parentSpecSeat, provenanceForChildrenRef, delegationSettlementRef, inheritedGateForChildren, carrierReadFace, carrierWriteFace, fullShellReachable, gateStopRef, stopForDenialLimit, enrichSpecToolCtx } = prepareInheritedGate({ spec, deps, internals, resume, sessions, sessionId, runId, hostTaskId, taskRootFinal, executionEnv, ownedEnv, lockedPreflight, checkpointSeat, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, autoModeSeat, approverPostureSeat, handsEnabled, reportUsage, harnessRef, skillScope, forwardEvent, subagentRetain, worktreeIsolation, requestReview, requestStopAfterTurn, enterPlanMode, reminderMark, reminderDisclosureCounts, fileHistoryEnabled, historyScope, historyRoot, historyFs, abortRun: () => { abortController.abort(); void harness.abort(); }, runtimeCaps: () => runtimeCaps, hooks: () => hooks, preToolUseObservational: () => preToolUseObservational, hookTimeoutMs: () => hookTimeoutMs, hookEnvFace: () => hookEnvFace, autoModeDecider: () => autoModeDecider, autoModeDenialTracking: () => autoModeDenialTracking, autoModeArming: () => autoModeArming, agentForkDenial: () => agentForkDenial, observersActive: () => observersActive, resolvedReadFace: () => resolvedReadFace, resolvedWriteFace: () => resolvedWriteFace, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized, handsLessResolvedFace: () => handsLessResolvedFace, handsLessResolvedWriteFace: () => handsLessResolvedWriteFace, centerAdoption: () => centerAdoptionRef.current, memoryEngineSession: () => memoryEngineSession });
380
382
  const { roster, blockedRef, runtimeCaps, runtimeCapsFaulted, complianceDenies, complianceDegraded, agentForkDenial, observersActive, autoModeArmReason, autoModeDecider, autoModeDenialTracking, autoModeArming, selfOrchestrationActive, workflowToolsActive, workflowSizeGuideline } = await prepareCapsAndWorkflow({ spec, deps, internals, session, sessionId, runId, hostTaskId, taskScope, taskRootFinal, model, thinking, lockedPreflight, checkpointSeat, toolEffects, toolFaceSnapshot, promptProfile, resolvedInteractionPosture, harnessRef, frozenOnAsk, parentSpecSeat, carrierReadFace, carrierWriteFace, forwardEvent, inheritedGateForChildren, enrichSpecToolCtx, maybeOffload, requestReview, enterPlanMode, autoModeIntent, peerLaneActive, peerSendMessageBuiltIn, runnerSelf, memoryEngineSession: () => memoryEngineSession, centerAdoption: () => centerAdoptionRef.current, readDenyAdditionsNormalized: () => readDenyAdditionsNormalized });
381
383
  const tools = roster.tools;
382
384
  const pausedRef = {};
@@ -700,15 +702,16 @@ export async function prepareTask(spec, deps, sessions, resume, internals, runne
700
702
  });
701
703
  }
702
704
  const { askLane } = prepareAskLane({ gateMachineryActive, abortController, effectivePolicy, budgetSnapshot, handsCwdRef, advertisedWritableDirs, tools, inheritedUnavailableAsks, inheritedAskGrants, onAsk, humanReviewRef, now, ruleOffersOf, askSourceIdentity, riskAxesOf, autoModeDenialTracking, spec, deps, sessionId, runId, hooks, hookTimeoutMs, notifyOwnHookCrash });
703
- const { saga } = prepareSuspendSaga({ gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, autoModeIntent, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter });
705
+ const { saga } = prepareSuspendSaga({ gateMachineryActive, abortController, harness, checkpointStore, deps, sessionId, hostTaskId, taskScope, liveSpendRef, resume, nestedStats, internals, activeTools, outputRef, readFileStateForCheckpoint, faceCheckpointSection, writeFaceCheckpointSection, reminderMark, handsCwdRef, worktreeSessionRef, undeliveredContinuationRef, inheritedParentConstraints, seedInheritedGate, ancestorRulesNow, inheritedShellGate, autoModeIntent, approverPosture: approverPostureResolved, inheritedAdmittedOrgScopes, ownOrgVerdictRef, orgGovernedProvenance, announcedListingsRef, gitStatusRef, hookIdentity, placementRootResolved, externalContentTargetActive, remoteEnvFailures, memoryEngineSession, suspendLoopRef, ownedEnv, incompleteSuspendAdapter });
704
706
  const { suspendForResource, suspendForPlatformLimit, suspendForReview } = prepareBoundaryParks({ saga, spec, checkpointStore, abortController, harness, session, sessions, sessionId, deps, priorLedger, maxSlices, maxSuspends, resourceTotal, priorSuspendCount, suspendChainBase, humanReviewRef, liveSpendRef, now, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, pausedRef, remoteEnvFailures, resourceSuspendEligible, durableSuspendInfraReady, incompleteSuspendAdapter });
705
707
  const { parkAsk } = prepareParkAsk({ askLane, saga, spec, deps, sessionId, checkpointStore, toolRosterDeltas: rosterSeat, parkLaneArmed, contentAskRoutable, liveQuestionFace, mountedQuestionTool, contentAskBindings, lateStrandedAnswers, discloseStrandedAnswers, onAsk, runtimeCaps, inheritedUnavailableAsks, basePolicyForResumeEdit, budgetSnapshot, handsCwdRef, advertisedWritableDirs, offloadStore, ownedEnv, incompleteSuspendAdapter, session, sessions, suspendChainBase, maxSuspends, remoteEnvFailures, shellGatedTools, effectiveShellGate, durableApproval, priorLedger, liveSpendRef, resourceTotal, faceCheckpointState, f012CheckpointState, orgAdmissionCheckpointState, ruleOffersOf, now, humanReviewRef, abortController, harness, pausedRef });
706
708
  prepareGateStations({ askLane, parkAsk, tools, toolRosterDeltas: rosterSeat, toolEffects, deps, toolCallGateArmedRef, effectivePolicy, hooks, egressTools, irreversibleTools, spec, complianceDenies, harness, blockedToolCalls, inheritedAskGrants, inheritedUnavailableAsks, foldAskClasses, ancestorSandboxAdmissions, preToolContexts, gateOutcomes, batchHaltRef, blockedTracked, hookIdentity, reminderMark, planModeRef, hostTaskId, sessionId, ownGatePreToolUse, hookTimeoutMs, handsCwdRef, hookEnvFace, writeProtectionCheck, shellReadCheck, irreversibilityTier, reversibilityProbes, abortController, shellGatedTools, autoModeDecider, autoModeDenialTracking, stopForDenialLimit, permissionRuleLane, permissionRuleOrgLane, questionToolMounted, sandboxAdmissionArmed, sandboxBoundaryCapable, emitSandboxAdmitted, delegation, notifyOwnHookCrash });
707
- const { microCompact, charsPerToken } = prepareContextLane({ model, deps, sessionId, runId, hostTaskId, offloadStore, microCompactKnob, harness, batchHaltRef, gitStatusRef, requestLossyRef, trimPressureRef, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}) });
709
+ const postureFrameRef = {};
710
+ const { microCompact, charsPerToken } = prepareContextLane({ model, deps, sessionId, runId, hostTaskId, offloadStore, microCompactKnob, harness, batchHaltRef, gitStatusRef, postureFrameRef, requestLossyRef, trimPressureRef, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}) });
708
711
  const preparedHolder = {};
709
712
  const { cacheBreakDetector, cacheFingerprint, promptOverheadTokens, readTaskFile, normalizeAttachmentPath, isDedupStubResult, recentlyReadFiles, onCompactionApplied, detectExternalChanges, listBackgroundTasks, centerCompactionCandidate, effectiveReadFaceObserved, effectiveWriteFaceObserved, effectiveReadDenyObserved } = prepareTurnWiring({ deps, systemPromptSeat, harnessTools, fpRef, model, epochArtifactDigestForSnapshot, promptProfile, fableMitigations, systemBlocks, thinking, spec, sessionId, turnSnapshotRef, promptManifest, charsPerToken, handsEnabled, executionEnv, attachmentRootCanonical, additionalRootsCanonical, additionalReadRootsCanonical, readDenyMatcher, resolvedReadFace, handsCwdRef, readFileStateForCheckpoint, denyNarrowingPolicy, abortController, hostTaskId, taskScope, buildAssembleInputs, centerAdoptionRef, harnessRef, epochDeclaredSections, carrierReadFace, carrierWriteFace, readDenyAdditionsNormalized, preparedHolder });
710
713
  await onceLedger.settle(session, announcedListingsRef);
711
- const buildPrepared = () => ({ harness, session, sessionId, runId, reminderMark, reminderDisclosureCounts, editedFilesSnapshot, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp, ...(mcpRedial !== undefined ? { mcpRedial } : {}), ...(a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, gateOutcomes, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(fileHistoryBoundary !== undefined ? { fileHistoryBoundary } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved, effectiveWriteFace: effectiveWriteFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, toolProgressRef, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}), ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), ...(sealReadStateSeat !== undefined ? { sealReadStateSeat } : {}), denyNarrowingPolicy, ...(permissionRuleLane !== undefined ? { persistedRuleLane: permissionRuleLane } : {}), ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), ownedEnv, pausedRef, undeliveredContinuationRef, suspendProgressRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, workCutRef, brainCallGuardrailRef, gateStopRef, suspendForReview, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}), resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolRoster, toolRosterDeltas: rosterSeat, structuredProjector, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, microCompact, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), planModeRef, stopRequestedRef, announcedSnapshotRecovered: onceLedger.recovered, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(projectInstructionContent !== undefined ? { projectInstructionContent } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, frameReceipts: { pending: [], listingSnapshotOwed: false }, gitStatusRef, listBackgroundTasks, hookIdentity, ...(sessionBoxBound !== undefined ? { sessionBox: sessionBoxBound } : {}), hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}), ...(shellReadCheck !== undefined ? { shellReadCheck } : {}), ...(toolDisclosureSeat !== undefined ? { toolDisclosure: toolDisclosureSeat } : {}) });
714
+ const buildPrepared = () => ({ harness, session, sessionId, runId, reminderMark, reminderDisclosureCounts, approvalPosture: { autoMode: autoModeIntent, ...(approverPostureResolved !== undefined ? { approverPosture: approverPostureResolved } : {}) }, editedFilesSnapshot, taskRootPath: taskRootFinal, model, thinking, compModel: effectiveCompModel, mcp, ...(mcpRedial !== undefined ? { mcpRedial } : {}), ...(a2a.tools.length > 0 ? { a2a } : {}), blockedRef, outputRef, abortController, conflictRef, blockedToolCalls, gateOutcomes, batchHaltRef, nestedStats, ...(rewindNotes.length > 0 ? { rewindNotes } : {}), ...(fileHistoryBoundary !== undefined ? { fileHistoryBoundary } : {}), ...(effectiveReadFaceObserved !== undefined ? { effectiveReadFace: effectiveReadFaceObserved, effectiveWriteFace: effectiveWriteFaceObserved } : {}), ...(effectiveReadDenyObserved !== undefined ? { effectiveReadDenyPatterns: effectiveReadDenyObserved } : {}), effectiveMemoryScopes: memoryEffectiveScopes, cwdRef: handsCwdRef, toolProgressRef, ...(advertisedWritableDirs !== undefined ? { advertisedWritableDirs } : {}), ...(worktreeSessionRef !== undefined ? { worktreeSessionRef } : {}), ...(workspaceStateSettle !== undefined ? { workspaceStateSettle } : {}), ...(sealReadStateSeat !== undefined ? { sealReadStateSeat } : {}), denyNarrowingPolicy, ...(permissionRuleLane !== undefined ? { persistedRuleLane: permissionRuleLane } : {}), ...(basePolicyForResumeEdit !== undefined ? { basePolicyForResumeEdit } : {}), ...(permissionRuleOrgLane !== undefined ? { permissionRuleOrg: permissionRuleOrgLane } : {}), releaseSignal, settleContentAskBindings, cacheBreakDetector, cacheFingerprint, wiringManifest, promptManifest, epochDeclaredSections, activeTools, ...(deferred.size > 0 ? { deferredToolNames: deferred } : {}), toolMaterializeStatic, deferDirectCall, ...(staticFaceForRef.current !== undefined ? { staticFaceFor: staticFaceForRef.current } : {}), ownedEnv, pausedRef, undeliveredContinuationRef, suspendProgressRef, remoteEnvFailures, reviewRequestRef, suspendLoopRef, suspendForResource, ...(suspendForPlatformLimit !== undefined ? { suspendForPlatformLimit } : {}), ...(envLifetimeSuspendAt !== undefined ? { envLifetimeSuspendAt } : {}), ...(usageGovernance !== undefined ? { usageGovernance } : {}), callIssuedAtRef, workCutRef, brainCallGuardrailRef, gateStopRef, suspendForReview, ...(readFileStateForCheckpoint !== undefined ? { readFileState: readFileStateForCheckpoint } : {}), resourceLedger: priorLedger, liveSpendRef, humanReviewRef, now, tools, toolRoster, toolRosterDeltas: rosterSeat, structuredProjector, toolEffects, wakeRecovered, promptOverheadTokens, lastBrainContext, readTaskFile, recentlyReadFiles, normalizeAttachmentPath, isDedupStubResult, ...(onCompactionApplied ? { onCompactionApplied } : {}), compactionReuseRef, trimPressureRef, microCompact, ...(memoryEngineSession ? { memoryEngineSession } : {}), ...(subagentRetain ? { subagentRetain } : {}), ...(lspDiagnostics && nudgeLspOnEdit ? { lspDiagnostics: { registry: lspDiagnostics, nudge: nudgeLspOnEdit, runIdent: lspRunIdent } } : {}), planModeRef, stopRequestedRef, announcedSnapshotRecovered: onceLedger.recovered, ...(dateChange ? { dateChange } : {}), ...(instructionSources ? { instructionSources } : {}), ...(projectInstructionContent !== undefined ? { projectInstructionContent } : {}), ...(workflowSizeGuideline ? { workflowSizeGuideline } : {}), ...(detectExternalChanges ? { detectExternalChanges } : {}), ...(toolsDeltaRef ? { toolsDeltaRef } : {}), ...(agentListing ? { agentListing } : {}), ...(skillsListing ? { skillsListing } : {}), announcedListingsRef, frameReceipts: { pending: [], listingSnapshotOwed: false }, gitStatusRef, postureFrameRef, listBackgroundTasks, hookIdentity, ...(sessionBoxBound !== undefined ? { sessionBox: sessionBoxBound } : {}), hookTimeoutMs, ...(turnSnapshotRef.current !== undefined ? { turnSnapshot: turnSnapshotRef.current } : {}), ...(centerCompactionCandidate !== undefined ? { centerCompactionCandidate } : {}), ...(shellReadCheck !== undefined ? { shellReadCheck } : {}), ...(toolDisclosureSeat !== undefined ? { toolDisclosure: toolDisclosureSeat } : {}) });
712
715
  rollback.commit();
713
716
  const prepared = buildPrepared();
714
717
  preparedHolder.current = prepared;
@@ -8,7 +8,8 @@ import { buildHumanInputEvent, projectHumanInput } from "../human-input-projecti
8
8
  import { ORG_ADJUDICATION_TIMEOUT_MS, settleOrgVerdictWithin } from "../permission-rule-org.js";
9
9
  import { applyPersistedTightening, disclosedRuleSet } from "../persisted-rule-arms.js";
10
10
  import { computeCostMicroUsd } from "../pricing.js";
11
- import { mintSystemReminder } from "../reminder-mint.js";
11
+ import { composeApprovalPostureDirective } from "../auto-mode-directive.js";
12
+ import { mintSystemReminder, statedReminderBodiesOf } from "../reminder-mint.js";
12
13
  import { hasDestroy } from "../remote-env.js";
13
14
  import { advertisedWritableRootsSeat, refuseOutOfContractDecision } from "../tool-policy.js";
14
15
  import { callFaceSeat, judgeParkedToolIdentity, rosterEntryNamed, toolCallFaceOf } from "../tool-roster.js";
@@ -375,6 +376,13 @@ export async function runLeg(input) {
375
376
  effectiveObjective = `${firstFrames.map((f) => mintSystemReminder(sanitizeUntrustedText(f.body, attachmentEnvelopeTags(f.source)), prepared.reminderMark)).join("\n")}\n${effectiveObjective}`;
376
377
  }
377
378
  }
379
+ if (composeApprovalPostureDirective(prepared.approvalPosture) !== undefined) {
380
+ const postureDirective = composeApprovalPostureDirective(prepared.approvalPosture, await statedReminderBodiesOf(prepared.session, prepared.reminderMark));
381
+ if (postureDirective !== undefined) {
382
+ prepared.postureFrameRef.protectedText = mintSystemReminder(postureDirective, prepared.reminderMark);
383
+ effectiveObjective = `${prepared.postureFrameRef.protectedText}\n${effectiveObjective}`;
384
+ }
385
+ }
378
386
  const gitQueuedChars = gitLegDelivered !== undefined && gitLegDelivered.standalone ? gitLegDelivered.wrapped.length : 0;
379
387
  const precallMicroUsd = rs.budget.maxCostMicroUsd === undefined
380
388
  ? 0
@@ -6,7 +6,7 @@ import { emitTrace } from "../trace.js";
6
6
  import { isDegenerateCutMessage } from "../../brain/terminal-cause.js";
7
7
  import { rebaseCadenceWindows } from "./turn-attachments.js";
8
8
  import { buildWorkingFileAttachments, centerAdoptionOption, contextInstructionFilesOption, emitInputTruncated, forkContextOption } from "./compaction-call-options.js";
9
- import { gitRestateOption } from "./git-leg-delivery.js";
9
+ import { compactionRestateOptions } from "./compaction-restate.js";
10
10
  import { COMPACTION_FREED_EPSILON, COMPACTION_REGROWTH_FACTOR, MAX_CONSECUTIVE_COMPACTION_FAILURES } from "./compaction-knobs.js";
11
11
  import { compactionHookOptions, recordCompactionReuse, seamCCompactionOptions } from "./compaction-seams.js";
12
12
  import { asThrownError, thrownText } from "../thrown-value.js";
@@ -41,7 +41,7 @@ export function runRecoveryLanes(input) {
41
41
  ...contextInstructionFilesOption(prepared),
42
42
  ...(prepared.onCompactionApplied ? { onApplied: prepared.onCompactionApplied } : {}),
43
43
  ...seamCCompactionOptions(runner, prepared),
44
- ...gitRestateOption(prepared),
44
+ ...compactionRestateOptions(prepared),
45
45
  ...windowSafetyOptions(prepared.harness.getModel()),
46
46
  ...compactionHookOptions(runner, spec, prepared.sessionId, "forced", prepared.hookIdentity, { timeoutMs: prepared.hookTimeoutMs, signal: passSignal }),
47
47
  });
@@ -7,7 +7,7 @@ import { answerSettled, errorCodeOf } from "./assemble-result.js";
7
7
  import { awaitChargeWithSlowDisclosure, discloseUnevaluableWindow } from "./clock-and-limits.js";
8
8
  import { buildWorkingFileAttachments, centerAdoptionOption, contextInstructionFilesOption, emitInputTruncated, forkContextOption } from "./compaction-call-options.js";
9
9
  import { compactionHookOptions, recordCompactionReuse, seamCCompactionOptions } from "./compaction-seams.js";
10
- import { gitRestateOption } from "./git-leg-delivery.js";
10
+ import { compactionRestateOptions } from "./compaction-restate.js";
11
11
  import { MAX_CONSECUTIVE_COMPACTION_FAILURES } from "./compaction-knobs.js";
12
12
  import { reconciledToolEndBody } from "./tool-end-body.js";
13
13
  import { asThrownError, thrownText } from "../thrown-value.js";
@@ -149,7 +149,7 @@ async function finish(spec, prepared, opts, runner, sessions) {
149
149
  ...contextInstructionFilesOption(prepared),
150
150
  ...(prepared.onCompactionApplied ? { onApplied: prepared.onCompactionApplied } : {}),
151
151
  ...seamCCompactionOptions(runner, prepared),
152
- ...gitRestateOption(prepared),
152
+ ...compactionRestateOptions(prepared),
153
153
  ...compactionHookOptions(runner, spec, prepared.sessionId, "auto", prepared.hookIdentity, {
154
154
  timeoutMs: prepared.hookTimeoutMs,
155
155
  ...(opts?.cancelled === true ? { signal: prepared.abortController.signal } : {}),
@@ -13,7 +13,7 @@ import { droppedToolDigest, withAnnounceDigests } from "./announce-once-ledger.j
13
13
  import { formatDiagnosticsBlock } from "../lsp-diagnostics.js";
14
14
  import { workflowSizeGuidelineChangeNotice } from "../../orchestration/workflow-size-guideline.js";
15
15
  import { awaitChargeWithSlowDisclosure, discloseUnevaluableWindow, ENV_DUE_GOVERNANCE_READ_BUDGET_MS, GOVERNANCE_READ_STALLED, LIMIT_APPROACH_DEFAULT_THRESHOLDS, limitApproachFrames, platformLimitTerminal, raceUntilDeadline } from "./clock-and-limits.js";
16
- import { gitRestateOption } from "./git-leg-delivery.js";
16
+ import { compactionRestateOptions } from "./compaction-restate.js";
17
17
  import { COMPACTION_FREED_EPSILON, COMPACTION_REGROWTH_FACTOR, MAX_CONSECUTIVE_COMPACTION_FAILURES } from "./compaction-knobs.js";
18
18
  import { compactionHookOptions, recordCompactionReuse, seamCCompactionOptions } from "./compaction-seams.js";
19
19
  import { contextUsageSections } from "../../prompt-assembly/section-usage.js";
@@ -583,7 +583,7 @@ export function createTurnBoundary(input) {
583
583
  ...contextInstructionFilesOption(prepared),
584
584
  ...(prepared.onCompactionApplied ? { onApplied: prepared.onCompactionApplied } : {}),
585
585
  ...seamCCompactionOptions(runner, prepared),
586
- ...gitRestateOption(prepared),
586
+ ...compactionRestateOptions(prepared),
587
587
  ...windowSafetyOptions(event.model),
588
588
  ...compactionHookOptions(runner, spec, prepared.sessionId, passTrigger, prepared.hookIdentity, { timeoutMs: prepared.hookTimeoutMs, signal: prepared.abortController.signal }),
589
589
  });
@@ -0,0 +1,20 @@
1
+ /**
2
+ * The permission-rule WRITE-FACE contract: what every backend behind `PermissionRuleWriter.apply` owes
3
+ * its caller. The sibling kit (`permission-rule-sync-contract.ts`) pins the pure JOIN; this one pins the
4
+ * DOOR it arrives through. A delta is DATA — a foreign caller, a replayed record, a deserialized join —
5
+ * so "the type system says it cannot happen" is not an answer a door may give, and a kind outside the
6
+ * union must be refused rather than folded as whichever arm a dispatch chain happens to end with. The
7
+ * positive control rides the same run on purpose: a backend that throws at everything refuses the
8
+ * unnamed kind too, so a suite without the control proves nothing.
9
+ *
10
+ * KIT form: vitest-free (`node:assert`), runner seam via {@link ContractAssertionRunner}, no skip hook.
11
+ */
12
+ import type { WritableDurableRulePartition } from "../permission-rule-store.js";
13
+ import { type ContractAssertionRunner } from "./contract-harness.js";
14
+ export interface PermissionRuleWriteContractHooks {
15
+ /** Open ONE fresh, EMPTY partition with a write face — once per case; teardown stays with the caller. */
16
+ make: () => WritableDurableRulePartition | Promise<WritableDurableRulePartition>;
17
+ runAssertion?: ContractAssertionRunner;
18
+ }
19
+ /** Run the full conformance suite. Throws (assert) on the first violated clause. */
20
+ export declare function permissionRuleWriteContract(hooks: PermissionRuleWriteContractHooks): Promise<void>;
@@ -0,0 +1,77 @@
1
+ import assert from "node:assert";
2
+ import { PERMISSION_RULE_WRITER } from "../permission-rule-store.js";
3
+ import { beginContract } from "./contract-harness.js";
4
+ const GLOBAL = { kind: "global" };
5
+ function add(actor, counter) {
6
+ return { dot: { actor, counter }, origin: "user", createdAt: "2026-01-01T00:00:00.000Z" };
7
+ }
8
+ const IDENTITY = { behavior: "deny", rule: "Bash(curl:*)", scope: GLOBAL, tool: "Bash", match: "prefix", command: "curl" };
9
+ function tombstoneFor(removed, deletedBy) {
10
+ return { behavior: IDENTITY.behavior, rule: IDENTITY.rule, scope: GLOBAL, removedDots: removed.map((a) => a.dot), deletedBy };
11
+ }
12
+ function liveDots(rules) {
13
+ return new Set(rules.flatMap((r) => r.adds.map((a) => `${r.behavior}|${r.rule}|${a.dot.actor}#${a.dot.counter}`)));
14
+ }
15
+ async function rawShape(part) {
16
+ const raw = await part[PERMISSION_RULE_WRITER].readRaw();
17
+ const dots = [...liveDots(raw.rules)].sort();
18
+ const tombs = raw.tombstones.map((t) => `${t.behavior}|${t.rule}|${t.deletedBy.actor}#${t.deletedBy.counter}`).sort();
19
+ return JSON.stringify({ rev: raw.rev, dots, tombs });
20
+ }
21
+ export async function permissionRuleWriteContract(hooks) {
22
+ const { run, settle } = beginContract(hooks.runAssertion);
23
+ run("the named arms land, a landing ADVANCES the revision, and a spent revision is ANSWERED (positive control)", async () => {
24
+ const part = await hooks.make();
25
+ const writer = part[PERMISSION_RULE_WRITER];
26
+ const a1 = add("kit", 1);
27
+ const held = (await part.list()).rev;
28
+ const first = await writer.apply({ kind: "tighten-add", ...IDENTITY, add: a1 }, { expectedRev: held });
29
+ assert.ok(!("conflict" in first) && !("refused" in first), "a well-formed tighten-add must land");
30
+ const afterAdd = await part.list();
31
+ assert.deepEqual(liveDots(afterAdd.rules), new Set([`deny|${IDENTITY.rule}|kit#1`]), "the added dot must be visible through list()");
32
+ assert.ok(afterAdd.rev > held, "a landed write must ADVANCE the revision — a constant one is not a fence, it is a number");
33
+ assert.equal((await part[PERMISSION_RULE_WRITER].readRaw()).rev, afterAdd.rev, "every face must report ONE revision");
34
+ const rawBefore = await rawShape(part);
35
+ const stale = await writer.apply({ kind: "tighten-add", ...IDENTITY, add: add("kit", 2) }, { expectedRev: held });
36
+ assert.ok("conflict" in stale, "a spent expectedRev must answer `conflict`, not throw and not land");
37
+ const staleDelete = await writer.apply({ kind: "tighten-delete", tombstone: tombstoneFor([a1], { actor: "kit", counter: 8 }) }, { expectedRev: held });
38
+ assert.ok("conflict" in staleDelete, "a spent expectedRev must answer `conflict` on the delete arm too");
39
+ assert.equal(await rawShape(part), rawBefore, "a declined write must move nothing — rows, tombstones or revision");
40
+ const beforeDelete = (await part.list()).rev;
41
+ const removed = await writer.apply({ kind: "tighten-delete", tombstone: tombstoneFor([a1], { actor: "kit", counter: 9 }) }, { expectedRev: beforeDelete });
42
+ assert.ok(!("conflict" in removed) && !("refused" in removed), "a well-formed tighten-delete must land");
43
+ assert.deepEqual(liveDots((await part.list()).rules), new Set(), "the tombstoned dot must leave the live view");
44
+ assert.ok((await part.list()).rev > beforeDelete, "a landed delete must advance the revision too");
45
+ const raw = await part[PERMISSION_RULE_WRITER].readRaw();
46
+ assert.ok(liveDots(raw.rules).has(`deny|${IDENTITY.rule}|kit#1`), "the raw face must RETAIN a tombstoned add (list() filters; the evidence may not be filtered too)");
47
+ const tomb = raw.tombstones.find((t) => t.deletedBy.actor === "kit" && t.deletedBy.counter === 9);
48
+ assert.ok(tomb !== undefined, "the raw face must retain the tombstone a landed delete wrote");
49
+ assert.deepEqual({ behavior: tomb.behavior, rule: tomb.rule, scope: tomb.scope, removedDots: tomb.removedDots }, { behavior: IDENTITY.behavior, rule: IDENTITY.rule, scope: GLOBAL, removedDots: [a1.dot] }, "the retained tombstone must be the WHOLE one — a partial one identifies nothing");
50
+ });
51
+ run("a delta whose kind is outside the union is REFUSED — not folded as the arm it resembles", async () => {
52
+ for (const [label, payload] of [
53
+ ["① a legal tombstone under an unnamed kind", { kind: "tighten-purge", tombstone: tombstoneFor([add("kit", 1)], { actor: "kit", counter: 9 }) }],
54
+ ["② a legal add payload under an unnamed kind", { kind: "import-add", ...IDENTITY, add: add("kit", 2) }],
55
+ ]) {
56
+ const part = await hooks.make();
57
+ const writer = part[PERMISSION_RULE_WRITER];
58
+ const seeded = add("kit", 7);
59
+ await writer.apply({ kind: "tighten-add", ...IDENTITY, add: seeded }, { expectedRev: (await part.list()).rev });
60
+ const before = await part.list();
61
+ const rawShapeBefore = await rawShape(part);
62
+ let threw = false;
63
+ try {
64
+ await writer.apply(payload, { expectedRev: before.rev });
65
+ }
66
+ catch {
67
+ threw = true;
68
+ }
69
+ assert.equal(threw, true, `${label}: the write face must refuse an unnamed delta kind`);
70
+ const after = await part.list();
71
+ assert.deepEqual(liveDots(after.rules), liveDots(before.rules), `${label}: a refused delta must not change the rows`);
72
+ assert.equal(after.rev, before.rev, `${label}: a refused delta must not advance the revision`);
73
+ assert.equal(await rawShape(part), rawShapeBefore, `${label}: a refused delta must move nothing in the RAW state either`);
74
+ }
75
+ });
76
+ await settle();
77
+ }
@@ -121,11 +121,22 @@ export type EffectiveMemoryScopes = {
121
121
  state: "mounted";
122
122
  reason?: never;
123
123
  contract: "v2" | "legacy";
124
+ /** `shadowed` = how many of this scope's entries the session's write home retires from the
125
+ * DEFAULT read face by name (they stay reachable by id and through `includeShadowed`).
126
+ * PRESENT-IFF non-zero, and never on the write home's own row — absence is "none", which is
127
+ * why zero is not spelled. A mount-time snapshot: the tool faces re-derive it per call, so a
128
+ * later challenge or tombstone can make the live answer smaller than this number. */
124
129
  scopes: Array<{
125
130
  scope: string;
126
131
  origin: "deployment" | "request";
132
+ shadowed?: number;
127
133
  }>;
128
134
  writeScope: string | null;
135
+ /** The project identity the assembly point DECLARED for this task, echoed verbatim
136
+ * ({@link import("./memory.js").MemoryProjectKey}). PRESENT-IFF the spec carried one:
137
+ * absence means the deployment said nothing, which is not the same as `{ absent }`, which
138
+ * means it looked and found none. */
139
+ projectKey?: import("./memory.js").MemoryProjectKey;
129
140
  materializedResidue?: never;
130
141
  } | {
131
142
  state: "memoryless";