Skip to content

docs: ADR-0015, CLAUDE.md/AGENTS.md doc-truth, TODO/AUDIT closeout, run log - #291

Merged
qnbs merged 13 commits into
mainfrom
feat/worker-v2-docs-cleanup
Jul 30, 2026
Merged

docs: ADR-0015, CLAUDE.md/AGENTS.md doc-truth, TODO/AUDIT closeout, run log#291
qnbs merged 13 commits into
mainfrom
feat/worker-v2-docs-cleanup

Conversation

@qnbs

@qnbs qnbs commented Jul 29, 2026

Copy link
Copy Markdown
Owner

Summary

Closes out the worker-generation consolidation sprint (#286-288, #290) at the documentation layer. Stacked on #290#288#287#286 (merged).

  • New docs/adr/0015-worker-generation-consolidation.md — the decision record for the completed migration.
  • docs/adr/0014-*.md: status → "Superseded by 0015" (body untouched, per this repo's "supersede, don't edit history" convention).
  • docs/adr/README.md: index gained rows 0010-0015 (0010-0014 were pre-existing files missing from the index before this sprint — unrelated drift, fixed opportunistically).
  • CLAUDE.md/AGENTS.md: directory map, WorkerBus v2 section, DuckDB section, Known Technical Debt updated to post-migration state. Also fixed 2 pre-existing doc-truth bugs found while reading the WorkerBus v2 source directly (emitProgress is a flat function not context.progress.emit(...); no background priority tier).
  • TODO.md: F-14 marked done with the PR chain. Also caught and fixed an unrelated stale entry — "Tag v1.24.2 + publish release" was still marked open, but v1.24.2 was already tagged/released.
  • AUDIT.md: F-14 and F-11 (same stale-tag issue) updated to resolved.
  • CHANGELOG.md: [Unreleased] section added covering the full sprint.
  • New docs/audit/WS-RUN-LOG-2026-07-29-worker-consolidation.md — full execution log (baseline, the 4-PR breakdown, every correction-loop finding with its fix, the Vercel missing-export incident, final verification, deliberately-unresolved items).

Test plan

  • pnpm exec tsx scripts/audit-feature-parity.ts — 0 drifts
  • pnpm exec vitest run tests/unit/checkDocMetrics.test.ts tests/unit/featureCatalog.test.ts — 28/28 passing
  • pnpm run typecheck (exact CI command) — clean
  • pnpm run lint (Biome) — clean via pre-commit hook
  • CI quality gate green

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved reliability for analytics and local inference processing, including worker recovery, task cleanup, query handling, and fallback error reporting.
    • Resolved production build issues affecting inference services.
  • Documentation

    • Updated architecture, audit, changelog, ADR, and sprint documentation to reflect the consolidated worker system and current operational behavior.
    • Documented known limitations, including task timeout enforcement.

qnbs and others added 11 commits July 29, 2026 19:21
…ests

Neither workers/v2/duckdb.worker.ts's handlers nor
workers/v2/inference.worker.ts's handleInference were exported or
unit-tested against real logic (only webllm.worker.ts had that,
tests/unit/webllmWorkerHandler.test.ts) despite both already being
registered as live WorkerBus v2 pools with zero production callers —
docs/adr/0014-worker-generation-duplication.md's deferred migration
needs these to actually match v1 before any consumer cuts over.

Testing surfaced a real gap: initDuckDb()'s OPFS-unavailable catch
block silently swallowed the failure, unlike v1's out-of-band
OPFS_FALLBACK message that lets the UI warn users their analytics
won't persist. Fixed by threading ctx.emitProgress into initDuckDb()
and emitting an 'opfs-fallback' progress stage from the catch block —
reuses the existing progress channel WebLLM already uses for download
progress, no new message-protocol/schema change needed.

First PR of the ADR-0014 consolidation sprint (5 PRs total, see
.claude/plans/worker-generation-v1-vs-enumerated-fox.md).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…S-v3 format

CodeRabbit review on #286 found a real bug: initDuckDb()'s OPFS catch
block overwrote `connection` with the fallback connection without
closing the first (partially-attached) one, leaking a DuckDB
connection on every OPFS-attach failure. Fixed by tracking the OPFS
attempt in its own local variable and closing it in the catch path
before falling back. New regression test asserts close() is called.

Also collapsed several multi-line QNBS-v3 comments in
workers/v2/duckdb.worker.ts, workers/v2/inference.worker.ts, and both
new test files to the required single bracketed-line format.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Second of 5 PRs consolidating the v1/WorkerBus-v2 worker duplication
(ADR-0014). services/duckdb/duckdbClient.ts's public API (init/query/
exec/shutdown/terminate/setOpfsFallbackHandler) is unchanged — its 5
real consumers (useDuckDb.ts, duckdbAnalytics.ts, duckdbMigration.ts,
ragVectorMigration.ts, telemetryService.ts) need no changes. Only the
internals swapped from a raw `new Worker(duckdbWorker.ts)` to routing
through the shared WorkerBus v2 'duckdb' pool via a new
ensureDuckDbPool() (mirrors ensureWebLlmPool()'s decoupling from the
enableWorkerBusV2 flag — analytics is core, not experimental infra).

The v2 worker returns raw values and throws on failure, unlike v1's
{ok,rows,error} wrapper — the adapter in duckdbClient.ts's send()
translates at that one boundary instead of touching ~20 call sites
across 5 files. WorkerBus's default 2-retry is disabled per-call
(retryPolicy: {maxRetries:0}) to avoid stacking under
duckdbAnalytics.ts's existing withDuckDbRetry() app-level retry.

Two things surfaced while wiring this up, both fixed here:
- WorkerBus.shutdown() tears down all 4 pools with no way to scope to
  just 'duckdb' — added WorkerBus.terminatePool(poolId) so
  duckdbClient.terminate() doesn't kill in-flight inference/webllm/
  plugin work on the shared bus.
- send() is async and awaits ensureDuckDbPool() before a task handle
  exists; naively attaching the AbortSignal listener after that await
  (like a first draft did) can lose a same-tick abort, unlike v1's
  synchronous Promise-executor version. Fixed by attaching the
  listener synchronously up front and applying it once the handle is
  ready — regression-tested.

workers/duckdbWorker.ts deleted; features/featureCatalog.ts's
enableDuckDbAnalytics entry retargeted to workers/v2/duckdb.worker.ts.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ailure

CodeRabbit's second pass on #286 flagged this as an outside-diff-range
comment (not a resolvable inline thread) on bcc41c2's own fix: the
new `await opfsConnection?.close()` call could itself reject, which
would skip emitProgress('opfs-fallback', ...) and the fallback
newDb.connect(), and replace the original ATTACH error with the close
failure — turning a graceful degrade into a hard init failure.

Wrapped the cleanup in its own try/catch (best-effort, logged via
console.warn, never blocks the fallback path) so a failing close()
can't prevent the fallback connection or swallow the real error. New
regression test asserts both still happen when close() rejects.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Third of 5 PRs consolidating the v1/WorkerBus-v2 worker duplication
(ADR-0014). services/ai/localEmbeddingService.ts's public API
(embedText/embedBatch/cosineSimilarity/clearEmbeddingCache) is
unchanged — its consumers (localRagService.ts, crossProjectIndexService.ts,
ragPromptAssembly.ts, ragVectorMigration.ts, proForgeMemoryBank.ts,
loraEvaluationService.ts, loraDatasetBuilder.ts, inferenceGateway.ts)
need no changes. Internals swapped from a dedicated
`new Worker(inference.worker.ts)` instance to the shared WorkerBus v2
'inference' pool via ensureInferencePool() (same decoupling-from-
enableWorkerBusV2 pattern as ensureWebLlmPool/ensureDuckDbPool).

v2's handler already throws on failure (matching embedText's existing
throw-on-failure contract), so no response-shape translation is
needed here — just swap the transport. Deleted ~70 lines of hand-rolled
health-check/restart/backoff logic (30s ping, exponential backoff up
to 5 attempts), superseded by WorkerPool's generic health check.

Capped the shared 'inference' pool's maxWorkers to 2 (was
MAX_WORKERS_INFERENCE=4) in workerBusManager.ts: each pool replica
independently loads its own transformers.js pipeline with no
cross-replica cache sharing, so 4 concurrent workers under a burst
(e.g. embedBatch's micro-batching) could mean 4x the model memory
footprint.

workers/inference.worker.ts (v1) is NOT deleted yet — localNlpService.ts
still uses it directly; removal is PR 4 of this sequence, after that
migration lands too.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…QL param binding

Three real findings from CodeRabbit's review of #287 (duckdbClient
migration onto WorkerBus v2), all confirmed against current code
before fixing:

1. CRITICAL — params silently dropped (workers/v2/duckdb.worker.ts).
   duckdbClient.send() enqueues {sql, params}, but handleQuery/
   handleExec only read req.sql and called connection.query(sql)
   directly, ignoring params entirely and leaving an unbound-query/
   SQL-injection surface flagged by static analysis. This was already
   live: services/ai/telemetryService.ts's writeToDuckDb() passes real
   bound params ([taskType, backend, modelId, latencyMs, success])
   that were being silently discarded. Fixed via DuckDB-WASM prepared
   statements (connection.prepare(sql) + stmt.query(...params) +
   stmt.close()) when params is non-empty; unchanged direct
   connection.query(sql) otherwise. Needed a narrow structural type
   (PreparableConnection) since tsgo doesn't resolve
   AsyncDuckDBConnection.prepare() through this package's nested
   `export *` chain, despite it being present in the package's own
   .d.ts — same class of tsgo/external-package gap CLAUDE.md already
   documents for the transformers.js path alias.

2. MAJOR — DuckDB pool worker losing its connection on respawn
   (services/duckdb/duckdbClient.ts). The duckdb pool's minWorkers:0
   lets its worker idle-terminate; a freshly spawned replacement
   worker's module state has no `connection` until INIT runs again on
   it, so the next QUERY/EXEC after a respawn failed with "DuckDB not
   initialized". Fixed by transparently re-sending INIT once and
   retrying the original call when send() sees that specific error
   message (bounded to one retry, INIT/SHUTDOWN excluded from the
   check to avoid any recursive loop).

3. MAJOR — terminatePool() had no lifecycle contract
   (packages/worker-bus/src/workerBus.ts +
   services/workerBusManager.ts). Removing a pool didn't settle tasks
   already routed to it — their result promises would await a RESULT
   message from a worker that no longer exists. Fixed by tracking
   task->pool assignment (new taskPools map, set/cleared around
   runTask's pool-acquire phase) and cancelling matching tasks before
   deletion. Separately, ensureDuckDbPool() assumed a non-null bus
   always has every pool, so any call after duckdbClient.terminate()
   would fail with NO_POOL forever; added WorkerBus.hasPool() and a
   re-registration path (duckDbPoolOptions()/reRegisterDuckDbPool(),
   shared with doInitWorkerBus() to avoid option drift between the two
   registration sites).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…uilds

Root cause of both Vercel deployment failures on this branch/PR chain:
services/ai/localEmbeddingService.ts imports and calls
ensureInferencePool() from workerBusManager.ts, but that function was
never actually added there — only ensureDuckDbPool() was. Vitest never
caught this because localEmbeddingService.test.ts mocks the entire
workerBusManager module (vi.mock(...) replaces it wholesale), so the
real file's missing export was invisible to that suite. tsgo's
typecheck also passed clean locally for reasons still unclear (worth
a follow-up look), but rolldown's stricter static bundling in the
actual `pnpm run build:edge` (Vercel's exact build command) correctly
failed hard with [MISSING_EXPORT] — reproduced locally with
`NODE_OPTIONS=--max-old-space-size=3072 pnpm run build:edge`, which is
what actually surfaced this.

Added ensureInferencePool(), mirroring ensureDuckDbPool()'s shape
(decoupled from enableWorkerBusV2, re-registers the pool if it was
removed via terminatePool() while the bus stays alive). Refactored
doInitWorkerBus()'s inline 'inference' pool registration to share the
same inferencePoolOptions() the new function uses, avoiding the same
kind of drift the duckdb pool options already guard against.

New tests in workerBusManager.test.ts import the *real* module (only
@domain/worker-bus is mocked) — this exact suite would have caught the
missing export immediately, unlike the fully-mocked consumer-side
test. Verified fixed via a full local `pnpm run build:edge`
reproduction of Vercel's exact build command (previously failed with
[MISSING_EXPORT], now builds clean).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…nce worker

Fourth and final migration PR of the worker-generation consolidation
(ADR-0014). services/ai/localNlpService.ts's public API
(analyzeSentiment/summarizeText/classifyWritingTopic) is unchanged —
confirmed zero real production callers beyond its own tests and a
barrel re-export in services/ai/index.ts, so this is the
lowest-blast-radius of the three migrations. Internals swapped from a
dedicated `new Worker(inference.worker.ts)` instance to the shared
WorkerBus v2 'inference' pool via ensureInferencePool() (added in the
previous PR), routing to the 'inference.text' capability.

analyzeSentiment/summarizeText already had their own app-level
graceful-degrade behavior on failure (NEUTRAL sentiment / truncated
text) — that's preserved unchanged via try/catch around the new
throw-on-failure requestInference() call, replacing the old
ok:false-check.

With both localEmbeddingService.ts (previous PR) and this migrated,
workers/inference.worker.ts (v1) has no remaining callers — deleted,
along with its dedicated test (tests/unit/inferenceWorker.test.ts).
tests/unit/localNlpService.test.ts rewritten against a
workerBusManager mock; tests/unit/services/localNlpService.test.ts
deleted as a near-total duplicate of the rewritten suite (same module,
overlapping classifyWritingTopic/analyzeSentiment coverage, the
comprehensive file already had everything it did).

Verified via typecheck and a full local `pnpm run build:edge`
reproduction of Vercel's exact build command (the lesson from the
previous PR's missing-export incident) — both clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…un log

Closes out the worker-generation consolidation sprint (PRs #286-288,
#290) at the documentation layer.

- New docs/adr/0015-worker-generation-consolidation.md — the decision
  record for the completed migration (target generation, per-workload
  changes, parity gaps found and fixed, deliberately-unchanged
  WebLLM-fallback-shape rationale, the timeoutMs follow-up).
- docs/adr/0014-worker-generation-duplication.md: status line changed
  to "Superseded by 0015" (body untouched, per this repo's own
  "supersede, don't edit history" convention).
- docs/adr/README.md: index gained rows 0010-0015 — 0010-0014 were
  pre-existing files missing from the index before this sprint,
  unrelated drift fixed opportunistically while editing the file.
- CLAUDE.md/AGENTS.md: directory map, WorkerBus v2 section, DuckDB
  section, and Known Technical Debt all updated to the post-migration
  state. Also fixed 2 pre-existing doc-truth bugs found while reading
  the WorkerBus v2 source directly: WorkerHandlerContext.emitProgress
  is a flat function, not context.progress.emit(...); there is no
  'background' priority tier, only critical > high > normal > low.
- TODO.md: F-14 entry marked done with the PR chain and correction-
  loop findings. Also caught and fixed a genuinely stale, unrelated
  entry while in the file — "Tag v1.24.2 + publish release" was still
  marked open, but v1.24.2 was already tagged/released (verified via
  git tag/gh release list).
- AUDIT.md: F-14 and F-11 (the same stale-tag issue) findings updated
  to resolved.
- CHANGELOG.md: [Unreleased] section added, covering the full sprint
  (Changed/Fixed/Removed).
- New docs/audit/WS-RUN-LOG-2026-07-29-worker-consolidation.md —
  execution log in this repo's established WS-RUN-LOG format: baseline,
  the 4-PR breakdown, every correction-loop finding with its fix, the
  Vercel missing-export incident, final verification, and what's
  deliberately left unresolved (timeoutMs enforcement; why local tsgo
  typecheck didn't catch the missing export).

Verified: pnpm exec tsx scripts/audit-feature-parity.ts (0 drifts),
checkDocMetrics.test.ts + featureCatalog.test.ts (28/28), typecheck
clean.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 29, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
worldscript-studio Ready Ready Preview Jul 30, 2026 3:58am

@coderabbitai

coderabbitai Bot commented Jul 29, 2026

Copy link
Copy Markdown

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 3 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: bb009cd7-8d02-4ba5-a271-790aba9fe2ae

📥 Commits

Reviewing files that changed from the base of the PR and between aeaee84 and e770de1.

📒 Files selected for processing (1)
  • CLAUDE.md
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is clearly related to the PR’s doc closeout and worker-consolidation updates, though it is verbose and file-list-like.

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

@codeant-ai

codeant-ai Bot commented Jul 29, 2026

Copy link
Copy Markdown

🏁 CodeAnt Quality Gate Results

Commit: dcf4144f
Scan Time: 2026-07-30 03:48:14 UTC

✅ Overall Status: PASSED

Quality Gate Details

Quality Gate Status Details
Secrets ✅ PASSED 0 secrets found
Duplicate Code ✅ PASSED 0.0% duplicated
SAST ✅ PASSED No security issues
Bugs ✅ PASSED Rating S: No bugs
IAC ✅ PASSED No IAC issues

View Full Results

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CLAUDE.md`:
- Line 409: Update the CLAUDE.md entry for workers/v2/inference.worker.ts to
remove the recommendation to restore `@ts-expect-error`. State that any
`@huggingface/transformers` v3 path-alias issue must be resolved by fixing the
tsconfig.json alias or the relevant type declaration, without introducing
suppression directives.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 52cf6d5b-7f45-4114-9aed-3c596ef18a10

📥 Commits

Reviewing files that changed from the base of the PR and between 4519180 and aeaee84.

📒 Files selected for processing (9)
  • AGENTS.md
  • AUDIT.md
  • CHANGELOG.md
  • CLAUDE.md
  • TODO.md
  • docs/adr/0014-worker-generation-duplication.md
  • docs/adr/0015-worker-generation-consolidation.md
  • docs/adr/README.md
  • docs/audit/WS-RUN-LOG-2026-07-29-worker-consolidation.md

Comment thread CLAUDE.md Outdated
CodeRabbit review on #291 correctly flagged that the Known Technical
Debt line I edited (only updating the file path to
workers/v2/inference.worker.ts) still carried a pre-existing
recommendation to "restore @ts-expect-error" if the transformers.js
path alias breaks — in direct tension with this repo's own
suppression-ratchet policy documented a few sections above in the
same file. Reworded to point at fixing the alias/type declaration
directly instead, consistent with the structural-type workaround this
same sprint used for a different tsgo/external-package resolution gap
(workers/v2/duckdb.worker.ts's PreparableConnection, PR #287) rather
than reaching for a suppression.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@qnbs
qnbs deleted the branch main July 30, 2026 03:46
@qnbs qnbs closed this Jul 30, 2026
@qnbs qnbs reopened this Jul 30, 2026
@qnbs
qnbs changed the base branch from feat/worker-v2-nlp-migration to main July 30, 2026 03:47
…leanup

# Conflicts:
#	packages/worker-bus/tests/workerBus.test.ts
#	services/ai/localNlpService.ts
#	services/duckdb/duckdbClient.ts
#	services/workerBusManager.ts
#	tests/unit/duckdbClient.test.ts
#	tests/unit/inferenceWorkerHandlerV2.test.ts
#	tests/unit/localNlpService.test.ts
#	tests/unit/workerBusManager.test.ts
#	workers/v2/duckdb.worker.ts
@codecov

codecov Bot commented Jul 30, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@qnbs
qnbs merged commit 5182f26 into main Jul 30, 2026
21 checks passed
@qnbs
qnbs deleted the feat/worker-v2-docs-cleanup branch July 30, 2026 04:21
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