Skip to content

fix(members): avoid publishing reports branch from read-only queries #558

Description

@jeff-r2026

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

  1. Create a bare remote containing only main, with a valid teamai.yaml.
  2. Configure a local user-scope independent clone pointing to that remote, with no reports worktree.
  3. Run teamai members.
  4. 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])

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinghelp wantedExtra attention is needed

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions