session-orchestrator 4.1.0 → 5.0.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 (230) hide show
  1. package/.agents/skills/session-plan/SKILL.md +1 -1
  2. package/.agents/skills/session-start/SKILL.md +1 -1
  3. package/.agents/skills/ux-grill/SKILL.md +22 -0
  4. package/.claude-plugin/marketplace.json +1 -1
  5. package/.claude-plugin/plugin.json +3 -2
  6. package/.codex-plugin/plugin.json +1 -1
  7. package/.codex-plugin/skills/session-plan/SKILL.md +1 -1
  8. package/.codex-plugin/skills/session-start/SKILL.md +1 -1
  9. package/.codex-plugin/skills/ux-grill/SKILL.md +21 -0
  10. package/.codex-plugin/skills/ux-grill/agents/openai.yaml +5 -0
  11. package/.cursor/commands/ux-grill.md +14 -0
  12. package/.cursor/skills/session-plan/SKILL.md +1 -1
  13. package/.cursor/skills/session-start/SKILL.md +1 -1
  14. package/.cursor/skills/ux-grill/SKILL.md +13 -0
  15. package/.cursor-plugin/plugin.json +1 -1
  16. package/AGENTS.md +2 -1
  17. package/CHANGELOG.md +128 -1
  18. package/README.md +98 -86
  19. package/agents/dialectic-deriver.md +11 -0
  20. package/agents/ux-evaluator.md +1 -1
  21. package/commands/close.md +3 -3
  22. package/commands/go.md +2 -0
  23. package/commands/memory-cleanup.md +4 -3
  24. package/commands/persona-panel.md +1 -1
  25. package/commands/session.md +3 -2
  26. package/commands/ux-grill.md +51 -0
  27. package/docs/README.md +4 -4
  28. package/docs/USER-GUIDE.md +117 -50
  29. package/docs/agent-authoring.md +2 -2
  30. package/docs/baseline.md +55 -1
  31. package/docs/ci-setup.md +1 -1
  32. package/docs/codex-setup.md +9 -0
  33. package/docs/components.md +9 -9
  34. package/docs/cursor-setup.md +1 -0
  35. package/docs/events-schema.md +13 -6
  36. package/docs/github-mirror-protection.md +61 -20
  37. package/docs/instruction-delivery.md +1 -1
  38. package/docs/memory-proposal-flow.md +3 -3
  39. package/docs/migration-v4.md +2 -2
  40. package/docs/migration-v5.md +62 -0
  41. package/docs/owner-config-schema.md +74 -90
  42. package/docs/persona-panel.md +4 -4
  43. package/docs/pi-setup.md +1 -0
  44. package/docs/rule-authoring.md +13 -6
  45. package/docs/scope-collision-guard.md +16 -0
  46. package/docs/session-config-reference.md +55 -22
  47. package/docs/session-config-template.md +9 -5
  48. package/docs/vault-docs-architecture.md +4 -2
  49. package/hooks/_lib/hook-import-set.json +70 -3
  50. package/hooks/_lib/lock-bootstrap.mjs +84 -1
  51. package/hooks/_lib/vcs-create-matcher.mjs +401 -16
  52. package/hooks/enforce-scope.mjs +201 -0
  53. package/hooks/hooks-codex.json +1 -1
  54. package/hooks/hooks-cursor.json +5 -0
  55. package/hooks/hooks.json +7 -2
  56. package/hooks/on-session-start.mjs +171 -49
  57. package/hooks/post-bash-issue-budget-refund.mjs +375 -0
  58. package/hooks/pre-auq-clarity.mjs +70 -18
  59. package/hooks/pre-bash-issue-budget.mjs +170 -26
  60. package/hooks/subagent-telemetry.mjs +106 -20
  61. package/package.json +5 -4
  62. package/pi/prompts/ux-grill.md +12 -0
  63. package/scripts/baseline-archetypes.mjs +28 -0
  64. package/scripts/ci/assert-vitest-green.mjs +4 -2
  65. package/scripts/dialectic-deriver.mjs +32 -8
  66. package/scripts/emit-session.mjs +72 -1
  67. package/scripts/lib/agent-status.mjs +441 -9
  68. package/scripts/lib/auq/schema.mjs +10 -3
  69. package/scripts/lib/auto-dialectic.mjs +0 -68
  70. package/scripts/lib/baseline-archetypes.mjs +439 -0
  71. package/scripts/lib/build-live-signals.mjs +5 -6
  72. package/scripts/lib/ci-status-banner.mjs +29 -6
  73. package/scripts/lib/claude-md-budget-lint.mjs +52 -2
  74. package/scripts/lib/config/issue-budget.mjs +68 -8
  75. package/scripts/lib/config/private-config-dir.mjs +3 -2
  76. package/scripts/lib/config/remote-hosts.mjs +2 -2
  77. package/scripts/lib/config-schema.mjs +79 -0
  78. package/scripts/lib/config.mjs +12 -1
  79. package/scripts/lib/eval/engine.mjs +7 -1
  80. package/scripts/lib/file-lock.mjs +151 -8
  81. package/scripts/lib/git-porcelain.mjs +113 -0
  82. package/scripts/lib/instruction-budget-guard.mjs +415 -47
  83. package/scripts/lib/io.mjs +29 -4
  84. package/scripts/lib/issue-budget-reconcile.mjs +392 -0
  85. package/scripts/lib/issue-budget.mjs +412 -9
  86. package/scripts/lib/learnings/evolve-telemetry.mjs +1 -2
  87. package/scripts/lib/learnings/sizing-subject.mjs +44 -0
  88. package/scripts/lib/locks/staging-fence-lock.mjs +19 -38
  89. package/scripts/lib/locks/state-md-lock.mjs +19 -41
  90. package/scripts/lib/maintenance-due-banner.mjs +450 -0
  91. package/scripts/lib/owner-config.example.yaml +29 -46
  92. package/scripts/lib/owner-yaml.mjs +14 -13
  93. package/scripts/lib/peer-cards/merger.mjs +143 -0
  94. package/scripts/lib/pre-dispatch-check.mjs +20 -14
  95. package/scripts/lib/project-hygiene.mjs +81 -30
  96. package/scripts/lib/quality-gate.mjs +27 -71
  97. package/scripts/lib/reconcile/engine.mjs +19 -1
  98. package/scripts/lib/reconcile/writer.mjs +278 -11
  99. package/scripts/lib/resource-probe/evaluate.mjs +19 -21
  100. package/scripts/lib/rules-sync.mjs +34 -4
  101. package/scripts/lib/scope-echo.mjs +346 -0
  102. package/scripts/lib/session-close-backfill.mjs +182 -40
  103. package/scripts/lib/session-end/phase-skip.mjs +85 -86
  104. package/scripts/lib/session-end/tail-runner.mjs +178 -0
  105. package/scripts/lib/session-lock.mjs +62 -2
  106. package/scripts/lib/session-record-repair.mjs +91 -0
  107. package/scripts/lib/session-schema/constants.mjs +6 -0
  108. package/scripts/lib/session-schema/filters.mjs +26 -1
  109. package/scripts/lib/session-schema/validator.mjs +20 -0
  110. package/scripts/lib/session-shape.mjs +558 -0
  111. package/scripts/lib/session-start-probes.mjs +429 -56
  112. package/scripts/lib/session-token-rollup.mjs +95 -10
  113. package/scripts/lib/state-md/frontmatter-mutators.mjs +22 -34
  114. package/scripts/lib/state-md.mjs +1 -0
  115. package/scripts/lib/subagents-schema.mjs +77 -9
  116. package/scripts/lib/telemetry/pricing.mjs +197 -0
  117. package/scripts/lib/telemetry/sync.mjs +50 -1
  118. package/scripts/lib/test-runner/artifact-paths.mjs +30 -5
  119. package/scripts/lib/test-runner/issue-reconcile.mjs +45 -8
  120. package/scripts/lib/tmux-layout/layouts.mjs +62 -4
  121. package/scripts/lib/ux-grill/collect.mjs +1163 -0
  122. package/scripts/lib/ux-grill/compare.mjs +285 -0
  123. package/scripts/lib/ux-grill/manifest.mjs +618 -0
  124. package/scripts/lib/ux-grill/measures.mjs +431 -0
  125. package/scripts/lib/ux-grill/paths.mjs +224 -0
  126. package/scripts/lib/ux-grill/pencil-coverage.mjs +284 -0
  127. package/scripts/lib/ux-grill/reconcile.mjs +344 -0
  128. package/scripts/lib/ux-grill/run-record.mjs +316 -0
  129. package/scripts/lib/ux-grill/schema.mjs +321 -0
  130. package/scripts/lib/validate/check-skill-script-paths.mjs +33 -10
  131. package/scripts/lib/validate/check-untracked-test-deps.mjs +33 -19
  132. package/scripts/lib/validate/check-unwired-features.mjs +56 -27
  133. package/scripts/lib/vault-mirror/process.mjs +2 -1
  134. package/scripts/lib/vault-status/board-lock.mjs +18 -0
  135. package/scripts/lib/vault-status/board-writer.mjs +8 -0
  136. package/scripts/lib/vault-status/narrative-mirror.mjs +4 -4
  137. package/scripts/lib/wave-resource-gate.mjs +23 -27
  138. package/scripts/lib/wave-sizing.mjs +10 -3
  139. package/scripts/materialize-wave-scope.mjs +68 -14
  140. package/scripts/mcp-server.sh +16 -1
  141. package/scripts/print-applicable-rules.mjs +7 -6
  142. package/scripts/print-learnings-index.mjs +3 -2
  143. package/scripts/release.mjs +7 -2
  144. package/scripts/session-shape.mjs +266 -0
  145. package/skills/_shared/config-reading.md +15 -9
  146. package/skills/_shared/private-capability-context.md +89 -0
  147. package/skills/bootstrap/SKILL.md +60 -209
  148. package/skills/bootstrap/_shared-template.md +99 -14
  149. package/skills/bootstrap/deep-template.md +36 -26
  150. package/skills/bootstrap/fast-template.md +44 -8
  151. package/skills/bootstrap/intensity-heuristic.md +10 -4
  152. package/skills/bootstrap/private-contract.md +119 -0
  153. package/skills/bootstrap/public-fallback.md +30 -18
  154. package/skills/bootstrap/references/bootstrap-ecosystem-health-flow.md +48 -0
  155. package/skills/bootstrap/references/bootstrap-refresh-lock-flow.md +37 -0
  156. package/skills/bootstrap/references/bootstrap-retroactive-flow.md +108 -0
  157. package/skills/bootstrap/references/bootstrap-rules-fetch-bridge.md +64 -0
  158. package/skills/bootstrap/standard-template.md +39 -24
  159. package/skills/claude-md-drift-check/SKILL.md +9 -2
  160. package/skills/claude-md-drift-check/checker.mjs +213 -21
  161. package/skills/discovery/SKILL.md +6 -173
  162. package/skills/discovery/probes/vault-staleness.mjs +35 -5
  163. package/skills/discovery/probes-docs.md +8 -4
  164. package/skills/discovery/probes-supply-chain.md +4 -2
  165. package/skills/discovery/probes-ui.md +8 -4
  166. package/skills/discovery/probes-vault.md +12 -4
  167. package/skills/discovery/references/discovery-interactive-triage.md +139 -0
  168. package/skills/discovery/references/discovery-triage-state.md +54 -0
  169. package/skills/docs-orchestrator/audience-mapping.md +1 -1
  170. package/skills/eval/rubric-v1.md +13 -0
  171. package/skills/evolve/SKILL.md +2 -458
  172. package/skills/evolve/references/evolve-analyze-mode.md +360 -0
  173. package/skills/evolve/references/evolve-dialectic-mode.md +139 -0
  174. package/skills/gitlab-ops/SKILL.md +3 -3
  175. package/skills/grill/SKILL.md +1 -1
  176. package/skills/memory-cleanup/SKILL.md +2 -2
  177. package/skills/plan/mode-new.md +9 -0
  178. package/skills/plan/mode-retro.md +4 -3
  179. package/skills/reconcile/SKILL.md +11 -1
  180. package/skills/session-end/SKILL.md +3 -2
  181. package/skills/session-end/drift-operations.md +20 -5
  182. package/skills/session-end/metrics-collection.md +1 -0
  183. package/skills/session-end/phase-3-2-docs-verification.md +1 -1
  184. package/skills/session-end/phase-3-6-tail.md +27 -67
  185. package/skills/session-end/phase-3-7a-recommendations.md +2 -2
  186. package/skills/session-end/references/phase-2-quality-gate.md +3 -3
  187. package/skills/session-end/references/phase-3-documentation-updates.md +8 -6
  188. package/skills/session-end/references/phase-5-issue-cleanup.md +32 -1
  189. package/skills/session-end/session-metrics-write.md +33 -12
  190. package/skills/session-plan/SKILL.md +46 -180
  191. package/skills/session-plan/references/session-plan-task-classification.md +152 -0
  192. package/skills/session-plan/wave-template.md +8 -15
  193. package/skills/session-start/SKILL.md +41 -7
  194. package/skills/session-start/phase-2-5-docs-planning.md +1 -1
  195. package/skills/session-start/phase-8-5-express-path.md +12 -9
  196. package/skills/session-start/references/operations-contract.md +114 -0
  197. package/skills/session-start/references/phase-1-5-session-continuity.md +2 -0
  198. package/skills/session-start/references/phase-4-ssot-environment-check.md +42 -24
  199. package/skills/session-start/references/phase-6-7-memory-banner-telemetry-consent.md +3 -1
  200. package/skills/session-start/soul.md +2 -2
  201. package/skills/test-runner/SKILL.md +1 -1
  202. package/skills/test-runner/rubric-v1.md +2 -2
  203. package/skills/tmux-layout/SKILL.md +3 -1
  204. package/skills/ux-grill/SKILL.md +211 -0
  205. package/skills/ux-grill/rubric-v2.md +201 -0
  206. package/skills/ux-grill/soul.md +76 -0
  207. package/skills/wave-executor/SKILL.md +32 -127
  208. package/skills/wave-executor/circuit-breaker.md +3 -1
  209. package/skills/wave-executor/references/wave-executor-quality-gate.md +61 -0
  210. package/skills/wave-executor/references/wave-executor-state-init.md +86 -0
  211. package/skills/wave-executor/references/wave-loop-dispatch.md +12 -2
  212. package/skills/wave-executor/references/wave-loop-review.md +19 -6
  213. package/skills/wave-executor/references/wave-loop-scope-manifest.md +6 -2
  214. package/templates/_shared/ux-manifest.template.md +149 -0
  215. package/templates/nextjs-minimal/package.json +1 -1
  216. package/templates/node-minimal/package.json +1 -1
  217. package/scripts/lib/multi-provider-build/providers.mjs +0 -64
  218. package/scripts/lib/multi-provider-build/templating.mjs +0 -130
  219. package/scripts/lib/owner-config/coerce.mjs +0 -29
  220. package/scripts/lib/owner-config/constants.mjs +0 -21
  221. package/scripts/lib/owner-config/defaults.mjs +0 -50
  222. package/scripts/lib/owner-config/error.mjs +0 -19
  223. package/scripts/lib/owner-config/index.mjs +0 -13
  224. package/scripts/lib/owner-config/merge.mjs +0 -52
  225. package/scripts/lib/owner-config/validate.mjs +0 -259
  226. package/scripts/lib/owner-config-loader.mjs +0 -170
  227. package/scripts/lib/owner-config.mjs +0 -28
  228. package/scripts/lib/soul-resolve.mjs +0 -130
  229. package/scripts/lib/vault-mirror/render.mjs +0 -8
  230. package/templates/_shared/journey-manifest.md +0 -114
@@ -5,32 +5,38 @@
5
5
 
6
6
  ## Step 1: Detect PATH_TYPE (Silent — No User Interaction)
7
7
 
8
- Read `plan-baseline-path` from Session Config in `CLAUDE.md` (or `AGENTS.md` on Codex):
8
+ Run the dependency-free local contract reader. It uses the existing configuration
9
+ resolvers, with precedence `SO_BASELINE_PATH` → matching named baseline →
10
+ `owner.yaml` `paths.baseline-path` → committed Session Config. Paths resolve
11
+ host-locally and are never written into the contract result.
9
12
 
10
13
  ```bash
11
- BASELINE_PATH=$(grep -m1 "^plan-baseline-path:" "$REPO_ROOT/CLAUDE.md" 2>/dev/null | awk '{print $2}')
12
- # Expand leading ~ to $HOME so paths like ~/Projects/projects-baseline work correctly
13
- BASELINE_PATH="${BASELINE_PATH/#\~/$HOME}"
14
+ BOOTSTRAP_CONTRACT=$(node "$PLUGIN_ROOT/scripts/baseline-archetypes.mjs" --repo "$REPO_ROOT") || exit 2
15
+ PATH_TYPE=$(printf '%s' "$BOOTSTRAP_CONTRACT" | node --input-type=module -e '
16
+ let input = ""; for await (const chunk of process.stdin) input += chunk;
17
+ process.stdout.write(JSON.parse(input).status);
18
+ ')
19
+ export BOOTSTRAP_CONTRACT PATH_TYPE
14
20
  ```
15
21
 
16
- Decision logic (evaluated in order first match wins):
22
+ | Reader outcome | Bootstrap behavior |
23
+ |----------------|--------------------|
24
+ | No configured baseline, empty value, or missing directory | `public`; use bundled templates |
25
+ | Existing baseline with valid reduced schema v1 export | `private`; retain `archetypes` and `selected` |
26
+ | Existing baseline with missing CLI, invalid export, unsafe source, or producer failure | Abort; report the sanitized error reason |
27
+ | Valid private contract but no matching repository markers | `private`, `selected: null`; require selection for Standard/Deep |
17
28
 
18
- | Condition | PATH_TYPE |
19
- |-----------|-----------|
20
- | `plan-baseline-path` key is absent in Session Config | `public` |
21
- | `plan-baseline-path` key is present but value is empty | `public` |
22
- | Key is present, value is non-empty, AND `test -d "$BASELINE_PATH"` succeeds | `private` |
23
- | Key is present, value is non-empty, BUT path does not exist on disk | `public` |
24
-
25
- Set `PATH_TYPE = private | public`. Do not report this detection to the user — it is silent.
26
-
27
- > **Note on rules-fetch (Phase 3.5 of SKILL.md):** The optional rules-fetch step runs regardless of `PATH_TYPE`. Both private (with `plan-baseline-path` set) and public (without it) repos can opt into the fetch by setting `baseline-ref` in Session Config. The fetch is independent of how the initial scaffold was generated.
28
-
29
- ---
29
+ Lookup is offline and read-only. A broken configured contract never silently
30
+ switches to a public archetype. Missing directories retain the established
31
+ public fallback. Never guess a baseline path or fetch one automatically.
30
32
 
31
33
  ## Private Path
32
34
 
33
- When `PATH_TYPE = private`, the baseline templates are used directly. No new logic is needed here — the existing tier-template flow (`fast-template.md`, `standard-template.md`, `deep-template.md`) already calls `$BASELINE_PATH` scripts for CLAUDE.md generation and archetype file sourcing. Continue with the calling template file's steps unchanged.
35
+ Read and execute [`private-contract.md`](private-contract.md): use its selection,
36
+ staged scaffold, command/CI expectations, and S99 rule projection. Keep
37
+ `CONFIRMED_ARCHETYPE` as the validated contract ID throughout the tier flow.
38
+ The optional remote rules fetch is only a public-path opt-in; a configured
39
+ private baseline supplies selected rules locally.
34
40
 
35
41
  ---
36
42
 
@@ -38,6 +44,12 @@ When `PATH_TYPE = private`, the baseline templates are used directly. No new log
38
44
 
39
45
  When `PATH_TYPE = public`, no `projects-baseline` is available. Use the plugin-bundled templates and platform-appropriate CLAUDE.md generation described below.
40
46
 
47
+ For each file newly created by these steps, append its relative filename to the
48
+ existing `BOOTSTRAP_FILES` array. This includes instruction files, minimal
49
+ README/gitignore files and each copied template file. Preserve the accumulated
50
+ array through inherited tiers; never append a directory or an existing owner
51
+ file just because it is present.
52
+
41
53
  ### Detect Platform
42
54
 
43
55
  Read the current platform using the patterns from `skills/_shared/platform-tools.md`:
@@ -0,0 +1,48 @@
1
+ # Bootstrap — Ecosystem-Health Flow (`--ecosystem-health`)
2
+
3
+ > Reference of the `bootstrap` skill, split out of `SKILL.md` (#1246). Body moved **byte-identical**; only this header is new.
4
+ > **Sibling-file paths inside this body are relative to the parent directory, not to `references/`**: `SKILL.md` → `../SKILL.md`. They were deliberately NOT rewritten, so the moved bytes stay verifiable against the pre-split file.
5
+
6
+ ## Ecosystem-Health Flow (`--ecosystem-health`)
7
+
8
+ Entered when `$ARGUMENTS` contains `--ecosystem-health`. This is a **standalone flow** — it does not scaffold repo structure and does not write `bootstrap.lock`. Dispatch immediately; do not proceed to Phase 1.
9
+
10
+ **Purpose:** Populate the `health-endpoints`, `pipelines`, and `criticalIssueLabels` configuration consumed by `skills/ecosystem-health/SKILL.md`. Runs the interactive wizard in `scripts/lib/ecosystem-wizard.mjs`, which detects CI provider + package manager automatically and prompts the user for the remaining values.
11
+
12
+ **Steps:**
13
+
14
+ 1. **Run the wizard.**
15
+
16
+ ```bash
17
+ node "$PLUGIN_ROOT/scripts/lib/ecosystem-wizard.mjs" --repo-root "$(pwd)"
18
+ ```
19
+
20
+ The wizard will:
21
+ - Detect CI provider (`.gitlab-ci.yml` → `gitlab`; `.github/workflows/` → `github`; else `none`)
22
+ - Detect package manager from lockfile
23
+ - Prompt for health endpoints (format: `Name|URL`, comma-separated)
24
+ - Prompt for CI pipeline identifiers (format: `id` or `id:label`, comma-separated)
25
+ - Prompt for critical issue labels (comma-separated strings)
26
+
27
+ 2. **Wizard writes two files** (or skips each if already present):
28
+ - `CLAUDE.md` (or `AGENTS.md`) — appends `ecosystem-health:` block inside `## Session Config`
29
+ - `.orchestrator/policy/ecosystem.json` — full policy file (schema: `.orchestrator/policy/ecosystem.schema.json`)
30
+
31
+ 3. **No auto-commit.** The wizard prints what it wrote. The user reviews with `git status && git diff` and commits manually.
32
+
33
+ **Report:** The wizard prints a one-line summary per file:
34
+
35
+ ```
36
+ Ecosystem-Health Wizard complete.
37
+ Written: .orchestrator/policy/ecosystem.json, CLAUDE.md
38
+ Skipped (already present): (none)
39
+
40
+ Review changes with: git status && git diff
41
+ ```
42
+
43
+ **Idempotency:** Safe to re-run. If both output files are already present with matching content, the wizard exits 0 with "Nothing to do." To update, remove the existing `ecosystem-health:` key from Session Config and delete `.orchestrator/policy/ecosystem.json`, then re-run.
44
+
45
+ See `skills/ecosystem-health/wizard.md` for the full prompt spec and schema details.
46
+
47
+ ---
48
+
@@ -0,0 +1,37 @@
1
+ # Bootstrap — Refresh-Lock Flow (`--refresh-lock`)
2
+
3
+ > Reference of the `bootstrap` skill, split out of `SKILL.md` (#1246). Body moved **byte-identical**; only this header is new.
4
+ > **Sibling-file paths inside this body are relative to the parent directory, not to `references/`**: `SKILL.md` → `../SKILL.md`. They were deliberately NOT rewritten, so the moved bytes stay verifiable against the pre-split file.
5
+
6
+ ## Refresh-Lock Flow (`--refresh-lock`)
7
+
8
+ Entered when `$ARGUMENTS` contains `--refresh-lock`. No scaffolding questions are asked, and — unlike the Retroactive Flow above — this is NOT a no-op once the lock already has valid `version`/`tier` fields: refreshing is the load-bearing action.
9
+
10
+ **Purpose (#57):** Acknowledge the current plugin version and reset the freshness clock on an existing, already-valid `bootstrap.lock` without disturbing its original bootstrap provenance. This closes the gap left by the Retroactive Flow: once a lock already has `version` + `tier`, re-running `/bootstrap --retroactive` reports "bootstrap.lock already present ... Nothing to do." and changes nothing — exactly the no-op the bootstrap-lock-freshness probe (#186/#290) was recommending as its remediation. `--refresh-lock` is the actual remediation for a present-but-stale or version-drifted lock.
11
+
12
+ **Steps:**
13
+
14
+ 1. **Precondition check.** Read `.orchestrator/bootstrap.lock`. If missing, or present but missing a non-empty `version` or `tier` field, abort with: `Error: No valid bootstrap.lock found. Run /bootstrap or /bootstrap --retroactive first.` Do not fabricate a lock — this flow only refreshes an existing one.
15
+
16
+ 2. **Resolve the current plugin version.** Read `plugin-version` from `$PLUGIN_ROOT/package.json` (same source Phase 4 uses).
17
+
18
+ 3. **Call the refresh writer.**
19
+
20
+ ```js
21
+ import { refreshBootstrapLock } from '$PLUGIN_ROOT/scripts/lib/bootstrap-lock-refresh.mjs';
22
+ const result = refreshBootstrapLock({
23
+ repoRoot: REPO_ROOT,
24
+ currentPluginVersion: PLUGIN_VERSION,
25
+ });
26
+ ```
27
+
28
+ `refreshBootstrapLock` writes (or replaces, if already present) exactly two lines — `refreshed-at: <ISO 8601 UTC>` and `refreshed-plugin-version: <current plugin version>` — via the same atomic tmp-file + rename pattern used by the Retroactive Flow's lock write: write to a sibling tmp file, then rename over the target so the lock is never observed half-written. **Every other line of the lock — `bootstrapped-at`, `timestamp`, `plugin-version`, `tier`, `archetype`, `source`, … — is left byte-identical.** This is the provenance-honesty guarantee: a refresh is an acknowledgement, not a re-bootstrap. On failure (`result.ok === false`), surface `result.message` and stop — do not retry with a fabricated lock.
29
+
30
+ 4. **No auto-commit.** Unlike the Retroactive Flow, `--refresh-lock` does not stage or commit. The refreshed lock is a small, reviewable diff (two changed/added lines); the user commits it alongside their own work at their own cadence.
31
+
32
+ 5. **Report.** Print: `Lock refreshed (refreshed-at: <now>, plugin-version: <current>). Original bootstrap provenance unchanged.`
33
+
34
+ **Idempotency.** Running `/bootstrap --refresh-lock` twice in a row replaces the same two lines in place — it never duplicates them.
35
+
36
+ ---
37
+
@@ -0,0 +1,108 @@
1
+ # Bootstrap — Retroactive Flow (`--retroactive`)
2
+
3
+ > Reference of the `bootstrap` skill, split out of `SKILL.md` (#1246). Body moved **byte-identical**; only this header is new.
4
+ > **Sibling-file paths inside this body are relative to the parent directory, not to `references/`**: `SKILL.md` → `../SKILL.md`. They were deliberately NOT rewritten, so the moved bytes stay verifiable against the pre-split file.
5
+
6
+ ## Retroactive Flow (`--retroactive`)
7
+
8
+ Entered when `$ARGUMENTS` contains `--retroactive`. Writes the lock file and, per #182, optionally patches missing mandatory Session Config fields with defaults.
9
+
10
+ **Purpose:** Adopt an existing repo that already has `CLAUDE.md` + `## Session Config` but was bootstrapped manually (no `bootstrap.lock`). Writes the lock so the gate passes on all future invocations, and ensures the Session Config block satisfies the validated schema defined in `scripts/lib/config-schema.mjs`.
11
+
12
+ **Steps:**
13
+
14
+ 1. **Verify preconditions.** Confirm `CLAUDE.md` (or `AGENTS.md`) exists and contains `## Session Config`. If not, abort: `Error: CLAUDE.md with Session Config required for retroactive bootstrap.`
15
+
16
+ 2. **Check lock not already present.** If `.orchestrator/bootstrap.lock` already exists and has valid `version` + `tier` fields, report: `bootstrap.lock already present (tier: <tier>). Nothing to do.` and exit 0 (idempotent).
17
+
18
+ 3. **Infer tier from file inventory.** Examine the repo root:
19
+
20
+ | Condition (evaluated in order) | Inferred Tier |
21
+ |---|---|
22
+ | CI file present (`.gitlab-ci.yml` OR `.github/workflows/`) AND `CHANGELOG.md` present | `deep` |
23
+ | Package manifest present (`package.json` OR `pyproject.toml`) | `standard` |
24
+ | Neither of the above | `fast` |
25
+
26
+ Store as `INFERRED_TIER`.
27
+
28
+ 4. **Infer archetype.** Run Phase 0.5's read-only source detection. For a private
29
+ contract, use its detected `selected.id`; retain `null` with an explicit
30
+ `insufficient-evidence` report if no markers match. An invalid configured
31
+ contract aborts. Do not scaffold or apply rules in this retroactive flow.
32
+ For the public path, use best-effort detection from existing files:
33
+ - `pyproject.toml` present → `python-uv`
34
+ - `package.json` with `next` in dependencies → `nextjs-minimal`
35
+ - `package.json` without `next` → `node-minimal`
36
+ - No manifest → `null`
37
+
38
+ Store as `INFERRED_ARCHETYPE`.
39
+
40
+ 5. **Write bootstrap.lock.** Create `.orchestrator/` if needed, then write:
41
+ ```yaml
42
+ # .orchestrator/bootstrap.lock
43
+ version: 1
44
+ tier: <INFERRED_TIER>
45
+ archetype: <INFERRED_ARCHETYPE or null>
46
+ timestamp: <current ISO 8601 UTC>
47
+ source: retroactive
48
+ plugin-version: <current plugin version from $PLUGIN_ROOT/package.json>
49
+ ```
50
+
51
+ 6. **Patch Session Config (#182).** Run the validator against the current `## Session Config` block; append any missing mandatory fields with defaults. The 7 mandatory fields (per `scripts/lib/config-schema.mjs`) are: `test-command`, `typecheck-command`, `lint-command`, `agents-per-wave`, `waves`, `persistence`, `enforcement`.
52
+
53
+ ```bash
54
+ CONFIG_OUT="$(node "$PLUGIN_ROOT/scripts/parse-config.mjs" 2>&1 >/dev/null)"
55
+ # parse-config.mjs emits validation warnings to stderr when enforcement=warn.
56
+ # Grep for 'must be' lines (issued by validate-config.mjs) to detect missing fields.
57
+ MISSING_FIELDS="$(echo "$CONFIG_OUT" | grep -oE '(test-command|typecheck-command|lint-command|agents-per-wave|waves|persistence|enforcement)' | sort -u || true)"
58
+ if [[ -n "$MISSING_FIELDS" ]]; then
59
+ # Detect package manager to pick sensible defaults for commands.
60
+ PM_DEFAULTS="$(node --input-type=module -e "
61
+ import {detectPackageManager, defaultQualityGateCommands} from '$PLUGIN_ROOT/scripts/lib/package-manager.mjs';
62
+ const pm = detectPackageManager(process.cwd());
63
+ const cmds = defaultQualityGateCommands(pm);
64
+ console.log('test-command: ' + cmds.test.command);
65
+ console.log('typecheck-command: ' + cmds.typecheck.command);
66
+ console.log('lint-command: ' + cmds.lint.command);
67
+ " 2>/dev/null)"
68
+
69
+ CONFIG_FILE="CLAUDE.md"
70
+ [[ -f "AGENTS.md" ]] && CONFIG_FILE="AGENTS.md"
71
+
72
+ # Append each missing field under the ## Session Config block.
73
+ for field in $MISSING_FIELDS; do
74
+ case "$field" in
75
+ test-command|typecheck-command|lint-command)
76
+ default_line="$(echo "$PM_DEFAULTS" | grep "^$field:")" ;;
77
+ agents-per-wave) default_line="agents-per-wave: 6" ;;
78
+ waves) default_line="waves: 5" ;;
79
+ persistence) default_line="persistence: true" ;;
80
+ enforcement) default_line="enforcement: warn" ;;
81
+ esac
82
+ # Insert after `## Session Config` line if not already present.
83
+ grep -q "^$field:" "$CONFIG_FILE" \
84
+ || awk -v insert="$default_line" '/^## Session Config/ && !done { print; print ""; print insert; done=1; next } { print }' "$CONFIG_FILE" > "$CONFIG_FILE.tmp" \
85
+ && mv "$CONFIG_FILE.tmp" "$CONFIG_FILE"
86
+ done
87
+ echo "Patched $CONFIG_FILE with defaults for: $MISSING_FIELDS"
88
+ fi
89
+ ```
90
+
91
+ This patch is best-effort: existing fields are never overwritten. If no fields are missing, this step is a no-op.
92
+
93
+ 7. **Commit.** Stage the lock file (and the patched config file, if it changed) and commit:
94
+ ```bash
95
+ mkdir -p .orchestrator
96
+ git add .orchestrator/bootstrap.lock
97
+ # Also stage CLAUDE.md/AGENTS.md if step 6 patched it.
98
+ git diff --name-only --cached CLAUDE.md AGENTS.md 2>/dev/null | head -1 >/dev/null || {
99
+ [[ -f CLAUDE.md ]] && git diff --quiet CLAUDE.md || git add CLAUDE.md
100
+ [[ -f AGENTS.md ]] && git diff --quiet AGENTS.md || git add AGENTS.md
101
+ }
102
+ git commit -m "chore: bootstrap lock (retroactive)"
103
+ ```
104
+
105
+ 8. **Report.** Print: `Retroactive bootstrap complete. Lock written (tier: <INFERRED_TIER>, source: retroactive).` Include a second line `Patched Session Config: <fields>` when step 6 applied any patches, otherwise `No config changes.`.
106
+
107
+ ---
108
+
@@ -0,0 +1,64 @@
1
+ # Bootstrap — Phase 3.6: (Optional) Rules-Fetch Bridge
2
+
3
+ > Reference of the `bootstrap` skill, split out of `SKILL.md` (#1246). Body moved **byte-identical**; only this header is new.
4
+ > **Sibling-file paths inside this body are relative to the parent directory, not to `references/`**: `SKILL.md` → `../SKILL.md`, `private-contract.md` → `../private-contract.md`, `standard-template.md` → `../standard-template.md`, `deep-template.md` → `../deep-template.md`. They were deliberately NOT rewritten, so the moved bytes stay verifiable against the pre-split file.
5
+
6
+ ## Phase 3.6: (Optional) Rules-Fetch Bridge
7
+
8
+ > Closes session-orchestrator issue #110.
9
+
10
+ After scaffolding, the Standard and Deep templates execute S99. On the private
11
+ path, it applies the selected contract's local rule union, rechecks conditional
12
+ dependencies and preserves existing files. It excludes every plugin-owned
13
+ basename and aborts on an invalid configured contract; see `private-contract.md`.
14
+
15
+ On the public path, S99 retains the optional remote rules-fetch step. It pulls
16
+ canonical `.claude/rules/*.md` directly from the configured baseline GitLab
17
+ project, excluding all plugin-owned basenames. The remote step only fires when:
18
+
19
+ - `baseline-ref` is present in Session Config
20
+ - `GITLAB_TOKEN` env var is set
21
+ - `scripts/lib/fetch-baseline.mjs` is present in the plugin
22
+ - A GitLab host is resolvable from the `gitlab-host` Session Config key (or the `GITLAB_HOST` env var) — never a hardcoded default
23
+
24
+ When triggered, the step:
25
+
26
+ 1. Loops over a default rule manifest, invoking `node scripts/lib/fetch-baseline.mjs <project_id> <file_path> <baseline-ref>` once per rule. The CLI prints one file body to stdout (exit 0 success; 1 auth, 2 not-found, 3 network) — bootstrap redirects stdout to the target path and skips failures so a single 404 cannot abort the batch.
27
+ 2. Fetches each rule listed in the default manifest from the configured `baseline-project-id` (default `52`) at the configured `baseline-ref`
28
+ 3. Writes `.claude/.baseline-fetch.lock` (via an inline `node --input-type=module -e`) recording what was fetched
29
+ 4. Populates `.claude/.baseline-cache/` for offline fallback on subsequent invocations
30
+
31
+ When the fetch fails (network error, auth, missing file), bootstrap **does not abort**. Rules will arrive in the repo via Clank's weekly baseline sync MRs (the legacy path). A warning is printed.
32
+
33
+ **Why opt-in:** Repos without `baseline-ref` continue to receive rules via the existing Clank sync flow. The fetch bridge is a faster on-demand alternative for newly-bootstrapped repos that want current rules immediately.
34
+
35
+ **Local edits:** Re-running bootstrap with `baseline-ref` set will overwrite `.claude/rules/*.md` (rules are canonical). Repo-specific extensions belong in `.claude/rules/local/*.md` (not fetched, not overwritten).
36
+
37
+ See `standard-template.md` (Step S99) and `deep-template.md` (Step D99) for the implementation, and `docs/session-config-reference.md` for the `baseline-ref` and `baseline-project-id` field definitions.
38
+
39
+ ### `.claude/.baseline-fetch.lock` Schema
40
+
41
+ The lock file is committed to git and records what was fetched.
42
+
43
+ ```yaml
44
+ # .claude/.baseline-fetch.lock
45
+ version: 1
46
+ project_id: 52
47
+ baseline_ref: main
48
+ fetched_at: 2026-04-17T13:42:00Z # ISO 8601 UTC
49
+ files:
50
+ - .claude/rules/development.md
51
+ - .claude/rules/security.md
52
+ - .claude/rules/...
53
+ ```
54
+
55
+ | Field | Description |
56
+ |---|---|
57
+ | `version` | Lock file schema version. Currently `1`. |
58
+ | `project_id` | GitLab project ID the files were fetched from. |
59
+ | `baseline_ref` | The git ref (branch/tag/SHA) at fetch time. |
60
+ | `fetched_at` | ISO 8601 UTC timestamp. |
61
+ | `files` | List of fetched file paths (relative to repo root). |
62
+
63
+ ---
64
+
@@ -5,6 +5,20 @@
5
5
 
6
6
  Standard tier is a strict superset of Fast tier. Execute all Fast-tier steps first, then the Standard-specific steps below.
7
7
 
8
+ Track actual newly created relative files in one `BOOTSTRAP_FILES` array across
9
+ all inherited steps. Append each file when created; preserve the array across
10
+ repeated steps. Never append an existing owner file or a directory.
11
+
12
+ ## Private Contract Preparation
13
+
14
+ When `PATH_TYPE = private`, resolve `CONFIRMED_ARCHETYPE` with
15
+ `private-contract.md` and execute its Scaffold section **before** the inherited
16
+ Fast steps. Preserve its rendered metadata, manifests, source, README and CI.
17
+ Add its reported created files to `BOOTSTRAP_FILES`. Skip the four public
18
+ archetype sections below, then continue at Step 3a, S99 and the lock/quality-policy
19
+ steps. Populate command expectations from the selected contract as described
20
+ in `private-contract.md`.
21
+
8
22
  ## Step 1–7: Execute Fast Tier
9
23
 
10
24
  Read and execute `skills/bootstrap/fast-template.md` Steps 1–7 in full. Do not skip any step. The Fast commit (`chore: bootstrap (fast)`) is NOT made — Fast steps produce files only; the single commit happens at Standard Step 7 below.
@@ -21,9 +35,9 @@ Before executing the stack-specific steps, resolve the final archetype:
21
35
  ARCHETYPE = CONFIRMED_ARCHETYPE # set by SKILL.md from intensity-heuristic or user selection
22
36
  ```
23
37
 
24
- Valid values: `static-html` | `node-minimal` | `nextjs-minimal` | `python-uv`
38
+ For `PATH_TYPE = public`, valid values are `static-html` | `node-minimal` | `nextjs-minimal` | `python-uv`. Private IDs come only from the validated contract.
25
39
 
26
- If `ARCHETYPE` is `null` or unset at this point, default to `node-minimal`.
40
+ On the public path only, if `ARCHETYPE` is `null` or unset, default to `node-minimal`. A private missing/invalid selection aborts before scaffolding; it never reaches the public sections.
27
41
 
28
42
  The sections below are conditional on `ARCHETYPE`. Execute only the section that matches.
29
43
 
@@ -592,12 +606,11 @@ indent_size = 2
592
606
 
593
607
  Canonical implementation in [`_shared-template.md#parallel-sessions-rule`](_shared-template.md).
594
608
 
595
- Run `node "$PLUGIN_ROOT/scripts/lib/rules-sync.mjs" --repo-root "$REPO_ROOT"` it vendors every
596
- rule registered in `rules/_index.md` (idempotent: missing→create, identical→skip, differs→overwrite),
597
- and it is the only writer that applies the pre-write validator, the basename-collision guard and the
598
- copy-on-write branch that preserves repo-private rules. The shared partial also runs
599
- `cp "$PLUGIN_ROOT/templates/_shared/loop.md" "$REPO_ROOT/.claude/loop.md"` so bare `/loop` gets a
600
- repo-aware maintenance prompt. See shared partial for full shell command. Issues #155, #633, #1060.
609
+ Execute the shared partial's `syncBootstrapRules` action. It supplies private
610
+ required basenames to `scripts/lib/rules-sync.mjs`, preserving its manifest,
611
+ pre-write validation and local-override checks. The partial appends actual newly
612
+ created rule paths, and creates `.claude/loop.md` only when missing. See the
613
+ shared partial for the executable shell command. Issues #155, #633, #1060.
601
614
 
602
615
  Note: Runs before S99. S99 no longer fetches `parallel-sessions.md` from the baseline — that entry
603
616
  was removed from the S99 manifest in #1060. The baseline copy carries no provenance header, so
@@ -611,8 +624,10 @@ Create the metrics directory and an empty learnings file so the `/evolve` skill
611
624
 
612
625
  ```bash
613
626
  mkdir -p "$REPO_ROOT/.orchestrator/metrics"
614
- [[ -f "$REPO_ROOT/.orchestrator/metrics/learnings.jsonl" ]] || \
627
+ if [[ ! -e "$REPO_ROOT/.orchestrator/metrics/learnings.jsonl" && ! -L "$REPO_ROOT/.orchestrator/metrics/learnings.jsonl" ]]; then
615
628
  : > "$REPO_ROOT/.orchestrator/metrics/learnings.jsonl"
629
+ BOOTSTRAP_FILES+=(.orchestrator/metrics/learnings.jsonl)
630
+ fi
616
631
  ```
617
632
 
618
633
  **Idempotent.** Re-running bootstrap does not overwrite an existing file.
@@ -624,11 +639,13 @@ mkdir -p "$REPO_ROOT/.orchestrator/metrics"
624
639
 
625
640
  Canonical implementation in [`_shared-template.md#baseline-fetch`](_shared-template.md).
626
641
 
627
- OPT-IN: only fires when `baseline-ref` is in Session Config, `GITLAB_TOKEN` is set, and
642
+ PRIVATE: applies the selected local contract rule union without network access, preserving existing files and excluding every plugin-owned basename. An invalid contract aborts.
643
+
644
+ PUBLIC OPT-IN: only fires when `baseline-ref` is in Session Config, `GITLAB_TOKEN` is set, and
628
645
  `scripts/lib/fetch-baseline.mjs` exists. Fetches `.claude/rules/*.md` from the baseline GitLab
629
646
  project (default project 52) and writes `.claude/.baseline-fetch.lock`. Does NOT abort on failure.
630
- The rule manifest includes `owner-persona.md` (alongside `parallel-sessions.md`, `development.md`,
631
- and the rest of the always-on rules). See shared partial for full implementation.
647
+ The private rule manifest comes from the selected contract. Every plugin-owned
648
+ basename is excluded from either delivery path. See the shared partial.
632
649
 
633
650
  ---
634
651
 
@@ -649,6 +666,8 @@ Write `.orchestrator/bootstrap.lock` **atomically** (mktemp + mv prevents a corr
649
666
  process is interrupted mid-write):
650
667
 
651
668
  ```bash
669
+ _LOCK_CREATED=false
670
+ [[ -e "$REPO_ROOT/.orchestrator/bootstrap.lock" || -L "$REPO_ROOT/.orchestrator/bootstrap.lock" ]] || _LOCK_CREATED=true
652
671
  _LOCK_TMP=$(mktemp "$REPO_ROOT/.orchestrator/bootstrap.lock.XXXXXX")
653
672
  cat > "$_LOCK_TMP" << LOCK
654
673
  # .orchestrator/bootstrap.lock
@@ -661,6 +680,7 @@ plugin-version: <session-orchestrator plugin version — read from $PLUGIN_ROOT/
661
680
  bootstrapped-at: <current ISO 8601 UTC — same value as timestamp; distinct field for age-validation probe>
662
681
  LOCK
663
682
  mv "$_LOCK_TMP" "$REPO_ROOT/.orchestrator/bootstrap.lock"
683
+ if [[ "$_LOCK_CREATED" = true ]]; then BOOTSTRAP_FILES+=(.orchestrator/bootstrap.lock); fi
664
684
  ```
665
685
 
666
686
  Set `source` using the same logic as fast-template Step 5:
@@ -673,8 +693,8 @@ Set `source` using the same logic as fast-template Step 5:
673
693
 
674
694
  Canonical implementation in [`_shared-template.md#quality-gate-policy`](_shared-template.md).
675
695
 
676
- Write `.orchestrator/policy/quality-gates.json` with package-manager-detected defaults (idempotent:
677
- skip if file already exists). Uses `scripts/lib/package-manager.mjs`; falls back to npm defaults.
696
+ Write `.orchestrator/policy/quality-gates.json` from exact private contract gates,
697
+ or package-manager defaults on the public path. Skip existing owner policy.
678
698
  See shared partial for full shell command. Issue #183.
679
699
 
680
700
  <!-- @include _shared-template.md#state-md-scaffold -->
@@ -682,8 +702,9 @@ See shared partial for full shell command. Issue #183.
682
702
 
683
703
  Canonical implementation in [`_shared-template.md#state-md-scaffold`](_shared-template.md).
684
704
 
685
- Scaffold `.claude/STATE.md` from `skills/bootstrap/STATE.md.template` (idempotent: skip if
686
- already exists). On Codex CLI / Cursor IDE, substitute `.codex/` or `.cursor/` for `.claude/`.
705
+ Scaffold `.claude/STATE.md` (idempotent: skip if it already exists). <!-- path-check: example -->
706
+ Use `skills/bootstrap/STATE.md.template`.
707
+ On Codex CLI / Cursor IDE, substitute `.codex/` or `.cursor/` for `.claude/`.
687
708
  See shared partial for full shell command. Issue #184.
688
709
 
689
710
  <!-- @include _shared-template.md#agents-scaffold -->
@@ -701,15 +722,9 @@ Stage all created files and commit:
701
722
 
702
723
  ```bash
703
724
  cd "$REPO_ROOT"
704
- BOOTSTRAP_FILES=(
705
- CLAUDE.md AGENTS.md .gitignore README.md .orchestrator/bootstrap.lock
706
- .orchestrator/policy/quality-gates.json
707
- package.json pyproject.toml tsconfig.json eslint.config.mjs .prettierrc
708
- .editorconfig src/ tests/ .claude/
709
- )
710
725
  # Add only the files bootstrap created — no sweeping -u/-A to avoid catching pre-existing files
711
- for _f in "${BOOTSTRAP_FILES[@]}"; do
712
- [[ -e "$_f" ]] && git add -- "$_f"
726
+ for _f in ${BOOTSTRAP_FILES[@]+"${BOOTSTRAP_FILES[@]}"}; do
727
+ [[ -f "$_f" && ! -L "$_f" ]] && git add -- "$_f"
713
728
  done
714
729
  git commit -m "chore: bootstrap (standard)"
715
730
  ```
@@ -41,7 +41,7 @@ PHASE 1 IMPLEMENTED (2026-04-19). Session-end opt-in quality gate. Upstream of `
41
41
  | 6 | `session-config-parity` | Top-level keys under `## Session Config` in `CLAUDE.md` / `AGENTS.md` | diff against `docs/session-config-template.md`; a missing MANDATORY (minimal-baseline) key is an error, a missing OPT-IN-baseline-only key is a warning |
42
42
  | 7 | `vault-dir-parity` | `vault-integration.vault-dir` in BOTH `CLAUDE.md` AND `AGENTS.md` | reuse `_parseVaultIntegration`; flag when the two files disagree. Short-circuits to PASS when the two are the SAME file by construction (symlink / same inode / byte-identical generated copy) |
43
43
  | 8 | `generated-rule-staleness` *(WARN only)* | `.claude/rules/*.md` with `auto-generated: true` frontmatter | extract `learning-key`; WARN when the key is absent from `.orchestrator/metrics/learnings.jsonl` or its learning's `expires_at` is in the past; skipped silently when no auto-generated rules exist |
44
- | 9 | `rule-scoping` | `.claude/rules/*.md` frontmatter + `## See Also` footers + `.claude/rules/<name>.md` citations in `CLAUDE.md`/`AGENTS.md` | five probes: `paths:` frontmatter (error), cited-but-missing rule citations (error), zero-match `globs:` patterns (warn), foreign PascalCase glob tokens (warn), unreadable rule files (warn — surfaced instead of silently skipped); skipped silently when `.claude/rules/` is absent |
44
+ | 9 | `rule-scoping` | `.claude/rules/*.md` frontmatter + `## See Also` footers + `.claude/rules/<name>.md` citations in `CLAUDE.md`/`AGENTS.md` | six probes: `paths:` frontmatter (error), cited-but-missing rule citations (error), zero-match `globs:` patterns (warn), foreign PascalCase glob tokens (warn), unreadable rule files (warn — surfaced instead of silently skipped), declared fleet-intent globs (**note**, never warn); skipped silently when `.claude/rules/` is absent |
45
45
  | 10 | `docs-parity` | `docs/components.md` count-claims, Session Config key parity (`docs/session-config-template.md` vs `docs/session-config-reference.md`), `.claude/metrics/` stale-path references in `docs/*.md` / `docs/examples/*.md` | three sub-checks (a/b/c) — see below; skipped silently when `docs/components.md` is absent |
46
46
 
47
47
  Check 3 deliberately scopes to forward-looking sections. Mentions inside "Recently Closed", "Decisions", "Archive", etc. describe history and must not be flagged.
@@ -65,7 +65,9 @@ Check 7 (issue #600) is the **only** check that intentionally reads BOTH instruc
65
65
 
66
66
  The same predicate guards Check 9's probe 2a, which iterates `['CLAUDE.md', 'AGENTS.md']`: with an aliased `AGENTS.md` it scans `CLAUDE.md` only, because otherwise every dangling `.claude/rules/*.md` citation would be reported TWICE — one defect, two errors, and in `--mode strict` a doubled blocking surface against a file the operator is not allowed to edit.
67
67
 
68
- Check 9 (`rule-scoping`) validates `.claude/rules/*.md` frontmatter against the `scripts/lib/rule-loader.mjs` contract, catching the class of defect where a rule silently drifts out of the activation pipeline the loader actually implements. Four probes: **(1) paths-presence** — a top-level `paths:` frontmatter key is not a key `rule-loader.mjs` recognises (it only reads `globs:`), so a rule with `paths:` silently loads ALWAYS-ON regardless of intended file scope; flagged as an error. **(2) cited-but-missing** — `(a)` `.claude/rules/<name>.md` citations inside `CLAUDE.md`/`AGENTS.md` that don't resolve to a file on disk, and `(b)` bare `<name>.md` tokens in a rule's own `## See Also` footer that don't exist as sibling rule files (tokens carrying a path separator, e.g. `../../skills/_shared/state-ownership.md`, are cross-directory references and explicitly out of scope); both flagged as errors. **(3) zero-match-globs** — a `globs:` pattern matching zero files in `git ls-files` (falls back to a manual directory walk when git is unavailable); flagged as a WARNING, not an error, because library/exemplar repos legitimately carry dead stack rules (this repo alone carries ~37 by design — Swift/Next.js/Supabase rules with no matching files in a pure-Node-ESM codebase). **(4) foreign-glob** — a glob pattern containing a PascalCase product-like token (regex `[A-Z][a-z]+[A-Z]`, e.g. `WalkAITalkieTests`) — a likely copy-paste leftover from another project's rule scope; flagged as a WARNING. Glob matching reuses the same picomatch-with-inline-fallback resolution `scripts/lib/rule-loader.mjs` uses (`parseGlobsFrontmatter` is imported directly; the picomatch resolution itself is duplicated locally since `rule-loader.mjs` does not export a public matcher function). The check is skipped silently (no `checks_run` entry, no `checks_skipped` entry) when `.claude/rules/` is absent, or explicitly via `--skip-rule-scoping`.
68
+ Check 9 (`rule-scoping`) validates `.claude/rules/*.md` frontmatter against the `scripts/lib/rule-loader.mjs` contract, catching the class of defect where a rule silently drifts out of the activation pipeline the loader actually implements. Six probes: **(1) paths-presence** — a top-level `paths:` frontmatter key is not a key `rule-loader.mjs` recognises (it only reads `globs:`), so a rule with `paths:` silently loads ALWAYS-ON regardless of intended file scope; flagged as an error. **(2) cited-but-missing** — `(a)` `.claude/rules/<name>.md` citations inside `CLAUDE.md`/`AGENTS.md` that don't resolve to a file on disk, and `(b)` bare `<name>.md` tokens in a rule's own `## See Also` footer that don't exist as sibling rule files (tokens carrying a path separator, e.g. `../../skills/_shared/state-ownership.md`, are cross-directory references and explicitly out of scope); both flagged as errors. **(3) zero-match-globs** — a `globs:` pattern matching zero files in `git ls-files` (falls back to a manual directory walk when git is unavailable); flagged as a WARNING, not an error, because library/exemplar repos legitimately carry dead stack rules. **(4) foreign-glob** — a glob pattern containing a PascalCase product-like token (regex `[A-Z][a-z]+[A-Z]`, e.g. `WalkAITalkieTests`) — a likely copy-paste leftover from another project's rule scope; flagged as a WARNING. **(5) unreadable-file** — a rule file that could not be read (permissions, a race with a concurrent delete); flagged as a WARNING rather than silently skipped, so a completeness audit does not drop files, and so an unreadable file cannot brick the gate under `mode: hard`. **(6) fleet-intent-glob** — a `globs:` pattern DECLARED as fleet intent goes to `notes[]` (see below), never to `warnings[]`, and the declaration also settles probe 4 for that same pattern: a pattern the rule author declared is by construction not a copy-paste leftover. Glob matching reuses the same picomatch-with-inline-fallback resolution `scripts/lib/rule-loader.mjs` uses (`parseGlobsFrontmatter` is imported directly; the picomatch resolution itself is duplicated locally since `rule-loader.mjs` does not export a public matcher function). The check is skipped silently (no `checks_run` entry, no `checks_skipped` entry) when `.claude/rules/` is absent, or explicitly via `--skip-rule-scoping`.
69
+
70
+ **Fleet-intent declaration (Check 9, probe 6).** A rule may declare that a `globs:` pattern is aimed at CONSUMER repos rather than this one, via its own frontmatter key `fleet-intent-globs:`. Accepted forms are block style (`fleet-intent-globs:` plus indented ` - "<pattern>"` lines), flow style (`fleet-intent-globs: ["a", "b"]`) and a single inline value; surrounding quotes are stripped, blank and `#` comment lines are ignored. The key is inert for rule loading — `rule-loader.mjs`'s `parseGlobsFrontmatter` skips unknown top-level keys and their continuation lines. A declared pattern is reported in `notes[]` with its file, the pattern, whether it matches `≥1` or `0` tracked files here, and the declaration `source` (`frontmatter`). The grain is per pattern AND per rule file: declaring one glob amnesties neither a sibling glob in the same rule nor the same pattern in another rule. There is deliberately NO checker-side exemption list: a rule that does not declare the pattern itself is not exempt, so the declaration always sits with the rule it describes.
69
71
 
70
72
  Check 10 (`docs-parity`, issue #780) validates the public docs surface against live repository state via three sub-checks, all reported under the single `docs-parity` check id: **(a) count-claims** — `docs/components.md`'s own heading counts ("## Skills (N user-facing)", "## Commands (N)", "## Agents (N typed sub-agents)", "## Hook event types (N)") are compared against the SAME actual on-disk derivation the surface-count family (Check 5) uses (`countSkills`/the commands-dir listing/`countAgents`/`readHookCounts`) — NOT Check 5's `claimRe` regexes, which are tuned for CLAUDE.md/README prose phrasing and verifiably do not match `components.md`'s own heading convention; a dedicated set of regexes tailored to the doc's actual authored structure is used instead. **(b) config-block-parity** — top-level Session Config keys documented in `docs/session-config-template.md` (opt-in baseline, via the Check 6 'last'-occurrence extractor) are compared against `docs/session-config-reference.md`; a key is considered documented in the reference when it appears in a `yaml` fence (any indent), a `##`/`###`/`####` heading, or the first cell of a markdown table row (`` | `key-name` | ... ``) — the reference's dominant documentation shape is one table row per field, so a naive fence-only extraction under-counts what it actually documents. Missing keys surface as `docs-parity` errors attributed to `docs/session-config-reference.md`. **(c) metrics-path-liveness** — any `.claude/metrics/` path reference (stale; canonical is `.orchestrator/metrics/`) found in root `docs/*.md` or `docs/examples/*.md` surfaces as an error at its exact file/line. The whole check is skipped silently (no `checks_run` entry, no `checks_skipped` entry) when `docs/components.md` is absent, or explicitly via `--skip-docs-parity`.
71
73
 
@@ -124,10 +126,15 @@ Environment:
124
126
  "warnings": [
125
127
  { "check": "<name>", "file": "<relative path>", "line": N, "message": "<human>", "extracted": "<raw text>" }
126
128
  ],
129
+ "notes": [
130
+ { "check": "rule-scoping", "probe": "fleet-intent-glob", "file": "<relative path>", "line": N, "message": "<human>", "extracted": "<pattern>", "source": "frontmatter" }
131
+ ],
127
132
  "command_count": { "actual": N }
128
133
  }
129
134
  ```
130
135
 
136
+ `notes[]` is the THIRD output category, alongside `errors[]` and `warnings[]`: a finding that is reported but asks for no action. It never affects `status` or the exit code, and it is always present (empty array when nothing was noted, including on the short-circuit "no scope files matched" path). Its only producer today is Check 9's fleet-intent-glob probe.
137
+
131
138
  The `resolved_path` / `resolved_kind` pair surfaces the alias resolution outcome (issue #33 AC2) so users on either platform can audit which instruction file the checker scanned. `kind: 'claude'` for `CLAUDE.md`, `kind: 'agents'` for `AGENTS.md`, `null` when neither was found.
132
139
 
133
140
  When `command-count` fires a drift error, the error object also carries `"command_count": { "actual": N, "claimed": M }` for easy programmatic diffing.