Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
95 commits
Select commit Hold shift + click to select a range
5d37bd0
fix(worker): propagate RoomDO command rejections from POST /_/:room
audreyt Aug 10, 2026
7543317
fix(scripts): compute mutation score from mutant statuses in ratchet-…
audreyt Aug 10, 2026
b16cfd8
docs(migration): add production upgrade runbook and supporting audits
audreyt Aug 10, 2026
58c1af5
docs: document POST /_/:room rejection contract and changelog fixes
audreyt Aug 10, 2026
2a56271
docs(migration): harden prod upgrade plan with D1 Time Travel precond…
audreyt Aug 10, 2026
868ee0e
docs(migration): correct D1 rooms index scope and DO non-exportabilit…
audreyt Aug 10, 2026
5047531
docs(migration): clarify D1 log tails versus DO sheet state
audreyt Aug 10, 2026
989db0a
docs(migration): correct D1 cron table name
audreyt Aug 10, 2026
b42ad53
docs: document Durable Object mass PITR
audreyt Aug 10, 2026
5caeae3
docs: escalate private room non-enumerability in PITR runbook
audreyt Aug 10, 2026
d3e189b
docs(migration): update D1 private room recovery & backup handling in…
audreyt Aug 10, 2026
8c84f33
docs: reconcile §3.2 staging checklist with §5 empirically verified c…
audreyt Aug 10, 2026
b8e50d2
docs: source-verify section 7 self-host upgrade plan and record pinne…
audreyt Aug 10, 2026
9eac6ad
docs: call out zero-margin workerd lockstep and test skew
audreyt Aug 10, 2026
6e33aa3
docs: note CI guard for workerd date lockstep
audreyt Aug 10, 2026
523c980
docs: cross-reference upgrade automation guards
audreyt Aug 10, 2026
b71874d
test(worker): pin player.js MIME and socket.io handshake wire shape
audreyt Aug 10, 2026
1824aa4
test(worker): pin both serveAsset MIME branches for player.js
audreyt Aug 10, 2026
6334c03
docs(migration): document self-host workerd divergence in §7
audreyt Aug 10, 2026
2f2a9e8
docs(migration): document mixed-version cross-room risk in Phase 2
audreyt Aug 10, 2026
7f882e5
docs(migration): correct access 501 hazard claim in §4.3.1
audreyt Aug 10, 2026
416dd14
docs(migration): scale backup/recovery to ~1.8M rooms and 10GB D1
audreyt Aug 10, 2026
579b4e2
docs: add production cutover quick reference
audreyt Aug 10, 2026
2c41447
docs(migration): add cutover decision cards for irreversible gaps
audreyt Aug 10, 2026
dda49a6
docs(migration): tighten decision cards per navigation review
audreyt Aug 10, 2026
2c34e5f
docs(migration): correct pre-cutover bookmark recovery fallback
audreyt Aug 10, 2026
2327f5c
docs(migration): reconcile companion drift with runbook
audreyt Aug 10, 2026
7ef9cf3
docs(migration): fix unsafe rollback mitigations and stale preflight …
audreyt Aug 10, 2026
91a6d29
test(socialcalc-headless): prove legacy 3.0.x snapshots load on 3.1.0
audreyt Aug 10, 2026
5387340
docs(migration): clarify oracle-replay 10/13 is a stale floor
audreyt Aug 10, 2026
290dcc7
test(socialcalc-headless): prove 3.1.0 saves load on genuine 3.0.8
audreyt Aug 10, 2026
843284b
docs(migration): neutralize PROD_UPGRADE_PLAN AUTH=0 hazard
audreyt Aug 10, 2026
76ce06a
docs(migration): draft single-ramp cutover after baseline disprove
audreyt Aug 10, 2026
35e3f71
docs(migration): rebase skew analysis on deployed range
audreyt Aug 10, 2026
9c4e6ce
docs(migration): rescope DELTA_AUDIT status to deployed range
audreyt Aug 10, 2026
102fb4a
docs(migration): reclassify DELTA_AUDIT executive summary
audreyt Aug 10, 2026
ab536c0
docs(migration): mark DELTA_AUDIT a/b/c/d/e/i/j against d2afa90
audreyt Aug 10, 2026
b7d4039
docs(migration): rescope DELTA_AUDIT f/g/h/k still-in-delta items
audreyt Aug 10, 2026
61f1c32
docs(migration): inventory remaining d2afa90..HEAD ship delta
audreyt Aug 10, 2026
ae86756
revert: drop accidental robots changes from docs commit
audreyt Aug 10, 2026
c789249
fix(worker): restore search-indexing robots policy
audreyt Aug 10, 2026
c40f11f
docs(migration): record search-indexing policy as ship requirement
audreyt Aug 10, 2026
c94fd3e
docs(migration): rebaseline §10 item 5 passkeys already live
audreyt Aug 10, 2026
1a1b208
docs(migration): close STOP §10 row and honest §10 intro
audreyt Aug 10, 2026
6126a1c
with exact pin still
audreyt Aug 10, 2026
e6668a5
docs(migration): promote single-ramp naming and live critical-path map
audreyt Aug 10, 2026
59e6733
docs(migration): place live single-ramp §4 after preparation sections
audreyt Aug 10, 2026
7664916
docs(migration): fold deploy-config guard into live §4.0
audreyt Aug 10, 2026
4282f24
docs(migration): keep skew analysis with live single-ramp §4
audreyt Aug 10, 2026
acff08b
docs(migration): reframe §5 probes for auth-on single-ramp cutover
audreyt Aug 10, 2026
5f3f1bc
docs(migration): demote three-phase §4 into Appendix A
audreyt Aug 10, 2026
9da5221
docs(migration): keep live D1 restore as §6; appendix phase rollback
audreyt Aug 10, 2026
e26af2b
docs(migration): demote three-phase Go/No-Go checklist to Appendix A.3
audreyt Aug 10, 2026
f62969a
docs(migration): retarget primary cross-refs away from appendix paths
audreyt Aug 10, 2026
89cb72e
docs(migration): namespace appendix headings to avoid live § collisions
audreyt Aug 10, 2026
f1d64c3
docs(migration): fix Appendix A.2 heading doubled "old" prefix
audreyt Aug 10, 2026
b3361fb
docs(migration): drop void AUTH=0 soak rationale from §2.4.1
audreyt Aug 10, 2026
6718cc6
docs(migration): replace live §3 with single-ramp staging rehearsal
audreyt Aug 10, 2026
658c43c
docs(migration): harden live §3 staging flags and §8 Phase1 banner
audreyt Aug 10, 2026
a4b5acf
docs(migration): keep staging safety notes only in live §3.1
audreyt Aug 10, 2026
859fb46
docs(migration): pin DELTA_AUDIT classification target to 35e3f71
audreyt Aug 10, 2026
773037a
docs(migration): mark leftover DELTA_AUDIT baseline claims superseded
audreyt Aug 10, 2026
b18847e
docs(migration): drop invented staging list-command flags in live §3.2
audreyt Aug 10, 2026
758aa35
docs(migration): re-pin §1.2 worker test and coverage counts
audreyt Aug 10, 2026
1fc0226
docs(migration): retarget dangling §9/§4.3/§6.2 live-path refs
audreyt Aug 10, 2026
327fa3d
docs(migration): re-baseline PREFLIGHT_RESULTS at tip b18847e
audreyt Aug 10, 2026
89150c7
docs(migration): record fresh Gate 7 mutation ratchet at 327fa3d
audreyt Aug 10, 2026
31edb20
docs(migration): record mutation-ratchet fragility in §8
audreyt Aug 10, 2026
9c68ca2
docs(migration): pin SKEW classification target to 76ce06a
audreyt Aug 10, 2026
32a3478
docs(changelog): record search-indexing robots policy
audreyt Aug 10, 2026
25fd325
docs(historic): append prod-upgrade runbook session to §14
audreyt Aug 10, 2026
3884e3a
docs(agents): rotate session log for prod-upgrade runbook
audreyt Aug 10, 2026
d18a5ed
docs(agents): restore prior Latest session text verbatim
audreyt Aug 10, 2026
905e3b8
docs(migration): add directory README as runbook entry point
audreyt Aug 10, 2026
086d450
docs(migration): inventory @ethercalc/worker mutation survivors
audreyt Aug 10, 2026
87c3600
docs(migration): untruncate mutation survivor table cells
audreyt Aug 10, 2026
ebd44dd
docs(migration): pin prod baseline and fix §0.1 secret gate
audreyt Aug 10, 2026
fec2cda
docs(migration): repair STOP-banner blockquote continuity
audreyt Aug 10, 2026
0ba4f3f
docs(migration): qualify b7 bound with §4.0 redirect caveat
audreyt Aug 10, 2026
892b23f
docs(migration): fix STOP-banner bash fence blank line
audreyt Aug 10, 2026
b6647b3
docs(migration): keep STOP-banner blockquote continuous after fence
audreyt Aug 10, 2026
30ced79
docs(migration): finish STOP-banner continuity and trim blanks
audreyt Aug 10, 2026
ffe3c79
docs(migration): close last STOP-banner list blockquote gap
audreyt Aug 10, 2026
088e5e7
docs(migration): close final STOP-banner bullet gap
audreyt Aug 10, 2026
5e4d0c7
docs(migration): remove unquoted blank in STOP-banner list
audreyt Aug 10, 2026
a8c5fd4
docs(migration): eliminate last unquoted blank in STOP banner
audreyt Aug 10, 2026
0327f90
docs(migration): final STOP-banner unquoted-blank removal
audreyt Aug 10, 2026
8496bca
docs(migration): normalize STOP banner to continuous blockquote
audreyt Aug 10, 2026
8fd7e91
docs(migration): require 100/0 split before override smoke
audreyt Aug 10, 2026
f274ba6
docs(migration): record sticky-cache rollback probe and §1 re-verify
audreyt Aug 10, 2026
c3bdede
docs(migration): record production ramp to SHIP@100%
audreyt Aug 10, 2026
3df5612
fix(socialcalc-headless): declare socialcalc@3.0.8 alias for reverse-…
audreyt Aug 10, 2026
09673a4
ci: raise mutation-gate timeout to 45m for worker-sized PRs
audreyt Aug 10, 2026
3aa3bb9
docs(migration): record CI vs local worker mutation score gap
audreyt Aug 10, 2026
c31d88c
docs(migration): tighten mutation gap claims to retained evidence
audreyt Aug 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -237,13 +237,24 @@ jobs:
mutation-gate:
name: mutation-gate — changed packages only
runs-on: ubuntu-latest
timeout-minutes: 20
# Budget (2026-08-10): local full six-package ratchet exits 0 in ~828s
# (~13m48s) on M-series; this job mutates only packages with `src/`
# changes vs origin/main (this branch: worker only — robots + rooms).
# Worker alone dominates wall-clock. CI ubuntu runners are slower than
# local, and run 31388973185 cancelled the Stryker step at
# timeout-minutes:20 with zero score output — a cancelled gate is
# indistinguishable from green at a glance and trains people to ignore
# it. 45m ≈ ~2.5× the local full-matrix baseline, deliberate headroom
# so the gate can finish and report a real score rather than timing out.
# Do NOT lower Stryker break thresholds to "fit" a short budget.
timeout-minutes: 45
needs: test
# Fast PR-gate mutation check. Runs Stryker only on packages whose
# `src/` changed in the PR, so docs/test/config-only PRs skip it and
# single-package PRs stay under ~2 minutes. The full matrix lives in
# nightly.yml; see AGENTS.md §5.3 + docs/MUTATION_REPORT.md for the
# single-package PRs stay under the budget above. The full matrix lives
# in nightly.yml; see AGENTS.md §5.3 + docs/MUTATION_REPORT.md for the
# ratchet workflow.

steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
Expand Down
31 changes: 26 additions & 5 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ line/branch/function/statement coverage on gated packages in CI.
| User guide + FAQ | [docs.ethercalc.net](https://docs.ethercalc.net) · `packages/docs/` |
| HTTP API | `API.md` |
| Self-host hardening | `docs/SELFHOST_HARDENING.md` |
| Prod upgrade runbook | `docs/migration/PROD_UPGRADE_PLAN.md` |
| Oracle replay | `tests/oracle/README.md` · `packages/oracle-harness/` |
| Mutation baselines | `docs/MUTATION_REPORT.md` |
| Sandstorm `.spk` | `SANDSTORM.md` (manual `spk pack` — app owner signs) |
Expand Down Expand Up @@ -117,7 +118,31 @@ spikes/ Immutable research provenance (not the maintained work
## Session log

Per-session history is in `docs/historic/REWRITE_ULTRAPLAN.md` §14 (append-only,
newest last). Latest: full security audit of `main` against prod
newest last). Latest: operator runbook for upgrading production
`ethercalc.net` to `main` (`docs/migration/PROD_UPGRADE_PLAN.md` plus
`DELTA_AUDIT.md`, `SKEW_AND_RECONNECT.md`, `PREFLIGHT_RESULTS.md`,
`INVENTORY.md`). Read-only probes overturned the assumed
`0.20260717.0`/`149ebcf` baseline — `GET /_auth/whoami` returns
`{"uid":null,"enabled":true}`, root HTML serves `./static/passkey/ui.js`,
and `/static/index-bootstrap.js` is 404 — so production sits in
`[d2afa90, b7d8840)` with passkeys/`AuthDO`/ACL/private rooms already live
and the security-audit page-script extraction not. The original Phase-2
`ETHERCALC_AUTH="0"` soak would have failed `authEnabled()`, nulled every
session principal, and 403'd every private room; corrected plan is a single
gradual ramp with `AUTH="1"` throughout (three-phase analysis kept as
Appendix A). Candidate-range checks that made the simpler plan safe:
`wrangler.toml` declares only `v1`/`v2`, all three D1 migrations are
byte-identical, and `lib/authorize.ts` is byte-identical. Exposed
regression: `ec_sess` → `__Host-ec_sess` with no dual-read (silent logout
at rollout). Search-indexing policy restored after an accidental
dual-purpose-commit revert (`ae86756` → `c789249`; only `/` stays
indexable; robots.txt has no `Disallow` so crawlers can still observe
`noindex`). Tip `327fa3d`: worker 53/1526 node + 13/197 workers-pool at
100% coverage (2706/1936/298/2416); mutation ratchet exit 0 in 828s,
`@ethercalc/worker` +0.03pp, `robots.ts` 100% (9/0). Open: six
`[OPERATOR-VERIFY]` items (chiefly `CURRENT_PROD_VERSION_ID` via
`wrangler deployments list`) and the staging rehearsal.
Prior: full security audit of `main` against prod
`ethercalc.net`, with every confirmed finding fixed at its owning boundary —
stored DOM XSS in the client graph panel, multi-sheet `postMessage`/TOC trust,
per-message WS/Socket.IO authentication and attachment-room binding (no more
Expand All @@ -140,7 +165,3 @@ fail-closed Helm passkey anchors.
Mutation floors re-measured: worker 90.21, shared 99.69, socketio-shim 84.68,
migrate 90.38, oracle-harness 83.46, client 77.61 — equivalent mutants carry
written `// Stryker disable` justifications rather than padded tests.
Prior: merged passkey accounts + private sheets (Phase A) from
`feat/passkey-permissions` (PR #841) — WebAuthn `AuthDO` relying party,
RoomDO-enforced `meta:access`/`meta:acl` private rooms, principal-threaded
routes, dependency-free passkey UI, WS session-expiry enforcement.
8 changes: 8 additions & 0 deletions API.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,13 +100,21 @@ To find out which command corresponds to which spreadsheet actions,
perform the actions on the web interface and check the _Audit_ tab
for the recorded commands.

Command batches that would push the sheet's declared area past the 200,000-cell limit are rejected with `413`. The same limit applies over WebSocket, where the connection closes with code `1008` and reason `Command exceeds sheet limits`.

+ Request (application/json)
+ Response 202

```json
{"command": "..."}
```

+ Response 413 (text/plain)

```text
command exceeds sheet limits
```

## Append Rows [POST]

Takes a CSV structure that contains fields to be appended to the first column after the last row.
Expand Down
17 changes: 17 additions & 0 deletions Changes.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,13 @@ Security
- Made staging deploy validation pass `--config wrangler.toml` explicitly, so
Vite's generated production-config redirect cannot silently ignore
`--env staging`.
- Room URLs stay unlisted by design: every non-landing response now carries
`X-Robots-Tag: noindex, nofollow, noarchive`, and only `/` remains
indexable. `GET /robots.txt` returns a real robots file (Allow: `/` only)
instead of falling through the `/:room` catch-all as app HTML. The robots
file deliberately carries no `Disallow` directive — `Disallow` forbids
fetching, so a crawler blocked from a URL can never observe its `noindex`
header and already-indexed rooms would be stranded permanently.

Tooling
- Migrated the repository interface to Vite+ 0.2.4: package operations use
Expand All @@ -54,6 +61,10 @@ Tooling
- Hardened self-host Docker builds against local-state leakage: build context
excludes dependencies, Vite/Wrangler output, secrets, migration dumps, and
persistent room data; the smoke gate asserts the resulting image is clean.
- Fixed `scripts/ratchet-verify.sh` mutation score parsing in `jq` and `node`
branches by computing the score from mutant status counts rather than reading
a non-existent `mutationScore` property, resolving an unconditional parse
error (exit 2) during mutation ratchet audits.

Integration
- Prepared the headless adapter for the next SocialCalc release: bundle
Expand All @@ -68,6 +79,12 @@ Integration
measured 920px cutoff, avoiding overlap with SocialCalc's expanded menu while
preserving fixed top-right chrome on wider sheets.

Bug fixes
- Fixed `POST /_/:room` returning `202` on Durable Object command rejections,
which silently discarded writes. The route now propagates the DO status and
response body (such as `413 command exceeds sheet limits` when a batch exceeds
200,000 cells).

==============================================================================
0.20260717.0 — 2026-07-17
==============================================================================
Expand Down
3 changes: 3 additions & 0 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions docs/historic/REWRITE_ULTRAPLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,7 @@ Append one entry per session.
| 2026-07-16 | tooling | **Migrated the repository interface to Vite+ 0.2.4.** `vp install/add/remove/update` now owns package operations; `vp run`/`vp exec` own task and binary orchestration; package Vite/Vitest imports, scripts, contributor docs, e2e child processes, and CI/deploy/nightly/release workflows were migrated. Root `vite.config.ts` aggregates package Vitest projects and replaces Biome with Vite+ Oxlint while preserving the three-rule/no-formatter contract; Biome and its lockfile graph were removed. `bun@1.3.14` remains pinned as the underlying package manager/native runtime, with direct Bun confined to Bun-native scripts/tests and Docker or packed-package paths where Vite+ is not shipped. Verified frozen install, `vp check`, all-package typecheck, 2,208 aggregated Vitest tests, the full `vp run test` workflow, asset builds, and 15 Playwright e2e tests. | (this commit) |
| 2026-08-03 | security-audit | **Full-branch security audit of `main` against prod `ethercalc.net`, fixes landed at the owning boundary.** Closed: stored DOM XSS in the client graph panel (persisted `graphrange`/named-range values reached `innerHTML`; now `textContent` plus a built Hide-Help control, removing the last inline `onclick`); multi-sheet TOC/`postMessage` trust (origin+source checked in `index.html`, `SheetFrame` targets its own origin, TOC links validated by `isSafeMultiSheetLink`); WS/Socket.IO authentication and memory (per-message `verifyAuth` on every state-changing frame, attachment-room binding instead of client-supplied `parsed.room`, per-socket rate window, message/frame/chat/cell caps in `@ethercalc/shared`, bounded handshake sessions with teardown in the shim); unbounded ingestion (auth and suffix-import bodies now pass the shared size guard; ZIP archive/sheet/cell/column caps; bounded cross-sheet hydration; chunked-snapshot metadata validation); `/_timetrigger` no longer a public metadata/side-effect endpoint, and the scheduler no longer treats refusals as fires or deletes non-fired rows; session/identity (`__Host-ec_sess`, AuthDO-side revocation with monotonic windows, per-IP ceremony rate limits, challenge capacity cap, session expiry enforced on live sockets); trust-proxy correctness (`CF-Connecting-IP` honoured only from the trusted edge, nginx now replaces rather than appends forwarding headers, bucket eviction); room-ID entropy restored to ≥62 bits; creation-limit classifier extended to private/form/multi-import routes; `no-store` on authenticated/private responses plus global security headers; export sanitizer allowlist mirrored to the client policy. Root HTML page scripts were extracted to five tracked files under `static/` (`index-bootstrap`, `index-l10n`, `start-bootstrap`, `start-page`, `panels`) with a `requiredFiles` assertion in `scripts/build-assets.ts`, since the whole-directory copy checks no individual names — note the app CSP still needs `script-src 'unsafe-inline'` for SocialCalc's toolbar, so this is hygiene, not yet a boundary. Infra: compat date 2024-11-12 → 2026-07-21 (capnp in lockstep), `run_worker_first`, pinned base image, read-only/no-new-privileges containers, `automountServiceAccountToken: false`, Helm passkey trust anchors that fail closed, with `scripts/check-helm-hardening.sh` covering both. Verification: `vp check`, all-package typecheck, 1499 worker-node + 194 workers-pool + the remaining package suites, 100% worker coverage, 47/47 Playwright e2e, `wrangler deploy --dry-run`, helm lint/template matrix, self-host and proxy smoke. Mutation floors re-measured: worker 90.21 (break 90), shared 99.69 (99), socketio-shim 84.68 (84), migrate 90.38 (90), oracle-harness 83.46 (83), client 77.61 (77); provably equivalent mutants carry written `// Stryker disable` justifications instead of padded tests. | (working tree) |
| 2026-08-03 | security-audit/follow-up | **Closed the final Host-derived CSP seam caught during delivery review.** `index.ts` had built `connect-src ws(s)://…` from `new URL(request.url).host`, even though the same file correctly forbids using attacker-controlled `Host` for cross-origin redirects. New pure `lib/csp.ts#websocketAuthority` anchors the WebSocket authority on `ETHERCALC_ORIGIN` (including configured scheme/port), falling back to the request host only for self-hosts without a valid configured origin. Ten Node tests pin configured/fallback/scheme/port/invalid-origin lanes. Live `wrangler dev` probes with `Host: attacker.test` and `Host: evil.example:1337` both retained `connect-src 'self' wss://ethercalc.net`. Post-fix worker typecheck, 100% coverage, mutation 90.21 ≥ 90, and `vp check` passed; Stryker restored cleanly with no instrumentation markers. | (working tree) |
| 2026-08-10 | prod-upgrade | **Operator runbook for upgrading production `ethercalc.net` to `main`.** Deliverable `docs/migration/PROD_UPGRADE_PLAN.md` with companions `DELTA_AUDIT.md`, `SKEW_AND_RECONNECT.md`, `PREFLIGHT_RESULTS.md`, `INVENTORY.md`. Central finding overturned the original premise: production is not tag `0.20260717.0` / `149ebcf`. Read-only probes (`GET /_auth/whoami` → `{"uid":null,"enabled":true}`, a route absent at `149ebcf`; root HTML serves `./static/passkey/ui.js`; `GET /static/index-bootstrap.js` is 404) bound production to `[d2afa90, b7d8840)` — passkeys, `AuthDO`, ACL, and private rooms are already live; the security-audit page-script extraction is not. That mattered because the original three-phase cutover set `ETHERCALC_AUTH="0"` in Phase 2 as a safety soak; against real production that fails `authEnabled()`, nulls every session principal, and 403s every existing private room to its owner — a self-inflicted outage. Corrected plan is a single gradual ramp with `AUTH="1"` throughout; superseded three-phase analysis preserved as Appendix A. Verified constraints that made the simpler plan safe: across the whole candidate range `wrangler.toml` declares only `v1`/`v2` (no lifecycle change), all three D1 migration files are byte-identical (blob OIDs match), and `lib/authorize.ts` is byte-identical (no declassification on rollback). A real regression the corrected baseline exposed: `lib/session.ts` renames the session cookie `ec_sess` → `__Host-ec_sess` with no dual-read, so every signed-in passkey user is silently logged out at rollout and an open tab on a private room can fail to reconnect. Search-indexing policy restored and shipped (`c789249`): a worker session misread the originating commit's `docs(migration):` subject, judged the bundled production code accidental, and reverted it in `ae86756`; recovered in `c789249` with all four blobs verified byte-identical to the originals (cautionary for dual-purpose commits). Measurements at tip `327fa3d`: worker suites 53 files / 1526 tests (node), 13 / 197 (workers-pool), 100% coverage (2706/1936/298/2416); mutation ratchet exit 0 in 828s with every audited package under 1pp of its break floor and `@ethercalc/worker` at +0.03pp — `robots.ts` itself scored 100% (9 killed / 0 survived). Still open: six `[OPERATOR-VERIFY]` items needing live Cloudflare access, chiefly `wrangler deployments list` to pin `CURRENT_PROD_VERSION_ID` (needed for the rollback target, not for the ramp's safety), and the staging rehearsal. | (this branch) |

---

Expand Down
Loading
Loading