Problem
After #489, teamai members on an independent Git clone can create and push a remote teamai-reports branch even though the user only requested a member listing.
Reproduction and actual result
- Create a bare remote containing only
main, with a valid teamai.yaml.
- Configure a local user-scope independent clone pointing to that remote, with no reports worktree.
- Run
teamai members.
- Inspect branches in the bare remote.
Observed output: No team members registered. Before the command, the remote has only main; afterward it also has teamai-reports with an initialization commit.
Expected: a member listing may fetch/materialize a local view but should not publish a branch.
Suggested fix
Pass { pushIfCreated: false } consistently through the read-only refresh/ensure calls. Audit other read-only reports consumers, including digest and projects members, for the same default-push behavior. Preserve default publishing for actual writers.
Relevant code at reviewed head: src/members.ts:74-76; ensureReportsWorktree publishes a cold-start branch unless pushIfCreated is false. maintenance/paths.ts already uses the read-only option.
Verification
Reproduced on PR #489 head d11e1018af3cd6ce580bb1b4801e6efbb91b0af7 with Node 22.22.0 on macOS, real local bare Git remotes, and the built CLI. Build, typecheck, and 118 focused tests passed. The full Agent × Provider matrix was not run. Accepted as a follow-up when merging #489.
Standalone reproduction
Run npm run build, save the following as repro.py, then run python3 repro.py from the repository root. It creates isolated temporary repositories and a temporary HOME; no hosted remote is used. The script demonstrates both follow-up findings.
import os, json, subprocess, pathlib, tempfile
root=pathlib.Path(tempfile.mkdtemp(prefix='pr489-repro-')); home=root/'home'; home.mkdir(); work=root/'work'; work.mkdir()
env=dict(os.environ, HOME=str(home), GIT_CONFIG_NOSYSTEM='1', GIT_CONFIG_GLOBAL=str(root/'gitconfig'), GIT_AUTHOR_NAME='Alice', GIT_AUTHOR_EMAIL='alice@example.test', GIT_COMMITTER_NAME='Alice', GIT_COMMITTER_EMAIL='alice@example.test')
(root/'gitconfig').write_text('[init]\n defaultBranch = main\n')
def run(args,cwd=work):
p=subprocess.run(args,cwd=cwd,env=env,text=True,stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
if p.returncode: raise Exception(p.stdout)
return p.stdout
seed=root/'seed'; seed.mkdir(); run(['git','init'],seed); (seed/'teamai.yaml').write_text('team: review\nrepo: https://example.test/team.git\nsharing:\n recall:\n enabled: true\n'); (seed/'docs').mkdir(); (seed/'docs'/'guide.md').write_text('# Guide\nUseful guide for a reproducible review.\n'); run(['git','add','.'],seed); run(['git','commit','-m','seed'],seed)
remote=root/'remote.git'; run(['git','clone','--bare',str(seed),str(remote)])
clone=home/'.teamai'/'team-repo'; clone.parent.mkdir(); run(['git','clone',str(remote),str(clone)])
(clone.parent/'config.yaml').write_text(json.dumps(dict(repo=dict(kind='git',localPath=str(clone),remote=str(remote)),username='alice',scope='user',additionalRoles=[],agents=['claude'])))
cli=['node', str(pathlib.Path.cwd()/'dist/index.js')]
print('FIXTURE',root)
print('BRANCHES BEFORE',run(['git','branch'],remote)); print('MEMBERS',run(cli+['members'])); print('BRANCHES AFTER',run(['git','branch'],remote))
print('PULL1',run(cli+['pull','--force']))
idx=clone.parent/'search-index.json'; data=json.loads(idx.read_text()); print('INDEX',json.dumps(data)[:1700])
other=root/'other'; run(['git','clone','-b','teamai-reports',str(remote),str(other)])
(other/'votes').mkdir(); (other/'votes'/'bob.yaml').write_text('votes:\n docs/guide.md:\n at: "2026-09-14T00:00:00Z"\n'); run(['git','add','.'],other); run(['git','commit','-m','bob votes'],other); run(['git','push'],other)
print('PULL2',run(cli+['pull','--force'])); print('LOCAL VOTE EXISTS',(clone.parent/'reports-wt'/'votes'/'bob.yaml').exists()); print('REMOTE VOTE',run(['git','show','teamai-reports:votes/bob.yaml'],remote)); print('INDEX2',idx.read_text()[:1700])
print('MEMBERS REFRESH',run(cli+['members'])); print('PULL3',run(cli+['pull','--force'])); print('INDEX3',idx.read_text()[:1700])
Problem
After #489,
teamai memberson an independent Git clone can create and push a remoteteamai-reportsbranch even though the user only requested a member listing.Reproduction and actual result
main, with a validteamai.yaml.teamai members.Observed output:
No team members registered. Before the command, the remote has onlymain; afterward it also hasteamai-reportswith an initialization commit.Expected: a member listing may fetch/materialize a local view but should not publish a branch.
Suggested fix
Pass
{ pushIfCreated: false }consistently through the read-only refresh/ensure calls. Audit other read-only reports consumers, including digest and projects members, for the same default-push behavior. Preserve default publishing for actual writers.Relevant code at reviewed head:
src/members.ts:74-76;ensureReportsWorktreepublishes a cold-start branch unlesspushIfCreatedis false.maintenance/paths.tsalready uses the read-only option.Verification
Reproduced on PR #489 head
d11e1018af3cd6ce580bb1b4801e6efbb91b0af7with Node 22.22.0 on macOS, real local bare Git remotes, and the built CLI. Build, typecheck, and 118 focused tests passed. The full Agent × Provider matrix was not run. Accepted as a follow-up when merging #489.Standalone reproduction
Run
npm run build, save the following asrepro.py, then runpython3 repro.pyfrom the repository root. It creates isolated temporary repositories and a temporary HOME; no hosted remote is used. The script demonstrates both follow-up findings.