session: Harness-Session-Variable schliesst die Luecke im Session-Id-Fallback (Telemetrie-Join, Iteration-Budget-Gate); SIGPIPE-Nebenbefund im Emitter behoben
CI / verify (push) Successful in 52s
Release / release (push) Successful in 36s

Files changed:
- CHANGES.md
- EVALS.md
- INSTALL.md
- VERSION
- instructions/session-setup.md
- tools/CONTRACT.md
- tools/chemenu/cli.py
- tools/chemenu/commands/doctor.py
- tools/chemenu/commands/run_budget.py
- tools/chemenu/session.py
- tools/chemenu/telemetry/writer.py
- tools/chemenu/tests/conftest.py
- tools/chemenu/tests/test_cli.py
- tools/chemenu/tests/test_run_budget.py
- tools/chemenu/tests/test_telemetry_emit.py
This commit is contained in:
2026-09-16 19:18:00 +02:00
parent 536093f6c9
commit e4e2332e01
15 changed files with 610 additions and 44 deletions
+23 -11
View File
@@ -7,11 +7,13 @@ description: Scope the wikitool iteration budget to the task by exporting a stab
# Scope the session budget
Every `wikitool` call is counted against a per-session iteration budget. A "session" is keyed
by `WIKITOOL_SESSION_ID`, falling back to the parent process id when that variable is unset.
by a fallback chain (`chemenu.session`): `WIKITOOL_SESSION_ID` first, then a harness's own
session variable where one is registered (`CLAUDE_CODE_SESSION_ID` today), then the parent
process id.
Without an explicit id, the budget is scoped to whichever shell happened to run the command,
so a task spanning several terminals is counted as several sessions - and one that reuses a
shell inherits an unrelated count.
Without an explicit id, and on a harness with no registered variable, the budget is scoped to
whichever shell happened to run the command, so a task spanning several terminals is counted as
several sessions - and one that reuses a shell inherits an unrelated count.
## Steps
@@ -25,21 +27,31 @@ tools/wikitool sync
**An `export` only carries if the shell carries.** Several agent harnesses run every tool call in
a freshly initialised shell: the working directory survives, shell state - environment variables,
functions - does not, so the variable is gone by the next call and each call falls back to its own
parent pid. The budget then counts a run of thirty calls as thirty sessions of one, and neither
the 60-call ceiling nor the loop-breaker can ever trip. On such a harness, pass the id **inline on
every call** instead, keeping the same value for the whole task:
functions - does not, so the variable is gone by the next call and each call falls back to whatever
the chain's next step resolves to.
On a harness with a registered variable in that chain (Claude Code, via `CLAUDE_CODE_SESSION_ID`),
the fallback already keeps every call in one bucket without this step - but it scopes to the
*whole* harness session, not to this one task, so a long-running session can carry unrelated prior
work into the same count. Setting `WIKITOOL_SESSION_ID` explicitly still narrows the bucket to the
task at hand, and remains the only way to scope it at all on a harness with no registered
variable - each call falls back to its own parent pid there, and neither the 60-call ceiling nor
the loop-breaker can ever trip (measured directly on a real upgrade run: 33 `wikitool` calls in
one task split into 21 telemetry buckets under the pid fallback alone). On such a harness, pass
the id **inline on every call** instead of `export`, keeping the same value for the whole task:
```bash
WIKITOOL_SESSION_ID="wiki-1234" tools/wikitool sync
WIKITOOL_SESSION_ID="wiki-1234" tools/wikitool new entity --name "..."
```
Which of the two applies is answerable in one call: run `tools/wikitool budget status` twice in
separate calls after exporting, and see whether it names the same id both times.
Which of the three applies is answerable in one call: run `tools/wikitool budget status` twice in
separate calls, and see whether it names the same id both times, and where that id came from -
`budget status` prints both.
Check the current state at any time with `tools/wikitool budget status`, which is never
counted against the budget itself and prints the id it is counting under.
counted against the budget itself and prints the id it is counting under, and its origin
(`WIKITOOL_SESSION_ID`, a named harness variable, or the parent-pid fallback).
**Why `sync` here, not just at publish time.** `publish` already pulls before it pushes, but a
session that runs many `wikitool` calls before its first `publish` (an ingest, a multi-page