From 58518995b1ce397bad922418e7562647e330f66f Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Sun, 25 Oct 2026 01:58:39 -0700 Subject: [PATCH 1/4] docs(agents): seats may use any ego lite profile for browser walks Sam's 2026-10-25 direction lived only in operator-private config, which seats never load. Seats spawn in a repo clone with no --setting-sources override, so CLAUDE.md/AGENTS.md is what they read at spawn: add the rule there as a Quick Rules bullet, with the mechanics in docs/agents/browser-profiles.md. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Uub7iKypiDa4iTWuc1oYZQ --- CLAUDE.md | 2 ++ docs/agents/browser-profiles.md | 17 +++++++++++++++++ 2 files changed, 19 insertions(+) create mode 100644 docs/agents/browser-profiles.md diff --git a/CLAUDE.md b/CLAUDE.md index 2978e242aa..80f4d84b16 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -503,6 +503,8 @@ These are prescriptive rules not derivable from reading the code: - **Who a seat addresses (Sam, 2026-09-27).** Operator work (press, deploy, merge order, review routing) goes to the lane's operator account: `@lily-shen` for Sharpen, `@connector-ops` for connectors. A decision only Sam can make goes to him as a decision card, never a plain `@sam` in a thread. One re-ask after 12 hours of operator silence, then a decision card. Full rule: [`docs/agents/who-to-address.md`](docs/agents/who-to-address.md). +- **Browser walks may use any ego lite profile (Sam, 2026-10-25).** For Commonly work (vendor consoles, GitHub, commonly.me, connector walks) a seat may reuse the signed-in session of any ego lite profile, the operators' included. Never ask Sam to sign in until every profile from `await listProfiles()` has been checked and the signed-in account confirmed on the site itself. Never type a password, 2FA code or card data. Only `ego.createTaskSpace('', '')` honours a profile; confirm with `(await listTaskSpaces()).find(s => s.id === t.id).profileId`. ego profile ids are not Chrome directory names: choose by name, then confirm the account on the site. Full rule: [`docs/agents/browser-profiles.md`](docs/agents/browser-profiles.md). + - **A silent seat is not evidence of a broken seat.** Check the pod ledger before the log — `grep -c "posted via tool"` cannot detect a seat that is posting correctly, because `silentReply` is evaluated before `agentPostedItself`. Read the live spawn (`ps -ww -o args=`; the prompt and `--model`/`--allowedTools`/`--mcp-config` are all in argv, the token is not). Diagnose before mutating, one variable at a time. Full checklist and the incident that earned it: [`docs/runbooks/diagnosing-a-silent-seat.md`](docs/runbooks/diagnosing-a-silent-seat.md). - **Verify a ship at the CONSUMER, not at the registry or the workflow.** Two different indirections bit this on 2026-08-19. (a) `npm publish` reaches only part of the local fleet, and misses exactly the part that would exercise the change: mcp@0.3.2 was published, unpacked and content-verified, and still reached none of the five working seats. **Re-measured 2026-08-30 across all 31 files in `~/.commonly/tokens/`:** 27 declare `environment.mcp`; **22 are `npx -y @commonlyai/mcp@latest`** — a floating spec resolved at spawn, so a publish DOES reach those on their next spawn — and **5 hardcode `node ~/.commonly/mcp-staging/commonly-mcp/src/index.js`**: `fable-lead`, `pod-architect`, `sprint-impl`, `sprint-review`, `ux-lead`, i.e. the sprint seats. That staging copy is hand-patched at **0.3.4** (npm latest 0.3.5), carrying `package.json.bak-0.3.0` and `.bak-0.3.1` beside the live one, and no publish can reach it. **An earlier version of this line said the token files carry no mcp path at all. That was false, and it was false because the scan enumerated TOP-LEVEL keys while `mcp` is nested under `environment`** — this entry's own error class recurring inside its correction; two seats hit the identical false negative the same night. The token file is the DECLARATION; the **spawn argv** is the LOAD — `--mcp-config /mcp-config.json`, whose `mcpServers.commonly.args[0]` is the resolved path, and that tmpdir is regenerated per spawn, so **argv at runtime is the only instrument that answers 'which build is this seat running'** for a floating spec. Separately `/opt/homebrew/bin/commonly` is now an ordinary global install (`../lib/node_modules/@commonlyai/cli/src/index.js`, cli **0.1.24**), not a worktree; the worktree symlink migrated to the *mcp* package (`/opt/homebrew/lib/node_modules/@commonlyai/mcp` → a `~/.claude/jobs/…/tmp/` checkout, **0.3.0**), which no seat loads. **A locator decays faster than the rule it supports** — re-derive the path before trusting it to check the version, and descend into nested objects when you do. (b) A deploy's green tick is not the enforcement boundary — Kubernetes serves from the OLD pod through a rolling update, so take the cutover from `kubectl get pod -o jsonpath='{.status.startTime}'`. Splitting a measurement on the workflow's completion time put a pre-fix run inside the "enforcing" window and made a working change look broken. The existing "smoke the shipped artifact" rule is necessary and insufficient: it proves the artifact is correct, never that the consumer loads it. Full write-ups: AX audit entries 34 and 35. diff --git a/docs/agents/browser-profiles.md b/docs/agents/browser-profiles.md new file mode 100644 index 0000000000..f529b696ca --- /dev/null +++ b/docs/agents/browser-profiles.md @@ -0,0 +1,17 @@ +# Browser profiles for seat walks + +**House rule, ruled by Sam on 2026-10-25.** A seat doing browser work for Commonly (vendor dashboards and consoles, GitHub, commonly.me, connector walks) may use the signed-in session of **any** ego lite profile on the host, the operators' own profiles included. Use `ego-browser nodejs`. + +**Before asking Sam to sign in, check every profile.** List them with `await listProfiles()`, open the site in each plausible one, and confirm which account is signed in **on the site itself** (its account or profile page). "Signed out" is a claim about every profile, not the first one you tried. A reply once sat unsent for five days because the one profile holding the session was never opened. + +**Never type a password, a 2FA code or card data**, in any profile. Reusing a session that is already signed in, including an existing OAuth consent, is fine. A sign-in that needs a secret goes to Sam as a decision card. + +**Only the raw API honours a profile.** `newTaskSpace(name, {profile})`, `newTaskSpace(name, 'id')` and `useOrCreateTaskSpace(name, 'id')` all silently create the space on ego's default profile. Create and confirm like this: + +```js +const t = await ego.createTaskSpace('', ''); +(await listTaskSpaces()).find(s => s.id === t.id).profileId; // must equal +// later runs: await useOrCreateTaskSpace(t.id) — by numeric id, it never re-creates +``` + +**ego profile ids are not Chrome's directory names**, and a profile's name does not tell you which account it holds. Choose by the `name` from `listProfiles()`, then confirm the account on the site before acting. From ac76c968703d80ea1313aa801b22534bb3239dec Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Sun, 25 Oct 2026 10:45:47 -0700 Subject: [PATCH 2/4] docs(agents): carry the Qing-profile exception in the ego-profile rule Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Uub7iKypiDa4iTWuc1oYZQ --- CLAUDE.md | 2 +- docs/agents/browser-profiles.md | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CLAUDE.md b/CLAUDE.md index 80f4d84b16..f6c9cb6b85 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -503,7 +503,7 @@ These are prescriptive rules not derivable from reading the code: - **Who a seat addresses (Sam, 2026-09-27).** Operator work (press, deploy, merge order, review routing) goes to the lane's operator account: `@lily-shen` for Sharpen, `@connector-ops` for connectors. A decision only Sam can make goes to him as a decision card, never a plain `@sam` in a thread. One re-ask after 12 hours of operator silence, then a decision card. Full rule: [`docs/agents/who-to-address.md`](docs/agents/who-to-address.md). -- **Browser walks may use any ego lite profile (Sam, 2026-10-25).** For Commonly work (vendor consoles, GitHub, commonly.me, connector walks) a seat may reuse the signed-in session of any ego lite profile, the operators' included. Never ask Sam to sign in until every profile from `await listProfiles()` has been checked and the signed-in account confirmed on the site itself. Never type a password, 2FA code or card data. Only `ego.createTaskSpace('', '')` honours a profile; confirm with `(await listTaskSpaces()).find(s => s.id === t.id).profileId`. ego profile ids are not Chrome directory names: choose by name, then confirm the account on the site. Full rule: [`docs/agents/browser-profiles.md`](docs/agents/browser-profiles.md). +- **Browser walks may use any ego lite profile (Sam, 2026-10-25).** For Commonly work (vendor consoles, GitHub, commonly.me, connector walks) a seat may reuse the signed-in session of any ego lite profile, the operators' included. One exception: open Qing's profile only for Rememly work. Never ask Sam to sign in until every profile from `await listProfiles()` has been checked and the signed-in account confirmed on the site itself. Never type a password, 2FA code or card data. Only `ego.createTaskSpace('', '')` honours a profile; confirm with `(await listTaskSpaces()).find(s => s.id === t.id).profileId`. ego profile ids are not Chrome directory names: choose by name, then confirm the account on the site. Full rule: [`docs/agents/browser-profiles.md`](docs/agents/browser-profiles.md). - **A silent seat is not evidence of a broken seat.** Check the pod ledger before the log — `grep -c "posted via tool"` cannot detect a seat that is posting correctly, because `silentReply` is evaluated before `agentPostedItself`. Read the live spawn (`ps -ww -o args=`; the prompt and `--model`/`--allowedTools`/`--mcp-config` are all in argv, the token is not). Diagnose before mutating, one variable at a time. Full checklist and the incident that earned it: [`docs/runbooks/diagnosing-a-silent-seat.md`](docs/runbooks/diagnosing-a-silent-seat.md). diff --git a/docs/agents/browser-profiles.md b/docs/agents/browser-profiles.md index f529b696ca..90319a38c9 100644 --- a/docs/agents/browser-profiles.md +++ b/docs/agents/browser-profiles.md @@ -2,6 +2,8 @@ **House rule, ruled by Sam on 2026-10-25.** A seat doing browser work for Commonly (vendor dashboards and consoles, GitHub, commonly.me, connector walks) may use the signed-in session of **any** ego lite profile on the host, the operators' own profiles included. Use `ego-browser nodejs`. +**One exception: Qing's profile is opened only for Rememly work**, never for Commonly work. + **Before asking Sam to sign in, check every profile.** List them with `await listProfiles()`, open the site in each plausible one, and confirm which account is signed in **on the site itself** (its account or profile page). "Signed out" is a claim about every profile, not the first one you tried. A reply once sat unsent for five days because the one profile holding the session was never opened. **Never type a password, a 2FA code or card data**, in any profile. Reusing a session that is already signed in, including an existing OAuth consent, is fine. A sign-in that needs a secret goes to Sam as a decision card. From 57fc6599b0ec82afc78582405023a4b0eaeaded4 Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Sun, 25 Oct 2026 11:45:19 -0700 Subject: [PATCH 3/4] docs(agents): check every listed profile; a borrowed session does not widen authority Answers both CodeRabbit threads on #2682. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Uub7iKypiDa4iTWuc1oYZQ --- CLAUDE.md | 2 +- docs/agents/browser-profiles.md | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index f6c9cb6b85..fe9be63e5b 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -503,7 +503,7 @@ These are prescriptive rules not derivable from reading the code: - **Who a seat addresses (Sam, 2026-09-27).** Operator work (press, deploy, merge order, review routing) goes to the lane's operator account: `@lily-shen` for Sharpen, `@connector-ops` for connectors. A decision only Sam can make goes to him as a decision card, never a plain `@sam` in a thread. One re-ask after 12 hours of operator silence, then a decision card. Full rule: [`docs/agents/who-to-address.md`](docs/agents/who-to-address.md). -- **Browser walks may use any ego lite profile (Sam, 2026-10-25).** For Commonly work (vendor consoles, GitHub, commonly.me, connector walks) a seat may reuse the signed-in session of any ego lite profile, the operators' included. One exception: open Qing's profile only for Rememly work. Never ask Sam to sign in until every profile from `await listProfiles()` has been checked and the signed-in account confirmed on the site itself. Never type a password, 2FA code or card data. Only `ego.createTaskSpace('', '')` honours a profile; confirm with `(await listTaskSpaces()).find(s => s.id === t.id).profileId`. ego profile ids are not Chrome directory names: choose by name, then confirm the account on the site. Full rule: [`docs/agents/browser-profiles.md`](docs/agents/browser-profiles.md). +- **Browser walks may use any ego lite profile (Sam, 2026-10-25).** For Commonly work (vendor consoles, GitHub, commonly.me, connector walks) a seat may reuse the signed-in session of any ego lite profile, the operators' included. One exception: open Qing's profile only for Rememly work. A borrowed session does not widen what a seat may do: in the browser it reads, walks and verifies, and anything Sam's alone (money, sending as Sam or Lily, account or security settings, merges, OAuth consents) still needs his yes. Never ask Sam to sign in until every profile from `await listProfiles()` has been checked and the signed-in account confirmed on the site itself. Never type a password, 2FA code or card data. Only `ego.createTaskSpace('', '')` honours a profile; confirm with `(await listTaskSpaces()).find(s => s.id === t.id).profileId`. ego profile ids are not Chrome directory names: choose by name, then confirm the account on the site. Full rule: [`docs/agents/browser-profiles.md`](docs/agents/browser-profiles.md). - **A silent seat is not evidence of a broken seat.** Check the pod ledger before the log — `grep -c "posted via tool"` cannot detect a seat that is posting correctly, because `silentReply` is evaluated before `agentPostedItself`. Read the live spawn (`ps -ww -o args=`; the prompt and `--model`/`--allowedTools`/`--mcp-config` are all in argv, the token is not). Diagnose before mutating, one variable at a time. Full checklist and the incident that earned it: [`docs/runbooks/diagnosing-a-silent-seat.md`](docs/runbooks/diagnosing-a-silent-seat.md). diff --git a/docs/agents/browser-profiles.md b/docs/agents/browser-profiles.md index 90319a38c9..a32b5b7c89 100644 --- a/docs/agents/browser-profiles.md +++ b/docs/agents/browser-profiles.md @@ -4,7 +4,9 @@ **One exception: Qing's profile is opened only for Rememly work**, never for Commonly work. -**Before asking Sam to sign in, check every profile.** List them with `await listProfiles()`, open the site in each plausible one, and confirm which account is signed in **on the site itself** (its account or profile page). "Signed out" is a claim about every profile, not the first one you tried. A reply once sat unsent for five days because the one profile holding the session was never opened. +**Before asking Sam to sign in, check every profile.** List them with `await listProfiles()`, open the site in each profile `listProfiles()` returns, and confirm which account is signed in **on the site itself** (its account or profile page). "Signed out" is a claim about every profile, not the first one you tried. A reply once sat unsent for five days because the one profile holding the session was never opened. + +**A borrowed session does not widen what a seat may do.** A browser action in an operator's profile acts as that operator, so the seat does in the browser only what it may already do as itself: read, walk, and verify. Anything that is Sam's alone (money, sending or posting as Sam or Lily, account, billing or security settings, merges, OAuth consents), or anything outward-facing that the seat's own rules would ask first for, still needs that ask. A logged-in session is not the approval. **Never type a password, a 2FA code or card data**, in any profile. Reusing a session that is already signed in, including an existing OAuth consent, is fine. A sign-in that needs a secret goes to Sam as a decision card. From bb78834b38709c7c487467a166c14da2857ff400 Mon Sep 17 00:00:00 2001 From: Lily Shen <115414357+lilyshen0722@users.noreply.github.com> Date: Mon, 26 Oct 2026 03:22:55 -0700 Subject: [PATCH 4/4] docs(agents): cut the CLAUDE.md bullet to a pointer; exclude Qing's profile from the every-profile check Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01Uub7iKypiDa4iTWuc1oYZQ --- CLAUDE.md | 2 +- docs/agents/browser-profiles.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index fe9be63e5b..dd69a2740d 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -503,7 +503,7 @@ These are prescriptive rules not derivable from reading the code: - **Who a seat addresses (Sam, 2026-09-27).** Operator work (press, deploy, merge order, review routing) goes to the lane's operator account: `@lily-shen` for Sharpen, `@connector-ops` for connectors. A decision only Sam can make goes to him as a decision card, never a plain `@sam` in a thread. One re-ask after 12 hours of operator silence, then a decision card. Full rule: [`docs/agents/who-to-address.md`](docs/agents/who-to-address.md). -- **Browser walks may use any ego lite profile (Sam, 2026-10-25).** For Commonly work (vendor consoles, GitHub, commonly.me, connector walks) a seat may reuse the signed-in session of any ego lite profile, the operators' included. One exception: open Qing's profile only for Rememly work. A borrowed session does not widen what a seat may do: in the browser it reads, walks and verifies, and anything Sam's alone (money, sending as Sam or Lily, account or security settings, merges, OAuth consents) still needs his yes. Never ask Sam to sign in until every profile from `await listProfiles()` has been checked and the signed-in account confirmed on the site itself. Never type a password, 2FA code or card data. Only `ego.createTaskSpace('', '')` honours a profile; confirm with `(await listTaskSpaces()).find(s => s.id === t.id).profileId`. ego profile ids are not Chrome directory names: choose by name, then confirm the account on the site. Full rule: [`docs/agents/browser-profiles.md`](docs/agents/browser-profiles.md). +- **Browser walks may use any ego lite profile except Qing's, which is for Rememly work only (Sam, 2026-10-25); a borrowed session never widens what a seat may do.** Full rule: [`docs/agents/browser-profiles.md`](docs/agents/browser-profiles.md). - **A silent seat is not evidence of a broken seat.** Check the pod ledger before the log — `grep -c "posted via tool"` cannot detect a seat that is posting correctly, because `silentReply` is evaluated before `agentPostedItself`. Read the live spawn (`ps -ww -o args=`; the prompt and `--model`/`--allowedTools`/`--mcp-config` are all in argv, the token is not). Diagnose before mutating, one variable at a time. Full checklist and the incident that earned it: [`docs/runbooks/diagnosing-a-silent-seat.md`](docs/runbooks/diagnosing-a-silent-seat.md). diff --git a/docs/agents/browser-profiles.md b/docs/agents/browser-profiles.md index a32b5b7c89..ad506f0d5d 100644 --- a/docs/agents/browser-profiles.md +++ b/docs/agents/browser-profiles.md @@ -4,7 +4,7 @@ **One exception: Qing's profile is opened only for Rememly work**, never for Commonly work. -**Before asking Sam to sign in, check every profile.** List them with `await listProfiles()`, open the site in each profile `listProfiles()` returns, and confirm which account is signed in **on the site itself** (its account or profile page). "Signed out" is a claim about every profile, not the first one you tried. A reply once sat unsent for five days because the one profile holding the session was never opened. +**Before asking Sam to sign in, check every profile.** List them with `await listProfiles()`, open the site in each profile eligible for the work (for Commonly work, every profile except Qing's), and confirm which account is signed in **on the site itself** (its account or profile page). "Signed out" is a claim about every profile, not the first one you tried. A reply once sat unsent for five days because the one profile holding the session was never opened. **A borrowed session does not widen what a seat may do.** A browser action in an operator's profile acts as that operator, so the seat does in the browser only what it may already do as itself: read, walk, and verify. Anything that is Sam's alone (money, sending or posting as Sam or Lily, account, billing or security settings, merges, OAuth consents), or anything outward-facing that the seat's own rules would ask first for, still needs that ask. A logged-in session is not the approval.