Skip to content

fix(sdk): bound stale broker lock cleanup, add startup failure diagnostics (#3963) - #4015

Open
Yeachan-Heo wants to merge 224 commits into
devfrom
issue-3963-sdk-broker
Open

fix(sdk): bound stale broker lock cleanup, add startup failure diagnostics (#3963)#4015
Yeachan-Heo wants to merge 224 commits into
devfrom
issue-3963-sdk-broker

Conversation

@Yeachan-Heo

Copy link
Copy Markdown
Owner

Closes #3963

Problem

Three unbounded/undebuggable states in the SDK broker lifecycle (evidence from #3963):

  1. Stale lock tombstones never reclaimed#reclaimStaleLock renamed dead-owner lock dirs to .broker.lock.stale-<sha256> but nothing ever removed them; 54 accumulated on the reporter's machine, plus legacy broker-restart-backup-*/broker-stale-backup-* dirs.
  2. Dead-owner lock made detached broker startup exit cleanly with no usable diagnostics — the detached broker-internal is spawned with stdio: "ignore", so a code=0 exit before discovery left the caller with only a bare "exited before discovery" message.
  3. lifecycle-ledger.jsonl.corrupt grows unbounded#quarantine appended every malformed row with no cap/rotation; the reporter observed 126 MB.

Fix (broker lifecycle lane only; no #3955-#3958 SDK protocol/API overlap)

  • Lock artifact reaper (broker.ts): on broker startup as the lock owner, reap .broker.lock.stale-* tombstones and legacy broker-*-backup-* dirs with bounded retention — entries older than a 24h grace age are removed, and only the newest 10 are retained. The live broker.lock/owner.json is never matched (fail-closed); reaper failures never block startup.
  • Discovery failure diagnostics (internal.ts, ensure.ts, commands/sdk.ts): the broker-internal process writes a durable bounded sdk/broker.startup-failure.json marker before any clean exit without owned discovery; ensureBroker clears stale markers at spawn, captures a bounded 4 KB stderr tail, and raises a typed BrokerStartupError (code: "broker_startup_failed") carrying { exitCode, signal, reason, stderrExcerpt } — surfacing the marker reason instead of a bare code=0.
  • Corrupt ledger bounds (lifecycle-ledger.ts): #quarantine rotates lifecycle-ledger.jsonl.corrupt when it exceeds an 8 MB cap, retaining only the newest 2 rotated snapshots, so total retained corrupt bytes stay bounded.

Orphaned session-host lifecycle analysis

watchSessionHostBrokerLiveness (10-minute absence grace, 15s poll) plus the host self-disposal path are present and covered by sdk-broker-self-reap-process.test.ts / sdk-broker-lifecycle-e2e.test.ts. No concrete gap was proven in the current code path, so this lane ships the analysis as a finding (no host-code change): the 40 observed orphans predate the 0.12.6 self-disposal fix or were captured inside the 10-minute grace window; the broker-side fixes above (typed diagnostics + stale-lock reaping) address the surrounding wedge.

Tests / verification

  • New: sdk-broker-lock-reaper.test.ts (age/count bounds, legacy backup coverage, live-lock immunity, startup reaping) and sdk-broker-lifecycle-cleanup.test.ts (marker write/read/clear, reason bound, malformed marker, typed error shape, corrupt rotation).
  • Focused suites green: sdk-broker-lock-reaper, sdk-broker-lifecycle-cleanup, sdk-broker-restart, sdk-broker, sdk-broker-transport, sdk-broker-lifecycle-e2e, fixture-broker-cleanup, sdk-broker-source-isolation (163 pass, 0 fail).
  • bun --cwd=packages/coding-agent run check:types clean. (Root check:ts has one pre-existing lint error in smithery-env-trust.test.ts present at origin/dev — untouched by this PR.)
  • Not-tested: the reporter's pkill -f "sdk broker-internal" crash-loop reproduction (external process-kill scenario).

Yeachan-Heo added 30 commits August 7, 2026 23:13
Long-running sessions retained complete transcript graphs in memory even after compaction. Add disposable disk sidecars, bounded lazy reads, typed context overflow handling, rollout settings, and focused parity and large-session coverage while keeping transcript v5 authoritative.

Lore-id: 4ea1286f

Constraint: transcript v5 remains authoritative and sidecars stay disposable

Constraint: default rollout mode remains shadow

Confidence: medium

Scope-risk: wide

Reversibility: flag-off

Tested: coding-agent package check and 92 focused session tests

Not-tested: full crash-injection matrix and streaming 1 GiB fork transform
File and in-memory session deletion now have focused regression coverage proving disposable spill index, tail, and commit siblings are removed with their transcript.

Lore-id: 1c93bc21

Confidence: high

Scope-risk: narrow

Reversibility: easy

Tested: coding-agent package check and 24 focused integration/storage tests
Whole-session rewrites, model staging, entry updates, and explicit or nested startup now rehydrate the authoritative graph before mutation, rebuild disposable sidecars after publication, and run the bounded context preflight before returning an opened manager.

Lore-id: 56c10a8d

Constraint: never mutate an incomplete retired graph

Confidence: medium

Scope-risk: wide

Reversibility: flag-off

Tested: coding-agent package check and 21 focused session tests

Not-tested: managed crash-recovery matrix
Run one hundred file and memory session create-delete cycles and assert no derived sidecar or temporary overlay artifacts remain.

Lore-id: 9c7f124e

Confidence: high

Scope-risk: narrow

Reversibility: easy

Tested: 20 session memory storage contract tests
Subscribe each AgentSession to sessionMemory.mode changes so enabled, shadow, and off transitions reach the active SessionManager instead of being frozen at construction. Dispose the listener on normal and forced teardown.

Lore-id: a74e9b31

Confidence: high

Scope-risk: narrow

Reversibility: easy

Tested: coding-agent package check and 99 focused session tests
Prepared transition and async load frames retained full parsed transcript arrays after cold retirement. Drop adopted transition references, clear source snapshots promptly, replace oversized eager maps, reset materialization caches, and remove runtime dictionary/path-set duplication. Strict capture now uses descriptor-bound range passes and staged fork publication without a second whole-file string, preserves final non-newline records, and bounds checkpoint materialization.

Lore-id: d8183f42

Constraint: transcript v5 remains authoritative

Constraint: staged fork publication must remain no-replace

Confidence: medium

Scope-risk: wide

Reversibility: flag-off

Tested: coding-agent package check, 100 focused tests, and opt-in 1M-record retirement soak

Not-tested: transcript-linear fork prepass removal and managed five-class crash recovery
Only retire against a compaction reachable from the active leaf, preventing abandoned branches from authorizing cold history removal. Treat disposable sidecar build failures as an eager fallback and clean partial derived files without aborting authoritative transcript opens.

Lore-id: e4db18c7

Confidence: high

Scope-risk: narrow

Reversibility: easy

Tested: 13 session-memory integration tests and coding-agent typecheck
Reserve accountant and hot-suffix capacity before mutating or persisting an append. When the fixed budget cannot admit the entry, rehydrate the authoritative transcript and continue eagerly instead of silently exceeding the memory guarantee. Bind retirement to the active-branch compaction used by the sidecar build.

Lore-id: 64bd44a9

Constraint: memory budgets remain fixed implementation constants

Confidence: high

Scope-risk: medium

Reversibility: easy

Tested: 14 session-memory integration tests and coding-agent typecheck
Sidecar indexing now serializes candidate bytes without externalizing blobs or mutating persistence state. Patch-bearing transcripts fail closed to eager mode because applied patch records shift authoritative raw offsets until the bounded raw-line indexer is available.

Lore-id: 8b21c9e6

Confidence: high

Scope-risk: narrow

Reversibility: easy

Tested: 15 session-memory integration tests and coding-agent typecheck
Full cold hydration now invalidates materialized snapshots and persistence tokens. Branch mutations discard their sidecar proof, accounting, and derived files so a later enabled transition must rebuild against the new active branch rather than reusing stale compaction authority.

Lore-id: b57fc91d

Confidence: high

Scope-risk: medium

Reversibility: easy

Tested: 16 session-memory integration tests and coding-agent typecheck
Resident-store commits now reapply enabled retirement after rebuilding sidecars, while off mode refuses to recreate derived files. Session file switches therefore preserve the active rollout policy rather than silently reverting to shadow-like behavior.

Lore-id: c73d211b

Confidence: high

Scope-risk: medium

Reversibility: easy

Tested: 17 session-memory integration tests and coding-agent typecheck
Expose retained-authority descriptor capture for managed descendants and use it for commit publication and reopen classification. Linux retained roots no longer copy the complete transcript merely to obtain dev, inode, size, and time identity; authority-absent platforms retain their existing verified fallback.

Lore-id: 19a8cf63

Confidence: high

Scope-risk: narrow

Reversibility: easy

Tested: managed descriptor test, 17 session-memory integration tests, and coding-agent typecheck
Parse the checked marker bytes, validate descriptor/base/tail/terminal proof, and derive the five reopen classes from observed managed transcript identity instead of hard-coded booleans. Record both pre-rebuild recovery evidence and the post-publication current marker state.

Lore-id: f26c084e

Confidence: medium

Scope-risk: wide

Reversibility: easy

Tested: 62 marker, sidecar, integration, and managed append tests plus coding-agent typecheck

Not-tested: crash-window append-tail recovery
Reserve tail metadata before transcript publication, append exact persisted line evidence to the cold index and rolling tail, advance the in-memory terminal proof, then replace the managed commit marker. Sidecar failures hydrate and demote to eager state instead of invalidating the authoritative append.

Lore-id: 2d640f3c

Constraint: transcript append remains authoritative

Confidence: medium

Scope-risk: wide

Reversibility: flag-off

Tested: 107 focused tests, 1M-record soak, and coding-agent package check

Not-tested: process-crash injection between transcript, tail, and marker fsync boundaries
Exercise transcript-ahead recovery from a simulated crash after the authoritative append and verify corrupt terminal marker proof is classified, rebuilt, and republished as exact.

Lore-id: c4b0216a

Confidence: high

Scope-risk: narrow

Reversibility: easy

Tested: transcript-ahead and corrupt-marker managed reopen scenarios plus coding-agent typecheck
Current v5 transcripts without patch records now parse directly into the fork entry graph instead of retaining a second raw-record array, and fresh-header replacement avoids another history array plus spread copy. Patch-bearing transcripts retain the compatibility reducer path.

Lore-id: 5d912e7f

Constraint: preserve transcript-v5 patch compatibility

Confidence: medium

Scope-risk: medium

Reversibility: easy

Tested: 7 descriptor-bound capture/fork integration tests and coding-agent typecheck

Not-tested: four known managed path-security assertions in session-manager-resume-readonly remain environment-failing
Keep later compactions on the authenticated cold tail instead of hydrating and rebuilding the retired prefix, mutate the bounded tail record array rather than copying it on every append, and rebuild resident text storage from only the hot suffix after retirement. Add an opt-in 120k-record process RSS gate that measures three post-compaction growth cycles.

Lore-id: 79c5f83a

Constraint: steady accounted session state must remain at or below 64 MiB

Confidence: medium

Scope-risk: wide

Reversibility: flag-off

Tested: opt-in 120k RSS plateau gate, 61 integration/revision/sidecar tests, and coding-agent typecheck

Not-tested: first-open eager RSS peak remains outside the plateau assertion
Provider context construction now stops walking parent links once it reaches the active compaction's first kept entry. This preserves summary plus kept history semantics without lazily hydrating provider-invisible cold ancestors.

Lore-id: e418ad90

Confidence: high

Scope-risk: medium

Reversibility: easy

Tested: bounded provider-context cold-read regression and coding-agent typecheck
Explicit and custom-storage opens now derive session identity and cwd from descriptor-validated bounded range inspection before initialization. This removes the first full parsed entry graph that was immediately discarded before the authoritative load, reducing the 120k fixture's observed eager RSS delta from about 448 MiB to 317 MiB.

Lore-id: a703d5bf

Confidence: high

Scope-risk: medium

Reversibility: easy

Tested: 54 session memory/storage/context tests, coding-agent typecheck, and 120k RSS worker
Explicit and memory-backed transcripts now publish, replace, and classify the same descriptor-bound commit marker as managed sessions. This gives enabled explicit resumes authenticated base/tail evidence instead of leaving their disposable sidecars permanently uncommitted.

Lore-id: b12c7e44

Confidence: high

Scope-risk: medium

Reversibility: easy

Tested: 40 session memory/storage tests and coding-agent typecheck
Pass the configured session-memory mode into explicit, managed, and continue startup paths before transcript hydration. Enabled resumes can now select bounded sidecar reopen behavior at the load seam instead of learning the setting only after an eager manager has already materialized history.

Lore-id: f51e72ac

Confidence: high

Scope-risk: medium

Reversibility: easy

Tested: coding-agent typecheck and 29 settings/session-memory tests
Persist bounded reducer, label, usage, leaf, and retirement metadata in descriptor-bound commit markers. Enabled explicit resumes validate the base digest, marker descriptor, tail chain, terminal proof, and every hot record digest, then load only the header plus authenticated tail without a full transcript read. Ordinary explicit appends checkpoint markers at compaction boundaries while remaining safely classifiable as transcript-ahead between checkpoints.

Lore-id: 64e19f2c

Constraint: transcript v5 remains authoritative and sidecars remain disposable

Constraint: lazy reopen RSS delta must not exceed 64 MiB on the 120k fixture

Confidence: medium

Scope-risk: wide

Reversibility: flag-off

Tested: 110 focused tests, two opt-in 120k RSS gates, and coding-agent package check

Not-tested: managed lazy reopen still uses the eager compatibility path
Revalidate transcript identity after every base/tail/header read, require marker leaf and retirement boundaries to match the authenticated tail, validate bounded labels, reducer state, and usage totals, and continue the marker generation fence after adoption. Corrupt metadata now fails closed to the eager authoritative transcript path.

Lore-id: d84fa0e2

Confidence: high

Scope-risk: medium

Reversibility: easy

Tested: 110 focused tests, two 120k RSS gates, and coding-agent typecheck
Expose whether bounded reopen was attempted, whether authenticated adoption succeeded, and whether startup fell back to the eager authoritative path. This makes enabled rollout failures observable without adding user-tunable thresholds or weakening fail-closed behavior.

Lore-id: 86d91f0c

Confidence: high

Scope-risk: narrow

Reversibility: easy

Tested: lazy success/corrupt fallback integration and coding-agent typecheck
Record the authenticated explicit-path resume behavior, measured 120k RSS gates, and the retained-authority limitation that keeps managed resumes on the eager compatibility path.

Lore-id: a6f703d9

Confidence: high

Scope-risk: narrow

Reversibility: easy

Tested: changelog wording against current verified behavior
Remove the redundant structured clone from legacy direct fork loading and apply the configured session-memory mode before fork construction. Enabled forks now rebuild and retire compacted history immediately after durable publication instead of retaining the complete source graph until AgentSession applies settings later.

Lore-id: 7bfcde91

Confidence: high

Scope-risk: medium

Reversibility: easy

Tested: 42 direct-fork/resident tests, enabled fork retirement integration, and coding-agent typecheck
Allow descriptor-bound captured forks to inherit an enabled memory mode and rebuild disposable sidecars only after the staged destination is durably published. The returned manager then releases compacted source history instead of retaining the complete compatibility parse graph.

Lore-id: 71da3f86

Confidence: high

Scope-risk: medium

Reversibility: easy

Tested: captured memory-storage fork retirement integration and coding-agent typecheck

Not-tested: captured fork parsing peak remains transcript-linear
Exercise descriptor-mismatched transcript-ahead and terminal-proof-mismatched tail-ahead states through enabled startup. Both cases must reject lazy adoption, parse the authoritative transcript eagerly, and preserve the durable successor content.

Lore-id: 43ac3d11

Confidence: high

Scope-risk: narrow

Reversibility: easy

Tested: transcript-ahead/tail-ahead integration
Revalidate captured fork sources after destination publication, classify destination replacement and quarantine collisions at the strict fork boundary, and recheck picker consent immediately after prepared-receipt publication. A source swap after the publication callback now rolls back the prepared receipt instead of leaving migration authority behind. Test fault injection now targets staged-writer and native receipt publication seams used by the current implementation.

Lore-id: c0a58e43

Constraint: foreign destination evidence must never be deleted

Confidence: high

Scope-risk: wide

Reversibility: easy

Tested: 39 pass, 1 skip resume/read-only authority tests and coding-agent typecheck
Stream eligible transcript-v5 sessions through two bounded range-read passes, authenticate the disposable index in commit metadata, and materialize only the hot suffix. Fixed-size duplicate detection, byte-bounded labels, periodic collection, and process gates keep the measured 120k first-open RSS delta within 64 MiB while preserving eager fallback for branches and unsupported shapes.

Full hydration now becomes an explicit eager transition, patch appends invalidate cold offsets, staged model promotion is fenced by leaf revision, and label, reducer, and usage metadata are ordered before sidecar commit publication.

Lore-id: 8b742e1c

Constraint: transcript v5 remains authoritative and sidecars remain disposable

Constraint: managed retained-authority startup remains eager

Confidence: high

Scope-risk: wide

Reversibility: easy

Tested: 198 pass, 1 skip across nine focused suites; 3 process RSS gates; coding-agent check and typecheck
Yeachan-Heo and others added 28 commits August 10, 2026 21:11
fix(session): retire managed cold history safely
…ns (#4179)

Issue #4178 tracked four defects shipped in the merged quick-lane PR
(#4177, dev 1a28f92). The preserved owner-repair candidate covered
three; this commit completes the set and hardens the regressions:

- cli.ts: load the quick-lane command through a top-level import and
  return the imported class from the registry loader, replacing the
  runtime inline import() (repository top-level-import contract).
  Registration/lazy-load behavior is unchanged: the loader still
  resolves to the same command class on demand.
- classify.ts: recognize long decimal issue references (#123456 and
  up) as issue/PR anchors without reopening hex-color false positives
  — the issue-number pattern now excludes only 3-digit rgb short forms
  and tokens containing hex letters, so pure-decimal references of any
  length stay quick while #rgb/#rrggbb/#rrggbbaa colors stay deep.
- classify.ts: recognize extension-only dotfiles (.env, .env.example)
  as concrete file-path anchors by allowing a leading-dot filename
  segment in the file-path pattern.
- classify.ts: preserve path casing when counting distinct file paths
  for the multi-file breadth exclusion, so src/Foo.ts and src/foo.ts
  are two distinct paths on case-sensitive filesystems and force the
  deep lane (fail-closed; case-insensitive hosts may over-exclude,
  never under-exclude).
- Tests: golden regression block covering all four defects plus an
  explicit color-discrimination guard, a case-sensitivity platform
  contract test, and a lazy-load-through-registry regression for the
  top-level-import change.

Lore-id: 4178
Constraint: do not weaken lazy command registration/loading behavior
Constraint: keep hex-color discrimination fail-closed
Rejected: lowercasing path anchors | collapses case-distinct files
Rejected: keeping inline import() | violates top-level-import contract
Confidence: high
Scope-risk: narrow
Reversibility: trivial
Tested: quick-lane classify+cli (61) + cli-command-registry (10) + cli-command-surface (24) + issue-4178 adversarial probe (17/17) + coding-agent typecheck + biome
Not-tested: full native-dependent suites (CI legs)

Co-authored-by: Yeachan Heo <Yeachan-2@users.noreply.github.com>
The macOS verification gate only understood iTerm2 profiles, so normal Terminal.app sessions were rejected or left without an actionable check. Validate Terminal.app default and startup profiles for Use Option as Meta key, retain the iTerm2 path, and document the required profile setting.

Constraint: Option shortcuts require the terminal to preserve the modifier as Meta/Esc.
Confidence: high
Scope-risk: low
Reversibility: straightforward
Tested: Terminal.app profile verification, failure diagnostic, 102 focused keybinding tests, gjc smoke test
Not-tested: manual hardware keypress without synthesized CGEvent
…eue eviction (#4176) (#4180)

The virtual-integration job pinned a single global, non-branch-scoped
concurrency group (dev-ci-virtual-integration, cancel-in-progress: false).
GitHub keeps at most one running plus one pending entry per group, so with N
open PRs each newly queued run displaces the previously pending one, which
surfaces as a `cancelled` required check with zero executed steps and zero real
test failures. That silently gates the whole merge queue under ordinary
parallel traffic and makes genuine cancellations unreadable.

Scope the job-level group per merge candidate (per PR/head ref) so an unrelated
PR can never evict this run while it waits behind a single global slot, while a
fresh push to the same head still cancels its stale run via cancel-in-progress
(same-ref stale-run cancellation and bounded per-ref load are preserved).
Manual dispatch validation keeps its own non-cancelling lane so re-validations
cannot race a different base/head merge candidate.

Update the pinned topology guard test to assert the per-candidate group key and
add an eviction-semantics regression test that evaluates the live workflow
expression: unrelated PRs map to distinct groups, same-ref re-push keeps the
same group with cancel-in-progress true, and dispatch stays non-cancelling.

Lore-id: 4176
Confidence: high
Scope-risk: low
Reversibility: reversible
Tested: bun test scripts/dev-ci-guard-topology.test.ts; ci-virtual-integration,
  ci-risk-canary-manifest, check-workflow-permissions, check-workflow-yaml;
  tsc -p tsconfig.tools.json
Not-tested: live GitHub Actions run (requires a merge to dev that is out of scope)

Co-authored-by: Yeachan Heo <Yeachan-2@users.noreply.github.com>
…k check:ts (#4173)

* fix(sdk): keep broker teardown identity provable after a workspace is deleted

The identity fence that gates every session.close signal read only the
spawn-time marker inside the session's own workspace state root. Deleting that
workspace while its host kept running destroyed the sole evidence the broker
would accept, so the host survived as an orphan still serving the source it
started with and `restart:sdk-broker --close-session-hosts` named the same
close_refused sessions on every run -- precisely the stale-code failure the flag
exists to prevent. Hosts now publish their own OS start incarnation into the
broker-owned session index, which outlives any workspace, and reconciliation
carries it so the fence is not silently disarmed for lifecycle sessions.

Lore-id: 7c41ab9e
Constraint: a pid is reusable -- never signal one whose current OS incarnation no longer matches what was published
Constraint: a marker naming a different process is contradiction, not absence, and must still refuse
Rejected: mirror the spawn marker into the agent dir | introduces durable per-session files with their own leak and cleanup lifecycle
Rejected: signal any pid whose argv looks like a session host | argv is not identity and cannot fence pid reuse
Confidence: high
Scope-risk: moderate
Reversibility: safe
Directive: any event that republishes a session record must preserve every identity fact its host published
Tested: close of a live host whose workspace state root was deleted, refusal on a recycled incarnation, broker-side retirement of a host that died before unregistering, real restart:sdk-broker --close-session-hosts end to end
Not-tested: windows session hosts

* fix(ci): drop the dead pre-rename prefix that broke check:ts

The session-sdk-notifications risk class matched on
`packages/coding-agent/src/notifications/`, a directory that no longer exists
since the bus moved under `src/sdk/`. The rename scanner forbids that literal
anywhere in tracked files, so `check:ts` failed at its final gate -- after
paying the full multi-minute run and without ever reaching the workspace
typechecks the `&&` chain guards. The sibling `src/sdk/` prefix already covers
the bus, so removing the dead entry restores the gate without narrowing which
changes select the canaries.

Lore-id: 3fd8c206
Confidence: high
Scope-risk: narrow
Reversibility: safe
Tested: verify-gjc-sdk-rename passes, canary manifest and virtual integration suites unchanged

* perf(ci): run manifest receipts through an opt-in bounded pool

Manifest receipts ran strictly one at a time, so the SDK adapter parity gate
spent 735s serializing 588 `bun test` process startups and alone accounted for
three quarters of `check:ts`. Receipts are independent by construction -- each
is its own process -- so they now run through a bounded worker pool that
preserves the evidence exactly: one process, one argv, one receipt line, output
replayed in manifest order, and a failure that stops further claims and exits
non-zero. Concurrency is never assumed: it stays sequential unless a caller
opts a specific manifest in, because the telegram baseline receipts each spawn
a detached broker and load Node-API addons and fail when they race.

Lore-id: b95e07d4
Constraint: the gate must stay fail-closed -- in-flight receipts drain, unclaimed receipts are never reported as evidence
Constraint: only manifests measured safe may opt in; sequential remains the default
Rejected: collapse each file's rows into one process | per-row process isolation is the evidence this manifest exists to produce
Rejected: default every manifest to parallel | telegram baseline reproducibly fails at concurrency 8 and passes at 1
Confidence: high
Scope-risk: narrow
Reversibility: safe
Tested: parity manifest 735s to 335s with 588 clean receipts at concurrency 8 and 16, telegram baseline unchanged at 99s, check:sdk-closure 927s failing to 584s passing, check:ts 990s failing to 626s green
Not-tested: concurrency above 16 (saturated at 8 on this host)
Page exceptions and console errors were invisible to browser callers when an interaction otherwise succeeded. Capture only bounded metadata and drain it into the next successful act/run response without retaining messages, values, arguments, or stacks.

Constraint: keep at most 20 entries per tab and mask URL queries

Rejected: domain sandbox and irreversible-action gating | raw Puppeteer run makes those separate policy designs

Confidence: high

Scope-risk: narrow

Reversibility: easy

Tested: 24 browser tests; coding-agent typecheck; live Chromium exception and console probe
Maintainer review 4896349024 on PR #4175 reproduced raw pathname leakage into
persisted tool results, page-controlled error class names, and an unbounded
serialized diagnostics block. Close all three and make the feature opt-in:

- Emit http(s) URLs origin-only so path-embedded tokens (signed URLs, invite/
  reset links, per-tenant ids) can never reach persisted tool results; non-URL
  values are hashed irreversibly instead of echoed, and about: paths are
  restricted to safe fixed names.
- Map exception class names through a fixed allowlist of built-in error
  classes; page-controlled names like CustomerAlice123 are omitted, never
  echoed.
- Bound serialization: url field capped at 256 chars with a visible ellipsis
  marker and the whole block capped at 4 KiB, shedding oldest entries first
  and marking runtimeDiagnosticsTruncated: true (never silent).
- Make capture opt-in via open(..., { diagnostics: true }) so the extra CDP
  session and per-console-event traffic are opted into; docs, prompt, and
  changelog updated.
- Add a WorkerCore-level emission test proving diagnostics reach the result
  (ordering, drain-on-success, retention after failure, byte bound, opt-in
  off) via a loadPuppeteerInWorkerForTest seam, plus extended unit tests.

Constraint: origin-only URLs or irreversible bounded paths only
Constraint: fixed allowlist for error class names
Constraint: explicit truncation/drop metadata and total byte cap
Rejected: query-masked pathname retention | path segments can carry secrets
Rejected: identifier-regex class validation | pages control those names
Tested: 16 focused diagnostics tests; 42 supervisor/action browser tests;
coding-agent check:types; Biome
Not-tested: live Chromium probe in this environment

Lore-id: 4175-repair
Confidence: high
Scope-risk: narrow
Reversibility: easy
Supersedes: 412bd44 review posture
Resolve the current dev conflicts while retaining the Terminal.app Option+Q verifier and the upstream queued-continuation behavior.

Confidence: high

Scope-risk: medium

Tested: conflict-free tree and focused queue/keybinding suite
Terminal.app sends Option+Arrow as an ESC-wrapped CSI sequence when Option is configured as Meta. The input buffer previously split the prefix, so queued-message selection could not see alt+up/down. Preserve the wrapped sequence and decode it in the native matcher so selector restore, delete, and reorder flows receive the intended shortcut.

Constraint: preserve existing CSI modifier keybindings

Confidence: high

Scope-risk: medium

Reversibility: easy

Tested: cargo fmt, pi-natives key tests, focused TUI and coding-agent tests, package checks, CLI smoke test
…4168)

A code-less lifecycle startup failure was rewritten into "No ready SDK
endpoint remains available.", which named a resource that was never
exhausted. The live ledger records phase "startup", reason "pending",
message "SDK startup did not complete before readiness cutoff." and
endpointGeneration null -- startup never reached generation recording,
so no endpoint or broker registration existed to run out of.

Operators chasing this were sent looking for a leak. The actual limit
is the semantic-readiness budget: 10s default minus two 1s
cleanup/termination windows leaves the child about 8s to become ready.

Lore-id: 4146a3c1
Constraint: keep the generic text for a genuinely absent failure -- only a present message replaces it
Rejected: raise the readiness budget | hides whatever is slow instead of naming it
Rejected: keep the generic message and log the real one | operators read the error, not the ledger
Confidence: high
Scope-risk: narrow
Reversibility: easy
Tested: restoring the generic message turns 62 pass / 0 fail into 60 pass / 2 fail
Not-tested: which startup stage consumes the 8s -- that needs per-stage timing in the failure artifact
The #4006 schema change already lets provider/model IDs containing colon
tags (e.g. ollama-cloud/deepseek-v4-flash:0731) parse and resolve whole,
with only a recognized effort token consumed from the final colon. No
tracked test covered the numeric-tag form through the real surfaces, so
issue #4187's exact scenario (plus its :xhigh variant) could regress
silently.

Add regression coverage across schema validation, resolver resolution,
profile activation, and custom preset creation, proving a colon-bearing
mapping no longer blocks loading or saving other presets. Invalid
ambiguous forms remain rejected by the existing red-team cases.

Lore-id: 4187
Confidence: high
Scope-risk: low
Reversibility: revert-commit
Tested: model-profiles-schema, model-resolver, model-profile-activation, custom-model-preset-creation, model-profiles-redteam, model-profiles-catalog (220 pass)
Not-tested: macOS/Bun 1.3.14 runtime path from the report (covered by the same source under Linux)
Terminal.app exposes one Use Option as Meta key profile setting for both the left and right Option keys. Make that contract explicit in the parser, verifier, capture helper, documentation, and regression coverage so either key follows the same Meta/Esc path for queued-message controls.

Constraint: preserve the existing terminal-independent alt key IDs

Confidence: high

Scope-risk: low

Reversibility: easy

Tested: Rust keys, focused TUI/coding-agent tests, package checks, shell/Python syntax
…ons (#4188)

PR #4151 added a managed sidecar cache that allocates its own `i-` instance
directory under the shared resident-cache root, and a blanket rejection of
nested relative reads whenever retained root authority is unavailable. Both
landed on dev red: `moveTo` tripped the "exactly one instance dir" assumption
in seven tests, and `fork()` artifact copying hit
`managed_nested_path_unsupported` on every non-Linux platform.

Nested no-authority reads now verify each intermediate directory component as a
real same-device directory instead of being refused, which is strictly stronger
than the pre-#4151 lexical containment check. Tests that need the resident text
store address it through the manager rather than counting directories under the
root.

Lore-id: 7c4e19a2
Constraint: sidecar cache and resident text store share one cache root -- tests
  must not infer either from instance-dir counts
Rejected: give sidecar caches a separate root | GC sweep and managed authority
  both bind to the single resident-cache root
Rejected: drop the nested-read guard entirely | O_NOFOLLOW only covers the leaf
Confidence: high
Scope-risk: narrow
Reversibility: easy
Tested: session-resident-{lifecycle,cache,transition-seam,cache-root-derivation}, ultragoal-redteam-resident-cache, session-manager, task-managed-descendants
Not-tested: Linux retained-authority nested read path (guard is a no-op there)
Retained append identity and absence codes can be emitted after bytes are written, so treating them as pre-commit allowed unsafe causal rollback assumptions. Keep only size preflight failures direct and lock both committed terminal paths with physical append-count evidence.

Lore-id: 8c2f1d4a
Constraint: pre-commit injected header patch failures must preserve their causal error and source authority
Constraint: post-write identity_mismatch and not_found outcomes must remain committed-outcome-uncertain and must not be retried
Rejected: update the expected error string | would erase the pre-commit versus post-commit contract
Confidence: high
Scope-risk: narrow
Reversibility: clean
Tested: focused cwd patch suite 10 repetitions; title-source suite; move-to and session-storage suites; full session-manager suite; coding-agent package check
…or-contract

fix(session): preserve managed append uncertainty
…selector

test(models): cover numeric colon-tagged model selectors
fix(coding-agent): harden queued prompts and macOS Option+Q
…stics

feat(browser): surface bounded runtime diagnostics
…-replacement

fix(secrets): keyed replace-mode replacement derivation (#4166)
…3987)

Rebased onto current dev (d1d4807) for exact-head CI; conflicts in
model-registry.ts/test and both CHANGELOGs resolved to keep the six-file
change set with zero stray drift. Original commit authored by Yeachan-Heo.

Co-authored-by: Yeachan-Heo <yeachan-heo@gajae.dev>
…er and model id (P2)

Resolves the same-ID cross-provider collision from the 2026-08-09 signed
owner verdict: a user override for openai-codex/gpt-5.6-sol no longer
exempts an unrelated Codex-transport extension model with the same id
(mutation tooth: extension/gpt-5.6-sol at 1_000_000 plus an override-map
entry for gpt-5.6-sol now caps to 372_000). Exemptions are keyed by
codexContextOverrideKey(provider, modelId). Also fixes the latent
.ceiling/.fallback type error in the override diagnostics.

Lore-id: pr4024-p2
Tested: policy-level and registry-level same-ID cross-provider mutation teeth
Confidence: high
Scope-risk: narrow
Reversibility: simple
…erride keys (cohort gen-2)

Generation-1 cohort findings on the PR #4024 repair:
- Cleaner BLOCKING: applyModelOverride silently dropped non-positive/NaN
  contextWindow overrides for non-Codex providers with no diagnostic,
  contradicting the CHANGELOG 'ignored with a warning' contract. The drop
  site now warns for every non-Codex provider (Codex-tier is already
  diagnosed at collect time), and a registry test locks the behavior.
- Architect P3a: mixed-case modelOverrides config keys were exempted from
  the Codex cap without the value being merged (exact-case apply lookup vs
  lowercased exemption key). Override keys are now normalized to lowercase
  at the constructor boundary and all lookup sites match case-insensitively;
  a mixed-case regression test locks value-merge + exemption alignment.
- Architect P3c: policy doc prose named removed ceiling/fallback fields;
  now names enforced / CODEX_GENERIC_CONTEXT_WINDOW.
- Architect P3b (registerProvider OAuth exemption-before-merge) accepted as
  pre-existing risk, not changed in this lane.

Lore-id: pr4024-cohort-gen2
Tested: registry Codex/override subset 8/8 under CI-like env; full registry
suite 264/266 (2 pre-existing env-leak failures, reproduced on pristine dev);
both package typechecks clean; biome clean
Confidence: high
Scope-risk: narrow
Reversibility: simple
Runtime provider registration replaced configured Codex models after the override merge, allowing the cap exemption to preserve a stale registered context window. Apply the configured override before the final cap so the explicit user value remains authoritative.\n\nLore-id: pr4024-register-provider\nConstraint: preserve provider-qualified Codex override exemptions\nConfidence: high\nScope-risk: narrow\nReversibility: simple\nTested: model-registry contextWindow override subset; coding-agent and ai type checks\nNot-tested: full registry suite has two pre-existing credential-state failures
…stics (#3963)

Rebuild of the original PR commit on current dev (d1d4807). The lock
artifact reaper and corrupt-ledger rotation hunks are already merged on
dev via #4035 and are dropped here; what remains is the durable
startup-failure marker and typed BrokerStartupError surface, reconciled
with dev's spawn-log stderr capture (file-based, not a pipe).

Lore-id: 3963
Constraint: broker lifecycle cleanup/recovery only; no #3955-#3958 overlap
Directive: bounded cleanup by age/count; typed bounded diagnostics
Repair pass on top of the #3963 rebuild. Adopts the reviewer's P1
source-contract requirements for the marker code (namespace node:path
import, Bun.file/Bun.write for marker I/O with node:fs/promises reserved
for the directory op), folds the bounded stderr excerpt into the typed
BrokerStartupError message so the pre-existing 'Broker stderr:' contract
in sdk-broker-lock-artifacts.test.ts holds, and pins the marker surface
with a focused suite plus a real spawned-broker integration test proving
the marker reason reaches the caller as a typed error.

Lore-id: 3963
Directive: bounded cleanup by age/count; typed bounded diagnostics
Tested: 12 new lifecycle-cleanup tests, 229 focused broker tests, mutation 60/2 (0 PR-surface survivors)
Confidence: high
Scope-risk: low
Reversibility: low
A failure artifact can be durable before the detached host has exited and removed its endpoint. Retry the exact proof within a bounded grace window so callers retain the typed startup cause without weakening cleanup verification.

Lore-id: 4015-cleanup-proof\nConfidence: high\nScope-risk: narrow\nReversibility: straightforward\nTested: sdk-broker-lifecycle-e2e factory failure and owned startup failure regressions; coding-agent check

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e2f567515f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const timing = lifecycleTiming(broker);
const deadline = timing.now() + STARTUP_CLEANUP_PROOF_GRACE_MS;
for (;;) {
const proof = await exactCleanupProof(broker, root, id, expected, evidence);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Skip cleanup polling when no failure evidence exists

For every normal lifecycle request where evidence is undefined—including successful create, fork, and resume operations—exactCleanupProof can never return a proof, yet this loop still sleeps until the full 500 ms grace expires. This adds a fixed half-second delay while the broker's per-target request chain remains held, and a test timing implementation whose now() does not advance can loop indefinitely; return immediately unless the root, session ID, marker, and qualifying rollback evidence needed for a proof are present.

Useful? React with 👍 / 👎.

Startup marker helpers must not pull the broker implementation into SDK client entrypoints. Keep the process completion helper isolated and move shared diagnostic serialization to its own dependency-safe module.

Lore-id: 4015-sdk-closure\nConfidence: high\nScope-risk: narrow\nReversibility: straightforward\nTested: coding-agent check; sdk-broker-lifecycle-cleanup
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants