Commit a044ede
fix(agents-server,agents-runtime): unblock fresh-tenant first wake for shared-state writers (#4550)
## Summary
Two first-spawn races prevented writer-side shared-state entities from
ever reaching their first handler run on a fresh tenant.
- **`agents-server` `durable-streams-router.ts:708-735`** — `PUT
/_electric/shared-state/<id>` now inserts the `shared_state_links` row
synchronously when a valid `electric-owner-entity` header is supplied.
Was previously created only when the entity's manifest stream event was
processed eventually-consistently, so the runtime's own preload GET
(issued right after the PUT) always 401'd on the first wake.
- **`agents-runtime` `process-wake.ts:1410-1424`** — `createChildDb`
swallows `Stream not found` / 404 on the initial entity-observation
preload. A handler may legitimately observe an entity that hasn't been
spawned yet (parent observing its own future child); the previous
unconditional throw aborted the wake and prevented the entity from ever
recovering.
20 lines of source, plus a 1-line test mock update and a changeset.
## Why
Reproduction with OpenFactory's `daily-digest` entity (uses `mkdb` +
`observe(db(...))` + `observe(entity(<future writer>))`) against a
freshly torn-down local `agents-server`:
```text
[/daily-digest/main-1] handler failed: HTTP Error 401 at .../shared-state/daily-digest?offset=-1: Principal is not allowed to read shared state
```
with `shared_state_links` empty despite the runtime having just issued
the bootstrap PUT seconds earlier. Once the server-side patch lands the
link row, the next preload reads through `created_by` matching and the
handler proceeds. The handler then tries to observe its own future
writer (`/horton/daily-digest-writer-main-1`) which doesn't exist yet —
the runtime patch turns that 404 into a deferred "no events yet" instead
of aborting the wake.
Combined, the two patches let `daily-digest` self-bootstrap end-to-end
on a clean tenant without manual SQL or out-of-band link insertion.
## Test plan
- [x] `pnpm --filter @electric-ax/agents-runtime typecheck` — clean
- [x] `pnpm --filter @electric-ax/agents-server typecheck` — clean
- [x] `pnpm --filter @electric-ax/agents-runtime test` — **827 pass, 2
skipped (66 files)**
- [x] `pnpm --filter @electric-ax/agents-server test` — **402 pass, 41
skipped, 2 failing** (the two failing `oss-server-router` dashboard
tests fail on `origin/main` without this PR — verified by stashing the
patch and re-running, unrelated)
- [x] End-to-end against a freshly torn-down local agents-server (this
PR's build) + OpenFactory's daily-digest entity: first
\`spawn-daily-digest.sh --run-now\` writes the digest, the
discord-router subscriber posts to Discord. No manual DB inserts, no
out-of-band link bootstrapping.
## Out of scope
A few related concerns surfaced during diagnosis but aren't fixed here:
- `setup-context.ts:749` uses string concat for `serverBaseUrl +
streamPath` which produces `http://localhost:4437//horton/...` when the
env URL has a trailing slash. Cosmetic — most servers normalise — but
worth a follow-up using `appendPathToUrl`.
- The runtime's `prepareAgentRun` / post-handler ordering
(`process-wake.ts:2146-2152`) runs `waitForSharedStateWiring` *before*
`commitManifestEntries`. Once this PR's server-side link bootstrap
lands, the manifest event becomes a redundant secondary write rather
than the only source of truth, so the ordering is benign — but
conceptually the manifest should be committed before the wiring waits.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>1 parent 5238055 commit a044ede
4 files changed
Lines changed: 37 additions & 1 deletion
File tree
- .changeset
- packages
- agents-runtime/src
- agents-server
- src/routing
- test
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1408 | 1408 | | |
1409 | 1409 | | |
1410 | 1410 | | |
1411 | | - | |
| 1411 | + | |
| 1412 | + | |
| 1413 | + | |
| 1414 | + | |
| 1415 | + | |
| 1416 | + | |
| 1417 | + | |
| 1418 | + | |
| 1419 | + | |
| 1420 | + | |
| 1421 | + | |
1412 | 1422 | | |
1413 | 1423 | | |
1414 | 1424 | | |
| |||
Lines changed: 13 additions & 0 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
717 | 717 | | |
718 | 718 | | |
719 | 719 | | |
| 720 | + | |
| 721 | + | |
| 722 | + | |
| 723 | + | |
| 724 | + | |
| 725 | + | |
| 726 | + | |
| 727 | + | |
| 728 | + | |
| 729 | + | |
| 730 | + | |
| 731 | + | |
| 732 | + | |
720 | 733 | | |
721 | 734 | | |
722 | 735 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
83 | 83 | | |
84 | 84 | | |
85 | 85 | | |
| 86 | + | |
86 | 87 | | |
87 | 88 | | |
88 | 89 | | |
| |||
0 commit comments