Skip to content

fix(members): read the default-branch roster as an inherited root after the reports switch - #741

Merged
jeff-r2026 merged 1 commit into
Tencent:mainfrom
jimpablo:fix/members-legacy-roster
Sep 23, 2026
Merged

jeff-r2026 merged 1 commit into
Tencent:mainfrom
jimpablo:fix/members-legacy-roster

Conversation

@jimpablo

Copy link
Copy Markdown
Contributor

Summary

A team whose roster was written to the default branch by a pre-#489 CLI loses every member from members list / projects members once it upgrades past the orphan-branch split: the commands read only the teamai-reports worktree, which has no members/ yet, and the pre-switch copy on main was deliberately ignored (#489). The roster, unlike stats/sessions/votes, is never regenerated automatically — those come back as members report usage, while the roster needs one init per member — so an upgrading team just loses its member list.

This PR keeps the default-branch clone as a read-only inherited member root, the same pattern learnings already use (#485, learnings-roots.ts):

  • memberReadRoots() / readMemberConfig() in src/members.ts — primary root first (reports worktree, or the clone for HTTP), default-branch clone as the lowest-precedence inherited root.
  • members list and projects members list the union of both roots; when the same file exists on both, the reports-branch copy wins.
  • Nothing is copied or deleted, and read-only commands still never publish the reports branch (the fix(members): avoid publishing reports branch from read-only queries #558 guarantee is unchanged and still asserted).
  • Member registration (init, self-mode bootstrap) merges against the inherited copy, so a re-init preserves the original registeredAt/displayName/projects and writes the merged file to the reports branch — the data converges onto the branch gradually, one member at a time, with no migration step.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature causing existing behavior to change)
  • Documentation only
  • Refactor / internal cleanup

Test Plan

  • npx tsc --noEmit passes
  • npx vitest run passes — 272 files, 3885 passed | 1 skipped
  • Added/updated tests for the change
    • src/__tests__/members.test.ts — the two tests that pinned "ignore leftover clone members" now pin the union + branch-copy-wins behavior; new memberReadRoots / readMemberConfig describes.
    • src/__tests__/git-kind-reports.test.ts — new real-git describe inherited member root (#735): pre-switch roster listed without copying/publishing, union + conflict through a real worktree. The existing "not copied or deleted" test is kept (renamed; its assertions are unchanged and still hold).
    • src/__tests__/projects-members.test.ts (new) — projects members inherited-root fallback.
    • src/__tests__/e2e/members-legacy-roster.test.ts (new) — built-CLI repro of [bug] 0.25.0版本 teamai members list报No team members registered #735 with a bare origin + isolated HOME.
  • npm run build + full e2e: npx vitest run --config vitest.e2e.config.ts — 41 files passed | 3 skipped (live-provider files need credentials), 177 passed | 26 skipped, exit 0.

Real-CLI end-to-end records (built dist/index.js, macOS 15, Node v24.19.0, sandbox with a local bare origin):

1. The #735 repro — roster on main, no teamai-reports branch anywhere:

$ teamai members list

Team members (2):

  carol — Carol Pre
  dan
exit=0

$ git --git-dir=origin.git for-each-ref --format='%(refname:short)' refs/heads   # read stays read-only
main
$ ls ~/.teamai/reports-wt/members/    # nothing copied into the worktree
(empty or absent)

2. A teammate registers on the new CLI (origin grows teamai-reports with bob + a newer carol copy) — union, branch copy wins:

$ teamai members list

Team members (3):

  bob
  carol — Carol Branch
  dan
exit=0

3. Writer absorption — fresh machine, roster only on main, real teamai init (generic git provider):

$ teamai init https://fakehost.invalid/team/repo.git --scope user --agent claude --project billing --force
✔ Registered as team member: carol
✔ Member registered on the teamai-reports branch
✔ teamai initialized successfully!

$ git --git-dir=origin.git show teamai-reports:members/carol.yaml   # legacy fields absorbed
username: carol
displayName: Carol Pre        # from the pre-switch copy on main
registeredAt: 2025-01-01T00:00:00.000Z   # from the pre-switch copy on main
projects:
  - checkout                  # legacy project kept
  - billing                   # new project merged in

Related Issues

Fixes #735

Notes for Reviewers

  • feat(git): route independent-clone reports onto teamai-reports #489 documented "leftover report files on main are ignored" as intentional, so this is a deliberate design change for members/ only — argued in the Summary: the roster cannot self-heal the way stats/sessions/votes do, and the inherited-root pattern is the one the repo already established for learnings in Write learnings/ to a teamai-learnings branch (direct push, no PR) #485. The usage guide now documents the asymmetry the other way: members/ keeps being read, other reports stay ignored.
  • If you'd rather see a one-time migration that seeds the reports branch from the clone, the read path here would still be needed for machines that never re-init; happy to rework if you prefer that shape.
  • isNewMember (the "Register member" vs "Update member roster" message) still keys off the worktree path only — a re-registering pre-switch member reports as newly registered on the branch, which seemed fine to keep surgical; say if you want that to consider the inherited root too.

…er the reports switch

The orphan-branch switch (Tencent#489) made members list and projects members read
only the teamai-reports worktree, so a team whose roster still lives on the
default branch saw "No team members registered" right after upgrading (Tencent#735).

The default-branch clone now stays a read-only inherited member root, the way
learnings' already is (Tencent#485): listing unions both roots (the reports-branch
copy wins when the same file exists on both), nothing is copied or deleted, and
read-only commands still never publish the reports branch. Member registration
merges against the inherited copy too, so a re-init keeps the original
registeredAt/projects and converges the data onto the branch.

Fixes Tencent#735
@jimpablo
jimpablo force-pushed the fix/members-legacy-roster branch from f4ebe06 to 3e8dc3a Compare September 23, 2026 03:12
@jeff-r2026 jeff-r2026 self-assigned this Sep 23, 2026
@github-actions

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/init.ts:993 treats an inherited member as unchanged when the reports-branch file is absent. Consequently, initSelfRepo returns without writing the inherited entry; the same bug occurs at src/init.ts:1463 in normal init. Re-init only absorbs legacy data when a role/project changes, contradicting the documented gradual migration and leaving unchanged legacy members permanently on the default branch.
  • [P1 blocking] src/members.ts:99 refreshes only teamai-reports for Git-backed repositories, then reads the inherited roster from the potentially stale default-branch checkout. projects members has the same problem at src/projects-cmd.ts:140. A machine whose clone predates later legacy registrations continues omitting those members even though they exist on origin/main; the default-branch root must be refreshed before forming the union.

The PR description includes both a test plan and real-CLI end-to-end records, so it satisfies the testing-documentation requirement.

@jeff-r2026
jeff-r2026 merged commit bc6943e into Tencent:main Sep 23, 2026
12 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[bug] 0.25.0版本 teamai members list报No team members registered

2 participants