session-orchestrator 3.16.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.
- package/.claude-plugin/marketplace.json +29 -0
- package/.claude-plugin/plugin.json +18 -0
- package/.codex-plugin/agents/explorer.toml +14 -0
- package/.codex-plugin/agents/session-reviewer.toml +23 -0
- package/.codex-plugin/agents/wave-worker.toml +15 -0
- package/.codex-plugin/config.toml +20 -0
- package/.codex-plugin/plugin.json +37 -0
- package/.cursor/rules/000-session-orchestrator.mdc +73 -0
- package/.cursor/rules/010-session-workflow.mdc +170 -0
- package/.cursor/rules/020-quality-gates.mdc +128 -0
- package/.cursor/rules/030-wave-execution.mdc +216 -0
- package/.cursor/rules/040-discovery.mdc +242 -0
- package/.cursor/rules/050-plan.mdc +235 -0
- package/.cursor/rules/060-evolve.mdc +232 -0
- package/.cursor/rules/070-gitlab-ops.mdc +246 -0
- package/.cursor/rules/080-ecosystem-health.mdc +145 -0
- package/.mcp.json +8 -0
- package/CHANGELOG.md +1544 -0
- package/LICENSE +21 -0
- package/NOTICE +64 -0
- package/README.md +242 -0
- package/SECURITY.md +90 -0
- package/agents/AGENTS.md +136 -0
- package/agents/analyst.md +99 -0
- package/agents/architect-reviewer.md +93 -0
- package/agents/code-implementer.md +106 -0
- package/agents/db-specialist.md +104 -0
- package/agents/dialectic-deriver.md +139 -0
- package/agents/docs-writer.md +113 -0
- package/agents/eval-judge.md +146 -0
- package/agents/memory-proposal-collector.md +297 -0
- package/agents/qa-strategist.md +102 -0
- package/agents/schemas/analyst.schema.json +46 -0
- package/agents/schemas/architect-reviewer.schema.json +50 -0
- package/agents/schemas/code-implementer.schema.json +61 -0
- package/agents/schemas/db-specialist.schema.json +80 -0
- package/agents/schemas/docs-writer.schema.json +56 -0
- package/agents/schemas/persona-panel-sidecar.schema.json +245 -0
- package/agents/schemas/qa-strategist.schema.json +46 -0
- package/agents/schemas/security-reviewer.schema.json +86 -0
- package/agents/schemas/session-reviewer.schema.json +69 -0
- package/agents/schemas/test-writer.schema.json +69 -0
- package/agents/schemas/ui-developer.schema.json +90 -0
- package/agents/schemas/ux-evaluator.schema.json +51 -0
- package/agents/security-reviewer.md +236 -0
- package/agents/session-reviewer.md +201 -0
- package/agents/skill-applied-judge.md +122 -0
- package/agents/test-writer.md +123 -0
- package/agents/ui-developer.md +109 -0
- package/agents/ux-evaluator.md +161 -0
- package/assets/icon.svg +11 -0
- package/assets/og-card.png +0 -0
- package/assets/og-card.svg +47 -0
- package/commands/autopilot-multi.md +74 -0
- package/commands/autopilot.md +80 -0
- package/commands/bootstrap.md +56 -0
- package/commands/brainstorm.md +48 -0
- package/commands/close.md +24 -0
- package/commands/debug.md +36 -0
- package/commands/discovery.md +32 -0
- package/commands/dispatcher.md +59 -0
- package/commands/eval.md +28 -0
- package/commands/evolve.md +10 -0
- package/commands/go.md +41 -0
- package/commands/grill.md +45 -0
- package/commands/harness-audit.md +26 -0
- package/commands/memory-cleanup.md +25 -0
- package/commands/persona-panel.md +121 -0
- package/commands/plan.md +15 -0
- package/commands/portfolio.md +97 -0
- package/commands/reconcile.md +23 -0
- package/commands/repo-audit.md +24 -0
- package/commands/session.md +30 -0
- package/commands/spinout.md +15 -0
- package/commands/sunset-review.md +27 -0
- package/commands/templates-ack.md +96 -0
- package/commands/test.md +97 -0
- package/docs/README.md +105 -0
- package/docs/USER-GUIDE.md +1403 -0
- package/docs/ci-setup.md +81 -0
- package/docs/codex-setup.md +142 -0
- package/docs/components.md +74 -0
- package/docs/cursor-setup.md +104 -0
- package/docs/events-schema.md +81 -0
- package/docs/migration-v3.md +148 -0
- package/docs/owner-config-schema.md +154 -0
- package/docs/persona-panel.md +433 -0
- package/docs/pi-setup.md +115 -0
- package/docs/plugin-architecture-v3.md +296 -0
- package/docs/pm-skills-marketplace.md +114 -0
- package/docs/policy-cache-validation-2026-04-28.md +118 -0
- package/docs/rule-authoring.md +316 -0
- package/docs/session-config-reference.md +1439 -0
- package/docs/session-config-template.md +961 -0
- package/docs/vault-docs-architecture.md +297 -0
- package/hooks/_lib/lock-bootstrap.mjs +272 -0
- package/hooks/_lib/lock-reconcile.mjs +93 -0
- package/hooks/_lib/profile-gate.mjs +95 -0
- package/hooks/_lib/transcript-history.mjs +211 -0
- package/hooks/agent-teams-h3-test.sh +362 -0
- package/hooks/config-protection.mjs +0 -0
- package/hooks/cwd-change-restore.mjs +131 -0
- package/hooks/enforce-commands.mjs +179 -0
- package/hooks/enforce-scope.mjs +273 -0
- package/hooks/hooks-codex.json +60 -0
- package/hooks/hooks-cursor.json +15 -0
- package/hooks/hooks-pi.json +115 -0
- package/hooks/hooks.json +215 -0
- package/hooks/loop-guard.mjs +260 -0
- package/hooks/on-session-end.mjs +217 -0
- package/hooks/on-session-start.mjs +660 -0
- package/hooks/on-stop.mjs +294 -0
- package/hooks/operator-steer.mjs +64 -0
- package/hooks/post-edit-validate.mjs +225 -0
- package/hooks/post-subagent-discovery-validator.mjs +398 -0
- package/hooks/post-tool-batch-wave-signal.mjs +328 -0
- package/hooks/post-tool-failure-corrective-context.mjs +248 -0
- package/hooks/post-tooluse-frontend-slop.mjs +184 -0
- package/hooks/pre-bash-destructive-guard.mjs +515 -0
- package/hooks/pre-bash-memory-propose-audit.mjs +206 -0
- package/hooks/pre-bash-staging-fence.mjs +223 -0
- package/hooks/pre-bash-templates-first.mjs +404 -0
- package/hooks/run-node.sh +72 -0
- package/hooks/skill-invocation-telemetry.mjs +99 -0
- package/hooks/subagent-telemetry.mjs +249 -0
- package/hooks/wave-scope-commit-guard.mjs +191 -0
- package/monitors/monitors.json +14 -0
- package/output-styles/finding-report.md +48 -0
- package/output-styles/session-report.md +53 -0
- package/output-styles/wave-summary.md +38 -0
- package/package.json +94 -0
- package/pi/extensions/session-orchestrator.ts +25 -0
- package/pi/prompts/autopilot-multi.md +12 -0
- package/pi/prompts/autopilot.md +12 -0
- package/pi/prompts/bootstrap.md +12 -0
- package/pi/prompts/brainstorm.md +12 -0
- package/pi/prompts/close.md +11 -0
- package/pi/prompts/debug.md +12 -0
- package/pi/prompts/discovery.md +12 -0
- package/pi/prompts/dispatcher.md +12 -0
- package/pi/prompts/eval.md +12 -0
- package/pi/prompts/evolve.md +12 -0
- package/pi/prompts/go.md +12 -0
- package/pi/prompts/grill.md +12 -0
- package/pi/prompts/harness-audit.md +12 -0
- package/pi/prompts/memory-cleanup.md +12 -0
- package/pi/prompts/persona-panel.md +12 -0
- package/pi/prompts/plan.md +12 -0
- package/pi/prompts/portfolio.md +12 -0
- package/pi/prompts/reconcile.md +12 -0
- package/pi/prompts/repo-audit.md +12 -0
- package/pi/prompts/session.md +12 -0
- package/pi/prompts/spinout.md +12 -0
- package/pi/prompts/sunset-review.md +12 -0
- package/pi/prompts/templates-ack.md +12 -0
- package/pi/prompts/test.md +12 -0
- package/rules/_index.md +51 -0
- package/rules/always-on/commit-discipline.md +26 -0
- package/rules/always-on/npm-quality-gates.md +26 -0
- package/rules/always-on/parallel-sessions.md +43 -0
- package/rules/opt-in-domain/prompt-caching.md +270 -0
- package/rules/opt-in-stack/backend-data.md +188 -0
- package/rules/opt-in-stack/backend.md +390 -0
- package/rules/opt-in-stack/frontend.md +98 -0
- package/rules/opt-in-stack/security-web.md +194 -0
- package/rules/opt-in-stack/swift.md +65 -0
- package/scripts/archive-closed-prds.mjs +416 -0
- package/scripts/autopilot-multi.mjs +802 -0
- package/scripts/autopilot.mjs +383 -0
- package/scripts/backfill-abandoned-sessions.mjs +265 -0
- package/scripts/backfill-learnings-expires.mjs +196 -0
- package/scripts/backfill-learnings.mjs +203 -0
- package/scripts/backfill-sessions.mjs +282 -0
- package/scripts/check-doc-consistency.sh +279 -0
- package/scripts/check-package-manager.mjs +445 -0
- package/scripts/ci/assert-vitest-green.mjs +267 -0
- package/scripts/codex-install.mjs +435 -0
- package/scripts/compute-grounding-injection.sh +186 -0
- package/scripts/cursor-install.mjs +113 -0
- package/scripts/dialectic-deriver.mjs +573 -0
- package/scripts/emit-event.mjs +160 -0
- package/scripts/emit-session.mjs +212 -0
- package/scripts/eval-session.mjs +262 -0
- package/scripts/export-hw-learnings.mjs +437 -0
- package/scripts/gc-stale-worktrees.mjs +666 -0
- package/scripts/generate-pi-prompts.mjs +127 -0
- package/scripts/harness-audit.mjs +287 -0
- package/scripts/lib/agent-frontmatter.mjs +266 -0
- package/scripts/lib/agent-output-schema.mjs +166 -0
- package/scripts/lib/agent-status.mjs +303 -0
- package/scripts/lib/ajv-loader.mjs +34 -0
- package/scripts/lib/auto-dialectic.mjs +382 -0
- package/scripts/lib/auto-dream.mjs +471 -0
- package/scripts/lib/autonomy/suitability.mjs +212 -0
- package/scripts/lib/autopilot/dep-graph.mjs +417 -0
- package/scripts/lib/autopilot/durable-telemetry.mjs +121 -0
- package/scripts/lib/autopilot/flags.mjs +104 -0
- package/scripts/lib/autopilot/kill-switches.mjs +174 -0
- package/scripts/lib/autopilot/loop.mjs +320 -0
- package/scripts/lib/autopilot/mr-draft.mjs +520 -0
- package/scripts/lib/autopilot/multi-killswitch.mjs +184 -0
- package/scripts/lib/autopilot/recent-runs.mjs +106 -0
- package/scripts/lib/autopilot/stall-sampler.mjs +97 -0
- package/scripts/lib/autopilot/telemetry.mjs +224 -0
- package/scripts/lib/autopilot/worktree-pipeline.mjs +605 -0
- package/scripts/lib/autopilot-telemetry.mjs +11 -0
- package/scripts/lib/autopilot.mjs +39 -0
- package/scripts/lib/backlog-scan.mjs +179 -0
- package/scripts/lib/bootstrap-lock-freshness.mjs +260 -0
- package/scripts/lib/bootstrap-lock-refresh.mjs +186 -0
- package/scripts/lib/build-live-signals.mjs +150 -0
- package/scripts/lib/ci-status-banner.mjs +425 -0
- package/scripts/lib/claude-md-budget-lint.mjs +246 -0
- package/scripts/lib/cli-flags.mjs +158 -0
- package/scripts/lib/codex/plugin-contract.mjs +610 -0
- package/scripts/lib/cold-start-detector.mjs +240 -0
- package/scripts/lib/command-blocker.mjs +458 -0
- package/scripts/lib/common.mjs +333 -0
- package/scripts/lib/config/auto-dream.mjs +77 -0
- package/scripts/lib/config/block-header.mjs +94 -0
- package/scripts/lib/config/broken-window.mjs +114 -0
- package/scripts/lib/config/coercers.mjs +248 -0
- package/scripts/lib/config/cold-start.mjs +92 -0
- package/scripts/lib/config/config-protection.mjs +120 -0
- package/scripts/lib/config/cross-repo.mjs +104 -0
- package/scripts/lib/config/custom-phases.mjs +213 -0
- package/scripts/lib/config/dialectic.mjs +92 -0
- package/scripts/lib/config/discovery-validator.mjs +75 -0
- package/scripts/lib/config/dispatcher-autonomy-capture.mjs +240 -0
- package/scripts/lib/config/dispatcher-autonomy.mjs +152 -0
- package/scripts/lib/config/docs-orchestrator.mjs +90 -0
- package/scripts/lib/config/docs-staleness.mjs +96 -0
- package/scripts/lib/config/drift-check.mjs +155 -0
- package/scripts/lib/config/eval.mjs +130 -0
- package/scripts/lib/config/events-rotation.mjs +74 -0
- package/scripts/lib/config/evolve.mjs +308 -0
- package/scripts/lib/config/frontend-slop-hook.mjs +104 -0
- package/scripts/lib/config/gitlab-portfolio.mjs +150 -0
- package/scripts/lib/config/handover-gate.mjs +106 -0
- package/scripts/lib/config/host-paths.mjs +76 -0
- package/scripts/lib/config/io.mjs +54 -0
- package/scripts/lib/config/loop-guard.mjs +117 -0
- package/scripts/lib/config/memory.mjs +150 -0
- package/scripts/lib/config/persona-gate-wave.mjs +258 -0
- package/scripts/lib/config/reconcile.mjs +205 -0
- package/scripts/lib/config/section-extractor.mjs +100 -0
- package/scripts/lib/config/skill-evolution.mjs +112 -0
- package/scripts/lib/config/slopcheck.mjs +99 -0
- package/scripts/lib/config/state-md-lock.mjs +83 -0
- package/scripts/lib/config/templates-first.mjs +94 -0
- package/scripts/lib/config/test.mjs +113 -0
- package/scripts/lib/config/vault-integration.mjs +201 -0
- package/scripts/lib/config/vault-mirror-quality.mjs +99 -0
- package/scripts/lib/config/vault-staleness.mjs +84 -0
- package/scripts/lib/config/vault-sync.mjs +96 -0
- package/scripts/lib/config/verification-auto-fix.mjs +84 -0
- package/scripts/lib/config/wave-reviewers.mjs +133 -0
- package/scripts/lib/config-schema.mjs +345 -0
- package/scripts/lib/config.mjs +474 -0
- package/scripts/lib/convergence-monitor.mjs +389 -0
- package/scripts/lib/coordinator-snapshot.mjs +371 -0
- package/scripts/lib/crypto-digest-utils.mjs +91 -0
- package/scripts/lib/discovery/helpers.mjs +127 -0
- package/scripts/lib/discovery/triage-state.mjs +279 -0
- package/scripts/lib/dispatcher/cli.mjs +257 -0
- package/scripts/lib/dispatcher/enumerate.mjs +243 -0
- package/scripts/lib/dispatcher/rank.mjs +363 -0
- package/scripts/lib/ecosystem-health.mjs +224 -0
- package/scripts/lib/ecosystem-wizard/ci-detector.mjs +18 -0
- package/scripts/lib/ecosystem-wizard/config-parser.mjs +54 -0
- package/scripts/lib/ecosystem-wizard/config-writer.mjs +287 -0
- package/scripts/lib/ecosystem-wizard/package-manager-detector.mjs +42 -0
- package/scripts/lib/ecosystem-wizard/wizard-prompt.mjs +246 -0
- package/scripts/lib/ecosystem-wizard.mjs +48 -0
- package/scripts/lib/env-check.mjs +89 -0
- package/scripts/lib/eval/engine.mjs +605 -0
- package/scripts/lib/eval/judge.mjs +433 -0
- package/scripts/lib/eval/report.mjs +367 -0
- package/scripts/lib/eval/schema.mjs +618 -0
- package/scripts/lib/eval/session-resolve.mjs +137 -0
- package/scripts/lib/eval/sink.mjs +77 -0
- package/scripts/lib/events-rotation.mjs +86 -0
- package/scripts/lib/events-schema.mjs +81 -0
- package/scripts/lib/events.mjs +80 -0
- package/scripts/lib/evolve/autonomy-verdict.mjs +461 -0
- package/scripts/lib/evolve/autopilot-effectiveness.mjs +293 -0
- package/scripts/lib/exclusivity-matrix.mjs +68 -0
- package/scripts/lib/fetch-baseline.mjs +311 -0
- package/scripts/lib/file-lock.mjs +512 -0
- package/scripts/lib/frontend-detect/detect.mjs +138 -0
- package/scripts/lib/frontend-detect/rules.mjs +295 -0
- package/scripts/lib/frontmatter-guard.mjs +241 -0
- package/scripts/lib/gates/echo-stub-detect.mjs +39 -0
- package/scripts/lib/gates/gate-baseline.mjs +42 -0
- package/scripts/lib/gates/gate-full.mjs +85 -0
- package/scripts/lib/gates/gate-helpers.mjs +231 -0
- package/scripts/lib/gates/gate-incremental.mjs +76 -0
- package/scripts/lib/gates/gate-per-file.mjs +55 -0
- package/scripts/lib/gitlab-ops/stale-mr-sweep.mjs +447 -0
- package/scripts/lib/gitlab-portfolio/aggregator.mjs +383 -0
- package/scripts/lib/gitlab-portfolio/cli.mjs +428 -0
- package/scripts/lib/gitlab-portfolio/markdown-writer.mjs +289 -0
- package/scripts/lib/gitlab-portfolio/vcs-detect.mjs +182 -0
- package/scripts/lib/handover-gate.mjs +222 -0
- package/scripts/lib/hardening.mjs +43 -0
- package/scripts/lib/hardware-pattern-detector.mjs +238 -0
- package/scripts/lib/harness-audit/categories/category1.mjs +123 -0
- package/scripts/lib/harness-audit/categories/category2.mjs +145 -0
- package/scripts/lib/harness-audit/categories/category3.mjs +143 -0
- package/scripts/lib/harness-audit/categories/category4.mjs +202 -0
- package/scripts/lib/harness-audit/categories/category5.mjs +152 -0
- package/scripts/lib/harness-audit/categories/category6.mjs +211 -0
- package/scripts/lib/harness-audit/categories/category7.mjs +125 -0
- package/scripts/lib/harness-audit/categories/category8.mjs +328 -0
- package/scripts/lib/harness-audit/categories/category9.mjs +294 -0
- package/scripts/lib/harness-audit/categories/helpers.mjs +165 -0
- package/scripts/lib/harness-audit/categories.mjs +19 -0
- package/scripts/lib/historical-guard.mjs +15 -0
- package/scripts/lib/host-identity.mjs +262 -0
- package/scripts/lib/instruction-budget-guard.mjs +332 -0
- package/scripts/lib/io.mjs +304 -0
- package/scripts/lib/issue-close-strip-labels.mjs +161 -0
- package/scripts/lib/language-mappers/README.md +57 -0
- package/scripts/lib/language-mappers/index.mjs +165 -0
- package/scripts/lib/language-mappers/markdown.mjs +149 -0
- package/scripts/lib/language-mappers/python.mjs +249 -0
- package/scripts/lib/language-mappers/swift.mjs +201 -0
- package/scripts/lib/language-mappers/typescript.mjs +433 -0
- package/scripts/lib/learnings/expiry-sweep.mjs +164 -0
- package/scripts/lib/learnings/filters.mjs +43 -0
- package/scripts/lib/learnings/io.mjs +255 -0
- package/scripts/lib/learnings/schema.mjs +518 -0
- package/scripts/lib/learnings/surface.mjs +207 -0
- package/scripts/lib/learnings.mjs +42 -0
- package/scripts/lib/lock-reaper.mjs +648 -0
- package/scripts/lib/locks/index.mjs +31 -0
- package/scripts/lib/locks/lock-body.mjs +62 -0
- package/scripts/lib/locks/staging-fence-lock.mjs +267 -0
- package/scripts/lib/locks/state-md-lock.mjs +351 -0
- package/scripts/lib/loop-readiness-banner.mjs +144 -0
- package/scripts/lib/memory-banner.mjs +478 -0
- package/scripts/lib/memory-cleanup/worktree-sweep.mjs +108 -0
- package/scripts/lib/memory-cleanup-stamp.mjs +56 -0
- package/scripts/lib/memory-paths.mjs +31 -0
- package/scripts/lib/memory-proposals/collector.mjs +334 -0
- package/scripts/lib/memory-proposals/schema.mjs +289 -0
- package/scripts/lib/memory-proposals/sink.mjs +507 -0
- package/scripts/lib/memory-proposals/store.mjs +441 -0
- package/scripts/lib/mission-status-schema.mjs +114 -0
- package/scripts/lib/mode-selector/alternatives.mjs +64 -0
- package/scripts/lib/mode-selector/constants.mjs +29 -0
- package/scripts/lib/mode-selector/context-pressure.mjs +157 -0
- package/scripts/lib/mode-selector/rationale.mjs +55 -0
- package/scripts/lib/mode-selector/scoring.mjs +221 -0
- package/scripts/lib/mode-selector-accuracy.mjs +121 -0
- package/scripts/lib/mode-selector.mjs +160 -0
- package/scripts/lib/multi-provider-build/providers.mjs +64 -0
- package/scripts/lib/multi-provider-build/templating.mjs +130 -0
- package/scripts/lib/named-baseline-resolver.mjs +233 -0
- package/scripts/lib/named-vault-resolver.mjs +433 -0
- package/scripts/lib/owner-config/coerce.mjs +29 -0
- package/scripts/lib/owner-config/constants.mjs +21 -0
- package/scripts/lib/owner-config/defaults.mjs +50 -0
- package/scripts/lib/owner-config/error.mjs +19 -0
- package/scripts/lib/owner-config/index.mjs +13 -0
- package/scripts/lib/owner-config/merge.mjs +52 -0
- package/scripts/lib/owner-config/validate.mjs +259 -0
- package/scripts/lib/owner-config-banner.mjs +126 -0
- package/scripts/lib/owner-config-loader.mjs +159 -0
- package/scripts/lib/owner-config.example.yaml +72 -0
- package/scripts/lib/owner-config.mjs +28 -0
- package/scripts/lib/owner-interview.mjs +243 -0
- package/scripts/lib/owner-yaml.mjs +571 -0
- package/scripts/lib/package-manager.mjs +160 -0
- package/scripts/lib/path-utils.mjs +217 -0
- package/scripts/lib/peer-cards/merger.mjs +310 -0
- package/scripts/lib/peer-cards/reader.mjs +125 -0
- package/scripts/lib/peer-cards/schema.mjs +230 -0
- package/scripts/lib/peer-cards/staleness-banner.mjs +86 -0
- package/scripts/lib/peer-cards/writer.mjs +138 -0
- package/scripts/lib/peer-discovery.mjs +200 -0
- package/scripts/lib/persona-panel/catalog-loader.mjs +577 -0
- package/scripts/lib/persona-panel/consolidator.mjs +370 -0
- package/scripts/lib/persona-panel/persona-runner.mjs +375 -0
- package/scripts/lib/persona-panel/threshold.mjs +130 -0
- package/scripts/lib/pi-hook-bridge.mjs +328 -0
- package/scripts/lib/platform.mjs +266 -0
- package/scripts/lib/playwright-driver/runner.mjs +297 -0
- package/scripts/lib/plugin-root.mjs +210 -0
- package/scripts/lib/pre-dispatch-check.mjs +126 -0
- package/scripts/lib/product-repo-detect.mjs +121 -0
- package/scripts/lib/profiles/registry.mjs +176 -0
- package/scripts/lib/profiles/schema.mjs +209 -0
- package/scripts/lib/qg-command-drift-banner.mjs +88 -0
- package/scripts/lib/quality-gate/diagnostics.mjs +92 -0
- package/scripts/lib/quality-gate.mjs +536 -0
- package/scripts/lib/quality-gates-cache.mjs +228 -0
- package/scripts/lib/quality-gates-policy.mjs +95 -0
- package/scripts/lib/recommendations-v0.mjs +156 -0
- package/scripts/lib/reconcile/eligibility.mjs +203 -0
- package/scripts/lib/reconcile/emitter.mjs +244 -0
- package/scripts/lib/reconcile/engine.mjs +412 -0
- package/scripts/lib/reconcile/idempotency.mjs +239 -0
- package/scripts/lib/reconcile/renderer.mjs +211 -0
- package/scripts/lib/reconcile/writer.mjs +293 -0
- package/scripts/lib/reconcile-nudge-banner.mjs +284 -0
- package/scripts/lib/resource-probe/evaluate.mjs +190 -0
- package/scripts/lib/resource-probe/parsers.mjs +181 -0
- package/scripts/lib/resource-probe/probe-platform.mjs +300 -0
- package/scripts/lib/resource-probe.mjs +95 -0
- package/scripts/lib/rule-loader.mjs +552 -0
- package/scripts/lib/rules-sync.mjs +439 -0
- package/scripts/lib/scope-gate.mjs +496 -0
- package/scripts/lib/session-close-backfill.mjs +539 -0
- package/scripts/lib/session-discovery.mjs +256 -0
- package/scripts/lib/session-end/phase-skip.mjs +357 -0
- package/scripts/lib/session-end/worktree-cleanup.mjs +112 -0
- package/scripts/lib/session-id.mjs +362 -0
- package/scripts/lib/session-lock.mjs +703 -0
- package/scripts/lib/session-registry.mjs +355 -0
- package/scripts/lib/session-schema/aliases.mjs +71 -0
- package/scripts/lib/session-schema/constants.mjs +115 -0
- package/scripts/lib/session-schema/normalizer.mjs +66 -0
- package/scripts/lib/session-schema/timestamps.mjs +64 -0
- package/scripts/lib/session-schema/validator.mjs +453 -0
- package/scripts/lib/session-schema.mjs +71 -0
- package/scripts/lib/session-token-rollup.mjs +137 -0
- package/scripts/lib/sessions-staleness-banner.mjs +247 -0
- package/scripts/lib/skill-evolution/blast-radius-classifier.mjs +114 -0
- package/scripts/lib/skill-evolution/candidate-intake.mjs +270 -0
- package/scripts/lib/skill-evolution/config-validation-gate.mjs +279 -0
- package/scripts/lib/skill-evolution/engine.mjs +719 -0
- package/scripts/lib/skill-evolution/idempotency.mjs +279 -0
- package/scripts/lib/skill-evolution/mr-opener.mjs +507 -0
- package/scripts/lib/skill-health/join.mjs +181 -0
- package/scripts/lib/skill-health/score.mjs +123 -0
- package/scripts/lib/skill-invocations-schema.mjs +214 -0
- package/scripts/lib/skill-judge.mjs +348 -0
- package/scripts/lib/skill-judgments-schema.mjs +264 -0
- package/scripts/lib/slopcheck.mjs +501 -0
- package/scripts/lib/soul-resolve.mjs +118 -0
- package/scripts/lib/spiral-carryover.mjs +495 -0
- package/scripts/lib/state-md/body-sections.mjs +851 -0
- package/scripts/lib/state-md/frontmatter-mutators.mjs +453 -0
- package/scripts/lib/state-md/mission-status.mjs +247 -0
- package/scripts/lib/state-md/recommendations.mjs +57 -0
- package/scripts/lib/state-md/yaml-parser.mjs +234 -0
- package/scripts/lib/state-md-peer-guard.mjs +232 -0
- package/scripts/lib/state-md.mjs +53 -0
- package/scripts/lib/subagents-schema.mjs +309 -0
- package/scripts/lib/sunset/walker.mjs +1192 -0
- package/scripts/lib/test-runner/artifact-paths.mjs +94 -0
- package/scripts/lib/test-runner/fingerprint.mjs +33 -0
- package/scripts/lib/test-runner/issue-reconcile.mjs +770 -0
- package/scripts/lib/tmux-layout/layouts.mjs +224 -0
- package/scripts/lib/tmux-layout/telemetry-stats.mjs +100 -0
- package/scripts/lib/tmux-layout/telemetry.mjs +88 -0
- package/scripts/lib/tmux-layout/tmux-shell.mjs +82 -0
- package/scripts/lib/tmux-layout/vcs-detector.mjs +88 -0
- package/scripts/lib/validate/check-agents.mjs +457 -0
- package/scripts/lib/validate/check-codex-plugin.mjs +37 -0
- package/scripts/lib/validate/check-commands.mjs +148 -0
- package/scripts/lib/validate/check-component-paths.mjs +112 -0
- package/scripts/lib/validate/check-dead-bridge.mjs +180 -0
- package/scripts/lib/validate/check-hooks-symmetry.mjs +258 -0
- package/scripts/lib/validate/check-json-files.mjs +116 -0
- package/scripts/lib/validate/check-owner-leakage.mjs +1011 -0
- package/scripts/lib/validate/check-path-utils-canary.mjs +175 -0
- package/scripts/lib/validate/check-peekaboo-driver-canary.mjs +201 -0
- package/scripts/lib/validate/check-pi-package.mjs +110 -0
- package/scripts/lib/validate/check-pi-prompts.mjs +43 -0
- package/scripts/lib/validate/check-playwright-mcp-canary.mjs +154 -0
- package/scripts/lib/validate/check-plugin-json.mjs +96 -0
- package/scripts/lib/validate/check-plugin-monitors.mjs +206 -0
- package/scripts/lib/validate/check-plugin-schema.mjs +137 -0
- package/scripts/lib/validate/check-rules.mjs +143 -0
- package/scripts/lib/validate/check-session-plan-routing.mjs +154 -0
- package/scripts/lib/validate/check-test-fixture-shapes.mjs +280 -0
- package/scripts/lib/validate/check-unicode-safety.mjs +533 -0
- package/scripts/lib/validate/confidential-names.mjs +169 -0
- package/scripts/lib/validate/dead-bridge-corpus.mjs +141 -0
- package/scripts/lib/validate/dead-bridge-detectors.mjs +568 -0
- package/scripts/lib/validate/tier-inference.mjs +100 -0
- package/scripts/lib/validate-vendored-rules.mjs +519 -0
- package/scripts/lib/vault-archive.mjs +404 -0
- package/scripts/lib/vault-backfill/glab.mjs +164 -0
- package/scripts/lib/vault-backfill/manifest.mjs +75 -0
- package/scripts/lib/vault-backfill/template.mjs +130 -0
- package/scripts/lib/vault-consolidate-fs.mjs +331 -0
- package/scripts/lib/vault-migration-rules.mjs +155 -0
- package/scripts/lib/vault-mirror/auto-commit.mjs +203 -0
- package/scripts/lib/vault-mirror/namespace.mjs +152 -0
- package/scripts/lib/vault-mirror/process.mjs +567 -0
- package/scripts/lib/vault-mirror/pseudonym-map.mjs +164 -0
- package/scripts/lib/vault-mirror/render-learnings.mjs +201 -0
- package/scripts/lib/vault-mirror/render-sessions.mjs +367 -0
- package/scripts/lib/vault-mirror/render.mjs +8 -0
- package/scripts/lib/vault-mirror/utils.mjs +217 -0
- package/scripts/lib/vault-relocation-rules.mjs +555 -0
- package/scripts/lib/vault-repo-backfill.mjs +235 -0
- package/scripts/lib/vault-staleness-banner.mjs +142 -0
- package/scripts/lib/vault-status/board-writer.mjs +769 -0
- package/scripts/lib/vault-status/narrative-mirror.mjs +544 -0
- package/scripts/lib/vault-sync-baseline.mjs +152 -0
- package/scripts/lib/wave-context.mjs +29 -0
- package/scripts/lib/wave-executor/pool.mjs +248 -0
- package/scripts/lib/wave-resource-gate.mjs +204 -0
- package/scripts/lib/wave-sizing.mjs +75 -0
- package/scripts/lib/webhook-url.mjs +105 -0
- package/scripts/lib/workspace.mjs +198 -0
- package/scripts/lib/worktree/constants.mjs +35 -0
- package/scripts/lib/worktree/index.mjs +17 -0
- package/scripts/lib/worktree/lifecycle.mjs +287 -0
- package/scripts/lib/worktree/listing.mjs +118 -0
- package/scripts/lib/worktree/meta.mjs +64 -0
- package/scripts/lib/worktree-freshness.mjs +313 -0
- package/scripts/lib/worktree.mjs +15 -0
- package/scripts/lifecycle-sim-v6.mjs +347 -0
- package/scripts/lock-reaper.mjs +185 -0
- package/scripts/mcp-server.sh +241 -0
- package/scripts/measure-policy-cache-effectiveness.mjs +427 -0
- package/scripts/memory-propose.mjs +464 -0
- package/scripts/migrate-cold-start-seed.mjs +404 -0
- package/scripts/migrate-learnings-jsonl.mjs +189 -0
- package/scripts/migrate-legacy-learnings.sh +61 -0
- package/scripts/migrate-sessions-jsonl.mjs +448 -0
- package/scripts/migrate-subagents-jsonl.mjs +196 -0
- package/scripts/migrate-vault-paths.mjs +796 -0
- package/scripts/parse-config.mjs +149 -0
- package/scripts/pi-install.mjs +117 -0
- package/scripts/print-applicable-rules.mjs +247 -0
- package/scripts/promote-vault-strict.mjs +496 -0
- package/scripts/relocate-vault-corpus.mjs +1178 -0
- package/scripts/run-migrate-v2-cross-repo.mjs +385 -0
- package/scripts/run-quality-gate.mjs +216 -0
- package/scripts/spikes/h3-agent-teams/preflight.sh +53 -0
- package/scripts/spikes/h3-agent-teams/run-h3.sh +112 -0
- package/scripts/spikes/h3-agent-teams/setup.sh +137 -0
- package/scripts/spikes/h3-agent-teams/toggle.sh +38 -0
- package/scripts/sweep-expired-learnings.mjs +135 -0
- package/scripts/sync-vault-schema.mjs +376 -0
- package/scripts/tests/fixtures/fetch-baseline/sample-rule.md +8 -0
- package/scripts/tmux-layout.mjs +245 -0
- package/scripts/token-audit.sh +191 -0
- package/scripts/typecheck.mjs +42 -0
- package/scripts/upload-social-preview.mjs +316 -0
- package/scripts/validate-config.mjs +46 -0
- package/scripts/validate-plugin-manifests.mjs +163 -0
- package/scripts/validate-plugin.mjs +264 -0
- package/scripts/validate-wave-scope.mjs +289 -0
- package/scripts/vault-backfill.mjs +404 -0
- package/scripts/vault-consolidate.mjs +596 -0
- package/scripts/vault-integration-watcher.mjs +394 -0
- package/scripts/vault-mirror.mjs +430 -0
- package/skills/_shared/bootstrap-gate.md +111 -0
- package/skills/_shared/config-reading.md +226 -0
- package/skills/_shared/instruction-file-resolution.md +79 -0
- package/skills/_shared/model-selection.md +64 -0
- package/skills/_shared/monitor-patterns.md +300 -0
- package/skills/_shared/parallel-aware-auq.md +121 -0
- package/skills/_shared/parallel-aware-preamble.md +185 -0
- package/skills/_shared/platform-tools.md +96 -0
- package/skills/_shared/state-ownership.md +221 -0
- package/skills/architecture/DEEPENING.md +37 -0
- package/skills/architecture/INTERFACE-DESIGN.md +44 -0
- package/skills/architecture/LANGUAGE.md +53 -0
- package/skills/architecture/SKILL.md +92 -0
- package/skills/autopilot/SKILL.md +419 -0
- package/skills/bootstrap/SKILL.md +592 -0
- package/skills/bootstrap/STATE.md.template +24 -0
- package/skills/bootstrap/_shared-template.md +243 -0
- package/skills/bootstrap/deep-template.md +659 -0
- package/skills/bootstrap/fast-template.md +251 -0
- package/skills/bootstrap/intensity-heuristic.md +80 -0
- package/skills/bootstrap/public-fallback.md +342 -0
- package/skills/bootstrap/standard-template.md +736 -0
- package/skills/bootstrap/templates/agents/project-code-review.md +18 -0
- package/skills/bootstrap/templates/agents/project-discovery.md +18 -0
- package/skills/bootstrap/templates/agents/project-quality-gate.md +18 -0
- package/skills/brainstorm/SKILL.md +268 -0
- package/skills/brainstorm/soul.md +49 -0
- package/skills/claude-md-drift-check/SKILL.md +186 -0
- package/skills/claude-md-drift-check/checker.mjs +1380 -0
- package/skills/claude-md-drift-check/checker.sh +37 -0
- package/skills/claude-md-drift-check/package.json +16 -0
- package/skills/convergence-monitoring/README.md +39 -0
- package/skills/convergence-monitoring/SIGNALS.md +246 -0
- package/skills/convergence-monitoring/SKILL.md +285 -0
- package/skills/daily/SKILL.md +222 -0
- package/skills/daily/generate.sh +92 -0
- package/skills/daily/templates/daily.md.tpl +36 -0
- package/skills/debug/SKILL.md +188 -0
- package/skills/debug/soul.md +35 -0
- package/skills/discovery/SKILL.md +567 -0
- package/skills/discovery/issue-templates.md +237 -0
- package/skills/discovery/probes/docs-staleness.mjs +195 -0
- package/skills/discovery/probes/frontend-slop.mjs +186 -0
- package/skills/discovery/probes/ssot-code-diff.mjs +310 -0
- package/skills/discovery/probes/supply-chain-slopcheck.mjs +440 -0
- package/skills/discovery/probes/vault-narrative-staleness.mjs +355 -0
- package/skills/discovery/probes/vault-staleness.mjs +272 -0
- package/skills/discovery/probes-arch.md +252 -0
- package/skills/discovery/probes-audit.md +95 -0
- package/skills/discovery/probes-code.md +329 -0
- package/skills/discovery/probes-docs.md +76 -0
- package/skills/discovery/probes-feature.md +150 -0
- package/skills/discovery/probes-infra.md +138 -0
- package/skills/discovery/probes-intro.md +25 -0
- package/skills/discovery/probes-session.md +495 -0
- package/skills/discovery/probes-supply-chain.md +94 -0
- package/skills/discovery/probes-ui.md +147 -0
- package/skills/discovery/probes-vault.md +64 -0
- package/skills/discovery/slop-patterns.md +115 -0
- package/skills/dispatcher/SKILL.md +173 -0
- package/skills/docs-orchestrator/SKILL.md +362 -0
- package/skills/docs-orchestrator/audience-mapping.md +140 -0
- package/skills/domain-model/ADR-FORMAT.md +47 -0
- package/skills/domain-model/CONTEXT-FORMAT.md +77 -0
- package/skills/domain-model/SKILL.md +85 -0
- package/skills/ecosystem-health/SKILL.md +119 -0
- package/skills/ecosystem-health/wizard.md +193 -0
- package/skills/eval/SKILL.md +293 -0
- package/skills/eval/rubric-v1.md +218 -0
- package/skills/evolve/SKILL.md +546 -0
- package/skills/frontmatter-guard/SKILL.md +126 -0
- package/skills/gitlab-ops/SKILL.md +368 -0
- package/skills/gitlab-portfolio/SKILL.md +196 -0
- package/skills/grill/SKILL.md +185 -0
- package/skills/grill/soul.md +55 -0
- package/skills/hook-development/SKILL.md +413 -0
- package/skills/mcp-builder/SKILL.md +260 -0
- package/skills/memory-cleanup/SKILL.md +310 -0
- package/skills/mode-selector/SKILL.md +226 -0
- package/skills/peekaboo-driver/SKILL.md +237 -0
- package/skills/peekaboo-driver/soul.md +32 -0
- package/skills/persona-panel/SKILL.md +365 -0
- package/skills/persona-panel/persona-format.md +205 -0
- package/skills/persona-panel/presets/designer-lens.md +87 -0
- package/skills/persona-panel/presets/engineer-lens.md +88 -0
- package/skills/persona-panel/presets/pm-lens.md +86 -0
- package/skills/plan/SKILL.md +496 -0
- package/skills/plan/mode-feature.md +141 -0
- package/skills/plan/mode-new.md +297 -0
- package/skills/plan/mode-retro.md +271 -0
- package/skills/plan/prd-feature-template.md +132 -0
- package/skills/plan/prd-full-template.md +151 -0
- package/skills/plan/prd-reviewer-prompt.md +103 -0
- package/skills/plan/retro-template.md +75 -0
- package/skills/plan/soul.md +62 -0
- package/skills/playwright-driver/SKILL.md +226 -0
- package/skills/playwright-driver/soul.md +30 -0
- package/skills/quality-gates/SKILL.md +212 -0
- package/skills/reconcile/SKILL.md +324 -0
- package/skills/repo-audit/SKILL.md +272 -0
- package/skills/session-end/SKILL.md +1044 -0
- package/skills/session-end/discovery-scan.md +37 -0
- package/skills/session-end/drift-operations.md +97 -0
- package/skills/session-end/learning-patterns.md +78 -0
- package/skills/session-end/metrics-collection.md +175 -0
- package/skills/session-end/phase-3-2-docs-verification.md +148 -0
- package/skills/session-end/phase-3-6-tail.md +344 -0
- package/skills/session-end/phase-3-7a-recommendations.md +86 -0
- package/skills/session-end/plan-verification.md +288 -0
- package/skills/session-end/session-metrics-write.md +223 -0
- package/skills/session-end/vault-operations.md +50 -0
- package/skills/session-end/verification-checklist.md +20 -0
- package/skills/session-plan/SKILL.md +554 -0
- package/skills/session-plan/wave-template.md +37 -0
- package/skills/session-start/SKILL.md +1043 -0
- package/skills/session-start/phase-2-5-docs-planning.md +119 -0
- package/skills/session-start/phase-4-5-resource-health.md +49 -0
- package/skills/session-start/phase-7-1-premise-check.md +47 -0
- package/skills/session-start/phase-7-5-mode-selector.md +237 -0
- package/skills/session-start/phase-8-5-express-path.md +61 -0
- package/skills/session-start/presentation-format.md +81 -0
- package/skills/session-start/soul.md +57 -0
- package/skills/skill-creator/SKILL.md +168 -0
- package/skills/spinout/SKILL.md +76 -0
- package/skills/sunset-review/SKILL.md +96 -0
- package/skills/test-runner/SKILL.md +362 -0
- package/skills/test-runner/rubric-v1.md +388 -0
- package/skills/test-runner/soul.md +46 -0
- package/skills/tmux-layout/SKILL.md +104 -0
- package/skills/ubiquitous-language/SKILL.md +97 -0
- package/skills/using-orchestrator/SKILL.md +144 -0
- package/skills/vault-mirror/SKILL.md +234 -0
- package/skills/vault-sync/SKILL.md +319 -0
- package/skills/vault-sync/package-lock.json +40 -0
- package/skills/vault-sync/package.json +11 -0
- package/skills/vault-sync/tests/fixtures/archive-test-vault/90-archive/bad-archived.md +8 -0
- package/skills/vault-sync/tests/fixtures/archive-test-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/archive-test-vault/live-note.md +8 -0
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/bad-type.md +8 -0
- package/skills/vault-sync/tests/fixtures/broken-frontmatter-vault/good-note.md +8 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/.obsidian/config.md +8 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/01-projects/foo/projects-baseline.md +10 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/03-daily/daily-2026-04-13.md +8 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/README.md +3 -0
- package/skills/vault-sync/tests/fixtures/clean-vault/hello-world.md +11 -0
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/has-dangling.md +9 -0
- package/skills/vault-sync/tests/fixtures/dangling-link-vault/real-target.md +8 -0
- package/skills/vault-sync/tests/fixtures/empty-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/missing-field-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/missing-field-vault/missing-id.md +7 -0
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/03-daily/daily-2026-04-13.md +9 -0
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/nested-tag-vault/nested-tags-note.md +11 -0
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/README.md +3 -0
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_MOC.md +3 -0
- package/skills/vault-sync/tests/fixtures/no-frontmatter-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/with-moc-vault/_MOC.md +11 -0
- package/skills/vault-sync/tests/fixtures/with-moc-vault/_meta/.gitkeep +0 -0
- package/skills/vault-sync/tests/fixtures/with-moc-vault/hello-world.md +11 -0
- package/skills/vault-sync/tests/schema-drift.test.mjs +133 -0
- package/skills/vault-sync/validator.mjs +658 -0
- package/skills/vault-sync/validator.sh +55 -0
- package/skills/wave-executor/SKILL.md +496 -0
- package/skills/wave-executor/circuit-breaker.md +169 -0
- package/skills/wave-executor/wave-loop.md +1043 -0
- package/skills/write-executable-plan/SKILL.md +237 -0
- package/skills/write-executable-plan/plan-template.md +154 -0
- package/templates/_minimal/CLAUDE.md.tmpl +41 -0
- package/templates/_minimal/README.md.tmpl +15 -0
- package/templates/_minimal/gitignore.tmpl +47 -0
- package/templates/_shared/harte-regeln.md +16 -0
- package/templates/_shared/loop.md +90 -0
- package/templates/_shared/rules/parallel-sessions.md +77 -0
- package/templates/nextjs-minimal/README.md +30 -0
- package/templates/nextjs-minimal/app/layout.tsx +18 -0
- package/templates/nextjs-minimal/app/page.tsx +7 -0
- package/templates/nextjs-minimal/eslint.config.mjs +16 -0
- package/templates/nextjs-minimal/next.config.mjs +4 -0
- package/templates/nextjs-minimal/package.json +27 -0
- package/templates/nextjs-minimal/tsconfig.json +23 -0
- package/templates/node-minimal/README.md +33 -0
- package/templates/node-minimal/eslint.config.mjs +10 -0
- package/templates/node-minimal/package.json +21 -0
- package/templates/node-minimal/src/index.ts +1 -0
- package/templates/node-minimal/tests/sanity.test.ts +5 -0
- package/templates/node-minimal/tsconfig.json +17 -0
- package/templates/personas/README.md +150 -0
- package/templates/personas/accounting-compliance.v1.md +120 -0
- package/templates/personas/accounting-tax-advisor.v1.md +116 -0
- package/templates/personas/buyer-p1-cto.v1.md +125 -0
- package/templates/personas/buyer-p2-kanzlei.v1.md +134 -0
- package/templates/personas/buyer-p3-build.v1.md +130 -0
- package/templates/personas/buyer-p4-tech-veto.v1.md +130 -0
- package/templates/personas/buyer-p5-solo.v1.md +132 -0
- package/templates/personas/buyer-p6-ld.v1.md +130 -0
- package/templates/personas/klima-ai-expert.v1.md +114 -0
- package/templates/personas/klima-physicist.v1.md +117 -0
- package/templates/python-uv/README.md +28 -0
- package/templates/python-uv/pyproject.toml +32 -0
- package/templates/python-uv/src/__PROJECT_NAME__/__init__.py +0 -0
- package/templates/python-uv/src/__PROJECT_NAME__/main.py +6 -0
- package/templates/python-uv/tests/test_sanity.py +2 -0
- package/templates/static-html/README.md +19 -0
- package/templates/static-html/index.html +15 -0
- package/templates/static-html/script.js +1 -0
- package/templates/static-html/styles.css +26 -0
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* idempotency.mjs — Record-store I/O + logical dedupe + idempotency for the
|
|
3
|
+
* #695 FA2 Reconciliation Engine (Epic #693).
|
|
4
|
+
*
|
|
5
|
+
* This module OWNS all disk access for reconcile candidates. It is the
|
|
6
|
+
* reconcile-side sibling of `scripts/lib/skill-evolution/idempotency.mjs`
|
|
7
|
+
* (the C2 repair-candidates store) and mirrors its defensive posture: the
|
|
8
|
+
* store is ALWAYS rewritten in full via tmp-file + atomic rename and is NEVER
|
|
9
|
+
* appended to, because dedupe-by-`learning_key` must mutate/replace existing
|
|
10
|
+
* lines — an append would leave stale duplicates.
|
|
11
|
+
*
|
|
12
|
+
* Store: `.orchestrator/runtime/reconcile-candidates.jsonl` — a mutable
|
|
13
|
+
* work-queue in JSON-Lines format (one ReconcileCandidate per line).
|
|
14
|
+
*
|
|
15
|
+
* Two responsibilities differ from the repair store:
|
|
16
|
+
* 1. The IDEMPOTENCY KEY is the LOGICAL `learning_key` (issue #695), not the
|
|
17
|
+
* physical hashed `id`. Two candidates with the same `learning_key`
|
|
18
|
+
* describe the same reconciliation; the latest one replaces the older —
|
|
19
|
+
* UNLESS the older is already processed (`processed_at !== null`), in
|
|
20
|
+
* which case the processed verdict wins and is never regressed.
|
|
21
|
+
* 2. `makeCandidateId` mints the deterministic physical id from
|
|
22
|
+
* `(learning_key, slug)` for stable referencing.
|
|
23
|
+
*
|
|
24
|
+
* I/O contract: this module NEVER throws on I/O. Reads return safe defaults
|
|
25
|
+
* (`[]`); the write helper swallows filesystem errors and `mergeCandidates`
|
|
26
|
+
* reports `written: false` on a write failure.
|
|
27
|
+
*
|
|
28
|
+
* Part of Epic #693 → issue #695 (FA2 Reconciliation Engine).
|
|
29
|
+
*
|
|
30
|
+
* @typedef {Object} ReconcileCandidate
|
|
31
|
+
* @property {string} id - deterministic `rc-<sha256(learning_key + '\0' + slug)[:8]>`.
|
|
32
|
+
* @property {number} schema_version - schema version (1).
|
|
33
|
+
* @property {string} learning_key - THE logical dedupe key.
|
|
34
|
+
* @property {string} slug - the `.claude/rules/<slug>.md` slug.
|
|
35
|
+
* @property {'proposed'|'rejected'} status
|
|
36
|
+
* @property {string} reason
|
|
37
|
+
* @property {number} confidence
|
|
38
|
+
* @property {string} created_at - ISO timestamp.
|
|
39
|
+
* @property {string|null} processed_at - terminal stamp (mirrors repair store).
|
|
40
|
+
* @property {string|null} superseded_by
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
import { mkdirSync, readFileSync, writeFileSync, renameSync } from 'node:fs';
|
|
44
|
+
import { dirname, isAbsolute, join } from 'node:path';
|
|
45
|
+
import { createHash, randomBytes } from 'node:crypto';
|
|
46
|
+
|
|
47
|
+
/** Default repo-relative location of the reconcile-candidate work-queue. */
|
|
48
|
+
export const DEFAULT_STORE_PATH = '.orchestrator/runtime/reconcile-candidates.jsonl';
|
|
49
|
+
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
// Internal helpers
|
|
52
|
+
// ---------------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Resolve the store path against repoRoot when it is relative. An absolute
|
|
56
|
+
* storePath is honoured as-is. Falls back to {@link DEFAULT_STORE_PATH}, and
|
|
57
|
+
* to `process.cwd()` when no repoRoot is supplied.
|
|
58
|
+
* @param {string|undefined} repoRoot
|
|
59
|
+
* @param {string|undefined} storePath
|
|
60
|
+
* @returns {string}
|
|
61
|
+
*/
|
|
62
|
+
function resolveStorePath(repoRoot, storePath) {
|
|
63
|
+
const rel = typeof storePath === 'string' && storePath.length > 0 ? storePath : DEFAULT_STORE_PATH;
|
|
64
|
+
if (isAbsolute(rel)) return rel;
|
|
65
|
+
const root = typeof repoRoot === 'string' && repoRoot.length > 0 ? repoRoot : process.cwd();
|
|
66
|
+
return join(root, rel);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Read + defensively parse the store's JSONL lines into ReconcileCandidate
|
|
71
|
+
* records. Malformed lines (bad JSON, non-object) are skipped silently. A
|
|
72
|
+
* missing file yields `[]`. Never throws.
|
|
73
|
+
* @param {string} absPath
|
|
74
|
+
* @returns {ReconcileCandidate[]}
|
|
75
|
+
*/
|
|
76
|
+
function readStore(absPath) {
|
|
77
|
+
let raw;
|
|
78
|
+
try {
|
|
79
|
+
raw = readFileSync(absPath, 'utf8');
|
|
80
|
+
} catch {
|
|
81
|
+
// ENOENT or any read error → empty store.
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** @type {ReconcileCandidate[]} */
|
|
86
|
+
const records = [];
|
|
87
|
+
for (const line of raw.split('\n')) {
|
|
88
|
+
const trimmed = line.trim();
|
|
89
|
+
if (trimmed.length === 0) continue;
|
|
90
|
+
let parsed;
|
|
91
|
+
try {
|
|
92
|
+
parsed = JSON.parse(trimmed);
|
|
93
|
+
} catch {
|
|
94
|
+
continue; // skip malformed line
|
|
95
|
+
}
|
|
96
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
97
|
+
records.push(/** @type {ReconcileCandidate} */ (parsed));
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return records;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Atomically rewrite the entire store from an in-memory record array. Creates
|
|
105
|
+
* the parent dir with mkdir -p semantics, writes a tmp file, then renames over
|
|
106
|
+
* the target (same-filesystem rename is atomic on POSIX). Never appends.
|
|
107
|
+
* Never throws — filesystem errors are returned as `{ ok: false, ... }`.
|
|
108
|
+
* @param {string} absPath
|
|
109
|
+
* @param {ReconcileCandidate[]} records
|
|
110
|
+
* @returns {{ ok: true, lines: number } | { ok: false, reason: 'fs-error', error: string }}
|
|
111
|
+
*/
|
|
112
|
+
function writeStore(absPath, records) {
|
|
113
|
+
try {
|
|
114
|
+
const dir = dirname(absPath);
|
|
115
|
+
mkdirSync(dir, { recursive: true });
|
|
116
|
+
const body = records.map((r) => JSON.stringify(r)).join('\n');
|
|
117
|
+
const content = records.length > 0 ? body + '\n' : '';
|
|
118
|
+
const tmpFile = join(dir, `.reconcile-candidates.${randomBytes(6).toString('hex')}.tmp`);
|
|
119
|
+
writeFileSync(tmpFile, content, { encoding: 'utf8' });
|
|
120
|
+
renameSync(tmpFile, absPath);
|
|
121
|
+
return { ok: true, lines: records.length };
|
|
122
|
+
} catch (err) {
|
|
123
|
+
return { ok: false, reason: 'fs-error', error: err?.message ?? String(err) };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* True iff a candidate has a terminal `processed_at` stamp.
|
|
129
|
+
* @param {ReconcileCandidate|undefined|null} cand
|
|
130
|
+
* @returns {boolean}
|
|
131
|
+
*/
|
|
132
|
+
function isTerminal(cand) {
|
|
133
|
+
return !!cand && cand.processed_at !== null && cand.processed_at !== undefined;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// ---------------------------------------------------------------------------
|
|
137
|
+
// Public API
|
|
138
|
+
// ---------------------------------------------------------------------------
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Mint the deterministic physical id for a reconcile candidate. The same
|
|
142
|
+
* `(learningKey, slug)` pair always yields the same id, so it is a stable
|
|
143
|
+
* reference key. A NUL separator avoids cross-field collisions
|
|
144
|
+
* (e.g. `('ab','c')` vs `('a','bc')`). Never throws — coerces inputs to string.
|
|
145
|
+
* @param {string} learningKey
|
|
146
|
+
* @param {string} slug
|
|
147
|
+
* @returns {string} `rc-<sha256(learningKey + '\0' + slug)[:8]>`
|
|
148
|
+
*/
|
|
149
|
+
export function makeCandidateId(learningKey, slug) {
|
|
150
|
+
const key = typeof learningKey === 'string' ? learningKey : String(learningKey ?? '');
|
|
151
|
+
const s = typeof slug === 'string' ? slug : String(slug ?? '');
|
|
152
|
+
return 'rc-' + createHash('sha256').update(key + '\0' + s).digest('hex').slice(0, 8);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Load every ReconcileCandidate currently persisted in the store. Reads JSONL,
|
|
157
|
+
* skips malformed lines, returns `[]` for a missing file. Does NOT create the
|
|
158
|
+
* runtime dir (mkdir -p happens only on write). Never throws.
|
|
159
|
+
* @param {Object} [params]
|
|
160
|
+
* @param {string} [params.repoRoot] - repo root; relative `storePath` is resolved against it (defaults to `process.cwd()`).
|
|
161
|
+
* @param {string} [params.storePath] - store path (relative ⇒ joined to repoRoot). Defaults to {@link DEFAULT_STORE_PATH}.
|
|
162
|
+
* @returns {ReconcileCandidate[]}
|
|
163
|
+
*/
|
|
164
|
+
export function loadCandidates({ repoRoot, storePath } = {}) {
|
|
165
|
+
const absPath = resolveStorePath(repoRoot, storePath);
|
|
166
|
+
return readStore(absPath);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* True iff `existing` already holds a candidate that shares `candidate`'s
|
|
171
|
+
* `learning_key` AND has a terminal `processed_at` stamp. The reconcile engine
|
|
172
|
+
* uses this to idempotently SKIP re-proposing a learning whose verdict is
|
|
173
|
+
* already terminal. Never throws.
|
|
174
|
+
* @param {ReconcileCandidate} candidate - the candidate under consideration.
|
|
175
|
+
* @param {ReconcileCandidate[]} existing - the currently-persisted candidates.
|
|
176
|
+
* @returns {boolean}
|
|
177
|
+
*/
|
|
178
|
+
export function isProcessed(candidate, existing) {
|
|
179
|
+
if (!candidate || typeof candidate !== 'object') return false;
|
|
180
|
+
const key = candidate.learning_key;
|
|
181
|
+
if (typeof key !== 'string' || key.length === 0) return false;
|
|
182
|
+
const list = Array.isArray(existing) ? existing : [];
|
|
183
|
+
return list.some((rec) => rec && rec.learning_key === key && isTerminal(rec));
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Merge new candidates into the store, deduping by `learning_key`, then
|
|
188
|
+
* atomically rewrite the full store.
|
|
189
|
+
*
|
|
190
|
+
* Dedupe rule, per incoming candidate:
|
|
191
|
+
* - If an existing candidate shares the same `learning_key` AND is already
|
|
192
|
+
* processed (`processed_at !== null`) → KEEP existing (idempotent; do not
|
|
193
|
+
* regress a terminal verdict).
|
|
194
|
+
* - Else if an existing candidate shares the same `learning_key` → the NEW
|
|
195
|
+
* candidate REPLACES it (keep latest).
|
|
196
|
+
* - Else → add the new candidate.
|
|
197
|
+
*
|
|
198
|
+
* The runtime dir is created with mkdir -p semantics. The store is rewritten in
|
|
199
|
+
* full (read-all → merge → atomic tmp+rename), never appended. Output lines are
|
|
200
|
+
* sorted by `learning_key` for deterministic output. Never throws; on write
|
|
201
|
+
* failure returns `{ merged, written: false }`.
|
|
202
|
+
* @param {Object} [params]
|
|
203
|
+
* @param {ReconcileCandidate[]} [params.candidates] - newly minted candidates to merge.
|
|
204
|
+
* @param {string} [params.repoRoot]
|
|
205
|
+
* @param {string} [params.storePath]
|
|
206
|
+
* @returns {{ merged: ReconcileCandidate[], written: boolean }}
|
|
207
|
+
*/
|
|
208
|
+
export function mergeCandidates({ candidates, repoRoot, storePath } = {}) {
|
|
209
|
+
const absPath = resolveStorePath(repoRoot, storePath);
|
|
210
|
+
const store = readStore(absPath);
|
|
211
|
+
|
|
212
|
+
// Index existing records by learning_key for O(1) lookup. Last write wins for
|
|
213
|
+
// any pre-existing duplicates in the file (defensive — store should be unique).
|
|
214
|
+
/** @type {Map<string, ReconcileCandidate>} */
|
|
215
|
+
const byKey = new Map();
|
|
216
|
+
for (const rec of store) {
|
|
217
|
+
if (rec && typeof rec.learning_key === 'string') byKey.set(rec.learning_key, rec);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const incoming = Array.isArray(candidates) ? candidates : [];
|
|
221
|
+
for (const cand of incoming) {
|
|
222
|
+
if (!cand || typeof cand !== 'object' || typeof cand.learning_key !== 'string') continue;
|
|
223
|
+
|
|
224
|
+
const existing = byKey.get(cand.learning_key);
|
|
225
|
+
if (existing && isTerminal(existing)) {
|
|
226
|
+
// Terminal verdict wins — do not regress a processed candidate.
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
// New key, or live existing → the latest candidate replaces it.
|
|
230
|
+
byKey.set(cand.learning_key, cand);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
const merged = Array.from(byKey.values()).sort((a, b) =>
|
|
234
|
+
String(a.learning_key).localeCompare(String(b.learning_key)),
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
const result = writeStore(absPath, merged);
|
|
238
|
+
return { merged, written: result.ok === true };
|
|
239
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* renderer.mjs — Reconciliation-engine rule renderer (Epic #693 FA2, issue #695).
|
|
3
|
+
*
|
|
4
|
+
* Turns an eligible learning + its activation-metadata (from the sibling
|
|
5
|
+
* {@link module:reconcile/emitter.toActivationMetadata}) into a complete,
|
|
6
|
+
* loader-parseable `.claude/rules/<slug>.md` markdown document.
|
|
7
|
+
*
|
|
8
|
+
* Pure functions — NO file I/O. {@link renderRule} returns the markdown STRING
|
|
9
|
+
* plus the intended path; it does NOT write `.claude/rules/`. Writing (after
|
|
10
|
+
* operator approval) is FA3's job (issue #696).
|
|
11
|
+
*
|
|
12
|
+
* ── The never-always-on invariant (the "brandmauer") ─────────────────────────
|
|
13
|
+
* docs/rule-authoring.md § "The Never-Always-On Invariant". Every emitted rule
|
|
14
|
+
* carries `auto-generated: true` and MUST therefore carry ≥1 activation axis
|
|
15
|
+
* (non-empty `globs` OR a `host-class`), a `learning-key`, and an `expires-at`.
|
|
16
|
+
* The emitter already guards this, but {@link renderRule} defends it again —
|
|
17
|
+
* it THROWS when handed metadata with empty globs AND no hostClass so the
|
|
18
|
+
* renderer can never serialise an always-on auto-generated rule.
|
|
19
|
+
*
|
|
20
|
+
* Frontmatter is emitted in a fixed key order matching what the rule-loader
|
|
21
|
+
* (`scripts/lib/rule-loader.mjs`) parses: `auto-generated`, `alwaysApply`,
|
|
22
|
+
* `description`, block-style `globs:`, optional `host-class`, `learning-key`,
|
|
23
|
+
* `confidence`, `expires-at`. Glob values are always double-quoted (the loader
|
|
24
|
+
* strips surrounding quotes), which keeps `*`/`[`/`{` safe.
|
|
25
|
+
*
|
|
26
|
+
* @module reconcile/renderer
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import { createHash } from 'node:crypto';
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Slugify a string into a stable kebab-case token: lowercases, collapses every
|
|
33
|
+
* run of non-`[a-z0-9]` chars into a single `-`, and trims leading/trailing `-`.
|
|
34
|
+
*
|
|
35
|
+
* @param {string} s
|
|
36
|
+
* @returns {string}
|
|
37
|
+
*/
|
|
38
|
+
function kebab(s) {
|
|
39
|
+
return String(s)
|
|
40
|
+
.toLowerCase()
|
|
41
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
42
|
+
.replace(/^-+|-+$/g, '');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Short, stable SHA-1 prefix (first 7 hex chars) of an input string.
|
|
47
|
+
*
|
|
48
|
+
* @param {string} input
|
|
49
|
+
* @returns {string}
|
|
50
|
+
*/
|
|
51
|
+
function shortHash(input) {
|
|
52
|
+
return createHash('sha1').update(String(input)).digest('hex').slice(0, 7);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Derive a kebab-case, stable, idempotent slug for a learning's rule file.
|
|
57
|
+
*
|
|
58
|
+
* `slug = kebab(type) + '-' + kebab(title||subject) + '-' + sha1(`${type}/${title||subject}`)[:7]`.
|
|
59
|
+
* The hash suffix gives collision safety while staying deterministic: the same
|
|
60
|
+
* learning always yields the same slug; distinct learnings yield distinct slugs.
|
|
61
|
+
*
|
|
62
|
+
* Guard: when `type` is missing OR both `title` and `subject` are missing, fall
|
|
63
|
+
* back to `'auto-' + sha1(JSON.stringify(learning))[:7]`. Never throws.
|
|
64
|
+
*
|
|
65
|
+
* @param {object} learning
|
|
66
|
+
* @returns {string}
|
|
67
|
+
*/
|
|
68
|
+
export function deriveSlug(learning) {
|
|
69
|
+
if (learning === null || typeof learning !== 'object' || Array.isArray(learning)) {
|
|
70
|
+
return `auto-${shortHash(JSON.stringify(learning))}`;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const type = typeof learning.type === 'string' ? learning.type : '';
|
|
74
|
+
const subjectOrTitle =
|
|
75
|
+
(typeof learning.title === 'string' && learning.title !== '' ? learning.title : '') ||
|
|
76
|
+
(typeof learning.subject === 'string' && learning.subject !== '' ? learning.subject : '');
|
|
77
|
+
|
|
78
|
+
if (type === '' || subjectOrTitle === '') {
|
|
79
|
+
return `auto-${shortHash(JSON.stringify(learning))}`;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const base = `${kebab(type)}-${kebab(subjectOrTitle)}`;
|
|
83
|
+
const suffix = shortHash(`${type}/${subjectOrTitle}`);
|
|
84
|
+
|
|
85
|
+
// A kebab of all-symbol inputs can be empty; the hash suffix keeps the slug
|
|
86
|
+
// non-empty and stable regardless.
|
|
87
|
+
return base === '' ? `auto-${suffix}` : `${base}-${suffix}`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Render the body's `## Evidence` section content from a learning's `evidence`.
|
|
92
|
+
*
|
|
93
|
+
* Array → one `- ` bullet per item; string → as-is; absent/empty →
|
|
94
|
+
* `(no evidence recorded)`.
|
|
95
|
+
*
|
|
96
|
+
* @param {unknown} evidence
|
|
97
|
+
* @returns {string}
|
|
98
|
+
*/
|
|
99
|
+
function renderEvidence(evidence) {
|
|
100
|
+
if (Array.isArray(evidence)) {
|
|
101
|
+
const lines = evidence
|
|
102
|
+
.filter((e) => e !== null && e !== undefined && String(e) !== '')
|
|
103
|
+
.map((e) => `- ${String(e)}`);
|
|
104
|
+
return lines.length > 0 ? lines.join('\n') : '(no evidence recorded)';
|
|
105
|
+
}
|
|
106
|
+
if (typeof evidence === 'string' && evidence !== '') {
|
|
107
|
+
return evidence;
|
|
108
|
+
}
|
|
109
|
+
return '(no evidence recorded)';
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Render a complete `.claude/rules/<slug>.md` document for an eligible learning.
|
|
114
|
+
*
|
|
115
|
+
* @param {object} learning - the eligible learning (carries `type`,
|
|
116
|
+
* `subject`/`title`, `insight`, `evidence`, `id`, `source_session`).
|
|
117
|
+
* @param {object} metadata - activation metadata from the emitter:
|
|
118
|
+
* `{ globs: string[], description, learningKey, confidence, expiresAt,
|
|
119
|
+
* autoGenerated: true, alwaysApply: false, hostClass?: string }`.
|
|
120
|
+
* @returns {{ slug: string, path: string, content: string }}
|
|
121
|
+
* @throws {Error} when `metadata.globs` is empty AND `metadata.hostClass` is
|
|
122
|
+
* falsy — the never-always-on invariant (defends the emitter's guard).
|
|
123
|
+
*/
|
|
124
|
+
export function renderRule(learning, metadata) {
|
|
125
|
+
if (metadata === null || typeof metadata !== 'object' || Array.isArray(metadata)) {
|
|
126
|
+
throw new Error('renderer: metadata must be a non-null object');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
const globs = Array.isArray(metadata.globs) ? metadata.globs : [];
|
|
130
|
+
const hostClass =
|
|
131
|
+
typeof metadata.hostClass === 'string' && metadata.hostClass !== ''
|
|
132
|
+
? metadata.hostClass
|
|
133
|
+
: undefined;
|
|
134
|
+
|
|
135
|
+
// The brandmauer: refuse to serialise an always-on auto-generated rule.
|
|
136
|
+
if (globs.length === 0 && hostClass === undefined) {
|
|
137
|
+
throw new Error(
|
|
138
|
+
'renderer: no activation axis (globs/host-class) — refusing to render always-on auto-generated rule (never-always-on invariant)',
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const slug = deriveSlug(learning);
|
|
143
|
+
const path = `.claude/rules/${slug}.md`;
|
|
144
|
+
|
|
145
|
+
const humanTitle =
|
|
146
|
+
(typeof learning.title === 'string' && learning.title !== '' ? learning.title : '') ||
|
|
147
|
+
(typeof learning.subject === 'string' && learning.subject !== '' ? learning.subject : '') ||
|
|
148
|
+
metadata.learningKey ||
|
|
149
|
+
slug;
|
|
150
|
+
|
|
151
|
+
const insight =
|
|
152
|
+
typeof learning.insight === 'string' && learning.insight !== ''
|
|
153
|
+
? learning.insight
|
|
154
|
+
: '(no insight recorded)';
|
|
155
|
+
|
|
156
|
+
// ── Frontmatter (fixed key order; loader-parseable) ──────────────────────
|
|
157
|
+
const fm = [];
|
|
158
|
+
fm.push('---');
|
|
159
|
+
fm.push('auto-generated: true');
|
|
160
|
+
fm.push('alwaysApply: false');
|
|
161
|
+
fm.push(`description: ${metadata.description}`);
|
|
162
|
+
// OMIT the `globs:` key entirely when there are no globs (a host-class-only
|
|
163
|
+
// activation axis). An empty `globs:` line parses in the loader as
|
|
164
|
+
// `globs: []`, which short-circuits BEFORE the host-class gate runs
|
|
165
|
+
// (`if (globs.length === 0) continue;`) — dropping the rule on every host. A
|
|
166
|
+
// rule with no `globs:` key is treated as not glob-scoped, so it is gated
|
|
167
|
+
// purely by `host-class:` (loads only on a matching host) — the intended
|
|
168
|
+
// behavior, and still never always-on (host-class IS an activation axis).
|
|
169
|
+
if (globs.length > 0) {
|
|
170
|
+
fm.push('globs:');
|
|
171
|
+
for (const glob of globs) {
|
|
172
|
+
// Always double-quote — keeps `*`/`[`/`{` safe; loader strips quotes.
|
|
173
|
+
fm.push(` - "${glob}"`);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (hostClass !== undefined) {
|
|
177
|
+
fm.push(`host-class: ${hostClass}`);
|
|
178
|
+
}
|
|
179
|
+
fm.push(`learning-key: ${metadata.learningKey}`);
|
|
180
|
+
fm.push(`confidence: ${metadata.confidence}`);
|
|
181
|
+
fm.push(`expires-at: ${metadata.expiresAt}`);
|
|
182
|
+
fm.push('---');
|
|
183
|
+
|
|
184
|
+
// ── Body (free markdown; does not affect frontmatter parsing) ────────────
|
|
185
|
+
const learningId = learning.id ? String(learning.id) : 'n/a';
|
|
186
|
+
const sourceSession = learning.source_session ? String(learning.source_session) : 'n/a';
|
|
187
|
+
|
|
188
|
+
const body = [
|
|
189
|
+
'',
|
|
190
|
+
`# Auto-generated rule: ${humanTitle}`,
|
|
191
|
+
'',
|
|
192
|
+
insight,
|
|
193
|
+
'',
|
|
194
|
+
'## Evidence',
|
|
195
|
+
renderEvidence(learning.evidence),
|
|
196
|
+
'',
|
|
197
|
+
'<!-- provenance (auto-generated by the reconciliation engine — do not hand-edit) -->',
|
|
198
|
+
'## Provenance',
|
|
199
|
+
`- learning-key: \`${metadata.learningKey}\``,
|
|
200
|
+
`- learning-id: \`${learningId}\``,
|
|
201
|
+
`- source-session: \`${sourceSession}\``,
|
|
202
|
+
`- confidence: ${metadata.confidence}`,
|
|
203
|
+
'- generated-by: reconciliation-engine (Epic #693 FA2 / #695)',
|
|
204
|
+
`- expires-at: ${metadata.expiresAt}`,
|
|
205
|
+
'',
|
|
206
|
+
];
|
|
207
|
+
|
|
208
|
+
const content = `${fm.join('\n')}\n${body.join('\n')}`;
|
|
209
|
+
|
|
210
|
+
return { slug, path, content };
|
|
211
|
+
}
|