Skip to content

fix(cli): resolve a launched agent's flight on every orientation - #85

Merged
jlrickert merged 2 commits into
mainfrom
feat/agent-flight-late-binding
Aug 8, 2026
Merged

fix(cli): resolve a launched agent's flight on every orientation#85
jlrickert merged 2 commits into
mainfrom
feat/agent-flight-late-binding

Conversation

@jlrickert

Copy link
Copy Markdown
Owner

Re-opens the work from #84. That PR merged into its base branch
(feat/mcp-bootstrap-mode) rather than main#83 had been rebase-merged
seconds earlier, which rewrote the SHAs and left the base branch superseded but
undeleted. GitHub marked #84 merged, truthfully, into a branch that no longer
led anywhere. These two commits are rebased onto current main; nothing else
changed.


tap launch --agent NAME resolved the agent's flight once and exported the
result as TAP_FLIGHT. Environment variables outrank project and user config,
and a process cannot change its own environment, so the launched session was
pinned to whatever the flight was at launch. Editing the agent's flight and
calling orient again did nothing — and the session reported success while
staying on the old flight.

Export the reference instead of the value. TAP_AGENT names the agent, and
resolution reads agents[TAP_AGENT].flight out of the merged config on every
load. The variable is still constant — the agent driving a process genuinely
cannot change while it runs — but the mapping it points into is re-read at each
reload boundary, so a config edit plus orient now moves a running session.

Resolution happens once, at the end of ConfigService.load, rather than in
ActiveFlightName. load is what Reload re-runs, so late binding falls out
for free and every consumer — CLI, MCP orientation, keg listing, config explain — reads one already-resolved Flight() with no call-site changes. It
needs the env layer in isolation to tell a direct TAP_FLIGHT (which still
wins) from a flight that came from a file layer (which the agent overrides).

Precedence

1. --flight                      explicit runtime flag
2. TAP_FLIGHT                    direct env value (escape hatch)
3. agents[<active agent>].flight NEW - late-bound indirection
4. project  flight:
5. user     flight:              bootstrap baseline

Direct beats indirect, and naming an agent at launch beats an ambient project
default — which preserves what tap launch did before.

A TAP_AGENT naming an agent that is not configured warns and falls through
rather than failing: a session cannot fix its own environment, so a typo should
not brick a harness. The orientation payload names the active agent, so a reader
is told where the flight came from instead of being pointed at a flight: key
the agent silently outranks.

Codex's MCP registration gains TAP_AGENT in its env allowlist; TAP_FLIGHT
stays listed for a human overriding directly. Claude Code forwards its whole
environment and needs no change.

Telemetry

The reporter injects the active agent alongside the client version, so neither
call site has to remember to set it. This needs a matching hub-side field, which
is not yet released — until then an older hub rejects the batch, which degrades
to no telemetry rather than a retry loop thanks to the back-off fix in #81
(already on main).

Testing

  • 8 cases over the precedence matrix, reload-picks-up-edit, and explain
    attribution
  • 4 cases over a real config-driven MCP session, including
    edit-config-then-reorient-moves-the-session — the regression this exists for
  • Verified end to end against the built binary

Both commits build, pass go test ./..., and are gofmt-clean individually.

`tap launch --agent NAME` resolved the agent's flight once and exported
the result as TAP_FLIGHT. Environment variables outrank project and user
config, and a process cannot change its own environment, so the launched
session was pinned to whatever the flight was at launch. Editing the
agent's flight and calling `orient` again did nothing, and the session
reported success while staying on the old flight.

Export the reference instead of the value: TAP_AGENT names the agent,
and resolution reads agents[TAP_AGENT].flight out of the merged config
on every load. The variable is still constant — the agent driving a
process genuinely cannot change while it runs — but the mapping it
points into is re-read at each reload boundary, so a config edit plus
`orient` now moves a running session.

Resolution happens once, at the end of ConfigService.load, rather than
in ActiveFlightName. load is what Reload re-runs, so late binding falls
out for free, and every consumer — CLI, MCP orientation, keg listing,
config explain — reads one already-resolved Flight() with no call-site
changes. It needs the env layer in isolation to tell a direct
TAP_FLIGHT (which still wins) from a flight that came from a file layer
(which the agent overrides).

Precedence is --flight, TAP_FLIGHT, the agent's flight, project config,
then the user baseline: direct beats indirect, and naming an agent at
launch beats an ambient project default.

A TAP_AGENT naming an agent that is not configured warns and falls
through rather than failing, since a session cannot fix its own
environment. The orientation payload names the active agent so a reader
is told where the flight came from instead of being pointed at a
`flight:` key the agent silently outranks.
The reporter injects the active agent alongside the client version, so
neither the CLI nor the MCP call site has to remember to set it, and
agent-driven usage separates from human usage in aggregate. It is
resolved once at construction: the agent driving a process cannot change
while it runs.

The alias is user-chosen config text, not an identifier, and is omitted
when a human is driving. It is covered by the existing disableTelemetry
opt-out and validated by the hub like every other text field.

Requires a hub that accepts the field; the hub's decoder rejects unknown
fields, so an older hub refuses the batch. That degrades to no telemetry
rather than failing a command.
@jlrickert
jlrickert merged commit ee4577e into main Aug 8, 2026
1 check passed
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