@event4u/agent-config 9.20.0 → 9.22.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 (239) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +86 -0
  3. package/dist/agent-src/commands/worktree/cleanup.md +17 -0
  4. package/dist/agent-src/commands/worktree/status.md +6 -0
  5. package/dist/agent-src/skills/worktree-lifecycle/SKILL.md +22 -0
  6. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  7. package/dist/cli-delegate/{chunk-XGA2NTU4.js → chunk-3EWYC3YK.js} +40 -0
  8. package/dist/cli-delegate/cmd_handoff.js +1 -1
  9. package/dist/cli-delegate/cmd_memory_get.js +1 -1
  10. package/dist/cli-delegate/cmd_route_audit.js +1 -1
  11. package/dist/discovery/deprecation-report.md +1 -1
  12. package/dist/discovery/discovery-manifest.json +5 -5
  13. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  14. package/dist/discovery/discovery-manifest.summary.md +1 -1
  15. package/dist/discovery/orphan-report.md +1 -1
  16. package/dist/discovery/packs.json +2 -2
  17. package/dist/discovery/trust-report.md +1 -1
  18. package/dist/discovery/workspaces.json +2 -2
  19. package/dist/hooks/dispatch.js +58 -2
  20. package/dist/mcp/registry-manifest.json +2 -2
  21. package/dist/mcp/server.json +2 -2
  22. package/dist/mcp/server.mjs +47 -29
  23. package/package.json +1 -1
  24. package/src/config/gate-coverage.yml +22 -4
  25. package/src/config/gate-violation-baselines.json +3 -8
  26. package/src/scripts/_lib/bench_ab_activation.ts +368 -0
  27. package/src/scripts/_lib/gate_population.ts +70 -0
  28. package/src/scripts/_lib/roadmap_granularity.ts +157 -0
  29. package/src/scripts/_lib/scan_scope.ts +44 -0
  30. package/src/scripts/_lib/stdin.ts +82 -0
  31. package/src/scripts/audit_adr_coverage.ts +38 -0
  32. package/src/scripts/audit_auto_rules.ts +17 -3
  33. package/src/scripts/audit_command_surface.ts +51 -0
  34. package/src/scripts/audit_initial_context.ts +39 -3
  35. package/src/scripts/audit_mcp_tools.ts +25 -0
  36. package/src/scripts/audit_overlap.ts +24 -1
  37. package/src/scripts/audit_skill_descriptions.ts +19 -0
  38. package/src/scripts/{verify_before_complete_hook.ts → before_complete_hook.ts} +16 -3
  39. package/src/scripts/bench_ab_task_runner.ts +18 -0
  40. package/src/scripts/bench_ab_v2_run.ts +147 -6
  41. package/src/scripts/bench_ab_v2_stats.ts +74 -2
  42. package/src/scripts/check_adr_frontmatter.ts +23 -3
  43. package/src/scripts/check_always_budget.ts +24 -0
  44. package/src/scripts/check_artefact_checksums.ts +24 -1
  45. package/src/scripts/check_artefact_count_messaging.ts +20 -0
  46. package/src/scripts/check_augmentignore.ts +22 -15
  47. package/src/scripts/check_backstop_debt.ts +22 -0
  48. package/src/scripts/check_beta_review_markers.ts +22 -1
  49. package/src/scripts/check_bridge_derivation.ts +22 -1
  50. package/src/scripts/check_bundle_path_leakage.ts +26 -0
  51. package/src/scripts/check_ci_strict_superset.ts +21 -3
  52. package/src/scripts/check_claims.ts +22 -0
  53. package/src/scripts/check_cli_registry_budget_sync.ts +33 -4
  54. package/src/scripts/check_cluster_patterns.ts +26 -4
  55. package/src/scripts/check_command_count_messaging.ts +20 -4
  56. package/src/scripts/check_comparison.ts +18 -0
  57. package/src/scripts/check_condensed_paths.ts +23 -2
  58. package/src/scripts/check_consumer_scope_flip.ts +21 -2
  59. package/src/scripts/check_council_config_location.ts +20 -0
  60. package/src/scripts/check_council_layout.ts +22 -2
  61. package/src/scripts/check_council_references.ts +23 -1
  62. package/src/scripts/check_dependency_floors.ts +19 -3
  63. package/src/scripts/check_discovery_determinism.ts +26 -0
  64. package/src/scripts/check_enforcement_coverage.ts +20 -0
  65. package/src/scripts/check_evaluator_budgets.ts +23 -0
  66. package/src/scripts/check_finding_dispositions.ts +29 -0
  67. package/src/scripts/check_gate_coverage.ts +63 -22
  68. package/src/scripts/check_gate_paths.ts +30 -5
  69. package/src/scripts/check_generated_artefact_headers.ts +21 -0
  70. package/src/scripts/check_generator_output_coverage.ts +23 -0
  71. package/src/scripts/check_gitignore_freshness.ts +23 -0
  72. package/src/scripts/check_host_loadability.ts +34 -8
  73. package/src/scripts/check_installer_import_purity.ts +22 -8
  74. package/src/scripts/check_kernel_prefix_stability.ts +19 -1
  75. package/src/scripts/check_kernel_rule_bundle.ts +23 -0
  76. package/src/scripts/check_knowledge_cards.ts +13 -0
  77. package/src/scripts/check_knowledge_pages.ts +23 -0
  78. package/src/scripts/check_knowledge_sharing.ts +27 -0
  79. package/src/scripts/check_md_language.ts +24 -0
  80. package/src/scripts/check_media_deps.ts +23 -0
  81. package/src/scripts/check_memory.ts +26 -1
  82. package/src/scripts/check_memory_contradiction.ts +16 -1
  83. package/src/scripts/check_memory_proposal.ts +15 -0
  84. package/src/scripts/check_memory_similarity.ts +16 -0
  85. package/src/scripts/check_module_management_neutral.ts +19 -11
  86. package/src/scripts/check_no_conflict_markers.ts +22 -1
  87. package/src/scripts/check_no_external_sources.ts +26 -1
  88. package/src/scripts/check_no_local_settings_committed.ts +37 -10
  89. package/src/scripts/check_no_new_legacy_path.ts +30 -1
  90. package/src/scripts/check_one_off_location.ts +43 -0
  91. package/src/scripts/check_originality_freshness.ts +25 -1
  92. package/src/scripts/check_overlay_cascade_subdirs.ts +21 -0
  93. package/src/scripts/check_pack_size.ts +22 -0
  94. package/src/scripts/check_preamble_payload_budget.ts +35 -5
  95. package/src/scripts/check_proposal.ts +24 -1
  96. package/src/scripts/check_public_catalog_links.ts +27 -0
  97. package/src/scripts/check_public_links.ts +19 -0
  98. package/src/scripts/check_quality_regression.ts +19 -0
  99. package/src/scripts/check_reach_channels.ts +44 -0
  100. package/src/scripts/check_reach_staleness.ts +27 -0
  101. package/src/scripts/check_release_adjacent_health.ts +28 -0
  102. package/src/scripts/check_release_highlights.ts +34 -0
  103. package/src/scripts/check_release_includes_discovery.ts +25 -6
  104. package/src/scripts/check_release_pr_shape.ts +18 -1
  105. package/src/scripts/check_release_published.ts +21 -0
  106. package/src/scripts/check_release_surface_equality.ts +22 -0
  107. package/src/scripts/check_release_trunk_sync.ts +19 -0
  108. package/src/scripts/check_reply_consistency.ts +22 -0
  109. package/src/scripts/check_roadmap_trackable.ts +20 -0
  110. package/src/scripts/check_role_doc_links.ts +23 -0
  111. package/src/scripts/check_rule_invariants.ts +14 -0
  112. package/src/scripts/check_secret_leak.ts +47 -0
  113. package/src/scripts/check_skill_gaps.ts +32 -0
  114. package/src/scripts/check_skill_requires.ts +19 -0
  115. package/src/scripts/check_source_pointer_freshness.ts +20 -0
  116. package/src/scripts/check_structural_breaking.ts +29 -2
  117. package/src/scripts/check_surface_tiers.ts +34 -0
  118. package/src/scripts/check_template_pin_drift.ts +23 -1
  119. package/src/scripts/check_test_coverage_diff.ts +17 -1
  120. package/src/scripts/check_token_optimizer_freshness.ts +18 -3
  121. package/src/scripts/check_token_quality_golden.ts +21 -0
  122. package/src/scripts/check_token_regression.ts +25 -0
  123. package/src/scripts/check_tracked_but_ignored.ts +26 -0
  124. package/src/scripts/check_trigger_eval_presence.ts +23 -1
  125. package/src/scripts/check_trigger_evals.ts +36 -0
  126. package/src/scripts/check_trunk_drift.ts +23 -0
  127. package/src/scripts/check_update_banner.ts +24 -0
  128. package/src/scripts/gate_scope_classify.ts +513 -0
  129. package/src/scripts/hook_manifest.yaml +1 -1
  130. package/src/scripts/hooks/block_kernel_rule_writes.ts +1 -1
  131. package/src/scripts/hooks/concern_registry.ts +2 -2
  132. package/src/scripts/lint_agent_security.ts +19 -0
  133. package/src/scripts/lint_agent_skill_names.ts +30 -0
  134. package/src/scripts/lint_agents_layout.ts +20 -0
  135. package/src/scripts/lint_agents_md.ts +20 -0
  136. package/src/scripts/lint_archived_skills.ts +24 -3
  137. package/src/scripts/lint_behavioural_eval_freshness.ts +29 -3
  138. package/src/scripts/lint_bench_ab.ts +25 -0
  139. package/src/scripts/lint_bench_corpus.ts +20 -4
  140. package/src/scripts/lint_breaking_changes_index.ts +24 -0
  141. package/src/scripts/lint_budget_ownership.ts +20 -3
  142. package/src/scripts/lint_changelog_rollback.ts +24 -0
  143. package/src/scripts/lint_command_flow_coverage.ts +22 -0
  144. package/src/scripts/lint_command_tiers.ts +29 -5
  145. package/src/scripts/lint_commit_subjects.ts +15 -0
  146. package/src/scripts/lint_confusables.ts +24 -0
  147. package/src/scripts/lint_consumer_internal_refs.ts +24 -0
  148. package/src/scripts/lint_design_quality.ts +22 -0
  149. package/src/scripts/lint_design_slop.ts +23 -0
  150. package/src/scripts/lint_discovery_manifest.ts +23 -0
  151. package/src/scripts/lint_discovery_vocabulary.ts +19 -0
  152. package/src/scripts/lint_documented_commands.ts +23 -0
  153. package/src/scripts/lint_empty_roadmaps.ts +23 -0
  154. package/src/scripts/lint_eval_fixture_citations.ts +19 -0
  155. package/src/scripts/lint_eval_freshness.ts +35 -0
  156. package/src/scripts/lint_examples.ts +18 -3
  157. package/src/scripts/lint_explain_trace.ts +23 -0
  158. package/src/scripts/lint_featured_skills.ts +26 -1
  159. package/src/scripts/lint_flows.ts +19 -0
  160. package/src/scripts/lint_frontmatter_boilerplate.ts +20 -0
  161. package/src/scripts/lint_ghostwriter_source.ts +26 -6
  162. package/src/scripts/lint_glama_drift.ts +24 -0
  163. package/src/scripts/lint_global_paths.ts +24 -0
  164. package/src/scripts/lint_governed_writes.ts +20 -0
  165. package/src/scripts/lint_hidden_unicode.ts +25 -0
  166. package/src/scripts/lint_hook_concern_budget.ts +39 -6
  167. package/src/scripts/lint_hook_manifest.ts +22 -0
  168. package/src/scripts/lint_instruction_smuggling.ts +23 -0
  169. package/src/scripts/lint_knowledge_scale.ts +32 -2
  170. package/src/scripts/lint_legal_pack.ts +31 -0
  171. package/src/scripts/lint_marketplace.ts +26 -0
  172. package/src/scripts/lint_marketplace_install_completeness.ts +23 -0
  173. package/src/scripts/lint_mcp_config_security.ts +24 -0
  174. package/src/scripts/lint_mcp_registry_manifest.ts +22 -1
  175. package/src/scripts/lint_memory_tripwire.ts +35 -0
  176. package/src/scripts/lint_missions.ts +23 -7
  177. package/src/scripts/lint_model_tier_coverage.ts +18 -0
  178. package/src/scripts/lint_namespace_collisions.ts +19 -0
  179. package/src/scripts/lint_no_new_atomic_commands.ts +33 -0
  180. package/src/scripts/lint_no_python_twin_rationale.ts +18 -0
  181. package/src/scripts/lint_one_off_age.ts +38 -2
  182. package/src/scripts/lint_orchestration_dsl.ts +23 -3
  183. package/src/scripts/lint_orchestrator_auto_detect.ts +19 -12
  184. package/src/scripts/lint_originality.ts +22 -4
  185. package/src/scripts/lint_originality_shingles.ts +28 -3
  186. package/src/scripts/lint_output_slop.ts +19 -0
  187. package/src/scripts/lint_override_kernel_guard.ts +30 -0
  188. package/src/scripts/lint_pack_first_win.ts +23 -0
  189. package/src/scripts/lint_pack_risk_class.ts +19 -3
  190. package/src/scripts/lint_persistence.ts +19 -0
  191. package/src/scripts/lint_persona_governance.ts +18 -3
  192. package/src/scripts/lint_positioning.ts +23 -0
  193. package/src/scripts/lint_pre_migration_refs.ts +24 -0
  194. package/src/scripts/lint_profile_overlay_set_only.ts +24 -0
  195. package/src/scripts/lint_provenance.ts +21 -0
  196. package/src/scripts/lint_provenance_vocabulary.ts +24 -1
  197. package/src/scripts/lint_readme_jargon.ts +21 -3
  198. package/src/scripts/lint_readme_serial_comma.ts +21 -3
  199. package/src/scripts/lint_readme_size.ts +21 -3
  200. package/src/scripts/lint_regression.ts +28 -0
  201. package/src/scripts/lint_roadmap_blockers.ts +43 -0
  202. package/src/scripts/lint_roadmap_ci_steps.ts +45 -0
  203. package/src/scripts/lint_roadmap_complexity.ts +47 -0
  204. package/src/scripts/lint_roadmap_later_disposition.ts +23 -0
  205. package/src/scripts/lint_rule_interactions.ts +16 -2
  206. package/src/scripts/lint_rule_tiers.ts +19 -6
  207. package/src/scripts/lint_showcase_sessions.ts +19 -3
  208. package/src/scripts/lint_skill_descriptions.ts +19 -0
  209. package/src/scripts/lint_skill_frontmatter_safety.ts +24 -0
  210. package/src/scripts/lint_skill_originality.ts +18 -3
  211. package/src/scripts/lint_skill_scripts_readonly.ts +20 -0
  212. package/src/scripts/lint_skill_tools.ts +84 -145
  213. package/src/scripts/lint_spawn_payload.ts +27 -1
  214. package/src/scripts/lint_store_boundary.ts +19 -0
  215. package/src/scripts/lint_subagent_determinism.ts +23 -1
  216. package/src/scripts/lint_supported_tools_matrix.ts +18 -0
  217. package/src/scripts/lint_surface_matrix.ts +19 -3
  218. package/src/scripts/lint_ticket_buildable.ts +23 -0
  219. package/src/scripts/lint_topics_yaml.ts +19 -12
  220. package/src/scripts/lint_trigger_precision.ts +27 -0
  221. package/src/scripts/lint_trust_coherence.ts +21 -0
  222. package/src/scripts/lint_ui_stack_bundles.ts +17 -3
  223. package/src/scripts/lint_value_dashboard.ts +19 -3
  224. package/src/scripts/lint_versioned_cache.ts +19 -1
  225. package/src/scripts/lint_workflow_security.ts +22 -14
  226. package/src/scripts/pr_url_reminder_hook.ts +1 -1
  227. package/src/scripts/skill_collision_clusters.ts +37 -3
  228. package/src/scripts/skill_discovery.ts +20 -0
  229. package/src/scripts/skill_eval_coverage.ts +22 -0
  230. package/src/scripts/skill_overlap.ts +32 -6
  231. package/src/scripts/skill_preview.ts +31 -0
  232. package/src/scripts/skill_trigger_eval.ts +18 -0
  233. package/src/scripts/skill_usage_collect.ts +21 -0
  234. package/src/scripts/skill_usage_report.ts +21 -0
  235. package/src/scripts/sweep_dead_scan_roots.ts +11 -3
  236. package/src/scripts/verify_physical_move.ts +24 -0
  237. package/src/scripts/worktree_cleanup_check.ts +319 -4
  238. package/src/scripts/check_bite_sized_granularity.ts +0 -116
  239. package/src/scripts/lint_workspace_boundary.ts +0 -267
@@ -43,6 +43,8 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
43
43
 
44
44
  import * as yaml from 'js-yaml';
45
45
 
46
+ import { assertScanned, DeadScopeError } from './_lib/scan_scope.js';
47
+
46
48
  const _HERE = fileURLToPath(import.meta.url);
47
49
  const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
48
50
  const CORPUS = path.join(REPO_ROOT, 'internal/bench/corpora/token-quality-golden.yaml');
@@ -350,6 +352,25 @@ function main(argv: string[]): number {
350
352
  // Structure (incl. the fires-check) always validates against the FULL
351
353
  // universe — a maintainer-rule tag is not "unknown" under consumer scope.
352
354
  const report = validate(corpus, ruleIds, router);
355
+ // `tasks: []` parses, validates clean, and covers nothing — and without
356
+ // `--require-complete` an uncovered universe is only a ⚠️, so an emptied
357
+ // corpus exits 0 while proving nothing about any rule.
358
+ try {
359
+ assertScanned({
360
+ gate: 'check_token_quality_golden',
361
+ scanned: report.task_count,
362
+ units: 'golden task(s)',
363
+ roots: [path.relative(REPO_ROOT, CORPUS)],
364
+ });
365
+ } catch (e) {
366
+ if (e instanceof DeadScopeError) {
367
+ // 2 = invalid corpus; 1 is reserved for an unreadable file, and this one
368
+ // read and parsed fine.
369
+ process.stderr.write(`❌ ${e.message}\n`);
370
+ return 2;
371
+ }
372
+ throw e;
373
+ }
353
374
 
354
375
  // Coverage accounting runs against the SCOPED universe: which flip is
355
376
  // being gated decides what "complete" means.
@@ -41,6 +41,8 @@ import * as path from 'node:path';
41
41
  import process from 'node:process';
42
42
  import { fileURLToPath, pathToFileURL } from 'node:url';
43
43
 
44
+ import { assertScanned, DeadScopeError } from './_lib/scan_scope.js';
45
+
44
46
  const _HERE = fileURLToPath(import.meta.url);
45
47
  // REPO_ROOT = <repo>/src/scripts/<file> → up two.
46
48
  const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
@@ -216,6 +218,29 @@ function main(argv: string[]): number {
216
218
  }
217
219
  const current = extract_metrics(report);
218
220
 
221
+ // `_num` returns 0 both for "absent from the report" and for "genuinely
222
+ // zero", and no gated surface is ever legitimately zero tokens — so a
223
+ // renamed report key reads as 0, every pct goes negative against the
224
+ // baseline, and the gate reports ✅ while measuring nothing. Counting the
225
+ // metrics that resolved is counting units READ, not units passing. Asserted
226
+ // BEFORE --update-baseline so a report of zeros can never be recorded as the
227
+ // baseline every later run compares against. Exit 1 (the file/argument
228
+ // error class — the gate could not run); 2 means a real regression.
229
+ try {
230
+ assertScanned({
231
+ gate: 'check_token_regression',
232
+ scanned: Object.values(current.metrics).filter((v) => v > 0).length,
233
+ units: 'gated metric(s)',
234
+ roots: [path.relative(REPO_ROOT, PROJECTION_REPORT)],
235
+ });
236
+ } catch (err) {
237
+ if (err instanceof DeadScopeError) {
238
+ process.stderr.write(`❌ ${err.message}\n`);
239
+ return 1;
240
+ }
241
+ throw err;
242
+ }
243
+
219
244
  if (updateBaseline) {
220
245
  const payload = JSON.stringify(current, null, 2) + '\n';
221
246
  fs.writeFileSync(BASELINE_FILE, payload);
@@ -19,14 +19,23 @@ import * as fs from 'node:fs';
19
19
  import { fileURLToPath, pathToFileURL } from 'node:url';
20
20
  import * as path from 'node:path';
21
21
 
22
+ import { assertScanned, DeadScopeError } from './_lib/scan_scope.js';
23
+
22
24
  const _HERE = fileURLToPath(import.meta.url);
23
25
 
24
26
  function main(argv?: readonly string[]): number {
25
27
  const args = argv ?? process.argv.slice(2);
26
28
  const quiet = args.includes('--quiet');
27
29
 
30
+ let tracked: string;
28
31
  let output: string;
29
32
  try {
33
+ // The corpus is the tracked index, not the `-ci` result — that result IS
34
+ // the finding list, so counting it would count violations. Read the index
35
+ // itself: `git ls-files` is path-scoped to the cwd, so running from a
36
+ // subtree with nothing tracked yields an empty index AND an empty `-ci`
37
+ // result, and the gate prints its green line having examined nothing.
38
+ tracked = execSync('git ls-files', { encoding: 'utf-8', cwd: process.cwd() }).trim();
30
39
  output = execSync('git ls-files -ci --exclude-standard', {
31
40
  encoding: 'utf-8',
32
41
  cwd: process.cwd(),
@@ -36,6 +45,23 @@ function main(argv?: readonly string[]): number {
36
45
  return 2;
37
46
  }
38
47
 
48
+ try {
49
+ assertScanned({
50
+ gate: 'check_tracked_but_ignored',
51
+ scanned: tracked ? tracked.split('\n').filter(Boolean).length : 0,
52
+ units: 'tracked file(s)',
53
+ roots: ['git ls-files (cwd-scoped index)'],
54
+ });
55
+ } catch (exc) {
56
+ if (exc instanceof DeadScopeError) {
57
+ // 2 = "this gate could not run" (its git-unavailable code), never 1,
58
+ // which asserts the stronger claim that ignored-but-tracked files exist.
59
+ process.stderr.write(`❌ ${exc.message}\n`);
60
+ return 2;
61
+ }
62
+ throw exc;
63
+ }
64
+
39
65
  if (!output) {
40
66
  if (!quiet) {
41
67
  process.stdout.write(`✅ No tracked-but-ignored files found.\n`);
@@ -26,6 +26,8 @@ import * as fs from 'node:fs';
26
26
  import * as path from 'node:path';
27
27
  import { fileURLToPath, pathToFileURL } from 'node:url';
28
28
 
29
+ import { assertScanned, DeadScopeError } from './_lib/scan_scope.js';
30
+
29
31
  const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
30
32
  const SKILLS_DIR = path.join(REPO_ROOT, 'src', 'skills');
31
33
  const ALLOWLIST_PATH = path.join(REPO_ROOT, 'src', 'scripts', 'trigger_eval_grandfather.json');
@@ -128,7 +130,27 @@ function main(): number {
128
130
  return 2;
129
131
  }
130
132
 
131
- const { errors, covered, total } = check(list_skill_dirs(SKILLS_DIR), allowlist.skills);
133
+ // The ratchet only bites per skill directory found: an empty (or moved)
134
+ // skills root makes every rule in `check` iterate nothing, and the ✅ line
135
+ // reads "0/0 skills carry evals/triggers.json" — a passing presence gate
136
+ // over no skills at all.
137
+ const skills = list_skill_dirs(SKILLS_DIR);
138
+ try {
139
+ assertScanned({
140
+ gate: 'check_trigger_eval_presence',
141
+ scanned: skills.length,
142
+ units: 'skill director(ies)',
143
+ roots: ['src/skills'],
144
+ });
145
+ } catch (exc) {
146
+ if (exc instanceof DeadScopeError) {
147
+ process.stderr.write(`❌ ${exc.message}\n`);
148
+ return 1;
149
+ }
150
+ throw exc;
151
+ }
152
+
153
+ const { errors, covered, total } = check(skills, allowlist.skills);
132
154
 
133
155
  if (errors.length) {
134
156
  process.stderr.write('❌ check-trigger-eval-presence: presence-ratchet violation(s):\n');
@@ -27,6 +27,8 @@ import * as fs from 'node:fs';
27
27
  import * as path from 'node:path';
28
28
  import { fileURLToPath, pathToFileURL } from 'node:url';
29
29
 
30
+ import { assertScanned, DeadScopeError } from './_lib/scan_scope.js';
31
+
30
32
  const REPO_ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', '..');
31
33
  const GLOB = 'src/skills/*/evals/triggers.json';
32
34
  const MAX_AGE_DAYS = 90;
@@ -276,6 +278,20 @@ function _isFile(p: string): boolean {
276
278
  }
277
279
  }
278
280
 
281
+ /** Immediate `src/skills/*` directories — the population `_glob_triggers` filters. */
282
+ function _skill_dirs(root: string): string[] {
283
+ const skillsDir = path.join(root, 'src', 'skills');
284
+ let entries: fs.Dirent[];
285
+ try {
286
+ entries = fs.readdirSync(skillsDir, { withFileTypes: true });
287
+ } catch {
288
+ return [];
289
+ }
290
+ return entries
291
+ .filter((ent) => ent.isDirectory() || ent.isSymbolicLink())
292
+ .map((ent) => path.join(skillsDir, ent.name));
293
+ }
294
+
279
295
  /** Component-wise path comparison matching PosixPath ordering. */
280
296
  function _pathSort(a: string, b: string): number {
281
297
  const pa = a.split(path.sep);
@@ -342,6 +358,26 @@ function main(): number {
342
358
  return 2;
343
359
  }
344
360
 
361
+ // The scanned unit is the skill directory, not the triggers.json subset:
362
+ // whether a given skill ships evals is `check_trigger_eval_presence`'s
363
+ // ratchet, so zero trigger sets under a populated tree is that gate's
364
+ // finding, not this one's. Zero skill DIRECTORIES is this gate's blindness
365
+ // — the ✅ line then reads "0 trigger set(s) fresh + valid".
366
+ try {
367
+ assertScanned({
368
+ gate: 'check_trigger_evals',
369
+ scanned: _skill_dirs(REPO_ROOT).length,
370
+ units: 'skill director(ies)',
371
+ roots: ['src/skills'],
372
+ });
373
+ } catch (exc) {
374
+ if (exc instanceof DeadScopeError) {
375
+ process.stderr.write(`❌ ${exc.message}\n`);
376
+ return 1;
377
+ }
378
+ throw exc;
379
+ }
380
+
345
381
  const files = _glob_triggers(REPO_ROOT);
346
382
  const errors: string[] = [];
347
383
  for (const p of files) {
@@ -16,6 +16,8 @@ import * as fs from 'node:fs';
16
16
  import { fileURLToPath, pathToFileURL } from 'node:url';
17
17
  import * as path from 'node:path';
18
18
 
19
+ import { assertScanned, DeadScopeError } from './_lib/scan_scope.js';
20
+
19
21
  export type DriftInput = {
20
22
  /** True when origin/main is an ancestor of HEAD (branch contains trunk tip). */
21
23
  trunkIsAncestor: boolean;
@@ -42,8 +44,13 @@ function _git(args: string[]): string {
42
44
 
43
45
  export function main(): number {
44
46
  let input: DriftInput;
47
+ let trunkCommits = 0;
45
48
  try {
46
49
  _git(['fetch', 'origin', 'main', '--quiet']);
50
+ // The comparison is only meaningful against a trunk that has history.
51
+ // An origin/main pointing at an empty/orphan ref answers "0 behind" and
52
+ // scores the branch up to date without ever having read a trunk commit.
53
+ trunkCommits = Number(_git(['rev-list', '--count', 'origin/main']) || '0');
47
54
  let isAncestor = true;
48
55
  try {
49
56
  execFileSync('git', ['merge-base', '--is-ancestor', 'origin/main', 'HEAD'], { stdio: 'ignore' });
@@ -56,6 +63,22 @@ export function main(): number {
56
63
  process.stdout.write(`⚠️ trunk-drift check skipped (no origin/main or git error): ${String(e)}\n`);
57
64
  return 0; // advisory — never block on a missing remote
58
65
  }
66
+ try {
67
+ assertScanned({
68
+ gate: 'check_trunk_drift',
69
+ scanned: trunkCommits,
70
+ units: 'trunk commit(s)',
71
+ roots: ['origin/main'],
72
+ });
73
+ } catch (exc) {
74
+ if (exc instanceof DeadScopeError) {
75
+ // 1 is the only failure code; 0 means pass/advisory-skip, so a dead
76
+ // scope must not borrow it — a gate that read no trunk has not passed.
77
+ process.stderr.write(`❌ ${exc.message}\n`);
78
+ return 1;
79
+ }
80
+ throw exc;
81
+ }
59
82
  const v = driftVerdict(input);
60
83
  process.stdout.write(`${v.message}\n`);
61
84
  return v.ok ? 0 : 1;
@@ -22,6 +22,7 @@ import * as path from 'node:path';
22
22
  import { fileURLToPath, pathToFileURL } from 'node:url';
23
23
 
24
24
  import * as agent_settings from './_lib/agent_settings.js';
25
+ import { assertWatchlistResolves, DeadScopeError } from './_lib/scan_scope.js';
25
26
  import * as update_check from './_lib/update_check.js';
26
27
 
27
28
  const _HERE = fileURLToPath(import.meta.url);
@@ -107,6 +108,29 @@ async function main(argv: readonly string[] = process.argv.slice(2)): Promise<nu
107
108
  return 0;
108
109
  }
109
110
 
111
+ if (!args.installed_version) {
112
+ // Without the flag, `$PACKAGE_ROOT/package.json` IS the whole input.
113
+ // `_read_installed_version` swallows a missing or unparseable file into
114
+ // '' and the banner then simply never fires — indistinguishable from
115
+ // "you are up to date". The exit code stays 0 on purpose: "never
116
+ // raises, never exits non-zero" is this wrapper's pinned CLI contract,
117
+ // so the only loudness available is stderr, which is where the banner
118
+ // itself goes anyway.
119
+ try {
120
+ assertWatchlistResolves({
121
+ gate: 'check_update_banner',
122
+ candidates: ['package.json'],
123
+ repoRoot: ROOT,
124
+ });
125
+ } catch (err) {
126
+ if (err instanceof DeadScopeError) {
127
+ process.stderr.write(`❌ ${err.message}\n`);
128
+ return 0;
129
+ }
130
+ throw err;
131
+ }
132
+ }
133
+
110
134
  const installed = args.installed_version || _read_installed_version(ROOT);
111
135
  if (!installed) {
112
136
  return 0;