Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -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 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).

- **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 <tmpdir>/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.
Expand Down
21 changes: 21 additions & 0 deletions docs/agents/browser-profiles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# 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`.

**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 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.

**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('<name>', '<profileId>');
(await listTaskSpaces()).find(s => s.id === t.id).profileId; // must equal <profileId>
// 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.
Loading