docs: document the 0.7.0 surfaces (ledger, hooks, bridge, tj init) - #774
Conversation
0.7.0 is the largest OSS release in months and its new surfaces were undocumented in the repo's own docs. A user who upgrades and runs `tj optimize shipped` had nowhere to read what a confidence level means, what the commit hooks write into their repo, or what the Cloud bridge sends. That gap is the kind that makes a good feature look untrustworthy. Adds a `docs/ledger/` area covering the session-to-commit join, the optional git hooks and notes, and the bridge with the emission list it prints before the first byte leaves. Extends the CLI reference with the `tj init` name and its new flags, `tj commit-note`, and `tj backfill status` (which shipped in July and was never documented). Adds the repo context and developer identity attributes to the architecture reference's OTel semconv section, where only billing_account and plan_tier were listed. Every command documented here was verified with `--help` in a worktree rather than taken from the brief; `tj commit-note --json` does not parse, so the reference shows `tj --json commit-note` (Critical Rule 44). The shipped analyzer page was corrected where the implementation moved past it: an inherited trailer now ranks below the tool span that ran the commit, and refs/notes/tokenjam is read as well as written. RELEASE_NOTES_0.7.0.md is a working draft for the owner to paste into the GitHub release, not a file the package ships. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
| policy would have done; nothing is blocked or rewritten until you approve it. Subscription-plan | ||
| traffic is never intercepted and subscription OAuth credentials are never proxied or forwarded. |
There was a problem hiding this comment.
Subscription traffic reaches proxy
With --enforce, Claude Code’s provider URLs point to the proxy. Subscription requests are forwarded unmodified, but they still pass through the listener and its observation path. Saying they are “never intercepted” gives users an incorrect basis for deciding whether to enable the proxy; describe them as observe-only instead. The same assurance appears in the release notes and Cloud bridge page.
How this was verified: The enable path rewires provider URLs to the proxy, and the proxy forwards observe-only requests while recording the forwarding decision.
Prompt To Fix With AI
This is a comment left during a code review.
Path: docs/cli-reference.md
Line: 57-58
Comment:
**Subscription traffic reaches proxy**
With `--enforce`, Claude Code’s provider URLs point to the proxy. Subscription requests are forwarded unmodified, but they still pass through the listener and its observation path. Saying they are “never intercepted” gives users an incorrect basis for deciding whether to enable the proxy; describe them as observe-only instead. The same assurance appears in the release notes and Cloud bridge page.
**How this was verified:** The enable path rewires provider URLs to the proxy, and the proxy forwards observe-only requests while recording the forwarding decision.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| **Leaves the machine:** token counts, model names, cost, timestamps, tool names, the file *paths* | ||
| touched, session / repo / branch / commit identifiers, the hashed developer id, the git author | ||
| email. | ||
|
|
||
| **Never leaves by default:** prompt text, completions, tool outputs, file contents, diffs, secrets. | ||
|
|
||
| **Never, under any setting:** your subscription OAuth credentials are never proxied or forwarded, | ||
| and subscription-plan traffic is never intercepted. | ||
|
|
||
| Content crosses only when two independent switches are both on: your local `[capture]` toggles must | ||
| be keeping it, and `[cloud] forward_content` must be `true`. The strip runs on the sending side, so | ||
| the promise does not depend on the receiver, and it sweeps for content-shaped keys beyond the named |
There was a problem hiding this comment.
Forwarding disclosure omits defaults
This disclosure omits host.name, which is sent with spans when available. It also does not say that prompt and tool-input capture are already enabled by default: turning on forward_content can send those captured fields without changing [capture]. Naming both details here would help users make an informed forwarding choice.
Prompt To Fix With AI
This is a comment left during a code review.
Path: docs/ledger/cloud-bridge.md
Line: 23-34
Comment:
**Forwarding disclosure omits defaults**
This disclosure omits `host.name`, which is sent with spans when available. It also does not say that prompt and tool-input capture are already enabled by default: turning on `forward_content` can send those captured fields without changing `[capture]`. Naming both details here would help users make an informed forwarding choice.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
Three claims that a reader following them would have been stopped by. `tj init --reconfigure` on its own exits 1 against an existing config: plan tier is per provider and lives in `[budget.<provider>]`, which only the `--claude-code` / `--codex` flows write, so the bare form refuses rather than silently no-op'ing. The reference now shows the paired form and states the requirement. The architecture section implied `tj otel-resource-attrs` emits the ledger attributes. It prints `service.name` and, when the project is set in config, `service.namespace`, and nothing else. Since the per-terminal claude wrapper exports that output as OTEL_RESOURCE_ATTRIBUTES, a reader would have concluded the repo identity, email and developer id travel that way. They do not: for Claude Code and Codex they are derived at ingest from the transcript's cwd and branch and written onto the session row; an SDK or OTLP producer may stamp the names itself; and the Cloud bridge maps the stored columns back onto them on the wire, adding install id and host name there. The section now says which path does what, and the same claim is corrected in the ledger overview. `tokenjam.github_login` is marked reserved, since nothing in this build sets it. The post-commit hook reads the session cost through the daemon, so it returns in milliseconds when `tj serve` is not up and that commit gets no note. Documented, along with the point that the trailer is written either way (the join is unaffected, only the cost annotation is missing) and the `tj commit-note <sha>` fallback for filling one in later. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Two claims from the last docs pass that do not survive reading the source. The `tj commit-note` fallback was wrong, and wrong because of the fix that introduced it. The previous change documented that the post-commit HOOK checks for a live daemon, which is true, and then carried that dependency onto the manual command, which does not have it: `cmd_commit_note` resolves its session through the ordinary `ctx.obj["db"]` seam (it is not in `no_db_commands`), so the daemon is used only when it holds the DuckDB lock and the direct path is the by-hand one. The steps also told the reader to run `tj serve` first and then type a second command, which is not possible in one foreground shell. The recovery is `tj commit-note <sha>` on its own, and the paragraph now separates the hook's check from the command's behaviour. The section on producers that stamp the ledger attributes themselves conflated two routes that read different places. The in-process SDK exporter passes `resource_attrs` to `session_context_from_attrs` (`otel/provider.py:211`) and the Claude Code logs route does the same (`api/routes/logs.py:667`), so both are resource-only; the OTLP JSON ingest passes the merged dict (`otel/otlp_parsing.py:233`) and so accepts either level, span winning on conflict. A producer following span-level guidance through the SDK exporter would lose its repo and developer context with nothing raised. The page now gives the per-route table and says to stamp at resource level, the one placement every route reads. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
| That works on any commit carrying the trailer, with or without the hook installed and with or | ||
| without a daemon. It never fails the commit it is called from: every outcome exits 0, and | ||
| `tj -v commit-note` says what it did or why it did nothing. |
There was a problem hiding this comment.
If DuckDB is locked and the daemon API is unreachable, tj commit-note fails before it can handle the command's normal outcomes. A reader following this recovery step cannot write the skipped note, and the claim that every outcome exits 0 is incorrect. The installed hook still protects the commit by swallowing the failure.
Prompt To Fix With AI
This is a comment left during a code review.
Path: docs/ledger/hooks-and-notes.md
Line: 73-75
Comment:
**Manual note recovery can fail**
If DuckDB is locked and the daemon API is unreachable, `tj commit-note` fails before it can handle the command's normal outcomes. A reader following this recovery step cannot write the skipped note, and the claim that every outcome exits 0 is incorrect. The installed hook still protects the commit by swallowing the failure.
---
For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.The paragraph said tj commit-note works with or without a daemon, which is true of the ordinary cases and not of one: a database still locked by a daemon that has stopped answering leaves neither the shim nor the direct open available, so the command fails before it reaches any of its normal outcomes. Small, but this paragraph has now been wrong twice in the same way. Each correction described the happy path and stopped there, which is how the foreground tj serve advice got published in the first place. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
0.7.0 is the largest OSS release in months, and until this PR its new surfaces existed only in code and in the internal contracts doc. Someone who upgrades and runs
tj optimize shippedhad nowhere in the repo to read what a confidence level means, whattj init --hookswrites into their repo, or what the Cloud bridge sends. This fills that in.Summary
docs/ledger/area: the session-to-commit join, the optional git hooks and notes, the Cloud bridge.docs/cli-reference.mdpicks uptj initas the primary name, the newtj initflags,tj commit-note, andtj backfill status.docs/architecture.mdgains the repo-context and developer-identity OTel attributes.tj init, theshippedanalyzer and the ledger docs, with the install-first hero untouched.RELEASE_NOTES_0.7.0.mdat the repo root: a working draft grouped by theme.Pages added
docs/ledger/overview.mddocs/ledger/hooks-and-notes.mdtj init --hooks/--notes, theprepare-commit-msgandpost-commithooks, where the session id comes from (CLAUDE_CODE_SESSION_ID, then~/.tj/active_sessions.json), the note payload, which refs are read vs written, managed blocks and thecore.hooksPathrefusal, doctor and uninstalldocs/ledger/cloud-bridge.mdtj statusandtj doctor, the[cloud]blockPages changed
README.mdtj initnamed in Get started;shippedrow in the analyzer table; a paragraph on whatshippedis and is not; three rows in the Documentation tabledocs/cli-reference.mdtj onboardsection retitledtj init(alsotj onboard) with the alias explained;--hooks,--notes,--enforce,--add-project,--reconfigure,--plan,--analysis-span,--backfill-days/--backfill-alldocumented; newtj commit-notesection;tj backfill statusdocumented;shippedandstream-usageadded to the analyzer namesdocs/architecture.mddocs/configuration.mddocs/first-hour.mdtj optimize shipped; tokenmaxx renumbered to 5docs/optimize/shipped.mdrefs/notes/tokenjamis read too, and an inherited trailer is written atinferredrather thandeterministic; cross-links to the ledger pagesCommands verified
Every command in these docs was run with
--helpin the worktree rather than taken from the brief:tj --help,tj init --help,tj onboard --help,tj commit-note --help,tj optimize --help,tj backfill --help,tj backfill status --help,tj proxy --help(confirmingenable),tj otel-resource-attrs --help,tj doctor --help,tj status --help.Two findings from that pass:
tj commit-note --jsondoes not parse.--jsonand-vare group-level options, so the reference documentstj --json commit-noteandtj -v commit-note, both confirmed to run (Critical Rule 44).tj backfill statusis not new in 0.7.0. It shipped in July and was simply never documented. It is documented here and deliberately left out of the release notes.Release notes draft
RELEASE_NOTES_0.7.0.mdis grouped by theme (the ledger, first run and onboarding, correctness, docs, upgrading), not by PR number. Framing follows Critical Rule 14: the shipped finding is described as measured spend next to measured output, never as a saving, and the--enforceparagraph says suggest mode forwards everything unmodified and that nothing is blocked or rewritten until the user approves it.This file is a draft for the owner to paste into the GitHub release. Delete it from the branch before merge, or move it wherever release drafts should live, if you would rather it not sit at the repo root.
Tests / Verification
ruff check tokenjam/clean,mypy tokenjam/clean (288 files, no issues).pytest tests/unit/ tests/synthetic/ tests/agents/ tests/integration/ -n auto: 5955 passed, 5 failed. The same 5 fail on this branch with the changes stashed, so they are pre-existing and environment-dependent (test_summarize_relocate, two intest_summarize_cli, two intest_xdist_isolation); each passes when run alone.What's NOT in this PR
docs/agent-capability-matrix.mdis untouched. Its analyzer row is built around the numeral "thirteen" and a per-persona split that no longer matchesPERSONA_DISABLED_ANALYZERS. Correcting it properly means recounting the whole row against the registry, which is its own change rather than a drive-by inside a docs PR.stream-usage. It is named in the CLI reference's analyzer list so the list is complete, but adocs/optimize/stream-usage.mddeep-dive is out of scope here.tokenjam/docs/andREADME.mdonly.🤖 Generated with Claude Code