seamless docs

Sessions & briefings

How an agent gets your knowledge injected before it does anything - ambient sessions, the briefing's packing order, and what never gets dropped.

On this page

A Seamless session is one agent's stretch of work. A briefing is what that agent gets handed at the start of it, before it has called a single tool: a SessionStart hook - installed for Claude Code or Codex - resolves the working directory to a project, assembles constraints, plan rollups, and recent findings inside a token budget, and injects the result into the agent's context. A client without hooks gets a briefing only by calling session_start itself.

This is the mechanism that makes Seamless ambient rather than opt-in. An agent does not have to remember to ask what it knows; it begins already knowing your constraints.

Ambient vs. explicit sessions

Ambient Explicit
Opened by The SessionStart hook, automatically session_start
Named cc/<prefix>-<digest> (Claude Code) or cx/<prefix>-<digest> (Codex) Whatever you pass, or generated
Gets The short injected briefing The full briefing, returned by the call

The ambient handle keeps the first eight external-ID characters readable and adds 64 stable SHA-256 bits. Seamless resolves lifecycle activity by the full external ID plus client, so two UUIDv7 sessions sharing a timestamp prefix never share scope, findings, or provenance. Pre-upgrade handles keep their old names when resumed. The handle is a display label, not an API: treat cc/... and cx/... as opaque and never parse or construct them.

They are not two competing sessions. session_start adopts the sole ambient session for the same working directory rather than opening a second one - that adoption rule exists because the alternative was double-counting every agent.

Call session_start when the work is non-trivial: it returns the full briefing (the injected one is deliberately shorter), and it binds the connection so everything afterward inherits the project scope.

That distinction also appears in knowledge provenance. A write attributed by an ambient hook stores the session's cc/... or cx/... name in source_session; a write made through a bound MCP connection stores the session's ULID. Readers resolve both forms. Do not infer client, scope, or liveness from the spelling of source_session.

An actual briefing, annotated

This is a real briefing, in the order the assembler packs it:

Actual packing orderbudgeted
<seam-briefing>
Seam project: seamless -- 62 memories (24 constraints, 9 conventions, 1 stage), 3 recent findings.

Constraints (binding for every session):
- errcheck-check-blank-two-category-rule: errcheck runs with check-blank ...
- llm-degradation-remote-vs-local: llm errors split remote ...
... 2 more bullets ...
- +20 more, equally binding -- memory_read name=<name> before working near one: fts-or-vs-allterms-presence-probe, console-csrf-origin-check-contract, ...

Stages (world-state gates):
- deep-audit-f15-f18-landed -- status unknown (no Status: header)
(details: memory_read name=<stage>)

Plans:
- marketing -- 2/3 done, 1 claimable, 0 in flight
- seamless-documentation-site -- awaiting approval: Seamless documentation site (presented, 2m)
(steps: tasks_ready plan=<slug>; claim: tasks_claim id=<task id>; attach work via the plan:<slug> tag)

Conventions (project-local choices):
- wordmark-caret-l-spans-three-files: The wordmark markup must stay in sync ...
... 3 more bullets ...
(9 total, 4 shown -- recall kind=convention for the rest)

Recent findings:
- cc/1fa4b02d (1h): Landed the installer check; the release gate now fails on ...

Ready tasks (2):
- Fix tool.call misattribution
- Polish the docs nav
(full queue with ids: tasks_ready; claim: tasks_claim id=<id>)

Memories (seamless):
- gofmt-must-scope-to-tracked-files: gofmt walks the filesystem ...
- shared-worktree-concurrent-agents-verify: Agents share the main worktree ...
- (+34 older -- recall query=<topic>, optionally kind=<kind>)
Recall on demand with recall; read a memory with memory_read.
Seam session: cc/8dd2fd5b-55d96b8d15ff0104 (ambient)
</seam-briefing>
Situation before library: the pinned head leads (tiered constraints, stages, plan rollups), what just happened follows (pending plans, conventions, recent findings, ready tasks), the memory index packs after it, and retrieval guidance plus session identity close the envelope.

Line by line:

  • The header counts what exists, so an agent knows how much it is not being shown. Constraints, conventions, and pinned stages are memories too, so they are reported as subsets of the total, not second pools.
  • The Constraints section comes first and is never dropped for budget. A constraint is a rule the project cannot violate; a briefing that omitted one to fit a token budget would be worse than no briefing at all. It is tiered: the top briefing.constraint_max_full (default 4) render as full - name: description bullets - starred constraints first, then constraints a recent mishap referenced (last 30 days, most recent first), then the same blended recency+utility order the memory index uses - and the remainder collapse into the compact +N more, equally binding line, which still names every one so an agent can memory_read name=<name> before working near it. Setting the knob to 0 disables tiering and renders every constraint full.
  • The Stages section is pinned right after constraints, for the same reason: a gated stage's status is load-bearing for the whole session. The pin belongs to the gate: a stage whose body does not open with a live Status: open|in_progress|blocked header only renders (as status unknown (no Status: header) - the nudge names the fix) for the briefing.stage_unknown_max_age_days grace window (default 7) after its last update, then leaves the briefing rather than squatting in it forever.
  • The Plans section follows. The pinned rollup rows (2/3 done, 1 claimable), most recently active plan first, tell the next agent what work it can pick up right now, and the closing hint names the premade filters: tasks_ready plan=<slug> for a plan's steps, tasks_claim to take one.
  • awaiting approval rows sit in the same section but open the budgeted body: captured but unapproved plans are a hint, not a commitment, so unlike the rollups they compete for budget and expire after briefing.pending_plan_max_days.
  • The Conventions section follows: project-local choices and layout facts (kind: convention) - binding, but topically triggered, so unlike constraints they compete for budget. The top briefing.convention_max_full (default 4; 0 renders all) show in full and a count line always closes the section, pointing at recall kind=convention - a real command: recall with a kind and no query lists that kind newest-first. A starred convention heads the section and renders regardless of the tier cap or the budget.
  • Recent findings - what previous sessions learned, harvested at their end - render right after: they say what just happened here, so they pack (and render) before the memory index rather than below it.
  • The Ready tasks section closes the situation half: the open queue, newest first, before the library begins, with the full-queue and claim commands as its trailer. The display order is the only thing that changes at the top: tasks_ready itself still lists oldest-first, so claiming stays FIFO.
  • The Memories index is - name: description bullets only - the description is the only text an index ever shows, which is why writing a good one matters more than writing a good body. Starred memories head the list, exempt from every trim and from the budget.
  • (+34 older -- recall query=<topic>, optionally kind=<kind>) is the honest tail: the index was trimmed, and the briefing says so instead of pretending it is complete.

The budget, and what survives it

Sections are packed against budgets.max_briefing_tokens (default 1500), then the whole thing is hard-capped at briefing.hard_cap_multiplier times that (default 2x).

The never-drop invariant: constraints (both the full tier and the compact +N more, equally binding line), pinned stages, and active-plan rollups are counted first and are exempt from budget dropping - every constraint name appears in every briefing. Starred memories keep the same exemption inside their sections: a starred convention or index memory renders no matter how starved the budget. Everything else packs in render order - pending plans, conventions, recent findings, ready tasks, the memory index, sibling findings, sibling memories - so budget priority and render priority agree: the sections that say what is happening now pack before the memory library, a fat index can no longer evict the findings that render above it, and the sibling sections are the first to go when the budget runs out. The header counts only the findings that actually rendered, and findings or index lines cut by budget leave an explicit +N more/older trailer.

The memory index's own order starts as newest-first - but each memory also carries a utility score, a time-decayed record of actual demand (reads, recall hits, prompt matches; being briefed counts for nothing), and once a project's demand history matures the index is ranked by the blend (1-w)·recency + w·utility, both on a 14-day half-life, with briefing.utility_weight as w (default 0.4, 0 restores pure recency). The same blended key ranks constraints ahead of their tier split, after stars and recent mishaps have claimed the head of the full tier; while utility is inactive the constraint order degrades to pure recency. The switch is deliberate, not silent: in briefing.utility_mode: auto the gardener latches it per project only once the project's first demand is at least 14 days old and it has shown 20+ demand events and 10+ memories touched in 30 days - a young project keeps the recency order, because a utility signal with no history behind it is noise. on/off force it everywhere, and the console Settings page shows each scope's progress toward the latch, with a per-scope force.

Every knob is tunable in Configuration, and the briefing: block is also editable live in the console. Those runtime edits are stored in the database and win over both the file and the environment, applying from the next session start without a daemon restart. If a briefing setting seems to be ignored, check the console before you check the YAML.

Codex adds one client-specific safety ceiling after this packing step: every model-visible Seamless hook context is capped at 2,400 estimated tokens, below Codex's approximate 2,500-token temporary-file spill threshold. The cap runs before telemetry is recorded and preserves generated closing tags and the ambient handle. Claude Code does not use this additional cap.

Liveness

Sessions heartbeat. A session that ends cleanly cascades immediately; one that is abandoned is caught by an idle reaper after gardener.session_idle_minutes and marked expired. The TTL only applies when there was no end signal at all - a crashed agent's session does not sit "live" forever, and a slow one is not reaped out from under itself.

session_end is the explicit findings path. Claude Code's SessionEnd hook calls the same lifecycle. Codex 0.144.6 has no SessionEnd event, so each Stop hook updates provisional findings from last_assistant_message and the reaper later marks the idle ambient session expired; those expired ambient findings still surface in future briefings. Keep findings tight - briefings show a short preview

  • but they are stored in full, not rejected, so a long finding is fine when it earns it.

The end of a session also harvests real model token usage - input, cached, cache-creation, and output counts read from the client's own transcript (Claude Code) or rollout file (Codex), not estimated from injected bytes. The session's console page shows them as its "Model tokens" panel; a session whose client exposes no transcript simply records none.