Skip to content

fix(cli): scope squad cost to the shell's Squad session - #2348

Merged
tamirdresher merged 2 commits into
github:devfrom
joslat:joslat/2347-cost-squad-session
Oct 28, 2026
Merged

tamirdresher merged 2 commits into
github:devfrom
joslat:joslat/2347-cost-squad-session

Conversation

@joslat

@joslat joslat commented Oct 26, 2026

Copy link
Copy Markdown
Contributor

What

Plain squad cost now scopes the current session with a session id the shell writes into the usage ledger, instead of Scribe's session logs. The summary counts Squad sessions instead of SDK sessions, and the cost-tracking docs describe how it works today.

Why

Closes #2347

The usage comes from the ledger, which only the interactive shell writes. The session start came from the newest .squad/log/*.md file, a Scribe session log that the shell never writes. With no log, plain squad cost reports every session ever recorded. A log written by a Copilot session during a shell run hides the first part of that run. Once #2322 lands, nothing asks for session logs at all, so this needs to go in first (Tamir's comment on #2322).

How

  • SDK, runtime/usage-ledger.ts.
    • UsageLedgerRecord, UsageLedgerTurnStart and UsageTurnStart get an optional squadSessionId.
    • append() takes an optional second argument, UsageRecordContext, because the runtime UsageEvent doesn't know about Squad sessions.
    • The validators accept the field only as a non-empty string.
    • USAGE_LEDGER_VERSION stays 1.
  • Shell, shell/index.ts. The three ledger writes pass squadSessionId: persistedSession.id, the shell session that /resume restores. It's read at write time, so a /resume applies from the next turn.
  • CLI, commands/cost.ts.
    • The current session is the squadSessionId of the newest ledger row, and plain squad cost keeps only rows with that id. Session logs are ignored then.
    • If the newest row has no id (written by an older Squad), the old log/*.md cutoff applies unchanged. The legacy markdown fallback also keeps that cutoff.
    • --all is unchanged.
    • The summary counts squadSessionId ?? sessionId.
  • Docs, features/cost-tracking.md. The page said usage lives in orchestration-log Token usage rows (the template has no such row any more) and showed an output format that no longer exists. It now covers the ledger, which hosts write it, how the current session is picked, and real output.

Compatibility

  • Older CLIs read the new records: the field is just ignored. I checked this by running dev's UsageLedger.read() on stamped rows: 0 invalid.
  • Older ledgers fall back to today's behaviour.
  • No new record kind: older CLIs would count one as invalid.

Known limits

  • Two shells open at once in one repo: plain squad cost follows whichever wrote last. A --session <id> flag could be a follow-up.
  • Resumed sessions: the shell resumes a session that was active in the last 24 hours, so a session can span several launches. That's the same thing /resume calls a session.
  • Copilot CLI and VS Code still don't record usage. That's unchanged.

Testing

New tests:

  • test/usage-ledger.test.ts: the id is stamped on turn starts and usage; an empty id is rejected before anything is written.
  • test/cli/cost.test.ts, with ledgers shaped like the shell's writes (coordinator plus one SDK session per agent):
    • plain squad cost picks the newest Squad session even with a session log in the middle of it;
    • --all reports 2 sessions for two shell runs, not 5;
    • an unstamped newest row falls back to the session-log cutoff;
    • --agent doesn't reach into older sessions.

Results:

  • With the source change reverted, 5 of the 6 new tests fail. The fallback test passes both ways, since it guards the old behaviour.
  • usage-ledger, cli/cost and the tests that load the shell (signal-handling, ghost-response, init-autocast, regression-368, repl-streaming, sdk-failure-scenarios, stress): 212 passed, and the tracked-file guard from Guard tests against tracked file changes #2308 stays quiet.
  • npm run lint (after building the SDK): clean. ESLint on the changed files: 0 errors.
  • markdownlint and cspell on the docs page and changeset: clean.

⚠️ Quick Check

  • If SDK/CLI source files changed: changeset added (.changeset/cost-squad-session.md, cli and sdk patch)

PR Readiness Checklist

Branch & Commit

  • Branch created from dev (not main)
  • Branch is up to date with dev (938e56b)
  • Verified diff contains only intended changes (7 files)
  • PR is not in draft mode
  • Commit history is clean

Build & Test

  • npm run lint passes
  • Relevant tests pass
  • No file overlap with any open PR

Docs

  • features/cost-tracking.md updated

Breaking Changes

None. The new field is optional, and USAGE_LEDGER_VERSION is unchanged. UsageLedger.append() gains an optional second parameter. Plain squad cost reports a different (correct) range once the shell has written stamped records.

Waivers

None.

Plain `squad cost` read usage from the ledger, which only the
interactive shell writes, but took the session start from the newest
Scribe session log in .squad/log/, which the shell never writes. With
no log it reported every session; a log written by another host during
a shell run hid that run's earlier turns.

- the ledger records get an optional squadSessionId; the shell stamps
  turn starts and usage with its persisted session id (the one /resume
  restores);
- squad cost scopes to the Squad session of the newest ledger row, and
  falls back to the session-log cutoff when that row has no id;
- the summary counts Squad sessions instead of SDK sessions;
- cost-tracking.md describes the ledger and shows the real output.

USAGE_LEDGER_VERSION stays 1; older readers ignore the new field.

Closes github#2347
@github-actions

Copy link
Copy Markdown
Contributor

🟠 Impact Analysis — PR #2348

Risk tier: 🟠 HIGH

📊 Summary

Metric Count
Files changed 7
Files added 1
Files modified 6
Files deleted 0
Modules touched 5
Critical files 1

🎯 Risk Factors

  • 7 files changed (6-20 → MEDIUM)
  • 5 modules touched (5-8 → HIGH)
  • Critical files touched: packages/squad-cli/src/cli/shell/index.ts

📦 Modules Affected

docs (1 file)
  • docs/src/content/docs/features/cost-tracking.md
root (1 file)
  • .changeset/cost-squad-session.md
squad-cli (2 files)
  • packages/squad-cli/src/cli/commands/cost.ts
  • packages/squad-cli/src/cli/shell/index.ts
squad-sdk (1 file)
  • packages/squad-sdk/src/runtime/usage-ledger.ts
tests (2 files)
  • test/cli/cost.test.ts
  • test/usage-ledger.test.ts

⚠️ Critical Files

  • packages/squad-cli/src/cli/shell/index.ts

This report is generated automatically for every PR. See #733 for details.

@github-actions

github-actions Bot commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

🛫 PR Readiness Check

ℹ️ This comment updates on each push. Last checked: commit ceca8cf

PR Scope: 📦🔧 Mixed (product + infrastructure)

⚠️ 3 item(s) to address before review

Status Check Details
❌ Single commit 2 commits — consider squashing before review
✅ Not in draft Ready for review
✅ Branch up to date Up to date with dev
❌ Copilot review No Copilot review yet — it may still be processing
✅ Changeset present Changeset file found
✅ Scope clean No .squad/ or docs/proposals/ files
✅ No merge conflicts No merge conflicts
✅ Copilot threads resolved No Copilot review threads
❌ CI passing 14 check(s) still running

Files Changed (7 files, +240 −38)

File +/−
.changeset/cost-squad-session.md +6 −0
docs/src/content/docs/features/cost-tracking.md +27 −22
packages/squad-cli/src/cli/commands/cost.ts +42 −12
packages/squad-cli/src/cli/shell/index.ts +3 −1
packages/squad-sdk/src/runtime/usage-ledger.ts +24 −3
test/cli/cost.test.ts +91 −0
test/usage-ledger.test.ts +47 −0

Total: +240 −38


This check runs automatically on every push. Fix any ❌ items and push again.
See CONTRIBUTING.md and PR Requirements for details.

@joslat joslat mentioned this pull request Oct 26, 2026
12 of 13 tasks
@tamirdresher
tamirdresher merged commit 017e585 into github:dev Oct 28, 2026
23 checks passed
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.

squad cost finds the current session from Scribe's session logs, which the shell never writes

2 participants