Repository navigation
Write learnings/ to a teamai-learnings branch (direct push, no PR) #485
Description
Related
Depends on / follows #484 (report data → teamai-reports). Same motivation: members should not need write access on the default branch. Different data: session notes are knowledge, not telemetry.
Problem
teamai contribute currently lands learnings/*.md on the default branch:
kind: git—pushRepoDirectly()/pushLearningToOrigin()ontomain/masterkind: self— isolated knowledge worktree + PR onto main (contributeSelf)
That is the wrong gate for this content:
- Direct-to-main requires every member to write the protected branch.
- A PR is too heavy. Learnings are frequent, per-session notes (unique filenames, low conflict). Review cost is higher than the risk. Skills / rules / docs already have
teamai pushif a team wants a real review.
pull / recall / maintenance today index learnings/ from the knowledge clone (default branch). Whatever we change on write, those readers must follow.
Why not teamai-reports
#484 already owns members/ sessions/ votes/ stats/ on an orphan branch. Putting learnings there would be convenient (one worktree, one lock) and wrong:
- Reports are high-churn YAML telemetry. Learnings are recallable knowledge (
search-index,recall maintenance, KB Health, promote-to-skill). - An orphan history cannot later merge into main; learnings might still want that.
- One branch cannot split “lock down stats” vs “allow notes”.
pullalready splits knowledge vs reports; stuffing notes into the reports worktree couples recall to telemetry history.
Proposal (tentative)
New long-lived branch teamai-learnings.
- Not orphan. Create it from the default branch (or from the existing
learnings/tree on first use) so a later merge-to-main is a normal PR, not a cross-history copy. - Direct push. No per-contribution PR, no review requirement. Same write model as reports: isolated worktree + lock + fetch/rebase retry.
- Reuse the reports-branch machinery, not the branch. Extract or clone the worktree/lock/retry helper; do not share
REPORTS_BRANCHor the reports worktree. - Git-mode worktree: sibling of the clone (e.g.
<dirname(localPath)>/learnings-wt), same reason as Route report data (members/sessions/votes/stats) toteamai-reportsin independent git clones #484 (reset --hardmust not nest inside the clone). - Self mode: stop opening a knowledge PR for contribute. Write
learnings/in the learnings worktree; leaveskills/rules/docs/teamai.yamlon main via existingteamai push. - Readers:
pullindex rebuild,contributeimmediate index rebuild,recall,viz,maintenancereadlearnings/from the learnings worktree, not from the default-branch checkout. Docs / rules / skills stay on the knowledge clone. - Pending flush:
flushPendingLearningspushes ontoteamai-learnings, notpushLearningToOrigin()on the default branch. - Do not migrate leftover data. Existing
learnings/onmainis left as-is and not copied. New writes go toteamai-learnings. After the switch, readers only look at that branch, so old main copies are ignored (same policy as Route report data (members/sessions/votes/stats) toteamai-reportsin independent git clones #484).
No new CLI command. No --target-branch flag in this issue.
Open question for the community
Is teamai-learnings the right name and shape?
Alternatives we considered:
- This proposal — dedicated regular branch
teamai-learnings(current preference). - Same orphan
teamai-reportsas Route report data (members/sessions/votes/stats) toteamai-reportsin independent git clones #484 — fewer refs, mixes knowledge with telemetry, hard to merge later. - Rename the side channel to something like
teamai-dataand put reports + learnings together — simpler ops, blurs the knowledge/report split. - Keep contribute as PR-to-main but skip required reviewers — still needs default-branch integration, still a review-shaped workflow.
If you have a better branch name, or a reason to merge learnings into reports / a generic data branch, comment here before we lock the implementation.
Out of scope
- Report dirs (
members/sessions/votes/stats) — Route report data (members/sessions/votes/stats) toteamai-reportsin independent git clones #484 teamai pushof skills / rules / docs / env / agents (stays PR → default branch)- CI
extract-mr --mode write(follow-up: if it writes learnings, it should use this branch) - HTTP provider
- Copying or deleting historical
learnings/on the default branch - Auto-merging
teamai-learningsinto main
Acceptance
- An account without default-branch write permission can
teamai contributesuccessfully. The file appears onorigin/teamai-learningsunderlearnings/. No default-branch commit, no PR. - Same machine can
teamai recallthe new note without waiting for a merge to main (index reads the learnings worktree). - Offline contribute → next
teamai pullretries ontoteamai-learnings. kind: selfcontribute no longer opens a knowledge PR.- Concurrent contributes from two users still succeed via rebase retry.
- Existing
learnings/onmainis not copied, not deleted, and not read after the switch. kind: httpunchanged.
Activity
SaulMoro commentedon Sep 17, 2026
The name works, and so does the shape. A dedicated regular branch, direct push, and reuse of the worktree, lock, and retry code without sharing
REPORTS_BRANCHor the reports worktree. Options 2 and 3 tie recall to telemetry history. Option 4 keeps the default branch in the write path. This proposal avoids both.We would add two things. Neither depends on the branch decision, so both can ship as separate PRs.
Add one read seam before the switch
#484 was cheap because every reader already called
getReportsDir().learnings/has no such function. Eleven call sites build the path by hand:A missed call site does not throw. It reads an empty directory. Recall returns fewer results, and
prunearchives into a branch that nobody reads.The same list shows that
contributeis not the only writer.import,import-mr, and the maintenance commands also create and move learnings. The issue does not list them.We would land a pure refactor first, with no branch change:
buildIndexthen takeslearningsDirs: string[]and deduplicates by relative path. The first root wins. After that, the branch switch changes one function.The read list also answers the "do not migrate" rule. #484 could abandon the report files on
main, because the next session regenerates them. Nobody regenerates a learning. If readers stop looking at the clone'slearnings/, every note written before the switch drops out of recall. Keep that directory as the last read root, and nothing is copied, deleted, or lost.The read list also frees the branch shape. If the old tree stays readable,
teamai-learningscan be an orphan branch that holds onlylearnings/. The worktree then carries no second copy ofskills/,rules/,docs/, andteamai.yaml, which goes stale as soon as the branch exists.Make contribute offline-first
pending-learnings/is already a durable queue outside the clone. Make it the only write path:Three facts about the code today:
pull.ts:127discards the return value offlushPendingLearnings, and that function logs every failure atlog.debug. No message insrc/tells the user that learnings are queued. A member without push rights queues notes forever and reads "Will retry on the next pull" every time. That member is the reason for this issue.contributeSelfsaves nothing when the push fails, so the note is lost. This issue routes every self-mode contribution through that path.pushRepoDirectlyincontribute, andpushLearningToOriginin the flush. The issue has to move both.With the queue as the write path,
contributenever runs git. Self mode gets the durability that git mode has. The branch target lives in one function. We would also print the queue state during pull, for example2 learnings pending (last error: ...).jeff-r2026 commentedon Sep 17, 2026
Thanks — this is the right read on both fronts. Decisions, so we can lock the implementation:
1. Name & shape — confirmed.
teamai-learnings, dedicated branch, direct push, reuse the worktree/lock/retry helper fromsrc/utils/reports-branch.ts(#484 landed) without sharingREPORTS_BRANCHor the reports worktree.2. Orphan branch — agreed, changing the proposal. You're right that a non-orphan branch drags a second copy of
skills/rules/docs/teamai.yamlthat goes stale the moment the branch exists. Maketeamai-learningsan orphan branch holding onlylearnings/. We drop the "later merge-to-main is a normal PR" argument from the body — learnings don't need that path.3. Keep the old tree readable — agreed, this fixes a real data-loss bug. My "not read after the switch" line was wrong: reports regenerate, learnings don't, so cutting readers off from the clone's
learnings/would silently drop every pre-switch note out of recall. Fix as you describe: keep the oldlearnings/as the last read root. Precedence = new orphan worktree first, old clone dir last, dedup by relative path. Still nothing copied or deleted — just kept readable. This is also what makes the orphan shape in (2) safe.4. No separate refactor PR — fold the read-seam into the one PR. Don't land
getLearningsRoots()as a standalone pure-refactor step first; do the multi-root read seam and the branch switch together in a single PR. The{ write, read[] }shape andbuildIndex(learningsDirs: string[])are still the right mechanism (they're what makes (3) work), and yes — cover the writers I missed in the body:import,import-mr,ci/extract-mr,maintenance/prune. Just don't split it across two PRs.5. Offline-first / queue-only — out of scope here, please open a separate issue. Making
pending-learnings/the single write path, routing self-mode through the durable queue, and surfacing queue state during pull (N learnings pending (last error: ...)) is a real gap —contributeSelfdropping notes on push failure andflushPendingLearningsonly logging atdebugboth need fixing. But it's a separate change from the branch move and shouldn't gate it. Could you open a dedicated issue for it and link back here? Happy to review it there.Net change to the issue body: (a) branch becomes orphan,
learnings/only, (b) Acceptance drops "not read after the switch" and instead requires pre-switch notes stay recallable via the old read root, (c) offline-first moves out of scope to a follow-up. Everything else stands.SaulMoro commentedon Sep 17, 2026
Agreed on all five, and thanks for the call on (3) — that was the part worth getting wrong out loud.
Offline-first is now #615, out of this change's way. The PR for #485 and #486 lands the branch move, the multi-root read seam and the docs together, as you asked in (4).
fix(contribute): keep a contribution findable when nothing can be pub…
fix(learnings): do not call a successful push a failure, and cap how …
feat(learnings): write learnings to the teamai-learnings branch (#616)
fix(members): read the default-branch roster as an inherited root aft…
fix(members): read the default-branch roster as an inherited root aft…