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,453 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* session-schema/validator.mjs — ValidationError class + validateSession.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from scripts/lib/session-schema.mjs (W1A3 split).
|
|
5
|
+
* Imports: constants.mjs (leaf). No imports from siblings (normalizer,
|
|
6
|
+
* timestamps, aliases) or parent barrel.
|
|
7
|
+
*
|
|
8
|
+
* Exports: ValidationError, validateSession
|
|
9
|
+
* Module-private: isPlainObject, _validateSchemaVersion,
|
|
10
|
+
* _validateRequiredFields, _validateSessionId, _validateSessionType,
|
|
11
|
+
* _validateTimestamps, _validateWaves, _validateAgentSummary,
|
|
12
|
+
* _validateOptionalFields
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import {
|
|
16
|
+
CURRENT_SESSION_SCHEMA_VERSION,
|
|
17
|
+
VALID_SESSION_TYPES,
|
|
18
|
+
REQUIRED_FIELDS,
|
|
19
|
+
AGENT_SUMMARY_FIELDS,
|
|
20
|
+
} from './constants.mjs';
|
|
21
|
+
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Module-private enums + regex
|
|
24
|
+
// ---------------------------------------------------------------------------
|
|
25
|
+
|
|
26
|
+
/** Valid values for the optional `expected_cost_tier` field (ADR-364). */
|
|
27
|
+
const EXPECTED_COST_TIERS = Object.freeze(['quick', 'standard', 'deep']);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Valid values for the optional `status` field (Epic #724 C1).
|
|
31
|
+
* `completed` — record written by a normal /close flow.
|
|
32
|
+
* `abandoned` — stub backfilled by the SessionEnd hook because the session
|
|
33
|
+
* terminated without running /close.
|
|
34
|
+
*/
|
|
35
|
+
const SESSION_STATUS = Object.freeze(['completed', 'abandoned']);
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Canonical ISO-8601 UTC timestamp regex — accepts `YYYY-MM-DDTHH:MM:SSZ`
|
|
39
|
+
* and `YYYY-MM-DDTHH:MM:SS.SSSZ` (exactly 3 fractional digits).
|
|
40
|
+
*
|
|
41
|
+
* Issue #540 defense-in-depth layer (b): block malformed timestamp strings
|
|
42
|
+
* that `Date.parse` accepts as NaN OR (worse) accepts as valid but
|
|
43
|
+
* non-canonical (e.g. `.3NZ`, `.3Z`, `.300000Z`). The strict regex closes
|
|
44
|
+
* the gap left by `Date.parse` alone, which returned NaN for `.3NZ` in some
|
|
45
|
+
* inputs and silently coerced others.
|
|
46
|
+
*/
|
|
47
|
+
const ISO_8601_UTC_MS_RE = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d{3})?Z$/;
|
|
48
|
+
|
|
49
|
+
// ---------------------------------------------------------------------------
|
|
50
|
+
// Error class
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
|
|
53
|
+
export class ValidationError extends Error {
|
|
54
|
+
constructor(message) {
|
|
55
|
+
super(message);
|
|
56
|
+
this.name = 'ValidationError';
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
// Internal helper
|
|
62
|
+
// ---------------------------------------------------------------------------
|
|
63
|
+
|
|
64
|
+
function isPlainObject(v) {
|
|
65
|
+
return v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
// Sub-validators (module-private)
|
|
70
|
+
// ---------------------------------------------------------------------------
|
|
71
|
+
//
|
|
72
|
+
// Each helper throws ValidationError on the first violation it detects,
|
|
73
|
+
// preserving the original error message + ordering of the monolithic
|
|
74
|
+
// validateSession() body. Helpers are intentionally not exported; the
|
|
75
|
+
// public contract is validateSession() alone.
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Validate session entry's schema_version field.
|
|
79
|
+
*
|
|
80
|
+
* Accepted versions: 0 (legacy / pre-versioning), 1 (pre-#372 writes),
|
|
81
|
+
* 2 (current writes per CURRENT_SESSION_SCHEMA_VERSION, bumped via #372),
|
|
82
|
+
* 3 (ADR-364 follow-ups, commit eb820ca).
|
|
83
|
+
*
|
|
84
|
+
* Additive contract: as the schema evolves, older historical entries must remain readable.
|
|
85
|
+
* #576 expanded this from `0 | 1` to `0 | 1 | 2 | 3` after discovering schema_version=3 entries
|
|
86
|
+
* in production data (.orchestrator/metrics/sessions.jsonl line 11, session main-2026-05-24-0510-housekeeping).
|
|
87
|
+
* #372 bumped CURRENT_SESSION_SCHEMA_VERSION from 1 to 2 — the accepted set here was already
|
|
88
|
+
* wide enough (#576), so no change to ACCEPTED_VERSIONS was required for the bump itself.
|
|
89
|
+
*
|
|
90
|
+
* Cross-references:
|
|
91
|
+
* - Issue: #576, #372
|
|
92
|
+
* - Constant: CURRENT_SESSION_SCHEMA_VERSION in ./constants.mjs (current=2; bump when adding fields)
|
|
93
|
+
* - PRD: docs/prd/<future-schema-evolution> when v4 is introduced
|
|
94
|
+
*
|
|
95
|
+
* @throws {ValidationError} when schema_version is set but not in [0, 1, 2, 3]
|
|
96
|
+
*/
|
|
97
|
+
function _validateSchemaVersion(entry) {
|
|
98
|
+
if ('schema_version' in entry && entry.schema_version !== undefined) {
|
|
99
|
+
const ACCEPTED_VERSIONS = [0, 1, 2, 3];
|
|
100
|
+
if (!ACCEPTED_VERSIONS.includes(entry.schema_version)) {
|
|
101
|
+
throw new ValidationError(
|
|
102
|
+
`schema_version must be one of [0 (legacy), 1, 2, 3], got: ${entry.schema_version}`
|
|
103
|
+
);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function _validateRequiredFields(entry) {
|
|
109
|
+
for (const field of REQUIRED_FIELDS) {
|
|
110
|
+
if (!(field in entry)) {
|
|
111
|
+
throw new ValidationError(`session missing required field: ${field}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function _validateSessionId(entry) {
|
|
117
|
+
if (typeof entry.session_id !== 'string' || entry.session_id.length === 0) {
|
|
118
|
+
throw new ValidationError('session_id must be a non-empty string');
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function _validateSessionType(entry) {
|
|
123
|
+
if (!VALID_SESSION_TYPES.includes(entry.session_type)) {
|
|
124
|
+
throw new ValidationError(
|
|
125
|
+
`session_type must be one of ${VALID_SESSION_TYPES.join('|')}, got: ${entry.session_type}`
|
|
126
|
+
);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function _validateTimestamps(entry) {
|
|
131
|
+
if (typeof entry.started_at !== 'string') {
|
|
132
|
+
throw new ValidationError('started_at must be an ISO timestamp string');
|
|
133
|
+
}
|
|
134
|
+
const startedMs = Date.parse(entry.started_at);
|
|
135
|
+
if (Number.isNaN(startedMs)) {
|
|
136
|
+
throw new ValidationError(`started_at is not a parsable timestamp: ${entry.started_at}`);
|
|
137
|
+
}
|
|
138
|
+
// Issue #540 defense-in-depth layer (b): strict canonical ISO-8601 UTC
|
|
139
|
+
// format guard. `Date.parse` alone accepted malformed inputs like `.3NZ`
|
|
140
|
+
// as NaN, but the failure mode is the same as a non-canonical accepted
|
|
141
|
+
// shape. Require explicit `YYYY-MM-DDTHH:MM:SS[.SSS]Z`.
|
|
142
|
+
if (!ISO_8601_UTC_MS_RE.test(entry.started_at)) {
|
|
143
|
+
throw new ValidationError(
|
|
144
|
+
`started_at must match ISO-8601 UTC ms format (YYYY-MM-DDTHH:MM:SS[.SSS]Z), got: ${entry.started_at}`
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
if (typeof entry.completed_at !== 'string') {
|
|
148
|
+
throw new ValidationError('completed_at must be an ISO timestamp string');
|
|
149
|
+
}
|
|
150
|
+
const completedMs = Date.parse(entry.completed_at);
|
|
151
|
+
if (Number.isNaN(completedMs)) {
|
|
152
|
+
throw new ValidationError(`completed_at is not a parsable timestamp: ${entry.completed_at}`);
|
|
153
|
+
}
|
|
154
|
+
if (!ISO_8601_UTC_MS_RE.test(entry.completed_at)) {
|
|
155
|
+
throw new ValidationError(
|
|
156
|
+
`completed_at must match ISO-8601 UTC ms format (YYYY-MM-DDTHH:MM:SS[.SSS]Z), got: ${entry.completed_at}`
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
if (completedMs < startedMs) {
|
|
160
|
+
throw new ValidationError(
|
|
161
|
+
`completed_at (${entry.completed_at}) must be >= started_at (${entry.started_at})`
|
|
162
|
+
);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function _validateWaves(entry) {
|
|
167
|
+
// total_waves — non-negative number.
|
|
168
|
+
if (typeof entry.total_waves !== 'number' || entry.total_waves < 0) {
|
|
169
|
+
throw new ValidationError(
|
|
170
|
+
`total_waves must be a non-negative number, got: ${entry.total_waves}`
|
|
171
|
+
);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// waves — array, entries shape-checked.
|
|
175
|
+
if (!Array.isArray(entry.waves)) {
|
|
176
|
+
throw new ValidationError(`waves must be an array, got: ${typeof entry.waves}`);
|
|
177
|
+
}
|
|
178
|
+
for (let i = 0; i < entry.waves.length; i++) {
|
|
179
|
+
const w = entry.waves[i];
|
|
180
|
+
if (!isPlainObject(w)) {
|
|
181
|
+
throw new ValidationError(`waves[${i}] must be an object`);
|
|
182
|
+
}
|
|
183
|
+
if (typeof w.wave !== 'number' || w.wave < 1) {
|
|
184
|
+
throw new ValidationError(`waves[${i}].wave must be a number >= 1, got: ${w.wave}`);
|
|
185
|
+
}
|
|
186
|
+
if (typeof w.role !== 'string' || w.role.length === 0) {
|
|
187
|
+
throw new ValidationError(`waves[${i}].role must be a non-empty string`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function _validateAgentSummary(entry) {
|
|
193
|
+
if (!isPlainObject(entry.agent_summary)) {
|
|
194
|
+
throw new ValidationError('agent_summary must be an object');
|
|
195
|
+
}
|
|
196
|
+
for (const f of AGENT_SUMMARY_FIELDS) {
|
|
197
|
+
if (!(f in entry.agent_summary)) {
|
|
198
|
+
throw new ValidationError(`agent_summary missing required field: ${f}`);
|
|
199
|
+
}
|
|
200
|
+
const v = entry.agent_summary[f];
|
|
201
|
+
if (typeof v !== 'number' || v < 0) {
|
|
202
|
+
throw new ValidationError(`agent_summary.${f} must be a non-negative number, got: ${v}`);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// total_agents / total_files_changed — non-negative numbers.
|
|
207
|
+
if (typeof entry.total_agents !== 'number' || entry.total_agents < 0) {
|
|
208
|
+
throw new ValidationError(
|
|
209
|
+
`total_agents must be a non-negative number, got: ${entry.total_agents}`
|
|
210
|
+
);
|
|
211
|
+
}
|
|
212
|
+
if (typeof entry.total_files_changed !== 'number' || entry.total_files_changed < 0) {
|
|
213
|
+
throw new ValidationError(
|
|
214
|
+
`total_files_changed must be a non-negative number, got: ${entry.total_files_changed}`
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function _validateOptionalFields(entry) {
|
|
220
|
+
if (entry.effectiveness !== undefined && entry.effectiveness !== null) {
|
|
221
|
+
if (!isPlainObject(entry.effectiveness)) {
|
|
222
|
+
throw new ValidationError('effectiveness must be an object or null');
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (entry.discovery_stats !== undefined && entry.discovery_stats !== null) {
|
|
226
|
+
if (!isPlainObject(entry.discovery_stats)) {
|
|
227
|
+
throw new ValidationError('discovery_stats must be an object or null');
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
if (entry.review_stats !== undefined && entry.review_stats !== null) {
|
|
231
|
+
if (!isPlainObject(entry.review_stats)) {
|
|
232
|
+
throw new ValidationError('review_stats must be an object or null');
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (entry.platform !== undefined && entry.platform !== null) {
|
|
236
|
+
if (typeof entry.platform !== 'string') {
|
|
237
|
+
throw new ValidationError('platform must be a string or null');
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
if (entry.branch !== undefined && entry.branch !== null) {
|
|
241
|
+
if (typeof entry.branch !== 'string') {
|
|
242
|
+
throw new ValidationError('branch must be a string or null');
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (entry.base_branch !== undefined && entry.base_branch !== null) {
|
|
246
|
+
if (typeof entry.base_branch !== 'string') {
|
|
247
|
+
throw new ValidationError('base_branch must be a string or null');
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
if (entry.notes !== undefined && entry.notes !== null) {
|
|
251
|
+
if (typeof entry.notes !== 'string') {
|
|
252
|
+
throw new ValidationError('notes must be a string or null');
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
if (entry.duration_seconds !== undefined && entry.duration_seconds !== null) {
|
|
256
|
+
if (typeof entry.duration_seconds !== 'number' || entry.duration_seconds < 0) {
|
|
257
|
+
throw new ValidationError(
|
|
258
|
+
`duration_seconds must be a non-negative number or null, got: ${entry.duration_seconds}`
|
|
259
|
+
);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
if (entry.issues_closed !== undefined) {
|
|
263
|
+
if (
|
|
264
|
+
!Array.isArray(entry.issues_closed) ||
|
|
265
|
+
entry.issues_closed.some((n) => typeof n !== 'number')
|
|
266
|
+
) {
|
|
267
|
+
throw new ValidationError('issues_closed must be an array of numbers');
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (entry.issues_created !== undefined) {
|
|
271
|
+
if (
|
|
272
|
+
!Array.isArray(entry.issues_created) ||
|
|
273
|
+
entry.issues_created.some((n) => typeof n !== 'number')
|
|
274
|
+
) {
|
|
275
|
+
throw new ValidationError('issues_created must be an array of numbers');
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// ADR-364 optional additive fields — remote-agent substrate thin-slice.
|
|
280
|
+
// All are nullable; null is treated as "not provided" and passes without
|
|
281
|
+
// further checks. Only non-null present values are type/range validated.
|
|
282
|
+
|
|
283
|
+
if (entry.agent_identity !== undefined && entry.agent_identity !== null) {
|
|
284
|
+
if (typeof entry.agent_identity !== 'string' || entry.agent_identity.length === 0) {
|
|
285
|
+
throw new ValidationError('agent_identity must be a non-empty string or null');
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
if (entry.worktree_path !== undefined && entry.worktree_path !== null) {
|
|
289
|
+
if (typeof entry.worktree_path !== 'string' || entry.worktree_path.length === 0) {
|
|
290
|
+
throw new ValidationError('worktree_path must be a non-empty string or null');
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
if (entry.parent_run_id !== undefined && entry.parent_run_id !== null) {
|
|
294
|
+
if (typeof entry.parent_run_id !== 'string' || entry.parent_run_id.length === 0) {
|
|
295
|
+
throw new ValidationError('parent_run_id must be a non-empty string or null');
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
if (entry.lease_acquired_at !== undefined && entry.lease_acquired_at !== null) {
|
|
299
|
+
if (typeof entry.lease_acquired_at !== 'string') {
|
|
300
|
+
throw new ValidationError('lease_acquired_at must be an ISO 8601 timestamp string or null');
|
|
301
|
+
}
|
|
302
|
+
if (Number.isNaN(Date.parse(entry.lease_acquired_at))) {
|
|
303
|
+
throw new ValidationError(
|
|
304
|
+
`lease_acquired_at is not a parsable timestamp: ${entry.lease_acquired_at}`
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
// Issue #540 defense-in-depth layer (b): strict canonical guard for the
|
|
308
|
+
// ADR-364 optional lease timestamp. Matches the `started_at` / `completed_at`
|
|
309
|
+
// policy enforced above.
|
|
310
|
+
if (!ISO_8601_UTC_MS_RE.test(entry.lease_acquired_at)) {
|
|
311
|
+
throw new ValidationError(
|
|
312
|
+
`lease_acquired_at must match ISO-8601 UTC ms format (YYYY-MM-DDTHH:MM:SS[.SSS]Z), got: ${entry.lease_acquired_at}`
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
if (entry.lease_ttl_seconds !== undefined && entry.lease_ttl_seconds !== null) {
|
|
317
|
+
if (!Number.isFinite(entry.lease_ttl_seconds) || entry.lease_ttl_seconds < 0) {
|
|
318
|
+
throw new ValidationError(
|
|
319
|
+
`lease_ttl_seconds must be a non-negative finite number or null, got: ${entry.lease_ttl_seconds}`
|
|
320
|
+
);
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
if (entry.expected_cost_tier !== undefined && entry.expected_cost_tier !== null) {
|
|
324
|
+
if (!EXPECTED_COST_TIERS.includes(entry.expected_cost_tier)) {
|
|
325
|
+
throw new ValidationError(
|
|
326
|
+
`expected_cost_tier must be one of ${EXPECTED_COST_TIERS.join('|')} or null, got: ${entry.expected_cost_tier}`
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Epic #644 — session-level token rollup fields (additive, v1-compatible).
|
|
332
|
+
// total_token_input / total_token_output: non-negative finite number or null.
|
|
333
|
+
// Number.isFinite rejects NaN/Infinity (mirrors lease_ttl_seconds above) — a typeof-only
|
|
334
|
+
// guard let NaN through because NaN < 0 is false (W4 session-review WARN, #644).
|
|
335
|
+
if (entry.total_token_input !== undefined && entry.total_token_input !== null) {
|
|
336
|
+
if (!Number.isFinite(entry.total_token_input) || entry.total_token_input < 0) {
|
|
337
|
+
throw new ValidationError(
|
|
338
|
+
`total_token_input must be a non-negative finite number or null, got: ${entry.total_token_input}`
|
|
339
|
+
);
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
if (entry.total_token_output !== undefined && entry.total_token_output !== null) {
|
|
343
|
+
if (!Number.isFinite(entry.total_token_output) || entry.total_token_output < 0) {
|
|
344
|
+
throw new ValidationError(
|
|
345
|
+
`total_token_output must be a non-negative finite number or null, got: ${entry.total_token_output}`
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
// subagents_with_tokens: non-negative integer (never null — it is always a count, defaulting to 0).
|
|
350
|
+
if (entry.subagents_with_tokens !== undefined && entry.subagents_with_tokens !== null) {
|
|
351
|
+
if (
|
|
352
|
+
typeof entry.subagents_with_tokens !== 'number' ||
|
|
353
|
+
!Number.isInteger(entry.subagents_with_tokens) ||
|
|
354
|
+
entry.subagents_with_tokens < 0
|
|
355
|
+
) {
|
|
356
|
+
throw new ValidationError(
|
|
357
|
+
`subagents_with_tokens must be a non-negative integer, got: ${entry.subagents_with_tokens}`
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
// Epic #724 C1 — SessionEnd close-through backfill provenance fields.
|
|
363
|
+
// All additive-optional; null/absent passes without further checks so that
|
|
364
|
+
// pre-#724 records (and every normally-closed record) validate cleanly.
|
|
365
|
+
if (entry.status !== undefined && entry.status !== null) {
|
|
366
|
+
if (!SESSION_STATUS.includes(entry.status)) {
|
|
367
|
+
throw new ValidationError(
|
|
368
|
+
`status must be one of ${SESSION_STATUS.join('|')} or null, got: ${entry.status}`
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (entry._backfill_source !== undefined && entry._backfill_source !== null) {
|
|
373
|
+
if (typeof entry._backfill_source !== 'string' || entry._backfill_source.length === 0) {
|
|
374
|
+
throw new ValidationError('_backfill_source must be a non-empty string or null');
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
if (entry._backfill_incomplete_fields !== undefined && entry._backfill_incomplete_fields !== null) {
|
|
378
|
+
if (
|
|
379
|
+
!Array.isArray(entry._backfill_incomplete_fields) ||
|
|
380
|
+
entry._backfill_incomplete_fields.some((f) => typeof f !== 'string')
|
|
381
|
+
) {
|
|
382
|
+
throw new ValidationError('_backfill_incomplete_fields must be an array of strings or null');
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
if (entry._session_type_inferred !== undefined && entry._session_type_inferred !== null) {
|
|
386
|
+
if (typeof entry._session_type_inferred !== 'boolean') {
|
|
387
|
+
throw new ValidationError('_session_type_inferred must be a boolean or null');
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
if (entry._synthetic_session_id !== undefined && entry._synthetic_session_id !== null) {
|
|
391
|
+
if (typeof entry._synthetic_session_id !== 'boolean') {
|
|
392
|
+
throw new ValidationError('_synthetic_session_id must be a boolean or null');
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Issue #773 — Handover-Alignment-Gate open-question telemetry (additive,
|
|
397
|
+
// v1-compatible). Each field is a non-negative integer count surfaced by the
|
|
398
|
+
// session-end Phase 1.65 gate: how many open questions were surfaced in the
|
|
399
|
+
// triage AUQ (`_asked`), how many the operator answered (`_answered`), and how
|
|
400
|
+
// many stayed unanswered / over-cap and roundtripped to the next session
|
|
401
|
+
// (`_deferred`). Absent/null = the gate did not run or the session was not
|
|
402
|
+
// measured (fail-open skip, headless, pre-#773 record) — never coerced to 0,
|
|
403
|
+
// so "not measured" stays distinguishable from "zero questions". Same
|
|
404
|
+
// non-negative-integer contract as subagents_with_tokens above.
|
|
405
|
+
for (const field of ['open_questions_asked', 'open_questions_answered', 'open_questions_deferred']) {
|
|
406
|
+
if (entry[field] !== undefined && entry[field] !== null) {
|
|
407
|
+
if (
|
|
408
|
+
typeof entry[field] !== 'number' ||
|
|
409
|
+
!Number.isInteger(entry[field]) ||
|
|
410
|
+
entry[field] < 0
|
|
411
|
+
) {
|
|
412
|
+
throw new ValidationError(
|
|
413
|
+
`${field} must be a non-negative integer, got: ${entry[field]}`
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
// ---------------------------------------------------------------------------
|
|
421
|
+
// Public API
|
|
422
|
+
// ---------------------------------------------------------------------------
|
|
423
|
+
|
|
424
|
+
/**
|
|
425
|
+
* Validate a session entry for writing. Throws ValidationError on any
|
|
426
|
+
* required-field/type/range violation. Returns a NEW object (input is not
|
|
427
|
+
* mutated) with `schema_version` stamped to CURRENT_SESSION_SCHEMA_VERSION
|
|
428
|
+
* if absent. Does NOT apply SESSION_KEY_ALIASES — aliasing is the read-path
|
|
429
|
+
* concern of normalizeSession.
|
|
430
|
+
*
|
|
431
|
+
* @param {object} entry
|
|
432
|
+
* @returns {object} validated entry (new object)
|
|
433
|
+
*/
|
|
434
|
+
export function validateSession(entry) {
|
|
435
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) {
|
|
436
|
+
throw new ValidationError('session must be an object');
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
_validateSchemaVersion(entry);
|
|
440
|
+
_validateRequiredFields(entry);
|
|
441
|
+
_validateSessionId(entry);
|
|
442
|
+
_validateSessionType(entry);
|
|
443
|
+
_validateTimestamps(entry);
|
|
444
|
+
_validateWaves(entry);
|
|
445
|
+
_validateAgentSummary(entry);
|
|
446
|
+
_validateOptionalFields(entry);
|
|
447
|
+
|
|
448
|
+
// Return a NEW object — stamp schema_version if missing.
|
|
449
|
+
return {
|
|
450
|
+
...entry,
|
|
451
|
+
schema_version: entry.schema_version ?? CURRENT_SESSION_SCHEMA_VERSION,
|
|
452
|
+
};
|
|
453
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* session-schema.mjs — canonical schema lock for sessions.jsonl entries.
|
|
3
|
+
*
|
|
4
|
+
* Issues #249, #304. Companion to `scripts/lib/learnings.mjs`. Plain-JS
|
|
5
|
+
* validator + normalizer for session records written to
|
|
6
|
+
* `.orchestrator/metrics/sessions.jsonl`. No Zod — plain-JS matches the
|
|
7
|
+
* repo convention for script-layer utilities.
|
|
8
|
+
*
|
|
9
|
+
* ── CANONICAL SCHEMA (schema_version: 2) ─────────────────────────────────
|
|
10
|
+
*
|
|
11
|
+
* Two historical shapes coexist in sessions.jsonl on disk (#304: 73% of
|
|
12
|
+
* records were mirror-invalid before the migration helper shipped):
|
|
13
|
+
*
|
|
14
|
+
* OLD SHAPE (pre-v3, schema_version: 0 or absent):
|
|
15
|
+
* agents_dispatched number — total agents scheduled (scalar)
|
|
16
|
+
* agents_complete number — succeeded
|
|
17
|
+
* agents_partial number — partial success
|
|
18
|
+
* agents_failed number — failed
|
|
19
|
+
* waves_completed number — scalar wave count (no array)
|
|
20
|
+
*
|
|
21
|
+
* NEW SHAPE (v3+, schema_version: 1 or 2) — THIS IS THE CANONICAL SHAPE.
|
|
22
|
+
* The 1 -> 2 bump (#372) is additive-only — the shape below is unchanged:
|
|
23
|
+
* total_agents number — sum of all dispatched agents
|
|
24
|
+
* total_files_changed number — total files touched across all waves
|
|
25
|
+
* agent_summary object — { complete, partial, failed, spiral }
|
|
26
|
+
* waves array — [{ wave, role, ...waveMetrics }]
|
|
27
|
+
*
|
|
28
|
+
* Required fields for a valid schema_version=2 record (unchanged since v1):
|
|
29
|
+
* session_id, session_type, started_at, completed_at,
|
|
30
|
+
* total_waves, waves, agent_summary, total_agents, total_files_changed
|
|
31
|
+
*
|
|
32
|
+
* Migration: `scripts/migrate-sessions-jsonl.mjs` maps old → new shape.
|
|
33
|
+
* Backfill (apply to entire file): `scripts/backfill-sessions.mjs`.
|
|
34
|
+
*
|
|
35
|
+
* ── CONTRACT SUMMARY ─────────────────────────────────────────────────────
|
|
36
|
+
*
|
|
37
|
+
* validateSession(entry):
|
|
38
|
+
* Throws ValidationError on required-field/type/range violations.
|
|
39
|
+
* Returns a NEW object with `schema_version` stamped to
|
|
40
|
+
* CURRENT_SESSION_SCHEMA_VERSION (2) if absent. Unknown fields pass
|
|
41
|
+
* through (additive contract). Does NOT apply SESSION_KEY_ALIASES.
|
|
42
|
+
* Used by scripts/emit-session.mjs as the write-path gate — any entry
|
|
43
|
+
* that fails validation is rejected before it reaches disk.
|
|
44
|
+
*
|
|
45
|
+
* normalizeSession(entry):
|
|
46
|
+
* Never throws. Applies SAFE key aliases (same-shape renames only).
|
|
47
|
+
* Missing `schema_version` on read is tagged as 0 (pre-versioning
|
|
48
|
+
* legacy, distinct from CURRENT_SESSION_SCHEMA_VERSION=2 stamped on
|
|
49
|
+
* new writes). Emits a deduplicated WARN per session_id per process.
|
|
50
|
+
*
|
|
51
|
+
* Unsafe value-transforming aliases (e.g., `waves_executed` scalar →
|
|
52
|
+
* `waves` array, `duration_min` → `duration_seconds`) are explicitly OUT
|
|
53
|
+
* of scope here and belong in the migration / backfill scripts.
|
|
54
|
+
*
|
|
55
|
+
* ── MODULE STRUCTURE ─────────────────────────────────────────────────────
|
|
56
|
+
*
|
|
57
|
+
* This file is a thin barrel. All logic lives in submodules:
|
|
58
|
+
* session-schema/constants.mjs — CURRENT_SESSION_SCHEMA_VERSION,
|
|
59
|
+
* SESSION_KEY_ALIASES, VALID_SESSION_TYPES,
|
|
60
|
+
* REQUIRED_FIELDS, AGENT_SUMMARY_FIELDS
|
|
61
|
+
* session-schema/validator.mjs — ValidationError, validateSession
|
|
62
|
+
* session-schema/normalizer.mjs — normalizeSession
|
|
63
|
+
* session-schema/timestamps.mjs — clampTimestampsMonotonic
|
|
64
|
+
* session-schema/aliases.mjs — aliasLegacyEndedAt
|
|
65
|
+
*/
|
|
66
|
+
|
|
67
|
+
export { CURRENT_SESSION_SCHEMA_VERSION, SESSION_KEY_ALIASES } from './session-schema/constants.mjs';
|
|
68
|
+
export { ValidationError, validateSession } from './session-schema/validator.mjs';
|
|
69
|
+
export { normalizeSession } from './session-schema/normalizer.mjs';
|
|
70
|
+
export { clampTimestampsMonotonic } from './session-schema/timestamps.mjs';
|
|
71
|
+
export { aliasLegacyEndedAt } from './session-schema/aliases.mjs';
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* session-token-rollup.mjs — session-level token aggregation from subagents.jsonl.
|
|
3
|
+
*
|
|
4
|
+
* Reads `.orchestrator/metrics/subagents.jsonl` (or a caller-supplied path),
|
|
5
|
+
* filters to a given `parent_session_id`, and sums `token_input` /
|
|
6
|
+
* `token_output` across all matched records, skipping null/undefined values.
|
|
7
|
+
*
|
|
8
|
+
* Design notes:
|
|
9
|
+
* - Pure function — no top-level side effects, no writes.
|
|
10
|
+
* - File-absent or all-null-token sessions return a sentinel shape with null
|
|
11
|
+
* totals (not 0) so callers can distinguish "session had no token data" from
|
|
12
|
+
* "session was genuinely free / cost $0".
|
|
13
|
+
* - Malformed JSONL lines are silently skipped (resilience over strictness).
|
|
14
|
+
* - `subagents_with_tokens` counts distinct agent_ids that have at least one
|
|
15
|
+
* record with a non-null token_input or token_output value (coverage metric).
|
|
16
|
+
*
|
|
17
|
+
* @module session-token-rollup
|
|
18
|
+
*/
|
|
19
|
+
|
|
20
|
+
import { readFileSync } from 'node:fs';
|
|
21
|
+
import { resolve } from 'node:path';
|
|
22
|
+
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Default subagents.jsonl path (relative to cwd, mirroring the rest of the
|
|
25
|
+
// metrics layer which uses process.cwd() + relative paths).
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
const DEFAULT_SUBAGENTS_PATH = '.orchestrator/metrics/subagents.jsonl';
|
|
28
|
+
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Public API
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* @typedef {Object} TokenRollupResult
|
|
35
|
+
* @property {number|null} total_token_input - Sum of token_input across matched records; null when no record had a non-null value.
|
|
36
|
+
* @property {number|null} total_token_output - Sum of token_output across matched records; null when no record had a non-null value.
|
|
37
|
+
* @property {number} subagents_with_tokens - Count of distinct agent_ids that had at least one non-null token value.
|
|
38
|
+
* @property {number} matched_records - Total count of JSONL records matched by parentSessionId (includes null-token records).
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Aggregate token usage from subagents.jsonl for a single session.
|
|
43
|
+
*
|
|
44
|
+
* @param {object} opts
|
|
45
|
+
* @param {string} opts.parentSessionId - The UUID to filter on (`parent_session_id` field in JSONL).
|
|
46
|
+
* @param {string} [opts.subagentsPath] - Absolute or cwd-relative path to subagents.jsonl.
|
|
47
|
+
* Defaults to `.orchestrator/metrics/subagents.jsonl`.
|
|
48
|
+
* @returns {TokenRollupResult}
|
|
49
|
+
*/
|
|
50
|
+
export function rollupSessionTokens({
|
|
51
|
+
parentSessionId,
|
|
52
|
+
subagentsPath = DEFAULT_SUBAGENTS_PATH,
|
|
53
|
+
}) {
|
|
54
|
+
/** @type {TokenRollupResult} */
|
|
55
|
+
const ZERO = {
|
|
56
|
+
total_token_input: null,
|
|
57
|
+
total_token_output: null,
|
|
58
|
+
subagents_with_tokens: 0,
|
|
59
|
+
matched_records: 0,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
if (typeof parentSessionId !== 'string' || parentSessionId.length === 0) {
|
|
63
|
+
return { ...ZERO };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Resolve path — support both absolute and cwd-relative.
|
|
67
|
+
const resolvedPath = resolve(process.cwd(), subagentsPath);
|
|
68
|
+
|
|
69
|
+
// Read the file; absent file is a valid state (sparse early sessions).
|
|
70
|
+
let raw;
|
|
71
|
+
try {
|
|
72
|
+
raw = readFileSync(resolvedPath, 'utf8');
|
|
73
|
+
} catch (err) {
|
|
74
|
+
if (err.code === 'ENOENT') {
|
|
75
|
+
return { ...ZERO };
|
|
76
|
+
}
|
|
77
|
+
throw err;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Parse JSONL — skip malformed lines, filter to parentSessionId.
|
|
81
|
+
const lines = raw.split('\n');
|
|
82
|
+
const matched = [];
|
|
83
|
+
for (const line of lines) {
|
|
84
|
+
const trimmed = line.trim();
|
|
85
|
+
if (!trimmed) continue;
|
|
86
|
+
let record;
|
|
87
|
+
try {
|
|
88
|
+
record = JSON.parse(trimmed);
|
|
89
|
+
} catch {
|
|
90
|
+
// Malformed line — skip silently.
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (record && record.parent_session_id === parentSessionId) {
|
|
94
|
+
matched.push(record);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (matched.length === 0) {
|
|
99
|
+
return { ...ZERO };
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Aggregate — skip null/undefined token values.
|
|
103
|
+
let sumInput = null;
|
|
104
|
+
let sumOutput = null;
|
|
105
|
+
|
|
106
|
+
// Track distinct agent_ids that contributed at least one non-null token.
|
|
107
|
+
const agentsWithTokens = new Set();
|
|
108
|
+
|
|
109
|
+
for (const record of matched) {
|
|
110
|
+
const inp = record.token_input;
|
|
111
|
+
const out = record.token_output;
|
|
112
|
+
|
|
113
|
+
if (typeof inp === 'number' && inp >= 0) {
|
|
114
|
+
sumInput = (sumInput ?? 0) + inp;
|
|
115
|
+
}
|
|
116
|
+
if (typeof out === 'number' && out >= 0) {
|
|
117
|
+
sumOutput = (sumOutput ?? 0) + out;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Count this agent as having tokens if either field is a non-null number.
|
|
121
|
+
if (
|
|
122
|
+
(typeof inp === 'number' && inp >= 0) ||
|
|
123
|
+
(typeof out === 'number' && out >= 0)
|
|
124
|
+
) {
|
|
125
|
+
if (record.agent_id !== undefined && record.agent_id !== null) {
|
|
126
|
+
agentsWithTokens.add(record.agent_id);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
total_token_input: sumInput,
|
|
133
|
+
total_token_output: sumOutput,
|
|
134
|
+
subagents_with_tokens: agentsWithTokens.size,
|
|
135
|
+
matched_records: matched.length,
|
|
136
|
+
};
|
|
137
|
+
}
|