feat(cli): make the automation surface machine-readable - #361
Conversation
`lunora doctor` was the only project-preflight command with no options and
no identifier on its findings, so an agent or CI job had to scrape English
prose off stderr to learn what was wrong — and the prose is not a contract.
Every finding now carries a stable kebab-case `code` drawn from a single
`DOCTOR_CODES` const, and `--format json` emits the findings as one JSON
document on stdout (`{ ok, code, summary, findings }`) with the human report
routed to stderr, the same envelope `deploy` and `verify` already use. Exit
codes and the default pretty output are unchanged. `pass`-level findings are
included so the document describes everything that was checked, not only what
failed.
Adds a `cli-shadowed` check: a globally-installed `lunora` running against a
project with its own pinned install makes every other finding describe a
project this CLI may be the wrong version for, and no existing check can see
it — the version-skew check reads the manifest, which is exactly the file the
shadowing binary ignores. The comparison asks whether the running module lives
inside the project's installed CLI package rather than comparing bin paths,
because pnpm writes `node_modules/.bin/*` as a shell shim rather than a
symlink, and path equality would warn on every pnpm project.
The code table in the CLI docs is asserted against `DOCTOR_CODES` by a test,
so a new or renamed code cannot ship undocumented.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KBeSX2o4sTCPjVDRDWkVQG
`lunora deploy --format json` returned a document that could not tell a
caller where the thing it just deployed lives, and `--preview` told the
operator to "see the preview URL in the wrangler output above" — a
machine-readable command handing its answer back to a human to read with
their eyes. The URL parser already existed; the gate that enabled it was
off in exactly those two cases.
- `DeployCommandResult.deployment` carries `{ deployedAt, dryRun, env?,
preview, url?, workerName? }` on every run that reached wrangler. No
version id: the pinned wrangler has no structured deploy output, and
the id only appears in prose.
- Capture wrangler's stdout on every publishing run — with
`captureStdoutSilently` in json mode (replayed to stderr afterwards),
so stdout stays exactly one JSON document.
- Re-check the link on every real deploy instead of only the first: a
changed URL used to leave a stale link that `run`/`logs`/`--migrate`
silently targeted. An existing link is never rewritten — a mismatch
warns and names the `lunora link --url` to run. `--temporary` writes
no link (that account is gone in an hour).
- New `--health-check`: after a live deploy, probe
`/_lunora/health/ready` (falling back to the aggregate route), 5
attempts 2s apart, before any `--migrate`. A red probe exits non-zero
and says the deploy succeeded and the probe did not.
- Extract the probe to `util/health-probe.ts`; `verify --health-url`
now shares it, unchanged in behaviour.
BREAKING CHANGE: `autoLinkFromDeployOutput` takes the parsed `url`
instead of raw wrangler `output`, and `HealthFetch` moved from
`commands/verify/handler` to `util/health-probe`.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KBeSX2o4sTCPjVDRDWkVQG
Nothing measured how large the Worker a user deploys is, so the first signal of a size regression would have been Cloudflare rejecting someone's deploy for a dependency added weeks earlier. `lunora build` now reports what it wrote — raw and gzipped, counting only the files that are uploaded (the sourcemap and the esbuild metafile in the same out-dir are not) — as a line of pretty output and a `bundle` field in the `--format json` document. Measuring never changes the exit code. An out-dir with nothing uploadable in it warns instead of reporting 0 bytes, since a silent zero is what a changed wrangler layout looks like. `scripts/check-worker-size.js` builds `templates/standalone` against the workspace and fails when it exceeds the ceiling committed in `worker-size.json` (422,840 B gzipped + a 50 KiB allowance), naming the delta and pointing at `pnpm run worker-size:update`. It runs as its own `worker-size` job in test.yml, not from postinstall. BREAKING CHANGE: `lunora build --format json` now prints the build result rather than the deploy result — same fields, plus `bundle`. Measured for the baseline: 1684.9 KiB raw / 412.9 KiB gzipped, against Cloudflare's 3 MB (Free) and 10 MB (Paid) compressed script limits.
An agent could call a deployment but not see what happened: no logs, no grouped errors, no advisories, no query insights, no migration status. Every one already existed as an admin RPC backing the Studio and the CLI; only the MCP surface over them was missing. Adds five read tools — lunora_get_logs, lunora_get_issues, lunora_get_advisories, lunora_get_query_insights, lunora_get_migration_status — as a third tier. They are read-only but PRIVILEGED (production log lines and error messages reach the model's provider), so they are exposed only when an admin token resolved: omitted from ListTools without one, and refused at dispatch, the same omit-don't-refuse rule the write gate uses. The tier is independent of --allow-writes, which is about changing data, not reading operational data. In the composed local server the advertised list is a build-time snapshot of the resolved deployment's token (fail-closed), while dispatch re-checks the live one. Op paths come from ADMIN_FUNCTIONS rather than hand-written "__lunora_admin__:" literals, so a renamed op cannot ship a 404 to one consumer and not another; @lunora/shard-engine becomes a dependency for that constant. The reads go through client.query, since these ops ride the ordinary /_lunora/rpc envelope — the bearer, error envelope and wire decode are all the client's existing behaviour. Tool results now carry structuredContent described by each tool's outputSchema, alongside the existing text block so clients on an MCP revision older than 2025-06-18 are unaffected. structuredContent is serialized by the transport, so it goes through the same bigint -> string and bytes -> base64 mapping the text block already used; an unmapped bigint there would fail the whole response rather than one field. BREAKING CHANGE: toolDefinitions and callTool take an additional hasAdminToken argument (defaulting to false, so existing callers keep the read-only surface they had). Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KBeSX2o4sTCPjVDRDWkVQG
All four wave-20 plans shipped. Per the plans/ convention the plan files are removed once complete; the index carries what shipped, what each plan got wrong, and the follow-ups the wave opened. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KBeSX2o4sTCPjVDRDWkVQG
✅ Deploy Preview for lunorash ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Thank you for following the naming conventions! 🙏 |
|
Thank you for confirming the Contributor License Agreement! 🙏 |
|
Warning Review limit reached
Next review available in: 1 minute You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. How can I continue?After more reviews become available, a review can be triggered using the 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 configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
WalkthroughThe CLI adds deploy health checks, deployment metadata, URL linking updates, and structured doctor output. MCP adds admin-token-gated observability tools. CI adds a Worker size budget check and related documentation. ChangesCLI deployment flow
CLI doctor diagnostics
MCP observability tools
Worker size budget
Estimated code review effort: 5 (Critical) | ~120 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🧹 Nitpick comments (2)
packages/cli/docs/index.mdx (1)
184-185: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRemove the blank line before this bullet.
Line 183 is blank and separates this bullet from the list above it. The list stays intact, but Markdown renders it as a loose list, so this item gets extra vertical spacing that the other items do not have.
🤖 Prompt for 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. In `@packages/cli/docs/index.mdx` around lines 184 - 185, Remove the blank line immediately before the “CLI shadowing” bullet so it remains contiguous with the preceding Markdown list and renders consistently with the other items.packages/cli/src/commands/verify/handler.ts (1)
96-99: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueUse
probe.urlin the success message.
probeHealthreturns the URL the verdict came from. Line 99 rebuilds the same URL withjoinHealthUrl. The two agree only whileverifykeeps the defaultpaths. Read the value the probe reports instead, and thejoinHealthUrlimport can be dropped.♻️ Proposed refactor
- logger.success(`verify: health probe ok (${joinHealthUrl(options.healthUrl)})`); + logger.success(`verify: health probe ok (${probe.url})`);Then update the import at line 14:
-import { joinHealthUrl, probeHealth } from "../../util/health-probe"; +import { probeHealth } from "../../util/health-probe";🤖 Prompt for 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. In `@packages/cli/src/commands/verify/handler.ts` around lines 96 - 99, Update the success message in the verify handler to use the URL returned by probeHealth via probe.url instead of rebuilding it with joinHealthUrl, and remove the now-unused joinHealthUrl import.
🤖 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 @.github/workflows/test.yml:
- Around line 318-321: Add the worker-size-related paths
scripts/check-worker-size.js, worker-size.json, and .github/workflows/test.yml
to an existing packages/templates filter in .github/file-filters.yml, or create
a dedicated size-specific filter and wire it into the worker-size job’s if
condition so changes to any of these files run the gate.
In `@packages/cli/docs/index.mdx`:
- Around line 365-373: Update the CI shell snippet around the lunora deploy
command to enable strict failure handling with set -euo pipefail, then validate
the extracted deployment URL before invoking curl. Ensure a failed deploy or
missing/invalid deployment.url causes the pipeline to stop at the deploy/URL
validation step rather than requesting a null smoke-test URL.
- Around line 201-219: Make the JSON example self-consistent by changing its
fence to jsonc and adding a trailing comment entry in the findings array
indicating that the pass- and info-level findings are omitted, matching the
truncated deploy example pattern while preserving the summary counts.
In `@packages/cli/src/util/health-probe.ts`:
- Around line 105-125: Update the default fetch implementation in probeHealth to
enforce a per-attempt timeout using AbortSignal.timeout, while preserving any
caller-provided fetchImpl unchanged. Pass the timeout signal through the global
fetch call and use the existing probe timeout configuration or an appropriate
bounded duration so stalled requests abort and continue through probeOnce’s
transport-error handling.
In `@packages/mcp/README.md`:
- Around line 84-88: Update the README tool-parameter description around
lunora_get_migration_status to exclude migration status from the statement that
tools accept a limit. Document that migration status accepts only shardKey and
intentionally returns all migrations, while preserving the existing limit and
per-shard behavior for the other tools.
In `@scripts/check-worker-size.js`:
- Around line 143-169: Update scripts/check-worker-size.js lines 143-169 to add
an independent hard-failure check for bundle.rawBytes against Cloudflare’s 64 MB
pre-compression limit, without relying on the gzip ceiling check. Update
apps/docs/src/content/docs/deployment.mdx lines 150-169 to document the 64 MB
raw limit and instruct users to check both raw and gzip measurements.
---
Nitpick comments:
In `@packages/cli/docs/index.mdx`:
- Around line 184-185: Remove the blank line immediately before the “CLI
shadowing” bullet so it remains contiguous with the preceding Markdown list and
renders consistently with the other items.
In `@packages/cli/src/commands/verify/handler.ts`:
- Around line 96-99: Update the success message in the verify handler to use the
URL returned by probeHealth via probe.url instead of rebuilding it with
joinHealthUrl, and remove the now-unused joinHealthUrl import.
🪄 Autofix
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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: f8c928c5-a310-463d-83ef-b3b4d13dc67f
⛔ Files ignored due to path filters (17)
api-snapshots/cli.api.mdis excluded by none and included by noneapi-snapshots/mcp.api.mdis excluded by none and included by nonepackage.jsonis excluded by none and included by nonepackages/cli/__tests__/commands/build.test.tsis excluded by!**/__tests__/**,!**/*.test.tsand included bypackages/**packages/cli/__tests__/commands/deploy.test.tsis excluded by!**/__tests__/**,!**/*.test.tsand included bypackages/**packages/cli/__tests__/commands/doctor.test.tsis excluded by!**/__tests__/**,!**/*.test.tsand included bypackages/**packages/cli/__tests__/util/auto-link.test.tsis excluded by!**/__tests__/**,!**/*.test.tsand included bypackages/**packages/cli/__tests__/util/health-probe.test.tsis excluded by!**/__tests__/**,!**/*.test.tsand included bypackages/**packages/cli/src/commands/build/bundle-size.tsis excluded by!**/build/**and included bypackages/**packages/cli/src/commands/build/handler.tsis excluded by!**/build/**and included bypackages/**packages/mcp/__tests__/local.test.tsis excluded by!**/__tests__/**,!**/*.test.tsand included bypackages/**packages/mcp/__tests__/observability-tools.test.tsis excluded by!**/__tests__/**,!**/*.test.tsand included bypackages/**packages/mcp/__tests__/server.test.tsis excluded by!**/__tests__/**,!**/*.test.tsand included bypackages/**packages/mcp/__tests__/tools.test.tsis excluded by!**/__tests__/**,!**/*.test.tsand included bypackages/**plans/README.mdis excluded by none and included by nonepnpm-lock.yamlis excluded by!**/pnpm-lock.yamland included by noneworker-size.jsonis excluded by none and included by none
📒 Files selected for processing (23)
.github/workflows/test.ymlapps/docs/src/content/docs/deployment.mdxpackages/cli/docs/index.mdxpackages/cli/src/commands/deploy/handler.tspackages/cli/src/commands/deploy/index.tspackages/cli/src/commands/doctor/handler.tspackages/cli/src/commands/doctor/index.tspackages/cli/src/commands/mcp/index.tspackages/cli/src/commands/verify/handler.tspackages/cli/src/index.tspackages/cli/src/util/auto-link.tspackages/cli/src/util/deploy-summary.tspackages/cli/src/util/health-probe.tspackages/mcp/README.mdpackages/mcp/package.jsonpackages/mcp/src/index.tspackages/mcp/src/local.tspackages/mcp/src/observability-tools.tspackages/mcp/src/server.tspackages/mcp/src/tool-result.tspackages/mcp/src/tool-types.tspackages/mcp/src/tools.tsscripts/check-worker-size.js
Review fixes on the wave-20 branch. - health-probe: Node's `fetch` has no timeout, so a worker that accepts the connection and goes quiet would hang `deploy --health-check` until CI's job timeout killed it — defeating the fixed attempt budget. Each attempt now carries an `AbortSignal.timeout`; an injected `fetchImpl` still owns its own deadline. - worker-size: the gate was reachable only via the `packages`/`templates` filters, so a PR touching just the checker, the baseline, or the job skipped it — the gate could be switched off by editing the gate. Adds a `worker_size` filter, the matching `files-changed` output, and the `if` clause. - worker-size: Cloudflare enforces a 64 MB pre-compression limit as well as the per-plan gzip one. A bundle that compresses unusually well can pass a gzip baseline check and still be rejected at upload, so the raw size gets its own absolute check. Documented both limits. - docs: the doctor JSON example counted three findings and showed one; the CI snippet had no exit-code guard, so a failed deploy sent `curl` at `null/api/smoke` and failed at the wrong step. - mcp: `lunora_get_migration_status` takes no `limit`, unlike the other four. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KBeSX2o4sTCPjVDRDWkVQG
Every other consumer moved to @lunora/shard-engine@1.0.0-alpha.16 in the release; this pin was left at alpha.15 because the dependency did not exist when the release ran. The workspace `overrides:` block forces `workspace:*` locally, so the stale pin is invisible in the repo — but it is what the published manifest would carry. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KBeSX2o4sTCPjVDRDWkVQG
|
All six review comments addressed in d5ac5fb, plus one thing the
64 MB pre-compression limit. Correct, and I verified it against the Cloudflare limits page rather than taking it on trust: 3 MB Free / 10 MB Paid after gzip, and 64 MB before compression on both plans. The gzip check is a baseline comparison, so it cannot see a bundle that compresses unusually well — a large generated table is tiny gzipped and enormous raw, passes the gate, and is rejected at upload. Raw now gets its own absolute check with a message saying Doctor JSON example, CI snippet, MCP README. All three fixed as described — Separately: merging Verification on the merged tree: CLI 1204/1204, MCP 181/181, |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
scripts/check-worker-size.js (1)
150-164: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick winEnforce the raw limit before
--updatewrites the baseline.In update mode, the script writes and then calls
process.exit(0)before thebundle.rawBytes > RAW_LIMIT_BYTEScheck runs, so an oversized reference Worker is accepted intoworker-size.json. Move the raw-limit check before the update write and early return; keep the gzip-baseline update but requirebundle.rawBytes <= RAW_LIMIT_BYTESfirst.🤖 Prompt for 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. In `@scripts/check-worker-size.js` around lines 150 - 164, The update path currently writes a baseline before enforcing the raw-size limit. In the worker-size check flow, move the bundle.rawBytes versus RAW_LIMIT_BYTES validation ahead of the update write and process.exit(0), while preserving the gzip-baseline update for bundles whose raw size is within the limit.
🤖 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 @.github/file-filters.yml:
- Around line 81-88: Update the worker_size filter definition to include
.github/file-filters.yml, ensuring changes to the filter configuration trigger
the worker-size gate while preserving the existing entries and behavior.
---
Outside diff comments:
In `@scripts/check-worker-size.js`:
- Around line 150-164: The update path currently writes a baseline before
enforcing the raw-size limit. In the worker-size check flow, move the
bundle.rawBytes versus RAW_LIMIT_BYTES validation ahead of the update write and
process.exit(0), while preserving the gzip-baseline update for bundles whose raw
size is within the limit.
🪄 Autofix
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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 6948b33a-5462-41ea-b601-103b24291ceb
⛔ Files ignored due to path filters (2)
packages/cli/__tests__/util/health-probe.test.tsis excluded by!**/__tests__/**,!**/*.test.tsand included bypackages/**pnpm-lock.yamlis excluded by!**/pnpm-lock.yamland included by none
📒 Files selected for processing (8)
.github/file-filters.yml.github/workflows/test.ymlapps/docs/src/content/docs/deployment.mdxpackages/cli/docs/index.mdxpackages/cli/src/util/health-probe.tspackages/mcp/README.mdpackages/mcp/package.jsonscripts/check-worker-size.js
🚧 Files skipped from review as they are similar to previous changes (4)
- packages/mcp/package.json
- apps/docs/src/content/docs/deployment.mdx
- packages/mcp/README.md
- packages/cli/docs/index.mdx
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## alpha #361 +/- ##
========================================
Coverage ? 87.08%
========================================
Files ? 1170
Lines ? 63259
Branches ? 15350
========================================
Hits ? 55087
Misses ? 7643
Partials ? 529
🚀 New features to boost your workflow:
|
Merging this PR will degrade performance by 20.54%
|
| Benchmark | BASE |
HEAD |
Efficiency | |
|---|---|---|---|---|
| ❌ | batched: one IN-list UNION-ALL probe across all tables (locateTablesByIds) |
1.4 ms | 1.8 ms | -20.54% |
Tip
Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.
Comparing integrate/wave-20 (a2f54dd) with alpha (3709940)2
Footnotes
-
10 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩
-
No successful run was found on
alpha(c5222d8) during the generation of this report, so 3709940 was used instead as the comparison base. There might be some changes unrelated to this pull request in this report. ↩
Second round of review fixes. - file-filters: the worker_size filter did not list the filter file itself, so a PR editing the filter definition still skipped the gate — and test-required-check passes on skipped jobs. Same hole as the one the filter was added to close, one level up. - verify: report the URL probeHealth returned instead of rebuilding it. The two agree only while verify keeps the default paths; passing more would name a URL the verdict did not come from. Drops the joinHealthUrl import. - docs: a stray blank line rendered one doctor-check bullet as a loose list item. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01KBeSX2o4sTCPjVDRDWkVQG
|
Correction to my previous comment: it claimed all six review comments were addressed, which was true of the six actionable ones but not of everything outstanding. Three more were open, now fixed in a2f54dd.
Loose list item in the doctor-checks list — stray blank line removed. CLI 1204/1204, |
|
This pull request has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs. |
Four related changes to the surface an automated consumer — a CI job, a coding agent, a deploy pipeline — actually talks to. The capabilities were mostly already here; the machine-readable edges were inconsistent.
What changes
lunora doctorbecomes consumable. It was the only preflight command withoptions: []and findings identified solely by an English sentence. Now--format jsonwith an{ ok, code, summary, findings }envelope, a requiredcodeon every finding drawn from a 17-entry literal union (a typo failslint:types), and a newcli-shadowedcheck for a global binary shadowing the project's. The docs table is the fixture — a test parses the code column out ofdocs/index.mdxand asserts it equalsDOCTOR_CODES, so adding a code without documenting it fails the suite.lunora deployreports what it deployed. It already parsed the deployed URL out of wrangler's output, but the gate turned that off for--format jsonand--preview— the two paths automation uses.DeployCommandResultnow carriesdeployment(url,workerName,env,dryRun,preview,deployedAt); json mode captures viacaptureStdoutSilentlyand replays wrangler's log to stderr so stdout stays exactly one document. Links refresh on every real deploy: write when absent, no-op when equal, warn-and-keep when different — a stale link silently misrouting--migrateat a decommissioned URL is worse than the overwrite it avoids. New--health-checkprobes/_lunora/health/ready(falling back to the aggregate) with a bounded retry before--migrateruns;verifynow shares that probe.MCP gains the reads an agent needs to debug. It could call a deployment but not see what happened. Five token-gated read tools —
lunora_get_logs,_get_issues,_get_advisories,_get_query_insights,_get_migration_status— plusoutputSchema/structuredContentalongside the retained text block. These are read-only but privileged, so they are omitted fromListToolswithout an admin token and refused at dispatch, independent of--allow-writes.The deployed Worker gets weighed. Nothing measured it.
lunora buildnow reports raw + gzip size (gzipSyncreproduces wrangler's ownTotal Uploadline to the byte), and aworker-sizeCI job gates a reference template against a committed baseline. It is its own job, deliberately not inpostinstall, where one failure turns every job red in its setup step.BREAKING CHANGE
lunora build --format jsonnow emits its own result document instead of deploy's. There was nowhere to put the size field otherwise. Consumers reading build's json output see a different shape.Notable findings
35% of a hello-world Worker is an English NLP library.
@visulima/redacthard-depends oncompromise@^14.15.1— 606 KiB raw in the bundle, larger than any first-party package — reached frompackages/observability/src/request-log.ts:30. Every app carries it. Not fixed here; recorded as a follow-up.A reference worker measures 412.9 KiB gzipped against a 3 MB Free / 10 MB Paid ceiling — 13.4%. That is why only the CI gate shipped and the planned user-facing warning was dropped: it would have been an alarm nobody could legitimately trip, at a threshold nobody could justify.
Three of the four plans were wrong about something, corrected in-flight and recorded in
plans/README.md. The one worth repeating: the planned pnpm shadow-detection would have warned on every pnpm project, because pnpm writes a shell shim where the design assumed a symlink.Verification
Run against the merged tree, not the individual branches:
dist:check'sjsxDEVfindings reproduce on untouchedalpha— a dev-vs-prod build artifact, not from this branch.🤖 Generated with Claude Code
https://claude.ai/code/session_01KBeSX2o4sTCPjVDRDWkVQG
Summary by CodeRabbit
New Features
lunora doctorandlunora deploy.Documentation