{
  "repo": "core",
  "version": "7.31.1",
  "from": "289b2d1998a3e890f6a8659ffc4397074ddd720a",
  "to": "v7.31.1",
  "added": [],
  "removed": [],
  "changed": [],
  "narrowed": [
    {
      "id": "F1",
      "source": "behaviorFlips.summary",
      "text": "NARROWING (#1094, memory store; +1 error code `memory.target_outside_store`, registered in `NON_GOVERNANCE_MEMORY_CODES`; +0 exports, +0 wire keys): an act at a memory store path is judged by where it LANDS, not by how it is spelled. One verdict — the target's DIRECTORY, resolved through every link on its existing prefix, must be the store root's physical form or inside it (the final component is not judged: the backend's `rm` removes a link standing there and `rename` replaces it, the engine's in-place writes refuse it with `ELOOP` — none follows it) — gates `FileMemoryEngineBackend.applyPatches` (per op at staging, the whole batch at the commit point, per op at each act), erasure (`MemoryEngine.eraseMemoryEntries`: commit point and act), journal replay (the whole journal before its first act, each op again at its act), and the engine's own writes at store paths: the copy-out projection, the harvest's missing-file restore, the read-only layer restore and the `MEMORY.md` index writes. The backend takes its root's physical form when it opens the store and re-checks it at the head of every face; the engine takes its memory dir's physical form at its first mount and a later mount that resolves elsewhere is refused. Separately, the engine's in-place write refuses a file with more than one name (a hard link), before anything is truncated. A refused `applyPatches` / erasure throws a plain `Error` whose `.code` is the new code — before the commit marker nothing is written and no journal is left, after it the journal is kept and replays once the path resolves inside again; a refused mount throws the code; a refused restore or index write is not performed and is reported on `HarvestReport.warnings` (or `incident.detail`) with the code, a hard-link refusal names the file. WHO IS HURT: a deployment whose store holds a directory link under its root, or a hard-linked memory file — the act that used to follow it is refused until it is removed. Known limits KL-1094-a … i."
    },
    {
      "id": "F2",
      "source": "behaviorFlips.summary",
      "text": "NARROWING (#1099, spawned-environment scrub; `SecretEnvFindingKind` +1 member `namespace-rule`, confidence `medium`; +0 exports, +0 wire keys): inside the host shell's `SEMA_` namespace, and only there, a credential word as ANY `_`-separated segment makes the name a credential (`SEMA_NAMESPACE_SECRET_RE`, over the one `CREDENTIAL_WORD` alternation the suffix rule reads, the count-word reading of `TOKENS` included) — so `SEMA_MODEL_KEY_<ENTRY>`, `SEMA_WEBSEARCH_KEY_<PROVIDER>` and `SEMA_REGISTRY_TOKEN_FILE` no longer reach a tool's child shell or a language server, and the one `config.secret_env_scrubbed` notice lists them in `detail.names`. The rule is consulted after the two exact-name lists and the suffix rule, so no existing finding changes kind, and every name outside `SEMA_` keeps its v7.31.0 verdict and kind. WHO IS HURT: a `SEMA_` name with a credential-word segment that a child needed (`SEMA_REGISTRY_TOKEN_FILE`, `SEMA_LLM_KEY_DB`, six `SEMA_CODE_*` knobs a nested `sema` reads); an `inheritEnv` allowlist, `shellEnv` / a per-exec env and a host-supplied LSP `env` still pass a name verbatim."
    },
    {
      "id": "F3",
      "source": "behaviorFlips.summary",
      "text": "NARROWING (#1100, shell read boundary; tightening only — no verdict loosens against 7.31.0; +0 exports, +0 wire keys; supersedes #1004's no-opener ruling for tilde values): three rules on one mechanism, the declared-home deny floor. (1) The opener condition leaves the assignment-value reading: an environment value with a leading tilde is placed against the declared home whether or not a program opens it, so `F=~/<deny row>; cat \"$F\"` meets the deny judge exactly as its `$HOME` and literal spellings already did. (2) A LITERAL tilde (quoted, escaped, after an option's `=`) — a relative `./~…` path to bash, but a program such as npm expands it itself — also carries its declared-home spelling as a deny-only view: refused on a deny row, never a containment question. (3) Each member of a `:` list meets the deny rows (a tilde member placed against the home); the whole value is still no containment candidate. Where no deny row is hit nothing changes. WHO IS HURT: a text whose value or literal-tilde operand names a deny row under the declared home and is never read that way is refused, as its `$HOME` and literal spellings already were (KL-1100a / KL-1100b)."
    }
  ],
  "closedSetMembers": [
    {
      "set": "SecretEnvFindingKind",
      "at": [
        "src/core/secret-env.ts:181"
      ],
      "newSet": false,
      "added": [
        "namespace-rule"
      ],
      "removed": [],
      "breaking": false,
      "criteriaCovered": false
    }
  ],
  "closedSetsUnreadable": [],
  "typeKeys": [],
  "typeKeysUnreadableArms": [],
  "typeKeysFlattenedCount": 216,
  "declarationMoves": [],
  "mintMoves": [],
  "criteriaObserves": [
    {
      "kind": "engineNoticeCode",
      "name": "config.secret_env_scrubbed",
      "assertions": [
        "G2.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "AskRequest.mandated",
      "assertions": [
        "G3.a",
        "G3.b"
      ]
    },
    {
      "kind": "typeMember",
      "name": "EngineNotice.detail",
      "assertions": [
        "G2.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "FileMemoryEngineBackend.applyPatches",
      "assertions": [
        "G1.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "HarvestReport.incident",
      "assertions": [
        "G1.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "HarvestReport.ok",
      "assertions": [
        "G1.b"
      ]
    },
    {
      "kind": "typeMember",
      "name": "HarvestReport.restored",
      "assertions": [
        "G1.a",
        "G1.b"
      ]
    },
    {
      "kind": "typeMember",
      "name": "HarvestReport.warnings",
      "assertions": [
        "G1.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "MemoryEngine.eraseMemoryEntries",
      "assertions": [
        "G1.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "MemoryEngine.materialize",
      "assertions": [
        "G1.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "MemoryErasureAttestation.status",
      "assertions": [
        "G1.b"
      ]
    },
    {
      "kind": "typeMember",
      "name": "PatchReport.applied",
      "assertions": [
        "G1.b"
      ]
    },
    {
      "kind": "typeMember",
      "name": "SecretEnvFinding.confidence",
      "assertions": [
        "G2.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "SecretEnvFinding.key",
      "assertions": [
        "G2.b"
      ]
    },
    {
      "kind": "typeMember",
      "name": "SecretEnvFinding.kind",
      "assertions": [
        "G2.a",
        "G2.b"
      ]
    },
    {
      "kind": "typeMember",
      "name": "ShellReadVerdict.disposition",
      "assertions": [
        "G3.a",
        "G3.b"
      ]
    },
    {
      "kind": "typeMember",
      "name": "ShellReadVerdict.finding",
      "assertions": [
        "G3.a",
        "G3.b"
      ]
    },
    {
      "kind": "typeMember",
      "name": "ShellReadVerdict.readRootCandidate",
      "assertions": [
        "G3.b"
      ]
    }
  ],
  "consumersHint": {
    "cli": [],
    "server": [],
    "sdk": [],
    "note": "scanned cli@4e593722 server@c60beebe sdk@a9440f2 — a name listed is MENTIONED there today, which is evidence of consumption, not proof of it. Ledger files (**/docs/core-consumption/**, **/docs/wire-consumption/**, **/*LEDGER*.json) are not evidence of use — server: 1 name(s) matched ONLY in bookkeeping files and were set aside (SecretEnvFindingKind)"
  },
  "breakingCount": 0,
  "breakingItems": [],
  "toResolvedAt": "c7c5484e2ab1b4abcba1297de62424e71306e66c"
}
