From aa3b0cd047637b448f51cb5968842a684272ab5a Mon Sep 17 00:00:00 2001 From: Saul Moro Date: Wed, 23 Sep 2026 13:36:34 +0200 Subject: [PATCH] fix(pull): keep a debug.log record when the stub cannot be deployed The previous commit turned both deploy catches into `log.warn`, which is muted in silent mode and never reaches debug.log, and a SessionStart pull runs detached with its output discarded. So the automatic pull, the one that deploys the stub for most members, lost the only persistent record it had. Both catches now warn and write the same line to debug.log. --- src/__tests__/pull-skip-sync.test.ts | 4 ++++ src/pull.ts | 16 +++++++++++++--- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/src/__tests__/pull-skip-sync.test.ts b/src/__tests__/pull-skip-sync.test.ts index eadbc4be..bceff37e 100644 --- a/src/__tests__/pull-skip-sync.test.ts +++ b/src/__tests__/pull-skip-sync.test.ts @@ -319,6 +319,8 @@ describe('pull skip-sync when repo HEAD unchanged', () => { expect(log.success).toHaveBeenCalledWith(expect.stringContaining('Already synced at abc1234, skipping')); expect(log.warn).toHaveBeenCalledWith(expect.stringContaining('The built-in teamai skill was not deployed')); expect(log.warn).toHaveBeenCalledWith(expect.stringContaining('EACCES: permission denied')); + // A SessionStart pull runs detached and silent; debug.log is its only record. + expect(log.debug).toHaveBeenCalledWith(expect.stringContaining('The built-in teamai skill was not deployed: EACCES: permission denied')); }); it('warns when the built-in stub cannot be deployed on a full sync', async () => { @@ -331,6 +333,8 @@ describe('pull skip-sync when repo HEAD unchanged', () => { expect(log.warn).toHaveBeenCalledWith(expect.stringContaining('The built-in teamai skill was not deployed')); expect(log.warn).toHaveBeenCalledWith(expect.stringContaining('EACCES: permission denied')); + // A SessionStart pull runs detached and silent; debug.log is its only record. + expect(log.debug).toHaveBeenCalledWith(expect.stringContaining('The built-in teamai skill was not deployed: EACCES: permission denied')); }); it('should do full sync when HEAD rev differs from lastPullRev', async () => { diff --git a/src/pull.ts b/src/pull.ts index 90af492a..378db2e2 100644 --- a/src/pull.ts +++ b/src/pull.ts @@ -730,6 +730,17 @@ async function reconcileEnvForUnchangedRepo( } } +/** + * The stub is the agent's only way into TeamAI, so a failure to deploy it is + * not silent. A SessionStart pull runs detached with its output discarded, and + * `log.warn` is muted there, so debug.log keeps the record. + */ +function warnStubNotDeployed(scopeLabel: string, e: unknown): void { + const message = `[${scopeLabel}] The built-in teamai skill was not deployed: ${e instanceof Error ? e.message : String(e)}`; + log.warn(message); + log.debug(message); +} + async function pullForScope( localConfig: LocalConfig, options: GlobalOptions, @@ -1019,8 +1030,7 @@ async function pullForScope( const { deployBuiltinSkills } = await import('./builtin-skills.js'); await deployBuiltinSkills(freshConfig, localConfig); } catch (e) { - // The stub is the agent's only way into TeamAI, so this one is not silent. - log.warn(`[${scopeLabel}] The built-in teamai skill was not deployed: ${e instanceof Error ? e.message : String(e)}`); + warnStubNotDeployed(scopeLabel, e); } // Refresh managed culture/shared-instruction blocks as well. A CLI // upgrade may add a new target file while the team repo SHA and tool @@ -1300,7 +1310,7 @@ async function pullForScope( log.debug(`[${scopeLabel}] Deployed ${deployed} built-in skill(s)`); } } catch (e) { - log.warn(`[${scopeLabel}] The built-in teamai skill was not deployed: ${e instanceof Error ? e.message : String(e)}`); + warnStubNotDeployed(scopeLabel, e); } }