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,328 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* category8.mjs — Category 8: Large-Codebase Readiness (weight: 8)
|
|
3
|
+
*
|
|
4
|
+
* Checks (6, max_points sum = 10):
|
|
5
|
+
* c8.1 layered-claude-md (max 2)
|
|
6
|
+
* c8.2 codebase-map-present (max 2)
|
|
7
|
+
* c8.3 lsp-configured (max 2)
|
|
8
|
+
* c8.4 scoped-test-lint (max 1)
|
|
9
|
+
* c8.5 permissions-deny-present(max 1)
|
|
10
|
+
* c8.6 lean-root (max 2)
|
|
11
|
+
*
|
|
12
|
+
* Anthropic large-codebase best-practice surface: layered instruction files,
|
|
13
|
+
* a navigable codebase map, language-server tooling, scoped quality commands,
|
|
14
|
+
* version-controlled destructive-command exclusions, and a delegated lean root.
|
|
15
|
+
*
|
|
16
|
+
* status field is only 'pass' / 'fail' — partial tiers emit a reduced-points
|
|
17
|
+
* pass() (there is no 'partial' constructor in helpers.mjs).
|
|
18
|
+
*
|
|
19
|
+
* Stdlib only: node:fs, node:path.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
import { readdirSync } from 'node:fs';
|
|
23
|
+
import { join } from 'node:path';
|
|
24
|
+
|
|
25
|
+
import { safeRead, safeJson, pass, fail } from './helpers.mjs';
|
|
26
|
+
import { resolveInstructionFile } from '../../common.mjs';
|
|
27
|
+
|
|
28
|
+
const NESTED_SCAN_MAX_DEPTH = 4;
|
|
29
|
+
const EXCLUDED_DIRS = new Set(['node_modules', '.git', 'tests', 'fixtures']);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Recursively collect nested instruction files (CLAUDE.md / AGENTS.md) whose
|
|
33
|
+
* dirname is NOT root. Excludes node_modules/.git/tests/fixtures by
|
|
34
|
+
* path-relative-to-root segment. Depth-bounded (≤ 4).
|
|
35
|
+
*
|
|
36
|
+
* @returns {string[]} repo-relative paths of nested instruction files
|
|
37
|
+
*/
|
|
38
|
+
function collectNestedInstructionFiles(root) {
|
|
39
|
+
const found = [];
|
|
40
|
+
|
|
41
|
+
function walk(absDir, relParts, depth) {
|
|
42
|
+
if (depth > NESTED_SCAN_MAX_DEPTH) return;
|
|
43
|
+
let entries;
|
|
44
|
+
try {
|
|
45
|
+
entries = readdirSync(absDir, { withFileTypes: true });
|
|
46
|
+
} catch {
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
for (const ent of entries) {
|
|
50
|
+
if (ent.isDirectory()) {
|
|
51
|
+
if (EXCLUDED_DIRS.has(ent.name)) continue;
|
|
52
|
+
walk(join(absDir, ent.name), [...relParts, ent.name], depth + 1);
|
|
53
|
+
} else if (ent.isFile() && (ent.name === 'CLAUDE.md' || ent.name === 'AGENTS.md')) {
|
|
54
|
+
// dirname !== root → must be nested (relParts non-empty)
|
|
55
|
+
if (relParts.length > 0) {
|
|
56
|
+
found.push([...relParts, ent.name].join('/'));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
walk(root, [], 0);
|
|
63
|
+
return found;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export function runCategory8(root) {
|
|
67
|
+
const checks = [];
|
|
68
|
+
|
|
69
|
+
// Compute the nested instruction-file list ONCE; shared by c8.1 and c8.4.
|
|
70
|
+
const nestedFiles = collectNestedInstructionFiles(root);
|
|
71
|
+
const nestedContents = nestedFiles.map((rel) => ({
|
|
72
|
+
rel,
|
|
73
|
+
text: safeRead(join(root, rel)),
|
|
74
|
+
}));
|
|
75
|
+
|
|
76
|
+
// c8.1 layered-claude-md (max 2)
|
|
77
|
+
{
|
|
78
|
+
const checkId = 'layered-claude-md';
|
|
79
|
+
if (nestedFiles.length === 0) {
|
|
80
|
+
checks.push(fail({
|
|
81
|
+
checkId, maxPoints: 2, path: '.',
|
|
82
|
+
evidence: { nestedFiles: [], withMarker: false },
|
|
83
|
+
message: 'no nested CLAUDE.md/AGENTS.md found (excluding node_modules/.git/tests/fixtures)',
|
|
84
|
+
}));
|
|
85
|
+
} else {
|
|
86
|
+
const markerRe = /(^|\n)## |convention|test|lint|command/i;
|
|
87
|
+
const withMarker = nestedContents.some((f) => f.text && markerRe.test(f.text));
|
|
88
|
+
if (withMarker) {
|
|
89
|
+
checks.push(pass({
|
|
90
|
+
checkId, points: 2, maxPoints: 2, path: nestedFiles[0],
|
|
91
|
+
evidence: { nestedFiles, withMarker: true },
|
|
92
|
+
message: `${nestedFiles.length} nested instruction file(s), ≥1 with a structural/convention marker`,
|
|
93
|
+
}));
|
|
94
|
+
} else {
|
|
95
|
+
checks.push(pass({
|
|
96
|
+
checkId, points: 1, maxPoints: 2, path: nestedFiles[0],
|
|
97
|
+
evidence: { nestedFiles, withMarker: false },
|
|
98
|
+
message: `${nestedFiles.length} nested instruction file(s) but none contains a structural/convention marker`,
|
|
99
|
+
}));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// c8.2 codebase-map-present (max 2)
|
|
105
|
+
{
|
|
106
|
+
const checkId = 'codebase-map-present';
|
|
107
|
+
const candidates = [
|
|
108
|
+
'.orchestrator/steering/structure.md',
|
|
109
|
+
'docs/architecture.md',
|
|
110
|
+
'docs/ARCHITECTURE.md',
|
|
111
|
+
'ARCHITECTURE.md',
|
|
112
|
+
'docs/codebase-map.md',
|
|
113
|
+
];
|
|
114
|
+
let matched = null;
|
|
115
|
+
let matchedText = null;
|
|
116
|
+
for (const rel of candidates) {
|
|
117
|
+
const text = safeRead(join(root, rel));
|
|
118
|
+
if (text !== null) {
|
|
119
|
+
matched = rel;
|
|
120
|
+
matchedText = text;
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (matched === null) {
|
|
125
|
+
checks.push(fail({
|
|
126
|
+
checkId, maxPoints: 2, path: candidates[0],
|
|
127
|
+
evidence: { matched: null, lineCount: 0 },
|
|
128
|
+
message: `no codebase map found (looked for: ${candidates.join(', ')})`,
|
|
129
|
+
}));
|
|
130
|
+
} else {
|
|
131
|
+
const lc = matchedText.split('\n').length;
|
|
132
|
+
if (lc >= 10) {
|
|
133
|
+
checks.push(pass({
|
|
134
|
+
checkId, points: 2, maxPoints: 2, path: matched,
|
|
135
|
+
evidence: { matched, lineCount: lc },
|
|
136
|
+
message: `${matched} present (${lc} lines, ≥ 10)`,
|
|
137
|
+
}));
|
|
138
|
+
} else {
|
|
139
|
+
checks.push(fail({
|
|
140
|
+
checkId, maxPoints: 2, path: matched,
|
|
141
|
+
evidence: { matched, lineCount: lc },
|
|
142
|
+
message: `${matched} present but only ${lc} lines (< 10)`,
|
|
143
|
+
}));
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// c8.3 lsp-configured (max 2)
|
|
149
|
+
{
|
|
150
|
+
const checkId = 'lsp-configured';
|
|
151
|
+
const mcpRel = '.mcp.json';
|
|
152
|
+
const mcpJson = safeJson(safeRead(join(root, mcpRel)));
|
|
153
|
+
const lspRe = /serena|language[- ]?server|lsp|pyright|typescript-language-server/i;
|
|
154
|
+
let mcpMatch = false;
|
|
155
|
+
if (mcpJson && mcpJson.mcpServers && typeof mcpJson.mcpServers === 'object') {
|
|
156
|
+
for (const [key, srv] of Object.entries(mcpJson.mcpServers)) {
|
|
157
|
+
const command = srv && typeof srv.command === 'string' ? srv.command : '';
|
|
158
|
+
const args = srv && Array.isArray(srv.args) ? srv.args.join(' ') : '';
|
|
159
|
+
if (lspRe.test(key) || lspRe.test(command) || lspRe.test(args)) {
|
|
160
|
+
mcpMatch = true;
|
|
161
|
+
break;
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
if (mcpMatch) {
|
|
166
|
+
checks.push(pass({
|
|
167
|
+
checkId, points: 2, maxPoints: 2, path: mcpRel,
|
|
168
|
+
evidence: { mcpConfigured: true, docFallback: false },
|
|
169
|
+
message: '.mcp.json declares a language-server / LSP MCP server',
|
|
170
|
+
}));
|
|
171
|
+
} else {
|
|
172
|
+
// partial tier: a doc references a language server / LSP
|
|
173
|
+
const docCandidates = ['docs/lsp.md', '.claude/rules/lsp.md'];
|
|
174
|
+
const globDirs = [
|
|
175
|
+
{ dir: '.claude/rules', ext: '.md' },
|
|
176
|
+
{ dir: 'docs', ext: '.md' },
|
|
177
|
+
];
|
|
178
|
+
for (const { dir, ext } of globDirs) {
|
|
179
|
+
let entries;
|
|
180
|
+
try {
|
|
181
|
+
entries = readdirSync(join(root, dir));
|
|
182
|
+
} catch {
|
|
183
|
+
entries = [];
|
|
184
|
+
}
|
|
185
|
+
for (const e of entries) {
|
|
186
|
+
if (e.endsWith(ext)) docCandidates.push(`${dir}/${e}`);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
const docBodyRe = /language server|\bLSP\b/i;
|
|
190
|
+
let docHit = null;
|
|
191
|
+
for (const rel of docCandidates) {
|
|
192
|
+
const text = safeRead(join(root, rel));
|
|
193
|
+
if (text && docBodyRe.test(text)) {
|
|
194
|
+
docHit = rel;
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (docHit) {
|
|
199
|
+
checks.push(pass({
|
|
200
|
+
checkId, points: 1, maxPoints: 2, path: docHit,
|
|
201
|
+
evidence: { mcpConfigured: false, docFallback: true },
|
|
202
|
+
message: `no LSP MCP server, but ${docHit} documents a language server / LSP`,
|
|
203
|
+
}));
|
|
204
|
+
} else {
|
|
205
|
+
checks.push(fail({
|
|
206
|
+
checkId, maxPoints: 2, path: mcpRel,
|
|
207
|
+
evidence: { mcpConfigured: false, docFallback: false },
|
|
208
|
+
message: 'no LSP MCP server in .mcp.json and no language-server documentation',
|
|
209
|
+
}));
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// c8.4 scoped-test-lint (max 1)
|
|
215
|
+
{
|
|
216
|
+
const checkId = 'scoped-test-lint';
|
|
217
|
+
const cmdKeyRe = /(test|lint)[- ]?command\s*:/i;
|
|
218
|
+
const tokenRe = /\b(npm|pnpm|vitest|eslint)\b/i;
|
|
219
|
+
const headingRe = /(^|\n)## /;
|
|
220
|
+
const testLintTokenRe = /\b(test|lint)\b/i;
|
|
221
|
+
const scoped = nestedContents.some((f) => {
|
|
222
|
+
if (!f.text) return false;
|
|
223
|
+
if (cmdKeyRe.test(f.text)) return true;
|
|
224
|
+
// an npm/pnpm/vitest/eslint test|lint token co-located with a '## ' heading
|
|
225
|
+
return headingRe.test(f.text) && tokenRe.test(f.text) && testLintTokenRe.test(f.text);
|
|
226
|
+
});
|
|
227
|
+
if (scoped) {
|
|
228
|
+
checks.push(pass({
|
|
229
|
+
checkId, points: 1, maxPoints: 1, path: nestedFiles[0] ?? '.',
|
|
230
|
+
evidence: { nestedFiles, scoped: true },
|
|
231
|
+
message: 'a nested instruction file declares a scoped test/lint command',
|
|
232
|
+
}));
|
|
233
|
+
} else {
|
|
234
|
+
checks.push(fail({
|
|
235
|
+
checkId, maxPoints: 1, path: nestedFiles[0] ?? '.',
|
|
236
|
+
evidence: { nestedFiles, scoped: false },
|
|
237
|
+
message: 'no nested instruction file declares a scoped test/lint command',
|
|
238
|
+
}));
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
// c8.5 permissions-deny-present
|
|
243
|
+
//
|
|
244
|
+
// CROSS-REFERENCE — NOT a duplicate of repo-audit Category 1.
|
|
245
|
+
// skills/repo-audit/SKILL.md:52 checks only that `.claude/settings.json`
|
|
246
|
+
// EXISTS (`ls .claude/settings.json`, pass/fail). This check scores the
|
|
247
|
+
// `permissions.deny` ARRAY specifically: present AND non-empty. The two are
|
|
248
|
+
// orthogonal — repo-audit answers "is there a settings file?"; this answers
|
|
249
|
+
// "does it version-control destructive-command exclusions?" (Anthropic
|
|
250
|
+
// large-codebase best-practice: deny-list in VCS). Do NOT fold these; the
|
|
251
|
+
// harness-audit rubric (deterministic, scored) and repo-audit (agent-driven,
|
|
252
|
+
// baseline-compliance) are explicitly distinct surfaces per
|
|
253
|
+
// skills/repo-audit/SKILL.md:14.
|
|
254
|
+
{
|
|
255
|
+
const checkId = 'permissions-deny-present';
|
|
256
|
+
const relPath = '.claude/settings.json';
|
|
257
|
+
const json = safeJson(safeRead(join(root, relPath)));
|
|
258
|
+
const denyArr = json?.permissions?.deny;
|
|
259
|
+
if (Array.isArray(denyArr) && denyArr.length > 0) {
|
|
260
|
+
checks.push(pass({
|
|
261
|
+
checkId, points: 1, maxPoints: 1, path: relPath,
|
|
262
|
+
evidence: { denyCount: denyArr.length },
|
|
263
|
+
message: `.claude/settings.json permissions.deny has ${denyArr.length} entr${denyArr.length === 1 ? 'y' : 'ies'}`,
|
|
264
|
+
}));
|
|
265
|
+
} else {
|
|
266
|
+
checks.push(fail({
|
|
267
|
+
checkId, maxPoints: 1, path: relPath,
|
|
268
|
+
evidence: { denyCount: Array.isArray(denyArr) ? 0 : null },
|
|
269
|
+
message: '.claude/settings.json missing a non-empty permissions.deny array',
|
|
270
|
+
}));
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// c8.6 lean-root (max 2)
|
|
275
|
+
//
|
|
276
|
+
// ORTHOGONALITY TO category6 c6.1 (claude-md-line-count):
|
|
277
|
+
// c6.1 = single whole-file scalar (lineCount <= 250).
|
|
278
|
+
// c8.6 = (A) delegation-link presence AND (B) per-SECTION max line count <= 60.
|
|
279
|
+
// These disagree on real inputs: a 240-line file with one 200-line monolithic
|
|
280
|
+
// section passes c6.1 but fails c8.6/B; a 300-line file of tight delegated
|
|
281
|
+
// sections fails c6.1 but passes c8.6. Neither predicate computes the metric
|
|
282
|
+
// the other computes. lean-root is a STRUCTURE check, not a SIZE check.
|
|
283
|
+
{
|
|
284
|
+
const checkId = 'lean-root';
|
|
285
|
+
const instr = resolveInstructionFile(root);
|
|
286
|
+
const instrRel = instr ? (instr.kind === 'agents' ? 'AGENTS.md' : 'CLAUDE.md') : 'CLAUDE.md';
|
|
287
|
+
const text = instr ? safeRead(instr.path) : null;
|
|
288
|
+
if (!text) {
|
|
289
|
+
checks.push(fail({
|
|
290
|
+
checkId, maxPoints: 2, path: instrRel,
|
|
291
|
+
evidence: { delegationLink: false, maxSectionLines: 0 },
|
|
292
|
+
message: 'CLAUDE.md (or AGENTS.md alias) missing',
|
|
293
|
+
}));
|
|
294
|
+
} else {
|
|
295
|
+
// Predicate A — at least one markdown link to a delegated doc
|
|
296
|
+
const predicateA = /\]\(([^)]*\/)?(README\.md|AGENTS\.md|[^)]*CLAUDE\.md|\.orchestrator\/steering\/[^)]+\.md|skills\/_shared\/[^)]+\.md)/.test(text);
|
|
297
|
+
// Predicate B — split on '## ' headings, max section line count ≤ 60
|
|
298
|
+
const sections = text.split(/^## /m);
|
|
299
|
+
let maxSectionLines = 0;
|
|
300
|
+
for (const sec of sections) {
|
|
301
|
+
const lines = sec.split('\n').length;
|
|
302
|
+
if (lines > maxSectionLines) maxSectionLines = lines;
|
|
303
|
+
}
|
|
304
|
+
const predicateB = maxSectionLines <= 60;
|
|
305
|
+
if (predicateA && predicateB) {
|
|
306
|
+
checks.push(pass({
|
|
307
|
+
checkId, points: 2, maxPoints: 2, path: instrRel,
|
|
308
|
+
evidence: { delegationLink: true, maxSectionLines },
|
|
309
|
+
message: `${instrRel} delegates via links and every section ≤ 60 lines (max ${maxSectionLines})`,
|
|
310
|
+
}));
|
|
311
|
+
} else if (predicateA) {
|
|
312
|
+
checks.push(pass({
|
|
313
|
+
checkId, points: 1, maxPoints: 2, path: instrRel,
|
|
314
|
+
evidence: { delegationLink: true, maxSectionLines },
|
|
315
|
+
message: `${instrRel} delegates via links but a section exceeds 60 lines (max ${maxSectionLines})`,
|
|
316
|
+
}));
|
|
317
|
+
} else {
|
|
318
|
+
checks.push(fail({
|
|
319
|
+
checkId, maxPoints: 2, path: instrRel,
|
|
320
|
+
evidence: { delegationLink: false, maxSectionLines },
|
|
321
|
+
message: `${instrRel} has no delegation links to README/AGENTS/CLAUDE/steering/_shared docs`,
|
|
322
|
+
}));
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return checks;
|
|
328
|
+
}
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* category9.mjs — Category 9: Skill-Health Surfacing (weight: 8)
|
|
3
|
+
*
|
|
4
|
+
* Checks (3, max_points sum = 10):
|
|
5
|
+
* c9.1 skill-telemetry-hygiene (max 4)
|
|
6
|
+
* c9.2 skill-scorer-wired (max 3)
|
|
7
|
+
* c9.3 skill-health-advisory (max 3)
|
|
8
|
+
*
|
|
9
|
+
* Surfaces whether a repo has adopted the #648 per-skill health pipeline:
|
|
10
|
+
* L1 telemetry (skill-invocations.jsonl), the scorer module (score.mjs +
|
|
11
|
+
* join.mjs), and the advisory verdict surface. The VERDICT content is ADVISORY
|
|
12
|
+
* ONLY — it NEVER affects points. The firewall between "surfacing a diagnosis"
|
|
13
|
+
* and "scoring a repo on it" is enforced here by construction.
|
|
14
|
+
*
|
|
15
|
+
* CRITICAL — no-adoption is a HEALTHY state, never a failure. A repo without
|
|
16
|
+
* skill-health telemetry or the scorer module MUST score full points on every
|
|
17
|
+
* check. "Insufficient signal" and "feature not adopted" are healthy states,
|
|
18
|
+
* not defects. The clean-repo integration fixture has neither the module nor
|
|
19
|
+
* any telemetry and must still score 10/10 here (it asserts overall ≥ 8).
|
|
20
|
+
*
|
|
21
|
+
* status field is only 'pass' / 'fail' — partial tiers emit a reduced-points
|
|
22
|
+
* pass() (there is no 'partial' constructor in helpers.mjs).
|
|
23
|
+
*
|
|
24
|
+
* Stdlib only: node:fs, node:path, node:child_process. The advisory tally in
|
|
25
|
+
* c9.3 imports the AUDITOR's OWN (trusted) join/score modules in a short-lived
|
|
26
|
+
* worker subprocess and runs them over the TARGET repo's telemetry DATA files —
|
|
27
|
+
* it never imports code rooted at the audited repo (review N2b: RCE-on-audit).
|
|
28
|
+
* The worker is wrapped so any failure degrades to a structural pass — a broken
|
|
29
|
+
* or empty target repo never throws out of runCategory9.
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
33
|
+
import { join } from 'node:path';
|
|
34
|
+
import { spawnSync } from 'node:child_process';
|
|
35
|
+
|
|
36
|
+
import { pass, fail } from './helpers.mjs';
|
|
37
|
+
|
|
38
|
+
const TELEMETRY_REL = '.orchestrator/metrics/skill-invocations.jsonl';
|
|
39
|
+
const SESSIONS_REL = '.orchestrator/metrics/sessions.jsonl';
|
|
40
|
+
const SCORER_REL = 'scripts/lib/skill-health/score.mjs';
|
|
41
|
+
const JOIN_REL = 'scripts/lib/skill-health/join.mjs';
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Classify the lines of a skill-invocations.jsonl file.
|
|
45
|
+
* A line is "valid" iff it parses as JSON AND carries a string `event` field
|
|
46
|
+
* AND a `schema_version` key (the L1 writer contract).
|
|
47
|
+
*
|
|
48
|
+
* @param {string} text
|
|
49
|
+
* @returns {{ total: number, valid: number, malformed: number }}
|
|
50
|
+
*/
|
|
51
|
+
function classifyTelemetryLines(text) {
|
|
52
|
+
const lines = text.split('\n').filter((l) => l.trim().length > 0);
|
|
53
|
+
let valid = 0;
|
|
54
|
+
let malformed = 0;
|
|
55
|
+
for (const line of lines) {
|
|
56
|
+
let obj;
|
|
57
|
+
try {
|
|
58
|
+
obj = JSON.parse(line);
|
|
59
|
+
} catch {
|
|
60
|
+
malformed += 1;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (obj && typeof obj.event === 'string' && Object.prototype.hasOwnProperty.call(obj, 'schema_version')) {
|
|
64
|
+
valid += 1;
|
|
65
|
+
} else {
|
|
66
|
+
malformed += 1;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { total: lines.length, valid, malformed };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export function runCategory9(root) {
|
|
73
|
+
const checks = [];
|
|
74
|
+
|
|
75
|
+
// c9.1 skill-telemetry-hygiene (max 4)
|
|
76
|
+
//
|
|
77
|
+
// ABSENT telemetry → full points: the skill-health feature is simply not
|
|
78
|
+
// adopted, which is a healthy state. PRESENT telemetry → full points iff
|
|
79
|
+
// every line is valid JSON with a known `event` + `schema_version`; reduced
|
|
80
|
+
// points (with a quoted malformed-line count) when some lines are malformed.
|
|
81
|
+
{
|
|
82
|
+
const checkId = 'skill-telemetry-hygiene';
|
|
83
|
+
const telemetryPath = join(root, TELEMETRY_REL);
|
|
84
|
+
if (!existsSync(telemetryPath)) {
|
|
85
|
+
checks.push(pass({
|
|
86
|
+
checkId, points: 4, maxPoints: 4, path: TELEMETRY_REL,
|
|
87
|
+
evidence: { present: false, total: 0, valid: 0, malformed: 0 },
|
|
88
|
+
message: 'no skill-invocations telemetry yet (feature not adopted)',
|
|
89
|
+
}));
|
|
90
|
+
} else {
|
|
91
|
+
let text;
|
|
92
|
+
try {
|
|
93
|
+
text = readFileSync(telemetryPath, 'utf8');
|
|
94
|
+
} catch {
|
|
95
|
+
text = null;
|
|
96
|
+
}
|
|
97
|
+
if (text === null) {
|
|
98
|
+
// Present-but-unreadable: treat as not-adopted rather than penalize.
|
|
99
|
+
checks.push(pass({
|
|
100
|
+
checkId, points: 4, maxPoints: 4, path: TELEMETRY_REL,
|
|
101
|
+
evidence: { present: true, readable: false, total: 0, valid: 0, malformed: 0 },
|
|
102
|
+
message: 'skill-invocations telemetry present but unreadable (treated as not-adopted)',
|
|
103
|
+
}));
|
|
104
|
+
} else {
|
|
105
|
+
const { total, valid, malformed } = classifyTelemetryLines(text);
|
|
106
|
+
if (malformed === 0) {
|
|
107
|
+
checks.push(pass({
|
|
108
|
+
checkId, points: 4, maxPoints: 4, path: TELEMETRY_REL,
|
|
109
|
+
evidence: { present: true, total, valid, malformed: 0 },
|
|
110
|
+
message: `${valid}/${total} skill-invocation line(s) valid (event + schema_version), 0 malformed`,
|
|
111
|
+
}));
|
|
112
|
+
} else {
|
|
113
|
+
checks.push(pass({
|
|
114
|
+
checkId, points: 2, maxPoints: 4, path: TELEMETRY_REL,
|
|
115
|
+
evidence: { present: true, total, valid, malformed },
|
|
116
|
+
message: `${malformed} of ${total} skill-invocation line(s) malformed (${valid} valid)`,
|
|
117
|
+
}));
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// c9.2 skill-scorer-wired (max 3)
|
|
124
|
+
//
|
|
125
|
+
// ABSENT scorer module → full points (scoring not adopted is healthy).
|
|
126
|
+
// PRESENT → confirm it exports scoreSkillHealth via a string-grep; full
|
|
127
|
+
// points on match, fail only when the module exists but the export is gone
|
|
128
|
+
// (a genuine wiring regression).
|
|
129
|
+
{
|
|
130
|
+
const checkId = 'skill-scorer-wired';
|
|
131
|
+
const scorerPath = join(root, SCORER_REL);
|
|
132
|
+
if (!existsSync(scorerPath)) {
|
|
133
|
+
checks.push(pass({
|
|
134
|
+
checkId, points: 3, maxPoints: 3, path: SCORER_REL,
|
|
135
|
+
evidence: { present: false, exportsScorer: false },
|
|
136
|
+
message: 'skill-health scoring not adopted',
|
|
137
|
+
}));
|
|
138
|
+
} else {
|
|
139
|
+
let src;
|
|
140
|
+
try {
|
|
141
|
+
src = readFileSync(scorerPath, 'utf8');
|
|
142
|
+
} catch {
|
|
143
|
+
src = null;
|
|
144
|
+
}
|
|
145
|
+
const exportRe = /export\s+function\s+scoreSkillHealth\b|export\s*\{[^}]*\bscoreSkillHealth\b/;
|
|
146
|
+
if (src && exportRe.test(src)) {
|
|
147
|
+
checks.push(pass({
|
|
148
|
+
checkId, points: 3, maxPoints: 3, path: SCORER_REL,
|
|
149
|
+
evidence: { present: true, exportsScorer: true },
|
|
150
|
+
message: 'scripts/lib/skill-health/score.mjs exports scoreSkillHealth',
|
|
151
|
+
}));
|
|
152
|
+
} else {
|
|
153
|
+
checks.push(fail({
|
|
154
|
+
checkId, maxPoints: 3, path: SCORER_REL,
|
|
155
|
+
evidence: { present: true, exportsScorer: false },
|
|
156
|
+
message: 'score.mjs present but does not export scoreSkillHealth (wiring regression)',
|
|
157
|
+
}));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// c9.3 skill-health-advisory (max 3) — ALWAYS PASSES.
|
|
163
|
+
//
|
|
164
|
+
// Advisory-only surface: compute the verdict distribution when both the join
|
|
165
|
+
// + score modules exist AND telemetry is present, then put the verdict tally
|
|
166
|
+
// in the check evidence/message as INFORMATIONAL output. Verdict CONTENT
|
|
167
|
+
// never affects points — this check is structurally pass-only. Any import
|
|
168
|
+
// failure, an absent module, or absent telemetry → still pass(3) with an
|
|
169
|
+
// "insufficient signal" note.
|
|
170
|
+
checks.push(buildAdvisoryCheck(root));
|
|
171
|
+
|
|
172
|
+
return checks;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Builds the c9.3 advisory check (ALWAYS a pass). Isolated so the worker-import
|
|
177
|
+
* + scorer invocation can be wrapped in a single try/catch that never throws
|
|
178
|
+
* out of runCategory9 on a broken or older target repo.
|
|
179
|
+
*
|
|
180
|
+
* @param {string} root
|
|
181
|
+
* @returns {object} a pass() check result with points = 3
|
|
182
|
+
*/
|
|
183
|
+
function buildAdvisoryCheck(root) {
|
|
184
|
+
const checkId = 'skill-health-advisory';
|
|
185
|
+
const maxPoints = 3;
|
|
186
|
+
|
|
187
|
+
const scorerPath = join(root, SCORER_REL);
|
|
188
|
+
const joinPath = join(root, JOIN_REL);
|
|
189
|
+
const telemetryPath = join(root, TELEMETRY_REL);
|
|
190
|
+
|
|
191
|
+
// No module(s) or no telemetry → advisory surface has nothing to say, but
|
|
192
|
+
// that is a healthy "insufficient signal" state. Pass full points.
|
|
193
|
+
if (!existsSync(scorerPath) || !existsSync(joinPath) || !existsSync(telemetryPath)) {
|
|
194
|
+
return pass({
|
|
195
|
+
checkId, points: maxPoints, maxPoints, path: SCORER_REL,
|
|
196
|
+
evidence: { computed: false, verdictTally: {} },
|
|
197
|
+
message: 'no advisory verdicts (insufficient signal)',
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
let tally;
|
|
202
|
+
try {
|
|
203
|
+
tally = computeAdvisoryTally(scorerPath, joinPath, telemetryPath, root);
|
|
204
|
+
} catch {
|
|
205
|
+
tally = null;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (tally && tally.computed) {
|
|
209
|
+
return pass({
|
|
210
|
+
checkId, points: maxPoints, maxPoints, path: SCORER_REL,
|
|
211
|
+
evidence: { computed: true, verdictTally: tally.verdictTally, skills: tally.skillCount },
|
|
212
|
+
message: `advisory verdict tally: ${JSON.stringify(tally.verdictTally)} (informational, never scored)`,
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return pass({
|
|
217
|
+
checkId, points: maxPoints, maxPoints, path: SCORER_REL,
|
|
218
|
+
evidence: { computed: false, verdictTally: {} },
|
|
219
|
+
message: 'no advisory verdicts (insufficient signal)',
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Compute the advisory verdict tally by importing the AUDITOR's OWN (trusted)
|
|
225
|
+
* join + score modules in a short-lived worker subprocess, running
|
|
226
|
+
* joinSkillOutcomes → scoreSkillHealth over the TARGET repo's telemetry DATA
|
|
227
|
+
* files, and tallying verdicts.
|
|
228
|
+
*
|
|
229
|
+
* SECURITY (review N2b): harness-audit can target an arbitrary repo, so we must
|
|
230
|
+
* never `import()` CODE rooted at the audited `root` — that would execute the
|
|
231
|
+
* target's modules during an audit (RCE-on-audit, a trust-model expansion). The
|
|
232
|
+
* worker therefore imports the auditor's own `scripts/lib/skill-health/{join,score}.mjs`
|
|
233
|
+
* (derived from THIS module's location, not from `root`) and feeds them only the
|
|
234
|
+
* target's telemetry JSONL FILE paths as DATA arguments. The auditor runs only
|
|
235
|
+
* TRUSTED code over UNTRUSTED data.
|
|
236
|
+
*
|
|
237
|
+
* runCategory* are synchronous by contract and joinSkillOutcomes is async, so a
|
|
238
|
+
* subprocess is used to bridge the async/sync gap without making the whole
|
|
239
|
+
* category async. The tally is purely INFORMATIONAL advisory output — any
|
|
240
|
+
* failure returns { computed: false } and the caller passes full points anyway.
|
|
241
|
+
*
|
|
242
|
+
* @param {string} scorerPath absolute path to score.mjs in the target root (existence-checked only)
|
|
243
|
+
* @param {string} joinPath absolute path to join.mjs in the target root (existence-checked only)
|
|
244
|
+
* @param {string} telemetryPath absolute path to skill-invocations.jsonl (DATA)
|
|
245
|
+
* @param {string} root target repo root
|
|
246
|
+
* @returns {{ computed: boolean, verdictTally?: Record<string,number>, skillCount?: number }}
|
|
247
|
+
*/
|
|
248
|
+
// NOTE: scorerPath/joinPath are intentionally unused inside this body — they are
|
|
249
|
+
// existence-checked by the caller (buildAdvisoryCheck) and retained in the signature
|
|
250
|
+
// for caller symmetry. The CODE imported here comes from the auditor's OWN modules
|
|
251
|
+
// (derived from import.meta.url below), never from the audited target (review N2b).
|
|
252
|
+
function computeAdvisoryTally(scorerPath, joinPath, telemetryPath, root) {
|
|
253
|
+
const sessionsPath = join(root, SESSIONS_REL);
|
|
254
|
+
// Import the AUDITOR's OWN trusted modules — derived from THIS file's location,
|
|
255
|
+
// NEVER from the audited `root`. The target's join.mjs/score.mjs are existence-
|
|
256
|
+
// checked by the caller but never imported (would execute target code → N2b).
|
|
257
|
+
const joinUrl = new URL('../../skill-health/join.mjs', import.meta.url).href;
|
|
258
|
+
const scoreUrl = new URL('../../skill-health/score.mjs', import.meta.url).href;
|
|
259
|
+
|
|
260
|
+
const worker = `
|
|
261
|
+
(async () => {
|
|
262
|
+
try {
|
|
263
|
+
const { joinSkillOutcomes } = await import(${JSON.stringify(joinUrl)});
|
|
264
|
+
const { scoreSkillHealth } = await import(${JSON.stringify(scoreUrl)});
|
|
265
|
+
const joined = await joinSkillOutcomes({
|
|
266
|
+
invocationsPath: ${JSON.stringify(telemetryPath)},
|
|
267
|
+
sessionsPath: ${JSON.stringify(sessionsPath)},
|
|
268
|
+
});
|
|
269
|
+
const rows = scoreSkillHealth({ bySkill: joined.bySkill });
|
|
270
|
+
const tally = {};
|
|
271
|
+
for (const r of rows) { tally[r.verdict] = (tally[r.verdict] || 0) + 1; }
|
|
272
|
+
process.stdout.write(JSON.stringify({ ok: true, verdictTally: tally, skillCount: rows.length }));
|
|
273
|
+
} catch {
|
|
274
|
+
process.stdout.write(JSON.stringify({ ok: false }));
|
|
275
|
+
}
|
|
276
|
+
})();
|
|
277
|
+
`;
|
|
278
|
+
|
|
279
|
+
const result = spawnSync(process.execPath, ['--input-type=module', '-e', worker], {
|
|
280
|
+
encoding: 'utf8',
|
|
281
|
+
timeout: 5000,
|
|
282
|
+
stdio: ['ignore', 'pipe', 'ignore'],
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
if (result.status !== 0 || !result.stdout) return { computed: false };
|
|
286
|
+
let parsed;
|
|
287
|
+
try {
|
|
288
|
+
parsed = JSON.parse(result.stdout);
|
|
289
|
+
} catch {
|
|
290
|
+
return { computed: false };
|
|
291
|
+
}
|
|
292
|
+
if (!parsed || parsed.ok !== true) return { computed: false };
|
|
293
|
+
return { computed: true, verdictTally: parsed.verdictTally || {}, skillCount: parsed.skillCount || 0 };
|
|
294
|
+
}
|