Skip to content

feat(update): record installations and update registered npm-global copies - #1810

Merged
SaulMoro merged 5 commits into
Tencent:mainfrom
Walternater:feat/update-registered-installations
Nov 10, 2026
Merged

SaulMoro merged 5 commits into
Tencent:mainfrom
Walternater:feat/update-registered-installations

Conversation

@Walternater

@Walternater Walternater commented Nov 10, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

  • Record every TeamAI copy this machine runs in ~/.teamai/installations.json: entry, version, install method, npm prefix, whether automatic updates are supported, and the last update result. A copy registers on any command or hook, at most once an hour. Registration is diagnostic: a read-only home is logged at debug level and never stops the command or hook. --help, --version and --dry-run skip it; TEAMAI_DRY_RUN carries the dry-run skip into a workflow child.
  • Add the member setting updateOtherInstallations (default false). With updatePolicy: auto, an update of the running copy can also update one other registered npm-global copy, under that copy's own --prefix, when the running copy is already current. A Stop that installs the running copy leaves the other copy for the next Stop.
  • Pin each install to the version just checked (teamai-cli@<latest>), so a release landing between check and install cannot look like a failed update. Read the target copy's package.json after npm exits; npm exiting 0 without changing that copy is reported as failed, not success. Hooks are not refreshed for another copy, so the active session stays bound to the copy that runs it.
  • Volta, vendored and local checkouts are recorded and listed with a manual remedy; they are never passed to npm.
  • teamai update and teamai doctor list the recorded copies with their version and method. No new command.
  • The no-write E2E checks keep their strict snapshots: a dry run writes no inventory, including its workflow children. Only a non-dry-run check that runs a real workflow ignores the diagnostic inventory file.
  • Moved prefixFromEntryPath / isVoltaPackageImage to src/install-prefix.ts and compareVersions to src/version.ts, so the inventory does not import the updater.

Refs #1753 (scope (a) from the maintainer decision). Scope (b), the team-visible summary and effective-policy reporting, stays a follow-up, so this PR does not close the issue.

Validation

  • npm run build — passes
  • npm run typecheck — passes
  • npm run lint — passes, including the documentation checks
  • npx vitest run src/__tests__/update.test.ts src/__tests__/installations.test.ts src/__tests__/update-policy.test.ts src/__tests__/member-settings-overlay.test.ts — 140/140 pass
  • npx vitest run --config vitest.e2e.config.ts src/__tests__/e2e/init-keeps-member-settings-1458.test.ts — 4/4 pass

Real-CLI run

Built CLI, two npm-global prefixes on one machine, and a local registry serving teamai-cli@0.3.0:

  • copy A: .../A/lib/node_modules/teamai-cli, version 0.3.0 (current)
  • copy B: .../B/lib/node_modules/teamai-cli, version 0.1.0
  • member config: updatePolicy: auto, updateOtherInstallations: true
$ node A/lib/node_modules/teamai-cli/dist/cli.js update
✔ Updated the registered TeamAI copy at /tmp/.../B to v0.3.0
ℹ Registered installations:
ℹ   TeamAI installation: /tmp/.../A/lib/node_modules/teamai-cli/dist/cli.js (v0.3.0, npm-global, automatic updates supported). npm prefix /tmp/.../A.
ℹ   TeamAI installation: /tmp/.../B/lib/node_modules/teamai-cli/dist/cli.js (v0.3.0, npm-global, automatic updates supported). npm prefix /tmp/.../B.
  • B's package.json changed from 0.1.0 to 0.3.0.
  • A stayed on 0.3.0; it was not installed twice.
  • The inventory recorded B as "lastUpdateOutcome": "updated" with the new version.
  • teamai doctor printed the same two installation lines.

Limitations

  • At most one other copy per automatic update, and none on a Stop that installed the running copy. Three outdated copies need several Stops, which keeps one Stop inside the existing update budget. The wait is deliberate: one Stop runs one npm install.
  • A failed update of another copy makes that teamai update exit 1 (and records the automatic update as failed), even when the running copy is current. That is the same failure signal the running copy's own update already uses.
  • prompt and skip never update another copy; only auto plus the opt-in does.
  • Scope (b) is not in this PR: no team-visible summary, and the effective update policy is not stored in the local inventory. The maintainer decision defers that until the shared report format is agreed.
  • A copy that has never run on this machine is not known and is not updated. Records of copies whose directory is gone are marked missing in teamai doctor and are never picked for an update. The inventory is machine-local and contains absolute paths only; nothing from it is published.

@github-actions

Copy link
Copy Markdown
  • [P1 blocking] Inventory write failures prevent unrelated commands from running — src/index.ts:2687, src/installations.ts:78. Registration runs before program.parseAsync() and does not catch filesystem errors. If ~/.teamai exists but is read-only, writing the inventory throws and aborts every ordinary command or hook before its handler runs. Make this diagnostic registration best-effort.

  • [P2 non-blocking] Failed updates promote their target as the most recently used copy — src/installations.ts:191. setInstallationUpdate() changes lastSeenAt even on failure, while selection prioritizes that timestamp. With current copy A and outdated copies B/C, a permission-denied update of B makes B newest; subsequent Stops from A repeatedly retry B and never reach C. Preserve the actual last-run timestamp and use lastUpdateAt for update attempts.

  • [P2 non-blocking] Self-update failures leave a stale successful inventory result — src/update.ts:1368. The running copy’s inventory result is written only on success. If a subsequent npm install fails, the failure path returns without updating its record, leaving the previous "updated" outcome and message as its purported last update result. Record failed self-update attempts too.

The PR description provides a sufficient representative real-CLI verification record. I did not run, build, or install anything from the PR.

@Walternater
Walternater force-pushed the feat/update-registered-installations branch from e4c2006 to 8086938 Compare November 10, 2026 02:55
@github-actions

Copy link
Copy Markdown

All three earlier findings remain unresolved in the current diff:

  • [P1 blocking] Inventory write failures abort unrelated commands — src/index.ts:2687, src/installations.ts:78. Registration runs before program.parseAsync() without catching filesystem errors. When ~/.teamai is read-only and registration needs refreshing, the inventory write throws and prevents any ordinary command or hook from reaching its handler. Make diagnostic registration best-effort.

  • [P2 non-blocking] Failed updates promote their target as the most recently used copy — src/installations.ts:191. setInstallationUpdate() overwrites lastSeenAt even on failure, while candidate selection prioritizes that timestamp. With current copy A and outdated copies B/C, a permission-denied update of B makes B newest; subsequent Stops keep retrying B instead of reaching C. Preserve lastSeenAt and record attempts through lastUpdateAt.

  • [P2 non-blocking] Failed self-updates retain a stale successful inventory result — src/update.ts:1368. The running copy’s update result is recorded only on success. If a later npm install fails, its failure path leaves the previous "updated" outcome and message intact. Record failed self-update attempts too.

The PR description provides a sufficient representative real-CLI verification record. I only read the specified diff; I did not run, build, or install anything from the PR.

@SaulMoro SaulMoro left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was generated by AI during triage.

Reviewed at 8086938a. The main path works as the body describes. With updatePolicy: auto and updateOtherInstallations: true, an update of copy A updates the other registered npm-global copy under its own prefix, and the inventory and teamai doctor list both copies. prompt and skip never update another copy, and Volta, vendored and local copies are listed without being passed to npm. I ran the built CLI with an isolated HOME, separate npm prefixes and a local registry. Three things still need fixing, and I reproduced each one.

Blocking

  • [P1 blocking] A ~/.teamai that cannot be written stops every command and hook before it runs (the CI review's P1). registerRunningInstallation() runs before program.parseAsync() in src/index.ts, and writeInstallations (src/installations.ts) does not catch its mkdirSync / writeFileSync / renameSync. I made ~/.teamai mode 555, and teamai status exited 1 with an uncaught EACCES stack from installations.json.<pid>.tmp. The merge-base build reaches the command's own check. The inventory is diagnostic, so registration should never fail a command: catch inside registerRunningInstallation and log at debug.

  • [P1 blocking] A copy that cannot be updated is picked on every run, so no other copy is reached (the CI review's first P2, reproduced). Candidates are sorted by lastSeenAt newest first (src/installations.ts:161), and setInstallationUpdate advances lastSeenAt on every outcome, failures included. With C (0.20.0) and B (0.21.0) registered and B's lib/node_modules read-only, three runs in a row picked B and failed with npm error code EACCES, and C was never updated. Once B was writable, B updated on the next run and C on the one after. The CI review's fix covers half of it: keep lastSeenAt as the last run time, but also sort by lastUpdateAt oldest first, with copies never tried before the rest. Removing the bump alone is not enough, because B was registered after C and stays first.

  • [P1 blocking] The inventory reports updated after a failed update (the CI review's second P2, plus one more case). src/update.ts writes updated before the hook refresh, and the install failure path writes nothing:

    • Latest moved to a new version and the install directory was read-only: teamai update exited 1 with "Permission denied", and the record still said updated with the previous message.
    • The install succeeded and the hook refresh then failed: the run exited 1 with "Hook refresh after update skipped", and the record said updated.

    Writing the outcome once, after the whole update is known, and recording failed on every failure branch would fix both.

Non-blocking

  • [P2 non-blocking] A Stop that updates the running copy can run two npm installs. One install is for the running copy and one for the other copy, each with the 60 s install timeout, inside the 90 s UPDATE_BUDGET_MS. Two slow installs pass the budget during the second one. The body says the one-copy limit "keeps one Stop inside the existing update budget", and the issue's design is one install per Stop. Skipping the other copy when the running copy installed in this Stop, and leaving it for the next Stop, would make that true. I did not run slow installs.
  • [P2 non-blocking] A failed update of another copy makes teamai update exit 1, and makes the automatic update fail, even when the running copy is current (src/update.ts, src/hook-handlers.ts). I saw exit 1 in the run above with A already current. That may be what you want, but it is new behaviour of an existing command, and the body and CHANGELOG do not mention it.
  • [P2 non-blocking] Records of removed copies stay forever. After deleting a copy's directory, teamai update --check still lists it as "automatic updates supported", and doctor counts it. The update path is safe, because readInstalledVersion returns null and the copy is skipped. But every npx or vendored path that ever ran becomes a permanent row. Marking a record whose package directory is gone as missing, or pruning it on registration, would cover it.
  • [P2 non-blocking] No CHANGELOG entry for the new setting, the inventory file and the new update and doctor output.
  • [P3 nit]
    • RESEEN_AFTER_MS reads as a typo.
    • compareVersions in update.ts is a pass-through to compareSemver, so a re-export would do.
    • Registration is an unlocked read-modify-write, so two processes registering at once can drop a row until that copy runs again. I found this by reading the code, not by running it.

CI review

It reports one P1 and two P2s. I agree with all three and reproduced each one. I grade both P2s as blocking because they reproduce and fit this PR. The first leaves the other copies outdated for good, and the second makes the inventory say the opposite of what happened.

CI

At this head the Windows job and the build passed. Lint & Test, the unit shards and the E2E shards were still running when I wrote this.

Merge risk

Merge risk: high.

  • Alignment: decent. Part (a) of #1753 is there, but the three cases above contradict the body.
  • Impact: medium. The PR writes a file on every command and hook, and runs npm installs in other prefixes.
  • Verification: decent. The built CLI ran with real npm prefixes and a local registry, and the record in the body covers the main path. Slow installs and concurrent registration were not run.
  • Rollback cost: low. The inventory file and the opt-in setting can be removed.
  • Error detection: poor. The three blocking cases either fail loudly in the wrong place or record success after a failure.

@jeff-r2026 your call on this one. It adds the scope (a) inventory and opt-in from #1753, and the three blocking items need fixing whichever way you decide.

@SaulMoro
SaulMoro requested a review from jeff-r2026 November 10, 2026 03:08
@Walternater
Walternater force-pushed the feat/update-registered-installations branch from 8086938 to 8d925e6 Compare November 10, 2026 04:02
@github-actions

Copy link
Copy Markdown

All three earlier findings remain unresolved:

  • [P1 blocking] Inventory write failures abort unrelated commands — src/index.ts:2687, src/installations.ts:78. Registration runs before program.parseAsync() without catching filesystem errors. If ~/.teamai is read-only and registration needs refreshing, its write throws before any command or hook handler runs. Make registration best-effort.

  • [P2 non-blocking] Failed updates promote their target as the most recently used copy — src/installations.ts:191. Update attempts overwrite lastSeenAt, which determines candidate priority. A permission-denied update of B makes B newest, so subsequent runs from current copy A keep retrying B instead of reaching outdated C. Preserve lastSeenAt; use lastUpdateAt for attempts.

  • [P2 non-blocking] Failed self-updates retain a stale successful inventory result — src/update.ts:1370. The running copy’s result is recorded only on success. A later npm installation failure leaves its previous "updated" outcome and message intact. Record failed self-update attempts too.

Additional finding:

  • [P2 non-blocking] A newer registry release is incorrectly reported as an update failure — src/update.ts:1072, src/update.ts:1100. npm installs the unversioned package, but verification requires exactly latest. If the registry releases a newer version between the check and installation, the target upgrades successfully yet receives a failed result. Pin installation to ${pkgName}@${latest}, or accept a newer installed version.

The PR description provides a sufficient representative real-CLI verification record. I only read the specified diff; I did not run, build, or install anything from the PR.

@Walternater
Walternater force-pushed the feat/update-registered-installations branch from 8d925e6 to 4333dcc Compare November 10, 2026 04:07
@Walternater

Copy link
Copy Markdown
Collaborator Author

Thanks for the reproduction. Fixed all three blocking items and the P2s in 4333dcca.

P1 — read-only home. registerRunningInstallation() now catches mkdirSync / writeFileSync / renameSync and logs at debug; setInstallationUpdate does the same. Verified on the built CLI with ~/.teamai at mode 555: HOME=<tmp> node dist/cli.js update --check exits 0 and reaches the command, with no EACCES stack.

P1 — a failing copy hiding the rest. Candidates are now ordered by lastUpdateAt oldest first, with copies never tried first; lastSeenAt stays the last run time and is no longer advanced by an update outcome. A copy that failed once goes behind the never-tried ones. Added selectOtherInstallation coverage for exactly that case.

P1 — updated after a failed update. The running copy is recorded only after the install, version read-back and hook refresh all succeed. Every failure branch now records failed: restored-on-success, version mismatch, hook-refresh failure, and the outer install catch. The hook-refresh test asserts failed and asserts no updated record.

P2 — two installs in one Stop. The other copy is only considered when the running copy is already current. A Stop that installs the running copy leaves the other copy for the next Stop; the PR body and docs now say so.

P2 — exit 1 when the other copy fails. Kept, because the inventory should not report success for a failed requested update. Documented in the Limitations section and in docs/guide/diagnostics.md.

P2 — removed copies. doctor now says a recorded copy whose package directory is gone is “no longer on disk”; the update path already skipped it via the missing package.json.

P2 — CHANGELOG. Added an Unreleased entry for the setting, the inventory file, the update behavior and the doctor/update output.

P3. RESEEN_EVERY_MS replaces the typo-looking name; compareVersions is a re-export instead of a pass-through. The unlocked read-modify-write remains: the inventory is diagnostic and the next run of the copy repairs its own row, so I did not add a lock around every command for it. Say if you want that hardened anyway.

Latest local checks: npm run build, npm run typecheck, npm run lint, 123 unit tests across update.test.ts + installations.test.ts, and the init-keeps-member-settings-1458 E2E. Scope (b) still stays out of this PR.

@Walternater
Walternater force-pushed the feat/update-registered-installations branch 3 times, most recently from 4d07216 to 016846c Compare November 10, 2026 04:11
@Walternater

Copy link
Copy Markdown
Collaborator Author

The three findings in the earlier review were fixed in 4333dcca; 016846cd also fixes the newer-release finding:

  • Both installs are now pinned to the version just checked: npm install -g ${pkgName}@${latest} for the running copy and for the registered copy. A release landing between the check and the install can no longer make a successful update look like a failure, and the exact-version read-back stays the acceptance check.
  • registerRunningInstallation / setInstallationUpdate catch every filesystem error and log at debug; verification on a read-only ~/.teamai is in my previous comment.
  • Selection orders by lastUpdateAt (never tried first), and lastSeenAt is no longer touched by an update outcome.
  • The running copy records failed on every failure branch and records updated only after the hook refresh succeeds.

@Walternater
Walternater force-pushed the feat/update-registered-installations branch from 016846c to 94d9bfb Compare November 10, 2026 04:12
@github-actions

Copy link
Copy Markdown

No new findings I’m confident are real in the specified diff.

All four earlier findings are resolved:

  • Inventory registration catches filesystem errors instead of aborting commands.
  • Update attempts preserve lastSeenAt; candidate selection rotates using lastUpdateAt.
  • Failed self-updates record a failed inventory result.
  • Both install paths pin the checked version, eliminating the newer-release race.

The PR description provides a sufficient representative real-CLI verification record.

I only read the specified diff; I did not run, build, or install anything from the PR.

@Walternater
Walternater force-pushed the feat/update-registered-installations branch 3 times, most recently from b040c11 to be0dbde Compare November 10, 2026 04:20
@Walternater Walternater reopened this Nov 10, 2026

@SaulMoro SaulMoro left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was generated by AI during triage.

Re-checked with the built CLI at 94d9bfbc. The later pushes (b040c11d changes tests, and be0dbdeb makes an automatic update fall back to the running scope when the member config cannot be read) do not touch what these runs cover, so the runs still apply to be0dbdeb. All three blocking items from my review are fixed:

  • Read-only ~/.teamai. With the directory at mode 555, commands reach their handler. Registration catches the write error and logs it at debug.
  • A failing copy no longer hides the rest. Selection now goes by lastUpdateAt, oldest first, with never-tried copies first, and lastSeenAt is no longer advanced by an attempt. With B failing, C is reached on the next run.
  • Failures are recorded as failed. That covers a refused install and a failed hook refresh, and no updated record is left behind.

The P2s are covered too:

  • a Stop that installs the running copy leaves the other copy for the next Stop;
  • removed copies show as "no longer on disk";
  • the CHANGELOG entry is in.

The CI review's newer-release point reproduced at d91293b3. Both installs are now pinned to the checked version, and the same race ends on that version. I accept keeping exit 1 when the opt-in copy fails, since it is documented. I also accept leaving the inventory write unlocked: the next run repairs its own row.

Three things are left. None of them blocks on its own:

  • [P2 non-blocking] --dry-run now writes ~/.teamai/installations.json. Registration runs for any command with arguments, --dry-run included, and also for one that refuses --dry-run and exits 1. The global option's help says "Preview mode, no changes made". The head relaxes real-CLI tests that checked a command leaves the data home untouched so that they ignore this file. Five of them are dry runs: dry-run-refusal, maintenance-checkouts-dry-run, recall-feedback-dry-run, stats-recall-dry-run and update-dry-run. contribute-namespace is relaxed the same way. debug.log is already exempt, so there is a precedent for a diagnostic file. Even so, adding --dry-run to the skip list next to --help and --version in src/index.ts is a one-line change. It would keep the dry-run tests as strict as they were, and the same goes for the codebase-deep-enrich and ci extract-mr checks.
  • [P2 non-blocking] A failed automatic update of another copy is not in debug.log. It is logged with log.warn only, while the hook's message says to look in debug.log. doctor and update do not print the recorded outcome either, so the cause is nowhere a member would look. log.persist for that line, or printing the last outcome in the installation notes, would close it.
  • The branch needs a rebase. It is on 27b7f1e6, and CHANGELOG.md conflicts with main, so CI will not run on what would be merged.

CI review at this head: no findings, and it calls all earlier findings resolved. I agree, with the two P2s above as additions.

Merge risk: medium. It writes a new file on every command, and runs npm in other prefixes, but only on opt-in.

@jeff-r2026 your call on this one. From our side the blocking items are fixed. What is left is whether you want --dry-run to skip the inventory, and the rebase.

@github-actions

Copy link
Copy Markdown

No new findings I’m confident are real in the specified diff.

All four earlier findings are resolved:

  • Inventory registration catches filesystem errors instead of aborting commands.
  • Update attempts preserve lastSeenAt; selection rotates using lastUpdateAt.
  • Failed self-updates record a failed inventory result.
  • Both install paths pin the checked version, eliminating the newer-release race.

The PR description documents sufficient representative real-CLI verification.

I only read the specified diff; I did not run, build, or install anything from the PR.

@Walternater
Walternater force-pushed the feat/update-registered-installations branch 3 times, most recently from c9eae68 to 48bf8ec Compare November 10, 2026 04:27
@Walternater
Walternater requested a review from SaulMoro November 10, 2026 04:28
@github-actions

Copy link
Copy Markdown

No new findings I’m confident are real in the specified diff.

All four earlier findings are resolved:

  • Inventory registration catches filesystem errors instead of aborting commands.
  • Update attempts preserve lastSeenAt; selection rotates using lastUpdateAt.
  • Failed self-updates record a failed inventory result.
  • Both install paths pin the checked version, eliminating the newer-release race.

The PR description documents sufficient representative real-CLI verification.

I only read the specified diff; I did not run, build, or install anything from the PR.

@Walternater

Copy link
Copy Markdown
Collaborator Author

Rebased onto b8c811db and CI is green at 48bf8ecd: unit shards 1/2 and 2/2, E2E shards 1/3–3/3, Windows, Node 20/22/24 lint & test, and coverage all passed. Requested re-review from @SaulMoro.

@Walternater
Walternater force-pushed the feat/update-registered-installations branch from 48bf8ec to b58f8fb Compare November 10, 2026 06:40
@Walternater

Copy link
Copy Markdown
Collaborator Author

Both P2s from the re-check are fixed in b58f8fb0.

  • --dry-run no longer writes the inventory. Registration skips --dry-run together with --help / --version, and sets TEAMAI_DRY_RUN=1 so a workflow child it starts skips too. The five dry-run E2E checks are back to their strict snapshots, and there is a unit test for the skip.
  • A failed update of another copy reaches debug.log. The install-failure and version-mismatch paths now log.persist the same message they warn with, so the hook's "look in debug.log" advice has the failure there. teamai update / doctor already print the record.

The branch is rebased onto b8c811db (CHANGELOG conflict resolved). CI was green at 48bf8ecd before these changes; the new head is b58f8fb0.

@github-actions

Copy link
Copy Markdown

No new findings I’m confident are real in the specified diff.

All four earlier findings are resolved:

  • Inventory registration catches filesystem errors instead of aborting commands.
  • Update attempts preserve lastSeenAt; candidate selection rotates using lastUpdateAt.
  • Failed self-updates record a failed inventory result.
  • Both install paths pin the checked version, eliminating the newer-release race.

The PR description documents sufficient representative real-CLI verification.

I only read the specified diff; I did not run, build, or install anything from the PR.

@SaulMoro

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

Checked b58f8fb0 against 48bf8ecd. Both P2s from my last comment are fixed:

  • --dry-run no longer writes the inventory. src/index.ts skips registration when the arguments include --dry-run, next to --help and --version, and sets TEAMAI_DRY_RUN=1 so a child it starts skips it too (registerRunningInstallation returns early on it). The five dry-run real-CLI tests are strict again.
  • A failed update of another copy reaches debug.log. Both failure paths in updateRegisteredCopy now call log.persist next to log.warn, so the hook's pointer to debug.log holds.

The branch is on current main (b8c811db). CI is still running at this head.

@jeff-r2026 your call on this one. From our side nothing is left: the blocking items were fixed earlier, and these were the last two suggestions.

@Walternater

Copy link
Copy Markdown
Collaborator Author

A status question rather than a nudge: this has been green since b58f8fb0 and both review rounds closed with nothing left, so I would like to know whether anything is still expected here before it sits longer.

  • b58f8fb0 — CI green (17 checks), npm run lint, typecheck, the unit shards and the E2E shards all pass.
  • SaulMoro's last two comments: the blocking items were fixed earlier, and the two later suggestions are fixed too; he handed the call to you.
  • The PR is Refs #1753 and keeps scope (b), the team-visible summary, out — the issue stays open for it by design.

If the remaining decision is about that deferred scope rather than this diff, say so and I will leave the PR as it is; otherwise it is ready whenever you are.

teamai uninstall no longer records the running copy, and its sweep of
machine-wide leftovers removes ~/.teamai/installations.json under the same
guards: kept while another install shares HOME.
@SaulMoro

Copy link
Copy Markdown
Collaborator

This was generated by AI during triage.

To get this merged, I pushed on top of b58f8fb0. Your commit is unchanged.

Commit What it does
9dbf258e docs(glossary) Adds Installation: a new public concept goes in the glossary in the same PR.
ba8b9540 docs(bundled-runtime) The compareSemver note now points at version.ts, where this PR moved compareVersions.
ca72b2a6 Merges main, which brings in #1817's uninstall sweep. There were no conflicts.
1eb5672a fix(installations) teamai uninstall left ~/.teamai/installations.json behind, because uninstall itself registered the running copy.

How 1eb5672a fixes it:

The sweep cases in uninstall.test.ts and a new case in installations.test.ts cover both. npm run build, typecheck and lint pass, and the uninstall, installations, update and update-policy suites pass 394/394.

Real-CLI run, world built from 1eb5672a:

$ npm run -s verify -- teamai <run> init https://teamai.test/team/repo.git --provider git --agent claude   exit 0
$ npm run -s verify -- teamai <run> pull                                                                  exit 0
$ npm run -s verify -- sh <run> sh -c 'ls -A "$HOME/.teamai"'
bin dashboard debug.log env-loader.sh env-scopes installations.json locks projects
$ npm run -s verify -- teamai <run> uninstall --force                                                     exit 0
$ npm run -s verify -- sh <run> sh -c 'ls -A "$HOME/.teamai"'
debug.log projects
$ npm run -s verify -- ledger <run>
- home/.claude left behind, known: #1808
- project/.claude left behind, known: #1808
Ledger: CLEAN

I listed ~/.teamai directly because the verify step runs its own user-scope uninstall before it checks. A CLEAN result on its own would not show that the workspace uninstall removed the file.

I'll merge once CI is green at 1eb5672a.

@github-actions

Copy link
Copy Markdown

No new findings I’m confident are real in the specified diff.

All four earlier findings are resolved:

  • Inventory registration catches filesystem errors without aborting commands.
  • Update attempts preserve lastSeenAt; selection rotates using lastUpdateAt.
  • Failed self-updates record a failed inventory result.
  • Both install paths pin the checked version, eliminating the newer-release race.

The PR description documents sufficient representative real-CLI verification.

I only read the specified diff; I did not run, build, or install anything from the PR.

@SaulMoro SaulMoro left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This was generated by AI during triage.

Approving at 1eb5672a. The blocking items and both suggestions were fixed earlier, and the commits on top close the inventory left behind by uninstall. The CI review at this head has no findings. I'll merge once CI is green.

@SaulMoro
SaulMoro merged commit 2cfe85a into Tencent:main Nov 10, 2026
18 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants