Skip to content

[bug] Follow-ups to the worktree fixes: import --from-mr never shares its learning, and push can still revert a teammate's update #823

Description

@SaulMoro

Description

These gaps exist on main (origin/main at 5e5b86d9) and are outside the worktree fixes. Two lose work, and three can make a push revert a teammate's update. They come from reading the code and the PR reviews (#813, #814, #819). Item 1 has been reproduced with the CLI (#825); the rest come from reading the code. Anything that only exists in an open PR's branch is fixed in that PR, not tracked here.

where teamai decides "did the member edit this copy?"
├── learnings-wt/                import --from-mr writes here        ── 1. never committed (fixed: #825, merged)
├── pre-push sync (git show)     single-repo mode                    ── 2. path never resolves
├── push base
│   ├── per checkout             rules, skills                       ── fixed by #819 (merged)
│   ├── agents                   still the shared lastPullRev        ── 3.
│   ├── user scope               no per-checkout record              ── 4.
│   └── skill copy               fails partway                       ── 5.

Loses or reverts work

# What happens Where
1 import --from-mr writes the learning into learnings-wt, but autoPushViaMR commits . in repo.localPath, which is another checkout. It gets "nothing to commit", so the learning is recallable here and never reaches the team. The SessionStart MR hint sends members to this command. main: src/import.ts:302, src/utils/git.ts:417, src/utils/learnings-roots.ts:64
2 In single-repo mode the pre-push sync never matches. Push runs with repo.localPath = <knowledge-wt>/.teamai, and the sync passes rules/x.md to git show <rev>:<path>, which git resolves from the repo root (fatal: path '.teamai/rules/x.md' exists, but not 'rules/x.md'). Every teammate update then reads as a local edit. getFileContentWhenAdded already prefixes ./. Fixed in #827 (merged). main: src/utils/pre-push-sync.ts:122, :227, :244, src/utils/reports-branch.ts:161, src/utils/git.ts:943
3 Agents placed with --role/--project compare with the shared lastPullRev, not with this checkout's bases. This is the #812 revert for agents. Fixed in #827 (merged). main: src/resources/agents.ts:177, :417
4 User scope never records a push base. HOME's copy synced to R2 is compared with R1 after a teammate's R3 and offered over it. main: src/push.ts:974; #819: src/push.ts:976 (project scope only)
5 If the skill copy in the pre-push sync fails partway, the mix of R1 and R2 files matches no base and the skill reads as modified. Fix: copy to a sibling temp dir, then rename. Fixed in #827 (merged). main: src/utils/pre-push-sync.ts:259; #819: :237
 push, then a teammate's update, before any pull
-  base = lastPullRev (R1)  -> local R2 != R1 -> "edited" -> offered over R3
+  base = rev push synced   -> local R2 == R2 -> updated to R3        (#819: project scope, rules + skills)

Also on main, found while fixing item 1

# What happens Where
6 import --from-mr writes the learning at the shared root, not in the project's namespace as contribute does. Fixed in #825 (merged). src/contribute.ts:170
9 import --from-mr checks whether a new learning supersedes an existing one, but reads only the top level of each learnings directory, so learnings inside a project namespace are never compared. The result is never saved either, so its "marking as superseded" warning is false. contribute has no such check. src/utils/dedup.ts:97, src/import-mr.ts:222-246
10 Single-repo mode, with the member's branch behind main: the active tree's .teamai/rules and .teamai/skills are themselves push scan sources, so their stale copy of a rule or skill a teammate updated on main is listed as modified, even after the pre-push sync fixes .claude/rules. src/resources/rules.ts:132 (guard only for placed rules), src/resources/skills.ts:458 (no guard)
7 Learnings that 0.22's import --from-mr left untracked in learnings-wt are never published or cleaned up. src/utils/branch-worktree.ts:330, :574
8 The bind-hint markers are machine-wide files in os.tmpdir(), shared by every user and never removed. Dropped: #829 was closed because the bind hint itself will be removed (comment). src/local-agent.ts:1117, :1192
11 Queue writes take no lock. A contribute or import --from-mr already running while a migration retires .teamai, or while init switches modes, can write after the queue was moved: the learning ends up in .teamai.bak, or loses the config that says which install queued it. Found in #814's review. Being fixed in #814: queue writes, the migration and init share one lock. Fixed in #814 (merged). src/utils/pending-learnings.ts (savePendingLearning), src/migrate.ts, src/init.ts
12 recall's index rebuild throws in deliveredIndexSources when roles.yaml or projects.yaml can't be read, and builds no index, so recall finds nothing until the team config is fixed. A skills collision no longer throws since #816, but with no previous index it indexes 0 skills without a warning. src/recall.ts:341-355
13 A queue's owner is recorded only by its kind. Re-running init in git mode against another team repo keeps the old team's queue, and the next pull publishes it to the new one. Fixed in #814 (merged). src/utils/pending-learnings.ts, src/init.ts
14 config.yaml is written in place, not atomically. A command reading it at that moment fails; a concurrent contribute fails closed (exit 1, nothing lost). Fixed in #831 (merged). src/config.ts:253
15 After a refresh fails, the reports readers still call ensure without the lock, so they can race a writer that holds it. src/utils/reports-branch.ts:102
16 viz in user scope builds its fallback index from [getUserLearningsDir(), ...learningsRoots().read] instead of indexableLearningsRoots, so a learnings checkout of another repository is not left out; and in both scopes it passes no learningsNamespaces, so project-namespaced learnings are missing from the dashboard. src/viz.ts (resolveVizRoot, loadEntries)
17 If init fails between re-cloning the team repo and saving the new config (for example the queue lock is busy), the old team's config.yaml is left beside the new team's clone. src/init.ts (clone, then save)
18 teamai import --from-mr runs inside a listr2 task list, which in a TTY can hide its Accept learning? [Y/n] prompt. Found while fixing item 9 (#834). src/import.ts
19 A project pull that inherits user scope (inheritUserScope) updates the HOME copies but not the user-scope push bases, so the next user-scope push can compare them with an older base. Found while fixing item 4. src/pull.ts (inheritUserScope)
20 pull --dry-run still publishes the queued learnings (from reading the code). src/pull.ts (publish before the dry-run returns)
21 In single-repo mode the duplicate check for leftover learnings reads the old .teamai/learnings-wt checkout, which may be stale. Found in #838. src/utils/learnings-publish.ts

Cleanup (fixed in #825, merged)

  • A main checkout opened through a symlink with another name gets that name as its wiki slug, while its worktrees get the real one (src/codebase-extract.ts:548).
  • A remote-form key and a colliding local path can share a label and merge in stats --by-repo (src/utils/repo-attribution.ts:139-145).
  • local-agent.test.ts "emits hint only once per sessionId" is flaky: its marker is a shared file in os.tmpdir() keyed by a fixed session id (src/local-agent.ts:1192).

Proposed order

Done:

In open PRs:

In progress, three PRs by area (started after #814 merged):

PR Items Why together
Push (#835) 4, 10, 19 Both decide whether a copy is a local edit or a teammate's update on push; same e2e harness as #827.
Learnings lookup (#834) 9, 12, 16, 18 Both decide which learnings are found (the duplicate check and the recall index).
Data layout (#838) 7, 15, 17, 20, 21, and #814's follow-ups Cleanup of the layout #814 introduces: drop the now-redundant checkout-queue drain, reword retire-only's "Finished an interrupted migration" for linked worktrees, and ignore the temp file an interrupted config save can leave in a legacy in-repo .teamai (#831).

Items 4, 7 and 12 are decided, so these start as soon as #814 merges. Items 15 and 17 are in #838. Every open item is now in an open PR: 16 and 18 in #834, 19 in #835, 20 and 21 in #838.

Reproduction (not run)

Item 1 was reproduced with the real CLI in #825: the output says "✔ Push changes via MR", but the learning stays untracked in learnings-wt and the team repo gets no teamai-learnings branch.

Item 2:

  1. A single-repo project where a teammate updates .teamai/rules/x.md after your pull.
  2. teamai --dry-run push: x is listed as modified although you never edited it.

Environment

  • OS: macOS (Darwin 25.5.0)
  • teamai: 0.22.0 (origin/main 5e5b86d9)
  • Provider: GitHub
  • AI tool(s): Claude Code

Related: #807, #808, #809, #812, #813, #814, #819.

Activity

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

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions