fix(cli): branch and memory refuse without a configured identity (#499) - #501
Conversation
`tps branch` no longer falls back to the hostname fragment and `tps memory` no longer falls back to the `admin` literal. Both resolve the configured identity and refuse by name when none is set, using the shared helper. The no-identity-in-src guard now covers a re-added fallback in either file.
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 37 minutes. View limit detailsLimit details: You’ve used the included review currently available. Review configuration: ⚙️ Run configuration
📒 Files selected for processing (11)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Pre-delivery sweep (cli#499) — measured on e69bb1d (main 77ee454)Every sentence this PR adds or changes, checked against the code: outcomes on every branch, scope words, and coverage claims.
Scope words ( |
Mechanical merge: no conflicts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@coderabbitai review |
|
…entity refusal for branch and memory Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…l reaches the CLI (#499) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
|
@coderabbitai review |
|
tps-kern
left a comment
There was a problem hiding this comment.
Review of head 9cd4676. Repo visibility checked before writing: repos/tpsdev-ai/cli .visibility = public; no findings below require private handling. Internal author (tps-anvil): I built and ran the touched suites in a review worktree.
What I verified
- Build (
bun install --frozen-lockfile && bun run build) succeeds. Targeted suites, via the isolated launcher:branch-daemon,branch-mail-identity,memory-cli,memory-entry,no-identity-defaults-499,no-identity-in-src— 42/42 pass. - Same configuration pattern as #485/#498: both new call sites go through
requireLocalAgentIdfromutils/local-agent.ts(the #397 helper — explicit arg orTPS_AGENT_ID, else a named refusal, no principal fallback).branchAgentId()adds thebranch init --agentpersisted value in the documented precedence (TPS_AGENT_IDwins);routeHandlerAction's default refuses with only the env, and the daemon passes the fully-resolved id explicitly (branch.ts:389), so no path silently re-derives one. - No remaining identity fallback for these surfaces: grepped
packages/cli/src— the hostname-fragment fallback in branch.ts and the"admin"literal in memory.ts are gone, and the guard (no-identity-in-src.test.ts) pins the exact removed expressions with a self-check that each pattern matches the expression it guards, so a re-added fallback turns red. - Refusal ordering is before side effects:
runStartresolves identity and requiresbranch.conf.jsonbefore the daemonization spawn — the new tests assert no spawn, no pid file, no log on refusal, and the identity error is named ("no branch agent id"). For memory, the refusal happens before any Flair client construction, and each CLI action still validates its own missing positional (agentId/memoryId) before any network call. - CLI dispatch: meow strips flags from
cli.input, sorest[1]can never swallow a--limit/--json; thedurabilityraw-value flag registration matchesrunMemory's expectations. - Memory test hygiene: env mutation is saved/restored around each case, and the entry tests spawn the built binary under a fresh HOME with
TPS_AGENT_IDdeleted.
Observations (non-blocking)
- [packages/cli/src/utils/identity.ts:412-419] (observation, out of #499 scope)
loadHostIdentityId()still returnshostname().split(".")[0]unconditionally. That is the host principal's definition (feedsresolveCliSenderIdandmail.ts's sender resolution), not an agent-principal fallback, so the no-fallback claim holds for what #499 covers — but note the brief-level phrasing "no remaining identity fallback in packages/cli/src" is only true in that narrower sense. Its downstream|| "unknown"(sender-id.ts:32, mail.ts:145) means mail with nothing configured signs as the literal principal "unknown"; pre-existing, worth its own issue rather than this PR. - [packages/cli/src/commands/memory.ts:47] (nit, documented) For
review/list/searchwith noTPS_AGENT_ID, the operator resolves to the target agent id. The changelog states this, and it is safe because the client then needs that agent's key atdefaultFlairKeyPath(operatorId)— no key, no request — but it does meantps memory review flinton a machine holding flint's key acts as flint, not as an admin. Server-side authorization is the real boundary here; fine to leave.
Verdict: APPROVE.
tps-sherlock
left a comment
There was a problem hiding this comment.
Reviewed head 9cd46766 (author tps-anvil, a tps-* agent → internal). Visibility checked: repos/tpsdev-ai/cli .visibility = public (PUBLIC). Built and ran in my own worktree ~/work/review-501-sherlock: bun install --frozen-lockfile && bun run build → exit 0; focused suites (no-identity-in-src, no-identity-defaults-499, branch-mail-identity, branch-daemon, memory-cli, memory-entry) through scripts/test-suite.mjs with HOME on an empty dir → 42 pass / 0 fail (127 expect). I also ran three guard mutations.
Sherlock focus — an unset identity refuses (never falls back to a principal); the guard catches a re-added fallback.
-
Refusal holds at both sites.
requireLocalAgentId(packages/cli/src/utils/local-agent.ts:9-14) returns the explicit arg orTPS_AGENT_IDand otherwise throwsno <what>: pass an explicit agent id or set TPS_AGENT_ID.branchAgentId(packages/cli/src/commands/branch.ts:41-42) passesprocess.env.TPS_AGENT_ID ?? confAgentId— no hostname;runMemory(packages/cli/src/commands/memory.ts:47) passesprocess.env.TPS_AGENT_ID ?? args.agentId. Behavior tests confirm: "mail routing refuses when no identity is configured", "refuses by name when no operator identity is configured", and branch-daemon "refuses without identity or branch configuration before spawning" (spawned/pidWritten/logWrittenall false — the identity check precedes the daemon spawn atbranch.ts:76-77). An emptyTPS_AGENT_IDalso refuses because!idcatches""(tested for""in branch-daemon). -
The guard catches re-adding the two removed expressions — verified by mutation. Re-adding
hostname().split(".")[0]inbranch.tsturns "each covered file resolves its identity without a fallback" red; re-adding?? "admin"inmemory.tsturns that test and the behavioral refusal test red. -
Non-blocking — the guard is expression-specific.
packages/cli/test/no-identity-in-src.test.ts:32-35pins two regexes (/hostname\(\)\.split\(/in branch.ts,/"admin"/in memory.ts). A fallback written differently is invisible to the static guard: I added?? "operator"tomemory.ts:47and the guard stayed green (only the behavioral test "refuses by name when no operator identity is configured" went red). So the issue's "add both files … so a fallback cannot return" holds only for those two spellings; the behavioral tests, not the guard, are the real backstop. A comment stating that reliance would help the next reader. -
Non-blocking, outside #499's two-file scope — other hostname-derived identity fallbacks remain in
packages/cli/src.packages/cli/src/utils/identity.ts:413const safeHostname = () => hostname().split(".")[0]!;is returned byloadHostIdentityId()on every path (:415-422), andpackages/cli/src/commands/mail.ts:133-152(resolveAgentId) reaches it as the sender fallback (const id = await loadHostIdentityId() || "unknown"). Neither is covered by the guard (the literal scan flags only known agent ids; the new pattern list covers only branch.ts and memory.ts). Pre-existing and not what #499 asked to change — flagged only because this lane asks whether any identity fallback remains insrc.
Minor notes (non-blocking). requireLocalAgentId tests !id only, so a whitespace-only TPS_AGENT_ID/argument passes as the principal (empty is refused; whitespace is untested). packages/cli/src/commands/branch.ts:209-212 still carries the comment describing the removed order "(TPS_AGENT_ID env → conf.agentId → hostname)" while the code persists args.agent (writeBranchConf(port, advertiseHost, transport, undefined, args.agent)), so that comment now references a fallback this PR deleted.
No blocking findings. Verdict: APPROVE.
Closes #499
tps branch startand memory governance actions (review,approve,reject,archive,unarchive,purge,list,show,search) userequireLocalAgentIdand refuse by name without an identity. The branch hostname-fragment fallback and memoryadminfallback are removed. Re-adding either removed expression (hostname().split(".")[0]in branch or?? "admin"in memory) turns the guard red.Counts measured on e69bb1d (main 77ee454):
packages/cli/test/no-identity-defaults-499.test.ts(4 tests) and the rewrittenpackages/cli/test/branch-mail-identity.test.tsfail onorigin/main(4 fail / 1 file error) and pass on this head.hostname().split(".")[0]insrc/commands/branch.tsor?? "admin"insrc/commands/memory.tsmakesno-identity-in-src.test.tsred (measured one file at a time); restoring makes it pass.bun run lint:ci: exit 0.bun run build(tsc, all workspace packages): exit 0.Merged main 883f889 (#496, #497);
branch.tskeeps #497'sOutboxSendTrackerunchanged and adds this PR'srequireLocalAgentIdimport. On b8a99c3:bun run buildexit 0,bun run lint:ciexit 0;branch-mail-identity5/0,no-identity-defaults-4994/0,no-identity-in-src7/0,memory-cli11/0,relay-delivery-loss16/0,relay16/0 (pass/fail).On 9cd4676:
bun run build,bun run lint:ci, and CLIbun x tsc --noEmit: exit 0.memory-entryandbranch-daemon) applied to b8a99c3: 2 pass / 13 fail; all pass in the focused run on 9cd4676.