Skip to content

monorepo follow-ups: glance dashboards and poller, gitq undo, extension bundle (test first) - #519

Merged
m4ttheweric merged 18 commits into
mainfrom
followups-tdd
Sep 27, 2026
Merged

m4ttheweric merged 18 commits into
mainfrom
followups-tdd

Conversation

@m4ttheweric

Copy link
Copy Markdown
Collaborator

Closes the review follow-ups left over from the monorepo fold-in, each fixed test-first: four glance bugs, the gitq undo data-loss bug, and the VS Code extension bundle that never loaded. Plan: docs/superpowers/plans/2026-09-27-monorepo-followups.md.

What changed

glance (packages/glance)

  • GitHubEventsPoller commits hasTicked and its etag only after a full tick, so a failed first tick retries cold and a failed later page never skips unseen events (GLANCE-36)
  • The single MR dashboard follows the cable's connection state; the watcher reports disconnected after a drop once it has connected (GLANCE-37)
  • Group dashboards ride the shared cable through a new optional GitProvider.subscribePullRequestEvents: one fetch and poll loop per group, every cable event reaches the group once, late MRs attach incrementally, and attaching during a cable outage waits for the welcome (GLANCE-38). GitHub groups no longer call GitHub's throwing watchMR; they poll
  • The live integration script mutates only the sandboxes named in GLANCE_HARNESS_GITHUB_SANDBOX and GLANCE_HARNESS_GITLAB_SANDBOX, refuses otherwise, and exits non-zero on any refusal (GLANCE-39)

gitq (apps/gitq)

  • gitq undo moves refs by compare-and-swap from the heads the operation recorded to the snapshot, never checks out, never moves trunk, refuses a dirty tree or a dirty, rebasing or moved holding worktree up front, and can resume a partial undo left by a race (GITQ-27)
  • Drops a dead dev.deploy from the manifest and an unused destructure

VS Code extension (extensions/vscode/rt-context, packages/rt-client, release.yml)

  • The bundle loads in Node: jsonc-parser bundles from its ESM entry, and rt-client builds its bun:sqlite require inside the function instead of at module scope
  • A load smoke runs right after bun run package in the release job, and the extension has a test script so bun run check runs its tests

Follow-up

  • GLANCE-40 covers a pre-existing collision when two watchers share one MR on one provider
  • GLANCE-39's live run waits on restoring packages/glance/harness_credentials.json
  • RT-327's last acceptance point (a .vsix installs and activates in an editor) is Matt's to check

Verification

bun run check green (46/46 package tasks, purity ok); rt unit suite 11453 pass with the six known rotating flakes green alone; e2e 150 pass (one failure from this machine's mise node shim in the plugin scaffold test); pty 8/8; glance 620/620, gitq 527 unit and 431 integration, rt-client 946/946, extension 22/22. Every task had an Opus review and scoped re-reviews of its fixes, then an Opus whole-branch review. Main merged in at 8ab1df5.

🤖 Generated with Claude Code

m4ttheweric and others added 18 commits September 27, 2026 11:20
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ll tick

A cold tick that threw left the poller warm, so the retry replayed the
first page as invalidations; a tick that threw on page 2 kept page 1's new
etag, so the next tick 304'd past the unseen pages. Both are now staged in
locals and committed once every page has been fetched.

Fixes GLANCE-36

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
createSingleDashboard built a status listener it never handed to watchMR,
so its connection stayed 'connecting' forever. RealtimeWatcherOptions now
carries onStatusChange (watchMR already spreads its options into the
watcher), and the watcher reports 'disconnected' after a connected cable
drops instead of falling back to 'connecting'.

Fixes GLANCE-37

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…hers

The group dashboard called watchMR once per MR only to learn about pushes,
which ran a fetch and poll loop per MR and dropped every event and
connection change the shared cable delivered. GitProvider gains an optional
push-only subscribePullRequestEvents; GitLabProvider implements it over the
same shared cable as watchMR (the registration is now one private method
both use), and the group subscribes once for the whole group, so a cable
event triggers one batched refetch and connect or disconnect moves the
group's connection state.

Behaviour change: on GitHub, where the method is absent, group dashboards
no longer call watchMR (which throws there); they report connected and
poll, as the branch dashboard already does.

Fixes GLANCE-38

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The two poller throw tests reuse scriptedFetch (now scripted Errors throw),
the single dashboard test pins reconnect back to connected, and a watcher
test pins that the params onStatusChange wins over the options one.

Refs GLANCE-36, GLANCE-37

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
integration.live.ts picked its mutation targets from the token user's own
PR and MR lists, so a hand run with real credentials wrote notes, approvals
and merges onto real, unrelated work. Every mutating step now takes its
target from GLANCE_HARNESS_GITHUB_SANDBOX (owner/repo) or
GLANCE_HARNESS_GITLAB_SANDBOX (group/project!iid) and refuses, sending no
request, while it is unset. Read-only probes keep the own-list fallback.
GITLAB_PROJECT_PATH no longer steers mutations.

Target selection lives in tests/live/sandboxTarget.ts with unit tests; the
live run against the sandbox is still to do.

Refs GLANCE-39

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
undo checked out each snapshotted branch and reset --hard it in the launch
tree: uncommitted edits carried across the checkout were discarded, trunk
was checked out and reset too (throwing part way when another worktree held
it, with the store update skipped), and commits made since the operation
were dropped.

Now undo refuses a dirty launch tree, skips the stack root, and checks every
branch before moving any: one that moved since the operation refuses the
whole undo, naming each. The operation log records resultHeads (every
branch's head once the operation finished) for that check; older entries
fall back to the head read at undo time. Each ref then moves through
finalizeBranchRef from its result head to its snapshot sha, so undo works
from any worktree. A move that still fails part way stops, names the
branch, and reports what was already restored; the stack record is updated
only when every branch restored.

Fixes GITQ-27

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A group fetch that returns an MR its push subscription does not cover now
re-attaches (new before old, so the shared cable never tears down in
between, and without a spurious reconnect refetch). A watcher attached
while the shared cable is connecting or down is told it is connected only
on the next welcome, not immediately. Shared-cable log lines drop the
watchMR prefix since groups use the cable too.

Tests add a burst of events from different MRs giving one refetch, and
watchMR's subscribe, refetch, reconnect and dispose against a fake socket.

Fixes GLANCE-38

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GitHub's sandbox now parses as exactly owner/repo (no extra segments, no
!iid) and says so in its refusal. Path segments must be [\w.-]+ and never
. or ..; an iid must be 1 to 10 digits with no leading zero. A sandbox MR
that is missing fails that step instead of killing the run, one that is
not open is refused, and any refusal makes the run exit non-zero so an
all-refused run cannot read as success.

Refs GLANCE-39

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A branch already at its snapshot now counts as restored, so rerunning undo
after one that stopped part way finishes it instead of refusing those
branches as moved. The precondition pass also applies finalizeBranchRef's
own refusals (holding worktree dirty, mid-rebase, or off the branch head)
and refuses with nothing moved, naming each branch and its slot;
finalizeBranchRef keeps them as the race guard. A failed undo's JSON no
longer carries the snapshot tree, since the stack record was not touched.

The partial path is tested for real: a reference-transaction hook makes
git reject one branch's update, then a retry without it completes.

Fixes GITQ-27

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
GitLab keys cable channels by MR, so re-subscribing the whole group and
then disposing the old subscription unsubscribed every MR already covered:
after a late MR arrived, only that MR kept push. The group now subscribes
just the uncovered MRs and keeps every subscription until restart or
dispose; the added ones forward events only, so connection changes are
reported once. Tested against the real GitLabProvider over a fake socket.

Fixes GLANCE-38

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s it

Two load failures killed every shipped VSIX. esbuild picked jsonc-parser's
UMD entry, whose runtime require('./impl/format') the bundle cannot
satisfy; the build now prefers ESM entries (--main-fields=module,main).
And rt-client's bare require("bun:sqlite") made Bun's bundler emit a
module-scope createRequire(import.meta.url), which a CJS bundle executes
at load with an empty import.meta. repos.ts now builds that require from
the module URL inside loadBunSqliteDatabase and returns null when there is
none, since a CJS bundle runs outside Bun. ESM behaviour is unchanged.

scripts/load-smoke.mjs loads the bundle in plain node with a stub vscode
module, run by a bun test in the extension and by the release job right
after bun run package, so a VSIX that cannot load fails the release.

Fixes RT-327

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Deck neither registers nor serves gitq, so the manifest's dev.deploy
(deck restart gitq) could never run; the manifest stays for its bundle
recipe. hasStagedDiff no longer destructures a stdout it never reads.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The partial-undo tests wrote their reference-transaction hook into the
common dir's hooks/, which a global core.hooksPath silently bypasses. They
now share rejectRefUpdates, which pins a repo-local core.hooksPath, so the
hook always fires and never lands in a global hooks directory.

Refs GITQ-27

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Refs RT-327

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ings

A failed run exited inside the try, skipping the finally that removes the
stub vscode dir; it now sets exitCode. The bun test asserts the exit
status and the ok line instead of an empty stderr, so a future node
deprecation warning cannot fail it.

Refs RT-327

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Also notes on rt-client's dist-cjs-safe test that its regex matches Bun's
current emit shape, with the rt-context bundle-load test as the backstop.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 27, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 32 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 2 included reviews currently available. Your 71 included PR review attempts over the past 7 days set your current allowance at 2 reviews per hour.

This review is too large to run within your organization's remaining usage spending cap. Raise or remove your spending cap in the billing tab, then retry.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: 8955719e-2d88-4bea-9196-798f39eec17f

📥 Commits

Reviewing files that changed from the base of the PR and between a694bdd and 0a01836.

📒 Files selected for processing (38)
  • .github/workflows/release.yml
  • apps/gitq/mattstack.deck.json
  • apps/gitq/src/cli/commands/undo.ts
  • apps/gitq/src/cli/op-log.ts
  • apps/gitq/src/core/git-shell.ts
  • apps/gitq/src/core/operation-log.ts
  • apps/gitq/src/core/undo.ts
  • apps/gitq/tests/integration/cli.test.ts
  • apps/gitq/tests/integration/helpers.ts
  • apps/gitq/tests/integration/undo-cas.test.ts
  • apps/gitq/tests/undo.test.ts
  • docs/superpowers/plans/2026-09-27-monorepo-followups.md
  • extensions/vscode/rt-context/.vscodeignore
  • extensions/vscode/rt-context/package.json
  • extensions/vscode/rt-context/scripts/load-smoke.mjs
  • extensions/vscode/rt-context/src/__tests__/bundle-load.test.ts
  • lib/__tests__/release-workflow.test.ts
  • packages/glance/CHANGELOG.md
  • packages/glance/README.md
  • packages/glance/harness_credentials.example.json
  • packages/glance/src/GitHubEventsPoller.ts
  • packages/glance/src/GitLabProvider.ts
  • packages/glance/src/GitProvider.ts
  • packages/glance/src/MRDashboard.ts
  • packages/glance/src/RealtimeWatcher.ts
  • packages/glance/tests/github-events-poller.test.ts
  • packages/glance/tests/gitlab-subscribe-pr-events.test.ts
  • packages/glance/tests/integration.live.ts
  • packages/glance/tests/live-expectations.test.ts
  • packages/glance/tests/live-sandbox-target.test.ts
  • packages/glance/tests/live/conformance.ts
  • packages/glance/tests/live/expectations.ts
  • packages/glance/tests/live/sandboxTarget.ts
  • packages/glance/tests/mrdashboard-group-cable.test.ts
  • packages/glance/tests/mrdashboard-status.test.ts
  • packages/rt-client/src/repos.ts
  • packages/rt-client/test/dist-cjs-safe.test.ts
  • packages/rt-client/test/repos.test.ts

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

@m4ttheweric
m4ttheweric merged commit e4f3f8a into main Sep 27, 2026
13 checks passed
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.

1 participant