session-id wire + attach-or-start TUI - #348
Open
EdwardIrby wants to merge 8 commits into
Open
EdwardIrby wants to merge 8 commits into
EdwardIrby wants to merge 8 commits into
Conversation
The engine self-mints `instanceId = ueid('bp_')` and stamps it on every
trace. With an ACP ingress landing later, the host owns session identity
policy (mints on `session/new`, passes a client's old id on `session/load`),
so the engine must ACCEPT a host-supplied session id — it never mints one
and never returns ids. No rename: the two ids are separate axes and both
live on the wire.
- `TraceBase` gains `sessionId: string` alongside `instanceId`; a new
exported `TraceBaseSchema` is the one home for the trace wire's common
shape, so per-kind validators derive from it instead of hand-mirroring.
- `behavioral()` gains `{ sessionId?: string }`; the frozen return object
is unchanged. Session id is computed once at factory time
(`options?.sessionId ?? instanceId`) and stamped on every trace next to
`instanceId`; `resumePendingThreadsForSelectedEvent` stamps it on
interrupt traces too.
- The frontier faculty speaks the same wire with no drift: replay/explore/
verify inputs accept `sessionId` (AJV `nullable`, defaulting to the
`instanceId`), and every synthetic trace it builds (frontier, selection,
deadlock) carries both ids.
- `serve.ts`/`trace-consumer.ts` are pass-through only — no construction
sites existed in production code; only spec fixtures gained the field.
- `skills/behavioral/references/behavioral.md` factory signature updated.
Validation: `bun --bun tsc --noEmit` plus the behavioral, frontier
faculty, and cli serve/trace-consumer suites (211 specs).
The direction was dropped from the plan; these comment-only references
survived the sweep. No behavior change — doc lines and one doc-comment
re-wrap only.
- behavioral.ts / behavioral.types.ts / skill reference: session identity
is owned by "the host layer", no protocol names
- controller.types.ts: Transport seam examples now generic ("native IPC")
- store/faculty.ts: swap-backings list drops the Tauri engine example
Validation: bun --bun tsc --noEmit clean; comment-only changes, no
executable surface touched.
Slice 1 of the attach-or-start lifecycle: the running instance owns <home>/instance.pid. acquireInstanceLock takes the lock when the home is free, blocks with the live holder pid when one runs, and reaps a stale pidfile (pid no longer alive) before acquiring. Injectable home + pid; release removes the pidfile for the SIGINT/SIGTERM terminate path.
Slice 2: the host gains its single listener, <home>/instance.sock — a Bun.serve with the unix option whose WebSocket carrier speaks the same JSON-RPC lane as stdio (one text frame = one message). The host-side dispatcher and egress wiring are extracted from serve.ts (dispatchToRuntime, wireRuntimeEgress) and reused, one protocol one dispatcher multiple carriers. Redacted traces and ui_* selections fan out to every client; socket file removed on close, stale file reaped at start. Transport-shaped clients exercise the real server over ws+unix in the specs.
Slice 3: a single-file readline TUI (src/cli/tui.ts, no framework) with exactly three primitives: emit (egress-only, Bun.color-resolved kind severity colors that never touch the wire or a prompt), prompt (a raw line resolves to a tui_command ingress event), and select (a numbered choice resolves to tui_select). The tui_* family is ingress-only with AJV detail schemas at the top of tui.ts; the root guard pack now derives its guard entries from TUI_DETAIL_SCHEMAS alongside the controller wire, so malformed tui_* details block visibly. Lines are consumed through an explicit queue (terminal:false readline drops buffered lines between questions).
Slice 4: bare `behavioral` (no subcommand) is the attach-or-start entry. A live instance (pidfile held) is attached to over <home>/instance.sock with "attached to running instance <id>"; a free or stale home starts the foreground instance — engine + socket host + TUI in one process, the shell supervising. The instance own TUI rides the socket like every other client (no in-process fast path); SIGINT/SIGTERM terminate the engine and remove the pidfile and socket (the daemon-door discipline). The router gains a lazy `default` entry; --help and subcommands still never load the composition graph. Two-process specs drive the real lifecycle across Bun.spawn children: attach notice, cross-process trigger, no second instance, stale-pidfile reap, SIGTERM cleanup. File IO is Bun-native (Bun.file/Bun.write/Bun.file().delete()); node:fs only where Bun has no equivalent (mkdir, socket-file existence).
Slice 5: the host single listener now serves the bundled controller GUI at /.behavioral/connect.js alongside the WebSocket upgrade — the browser carrier and the TUI carrier are two clients of one Bun.serve (Carriers/H). bundleController is promoted from the controller fixture to src/controller/ (one home for the bundle; the fixture re-exports it). In prod the AOT bundle is built once and cached; --dev (start-time only, attachers cannot flip a running instance) rebuilds per request for source editing. Engine and wire are identical in both modes. The router default forwards argv so the bin entry recognizes the flag.
| export const behavioral = (options?: { sessionId?: string }) => { | ||
| const instanceId = ueid('bp_') | ||
| /** @internal Host session identity — accepted at factory time, never minted. */ | ||
| const sessionId = options?.sessionId ?? instanceId |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
.prompts/attach-or-start-tui.md(TDD, one commit per slice). plan.md (gitignored by design) is the navigator's decision log for the design chain.Summary
TraceBasegainssessionIdbesideinstanceId(accept-only, host-supplied, defaults to the instanceId;behavioral()return unchanged) — the dormant audit axis, landed with the wire-wide schemas + specs<home>/instance.pid) + unix-socket attach lane (<home>/instance.sock) speaking the existing JSON-RPC vocabulary through the same dispatcher asserve(one protocol, one dispatcher, multiple carriers)src/cli/tui.ts, 190 lines): three primitives (emit/prompt/select), two ingress-onlytui_*constants with AJV schemas; the root guard pack derivestui_*entries fromTUI_DETAIL_SCHEMASso malformed tui events block visiblybehavioralis attach-or-start: attaches to a running instance over the socket (with the notice), or starts the foreground host+TUI in one process; the instance's own TUI rides the socket like every client; SIGINT/SIGTERM cleanup/.behavioral/connect.js),--dev(start-time only) flips to per-request rebundling;bundleControllerpromoted tosrc/controller/Changed Files
src/behavioral/—behavioral.ts,behavioral.types.ts,behavioral.utils.ts(sessionId wire), tests (session-id.spec.ts,schemas.spec.ts,add-b-thread.spec.ts)src/faculties/—frontier/faculty.ts(sessionId on synthetic traces + replay schemas),faculties.threads.ts(TUI guard entries),instance-lock.ts,store/faculty.ts(doc line)src/cli/—tui.ts,socket-host.ts,attach.ts,attach-or-start.ts,serve.ts(dispatcher extraction),cli.ts(lazy default entry),json-rpc.ts,load-config.ts,trace-consumer.ts+ suitessrc/controller/—bundle-controller.ts(promoted bundler),controller.types.ts(doc lines)bin/behavioral.ts— default command registrationskills/behavioral/references/behavioral.md— factory signature docKnown Failures / Drift
terminal: falsedrops buffered lines betweenquestion()calls (explicit queue);Bun.file().exists()returns false for socket files (socket checks usenode:fs existsSync)Review Notes / Residual Risks
--dev) is Bun-WIP upstream; degrade path is full page reloads, and the flag is opt-in start-time onlysessionIdis a dormant axis: landed and defaulted but currently degenerate (always equal to instanceId) — no consumer supplies a real one yet; removing it later requires revisiting the frontier-analysis grouping usebun --bun tsc --noEmitclean