@theholocron/astromech 4.19.0 → 5.0.0-alpha.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,8 +3,7 @@
3
3
  The Holocron task runner. One **task manifest** per repo, and every
4
4
  derived surface comes from it: `holocron run` (local), `holocron ci` (the
5
5
  CI suite run locally), the generated GitHub Actions workflows, the
6
- `package.json` scripts, the linter set, and the branch-protection
7
- required-checks list.
6
+ `package.json` scripts, and the branch-protection required-checks list.
8
7
 
9
8
  > An astromech droid runs a starfighter's maintenance, diagnostics and
10
9
  > system wiring while the pilot flies. This does that for a repo.
@@ -25,27 +24,75 @@ import { createAstromech } from "@theholocron/astromech";
25
24
 
26
25
  const astromech = createAstromech({ cwd });
27
26
 
28
- const report = astromech.run("test", { passthrough: ["--watch"] });
27
+ const report = astromech.run("verification.unitTests", { passthrough: ["--watch"] });
29
28
  // → { status: "ok" | "fail" | "skip" | "dry-run" | "unknown", command?, message? }
30
29
  ```
31
30
 
32
31
  ### `holocron run <task>` resolution
33
32
 
34
- `holocron run test` runs your tests — you don't tell it turbo vs pnpm vs
35
- npm, or which runner:
33
+ `holocron run verification.unitTests` runs your tests — you don't tell it
34
+ turbo vs pnpm vs npm, or which runner:
36
35
 
37
36
  ```
38
37
  1. turbo.json defines the task → turbo run <task>
39
38
  2. package.json has a <task> script → <detected pm> run <task>
40
39
  (a "holocron run …" thin caller is skipped — no recursion)
41
40
  3. the registry has a local runner → <tool> <args> <org-flags> (e.g. --coverage)
42
- 4. known task, nothing to run → "no <task> task", exit 0 (exit 1 with --required)
43
- 5. unknown task → error, exit 1
41
+ 3b. the task is a linterGroup → each resolved linter, run natively
42
+ 4. the task is a container of jobs → each job, in declared order
43
+ 5. known task, nothing to run → "no <task> task", exit 0 (exit 1 with --required)
44
+ 6. unknown task → error, exit 1
44
45
  ```
45
46
 
46
- The registry (`TASKS`) covers `test` / `typecheck` / `lint` / `build` /
47
- `sync` / `wiki`; `codeql` / `deploy` have no local equivalent. Adding a
48
- task here gives every repo that task.
47
+ ## Intent → technology
48
+
49
+ Tasks are named by **intent**, not by tool — a repo declares
50
+ `verification.unitTests`, not `vitest`. The table below is the full
51
+ registry (`TASKS` in `src/registry.ts`): every task name astromech knows,
52
+ what it actually runs, and why. Tool names never appear in
53
+ `holocron.config.ts` — they're an implementation detail this table
54
+ documents, not a naming convention repos need to follow.
55
+
56
+ | Task | Runs | Notes |
57
+ | ---------------------------------------- | -------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
58
+ | `verification.unitTests` | vitest | carries the `--coverage` org default |
59
+ | `verification.typeSafety` | tsc | `tsc --noEmit` |
60
+ | `verification.performance` | Lighthouse CI | only runs with a `lighthouse.config.*` present |
61
+ | `sourceQuality.staticAnalysis` | eslint, actionlint, git-merge-conflict-markers | a `linterGroup` — see below |
62
+ | `sourceQuality.formatting` | prettier, editorconfig-checker, markdownlint-cli2 | a `linterGroup` |
63
+ | `sourceQuality.structuredDataValidation` | yamllint | |
64
+ | `sourceQuality.deadCodeAnalysis` | knip | |
65
+ | `security.secretDetection` | gitleaks | |
66
+ | `security.codeScanning` | CodeQL | no local equivalent — CI only |
67
+ | `security.dependencyReview` | GitHub's native Dependabot alerts/graph | a capability-model method (`Source.enableVulnerabilityAlerts()`), not a task |
68
+ | `delivery.build` | tsdown / vite / rollup / tsc, detected from the repo's own config file | |
69
+ | `delivery.publish` | semantic-release | no local equivalent — CI only; carries `preview` (npm dist-tags) |
70
+ | `delivery.deploy` | Cloudflare Pages / Vercel | no local equivalent — CI only; carries `preview` |
71
+ | `delivery.bundleSize` | bundle-size upload to Codecov | no local equivalent — CI only |
72
+ | `platform.repoSync` | `holocron sync` | keeps generated files current |
73
+ | `platform.commitStandards` | commitlint | no local equivalent — enforced by the `commit-msg` hook locally, over the PR's commit range in CI |
74
+ | `platform.repoValidation` | `scripts/validate-adrs.mjs`, `scripts/validate-registry.mjs`, `scripts/validate-docs-presence.mjs` | a job-bearing task — spec/ADR frontmatter, registry-doc completeness, and docs-presence, not linters |
75
+ | `knowledge.wiki` | Fern | publishes `docs/wiki/*.md` — not a sync, a publish; carries `preview` |
76
+ | `knowledge.docs` | Astro build | the docs site itself; carries `preview` (defaults on) |
77
+ | `knowledge.components` | Storybook build | a browsable component catalog — "Storybook" is the tool, not the intent; carries `preview` (defaults on) |
78
+
79
+ **`preview` is a cross-cutting feature, not a namespace.** npm has staging
80
+ dist-tags, Cloudflare/Vercel do per-PR preview deploys, Fern previews
81
+ docs — none of these earn their own task or verb; it's one capability any
82
+ publish/deploy-shaped task can carry, toggled via that task's `with:`.
83
+
84
+ **`linterGroup` tasks** (`sourceQuality.staticAnalysis`,
85
+ `sourceQuality.formatting`) bundle more than one tool under a single
86
+ required check. Each tool is still gated by its own detection rule (e.g.
87
+ `eslint` only runs if `eslint.config.*` is present) — the
88
+ `tool name → detection rule → local binary` mapping lives in one place,
89
+ `src/linters.ts`. A repo's choice of which of these run is just which
90
+ tasks it includes in `tasks: [...]`, same as any other task.
91
+
92
+ **No super-linter.** Each task above runs its own tool directly, through
93
+ the same `holocron` composite action that resolves it locally — CI runs
94
+ the identical command a developer runs, not a third-party action bundling
95
+ a tool version this org doesn't control.
49
96
 
50
97
  ## Config — `@theholocron/astromech/config`
51
98
 
@@ -59,9 +106,9 @@ import { defineConfig } from "@theholocron/astromech/config";
59
106
 
60
107
  export default defineConfig({
61
108
  tasks: [
62
- "typecheck",
63
- { name: "test", required: true, with: { "run-coverage": true } },
64
- { name: "audit", ci: true, local: false }, // CI-only
109
+ "verification.typeSafety",
110
+ { name: "verification.unitTests", required: true, with: { "run-coverage": true } },
111
+ { name: "security.codeScanning", ci: true, local: false }, // CI-only
65
112
  ],
66
113
  });
67
114
  ```
@@ -77,7 +124,6 @@ export default defineConfig({
77
124
  | `local: false` | `holocron run <name>` → "CI-only task", exit 0 |
78
125
  | `required` | the task's check context is a required status check |
79
126
  | `with` | per-repo overrides on the reusable-workflow channel |
80
- | `linters` (`lint` only) | explicit linter list; omitted → auto-detect |
81
127
 
82
128
  Top-level keys: `syncScripts: false` disables the `package.json` script
83
129
  writes entirely; `holocronScript` sets the command the synced `"holocron"`
@@ -89,39 +135,43 @@ script runs (default `"holocron"`).
89
135
  const astromech = createAstromech({ cwd, config, orgContext: { org, domain } });
90
136
 
91
137
  astromech.thinCallers(); // Map<"<name>.yml", yaml> — one per templated, ci-enabled task
92
- astromech.packageScripts(); // { holocron: "holocron", lint: "holocron run lint", … }
93
- astromech.requiredChecks(); // ["Lint / Conclusion", "codecov/patch", …] — branch-protection contexts
138
+ astromech.packageScripts(); // { holocron: "holocron", "verification.unitTests": "holocron run verification.unitTests", … }
139
+ astromech.requiredChecks(); // ["Typecheck / tsc --noEmit", "codecov/patch", …] — branch-protection contexts
94
140
  astromech.codecovConfig(existing); // codecov.yml content — merges into `existing`, or scaffolds fresh when null
95
141
  astromech.ci({ scope: "required" }); // CiReport — run the gating checks locally, in CI order
96
142
  ```
97
143
 
98
- `ci()` runs every `required: true` task (else every `ci: true` task) through the
99
- same resolution as `run()`, in `CI_ORDER`, and returns `{ status, jobs }`. A
100
- `required` task whose local runner can't run is a failure; `local: null` tasks
101
- (`audit` / `codeql` / `deploy`) are reported skipped. `holocron ci` sets the
144
+ `ci()` runs every `required: true` task (else every `ci: true` task) through
145
+ the same resolution as `run()`, in `CI_ORDER`, and returns `{ status, jobs }`.
146
+ A `required` task whose local runner can't run is a failure — **except** a
147
+ task that's genuinely CI-only (`local: null`, or a `linterGroup` whose every
148
+ member lacks a local binary entirely, like `platform.commitStandards`),
149
+ which is reported skipped even when required. `holocron ci` sets the
102
150
  process exit code from `status`.
103
151
 
104
152
  `thinCallers()` returns the raw `.github/workflows/*.yml` content (no
105
- generated-by header — the caller prefixes its own). `deploy` with
106
- `preview:` shorthand produces the combined push-to-Pages / PR-to-preview
107
- workflow. `packageScripts()` emits the `holocron` entry
108
- (`holocronScript ?? "holocron"`) plus one `"<task>": "holocron run <task>"`
109
- per runnable task; it skips `local: false` entries and tasks with no local
110
- runner (`codeql`, `deploy`), and returns `{}` when `syncScripts: false` or
111
- there is no config.
153
+ generated-by header — the caller prefixes its own). `delivery.deploy` /
154
+ `knowledge.docs` / `knowledge.components` with `preview:` produce the
155
+ combined push-to-Pages / PR-to-preview workflow —
156
+ `knowledge.docs`/`knowledge.components` default `preview` on, since
157
+ neither has a plain-production-only fallback template.
158
+ `packageScripts()` emits the `holocron` entry (`holocronScript ?? "holocron"`)
159
+ plus one `"<task>": "holocron run <task>"` per runnable task; it skips
160
+ `local: false` entries and tasks with no local runner at all, and returns
161
+ `{}` when `syncScripts: false` or there is no config.
112
162
 
113
163
  ### Required checks
114
164
 
115
165
  `requiredChecks()` derives the branch-protection required-status-check list
116
- from the manifest: every `{ required: true }` task's check context (the
117
- `… / Conclusion` aggregate job, from `WORKFLOW_CHECK_CONTEXTS`), ordered by
118
- `CI_ORDER`, then `config.extraRequiredChecks` (codecov gates, the
119
- bundle-build check, …), de-duplicated. `holocron setup` prepends `"DCO"` and
120
- applies the list for `protection: "strict"` repos. Policy-free — manifest
121
- only.
122
-
123
- `holocron run` itself does not read the config yet — that (and
124
- `holocron ci`) come in later phases (epic #581).
166
+ from the manifest: every `{ required: true }` task's check context, ordered
167
+ by `CI_ORDER`, then `config.extraRequiredChecks` (codecov gates, …),
168
+ de-duplicated. Most tasks are single always-run jobs now, so their context
169
+ names that job directly (`"Typecheck / tsc --noEmit"`) — the `… /
170
+ Conclusion` fan-in aggregate is only used where a task genuinely has
171
+ several conditionally-run jobs feeding one check
172
+ (`verification.unitTests`, `platform.repoValidation`). `holocron setup`
173
+ prepends `"DCO"` and applies the list for `protection: "strict"` repos.
174
+ Policy-free — manifest only.
125
175
 
126
176
  ### `codecov.yml`
127
177
 
@@ -135,56 +185,16 @@ current file's content (or `null`) and it either merges the
135
185
  thresholds, flags, custom rules) or scaffolds a fresh file from the base
136
186
  template. `holocron setup` writes the result via the `source` capability;
137
187
  this method never touches the filesystem beyond reading `packages/*` and
138
- `apps/*` under `cwd`. Moved here from `@theholocron/cli` (#650) — the
139
- coverage setup tracks the `test` task, the same way required checks track
140
- `tasks`.
141
-
142
- ## Lint parity
143
-
144
- One linter list drives both CI and local — no asymmetry. Source: the
145
- `lint` task's `linters` array, or auto-detection from the config files
146
- present. The `linter name → super-linter VALIDATE_* keys` mapping lives in
147
- one place, `src/linters.ts`.
148
-
149
- | linter | `VALIDATE_*` | always-on | local binary |
150
- | ---------------------------- | ----------------------------------------- | ----------------------------------- | ---------------------------------------- |
151
- | `eslint` | `JAVASCRIPT_ES`, `TYPESCRIPT_ES` | on `eslint.config.*` / `.eslintrc*` | `eslint .` |
152
- | `prettier` | `*_PRETTIER` (JS/JSX/TS/TSX/MD) + `FIX_*` | yes | `prettier --check .` |
153
- | `yamllint` | `YAML` | yes | `yamllint .` (usually CI-only) |
154
- | `actionlint` | `GITHUB_ACTIONS` | yes | `actionlint` (usually CI-only) |
155
- | `gitleaks` | `GITLEAKS` | yes | `gitleaks dir` (usually CI-only) |
156
- | `editorconfig` | `EDITORCONFIG` | yes | `editorconfig-checker` (usually CI-only) |
157
- | `commitlint` | `GIT_COMMITLINT` | yes | — (commit-msg hook + CI) |
158
- | `git-merge-conflict-markers` | `GIT_MERGE_CONFLICT_MARKERS` | yes | — (CI only) |
159
- | `markdownlint` | `MARKDOWN` | on `.markdownlint*` | `markdownlint-cli2` |
160
-
161
- ```ts
162
- import { superLinterConfig, resolveLinters } from "@theholocron/astromech";
163
-
164
- superLinterConfig({ explicit: ["eslint", "prettier"], rootFiles: fs.readdirSync(cwd) });
165
- // → { env: { VALIDATE_JAVASCRIPT_ES: "true", … }, linters: ["eslint","prettier"], configInputs: { … } }
166
- ```
167
-
168
- `superLinterConfig().env` is the exact `VALIDATE_*`/`FIX_*` map the CI
169
- `lint` job needs — the CLI serializes it as the `super-linter-env` input on
170
- each repo's generated `lint` thin caller. Setting any `VALIDATE_*` puts
171
- super-linter in allow-list mode, so emitting only the enabled keys makes it
172
- run exactly the resolved set.
173
-
174
- `holocron run lint` (later phase) runs the same set natively: `turbo run
175
- lint` for the eslint portion (cached), then each other linter whose binary
176
- resolves; linters with a binary that is not on `PATH` are flagged with an
177
- install hint; the rest print "CI only".
188
+ `apps/*` under `cwd`.
178
189
 
179
190
  ## Development
180
191
 
181
- | Script | Description |
182
- | -------------------- | ----------------------- |
183
- | `pnpm build` | Bundle with tsdown |
184
- | `pnpm test` | Run the vitest suite |
185
- | `pnpm test:coverage` | Run tests with coverage |
186
- | `pnpm typecheck` | `tsc --noEmit` |
187
- | `pnpm lint` | ESLint |
192
+ | Script | Description |
193
+ | --------------------------------------- | ------------------------------------------- |
194
+ | `pnpm run delivery.build` | Bundle with tsdown |
195
+ | `pnpm run verification.unitTests` | Run the vitest suite (always with coverage) |
196
+ | `pnpm run verification.typeSafety` | `tsc --noEmit` |
197
+ | `pnpm run sourceQuality.staticAnalysis` | ESLint |
188
198
 
189
199
  ## Releases
190
200
 
@@ -1,4 +1,4 @@
1
- import { i as normalizeTaskEntry, n as TaskEntry, r as TasksConfig, t as TaskConfigItem } from "../schema-BegK4MsY.mjs";
1
+ import { i as normalizeTaskEntry, n as TaskEntry, r as TasksConfig, t as TaskConfigItem } from "../schema-D01VYg43.mjs";
2
2
  //#region src/config/define.d.ts
3
3
  /**
4
4
  * Typed identity helper for `astromech.config.ts`:
package/dist/index.d.mts CHANGED
@@ -1,32 +1,33 @@
1
- import { r as TasksConfig } from "./schema-BegK4MsY.mjs";
1
+ import { r as TasksConfig } from "./schema-D01VYg43.mjs";
2
2
  //#region src/run.d.ts
3
3
  /**
4
4
  * `holocron run <task> [job] [-- <passthrough>]` — run a registry task (or one
5
5
  * of its sub-jobs) locally.
6
6
  *
7
- * A `job` argument only applies to tasks that declare `jobs` (today: `audit`);
8
- * for any other task it is folded back into the passthrough (`holocron run
9
- * build src/`). `holocron run audit performance` runs one job; `holocron run
10
- * audit` runs every job in declared order.
7
+ * A `job` argument only applies to tasks that declare `jobs`; for any other
8
+ * task it is folded back into the passthrough (`holocron run delivery.build
9
+ * src/`).
11
10
  *
12
11
  * Resolution:
13
12
  *
14
- * 0. task === "lint" (no job) → the linter aggregate (see below)
15
13
  * J. job given → TASKS[task].jobs[job].local (unknown job → exit 1)
16
14
  * 1. turbo.json defines the task → `turbo run <task>`
17
15
  * 2. package.json has a `<task>` script → `<pm> run <task>`
18
16
  * (unless it's the `holocron run …` thin caller — that recurses)
19
17
  * 3. TASKS[task].local resolves → `<tool> <args> <org-flags> <passthrough>`
18
+ * 3b. TASKS[task].linterGroup → run each resolved linter natively (see below)
20
19
  * 4. TASKS[task].jobs has entries → run each job in declared order
21
20
  * 5. TASKS[task].local === null → "enforced in CI" (skip, even with --required)
22
21
  * 5b. known task, nothing resolved → "no <task> task" (exit 0, or 1 with --required)
23
22
  * 6. unknown task → "unknown task" (exit 1)
24
23
  *
25
- * `lint` runs the resolved linter set (`config.tasks` `linters`, else
26
- * auto-detected): the eslint slot goes through the standard turbo / script /
27
- * `eslint .` resolution (so turbo caching is kept); every other linter runs
28
- * its `localBin` when found on PATH. Missing tools are flagged; the exit code
29
- * is the worst of the lot.
24
+ * A `linterGroup` task (`sourceQuality.staticAnalysis`, etc.) runs its fixed
25
+ * set of `linters.ts` entries, each still gated by that linter's own
26
+ * `detect`/`always` rule — every entry runs its `localBin` natively when
27
+ * found on PATH. Missing tools are flagged; the exit code is the worst of
28
+ * the lot. Unlike the old single `lint` task, this check runs *after*
29
+ * turbo/`package.json` resolution (step 3b, not step 0) — a `linterGroup`
30
+ * task is a normal turbo-delegatable task like any other.
30
31
  */
31
32
  /** Minimal structural logger — `@theholocron/observability`'s `Logger` satisfies it. */
32
33
  interface RunLogger {
@@ -70,8 +71,6 @@ interface RunTaskInput extends RunDeps {
70
71
  dryRun?: boolean;
71
72
  /** Turn "no such task for this repo" (normally exit 0) into a failure. */
72
73
  required?: boolean;
73
- /** The `lint` task's explicit linter list from `config.tasks`, if any. */
74
- linters?: string[];
75
74
  /** `turbo --filter=<pkg>` passthrough (monorepo). Ignored when the repo has no `turbo.json`. */
76
75
  filter?: string;
77
76
  }
@@ -107,68 +106,9 @@ interface CiReport {
107
106
  interface CiInput extends RunDeps, CiOptions {
108
107
  cwd: string;
109
108
  config: TasksConfig;
110
- /** Explicit linter list for the `lint` task (the config's lint entry `linters`). */
111
- linters?: string[];
112
109
  }
113
110
  declare function runCi(input: CiInput): CiReport;
114
111
  //#endregion
115
- //#region src/super-linter.d.ts
116
- /**
117
- * `superLinterConfig()` — turn the resolved linter set into the exact
118
- * super-linter `VALIDATE_*` / `FIX_*` env the CI `lint` job needs. The CLI
119
- * serializes {@link SuperLinterConfig.env} as the `super-linter-env` input
120
- * on each repo's generated `lint` thin caller; the reusable workflow
121
- * expands it verbatim. This is the CI half of "lint parity" — the local
122
- * half is the `holocron run lint` aggregate, driven by the same
123
- * {@link resolveLinters}.
124
- */
125
- interface SuperLinterConfig {
126
- /**
127
- * Enabled `VALIDATE_*` / `FIX_*` keys → `"true"`. Only enabled keys are
128
- * present (super-linter allow-list mode). Ready for `JSON.stringify`.
129
- */
130
- env: Record<string, string>;
131
- /** Resolved linter names in execution order — for the human-readable comment. */
132
- linters: string[];
133
- /** Config-file inputs the resolved set honors (`eslint-config`, …). */
134
- configInputs: Partial<Record<"eslint-config" | "prettier-config" | "yaml-config", true>>;
135
- }
136
- /**
137
- * Resolve the super-linter env for a repo's `lint` task.
138
- *
139
- * @param opts.explicit the task's `linters` list, if any (else auto-detect)
140
- * @param opts.rootFiles repo-root filenames (from `listDir(cwd)`)
141
- * @param opts.includeFix emit `FIX_*` keys too (default `true`)
142
- */
143
- declare function superLinterConfig(opts: {
144
- explicit?: string[];
145
- rootFiles: string[];
146
- includeFix?: boolean;
147
- }): SuperLinterConfig;
148
- /**
149
- * The always-on baseline env — every `always` linter, no detection. This is
150
- * what the reusable `lint.yml`'s `super-linter-env` input defaults to, so a
151
- * repo whose thin caller has not been re-synced yet behaves exactly as before.
152
- */
153
- declare function baselineSuperLinterEnv(): Record<string, string>;
154
- /**
155
- * The `lint` thin caller's `with:` overrides + the `# linters: …` comment,
156
- * from the resolved linter set. Shared by `createAstromech().thinCallers()`
157
- * and the CLI's `sync` / `setup` workflow writers so the three stay in step.
158
- *
159
- * @param opts.explicit the `lint` task's `linters` list, if any
160
- * @param opts.rootFiles repo-root filenames (auto-detect fallback)
161
- * @param opts.extra per-repo `with:` overrides that win over the defaults
162
- */
163
- declare function lintThinCallerWith(opts: {
164
- explicit?: string[];
165
- rootFiles: string[];
166
- extra?: Record<string, unknown>;
167
- }): {
168
- withOverrides: Record<string, unknown>;
169
- comments: Record<string, string>;
170
- };
171
- //#endregion
172
112
  //#region src/thin-callers.d.ts
173
113
  /**
174
114
  * Workflow templates + thin-caller generation.
@@ -183,10 +123,17 @@ declare const KNOWN_WORKFLOWS: Set<string>;
183
123
  * The GitHub status-check context a `required` task contributes to branch
184
124
  * protection. Format: `"{workflow name} / {job name}"`.
185
125
  *
186
- * These name the **aggregate `Conclusion` job** (fan-in, `if: always()`), not
187
- * an individual inner job — `test` has several conditionally-run sub-jobs, so
188
- * `"Test / Conclusion"` is the only stable gate. Only merge-gating workflows
189
- * are listed. `astromech.requiredChecks()` reads this for every `required` task.
126
+ * Most of these are single-job workflows now (D3's decomposition split what
127
+ * used to be multi-job `lint`/`audit` into one task per concern) — the
128
+ * context names that job directly, no `Conclusion` aggregator needed; a
129
+ * single job's own conclusion already *is* the workflow's conclusion.
130
+ * `Conclusion` fan-in jobs are kept only where a task genuinely has several
131
+ * conditionally-run jobs feeding one required check:
132
+ * `verification.unitTests` (unit / Storybook / Chromatic / interaction /
133
+ * user-flow, each gated by its own `run-*` input) and
134
+ * `platform.repoValidation` (its three script jobs all must pass). Only
135
+ * merge-gating workflows are listed. `astromech.requiredChecks()` reads this
136
+ * for every `required` task.
190
137
  */
191
138
  declare const WORKFLOW_CHECK_CONTEXTS: Partial<Record<string, string>>;
192
139
  /**
@@ -348,14 +295,6 @@ interface Astromech {
348
295
  * when there is no config or `syncScripts: false`.
349
296
  */
350
297
  packageScripts(): Record<string, string>;
351
- /**
352
- * The resolved super-linter env for this repo's `lint` task — the CI half
353
- * of lint parity. `thinCallers()` already bakes `env` into the `lint` thin
354
- * caller's `super-linter-env` input; this method exposes the full result
355
- * for `holocron doctor` / diagnostics. Driven by the `lint` entry's
356
- * `linters` list, else auto-detection from the repo's config files.
357
- */
358
- superLinterConfig(): SuperLinterConfig;
359
298
  /**
360
299
  * The branch-protection required-status-check contexts for this repo —
361
300
  * every `required: true` task's check context plus `extraRequiredChecks`,
@@ -370,6 +309,12 @@ interface Astromech {
370
309
  * as {@link requiredChecks} — `holocron setup` owns writing the result.
371
310
  */
372
311
  codecovConfig(existing: string | null): string;
312
+ /**
313
+ * The generated `turbo.json` for this repo's manifest, pretty-printed —
314
+ * write directly, no merge. `null` when no manifest task has turbo fan-out
315
+ * config (nothing to write). See {@link TurboTaskConfig} in `registry.ts`.
316
+ */
317
+ turboConfig(): string | null;
373
318
  }
374
319
  declare function createAstromech(options: AstromechOptions): Astromech;
375
320
  //#endregion
@@ -495,7 +440,19 @@ declare function resolveLinters(opts: {
495
440
  * Actions. `holocron run <task>` and `holocron ci` resolve against this;
496
441
  * adding a task here gives every repo that task.
497
442
  *
498
- * Keyed identically to the workflow templates — a task IS a workflow.
443
+ * Keyed identically to the workflow templates — a task IS a workflow. This
444
+ * is the canonical vocabulary table (epic #672, D11): every other artifact
445
+ * (`KNOWN_TASKS`, `thin-callers.ts`'s `KNOWN_WORKFLOWS`/`WORKFLOW_CHECK_CONTEXTS`,
446
+ * `CI_ORDER`) derives from these keys rather than hand-duplicating them —
447
+ * including the future GitHub App (#679), which imports this same table for
448
+ * config-schema validation instead of reimplementing its own copy.
449
+ *
450
+ * Task names are an intent-facing vocabulary (`verification.*`,
451
+ * `sourceQuality.*`, `security.*`, `delivery.*`, `platform.*`,
452
+ * `knowledge.*`), not tool names — `eslint`/`vitest`/`tsdown`/… stay
453
+ * internal to this file and `theholocron/configs`. See
454
+ * `.notes/tech-vocabulary-rename.spec.md` (#675) for the full mapping and
455
+ * the reasoning behind each namespace and decomposition.
499
456
  *
500
457
  * Spec: `docs/wiki/specifications/tech-astromech-task-runner.spec.md` (epic #581).
501
458
  */
@@ -514,40 +471,65 @@ interface LocalRunner {
514
471
  /** The task is already a holocron subcommand (`sync`, `sync-wiki`). */
515
472
  command?: string;
516
473
  }
474
+ /**
475
+ * How a task fans out across every workspace via Turborepo — content-hash
476
+ * caching + `^`-prefixed cross-package ordering, epic #672 D9 (#681).
477
+ * Present only on tasks that genuinely run *per workspace* (a real build/
478
+ * compile/test step); whole-repo single-run tools (prettier, gitleaks,
479
+ * commitlint, yamllint, …) have no entry here — they stay off turbo.json
480
+ * entirely and run once, un-fanned-out, the way they already do.
481
+ */
482
+ interface TurboTaskConfig {
483
+ /** Glob patterns turbo hashes to decide whether a cached run is still valid. */
484
+ inputs: string[];
485
+ /** Glob patterns turbo caches/restores after a run. Empty array for a task with no build artifact (lint, typecheck). */
486
+ outputs: string[];
487
+ /** Task names this depends on — a bare name runs in this package first; `^name` waits on every upstream workspace's task. */
488
+ dependsOn: string[];
489
+ }
517
490
  /** One sub-job of a task — `performance` in `holocron run audit performance`. */
518
491
  interface JobDef {
519
492
  /** How the job runs locally; `null` → no local equivalent (enforced in CI). */
520
493
  local: LocalRunner | null;
521
494
  /**
522
- * The CI status-check context this job reports as (`audit / Knip`) — every
523
- * sub-job is a CI job. `holocron run audit` and `holocron ci` label each job
524
- * line with it; the task-level `… / Conclusion` context lives in
525
- * `WORKFLOW_CHECK_CONTEXTS`.
495
+ * The CI status-check context this job reports as (e.g.
496
+ * `platform.repoValidation / Validate registry consistency`) — every sub-job is a CI job.
497
+ * `holocron run <task>` and `holocron ci` label each job line with it;
498
+ * the task-level `… / Conclusion` context (only for tasks with several
499
+ * jobs) lives in `WORKFLOW_CHECK_CONTEXTS`.
526
500
  */
527
501
  checkContext: string;
528
502
  }
529
503
  interface TaskDef {
530
504
  /**
531
- * `null` — the registry has no built-in runner (CodeQL, deploys, audit's
532
- * server / baseline jobs). An explicit turbo task or `package.json` script
533
- * still runs (resolution steps 1–2); with neither, `holocron run` does
534
- * nothing and `holocron ci` skips it — never a failure, even when the task
535
- * is `required` (a CI-only check isn't a local one).
505
+ * `null` — the registry has no built-in runner (CodeQL, deploys, the
506
+ * bundle-size job). An explicit turbo task or `package.json` script still
507
+ * runs (resolution steps 1–2); with neither, `holocron run` does nothing
508
+ * and `holocron ci` skips it — never a failure, even when the task is
509
+ * `required` (a CI-only check isn't a local one).
536
510
  */
537
511
  local: LocalRunner | null;
538
512
  /**
539
- * Sub-jobs, keyed by slug — `holocron run audit performance`. Declared order
540
- * is run order: `holocron run audit` (no job) runs each in turn.
513
+ * Sub-jobs, keyed by slug. Declared order is run order: `holocron run
514
+ * <task>` (no job) runs each in turn.
541
515
  */
542
516
  jobs?: Record<string, JobDef>;
543
517
  /** Org-default flags injected by tool name. Removed by a repo override. */
544
518
  flags?: Record<string, string[]>;
545
519
  /**
546
- * This task is the linter aggregate: `holocron run lint` resolves the
547
- * linter set (`config.tasks` `linters` or auto-detect) and runs each
548
- * natively instead of using `local`. See `linters.ts` / `super-linter.ts`.
520
+ * This task is a linter-group aggregate: `holocron run <task>` resolves
521
+ * this fixed set of `linters.ts` entries (still gated by each linter's
522
+ * own `detect`/`always` rule) and runs each natively, instead of using
523
+ * `local`. Replaces the old single `lint` task's auto-detected linter
524
+ * list (`config.tasks[].linters`) — a repo's choice of which of these
525
+ * run is now just whether it includes this task in `tasks: [...]`, same
526
+ * as any other task. See `linters.ts` / `run.ts`.
549
527
  */
550
- linters?: boolean;
528
+ linterGroup?: string[];
529
+ /** Carries a `preview` mode (Cloudflare/Vercel deploy, npm dist-tag, Fern preview docs, …). Cross-cutting, not its own task. */
530
+ preview?: boolean;
531
+ /** Turborepo fan-out config for this task — see {@link TurboTaskConfig}. Omitted for whole-repo, non-fan-out tasks. */
532
+ turbo?: TurboTaskConfig;
551
533
  }
552
534
  declare const TASKS: Record<string, TaskDef>;
553
535
  /** Every task name the registry knows. */
@@ -591,4 +573,4 @@ declare const WORKFLOW_TEMPLATE_PROPERTIES: Record<string, string>;
591
573
  */
592
574
  declare function reusableTemplates(): Map<string, string>;
593
575
  //#endregion
594
- export { type Astromech, type AstromechOptions, CI_ORDER, type CiJobReport, type CiOptions, type CiReport, type ExecFn, type JobDef, KNOWN_TASKS, KNOWN_WORKFLOWS, LINTERS, LINTER_NAMES, type LinterDef, type LocalRunner, type OrgContext, type PreviewConfig, REUSABLE_ACTIONS, REUSABLE_WORKFLOWS, type RunLogger, type RunOptions, type RunTaskInput, type RunTaskReport, type SuperLinterConfig, TASKS, type TaskDef, WORKFLOW_CHECK_CONTEXTS, WORKFLOW_TEMPLATES, WORKFLOW_TEMPLATE_PROPERTIES, type WorkspacePackage, baselineSuperLinterEnv, codecovComponentBlock, codecovConfig, createAstromech, createCodecovConfig, deriveDeployPaths, ensureIfNotFound, extractPreviewConfig, generateCombinedDeployContent, generateThinCallerContent, lintThinCallerWith, mergeCodecovComponents, normalizeWorkflowWith, readWorkspacePackages, requiredChecks, resolveLinters, reusableTemplates, runCi, runTask, superLinterConfig };
576
+ export { type Astromech, type AstromechOptions, CI_ORDER, type CiJobReport, type CiOptions, type CiReport, type ExecFn, type JobDef, KNOWN_TASKS, KNOWN_WORKFLOWS, LINTERS, LINTER_NAMES, type LinterDef, type LocalRunner, type OrgContext, type PreviewConfig, REUSABLE_ACTIONS, REUSABLE_WORKFLOWS, type RunLogger, type RunOptions, type RunTaskInput, type RunTaskReport, TASKS, type TaskDef, WORKFLOW_CHECK_CONTEXTS, WORKFLOW_TEMPLATES, WORKFLOW_TEMPLATE_PROPERTIES, type WorkspacePackage, codecovComponentBlock, codecovConfig, createAstromech, createCodecovConfig, deriveDeployPaths, ensureIfNotFound, extractPreviewConfig, generateCombinedDeployContent, generateThinCallerContent, mergeCodecovComponents, normalizeWorkflowWith, readWorkspacePackages, requiredChecks, resolveLinters, reusableTemplates, runCi, runTask };