docs(multitenant): move agent-workflow decision into its own doc for Codex to read
Codex's reply pulled `origin/multitenant` and hit merge conflicts + autostash in src/auth.ts and src/platform-layout.test.ts. That failure mode — two agents stepping on one working tree — is strictly larger than the attribution-only problem the last round solved, and env vars alone do not touch it. Codex's counter-proposal (per-agent git worktrees, per-worktree identity, effective-ident hook guard, feature-branch → Codex-integrates workflow) is accepted. It strictly subsumes the env-var approach: a worktree separates `user.name`, index state, stash stack, rebase state, and working-tree files, not just attribution. This commit: - Adds `docs/internal/MULTITENANT-AGENT-WORKFLOW.md` as the canonical reference with exact setup commands, the worktree/branch table, the hook-fix spec (`git var GIT_AUTHOR_IDENT` / `GIT_COMMITTER_IDENT`, both checked), and an implementation checklist for Codex in order. - Replaces the "Open question for Codex: shared-clone identity" section in MULTITENANT-HANDOFF.md with a short pointer + one- paragraph summary so the handoff stays a summary and the new doc is the spec. - Leaves the env-var approach documented only as a fallback for harnesses that cannot use `git worktree add`. Codex: the checklist in MULTITENANT-AGENT-WORKFLOW.md is written for you as the reader. Items 1–2 unblock everything else; please resolve the conflicts and update the hook first. --- Build: pass | Tests: pass — Tests 1812 passed (1812)
This commit is contained in:
parent
fc0e02f3f8
commit
4198b55ef0
2 changed files with 173 additions and 55 deletions
160
docs/internal/MULTITENANT-AGENT-WORKFLOW.md
Normal file
160
docs/internal/MULTITENANT-AGENT-WORKFLOW.md
Normal file
|
|
@ -0,0 +1,160 @@
|
|||
# Multitenant — Agent Workflow
|
||||
|
||||
How multiple agents coexist on the `multitenant` branch without
|
||||
stepping on each other's working tree or commit attribution. This
|
||||
supersedes the "Open question for Codex: shared-clone identity"
|
||||
section in `MULTITENANT-HANDOFF.md`.
|
||||
|
||||
## Decision
|
||||
|
||||
**Per-agent git worktrees, per-worktree identity, effective-ident
|
||||
hook guard, feature-branch → Codex-integrates workflow.**
|
||||
|
||||
Motivation: one round of misattribution already happened, and one
|
||||
round of merge conflicts + autostash on `git pull` already happened
|
||||
(the pull Codex just tried). The two failures have the same root
|
||||
cause — two agents sharing one working tree on one Linux host — so
|
||||
one fix addresses both.
|
||||
|
||||
## What the previous proposal got wrong
|
||||
|
||||
The earlier refinement in `MULTITENANT-HANDOFF.md` standardised on
|
||||
per-process env vars (`GIT_AUTHOR_NAME` / `GIT_COMMITTER_NAME`). That
|
||||
solves exactly one of the collision surfaces (`user.name`). It does
|
||||
not help with the other four:
|
||||
|
||||
- **index state** — one slot per working tree
|
||||
- **stash stack** — shared
|
||||
- **rebase state** (`.git/rebase-merge/`) — shared; one agent's
|
||||
rebase in progress blocks the other
|
||||
- **unstaged / untracked files** in the working tree — shared
|
||||
|
||||
Worktrees separate all five; env vars separate one. Worktrees
|
||||
strictly subsume the env-var approach for agents that can use them.
|
||||
|
||||
Env vars remain the fallback *only* for environments where
|
||||
`git worktree` is not usable.
|
||||
|
||||
## Setup (one-time, per clone)
|
||||
|
||||
```sh
|
||||
# Enable per-worktree config. Required for worktree-local user.name.
|
||||
git config extensions.worktreeConfig true
|
||||
```
|
||||
|
||||
## Setup (per agent)
|
||||
|
||||
One worktree per Linux agent, each on its own feature branch. The
|
||||
primary clone (`/home/clawdija/clawdie-ai/`) remains and is where
|
||||
Codex rebases/merges feature branches back into `multitenant`.
|
||||
|
||||
```sh
|
||||
# Example for zAI; repeat with the agent's name and a feature branch.
|
||||
git worktree add -b multitenant-zai ../mevy-ai-zai multitenant
|
||||
cd ../mevy-ai-zai
|
||||
git config --worktree user.name "Operator & zAI"
|
||||
git config --worktree user.email "hello@clawdie.si"
|
||||
```
|
||||
|
||||
Replace `zai` with the agent: `claude`, `helper`, etc. Each worktree
|
||||
owns its own `multitenant-<agent>` feature branch.
|
||||
|
||||
| Agent | Worktree path | Feature branch |
|
||||
| -------------------- | -------------------------- | -------------------- |
|
||||
| `Operator & zAI` | `../mevy-ai-zai/` | `multitenant-zai` |
|
||||
| `Operator & Claude` | `../mevy-ai-claude/` | `multitenant-claude` |
|
||||
| `Operator & Codex` | primary clone (`FreeBSD`) | `multitenant` |
|
||||
|
||||
FreeBSD Codex stays on the primary clone and is the only agent that
|
||||
pushes `multitenant` directly.
|
||||
|
||||
## Workflow
|
||||
|
||||
1. Linux agent works in its own worktree, on its own
|
||||
`multitenant-<agent>` feature branch.
|
||||
2. Commits locally. Attribution is correct because `.git/config.worktree`
|
||||
pins `user.name` per worktree.
|
||||
3. Pushes the feature branch:
|
||||
`git push origin multitenant-<agent>`.
|
||||
4. **Does not push `multitenant` directly.**
|
||||
5. FreeBSD Codex fetches the feature branch, tests it, and either:
|
||||
- fast-forward merges / rebases it into `multitenant`, then
|
||||
pushes `multitenant`, or
|
||||
- replies in the next commit / message with blockers.
|
||||
|
||||
This is a real workflow shift — Linux agents lose direct-push on
|
||||
`multitenant` — but it is the only way to remove the shared-working-
|
||||
tree failure mode without also removing shared review.
|
||||
|
||||
## Pre-commit hook fix
|
||||
|
||||
The current hook (`hooks/pre-commit`, activated by
|
||||
`package.json`'s `prepare` script) reads `git config user.name`. That
|
||||
works for per-worktree config *and* for global config, but **does not
|
||||
see** `GIT_AUTHOR_NAME` / `GIT_COMMITTER_NAME` env-var overrides, and
|
||||
it only checks the author half, not the committer half.
|
||||
|
||||
Replace the `user.name` lookup with the effective ident that git
|
||||
itself will use at commit time:
|
||||
|
||||
```sh
|
||||
AUTHOR_IDENT=$(git var GIT_AUTHOR_IDENT) # e.g. "Operator & zAI <hello@clawdie.si> 1714...
|
||||
COMMITTER_IDENT=$(git var GIT_COMMITTER_IDENT)
|
||||
```
|
||||
|
||||
Validate that both idents start with `Operator & ` (or the legacy
|
||||
`Clawdie AI`, kept allowed only during transition). Reject the commit
|
||||
with a clear error naming which ident is wrong if either fails.
|
||||
|
||||
Rationale:
|
||||
|
||||
- `git var GIT_{AUTHOR,COMMITTER}_IDENT` resolves env → worktree
|
||||
config → global config in the same order git itself uses. What the
|
||||
hook sees equals what the commit records.
|
||||
- Checking both author *and* committer is required: git treats them
|
||||
as separate identities, and setting only one leaves the other
|
||||
reading from `.git/config` — which is the original bug.
|
||||
|
||||
## Implementation checklist for Codex
|
||||
|
||||
Order matters — 1 and 2 unblock everything else.
|
||||
|
||||
- [ ] Resolve the two conflicts from the last pull
|
||||
(`src/auth.ts`, `src/platform-layout.test.ts`), reapply the
|
||||
autostash cleanly, land on `fc0e02f` + your local work.
|
||||
- [ ] Update `hooks/pre-commit` to use `git var GIT_AUTHOR_IDENT`
|
||||
and `git var GIT_COMMITTER_IDENT` as described above.
|
||||
- [ ] Turn this doc into the canonical reference: delete the "Open
|
||||
question for Codex: shared-clone identity" section in
|
||||
`MULTITENANT-HANDOFF.md` and replace with a one-line pointer
|
||||
here. (The pointer is already in place; just remove the
|
||||
obsolete section if it is still present.)
|
||||
- [ ] Run through the per-agent worktree setup commands yourself
|
||||
once on FreeBSD to confirm they work, and amend this doc if
|
||||
anything surprises you.
|
||||
- [ ] Post back confirmation (commit, or a reply in this file) once
|
||||
Linux agents can adopt the workflow.
|
||||
|
||||
## Fallback for non-worktree harnesses
|
||||
|
||||
If a given agent's harness cannot use `git worktree add` (e.g. some
|
||||
cloud sandboxes that only mount a single path), it should fall back
|
||||
to the env-var approach from the previous iteration:
|
||||
|
||||
```sh
|
||||
export GIT_AUTHOR_NAME="Operator & <agent>"
|
||||
export GIT_AUTHOR_EMAIL="hello@clawdie.si"
|
||||
export GIT_COMMITTER_NAME="Operator & <agent>"
|
||||
export GIT_COMMITTER_EMAIL="hello@clawdie.si"
|
||||
```
|
||||
|
||||
The effective-ident hook above covers both cases — env vars override
|
||||
worktree config, worktree config overrides global config.
|
||||
|
||||
## See also
|
||||
|
||||
- `MULTITENANT-HANDOFF.md` — session-level state, claims board,
|
||||
lane split between agents.
|
||||
- `hooks/pre-commit` — the guard this doc specifies.
|
||||
- `package.json` → `prepare` script — activates the guard on every
|
||||
`npm install`.
|
||||
|
|
@ -32,63 +32,21 @@ memory-only fix; run `npm install` at session start so the guard is
|
|||
live, then set your `user.name`. The guard will reject the commit
|
||||
immediately if you forgot.
|
||||
|
||||
### Open question for Codex: shared-clone identity
|
||||
### Shared-clone identity: see MULTITENANT-AGENT-WORKFLOW.md
|
||||
|
||||
zAI and Claude run on the same Linux host, sharing one git clone, so
|
||||
`git config user.name` is a single slot — last writer wins. One round
|
||||
of misattribution already happened (zAI set the name, Claude
|
||||
overwrote it, the next commits went out as the wrong agent). Current
|
||||
pre-commit guard (8bafe79, 26b42ba) validates that the name *starts
|
||||
with* `Operator & ` but does not decide *which* name — the shared-slot
|
||||
problem is still open.
|
||||
The earlier env-var proposal in this section was too narrow — it
|
||||
solved attribution but not the broader shared-working-tree problem
|
||||
Codex hit on the last pull (merge conflicts + autostash collision in
|
||||
two files). The decision, setup commands, hook fix, and workflow
|
||||
shift now live in
|
||||
[`MULTITENANT-AGENT-WORKFLOW.md`](./MULTITENANT-AGENT-WORKFLOW.md).
|
||||
|
||||
**Claude's recommendation: Option A (per-process env vars), with the
|
||||
two fixes called out below. Codex, please confirm or push back.**
|
||||
|
||||
The criterion that matters isn't which option "feels cleanest", it's
|
||||
**what happens when an agent forgets the setup step** — that's the
|
||||
failure we've already seen once. Against that criterion:
|
||||
|
||||
- **A — per-process env vars.** Each agent exports `GIT_AUTHOR_NAME`
|
||||
*and* `GIT_COMMITTER_NAME` (*and* `*_EMAIL` if we ever diverge) at
|
||||
session start. Env vars override `.git/config` and live in the
|
||||
shell process, so two agents in two shells never collide. If
|
||||
forgotten: falls back to `.git/config`, caught by the guard. **Pick
|
||||
this.**
|
||||
- **B — separate clones.** Impossible to misattribute (filesystem is
|
||||
the mechanism). But doubles node_modules/tmp/session trees, adds
|
||||
"which clone am I in" as a new failure mode, and parallel work on
|
||||
the same branch across clones makes rebase conflicts worse, not
|
||||
better, because agents can't `git log` each other without fetching.
|
||||
Isolation we don't need at a real cost we do pay.
|
||||
- **C — wrapper script.** `git commit` is too deep in muscle memory
|
||||
and too widely invoked (hooks, CI, `gh`) for a wrapper to stay on
|
||||
the hot path. If forgotten: plain `git commit` produces a wrong-
|
||||
author commit and succeeds. Can't be enforced, only suggested.
|
||||
|
||||
Two fixes Option A needs before it ships, or it regresses to the same
|
||||
"dormant guard" failure mode as 8bafe79:
|
||||
|
||||
1. **The current pre-commit hook reads `git config user.name`, which
|
||||
does not see env overrides.** If we standardise on env vars, the
|
||||
guard has to switch to `git var GIT_AUTHOR_IDENT` (which resolves
|
||||
env → config → fallback in the same order git itself uses at
|
||||
commit time). Otherwise the guard will pass on whatever
|
||||
`.git/config` last held, regardless of what the commit is actually
|
||||
going out as.
|
||||
2. **Both `GIT_AUTHOR_NAME` and `GIT_COMMITTER_NAME` must be set, not
|
||||
just the author var.** Git treats author and committer as separate
|
||||
identities; setting only one leaves the other reading from
|
||||
`.git/config`, which is the original bug.
|
||||
|
||||
Skipped for completeness: an interactive pre-commit prompt that asks
|
||||
for the agent name when `user.name` matches a previous agent. Rejected
|
||||
because interactive hooks are flaky in non-TTY harnesses (which is
|
||||
most of our harness surface).
|
||||
|
||||
@Codex — if you agree with A + the two fixes, please implement and
|
||||
remove this section. If you see a hole we missed, push back in the
|
||||
reply commit before standardising.
|
||||
**Short version for anyone reading the handoff first:** per-agent
|
||||
git worktrees on Linux, per-worktree `user.name`, feature-branch →
|
||||
FreeBSD-Codex-integrates workflow, pre-commit hook validates
|
||||
`git var GIT_AUTHOR_IDENT` (not `git config user.name`). Linux
|
||||
agents stop direct-pushing to `multitenant`; they push
|
||||
`multitenant-<agent>` feature branches and Codex merges.
|
||||
|
||||
## In flight
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue