Problem
Report writers read, merge, and write their own per-member file inside the teamai-reports worktree, but never sync that worktree with origin first:
sessions/<user>/<month>.md (session save --push) — still stale
votes/<user>.yaml (Stop hook) — still stale
members/<user>.yaml (init / bootstrap) — still stale
stats/<user>.yaml / votes from reportUsageToTeam — still stale when that function runs without a prior refresh
commitAndPushReports only fetches and rebases after a push is rejected. Its comment assumes rebase conflicts are effectively impossible because each member writes only their own file. That is false when the same member reports from two checkouts: both write sessions/alice/… (or stats/alice.yaml, votes/alice.yaml).
When that happens, the merge starts from a stale copy of the member's own file. The push is rejected, the rebase conflicts on that file, and the new entry never reaches origin.
#560 (merged as 1734c31) already:
Unstick ≠ recover: a later members / pull refresh will not bring the lost session/vote/stats entry back.
Independent git clones have been affected since #489 (before it, the git-kind auto-report ran resetToCleanMaster + pullRepo before merging). Single-repo mode has been affected since #292.
Reproduction and actual result
Setup: built CLI, a local bare remote, two isolated HOMEs configured as the same member alice (machines A and B), independent git clone, user scope. Each HOME needs a dashboard events.jsonl entry for the --session-id being saved.
Precondition (required): A already has a reports worktree. Run teamai members on A first so ensureReportsWorktree later returns the existing checkout without fetching. This is the usual "both machines have been used" case. If A is a brand-new clone with no reports worktree, its first ensure fetches origin and the loss shows up on B's next write (alice-b1) instead of on alice-a1.
- A:
teamai members (materialize a local reports worktree; do not refresh after B publishes)
- B:
teamai session save --push --force --session-id alice-b0 --scope user
- A:
teamai session save --push --force --session-id alice-a1 --scope user
- B:
teamai session save --push --force --session-id alice-b1 --scope user
Expected: sessions/alice/<month>.md on teamai-reports contains alice-b0, alice-a1, and alice-b1.
Actual on current main (1734c31, includes #560): A prints Nothing new to push. Origin contains alice-b0 and alice-b1; alice-a1 is missing. A appended to a stale month log that did not contain alice-b0. The push was rejected, the fetch + rebase retry conflicted on that file, and the entry never reached origin.
Suggested fix
Before a writer merges into a per-member reports file, sync the reports worktree with origin under the reports lock, then merge, commit, and push. Reuse commitAndPushReports unless a single helper that does sync + write + push is clearly simpler.
Idle Stop / auto-report with nothing pending should not fetch.
Relevant code at 1734c31: src/save-session.ts:141-151, src/hook-handlers.ts:337-341, src/init.ts / src/bootstrap.ts member registration, src/team-push.ts:404-405, src/utils/reports-branch.ts (ensureReportsWorktree, commitAndPushReports).
Out of scope
Verification
Reproduced on main 1734c31 (Node 22, macOS) with the built CLI, a local bare remote, and two isolated HOMEs. With the members precondition on A, origin's month log has alice-b0 and alice-b1 and not alice-a1. Without that precondition, alice-a1 can land and alice-b1 is the entry that is dropped instead.
Problem
Report writers read, merge, and write their own per-member file inside the
teamai-reportsworktree, but never sync that worktree with origin first:sessions/<user>/<month>.md(session save --push) — still stalevotes/<user>.yaml(Stop hook) — still stalemembers/<user>.yaml(init/bootstrap) — still stalestats/<user>.yaml/ votes fromreportUsageToTeam— still stale when that function runs without a prior refreshcommitAndPushReportsonly fetches and rebases after a push is rejected. Its comment assumes rebase conflicts are effectively impossible because each member writes only their own file. That is false when the same member reports from two checkouts: both writesessions/alice/…(orstats/alice.yaml,votes/alice.yaml).When that happens, the merge starts from a stale copy of the member's own file. The push is rejected, the rebase conflicts on that file, and the new entry never reaches origin.
#560 (merged as
1734c31) already:pullreads votes/stats (fix(pull): refresh reports worktree before reading votes and stats #557)teamai-reports(fix(members): avoid publishing reports branch from read-only queries #558)pull's auto-report after that refresh, so the auto-report merge in the samepullstarts from fresh dataUnstick ≠ recover: a later
members/pullrefresh will not bring the lost session/vote/stats entry back.Independent git clones have been affected since #489 (before it, the git-kind auto-report ran
resetToCleanMaster+pullRepobefore merging). Single-repo mode has been affected since #292.Reproduction and actual result
Setup: built CLI, a local bare remote, two isolated
HOMEs configured as the same memberalice(machines A and B), independent git clone, user scope. Each HOME needs a dashboardevents.jsonlentry for the--session-idbeing saved.Precondition (required): A already has a reports worktree. Run
teamai memberson A first soensureReportsWorktreelater returns the existing checkout without fetching. This is the usual "both machines have been used" case. If A is a brand-new clone with no reports worktree, its firstensurefetches origin and the loss shows up on B's next write (alice-b1) instead of onalice-a1.teamai members(materialize a local reports worktree; do not refresh after B publishes)teamai session save --push --force --session-id alice-b0 --scope userteamai session save --push --force --session-id alice-a1 --scope userteamai session save --push --force --session-id alice-b1 --scope userExpected:
sessions/alice/<month>.mdonteamai-reportscontainsalice-b0,alice-a1, andalice-b1.Actual on current
main(1734c31, includes #560): A printsNothing new to push. Origin containsalice-b0andalice-b1;alice-a1is missing. A appended to a stale month log that did not containalice-b0. The push was rejected, the fetch + rebase retry conflicted on that file, and the entry never reached origin.Suggested fix
Before a writer merges into a per-member reports file, sync the reports worktree with origin under the reports lock, then merge, commit, and push. Reuse
commitAndPushReportsunless a single helper that does sync + write + push is clearly simpler.Idle Stop / auto-report with nothing pending should not fetch.
Relevant code at
1734c31:src/save-session.ts:141-151,src/hook-handlers.ts:337-341,src/init.ts/src/bootstrap.tsmember registration,src/team-push.ts:404-405,src/utils/reports-branch.ts(ensureReportsWorktree,commitAndPushReports).Out of scope
teamai-reports(fix(members): avoid publishing reports branch from read-only queries #558, done)pullreading votes/stats from a stale checkout (fix(pull): refresh reports worktree before reading votes and stats #557, done)git stashentries (fix(reports): keep teamai-reports reads fresh and read-only #560 review P1s, done)Verification
Reproduced on
main1734c31(Node 22, macOS) with the built CLI, a local bare remote, and two isolated HOMEs. With themembersprecondition on A, origin's month log hasalice-b0andalice-b1and notalice-a1. Without that precondition,alice-a1can land andalice-b1is the entry that is dropped instead.