Skip to content

fix: keep Ambient Resume for Classic-only projects - #338

Open
mayzhaoyu wants to merge 1 commit into
rpamis:masterfrom
mayzhaoyu:fix/classic-ambient-resume
Open

fix: keep Ambient Resume for Classic-only projects#338
mayzhaoyu wants to merge 1 commit into
rpamis:masterfrom
mayzhaoyu:fix/classic-ambient-resume

Conversation

@mayzhaoyu

@mayzhaoyu mayzhaoyu commented Aug 18, 2026

Copy link
Copy Markdown

✨ Summary

Fixes #325.

comet init and comet update no longer tie Ambient Resume instruction syncing to the Native workflow. Classic-only projects now keep the managed block when ambient_resume is enabled, while ambient_resume: false still removes it.

Regression coverage was added for both commands, including preservation of existing content in AGENTS.md and CLAUDE.md.

🎯 Scope

  • CLI commands (init, status, doctor, update)
  • Core installer / platform detection
  • Comet skills (assets/skills/, assets/skills-zh/)
  • Comet shell scripts (assets/skills/comet/scripts/)
  • Tests / CI
  • Documentation / changelog
  • Other:

🧪 Testing

  • pnpm build
  • pnpm lint
  • pnpm run lint:architecture
  • pnpm format:check
  • pnpm test
  • pnpm test -- test/domains/comet-classic/comet-scripts.test.ts
  • Not run:

Dependency installation could not complete locally because pnpm ran out of disk space (ERR_PNPM_ENOSPC), so build, lint, and tests were not run locally.

Additional checks:

  • git diff --check HEAD~1..HEAD
  • Version metadata is consistent across package.json, package-lock.json, and assets/manifest.json.

✅ Checklist

  • PR title follows Conventional Commits, for example fix: handle project-scope init
  • User-facing behavior is documented in README.md, README-zh.md, or CONTRIBUTING.md
  • CHANGELOG.md is updated when behavior changes
  • Skill changes were made in Chinese first when applicable, then synced to English
  • New scripts are included in assets/manifest.json and relevant tests
  • Shell scripts remain portable across macOS, Linux, and Windows Git Bash
  • No unrelated generated files or local artifacts are included

👀 Notes for Reviewers

  • README.md and README-zh.md already document Ambient Resume as a shared setting for Native and Classic projects.
  • The existing ambient_resume: false behavior is unchanged.

@sourcery-ai

sourcery-ai Bot commented Aug 18, 2026

Copy link
Copy Markdown

Reviewer's Guide

This PR decouples Ambient Resume instruction syncing from the Native workflow so Classic-only projects retain the managed instructions when ambient_resume is enabled, adds regression tests around init/update behavior for Classic-only projects, and updates release/version metadata and documentation accordingly.

Sequence diagram for Ambient Resume syncing in init and update commands

sequenceDiagram
  actor User
  participant initCommand
  participant updateSingleProject
  participant syncCometProjectInstructions

  User->>initCommand: run comet init
  initCommand->>syncCometProjectInstructions: syncCometProjectInstructions(projectPath, languageId, ambientResumeFlag)
  alt [initialProjectConfigDocument.ambient_resume is defined]
    syncCometProjectInstructions-->>initCommand: uses initialProjectConfigDocument.ambient_resume
  else [initialProjectConfigDocument.ambient_resume is undefined]
    syncCometProjectInstructions-->>initCommand: uses true (default)
  end

  User->>updateSingleProject: run comet update
  updateSingleProject->>syncCometProjectInstructions: syncCometProjectInstructions(projectPath, projectLanguageId, ambientResumeFlag)
  alt [projectConfigDocument.ambient_resume is defined]
    syncCometProjectInstructions-->>updateSingleProject: uses projectConfigDocument.ambient_resume
  else [projectConfigDocument.ambient_resume is undefined]
    syncCometProjectInstructions-->>updateSingleProject: uses true (default)
  end
Loading

File-Level Changes

Change Details Files
Ensure Ambient Resume instructions are synced for Classic-only projects when enabled, instead of being tied to Native workflow presence.
  • Update initCommand to always pass the ambient_resume flag (defaulting to true) to syncCometProjectInstructions regardless of selected workflow
  • Update updateSingleProject to always pass the ambient_resume flag (defaulting to true) to syncCometProjectInstructions regardless of nativeProject state
app/commands/init.ts
app/commands/update.ts
Add regression tests to cover Ambient Resume behavior for Classic-only projects and verify instruction content and preservation.
  • Add init E2E test ensuring Classic-only workflow installs Ambient Resume instructions while preserving existing AGENTS.md and CLAUDE.md content
  • Add update-command test ensuring Classic-only projects receive Ambient Resume instructions and preserve existing content while reporting updated instructions
test/app/init-e2e.test.ts
test/app/update.test.ts
Update changelog and version metadata to 0.4.0-beta.21 and keep tests aligned with the new version.
  • Document Classic Ambient Resume fix in CHANGELOG with new 0.4.0-beta.21 entry
  • Bump version fields to 0.4.0-beta.21 in package.json, package-lock.json, and assets/manifest.json
  • Adjust tests that assert package/version metadata to expect 0.4.0-beta.21
CHANGELOG.md
assets/manifest.json
package.json
package-lock.json
test/app/cli-help.test.ts
test/repository/release-metadata.test.ts

Assessment against linked issues

Issue Objective Addressed Explanation
#325 Ensure comet init and comet update gate Ambient Resume instructions solely on the ambient_resume setting (defaulting to true), not on Native workflow presence, so Classic-only projects with ambient_resume: true keep the <comet-ambient-resume> block.
#325 Add caller-level tests for comet init and comet update verifying that Classic-only projects with ambient_resume: true install and preserve the Ambient Resume managed block in AGENTS.md and CLAUDE.md.

Possibly linked issues


Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@github-actions

Copy link
Copy Markdown

👋 Thanks for opening your first PR to Comet, @mayzhaoyu.

Before review, please make sure the PR title follows Conventional Commits, for example fix: handle project-scope init, and that the checklist in the PR template is up to date.

🧪 The most useful local checks are:

pnpm build
pnpm lint
pnpm format:check
pnpm test

🧰 If your change touches assets/skills/comet/scripts/, please also check script portability across macOS, Linux, and Windows Git Bash. Avoid sed -i, support both sha256sum and shasum -a 256, and guard optional grep pipelines with || true.

✨ We appreciate the contribution and will take a look as soon as we can.

@github-actions

github-actions Bot commented Aug 18, 2026

Copy link
Copy Markdown

✅ PR template check passed.

@coderabbitai

coderabbitai Bot commented Aug 18, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 1ee45e65-3467-455d-9c76-c4e714339f2d

📥 Commits

Reviewing files that changed from the base of the PR and between 147b1f2 and 9a5a3cb.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (9)
  • CHANGELOG.md
  • app/commands/init.ts
  • app/commands/update.ts
  • assets/manifest.json
  • package.json
  • test/app/cli-help.test.ts
  • test/app/init-e2e.test.ts
  • test/app/update.test.ts
  • test/repository/release-metadata.test.ts

Included review availability: Your plan includes up to 8 reviews per rolling hour; 7 remain after this review.


📝 Walkthrough

Walkthrough

Ambient Resume synchronization no longer depends on Native workflow selection. Classic-only comet init and comet update operations now preserve or install managed instructions when ambient_resume is enabled. Tests cover both commands, and release metadata is updated to 0.4.0-beta.21.

Changes

Ambient Resume synchronization

Layer / File(s) Summary
Workflow-independent instruction synchronization
app/commands/init.ts, app/commands/update.ts, test/app/init-e2e.test.ts, test/app/update.test.ts
Init and update now use the project’s ambient_resume setting for Classic-only projects. Tests verify installation in AGENTS.md and CLAUDE.md, preservation of existing content, and update reporting.
Release metadata alignment
package.json, assets/manifest.json, test/app/cli-help.test.ts, test/repository/release-metadata.test.ts, CHANGELOG.md
Release metadata and version expectations update to 0.4.0-beta.21. The changelog records the Ambient Resume behavior change.

Estimated code review effort: 2 (Simple) | ~10 minutes

Merge Risk: ⚪ Minimal · up to 9a5a3

The change is localized to preserving Ambient Resume instructions for Classic-only projects while retaining the disabled-setting behavior. No actionable merge-blocking risk remains.

Suggested reviewers: benym

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The call sites now use ambient_resume alone, and tests cover Classic-only init and update behavior with preserved user content for issue #325.
Out of Scope Changes check ✅ Passed The version, changelog, metadata, and regression test updates support the documented fix and release.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes preserving Ambient Resume for Classic-only projects, which is the main change.
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 2 issues, and left some high level feedback:

  • The new tests for Ambient Resume instructions duplicate the same string and assertion logic in multiple places (e.g., checking for <comet-ambient-resume> and comet resume-probe . --stdin --json); consider extracting a small helper to assert the ambient-resume block to keep the tests DRY and easier to update.
  • In the update test you manually spy on console.log and aggregate JSON output; if a shared helper like captureJsonOutput is available (as used in init tests), reusing it here would simplify the test and keep the approach consistent across commands.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The new tests for Ambient Resume instructions duplicate the same string and assertion logic in multiple places (e.g., checking for `<comet-ambient-resume>` and `comet resume-probe . --stdin --json`); consider extracting a small helper to assert the ambient-resume block to keep the tests DRY and easier to update.
- In the `update` test you manually spy on `console.log` and aggregate JSON output; if a shared helper like `captureJsonOutput` is available (as used in `init` tests), reusing it here would simplify the test and keep the approach consistent across commands.

## Individual Comments

### Comment 1
<location path="test/app/update.test.ts" line_range="3529-3527" />
<code_context>
+  it('installs ambient resume instructions for Classic-only projects', async () => {
</code_context>
<issue_to_address>
**suggestion (testing):** Add coverage for Classic-only projects with `ambient_resume: false` in the update flow.

To fully cover this bug, please add a companion test for a Classic-only project with `ambient_resume: false` in the config. That test should assert that the managed ambient resume block is absent (or removed if previously inserted) while preserving existing `AGENTS.md`/`CLAUDE.md` content, analogous to the existing test for projects that disable the probe, but in the Classic-only case.

Suggested implementation:

```typescript
    expect(claude).toContain('<comet-ambient-resume>');
  });

  it('does not install ambient resume instructions for Classic-only projects with ambient_resume disabled', async () => {
    await arrangeClassicDocsOpenSpecUpdate(tmpDir, { ambientResume: false });

    // Pre-existing user content plus a previously inserted ambient resume block
    await fs.writeFile(
      path.join(tmpDir, 'AGENTS.md'),
      '# User\n\nKeep this.\n\n<comet-ambient-resume>\nPrevious content\n</comet-ambient-resume>\n',
      'utf8',
    );
    await fs.writeFile(
      path.join(tmpDir, 'CLAUDE.md'),
      '# User\n\nAlso keep this.\n\n<comet-ambient-resume>\nPrevious content\n</comet-ambient-resume>\n',
      'utf8',
    );

    const fakeHome = path.join(tmpDir, 'fake-home-classic-instructions-disabled');
    const homedirSpy = vi.spyOn(os, 'homedir').mockReturnValue(fakeHome);
    const log = vi.spyOn(console, 'log').mockImplementation(() => undefined);
    let json: string;

    try {
      await updateCommand(tmpDir, { json: true, skipNpm: true });
    } finally {
      homedirSpy.mockRestore();
      log.mockRestore();
    }

    const agents = await fs.readFile(path.join(tmpDir, 'AGENTS.md'), 'utf8');
    const claude = await fs.readFile(path.join(tmpDir, 'CLAUDE.md'), 'utf8');

    // The managed ambient resume block should be absent
    expect(agents).not.toContain('<comet-ambient-resume>');
    expect(claude).not.toContain('<comet-ambient-resume>');

    // User content should be preserved
    expect(agents).toContain('# User\n\nKeep this.\n');
    expect(claude).toContain('# User\n\nAlso keep this.\n');
  });

  it('installs ambient resume instructions for Classic-only projects', async () => {

```

To fully wire this up, ensure that Classic-only projects with `ambient_resume: false` are correctly arranged:

1. Update `arrangeClassicDocsOpenSpecUpdate` (or introduce a new helper) to accept an options object with `{ ambientResume: boolean }` and, when `ambientResume === false`, write the project config with `ambient_resume: false` in the Classic-only configuration.
2. If your existing "projects that disable the probe" test uses a different helper (e.g. `arrangeDocsOpenSpecUpdateWithAmbientResumeDisabled`), you can alternatively create a Classic-specific variant (e.g. `arrangeClassicDocsOpenSpecUpdateWithAmbientResumeDisabled(tmpDir)`) and call that from this new test instead of passing an options object—keep the test body the same, only change the helper invocation.
3. Confirm that any other tests calling `arrangeClassicDocsOpenSpecUpdate` are updated if you change its signature; if you add a new helper instead, no further changes should be required.
</issue_to_address>

### Comment 2
<location path="test/app/init-e2e.test.ts" line_range="509-507" />
<code_context>
     ).rejects.toMatchObject({ code: 'ENOENT' });
   });

+  it('installs Ambient Resume instructions for Classic-only project init', async () => {
+    mockExternalSuccess();
+    await fs.mkdir(path.join(tmpDir, '.claude'), { recursive: true });
+    await fs.writeFile(path.join(tmpDir, 'AGENTS.md'), '# User\n\nKeep this.\n', 'utf8');
+    await fs.writeFile(path.join(tmpDir, 'CLAUDE.md'), '# User\n\nAlso keep this.\n', 'utf8');
+
+    const { initCommand } = await import('../../app/commands/init.js');
+    const result = await captureJsonOutput(() =>
+      initCommand(tmpDir, { yes: true, json: true, workflow: 'classic', language: 'en' }),
+    );
+
+    expect(result).toMatchObject({
+      workflow: 'classic',
+      initializedWorkflows: ['classic'],
+    });
+    const agents = await fs.readFile(path.join(tmpDir, 'AGENTS.md'), 'utf8');
+    const claude = await fs.readFile(path.join(tmpDir, 'CLAUDE.md'), 'utf8');
</code_context>
<issue_to_address>
**suggestion (testing):** Add a negative-path init E2E test for Classic-only projects with `ambient_resume: false`.

Please also add a negative-path E2E that initializes a Classic-only project with `ambient_resume: false` (or the probe disabled via config) and asserts that no `<comet-ambient-resume>` block is written to `AGENTS.md`/`CLAUDE.md` and existing user content is preserved. This will mirror the non-Classic removal behavior and verify that `ambient_resume: false` semantics remain unchanged.

Suggested implementation:

```typescript
  it('installs Ambient Resume instructions for Classic-only project init', async () => {
    mockExternalSuccess();
    await fs.mkdir(path.join(tmpDir, '.claude'), { recursive: true });
    await fs.writeFile(path.join(tmpDir, 'AGENTS.md'), '# User\n\nKeep this.\n', 'utf8');
    await fs.writeFile(path.join(tmpDir, 'CLAUDE.md'), '# User\n\nAlso keep this.\n', 'utf8');

    const { initCommand } = await import('../../app/commands/init.js');
    const result = await captureJsonOutput(() =>
      initCommand(tmpDir, { yes: true, json: true, workflow: 'classic', language: 'en' }),
    );

    expect(result).toMatchObject({
      workflow: 'classic',
      initializedWorkflows: ['classic'],
    });
    const agents = await fs.readFile(path.join(tmpDir, 'AGENTS.md'), 'utf8');
    const claude = await fs.readFile(path.join(tmpDir, 'CLAUDE.md'), 'utf8');
    for (const content of [agents, claude]) {
      expect(content).toContain('<comet-ambient-resume>');
      expect(content).toContain('comet resume-probe . --stdin --json');
    }
    expect(agents).toContain('# User\n\nKeep this.');
    expect(claude).toContain('# User\n\nAlso keep this.');
  });

  it('does not install Ambient Resume instructions when Classic-only project has ambient_resume disabled', async () => {
    mockExternalSuccess();
    await fs.mkdir(path.join(tmpDir, '.claude'), { recursive: true });
    await fs.writeFile(path.join(tmpDir, 'AGENTS.md'), '# User\n\nKeep this.\n', 'utf8');
    await fs.writeFile(path.join(tmpDir, 'CLAUDE.md'), '# User\n\nAlso keep this.\n', 'utf8');

    // Disable Ambient Resume via config for a Classic-only project
    await fs.writeFile(
      path.join(tmpDir, '.claude', 'config.json'),
      JSON.stringify(
        {
          ambient_resume: false,
        },
        null,
        2,
      ),
      'utf8',
    );

    const { initCommand } = await import('../../app/commands/init.js');
    const result = await captureJsonOutput(() =>
      initCommand(tmpDir, { yes: true, json: true, workflow: 'classic', language: 'en' }),
    );

    expect(result).toMatchObject({
      workflow: 'classic',
      initializedWorkflows: ['classic'],
    });

    const agents = await fs.readFile(path.join(tmpDir, 'AGENTS.md'), 'utf8');
    const claude = await fs.readFile(path.join(tmpDir, 'CLAUDE.md'), 'utf8');

    for (const content of [agents, claude]) {
      expect(content).not.toContain('<comet-ambient-resume>');
      expect(content).not.toContain('comet resume-probe . --stdin --json');
    }

    expect(agents).toContain('# User\n\nKeep this.');
    expect(claude).toContain('# User\n\nAlso keep this.');
  });

  it('adds Classic with the docs layout when a Native-only project is reinitialized as Both', async () => {
    mockExternalSuccess();

```

If this repository uses a different config file name, location, or schema to disable Ambient Resume (e.g. `.claude/project.json`, YAML, or a nested `features: { ambient_resume: false }` object), adjust the `fs.writeFile` path and JSON structure in the new test to match the existing non-Classic negative-path Ambient Resume tests so that the init command actually observes the disabled setting.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread test/app/update.test.ts
@@ -3526,6 +3526,36 @@ describe('update command helpers', () => {
expect(claude).toContain('<comet-ambient-resume>');
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (testing): Add coverage for Classic-only projects with ambient_resume: false in the update flow.

To fully cover this bug, please add a companion test for a Classic-only project with ambient_resume: false in the config. That test should assert that the managed ambient resume block is absent (or removed if previously inserted) while preserving existing AGENTS.md/CLAUDE.md content, analogous to the existing test for projects that disable the probe, but in the Classic-only case.

Suggested implementation:

    expect(claude).toContain('<comet-ambient-resume>');
  });

  it('does not install ambient resume instructions for Classic-only projects with ambient_resume disabled', async () => {
    await arrangeClassicDocsOpenSpecUpdate(tmpDir, { ambientResume: false });

    // Pre-existing user content plus a previously inserted ambient resume block
    await fs.writeFile(
      path.join(tmpDir, 'AGENTS.md'),
      '# User\n\nKeep this.\n\n<comet-ambient-resume>\nPrevious content\n</comet-ambient-resume>\n',
      'utf8',
    );
    await fs.writeFile(
      path.join(tmpDir, 'CLAUDE.md'),
      '# User\n\nAlso keep this.\n\n<comet-ambient-resume>\nPrevious content\n</comet-ambient-resume>\n',
      'utf8',
    );

    const fakeHome = path.join(tmpDir, 'fake-home-classic-instructions-disabled');
    const homedirSpy = vi.spyOn(os, 'homedir').mockReturnValue(fakeHome);
    const log = vi.spyOn(console, 'log').mockImplementation(() => undefined);
    let json: string;

    try {
      await updateCommand(tmpDir, { json: true, skipNpm: true });
    } finally {
      homedirSpy.mockRestore();
      log.mockRestore();
    }

    const agents = await fs.readFile(path.join(tmpDir, 'AGENTS.md'), 'utf8');
    const claude = await fs.readFile(path.join(tmpDir, 'CLAUDE.md'), 'utf8');

    // The managed ambient resume block should be absent
    expect(agents).not.toContain('<comet-ambient-resume>');
    expect(claude).not.toContain('<comet-ambient-resume>');

    // User content should be preserved
    expect(agents).toContain('# User\n\nKeep this.\n');
    expect(claude).toContain('# User\n\nAlso keep this.\n');
  });

  it('installs ambient resume instructions for Classic-only projects', async () => {

To fully wire this up, ensure that Classic-only projects with ambient_resume: false are correctly arranged:

  1. Update arrangeClassicDocsOpenSpecUpdate (or introduce a new helper) to accept an options object with { ambientResume: boolean } and, when ambientResume === false, write the project config with ambient_resume: false in the Classic-only configuration.
  2. If your existing "projects that disable the probe" test uses a different helper (e.g. arrangeDocsOpenSpecUpdateWithAmbientResumeDisabled), you can alternatively create a Classic-specific variant (e.g. arrangeClassicDocsOpenSpecUpdateWithAmbientResumeDisabled(tmpDir)) and call that from this new test instead of passing an options object—keep the test body the same, only change the helper invocation.
  3. Confirm that any other tests calling arrangeClassicDocsOpenSpecUpdate are updated if you change its signature; if you add a new helper instead, no further changes should be required.

Comment thread test/app/init-e2e.test.ts
@@ -506,6 +506,31 @@ describe('comet init E2E', () => {
).rejects.toMatchObject({ code: 'ENOENT' });
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

suggestion (testing): Add a negative-path init E2E test for Classic-only projects with ambient_resume: false.

Please also add a negative-path E2E that initializes a Classic-only project with ambient_resume: false (or the probe disabled via config) and asserts that no <comet-ambient-resume> block is written to AGENTS.md/CLAUDE.md and existing user content is preserved. This will mirror the non-Classic removal behavior and verify that ambient_resume: false semantics remain unchanged.

Suggested implementation:

  it('installs Ambient Resume instructions for Classic-only project init', async () => {
    mockExternalSuccess();
    await fs.mkdir(path.join(tmpDir, '.claude'), { recursive: true });
    await fs.writeFile(path.join(tmpDir, 'AGENTS.md'), '# User\n\nKeep this.\n', 'utf8');
    await fs.writeFile(path.join(tmpDir, 'CLAUDE.md'), '# User\n\nAlso keep this.\n', 'utf8');

    const { initCommand } = await import('../../app/commands/init.js');
    const result = await captureJsonOutput(() =>
      initCommand(tmpDir, { yes: true, json: true, workflow: 'classic', language: 'en' }),
    );

    expect(result).toMatchObject({
      workflow: 'classic',
      initializedWorkflows: ['classic'],
    });
    const agents = await fs.readFile(path.join(tmpDir, 'AGENTS.md'), 'utf8');
    const claude = await fs.readFile(path.join(tmpDir, 'CLAUDE.md'), 'utf8');
    for (const content of [agents, claude]) {
      expect(content).toContain('<comet-ambient-resume>');
      expect(content).toContain('comet resume-probe . --stdin --json');
    }
    expect(agents).toContain('# User\n\nKeep this.');
    expect(claude).toContain('# User\n\nAlso keep this.');
  });

  it('does not install Ambient Resume instructions when Classic-only project has ambient_resume disabled', async () => {
    mockExternalSuccess();
    await fs.mkdir(path.join(tmpDir, '.claude'), { recursive: true });
    await fs.writeFile(path.join(tmpDir, 'AGENTS.md'), '# User\n\nKeep this.\n', 'utf8');
    await fs.writeFile(path.join(tmpDir, 'CLAUDE.md'), '# User\n\nAlso keep this.\n', 'utf8');

    // Disable Ambient Resume via config for a Classic-only project
    await fs.writeFile(
      path.join(tmpDir, '.claude', 'config.json'),
      JSON.stringify(
        {
          ambient_resume: false,
        },
        null,
        2,
      ),
      'utf8',
    );

    const { initCommand } = await import('../../app/commands/init.js');
    const result = await captureJsonOutput(() =>
      initCommand(tmpDir, { yes: true, json: true, workflow: 'classic', language: 'en' }),
    );

    expect(result).toMatchObject({
      workflow: 'classic',
      initializedWorkflows: ['classic'],
    });

    const agents = await fs.readFile(path.join(tmpDir, 'AGENTS.md'), 'utf8');
    const claude = await fs.readFile(path.join(tmpDir, 'CLAUDE.md'), 'utf8');

    for (const content of [agents, claude]) {
      expect(content).not.toContain('<comet-ambient-resume>');
      expect(content).not.toContain('comet resume-probe . --stdin --json');
    }

    expect(agents).toContain('# User\n\nKeep this.');
    expect(claude).toContain('# User\n\nAlso keep this.');
  });

  it('adds Classic with the docs layout when a Native-only project is reinitialized as Both', async () => {
    mockExternalSuccess();

If this repository uses a different config file name, location, or schema to disable Ambient Resume (e.g. .claude/project.json, YAML, or a nested features: { ambient_resume: false } object), adjust the fs.writeFile path and JSON structure in the new test to match the existing non-Classic negative-path Ambient Resume tests so that the init command actually observes the disabled setting.

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.

fix: comet init/update strips the Ambient Resume block in Classic-only projects

1 participant