{
  "repo": "core",
  "version": "7.20.0",
  "from": "8b87d43b2229199567e89f93e12d5e88d847899b",
  "to": "v7.20.0",
  "added": [
    {
      "name": "ClassifierSeat",
      "kind": "type",
      "tier": "internal",
      "at": [
        "src/core/runner/classifier-ladder.ts:90",
        "src/index.ts:1428"
      ],
      "criteriaCovered": true,
      "breaking": false
    },
    {
      "name": "MemoryProjectionError",
      "kind": "class",
      "tier": "advanced",
      "at": [
        "src/core/memory.ts:344",
        "src/index.ts:1905"
      ],
      "criteriaCovered": true,
      "breaking": false
    },
    {
      "name": "WRITE_EXEMPT_DIR_COVERS_ROOT_CODE",
      "kind": "variable",
      "tier": "advanced",
      "at": [
        "src/core/fs-write-gate-policy.ts:130",
        "src/index.ts:925"
      ],
      "criteriaCovered": false,
      "breaking": false
    },
    {
      "name": "WriteProtectionCheck",
      "kind": "type",
      "tier": "advanced",
      "at": [
        "src/core/write-protect.ts:686",
        "src/index.ts:653"
      ],
      "criteriaCovered": false,
      "breaking": false
    },
    {
      "name": "WriteProtectionCompileOptions",
      "kind": "interface",
      "tier": "advanced",
      "at": [
        "src/core/write-protect.ts:508",
        "src/index.ts:652"
      ],
      "criteriaCovered": true,
      "breaking": false
    },
    {
      "name": "WriteProtectionJudgeContext",
      "kind": "interface",
      "tier": "advanced",
      "at": [
        "src/core/write-protect.ts:465",
        "src/index.ts:651"
      ],
      "criteriaCovered": false,
      "breaking": false
    },
    {
      "name": "WriteProtectionVerdict",
      "kind": "type",
      "tier": "advanced",
      "at": [
        "src/core/write-protect.ts:459",
        "src/index.ts:650"
      ],
      "criteriaCovered": true,
      "breaking": false
    },
    {
      "name": "canonicalPathIsAuthoritative",
      "kind": "function",
      "tier": "advanced",
      "at": [
        "src/core/remote-env.ts:624",
        "src/index.ts:527"
      ],
      "criteriaCovered": false,
      "breaking": false
    },
    {
      "name": "classifierSeatModel",
      "kind": "function",
      "tier": "internal",
      "at": [
        "src/core/runner/classifier-ladder.ts:116",
        "src/index.ts:1428"
      ],
      "criteriaCovered": false,
      "breaking": false
    }
  ],
  "removed": [
    {
      "name": "RECOMMENDED_SENSITIVE_ASK_PATTERNS",
      "kind": "variable",
      "tier": "advanced",
      "at": [
        "src/core/sensitive-path-policy.ts:196",
        "src/index.ts:914"
      ],
      "breaking": true
    }
  ],
  "changed": [],
  "narrowed": [
    {
      "id": "F1",
      "source": "behaviorFlips.summary",
      "text": "NARROWING (every deployment, default-on) — the always-on write-protection table judges the file the tool will WRITE, symlinks resolved (#817; car-817): `WriteProtectionMatcher.judge` sees TWO views (the spelling anchored at the tracked cwd, then — only on a spelling miss — the resolved real target, through the same `canonicalizeTarget` every other path guard reads) and asks on either; a target the environment cannot resolve is DENIED (`deniedBy: \"write_protection\"`) rather than vouched; the judgment is bounded by the fold's own deadline (`approvalTimeoutMs`, else 30 s) and an elapsed or aborted one is that same refusal; a spelling is read under each separator family with its own component normalization, so a POSIX file literally named `..\\x` under a guarded directory no longer pops the protected segment away; the approved-edit re-check arm denies an aliased rewrite the way it already denied a spelled one. Absence law: a judge compiled with no `ExecutionEnv` has no target view and answers by the spelling alone. Contract sentence `write_protection.judges_the_real_target`. Who needs it: every deployment — an innocent-looking alias onto `.bashrc` / `.gitconfig` / `.ssh` executed silently and always had. Who is hurt: a workflow that writes through a stable alias onto a table row pays one ask (headless: a refusal), and a covered write costs 1 canonicalization (2 env calls; 4 for a not-yet-existing file, +2 per distinct root spelling when the target hits a row). Compensation: the spelling view runs first (a spelling hit costs 0 env calls), the ask clears exactly as the spelled form's ask always did (approver / `onAsk:\"allow\"` / classifier / matching persisted allow rule), and the spelled form's sentence is byte-identical."
    },
    {
      "id": "F3",
      "source": "behaviorFlips.summary",
      "text": "NARROWING (auto mode) — a CONTRADICTORY classifier reply is decided where it was given (#813; ruling C-R49 (a); car-813): the candidate ladder advances on NO EVIDENCE only — the rung's leg threw, its round-trip cap elapsed, or the reply carried no answer at all (`missing`) — so a reply holding two answers (`<thinking><block>yes</block></thinking>no`) stays the fail-closed `parse_error` deny it already was, at the rung that produced it. Contract sentence `auto_mode.ladder.advances_on_no_evidence`. Who needs it: the classify leg already refused to RE-ASK a contradiction for the same reason; the two seats offering a second attempt at one question disagreed. Who is hurt: a call a later rung would have ALLOWED on a contradictory seat reply is now denied at the seat. Compensation: nothing widens (no deny becomes an allow), the `missing` shape the ladder exists for is untouched, the denial limit counts this deny like any other so a lane with a person still falls back to one, and `AUTO_MODE_FALLBACK_CAUSES` / `AUTO_MODE_PARSE_FAILURES` do not move."
    },
    {
      "id": "F5",
      "source": "behaviorFlips.summary",
      "text": "NARROWING of a marker's reach — a connection that breaks AFTER the provider's terminal frame no longer makes the response \"partial\" (#814; car-813): TORN is ONE predicate — the provider never terminated the stream AND the stream broke. The three adapters report the FACT (`terminalFrameSeen`: openai `finish_reason`, anthropic `message_delta.stop_reason`, responses `response.completed`/`response.incomplete`) off the same state their own finalize reads, and the engine decides what it means. Who needs it: a body that had already delivered its finish frame and then errored (socket reset, idle watchdog) was promoted as torn — `partialFinalized`, the CC \"may be incomplete\" line, and the agent loop's bounded continue nudge: a second PAID turn to finish an answer the model had already finished. Who is hurt: a consumer counting partial turns as a health signal sees the count fall. Compensation: the exit finalises without re-judging (a `length` finish frame keeps its `length`; a declared-incomplete Responses terminal keeps its own sentence), every stream cut BEFORE its terminal frame is unchanged on all three wires, and each adapter's own `tornWithContent` stamp stays independent of the engine's tiering."
    },
    {
      "id": "F6",
      "source": "behaviorFlips.summary",
      "text": "NARROWING (engine-internal children) — every engine-built child spec carries the WHOLE of its parent's tighten-only clamps, through one carrier and behind a gate (#777; car-752): `childClampSeats` owns the projection (`CHILD_CLAMP_SEATS` (`src/core/child-clamp-seats.ts:32`) = `handsReadOnly` · `interactiveTools` · `readFace` · `writeFace` · `readDenyPatterns`; only the restricting value travels, arrays copied at spawn, an unclamped seat adds no key), `ChildClampSource` reds `tsc` at every construction site when a member is added, and `gate:child-clamp-seats` (in the `gate:all` roster) checks the registry `ENGINE_CHILD_SPEC_CONSTRUCTORS` (rows keyed `<file>#<name>`), the not-carried table's reasons, and that nothing under `src/agents` / `src/orchestration` hand-writes a clamp projection. Who needs it: three separate rounds found the same root cause as three points (a workflow child free of its host's clamp, a re-park's persisted seed, an observer sidecar with no containment seat) and a `grep` census cannot find the next one. Who is hurt: two stations tighten — the observer sidecar (a read-only parent's sidecar had WRITE hands, because prepare mounts the built-in hand band on every task regardless of the spec's `tools`) and the verifier leg (`verifierHandsReadOnly: false` can no longer widen past a read-only caller). Compensation: everything else is byte-identical behaviour through a different spelling; nothing on the wire moves and the carrier is not exported."
    },
    {
      "id": "F7",
      "source": "behaviorFlips.summary",
      "text": "NARROWING (security) — a writable DOMAIN that covers the task root is refused, at both places one can be offered (#752; car-752): one predicate (`swallowedFenceRoot`) states \"a domain must not cover a root the fence holds\" — equal or ancestor — and both doors apply it. The ADMISSION seat skips the advertised directory and announces it through its existing outlet (`RunnerDeps.onError`, phase `config`), counting every root the fence holds at that moment (the task root, each declared `additionalDirectories` / `additionalReadDirectories` as the hands phase resolves it, and the directories already admitted), and it resolves the declared roots ITSELF before judging so the memory advertiser can no longer be admitted merely because nobody had named that root yet. The WRITE GATE refuses to be BUILT: `createFsWriteGatePolicy` throws `config.write_exempt_dir_covers_root` (`WRITE_EXEMPT_DIR_COVERS_ROOT_CODE`, `detail: { exemptDir, exemptDirResolved, root }`). Two walk-arounds close in the same window: the gate's `rootPath` is absolutized ONCE against the env's cwd (before, it was resolved against ITSELF), and a configured root the env cannot resolve no longer reads as \"no root to cover\". Contract sentence `fs_write_gate.exempt_dir_never_covers_root`. Who needs it: a scratch-shaped run whose cwd IS the scratchpad silently switched write governance off, with no symlink needed. Who is hurt: such a deployment now fails to start. Compensation: turning write governance off stays available with the knob that MEANS it (`defaultWrite: \"allow\"`, an open write face, a permission rule), the message says so, and `acceptDirs` — a grant a person won for the session — is deliberately untouched."
    },
    {
      "id": "F14",
      "source": "behaviorFlips.summary",
      "text": "BREAKING (wire detail key) + widening — the `<skills>` ceiling is retired and the attachment lane has two delivery classes (#818; ruling C-R46; car-818). `ATTACHMENT_DELIVERY_CLASS` declares each source's class once and `satisfies Record<AttachmentSource, …>` makes it total: CLIPPABLE sources (window-based reminders that re-fire at the next quiet boundary) keep the 8 KiB `ATTACHMENT_BYTE_CAP`, whose semantics narrow to \"the clippable bundle\"; INTACT sources (`tools_delta`, `agent_listing`, `skills_listing`, `mcp_instructions`, `mcp_dropped_tools`, `date_change`, `instructions_change`, `workflow_size_guideline_change`) ride whole and each declares the bound its own producer honours. The three 8 000-byte clamps, `SKILLS_BLOCK_MAX_BYTES`, the starvation latch and the `skills.listing_budget_ceiling` contract are gone. REMOVED: `skills.listing_truncated.detail.requestedBudgetBytes` — it existed only to disclose the gap between the declared budget and the lane's clamp, and there is no clamp, so `budgetBytes` IS the budget in force. The agent-type roster folds through the one listing engine (`foldListingWithin`) and gets the same budget plus its own notice `agents.listing_truncated`; `tools_delta`'s `added` arm collapses by server above 30 names, the form its `removed` arm already had. Contract sentences `skills.listing_budget_enforced_as_declared`, `attachments.delivery_class`. Who needs it: a listing frame's record advances only when the frame arrives INTACT, so a clipped `<skills>` or roster frame could never be committed and re-rendered and re-clipped at every boundary forever — and the roster had no self-bound at all. Who is hurt: a consumer that compared the two numbers to tell \"lane-clamped\" from \"fraction-bound\"; a consumer that pins `ENGINE_NOTICE_CODES` membership or builds an exhaustive table over it; a consumer that pins the roster frame's text for a LARGE roster; and deployment operators, for whom a single boundary steer and the first user message may now exceed 8 KiB by exactly what the deployment declared. Compensation: read `budgetBytes` alone — there is only one number now; the notice's code, audience, firing rule and every other detail key are unchanged; at the default 200 000-token window the listing is byte-identical to 7.19.0; the postToolBatch `additionalContext` room GROWS (it is now `cap − clippable bytes`, where a 7 KiB drift frame used to squeeze it to nothing)."
    },
    {
      "id": "F16",
      "source": "behaviorFlips.summary",
      "text": "NARROWING on the invented half, WIDENING THE GATE on the missed half — an option's VALUE is read from the program's own option grammar (#764; car-764): one declaration, `VERB_OPTION_SLOTS`, says per verb which short letters and long names take a value and WHAT that value is (`path`, `indirect`, `pattern`, `script`, `count`, `delimiter`, `glob`, `header`, `label`); one membership test decides whether a value becomes a path candidate; the ATTACHED and SEPARATED spellings of one option read the same row. Ten rows ship (`head`, `tail`, `cut`, `grep`, `diff`, `cmp`, `sed`, `date`, `tar`, `curl`), each checked against a real binary rather than a manual page. RULES ARE REMOVED, NOT ADDED: the two hard-coded verb branches, the `/^-d./` delimiter regex and three predicates are gone. Fail-safe by construction — the FIRST character a row does not declare hands the token back to the verb-blind scan, so a verb with no row is judged exactly as before. Three arities because `getopt_long` has three, a disputed arity resolves towards the reading that CONSUMES NOTHING (only consuming can swallow a file operand), and past the first operand BOTH readings are taken and neither silences the other. Contract sentence `shell.read_boundary.option_value_slots`. Who needs it: the payload extractor approximated getopt, which INVENTED reads (`sed -e's/a/b/' f` mandated on `/a/b`; `head -n1/x f` on `/x`) and MISSED them (`curl -#K.npmrc` hid the `-K` config file behind curl's `#` progress flag; `curl -d@<file>` resolved as an ordinary relative name). Who is hurt: under the DEFAULT doctrine a named set of spellings that ran with ZERO asks now raise ONE mandated ask each and do not run — every one confirmed by running the program. Compensation: ten spellings that raised a mandated ask on a path the command never opens now run unasked, and the stdin floor deliberately keeps its own option model (reading it off the new table was measured over a 580-row corpus and moved twelve verdicts, eight of them wrongly)."
    },
    {
      "id": "F17",
      "source": "behaviorFlips.summary",
      "text": "NARROWING, opt-in (absent reads as today) — the execute-time read boundary asks what an adapter DECLARES about its own resolver's silence (#787; car-764): `RemoteExecutionEnv.capabilities.canonicalPathAuthoritative?: boolean`, read STRICTLY by the exported `canonicalPathIsAuthoritative`, because a `true` makes a containment leg refuse where it used to pass. Who needs it: resolving a spelling walks it from the deepest answerable ancestor, and when NO ancestor answered — not even `/` — the judge returned with no finding and the lexical verdict stood. Its two siblings on the same factory were already fail-closed and a PARTIAL failure a few lines below is the `unverifiable` refusal; only the whole-chain silence passed. The premise behind it (\"`/` resolves in any normal env\") is a statement about a LOCAL filesystem — on an RPC-backed lane `canonicalPath` is a call into somebody else's namespace, and one dropped call released every candidate of the command. Who is hurt: a declaring adapter whose resolver goes silent refuses the read (with the sentence the partial-failure arm already issues) instead of passing it. Compensation: absent — and every value that is not the boolean `true` — is the reading every adapter has today, so no existing env moves and `NodeExecutionEnv` (not remote, no capability block) is unchanged; a simple unconditional fail-closed was TRIED and REVERTED before this car because an env that answers no path question at all had every in-root read refused (twelve red in the read-boundary suite), and the key is what tells the two silences apart. Orthogonal to `isolation`: a declared isolated sandbox skips the resolving pass entirely."
    }
  ],
  "closedSetMembers": [
    {
      "set": "BASH_CLASSIFY_EXTRA_ALLOW",
      "at": [
        "src/tools/fs/bash-readonly-classifier.ts:89"
      ],
      "newSet": true,
      "added": [
        "cd",
        "find",
        "sed"
      ],
      "removed": [],
      "breaking": false,
      "criteriaCovered": false
    },
    {
      "set": "CHILD_CLAMP_SEATS",
      "at": [
        "src/core/child-clamp-seats.ts:32"
      ],
      "newSet": true,
      "added": [
        "handsReadOnly",
        "interactiveTools",
        "readDenyPatterns",
        "readFace",
        "writeFace"
      ],
      "removed": [],
      "breaking": false,
      "criteriaCovered": false
    },
    {
      "set": "ENGINE_CHILD_SPEC_CONSTRUCTORS",
      "at": [
        "src/core/child-clamp-seats.ts:61"
      ],
      "newSet": true,
      "added": [
        "src/agents/subagent.ts#buildChildSpec",
        "src/agents/subagent.ts#observerSpec",
        "src/agents/subagent.ts#resumeSpec",
        "src/agents/teacher.ts#inheritedDeploymentConfig",
        "src/agents/verify.ts#fixLegSpec",
        "src/agents/verify.ts#verifierSpec",
        "src/orchestration/workflow-primitives.ts#workflowChildClamps"
      ],
      "removed": [],
      "breaking": false,
      "criteriaCovered": false
    },
    {
      "set": "ENGINE_NOTICE_CODES",
      "at": [
        "src/core/governance-codes.ts:309"
      ],
      "newSet": false,
      "added": [
        "agents.listing_truncated",
        "config.execution_env_capability_invalid",
        "config.forward_sink_failed",
        "config.task_root_not_canonical",
        "memory.restore_withheld"
      ],
      "removed": [],
      "breaking": false,
      "criteriaCovered": true
    },
    {
      "set": "ListingKind",
      "at": [
        "src/core/skill-listing-budget.ts:48"
      ],
      "newSet": true,
      "added": [
        "agents",
        "skills"
      ],
      "removed": [],
      "breaking": false,
      "criteriaCovered": false
    }
  ],
  "closedSetsUnreadable": [
    {
      "set": "BASH_CLASSIFY_DEFAULT_ALLOW",
      "from": "unreadable",
      "to": "absent",
      "unreadable": false,
      "at": [
        "src/tools/fs/bash-readonly-classifier.ts:89",
        "src/tools/fs/index.ts:23"
      ],
      "reason": "a spread element (`...x`) at src/tools/fs/bash-readonly-classifier.ts:89"
    }
  ],
  "typeKeys": [
    {
      "type": "HarvestReport",
      "at": [
        "src/core/memory-engine/types.ts:578"
      ],
      "added": [
        "restoreWithheld"
      ],
      "removed": [],
      "breaking": false,
      "criteriaCovered": true
    },
    {
      "type": "RunInternals",
      "at": [
        "src/core/runner/contracts.ts:1295"
      ],
      "added": [
        "forwardScope"
      ],
      "removed": [],
      "breaking": false,
      "criteriaCovered": true
    },
    {
      "type": "WriteProtectionMatcher",
      "at": [
        "src/core/write-protect.ts:479"
      ],
      "added": [
        "judge"
      ],
      "removed": [],
      "breaking": false,
      "criteriaCovered": true
    }
  ],
  "declarationMoves": [
    {
      "name": "ClassifierSeat",
      "kind": "type",
      "onSurface": true,
      "from": [
        "src/core/runner/classifier-ladder.ts"
      ],
      "to": [
        "src/core/runner/classifier-ladder.ts",
        "src/index.ts"
      ]
    },
    {
      "name": "classifierSeatModel",
      "kind": "function",
      "onSurface": true,
      "from": [
        "src/core/runner/classifier-ladder.ts"
      ],
      "to": [
        "src/core/runner/classifier-ladder.ts",
        "src/index.ts"
      ]
    }
  ],
  "mintMoves": [],
  "criteriaObserves": [
    {
      "kind": "engineNoticeCode",
      "name": "agents.listing_truncated",
      "assertions": [
        "G5.b"
      ]
    },
    {
      "kind": "engineNoticeCode",
      "name": "classifier.fallback",
      "assertions": [
        "G2.b"
      ]
    },
    {
      "kind": "engineNoticeCode",
      "name": "config.execution_env_capability_invalid",
      "assertions": [
        "G6.d"
      ]
    },
    {
      "kind": "engineNoticeCode",
      "name": "config.forward_sink_failed",
      "assertions": [
        "G4.e"
      ]
    },
    {
      "kind": "engineNoticeCode",
      "name": "config.task_root_not_canonical",
      "assertions": [
        "G3.c"
      ]
    },
    {
      "kind": "engineNoticeCode",
      "name": "memory.restore_withheld",
      "assertions": [
        "G4.b"
      ]
    },
    {
      "kind": "engineNoticeCode",
      "name": "skills.listing_truncated",
      "assertions": [
        "G5.a"
      ]
    },
    {
      "kind": "taskEventField",
      "name": "task_notification.notification",
      "assertions": [
        "G4.d"
      ]
    },
    {
      "kind": "typeMember",
      "name": "AskRequest.toolName",
      "assertions": [
        "G1.d"
      ]
    },
    {
      "kind": "typeMember",
      "name": "AssistantMessage.partialFinalized",
      "assertions": [
        "G2.d"
      ]
    },
    {
      "kind": "typeMember",
      "name": "AutoModeClassifierRound.fallback",
      "assertions": [
        "G2.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "ClassifierSeat.model",
      "assertions": [
        "G2.e"
      ]
    },
    {
      "kind": "typeMember",
      "name": "CompoundReadonlyVerdict.outOfRootPaths",
      "assertions": [
        "G6.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "CompoundReadonlyVerdict.readDenied",
      "assertions": [
        "G6.c"
      ]
    },
    {
      "kind": "typeMember",
      "name": "FsWriteGatePolicyOptions.exemptDirs",
      "assertions": [
        "G3.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "GateDisposition.cause",
      "assertions": [
        "G2.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "GateDisposition.classifier",
      "assertions": [
        "G2.c"
      ]
    },
    {
      "kind": "typeMember",
      "name": "HarvestReport.restoreWithheld",
      "assertions": [
        "G4.b"
      ]
    },
    {
      "kind": "typeMember",
      "name": "MemoryProjectionError.committed",
      "assertions": [
        "G4.c"
      ]
    },
    {
      "kind": "typeMember",
      "name": "RemoteExecutionEnv.capabilities",
      "assertions": [
        "G6.b"
      ]
    },
    {
      "kind": "typeMember",
      "name": "RunInternals.forwardScope",
      "assertions": [
        "G4.e"
      ]
    },
    {
      "kind": "typeMember",
      "name": "RunnerDeps.onError",
      "assertions": [
        "G3.b"
      ]
    },
    {
      "kind": "typeMember",
      "name": "SkillSpec.baseDir",
      "assertions": [
        "G4.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "TaskResult.stats.mechanisms.attachmentsInjected",
      "assertions": [
        "G5.d"
      ]
    },
    {
      "kind": "typeMember",
      "name": "TaskSpec.handsReadOnly",
      "assertions": [
        "G3.d"
      ]
    },
    {
      "kind": "typeMember",
      "name": "TaskSpec.interactiveTools",
      "assertions": [
        "G3.d"
      ]
    },
    {
      "kind": "typeMember",
      "name": "TaskSpec.skillListingBudgetFraction",
      "assertions": [
        "G5.c",
        "G7.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "TaskSpec.skillListingMaxDescChars",
      "assertions": [
        "G5.c",
        "G7.b"
      ]
    },
    {
      "kind": "typeMember",
      "name": "WriteProtectionCompileOptions.env",
      "assertions": [
        "G1.c"
      ]
    },
    {
      "kind": "typeMember",
      "name": "WriteProtectionMatcher.judge",
      "assertions": [
        "G1.a"
      ]
    },
    {
      "kind": "typeMember",
      "name": "WriteProtectionVerdict.kind",
      "assertions": [
        "G1.b"
      ]
    }
  ],
  "consumersHint": {
    "cli": [
      "ENGINE_NOTICE_CODES",
      "HarvestReport",
      "RECOMMENDED_SENSITIVE_ASK_PATTERNS",
      "RunInternals",
      "WriteProtectionMatcher"
    ],
    "server": [
      "ENGINE_NOTICE_CODES",
      "HarvestReport",
      "RECOMMENDED_SENSITIVE_ASK_PATTERNS",
      "RunInternals",
      "WriteProtectionMatcher"
    ],
    "sdk": [],
    "note": "scanned cli@f37adea3 server@099dd106 sdk@6209e5e — a name listed is MENTIONED there today, which is evidence of consumption, not proof of it"
  },
  "breakingCount": 1,
  "breakingItems": [
    "export removed: RECOMMENDED_SENSITIVE_ASK_PATTERNS (variable)"
  ],
  "toResolvedAt": "976961dde49a08fb8cdab8991ab7151d00b286dc"
}
