{
  "repo": "core",
  "version": "7.23.0",
  "from": "116cf2ddb6b4ade389fae5a91529f0520fa494af",
  "to": "v7.23.0",
  "added": [
    {
      "name": "WriteProtectionViewAbsence",
      "kind": "interface",
      "tier": "advanced",
      "at": [
        "src/core/write-protect.ts:543",
        "src/index.ts:656"
      ],
      "criteriaCovered": true,
      "breaking": false
    }
  ],
  "removed": [],
  "changed": [],
  "narrowed": [
    {
      "id": "F7",
      "source": "behaviorFlips.summary",
      "text": "NARROWING on POSIX trees, WIDENING on win trees, on the symlink hop (#871). Every hop TARGET now passes the same `win32NamespaceScreen` (`src/tools/fs/safety.ts:831`) the entry spelling already passes, with the same family and the same refusal word, before anything is joined or probed (`src/tools/fs/safety.ts:1396`). A link target is a spelling this function has never seen and it goes on to be probed exactly like one the model wrote, so a target reading `\\\\.\\pipe\\p` had a raw device object PROBED (opening one can hang the process) and an extended-length target (`\\\\?\\C:\\x`) was carried forward verbatim. The verdict was already fail-closed — `isBlockedDevicePath` catches the device case on the terminal key — so what was open was the PROBE in between. The screen may REFUSE on any tree; it may only REWRITE on the tree whose namespace it is (`:1407-1412`): on a POSIX tree a backslash is a name byte, so mapping `\\\\?\\C:\\x` to `C:\\x` would name a DIFFERENT file. Who needs it: any deployment on a tree that can hold such a link. Who is hurt: a POSIX tree holding a file whose name literally begins `\\\\.\\` or `\\\\host\\` reached through a link target spelled bare — now refused. Compensation: that is exactly what the same spelling already got when the MODEL wrote it (the entry screen has refused those two shapes on POSIX trees since 7.18.0, pinned here as the consistency case); the screen is anchored at the start of the spelling, so a name with a directory in front of it (`<dir>/\\\\.\\pipe\\p`) is not screened and still resolves, on the entry side and on a hop alike. SCOPE, stated: this walk is only reached when `canonicalPath` could NOT answer, so a chain the environment's own `realpath` resolves is never read hop by hop and its intermediate targets are not screened (#907)."
    },
    {
      "id": "F9",
      "source": "behaviorFlips.summary",
      "text": "CLOSED SET GAINS ONE MEMBER (#869): `ENGINE_NOTICE_CODES` **66 -> 67**, the new member `config.write_protection_unresolved` (registered `src/core/governance-codes.ts:334`, audience `operator` `:533`, catalogued on `EngineNotice.code` `src/core/engine-notice.ts:50`, minted ONCE at `src/core/runner/write-protection-station.ts:88` inside the per-leg `onViewAbsent` sink `:83`). Two trees diffed member-by-member: added exactly `[\"config.write_protection_unresolved\"]`, removed `[]`. `detail: { reason, cause, sessionId, runId }` with `reason` in `\"unresolved-symlink\"` / `\"unresolvable-target\"` (`:58`) and `cause` the environment's own sentence. Dedup unit: **ONCE PER LEG PER REASON** (`:78`, the latch lives in the mount's frame because the judgment does — a module-level latch would make the second leg of a session silent about its own environment). It carries NO call identity (no tool name, no `toolCallId`): the fact is about the ENVIRONMENT, not about a write. Who needs it: the deployment that used to learn its environment cannot answer path questions from the REFUSAL F8 removed. Who is hurt: a consumer pinning the closed set's cardinality. Compensation: the member is additive, zero members removed, the audience row is set in the same commit, and announcing never narrows — no verdict moves because of the notice."
    },
    {
      "id": "F10",
      "source": "behaviorFlips.summary",
      "text": "NARROWING (allow -> the table's ask), the write-protection SEGMENT fold (#841). Two spellings that name a protected component on a real filesystem were cleared and are now judged: one carrying an INVISIBLE format character (a zero-width non-joiner inside `.ssh`, a right-to-left override in front of `.bashrc`) and one hiding the component behind an NTFS DATA-STREAM suffix (`.bashrc:evil`, which names `.bashrc` on win32). The reference implementation's own segment fold strips both; sema now does too, on the two axes they actually belong to. The INVISIBLE strip is on the ONE segment fold (`foldWriteProtectSegment`, `src/core/write-protect.ts:248`, class `U+200C-200F` / `U+202A-202E` / `U+206A-206F` / `U+FEFF` at `stripInvisibleFormat` `:261`), so it applies to EVERY reading and to BOTH sides of every comparison — a deployment row carrying one folds onto the plain spelling, and two rows differing only by an invisible are one row. The DATA-STREAM strip belongs to the win32 NAMESPACE readings alone (`stripWin32Component` `:268`), because on POSIX `a:b` is a legal filename that keeps its own identity; those readings are a 2x2 PRODUCT (`WIN32_NORMALIZATIONS` `:461-466`) because the two strips are independent of each other and every combination names a spelling the others cannot, and the invisible strip runs BEFORE the namespace strips (order is load-bearing: run the other way round and removing an invisible EXPOSES a trailing dot that nothing then strips). A strip that would EMPTY a component keeps the component. Who needs it: any deployment whose table names `.ssh` / `.bashrc` / `.claude` and whose tree can hold such a spelling. Who is hurt: **headless / no-approver lanes — these spellings move from executing to refused, as every table hit is there.** Compensation: ONE extra question at most on a TIGHTEN; the data-root passthrough is deliberately untouched (its comparison is byte-exact, and there an added match would be a guard BYPASSED rather than a spare question); and the fold is deliberately NOT full Unicode confusable folding, which would be a wider claim than the one made."
    }
  ],
  "closedSetMembers": [
    {
      "set": "ENGINE_NOTICE_CODES",
      "at": [
        "src/core/governance-codes.ts:312"
      ],
      "newSet": false,
      "added": [
        "config.write_protection_unresolved"
      ],
      "removed": [],
      "breaking": false,
      "criteriaCovered": true
    }
  ],
  "closedSetsUnreadable": [],
  "typeKeys": [
    {
      "type": "WriteProtectionCompileOptions",
      "at": [
        "src/core/write-protect.ts:630"
      ],
      "added": [
        "onViewAbsent"
      ],
      "removed": [],
      "breaking": false,
      "criteriaCovered": true
    }
  ],
  "declarationMoves": [],
  "mintMoves": [],
  "criteriaObserves": [
    {
      "kind": "engineNoticeCode",
      "name": "config.write_protection_target_view_absent",
      "assertions": [
        "G9.b"
      ]
    },
    {
      "kind": "engineNoticeCode",
      "name": "config.write_protection_unresolved",
      "assertions": [
        "G9.a",
        "G9.b"
      ]
    },
    {
      "kind": "taskEventField",
      "name": "tool_end.isError",
      "assertions": [
        "G6.a",
        "G6.b",
        "G6.c",
        "G7.a",
        "G7.b",
        "G7.c"
      ]
    },
    {
      "kind": "taskEventField",
      "name": "tool_end.output",
      "assertions": [
        "G4.a",
        "G4.b",
        "G7.a",
        "G7.b"
      ]
    },
    {
      "kind": "taskEventField",
      "name": "tool_end.structured",
      "assertions": [
        "G4.a",
        "G6.a",
        "G6.c"
      ]
    },
    {
      "kind": "typeMember",
      "name": "AskRequest.message",
      "assertions": [
        "G8.a",
        "G10.c",
        "G11.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "AskRequest.origin",
      "assertions": [
        "G8.a",
        "G8.b",
        "G10.a",
        "G10.b",
        "G10.c",
        "G11.a",
        "G12.c"
      ]
    },
    {
      "kind": "typeMember",
      "name": "AskRequest.persistedRuleShadowed",
      "assertions": [
        "G8.b"
      ]
    },
    {
      "kind": "typeMember",
      "name": "AskRequest.readRootCandidate",
      "assertions": [
        "G12.a",
        "G12.b",
        "G12.c"
      ]
    },
    {
      "kind": "typeMember",
      "name": "AskRequest.ruleOffersAbsence",
      "assertions": [
        "G11.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "AskRequest.toolName",
      "assertions": [
        "G10.a",
        "G11.b"
      ]
    },
    {
      "kind": "typeMember",
      "name": "ExecutionEnv.canonicalChain",
      "assertions": [
        "G5.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "ReadRootGrantCandidate.covers",
      "assertions": [
        "G12.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "ReversibilityVerdict.readRootCandidate",
      "assertions": [
        "G12.b"
      ]
    },
    {
      "kind": "typeMember",
      "name": "SymlinkChain.hops",
      "assertions": [
        "G5.a",
        "G5.b"
      ]
    },
    {
      "kind": "typeMember",
      "name": "TaskResult.stats.humanReview.gates.decision",
      "assertions": [
        "G3.b"
      ]
    },
    {
      "kind": "typeMember",
      "name": "TaskResult.stats.humanReview.gates.kind",
      "assertions": [
        "G3.c"
      ]
    },
    {
      "kind": "typeMember",
      "name": "TaskResult.stats.humanReview.gates.toolArg",
      "assertions": [
        "G3.b"
      ]
    },
    {
      "kind": "typeMember",
      "name": "TaskResult.stats.humanReview.gates.toolCallId",
      "assertions": [
        "G3.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "TaskResult.stats.humanReview.gates.toolName",
      "assertions": [
        "G3.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "TaskResult.stats.mechanisms.repetitionCuts",
      "assertions": [
        "G2.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "TaskResult.stats.mechanisms.repetitionEvents.segment",
      "assertions": [
        "G2.a",
        "G2.b"
      ]
    },
    {
      "kind": "typeMember",
      "name": "TaskResult.terminal.code",
      "assertions": [
        "G1.b",
        "G1.c"
      ]
    },
    {
      "kind": "typeMember",
      "name": "TaskResult.terminal.kind",
      "assertions": [
        "G1.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "TaskResult.terminal.message",
      "assertions": [
        "G1.a",
        "G1.c"
      ]
    },
    {
      "kind": "typeMember",
      "name": "WiringManifest.writeProtection",
      "assertions": [
        "G8.d"
      ]
    },
    {
      "kind": "typeMember",
      "name": "WriteProtectionCompileOptions.onViewAbsent",
      "assertions": [
        "G8.c"
      ]
    },
    {
      "kind": "typeMember",
      "name": "WriteProtectionViewAbsence.cause",
      "assertions": [
        "G8.c"
      ]
    },
    {
      "kind": "typeMember",
      "name": "WriteProtectionViewAbsence.unresolvedSymlink",
      "assertions": [
        "G8.c"
      ]
    }
  ],
  "consumersHint": {
    "cli": [
      "ENGINE_NOTICE_CODES",
      "WriteProtectionCompileOptions"
    ],
    "server": [
      "ENGINE_NOTICE_CODES",
      "WriteProtectionCompileOptions"
    ],
    "sdk": [],
    "note": "scanned cli@1faaadb8 server@ca4b7b34 sdk@6209e5e — a name listed is MENTIONED there today, which is evidence of consumption, not proof of it"
  },
  "breakingCount": 0,
  "breakingItems": [],
  "toResolvedAt": "020c4cb43b366fc76ed504baa2b072a1c67cb476"
}
