Repository navigation
Conversation
12e2cad to
3405503
Compare
|
@Stener1 is attempting to deploy a commit to the seanbrydonio's projects Team on Vercel. A member of the Team first needs to authorize it. |
3405503 to
f707c3b
Compare
|
Rebased onto main ( Checks on the rebased head |
f707c3b to
84bf934
Compare
There was a problem hiding this comment.
3 issues found across 17 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="plugins/usage/README.md">
<violation number="1" location="plugins/usage/README.md:54">
P2: The Usage screen does not show an indefinitely old reading with its age: `dedupeLimits` drops readings before the selected usage period, so an account unused beyond that period has no limits displayed. Update this statement or retain limit readings independently of the token period.</violation>
</file>
<file name=".claude/skills/verify-berthd/scripts/claude-usage.sh">
<violation number="1" location=".claude/skills/verify-berthd/scripts/claude-usage.sh:17">
P2: `dir` is interpolated unquoted into the shell command that tmux runs, so valid config paths containing spaces fail and metacharacters can execute commands. Pass `CLAUDE_CONFIG_DIR` through tmux's environment option instead of embedding it in the command string.</violation>
</file>
<file name=".claude/skills/verify-berthd/scripts/scenarios/usage-limits-live.mjs">
<violation number="1" location=".claude/skills/verify-berthd/scripts/scenarios/usage-limits-live.mjs:62">
P2: Session creation runs outside the cleanup scope, so a failed or timed-out POST can leave `limits` running without ever reaching the DELETE. Put creation inside the cleanup flow and reconcile ambiguous POST failures before returning.</violation>
</file>
Reply with feedback, questions, or to request a fix.
View guided diff | Re-trigger cubic
| # …then print your status line from "$in" as before. | ||
| ``` | ||
|
|
||
| The account is `default` for `~/.claude` and the folder's name for `~/.berth/accounts/claude/<name>`. A file for any other name is ignored. The screen shows the limits as of `at`, so an account no agent has used for a while shows an old reading, labelled with its age. Any other keys in the file are ignored. |
There was a problem hiding this comment.
P2: The Usage screen does not show an indefinitely old reading with its age: dedupeLimits drops readings before the selected usage period, so an account unused beyond that period has no limits displayed. Update this statement or retain limit readings independently of the token period.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At plugins/usage/README.md, line 54:
<comment>The Usage screen does not show an indefinitely old reading with its age: `dedupeLimits` drops readings before the selected usage period, so an account unused beyond that period has no limits displayed. Update this statement or retain limit readings independently of the token period.</comment>
<file context>
@@ -26,10 +26,32 @@ The numbers come from the files each agent writes on the box. Nothing is estimat
+# …then print your status line from "$in" as before.
+```
+
+The account is `default` for `~/.claude` and the folder's name for `~/.berth/accounts/claude/<name>`. A file for any other name is ignored. The screen shows the limits as of `at`, so an account no agent has used for a while shows an old reading, labelled with its age. Any other keys in the file are ignored.
### How it runs on the box
</file context>
| The account is `default` for `~/.claude` and the folder's name for `~/.berth/accounts/claude/<name>`. A file for any other name is ignored. The screen shows the limits as of `at`, so an account no agent has used for a while shows an old reading, labelled with its age. Any other keys in the file are ignored. | |
| The account is `default` for `~/.claude` and the folder's name for `~/.berth/accounts/claude/<name>`. A file for any other name is ignored. The screen shows limits only when their `at` timestamp falls within the selected usage period; older readings are omitted. Any other keys in the file are ignored. |
There was a problem hiding this comment.
Fixed in 8bda77d, in the README: Claude Code's limits follow the same period rule as Codex's (the comment on dedupeLimits says this is on purpose), so the text now says a reading shows with its age as long as at falls in the period on screen, and an account unused in that period shows none.
|
|
||
| step("one cheap structured turn on acme-max"); | ||
| const started = Date.now(); | ||
| await call(`/v1/boxes/${BOX}/api/sessions`, { method: "POST", body: JSON.stringify({ name: SESSION, location: "acme-shop", agent: "claude", driver: "claude", model: "haiku", prompt: "Reply with just: ok" }) }); |
There was a problem hiding this comment.
P2: Session creation runs outside the cleanup scope, so a failed or timed-out POST can leave limits running without ever reaching the DELETE. Put creation inside the cleanup flow and reconcile ambiguous POST failures before returning.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At .claude/skills/verify-berthd/scripts/scenarios/usage-limits-live.mjs, line 62:
<comment>Session creation runs outside the cleanup scope, so a failed or timed-out POST can leave `limits` running without ever reaching the DELETE. Put creation inside the cleanup flow and reconcile ambiguous POST failures before returning.</comment>
<file context>
@@ -0,0 +1,100 @@
+
+ step("one cheap structured turn on acme-max");
+ const started = Date.now();
+ await call(`/v1/boxes/${BOX}/api/sessions`, { method: "POST", body: JSON.stringify({ name: SESSION, location: "acme-shop", agent: "claude", driver: "claude", model: "haiku", prompt: "Reply with just: ok" }) });
+ try {
+ await checkLimits({ page, shot, step, expect, started });
</file context>
There was a problem hiding this comment.
Fixed in 8bda77d: the POST is inside the try now, so the finally DELETE runs even when starting the session throws or times out. The DELETE already ignores a session that never started.
… limits Claude Code passes its plan limits (rate_limits.five_hour and seven_day) only to a status line. usage.py now reads them from ~/.berth/limits/<account>.json, which a status line writes, and reports them in Codex's window shape, so the Usage tile and the Home widget show them beside Codex's. The plugin README has the status line lines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…d agents A structured Claude Code agent (claude -p) runs no status line, so the limits file the Usage plugin reads stayed empty on a box whose agents all run that way. Each of its turns says the account's plan limits in a rate_limit_event; berthd now writes them to ~/.berth/limits/<account>.json as they come, keeping the newest reading and any window a turn leaves out. A log read back after a restart writes nothing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…n a real box usage-limits-live turns the Usage plugin on, puts a dev box's new sessions on a linked account, runs one Haiku turn and checks the limits file, Claude Code's own /usage (claude-usage.sh) and the screen's meters agree. dev-box.sh can give the box a HOME of its own (VERIFY_BOX_HOME) with a signed-in account linked in as acme-max, and drive.mjs's shot() can keep a single element. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…only for known accounts - The Usage screen and Home widget pick limits by the box's own day, as they do tokens, instead of the UTC date of the reading (a reading just after local midnight east of UTC dropped out of Today, Codex's too). - The box records limits only for ~/.claude and ~/.berth/accounts/claude/*, the accounts the plugin knows, so a folder elsewhere named like one isn't taken for it. The README's status-line lines do the same and write through a temp file of their own. - verify-berthd: claude-usage.sh passes CLAUDE_CONFIG_DIR through tmux's environment and empties its default folder; the live scenario removes its session even when starting it fails and rejects a bad timestamp; its commands run from the repository root. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018aGVrwQaYK6hFHuTMpR2wB
84bf934 to
8bda77d
Compare
Summary
The Usage screen showed Codex's 5-hour and weekly limits, but none for Claude Code. Claude Code doesn't write its limits to any file the plugin can read.
The agents Shipyard runs are structured (
claude -p, stream-json). Every turn they take emits arate_limit_event, which berthd already logs and then dropped:{"type":"rate_limit_event","rate_limit_info":{"status":"allowed","resetsAt":1793124000,"rateLimitType":"five_hour", "unifiedWindows":{"five_hour":{"utilization":0.84,"resetsAt":1793124000,"status":"allowed"}, "seven_day":{"utilization":0.29,"resetsAt":1793638800,"status":"allowed"}}}}This PR has berthd record those readings itself, and the plugin shows them.
live == false). Those readings are old, so berthd doesn't write them.rate_limit_eventarrives. Both writers use one shape ({"at", "limits": {"five_hour": {"used_percentage", "resets_at"}, …}}, plusaccountanddirfrom berthd). The reader ignores unknown keys, and berthd reads a file with extra keys (a status line's, another tool's) and writes it afresh when it's broken.features/usage-limits.md,scenarios/usage-limits-live.mjsandscripts/claude-usage.sh. The last one reads/usageas ground truth.dev-box.shgainsVERIFY_BOX_HOME/VERIFY_CLAUDE_ACCOUNT, so a dev box can have a HOME of its own (it never writes the real~/.berth/limits) with a signed-in account linked in under a neutral name.drive.mjs'sshot(name, locator)can now capture just one element.The open question from the first version
The first version left people to add a status-line snippet themselves, and asked whether berthd should take over the status line. It doesn't need to. Structured agents report their limits on every turn, so berthd records them with no setup. The snippet stays as an option only for sessions on Claude Code's own screen.
Follow-up (not in this PR)
berthd headlessrun steps (judges,internal/box/headless.go) also runclaude -p, and they could record the same event. This PR covers the structured driver only.Evidence
Real box, end to end (
verify-berthd, scenariousage-limits-live, ondev-box.sh upwith its own HOME; the test account linked in asacme-max):~/.berth/limits/acme-max.json: 5-hour 51%, weekly 23%,at2026-10-28T16:09:30.364Z./usagefor the same account, read seconds later (claude-usage.sh): 5-hour 51%, weekly 23%.5-hour limit= 51 andWeekly limit= 23. No console errors.On an account other agents were using at the same time, the file and
/usageagreed or differed by one point (/usageonce showed 23% weekly when the event said 0.22). The scenario allows that one point; the screen has to match the file exactly.Go:
internal/box/claudedriver_test.godrives the stand-in Claude through a served box with a temp HOME:resets_atin seconds);TestPlanLimitsKeepTheLatestReadingcovers four cases: an older reading, an empty one, another writer's file with extra keys, and a broken file.TestAClaudeAccountIsNamedAsTheUsagePluginNamesItcovers the naming. With the newcaseremoved, or the log read back as live, the end-to-end test fails.Checks, run locally:
go vet ./...passes.go test -race ./...passes, exceptTestTheCatalogIsCapped, which counts the commands on the machine it runs on and fails onmainthere too.cargo checkpasses (on macOS, on this branch's tree).pnpm typecheck:plugins,check:titles,check:type,check:grid,check:csp,check:themesandpnpm buildpass.pnpm testpasses (618, plus 24 plugin tests).npx tsc -p e2eand thedocs-sitebuild pass.Ask
Could a maintainer add the
run-cilabel? I can't add labels on this repo.🤖 Generated with Claude Code