Skip to content

fix(pull): sync nothing in a project whose config cannot be read (#784) - #792

Merged
jeff-r2026 merged 5 commits into
Tencent:mainfrom
SaulMoro:fix/784-pull-unreadable-config
Sep 24, 2026
Merged

jeff-r2026 merged 5 commits into
Tencent:mainfrom
SaulMoro:fix/784-pull-unreadable-config

Conversation

@SaulMoro

@SaulMoro SaulMoro commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #784
Part of #752

Summary

 pull(options)
-  projectConfig = detectProjectConfig()          # skips a broken file, returns what loads next
+  projectConfig = detectProjectConfig(onUnreadable)
+  if unreadable
+    silent ? log.persist(msg) : log.error(msg)   # msg = "Nothing was synced: <file>: <reason>. <advice>"
+    exitCode = 1; return                          # no scope synced, no report

 pullHandler.execute(stdin, tool, config)        # session start
+  if !config && findUnreadableProjectConfig(cwd) → return   # unreadable: no seed, no pull, no hint
   packageManifestHashForCwd → seedProjectAgentRoot → pull({ silent }) → stashPackageHintAfterPull
                                                  # no teamai here at all (config null, nothing unreadable): runs as on main

describeUnreadableConfig (config.ts) builds the one-line detail + BROKEN_CONFIG_ADVICE for both pull and detectTeam. No teamai doctor pointer: doctor checks the config detection falls back to, not the broken one; skill-data troubleshooting says so and asks for the user's consent before the config is moved aside. Docs: CHANGELOG, usage guide (en + zh-CN), docs/designs/skill-serving.md.

pull --silent exits 1 on the refusal too. Hosts are not affected: pre-dispatch hooks run it as bash -lc "teamai pull --silent" 2>/dev/null || true (; exit 0 in the Windows form), so Claude Code, Codex, CodeBuddy and OpenCode still see exit 0, and hook-dispatch never reaches this path (the handler returns first).

Evidence

Real CLI at b119ec5 vs origin/main (57afe76); the code is unchanged in 6836785. Workspace with a broken partition config.yaml and, behind it, either a legacy <ws>/.teamai/ of other-team or only a user scope. Team repos are local bare remotes; the provider axis sets the team's provider: to git, github, gitlab.

Case origin/main b119ec5
session start, legacy other-team behind — Claude, Codex, CodeBuddy, OpenCode <ws>/.claude / .codex / .codebuddy / .opencode seeded; other-team-skill deployed (Claude, OpenCode); .teamai/ gains reports-wt/ learnings-wt/ state.json search-index.json nothing seeded, deployed or created; hook exit 0, stdout/stderr empty
session start, user scope behind — same four agents user-team-skill deployed nothing deployed
teamai pull, user scope / legacy behind — git, github, gitlab exit 0; user scope synced / silent exit 1, ✖ Nothing was synced: …/config.yaml: Flow sequence … at line 2, column 1. Fix the file, or move it aside and run `teamai init` to write a new one.
session start, github / gitlab team as above nothing seeded or deployed
teamai pull --silent (pre-dispatch hook) exit 0, user scope synced exit 1, no output, nothing synced, reason in debug.log
readable config: session start (four agents), pull (three providers) pulls, seeds, hints same

Tests (each red on origin/main, green here):

pull-unreadable-config
  legacy other-team behind broken partition → pullRepo never called, exit 1, one-line message   ✗ → ✓
  user scope behind broken config → not pulled, not reported                                   ✗ → ✓
  --silent → nothing pulled, no stderr, message in debug.log, exit 1                            ✗ → ✓
  readable project config / no project config → pulls as before                               ✓ → ✓ (guards)
hook-dispatch-scope
  session start there → pull not called, no <ws>/.claude, no package-hints/                     ✗ → ✓
hook-handlers
  session-start pull, unreadable project config → no seed, no pull, no hint stash              ✗ → ✓
  session-start pull, no config at all → seeds and pulls as before                             ✓ → ✓ (guards)

Test plan

Head 6836785: b119ec5 rebased onto origin/main 8cee7ab. Only CHANGELOG.md conflicted (kept main's entries plus #784's); every other line of the diff is identical to 57afe76..b119ec5, so the real-CLI evidence above still holds.

  • npm run build
  • npx vitest run: 4635 passed, 1 skipped, 11 failed, all in stats-scope.test.ts
  • npx tsc --noEmit: 4 errors, all in src/stats.ts
  • On b119ec5: npm run test:e2e 229 passed, 26 skipped; the real-CLI matrix above

The stats.ts / stats-scope.test.ts failures come from main at 352cfc4 (#795) and appear there without this PR (main CI red on 352cfc4 and 8cee7ab, green on a84bf6f). This PR touches neither file; the fix is tracked separately.

Merge Danger

Door: two-way

Blast Radius: narrow

Only projects whose config exists and cannot be read change: they now sync nothing where they used to sync the wrong team. Not changed, and outside #784:

@jeff-r2026 jeff-r2026 self-assigned this Sep 24, 2026
@github-actions

Copy link
Copy Markdown
  • [P1 blocking] src/pull.ts:1925 treats options.silent as permission to return success, so teamai pull --silent exits 0 when synchronization was refused. This breaks automation that relies on exit status; the dispatcher already forces hook processes to exit 0, and legacy hooks use || true, so set the pull failure status regardless of output mode.

  • [P1 blocking] The PR’s test plan does not satisfy the repository’s required real-CLI matrix. It records only Claude with the git provider, while AGENTS.md requires verification for Claude, Codex, CodeBuddy, and OpenCode, and for git, gitlab, and github.

  • [P1 blocking] The behavior change was not propagated to all required documentation surfaces. docs/designs/skill-serving.md:99 still claims nothing else reports an unreadable project config, and skill-data/core/references/troubleshooting.md:6 still directs agents to doctor, although the PR explicitly states that doctor cannot diagnose this case. The repo rules require affected design docs and skill-data/ guidance to stay synchronized.

@github-actions

Copy link
Copy Markdown

Findings

  • [P1 blocking] src/pull.ts:1926 still returns before setting process.exitCode, so a directly invoked teamai pull --silent reports success when synchronization was refused. Silent mode should suppress output, not failure status; the hook dispatcher already normalizes hook process exits.
  • [P1 blocking] src/hook-handlers.ts:108 treats every null config as an unreadable project config, but null also means no TeamAI configuration exists. This now skips the previously unconditional SessionStart pull, agent-root seeding, and package-hint handling in unconfigured directories, contradicting docs/usage-guide.md:536. Distinguish “unreadable” from “absent” instead of gating on config.
  • [P1 blocking] docs/designs/skill-serving.md:99 still says nothing else reports an unreadable project config, although this PR makes pull report it. The required affected design documentation remains out of sync.
  • [P1 blocking] The PR description’s real-CLI evidence covers only Claude with the git provider. The repository requires verification across Claude, Codex, CodeBuddy, and OpenCode, and across git, gitlab, and github.

Resolved

  • The earlier skill-data/core/references/troubleshooting.md documentation finding is resolved by the new troubleshooting entry.

@github-actions

Copy link
Copy Markdown

Findings

  • No remaining findings in the specified diff.
  • The PR description includes the required test plan, E2E results, and real-CLI coverage for Claude, Codex, CodeBuddy, OpenCode, plus git, github, and gitlab.

Resolved

  • Silent pull now sets exit status 1 at src/pull.ts:1928.
  • Unconfigured directories still run the SessionStart pull; only unreadable project configs are skipped at src/hook-handlers.ts:112.
  • Design and skill troubleshooting documentation are synchronized.
  • The required real-CLI test matrix is now documented.

Review was read-only; no PR code was executed.

@jeff-r2026 jeff-r2026 assigned jeff-r2026 and unassigned jeff-r2026 Sep 24, 2026
@github-actions

Copy link
Copy Markdown

Findings

  • No remaining findings in the specified diff.
  • The PR description includes sufficient testing, including real-CLI coverage across the listed agents and providers.

Resolved

  • Silent pull now sets exit status 1 at src/pull.ts:1928.
  • SessionStart skips pull, seeding, and package hints only for unreadable project configs at src/hook-handlers.ts:109.
  • Design, usage, changelog, and skill troubleshooting documentation are synchronized.

Review was read-only; no PR code was executed.

…cent#784)

Detection skips a project config it cannot read and returns what loads
next: a legacy .teamai/ behind a broken partition, which may name another
team, or the user scope. pull() deployed and reported for that team, and
the session-start hook did so on every session (reports-wt/ and
learnings-wt/ appeared in the legacy .teamai/).

pull() now listens for the unreadable config, syncs no scope, prints the
problem with BROKEN_CONFIG_ADVICE and exits 1. A silent pull (the
session-start hook, or a pre-dispatch hook running `teamai pull --silent`)
records it in debug.log only. Agent-root seeding and the package hint
refuse the same way, so a session start there does nothing. Hooks and
usage already follow this rule since Tencent#748. The message trimming detectTeam
used moves to config.ts as describeUnreadableConfig so both share it.
- The session-start handler returns when the dispatcher resolved no
  config for the hook's cwd, which is what an unreadable project config
  resolves to since Tencent#748. That one guard replaces the unreadable-config
  sinks added to seedProjectAgentRoot and the package-hint context, and
  follows the Tencent#769 contract that handlers read their scope from the
  dispatcher. The handler tests that exercise cwd routing now pass a
  resolved scope; a new one pins that nothing runs without one.
- CHANGELOG and usage guide (en, zh-CN): a session start there runs no
  pull; only `teamai pull --silent` from a pre-dispatch hook writes the
  reason to debug.log.
- skill-data troubleshooting: what `Nothing was synced` means, and that
  moving the config aside and re-running init needs the user's consent.
- The session-start pull is registered with `requiresConfig` instead of
  returning early inside the handler: the dispatcher drops it wherever no
  config resolves, which covers an unreadable project config (Tencent#748), and
  spawns no detached pass for it. Where no teamai config exists at all
  it did nothing on main either (no scope to pull, no project root to
  seed, no config for a package hint). The Tencent#748 registry test and the
  docs no longer list it as machine-level work.
- `teamai pull --silent` exits 1 on the refusal too. Pre-dispatch hooks
  run it as `… 2>/dev/null || true` (`; exit 0` on Windows), so hosts
  still see success.
- The dispatch-scope test asserts the pull is skipped and resets the
  pull mock it queues.
- skill-serving design doc: `teamai pull` now reports an unreadable
  project config too. skill-data troubleshooting: `teamai doctor` can
  pass there.
…encent#784)

A null config from the dispatcher means either "no teamai here" or "the
project config cannot be read". Gating the session-start pull on
`requiresConfig` stopped it in both; only the second must stop it. The
handler now asks `findUnreadableProjectConfig` for the hook's cwd when
no config resolved (a cwd that no longer exists holds none) and runs
nothing when it reports a file. Everywhere else it runs as on main, so
the docs list it as machine-level work again.
@SaulMoro
SaulMoro force-pushed the fix/784-pull-unreadable-config branch from b119ec5 to 6836785 Compare September 24, 2026 11:29
@github-actions

Copy link
Copy Markdown

Findings

  • No remaining findings in the specified diff.
  • The PR description documents sufficient testing, including build, unit/E2E tests, and representative real-CLI verification.

Resolved

  • Silent pull now exits 1 at src/pull.ts:1916.
  • SessionStart distinguishes absent from unreadable configuration at src/hook-handlers.ts:119.
  • Design, usage, changelog, and skill troubleshooting documentation are synchronized.

Review was read-only; no PR code was executed.

@github-actions

Copy link
Copy Markdown

Findings

  • No remaining findings in the specified diff.
  • The PR description documents sufficient testing, including representative real-CLI coverage across the listed agents and providers.

Resolved

  • Silent pull now exits 1 at src/pull.ts:1916.
  • SessionStart skips pull, agent-root seeding, and package hints only for unreadable project configs at src/hook-handlers.ts:119.
  • Design, usage, changelog, and skill troubleshooting documentation are synchronized.

Review was read-only; no PR code was executed.

@jeff-r2026
jeff-r2026 merged commit 1fd400e into Tencent:main Sep 24, 2026
11 checks passed
@SaulMoro
SaulMoro deleted the fix/784-pull-unreadable-config branch September 24, 2026 12:09
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.

pull syncs nothing in a project whose config cannot be read

2 participants