feat(pr3): reader (status op) + version-cached hydration + tutorial docs - #3
Conversation
…tending, prod deltas)
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 33 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. 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: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (5)
📝 WalkthroughSummary by CodeRabbit
WalkthroughThe change implements the Lambda ChangesStatus operation
Estimated code review effort: 3 (Moderate) | ~25 minutes Sequence Diagram(s)sequenceDiagram
participant LambdaHandler
participant StatusReader
participant S3Store
participant ReadOnlySQLite
LambdaHandler->>StatusReader: getStatus(store, storeKey)
StatusReader->>S3Store: HEAD snapshot
S3Store-->>StatusReader: ETag or missing snapshot
StatusReader->>S3Store: GET changed snapshot
S3Store-->>StatusReader: SQLite snapshot bytes
StatusReader->>ReadOnlySQLite: open snapshot read-only
ReadOnlySQLite-->>StatusReader: status rows
StatusReader-->>LambdaHandler: StatusResult
Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
Comment |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 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 `@docs/01-architecture.md`:
- Around line 5-6: Update the SQLite lifecycle description in the architecture
documentation to state that the file is transient for the lifetime of the
execution environment, not a single invocation. Preserve the durable
single-S3-object description.
In `@docs/02-rehydration.md`:
- Around line 24-28: Update the bootstrap write explanation to state that
ifMatch: null causes S3Store to send If-None-Match: "*" rather than performing
an unconditioned PUT, preserving concurrent-writer protection.
In `@docs/03-schema.md`:
- Around line 6-35: Update the schema listing to include the missing
idx_agent_notifications_source_posted_at index and the chk_op and chk_outcome
constraints defined by src/db/schema.ts; alternatively, clearly label the SQL
snippet as an abbreviated schema.
In `@README.md`:
- Around line 26-28: Update README.md lines 26-28 to replace the obsolete manual
Bedrock Model access workflow with prerequisites for AWS Marketplace permissions
and subscription access for zai.glm-4.7-flash, noting Anthropic’s separate
first-use requirements where applicable. Update docs/02-rehydration.md lines
65-76 likewise: remove the claim that model access is independent of IAM and
replace the console instructions with the current Bedrock account, Marketplace,
and subscription requirements.
In `@scripts/smoke.sh`:
- Around line 30-33: Replace the predictable /tmp/fetch-response.json path in
the smoke script with a file created via mktemp, store its path for the AWS CLI
response and subsequent jq read, and add that temporary file to the existing
exit trap cleanup.
- Around line 47-56: Update the credential setup before the curl invocation to
use aws configure export-credentials --format process for the selected PROFILE,
extracting AccessKeyId, SecretAccessKey, and any SessionToken. Write the
resolved access key and secret to the protected NETRC_FILE, and include
X-Amz-Security-Token in the signed request when SessionToken is present.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 126d0fda-7d26-47f3-839e-31b98df89159
📒 Files selected for processing (14)
README.mddocs/01-architecture.mddocs/02-rehydration.mddocs/03-schema.mddocs/04-extending.mddocs/05-from-tutorial-to-prod.mdpackage.jsonscripts/smoke.shsrc/agent/status.tssrc/db/open.tssrc/handler.tstests/db.test.tstests/handler.test.tstests/status.test.ts
- docs/01-architecture.md: SQLite /tmp copy is transient for the execution environment lifetime, not a single invocation (status reader relies on warm-container /tmp persistence). - docs/02-rehydration.md: bootstrap put sends If-None-Match: "*" via S3Store, not an unconditioned PUT — concurrent-writer protection is preserved. - docs/03-schema.md: include the source/posted_at index and the chk_op / chk_outcome CHECK constraints that src/db/schema.ts defines. - README.md + docs/02-rehydration.md: replace the obsolete manual Bedrock *Model access* console step with the current AWS Marketplace subscription prerequisite for zai.glm-4.7-flash; note that Anthropic models still need first-time-use EULA acceptance. - scripts/smoke.sh: fetch-response.json goes through mktemp; credentials resolve via `aws configure export-credentials --format process` and X-Amz-Security-Token is added when SessionToken is present (SSO / assumed-role profiles). Co-Authored-By: Claude <noreply@anthropic.com>
/fix-pr follow-upCommit: Review resolution
Verification
🤖 Generated with Claude Code |
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/smoke.sh (1)
83-84: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winReject null or missing
lastValue.
jqrenders a null or missinglastValueasnull. That value is non-empty, so Line 84 reports success without a weather value. Usejq -eto require a weather source whoselastValueis not null.Proposed fix
-weather_present=$(echo "$status_response" | jq '.sources[] | select(.name == "weather") | .lastValue') -if [ -z "$weather_present" ]; then +if ! jq -e 'any(.sources[]?; .name == "weather" and (.lastValue? != null))' \ + <<<"$status_response" >/dev/null; then echo "FAIL: no weather source with a lastValue in status response" >&2 exit 1 fi🤖 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/smoke.sh` around lines 83 - 84, Update the weather_present extraction in the smoke-test status check to use jq -e and require a matching weather source with a non-null lastValue. Ensure both missing and explicit null values produce an empty or failing result so the existing [ -z "$weather_present" ] validation rejects them.
🤖 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 `@scripts/smoke.sh`:
- Around line 71-72: Update the session_token handling in the smoke script so
the security-token header is written to a mode-0600 temporary file and supplied
to curl via --header "`@file`", keeping SessionToken out of process arguments.
Track the temporary file and remove it in the existing exit trap, while
preserving the conditional header behavior.
---
Outside diff comments:
In `@scripts/smoke.sh`:
- Around line 83-84: Update the weather_present extraction in the smoke-test
status check to use jq -e and require a matching weather source with a non-null
lastValue. Ensure both missing and explicit null values produce an empty or
failing result so the existing [ -z "$weather_present" ] validation rejects
them.
🪄 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: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 62e9af70-d9c2-433a-969c-713b405d9ffb
📒 Files selected for processing (5)
README.mddocs/01-architecture.mddocs/02-rehydration.mddocs/03-schema.mdscripts/smoke.sh
🚧 Files skipped from review as they are similar to previous changes (3)
- README.md
- docs/02-rehydration.md
- docs/03-schema.md
…astValue - SessionToken now written to a 0600 tempfile and passed via 'curl --header "@file"' instead of inline --header arg, so the temporary credential is not visible in ps aux for the curl lifetime. File is tracked in the existing exit-trap cleanup via ${VAR:+WORD} so it's a no-op when the profile has no session token. - weather_present check now treats the jq literal 'null' as missing too. jq renders a null lastValue as the four-character string 'null', which is non-empty and was letting the smoke check pass on a malformed response. CodeRabbit review on PR #3.
/fix-pr follow-upCommit: Review resolution
Verification
|
There was a problem hiding this comment.
Pull request overview
Implements the Phase 4 “reader” side of the tutorial: a status operation that reads the latest SQLite snapshot from S3 with version-cached hydration (ETag + open read-only DB handle), plus smoke-test automation and narrative tutorial docs.
Changes:
- Added
statusreader op with module-scope cache (createStatusReader) and wired it intorunHandler. - Added read-only SQLite open helper (
openReadOnlyDatabase) and tests covering reader hydration/versioning. - Added operator smoke script + tutorial documentation set (README + docs).
Reviewed changes
Copilot reviewed 14 out of 14 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/status.test.ts | Adds unit coverage for version-cached reader hydration and empty-state behavior. |
| tests/handler.test.ts | Updates handler routing tests to expect status to return 200 with JSON payload. |
| tests/db.test.ts | Adds coverage for opening SQLite in read-only mode. |
| src/handler.ts | Replaces status 501 stub with real reader path and module-scope reader cache. |
| src/db/open.ts | Adds openReadOnlyDatabase helper for reader-side SQLite access. |
| src/agent/status.ts | Introduces the status reader implementation with ETag-based hydration cache. |
| scripts/smoke.sh | Adds an end-to-end smoke test that runs fetch then queries status via SigV4 curl. |
| README.md | Adds tutorial landing page + quickstart instructions and doc index. |
| package.json | Adds npm run smoke script. |
| docs/01-architecture.md | Tutorial narrative: system architecture and rationale. |
| docs/02-rehydration.md | Tutorial narrative: bootstrap, conditional writes, and version-cached reads. |
| docs/03-schema.md | Tutorial narrative: schema explanation and design tradeoffs. |
| docs/04-extending.md | Tutorial narrative: how to add an additional source. |
| docs/05-from-tutorial-to-prod.md | Tutorial narrative: what changes when moving from tutorial to production. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
- status.ts: ORDER BY name on agent_sources for deterministic ordering; add id DESC tie-breaker to agent_notifications ORDER BY - smoke.sh: replace echo with printf when piping JSON to jq - status.test.ts: remove misleading writeFileSync of a path the reader does not read (reader uses a different path) - handler.test.ts: update description to match the actual coverage (empty-state response shape, not populated sources/recentNotifications)
/fix-pr follow-upCommit: Review resolution
Verification
🤖 Generated with Claude Code |
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 14 out of 14 changed files in this pull request and generated 1 comment.
Suppressed comments (2)
src/agent/status.ts:107
existsSync(dbPath)followed byrmSync(dbPath)can still throw if the file is removed between the check and the delete (or if/tmpcleanup happens). Usingforce: truemakes the refresh path robust without changing behavior when the file exists.
if (existsSync(dbPath)) {
rmSync(dbPath);
}
tests/status.test.ts:19
- These tests rely on calling
ctx.cleanup()at the end of each test. If an assertion throws earlier, the temp dir is leaked, which can cause cross-test interference and makes failures harder to reproduce. Register cleanup in anafterEachso it runs even on failures.
return { dir, dbPath, store, cleanup: () => rmSync(dir, { recursive: true, force: true }) };
}
async function seedSnapshot(dbPath: string, store: ReturnType<typeof createLocalStore>) {
const db = openDatabase(dbPath);
… cleanup
- handler: getStatusReader now uses `${writerDbPath}.reader` so the reader's
local SQLite file is disjoint from the writer's. The writer mutates its
local file on every invocation, including the conditional-write failure
path where a 412 from S3 leaves the local bytes with the
outcome='error' run row recorded but S3's ETag unchanged. A reader
sharing the writer's path would see an ETag cache hit on a warm call
and answer from the still-open reader handle against the writer's
mutated bytes — disjoint paths keep the reader's view strictly in step
with what the writer has actually published.
- status.ts: rmSync(dbPath, { force: true }) replaces existsSync + rmSync
so the refresh path is robust against /tmp races between the check
and the delete.
- tests/status.test.ts: cleanup is now in afterEach so a failing
assertion earlier in the body doesn't leak the temp dir into the next
test.
- spec \xC2\xA73.2, \xC2\xA74.3 and docs/01-architecture.md: updated to describe the
reader's separate local path.
/fix-pr follow-upCommit: Review resolution
Verification
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 15 out of 15 changed files in this pull request and generated no new comments.
Suppressed comments (1)
src/agent/status.ts:93
- When
head === null(no snapshot key found), the function returns the empty-state but leaves any previously openedstate.dbhandle (andcachedEtag) intact. If the snapshot is deleted after a prior successful hydration, this keeps a stale SQLite handle open unnecessarily and leaves a stale local copy on disk. Clearing the cached state and closing the handle in thehead === nullbranch avoids leaking resources and keeps local state consistent with S3.
// No snapshot yet — fetch has never run successfully (spec §4.3). Nothing to query.
if (head === null) {
return { snapshotVersion: null, sources: [], recentNotifications: [] };
}
Summary
Spec: docs/superpowers/specs/2026-08-08-sqlite-s3-agent-tutorial-design.md
Plan: docs/superpowers/plans/2026-08-08-sqlite-s3-agent-tutorial-pr3-reader-docs.md
Implements Phase 4 of the design spec (spec §9) — the
statusreader op with version-cached hydration (spec §4.3), thescripts/smoke.shend-to-end verification, and the tutorial's narrative docs (spec §10).openReadOnlyDatabase— read-only SQLite handle for the reader, matching its GetObject-only IAM grant.createStatusReader— module-scope hydration cache (last ETag + open read-only handle). HEAD-only on warm container; close-and-reopen on cold start or version change. Handles the empty-state (head === null) and HEAD/GET race branches.handler.ts— module-scopestatusReaders: Map<dbPath, StatusReader>so the cache survives warm Lambda invocations. Replaces the 501 stub.scripts/smoke.sh— operator-run smoke test that invokesfetchvia AWS CLI, then queriesstatusvia curl-with-SigV4 against the Function URL.README.md+docs/01..05-*.md— tutorial narrative: architecture, rehydration, schema, extending, prod deltas.Test Plan
npm test— 79 tests pass across 12 filesnpx tsc -p tsconfig.check.json— no errorsnpm run build— no errorsnpm run smoke— manual, requires deployed stackSpec Coverage
NoSuchKeyempty-state branch and close-before-reopen requirement)statusop, version cache,scripts/smoke.shextended)README.md+ fivedocs/*.mdfiles)🤖 Generated with Claude Code