Skip to content

Wave 2: the resilience + fleet fixes from dev (the canary's first guarded wave) - #1232

Merged
aarontrowbridge merged 18 commits into
mainfrom
dev
Sep 16, 2026
Merged

aarontrowbridge merged 18 commits into
mainfrom
dev

Conversation

@aarontrowbridge

Copy link
Copy Markdown
Member

The first wave the canary would have blessed: dev carries the fleet-poll hysteresis (#1230, #1202), the session-view SSE-loss resilience (#1231, #1203), the fence-scope pin repair (#1198), the pre-flight default-branch baseline (#1199), ADR-0021 + the canary glossary (#1197), and the --topology projection fix (#1195). CI green at the dev tip (conclusion success — the canary's blessed-SHA gate verified it). Deploying to main so the hub's served app carries the veil — the user is living the symptom this wave cures.

aarontrowbridge and others added 16 commits September 15, 2026 07:26
The fleet projection publish invoked by `amico fleet status --projection`
never passed a topology source to the fleet-authority CLI, so the
publisher rendered the mode from its base default (standalone) and the
validated-but-standalone projection landed in the stable cache on every
run — clobbering enrolled machines' correct projections and flipping
every consumer (installer, guard, extension fleet surfaces) to
standalone.

The publish now carries the machine's fleet.json
(fleetTopologyPath — the live-layout convention, beside the projection
cache) whenever that file exists; absent = unenrolled, and the flag is
omitted so the honest base-default standalone semantics are unchanged.
Hermetic default: the test world's checkFile is () => false so a suite
run never leaks the runner machine's own fleet.json into the seam
assertions.
…er excluded

With dev as the integration branch and main the tested trunk, the
mechanical pre-test of dev happens continuously on real fleet state: the
canary runs on the hub + the mini, downloads CI's dev-head artifacts
(universal VSIX), and never builds or touches the daily driver. Adds the
Canary + Wave glossary terms (Developer tooling).
fix(fleet): pass --topology to the projection publish (#1194)
ADR-0021: the canary — fleet arms, CI artifacts, daily-driver excluded (+ glossary)
…nato re-scope (#1193)

The fleet-gated regression pinned the hardware-loop skill's fence scope to
Strumento, but the live skill's fence resolves to Intonato. Adjudication from
history: the skill moved correctly, the pin was the drifted side.

- The pin (and the 'ten' claim inventory) date to 504277c (#1002), when the
  skill's fence read `using Strumento # reexports Intonato ...`.
- The 2026-09-10 freshness audit (armonissima#88, vault 10a93ce) deliberately
  updated the fence to `using Intonato` — the old `using` was the stale v0.2
  reexport idiom: the seam inverted in Strumento v0.2, so Intonato (the
  chassis) now reexports the substrate (Strumento), not vice versa.
- Inventory fallout: the old `Intonato` claim was minted by the old fence
  comment; the extracted claim set is nine now. Intonato coverage lives in
  the fence-scope pin and the reexport-chain inversion test.

Red→green verified on a fleet machine (live checkouts mounted): the focused
test failed with `expected [ 'Intonato' ] to deeply equal [ 'Strumento' ]`,
then the full skill_drift_lint file passed 68/68. Stays mount-gated (CI-blind
by design).
…efault branch (#1196)

The #992 deploy-race guard hardcoded origin/main as its ahead-of-trunk
baseline, so under the dev workflow (dev = default branch + integration
target) every legitimate ref-build tripped the guard and forced the
AMICODE_DEPLOY_OVERRIDE hotfix hatch.

The baseline is now the repository's DEFAULT BRANCH, resolved
dynamically after the fetch: `git remote show origin`'s HEAD branch
first (authoritative — a stale local refs/remotes/origin/HEAD, the exact
PR #1195 macbook failure, loses), then the symbolic ref, then the
"main" fallback. The refusal + remedy lines name the resolved baseline
verbatim, and the OVERRIDE record does too. Refusal semantics (#992:
AHEAD/BEHIND/DIVERGED refuse, at builds plain, dirty refuses, overrides
must be non-empty and recorded) are unchanged.

evaluatePreflight now takes baselineSha/baselineRef instead of
originMainSha; headRelation's params renamed to match. #992 pre-flight
tests updated to pin the new baseline resolution, plus new #1196 tests:
a tree merged into the default branch builds without override; a tree
ahead of it still refuses with the remedy naming origin/<default>; and
the resolveDefaultBranch preference order (remote-show > symbolic-ref >
fallback) is pinned, including the stale-origin/HEAD case.
fix(skills): the hardware-loop fence-scope pin follows the deliberate Strumento v0.2 reexport inversion (#1193)
fix(build): the #992 pre-flight baseline follows the remote's default branch (#1196)
…un at the integration tip (#421 first-light finding)

The canary (amicissimo#421) requires conclusion==success at the exact dev
SHA before it runs anything. CI ran on PRs and main pushes only, so the
dev tip — a merge commit — never had a run: the gate would report
not-blessed forever. Found by the canary's first real run.
ci: run on dev pushes — the canary's blessed-SHA gate needs CI at the integration tip
* fix(engine): stop using assistant prefill for max-steps prompt (#1179)

* fix(engine): stop using assistant prefill for max-steps prompt

The MAX_STEPS_PROMPT was injected as an assistant-role message at the end
of the messages array when an agent hit its step limit. The Bedrock
Converse API rejects this for models that do not support assistant message
prefill, producing: "This model does not support assistant message prefill.
The conversation must end with a user message."

Changes:
- Send the MAX_STEPS_PROMPT as a user message instead of assistant prefill
  in both V1 (prompt.ts) and V2 (llm.ts) session runners. The instruction
  works identically as a user message — the model still gets told to
  summarize and stop using tools.
- Add toolChoice: "none" on the V1 path's last step (the V2 path
  already had it) as a belt-and-suspenders guard against tool calls.
- Create an overlay for provider/error.ts that treats the string
  "undefined" the same as an empty message, so SDK adapters that coerce
  JS undefined into the string "undefined" no longer produce
  "undefined: <actual error>" in the chat panel.

* fix(app): detect stale rebuild state on mount via timestamp

When a rebuild was interrupted (window reload, extension crash, branch
switch), the "rebuilding" localStorage flag persisted but the build
process was dead. The existing 5-minute live timeout was too slow — it
restarted on every Settings open, so the user saw "Rebuilding..." stuck
indefinitely.

Fix: store a timestamp when the rebuild starts. On mount, if the build
started more than 2 minutes ago and never completed, immediately
transition to "failed" with "Rebuild was interrupted" instead of
restoring the spinner and waiting 5 minutes. The live timeout stays as
a secondary guard for builds that stall during the current mount.

* fix(server): harden adoption gate + fix rebuild status delivery (#1200)

Three independent fixes for the kill-free rebuild path:

1. Adoption retry: when the gate verdict is "stale" but the PID is alive,
   wait 1 s and retry the four live checks. This covers the transient
   unreachable window during a reload (the server flushes dying connections
   from the old extension host).

2. Health-check timeout: bump probeHealth and challengePassword from 2 s to
   5 s — the tight timeout was a plausible cause of transient adoption
   failures after a window reload.

3. Adoption logging: log all four gate inputs and the verdict to the output
   channel so a failed adoption is diagnosable from the log.

4. Rebuild status delivery: await the postMessage Thenable for the "done"
   message before starting the 300 ms reload timer. Without this the reload
   can race the two-hop delivery (extension → shell → iframe) and the
   localStorage flag never lands, leaving the Settings dialog stuck on
   "Rebuilding...".

5. Rebuild phase detail: the building-binary message now says "Overlay
   changed — rebuilding engine binary (server will restart)" so it is clear
   WHY the server restarts (the overlay hash differs from the cached hash,
   meaning the engine source tree changed between builds).

The BridgeIo.postToWebview return type widens from void to void | Thenable
so the single await site works without touching every other call site.

* fix(lineage): repair orphaned entries + propagate diff_version to parents (#1136) (#1174)

* fix(app-bundle): omit-agent promptAsync continues session's current agent (#1206) (#1207)

The deprecated amicode_ask tool ends the assistant turn and the user's
button-click answer arrives via session.promptAsync with no agent field.
createUserMessage fell back to agents.defaultInfo() — the global
default_agent "plan" — silently flipping an active develop/research
session into plan mode mid-campaign.

Server fix: hoist the session fetch in createUserMessage and resolve agent
as input.agent → session's tracked current agent → global default. Fresh
sessions (no tracked agent) still reach defaultInfo() as before.

Client fix: thread the session's current agent into both promptAsync calls
in message-timeline.tsx (onAsk and widget-prompt) that omitted it — belt
and suspenders alongside the server fix.

Regression test: omit-agent prompt on a session whose tracked agent is
"build" produces a user message stamped "build", not "plan".

Closes #1206

* fix(app-bundle): Bedrock cachePoint after reasoning block wedges the session (#1209)

Amazon Bedrock rejects "ValidationException: Cache point cannot be inserted
after reasoning block" when a cachePoint sits immediately after a reasoning
block. Message-level bedrock.cachePoint is emitted after all content parts, so
assistant turns that end in reasoning fail on every retry until the message
ages out of history — surfaced in-app as the cryptic `undefined:` error prefix.

Patch the engine overlay (the source of truth) to anchor the cache breakpoint
on the last non-reasoning part, or skip caching for reasoning-only messages:
  - packages/llm/src/cache-policy.ts            (markMessageAt)
  - packages/opencode/src/provider/transform.ts (applyBedrockCacheOptions)
  - packages/llm/src/protocols/bedrock-converse.ts (Converse lowering note)
plus 6 regression tests. manifest.json updated surgically (+6 entries).

Cherry-pick of anomalyco/opencode#36532 (auto-closed unmerged upstream; never
present in our pinned base 7fe99387).

Verification: drift-gate in sync; materialize verifies the manifest; llm 43/43
and opencode transform 402/402 (incl. 6 new) green in the materialized tree;
build:binary compiles and the smoke test passes.

Co-authored-by: Jackson Turner <jacktrnr@jacksons-mbp.mynetworksettings.com>

* feat: Slack MCP via slack-mcp-server + OAuth PKCE login (#1037) (#1158)

* feat(amico-run): `amico slack login` — OAuth PKCE flow (#1156)

Adds the `amico slack login` CLI command that runs an OAuth 2.0 PKCE flow:
- Starts a temporary HTTP server on localhost:54213
- Opens the browser to Slack's authorize endpoint with S256 challenge
- Receives the callback, verifies state (CSRF), exchanges code for a
  xoxp-* user token via oauth.v2.access
- Writes the token to ~/.amico/slack.json (atomic, 0600, AMICO_SLACK_FILE
  override respected)
- 120s timeout, structured error handling for denied consent, state
  mismatch, EADDRINUSE

No client_secret — PKCE only (public client).

Part of #1037 (revised approach per Jack's review).

* feat(extension): conditional slack-mcp-server MCP entry in config builder (#1157)

When a Slack credential exists (readCredential('slack')), the config builder
now includes a 'slack' MCP server entry that spawns slack-mcp-server with
SLACK_MCP_XOXP_TOKEN threaded into its environment. No credential → no entry
→ tools invisible to the agent.

Minimal-env graft per ADR 0002: only SLACK_MCP_XOXP_TOKEN and
SLACK_MCP_ADD_MESSAGE_TOOL are exported to the child process.

Part of #1037 (revised approach per Jack's review).

* feat(skill): rewrite amico-slack SKILL.md for slack-mcp-server (#1041)

Replaces all CLI references (amico-slack send/read/whois/status) with
slack-mcp-server MCP tool names (conversations_history, conversations_replies,
conversations_add_message, channels_list, users_search, etc.).

Key changes:
- Dialect switch: text/markdown (not Slack mrkdwn) — bold is **bold**
- Soft preview norm for sends documented
- LaTeX-to-Unicode conversion guidance preserved
- Connection hint: 'amico slack login' for authentication
- cli_tool frontmatter key removed
- Messages sent as user, not bot

Also updates ADR 0016 from proposed → accepted with revised approach note.

Part of #1037 (revised approach per Jack's review).

* feat(connections): Slack OAuth PKCE flow in the Connections panel (#1037)

Wire Slack OAuth directly into the Connections panel so users can connect
Slack by clicking 'Connect with Slack' — no terminal command needed.

UI changes:
- connectionAuthMethods() returns ['browser', 'token'] for Slack
- Connection picker shows 'Connect with Slack' button as primary,
  with token paste as fallback for power users

Server changes (extension + overlay mirror):
- startAuthResponse() now handles id='slack' with a full PKCE flow:
  starts a temp HTTP callback server on localhost:54213, opens the
  browser to Slack's authorize endpoint, exchanges the code for a
  xoxp-* user token, writes it to ~/.amico/slack.json (atomic, 0600),
  and persists 'connected' status for the panel to read
- 120s timeout on the callback server
- Error/success HTML pages rendered to the browser

Test: updated the 'non-google ids refuse browser auth' assertion to
verify Slack browser auth returns waiting-browser (not a refusal).

Part of #1037 (revised approach per Jack's review).

* chore: refresh overlay manifest for Slack OAuth UI changes

* fix(app-bundle): auto-refresh manifest in dev mode on mismatch

When materialize.mjs detects a manifest mismatch and OPENCODE_CHANNEL is
not 'prod' (i.e. local dev builds), auto-run refresh_manifest.mjs and
re-verify instead of failing. This prevents the 'Rebuild locally' button
from aborting when overlay files are edited — overlay edits are expected
in dev, and requiring a manual refresh_manifest.mjs step was a deploy-
mechanics tax that broke the Connections panel UI changes for #1037.

CI (channel=prod) still fails on manifest mismatch as before.

* feat: read AMICODE_SLACK_CLIENT_ID from env with shipped default fallback

All three OAuth entry points (CLI verb, extension connections, overlay
connections) now read the Slack client_id from the AMICODE_SLACK_CLIENT_ID
env var first, falling back to the shipped default. This lets users test
with their own Slack App immediately, and once Harmoniqs registers the
production app, the hardcoded default is updated and no env var is needed.

Part of #1037.

* feat(connections): Slack App client_id setup flow in Connections panel

End users from any Slack workspace can now configure Amicode's Slack
connection without touching environment variables:

1. Click 'Connect with Slack' → if no Slack App is configured, the error
   message explains what's needed
2. Expand 'Slack App not set up?' in the picker → paste the Client ID
   their admin gave them → Save
3. Click 'Connect with Slack' again → OAuth flow starts

Client ID resolution (all three OAuth entry points):
  env var AMICODE_SLACK_CLIENT_ID → ~/.amico/slack-app.json → empty (error)

The picker saves the client_id via a 'slack-app-client-id' pseudo-
credential submission that writes ~/.amico/slack-app.json (0600).
Once Harmoniqs registers a production Slack App, the shipped default
replaces the empty string and no setup step is needed.

Part of #1037.

* fix(connections): cleaner Slack picker — 'Ask your Slack admin for your Client ID'

Simplified the Slack connection picker:
- Primary prompt: 'Ask your Slack workspace admin for your Client ID'
- Client ID input field is front-and-center (not hidden in a details section)
- 'Save & Connect' saves the client_id AND triggers the OAuth flow in one click
- 'Don't have a Client ID?' expandable with admin instructions
- Token paste remains as advanced fallback

Part of #1037.

* fix(build): include overlay file hashes in staleness check

Both build:binary and build:app used overlay_sha + promoted_at to detect
stale materialized trees — but those fields are set at extraction time and
don't change when overlay files are edited locally. Local edits went
undetected, so 'Rebuild locally' silently shipped stale UI.

Now the staleness stamp includes a hash of manifest.files (the per-file
content hashes that refresh_manifest.mjs updates). Combined with the
auto-refresh in materialize.mjs, the chain is:

  overlay edit → build:binary materializes → manifest mismatch in dev →
  auto-refresh manifest (updates file hashes) → new stamp written →
  build:app sees fresh stamp → reuses the correct tree → UI matches source

Part of #1037.

* fix(connections): show Slack logo in form, fix external link in webview

Two fixes to the Slack connection picker:

1. The Slack form header now shows the Slack icon + bold name instead of the
   raw id text, consistent with the catalog list's rendering.

2. The api.slack.com/apps link was a plain <a target="_blank"> which is dead
   inside the VS Code webview iframe. Replaced with an onClick handler that
   uses the open-external postMessage bridge (the same pattern footer.tsx and
   home-cards.tsx use).

Part of #1037.

* fix(connections): move admin hint inside the Client ID dropdown

The 'Ask your admin' line was sitting above the input as always-visible
text. Folded it into the 'Don't have a Client ID?' details block so the
form is cleaner — icon, input, button, with help tucked away for those
who need it.

Part of #1037.

* fix(slack): send client_secret in OAuth token exchange

Slack's oauth.v2.access requires client_secret even when PKCE parameters
are present — PKCE is additive security on top of the secret, not a
replacement. This caused 'bad_client_secret' on every token exchange.

Changes:
- slack_verb.ts: readSlackAppCredentials() reads both client_id and
  client_secret from env vars or ~/.amico/slack-app.json; exchangeCode()
  sends client_secret in the POST body; credential guards re-read on
  every call (no stale module-level IIFE)
- connection-picker.tsx: Slack form now collects both Client ID and
  Client Secret fields; dropdown updated to 'Where do I find these?'
- connections.ts (extension + overlay): submitCredentialResponse handles
  new 'slack-app-credentials' id carrying JSON {client_id, client_secret};
  startAuthResponse reads client_secret alongside client_id
- Test isolation: connection test backs up / restores real slack-app.json
  so the 'no config' refusal test passes on machines with Slack configured

Part of #1037.

* fix(build): always clear stale vite dist before app build

The overlay staleness check correctly re-materializes source files, but
vite's incremental build inside the materialized tree can reuse its own
cached dist output — serving a stale UI even though the source changed.
This was the root cause of 'Rebuild locally' not picking up UI changes.

Now build_app_bundle.mjs unconditionally clears packages/app/dist and
the .vite cache before running vite build. The 14s vite build is cheap
compared to shipping a stale UI.

Part of #1037.

* feat(connections): auto-refresh status after browser OAuth completes

After a browser-based OAuth flow starts (Slack, Google), the panel now
polls /amicode/connections every 2s until the connection state settles.
Previously the OAuth callback wrote the credential on a background HTTP
server but the panel had no way to know — the user had to close and
reopen the status popover to see 'connected'.

The poll stops automatically when all connections leave the
waiting-browser/validating states (the OAuth completed), or after a
2-minute timeout. The interval is cleaned up on component unmount.

Part of #1037.

* fix(slack): restart server on credential change so MCP entry updates

Part of #1037. Closes #1204.

* fix(status): scope auth poll to target connection + unstick rebuild flag

Two fixes for status popover stability:

1. Browser-auth polling (Slack/Google OAuth) now tracks which connection
   id started the auth and stops as soon as THAT connection settles. The
   previous code polled every 2s checking ALL connections, causing the
   Connections tab to re-render repeatedly while the user was browsing it.

2. The 'Rebuilding...' status could get stuck when the 'done' postMessage
   raced the window reload (extension → shell → iframe three-hop path).
   Reduced the stale-build detection from 2 minutes to 30 seconds: if the
   window reloaded and the extension host is healthy, the build completed
   and the message was lost — treat as success, not 'still building'. Also
   writes a rebuild-done marker file as belt-and-suspenders.

Part of #1037.

* feat(engine): dynamic MCP add/remove for Slack connect/disconnect

Part of #1037. Addresses #1205.

- Add `remove` method to the MCP service Interface and implementation
  (idempotent: removing a non-existent server is a no-op)
- Add `DELETE /mcp/:name` HTTP route (groups/mcp.ts + handlers/mcp.ts)
- Refactor slack_watcher to pass `{ exists: boolean }` to the callback
- Extension now calls POST /mcp (add) or DELETE /mcp/slack (remove)
  dynamically instead of restarting the server
- Falls back to server restart if the API call fails
- OPENCODE_CONFIG_CONTENT unchanged (still includes Slack at startup)

* fix(connections): auth poll fetches independently of popover visibility

The browser-auth poll called refetchConnections() which is gated on the
popover being visible (the resource's source signal is undefined when
shown() is false). During OAuth the user is in the browser with the
popover closed, so every poll tick was a no-op — the status never
updated.

Now the poll fetches /amicode/connections directly (independent of the
resource), checks the target connection's state, and only calls
refetchConnections() once the OAuth completes (to warm the cache for
when the popover reopens). The poll is fully self-contained: it works
whether the popover is open, closed, or never opened again.

Part of #1037.

* fix(engine): emit ToolsChanged after dynamic MCP add

The MCP add method (POST /mcp) registered the server and spawned the
process but never published a ToolsChanged event. The UI listens for
mcp.status.changed SSE events to refresh the MCP tab — without the
event, a dynamically added server (like slack-mcp-server after OAuth)
was invisible until a manual restart.

Now add publishes ToolsChanged, matching what remove already does.

Part of #1037.

* fix(slack): retry watcher API call when server isn't ready yet

The watcher's credential-change callback silently skipped when
opencodeReadyUrl was undefined (server not ready). This happens right
after a window reload or server restart — the token write from OAuth
often lands in that window. The watcher detected the file but the API
call never fired.

Now retries up to 5 times (1s apart) waiting for the server to become
ready before giving up.

Part of #1037.

* chore(overlay): drop posture-indicator-view — upstream adopted it

The fork now ships posture-indicator-view.tsx identically; the overlay
copy is a duplicate the drift gate rejects. Remove the overlay file and
its manifest exception.

* chore(overlay): drop 20 fork-adopted duplicates + add archive sha

The upstream fork adopted 20 overlay files identically. The drift gate
rejects these as exception-duplicates. Removed the overlay copies, their
manifest exceptions, and re-pointed 15 tests from overlay/ to
.materialized/ imports. Also added upstream_base_archive_sha256 to the
manifest (required when the CI cache has the archive stamp).

Drift gate passes locally.

* fix(tests): remove tests for upstream-adopted overlay files

11 test files imported from overlay copies that upstream adopted — the
overlay files were deleted by the drift-gate fix, breaking both local
(.materialized fallback) and CI (no materialized tree). These tests
belong with the upstream code now; delete them.

Remaining tests that reference the overlay all point at files still
committed in the overlay tree.

Part of #1037.

* docs: ADR 0021 (pause/resume/redirect subagents) + CONTEXT.md glossary

Record the design decision for #1208 — Pause, Resume, and Redirect as
session-level primitives. ADR covers the decision shape, the foreground-
parent edge case, the upstream-base boundary, slice decomposition, and
rejected alternatives.

CONTEXT.md gains four glossary terms under Agentic work: Pause, Paused,
Resume (session control), and Redirect — with Avoid lines disambiguating
from cancel/stop/abort and the existing Resume-session nav widget.

Refs: #1208

* docs: ADR 0021 — research layout is a resolved contract, not a fixed directory tree

Reframe the research project/environment layout as a resolved contract
(role → path) rather than a frozen directory constant. Three profiles
(separate, monorepo, standalone) reduce to the same contract; the default
is byte-identical to today's layout.

Fixes two production defects as prerequisites:
- The injected project/environment context blocks are dead (env vars never
  passed to the server spawn)
- The research agent card and skill contradict each other on whether the
  vault or the project is the source of truth

Part of #1210.

---------

Co-authored-by: Jackson Turner <32987603+jacktrnr@users.noreply.github.com>
Co-authored-by: Jackson Turner <jacktrnr@jacksons-mbp.mynetworksettings.com>
…s fleetReady or offers standalone (#1202)

The fleet client's checkFleet flipped fleetReady off on ONE failed probe
(1.5s timeout) and fired the 'Go Standalone?' popup after 10s, repeatedly,
through multi-minute tunnel degradation windows (the 2026-09-15 live log:
37→119 consecutive failed checks during active use).

Extract the threshold decision into FleetPollHysteresis (pure, injectable —
the server_keepalive #1187 pattern: DEFAULT_*_THRESHOLD + consecutive-failure
counting), keep extension.ts's wiring thin:

- down-flip (fleetReady=false + status bar) only at 15 consecutive failures
  (≈30s at the 2s poll); a blip logs a single 'holding' line
- standalone offer only after a SUSTAINED outage (150 consecutive ≈ 5 min),
  once per outage, never re-offering within a 15-min cooldown that survives
  a recovery — flapping cannot extract repeated popups
- up-transition behavior unchanged (SSE re-attach, panel reveal, provider
  signal); a successful probe clears the consecutive count
- thresholds configurable via amicode.fleetTunnelDownConsecutiveFailures /
  fleetStandaloneOfferConsecutiveFailures / fleetStandaloneOfferCooldownMinutes
  (0 = the default, the D6 tuning precedent)
…l + honest send refusal + stale-resource read gate

On stream loss the session view now STAYS MOUNTED: a reconnecting veil
(v2 tokens, pointer-events none) over the last rendered timeline, cleared
in place on gap-clear — no reload, no session-list refetch on the
reconnect path. A send during the gap refuses honestly (toast, before
history/clear) — never a fake success. The #638 staleness machine's
transitions are unchanged; the render response is what changed.
The session page's cached-timeline read is gated on messagesReady() so a
failed refetch stops suspending the whole view.
fix(extension): fleet-poll hysteresis — a blip holds, a sustained outage offers once (#1202)
…le check)

The new session-stream-veil/stream-veil-view/stream-gap files plus the 30
changed overlay paths' hashes — the manifest is the overlay's provenance
record; the drift gate catches unregistered files by design.
…e-loss

fix(app): keep the session view mounted across SSE loss (#1203)
@coderabbitai

coderabbitai Bot commented Sep 16, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 6c5ccf25-7049-4353-8d38-a3d3770f0dc0

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

- transform.ts: dev's overlay state + main's Bedrock reasoning fix ported
  (the adapter's silent-drop bug — dev's syncs predate 568306f)
- transform.test.ts: main's (the fix's tests)
- manifest.json: the union of both sides' overlay hashes
- the manifest re-hashed against the merged overlay (826 files, the
  union of both sides' entries, the disk as truth)
- packages/app/public/amico.svg: the symlink materialized as its content
  (695B) — the manifest records content hashes, and CI's gate (green on
  dev) hashes the materialized file; the symlink form was an
  environmental asymmetry that broke the gate on symlink-materializing
  checkouts
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.

2 participants