Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
b018180
fix(learnings): publish what an older import --from-mr left in the ch…
SaulMoro Sep 25, 2026
c23302d
fix(learnings): address review — literal pathspecs, carry settings af…
SaulMoro Sep 26, 2026
8523c0d
fix(learnings): address review — HTTP maintenance, agent lists on a p…
SaulMoro Sep 26, 2026
331d929
fix(learnings): address review — retry maintenance a busy lock or fai…
SaulMoro Sep 26, 2026
4defc3e
fix(learnings): address review — queue remnants when origin has no le…
SaulMoro Sep 26, 2026
81af8be
fix(learnings): address review — dedup remnants against origin's tree…
SaulMoro Sep 26, 2026
9f72d61
Merge remote-tracking branch 'origin/main' into fix-823-data-layout-f…
SaulMoro Sep 26, 2026
119692b
Merge remote-tracking branch 'origin/main' into fix-823-data-layout-f…
SaulMoro Sep 26, 2026
ea4f1f4
fix(learnings): address review — commit only the published paths, not…
SaulMoro Sep 26, 2026
62b3103
Merge remote-tracking branch 'origin/main' into fix-823-data-layout-f…
SaulMoro Sep 26, 2026
52deae8
fix(learnings): address review — keep a staged file across the push-r…
SaulMoro Sep 26, 2026
f2f35c6
fix(learnings): address review — read the queue for remnant dedup und…
SaulMoro Sep 26, 2026
dcbe572
ci: re-run checks (flaky dry-run-load-path test, unrelated to this PR)
SaulMoro Sep 26, 2026
e1971a3
Merge remote-tracking branch 'origin/main' into fix-823-data-layout-f…
SaulMoro Sep 27, 2026
e29a54e
fix(learnings): address review — keep staged files staged when the sn…
SaulMoro Sep 27, 2026
fe73cbe
fix(learnings): address review — resolve snapshot conflicts from the …
SaulMoro Sep 27, 2026
d3c108d
fix(learnings): address review — point the unpublished-edit warning a…
SaulMoro Sep 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 42 additions & 3 deletions docs/designs/data-directory-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -453,7 +453,7 @@ Upgrading from the per-checkout layout:
migrated still says `kind: self`, and its queue would publish to the new
repository. It is set aside instead, as a mode switch does (below), to
`pending-learnings.self` beside the partition queue, with a warning naming
it; `contribute` and `import --from-mr` take the same step before they queue.
it.
A queue with no `config.yaml` beside it is one an older self install left
after its config had moved to the partition; once the project serves another
install no plan covers it, so every migrating command sets it aside the same
Expand Down Expand Up @@ -518,7 +518,31 @@ Upgrading from the per-checkout layout:
linked worktree works with no pull first. One with uncommitted changes stays:
the side-branch step fails with an error naming the path and the next step
(commit or move the changes, or delete the path by hand), and the queue keeps
the learnings until then. The error is also printed as a warning, because
the learnings until then. What `import --from-mr` in 0.25.0 to 0.26.0-beta.3 left there is not
such a change (#823 item 7): it wrote `learnings/<YYYY-MM-DD>-<title>.md` with
`source_mr` in the frontmatter and never committed it. `publishQueuedLearnings`
first finds the branch's one registered checkout (`git worktree list` of the
owning repo, so the shared one or the old `.teamai/<dirname>`, never another
repository's), and queues each untracked file of exactly that shape, directly
under `learnings/`, into the active namespace with `contribute`'s name, then
deletes the original. A file the branch (any namespace) or the queue
already has, by `source_mr` or by content, is deleted without queueing. The
branch is the tree of `origin/teamai-learnings`, fetched first, never the
checkout's tracked files: the old checkout is never synced again, so it may
miss a teammate's later import of the same MR, or still track one origin has
since deleted (#823 item 21). When that fetch fails, every such file stays where it is
until a run can fetch, so a stale ref never queues a duplicate; when
`git ls-remote` shows origin has no such branch (an offline first publish
never pushed it), origin adds nothing and the files are queued. A file of
that shape that cannot be read is skipped, never holding back the rest. The
queue is read under the queue lock, and only while the data home's config
still names the install the command loaded (`readPendingForInstall`, the
check `listPendingForInstall` makes): after `init` switched the project to
another team repository the queue holds that install's learnings, so while it
does, or while the lock stays busy, every such file stays where it is. It runs only under the sync lock, before the queue is listed,
and never on a dry run, which publishes nothing from the queue either and
`pull` reports as `Would publish N queued learning(s)` (#823 item 20); any
other file in the checkout is left alone. The error is also printed as a warning, because
several callers treat a side-branch failure as non-fatal and log it at debug
only; a silent (hook) run prints nothing. `refresh` does not swallow it:
`recall maintenance` and `recall promote` stop with exit code 1 and write
Expand All @@ -535,7 +559,10 @@ Upgrading from the per-checkout layout:
reader (`members`, `projects members`, `digest`, `pull`, `stats` and `viz`,
through `readableReportsWorktree`) uses the local copy and never ensures it,
after the same ownership probe as `indexableVotesDir` (below): another
repository's copy is refused with `ForeignCheckoutError`. The
repository's copy is refused with `ForeignCheckoutError`. A refresh that
failed is tried once more, under the lock, and a second failure throws its
cause: a reader never creates the checkout without the lock, which a writer
that took it meanwhile may be creating (#823 item 15). The
checkout there may be another repository's, or not created yet, so
`recall maintenance` and `recall promote` stop with exit code 1 and write
nothing when either the reports lock (they rank by its votes) or the
Expand Down Expand Up @@ -595,6 +622,18 @@ exists but cannot be read names no owner: the loaders return null for it as for
a fresh install, so `init` checks for the file, and with learnings queued moves
them to `pending-learnings.unknown` the same way, the warning naming that config;
queued or not, it drops the search indexes, as on an owner change.
When `init` is about to clone another owner's team repository where the old
install's clone was, or reuses a clone of it that an earlier `init` left there,
it settles the old install right then, before the clone or the refresh,
the same way (queue set aside, indexes dropped), and moves the old
`config.yaml` to the first free `config.yaml.previous[.<n>]` instead of saving
(#823 item 17). The save at the end still compares with the old install
loaded before the clone, finds nothing left to set aside, and writes the new
config. An `init` that stops in between (an unknown `--role`, a busy queue lock, a
prompt left) leaves no config naming the old team beside the new clone, so every
command asks for `teamai init`; nothing is deleted. The rerun finds no
`config.yaml`, so it reads the last `config.yaml.previous[.<n>]` for the
settings a re-init carries forward (`inheritUserScope`, agents, tool roots).
A self install whose
business repository moved to another URL (renamed or transferred) is another
owner too: `init` sets its queue aside, and the warning names the directory to
Expand Down
17 changes: 15 additions & 2 deletions docs/usage-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -411,7 +411,13 @@ a side-branch checkout removes the old one. An old checkout with uncommitted
changes is kept, and the command names it: nothing is published to or recalled
from that branch until you commit, move or delete those changes, and
`recall maintenance` and `recall promote` stop. Queued learnings stay queued
and recallable. Maintenance and promote also stop, naming the cause, when the
and recallable. A learning an older `import --from-mr` (0.25.0 to 0.26.0-beta.3) wrote into a
learnings checkout and never committed does not count: the next `pull` or
`contribute` (or an `import --from-mr` that queues a learning) queues and publishes it (in the project's
namespace, named as `contribute` names it) and says where it was, so the old
checkout can go. If the branch or the queue already has it, by its `source_mr`
or its content, it is deleted instead, and the message names the learning that
has it. Maintenance and promote also stop, naming the cause, when the
checkout cannot be created, such as when `teamai-learnings` is checked out
somewhere else.
A git-mode install of the same project keeps its checkouts at the same paths.
Expand All @@ -425,7 +431,12 @@ for the old repository (the next `recall` rebuilds them). Re-running `init`
against another team repository of the same kind does the same, to
`pending-learnings.<kind>-<repo>` (for example
`pending-learnings.git-github.com-org-team-a`); the same repository written
another way (with or without `.git`, SSH or HTTPS) keeps the queue. When the old
another way (with or without `.git`, SSH or HTTPS) keeps the queue. Before it
clones the other team repository, or reuses a clone of it an earlier `init` left, `init` moves the old `config.yaml` to
`config.yaml.previous` beside it and says so: if `init` stops before it saves
the new config, every command asks for `teamai init` instead of running the old
team's config against the new clone. Run `init` again: it carries that config's
settings (agents, tool roots) from `config.yaml.previous`. When the old
install's `config.yaml` exists but cannot be read, nothing says whose the queue is:
`init` moves it to `pending-learnings.unknown`, names that file and deletes the search indexes. A checkout that had
not been upgraded yet keeps its old queue the same way: the next command there
Expand Down Expand Up @@ -1263,6 +1274,8 @@ After `--update-quality`, review the generated `.draft.md` files and rename them

While another teamai command holds the learnings or reports checkout's lock, `recall maintenance` and `recall promote` exit 1 without writing anything (`The learnings checkout is locked: …`). Run them again when that command finishes.

Maintenance and promote publish only the learnings they changed. A file in the learnings checkout that nobody committed stays out of their commit. When the publish cannot run or push (`Maintenance changes stay local for now: …`), the next `teamai pull` or `contribute` publishes the change, even with no learning queued. A learning edited by hand after that run is not published as part of it: the edit stays uncommitted, and a warning names the file. A file someone staged in the checkout stays staged across the publish; when origin changed it too and it can no longer be staged as it was, it keeps its content as an unstaged change, and a warning names it.

### Promoting Learnings

When a learning reaches maturity, promote it to formal team knowledge (a skill, rule, or doc). Promotion criteria: confidence ≥ 0.90, ≥ 5 upvotes, ≥ 2 distinct contributors, age ≥ 14 days.
Expand Down
11 changes: 9 additions & 2 deletions docs/usage-guide.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -375,15 +375,20 @@ git 同一分支只能在一个 worktree 中检出,所以仓库的所有检出
的命令会移除旧检出。旧检出中若有未提交的改动则保留,命令会指出其路径:在你提交、
移走或删除这些改动之前,不会向该分支发布内容,也不会从该分支召回内容,`recall maintenance` 与
`recall promote` 会停止。已排队的 learning
仍留在队列中,仍可被召回。检出无法创建时(例如 `teamai-learnings` 已在别处检出),maintenance 与 promote
仍留在队列中,仍可被召回。旧版 `import --from-mr`(0.25.0 至 0.26.0-beta.3)写进 learnings 检出却从未提交的 learning
不算在内:下一次 `pull` 或 `contribute`(或排队了 learning 的 `import --from-mr`)会把它排队并发布(放入项目的命名空间,按 `contribute`
的方式命名),并指出它原来的位置,旧检出因此可以移除。若分支或队列中已有同一条(按 `source_mr` 或内容判断),
则删除它,提示中会给出已有的那条 learning。检出无法创建时(例如 `teamai-learnings` 已在别处检出),maintenance 与 promote
同样会停止,并说明原因。
同一项目的 git 模式安装把检出放在相同的路径。切换模式后,teamai 会拒绝使用属于另一个
仓库的检出,并打印清除它的 `git worktree remove` 命令:不会向它发布、不会为它建索引(包括其中的投票)、
也不会改写其中内容(`recall maintenance` 与 `recall promote` 会停止)。旧安装仍在队列中的
learning 会被移到同一数据目录下的 `pending-learnings.<旧类型>`,新安装不会发布它们;`init`
会说明数量和位置,并删除为旧仓库构建的搜索索引(下一次 `recall` 会重建)。以同一类型对另一个团队仓库重新运行 `init`
也同样处理,队列移到 `pending-learnings.<类型>-<仓库>`(例如 `pending-learnings.git-github.com-org-team-a`);
同一仓库换一种写法(带或不带 `.git`、SSH 或 HTTPS)不会移动队列。若旧安装的 `config.yaml` 存在但无法读取,
同一仓库换一种写法(带或不带 `.git`、SSH 或 HTTPS)不会移动队列。克隆另一个团队仓库之前(或复用之前某次 `init` 留下的该仓库克隆之前),
`init` 会把旧的 `config.yaml` 移到旁边的 `config.yaml.previous` 并给出提示:若 `init` 在保存新配置前停止,
所有命令都会要求先运行 `teamai init`,而不会用旧团队的配置操作新的克隆。重新运行 `init` 即可:它会从 `config.yaml.previous` 沿用该配置的设置(agent、工具根目录)。若旧安装的 `config.yaml` 存在但无法读取,
就无从得知队列属于谁:`init` 会把它移到 `pending-learnings.unknown`,指出该文件,并删除搜索索引。尚未升级的检出中的旧队列也同样处理:
在该检出运行的下一个命令会把它移到 `pending-learnings.self` 并给出路径。反过来,当某个检出的 `init --self`
把 git 模式项目切换为单仓库模式,而另一个仍保留旧安装的检出从 main 取得了知识时,在那里运行的下一个
Expand Down Expand Up @@ -1172,6 +1177,8 @@ teamai recall maintenance --update-quality

另一个 teamai 命令持有 learnings 或 reports checkout 的锁时,`recall maintenance` 与 `recall promote` 会以退出码 1 停止,不写入任何内容(`The learnings checkout is locked: …`)。待该命令结束后再运行。

maintenance 与 promote 只发布它们改动过的 learning。learnings 检出中无人提交的文件不会进入它们的提交。发布无法进行或推送失败时(`Maintenance changes stay local for now: …`),下一次 `teamai pull` 或 `contribute` 会发布这些改动,即使队列中没有 learning。在那次运行之后被手动编辑过的 learning 不会作为它的一部分发布:该编辑保持未提交,警告会指出文件名。检出中有人暂存(staged)的文件在发布后仍保持暂存;若 origin 也改动了它、无法按原样重新暂存,它的内容会保留为未暂存的改动,警告会指出该文件。

### 晋升 Learnings

当 learning 达到成熟标准时,可将其晋升为正式团队知识(skill、rule 或 doc)。晋升判据:置信度 ≥ 0.90、≥ 5 次 upvote、≥ 2 个不同贡献者、存在时长 ≥ 14 天。
Expand Down
Loading
Loading