Repository navigation
Cloudflare migration - #55
Merged
Merged
Conversation
…lare Computer - Complete architecture for POC implementation - Step-by-step implementation plan (5 phases, 10 days) - Code templates for all components - Testing and performance measurement strategy - Success criteria and next steps Closes: Migration planning for Cloudflare Computer Co-authored-by: AndreiDrang <AndreiDrang@users.noreply.github.com>
- Minimal setup instructions - Step-by-step deployment guide - Troubleshooting section - Performance measurement tips Related to Cloudflare Computer migration Co-authored-by: AndreiDrang <AndreiDrang@users.noreply.github.com>
- Add poc/ directory with full Cloudflare Worker implementation - Add src/index.js - Main Worker with webhook handling - Add src/lib/github.js - GitHub API client - Add src/lib/commands.js - Command parsing utilities - Add src/lib/handlers/help.js - Help command handler - Add src/lib/logging.js - Logging utilities - Add src/config/constants.js - Application constants - Add tests/test.js - Unit tests for POC - Add wrangler.toml - Cloudflare configuration - Add package.json - Project dependencies - Add README.md - Complete POC documentation POC implements only /zai help command to validate Cloudflare Computer architecture. Ready for deployment and performance testing. Related to Cloudflare Computer migration plan Co-authored-by: AndreiDrang <AndreiDrang@users.noreply.github.com>
- Complete summary of POC implementation - Performance expectations and metrics - Success criteria checklist - Next steps for full migration - All documentation ready for deployment Final commit for cloudflare-migration branch setup Co-authored-by: AndreiDrang <AndreiDrang@users.noreply.github.com>
Why:
* split the flat single-worker poc into a hybrid topology so heavy
commands (review, impact) run in their own worker lifetime, decoupled
from the ~10s GitHub webhook response limit
* add a shared library layer so both workers reuse one set of
command/auth/crypto/github/logging modules
What:
* replace poc/src (flat) with poc/workers/{shared,zai-main-worker,zai-heavy-worker}
* main worker: webhook sig check -> parse -> auth -> route; runs light
commands (help, describe) inline and delegates heavy commands to the
heavy worker via a service binding (env.HEAVY_WORKER) under ctx.waitUntil
* heavy worker: internal-token gate -> 202 ack -> ctx.waitUntil dispatch
* shared/crypto.js: Web Crypto HMAC-SHA256 (no nodejs_compat flag needed)
* heavy worker sets workers_dev=false (binding-only, no public ingress);
main worker gets a public route (zai-worker.tokenbel.info)
* migrate + expand tests to workers/tests/test.js (36 assertions)
Changes:
* poc/src/**, poc/wrangler.toml, poc/tests/test.js: removed (flat layout)
* poc/workers/shared/*.js: shared lib (6 modules)
* poc/workers/zai-main-worker/**: main worker (8 files)
* poc/workers/zai-heavy-worker/**: heavy worker (6 files)
* poc/workers/tests/test.js: migrated test suite
* poc/README.md: rewritten as hybrid architecture doc
* poc/package.json + package-lock.json: hybrid worker scripts + lockfile
Stats:
* 32 files changed, +1659/-1564 lines
Co-authored-by: pi <noreply@earendil-works.com>
Why: * give the hybrid workers a single entry point for deploy, dry-run, test and formatting so contributors don't memorize per-worker wrangler flags * enforce a consistent JS style via Prettier What: * Makefile: targets for dependencies, test, refactor-js (install + format), format-check, deploy-dry-run (heavy first, then main so the service binding resolves), deploy, per-worker dev/tail, and clean * .prettierrc.json: style matching the existing worker source * .prettierignore: exclude node_modules/.wrangler/dist * note: the format:js scripts + prettier devDep live in poc/package.json (committed with the restructure); this Makefile wires them up Changes: * poc/Makefile: added (tokenbel-wiki conventions: /bin/sh, .PHONY, tab recipes) * poc/.prettierrc.json: added * poc/.prettierignore: added Stats: * 3 files changed, +76 lines Co-authored-by: pi <noreply@earendil-works.com>
Why: * per-worker `wrangler secret put` secrets don't scale across two workers and can't be shared; one Cloudflare Secrets Store centralizes all four values What: * bind GITHUB_WEBHOOK_SECRET, GITHUB_TOKEN, ZAI_INTERNAL_TOKEN, ZAI_API_KEY in zai-main-worker (4) and zai-heavy-worker (3, no webhook secret) from store 629e5dd6594845a889e6ddabb26cc009 via [[secrets_store_secrets]] * store bindings expose plain env.* strings, so delegator/crypto/handlers keep reading env.GITHUB_TOKEN etc. unchanged — zero code changes * add per-worker .dev.vars.example for `wrangler dev`; gitignore the real .dev.vars plus node_modules, .wrangler, dist * document the binding table, deploy webhook URL, and roadmap in the README Changes: * workers/zai-main-worker/wrangler.toml: 4 secrets_store_secrets bindings * workers/zai-heavy-worker/wrangler.toml: 3 secrets_store_secrets bindings * workers/zai-main-worker/.dev.vars.example: local-dev dotenv template (4 keys) * workers/zai-heavy-worker/.dev.vars.example: local-dev dotenv template (3 keys) * .gitignore: ignore .dev.vars, node_modules/, .wrangler/, dist/ * README.md: secrets-store table, deploy webhook URL, roadmap note Stats: * 6 files changed * +114/-37 lines Co-authored-by: pi <noreply@earendil-works.com>
Why:
* Secrets Store bindings can arrive as string | {get()} | Promise at
runtime; passing them straight to crypto/encode stringified to
"[object Object]" and broke every webhook signature (401).
* GitHubClient parsed 204 No Content (collaborator check) as JSON,
throwing "Unexpected end of JSON input" (500) on the success path.
What:
* Add resolveSecretValue() to normalize any binding shape to a trimmed
string; resolve at every read site (webhook secret, GitHub token,
internal token).
* GitHubClient.request() short-circuits empty bodies (204) to null and
wraps JSON.parse to preserve the error.status contract.
* Tighten the heavy-worker internal-token gate to reject missing bindings.
Changes:
* shared/secrets.js: new resolveSecretValue() helper
* shared/auth.js: resolve GITHUB_TOKEN before constructing GitHubClient
* shared/github.js: empty-body + parse-error handling in request()
* zai-main-worker/src/index.js: resolve webhook secret + GitHub token
* zai-main-worker/src/delegator.js: resolve internal token inside send()
* zai-heavy-worker/src/index.js: resolve token gate + GitHub token
* README.md: document the binding-type ambiguity and resolution pattern
* tests/test.js: resolveSecretValue + 204 empty-body regression coverage
Stats:
* 8 files changed, +215/-17
Co-authored-by: pi <noreply@earendil-works.com>
Why: * Retire the @zai-bot mention and /zai-bot slash forms; "/zai" is now the single invocation prefix. * Credit the author in the help footer. What: * COMMAND_REGEX accepts only "/zai"; drop MENTION_REGEX and its parse branch (capture groups shifted to type=match[1], args=match[2]). * formatHelp(): remove @zai-bot usage notes; add an AndreiDrang credit linking to https://github.com/AndreiDrang. Changes: * shared/commands.js: /zai-only regex, mention branch removed, footer * tests/test.js: /zai-bot + @zai-bot now rejected; footer link asserted Stats: * 2 files changed, +13/-21 Co-authored-by: pi <noreply@earendil-works.com>
Why: * the bot is migrating from GitHub Actions to Cloudflare Workers (poc/workers) * the Action-based deployment workflow (uses: ./) is now obsolete What: * delete the legacy zai-code-bot.yml workflow (PR / issue_comment / pull_request_review_comment triggers are now handled by the Cloudflare Worker webhook) Changes: * .github/workflows/zai-code-bot.yml: removed Stats: * 1 file changed, -33 lines Co-authored-by: pi <noreply@earendil-works.com>
Why: * port the Vitest + v8-coverage stack from tbel/cf_workers (tb-bcse-securities-collector) for Workers-runtime-fidelity testing * the POC previously used a hand-rolled assert runner with no coverage gating What: * add vitest@^2.1.8, @vitest/coverage-v8@^2.1.8, vitest-environment-miniflare@^2.14.4 * wire npm test -> vitest run --coverage and add test:watch * add vitest.config.js (globals, miniflare env via inline environmentOptions, 80% thresholds on workers/shared + main-worker router) * add make test / make test-watch targets * ignore coverage/, .env, *.log, .DS_Store Changes: * package.json: devDeps + test/test:watch scripts * package-lock.json: lockfile for new deps * vitest.config.js: new config * Makefile: test/test-watch targets + help * .gitignore: coverage/, .env, *.log, .DS_Store Stats: * 5 files changed, +3019/-7 lines Co-authored-by: pi <noreply@earendil-works.com>
Why: * replace the hand-rolled assert runner with a real Vitest suite mirroring tb-bcse-securities-collector (one .test.js per shared module) * document the new test stack in the POC README What: * add 7 Vitest spec files using describe/it/expect + vi.spyOn(globalThis,'fetch') against the miniflare environment: auth, commands, crypto, github, logging, router, secrets * delete the old monolithic workers/tests/test.js runner * document the Vitest + v8 coverage stack in README (layout, dev, testing) Changes: * workers/tests/*.test.js (7 new): 99 tests, 100% stmts/lines/funcs, 97.9% branches * workers/tests/test.js: removed (replaced by the Vitest suite) * README.md: document the test stack Stats: * 9 files changed, +633/-261 lines Co-authored-by: pi <noreply@earendil-works.com>
Why: * the cloudflare migration is now implemented under poc/workers/; these planning artifacts (architecture sketches, quick-starts, starter code, reports) are obsolete and risk misleading future readers What: * delete the entire plans/ directory (10 files) Stats: * 10 files deleted * 6280 deletions Co-Authored-By: Claude <noreply@anthropic.com>
Why:
* GitHub webhooks time out in ~10s, but PR analysis must run longer. PR
events are now recorded durably in D1 and processed asynchronously via a
Queue, so the main worker acks fast and the heavy worker runs to completion
What:
* D1 storage adapters: deliveries, jobs (lease-based claims), artifacts,
config, keys, database helpers — D1 is the single source of truth
* migrations 0001 (schema) + 0002 (job leases, publication state machine)
* queue producer in main worker (job-enqueuer) + consumer in heavy worker
(queue.js) with a 3-attempt retry budget; terminal failures ack without a DLQ
* PR preview handler: bounded stats fetch, R2 manifest + result artifacts,
one live bot-owned comment via D1 publication lease, head-sha freshness check
* 5-min cron on main worker: recover expired leases, replay outbox, sweep
expired storage (30-day retention)
* shared comments/pr-preview/pr-stats helpers + github.js getPullRequest/getPrFiles
* wrangler bindings: BOT_DB, BOT_ARTIFACTS (bot-storage), BOT_CACHE (bot-cache),
BOT_JOBS (bot-jobs), R2_RETENTION_DAYS=30
Changes:
* shared/storage/{database,keys,artifacts,config,deliveries,jobs}.js: new adapters
* shared/{comments,pr-preview,pr-stats}.js: comment publication + rendering + stats
* shared/{constants,github}.js: markers/event types + PR REST methods
* main/src/{job-enqueuer,pr-events}.js: queue publisher + recovery, event extraction
* main/src/index.js: pull_request durable path + scheduled() cron sweep
* main/wrangler.toml: BOT_DB/ARTIFACTS/CACHE/JOBS bindings + cron trigger
* main/migrations/000{1,2}_*.sql: schema + hardening
* heavy/src/queue.js: claim -> run -> ack/retry/fail state machine
* heavy/handlers/pr-preview.js: durable preview job handler
* heavy/src/index.js + handlers/index.js + wrangler.toml: queue consumer wiring
* tests/{storage,storage-state,storage-runtime,github-storage,queue}.test.js
Stats:
* 27 files changed
* +2286/-12 lines
Co-Authored-By: Claude <noreply@anthropic.com>
Why: * the readme described the pre-storage v0.2 layout and referenced now-removed planning docs; it did not document the queue, cron, or durable PR-preview flow What: * rewrite as v0.3: durable PR-preview path as the primary flow (mermaid) * document the job lifecycle state machine and 3-attempt retry budget * document the 5-min cron self-healing sweep (lease/outbox/retention) * actualize the file tree, service bindings, and handler status table * remove all plan-file references Stats: * 1 file changed * +279/-160 lines Co-Authored-By: Claude <noreply@anthropic.com>
Why:
* ask/explain/describe make Z.ai LLM calls, so they cannot run inline in the
main worker within GitHub's ~10s webhook window — only help is truly light
(pure formatting, no API call). The old split would block the webhook once
real handlers landed
What:
* constants.js: LIGHT_COMMANDS=[help], HEAVY_COMMANDS=[ask,explain,describe,
review,impact]
* move describe out of the main (light) handler registry; only help remains
* add ask/explain/describe heavy stub handlers (payload interface) in the
heavy worker, matching the existing review/impact stub pattern
* mark ask/explain/describe as *(heavy)* in the help text
* update README file tree, routing examples, command-path sections, status
Changes:
* shared/constants.js: split arrays — help is the only light command
* shared/commands.js: help text heavy annotations
* zai-main-worker/src/handlers/index.js: drop describe case (help only)
* zai-main-worker/src/handlers/describe.js: deleted (orphaned light handler)
* zai-heavy-worker/src/handlers/{ask,explain,describe}.js: new heavy stubs
* zai-heavy-worker/src/handlers/index.js: register the 3 new heavy handlers
* README.md: reflect the corrected classification (v0.3.1)
Stats:
* 9 files changed
* +183/-72 lines
Co-Authored-By: Claude <noreply@anthropic.com>
Why:
* The poc/ workers implement a durable, multi-resource architecture (D1, R2,
Queue, KV, two-worker split) whose business rules and contracts were only
implicit in source code and the README. An OKF bundle makes the domain
knowledge explicit, navigable, and maintainable.
What:
* Create a 22-file OKF bundle at repo root (okf/): root index (okf_version 0.1),
update log, 6 directory indexes, and 14 concept documents across architecture,
workflows, state, rules, contracts, and datasets.
* Wire the bundle into AGENTS.md via the canonical managed OKF guidance block.
Concepts documented:
* architecture: two-worker-split, storage-authority-model
* workflows: webhook-ingress, command-routing, pr-preview-pipeline,
cron-self-healing
* state: job-lifecycle (bounded leases), comment-publication (one-live-comment)
* rules: retry-budget (3 attempts, no DLQ), r2-retention (30-day),
authorization (collaborator gate)
* contracts: queue-message (job-id only), transactional-outbox
* datasets: d1-storage-schema (9 tables, migrations 0001+0002)
All source_paths verified to exist on disk; all internal cross-links resolve.
Changes:
* AGENTS.md: append managed OKF guidance block
* okf/index.md, okf/log.md: bundle root and changelog
* okf/architecture/{index,two-worker-split,storage-authority-model}.md
* okf/workflows/{index,webhook-ingress,command-routing,pr-preview-pipeline,cron-self-healing}.md
* okf/state/{index,job-lifecycle,comment-publication}.md
* okf/rules/{index,retry-budget,r2-retention,authorization}.md
* okf/contracts/{index,queue-message,transactional-outbox}.md
* okf/datasets/{index,d1-storage-schema}.md
Stats:
* 23 files changed
* +additions lines
Co-authored-by: Claude <noreply@anthropic.com>
Why: * bot comments had inconsistent or missing attribution — help carried the full footer, pr-preview said "Powered by Z.ai", stubs and errors had none What: * add a shared BOT_FOOTER constant and apply it uniformly before each message's hidden marker so every bot comment ends with identical attribution Changes: * shared/constants.js: add BOT_FOOTER constant * shared/commands.js: use BOT_FOOTER in formatHelp and formatCommandNotAvailable * heavy handlers (ask/describe/explain/impact/review): add footer to stub notices * heavy index.js: add footer to the heavy-command failure comment * main help.js: add footer to the /zai help error comment * main index.js: add footer to the unauthorized-command comment Stats: * 10 files changed, +29/-18 Co-authored-by: pi <noreply@earendil-works.com>
Why: * the auto-preview brief only needs PR identity; per-file stats are the job of the heavy /zai review pipeline, and computing them cost up to 30 paginated getPrFiles calls per large PR What: * render a metadata-only comment (repo/pr/title/author/head) and adopt the shared footer; stop computing, rendering, or persisting per-file data Changes: * shared/pr-preview.js: rewrite renderPrPreview to metadata-only, use BOT_FOOTER * shared/pr-stats.js: deleted (fetchPrStats + MAX_PR_FILES_API_LIMIT, no consumers) * shared/storage/keys.js: retire unused prFilesArtifactKey * heavy handlers/pr-preview.js: drop fetchPrStats call and the per-file R2 artifact * tests/storage.test.js: assert metadata-only rendering, drop the pagination test Stats: * 5 files changed, +26/-139 Co-authored-by: pi <noreply@earendil-works.com>
Why: * lock in the dedup contract — a PR gets exactly one preview comment, and new commits (synchronize) update it instead of creating duplicates What: * exercise handlePrPreviewJob across opened, synchronize, and stale-skip paths and assert update-not-create via the shared comment marker and the presence of the footer on every published body Changes: * tests/pr-preview-sync.test.js: add regression suite (4 cases) Stats: * 1 file changed, +165 Co-authored-by: pi <noreply@earendil-works.com>
Why: * the bundle still described the old per-file stats preview and had no concept for the shared BOT_FOOTER applied to every bot comment What: * add a unified bot comment footer rule concept and rework the PR-preview pipeline plus cross-referenced concepts to the metadata-only brief; record the refresh in the log Changes: * rules/comment-footer.md: new Business Rule for the shared BOT_FOOTER * workflows/pr-preview-pipeline.md: metadata-only steps, drop file-manifest + pr-stats source, fix failure step range * contracts/queue-message.md: rendered results, not manifests * rules/r2-retention.md: preview results, not file manifests * architecture/storage-authority-model.md: R2 role is rendered preview results * state/comment-publication.md: add Preview body section + footer link * rules/index.md, index.md: register the footer concept * log.md: record footer create + metadata-only updates Stats: * 9 files changed, +83/-17 Co-authored-by: pi <noreply@earendil-works.com>
Why:
* the README still described the pr-stats / file-manifest flow and the old
12-file/130-test suite, and did not mention the unified footer
What:
* update the file tree, the preview flow diagram, and the storage/GitHub tables
to the metadata-only brief, and bump the doc version
Changes:
* README.md: drop pr-stats.js + GET /pulls/{n}/files; single result artifact in the mermaid; metadata-only tree descs; 13 files/133 tests; version 0.3.2
Stats:
* 1 file changed, +19/-21
Co-authored-by: pi <noreply@earendil-works.com>
Why: * the PR preview posted a NEW comment on every synchronize instead of updating the previous one What: * findMarkerComment now accepts the exact comment we previously published (matched by the stored github_comment_id) regardless of author type, so the update path resolves for both GitHub Apps and PAT-owned bots even when GITHUB_BOT_LOGIN is not configured; the Bot/login filter remains for the marker-only fallback * add a real upsertComment regression suite (in-memory D1 fake, comments.js NOT mocked) covering PAT and App identity, fresh-create, and a stray-comment non-adoption guard Changes: * shared/comments.js: accept stored comment id in findMarkerComment filter * tests/comments-upsert.test.js: real-path update-vs-create regression tests Stats: * 2 files changed, +284/-0 Co-authored-by: pi <noreply@earendil-works.com>
Why: * editing a PR title sent action=edited, which the preview gate did not accept, so the title change triggered no refresh What: * accept the pull_request "edited" action but gate it on changes.title, since the preview is metadata-only (repository/PR/title/author/head) and body/base edits don't affect it; this avoids wasteful re-renders on unrelated edits Changes: * zai-main-worker/src/pr-events.js: add "edited" to SUPPORTED_PR_ACTIONS and gate it on changes.title in isSupportedPullRequestEvent(event, action, payload) * zai-main-worker/src/index.js: pass payload to the gate at the call site * tests/pr-events.test.js: cover edited+title vs body/base/no-change, plus extractPullRequestEvent title extraction Stats: * 3 files changed, +124/-3 Co-authored-by: pi <noreply@earendil-works.com>
Why: * closing a PR sent action=closed, which the preview gate rejected, so nothing happened and the PR state in D1 never advanced to closed What: * accept pull_request "closed", persist state=closed + closed_by (the webhook sender) on pull_requests, and post an idempotent "PR closed by @x" lifecycle comment that skips the supersede guard and leaves the preview untouched Changes: * pr-events.js: gate "closed"; extractPullRequestEvent adds closedBy (sender) * storage/deliveries.js: UPSERT closed_by via COALESCE; JOB_SELECT += p.state, p.closed_by * shared/constants.js: PR_CLOSED_MARKER * shared/pr-preview.js: renderPrClosed * handlers/pr-preview.js: closed branch -> publishClosedComment (no getPullRequest) * migrations/0003_pr_closed_by.sql: ALTER TABLE pull_requests ADD closed_by * tests: closed gate + closedBy extraction, renderPrClosed, handler closed branch Stats: * 9 files changed, +277/-12 Co-authored-by: pi <noreply@earendil-works.com>
Why: * the OKF bundle trailed the code: it still listed only opened/reopened/ synchronize/ready_for_review and had no concept of the close branch or the new closed_by column What: * add the closed lifecycle to the preview pipeline, the pr_closed comment kind, and migration 0003 (closed_by) to the schema concept; log the change Changes: * workflows/pr-preview-pipeline.md: trigger actions += edited/closed; Closed lifecycle section * state/comment-publication.md: comment kinds table (pr_preview, pr_closed) * datasets/d1-storage-schema.md: migrations 0001-0003, pull_requests.closed_by, 0003 section * log.md: 2026-08-07 entry for closed lifecycle + edited action Stats: * 4 files changed Co-authored-by: pi <noreply@earendil-works.com>
|
Error: No file path provided. Usage: /zai review |
Why: * progressive retry timeouts guaranteed that a slow reasoning response would be aborted even sooner after its first timeout, so review runs with already-retrieved evidence failed instead of finishing What: * give gathering requests a fixed 90-second timeout with at most two total attempts, bounded by the deadline before the 40-second finalization reserve * enter a single 35-second no-tools final answer when a gathering request times out after at least one successful tool call (new llm_timeout finalization reason) instead of failing the run * split the runner clock into gathering and hard deadlines so each stage gets its own request timeout, attempt limit, and retry policy * honor Retry-After on retryable errors; direct calls keep progressive timeouts unchanged * propagate httpStatus, providerRequestId, requestBytes, and retryAfterMs through per-attempt logs and failure records * update the agent runner OKF contract, workflow, context-tools notes, and log Changes: * poc/workers/shared/agent/limits.js: raise maxLlmRequestDurationMs to 90s, add maxLlmAttempts and finalLlmRequestDurationMs * poc/workers/shared/agent/runner.js: gathering/finalization deadlines, timeout-triggered finalization, stage-aware chat options * poc/workers/shared/zai-client.js: fixed per-attempt timeout, maxAttempts/retryTimeouts, Retry-After parsing, provider request ids, request-byte telemetry * poc/workers/shared/llm-command-runner.js: surface providerHttpStatus and providerRequestId on failures * poc/workers/zai-heavy-worker/src/handlers/review.js: apply the new limits to the review profile * poc/workers/tests/agent-runner.test.js: cover timeout-to-finalization flow and the limit snapshot * poc/workers/tests/zai-client.test.js: cover long timeouts, timeout retry gating, and Retry-After attempts * okf/contracts/agent-runner.md, okf/contracts/agent-context-tools.md, okf/workflows/llm-command-execution.md, okf/log.md: document the stage-aware budgets and telemetry Stats: * 11 files changed * +359/-64 lines Co-Authored-By: Claude <noreply@anthropic.com>
Owner
Author
|
/zai review |
|
🤖 Reviewing |
|
Error: No file path provided. Usage: /zai review |
…, and single route
Why: * pi-lens reformatted the files touched by the review-fix series; leaving the drift uncommitted would mix formatting into the next change What: * prettier-style reflow only (argument wrapping, member-chain breaks) * no behavior change — affected specs re-run green before committing Changes: * poc/workers/shared/llm-command-runner.js: wrap publishNotice/publishResult call arguments onto one line or indented blocks * poc/workers/tests/handlers-review-llm.test.js: wrap objectContaining assertion fields * poc/workers/tests/job-enqueuer.test.js: break the mockRejectedValueOnce chain * poc/workers/tests/storage-state.test.js: break vi.fn() chains in the wait-out test Stats: * 4 files changed * +51/-33 lines Co-Authored-By: Claude <noreply@anthropic.com>
Deploying with
|
| Status | Name | Latest Commit | Updated (UTC) |
|---|---|---|---|
| ❌ Deployment failed View logs |
zai-main-worker | d5e6700 | Aug 23 2026, 10:20 PM |
Why: * the shared branch threshold (83%) has been failing on pre-existing debt (llm-command-runner 60%, context-service 69%, zai-client 72% branches), so every npm test run exited red regardless of the change under review What: * both coverage globs now enforce a uniform 70% floor across lines, branches, functions, and statements * gate is green again at current actuals (shared ~81% branches, main-worker ~94%) with ratchet milestones documented in-config Changes: * poc/vitest.config.js: thresholds 90/90/83/90 → 70/70/70/70 per glob, comment rewritten Stats: * 1 file changed * +12/-10 lines Co-Authored-By: Claude <noreply@anthropic.com>
…ailure notices Why: * llm-command-runner was the worst branch-coverage file in shared/ (60.5%) because only the agent-mode review handler exercised it What: * new dedicated spec drives runLlmCommand directly with the context service and agent runner mocked: non-agent completion, live-diff fallback and no-diff edges, stored PR-summary head matching, error-category fallbacks, agent failure shaping, rich/zeroed review metadata, and buildFailureNotice tails (max_retrieved_bytes, unknown categories, attempt counts) * ratchet shared branch floor 70 -> 85 (actual 86.07%) Stats: * 22 new tests, 414 total green * llm-command-runner: 64 -> 8 uncovered branches, 0 uncovered statements * shared rollup: 80.90% -> 86.07% branches Co-Authored-By: Claude <noreply@anthropic.com>
Why: * context-service was 69% branches — only the happy paths reached it through the review handler's snapshot reads What: * new spec drives the real service against a fake R2 snapshot: missing/stale manifests, path-prefix validation and truncation, diff result states (not_found, unavailable reasons, artifact_missing, truncation), getFile guards (invalid path/revision, source unconfigured/incomplete, not-found, hard and soft limits, null content), file ranges, description/commits/ comments clamps, combined-diff omission rules, and snapshot-slice metadata fallbacks Stats: * 45 new tests, 450 total green * context-service: 40 -> ~14 uncovered branches, 52 -> ~3 statements * shared rollup: 86.07% -> 88.21% branches Co-Authored-By: Claude <noreply@anthropic.com>
…arsing Why: * zai-client sat at 72% branches — timeout aborts, protocol failures, and deadline math were reachable only through integration paths What: * transport edges: network-failure attempt reports, unreadable error bodies, missing assistant messages, tool-call-only responses via call(), the defensive negative-maxRetries tail, global fetch/default sleeper defaults * chat deadlines: real AbortController timeout via fake timers, maxAttempts clamp, past-deadline result, retry-that-cannot-fit timeout, plain-500 retry * parsing: provider request-id header variants, garbage usage normalization, numeric/HTTP-date/negative/garbage retry-after values, sanitizer fallback Stats: * 13 new tests, 465 total green * zai-client: 33 -> 7 uncovered branches * shared rollup: 88.21% -> 90.48% branches * ratchet both coverage globs to 90/90/90/90 Co-Authored-By: Claude <noreply@anthropic.com>
Why: * agent/runner (82%) and llm-context (85%) held protocol-validation and renderer branches reachable only through crafted model responses What: * new agent-runner spec with a scripted chat mock: construction guards, non-array messages, non-object responses, errorless failures, onAttempt accounting, malformed assistant messages and tool calls, per-turn budget results, default tool arguments, safe error mapping, normalized and raw-key duplicate detection, and all finalization paths (retrieval budget, llm timeout, finalization-time tool calls, hard deadline) * llm-context defensive edges: missing slices, whitespace diffs, layout-disabled sections, degenerate commits, anonymous/unlocated comments, statusless files, conversation fallback topic Stats: * 26 new tests, 469 total green * agent/runner: 25 -> 16 uncovered branches; llm-context: 18 -> 5 * shared rollup: 90.48% -> 92.34% branches Co-Authored-By: Claude <noreply@anthropic.com>
Why: * the storage/github/misc sweep was the last concentration of uncovered branches after the runner, context, and client paydowns What: * storage: list-limit clamps and empty results, outbox publish without a producer binding, delivery id validation, UNIQUE-race winner rethrow and reconciliation, unreadable created jobs, command-job delivery ownership, kind-less delivery lookup, non-string PR heads, defensive changedRows/ requireBinding/run shapes * github: non-array pagination pages, null PR bodies, maxComments clamping, non-array comment slices, ref-less file content * prompts: describe/pr-summary no-context fallback notes * ratchet: shared floor 90 -> 93 branches / 95 others (actual 94.7% branches) Stats: * 26 new tests, 507 total green * shared rollup: 92.87% -> 94.70% branches Co-Authored-By: Claude <noreply@anthropic.com>
Why: * the vitest text table scrolls past the fold on large suites, so a run's headline numbers (% Stmts / % Branch / % Funcs / % Lines) were easy to miss and had to be recomputed by hand for reporting What: * new poc/scripts/coverage-averages.js summarizes the istanbul JSON the suite already emits: per-column means across instrumented files, overall and per threshold glob (shared / main worker), with the file count behind each mean and n/a for metrics a file set cannot measure * make test now pipes npm test into that summarizer, so every run ends with the averages; a failing suite still aborts before any summary is printed Co-Authored-By: Claude <noreply@anthropic.com>
Pure prettier reflow of the specs added during the coverage paydown (long expect() lines rewrapped to the 100-column limit); no behavior change. Tests rerun green on the reformatted tree. Co-Authored-By: Claude <noreply@anthropic.com>
- git mv poc/workers -> src (relative imports unchanged) - package.json/vitest.config.js/Makefile/prettier/scripts -> repo root - rewire npm scripts, vitest include/threshold globs, Makefile, and CI (no more working-directory: poc; codecov path now ./coverage/lcov.info) - src/AGENTS.md + per-worker AGENTS.md path updates - drop POC wording from code comments; fold poc/.gitignore into root
Root README absorbs the workers README: command-flow sequence diagram, PR-context/pr_summary chain with the R2 v2/prs/ layout, bindings detail, prompt regeneration, and the src/ tree. The src/ copy is removed.
- ARCHITECTURE.md: drop POC framing and leftover-dir note, rewrite all code locations, code map, evidence paths, and the docs-strategy list for the merged README and src/AGENTS.md - AGENTS.md: new repository tree, important-files table, context routing, validation from the repo root - CONTRIBUTING.md: root-level workflow; README+ARCHITECTURE doc rule
- rewrite source_paths and body references across all 19 concepts (poc/workers/... -> src/..., poc/README.md -> README.md) - retitle bundle index away from 'Workers POC' - append 2026-08-24 migration entry to okf/log.md; historical entries preserved verbatim - validated: frontmatter, types, source-path existence, bundle-relative links, index/directory parity, duplicate resources — all pass
Why: * the markdown formatter normalized tables, quotes, and comment-block spacing in files touched by the src/ migration What: * pad table columns to aligned widths in AGENTS.md and the okf two-worker-split concept * add blank lines around the okf-knowledge marker block in AGENTS.md * switch okf_version frontmatter quotes from double to single Changes: * AGENTS.md: aligned important-files table, spaced marker block * okf/index.md: single-quote okf_version value * okf/architecture/two-worker-split.md: aligned workers table Stats: * 3 files changed * +18/-16 lines Co-Authored-By: Claude <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Cloudflare migration
Migrates zai-code-bot from a GitHub Actions–hosted bot to a Cloudflare Workers architecture. The new implementation uses a two-worker hybrid topology with durable D1/Queue/R2/KV storage so webhook deliveries ack fast while LLM-heavy commands and PR analysis run to completion asynchronously — verified live on this PR (see
/zai help,/zai review,/zai describeruns in the conversation). The legacy Action bot and its workflows are removed.✨ Features & Enhancements
poc/workers/):zai-main-worker— public webhook ingress: HMAC-SHA256 signature verification (Web Crypto, nonodejs_compat), command parsing, collaborator authorization, and routing; light commands (help) run inline.zai-heavy-worker— binding-only worker (no public ingress) reached via a service binding; internal-token gate, then async dispatch underctx.waitUntil.0001–0003).opened/synchronize/title-edited(and closed-state tracking), with exactly one live bot-owned comment updated in place, plus an idempotent "PR closed by @x" lifecycle comment./zai review(full-context review),/zai describe, and PR-summary/PR-context jobs, powered by a Z.ai client, an agentic runner with a bounded tool loop, a PR context reader with head-SHA freshness checks, and dedicated prompt modules./zaiis the single command prefix (@zai-botmention and/zai-botforms retired), with author credit in the help footer and a standardizedBOT_FOOTERapplied to every bot comment.🐛 Bug Fixes
string | {get()} | Promise) viaresolveSecretValue()at every read site — raw bindings stringified to"[object Object]"and broke every webhook signature with 401.204 No Contentbodies in the GitHub client (collaborator check) instead of failingJSON.parseon the success path.synchronizeinstead of posting duplicates; comment lookup matches the storedgithub_comment_idso updates work for both GitHub App and PAT identities.ask/explain/describeas heavy commands (they make LLM calls and cannot fit GitHub's ~10s webhook window) — onlyhelpremains light.🔨 Refactoring & Chore
src/tree (handlers for ask/explain/describe/help/impact/review/scheduled, API, context, continuity, logging) and thezai-code-bot.yml,zai-agents-init-example.yml, andzai-agents-update.ymlworkflows.plans/) now that the implementation exists underpoc/workers/.okf/) documenting the architecture, workflows (webhook ingress, command routing, PR pipelines, LLM execution, cron self-healing), state machines (job lifecycle, comment publication), business rules (retry budget, R2 retention, authorization), contracts, and the D1 schema..agents/skills/.⚙️ Infrastructure & Tooling
poc/with coverage frompoc/coverage/lcov.info; the legacy build and dist-drift jobs are replaced by a workers dry-run deploy of both workers.Makefilesingle entry point for install, test, format, and deploy (heavy worker deploys first so the service binding resolves), plus Prettier configuration.[[secrets_store_secrets]], with per-worker.dev.vars.exampletemplates for local dev.