{
  "repo": "core",
  "version": "7.30.1",
  "from": "3a100146dad04ac157def06e16d624fc8c1fe6a1",
  "to": "v7.30.1",
  "added": [],
  "removed": [],
  "changed": [],
  "narrowed": [
    {
      "id": "F1",
      "source": "behaviorFlips.summary",
      "text": "NARROWING (#1064 ⑨, shell read boundary): an unquoted extended-glob opener (`?(` `*(` `+(` `@(` `!(`) in an argv word, a redirection target or a here-document delimiter makes the WHOLE text unestablishable — it joins the plan's `unreadable` floor, so both full-shell seats raise one MANDATED card, the read-only face and every headless face refuse, and a deny hit elsewhere in the same text still refuses outright. An `$((…))` arithmetic body keeps its reading only when the whole body is ONE complete parenthesised group, read off the lexer's structure (a quoted parenthesis is text) and followed across the segments the lexer cuts at `&&` `||` `&` `|` and line breaks; a quoted opener and a here-document body are the text they always were."
    },
    {
      "id": "F2",
      "source": "behaviorFlips.summary",
      "text": "NARROWING (#1064 ⑬, shell read boundary; the CHANGELOG reads it as the repair of a 7.30.0 regression against 7.29.0 on the answered seats — 7.29.0 is not measured here): the declared-home spelling of an unplaced home word becomes a deny-only SHADOW candidate (`ReadCandidate.denyOnly`) established exactly as a read is — canonical target, a pattern's matches, a traversal root's tree — and judged for deny rows only; a bare `cd` / `cd --` puts the declared home on the deny floor; a `[k]=` compound element is read as the value bash assigns. The pre-spawn identity re-check compares each candidate's targets with its own re-establishment (keyed by plan position and candidate identity, deny-only reads their own class), and candidate de-duplication keys on `denyOnly`."
    },
    {
      "id": "F3",
      "source": "behaviorFlips.summary",
      "text": "NARROWING (#1072, spawned-environment scrub): `SECRET_ENV_RE` reads `KEYS`, `TOKENS` and `PASSWORDS` as it reads their singulars, so `SERVICE_AUTH_TOKENS`, `API_KEYS`, `DB_PASSWORDS`, `GITHUB_TOKENS` no longer reach a tool's child shell; a `TOKENS` right after a count word (`TOKEN_COUNT_WORDS`: MAX OUTPUT INPUT THINKING NUM TOTAL) is a token count and is kept. The singular `TOKEN` and the exact-name exemptions keep their 7.30.0 reading."
    },
    {
      "id": "F4",
      "source": "behaviorFlips.summary",
      "text": "NARROWING + additive option (#1073, language-server environment): `defaultLspSpawn` passes `scrubSecretEnv(process.env)` to `child_process.spawn` instead of no `env` at all, so a language server no longer inherits credential-shaped host variables; `NodeLspManagerOptions.env?` (new, optional) is the server's environment used VERBATIM by the manager's default spawn. A host-supplied `spawn` still owns its child's environment. The LSP scrub reports nothing (no findings collector)."
    }
  ],
  "closedSetMembers": [],
  "closedSetsUnreadable": [],
  "typeKeys": [
    {
      "type": "NodeLspManagerOptions",
      "at": [
        "src/engine/lsp/node-lsp-manager.ts:76"
      ],
      "added": [
        "env"
      ],
      "addedDetail": [
        {
          "key": "env",
          "optional": true
        }
      ],
      "addedRequired": [],
      "removed": [],
      "breaking": false,
      "criteriaCovered": true
    },
    {
      "type": "ReadCandidate",
      "at": [
        "src/tools/fs/shell-read-plan.ts:104"
      ],
      "added": [
        "denyOnly"
      ],
      "addedDetail": [
        {
          "key": "denyOnly",
          "optional": true
        }
      ],
      "addedRequired": [],
      "removed": [],
      "breaking": false,
      "criteriaCovered": true
    }
  ],
  "typeKeysUnreadableArms": [],
  "typeKeysFlattenedCount": 215,
  "declarationMoves": [],
  "mintMoves": [],
  "criteriaObserves": [
    {
      "kind": "taskEventField",
      "name": "tool_end.gate",
      "assertions": [
        "G1.a",
        "G1.b",
        "G2.a"
      ]
    },
    {
      "kind": "taskEventField",
      "name": "tool_end.isError",
      "assertions": [
        "G1.b",
        "G3.a"
      ]
    },
    {
      "kind": "taskEventField",
      "name": "tool_end.output",
      "assertions": [
        "G1.b",
        "G2.a",
        "G3.a",
        "G4.a",
        "G4.b"
      ]
    },
    {
      "kind": "taskEventField",
      "name": "tool_end.structured",
      "assertions": [
        "G1.b",
        "G2.a",
        "G3.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "AskRequest.mandated",
      "assertions": [
        "G1.a",
        "G2.b",
        "G3.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "NodeLspManagerOptions.env",
      "assertions": [
        "G5.a",
        "G5.b"
      ]
    },
    {
      "kind": "typeMember",
      "name": "ReadCandidate.denyOnly",
      "assertions": [
        "G2.b",
        "G3.b"
      ]
    }
  ],
  "consumersHint": {
    "cli": [
      "ReadCandidate"
    ],
    "server": [
      "ReadCandidate"
    ],
    "sdk": [],
    "note": "scanned cli@4e593722 server@fa3e0b9b sdk@350f64f — 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 (NodeLspManagerOptions)"
  },
  "breakingCount": 0,
  "breakingItems": [],
  "toResolvedAt": "0034a646af1de9c0cdd8ad7bd9b56234ec527c2c"
}
