Skip to content

A session id is the host's to supply, so the model is no longer shown one (comms-tg70) - #214

Merged
GraemeF merged 1 commit into
mainfrom
comms-tg70
Aug 1, 2026
Merged

GraemeF merged 1 commit into
mainfrom
comms-tg70

Conversation

@GraemeF

@GraemeF GraemeF commented Aug 1, 2026

Copy link
Copy Markdown
Contributor

session_id was declared on seven tool schemas, so tools/list showed the
model a parameter it has no way to fill. The value comes from Claude Code's
PreToolUse hook, or from a non-CC host that stamps it into arguments itself —
neither route ever needed it advertised. A human does not type their session id
into the compose box (docs/agent-experience.md principle 1).

This is an encoding subtraction, not a supply deletion. Every channel that
binds an identity still binds one.

What the tool advertises and what it accepts are now separate

ToolDef.hostSuppliedArgs names the arguments a host stamps in out of band.
The guard in registerTools admits properties ∪ hostSuppliedArgs, while the
unknown-argument error still names only the advertised ones — an error reading
"Valid arguments: …, session_id" would put the parameter back on the surface
this removes it from.

The relaxation is per-tool, not a hole in the check: a tool listing nothing
still rejects session_id, and every tool still rejects everything else.

Seven tools carry it, not the five in the hook matcher. subscribe and
unsubscribe are the identity route for a listen-first non-CC seat, and a host
stamping the argument on a tool CC never stamps must not be told it is unknown.

An eighth site: the instructions block

This one is past the seven schemas, and is called out deliberately.

COMMY_INSTRUCTIONS in packages/mcp/mcp-server.ts is the MCP instructions:
field — injected into every connected agent's context verbatim. It carried a
**session_id.** paragraph telling the model: "Pass your conversation's
session id as the optional argument on post, edit_message, react,
unreact, and current_identity."

That is the agent-visible surface in prose, and principle 1 reaches it for the
same reason it reaches the schema. Removing the property while leaving the
paragraph would ship a self-contradictory build: tools/list no longer
advertises the argument, and the instructions still tell the model to send it.
Same subtraction, second encoding.

It is a clean revert if it should be split out — packages/mcp/mcp-server.ts
plus its test in packages/mcp/mcp-server.test.ts, nothing else depends on it.

The contract a non-CC host implements now lives in docs/self-hosting.md,
which its operator reads: the seven accepting tools, the fact that the argument
is deliberately not advertised, and the UUID-or-unbound-stub rule.

The manifest test asserts the rule one seam over

hooks-manifest.test.ts held that a tool whose adapter path reaches
boundHttp must DECLARE session_id. Nothing declares one now, so the rule is
restated as RECEIVE and traced from the accept-side marker instead. The
substance is unchanged — the id still has to reach the tool.

That rule comes from a source scan, and a scan that stops matching satisfies
every "no tool violates this" assertion by looking at nothing. So the receiving
set is pinned at exactly seven, and the advertise side is traced separately and
asserted empty. Verified by mutation, not by inspection: renaming the marker
fails the pin, and returning a session_id property to a schema fails the
advertise-side rule. Without the pin, the first mutation went silently green.

Supply is proven by the tests that were already there

The tools-session suite passes session_id in arguments and asserts the
mint. Those are now tests of an undeclared argument being read, and they would
fail loudly if the guard rejected it. Three new ones state the property
directly: the argument is accepted where no schema advertises it, a tool that
takes none still rejects it, and a genuinely unknown argument is still rejected
alongside it.

Closes comms-tg70.

… one (comms-tg70)

docs/agent-experience.md principle 1: a human does not type their session id
into the compose box. `session_id` was declared on seven tool schemas, so the
model saw a parameter it has no way to fill — the value comes from Claude
Code's PreToolUse hook, or from a non-CC host that stamps it into `arguments`
itself. Neither route ever needed it advertised.

What the tool ADVERTISES and what it ACCEPTS are now separate. `hostSuppliedArgs`
names the arguments a host stamps in out of band; the guard in `registerTools`
admits `properties ∪ hostSuppliedArgs` while the unknown-argument error still
names only the advertised ones — an error listing `session_id` as valid would
put the parameter back on the surface this removes it from. The relaxation is
per-tool: a tool listing nothing still rejects `session_id`, and every tool
still rejects everything else.

Every supply channel is untouched. The boot-env feeder, the hook injection, and
`readSessionId`'s read-back all work as before, which the existing
tools-session suite proves — those tests pass `session_id` in `arguments` and
assert the mint, and they are now tests of an undeclared argument being read.

Seven tools carry it, not the five in the hook matcher: subscribe and
unsubscribe are the identity route for a listen-first non-CC seat, and a host
stamping the argument on a tool CC never stamps must not be told it is unknown.

The instructions block goes too. `COMMY_INSTRUCTIONS` is injected into every
agent's context verbatim, and it told the model to pass its conversation's
session id. Removing the schema property while leaving that paragraph would
have shipped a surface that instructs the model to send an argument no tool
advertises. The contract a non-CC host implements now lives only in
docs/self-hosting.md, which its operator reads.

The manifest test asserts the same rule one seam over: a tool whose adapter path
reaches `boundHttp` must RECEIVE `session_id`, not declare it. That rule is
traced from a source scan, and a scan that stops matching satisfies every
"no tool violates this" assertion by looking at nothing — so the receiving set
is pinned at seven, and the advertise side is traced separately and asserted
empty. Verified by mutation: renaming the marker fails the pin, and returning a
`session_id` property to a schema fails the advertise-side rule.
@GraemeF
GraemeF merged commit 5b210eb into main Aug 1, 2026
2 checks passed
@GraemeF
GraemeF deleted the comms-tg70 branch August 1, 2026 09:01
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.

1 participant