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,247 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* sessions-staleness-banner.mjs — #724
|
|
3
|
+
*
|
|
4
|
+
* Deterministic session-start nudge for the "close-through" gap: sessions
|
|
5
|
+
* that end (agents stop, work happens) without ever writing a
|
|
6
|
+
* `.orchestrator/metrics/sessions.jsonl` ledger record. W1-D4 fleet finding:
|
|
7
|
+
* 141 `orchestrator.session.started` events vs. 39 sessions.jsonl records in
|
|
8
|
+
* this repo (~27% close-through rate) — the ledger silently under-counts
|
|
9
|
+
* completed sessions with no signal to the operator.
|
|
10
|
+
*
|
|
11
|
+
* Mirrors the contract used by the sibling Phase 4 banners
|
|
12
|
+
* (`scripts/lib/reconcile-nudge-banner.mjs`, `scripts/lib/vault-staleness-banner.mjs`,
|
|
13
|
+
* `scripts/lib/loop-readiness-banner.mjs`): a single `checkXxx({repoRoot})`
|
|
14
|
+
* entry point that is COMPLETELY try/catch-wrapped (never throws) and returns
|
|
15
|
+
* either `null` (silent no-op) or `{severity:'warn'|'alert', message, ...extra}`.
|
|
16
|
+
*
|
|
17
|
+
* Design — self-exclusion over time, NOT lock-presence (load-bearing
|
|
18
|
+
* correction; see issue #724 discussion): Phase 4 always runs INSIDE an
|
|
19
|
+
* active session — Phase 1.2 has already acquired `.orchestrator/session.lock`
|
|
20
|
+
* by the time this probe fires. Gating on "no live lock" would make the
|
|
21
|
+
* banner structurally silent forever. Instead:
|
|
22
|
+
*
|
|
23
|
+
* - `lastLedgerAt` = `completed_at` of the last PARSEABLE
|
|
24
|
+
* `sessions.jsonl` record (scanned from EOF
|
|
25
|
+
* backward, skipping malformed lines).
|
|
26
|
+
* - `cutoff` = the CURRENT session's `session.lock`
|
|
27
|
+
* `started_at` (via `readLock()`); when no lock is
|
|
28
|
+
* readable, `cutoff = now` (all events count).
|
|
29
|
+
* - `lastForeignEventAt`= the NEWEST `events.jsonl` line whose `timestamp`
|
|
30
|
+
* is STRICTLY BEFORE `cutoff` — this structurally
|
|
31
|
+
* excludes the current session's own events without
|
|
32
|
+
* needing a `session_id` filter (most event lines
|
|
33
|
+
* don't carry one; see the mission-log sample in
|
|
34
|
+
* `.orchestrator/metrics/events.jsonl`).
|
|
35
|
+
* - `deltaHours` = (lastForeignEventAt − lastLedgerAt) / 1h, only
|
|
36
|
+
* meaningful when > 0 (foreign activity happened
|
|
37
|
+
* AFTER the last ledger entry).
|
|
38
|
+
*
|
|
39
|
+
* Severity: warn above `2 × DEFAULT_TTL_HOURS` (8h, imported from
|
|
40
|
+
* `session-lock.mjs` rather than duplicated), alert above 24h.
|
|
41
|
+
*
|
|
42
|
+
* Plain-JS — no Zod dependency. Never throws. Never mutates input. No
|
|
43
|
+
* `console.*` calls (repo ESLint `no-console` rule).
|
|
44
|
+
*
|
|
45
|
+
* Cross-references:
|
|
46
|
+
* - `.claude/rules/verification-before-completion.md` — evidence-before-claims.
|
|
47
|
+
* - `scripts/lib/session-lock.mjs` — `readLock()`, `DEFAULT_TTL_HOURS`.
|
|
48
|
+
* - `scripts/lib/reconcile-nudge-banner.mjs` / `vault-staleness-banner.mjs` /
|
|
49
|
+
* `loop-readiness-banner.mjs` — the banner-shape template.
|
|
50
|
+
* - `scripts/backfill-abandoned-sessions.mjs` — the CLI this banner's
|
|
51
|
+
* message recommends running (`--dry-run` is its default/safe mode).
|
|
52
|
+
* - `scripts/lib/session-close-backfill.mjs` — the backfill engine
|
|
53
|
+
* (`synthesizeRecord`) that produces the abandoned-session stubs seen in
|
|
54
|
+
* `sessions.jsonl` (e.g. `_backfill_source: "events-jsonl"`).
|
|
55
|
+
* - `skills/session-start/SKILL.md` Phase 4 — banner render site.
|
|
56
|
+
* - Issue #724.
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
60
|
+
import path from 'node:path';
|
|
61
|
+
|
|
62
|
+
import { readLock, DEFAULT_TTL_HOURS } from './session-lock.mjs';
|
|
63
|
+
|
|
64
|
+
/** Repo-relative path to the session ledger (one record per closed session). */
|
|
65
|
+
const SESSIONS_PATH = '.orchestrator/metrics/sessions.jsonl';
|
|
66
|
+
|
|
67
|
+
/** Repo-relative path to the generic telemetry stream (one line per event). */
|
|
68
|
+
const EVENTS_PATH = '.orchestrator/metrics/events.jsonl';
|
|
69
|
+
|
|
70
|
+
/** warn threshold: 2x the session-lock default TTL (8h) — imported, not duplicated. */
|
|
71
|
+
export const WARN_THRESHOLD_HOURS = DEFAULT_TTL_HOURS * 2;
|
|
72
|
+
|
|
73
|
+
/** alert threshold: a full day behind is a strong close-through signal. */
|
|
74
|
+
export const ALERT_THRESHOLD_HOURS = 24;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Read a JSONL file's non-empty lines. Returns `null` when the file is
|
|
78
|
+
* absent or unreadable, `[]` when it exists but has no non-empty lines.
|
|
79
|
+
* Never throws.
|
|
80
|
+
*
|
|
81
|
+
* @param {string} filePath
|
|
82
|
+
* @returns {string[]|null}
|
|
83
|
+
*/
|
|
84
|
+
function readJsonlLines(filePath) {
|
|
85
|
+
if (!existsSync(filePath)) return null;
|
|
86
|
+
let raw;
|
|
87
|
+
try {
|
|
88
|
+
raw = readFileSync(filePath, 'utf8');
|
|
89
|
+
} catch {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
return raw.split('\n').filter((line) => line.length > 0);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Scan `sessions.jsonl` lines from EOF backward and return the `completed_at`
|
|
97
|
+
* of the last PARSEABLE record that carries a valid ISO timestamp. Malformed
|
|
98
|
+
* or non-conforming lines (bad JSON, missing/invalid `completed_at`) are
|
|
99
|
+
* skipped, not treated as fatal.
|
|
100
|
+
*
|
|
101
|
+
* @param {string[]} lines
|
|
102
|
+
* @returns {{iso: string, ms: number}|null}
|
|
103
|
+
*/
|
|
104
|
+
function lastLedgerEntry(lines) {
|
|
105
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
106
|
+
let record;
|
|
107
|
+
try {
|
|
108
|
+
record = JSON.parse(lines[i]);
|
|
109
|
+
} catch {
|
|
110
|
+
continue;
|
|
111
|
+
}
|
|
112
|
+
if (!record || typeof record !== 'object' || typeof record.completed_at !== 'string') continue;
|
|
113
|
+
const ms = Date.parse(record.completed_at);
|
|
114
|
+
if (!Number.isFinite(ms)) continue;
|
|
115
|
+
return { iso: record.completed_at, ms };
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Scan ALL `events.jsonl` lines and return the newest one whose `timestamp`
|
|
122
|
+
* is strictly before `cutoffMs`. This is the self-exclusion mechanism: the
|
|
123
|
+
* current session's own events (all >= cutoff, since cutoff is this
|
|
124
|
+
* session's lock `started_at`) are structurally excluded without needing a
|
|
125
|
+
* `session_id` filter. Malformed lines and lines with a missing/invalid
|
|
126
|
+
* `timestamp` are skipped.
|
|
127
|
+
*
|
|
128
|
+
* @param {string[]} lines
|
|
129
|
+
* @param {number} cutoffMs
|
|
130
|
+
* @returns {{iso: string, ms: number}|null}
|
|
131
|
+
*/
|
|
132
|
+
function newestForeignEvent(lines, cutoffMs) {
|
|
133
|
+
let best = null;
|
|
134
|
+
for (const line of lines) {
|
|
135
|
+
let record;
|
|
136
|
+
try {
|
|
137
|
+
record = JSON.parse(line);
|
|
138
|
+
} catch {
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
if (!record || typeof record !== 'object' || typeof record.timestamp !== 'string') continue;
|
|
142
|
+
const ms = Date.parse(record.timestamp);
|
|
143
|
+
if (!Number.isFinite(ms)) continue;
|
|
144
|
+
if (ms >= cutoffMs) continue; // not "foreign" — belongs to (or postdates) the current session
|
|
145
|
+
if (best === null || ms > best.ms) best = { iso: record.timestamp, ms };
|
|
146
|
+
}
|
|
147
|
+
return best;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Resolve the cutoff instant (in epoch ms) that separates "the current
|
|
152
|
+
* session's own events" from "foreign/pre-session events". Prefers the
|
|
153
|
+
* current session's `session.lock` `started_at`; falls back to `now` when no
|
|
154
|
+
* lock is readable (or its `started_at` is unparseable) — in that fallback
|
|
155
|
+
* case every events.jsonl line counts as a candidate "foreign" event.
|
|
156
|
+
*
|
|
157
|
+
* Known limitation: the cutoff uses `lock.started_at` as recorded at lock
|
|
158
|
+
* acquisition time — a `forceAcquire()` takeover (stale-lock override) or
|
|
159
|
+
* cross-machine clock skew can shift this value away from the wall-clock
|
|
160
|
+
* instant the CURRENT process actually started, which can widen or narrow
|
|
161
|
+
* the "foreign" window by the skew amount. This banner is advisory-only
|
|
162
|
+
* tolerance (a nudge, never a gate), so an imprecise cutoff only affects when
|
|
163
|
+
* the warn/alert nudge fires, never blocks anything.
|
|
164
|
+
*
|
|
165
|
+
* @param {string} repoRoot
|
|
166
|
+
* @param {number} nowMs
|
|
167
|
+
* @returns {number}
|
|
168
|
+
*/
|
|
169
|
+
function resolveCutoffMs(repoRoot, nowMs) {
|
|
170
|
+
let lock;
|
|
171
|
+
try {
|
|
172
|
+
lock = readLock({ repoRoot });
|
|
173
|
+
} catch {
|
|
174
|
+
return nowMs;
|
|
175
|
+
}
|
|
176
|
+
if (!lock || typeof lock.started_at !== 'string') return nowMs;
|
|
177
|
+
const parsed = Date.parse(lock.started_at);
|
|
178
|
+
return Number.isFinite(parsed) ? parsed : nowMs;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Check sessions-ledger staleness and produce a session-start banner.
|
|
183
|
+
*
|
|
184
|
+
* Silent (`null`) when: `sessions.jsonl` is missing/empty/entirely
|
|
185
|
+
* unparseable, `events.jsonl` is missing/empty, no foreign (pre-cutoff)
|
|
186
|
+
* event exists, the foreign event is not after the last ledger entry, or the
|
|
187
|
+
* resulting gap is under the warn threshold. Never throws.
|
|
188
|
+
*
|
|
189
|
+
* @param {{repoRoot: string, now?: number}} opts
|
|
190
|
+
* - `repoRoot`: REQUIRED absolute path to the repo root.
|
|
191
|
+
* - `now`: optional injectable clock (epoch ms); defaults to `Date.now()`.
|
|
192
|
+
* Used only as the cutoff fallback when no session.lock is readable.
|
|
193
|
+
* @returns {null | {
|
|
194
|
+
* severity: 'warn'|'alert',
|
|
195
|
+
* message: string,
|
|
196
|
+
* lastLedgerAt: string,
|
|
197
|
+
* lastForeignEventAt: string,
|
|
198
|
+
* deltaHours: number,
|
|
199
|
+
* }}
|
|
200
|
+
*/
|
|
201
|
+
export function checkSessionsStaleness({ repoRoot, now = Date.now() } = {}) {
|
|
202
|
+
try {
|
|
203
|
+
if (!repoRoot || typeof repoRoot !== 'string') return null;
|
|
204
|
+
|
|
205
|
+
const nowMs = typeof now === 'number' && Number.isFinite(now) ? now : Date.now();
|
|
206
|
+
|
|
207
|
+
const sessionLines = readJsonlLines(path.join(repoRoot, SESSIONS_PATH));
|
|
208
|
+
if (sessionLines === null || sessionLines.length === 0) return null;
|
|
209
|
+
|
|
210
|
+
const ledger = lastLedgerEntry(sessionLines);
|
|
211
|
+
if (ledger === null) return null;
|
|
212
|
+
|
|
213
|
+
const eventLines = readJsonlLines(path.join(repoRoot, EVENTS_PATH));
|
|
214
|
+
if (eventLines === null || eventLines.length === 0) return null;
|
|
215
|
+
|
|
216
|
+
const cutoffMs = resolveCutoffMs(repoRoot, nowMs);
|
|
217
|
+
|
|
218
|
+
const foreign = newestForeignEvent(eventLines, cutoffMs);
|
|
219
|
+
if (foreign === null) return null;
|
|
220
|
+
|
|
221
|
+
const deltaMs = foreign.ms - ledger.ms;
|
|
222
|
+
if (deltaMs <= 0) return null; // foreign activity is not AFTER the last ledger entry
|
|
223
|
+
|
|
224
|
+
const deltaHours = Math.round((deltaMs / 3600000) * 10) / 10;
|
|
225
|
+
if (deltaHours <= WARN_THRESHOLD_HOURS) return null;
|
|
226
|
+
|
|
227
|
+
const severity = deltaHours > ALERT_THRESHOLD_HOURS ? 'alert' : 'warn';
|
|
228
|
+
|
|
229
|
+
const base =
|
|
230
|
+
`sessions-staleness: last sessions.jsonl entry ${ledger.iso} is ${deltaHours}h behind ` +
|
|
231
|
+
`pre-session events.jsonl activity ${foreign.iso} — possible close-through gap ` +
|
|
232
|
+
`(sessions ended without a ledger record; run node scripts/backfill-abandoned-sessions.mjs --dry-run)`;
|
|
233
|
+
|
|
234
|
+
const message = severity === 'alert' ? `🚨 ${base} — gap exceeds 24h.` : `⚠ ${base}.`;
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
severity,
|
|
238
|
+
message,
|
|
239
|
+
lastLedgerAt: ledger.iso,
|
|
240
|
+
lastForeignEventAt: foreign.iso,
|
|
241
|
+
deltaHours,
|
|
242
|
+
};
|
|
243
|
+
} catch {
|
|
244
|
+
// Defensive catch-all — banner must never throw.
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* blast-radius-classifier.mjs — R5 blast-radius classifier for the C2
|
|
3
|
+
* auto-repair engine (Epic #643 Skill Self-Evolution Foundation / issue #647).
|
|
4
|
+
*
|
|
5
|
+
* The heart of the gate-per-artifact-type design: maps a repair target's path
|
|
6
|
+
* to a `{ targetType, gate, posture }` triple that the auto-repair engine uses
|
|
7
|
+
* to decide whether a change may be applied autonomously (behind a gate) or
|
|
8
|
+
* must always go through a merge request.
|
|
9
|
+
*
|
|
10
|
+
* SECURITY (R5 traversal safety): the classifier is PATH-TRAVERSAL-SAFE and
|
|
11
|
+
* FAIL-CLOSED. `targetPath` is never trusted lexically — it is resolved against
|
|
12
|
+
* a canonicalised `repoRoot` and any path that escapes the repo (or is
|
|
13
|
+
* otherwise ambiguous) is classified as `unknown` with the safest posture
|
|
14
|
+
* (`always-mr`). The ONLY input that yields `autonomous-gated` is the repo's
|
|
15
|
+
* own ROOT `CLAUDE.md` / `AGENTS.md`.
|
|
16
|
+
*
|
|
17
|
+
* No throwing: every code path returns a valid classification triple.
|
|
18
|
+
*
|
|
19
|
+
* Consumers:
|
|
20
|
+
* - C2 auto-repair engine (issue #647) — decides apply-vs-MR per repair target.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import path from 'node:path';
|
|
24
|
+
import fs from 'node:fs';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @typedef {'plugin-skill' | 'local-skill' | 'local-config' | 'unknown'} TargetType
|
|
28
|
+
* @typedef {'none' | 'config-validation'} Gate
|
|
29
|
+
* @typedef {'always-mr' | 'autonomous-gated'} Posture
|
|
30
|
+
* @typedef {{ targetType: TargetType, gate: Gate, posture: Posture }} Classification
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The fail-closed default: returned for any path that escapes the repo, is
|
|
35
|
+
* empty/ambiguous, or matches no known artifact type. Never silent-applies.
|
|
36
|
+
* @type {Classification}
|
|
37
|
+
*/
|
|
38
|
+
const FAIL_CLOSED = Object.freeze({
|
|
39
|
+
targetType: 'unknown',
|
|
40
|
+
gate: 'none',
|
|
41
|
+
posture: 'always-mr',
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Classify a repair target's path into its blast-radius triple.
|
|
46
|
+
*
|
|
47
|
+
* The path is resolved against a canonicalised `repoRoot` before any
|
|
48
|
+
* classification rule is applied; paths that resolve outside the repo are
|
|
49
|
+
* fail-closed to `unknown` / `always-mr`. Path rules are applied first-match-wins
|
|
50
|
+
* against the repo-relative POSIX path:
|
|
51
|
+
*
|
|
52
|
+
* 1. `.claude/skills` (or under it) → local-skill / none / always-mr
|
|
53
|
+
* 2. `skills` (or under it) → plugin-skill / none / always-mr
|
|
54
|
+
* 3. ROOT `CLAUDE.md` / `AGENTS.md` → local-config / config-validation / autonomous-gated
|
|
55
|
+
* 4. anything else → unknown / none / always-mr (fail-closed)
|
|
56
|
+
*
|
|
57
|
+
* @param {string} targetPath — repair target path (relative or absolute); not trusted lexically.
|
|
58
|
+
* @param {{ repoRoot: string }} options — `repoRoot` is the repository root to resolve against.
|
|
59
|
+
* @returns {Classification} the blast-radius triple; never throws.
|
|
60
|
+
*/
|
|
61
|
+
export function classifyTarget(targetPath, { repoRoot } = {}) {
|
|
62
|
+
// Guard the inputs — fail-closed on anything we cannot work with.
|
|
63
|
+
if (typeof targetPath !== 'string' || typeof repoRoot !== 'string' || repoRoot.length === 0) {
|
|
64
|
+
return { ...FAIL_CLOSED };
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Canonicalise the repo root. realpathSync resolves symlinks so a symlinked
|
|
68
|
+
// repoRoot still anchors the escape check correctly. If it throws (e.g. the
|
|
69
|
+
// root does not exist yet), fall back to a lexical resolve.
|
|
70
|
+
let root;
|
|
71
|
+
try {
|
|
72
|
+
root = fs.realpathSync(path.resolve(repoRoot));
|
|
73
|
+
} catch {
|
|
74
|
+
root = path.resolve(repoRoot);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Resolve the target against the canonical root, then compute the repo-relative
|
|
78
|
+
// path. We do NOT inspect `targetPath` lexically before this point.
|
|
79
|
+
const abs = path.resolve(root, targetPath);
|
|
80
|
+
let rel = path.relative(root, abs);
|
|
81
|
+
|
|
82
|
+
// ESCAPE CHECK (fail-closed): empty rel means the target IS the root itself;
|
|
83
|
+
// a `..` prefix or an absolute rel means the target escaped the repo. Either
|
|
84
|
+
// way → unknown / always-mr. Never throw.
|
|
85
|
+
if (rel === '' || rel.startsWith('..') || path.isAbsolute(rel)) {
|
|
86
|
+
return { ...FAIL_CLOSED };
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Normalize separators to POSIX so the rules below are platform-independent.
|
|
90
|
+
rel = rel.split(path.sep).join('/');
|
|
91
|
+
|
|
92
|
+
// Rule 1 — local (project) skills under `.claude/skills`.
|
|
93
|
+
if (rel === '.claude/skills' || rel.startsWith('.claude/skills/')) {
|
|
94
|
+
return { targetType: 'local-skill', gate: 'none', posture: 'always-mr' };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Rule 2 — plugin skills under `skills`.
|
|
98
|
+
if (rel === 'skills' || rel.startsWith('skills/')) {
|
|
99
|
+
return { targetType: 'plugin-skill', gate: 'none', posture: 'always-mr' };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Rule 3 — ROOT instruction files ONLY (no path separator in rel). A nested
|
|
103
|
+
// `subdir/CLAUDE.md` falls through to the fail-closed default below.
|
|
104
|
+
if (rel === 'CLAUDE.md' || rel === 'AGENTS.md') {
|
|
105
|
+
return {
|
|
106
|
+
targetType: 'local-config',
|
|
107
|
+
gate: 'config-validation',
|
|
108
|
+
posture: 'autonomous-gated',
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Rule 4 — fail-closed default for everything else.
|
|
113
|
+
return { ...FAIL_CLOSED };
|
|
114
|
+
}
|
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* candidate-intake.mjs — Pure transform for the #647 C2 auto-repair engine.
|
|
3
|
+
*
|
|
4
|
+
* Ingests the two repair-candidate feeders and normalises them into a single
|
|
5
|
+
* `RepairCandidate[]` shape:
|
|
6
|
+
* 1. `/evolve` learnings (`.orchestrator/metrics/learnings.jsonl` records)
|
|
7
|
+
* 2. `claude-md-drift-check` output (`driftResult.errors[]`)
|
|
8
|
+
*
|
|
9
|
+
* This module is a PURE transform: it performs NO I/O. It never reads or writes
|
|
10
|
+
* any file. Persistence and the `processed_at` / `superseded_by` lifecycle are
|
|
11
|
+
* OWNED BY the sibling `idempotency.mjs` module — this module only emits the
|
|
12
|
+
* raw candidates with those fields nulled out.
|
|
13
|
+
*
|
|
14
|
+
* The `id` field is a deterministic short hash of (source, target_path,
|
|
15
|
+
* fingerprint), so the same input always yields the same id. That determinism
|
|
16
|
+
* is the idempotency key the sibling module relies on.
|
|
17
|
+
*
|
|
18
|
+
* Part of Epic #643 → issue #647 (C2 auto-repair engine).
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import { createHash } from 'node:crypto';
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* @typedef {Object} RepairCandidate
|
|
25
|
+
* @property {string} id Deterministic short hash (idempotency key).
|
|
26
|
+
* @property {1} schema_version Schema version, always 1.
|
|
27
|
+
* @property {'evolve-learning'|'drift-check'} source Originating feeder.
|
|
28
|
+
* @property {string} source_ref Back-reference into the source feeder.
|
|
29
|
+
* @property {string} target_path Repo-relative path the repair targets.
|
|
30
|
+
* @property {number} evidence Numeric confidence/strength of the signal.
|
|
31
|
+
* @property {'confidence'|'filesystem-fact'} evidence_kind Interpretation of `evidence`.
|
|
32
|
+
* @property {string} proposed_change Short human-readable description of the fix.
|
|
33
|
+
* @property {string} rationale Why this candidate exists.
|
|
34
|
+
* @property {string} created_at ISO timestamp when the candidate was minted.
|
|
35
|
+
* @property {null} processed_at Always null (idempotency.mjs sets this later).
|
|
36
|
+
* @property {null} superseded_by Always null (idempotency.mjs sets this later).
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
/** Drift-check checks that map to error candidates → `proposed_change` template. */
|
|
40
|
+
const DRIFT_PROPOSED_CHANGE = {
|
|
41
|
+
'path-resolver': () => 'Update/remove stale absolute path',
|
|
42
|
+
'project-count-sync': () => 'Sync project count',
|
|
43
|
+
'session-file-existence': () => 'Remove/repoint missing session-file ref',
|
|
44
|
+
'issue-reference-freshness': () =>
|
|
45
|
+
'Move/remove closed issue ref out of forward-looking section',
|
|
46
|
+
'session-config-parity': () => 'Add missing Session Config key',
|
|
47
|
+
'vault-dir-parity': () => 'Reconcile vault-dir between CLAUDE.md and AGENTS.md',
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/** Path-extraction regex: repo-relative dir paths OR bare known-extension filenames. */
|
|
51
|
+
const PATH_RE =
|
|
52
|
+
/(scripts|skills|hooks|tests|docs)\/[\w./-]+|\b[\w-]+\.(mjs|md|json|js|ts)\b/;
|
|
53
|
+
|
|
54
|
+
/** Prescriptive-verb regex: the insight must propose an action, not merely describe. */
|
|
55
|
+
const PRESCRIPTIVE_RE =
|
|
56
|
+
/\b(fix|change|switch|default|pin|port|remove|add|update|replace|disable|enable|require)\b/i;
|
|
57
|
+
|
|
58
|
+
/** Drift-check statuses that mean "no candidates to emit". */
|
|
59
|
+
const DRIFT_INERT_STATUSES = new Set(['skipped', 'skipped-mode-off', undefined]);
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Slugify a string for use inside the deterministic fingerprint. The exact form
|
|
63
|
+
* is internal (it only feeds the hash) — kebab-case, alnum-only, collapsed.
|
|
64
|
+
* @param {string} input
|
|
65
|
+
* @returns {string}
|
|
66
|
+
*/
|
|
67
|
+
function slug(input) {
|
|
68
|
+
return String(input ?? '')
|
|
69
|
+
.toLowerCase()
|
|
70
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
71
|
+
.replace(/^-+|-+$/g, '');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Build the deterministic candidate id from its idempotency triple.
|
|
76
|
+
* @param {string} source
|
|
77
|
+
* @param {string} targetPath
|
|
78
|
+
* @param {string} fingerprint
|
|
79
|
+
* @returns {string}
|
|
80
|
+
*/
|
|
81
|
+
function makeId(source, targetPath, fingerprint) {
|
|
82
|
+
const hash = createHash('sha256')
|
|
83
|
+
.update(source + '\0' + targetPath + '\0' + fingerprint)
|
|
84
|
+
.digest('hex')
|
|
85
|
+
.slice(0, 8);
|
|
86
|
+
return `rc-${hash}`;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Extract the first repo-relative path from a candidate's text fields.
|
|
91
|
+
* @param {string} text
|
|
92
|
+
* @returns {string|null}
|
|
93
|
+
*/
|
|
94
|
+
function extractPath(text) {
|
|
95
|
+
const match = PATH_RE.exec(String(text ?? ''));
|
|
96
|
+
return match ? match[0] : null;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Map a single learning record to a RepairCandidate, or null when it fails any
|
|
101
|
+
* actionable filter. learnings.jsonl records are NOT uniform — only `confidence`
|
|
102
|
+
* and `created_at` are guaranteed — so id/subject/insight/evidence are handled
|
|
103
|
+
* defensively.
|
|
104
|
+
* @param {Record<string, unknown>} learning
|
|
105
|
+
* @param {number} evidenceFloor
|
|
106
|
+
* @param {string} nowIso
|
|
107
|
+
* @returns {RepairCandidate|null}
|
|
108
|
+
*/
|
|
109
|
+
function learningToCandidate(learning, evidenceFloor, nowIso) {
|
|
110
|
+
if (!learning || typeof learning !== 'object') return null;
|
|
111
|
+
|
|
112
|
+
// Filter 1: confidence gate.
|
|
113
|
+
const confidence = /** @type {unknown} */ (learning.confidence);
|
|
114
|
+
if (typeof confidence !== 'number' || !Number.isFinite(confidence)) return null;
|
|
115
|
+
if (confidence < evidenceFloor) return null;
|
|
116
|
+
|
|
117
|
+
// Filter 4: not expired (missing expires_at ⇒ live).
|
|
118
|
+
const expiresAt = learning.expires_at;
|
|
119
|
+
if (typeof expiresAt === 'string' && expiresAt.length > 0 && expiresAt < nowIso) {
|
|
120
|
+
return null;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const subject = typeof learning.subject === 'string' ? learning.subject : '';
|
|
124
|
+
const insight = typeof learning.insight === 'string' ? learning.insight : '';
|
|
125
|
+
|
|
126
|
+
// Filter 2: resolvable target path from subject OR insight.
|
|
127
|
+
const targetPath = extractPath(subject) ?? extractPath(insight);
|
|
128
|
+
if (!targetPath) return null;
|
|
129
|
+
|
|
130
|
+
// Filter 3: insight must be prescriptive.
|
|
131
|
+
if (!PRESCRIPTIVE_RE.test(insight)) return null;
|
|
132
|
+
|
|
133
|
+
const source = 'evolve-learning';
|
|
134
|
+
const sourceRef = typeof learning.id === 'string' && learning.id.length > 0
|
|
135
|
+
? learning.id
|
|
136
|
+
: null;
|
|
137
|
+
const fingerprint = slug(targetPath + '-' + insight.slice(0, 80));
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
id: makeId(source, targetPath, fingerprint),
|
|
141
|
+
schema_version: 1,
|
|
142
|
+
source,
|
|
143
|
+
source_ref: sourceRef,
|
|
144
|
+
target_path: targetPath,
|
|
145
|
+
evidence: confidence,
|
|
146
|
+
evidence_kind: 'confidence',
|
|
147
|
+
proposed_change: insight,
|
|
148
|
+
rationale: `evolve learning (confidence ${confidence}): ${insight}`,
|
|
149
|
+
created_at: nowIso,
|
|
150
|
+
processed_at: null,
|
|
151
|
+
superseded_by: null,
|
|
152
|
+
};
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Map a single drift-check error to a RepairCandidate. Drift errors are
|
|
157
|
+
* filesystem facts (evidence 1.0). Unknown checks fall back to a generic
|
|
158
|
+
* proposed-change derived from the check name.
|
|
159
|
+
* @param {Record<string, unknown>} err
|
|
160
|
+
* @param {string} nowIso
|
|
161
|
+
* @returns {RepairCandidate|null}
|
|
162
|
+
*/
|
|
163
|
+
function driftErrorToCandidate(err, nowIso) {
|
|
164
|
+
if (!err || typeof err !== 'object') return null;
|
|
165
|
+
|
|
166
|
+
const check = typeof err.check === 'string' ? err.check : '';
|
|
167
|
+
const file = typeof err.file === 'string' ? err.file : '';
|
|
168
|
+
const line = err.line ?? '';
|
|
169
|
+
const message = typeof err.message === 'string' ? err.message : '';
|
|
170
|
+
|
|
171
|
+
// target_path is required — drift errors carry a repo-relative file already.
|
|
172
|
+
if (!file) return null;
|
|
173
|
+
|
|
174
|
+
const source = 'drift-check';
|
|
175
|
+
const sourceRef = `${check}:${file}:${line}`;
|
|
176
|
+
|
|
177
|
+
// Per-check proposed-change templates; command-count needs the actual count.
|
|
178
|
+
let proposedChange;
|
|
179
|
+
if (check === 'command-count') {
|
|
180
|
+
// Prefer the STRUCTURED bare claimed number (checker.mjs:495 exposes
|
|
181
|
+
// `command_count: { actual, claimed }` where `claimed` is `parseInt(m[1])`).
|
|
182
|
+
// `err.extracted` is the FULL regex match (e.g. "8 commands"); using it here
|
|
183
|
+
// produced the malformed double-word `'8 commands commands'` that the engine
|
|
184
|
+
// whitelist regex (engine.mjs COMMAND_COUNT_SHAPE) correctly rejects — making
|
|
185
|
+
// autonomous-apply dead code for this shape (#651 FIX 1).
|
|
186
|
+
const claimed =
|
|
187
|
+
(err.command_count && typeof err.command_count === 'object' && err.command_count !== null
|
|
188
|
+
? /** @type {Record<string, unknown>} */ (err.command_count).claimed
|
|
189
|
+
: undefined) ?? err.extracted ?? '?';
|
|
190
|
+
const actual =
|
|
191
|
+
err.command_count && typeof err.command_count === 'object' && err.command_count !== null
|
|
192
|
+
? /** @type {Record<string, unknown>} */ (err.command_count).actual ?? '?'
|
|
193
|
+
: '?';
|
|
194
|
+
proposedChange = `Update narrative '${claimed} commands' to actual ${actual}`;
|
|
195
|
+
} else if (Object.prototype.hasOwnProperty.call(DRIFT_PROPOSED_CHANGE, check)) {
|
|
196
|
+
proposedChange = DRIFT_PROPOSED_CHANGE[check]();
|
|
197
|
+
} else {
|
|
198
|
+
proposedChange = `Resolve drift-check '${check}'`;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const fingerprint = slug(file + '-' + message.slice(0, 80));
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
id: makeId(source, file, fingerprint),
|
|
205
|
+
schema_version: 1,
|
|
206
|
+
source,
|
|
207
|
+
source_ref: sourceRef,
|
|
208
|
+
target_path: file,
|
|
209
|
+
evidence: 1.0,
|
|
210
|
+
evidence_kind: 'filesystem-fact',
|
|
211
|
+
proposed_change: proposedChange,
|
|
212
|
+
rationale: message,
|
|
213
|
+
created_at: nowIso,
|
|
214
|
+
processed_at: null,
|
|
215
|
+
superseded_by: null,
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Ingest the two repair-candidate feeders and return a normalised
|
|
221
|
+
* `RepairCandidate[]`. Pure transform — performs no I/O.
|
|
222
|
+
*
|
|
223
|
+
* @param {Object} params
|
|
224
|
+
* @param {Array<Record<string, unknown>>} [params.learnings] - `/evolve` learning records.
|
|
225
|
+
* @param {{ status?: string, errors?: Array<Record<string, unknown>>, warnings?: unknown[] }|null} [params.driftResult]
|
|
226
|
+
* claude-md-drift-check output. Only `errors[]` are mapped; `warnings[]`
|
|
227
|
+
* are skipped. A null result, or a status of `skipped`/`skipped-mode-off`/
|
|
228
|
+
* undefined, emits zero drift candidates.
|
|
229
|
+
* @param {string} [params.repoRoot] - repo root (accepted for caller symmetry; unused in the pure transform).
|
|
230
|
+
* @param {number} [params.evidenceFloor=0.5] - minimum learning confidence to qualify.
|
|
231
|
+
* @param {string} [params.now] - ISO timestamp for `created_at` + expiry checks (test determinism).
|
|
232
|
+
* @returns {RepairCandidate[]}
|
|
233
|
+
*/
|
|
234
|
+
export function extractCandidates({
|
|
235
|
+
learnings,
|
|
236
|
+
driftResult,
|
|
237
|
+
// eslint-disable-next-line no-unused-vars -- accepted for caller symmetry; pure transform does no path resolution
|
|
238
|
+
repoRoot,
|
|
239
|
+
evidenceFloor = 0.5,
|
|
240
|
+
now,
|
|
241
|
+
} = {}) {
|
|
242
|
+
const nowIso = typeof now === 'string' && now.length > 0 ? now : new Date().toISOString();
|
|
243
|
+
const floor = Number.isFinite(evidenceFloor) ? evidenceFloor : 0.5;
|
|
244
|
+
|
|
245
|
+
/** @type {RepairCandidate[]} */
|
|
246
|
+
const candidates = [];
|
|
247
|
+
|
|
248
|
+
// Feeder 1: /evolve learnings.
|
|
249
|
+
if (Array.isArray(learnings)) {
|
|
250
|
+
for (const learning of learnings) {
|
|
251
|
+
const candidate = learningToCandidate(learning, floor, nowIso);
|
|
252
|
+
if (candidate) candidates.push(candidate);
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Feeder 2: drift-check errors (skip when inert).
|
|
257
|
+
if (
|
|
258
|
+
driftResult &&
|
|
259
|
+
typeof driftResult === 'object' &&
|
|
260
|
+
!DRIFT_INERT_STATUSES.has(driftResult.status) &&
|
|
261
|
+
Array.isArray(driftResult.errors)
|
|
262
|
+
) {
|
|
263
|
+
for (const err of driftResult.errors) {
|
|
264
|
+
const candidate = driftErrorToCandidate(err, nowIso);
|
|
265
|
+
if (candidate) candidates.push(candidate);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return candidates;
|
|
270
|
+
}
|