Skip to content

Conformance Task Architecture

xtarterize applies conformance configuration through discrete, independently applicable tasks. This page covers the architecture and how to contribute new tasks.

All tasks implement the Task interface from @xtarterize/core:

interface Task {
id: string
label: string
group: string
applicable: (profile: ProjectProfile) => boolean
check: (cwd: string, profile: ProjectProfile) => Promise<TaskStatus>
dryRun: (cwd: string, profile: ProjectProfile) => Promise<FileDiff[]>
apply: (cwd: string, profile: ProjectProfile) => Promise<void>
}
classDiagram
    class Task {
        +string id
        +string label
        +string group
        +applicable(profile) boolean
        +check(cwd, profile) TaskStatus
        +dryRun(cwd, profile) FileDiff[]
        +apply(cwd, profile) void
    }
    class TaskStatus {
        <<enumeration>>
        new
        patch
        skip
        conflict
    }
    class FileDiff {
        +string filepath
        +string before
        +string after
    }
    class ProjectProfile {
        +Framework framework
        +Bundler bundler
        +PackageManager packageManager
        +boolean typescript
        +boolean monorepo
        +boolean hasGitHub
    }
    Task --> TaskStatus
    Task --> FileDiff
    Task --> ProjectProfile
  • Directorypackages/tasks/src/
    • Directoryagent/
      • agents-md.ts
      • module.ts
      • skills.ts
    • Directoryci/
      • auto-update.ts
      • ci.ts
      • release.ts
    • Directorycodegen/
      • plop.ts
    • Directorydeps/
      • renovate.ts
    • Directoryeditor/
      • vscode.ts
    • factory-config.ts
    • factory-ops.ts
    • factory.ts
    • Directorylint/
      • biome.ts
    • Directorymonorepo/
      • turbo.ts
    • Directoryquality/
      • knip.ts
    • Directoryrelease/
      • cat-version.ts
      • commitlint.ts
      • czg.ts
    • Directoryscripts/
      • package-scripts.ts
    • Directoryts/
      • strict.ts
      • paths.ts
      • incremental.ts
      • gitignore-tsbuildinfo.ts
    • Directoryvite/
      • checker.ts
      • visualizer.ts

Most tasks are created through factory functions in factory.ts that eliminate boilerplate:

This reduces most tasks from 40-80 lines to 10-15 lines of declarative configuration.

Shared implementation seams used by the factory layer:

The factory provides several utilities for robust equivalence detection:

HelperPurposeUsed By
deepEqualCompare objects regardless of key orderingcreateJsonMergeTask, createMultiFileJsonMergeTask
normalizeExtendsNormalize "extends": "str" to "extends": ["str"]biomeTask, renovateTask
normalizeLineEndingsConvert \r\n to \n before comparisoncreateSimpleFileTask
hasScriptValueCheck if a command value already exists under any script namecreatePackageJsonTask

Config tools like Renovate and Biome accept extends as either a string or array. normalizeExtends ensures both forms are treated as equivalent during comparison:

normalizeExtends({ extends: "config:base" })
// → { extends: ["config:base"] }

Package script tasks skip adding a script not only when the key exists, but also when the exact same command already exists under a different name. This handles real-world aliases like type:check vs typecheck.

  1. Implement the Task interface from @xtarterize/core
  2. Create your task file in packages/tasks/src/<category>/<task>.ts
  3. Export it from packages/tasks/src/index.ts
  4. Add tests in test/tasks/

Each task must implement:

  • applicable(profile) — Should this task run for this project?
  • check(cwd, profile) — What’s the current status?
  • dryRun(cwd, profile) — What would change?
  • apply(cwd, profile) — Make the changes

For tasks that patch JSON config files, use a tristate detection pattern to avoid permanent patch loops:

StateConditionStatusBehavior
MissingKey does not existpatchAdd the key
MatchKey exists with expected valueskipNo changes
MismatchKey exists with different valueconflictAlert user, don’t overwrite

This prevents infinite loops when a user explicitly sets a value that differs from xtarterize’s recommendation (e.g., strict: false in tsconfig).

Learn about the overall architecture →