Skip to content

fix: persist and resolve global Comet artifact language#191

Merged
benym merged 1 commit into
rpamis:masterfrom
1919chichi:fix/global-artifact-language
Jul 11, 2026
Merged

fix: persist and resolve global Comet artifact language#191
benym merged 1 commit into
rpamis:masterfrom
1919chichi:fix/global-artifact-language

Conversation

@1919chichi

@1919chichi 1919chichi commented Jul 10, 2026

Copy link
Copy Markdown
Contributor

Summary

  • Global comet init/comet update now persist the selected artifact language to ~/.comet/config.yaml, and Classic workflows resolve project config before falling back to that global default — globally installed Chinese Skills now create/validate OpenSpec and Superpowers artifacts in zh-CN instead of silently falling back to English.
  • Fixes several correctness issues found in code review of that change before it reached master: mergeProjectConfig silently ignoring a re-selected language on a config file that already had one; the shared config reader throwing on unrelated YAML syntax errors and blocking guard language checks; an explicit empty config value being treated as unset; and comet update picking an arbitrary platform's language when multiple platforms disagree at the same scope.

Fixes #174

Test plan

  • pnpm build
  • pnpm lint
  • pnpm format:check
  • pnpm test (full suite, excluding one pre-existing environment-specific failure in bundle-command.test.ts unrelated to this change — it scans the real ~/.claude/skills directory and trips on a locally installed personal skill with malformed frontmatter)

Summary by Sourcery

Persist and correctly resolve Comet’s global artifact language across init/update workflows and Classic guards, with project-over-global precedence and robust YAML config handling.

New Features:

  • Persist the selected artifact language in global ~/.comet/config.yaml when running comet init or comet update in global scope.
  • Resolve Classic workflow artifact language by reading project .comet/config.yaml first and falling back to global ~/.comet/config.yaml when absent.

Bug Fixes:

  • Ensure project config merge and rendering always respect explicit language overrides and preserve existing language when none is specified.
  • Avoid silently defaulting when review_mode is explicitly set to an empty value and instead surface a validation error.
  • Make Classic config reads tolerant of unrelated YAML syntax errors so malformed fields outside the target key do not block language or mode resolution.
  • Prevent comet update from guessing an artifact language when installed platforms in the same scope disagree and no language is requested, preserving the existing config value instead.

Enhancements:

  • Introduce a shared Classic project config helper to encapsulate precedence, file discovery, and YAML parsing for state and guard commands.
  • Refine language detection and guard checks so installed Chinese skills reliably enforce zh-CN for OpenSpec and Superpowers artifacts while still allowing per-project overrides.

Build:

  • Bump package and manifest versions to 0.4.0-beta.4 and refresh the bundled YAML runtime paths.

Documentation:

  • Update English and Chinese skill reference docs to describe project-vs-global language configuration and precedence, including ~/.comet/config.yaml defaults.

Tests:

  • Add unit and E2E tests covering global/project language persistence, precedence rules, invalid/empty config values, YAML error handling, and multi-platform disagreement cases in init, update, state, and guard flows.

Summary by CodeRabbit

  • Bug Fixes

    • Global comet init and comet update now save the selected artifact language.
    • Classic workflows prioritize project language settings, then use global defaults.
    • Chinese workflows now consistently create and validate OpenSpec and Superpowers artifacts in zh-CN.
    • Existing language settings are preserved when installed platforms have conflicting languages, unless explicitly overridden.
  • Documentation

    • Clarified language precedence and configuration behavior for project and global settings.

Global `comet init`/`comet update` now write the selected artifact
language to `~/.comet/config.yaml`, and Classic workflows resolve
project config before falling back to that global default. Globally
installed Chinese Skills now create and validate OpenSpec/Superpowers
artifacts in zh-CN instead of always falling back to English.

Also fixes several correctness issues found in code review of the
above change before it reached master:
- mergeProjectConfig silently ignored a re-selected language once
  `.comet/config.yaml` already had a `language` key (init/update did
  nothing on a second run with a different --language).
- The shared config reader threw on any unrelated YAML syntax error
  in config.yaml, blocking Classic guard language checks that had
  nothing to do with the broken field, and surfacing as an
  inconsistent exit code (70 instead of 1) from comet-state.
- An explicit empty string config value was silently treated as
  "unset" instead of failing validation.
- comet update picked an arbitrary platform's language when multiple
  platforms were installed at the same scope with different languages.

Fixes rpamis#174

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Reviewer's Guide

Persist and correctly resolve Comet artifact language across global and project scopes by introducing a shared Classic config reader, updating init/update workflows to write language defaults, and tightening validation/merging logic so language and other fields behave predictably even with malformed config files or conflicting platform installs.

Sequence diagram for language persistence in updateCommand

sequenceDiagram
  participant User
  participant updateCommand
  participant getBaseDir
  participant mergeProjectConfig
  participant LANGUAGES

  User->>updateCommand: updateCommand(projectPath, options)
  updateCommand->>updateCommand: resolveTargetLanguage(options.language, target.language)
  loop scopes (project, global)
    updateCommand->>updateCommand: scopeTargets = targets.filter(scope)
    alt explicit --language
      updateCommand->>updateCommand: languageId = resolveTargetLanguage(options.language, scopeTargets[0].language)
    else platforms agree
      updateCommand->>updateCommand: agreedLanguage = scopeTargets[0].language
      updateCommand->>updateCommand: languageId = agreedLanguage
    else conflicting platforms
      updateCommand->>updateCommand: languageId = null
    end
    updateCommand->>getBaseDir: getBaseDir(scope, projectPath)
    getBaseDir-->>updateCommand: configRoot
    alt languageId
      updateCommand->>LANGUAGES: languageToArtifactLanguage(languageId)
      LANGUAGES-->>updateCommand: artifactLanguage
      updateCommand->>mergeProjectConfig: mergeProjectConfig(configRoot, artifactLanguage)
    else no definitive language
      updateCommand->>mergeProjectConfig: mergeProjectConfig(configRoot, null)
    end
  end
Loading

File-Level Changes

Change Details Files
Introduce a shared Classic config reader that resolves project-over-global precedence and tolerates unrelated YAML syntax errors.
  • Add classic-project-config module that enumerates project and home (~/.comet) config.yaml candidates with de-duplication.
  • Implement readClassicConfigValue to parse YAML with parseDocument, return typed {value, source}, and ignore fields that are null/undefined.
  • Wire readClassicConfigValue into classic-state-command and classic-guard to replace ad-hoc file/line parsing and ensure project config is checked before global defaults.
domains/comet-classic/classic-project-config.ts
domains/comet-classic/classic-state-command.ts
domains/comet-classic/classic-guard.ts
assets/skills/comet/reference/comet-yaml-fields.md
assets/skills-zh/comet/reference/comet-yaml-fields.md
Ensure global and project installs persist artifact language correctly and that mergeProjectConfig/renderProjectConfig respect explicit language overrides while preserving existing values when none is provided.
  • Update initCommand to merge global .comet/config.yaml with the selected artifact language for global installs, while keeping project scope creating working dirs only.
  • Enhance updateCommand to compute language per scope, only assert a language when either explicitly requested or unanimously agreed by all platforms in that scope, and pass null to preserve existing config when platforms disagree.
  • Modify renderProjectConfig and mergeProjectConfig to accept nullable language: non-null values force the managed language field, null preserves existing language and defaults to 'en' only when absent.
  • Map skill language ids (en/zh) to artifactLanguage (en/zh-CN) via LANGUAGES and languageToArtifactLanguage for config persistence.
app/commands/init.ts
app/commands/update.ts
domains/skill/platform-install.ts
domains/skill/languages.js
assets/skills/comet/reference/comet-yaml-fields.md
assets/skills-zh/comet/reference/comet-yaml-fields.md
assets/skills/comet/SKILL.md
assets/skills/comet-open/SKILL.md
assets/skills/comet-hotfix/SKILL.md
assets/skills/comet-tweak/SKILL.md
assets/skills-zh/comet/SKILL.md
assets/skills-zh/comet-open/SKILL.md
assets/skills-zh/comet-hotfix/SKILL.md
assets/skills-zh/comet-tweak/SKILL.md
Tighten correctness around language and review_mode validation so invalid or empty values are rejected while unrelated malformed YAML does not block guards or init.
  • Use readClassicConfigValue in projectLanguageDefault and other helpers so field-level type/enum validation surfaces errors with contextual messages, rather than raw YAML parse failures.
  • Treat explicit empty review_mode (e.g. review_mode: "") as invalid instead of silently defaulting, and ensure invalid global language values cause init to fail with a clear error.
  • Rely on yaml.parseDocument’s error-tolerant behavior so malformed unrelated fields in .comet/config.yaml don’t prevent language resolution or guard checks.
domains/comet-classic/classic-state-command.ts
domains/comet-classic/classic-guard.ts
domains/comet-classic/classic-project-config.ts
Extend tests to cover new language resolution and persistence behaviors for init/update and Classic guards, plus config merge semantics.
  • Add tests for falling back to global language when project config is absent, project overriding global language, rejecting invalid global language, and ignoring unrelated malformed fields in .comet/config.yaml when initializing a change.
  • Add guard tests verifying use of global language when change/project values are absent and that malformed unrelated config fields do not block language checks.
  • Add init E2E tests asserting that global and zh installs persist language to the appropriate .comet/config.yaml.
  • Add update command tests ensuring global skill updates persist zh-CN in ~/.comet/config.yaml, re-persist explicit language over existing values, and avoid guessing language when platforms disagree.
  • Add skills tests for renderProjectConfig and mergeProjectConfig to verify language override vs preservation semantics and retention of user-defined fields.
test/domains/comet-classic/comet-scripts.test.ts
test/app/update.test.ts
test/app/init-e2e.test.ts
test/domains/skill/skills.test.ts
Bump package/manifest versions and adjust bundled YAML import paths.
  • Increment version to 0.4.0-beta.4 in manifest.json, package.json, and CHANGELOG, documenting the global artifact language fix.
  • Update comet-runtime bundled YAML require paths from pnpm-specific node_modules/.pnpm/... to node_modules/yaml/... to match current layout.
assets/manifest.json
package.json
CHANGELOG.md
assets/skills/comet/scripts/comet-runtime.mjs

Assessment against linked issues

Issue Objective Addressed Explanation
#174 Ensure that when Comet is initialized globally with the Chinese language (zh-CN), the selected language is persisted and used as the default artifact language so that generated docs and OpenSpec artifacts are in zh-CN instead of falling back to English.

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

@coderabbitai

coderabbitai Bot commented Jul 10, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Artifact language persistence and fallback

Layer / File(s) Summary
Persist language during init and update
app/commands/init.ts, app/commands/update.ts, domains/skill/platform-install.ts, test/app/*, test/domains/skill/skills.test.ts
Global initialization and updates persist normalized artifact languages, preserve existing values when detected languages conflict, and honor explicit overrides.
Centralize Classic configuration lookup
domains/comet-classic/*, test/domains/comet-classic/comet-scripts.test.ts
Classic workflows read project configuration before global configuration, validate configured languages, and handle malformed unrelated fields without blocking language resolution.
Refresh bundled Classic runtime
assets/skills/comet/scripts/comet-runtime.mjs
The bundled YAML, Classic guard, handoff, hook-guard, and state implementations are refreshed with the corresponding configuration and filesystem behavior.
Align skill instructions and release metadata
assets/skills*/comet*, CHANGELOG.md, assets/manifest.json, package.json
Skill instructions document normalized project-over-global language resolution, and package metadata is updated to beta.4.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant InitUpdate
  participant ConfigYaml
  participant ClassicWorkflow
  participant Artifacts
  User->>InitUpdate: select or pass skill language
  InitUpdate->>ConfigYaml: persist normalized artifact language
  ClassicWorkflow->>ConfigYaml: read project config, then global config
  ConfigYaml-->>ClassicWorkflow: resolved language
  ClassicWorkflow->>Artifacts: generate and validate artifacts using resolved language
Loading

Possibly related PRs

  • rpamis/comet#153 — Introduced related artifact-language persistence and resolution behavior that this change extends to global scope.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.70% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title concisely summarizes the main fix: persisting and resolving the global Comet artifact language.
Linked Issues check ✅ Passed The changes satisfy issue #174 by persisting global language on init/update and resolving project-first, global-fallback language for Classic/OpenSpec flows.
Out of Scope Changes check ✅ Passed No clearly unrelated feature work is evident; the docs, tests, and runtime/config updates all support the language-resolution fix.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

package.json

Oops! Something went wrong! :(

ESLint: 10.4.0

TypeError: Error while loading rule 'no-irregular-whitespace': sourceCode.getAllComments is not a function
Occurred while linting /package.json
at Object.create (/node_modules/eslint/lib/rules/no-irregular-whitespace.js:92:35)
at createRuleListeners (/node_modules/eslint/lib/linter/linter.js:497:15)
at /node_modules/eslint/lib/linter/linter.js:623:7
at Array.forEach ()
at runRules (/node_modules/eslint/lib/linter/linter.js:557:31)
at #flatVerifyWithoutProcessors (/node_modules/eslint/lib/linter/linter.js:1264:4)
at Linter._verifyWithFlatConfigArrayAndWithoutProcessors (/node_modules/eslint/lib/linter/linter.js:1349:43)
at Linter._verifyWithFlatConfigArray (/node_modules/eslint/lib/linter/linter.js:1416:15)
at Linter.verify (/node_modules/eslint/lib/linter/linter.js:861:9)
at Linter.verifyAndFix (/node_modules/eslint/lib/linter/linter.js:1534:20)


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 left some high level feedback:

  • In updateCommand, languageToArtifactLanguage uses a non-null assertion on LANGUAGES.find(...); consider handling the case where a language id is missing (e.g. by throwing a clear error) instead of relying on ! to avoid an opaque runtime crash.
  • In readClassicConfigValue, parseDocument errors are always ignored; you might want to surface a structured error when the parsed document has errors and the requested field is present, so users see a clear message instead of silently falling back to defaults in the case of a fully malformed config.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `updateCommand`, `languageToArtifactLanguage` uses a non-null assertion on `LANGUAGES.find(...)`; consider handling the case where a language id is missing (e.g. by throwing a clear error) instead of relying on `!` to avoid an opaque runtime crash.
- In `readClassicConfigValue`, `parseDocument` errors are always ignored; you might want to surface a structured error when the parsed document has errors and the requested field is present, so users see a clear message instead of silently falling back to defaults in the case of a fully malformed config.

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.

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
domains/comet-classic/classic-guard.ts (1)

136-147: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Empty language still skips validation in the guard. projectConfigValue() collapses a missing config value and an explicit empty string to '', and configuredLanguage() still treats any falsy value as unset. That means .comet/config.yaml with language: "" falls back to en here instead of raising the invalid-language error that classic-state-command.ts now uses.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@domains/comet-classic/classic-guard.ts` around lines 136 - 147, Update
projectConfigValue and configuredLanguage to preserve the distinction between a
missing language and an explicitly empty string. Ensure configuredLanguage
defaults to en only when no value is configured, while an explicit empty string
proceeds to validation and throws the invalid-language error, matching
classic-state-command.ts behavior.
🧹 Nitpick comments (1)
test/domains/comet-classic/comet-scripts.test.ts (1)

1055-1088: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Consider adding a guard test for explicit empty language in .comet/config.yaml.

The existing tests cover malformed-field tolerance and global-language fallback for the guard, but none exercise language: "" (explicit empty) against the guard's configuredLanguage, which is the scenario affected by the issue raised in classic-guard.ts.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/domains/comet-classic/comet-scripts.test.ts` around lines 1055 - 1088,
Add a guard test alongside the existing malformed-config test that writes
`.comet/config.yaml` with an explicit empty `language: ""`, creates a change
whose `configuredLanguage` is populated, and invokes `guardScript` for that
change. Assert the guard succeeds, covering the empty-language path in
`classic-guard.ts` without relying on global-language fallback.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@domains/comet-classic/classic-guard.ts`:
- Around line 136-147: Update projectConfigValue and configuredLanguage to
preserve the distinction between a missing language and an explicitly empty
string. Ensure configuredLanguage defaults to en only when no value is
configured, while an explicit empty string proceeds to validation and throws the
invalid-language error, matching classic-state-command.ts behavior.

---

Nitpick comments:
In `@test/domains/comet-classic/comet-scripts.test.ts`:
- Around line 1055-1088: Add a guard test alongside the existing
malformed-config test that writes `.comet/config.yaml` with an explicit empty
`language: ""`, creates a change whose `configuredLanguage` is populated, and
invokes `guardScript` for that change. Assert the guard succeeds, covering the
empty-language path in `classic-guard.ts` without relying on global-language
fallback.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: a702968d-b49c-4334-ae6e-54128661f855

📥 Commits

Reviewing files that changed from the base of the PR and between da58122 and ea21817.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (24)
  • CHANGELOG.md
  • app/commands/init.ts
  • app/commands/update.ts
  • assets/manifest.json
  • assets/skills-zh/comet-hotfix/SKILL.md
  • assets/skills-zh/comet-open/SKILL.md
  • assets/skills-zh/comet-tweak/SKILL.md
  • assets/skills-zh/comet/SKILL.md
  • assets/skills-zh/comet/reference/comet-yaml-fields.md
  • assets/skills/comet-hotfix/SKILL.md
  • assets/skills/comet-open/SKILL.md
  • assets/skills/comet-tweak/SKILL.md
  • assets/skills/comet/SKILL.md
  • assets/skills/comet/reference/comet-yaml-fields.md
  • assets/skills/comet/scripts/comet-runtime.mjs
  • domains/comet-classic/classic-guard.ts
  • domains/comet-classic/classic-project-config.ts
  • domains/comet-classic/classic-state-command.ts
  • domains/skill/platform-install.ts
  • package.json
  • test/app/init-e2e.test.ts
  • test/app/update.test.ts
  • test/domains/comet-classic/comet-scripts.test.ts
  • test/domains/skill/skills.test.ts

@benym

benym commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

LGTM

@benym benym merged commit a2b804d into rpamis:master Jul 11, 2026
17 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.

fix: Comet v0.4.0-beta.3 版本 - 全局的 language: zh-CN 没有生效

2 participants