Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ All notable changes to this project will be documented in this file. See [standa
- A `manifest/roles.yaml` that exists but does not parse now fails the pull for that scope instead of warning and syncing with no role filter at all, for a member with no role as much as for one with a role. The same applies to `init` and `push`, which each fell back to a guess at the namespaces when any error came out of the loader. The legacy role migration skips with a warning instead of failing, so every command, `pull` included, still loads the config and can fetch the fixed manifest; until it can run, the member holds no role rather than every role, so hooks, MCP servers and env variables scoped by `roles:` reach them no more than skills do. For a member with no active project that fallback meant an unfiltered sync, so a broken manifest delivered every namespace it was written to gate. Only an absent manifest still means "this team does not use roles"; an unreadable or empty file is an error, as it now is for `manifest/projects.yaml` too. `push` stops at its scan for such a manifest (exit 2) even with `--role <ns>`, since the scan needs it to tell which namespaces are the member's.
- `teamai members list` and `teamai projects members` read the roster registered before the reports switch, so a team upgrading past the orphan-branch split no longer sees "No team members registered" while its `members/` still lives on the default branch. The default-branch copy becomes a read-only inherited root, the way learnings' already was: listed in union with the `teamai-reports` copy, with the branch copy winning when the same file exists on both; nothing is copied or deleted, and a cold `members list` still does not publish the reports branch. Member registration merges against the inherited copy too, so a re-init keeps the original `registeredAt` and projects. Fixes [#735](https://github.com/Tencent/teamai-cli/issues/735).
- Cache GC now rejects partial integers such as `12abc`, decimals, zero and unsafe integers for `--max-bytes` and `--stale-days` before deleting anything. An invalid `TEAMAI_CACHE_MAX_BYTES` value falls back to the default 5 GB limit instead of using a numeric prefix.
- Each scope reports only the dashboard sessions recorded in it. Every scope read one machine-wide event log and picked its sessions out by the `cwd` they started in, so a user-scope pull reported every project's sessions to the user team, a project never reported its Copilot sessions (Copilot sends no `cwd`), and a session started under a symlinked path (or `/tmp` for `/private/tmp` on macOS) never matched its project. Each event now records the data directory of the scope `hook-dispatch` resolved, and a report keeps only its own scope's. Events recorded by an earlier release go to the project whose root holds their `cwd`, never to the user scope. The usage guide explains how to remove by hand a skill an earlier release reported into `stats/<user>.yaml` from another project (for [#785](https://github.com/Tencent/teamai-cli/issues/785)).
- A session whose events belong to two scopes is reported to both teams with the part recorded in each. The snapshots of what was already reported were machine-wide, so after a `cd` into another project mid-session, the second scope to report compared its part with the first scope's totals and sent nothing. Each scope now keeps its own (`<dataHome>/dashboard/reported-*.json`, and `~/.teamai/dashboard/user-reported-*.json` for the user scope), seeded the first time from the shared `~/.teamai/dashboard/reported-*.json`, so the first report after the upgrade sends nothing already reported. The shared file is no longer written; after a rollback an earlier release writes it again, and only a scope not yet seeded reads it (for [#786](https://github.com/Tencent/teamai-cli/issues/786)).
- Usage reporting scopes sessions by path on Windows too. The project/user scope filter compared an event's `cwd` against `projectRoot` with a hard-coded `/` separator, so on Windows only a session started in the project root itself matched: every session started in a subdirectory was dropped from the project team's report and counted in the user scope's instead, which is the isolation the usage guide promises. Windows paths are also compared case-insensitively, so a drive letter or a directory name spelled with different case in the two sources no longer leaks a project session into the user scope. POSIX paths keep their own rules: case-sensitive, and a `\` in a filename stays part of the name.
- `teamai tags subscribe` and `teamai tags unsubscribe` now invalidate the pull revision cache, as `teamai skill exclude` already does, so the next `teamai pull` applies the new subscriptions instead of reporting "Already synced" when the team repo has not changed.
- `teamai pull` now deletes a tombstoned agent under all three render extensions, so the Codex `.toml` and Kiro `.json` copies of a removed agent no longer survive on other machines. The cleanup also runs when the team repo rev is unchanged, so an upgrade reaches machines that already pulled the tombstone with an older CLI. `teamai remove agents <name>` also honours `enabledAgents` and no longer deletes from excluded tools. Fixes [#576](https://github.com/Tencent/teamai-cli/issues/576).
Expand Down
19 changes: 16 additions & 3 deletions docs/designs/data-directory-layout.md
Original file line number Diff line number Diff line change
Expand Up @@ -312,9 +312,22 @@ already route through `getDataHome()`. Skill usage moved there too (#748):
project's report carry every project's skills. The user scope records in
`~/.teamai/user-usage.jsonl`, not that old shared `~/.teamai/usage.jsonl`, which
an earlier release still writes after a rollback; the shared file is never
read. The dashboard is likewise an A2 singleton
(events carry `cwd`/`sessionId`); "two projects' events don't mix" is satisfied by
`getEventsPath()` reading `HOME` at call time, not by per-project dirs.
read. The dashboard stays an A2 singleton: `teamai dashboard`, `stats --by-repo`,
`session save` and the contribute check read across scopes. Each event instead
carries `dataHome`, the `getDataHome()` of the scope the hook resolved (#785), and a
scope's report keeps only its own. An event written before that field existed is
attributed by its `cwd`, realpath'd, to the project whose root holds it, never to
the user scope. The snapshots of what was already reported are per scope too
(#786), because a session can record events in two scopes (a `cd` mid-session):
`<dataHome>/dashboard/reported-*.json`, and `~/.teamai/dashboard/user-reported-*.json`
for the user scope. The first time a scope needs one it copies the shared
`~/.teamai/dashboard/reported-*.json`, so nothing reported before the upgrade is
sent again; after that it reads only its own. The shared file is no longer
written, except by an earlier release after a rollback, so every scope seeds from
what the machine had reported by then, never from another scope's later report.
The seed holds a session's whole total, so a session still running at the upgrade
that later records in a second scope reports nothing there until that scope's
part exceeds it.

**`anchor` on save.** Previously only migration wrote a partition's `anchor`
reverse-lookup file, so freshly-init'd partitions had none. `saveLocalConfigForScope`
Expand Down
29 changes: 28 additions & 1 deletion docs/usage-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,23 @@ branch, exactly where they are. Nothing is copied, deleted or migrated: that
directory is still read, so every existing learning keeps coming back from
`teamai recall`. New learnings go to `teamai-learnings`.

**Removing a skill another project reported into your `stats/`.** Before skill
usage was kept per scope, whichever project pulled next reported every
project's skills, so `stats/<user>.yaml` on `teamai-reports` can count a skill
that belongs to an unrelated repo. Those events recorded no directory, so
teamai cannot attribute them and never rewrites the file. Remove the entry by
hand, from a clone of your own so teamai's `reports-wt/` checkout is untouched:

```bash
git clone --branch teamai-reports --single-branch <team-repo-url> teamai-reports
cd teamai-reports
# delete the skill's entry under `skills:` in stats/<user>.yaml
git commit -am "stats: remove <skill> reported from another project"
git push origin teamai-reports
```

The next report reads the branch first, so the entry does not come back.

**Minimum Git permissions with a protected default branch.**

A member needs to:
Expand Down Expand Up @@ -1726,7 +1743,17 @@ Pull waits up to 5 seconds for the reporting batch, then continues its other
work while reporting finishes. A late successful push still updates the local
reported snapshots. Skill usage is recorded per scope, in the data directory of
the project teamai is set up for where the session ran (or the user scope), so
each target reports only its own; a directory without teamai records none. A
each target reports only its own; a directory without teamai records none.
Dashboard sessions stay in one machine-wide `~/.teamai/dashboard/events.jsonl`,
but each event records the data home of its scope, so a scope reports only the
sessions recorded in it: a user-scope pull no longer reports a project's
sessions, and a project reports its Copilot sessions and sessions started under
a symlinked path. Events recorded by an earlier release carry no data home: a
project reports those whose directory lies under its root, the user scope
reports none of them. Each scope also keeps its own snapshot of what it already
reported, so a session that moved into another project mid-session reaches both
teams with the part recorded in each; the first report after upgrading starts
from the snapshot every scope used to share, so nothing is reported twice. A
target removes its usage events only after it confirms success; failed pushes
preserve them. The affected sync locks remain
held until reporting finishes, preventing another pull from racing the report.
Expand Down
23 changes: 22 additions & 1 deletion docs/usage-guide.zh-CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -346,6 +346,21 @@ learnings 迁到独立分支之前团队已经写下的内容,原地留在默
不迁移:该目录仍会被读取,所有既有 learning 依然能从 `teamai recall` 中找回。新的
learning 写入 `teamai-learnings`。

**删除其他项目上报进你 `stats/` 的 skill。** 在 skill 使用按 scope 记录之前,下一个
执行 pull 的项目会上报所有项目的 skill,因此 `teamai-reports` 上的 `stats/<user>.yaml` 可能
统计了属于无关仓库的 skill。这些事件没有记录目录,teamai 无法归属,也不会改写该
文件。请手动删除该条目,在单独的 clone 中操作,不要动 teamai 的 `reports-wt/` 检出:

```bash
git clone --branch teamai-reports --single-branch <team-repo-url> teamai-reports
cd teamai-reports
# 删除 stats/<user>.yaml 中 `skills:` 下该 skill 的条目
git commit -am "stats: remove <skill> reported from another project"
git push origin teamai-reports
```

下一次上报会先读取该分支,所以条目不会再出现。

**默认分支受保护时所需的最小 Git 权限。**

成员需要:
Expand Down Expand Up @@ -1669,7 +1684,13 @@ teamai remove rules <name> --force # 跳过确认,用于脚本和 CI
Pull 对整批统计上报最多等待 5 秒,之后继续其他工作,上报任务仍会完成。
超时后推送成功,仍会更新本地已上报快照。skill 使用按 scope 记录:写入会话所在
目录对应的已配置 teamai 项目的数据目录(或 user scope),因此每个目标只上报
自己的使用;未配置 teamai 的目录不记录。目标确认成功后才清理自己的使用事件,
自己的使用;未配置 teamai 的目录不记录。Dashboard 会话仍写入整机共用的
`~/.teamai/dashboard/events.jsonl`,但每条事件都记下所属 scope 的数据目录(data home),因此每个 scope
只上报在其中记录的会话:user scope 的 pull 不再上报项目的会话,项目会上报自己的
Copilot 会话以及从软链接路径启动的会话。旧版本记录的事件没有数据目录:项目上报目录
位于其根目录下的那些,user scope 一条也不上报。每个 scope 还各自保存已上报快照,
因此会话中途切换到另一个项目时,两个团队各自收到在其中记录的那部分;升级后的首次上报
从原先各 scope 共用的快照开始,不会重复上报。目标确认成功后才清理自己的使用事件,
推送失败会保留事件。上报完成前继续持有相关同步锁,
避免另一次 Pull 与尚未完成的上报竞争。

Expand Down
Loading
Loading