Skip to content

fix(import, init): exit 1 when deep enrich does not finish or init --http would replace another local agent - #1534

Merged
SaulMoro merged 5 commits into
Tencent:mainfrom
hz89Dev:fix/1500-import-init-http-exit
Oct 29, 2026
Merged

SaulMoro merged 5 commits into
Tencent:mainfrom
hz89Dev:fix/1500-import-init-http-exit

Conversation

@hz89Dev

@hz89Dev hz89Dev commented Oct 28, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Two commands reported a failure and still exited 0, so scripts and CI carried on as if they had worked. teamai import printed Deep enrich complete after deep enrich had left docs missing, and it logged a deep enrich error only at debug level. teamai init --http swallowed the local agent's already initialized error, so the old endpoint and token stayed in place. Both now exit 1 and say what to run next.

 teamai import --dir | --from-repo | --from-repo-list | --from-org, deep enrich incomplete or failed
-  ⚠ deep-enrich[svc]: incomplete — missing component docs: src, architecture: true
-  ℹ Deep enrich complete: svc
-  ✔ Local directory svc import complete                          exit 0
+  ⚠ deep-enrich[svc]: incomplete — missing component docs: src, architecture: true
+  ✔ Local directory svc import complete
+  ⚠ Deep enrich incomplete: svc. Run `teamai import --dir ./svc` to retry.   exit 1
+  (with --output ./out: Run `teamai codebase --deep-enrich --project svc --output ./out` to retry.)
 teamai init --http <url> --token <t>, the local agent uses another endpoint or token, no --force
-  ✔ teamai initialized (HTTP read-only)!                         exit 0, local agent unchanged
+  ✖ The HTTP local agent already uses <old>. Re-run with --force to replace it with <url>.   exit 1, nothing written

The extracted evidence is still published. A deep enrich error is now a warning. In a team repo the retry is the same import, because only an import publishes: codebase --deep-enrich there leaves the docs uncommitted in the clone, and the next teamai push refuses the dirty clone. The retry drops --incremental, which would skip an unchanged source, and keeps --max-files, --domain and --ssh. A batch prints --from-repo <url> for each unfinished repo. The teamwiki update that import --from-mr runs (silently) and teamai ci extract-mr, and codebase --upgrade-wiki keep exit 0: importFromRepo now returns whether deep enrich finished, and only --from-repo and the batch loop turn that into exit 1.

Type of Change

  • Bug fix (non-breaking change that fixes an issue)

Test Plan

  • npx tsc --noEmit passes
  • npm run lint passes
  • npx vitest run passes. Locally: 18699 passed. The only failures were 60 s timeouts in workflow-local-driver, workflow-reconciliation and workflow-settle-cost while the machine's load average was around 40. Those files pass when run on their own, and this diff does not touch them. CI is the check for the full suite.
  • Added/updated tests for the change

Tests that fail on main and pass here:

  • import-dir.test.ts, through importCmd({ dir }): when deep enrich is incomplete it exits 1 with the retry line (including --output), and the evidence is on disk. When deep enrich throws, it warns with the error and exits 1. In a team repo the retry is teamai import --dir <dir>, with --max-files and without --incremental. A path with a space is single-quoted in the retry line, so it can be copied as is.
  • codebase-deep-enrich.test.ts: a deep enrich refused at a symbolic link names the link and says to replace it before retrying.
  • import-repo-merge.test.ts, through importCmd({ fromRepo }): exits 1 with the retry line. importFromRepo reports an unfinished deep enrich to its caller and leaves process.exitCode alone, which keeps --from-mr and --upgrade-wiki at exit 0.
  • import-repo-list.test.ts: the batch exits 1 and names only the repo whose deep enrich did not finish. In a team repo each line is teamai import --from-repo <url> with that entry's --ssh and --domain, and the batch's --max-files.
  • http-repo-integration.test.ts, through init({ http }):
    • A first user-scope run sets up the local agent and its token file. On main it skipped this without saying so.
    • Running it again on the same endpoint without --token keeps the stored token.
    • Another endpoint, or another token, is refused before the project config, the API key or the local agent change.
    • --force replaces it.
    • A first run with TEAMAI_API_TOKEN set to another key writes the --token value.
    • A first run with TEAMAI_HTTP_ENDPOINT set to another endpoint sets up the local agent instead of refusing.

Real-CLI record for the team-repo retry, at d678fa2 (npm run build, then dist/cli.js, isolated HOME, teamai init https://git.example.com/acme/team-ai.git --provider git served from a local bare repo through url.<path>.insteadOf, TEAMAI_AI_TOOL=codex pointing at a stub codex that fails or answers):

$ teamai import --dir ./svc                                      # AI tool failing
⚠ deep-enrich[svc]: incomplete — missing component docs: src, architecture: true
✔ Pushed to team knowledge repo (https://git.example.com/acme/team-ai.git)
✔ Local directory svc import complete
⚠ Deep enrich incomplete: svc. Run `teamai import --dir ./svc` to retry.
exit=1
$ teamai import --dir ./svc                                      # the printed retry, AI tool working
✔ deep-enrich[svc]: Deep knowledge generation complete
ℹ Deep enrich complete: svc
✔ Pushed to team knowledge repo (https://git.example.com/acme/team-ai.git)
exit=0
$ git -C ~/.teamai/team-repo status --short                      # clean
$ git --git-dir=team.git ls-tree -r --name-only main | grep svc/docs/
teamwiki/evidence/code/svc/docs/README.md
teamwiki/evidence/code/svc/docs/architecture.md
teamwiki/evidence/code/svc/docs/src.md
$ teamai import --dir ./svc --incremental --max-files 500
⚠ Deep enrich incomplete: svc. Run `teamai import --dir ./svc --max-files 500` to retry.
exit=1
$ teamai import --from-repo https://github.com/acme/orders-api
⚠ Deep enrich incomplete: github__acme__orders-api. Run `teamai import --from-repo https://github.com/acme/orders-api` to retry.
exit=1
$ teamai import --dir ./svc                                      # evidence docs/ is a symlink
⚠ Deep enrich failed for svc: Cannot deep-enrich through a symbolic link: …/teamwiki/evidence/code/svc/docs. Replace it with a regular file or directory before retrying.
⚠ Deep enrich incomplete: svc. Run `teamai import --dir ./svc` to retry.
exit=1
$ TEAMAI_HTTP_ENDPOINT=https://env-host.example/api teamai init --http http://127.0.0.1:9/api --token k --scope user
exit=0
local agent: endpoint http://127.0.0.1:9/api, token k

Earlier real-CLI record for --output and init --http, at bf8dcf8 (same setup, no AI tool installed):

$ teamai import --dir ./svc --output ./out
⚠ deep-enrich[svc]: incomplete — missing component docs: src, architecture: true
✔ Local directory svc import complete
⚠ Deep enrich incomplete: svc. Run `teamai codebase --deep-enrich --project svc --output ./out` to retry.
exit=1
$ teamai codebase --deep-enrich --project svc --output ./out      # the printed retry finds the same teamwiki
Deep enrichment incomplete: project=svc, missing component docs: src, missing architecture.md
exit=1
$ teamai import --dir ./svc --output "./my wiki"
⚠ Deep enrich incomplete: svc. Run `teamai codebase --deep-enrich --project svc --output './my wiki'` to retry.
$ teamai codebase --deep-enrich --project svc --output './my wiki'  # copied as printed
Deep enrichment incomplete: project=svc, missing component docs: src, missing architecture.md
$ teamai import --from-repo https://github.com/acme/orders-api --output ./outr
⚠ Deep enrich incomplete: github__acme__orders-api. Run `teamai codebase --deep-enrich --project github__acme__orders-api --output ./outr` to retry.
exit=1
$ teamai import --from-repo-list repos.yaml --output ./outl
⚠ Deep enrich incomplete: github__acme__orders-api. Run `teamai codebase --deep-enrich --project github__acme__orders-api --output ./outl` to retry.
✔ Batch import complete: 1 succeeded, 0 failed, 0 skipped
exit=1
$ teamai import --from-repo https://github.com/acme/orders-api --output ./outs --skip-enrich
exit=0

$ teamai init --http http://127.0.0.1:9/a --token t1 --scope user
✔ HTTP local agent initialized at …/.teamai/local-agent/config.json
✔ teamai initialized (HTTP read-only)!
exit=0
$ teamai init --http http://127.0.0.1:9/a --token t1                # project scope, same endpoint
✔ HTTP local agent initialized at …/.teamai/local-agent/config.json
exit=0
$ teamai init --http http://127.0.0.1:9/b --token t2
✖ The HTTP local agent already uses http://127.0.0.1:9/a. Re-run with --force to replace it with http://127.0.0.1:9/b.
exit=1
$ teamai init --http http://127.0.0.1:9/a --token t9 --scope user
✖ The HTTP local agent already uses http://127.0.0.1:9/a with another token. Re-run with --force to replace the token.
exit=1
local agent: endpoint http://127.0.0.1:9/a, token t1
$ teamai init --http http://127.0.0.1:9/b --token t2 --scope user --force
exit=0
local agent: endpoint http://127.0.0.1:9/b, token t2

On main (a484299 per the issue, and rechecked here at 898b82a), the same import --dir printed ℹ Deep enrich complete: svc and exited 0. The first init --http --scope user printed [debug] Local agent init: HTTP local agent is already initialized and exited 0.

Not run here: providers gitlab/github against real hosts, and agents other than the default; left to CI.

Related Issues

Closes #1500. This covers the remaining Part 1 rows (A3–A5). A1 and A2 landed in #1512, Part 2 in #1509 and Part 3 in #1510.

Notes for Reviewers

  • Where A5 differs from the issue. On main, every first init --http --scope user already hit already initialized: loadLocalAgentConfig rebuilds config.json from the ~/.teamai/config.yaml that init saved one step earlier. A literal "exit 1 on that error" would fail every first run. So the same endpoint and token count as a re-run and succeed, and only another endpoint or token is refused.
  • One check, before any write. assertNoLocalAgentReplacement is a read-only preflight (loadLocalAgentConfig({ dryRun: true, envFallback: false })). It runs before scope resolution, which can migrate a legacy config. initLocalAgentHttp no longer takes force and always writes. By Step 6 its own read can see the config this init saved, or a TEAMAI_API_TOKEN key, so a second check there would refuse after the writes.
  • A same-endpoint run without --token keeps the stored token. A token is never carried over to another endpoint. That also holds for teamai source add-http, which shares initLocalAgentHttp: on the endpoint already set up, without --token, it now keeps the stored token instead of clearing it.
  • The preflight reads only what is stored (config.json, or a legacy config.yaml), not TEAMAI_HTTP_ENDPOINT: the config init writes takes precedence over the environment, so there is nothing to replace.
  • In a batch, a repo whose deep enrich did not finish still counts as succeeded in the summary, because its evidence was published. The retry line above the summary names it.
  • Docs: docs/guide/knowledge.md and member-guide.md (both languages) gain short transcripts. The grep for text that calls a failed deep enrich non-blocking or exit 0 found none to change. skill-data is unchanged: the wiki workflow imports with --skip-enrich, and no flag changed.

import --dir and --from-repo logged "Deep enrich complete" and exited 0
when deep enrich left component or architecture docs missing, and turned
a deep enrich error into a debug line. They now warn, print the
teamai codebase --deep-enrich command that resumes it, and exit 1. The
extracted evidence is still published. ci extract-mr keeps exiting 0.

Part of Tencent#1500.
init --http swallowed the local agent's "already initialized" error and
exited 0, so the old endpoint and token stayed. It also hit that error on
every first user-scope run, because the local agent backfills from the
config init has just saved, so the local agent was never set up.

Re-running with the same endpoint and token is now not a replacement.
Another endpoint or token is refused before anything is written, and
exits 1 until --force. A same-endpoint run without --token keeps the
stored token, and a token is never carried to another endpoint.

Part of Tencent#1500.
@github-actions

Copy link
Copy Markdown
  • [P2 non-blocking] Shell-quote the retry arguments — src/import-codebase.ts:561. An import with --output './my wiki' prints a retry containing --output ./my wiki. Copying it splits the path into two arguments, so it cannot resume against the original teamwiki. Quote/escape the output path when constructing the command.

The PR description documents sufficient testing, including a build followed by representative real-CLI runs and exit statuses. No testing-record blocker. Nothing from the PR was run, built, or installed during this review.

A path with a space split into two arguments when the printed command
was copied.
@hz89Dev

hz89Dev commented Oct 28, 2026

Copy link
Copy Markdown
Contributor Author

Fixed in bf8dcf8. failIncompleteDeepEnrich now single-quotes --output with shellQuoteValue (src/utils/shell-profile.ts) when the path has characters outside [\w@%+=:,./-]. A plain ./out stays unquoted.

Checked on the built CLI:

$ teamai import --dir ./svc --output "./my wiki"
⚠ Deep enrich incomplete: svc. Run `teamai codebase --deep-enrich --project svc --output './my wiki'` to retry.
$ teamai codebase --deep-enrich --project svc --output './my wiki'
Deep enrichment incomplete: project=svc, missing component docs: src, missing architecture.md

The copied command resolves the same teamwiki. New test: import-dir.test.ts "quotes an --output path with a space in the retry command".

@github-actions

Copy link
Copy Markdown

No new findings in the specified PR diff.

  • Resolved: the earlier retry-argument quoting finding. src/import-codebase.ts:562 now quotes output paths when needed, with a regression test for paths containing spaces.
  • Testing record is sufficient: the description documents a build followed by representative real-CLI runs and exit statuses. No testing-record blocker; additional provider/agent coverage is explicitly deferred to CI.

Nothing from the PR was run, built, or installed during this review.

@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.

@hz89Dev Thanks for #1500's exit codes. With the built CLI at bf8dcf83:

  • import. --dir, --from-repo and --from-repo-list exit 1 when deep enrich is incomplete, and --dir also exits 1 when it throws. The evidence is still published. With --output, the printed retry works, including a quoted path with a space. That was the CI review's finding, now fixed.
  • init --http. Another endpoint or token is refused with exit 1 and nothing written, and --force replaces it. A first user-scope run now sets up the local agent and its token file, and --token wins over TEAMAI_API_TOKEN.
  • Checks. tsc and lint pass, the six changed test files pass (82/82), and 11 of their tests fail on main. The CHANGELOG and both docs pages match.

One thing blocks it:

  • [P1 blocking] In a team repo, the printed retry doesn't publish, and it blocks the next teamai push. Run teamai import --dir ./svc with no --output, and have the AI tool fail. The import prints Run `teamai codebase --deep-enrich --project svc` to retry and exits 1. Then:

    1. Running that retry with the AI tool working exits 0 with Deep enrichment complete, but architecture.md and lib.md stay in the local team clone. The remote never gets them.
    2. The clone is left dirty, so the member's next teamai push fails with Cannot push: the team repo has uncommitted changes … Paths: teamwiki/router.md.
    3. Re-running the same import (--dir ./svc, or --from-repo <url>) resumes deep enrich and does publish both docs.

    codebase --deep-enrich behaves the same way on main, but main never recommends it. The retry text comes from #1500's own proposal, which didn't cover the team-repo case. Suggested fix: keep the current line with --output. Without it, print the import command to re-run, naming --from-repo <url> for each unfinished repo in a batch. Update the CHANGELOG sentence to match.

Non-blocking:

  • [P3 nit] With TEAMAI_HTTP_ENDPOINT (or a similar variable) set, a first init --http is refused with "already uses …" though nothing is on disk.
  • [P3 nit] source add-http on the same endpoint now keeps the stored token instead of clearing it. That seems right, but neither the body nor the CHANGELOG mentions it.
  • [P3 nit] After a symlink refusal, the retry line points to a retry that can't succeed until the link is removed.

Overlap with #1495. Both PRs change src/init.ts, src/local-agent.ts, src/source.ts and the member-guide.md pages, and they conflict in both merge orders. This PR removes the cause of one of #1495's blockers, the "already initialized" error swallowed in Step 6. A clean split: this PR owns the refusal before any write and the Step 6 error handling. #1495 owns per-agent tokens, and on rebase it would move its "same endpoint, one agent's token" exception into assertNoLocalAgentReplacement, so this PR's "another token" refusal doesn't block it.

In a team repo, `codebase --deep-enrich` writes the missing docs only into
the local clone: nothing pushes them, and the dirty clone blocks the next
`teamai push`. Without `--output`, the line now names the same import,
which re-extracts and publishes what deep enrich adds. It drops
`--incremental`, which would skip an unchanged source, and keeps
`--max-files`, `--domain` and `--ssh`. A batch names `--from-repo <url>`
for each unfinished repo.

A deep enrich refused at a symbolic link now says to replace the link, so
the retry that follows can succeed.
…eplace

With TEAMAI_HTTP_ENDPOINT set, a first `init --http` on another endpoint
was refused as a replacement, though nothing was stored. The config init
writes takes precedence over the environment, so the preflight now reads
only config.json and a legacy config.yaml.
@hz89Dev

hz89Dev commented Oct 29, 2026

Copy link
Copy Markdown
Contributor Author

Fixed in 1fc8cd7 and d678fa2. The body has the new real-CLI record.

[P1] Retry in a team repo. Without --output, the line now names the same import: teamai import --dir ./svc, or teamai import --from-repo <url> for each unfinished repo in a batch. It drops --incremental, because that would skip a source that hasn't changed. It keeps --max-files, --domain and --ssh, so the re-import doesn't republish with different settings. With --output, the line is unchanged. I reproduced your scenario on the built CLI, with a stub codex that fails and then answers:

$ teamai import --dir ./svc                  # AI tool failing
⚠ Deep enrich incomplete: svc. Run `teamai import --dir ./svc` to retry.
exit=1
$ teamai import --dir ./svc                  # as printed, AI tool working
ℹ Deep enrich complete: svc
✔ Pushed to team knowledge repo (https://git.example.com/acme/team-ai.git)
exit=0

After the second run the team clone is clean, and the remote has svc/docs/architecture.md and svc/docs/src.md. I updated the CHANGELOG entry and both knowledge.md pages to match.

[P3] TEAMAI_HTTP_ENDPOINT. The preflight now reads only config.json and a legacy config.yaml (loadLocalAgentConfig({ dryRun: true, envFallback: false })). The config that init writes takes precedence over the variable, so there is nothing to replace. With the variable set to another endpoint, a first init --http --scope user exits 0 and writes the given endpoint and token.

[P3] source add-http keeps the token. This was intentional. It is now in the CHANGELOG and the notes.

[P3] Symlink refusal. The error now reads Cannot deep-enrich through a symbolic link: <path>. Replace it with a regular file or directory before retrying., so the retry line after it is actionable.

#1495. Agreed on the split. This PR keeps the refusal before any write and the Step 6 error handling. The "same endpoint, one agent's token" exception belongs in assertNoLocalAgentReplacement once #1495 rebases.

@github-actions

Copy link
Copy Markdown

No new findings in the specified PR diff.

  • Resolved: the earlier retry-argument quoting finding. Retry arguments now use shell quoting when needed, with regression coverage for output paths containing spaces.
  • Testing record is sufficient: the PR description documents a build followed by representative real-CLI runs and exit statuses. No testing-record blocker; additional provider/agent coverage is explicitly deferred to CI.

Nothing from the PR was run, built, or installed during this review.

@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.

@hz89Dev Thanks, approving d678fa20.

  • The team-repo retry works. We ran every printed retry line exactly as printed, in four modes:
    • --dir in a team repo prints teamai import --dir ./svc --max-files 300, without --incremental. It exits 0, the docs reach the remote, the clone is clean and teamai push exits 0.
    • --dir --output "./my wiki": the quoted retry exits 0 and writes there.
    • --from-repo … --domain sales keeps --domain and publishes.
    • A --from-repo-list batch prints one line, only for the unfinished repo, with that repo's --domain and the batch's --max-files. That line exits 0 and leaves the clone clean.
  • The P3s. A first init --http with TEAMAI_HTTP_ENDPOINT set to another endpoint exits 0. source add-http keeping the token is now in the CHANGELOG. The symlink error says what to do.
  • What #1544 got wrong stays right here. A first user-scope init --http on a clean HOME exits 0, and a refused one writes nothing. ci extract-mr and the --from-mr teamwiki update stay at exit 0. We checked that last one in the code and the unit test, not against a real MR API.
  • With #1541 on main. The merge with main (793e0351) is clean, and build and tsc pass on it. When the push fails, the import exits 1 with the push error and prints no retry line, and rerunning the same import publishes everything.

npm ci, build, tsc and lint pass, and the seven changed test files pass (102/102). The red Lint & Test (Node 24 on ubuntu-latest) was workflow-workspace.test.ts "does not publish conflict markers a prompt staged in a quoted path". It doesn't touch this PR, it passes locally, and it passed on rerun. The CI review found nothing new, and we agree.

Two notes, neither blocking: the --from-repo retry drops --depth, and a batch entry with auth: public has no flag to carry over. And a parallel --from-repo-list batch sometimes fails one repo with ENOENT on teamwiki/source-manifest.json. That predates this PR and is now #1546.

@SaulMoro
SaulMoro merged commit 5807b7d into Tencent:main Oct 29, 2026
31 of 32 checks passed
SaulMoro added a commit to SaulMoro/teamai-cli that referenced this pull request Oct 29, 2026
…encent#1534 lets it fail init

initHttp no longer swallows a local agent setup that fails, and this file's fs mock has no readJson, so the case that checks a re-init keeps the member's workflow settings failed on the setup it does not test.

Tested: init.test.ts passes (85).
SaulMoro added a commit to SaulMoro/teamai-cli that referenced this pull request Oct 29, 2026
…encent#1534 lets it fail init

initHttp no longer swallows a local agent setup that fails, and this file's fs mock has no readJson, so the case that checks a re-init keeps the member's workflow settings failed on the setup it does not test.

Tested: init.test.ts passes (85).
SaulMoro added a commit to SaulMoro/teamai-cli that referenced this pull request Oct 29, 2026
…encent#1534 lets it fail init

initHttp no longer swallows a local agent setup that fails, and this file's fs mock has no readJson, so the case that checks a re-init keeps the member's workflow settings failed on the setup it does not test.

Tested: init.test.ts passes (85).
Walternater pushed a commit to Walternater/teamai-cli that referenced this pull request Nov 2, 2026
…http would replace another local agent (Tencent#1534)

teamai import (--dir, --from-repo, --from-repo-list) exits 1 when its deep enrich is incomplete or fails, names what is missing, still publishes the extracted evidence, and prints a retry that works where it ran: the same import in a team repo, the codebase command with --output. ci extract-mr and the --from-mr teamwiki update stay best effort. teamai init --http refuses, before writing anything, to replace another local agent without --force, and a first init exits 0. Closes Tencent#1500.
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.

[bug] init and pull: failures that exit 0, a skeleton pushed to the business repo, and settings lost on init --force

3 participants