Skip to content

feat(pr3): reader (status op) + version-cached hydration + tutorial docs - #3

Merged
equationalapplications merged 10 commits into
mainfrom
feat/pr3-reader-docs
Aug 9, 2026
Merged

feat(pr3): reader (status op) + version-cached hydration + tutorial docs#3
equationalapplications merged 10 commits into
mainfrom
feat/pr3-reader-docs

Conversation

@equationalapplications

@equationalapplications equationalapplications commented Aug 8, 2026

Copy link
Copy Markdown
Owner

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 status reader op with version-cached hydration (spec §4.3), the scripts/smoke.sh end-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-scope statusReaders: Map<dbPath, StatusReader> so the cache survives warm Lambda invocations. Replaces the 501 stub.
  • scripts/smoke.sh — operator-run smoke test that invokes fetch via AWS CLI, then queries status via 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 files
  • npx tsc -p tsconfig.check.json — no errors
  • npm run build — no errors
  • npm run smoke — manual, requires deployed stack

Spec Coverage

  • §3.2 (all 5 steps of the reader op)
  • §4.3 (version-cached hydration including NoSuchKey empty-state branch and close-before-reopen requirement)
  • §9 Phase 4 deliverables (status op, version cache, scripts/smoke.sh extended)
  • §10 docs deliverable (README.md + five docs/*.md files)

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 33 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

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 configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b255843e-5509-414b-bdd2-a476ada3377d

📥 Commits

Reviewing files that changed from the base of the PR and between 7055c9c and 15a2683.

📒 Files selected for processing (5)
  • docs/01-architecture.md
  • docs/superpowers/specs/2026-08-08-sqlite-s3-agent-tutorial-design.md
  • src/agent/status.ts
  • src/handler.ts
  • tests/status.test.ts
📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added a status endpoint showing source activity and the ten most recent notifications.
    • Status data refreshes automatically when newer data is available.
    • Added read-only snapshot access for reliable status reporting.
  • Documentation

    • Added setup, architecture, database, rehydration, extension, and production guidance.
    • Documented deployment, testing, model access, and estimated costs.
  • Tests

    • Expanded coverage for status responses, snapshot refreshes, read-only access, and empty states.
  • Chores

    • Added a smoke-test command for validating deployed fetch and status operations.

Walkthrough

The change implements the Lambda status operation with an S3-backed, read-only SQLite snapshot reader. It adds warm-invocation caching, tests, a smoke-test command, and documentation for architecture, schema, rehydration, extension, deployment, and Bedrock setup.

Changes

Status operation

Layer / File(s) Summary
S3 snapshot status reader
src/agent/status.ts, src/db/open.ts, tests/status.test.ts, tests/db.test.ts
The status reader loads SQLite snapshots from S3, caches unchanged snapshots by ETag, refreshes changed snapshots, and returns source and notification data. Read-only access and cache behavior are tested.
Lambda status integration
src/handler.ts, tests/handler.test.ts
The handler caches readers by database path and returns status responses with HTTP 200. Handler tests cover empty snapshots, body fallbacks, and Function URL requests.
Operational documentation and smoke testing
README.md, docs/*, package.json, scripts/smoke.sh
Documentation covers system architecture, schema, rehydration, source extensions, production deployment, and Bedrock setup. The smoke script invokes fetch and validates weather status output.

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
Loading

Possibly related PRs

Poem

A rabbit checks the snapshot store,
Then hops through status at the door.
Read-only rows arrive just right,
Cached by ETag, fresh in flight.
The smoke test thumps its little drum,
“Weather found—deployment done!”

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the status reader, version-cached hydration, and tutorial documentation changes.
Description check ✅ Passed The description directly explains the status operation, hydration cache, smoke test, documentation, and verification results.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

📥 Commits

Reviewing files that changed from the base of the PR and between 5952763 and e6f2c7d.

📒 Files selected for processing (14)
  • README.md
  • docs/01-architecture.md
  • docs/02-rehydration.md
  • docs/03-schema.md
  • docs/04-extending.md
  • docs/05-from-tutorial-to-prod.md
  • package.json
  • scripts/smoke.sh
  • src/agent/status.ts
  • src/db/open.ts
  • src/handler.ts
  • tests/db.test.ts
  • tests/handler.test.ts
  • tests/status.test.ts

Comment thread docs/01-architecture.md Outdated
Comment thread docs/02-rehydration.md Outdated
Comment thread docs/03-schema.md
Comment thread README.md Outdated
Comment thread scripts/smoke.sh Outdated
Comment thread scripts/smoke.sh
- 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>
@equationalapplications

Copy link
Copy Markdown
Owner Author

/fix-pr follow-up

Commit: b15fa92

Review resolution

  • docs/01-architecture.md (SQLite lifecycle)Fixed: reworded the SQLite lifecycle to state the /tmp copy is transient for the execution-environment lifetime, with a one-line note that warm invocations share /tmp (which is what makes the reader's cached handle work). Durable single-S3-object claim preserved. (docs/01-architecture.md:5-9)
  • docs/02-rehydration.md (bootstrap write)Fixed: rewrote the bootstrap paragraph to say ifMatch: null translates to If-None-Match: "*" in S3Store (a conditional create that 409s if the key exists), not an unconditioned PUT. Concurrent-writer protection is now stated explicitly. (docs/02-rehydration.md:24-30)
  • docs/03-schema.md (DDL completeness)Fixed: added the missing idx_agent_notifications_source_posted_at index and the chk_op / chk_outcome CHECK constraints to the schema listing so it matches src/db/schema.ts exactly. (docs/03-schema.md:25-26,43-44)
  • README.md + docs/02-rehydration.md (Bedrock model access)Fixed: replaced the obsolete manual Bedrock → Model access console step with the current AWS Marketplace subscription prerequisite for zai.glm-4.7-flash. Removed the "independent of IAM" claim and called out that Anthropic models still need first-time-use EULA acceptance on the same page. (README.md:26-30, docs/02-rehydration.md:65-77)
  • scripts/smoke.sh (mktemp for fetch-response)Fixed: fetch response now goes through mktemp, and the existing exit trap now cleans up both FETCH_RESPONSE_FILE and NETRC_FILE. (scripts/smoke.sh:13-15)
  • scripts/smoke.sh (credential resolution + security token)Fixed: switched credential setup to aws configure export-credentials --format process (full chain, not just static profile values), parses AccessKeyId / SecretAccessKey / SessionToken with jq, writes the key/secret to the protected netrc, and conditionally adds X-Amz-Security-Token to the curl call when SessionToken is present. (scripts/smoke.sh:54-79)

Verification

  • typecheck — pass (npm run typecheck)
  • lint — N/A (no lint script defined in package.json)
  • tests — pass, 79/79 across 12 files (npm test)
  • bash syntax — pass (bash -n scripts/smoke.sh)

🤖 Generated with Claude Code

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 win

Reject null or missing lastValue.

jq renders a null or missing lastValue as null. That value is non-empty, so Line 84 reports success without a weather value. Use jq -e to require a weather source whose lastValue is 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

📥 Commits

Reviewing files that changed from the base of the PR and between e6f2c7d and b15fa92.

📒 Files selected for processing (5)
  • README.md
  • docs/01-architecture.md
  • docs/02-rehydration.md
  • docs/03-schema.md
  • scripts/smoke.sh
🚧 Files skipped from review as they are similar to previous changes (3)
  • README.md
  • docs/02-rehydration.md
  • docs/03-schema.md

Comment thread scripts/smoke.sh Outdated
…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.
@equationalapplications

Copy link
Copy Markdown
Owner Author

/fix-pr follow-up

Commit: a378a8c

Review resolution

  • scripts/smoke.sh (session_token / lines 71-72)Fixed: SessionToken now written to a mode-0600 tempfile and passed via curl --header "@file", keeping it out of the process command line for the curl lifetime. The tempfile is tracked in the existing exit-trap cleanup using ${VAR:+WORD} so it is a no-op when the profile has no session token.
  • scripts/smoke.sh (weather_present / lines 83-84)Fixed: the check now rejects both empty and the jq literal null for .lastValue, so an explicit-null or missing weather source fails the smoke test instead of passing.

Verification

  • syntax — pass (bash -n scripts/smoke.sh)
  • tests — pass (npm test — 79 passed across 12 files)
  • typecheck — pass (npx tsc -p tsconfig.check.json)
  • weather_present fix — verified against six JSON shapes (present, explicit-null, missing key, no weather source, empty sources, missing sources) via a throwaway harness.

@equationalapplications
equationalapplications requested a lite review from Copilot August 8, 2026 23:58

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 status reader op with module-scope cache (createStatusReader) and wired it into runHandler.
  • 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.

Comment thread tests/handler.test.ts Outdated
Comment thread tests/status.test.ts
Comment thread src/agent/status.ts
Comment thread src/agent/status.ts
Comment thread scripts/smoke.sh Outdated
- 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)
@equationalapplications

Copy link
Copy Markdown
Owner Author

/fix-pr follow-up

Commit: 7055c9c

Review resolution

  • src/agent/status.ts — sources ordering: Fixed. Added ORDER BY name to the agent_sources query so the returned array order is deterministic across SQLite versions and vacuuming.
  • src/agent/status.ts — notifications tie-breaker: Fixed. Added id DESC to the agent_notifications ORDER BY so the LIMIT picks a stable subset when multiple rows share posted_at.
  • scripts/smoke.sh — JSON piping: Fixed. Replaced both echo "$status_response" | jq with printf "%s\n" "$status_response" | jq so backslash escapes and leading-- flags do not mangle the JSON.
  • tests/status.test.ts — cold-start simulation: Fixed. Removed the writeFileSync(ctx.dbPath, "") line and its inaccurate comment — the reader was created with reader-copy.db, which never had that file, so the overwrite was dead. The cold-start branch is exercised by the fresh reader-copy.db path. Also dropped the now-unused writeFileSync import.
  • tests/handler.test.ts — description/assertion mismatch: Fixed. Renamed the test to "routes op="status" through the reader and returns 200 with the empty-state shape when no snapshot exists" so it matches the actual assertions (the existing inline comment already explains why the populated-response shape is not covered here — that lives in tests/status.test.ts against a real LocalStore).

Verification

  • typecheck — pass (npm run typecheck)
  • tests — pass (79 tests across 12 files, npm test)
  • smoke.sh — syntax OK (bash -n scripts/smoke.sh)

🤖 Generated with Claude Code

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 by rmSync(dbPath) can still throw if the file is removed between the check and the delete (or if /tmp cleanup happens). Using force: true makes 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 an afterEach so 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);

Comment thread src/handler.ts
… 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.
@equationalapplications

Copy link
Copy Markdown
Owner Author

/fix-pr follow-up

Commit: 15a2683

Review resolution

  • src/handler.ts — reader cache poisoning by writer conditional-write failure (Copilot, unresolved). Fixed in commit 15a2683. getStatusReader now derives the readers local path as ${writerDbPath}.reader so the readers local SQLite file is disjoint from the writers. 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 S3s ETag unchanged — a reader sharing the writers path would see an ETag cache hit on a warm call and answer from the still-open reader handle against the writers mutated bytes. Spec §3.2 step 2-3, §4.3, and docs/01-architecture.md updated to describe the disjoint paths.
  • src/agent/status.ts:107 — existsSync+rmSync race (Copilot, suppressed). Fixed. Replaced with rmSync(dbPath, { force: true }). The existsSync import is retained for the cacheHit guard, but the delete is now robust against the file disappearing between the check and the delete.
  • tests/status.test.ts:19 — cleanup must run on early assertion failure (Copilot, suppressed). Fixed. Cleanup moved from per-test ctx.cleanup() calls into afterEach so a failing assertion earlier in the body doesnt leak the temp dir into the next test.

Verification

  • npm test — pass (npm test → 79 tests across 12 files pass)
  • npx tsc -p tsconfig.check.json — pass (no output, clean)
  • npm run build — pass (tsc -p tsconfig.json → no errors)
  • npm run smoke — N/A (requires deployed stack; manual)

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 opened state.db handle (and cachedEtag) 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 the head === null branch 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: [] };
      }

@equationalapplications
equationalapplications merged commit cf3b3af into main Aug 9, 2026
2 checks passed
@equationalapplications
equationalapplications deleted the feat/pr3-reader-docs branch August 9, 2026 00:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants