@event4u/agent-config 9.11.0 → 9.13.0

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.
Files changed (183) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +80 -204
  3. package/README.md +8 -1
  4. package/dist/agent-src/commands/fix/ci.md +29 -0
  5. package/dist/agent-src/commands/pr/create.md +60 -0
  6. package/dist/agent-src/contexts/authority/kernel-rule-edits.md +18 -2
  7. package/dist/agent-src/rules/active-remediation.md +0 -2
  8. package/dist/agent-src/rules/architecture.md +0 -1
  9. package/dist/agent-src/rules/artifact-drafting-protocol.md +0 -4
  10. package/dist/agent-src/rules/autonomous-execution.md +1 -2
  11. package/dist/agent-src/rules/broken-access-control.md +0 -2
  12. package/dist/agent-src/rules/code-comment-discipline.md +0 -3
  13. package/dist/agent-src/rules/code-provenance.md +0 -3
  14. package/dist/agent-src/rules/communication-through-line.md +2 -3
  15. package/dist/agent-src/rules/content-quoting-floor.md +0 -2
  16. package/dist/agent-src/rules/context-hygiene.md +1 -3
  17. package/dist/agent-src/rules/decision-revisit-gate.md +0 -2
  18. package/dist/agent-src/rules/design-fidelity.md +30 -1
  19. package/dist/agent-src/rules/domain-adoption-policy.md +0 -3
  20. package/dist/agent-src/rules/downstream-changes.md +10 -1
  21. package/dist/agent-src/rules/external-reference-deep-dive.md +0 -7
  22. package/dist/agent-src/rules/fast-path-marker-visibility.md +0 -1
  23. package/dist/agent-src/rules/git-history-discipline.md +0 -7
  24. package/dist/agent-src/rules/guidelines.md +0 -2
  25. package/dist/agent-src/rules/improve-before-implement.md +4 -2
  26. package/dist/agent-src/rules/invite-challenge.md +0 -4
  27. package/dist/agent-src/rules/linked-projects-onboarding-gate.md +0 -2
  28. package/dist/agent-src/rules/markdown-safe-codeblocks.md +0 -1
  29. package/dist/agent-src/rules/minimal-safe-diff.md +2 -2
  30. package/dist/agent-src/rules/missing-tool-handling.md +0 -1
  31. package/dist/agent-src/rules/no-attribution-footers.md +0 -3
  32. package/dist/agent-src/rules/no-decorative-emojis-in-git-surfaces.md +8 -5
  33. package/dist/agent-src/rules/no-pr-progress-comments.md +1 -3
  34. package/dist/agent-src/rules/no-roadmap-references.md +0 -2
  35. package/dist/agent-src/rules/output-discipline.md +0 -1
  36. package/dist/agent-src/rules/prefer-enums-over-literals.md +0 -2
  37. package/dist/agent-src/rules/preservation-guard.md +0 -3
  38. package/dist/agent-src/rules/role-mode-adherence.md +0 -1
  39. package/dist/agent-src/rules/secret-vcs-guard.md +0 -2
  40. package/dist/agent-src/rules/senior-engineering-discipline.md +0 -2
  41. package/dist/agent-src/rules/session-canary.md +0 -2
  42. package/dist/agent-src/rules/size-enforcement.md +0 -5
  43. package/dist/agent-src/rules/source-confidentiality.md +0 -3
  44. package/dist/agent-src/rules/source-discovery-gate.md +0 -2
  45. package/dist/agent-src/rules/think-before-action.md +3 -3
  46. package/dist/agent-src/rules/token-budget-discipline.md +0 -1
  47. package/dist/agent-src/rules/token-efficiency.md +1 -2
  48. package/dist/agent-src/rules/tool-safety.md +0 -1
  49. package/dist/agent-src/rules/user-interaction.md +0 -3
  50. package/dist/agent-src/rules/user-interrupt-priority.md +0 -1
  51. package/dist/agent-src/skills/agent-docs-writing/SKILL.md +8 -0
  52. package/dist/agent-src/skills/ai-code-blindspots/SKILL.md +1 -0
  53. package/dist/agent-src/skills/analysis-skill-router/SKILL.md +55 -86
  54. package/dist/agent-src/skills/character-consistency/SKILL.md +7 -4
  55. package/dist/agent-src/skills/decision-record/SKILL.md +8 -0
  56. package/dist/agent-src/skills/design-intelligence/SKILL.md +27 -1
  57. package/dist/agent-src/skills/design-intelligence/data/font-pairings-reference.csv +74 -74
  58. package/dist/agent-src/skills/design-intelligence/data/manifest.json +2 -0
  59. package/dist/agent-src/skills/design-review/SKILL.md +11 -0
  60. package/dist/agent-src/skills/design-system-capture/reference/design-system-json.md +8 -0
  61. package/dist/agent-src/skills/design-tokens/scripts/tokens.ts +0 -7
  62. package/dist/agent-src/skills/existing-ui-audit/SKILL.md +34 -3
  63. package/dist/agent-src/skills/fe-design/SKILL.md +10 -0
  64. package/dist/agent-src/skills/motion-choreographer/SKILL.md +7 -4
  65. package/dist/agent-src/skills/overbuild-review-lens/SKILL.md +201 -0
  66. package/dist/agent-src/skills/performance/SKILL.md +14 -0
  67. package/dist/agent-src/skills/pixar-storyteller/SKILL.md +8 -5
  68. package/dist/agent-src/skills/scene-expander/SKILL.md +9 -6
  69. package/dist/agent-src/skills/supply-chain-intake/SKILL.md +10 -0
  70. package/dist/agent-src/skills/typography-system/SKILL.md +55 -11
  71. package/dist/agent-src/skills/ui-apply-generic/SKILL.md +180 -0
  72. package/dist/agent-src/skills/universal-project-analysis/SKILL.md +14 -37
  73. package/dist/agent-src/skills/video-director/SKILL.md +9 -6
  74. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  75. package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +162 -17
  76. package/dist/agent-src/templates/scripts/work_engine/directives/ui/design.ts +118 -0
  77. package/dist/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +49 -6
  78. package/dist/agent-src/templates/scripts/work_engine/directives/ui/review.ts +9 -5
  79. package/dist/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +182 -29
  80. package/dist/agent-src/templates/scripts/work_engine/stack/detect.ts +311 -24
  81. package/dist/agent-src/templates/scripts/work_engine/state.ts +55 -0
  82. package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
  83. package/dist/cli-delegate/{chunk-IR7P7JWA.js → chunk-A3HJ6NTI.js} +271 -59
  84. package/dist/cli-delegate/chunk-EJYIGZ3T.js +125 -0
  85. package/dist/cli-delegate/{chunk-RDGRHAAB.js → chunk-HYWSRNUX.js} +6 -86
  86. package/dist/cli-delegate/{chunk-YMFO6FDN.js → chunk-QFKQLQ7X.js} +313 -81
  87. package/dist/cli-delegate/{chunk-4SV3XGFA.js → chunk-TEGJU2JX.js} +7 -9
  88. package/dist/cli-delegate/chunk-TJIAM5FW.js +133 -0
  89. package/dist/cli-delegate/cmd_conformance.js +4 -7
  90. package/dist/cli-delegate/cmd_doctor.js +3 -5
  91. package/dist/cli-delegate/cmd_explain.js +153 -85
  92. package/dist/cli-delegate/cmd_export.js +5 -3
  93. package/dist/cli-delegate/cmd_fleet.js +2 -1
  94. package/dist/cli-delegate/cmd_preflight.js +6 -123
  95. package/dist/cli-delegate/cmd_prune.js +5 -3
  96. package/dist/cli-delegate/cmd_settings_migrate.js +5 -3
  97. package/dist/cli-delegate/cmd_sync.js +5 -3
  98. package/dist/cli-delegate/cmd_uninstall.js +6 -5
  99. package/dist/cli-delegate/cmd_validate.js +5 -3
  100. package/dist/discovery/deprecation-report.md +1 -1
  101. package/dist/discovery/discovery-manifest.json +116 -72
  102. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  103. package/dist/discovery/discovery-manifest.summary.md +3 -3
  104. package/dist/discovery/orphan-report.md +1 -1
  105. package/dist/discovery/packs.json +7 -5
  106. package/dist/discovery/trust-report.md +2 -2
  107. package/dist/discovery/workspaces.json +6 -4
  108. package/dist/hooks/dispatch.js +174 -13
  109. package/dist/install/install.mjs +605 -412
  110. package/dist/install/ruleInScope.js +123 -0
  111. package/dist/install/ruleInScope.js.map +1 -0
  112. package/dist/install/rule_scope.js +2 -2
  113. package/dist/install/rule_scope.js.map +1 -1
  114. package/dist/install/wizard-plan.js +10 -1
  115. package/dist/install/wizard-plan.js.map +1 -1
  116. package/dist/mcp/registry-manifest.json +3 -3
  117. package/dist/mcp/server.json +2 -2
  118. package/dist/mcp/server.mjs +710 -2491
  119. package/dist/router.json +1 -1
  120. package/dist/scripts/condense.js +8 -24
  121. package/dist/scripts/condense.js.map +1 -1
  122. package/docs/MIGRATION.md +1 -1
  123. package/docs/guidelines/agent-infra/agent-interaction-and-decision-quality.md +71 -0
  124. package/docs/guidelines/agent-infra/minimal-safe-diff-mechanics.md +24 -1
  125. package/docs/guidelines/code-clarity.md +9 -2
  126. package/docs/guidelines/design-fidelity-mechanics.md +58 -0
  127. package/llms.txt +63 -27
  128. package/package.json +4 -4
  129. package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +162 -17
  130. package/src/agent-src/templates/scripts/work_engine/directives/ui/design.ts +118 -0
  131. package/src/agent-src/templates/scripts/work_engine/directives/ui/polish.ts +49 -6
  132. package/src/agent-src/templates/scripts/work_engine/directives/ui/review.ts +9 -5
  133. package/src/agent-src/templates/scripts/work_engine/directives/ui/stack_bundles.ts +182 -29
  134. package/src/agent-src/templates/scripts/work_engine/stack/detect.ts +311 -24
  135. package/src/agent-src/templates/scripts/work_engine/state.ts +55 -0
  136. package/src/config/agent-settings.template.yml +8 -0
  137. package/src/install/ruleInScope.ts +133 -0
  138. package/src/install/rule_scope.ts +2 -2
  139. package/src/install/wizard-plan.ts +11 -1
  140. package/src/scripts/_lib/counted_probe.ts +142 -0
  141. package/src/scripts/_lib/global_deploy_inventory.ts +38 -2
  142. package/src/scripts/_lib/overbuild_lens_contract.ts +196 -0
  143. package/src/scripts/_lib/release_scope.ts +207 -0
  144. package/src/scripts/ai_council/stance_tally.ts +37 -2
  145. package/src/scripts/audit_auto_rules.ts +9 -9
  146. package/src/scripts/audit_likelihood.ts +3 -3
  147. package/src/scripts/audit_overlap.ts +2 -2
  148. package/src/scripts/audit_skill_overlap.ts +233 -95
  149. package/src/scripts/audit_skill_overlap_allowlist.json +4 -0
  150. package/src/scripts/build_proof.ts +15 -1
  151. package/src/scripts/check_dependency_floors.ts +16 -6
  152. package/src/scripts/check_gate_coverage.ts +6 -6
  153. package/src/scripts/check_installer_import_purity.ts +295 -0
  154. package/src/scripts/check_kernel_rule_bundle.ts +5 -4
  155. package/src/scripts/check_token_quality_golden.ts +0 -18
  156. package/src/scripts/code_graph/loader.ts +35 -6
  157. package/src/scripts/compile_router.ts +3 -8
  158. package/src/scripts/condense.ts +9 -30
  159. package/src/scripts/explain_run.ts +0 -1
  160. package/src/scripts/generate_pack_manifests.ts +3 -20
  161. package/src/scripts/hooks/block_kernel_rule_writes.ts +145 -10
  162. package/src/scripts/hooks/block_no_verify.ts +101 -0
  163. package/src/scripts/install-hooks.sh +36 -4
  164. package/src/scripts/install.ts +210 -5
  165. package/src/scripts/lint_agent_security.ts +5 -4
  166. package/src/scripts/lint_framework_leakage_allowlist.json +2 -2
  167. package/src/scripts/lint_regression.ts +92 -1
  168. package/src/scripts/lint_skill_descriptions.ts +93 -5
  169. package/src/scripts/lint_ui_stack_bundles.ts +31 -0
  170. package/src/scripts/project_thin_rules.ts +2 -2
  171. package/src/scripts/release.ts +107 -3
  172. package/src/scripts/resolve_lint_scope.ts +53 -0
  173. package/src/scripts/router_telemetry.ts +0 -5
  174. package/src/scripts/runtime_dispatcher.ts +40 -0
  175. package/src/scripts/schemas/pack.schema.json +1 -5
  176. package/src/scripts/schemas/rule.schema.json +1 -2
  177. package/src/scripts/self_review_gate.ts +15 -73
  178. package/src/scripts/skill_linter.ts +93 -11
  179. package/src/scripts/sweep_dead_scan_roots.ts +550 -0
  180. package/src/scripts/trigger_coverage.ts +11 -37
  181. package/dist/cli-delegate/chunk-7DSAMZFU.js +0 -356
  182. package/dist/cli-delegate/chunk-BJGXZNPM.js +0 -266
  183. package/dist/cli-delegate/chunk-TFG57JJM.js +0 -1969
@@ -6,7 +6,7 @@
6
6
  "$comment": "Generated by scripts/generate_pack_manifests.py and validated on load by scripts/validate_pack_yaml.py. Schema introduced in road-to-6.0.0-D Phase 0 Step 2. `dependencies` is the artefact-level include graph (skills/rules the pack's commands reference); `requires` is the pack-level hard-dependency graph (must be acyclic — enforced by scripts/lint_pack_dependencies.py).",
7
7
  "type": "object",
8
8
  "additionalProperties": false,
9
- "required": ["id", "version", "artefact_count"],
9
+ "required": ["id", "artefact_count"],
10
10
  "properties": {
11
11
  "id": {
12
12
  "type": "string",
@@ -58,10 +58,6 @@
58
58
  "trust_level_default": {
59
59
  "type": "string"
60
60
  },
61
- "version": {
62
- "type": "string",
63
- "minLength": 1
64
- },
65
61
  "artefact_count": {
66
62
  "type": "integer",
67
63
  "minimum": 0
@@ -63,14 +63,13 @@
63
63
  "properties": {
64
64
  "keyword": {"type": "string"},
65
65
  "phrase": {"type": "string"},
66
- "intent": {"type": "string"},
67
66
  "file_pattern": {"type": "string"},
68
67
  "path_prefix": {"type": "string", "description": "Literal path-prefix match pattern the host evaluates against the file the agent is editing — NOT a file reference. The rewriter leaves it verbatim. Source-of-truth rules that must fire on edits under `.agent-src.uncondensed/` keep that prefix here (the prefix ban applies only to `load_context:` and body links — see road-to-path-fixes.md P2.2 / AI-Council 2026-05-06)."},
69
68
  "command": {"type": "string"},
70
69
  "reason": {"type": "string"}
71
70
  }
72
71
  },
73
- "description": "Router activation triggers (Phase 3 of road-to-kernel-and-router.md). Forbidden on kernel rules, required on non-kernel rules. Schema: docs/contracts/rule-router.md."
72
+ "description": "Router activation triggers (Phase 3 of road-to-kernel-and-router.md). Forbidden on kernel rules, required on non-kernel rules. Five match keys: keyword, phrase, file_pattern, path_prefix, command. `intent` was removed — it never auto-matched at runtime and only gave rule authors false confidence that an activation path existed; additionalProperties:false now rejects it. Schema: docs/contracts/rule-router.md."
74
73
  },
75
74
  "routes_to": {
76
75
  "type": "array",
@@ -125,79 +125,21 @@ function changedLineCount(baseRef: string, files: string[], cwd: string = REPO_R
125
125
  }
126
126
 
127
127
  // ── Release-PR detection (road-to-feedback-9.2.0-followups Phase 3) ────
128
- /**
129
- * A release PR is detected from its own packaging diff (baseRef...HEAD,
130
- * scoped to CHANGELOG.md + package.json): an added changelog heading and an
131
- * added package.json version bump, agreeing on the same version. Pure over
132
- * the patch text — no git call — so it is unit-testable with synthetic
133
- * patches. See docs/design/release-pr-review-mode.md.
134
- */
135
- export function detectReleaseVersion(patchText: string): string | null {
136
- const addedLines = patchText
137
- .split('\n')
138
- .filter((l) => l.startsWith('+') && !l.startsWith('+++'))
139
- .map((l) => l.slice(1));
140
-
141
- let changelogVersion: string | null = null;
142
- let packageVersion: string | null = null;
143
- for (const line of addedLines) {
144
- const changelogMatch = /^##\s*\[(\d+\.\d+\.\d+)\]/.exec(line);
145
- if (changelogMatch) changelogVersion = changelogMatch[1] ?? null;
146
- const packageMatch = /^\s*"version"\s*:\s*"(\d+\.\d+\.\d+)"/.exec(line);
147
- if (packageMatch) packageVersion = packageMatch[1] ?? null;
148
- }
149
- if (changelogVersion && packageVersion && changelogVersion === packageVersion) {
150
- return changelogVersion;
151
- }
152
- return null;
153
- }
154
-
155
- function releaseDetectionPatch(baseRef: string, cwd: string): string {
156
- const r = spawnSync('git', ['diff', `${baseRef}...HEAD`, '--', 'CHANGELOG.md', 'package.json'], {
157
- cwd,
158
- encoding: 'utf8',
159
- maxBuffer: 8 * 1024 * 1024,
160
- });
161
- return (r.stdout ?? '').toString();
162
- }
163
-
164
- /** Impure wrapper: collects the packaging patch via git, then detects. */
165
- export function detectReleaseVersionFromGit(baseRef: string, cwd: string = REPO_ROOT): string | null {
166
- return detectReleaseVersion(releaseDetectionPatch(baseRef, cwd));
167
- }
168
-
169
- /** `major.minor.patch`, tolerating an optional `v` prefix; null if not semver-shaped. */
170
- function parseSemver(raw: string): [number, number, number] | null {
171
- const m = /^v?(\d+)\.(\d+)\.(\d+)$/.exec(raw.trim());
172
- if (!m) return null;
173
- return [Number(m[1]), Number(m[2]), Number(m[3])];
174
- }
175
-
176
- function semverLessThan(a: [number, number, number], b: [number, number, number]): boolean {
177
- const [aMajor, aMinor, aPatch] = a;
178
- const [bMajor, bMinor, bPatch] = b;
179
- if (aMajor !== bMajor) return aMajor < bMajor;
180
- if (aMinor !== bMinor) return aMinor < bMinor;
181
- return aPatch < bPatch;
182
- }
183
-
184
- /**
185
- * Highest semver-shaped tag strictly below `version`, or null if none. Pure
186
- * over the supplied tag list — no git call. Non-semver tags (branch-backup
187
- * names etc.) are ignored rather than throwing.
188
- */
189
- export function pickPreviousTag(version: string, tags: readonly string[]): string | null {
190
- const target = parseSemver(version);
191
- if (!target) return null;
192
- let best: { raw: string; parsed: [number, number, number] } | null = null;
193
- for (const raw of tags) {
194
- const parsed = parseSemver(raw);
195
- if (!parsed) continue;
196
- if (!semverLessThan(parsed, target)) continue;
197
- if (!best || semverLessThan(best.parsed, parsed)) best = { raw, parsed };
198
- }
199
- return best ? best.raw : null;
200
- }
128
+ //
129
+ // Moved to `_lib/release_scope.ts` so the release-aware content checks reuse
130
+ // this detector instead of re-deriving it (road-to-release-shape-honesty
131
+ // Phase 1). Imported for local use AND re-exported — a bare `export … from`
132
+ // would not bind the names in this module's scope. Public surface unchanged.
133
+ import {
134
+ detectReleaseVersionFromGit,
135
+ pickPreviousTag,
136
+ } from './_lib/release_scope.js';
137
+
138
+ export {
139
+ detectReleaseVersion,
140
+ detectReleaseVersionFromGit,
141
+ pickPreviousTag,
142
+ } from './_lib/release_scope.js';
201
143
 
202
144
  function listGitTags(cwd: string): string[] {
203
145
  const r = spawnSync('git', ['tag'], { cwd, encoding: 'utf8' });
@@ -30,7 +30,6 @@
30
30
  * 2 = errors
31
31
  * 3 = internal error / structural malice
32
32
  */
33
- import { spawnSync } from 'node:child_process';
34
33
  import * as fs from 'node:fs';
35
34
  import * as path from 'node:path';
36
35
  import process from 'node:process';
@@ -44,6 +43,9 @@ import {
44
43
  type YamlValue,
45
44
  } from './validate_frontmatter.js';
46
45
  import { artefact_roots, resolve_logical } from './_lib/agent_src.js';
46
+ import { ProbeOverflowError, runCountedProbe } from './_lib/counted_probe.js';
47
+ import { resolveContentLintScope } from './_lib/release_scope.js';
48
+ import { DeadScopeError, assertScanned } from './_lib/scan_scope.js';
47
49
 
48
50
  // __file__ equivalent for repo-root derivation (mirrors Python
49
51
  // `Path(__file__).resolve().parent.parent.parent` = <repo>/src/scripts → repo).
@@ -166,10 +168,10 @@ const DOUBLE_BLANK_PATTERN = /\n{3,}/;
166
168
  const VALID_RULE_TYPES = new Set(['always', 'auto', 'manual']);
167
169
  const VALID_RULE_SOURCES = new Set(['package', 'project']);
168
170
 
171
+ // `intent` was removed — see compile_router.ts's copy of this allowlist.
169
172
  const ROUTER_ALLOWED_TRIGGER_KEYS = new Set([
170
173
  'keyword',
171
174
  'phrase',
172
- 'intent',
173
175
  'file_pattern',
174
176
  'path_prefix',
175
177
  'command',
@@ -2428,20 +2430,42 @@ export function gather_candidate_files_under(srcRoot: string): string[] {
2428
2430
  return sortedUnique(out);
2429
2431
  }
2430
2432
 
2431
- export function gather_changed_candidate_files(root: string): string[] {
2433
+ /**
2434
+ * The scope `--changed` resolved, for the caller to report.
2435
+ *
2436
+ * Exported alongside the file list because a release PR silently widening its
2437
+ * own scope would be as opaque as the blindness it fixes — the caller prints
2438
+ * this line.
2439
+ */
2440
+ export let last_changed_scope_reason = '';
2441
+
2442
+ /**
2443
+ * Candidate files changed against the resolved base.
2444
+ *
2445
+ * The base is release-aware (`_lib/release_scope.ts`): on a release PR it
2446
+ * widens to `previous_release_tag...HEAD`, because a release PR's own diff is
2447
+ * the version bump and the changelog — everything substantive landed in the
2448
+ * commits before the cut. Scoped to the PR's own file list this returned
2449
+ * nothing on a 179-commit release, and the gate reported
2450
+ * "0 skills checked, INCONCLUSIVE" and passed.
2451
+ *
2452
+ * `since` pins the base explicitly and wins over detection.
2453
+ */
2454
+ export function gather_changed_candidate_files(root: string, since?: string | null): string[] {
2455
+ const scope = resolveContentLintScope({ cwd: root, since: since ?? null });
2456
+ last_changed_scope_reason = scope.reason;
2432
2457
  const diffCommands = [
2433
- ['git', 'diff', '--name-only', 'origin/main...HEAD'],
2458
+ ['git', 'diff', '--name-only', `${scope.base}...HEAD`],
2434
2459
  ['git', 'diff', '--name-only', '--cached', 'HEAD'],
2435
2460
  ['git', 'diff', '--name-only', 'HEAD'],
2436
2461
  ];
2437
2462
  try {
2438
2463
  let rawLines: string[] = [];
2439
2464
  for (const cmd of diffCommands) {
2440
- const result = spawnSync(cmd[0] as string, cmd.slice(1), {
2441
- cwd: root,
2442
- encoding: 'utf-8',
2443
- });
2444
- if (result.status === 0 && (result.stdout ?? '').trim()) {
2465
+ // A truncated diff would silently shrink the change set — the same
2466
+ // degrade-to-zero shape this roadmap closes elsewhere.
2467
+ const result = runCountedProbe(cmd[0] as string, cmd.slice(1), { cwd: root });
2468
+ if (result.ok && result.stdout.trim()) {
2445
2469
  rawLines = splitlines(result.stdout);
2446
2470
  break;
2447
2471
  }
@@ -2479,7 +2503,15 @@ export function gather_changed_candidate_files(root: string): string[] {
2479
2503
  }
2480
2504
  }
2481
2505
  return sortedUnique(files);
2482
- } catch {
2506
+ } catch (exc) {
2507
+ // An overflowed diff is not an empty diff. Everything else (a shallow
2508
+ // clone with no `origin/main`, a detached worktree) legitimately has no
2509
+ // resolvable change set and keeps the historical empty-list degrade.
2510
+ if (exc instanceof ProbeOverflowError) {
2511
+ throw exc;
2512
+ }
2513
+ last_changed_scope_reason =
2514
+ `change set unresolvable (${exc instanceof Error ? exc.message : String(exc)})`;
2483
2515
  return [];
2484
2516
  }
2485
2517
  }
@@ -4082,6 +4114,8 @@ interface Args {
4082
4114
  report: boolean;
4083
4115
  repoRoot: string;
4084
4116
  quiet: boolean;
4117
+ /** Explicit diff base for `--changed`; wins over release detection. */
4118
+ since: string | null;
4085
4119
  }
4086
4120
 
4087
4121
  export function parse_args(argv: string[]): Args {
@@ -4095,6 +4129,7 @@ export function parse_args(argv: string[]): Args {
4095
4129
  condensationQuality: false,
4096
4130
  strictWarnings: false,
4097
4131
  report: false,
4132
+ since: null,
4098
4133
  repoRoot: '.',
4099
4134
  quiet: false,
4100
4135
  };
@@ -4104,6 +4139,15 @@ export function parse_args(argv: string[]): Args {
4104
4139
  args.all = true;
4105
4140
  } else if (a === '--changed') {
4106
4141
  args.changed = true;
4142
+ } else if (a === '--since') {
4143
+ const v = argv[i + 1];
4144
+ i += 1;
4145
+ if (v === undefined || v.startsWith('--')) {
4146
+ throw new ArgError('argument --since: expected a git ref');
4147
+ }
4148
+ args.since = v;
4149
+ } else if (a.startsWith('--since=')) {
4150
+ args.since = a.slice('--since='.length);
4107
4151
  } else if (a === '--format') {
4108
4152
  const v = argv[i + 1];
4109
4153
  i += 1;
@@ -4143,6 +4187,21 @@ export function parse_args(argv: string[]): Args {
4143
4187
 
4144
4188
  class ArgError extends Error {}
4145
4189
 
4190
+ /**
4191
+ * True when an empty change set is a defect rather than a legitimate scope.
4192
+ *
4193
+ * Only on a release PR whose scope actually widened to a tag span: a release
4194
+ * that changed no skill and no rule is possible in principle, but a *resolved*
4195
+ * span that matched nothing is the "0 skills checked, INCONCLUSIVE" shape this
4196
+ * gate exists to stop. A release whose previous tag could not be resolved is
4197
+ * excluded — that scope never widened, so failing it would punish a shallow
4198
+ * clone for the wrong reason.
4199
+ */
4200
+ function _release_scope_requires_findings(root: string, since?: string | null): boolean {
4201
+ const scope = resolveContentLintScope({ cwd: root, since: since ?? null });
4202
+ return scope.isRelease && scope.previousTag !== null;
4203
+ }
4204
+
4146
4205
  export function main(argv: string[]): number {
4147
4206
  let args: Args;
4148
4207
  try {
@@ -4162,7 +4221,8 @@ export function main(argv: string[]): number {
4162
4221
  paths.push(...gather_all_candidate_files(root));
4163
4222
  }
4164
4223
  if (args.changed) {
4165
- paths.push(...gather_changed_candidate_files(root));
4224
+ paths.push(...gather_changed_candidate_files(root, args.since));
4225
+ process.stderr.write(`skill_linter --changed: ${last_changed_scope_reason}\n`);
4166
4226
  }
4167
4227
  for (const raw of args.paths) {
4168
4228
  const p = path.isAbsolute(raw) ? raw : path.resolve(root, raw);
@@ -4183,6 +4243,28 @@ export function main(argv: string[]): number {
4183
4243
  } else if (args.format === 'json') {
4184
4244
  process.stdout.write(`${format_json([])}\n`);
4185
4245
  }
4246
+ // A release PR that resolved a real tag span and still matched
4247
+ // nothing has not passed — it examined nothing. Mirrors the
4248
+ // scan-scope regime the other gates already adopted
4249
+ // (`_lib/scan_scope.ts`; exit 2, as `check_iron_law_prominence`).
4250
+ // Ordinary PRs keep the historical exit 0: a change set with no
4251
+ // skill or rule in it is a legitimate empty scope, not a dead one.
4252
+ if (args.changed && _release_scope_requires_findings(root, args.since)) {
4253
+ try {
4254
+ assertScanned({
4255
+ gate: 'skill_linter --changed (release PR)',
4256
+ scanned: 0,
4257
+ units: 'skill/rule files',
4258
+ roots: [last_changed_scope_reason],
4259
+ });
4260
+ } catch (exc) {
4261
+ if (exc instanceof DeadScopeError) {
4262
+ process.stderr.write(`❌ ${exc.message}\n`);
4263
+ return 2;
4264
+ }
4265
+ throw exc;
4266
+ }
4267
+ }
4186
4268
  process.stderr.write('No matching skill/rule files found.\n');
4187
4269
  return 0;
4188
4270
  }