Conversation
Inject per-tenant identity into the agent's system prompt so it knows its own URL, owner, dashboard, runtime, and model. The overlay slots between Identity and Environment, reads non-secret env vars stamped into the process by phantom-firstboot, and degrades to the empty string in single-tenant or laptop dev mode. Vars consumed: PHANTOM_TENANT_SLUG, PHANTOM_TENANT_ID, PHANTOM_OWNER_EMAIL, PHANTOM_OWNER_NAME, PHANTOM_DOMAIN, PHANTOM_DASHBOARD_URL, PHANTOM_AGENT_RUNTIME, PHANTOM_MODEL, plus the PHANTOM_GRANTED_INTEGRATIONS (Phase 7) and PHANTOM_CHANNEL_ALLOWLIST (Phase 8b) hooks. Every line is gated on a real value so missing optional vars silently disappear; the block never carries provider keys or secrets. Tests: 23 unit tests on the builder + reader + 4 integration tests on the assembler covering position (Identity < overlay < Environment), defensive shape (slug + dashboard only), full-env shape, and the integration hooks. Failure-path coverage for missing env vars, whitespace-only inputs, malformed list values, and the no-tenant fallback. Mission v1 sequencing step 4 (master plan section 3 Phase 9). No phantomd / phantom-rootfs change required: the overlay is defensive against intermediate phantomd versions that have not yet landed PHANTOM_DOMAIN and PHANTOM_OWNER_NAME injection.
|
@codex review |
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 73cfc97a0a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // "you are the Phantom assigned to X's workspace `slug`" as a unit. | ||
| const ownerPhrase = composeOwnerPhrase(e.ownerName, e.ownerEmail); | ||
| if (ownerPhrase && e.tenantSlug) { | ||
| lines.push(`You are the Phantom assigned to ${ownerPhrase}'s workspace \`${e.tenantSlug}\`.`); |
There was a problem hiding this comment.
Escape tenant env values before composing prompt lines
buildTenantSelfKnowledge interpolates environment values directly into the system prompt, so a newline/control-character payload in fields like PHANTOM_OWNER_NAME, PHANTOM_MODEL, or future tenant-provided values can inject extra instructions into the highest-priority prompt block. This is a security/integrity regression because these variables are operational inputs, not trusted static literals, and a malformed value would affect every request until restart.
Useful? React with 👍 / 👎.
| const hasAnything = Boolean( | ||
| e.tenantSlug || | ||
| e.ownerEmail || | ||
| e.ownerName || | ||
| e.domain || |
There was a problem hiding this comment.
Avoid emitting overlay header from non-renderable domain values
The presence check treats any non-empty PHANTOM_DOMAIN as sufficient to render the overlay, but composeHomeUrl can later discard values like "https://" (or scheme-only/trailing-slash variants), leaving only the section header ("# Who You Are In This Workspace") and no content. In misconfigured or partially rolled-out environments this produces a stub prompt block instead of the intended silent fallback.
Useful? React with 👍 / 👎.
Summary
Inject per-tenant identity into the agent's system prompt so it knows its own URL, owner, dashboard, runtime, and model. Mission v1 sequencing step 4 (master plan section 3 Phase 9).
The overlay slots between Identity and Environment in
src/agent/prompt-assembler.ts, reads non-secret env vars stamped into the process by phantom-firstboot, and degrades to the empty string in single-tenant or laptop dev mode.What changed
src/agent/prompt-blocks/tenant-self-knowledge.tsexportingbuildTenantSelfKnowledge()andreadTenantSelfKnowledgeEnv().prompt-assembler.tsnow slots the overlay as section 1b, between Identity and Environment.CLAUDE.mddocuments the overlay, the env var contract, and the source of each var.Env vars consumed
All non-secret tenant identifiers; the overlay never carries provider keys, OAuth tokens, or other secrets.
PHANTOM_TENANT_SLUGPHANTOM_TENANT_IDPHANTOM_OWNER_EMAILPHANTOM_OWNER_NAMEPHANTOM_DOMAIN<slug>.phantom.ghostwright.devPHANTOM_DASHBOARD_URLPHANTOM_AGENT_RUNTIMEPHANTOM_MODELPHANTOM_GRANTED_INTEGRATIONSPHANTOM_CHANNEL_ALLOWLISTFailure-path coverage
The overlay is defensive against intermediate phantomd versions: every line is gated on a real value, so missing optional vars silently disappear. Specific cases covered by tests:
<slug>.phantom.ghostwright.dev.PHANTOM_DOMAIN=https://example.com/(with scheme/trailing slash): stripped to bare host before composing.PHANTOM_GRANTED_INTEGRATIONSandPHANTOM_CHANNEL_ALLOWLISTproduce no line.Refresh / restart survival
The C7 invariant (tenant.env consumed once at firstboot, then deleted) does not affect this overlay: phantom-firstboot stamps these values into
/etc/default/phantom, whichphantom.servicesources viaEnvironmentFile=, so they survive inprocess.envfor the lifetime of the agent process across systemd restarts.Coordination with phantomd
No phantomd or phantom-rootfs change is required for this PR.
PHANTOM_OWNER_NAMEandPHANTOM_DOMAINare queued as a one-line addition tophantomd/internal/state/orchestrator.gofirstbootStep(per phantomd CLAUDE.md "What's queued"); when those land, the overlay starts surfacing them automatically with no Phantom-side change.Test plan
bun test src/agent/prompt-blocks/__tests__/tenant-self-knowledge.test.ts(23 tests)bun test src/agent/__tests__/prompt-assembler.test.ts(22 tests)bun testfull suite: 2152 pass, 0 fail, 10 skip, 1 todobun run lintcleanbun run typecheckcleanOut of scope
PHANTOM_OWNER_NAMEandPHANTOM_DOMAIN(queued separately per phantomd CLAUDE.md).@codex review