Repository navigation
fix(init): refuse to initialize single-repo team URL as standalone team repo (#1500) - #1509
Conversation
The PR description includes a sufficient representative built-CLI verification record. No additional provider × agent coverage is required. |
SaulMoro
left a comment
There was a problem hiding this comment.
This was generated by AI during triage.
@hobostay Thanks. With the built CLI, a fresh init <url> against a single-repo team now exits 1 with the issue's message in both user and workspace scope. The business repo's main is untouched and no config is left. An empty repo still gets the skeleton, a repo with a root teamai.yaml is unchanged, and following the message (init --self in a clone) sets the member up. One thing blocks approval:
- [P1 blocking] The refusal deletes a clone it didn't create. This is the CI review's finding, and it reproduces. When
~/.teamai/team-repois already a clone of the same URL,inittakes the "Repo already exists … using existing clone" path, and the refusal then removes the whole directory. In our run that lost an untrackednotes.md, an unpushed commit and an uncommitted edit, and~/.teamaiwas left with onlydebug.log. Removing the directory only when this run cloned it, and otherwise refusing without touching it, keeps the fix. A test for the reuse path would cover it.
Non-blocking:
- [P2 non-blocking] The
mode: selfcheck now exists three times.isSingleRepoTeamrepeatsreadSelfModeMarker(src/bootstrap.ts:39) andholdsSelfKnowledge(src/migrate.ts). Exporting and reusingreadSelfModeMarkerwould keep one definition. - [P2 non-blocking]
init --force <single-repo url>from another team still moves the old config toconfig.yaml.previousand deletes its clone before refusing, so the member is left with no working install. Main does the same, so it isn't a regression of this PR. - [P3 nit] A workspace-scope refusal leaves an empty
~/.teamai/projects/<slug>/. The body's record shows only the run after the fix. One unit test asserts the internalremovecall rather than the result.
The CI review accepted the body's test record, and we agree. tsc, lint and init.test.ts (81 tests, also with a symlinked TMPDIR) pass. The new e2e file passes 3/3 on this head, and A1 fails on main. CI e2e was still running when we checked. Not run here: the gitlab and github providers, and Windows.
|
No new findings in the specified diff.
Review was read-only; no PR code was run, built, or installed. |
|
@SaulMoro Thank you for the detailed and helpful review! Both items have been addressed:
All unit tests (82/82), E2E refusal tests (4/4), |
|
No new findings in the specified diff.
Review was read-only; no PR code was run, built, or installed. |
SaulMoro
left a comment
There was a problem hiding this comment.
This was generated by AI during triage.
@hobostay Thanks, approving b2eddd4a.
- Reused clone. With the built CLI, an existing clone of the same URL holding an untracked
notes.md, an unpushed commit and an uncommitted edit keeps all three afterinit <url>refuses with exit 1. The business repo'smainis unchanged. A fresh clone is still removed, and no config is left. - One
mode: selfreader.isSingleRepoTeamnow reusesreadSelfModeMarker, with the same behavior and no import cycle. - Checks. On a merge with main (after the oxlint change in #1414), lint,
tscandinit.test.ts(82 tests) pass. The PR's e2e file passes 4/4, including the new preservation test. CI is green at this head.
The CI review reported no new findings and called the clone deletion resolved. We agree.
Optional, for later:
- [P3 nit] The new unit test queues
mockResolvedValueOnce(true)on thefs-extramock, butinitreadspathExistsfromutils/fs.js. Those lines do nothing, and theas anycan go with them. - [P3 nit] The body still says the refusal removes the clone "immediately", which now holds only for a clone this run created.
- Not a regression.
init --forcefrom another team's install to a single-repo URL still moves the old config aside before refusing. Main does the same.
…e team repo (Tencent#1509) teamai init <url> against a repository whose default branch holds .teamai/teamai.yaml with mode: self and no root teamai.yaml now exits 1 with a message pointing to teamai init --self, instead of writing a root teamai.yaml and pushing a skeleton to the business repository's main. A clone this run created is removed; a reused clone is left untouched. Refs Tencent#1500
Summary
A single-repo team keeps its knowledge in the business repository under
.teamai/withmode: selfin.teamai/teamai.yaml. When a member mistakenly runsteamai init <url>against that business repository's URL (instead ofteamai init ./teamai init --self),initpreviously cloned it as a separate-mode team repo, failed to find a rootteamai.yaml, generated a default standalone skeleton (teamai.yaml,skills/.gitkeep,rules/.gitkeep,docs/.gitkeep,env/.gitkeep,members/.gitkeep), and pushed it to the business repo'smainbranch.This PR addresses #1500 (Part 2):
isSingleRepoTeamby checking whether the clone's default branch holds.teamai/teamai.yamldeclaringmode: selfwhile lacking a rootteamai.yaml.teamai init, immediately removing the clone, logging✖ <url> is a single-repo team: its config is .teamai/teamai.yaml. Clone the repository and run \teamai init --self` in it.`, and exiting 1 before any user configuration, file write, or git push occurs.teamai.yaml(standalone team repo).CHANGELOG.md.Type of Change
Test Plan
npm run typecheckpasses cleanly (0 errors)npm run lintpasses with 0 warnings and 0 errors across 1397 filesnpm run buildsucceeds cleanlysrc/__tests__/init.test.ts:isSingleRepoTeamverification across rootteamai.yamlpresent,.teamai/teamai.yamlwithmode: self, withoutmode: self, missing files, and malformed YAML.initrefusal path: verifies clone removal, error message, exit 1, and no git commit or push.src/__tests__/e2e/init-single-repo-refusal.test.ts:teamai init <url>against a bare single-repo repository exits 1 with the exact error message, leaves remotemainbranch commit untouched, creates no local config in~/.teamai/config.yaml, and removes the cloned~/.teamai/team-repo.teamai.yamlis not refused.Real CLI Verification
Executed built CLI (
dist/index.js) against an isolated bare repository configured with.teamai/teamai.yaml(mode: self) on itsmainbranch:Remote repository's default branch commit SHA remained unchanged, and no local config was created.
Related Issues
Part of #1500 (Part 2: init with a single-repo team URL)