PR4: Reader state-invariant fixes + Discord webhook setup docs - #4
Conversation
Addresses the post-merge review of PR3: - Fix the 2 state-inconsistency bugs in src/agent/status.ts (swap order; reset cachedEtag on HEAD-succeeds-GET-fails) - Add spec §4.3.1 documenting the partial-failure state invariants - Add docs/06-discord-webhook-setup.md (new reader got stuck on this) - Remove aws-cloud-agent / core-llm-wiki references from public docs and spec Co-Authored-By: Claude <noreply@anthropic.com>
…ord webhook setup docs - Swap state.db/state.cachedEtag assignment order so a failed openReadOnlyDatabase leaves cachedEtag at its prior value, not the new one - Reset cachedEtag on HEAD-succeeds-GET-fails so the next call retries cleanly - Add regression tests for both partial-failure scenarios - Update spec §4.3.1 to document the state-invariant contract - Add docs/06-discord-webhook-setup.md (new reader got stuck on this) - Remove aws-cloud-agent / core-llm-wiki references from public docs and code comments (private repo; dead links for the public audience) Co-Authored-By: Claude <noreply@anthropic.com>
|
Warning Review limit reachedYou’ve reached a temporary PR review limit under our Fair Usage Limits Policy. Next review available in: 27 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 (4)
📝 WalkthroughSummary by CodeRabbit
WalkthroughThe PR fixes reader cache-state recovery, adds regression tests, and updates tutorial documentation for Discord webhooks, standalone architecture, production guidance, and Bedrock model evaluation. ChangesReader recovery and validation
Discord webhook setup and deployment guidance
Standalone tutorial documentation
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
Suggested reviewers: 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.
Pull request overview
This PR hardens the createStatusReader warm-cache state handling on partial failures in src/agent/status.ts, and removes references to a private sibling repo across docs while adding Discord webhook setup guidance for public tutorial readers.
Changes:
- Fixes reader cache-miss state update ordering and resets cached state on HEAD→GET races to better preserve the documented reader state invariant.
- Adds regression tests covering reader recovery after corrupted snapshots and HEAD-succeeds/GET-null races.
- Cleans up tutorial docs to remove private-repo references and adds Discord webhook setup documentation + README link.
Reviewed changes
Copilot reviewed 11 out of 11 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| tests/status.test.ts | Adds regression tests for reader partial-failure scenarios and recovery behavior. |
| src/format/families.ts | Removes private-repo references and clarifies the “live probe + negative control” requirement. |
| src/agent/status.ts | Adjusts cache-miss state updates to avoid inconsistent (cachedEtag, db) combinations. |
| README.md | Links quick start to the new Discord webhook setup guide. |
| docs/superpowers/specs/2026-08-08-sqlite-s3-agent-tutorial-design.md | Documents the reader state invariant and removes private-repo coupling references. |
| docs/superpowers/plans/2026-08-08-sqlite-s3-agent-tutorial-pr4-reader-bugfixes.md | Adds an implementation plan record for PR4 tasks and rationale. |
| docs/bedrock-model-comparison.md | Reframes provenance as general-purpose research (no private-repo references). |
| docs/06-discord-webhook-setup.md | New step-by-step guide for obtaining and using a Discord webhook URL. |
| docs/05-from-tutorial-to-prod.md | Generalizes guidance and removes private-repo framing. |
| docs/02-rehydration.md | Adds a link to the Discord webhook setup doc. |
| docs/01-architecture.md | Generalizes “one Lambda vs two” rationale without private-repo references. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
There was a problem hiding this comment.
Actionable comments posted: 10
🧹 Nitpick comments (1)
tests/status.test.ts (1)
117-161: 🗄️ Data Integrity & Integration | 🔵 Trivial | 🏗️ Heavy liftMake the new tests fail on the pre-fix implementation.
Both tests use a cold reader. In the first test, the cached ETag is already
null; in the second test, resetting it tonullis a no-op. The previous implementation therefore passes both tests. Add a warm-cache setup and expose the reader state through a test-only hook or an extracted testable state-transition helper. Also cover rejectedgetor write failures if the invariant applies to every cache-miss failure.Also applies to: 163-203
🤖 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 `@tests/status.test.ts` around lines 117 - 161, Make the recovery tests fail against the pre-fix implementation by warming the reader created with createStatusReader before inducing snapshot failures, rather than starting with a null cached ETag. Expose its cache state through a test-only hook or extracted state-transition helper, and assert failed getStatus calls clear both the cached ETag and database state. Add equivalent coverage for rejected store get or write operations if the cache-invalidation invariant applies to every cache-miss failure.
🤖 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/06-discord-webhook-setup.md`:
- Around line 57-63: Update the “Rotating the webhook” section by replacing the
claim that webhooks have no rate-limit concerns with guidance that webhook
requests remain subject to Discord rate limits, may return 429, and should honor
Retry-After and other rate-limit response headers.
- Around line 22-30: Update the webhook creation instructions under “Step 3:
Create a webhook” to state that users need MANAGE_WEBHOOKS permission for the
target channel, and advise contacting a server administrator when the New
Webhook option is unavailable.
- Around line 35-48: Update the deployment guidance around DISCORD_WEBHOOK_URL
to require SSM Parameter Store or Secrets Manager for production instead of
passing the webhook URL inline through infra/stack.ts. Retain local-development
guidance as appropriate, and explicitly warn readers not to commit .env,
cdk.out, or logs containing the webhook URL.
In `@docs/bedrock-model-comparison.md`:
- Around line 104-105: Update the ingest benchmark description and methodology
so it uses the repository’s actual INGEST_SYSTEM_PROMPT, matching the claim that
real prompts were tested. If the test must retain a typical synthetic prompt,
explicitly label the result as a synthetic proxy and exclude it from
application-specific conclusions and the ingest cost comparison.
- Around line 3-12: Align docs/bedrock-model-comparison.md with its stated
general-purpose scope: remove or rewrite the tier recommendations,
repository-specific prompt and implementation guidance, and incorrect
src/bedrock/families.ts reference. If retaining tutorial-specific material
instead, revise the provenance and introductory scope to explicitly cover the
tutorial and ensure all repository paths, including src/format/families.ts, are
accurate.
In
`@docs/superpowers/plans/2026-08-08-sqlite-s3-agent-tutorial-pr4-reader-bugfixes.md`:
- Line 17: Add language identifiers to every fenced code block in the document,
including the repository tree block and the embedded Discord document block: use
text for the tree and markdown for the Discord content, covering the additional
referenced section as well.
- Around line 752-767: Replace the hardcoded Discord webhook URL in the Step 4
examples with a secure, non-copy-paste secret mechanism supported by the
project, such as a masked CI secret, secure prompt, or untracked secret file.
Ensure the instructions never echo or expose the webhook value in shell history
or CI logs, while preserving both local and CDK deployment guidance.
- Around line 701-704: Update the repository verification steps in the plan so
grep explicitly excludes docs/superpowers/plans/ rather than relying on the
non-matching *-plan*.md pattern, and update the git diff --stat allowlist to
include 2026-08-08-sqlite-s3-agent-tutorial-pr4-reader-bugfixes.md. Apply the
same corrections to both referenced check sections.
In `@README.md`:
- Around line 18-20: Update the README deployment instructions around the npm
run deploy command to preserve DISCORD_WEBHOOK_URL in the environment, either by
exporting it before deployment or assigning it inline. Ensure the variable
remains available during infrastructure synthesis performed by the stack
configuration.
In `@src/agent/status.ts`:
- Around line 121-127: The reader state transition around state.db and
state.cachedEtag must preserve the invariant after any warm-cache miss failure:
reset cachedEtag when clearing db, or stage both replacement values and swap
them only after download, write, and openReadOnlyDatabase succeed. Update
src/agent/status.ts lines 121-127 accordingly; revise
docs/superpowers/specs/2026-08-08-sqlite-s3-agent-tutorial-design.md lines
167-175 to remove the prior-ETag/undefined-database failure state; and update
docs/superpowers/plans/2026-08-08-sqlite-s3-agent-tutorial-pr4-reader-bugfixes.md
lines 67-72 to describe the corrected fix and same-state explanation.
---
Nitpick comments:
In `@tests/status.test.ts`:
- Around line 117-161: Make the recovery tests fail against the pre-fix
implementation by warming the reader created with createStatusReader before
inducing snapshot failures, rather than starting with a null cached ETag. Expose
its cache state through a test-only hook or extracted state-transition helper,
and assert failed getStatus calls clear both the cached ETag and database state.
Add equivalent coverage for rejected store get or write operations if the
cache-invalidation invariant applies to every cache-miss failure.
🪄 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: 30f23501-4c4f-4c6b-9cd7-b131b0d8b61a
📒 Files selected for processing (11)
README.mddocs/01-architecture.mddocs/02-rehydration.mddocs/05-from-tutorial-to-prod.mddocs/06-discord-webhook-setup.mddocs/bedrock-model-comparison.mddocs/superpowers/plans/2026-08-08-sqlite-s3-agent-tutorial-pr4-reader-bugfixes.mddocs/superpowers/specs/2026-08-08-sqlite-s3-agent-tutorial-design.mdsrc/agent/status.tssrc/format/families.tstests/status.test.ts
Co-Authored-By: Claude <noreply@anthropic.com>
src/agent/status.ts: - Clear both cachedEtag and db at the top of the cache-miss branch (when closing the old handle) so any partial failure (store.get, writeFileSync, openReadOnlyDatabase) leaves the cache in the valid empty state (null, undefined). The previous 'swap order' fix only addressed the openReadOnlyDatabase case — store.get and writeFileSync failures still left cachedEtag at the prior warm-cache value (spec §4.3.1 invariant). - Add test-only __peekReaderState() method so regression tests can assert the invariant directly. tests/status.test.ts: - Replace the two cold-cache regression tests with warm-cache tests that actually exercise the cache-miss failure paths (store.get throws and HEAD-succeeds-GET-null). Assert the post-failure state via the new peek hook — both tests fail on the pre-fix implementation. docs/superpowers/specs/2026-08-08-...-design.md: - Update §4.3.1 invariant description to match the corrected fix: the cache-miss branch transitions to (null, undefined) at entry and only restores (string, open handle) after open succeeds. docs/superpowers/plans/2026-08-08-...-pr4-reader-bugfixes.md: - Update Task 1 fix description and code block to match the new shape. - Add language identifiers to fenced code blocks (markdownlint MD040). - Fix grep --exclude to use --exclude-dir=docs/superpowers/plans so the verification command excludes this plan file. - Add this plan file to the git diff --stat allowlist. - Replace hardcoded webhook URL examples with secure secret-sourcing patterns (untracked .env file or masked CI variable). - Update rate-limit statement in 'Rotating the webhook' to honour Retry-After and X-RateLimit-* headers. docs/06-discord-webhook-setup.md: - Add MANAGE_WEBHOOKS permission note in Step 3, with guidance to contact a server administrator when New Webhook is unavailable. - Replace inline DISCORD_WEBHOOK_URL examples with secure sourcing patterns (.env for local, CI secret for deploy); recommend SSM/Secrets Manager for production. - Update 'Rotating the webhook' to honour Retry-After and rate-limit response headers. README.md: - Add DISCORD_WEBHOOK_URL sourcing via untracked .env.discord before npm run deploy (infra/stack.ts reads it at synth time). docs/bedrock-model-comparison.md: - Fix src/bedrock/families.ts typo → src/format/families.ts. - Relabel the ingest benchmark as a synthetic proxy and exclude it from the application-specific recommendations (the file's claim of 'real prompts' did not match the synthetic INGEST_SYSTEM_PROMPT-style payload). - Reframe the Provenance blockquote to acknowledge the tier recommendations are tutorial-specific. Co-Authored-By: Claude <noreply@anthropic.com>
/fix-pr follow-upCommit: Review resolution
Verification
All 11 review threads resolved. 🤖 Generated with Claude Code |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 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/06-discord-webhook-setup.md`:
- Around line 61-64: Add an explicit .env.discord entry to the repository’s
.gitignore rules so the secret file referenced by the deployment instructions
remains untracked.
- Around line 89-92: Update the rate-limit guidance to match the behavior in the
poster implementation: describe HTTP 429 as a DiscordPostError recorded as a
per-source failure unless bounded 429 handling is implemented. Remove the claim
that fetch honors Retry-After or X-RateLimit-* headers, and retain the fixed 250
ms retry wording for existing 5xx handling.
- Around line 67-71: Update the production deployment warning near the SSM
Parameter Store and Secrets Manager guidance to remove the claim that
logs:GetLogEvents exposes the webhook through cold-start records. Distinguish
CloudWatch log access from exposure through Lambda configuration, CloudFormation
templates, or cdk.out artifacts, while retaining the warning not to log or
commit the webhook URL.
🪄 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: 151ef042-891c-4556-afe5-7c34f317bbec
📒 Files selected for processing (7)
README.mddocs/06-discord-webhook-setup.mddocs/bedrock-model-comparison.mddocs/superpowers/plans/2026-08-08-sqlite-s3-agent-tutorial-pr4-reader-bugfixes.mddocs/superpowers/specs/2026-08-08-sqlite-s3-agent-tutorial-design.mdsrc/agent/status.tstests/status.test.ts
🚧 Files skipped from review as they are similar to previous changes (4)
- README.md
- docs/bedrock-model-comparison.md
- docs/superpowers/specs/2026-08-08-sqlite-s3-agent-tutorial-design.md
- docs/superpowers/plans/2026-08-08-sqlite-s3-agent-tutorial-pr4-reader-bugfixes.md
- .gitignore: add .env.discord so the deployment secret file is untracked - docs/06-discord-webhook-setup.md: replace the incorrect logs:GetLogEvents cold-start claim with the real exposure path (lambda:GetFunctionConfiguration + CloudFormation templates + cdk.out/) and call out CloudWatch log access as a separate concern that only matters if application code echoes the URL - docs/06-discord-webhook-setup.md: align the rate-limit guidance with src/discord/poster.ts — 429 throws DiscordPostError and is recorded as a per-source failure in agent_runs.error unless bounded 429 handling is added Co-Authored-By: Claude <noreply@anthropic.com>
/fix-pr follow-upCommit: Review resolution
Verification
|
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 11 out of 12 changed files in this pull request and generated 2 comments.
Suppressed comments (8)
docs/bedrock-model-comparison.md:187
- The
zaifamily already exists insrc/format/families.ts:17-23; only the recommended DeepSeek model lacks a family entry. As written, this tells readers to add a duplicate Z.AI family before adopting the recommendations.
- Adopting these requires new `zai` and `deepseek` family entries in `src/format/families.ts`
and matching resource ARNs in `infra/stack.ts`, or invocation fails with AccessDenied.
docs/bedrock-model-comparison.md:112
- The tutorial has no
INGEST_SYSTEM_PROMPTor ingest workflow (the only application prompt is the formatter path), so this replacement still attributes a nonexistent prompt to this repository. Describe this solely as a synthetic benchmark and explicitly say the tutorial has no ingest request.
treated as a **synthetic proxy** rather than a measurement of this tutorial's
real request. The tutorial's `INGEST_SYSTEM_PROMPT` itself is too tightly bound to
the writer's schema to reuse as a generic benchmark, so the proxy is used only to
sanity-check output-token counts; the application-specific ingest cost row is
explicitly **excluded** from the recommendations below.
docs/superpowers/plans/2026-08-08-sqlite-s3-agent-tutorial-pr4-reader-bugfixes.md:324
- This prescribed spec text reintroduces the invalid warm-failure state: a prior string ETag paired with
db === undefined. It also conflicts with the actual §4.3.1 text, which correctly says both fields are cleared. Update this code block so implementers do not copy the bug back into the spec.
- **`openReadOnlyDatabase` throws** (corrupted snapshot, non-SQLite bytes, disk error): `state.db` stays `undefined`, `state.cachedEtag` stays at its prior value (or `null` on a cold start). The error propagates up — the Lambda returns 500, the next call retries the cache-miss path from scratch.
src/agent/status.ts:136
- The new tests never reach this assignment with a failing open: one fails in
store.get, and the other returnsnull. A regression that movescachedEtagback beforeopenReadOnlyDatabasewould therefore pass both tests. Add a warm-cache test that returns corrupt SQLite bytes, asserts the open rejects and the state is(null, false), then verifies recovery with valid bytes.
state.db = openReadOnlyDatabase(dbPath);
state.cachedEtag = object.etag;
docs/06-discord-webhook-setup.md:84
- The deployed handler does not log
RunFetchResultor per-source errors, so CloudWatch will not contain this diagnostic; onlysrc/localFetch.tsprints the returned result. Direct deployed users to manually invokefetchand inspect its returned JSON (or add explicit logging) instead of promising an error in CloudWatch.
Run `npm run local-fetch` once. Within a few seconds you should see a post in the
Discord channel. If you don't see one, check the CloudWatch logs (when deployed) or
the script's stdout (when running locally) — the `agent_runs.error` column captures
per-source failures including Discord post failures.
docs/bedrock-model-comparison.md:10
- This provenance now incorrectly says the tutorial uses
low/med/hightiers. The actual config exposes onebedrockModelIdand onebedrockMaxOutputTokens(src/config.ts:103-124), while the rest of this document still discusses nonexistent tier prompts and workflows. Reframe the file as historical/general research and clearly state that the tier recommendations do not describe this tutorial.
This issue also appears in the following locations of the same file:
- line 108
- line 186
> is a starting point for any reader picking a Bedrock model. The tier recommendations
> at the bottom of the file are tutorial-specific — they cover the `low`/`med`/`high`
> tiers the tutorial uses. Adding a new tier or pointing an existing tier at a model
> from a different family requires an entry in `src/format/families.ts` (verified by a
> live probe with a negative control, not by reading model cards) and a matching
> resource ARN in `infra/stack.ts`.
docs/superpowers/plans/2026-08-08-sqlite-s3-agent-tutorial-pr4-reader-bugfixes.md:9
- This sentence contradicts the invariant immediately before it: after a warm-cache open failure, retaining the prior string ETag while
dbis undefined is exactly the invalid combination this PR fixes. The plan’s architecture summary should say the failure leaves both fields empty.
This issue also appears on line 324 of the same file.
**Architecture:** The two bugs share one root cause: the cache-miss branch in `src/agent/status.ts` updates `state.cachedEtag` and `state.db` in an order that allows a partial failure to leave them in an inconsistent combination. The invariant the cache must preserve is "either both fields are populated and the pair is valid, or both are null/undefined." A failing `openReadOnlyDatabase` should leave `cachedEtag` at its prior value (not the new one). The HEAD-succeeds-but-GET-fails branch should reset `cachedEtag` to `null` so the next call retries cleanly. The retry loop on a permanently broken S3 object is unchanged — it terminates when the operator or the writer fixes the underlying object.
src/agent/status.ts:42
- This “test-only” member is part of the exported
StatusReaderinterface, so it is in fact a required public contract: downstream implementations/mocks must provide it and production callers can depend on it. Keep the invariant probe in a test harness or another non-public seam rather than expanding the production API solely for assertions.
* Test-only: returns a snapshot of the internal reader state for assertions on the
* cache invariant (spec §4.3.1). Production code must not rely on this — it exposes
* implementation detail that the public contract deliberately does not promise.
*/
__peekReaderState(): { cachedEtag: string | null; dbIsOpen: boolean };
- README.md: source DISCORD_WEBHOOK_URL from .env in the quick start
rather than embedding the URL in the shell command (shell history leak).
- plan: fix proposed test code that referenced seeded.body — seedSnapshot
returns store.put() which is { etag }, not { body, etag }.
Co-Authored-By: Claude <noreply@anthropic.com>
/fix-pr follow-upCommit: Review resolution
The 14 CodeRabbit and Copilot threads from earlier reviews are already resolved and acknowledged via the "Addressed in commit" markers; this pass only addressed the two threads that were still open at run time. Verification
|
Addresses the post-merge review of PR3 (
src/agent/status.ts:111andsrc/agent/status.ts:119) plus the doc cleanup flagged in the same review.The two reader bugs share one root cause: the cache-miss branch in
src/agent/status.tsupdates state.cachedEtag and state.db in an order thatallows a partial failure to leave them in an inconsistent combination. The fix
preserves the spec's documented invariant (either both fields are populated and
the pair is valid, or both are null/undefined) with the smallest possible diff
and no new error semantics — the retry loop on a permanently broken S3 object is
unchanged.
Doc cleanup: the tutorial referenced a private sibling repo (
aws-cloud-agent)in multiple places, producing dead links for the public audience. Removed.
Added
docs/06-discord-webhook-setup.mdto fill the gap I hit on first run —the README mentions the env var but doesn't explain how to obtain the URL.
� Generated with Claude Code