{"assessments":[],"deployments":[],"fuzz":[],"identity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"interpretation":"Records acceptance and evidence. Neither completion nor an AI assessment establishes correctness, safety, or independent review.","jobId":"1485431c-0a9e-4f34-b655-3bfae6ae0861","kind":"skill:build-ponder-indexer","nodes":[{"acceptedSubmissionHash":"4853b3ca2ea286977a2b0898e5a98392a2ab3329f3748acebfcb48301021992e","dependsOn":[],"execution":{"mustProduce":["INDEXER.md"],"network":true,"profile":"none","requires":["network"],"skillHash":"de7d672edd661a5cf3efc279e8e2dcf49065c52be4f6f75989f77dfa7e91e833","skillId":"build-ponder-indexer","tools":[]},"key":"build_ponder_indexer","kind":"code","role":"implement","skillHash":"de7d672edd661a5cf3efc279e8e2dcf49065c52be4f6f75989f77dfa7e91e833","skillId":"build-ponder-indexer","state":"accepted"}],"objective":"Continue the existing rental indexer. Repair the health-check problems below in delivered commit 7631f26753845dd5bd8254f5099fe8d042bff3df, keeping the rest of this maintenance change. This is the same public repository: https://github.com/identity-md-launches/launch-2237-build-standalone-read-only-ponder. Source delivery only; no deployment or access to private infrastructure.\n\nIndependent review passed the Docker build, typecheck, all 28 unit tests and the supplied integration suite using PostgreSQL 17.7 and disposable Anvil. Additional tests of the actual API module, with controlled dependencies, found these defects:\n\n1. A late result can reinstate stale readiness after timeout. shared/health.ts:createBoundedCache caches a loader result at its completion time even if the operation previously timed out. src/api/index.ts collects the database, head and readiness with Promise.all. Reproduction: HEALTH_TIMEOUT_MS=100, HEALTH_CACHE_MS=100, checkpoint 900, head 1000, MAX_SERVING_LAG=200, native readiness 200. Delay only the checkpoint read for 600 ms; head responds immediately. The first health request times out. Advance head to 1600 while the read is delayed. When the old operation finally completes, /healthz returns 200 ready with lag 100 and an observation over 600 ms old, despite a 100 ms cache limit. GraphQL/SQL guards also permit that sample. A fresh probe then correctly reports lag 700 and rejects queries. Do not turn expired/timed-out observations into fresh successful eligibility just because a slow dependency finally settles. Bound observation age and require a fresh completed sample after timeout, while keeping at most one active probe and preventing unbounded retry/work accumulation. Retain recovery once dependencies respond normally.\n\n2. The timeout diagnosis is fabricated. failureSample marks every dependency as timed out and sets observedAt to now. A slow RPC with an immediately healthy database/native readiness therefore returns reason database_timeout. Use accurate per-dependency outcomes, or an honest generic probe-timeout reason when attribution is unknown. Do not invent a successful observation timestamp or erase known distinctions. /sync should remain interpretable and its existing response shape preserved.\n\n3. nativeReadiness maps every non-2xx response to not_ready. A native /ready HTTP 500 therefore appears as ordinary catching_up. Distinguish the documented startup/backfill response (503) from unexpected failures such as HTTP 500/404; unexpected failure must produce unavailable with a safe reason.\n\nAdd regression tests against the actual health route and production probe orchestration, including the above delayed-completion/head-advance case and GraphQL/SQL eligibility, RPC-only timeout, database-only timeout, native HTTP 500 versus 503, and recovery. Use controlled dependencies/time where possible. Keep all existing tests. The current tests only supply prebuilt decisions to the health handler, so they missed these wiring failures.\n\nAlso correct the rollback instructions: the previous deployed release 95cf662 does not have /healthz, so rollback validation must use its supported /ready, /sync and an actual query. State plainly that the existing source fingerprint gives this API update a new physical schema/backfill; retain both schemas and avoid overlapping writers. Do not alter fingerprinting or introduce a schema migration to disguise this behavior.\n\nKeep contracts, ABIs, indexing logic, financial rules, schema/configuration defaults, dependency versions and lockfile unchanged. Preserve query interfaces and existing safeguards. Use safe reason codes without raw errors or credentials. Report exact changes, commands/results and remaining limitations honestly; mark unavailable integration checks NOT RUN. Only public rental source, documentation and packages; no private files, credentials, cloud access, paid provisioning, signing or public-chain transactions. We will independently review and test before releasing.","parentJobId":"5d00ffbe-58d0-4383-a6af-e120f0b3d606","planHash":"c667dd0a99673fd198f5e72771390034c6850ea20ae1d5c15194dbcec89d3f72","previousHash":"0000000000000000000000000000000000000000000000000000000000000000","projectId":"e4eb1329-68c8-4492-b2f6-0632e1009180","publication":{"commit":null,"deliveredAt":null,"repoUrl":"https://github.com/identity-md-launches/launch-2237-build-standalone-read-only-ponder"},"receiptIdentity":{"adapter":"0xde152afb7db5373f34876e1499fbd893a82dd336","chainId":1,"collection":"0x0000ec93127baa929e58e97dd0095a2bfb38ec1d","registry":"0x8004a169fb4a3325136eb29fa0ceb6d2e539a432"},"registry":"0xb6d0a187b050fa5bb0b87033a203f37becf4a775","research":[],"schema":"identitymd-work-v1","signals":[{"agentId":"53938","feedbackHash":"cc7d71c83a80a9095430cb0ae5e3dcf381355911fa458f4b9bc5c9bf6cd8ac5b","nodeKey":"build_ponder_indexer","submissionHash":"4853b3ca2ea286977a2b0898e5a98392a2ab3329f3748acebfcb48301021992e","tag1":"verification:structural","tag2":"acceptance-v2","value":1}],"site":null,"snapshotHash":"22c316d67eba7b05c9888edef38849069637842f04d2b179d57a7680020c3447","state":"completed","submissions":[{"artifacts":[],"attempt":1,"bundleHash":"ddc37384219bc1ecbbcf697391003a34d84ba790b302509cba41728353b68050","device":"01695e9524859621","findings":[],"hash":"4853b3ca2ea286977a2b0898e5a98392a2ab3329f3748acebfcb48301021992e","nodeId":"140b138e-5248-44f6-8dbf-e2e22ce74385","outcome":"completed","summary":"I fixed all three health-check defects and the rollback instructions, and committed the result locally (not pushed). Typecheck and all 37 unit tests pass, and the Anvil integration suite passes. The integration suite on PostgreSQL 17 and the Docker/Compose/Railway checks were **NOT RUN**, because this machine has no PostgreSQL or Docker.\n\n**1. A late result no longer brings back stale readiness** (`shared/health.ts`, `createBoundedCache`)\n- Only one probe runs at a time. Once a probe misses its deadline, every check answers straight away from what had finished by the deadline, and nothing new starts.\n- A probe that finishes after its deadline is thrown away, never cached. The next check starts a fresh probe, and GraphQL/SQL are allowed again only when a fresh probe finishes on time and passes the lag rules.\n- A cached result counts its age from when the probe started, so a decision never rests on data older than `HEALTH_CACHE_MS`.\n- Recovery needs no restart.\n\n**2. Timeouts are blamed on the right dependency** (new `src/api/probe.ts`)\n- At the deadline, each of the database, RPC head and native `/ready` is reported as what it actually did: succeeded, failed or timed out. A slow RPC with a healthy database now gives `rpc_timeout`, not `database_timeout`.\n- Values that arrived in time are kept, such as the real head and its real time when only the database is slow. `observedAt` is `null` unless the head request actually finished in time.\n- A new `probe_timeout` reason covers the case where no single dependency can be blamed.\n- `/sync` has the same fields, but `rpcError` is now a fixed code (`rpc_unavailable`, `rpc_timeout` or `rpc_wrong_chain`) instead of the raw exception text it returned in both earlier releases.\n\n**3. Native `/ready` status mapping**\n- 200 means ready and 503 means catching up.\n- Any other status, such as 500 or 404, now gives `unavailable` with the reason `readiness_unexpected_response`.\n\n**Wiring and tests**\n- The routes and guards moved into `createApi` (new `src/api/app.ts`). `src/api/index.ts` now only plugs in Ponder's database, the RPC client and the official GraphQL/SQL middleware. Routes, query limits, the request budget and `/snapshot` are unchanged.\n- The 9 new tests in `tests/serving.test.ts` run against the real routes and the real probe code, with a manual clock and controlled dependencies. They cover your exact reproduction (database timeout, head moved to 1600, the late 900/1000 result thrown away, fresh lag 700 rejecting both GraphQL and SQL, then recovery), plus:\n  - cache age\n  - RPC-only and database-only timeouts\n  - native 503 vs 500/404, and native timeout or failure\n  - safe error codes and wrong chain\n  - single probe under many concurrent checks\n  - bounded mode\n- I put the old caching behaviour back temporarily, and 3 of the new tests failed, so they do catch the original bug. All 28 existing tests are kept.\n\n**README and INDEXER.md**\n- There is a reason-code table and the new probe rules.\n- The rollback section now says plainly that this update gets a new physical schema and a full backfill, because the fingerprint covers `src/`, `shared/` and `scripts/`. It says to keep both schemas and never run overlapping writers, with a Railway-specific caution.\n- It also notes that 95cf662 has no `/healthz`, so rollback is checked with its `/ready`, `/sync` and a real `accounting` GraphQL query.\n- The fingerprinting, contracts, ABIs, indexing logic, schema, defaults, dependencies and lockfile are unchanged, and there is no migration.\n\n**Commands run** (in copies under `test/scratch/`, since the root `node_modules` is read-only here)\n\n| Command | Result |\n|---|---|\n| `pnpm install --frozen-lockfile` | PASS |\n| `pnpm codegen` | PASS; `ponder-env.d.ts` unchanged |\n| `pnpm typecheck` | PASS |\n| `pnpm test` | 37/37 pass |\n| `pnpm test:integration` (Anvil 31337, PGlite, real `ponder start`) | PASS on all 7 checks, including the RPC-outage 503 from `/healthz` and GraphQL, and recovery |\n| Integratio","treeHash":"d2be8ed593f4a10b900f13cf398921bff9317fcc","usage":{"cachedInputTokens":2518409,"inputTokens":60,"model":"claude-opus-5-5","outputTokens":37762,"runtime":"claude","turns":30,"wallClockMs":424734}}],"verification":[{"checks":[],"detail":"paths and tree verified; no suite was run for this kind of work","evaluation":"structural","profile":"none","status":"accepted","submissionHash":"4853b3ca2ea286977a2b0898e5a98392a2ab3329f3748acebfcb48301021992e","verifiedTreeHash":"d2be8ed593f4a10b900f13cf398921bff9317fcc","verifierVersion":"0.1.0+6a374fcd"}]}