Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
716da84
docs(spec): add loop mode + poetic closing design
claude Aug 9, 2026
dbc9887
fix(spec): tighten three points after self-review
claude Aug 9, 2026
8414c4f
fix(spec): strip dedup; loop-start/stop disable EventBridge rule
claude Aug 9, 2026
c1749a3
fix(spec): require LOOP_RULE_NAME whenever LOOP_TOKEN is set
claude Aug 9, 2026
171c6cc
fix(spec): one combined message per tick; auto-generate LOOP_TOKEN
claude Aug 9, 2026
51fa252
spec: change loop cadence from 3 minutes to 5 minutes
claude Aug 9, 2026
e0fb955
fix(spec): add base_message column to prevent suffix snowball
claude Aug 9, 2026
17f2025
docs(spec): mark loop mode + poetic closing as approved
claude Aug 9, 2026
993f4fa
feat(schema): add base_message column for snowball-free RAG suffix
claude Aug 9, 2026
2d708ff
feat(rag): global KNN returns baseMessage, filter null base_message
claude Aug 9, 2026
a470937
refactor(format): introduce LoopContext, remove SimilarPastResult
claude Aug 9, 2026
efb7b6a
refactor(format): local template uses LoopContext, drops LABELS
claude Aug 9, 2026
80e15c6
feat(format): Bedrock formatter uses LoopContext, system prompt asks …
claude Aug 9, 2026
a992bd7
feat(fetch): combined-message + two-step RAG + base_message separation
claude Aug 9, 2026
ebe652e
feat(fetch): thread weatherLocation through localFetch and handler
claude Aug 9, 2026
358ac75
test(handler): assert one Converse call per tick with two sources
claude Aug 9, 2026
38ce6a0
feat(infra): 5-min schedule, LoopRuleName output, 60s lambda timeout
claude Aug 9, 2026
b84a76e
feat(scripts): loop-start.sh and loop-stop.sh toggle EventBridge rule
claude Aug 9, 2026
aa78838
chore(package): expose loop-start and loop-stop npm scripts
claude Aug 9, 2026
d02f425
docs(readme): add Loop mode subsection with start/stop scripts
claude Aug 9, 2026
7294005
docs(budget): note 5-min loop Bedrock-call rate and row growth
claude Aug 9, 2026
0646b7c
docs(rehydration): note Lambda /tmp storage ceiling at loop cadence
claude Aug 9, 2026
2e0dcf7
docs(spec): land loop-mode + poetic-closing plan and implemented spec
claude Aug 9, 2026
2dccc00
fix(pr): address CodeRabbit review comments
claude Aug 9, 2026
77906d9
fix(pr): address CodeRabbit round 2 review comments
claude Aug 9, 2026
a77ec71
feat(loop): add loop-status script and docs
claude Aug 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 30 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,36 @@ manual *Bedrock → Model access* console flow is no longer the gate for this mo
[docs/02-rehydration.md](docs/02-rehydration.md#bedrock-setup) for what else is required and
what breaks if you skip it.

## Loop mode

The deployed EventBridge schedule runs every 5 minutes by default. Use `loop-stop` to
pause the loop, and re-run `loop-start` only after `loop-stop` to resume it. Both
scripts toggle the EventBridge rule directly from your shell — no Lambda invocation,
no token, no extra IAM grants:

```bash
npm run loop-start # calls `aws events enable-rule` on the deployed rule
npm run loop-stop # calls `aws events disable-rule` — no further ticks
npm run loop-status # reads `aws events describe-rule` — no side effects
```

While the loop is running, each tick posts one combined Discord message: a short
friendly comment drawn from today's date, weather, and crypto price, ending with a
haiku. If a past message in the corpus is close enough, the LLM's pre-suffix output
is mechanically appended with a `Reminds me of: <past message>` line. All three
scripts read the rule name from the `LoopRuleName` stack output and call the
EventBridge API directly using the same AWS CLI credentials the smoke script
already requires. `loop-status` is read-only — print the rule's current
`ENABLED`/`DISABLED` state plus its schedule expression and ARN.

**Stop the loop when you're done** — `loop-stop.sh` disables the EventBridge rule so
no further invocations occur and the recurring AWS cost stops. Note: running
`npm run deploy` after `loop-stop.sh` re-enables the rule, since the CDK stack
declares it `enabled: true` — run `npm run loop-status` after any redeploy to
confirm the state, then re-run `loop-stop.sh` if you want the loop to stay off. See
[docs/07-budget-protection.md](docs/07-budget-protection.md) for the per-day
Bedrock call rate at 5-min cadence.

## Triggering a fetch on demand

The daily `fetch` run is normally EventBridge's job, but you can also trigger one over
Expand Down
26 changes: 26 additions & 0 deletions docs/02-rehydration.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,32 @@ happens if you just overwrite `/tmp/memory.db` without closing first — the han
cache goes stale silently. Queries keep succeeding; they just return wrong answers. Closing
first is what prevents that.

## 4. `/tmp` storage ceiling

The hydration pattern depends on a local file at `/tmp/memory.db`, which lives on
Lambda's ephemeral local storage — not on S3 and not on any volume that persists across
invocations. That storage has a hard ceiling:

- **Default:** 512 MB. `/tmp` is provisioned at this size unless you override it.
- **AWS maximum:** 10,240 MB (10 GB), set via the `ephemeralStorage` prop on
`aws-cdk-lib/aws-lambda`'s `DockerImageFunction`. `infra/stack.ts` does not override
the default, so a fresh deploy runs at 512 MB.

A snapshot row plus its embedding is roughly 4 KB on disk. 512 MB holds about 131,000
ticks' worth of rows — enough that the file still fits in `/tmp` after about 450 days
of running the 5-minute loop (`npm run loop-start`, 288 ticks/day — see the README's Loop
mode section). Past that, the writer's local `writeFileSync` of the hydrated snapshot
to `/tmp/memory.db` fails with `ENOSPC: no space left on device` (the S3 `GetObject`
itself succeeds — the bytes are in memory by then), and the writer publishes nothing
until a redeploy resurfaces a fresh container with an empty `/tmp`.

If you intend to leave the loop running unattended for longer than that, set
`ephemeralStorage: Size.gibibytes(10)` on `agentFunction` in `infra/stack.ts` and
redeploy. At the AWS cap the same math gives roughly 2.6 million ticks of headroom —
about 25 years at 5-minute cadence. The RAG corpus (`agent_notifications` +
`agent_embeddings`) is the dominant growth term; status reads and `agent_runs` rows are
small by comparison.

## Bedrock setup

Before the first `fetch` invocation can succeed, the deploying account in `us-east-1` needs
Expand Down
12 changes: 10 additions & 2 deletions docs/07-budget-protection.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# Budget protection

This tutorial's normal cost is near zero — see [the README's Cost section](../README.md#cost).
But three things can push it above "near zero" if misconfigured, and none of them are
But four things can push it above "near zero" if misconfigured, and none of them are
caught by anything else in this repo: they're all *valid* requests that just happen more
often than intended.

Expand All @@ -21,13 +21,21 @@ often than intended.
- **`SOURCES` or `BEDROCK_MAX_OUTPUT_TOKENS` misconfiguration.** More sources checked per
run, or a much larger output token cap, both scale cost linearly and both are one
environment variable away from the defaults.
- **Loop mode running unattended.** Switching the 5-minute loop on (`npm run loop-start`)
drives ~576 Bedrock calls per day (1 Converse + 1 Titan per tick × 288 ticks/day) and
grows both `agent_notifications` and `agent_embeddings` by ~576 rows each per day
(~1,152 rows/day combined). At default model pricing this is roughly $0.02–$0.04/day,
but a loop left running for a weekend amplifies the spend noticeably. `npm run
loop-stop` disables the EventBridge rule so no further ticks fire — re-run it after
any `npm run deploy` that re-enables the rule (see the redeploy caveat in the
README's Loop mode section).
Comment thread
coderabbitai[bot] marked this conversation as resolved.

None of these are bugs this codebase can prevent by construction — they're operator
error, and the right backstop for operator error is a spending alarm, not more code.

## Set up an AWS Budget

A cost budget with an email (or SNS) alert takes a few minutes and catches all three cases
A cost budget with an email (or SNS) alert takes a few minutes and catches all four cases
above, since all of them show up as spend regardless of which one caused it.

Console: **Billing and Cost Management → Budgets → Create budget → Cost budget**. Suggested
Expand Down
Loading