@event4u/agent-config 14.6.0 → 14.8.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 (258) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +255 -145
  3. package/README.md +1 -1
  4. package/dist/agent-src/commands/analyze/inbox.md +264 -10
  5. package/dist/agent-src/commands/analyze.md +1 -1
  6. package/dist/agent-src/commands/evals/git-pr-merge.json +13 -0
  7. package/dist/agent-src/commands/evals/roadmap.json +17 -1
  8. package/dist/agent-src/commands/pr/create.md +14 -2
  9. package/dist/agent-src/commands/pr/merge.md +379 -0
  10. package/dist/agent-src/commands/roadmap/process-full.md +272 -32
  11. package/dist/agent-src/commands/roadmap.md +1 -1
  12. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +46 -36
  13. package/dist/agent-src/contexts/execution/autonomy-mechanics.md +37 -2
  14. package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +95 -2
  15. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +289 -106
  16. package/dist/agent-src/rules/artifact-drafting-protocol.md +5 -1
  17. package/dist/agent-src/rules/context-hygiene.md +2 -0
  18. package/dist/agent-src/rules/decision-revisit-gate.md +67 -32
  19. package/dist/agent-src/rules/fix-what-you-see.md +108 -0
  20. package/dist/agent-src/rules/no-roadmap-references.md +0 -5
  21. package/dist/agent-src/rules/recurring-criticism.md +110 -0
  22. package/dist/agent-src/rules/roadmap-progress-sync.md +20 -1
  23. package/dist/agent-src/rules/rule-type-governance.md +0 -2
  24. package/dist/agent-src/rules/self-repair-loop.md +17 -0
  25. package/dist/agent-src/rules/skill-quality.md +0 -2
  26. package/dist/agent-src/rules/source-confidentiality.md +37 -7
  27. package/dist/agent-src/scripts/archival_sweep.ts +117 -0
  28. package/dist/agent-src/scripts/archive_completed_roadmaps.ts +26 -7
  29. package/dist/agent-src/scripts/dashboard_mode.ts +212 -0
  30. package/dist/agent-src/scripts/gate_budget.ts +282 -0
  31. package/dist/agent-src/scripts/gate_execute.ts +72 -31
  32. package/dist/agent-src/scripts/roadmap_gates.ts +74 -3
  33. package/dist/agent-src/scripts/update_roadmap_progress.ts +296 -53
  34. package/dist/agent-src/skills/adr-create/SKILL.md +124 -5
  35. package/dist/agent-src/skills/ai-council/SKILL.md +48 -807
  36. package/dist/agent-src/skills/ai-council/references/advanced-modes.md +320 -0
  37. package/dist/agent-src/skills/ai-council/references/cost-and-redaction.md +99 -0
  38. package/dist/agent-src/skills/ai-council/references/execution-modes.md +162 -0
  39. package/dist/agent-src/skills/ai-council/references/output-and-synthesis.md +125 -0
  40. package/dist/agent-src/skills/ai-council/references/procedure.md +143 -0
  41. package/dist/agent-src/skills/decision-record/SKILL.md +61 -3
  42. package/dist/agent-src/skills/decision-review/SKILL.md +65 -0
  43. package/dist/agent-src/skills/merge-conflicts/SKILL.md +26 -0
  44. package/dist/agent-src/skills/quality-tools/SKILL.md +34 -326
  45. package/dist/agent-src/skills/quality-tools/references/js-ts-tools.md +113 -0
  46. package/dist/agent-src/skills/quality-tools/references/php-tools.md +222 -0
  47. package/dist/agent-src/skills/roadmap-management/SKILL.md +15 -409
  48. package/dist/agent-src/skills/roadmap-management/references/archival.md +226 -0
  49. package/dist/agent-src/skills/roadmap-management/references/authoring.md +107 -0
  50. package/dist/agent-src/skills/roadmap-management/references/dashboard.md +47 -0
  51. package/dist/agent-src/skills/roadmap-management/references/lifecycle.md +58 -0
  52. package/dist/agent-src/skills/security/SKILL.md +8 -0
  53. package/dist/agent-src/skills/security-audit/SKILL.md +12 -1
  54. package/dist/agent-src/skills/skill-writing/SKILL.md +34 -570
  55. package/dist/agent-src/skills/skill-writing/references/procedure.md +275 -0
  56. package/dist/agent-src/skills/skill-writing/references/read-only-scripts.md +18 -0
  57. package/dist/agent-src/skills/skill-writing/references/section-patterns.md +298 -0
  58. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  59. package/dist/agent-src/templates/roadmaps.md +65 -4
  60. package/dist/agent-src/templates/scripts/telemetry/flush_sender.mjs +163 -0
  61. package/dist/agent-src/templates/scripts/telemetry/remote.ts +160 -7
  62. package/dist/agent-src/templates/scripts/telemetry/transport.ts +169 -0
  63. package/dist/cli/registry.js +2 -1
  64. package/dist/cli/registry.js.map +1 -1
  65. package/dist/cli-delegate/chunk-2GNWGMAL.js +172 -0
  66. package/dist/cli-delegate/{chunk-P6LQDIWK.js → chunk-7PRE6ZV2.js} +338 -0
  67. package/dist/cli-delegate/{chunk-BKO2AZ7Z.js → chunk-B2ERU2VA.js} +1 -1
  68. package/dist/cli-delegate/chunk-E4XIZDIB.js +402 -0
  69. package/dist/cli-delegate/{chunk-3UCJWMMP.js → chunk-EEJUMCDZ.js} +16 -16
  70. package/dist/cli-delegate/{chunk-NXFWFAYV.js → chunk-G23AT6GD.js} +1 -0
  71. package/dist/cli-delegate/{chunk-XURJ4AQC.js → chunk-H4VIVD4C.js} +385 -284
  72. package/dist/cli-delegate/chunk-I3EIN2YG.js +36 -0
  73. package/dist/cli-delegate/{chunk-RLN73PR5.js → chunk-LPPWO5CE.js} +12 -8
  74. package/dist/cli-delegate/chunk-MWRD5YOH.js +100 -0
  75. package/dist/cli-delegate/{chunk-UEZ7D35A.js → chunk-N7CDDGI3.js} +4 -23
  76. package/dist/cli-delegate/{chunk-63WSP6YZ.js → chunk-PMEKGDED.js} +74 -167
  77. package/dist/cli-delegate/{chunk-GRRPEPRN.js → chunk-T7L2QK3K.js} +24 -5
  78. package/dist/cli-delegate/{chunk-EVTY7ISR.js → chunk-YM4GZDJG.js} +9 -1
  79. package/dist/cli-delegate/cmd_adr_effective.js +1162 -0
  80. package/dist/cli-delegate/cmd_conformance.js +466 -716
  81. package/dist/cli-delegate/cmd_converge.js +3 -3
  82. package/dist/cli-delegate/cmd_doctor.js +10 -10
  83. package/dist/cli-delegate/cmd_explain.js +1 -1
  84. package/dist/cli-delegate/cmd_export.js +10 -9
  85. package/dist/cli-delegate/cmd_handoff.js +10 -9
  86. package/dist/cli-delegate/cmd_memory_get.js +2 -1
  87. package/dist/cli-delegate/cmd_migrate.js +2 -2
  88. package/dist/cli-delegate/cmd_packs_active.js +1 -1
  89. package/dist/cli-delegate/cmd_preflight.js +5 -5
  90. package/dist/cli-delegate/cmd_prune.js +11 -10
  91. package/dist/cli-delegate/cmd_route_audit.js +6 -5
  92. package/dist/cli-delegate/cmd_route_explain.js +3 -3
  93. package/dist/cli-delegate/cmd_session_recycle.js +840 -30
  94. package/dist/cli-delegate/cmd_settings_get.js +3 -3
  95. package/dist/cli-delegate/cmd_settings_migrate.js +10 -9
  96. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  97. package/dist/cli-delegate/cmd_sync.js +11 -10
  98. package/dist/cli-delegate/cmd_uninstall.js +13 -11
  99. package/dist/cli-delegate/cmd_update.js +4 -4
  100. package/dist/cli-delegate/cmd_upgrade.js +4 -4
  101. package/dist/cli-delegate/cmd_validate.js +11 -10
  102. package/dist/cli-delegate/cmd_versions.js +3 -3
  103. package/dist/discovery/deprecation-report.md +1 -1
  104. package/dist/discovery/discovery-manifest.json +111 -37
  105. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  106. package/dist/discovery/discovery-manifest.summary.md +5 -5
  107. package/dist/discovery/dormancy-report.md +1 -1
  108. package/dist/discovery/orphan-report.md +1 -1
  109. package/dist/discovery/packs.json +15 -12
  110. package/dist/discovery/trust-report.md +3 -3
  111. package/dist/discovery/workspaces.json +11 -8
  112. package/dist/hooks/dispatch.js +3165 -2192
  113. package/dist/install/claudePathsPlan.js +187 -0
  114. package/dist/install/claudePathsPlan.js.map +1 -0
  115. package/dist/install/globalRuleLayers.js +169 -0
  116. package/dist/install/globalRuleLayers.js.map +1 -0
  117. package/dist/install/hostLayerFingerprint.js +136 -0
  118. package/dist/install/hostLayerFingerprint.js.map +1 -0
  119. package/dist/install/install.mjs +553 -425
  120. package/dist/install/partitionEligibility.js +432 -0
  121. package/dist/install/partitionEligibility.js.map +1 -0
  122. package/dist/install/ruleLayerPartition.js +157 -0
  123. package/dist/install/ruleLayerPartition.js.map +1 -0
  124. package/dist/mcp/registry-manifest.json +3 -3
  125. package/dist/mcp/server.json +2 -2
  126. package/dist/mcp/server.mjs +1863 -1234
  127. package/dist/router.json +1 -1
  128. package/dist/scripts/_lib/agent_src.js +41 -0
  129. package/dist/scripts/_lib/agent_src.js.map +1 -1
  130. package/dist/scripts/_lib/install_layout.js +83 -0
  131. package/dist/scripts/_lib/install_layout.js.map +1 -0
  132. package/dist/scripts/_lib/installed_lock.js +444 -0
  133. package/dist/scripts/_lib/installed_lock.js.map +1 -0
  134. package/dist/scripts/_lib/prune_empty_dirs.js +85 -0
  135. package/dist/scripts/_lib/prune_empty_dirs.js.map +1 -0
  136. package/dist/scripts/condense.js +63 -251
  137. package/dist/scripts/condense.js.map +1 -1
  138. package/dist/server/schemas/settings.js +4 -0
  139. package/dist/server/schemas/settings.js.map +1 -1
  140. package/docs/contracts/settings-classes.md +12 -6
  141. package/docs/guidelines/agent-infra/agent-interaction-and-decision-quality.md +8 -3
  142. package/docs/guidelines/agent-infra/artifact-drafting-protocol-mechanics.md +45 -1
  143. package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +26 -0
  144. package/docs/guidelines/agent-infra/false-green.md +47 -1
  145. package/docs/guidelines/agent-infra/memory-access.md +17 -0
  146. package/docs/guidelines/agent-infra/recurring-criticism-mechanics.md +96 -0
  147. package/package.json +1 -1
  148. package/src/agent-src/scripts/archival_sweep.ts +117 -0
  149. package/src/agent-src/scripts/archive_completed_roadmaps.ts +26 -7
  150. package/src/agent-src/scripts/dashboard_mode.ts +212 -0
  151. package/src/agent-src/scripts/gate_budget.ts +282 -0
  152. package/src/agent-src/scripts/gate_execute.ts +72 -31
  153. package/src/agent-src/scripts/roadmap_gates.ts +74 -3
  154. package/src/agent-src/scripts/update_roadmap_progress.ts +296 -53
  155. package/src/agent-src/templates/scripts/telemetry/flush_sender.mjs +163 -0
  156. package/src/agent-src/templates/scripts/telemetry/remote.ts +160 -7
  157. package/src/agent-src/templates/scripts/telemetry/transport.ts +169 -0
  158. package/src/config/agent-settings.template.yml +27 -2
  159. package/src/config/ci-local-parity.yml +39 -0
  160. package/src/config/estate-count-budget.json +612 -3
  161. package/src/config/evaluator-budgets.json +3 -3
  162. package/src/config/gate-coverage.yml +220 -12
  163. package/src/config/gate-violation-baselines.json +73 -63
  164. package/src/config/gitignore-block.txt +15 -2
  165. package/src/config/hook-latency-budget.json +5 -0
  166. package/src/config/rule-activation-census.json +14 -6
  167. package/src/config/rule-stub-ceilings.json +34 -7
  168. package/src/install/claudePathsPlan.ts +196 -0
  169. package/src/install/globalRuleLayers.ts +197 -0
  170. package/src/install/hostLayerFingerprint.ts +145 -0
  171. package/src/install/partitionEligibility.ts +502 -0
  172. package/src/install/ruleLayerPartition.ts +212 -0
  173. package/src/scripts/_cli/cmd_adr_effective.ts +484 -0
  174. package/src/scripts/_cli/cmd_session_recycle.ts +9 -1
  175. package/src/scripts/_dispatch.bash +17 -1
  176. package/src/scripts/_lib/ac_heading.ts +30 -0
  177. package/src/scripts/_lib/adr_frontmatter.test.ts +365 -0
  178. package/src/scripts/_lib/adr_frontmatter.ts +459 -0
  179. package/src/scripts/_lib/agent_src.ts +42 -0
  180. package/src/scripts/_lib/bench_ab_pinned_repo.ts +162 -0
  181. package/src/scripts/_lib/bench_ab_workspace.ts +10 -1
  182. package/src/scripts/_lib/envelope_grounding.ts +49 -9
  183. package/src/scripts/_lib/installed_lock.ts +50 -5
  184. package/src/scripts/_lib/layer_overlap_notice.ts +11 -2
  185. package/src/scripts/_lib/md_section.ts +129 -0
  186. package/src/scripts/_lib/prune_empty_dirs.ts +80 -0
  187. package/src/scripts/_lib/scope_guard.sh +26 -3
  188. package/src/scripts/_lib/self_repair.ts +19 -0
  189. package/src/scripts/_lib/self_repair_class_b.ts +171 -0
  190. package/src/scripts/adr/evidence_census.ts +899 -0
  191. package/src/scripts/adr/regenerate_index.ts +12 -32
  192. package/src/scripts/adr_cite_check.ts +540 -39
  193. package/src/scripts/ai_council/cli_help.ts +55 -0
  194. package/src/scripts/audit_adr_coverage.ts +103 -17
  195. package/src/scripts/before_complete_hook.ts +153 -28
  196. package/src/scripts/bench_ab_v2_complexity.ts +27 -7
  197. package/src/scripts/bench_hook_latency.ts +204 -0
  198. package/src/scripts/build_archive_index.ts +34 -0
  199. package/src/scripts/check_adr_frontmatter.ts +574 -39
  200. package/src/scripts/check_hook_bundle_content.ts +219 -0
  201. package/src/scripts/check_new_adr_evidence.ts +529 -0
  202. package/src/scripts/check_references.ts +78 -0
  203. package/src/scripts/check_release_published.ts +32 -11
  204. package/src/scripts/check_rule_layer_partition.ts +531 -0
  205. package/src/scripts/check_single_delivery.ts +194 -17
  206. package/src/scripts/check_standing_rule_delivery.ts +8 -2
  207. package/src/scripts/ci_settle.ts +170 -0
  208. package/src/scripts/condense.ts +79 -259
  209. package/src/scripts/council_cli.ts +3 -3
  210. package/src/scripts/dispatch_r2_reviewer.ts +6 -14
  211. package/src/scripts/external_sources_denylist.json +4 -1
  212. package/src/scripts/hook_manifest.json +1 -1
  213. package/src/scripts/hook_manifest.yaml +77 -14
  214. package/src/scripts/hooks/block_no_verify.ts +61 -10
  215. package/src/scripts/hooks/block_unauthorized_git.ts +21 -1
  216. package/src/scripts/hooks/concern_registry.ts +4 -0
  217. package/src/scripts/hooks/dispatch_hook.ts +154 -187
  218. package/src/scripts/hooks/dispatch_issues.ts +38 -19
  219. package/src/scripts/hooks/fallback_yaml.ts +66 -0
  220. package/src/scripts/hooks/host_semantics.ts +21 -0
  221. package/src/scripts/hooks/injection_budget.ts +13 -3
  222. package/src/scripts/hooks/py_json_dumps.ts +103 -0
  223. package/src/scripts/hooks/rtk_wrap_hook.ts +8 -3
  224. package/src/scripts/hooks/run_continuation_hook.ts +8 -5
  225. package/src/scripts/hooks/ship_diff_volume_hook.ts +81 -15
  226. package/src/scripts/hooks/source_first_gate_hook.ts +464 -0
  227. package/src/scripts/hooks/state_io.ts +870 -21
  228. package/src/scripts/hooks/stdin_failure_policy.ts +96 -0
  229. package/src/scripts/hooks/subagent_ledger_hook.ts +44 -15
  230. package/src/scripts/hooks/telemetry_flush_hook.ts +139 -0
  231. package/src/scripts/hooks/telemetry_self_repair.ts +128 -0
  232. package/src/scripts/hooks/telemetry_usage_hook.ts +22 -4
  233. package/src/scripts/hooks/turn_end_gate_hook.ts +81 -15
  234. package/src/scripts/injection_scan_hook.ts +137 -5
  235. package/src/scripts/install.ts +16 -11
  236. package/src/scripts/language_mirror_hook.ts +561 -25
  237. package/src/scripts/lint_framework_leakage_allowlist.json +2 -2
  238. package/src/scripts/lint_hook_manifest.ts +49 -0
  239. package/src/scripts/lint_plan_risk_register.ts +4 -3
  240. package/src/scripts/lint_provenance_vocabulary.ts +472 -4
  241. package/src/scripts/lint_skill_router_head.ts +4 -4
  242. package/src/scripts/mcp_server/tools.ts +2 -2
  243. package/src/scripts/new_roadmap.ts +136 -0
  244. package/src/scripts/pack_mcp_content.ts +20 -5
  245. package/src/scripts/release.ts +37 -38
  246. package/src/scripts/report_layer_overlap.ts +6 -0
  247. package/src/scripts/routing_doctor.ts +5 -1
  248. package/src/scripts/self_repair_hook.ts +37 -5
  249. package/src/scripts/session_register_hook.ts +11 -0
  250. package/src/scripts/skill_usage_collect.ts +11 -1
  251. package/src/scripts/skill_usage_report.ts +278 -8
  252. package/src/scripts/sync_pr_branch.ts +170 -20
  253. package/src/server/schemas/settings.ts +8 -0
  254. package/dist/cli-delegate/chunk-EJITJBJX.js +0 -172
  255. package/dist/cli-delegate/chunk-PZWRPRWP.js +0 -38
  256. package/dist/cli-delegate/{chunk-PPQO4NGR.js → chunk-6RJA2F5A.js} +3 -3
  257. package/dist/cli-delegate/{chunk-RYNADUFG.js → chunk-LWTYTX6F.js} +0 -0
  258. package/dist/cli-delegate/{chunk-FBKL6JWT.js → chunk-MMSGVZKV.js} +3 -3
@@ -0,0 +1,432 @@
1
+ /**
2
+ * The single-delivery partition predicate — one artefact, one layer.
3
+ *
4
+ * ## What it decides
5
+ *
6
+ * Two layers deliver agent artefacts: a machine-local project layer at
7
+ * `<repo>/.claude/` (gitignored, 0 tracked files, rewritten by every
8
+ * `task generate-tools`) and a host-global layer at `~/.claude/` (written by
9
+ * `agent-config install`, never by the build). Measured 2026-08-19 on a freshly
10
+ * regenerated tree: **110 rules arrive twice**, and standing rule prose is
11
+ * 203,873 tokens against a 110,000 cap (185.3 %).
12
+ *
13
+ * ADR-236 partitions them: an artefact that exists ONLY for this package stays
14
+ * in the project layer; everything else is delivered only globally. That takes
15
+ * `<repo>/.claude/` from 111 rules and 338 skills to **16 rules and zero
16
+ * skills**.
17
+ *
18
+ * ## Why the predicate is fail-safe and never fails the build
19
+ *
20
+ * The partition is a removal, so the build loses its repair path: it can no
21
+ * longer heal a stale global layer by regenerating, because it stops writing
22
+ * those files. Every uncertainty therefore resolves to `standalone/full` — the
23
+ * pre-partition behaviour — and **never** to a refusal:
24
+ *
25
+ * `.github/workflows/consistency.yml:169` runs `task generate-tools` on a fresh
26
+ * checkout where, by that workflow's own comment at `:172-174`, the host rule
27
+ * trees are gitignored and absent. An option that made a missing global layer a
28
+ * hard failure was eliminated by that fact in the 2026-08-19 council round, not
29
+ * by preference. Under-governing a checkout is the one regression this estate
30
+ * exists to prevent; breaking the Consistency pipeline is the other. Full
31
+ * projection is the only branch that does neither.
32
+ *
33
+ * ## Why content and not a version number
34
+ *
35
+ * Rationale and the 153-skill measurement that decided it: see
36
+ * `hostLayerFingerprint.ts`. Version equality is checked too, but as a cheap
37
+ * pre-filter — it is necessary, never sufficient.
38
+ *
39
+ * ## Contract
40
+ *
41
+ * Side-effect-free, no I/O of its own (callers supply the facts), no CLI entry,
42
+ * no `process.exit`. Ships inside the consumer installer bundle, same
43
+ * constraint as `ruleInScope.ts`.
44
+ */
45
+ import * as fs from 'node:fs';
46
+ import * as os from 'node:os';
47
+ import { parseFrontmatter } from './ruleInScope.js';
48
+ import { fingerprintLayers, hostLayerInputs } from './hostLayerFingerprint.js';
49
+ import { current_package_version, read_lockfile, write_lockfile, } from '../scripts/_lib/installed_lock.js';
50
+ /** The workspace id that marks an artefact as existing only for this package. */
51
+ export const MAINTAINER_WORKSPACE = 'agent-config-maintainer';
52
+ /**
53
+ * Select the delivery mode. Total function: always returns a verdict, never
54
+ * throws, never refuses.
55
+ *
56
+ * The order of the guards is the fail-safe order — cheapest and most decisive
57
+ * first, so the fingerprint is computed only when everything else already
58
+ * agrees.
59
+ */
60
+ export function partitionVerdict(inputs) {
61
+ if (!inputs.hostLayerPresent) {
62
+ return {
63
+ mode: 'standalone/full',
64
+ reason: 'no host-global layer on this machine',
65
+ };
66
+ }
67
+ const lock = inputs.lockfile;
68
+ if (lock === null) {
69
+ return {
70
+ mode: 'standalone/full',
71
+ reason: 'host layer present but no install record (installed.lock absent)',
72
+ };
73
+ }
74
+ const recorded = lock.agent_config_version;
75
+ if (!recorded) {
76
+ return {
77
+ mode: 'standalone/full',
78
+ reason: 'install record carries no version',
79
+ };
80
+ }
81
+ // Exact equality, deliberately not `>=`. A NEWER global layer is not a
82
+ // superset: a later release may have renamed or removed an artefact this
83
+ // checkout still expects, and ordering does not establish substitutability.
84
+ if (recorded !== inputs.projectVersion) {
85
+ return {
86
+ mode: 'standalone/full',
87
+ reason: `version mismatch (installed ${recorded}, building ${inputs.projectVersion})`,
88
+ };
89
+ }
90
+ const installedFp = lock.host_layer_fingerprint;
91
+ if (!installedFp) {
92
+ return {
93
+ mode: 'standalone/full',
94
+ reason: 'install predates host-layer fingerprinting — re-run `agent-config install` to enable the partition',
95
+ };
96
+ }
97
+ let expected;
98
+ try {
99
+ expected = inputs.expectedFingerprint();
100
+ }
101
+ catch {
102
+ return {
103
+ mode: 'standalone/full',
104
+ reason: 'could not compute the expected host-layer fingerprint',
105
+ };
106
+ }
107
+ if (installedFp !== expected) {
108
+ return {
109
+ mode: 'standalone/full',
110
+ reason: 'host-layer content differs from this checkout — re-run `agent-config install`',
111
+ };
112
+ }
113
+ return {
114
+ mode: 'dual-layer/partitioned',
115
+ reason: `host layer verified at ${recorded} (fingerprint ${installedFp.slice(0, 12)})`,
116
+ };
117
+ }
118
+ /**
119
+ * Does this artefact exist ONLY for this package?
120
+ *
121
+ * True iff `workspaces:` is present, non-empty, and its every entry is
122
+ * {@link MAINTAINER_WORKSPACE}. Measured 2026-08-20: exactly **16** rules in
123
+ * `src/rules/` satisfy this — the figure ADR-236 partitions on.
124
+ *
125
+ * The direction of the default is the opposite of `rule_in_scope`'s, and that
126
+ * is deliberate rather than an inconsistency. There, an untagged artefact ships
127
+ * — over-shipping is the safe error for a scope filter. Here, an untagged
128
+ * artefact is NOT package-only, so it is delivered globally and withheld from
129
+ * the project layer. Both defaults resolve toward "the artefact is generally
130
+ * useful"; only one of them is about withholding.
131
+ *
132
+ * ## The state space, MEASURED rather than enumerated defensively (2026-08-21)
133
+ *
134
+ * Three of this function's branches — unreadable file, absent `workspaces:`,
135
+ * empty list — all resolve to `false`, and the closure review asked whether one
136
+ * of them was resolving by accident. Counted over all 119 files in `src/rules/`:
137
+ *
138
+ * absent=0 · empty=0 · maintainer-only=16 · mixed=103 · scalar-or-other=0
139
+ *
140
+ * So exactly TWO states occur today, both deliberate, and the 16 is the figure
141
+ * ADR-236 partitions on.
142
+ *
143
+ * **The other three branches are CURRENTLY ABSENT from the generated tree, not
144
+ * unreachable** — an earlier revision of this note said "the state space is
145
+ * closed by that count", and a neutral review was right to refuse it. A file can
146
+ * become unreadable at runtime (permissions, a partial write, a truncated
147
+ * checkout) and a rule can acquire a malformed `workspaces:` on any commit. The
148
+ * count is a current inventory, never a proof of impossibility.
149
+ *
150
+ * What the count DOES buy is a bound on the defensive work worth doing now:
151
+ * fixtures for the absent and malformed cases would pin behaviour on inputs no
152
+ * committed rule produces, so they were not written. All three branches already
153
+ * resolve to `false` — deliver globally, withhold from the project layer — which
154
+ * is the over-delivery direction rather than the losing one, so the untested
155
+ * branches fail safe. Should that count move, this note is the thing that dates.
156
+ */
157
+ export function isExclusivelyPackageOnly(source_path) {
158
+ let meta;
159
+ try {
160
+ [meta] = parseFrontmatter(fs.readFileSync(source_path, 'utf-8'));
161
+ }
162
+ catch {
163
+ return false; // unreadable → not package-only → delivered globally
164
+ }
165
+ const raw = meta['workspaces'];
166
+ if (!Array.isArray(raw) || raw.length === 0) {
167
+ return false;
168
+ }
169
+ return raw.every((w) => String(w) === MAINTAINER_WORKSPACE);
170
+ }
171
+ let _memo = null;
172
+ let _announce = (m) => process.stdout.write(`${m}\n`);
173
+ /**
174
+ * Install the emitter used for the mode line. Callers pass a function visible at
175
+ * their DEFAULT output level — the first implementation used one that prints only
176
+ * at `verbose`, which withheld ~100 rules while saying nothing in a normal run.
177
+ */
178
+ export function setPartitionAnnounce(fn) {
179
+ _announce = fn;
180
+ }
181
+ /** Test seam — drop the memo so one process can exercise both delivery modes. */
182
+ export function _resetPartitionVerdictForTest() {
183
+ _memo = null;
184
+ }
185
+ /**
186
+ * Is the partition active for this generation? Memoised per process, because the
187
+ * fingerprint costs ~100 ms and the answer cannot change mid-run.
188
+ *
189
+ * This is the single entry point the generators call, deliberately: wiring a
190
+ * feature into two files that are already past the 1,500-line source ceiling
191
+ * costs a ratchet violation per line, so the decision, its fail-safe ordering,
192
+ * its printed reason and its memo all live here and the call sites are one line
193
+ * each.
194
+ */
195
+ export function partitionActive(projectRoot) {
196
+ return resolvePartitionVerdict(projectRoot).mode === 'dual-layer/partitioned';
197
+ }
198
+ /**
199
+ * Resolve the delivery mode for one generation, and say so once.
200
+ *
201
+ * Lives here rather than in `condense.ts` for two reasons. The decision belongs
202
+ * beside {@link partitionVerdict}, whose fail-safe ordering it depends on; and
203
+ * `condense.ts` is 1,500+ lines, where the source-size ratchet counts every
204
+ * added line as a violation — so a block that has no reason to live there is a
205
+ * cost as well as a misplacement.
206
+ *
207
+ * **Both council seats (2026-08-20, 2/2) required that generation PRINT the mode
208
+ * it selected** rather than partition silently: a withheld artefact nobody
209
+ * announced is exactly the under-governance the partition exists to remove. The
210
+ * caller supplies `announce`, and the level matters — the first implementation
211
+ * used an `info()` that prints only at `verbose`, so it withheld ~100 rules while
212
+ * saying nothing in a default run. Pass a function that is visible at the default
213
+ * level. Residual, stated: at an explicitly silent output level the line is
214
+ * dropped; that is an operator choice and the partition stays fail-safe anyway.
215
+ *
216
+ * The fingerprint compares the host layer against **what the installer recorded
217
+ * when it wrote that layer**, not against a re-derivation from source. That
218
+ * avoids guessing the installer's byte representation — a mismatch there would
219
+ * make the partition permanently unreachable rather than merely inactive — and it
220
+ * is the property the partition needs: the omitted artefacts are still present,
221
+ * in the form this version's installer left them.
222
+ *
223
+ * **Known residual:** an installer that crashes mid-write and still reaches the
224
+ * lockfile would fingerprint its own partial layer, and that fingerprint then
225
+ * verifies. Ordering narrows the window (the lockfile is written last) without
226
+ * closing it; a per-artefact manifest would close it and is not built here.
227
+ */
228
+ export function resolvePartitionVerdict(projectRoot) {
229
+ if (_memo !== null) {
230
+ return _memo;
231
+ }
232
+ const layers = hostLayerInputs(os.homedir());
233
+ const present = layers.some((l) => {
234
+ try {
235
+ return fs.statSync(l.root).isDirectory();
236
+ }
237
+ catch {
238
+ return false;
239
+ }
240
+ });
241
+ let lock = null;
242
+ try {
243
+ lock = read_lockfile();
244
+ }
245
+ catch {
246
+ lock = null; // unreadable record → fail safe
247
+ }
248
+ _memo = partitionVerdict({
249
+ projectVersion: current_package_version(projectRoot),
250
+ lockfile: lock,
251
+ hostLayerPresent: present,
252
+ expectedFingerprint: () => fingerprintLayers(layers),
253
+ });
254
+ _announce(`projection mode: ${_memo.mode} — ${_memo.reason}`);
255
+ return _memo;
256
+ }
257
+ /**
258
+ * Stamp the host-layer content fingerprint into the install record.
259
+ *
260
+ * The write side of the partition, and what makes it reachable at all: the build
261
+ * withholds artefacts only when this fingerprint matches what it finds on disk.
262
+ * No fingerprint recorded → full projection, which is the safe direction.
263
+ *
264
+ * **Call it AFTER the deploy and after the failed-tool postcheck.** The install
265
+ * writes its lockfile once before the redeploy; a fingerprint taken there would
266
+ * describe the PREVIOUS install and then verify against a layer this run
267
+ * replaced. Both council seats required the record be written last, and this is
268
+ * the function that respects it.
269
+ *
270
+ * On failure it reports and writes NOTHING. A missing fingerprint is fail-safe; a
271
+ * wrong one would authorise a partition against an unverified layer, so the two
272
+ * errors are not symmetric and this must never prefer the second.
273
+ *
274
+ * Lives here rather than inline in `install.ts` for the same reason
275
+ * {@link resolvePartitionVerdict} does: it belongs beside the predicate it
276
+ * serves, and `install.ts` is 5,000+ lines where the source-size ratchet counts
277
+ * every added line.
278
+ *
279
+ * @returns the fingerprint written, or `null` when nothing was recorded.
280
+ */
281
+ export function stampHostLayerFingerprint(installedVersion, tools, lockfilePath,
282
+ /** Skip entirely — e.g. when the claude-code deploy itself failed. */
283
+ skip, report) {
284
+ if (skip) {
285
+ return null;
286
+ }
287
+ try {
288
+ const fingerprint = fingerprintLayers(hostLayerInputs(os.homedir()));
289
+ write_lockfile(installedVersion, [...tools], {
290
+ path: lockfilePath,
291
+ host_layer_fingerprint: fingerprint,
292
+ });
293
+ report(`Host-layer fingerprint recorded: ${fingerprint.slice(0, 12)} (enables single delivery)`);
294
+ return fingerprint;
295
+ }
296
+ catch (e) {
297
+ report(`Host-layer fingerprint NOT recorded (${String(e)}) — the project layer keeps the full projection.`);
298
+ return null;
299
+ }
300
+ }
301
+ /**
302
+ * Per-tool-directory persona list under the partition — ADR-236's closure for the
303
+ * one family it never reached.
304
+ *
305
+ * The partition shipped for rules and skills and stopped there, so
306
+ * `<repo>/.claude/personas` kept being written while `~/.claude/personas` was
307
+ * installed from `_CLAUDE_SKILL_BUNDLE` (`install.ts:1916-1921`). Measured
308
+ * 2026-08-21 on a freshly regenerated tree with the partition ACTIVE: **29 shared
309
+ * names**, and neither `check_single_delivery` nor `_lib/layer_overlap_notice`
310
+ * looked, because `personas` was in neither's `TYPES`.
311
+ *
312
+ * ## Two properties the caller depends on
313
+ *
314
+ * **Scoped to `.claude/` only.** {@link partitionActive} verifies the CLAUDE host
315
+ * layer against `installed.lock`. It says nothing about `~/.cursor`, so
316
+ * withholding a cursor persona on the strength of a claude fingerprint would
317
+ * deliver it nowhere — the one failure the fail-safe design exists to prevent.
318
+ * Every other tool directory keeps the full projection.
319
+ *
320
+ * **Reconciliation is the empty list, not a second code path.** The caller's
321
+ * stale-symlink sweep removes any link whose name is absent from the list it was
322
+ * given for that directory, so returning `[]` empties a directory an earlier
323
+ * version populated. A gate that only declined to WRITE would leave the existing
324
+ * duplicate standing — a partition that stops new duplication and keeps the old
325
+ * is not a partition.
326
+ *
327
+ * Verified before shipping: every one of the 29 project personas is present in
328
+ * the global layer (32 there, a strict superset). The partition is a removal and
329
+ * has no repair path, so withholding is only safe once the surviving layer is
330
+ * known to carry what is withheld.
331
+ */
332
+ export function personaPartition(projectRoot, all) {
333
+ const active = partitionActive(projectRoot);
334
+ return {
335
+ all,
336
+ listFor: (toolDir) => (personaWithheldFor(toolDir, active) ? [] : all),
337
+ note: active ? ' — .claude/ withheld: ADR-236 partition, personas arrive from ~/.claude' : '',
338
+ countFor: (toolDir) => (personaWithheldFor(toolDir, active) ? 0 : all.length),
339
+ };
340
+ }
341
+ /**
342
+ * The pure half of {@link personaPartition}: withhold iff the partition is active
343
+ * AND this is a Claude tool directory.
344
+ *
345
+ * Split out so the decision is testable in BOTH directions. `personaPartition`
346
+ * reads `installed.lock` through a memoized `partitionActive`, so a test over it
347
+ * can only assert whatever this machine happens to be — which is a test that
348
+ * passes either way and therefore proves nothing. The two properties worth
349
+ * pinning are exactly the two this signature exposes: `.claude/` is withheld when
350
+ * active, and NOTHING else ever is.
351
+ */
352
+ export function personaWithheldFor(toolDir, active) {
353
+ return active && toolDir.startsWith('.claude/');
354
+ }
355
+ /**
356
+ * Does the project layer withhold the colon-form `/cluster:sub` commands?
357
+ *
358
+ * ## The claim this replaces, and the measurement that revised it
359
+ *
360
+ * `generate_claude_project_commands` was written on the reasoning that "Claude
361
+ * Code dedupes project and user scope by name, so the two copies of
362
+ * `/cluster:sub` collapse" — a host-behaviour claim with no first-party
363
+ * observation behind it. Its sibling claim about skills WAS probed under
364
+ * ADR-236's roadmap (Phase 5.2, `claudeMdExcludes`) and came back negative, so
365
+ * an unprobed host assumption in the same area was not a safe default.
366
+ *
367
+ * **MEASURED 2026-08-21, Claude Code 2.1.238.** Fixture: `/analyze:inbox`
368
+ * present in BOTH `~/.claude/commands/analyze/` and a temp project's
369
+ * `.claude/commands/analyze/`. The session reports `COUNT=1`. A control second
370
+ * entry (`/analyze:inboxctl`, project-only) makes the same probe report
371
+ * `COUNT=2` — so the 1 is an observation, not a probe that can only say one.
372
+ *
373
+ * **The half nobody had checked: the surviving copy is the GLOBAL one.** Asked
374
+ * for the command's description, the session returned the global body, not the
375
+ * project fixture's. So the dedup claim holds and its unstated corollary
376
+ * inverts the value of the project-layer copy: where a verified global layer
377
+ * exists, those 40 symlinks are written, deduped away, and LOSE. They are dead
378
+ * weight there — not a second listing, and not a reachability guarantee either.
379
+ *
380
+ * ## Why `partitionActive` is the right predicate rather than a new one
381
+ *
382
+ * It is true exactly when a global layer is present and verified against
383
+ * `installed.lock`, which is exactly the case where the project copy loses; and
384
+ * false on a fresh checkout, an unverified install, or a version mismatch —
385
+ * every case where the project copy is the only reachable one. So the
386
+ * fail-safe direction the whole module is built around already matches the
387
+ * measurement, and withholding needs no separate switch.
388
+ *
389
+ * ## Honest limits
390
+ *
391
+ * Self-report, n=1 per condition, one host version, one machine. What is NOT
392
+ * claimed: that older or newer hosts dedupe the same way, or that precedence is
393
+ * stable across them.
394
+ *
395
+ * **And a claim that WAS made here has been withdrawn as false** (neutral review,
396
+ * 2026-08-21). It read: *"A host that stopped deduping would show up as a
397
+ * double-listing, and `check_single_delivery` reports the overlap either way."*
398
+ * It cannot. When this predicate returns true the project copy is never written,
399
+ * so there is no second copy for the host to double-list or for the gate to
400
+ * count — it would report zero overlap while the assumption underneath had
401
+ * failed. Worse, `partitionActive` verifies the INSTALL (version + content
402
+ * fingerprint), never the HOST version whose behaviour was measured, so a host
403
+ * upgrade changes the premise and moves nothing this code reads.
404
+ *
405
+ * What actually holds: reachability survives a dedup change either way, because
406
+ * the global copy is delivered regardless. What is lost is only the project
407
+ * copy's redundancy, and nothing detects that. A real detector would have to
408
+ * pin the host version this measurement was taken against and re-probe when it
409
+ * moves — not built here, and named as absent rather than implied away.
410
+ */
411
+ export function commandsWithheld(projectRoot) {
412
+ return partitionActive(projectRoot);
413
+ }
414
+ /**
415
+ * ## The caller's early return, flagged by review and kept
416
+ *
417
+ * `generate_claude_project_commands` applies this predicate and then returns
418
+ * early when `src/domains/` is absent — BEFORE its stale-link sweep. So a tree
419
+ * with no `src/domains` keeps whatever `.claude/commands` it already had, even
420
+ * when the commands are now withheld.
421
+ *
422
+ * Invariant rather than oversight: this package always ships `src/domains/`, the
423
+ * generator only runs from a checkout that has it, and a consumer never reaches
424
+ * this path. Documented rather than reordered, because hoisting the sweep above
425
+ * the guard would make a non-package tree lose a directory this code did not
426
+ * write — a worse failure than the one being avoided, and on a tree we do not own.
427
+ *
428
+ * The transition that DOES occur is covered: `partition_delivery_topology.test.ts`
429
+ * runs inactive→active over a fixture that has `src/domains`, and asserts both
430
+ * the links and the empty cluster directories are gone after one run.
431
+ */
432
+ //# sourceMappingURL=partitionEligibility.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"partitionEligibility.js","sourceRoot":"","sources":["../../src/install/partitionEligibility.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,157 @@
1
+ /**
2
+ * The ADR-236 rule partition, decided per HOST directory on that host's own
3
+ * evidence.
4
+ *
5
+ * ## Why the decision moved from per-run to per-directory
6
+ *
7
+ * The filter used to sit in `condense.ts::_scoped_rule_basenames()`, which runs
8
+ * once per generation, so `partitionActive` — a claude-only fingerprint against
9
+ * `installed.lock` — decided for every host at once. That contradicted
10
+ * `partitionEligibility.ts`'s own fail-safe reasoning, which refuses to withhold a
11
+ * cursor artefact on claude's evidence because it "would deliver it nowhere", and
12
+ * it was measurably wrong in both directions on 2026-08-22 (partition ACTIVE,
13
+ * freshly generated worktree):
14
+ *
15
+ * ```
16
+ * .claude/rules 13 files, 13 package-only, 0 global-only
17
+ * .clinerules 14 files, 13 package-only, 0 global-only ← narrowed on
18
+ * borrowed evidence
19
+ * .cursor/rules 126 files, 26 package-only, 100 global-only ← never narrowed
20
+ * .windsurf/rules 113 files, 13 package-only, 100 global-only ← never narrowed
21
+ * .augment/rules 118 files, 15 package-only, 103 global-only ← never narrowed
22
+ * ```
23
+ *
24
+ * So the narrowing is per directory and gated on real evidence:
25
+ * {@link hostLayerCarries} reads that host's global directory and answers whether
26
+ * every name about to be withheld is actually there. No layer, an unreadable layer,
27
+ * or one missing a single name → the full projection is returned. A withhold now
28
+ * costs a directory read; the alternative cost a rule.
29
+ *
30
+ * ## Why it lives here rather than in condense.ts
31
+ *
32
+ * Four call sites consume it — the symlink emit plan, the cursor `.mdc` emitter,
33
+ * the windsurf emitter, the augment projector — and four sites deciding this
34
+ * separately is exactly how three of them came to disagree. It is in `src/install/`
35
+ * beside {@link partitionActive} and {@link hostLayerCarries}, the two predicates
36
+ * it composes, rather than in the 2,700-line generator that calls it.
37
+ *
38
+ * ## The classification source is the PROJECTION, not the authored tree
39
+ *
40
+ * `rulesSource` is `dist/agent-src/rules` at every call site, deliberately. A rule
41
+ * absent from `dist/` is compile-disabled and never projected, so it can be neither
42
+ * withheld nor duplicated — classifying from `src/rules` would count
43
+ * `telegraph-speak.md` (compile-disabled: 119 rules / 16 package-only in `src/`,
44
+ * 118 / 15 in `dist/`) and disagree with the emitter by exactly one file.
45
+ */
46
+ import * as fs from 'node:fs';
47
+ import * as os from 'node:os';
48
+ import * as path from 'node:path';
49
+ import { isExclusivelyPackageOnly, partitionActive } from './partitionEligibility.js';
50
+ import { hostLayerCarries, toolIdForProjectRuleDir } from './globalRuleLayers.js';
51
+ /**
52
+ * Narrow one directory's rule list to the package-only set — but only when THIS
53
+ * host's global layer is verified to carry what would be withheld.
54
+ *
55
+ * Returns a copy of `rules` unchanged in every uncertain case. That direction is
56
+ * the whole design: an unnecessary duplicate costs context, a wrong withhold costs
57
+ * the rule.
58
+ */
59
+ export function partitionRulesForDir(opts) {
60
+ const { toolDir, rules, projectRoot, rulesSource } = opts;
61
+ const active = opts.active ?? partitionActive(projectRoot);
62
+ if (!active) {
63
+ return [...rules];
64
+ }
65
+ const toolId = toolIdForProjectRuleDir(toolDir);
66
+ if (toolId === null) {
67
+ // An unmapped directory has no global layer this code can point at, so
68
+ // there is no evidence to withhold on.
69
+ return [...rules];
70
+ }
71
+ const packageOnly = rules.filter((r) => isExclusivelyPackageOnly(path.join(rulesSource, r)));
72
+ const keep = new Set(packageOnly);
73
+ const withheld = rules.filter((r) => !keep.has(r));
74
+ const home = opts.userHome ?? process.env['HOME'] ?? os.homedir();
75
+ const verdict = hostLayerCarries(toolId, withheld, home);
76
+ if (!verdict.carries) {
77
+ opts.announce?.(` ⚠️ ${toolDir}: full projection kept — ${toolId}'s global layer ${verdict.reason}` +
78
+ (verdict.missing.length > 0
79
+ ? ` (${String(verdict.missing.length)} rule(s) not there, e.g. ` +
80
+ `${verdict.missing.slice(0, 3).join(', ')})`
81
+ : ''));
82
+ return [...rules];
83
+ }
84
+ return packageOnly;
85
+ }
86
+ // ── The older, weaker duplicate-avoidance mechanism ──────────────────────────
87
+ //
88
+ // `projection.scope_dedup` (default OFF) predates the partition and solves the
89
+ // same problem with a narrower method: skip a project copy only when its
90
+ // user-scope twin is BYTE-IDENTICAL. It moved here from condense.ts alongside the
91
+ // partition because the two are one subject — a reader deciding how a duplicate is
92
+ // avoided should find both in one file rather than one here and one in a
93
+ // 2,700-line generator. Behaviour is unchanged; only `MODULE_STATE` reads became
94
+ // parameters.
95
+ /** `fs.statSync(p).isFile()`, false on any error — condense.ts's `_isFile`. */
96
+ function isFile(p) {
97
+ try {
98
+ return fs.statSync(p).isFile();
99
+ }
100
+ catch {
101
+ return false;
102
+ }
103
+ }
104
+ /**
105
+ * Per-tool user-scope rule directory, for the scope-dedup below. Only tools
106
+ * whose host reads a user-scope rules directory can have a redundant twin;
107
+ * everything else has nothing to de-duplicate against.
108
+ */
109
+ const USER_SCOPE_RULE_DIRS = {
110
+ '.claude/rules': path.join('.claude', 'rules'),
111
+ };
112
+ export function dedupableRules(opts) {
113
+ const { toolDir: tool_dir, rules, userHome, rulesSource } = opts;
114
+ const relative = USER_SCOPE_RULE_DIRS[tool_dir];
115
+ if (relative === undefined) {
116
+ return new Set();
117
+ }
118
+ // A hostile or simply absent $HOME must make the dedup inert, not
119
+ // adventurous. In a container `$HOME` is often unset (so `homedir()` can
120
+ // resolve to `/`) or world-writable, and this function decides which rules
121
+ // to STOP emitting — reading an unexpected tree there is how a projection
122
+ // silently loses a rule. Council review of PR #1055 raised exactly this.
123
+ let userDirStat;
124
+ const userDir = path.join(userHome, relative);
125
+ try {
126
+ userDirStat = fs.statSync(userDir);
127
+ }
128
+ catch {
129
+ return new Set();
130
+ }
131
+ if (!userDirStat.isDirectory()) {
132
+ return new Set();
133
+ }
134
+ // World-writable user scope: anyone on the box could plant a byte-identical
135
+ // twin and thereby delete a rule from the project projection. Refuse.
136
+ if ((userDirStat.mode & 0o002) !== 0) {
137
+ opts.announce?.(` ⚠️ ${tool_dir}: user-scope rules dir is world-writable — scope-dedup skipped`);
138
+ return new Set();
139
+ }
140
+ const skip = new Set();
141
+ for (const rule of rules) {
142
+ const twin = path.join(userDir, rule);
143
+ const source = path.join(rulesSource, rule);
144
+ try {
145
+ if (!isFile(twin) || !isFile(source))
146
+ continue;
147
+ if (fs.readFileSync(twin).equals(fs.readFileSync(source))) {
148
+ skip.add(rule);
149
+ }
150
+ }
151
+ catch {
152
+ // An unreadable twin is simply not de-duplicable — emit the project copy.
153
+ }
154
+ }
155
+ return skip;
156
+ }
157
+ //# sourceMappingURL=ruleLayerPartition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ruleLayerPartition.js","sourceRoot":"","sources":["../../src/install/ruleLayerPartition.ts"],"names":[],"mappings":"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"}
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "discovery": {
3
- "artefact_count": 609,
3
+ "artefact_count": 612,
4
4
  "scanner_version": "de0a22b4ac4c"
5
5
  },
6
- "generated_at": "2026-08-20",
6
+ "generated_at": "2026-08-22",
7
7
  "package": {
8
8
  "description": "Governed skills, rules and commands for AI coding tools (Claude Code, Cursor, Windsurf, Copilot) \u2014 every claim machine-checked, including \"zero runtime daemon\".",
9
9
  "homepage": "https://event4u-app.github.io/agent-config",
10
10
  "name": "@event4u/agent-config",
11
11
  "repository": "https://github.com/event4u-app/agent-config",
12
- "version": "14.6.0"
12
+ "version": "14.8.0"
13
13
  },
14
14
  "registries": [
15
15
  {
@@ -9,12 +9,12 @@
9
9
  "transport": {
10
10
  "type": "stdio"
11
11
  },
12
- "version": "14.6.0"
12
+ "version": "14.8.0"
13
13
  }
14
14
  ],
15
15
  "repository": {
16
16
  "source": "github",
17
17
  "url": "https://github.com/event4u-app/agent-config"
18
18
  },
19
- "version": "14.6.0"
19
+ "version": "14.8.0"
20
20
  }