Skip to content

fix(ci): unwedge the develop base — test-mock rejection, bsc-brief infinite loop, job timeouts - #2415

Merged
kevinthelago merged 8 commits into
developfrom
2414-ci-base-fix
Jul 6, 2026
Merged

fix(ci): unwedge the develop base — test-mock rejection, bsc-brief infinite loop, job timeouts#2415
kevinthelago merged 8 commits into
developfrom
2414-ci-base-fix

Conversation

@kevinthelago

Copy link
Copy Markdown
Owner

Every open PR's CI gate is red from two develop-base breakages (the tail #2408 didn't reach). Fixed here:

1. Frontend — unhandled rejection fails the job while all 3389 tests pass

useTunnelHookTelemetry.test.ts mocks the bsc bridge as a bare vi.fn(), but also imports @/store — whose persist-rehydrate calls bscJson(null, ["blueprint","list","--full"], []).then(...). The bare mock returns undefined.then of undefined → Unhandled Rejection → vitest exits non-zero. Reproduced identically on PR #2400 and the frontend-untouched dependabot PR #2410. The mock now resolves its fallback (the real bridge's documented degrade behavior) and stubs bscWrite.

2. Rust — bsc_brief_emits_tab_aligned_coord_line hangs 6 hours

The test runs bash -c "bsc-brief director --ref #77" — bash eats unquoted #77 as a comment, leaving --ref dangling, and in coord-emit.sh shift 2 with one arg left shifts nothing, so the parser loops forever. Verified empirically both ways. Six PRs burned a 6-hour runner each on this.

  • Hardened all three flag parsers (bsc-issue/bsc-assign/bsc-brief): "${2-}" + shift 2 2>/dev/null || shift — a dangling flag falls through instead of spinning. This was a live-agent hazard too: a planner emitting an unquoted bsc-brief director --ref #123 would wedge its session forever.
  • The test quotes the ref (--ref '#77', so it actually round-trips into the log) and adds the dangling-flag shape as a terminating regression case.
  • Verified end-to-end against real bash: all emitters terminate, columns exact.

3. ci.yml job timeouts

No job had timeout-minutes; a hang cost 6h × N runners. Added: changes 10 / frontend 25 / plan-contract 15 / rust 90 (cold DuckDB build headroom) / gate 5.

Gate: typecheck clean · lint 0 errors · cargo clippy --workspace --all-targets -D warnings clean.

Closes #2414.

🤖 Generated with Claude Code

kevinthelago and others added 8 commits July 6, 2026 12:19
…finite loop, job timeouts

Three fixes so every open PR's CI gate can go green (#2414):

- useTunnelHookTelemetry.test.ts mocked the bsc bridge as a bare vi.fn();
  this file imports @/store, whose persist-rehydrate calls
  `bscJson(...).then(...)` — undefined.then → an unhandled rejection that
  failed every frontend job while all 3389 tests passed. The mock now
  resolves its fallback, matching the real bridge's contract.

- coord-emit.sh flag parsers (bsc-issue/bsc-assign/bsc-brief) spun forever
  on a dangling flag: `shift 2` with one arg left shifts NOTHING in bash.
  The test's unquoted `--ref #77` (bash eats `#77` as a comment) hit
  exactly that, hanging the Rust job to the 6-hour runner timeout on six
  PRs. Parsers now degrade (`shift 2 2>/dev/null || shift`, `"${2-}"`),
  the test quotes the ref, and a dangling-flag regression case pins it.
  This was a live-agent hazard too: a planner emitting an unquoted
  `bsc-brief director --ref #123` would wedge its session forever.

- ci.yml jobs now carry timeout-minutes so a future hang fails in
  minutes, not 6 hours per runner.

Closes #2414.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The bsc-brief hang meant the Rust suite never ran to completion on CI, so
two born-broken tests (both fleet-pushed straight to develop) were
invisible until the hang fix let the run finish:

- pty_faults::extracts_a_rust_panic_with_its_note (#2264, born broken):
  a Rust panic head ends with ':' and carries its message on the NEXT
  line (the 1.65+ two-line format). That message line matches no frame
  shape, so the stack walk stopped on it and dropped the note line the
  test asserts on. extract_faults now absorbs the panic-message line
  into the stack (guarded to never absorb another fault head), which
  also gives the fault fingerprint the actual message.

- planner::directives::embedded_directive_key_set_matches_the_migrated_set:
  the new test_ui stage (data/stages/test_ui.json) carries a directive
  but the drift-guard's expected set was never updated. Added test_ui.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s per run

- bsc-agent's project_cli_with_its_store_present_is_not_flagged rotted
  when the `errors` sidecar (#2260) added BSC_ERROR_DB as a project
  store: the test hardcoded only PLAN + DATA, so `errors` was flagged
  "needs a project context". The store list now derives from SIDECARS,
  so the next project-scoped sidecar cannot rot it again.

- ci.yml: `cargo test --workspace --no-fail-fast` — the default aborts
  at the first failing test target, which is why this base rot has
  surfaced one crate per 12-minute CI round instead of all at once.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…d was dead code

Fourth and final masked rot (#2414): slug_check_rejects_empty_id_with_the_
stores_noun expects safe_id("..") to be rejected, but the mapper turns
every dot into '_' before the guard runs, so `safe == "."` / `".."` could
never match and ".." sailed through as Ok("__"). Requiring at least one
alphanumeric rejects the same traversal shapes the guard intended and
keeps all-punctuation ids from colliding onto one underscores-only file.
Diverges from fsx::safe_dir_segment only for those degenerate ids (the
doc comment now says so); identical output for every real id.

The --no-fail-fast sweep confirms this was the last red test target in
the workspace.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… tests

Develop grew a fifth rot mid-repair: PR #2424 merged while the base gate
was red, bringing the TextArea primitive with tests that never ran green.

- Field.test.tsx asserted `ta.spellcheck === false`, but jsdom doesn't
  implement that IDL reflection on textarea — the property reads
  undefined regardless of the attr. Assert getAttribute("spellcheck")
  instead (what React actually renders).

- specimens.test.tsx requires every react-ui primitive to have a bespoke
  specimen; TextArea was registered in the manifest without one and hit
  the "rendered in-app" fallback. Added the bespoke specimen (TextField's
  multiline sibling) + a shape-matched loading skeleton (84px, matching
  the component's own Skeleton h).

Also merges origin/develop (#2422-#2425) into the branch.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…thout one

#2426 (fill UI-kit manifest gaps) merged past the red gate mid-repair and
registered LabelChip, ActivityFeed, Pane, TelemetryPanel, ItemBars, and
SplitBar in the react-ui kit with no Design Studio specimen — each one a
fresh no-fallback guard failure. Added all six in the established inline
style, verified complete by statically diffing the kit's component names
against the specimen switch (no more one-per-CI-round whack-a-mole).

Also merges origin/develop (#2426).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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