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,196 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* backfill-learnings-expires.mjs — one-shot backfill helper for `expires_at`.
|
|
4
|
+
*
|
|
5
|
+
* Closes issue #323. Surfaced by 2026-05-07 W1 D2 surface-map: 1/129 records
|
|
6
|
+
* in .orchestrator/metrics/learnings.jsonl was missing `expires_at` (id:
|
|
7
|
+
* `archive-en-masse-residual-policy-2026-05-01`).
|
|
8
|
+
*
|
|
9
|
+
* For each record without `expires_at`, derives one from `created_at` + `type`
|
|
10
|
+
* via `deriveExpiresAt()` from `scripts/lib/learnings.mjs`. Records that
|
|
11
|
+
* already have `expires_at` pass through untouched.
|
|
12
|
+
*
|
|
13
|
+
* Forensics: every patched record gets a `_backfilled_expires_at: true` tag
|
|
14
|
+
* so downstream readers can identify backfilled vs originally-stamped values.
|
|
15
|
+
*
|
|
16
|
+
* Usage:
|
|
17
|
+
* node scripts/backfill-learnings-expires.mjs [--source <path>] [--dry-run] [--apply]
|
|
18
|
+
*
|
|
19
|
+
* Flags:
|
|
20
|
+
* --source <path> Path to learnings.jsonl (default: .orchestrator/metrics/learnings.jsonl)
|
|
21
|
+
* --dry-run Preview changes without writing (DEFAULT)
|
|
22
|
+
* --apply Write patched records back; backup original to <path>.bak.<isoDate>
|
|
23
|
+
*
|
|
24
|
+
* Exit codes:
|
|
25
|
+
* 0 Success (including no-op when nothing needs backfilling)
|
|
26
|
+
* 1 Input/validation error
|
|
27
|
+
* 2 I/O error
|
|
28
|
+
*
|
|
29
|
+
* Idempotent: a second --apply run produces 0 to_backfill (every record now
|
|
30
|
+
* has expires_at; the `_backfilled_expires_at` tag is preserved).
|
|
31
|
+
*
|
|
32
|
+
* Output (stdout — single JSON summary line):
|
|
33
|
+
* {"total":N,"already_has_expires":A,"to_backfill":B,"parse_errors":P,"applied":bool}
|
|
34
|
+
*
|
|
35
|
+
* Per-record narration goes to stderr so stdout stays machine-parseable.
|
|
36
|
+
*/
|
|
37
|
+
|
|
38
|
+
import { existsSync, readFileSync, writeFileSync, renameSync, copyFileSync, mkdirSync } from 'node:fs';
|
|
39
|
+
import { dirname } from 'node:path';
|
|
40
|
+
import { deriveExpiresAt } from './lib/learnings.mjs';
|
|
41
|
+
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Arg parsing
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
const args = process.argv.slice(2);
|
|
47
|
+
if (args.includes('--help') || args.includes('-h')) {
|
|
48
|
+
process.stdout.write(
|
|
49
|
+
`Usage: backfill-learnings-expires.mjs [--source <path>] [--dry-run] [--apply]
|
|
50
|
+
|
|
51
|
+
Options:
|
|
52
|
+
--source <path> Path to learnings.jsonl (default: .orchestrator/metrics/learnings.jsonl)
|
|
53
|
+
--dry-run Preview changes without writing (DEFAULT)
|
|
54
|
+
--apply Write patched records; backup original to <path>.bak.<isoDate>
|
|
55
|
+
|
|
56
|
+
Exit codes: 0 success 1 input error 2 I/O error
|
|
57
|
+
`
|
|
58
|
+
);
|
|
59
|
+
process.exit(0);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const applyFlag = args.includes('--apply');
|
|
63
|
+
const dryRun = !applyFlag;
|
|
64
|
+
|
|
65
|
+
const sourceIdx = args.indexOf('--source');
|
|
66
|
+
const sourcePath =
|
|
67
|
+
sourceIdx !== -1 && args[sourceIdx + 1]
|
|
68
|
+
? args[sourceIdx + 1]
|
|
69
|
+
: '.orchestrator/metrics/learnings.jsonl';
|
|
70
|
+
|
|
71
|
+
// ---------------------------------------------------------------------------
|
|
72
|
+
// Load
|
|
73
|
+
// ---------------------------------------------------------------------------
|
|
74
|
+
|
|
75
|
+
if (!existsSync(sourcePath)) {
|
|
76
|
+
process.stderr.write(`backfill-learnings-expires: source file not found: ${sourcePath}\n`);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
let raw;
|
|
81
|
+
try {
|
|
82
|
+
raw = readFileSync(sourcePath, 'utf8');
|
|
83
|
+
} catch (err) {
|
|
84
|
+
process.stderr.write(
|
|
85
|
+
`backfill-learnings-expires: failed to read ${sourcePath}: ${err.message}\n`
|
|
86
|
+
);
|
|
87
|
+
process.exit(2);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const lines = raw.split('\n').filter((l) => l.trim().length > 0);
|
|
91
|
+
|
|
92
|
+
// ---------------------------------------------------------------------------
|
|
93
|
+
// Backfill
|
|
94
|
+
// ---------------------------------------------------------------------------
|
|
95
|
+
|
|
96
|
+
let countAlready = 0;
|
|
97
|
+
let countToBackfill = 0;
|
|
98
|
+
let countParseErrors = 0;
|
|
99
|
+
const outputLines = [];
|
|
100
|
+
|
|
101
|
+
for (const line of lines) {
|
|
102
|
+
let parsed;
|
|
103
|
+
try {
|
|
104
|
+
parsed = JSON.parse(line);
|
|
105
|
+
} catch {
|
|
106
|
+
countParseErrors++;
|
|
107
|
+
process.stderr.write(
|
|
108
|
+
`backfill-learnings-expires: WARN skipping malformed JSON line: ${line.slice(0, 80)}\n`
|
|
109
|
+
);
|
|
110
|
+
// Preserve original line — never destroy data
|
|
111
|
+
outputLines.push(line);
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const hasExpires =
|
|
116
|
+
typeof parsed.expires_at === 'string' && parsed.expires_at.length > 0;
|
|
117
|
+
if (hasExpires) {
|
|
118
|
+
countAlready++;
|
|
119
|
+
outputLines.push(JSON.stringify(parsed));
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Derive expires_at from created_at + type. deriveExpiresAt() falls back
|
|
124
|
+
// to Date.now() when created_at is missing/unparseable, so we always
|
|
125
|
+
// produce a valid ISO timestamp.
|
|
126
|
+
const newExpires = deriveExpiresAt(parsed.created_at, parsed.type);
|
|
127
|
+
const patched = {
|
|
128
|
+
...parsed,
|
|
129
|
+
expires_at: newExpires,
|
|
130
|
+
_backfilled_expires_at: true,
|
|
131
|
+
};
|
|
132
|
+
countToBackfill++;
|
|
133
|
+
outputLines.push(JSON.stringify(patched));
|
|
134
|
+
process.stderr.write(
|
|
135
|
+
`backfill-learnings-expires: patched id=${parsed.id ?? '<no id>'} type=${parsed.type ?? '<no type>'} expires_at=${newExpires}\n`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// ---------------------------------------------------------------------------
|
|
140
|
+
// Write (--apply only)
|
|
141
|
+
// ---------------------------------------------------------------------------
|
|
142
|
+
|
|
143
|
+
let applied = false;
|
|
144
|
+
if (!dryRun && countToBackfill > 0) {
|
|
145
|
+
// Backup first
|
|
146
|
+
const isoDate = new Date().toISOString().replace(/[:.]/g, '-');
|
|
147
|
+
const backupPath = `${sourcePath}.bak.${isoDate}`;
|
|
148
|
+
try {
|
|
149
|
+
copyFileSync(sourcePath, backupPath);
|
|
150
|
+
} catch (err) {
|
|
151
|
+
process.stderr.write(
|
|
152
|
+
`backfill-learnings-expires: ERROR failed to create backup ${backupPath}: ${err.message}\n`
|
|
153
|
+
);
|
|
154
|
+
process.exit(2);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// Atomic write via tmp + rename
|
|
158
|
+
const body = outputLines.join('\n') + '\n';
|
|
159
|
+
const tmp = `${sourcePath}.backfill-tmp-${process.pid}-${Date.now()}`;
|
|
160
|
+
try {
|
|
161
|
+
mkdirSync(dirname(sourcePath), { recursive: true });
|
|
162
|
+
writeFileSync(tmp, body, 'utf8');
|
|
163
|
+
renameSync(tmp, sourcePath);
|
|
164
|
+
applied = true;
|
|
165
|
+
process.stderr.write(
|
|
166
|
+
`backfill-learnings-expires: backup written to ${backupPath}\n`
|
|
167
|
+
);
|
|
168
|
+
} catch (err) {
|
|
169
|
+
process.stderr.write(
|
|
170
|
+
`backfill-learnings-expires: ERROR failed to write ${sourcePath}: ${err.message}\n`
|
|
171
|
+
);
|
|
172
|
+
try {
|
|
173
|
+
if (existsSync(tmp)) renameSync(tmp, tmp + '.failed');
|
|
174
|
+
} catch {
|
|
175
|
+
/* ignore */
|
|
176
|
+
}
|
|
177
|
+
process.exit(2);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// ---------------------------------------------------------------------------
|
|
182
|
+
// Summary (single JSON line on stdout)
|
|
183
|
+
// ---------------------------------------------------------------------------
|
|
184
|
+
|
|
185
|
+
const summary = {
|
|
186
|
+
total: lines.length,
|
|
187
|
+
already_has_expires: countAlready,
|
|
188
|
+
to_backfill: countToBackfill,
|
|
189
|
+
parse_errors: countParseErrors,
|
|
190
|
+
applied,
|
|
191
|
+
dry_run: dryRun,
|
|
192
|
+
source: sourcePath,
|
|
193
|
+
};
|
|
194
|
+
process.stdout.write(JSON.stringify(summary) + '\n');
|
|
195
|
+
|
|
196
|
+
process.exit(0);
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* backfill-learnings.mjs — normalize producer DIALECTS in learnings.jsonl.
|
|
4
|
+
*
|
|
5
|
+
* Epic #723 B2. The live corpus historically carried the rule-conversion scope
|
|
6
|
+
* under `files` (not the canonical `file_paths`), duplicated `source_session`
|
|
7
|
+
* into `session_id`, used `last_seen` instead of `updated_at`, and left
|
|
8
|
+
* `next_review: null` / non-canonical (no-millis) timestamps. This one-shot
|
|
9
|
+
* backfill routes every line through the schema SSOT (`migrateLegacyLearning`,
|
|
10
|
+
* which applies `normalizeDialects` + the insight/subject alias chain + a
|
|
11
|
+
* `schema_version:1` stamp), gates the result with `validateLearning`, and
|
|
12
|
+
* rewrites the file where a safe normalization exists.
|
|
13
|
+
*
|
|
14
|
+
* Data-loss safety (three guarantees):
|
|
15
|
+
* - A line that fails `JSON.parse` is preserved byte-identical and counted
|
|
16
|
+
* (`parse_errors`) — never dropped.
|
|
17
|
+
* - A record that still fails `validateLearning` AFTER migration is passed
|
|
18
|
+
* through byte-identical and counted (`invalid_after_migration`) — never
|
|
19
|
+
* mutated, never flagged.
|
|
20
|
+
* - `--apply` writes atomically (copy-to-`.bak-<ISO>` → tmp → rename), so the
|
|
21
|
+
* canonical file always exists holding either the old or the new content.
|
|
22
|
+
*
|
|
23
|
+
* This script duplicates NO normalization logic — all shape work lives in
|
|
24
|
+
* `scripts/lib/learnings/schema.mjs`.
|
|
25
|
+
*
|
|
26
|
+
* Usage:
|
|
27
|
+
* node scripts/backfill-learnings.mjs [--file PATH] [--apply|--dry-run]
|
|
28
|
+
*
|
|
29
|
+
* Modes:
|
|
30
|
+
* (default / --dry-run) compute + report intended rewrites per line; does
|
|
31
|
+
* not touch the file
|
|
32
|
+
* --apply write the normalized file atomically via tmp+rename,
|
|
33
|
+
* after copying the original to `${file}.bak-<ISO>`
|
|
34
|
+
*
|
|
35
|
+
* Exit codes:
|
|
36
|
+
* 0 — completed (dry-run or apply)
|
|
37
|
+
* 1 — I/O error (file not found, unreadable, write failed)
|
|
38
|
+
* 2 — usage error (unknown argument)
|
|
39
|
+
*
|
|
40
|
+
* Output: JSON summary on stdout (single line)
|
|
41
|
+
* {"scanned":N,"normalized":N,"unchanged":N,"parse_errors":N,
|
|
42
|
+
* "invalid_after_migration":N,"applied":bool,"dry_run":bool,"backup":"<path>|null","file":"<path>"}
|
|
43
|
+
*/
|
|
44
|
+
|
|
45
|
+
import { readFileSync, writeFileSync, renameSync, copyFileSync, existsSync } from 'node:fs';
|
|
46
|
+
import { join, dirname, basename } from 'node:path';
|
|
47
|
+
import {
|
|
48
|
+
migrateLegacyLearning,
|
|
49
|
+
normalizeDialects,
|
|
50
|
+
validateLearning,
|
|
51
|
+
ValidationError,
|
|
52
|
+
} from './lib/learnings/schema.mjs';
|
|
53
|
+
|
|
54
|
+
const DEFAULT_FILE = '.orchestrator/metrics/learnings.jsonl';
|
|
55
|
+
|
|
56
|
+
function parseArgs(argv) {
|
|
57
|
+
const args = { file: DEFAULT_FILE, apply: false };
|
|
58
|
+
for (let i = 0; i < argv.length; i++) {
|
|
59
|
+
const a = argv[i];
|
|
60
|
+
if (a === '--file') args.file = argv[++i];
|
|
61
|
+
else if (a === '--apply') args.apply = true;
|
|
62
|
+
else if (a === '--dry-run') args.apply = false;
|
|
63
|
+
else if (a === '--help' || a === '-h') {
|
|
64
|
+
process.stdout.write(
|
|
65
|
+
'Usage: node scripts/backfill-learnings.mjs [--file PATH] [--apply|--dry-run]\n' +
|
|
66
|
+
' --file target learnings.jsonl (default: .orchestrator/metrics/learnings.jsonl)\n' +
|
|
67
|
+
' --dry-run report normalizations; do not modify file (default)\n' +
|
|
68
|
+
' --apply rewrite file atomically with a .bak-<ISO> backup\n'
|
|
69
|
+
);
|
|
70
|
+
process.exit(0);
|
|
71
|
+
} else {
|
|
72
|
+
process.stderr.write(`backfill-learnings: unknown argument: ${a}\n`);
|
|
73
|
+
process.exit(2);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
return args;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Classify + transform a single parsed record via the schema SSOT.
|
|
81
|
+
*
|
|
82
|
+
* Returns { outcome, line } where outcome is one of:
|
|
83
|
+
* - 'unchanged' : migration is a semantic no-op (serialized form matches input)
|
|
84
|
+
* - 'normalized' : migration changed the record AND it validates
|
|
85
|
+
* - 'invalid' : migration result still fails validateLearning → keep original
|
|
86
|
+
*
|
|
87
|
+
* @param {object} parsed — the JSON.parse'd record
|
|
88
|
+
* @param {string} originalLine — the verbatim source line (returned untouched on 'invalid')
|
|
89
|
+
* @returns {{ outcome: 'unchanged'|'normalized'|'invalid', line: string }}
|
|
90
|
+
*/
|
|
91
|
+
function processRecord(parsed, originalLine) {
|
|
92
|
+
// Two SSOT calls, no duplicated normalization logic:
|
|
93
|
+
// 1. migrateLegacyLearning — aliases + schema_version:1 stamp + dialects
|
|
94
|
+
// (files→file_paths, session_id/last_seen/next_review), byte-exact timestamps.
|
|
95
|
+
// 2. normalizeDialects (full) — canonicalize timestamp FORMAT to millis+Z.
|
|
96
|
+
// Idempotent for every other dialect (already handled in step 1).
|
|
97
|
+
const migrated = normalizeDialects(migrateLegacyLearning(parsed));
|
|
98
|
+
|
|
99
|
+
// Gate: a record that cannot pass the schema after migration is passed
|
|
100
|
+
// through byte-identical (never mutated, never flagged).
|
|
101
|
+
try {
|
|
102
|
+
validateLearning(migrated);
|
|
103
|
+
} catch (err) {
|
|
104
|
+
if (err instanceof ValidationError) {
|
|
105
|
+
return { outcome: 'invalid', line: originalLine };
|
|
106
|
+
}
|
|
107
|
+
throw err;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const migratedLine = JSON.stringify(migrated);
|
|
111
|
+
// Semantic-equality against the re-serialized parsed input: a migration that
|
|
112
|
+
// reorders nothing and changes no value is a no-op.
|
|
113
|
+
if (migratedLine === JSON.stringify(parsed)) {
|
|
114
|
+
return { outcome: 'unchanged', line: migratedLine };
|
|
115
|
+
}
|
|
116
|
+
return { outcome: 'normalized', line: migratedLine };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function main() {
|
|
120
|
+
const args = parseArgs(process.argv.slice(2));
|
|
121
|
+
|
|
122
|
+
if (!args.file || !existsSync(args.file)) {
|
|
123
|
+
process.stderr.write(`backfill-learnings: file not found: ${args.file}\n`);
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
let lines;
|
|
128
|
+
try {
|
|
129
|
+
lines = readFileSync(args.file, 'utf8').split('\n').filter((l) => l.length > 0);
|
|
130
|
+
} catch (err) {
|
|
131
|
+
process.stderr.write(`backfill-learnings: read failed: ${err.message}\n`);
|
|
132
|
+
process.exit(1);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const outputLines = [];
|
|
136
|
+
const counts = {
|
|
137
|
+
scanned: 0,
|
|
138
|
+
normalized: 0,
|
|
139
|
+
unchanged: 0,
|
|
140
|
+
parse_errors: 0,
|
|
141
|
+
invalid_after_migration: 0,
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
for (const line of lines) {
|
|
145
|
+
counts.scanned++;
|
|
146
|
+
let parsed;
|
|
147
|
+
try {
|
|
148
|
+
parsed = JSON.parse(line);
|
|
149
|
+
} catch {
|
|
150
|
+
counts.parse_errors++;
|
|
151
|
+
outputLines.push(line); // byte-identical preservation — never drop
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const { outcome, line: outLine } = processRecord(parsed, line);
|
|
156
|
+
if (outcome === 'invalid') counts.invalid_after_migration++;
|
|
157
|
+
else counts[outcome]++;
|
|
158
|
+
outputLines.push(outLine);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const summary = {
|
|
162
|
+
scanned: counts.scanned,
|
|
163
|
+
normalized: counts.normalized,
|
|
164
|
+
unchanged: counts.unchanged,
|
|
165
|
+
parse_errors: counts.parse_errors,
|
|
166
|
+
invalid_after_migration: counts.invalid_after_migration,
|
|
167
|
+
applied: false,
|
|
168
|
+
dry_run: !args.apply,
|
|
169
|
+
backup: null,
|
|
170
|
+
file: args.file,
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
if (!args.apply) {
|
|
174
|
+
process.stdout.write(JSON.stringify(summary) + '\n');
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Apply path — POSIX atomic replace, self-contained (no io.mjs dependency).
|
|
179
|
+
// 1. copyFileSync: canonical → .bak-<ISO> (backup; canonical still present)
|
|
180
|
+
// 2. writeFileSync: normalized → tmp (new content; canonical still present)
|
|
181
|
+
// 3. renameSync: tmp → canonical (atomic replace per POSIX)
|
|
182
|
+
// The canonical file exists at every step, holding old or new content.
|
|
183
|
+
const iso = new Date().toISOString().replace(/[:.]/g, '-');
|
|
184
|
+
const backupPath = `${args.file}.bak-${iso}`;
|
|
185
|
+
const tmpPath = join(dirname(args.file), `.${basename(args.file)}.tmp-${process.pid}`);
|
|
186
|
+
try {
|
|
187
|
+
copyFileSync(args.file, backupPath);
|
|
188
|
+
writeFileSync(tmpPath, outputLines.join('\n') + '\n', 'utf8');
|
|
189
|
+
renameSync(tmpPath, args.file);
|
|
190
|
+
summary.applied = true;
|
|
191
|
+
summary.backup = backupPath;
|
|
192
|
+
} catch (err) {
|
|
193
|
+
const recoveryHint = existsSync(backupPath)
|
|
194
|
+
? `Canonical file is intact; backup at ${backupPath}.`
|
|
195
|
+
: 'Backup was not created.';
|
|
196
|
+
process.stderr.write(`backfill-learnings: write failed: ${err.message}. ${recoveryHint}\n`);
|
|
197
|
+
process.exit(1);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
process.stdout.write(JSON.stringify(summary) + '\n');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
main();
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* backfill-sessions.mjs — normalize legacy shapes in sessions.jsonl.
|
|
4
|
+
*
|
|
5
|
+
* Issue #249 follow-up. Reads a sessions.jsonl file and rewrites each line
|
|
6
|
+
* into the canonical schema_version=2 shape (bumped 1 -> 2 via #372) where a
|
|
7
|
+
* safe deterministic mapping exists. Entries that cannot be safely mapped are
|
|
8
|
+
* tagged `_deprecated: true` (preserved but flagged — never dropped).
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* node scripts/backfill-sessions.mjs [--file PATH] [--apply] [--mark-deprecated-only]
|
|
12
|
+
*
|
|
13
|
+
* Modes:
|
|
14
|
+
* (default / --dry-run) reports intended rewrites + deprecations per line;
|
|
15
|
+
* does not touch the file
|
|
16
|
+
* --apply writes the rewritten file atomically via temp+rename;
|
|
17
|
+
* creates a `.bak-<ISO>` backup of the original
|
|
18
|
+
* --mark-deprecated-only same as --apply but only tags unmappable entries
|
|
19
|
+
* with `_deprecated: true`; leaves mappable entries
|
|
20
|
+
* untouched (conservative path)
|
|
21
|
+
*
|
|
22
|
+
* Exit codes:
|
|
23
|
+
* 0 — completed (dry-run or apply)
|
|
24
|
+
* 1 — I/O error (unreadable file, write failed)
|
|
25
|
+
* 2 — usage error
|
|
26
|
+
*
|
|
27
|
+
* Output: JSON summary on stdout
|
|
28
|
+
* {"mode":"dry-run|apply|mark-deprecated-only","total":N,"rewritten":N,"deprecated":N,"unchanged":N,"backup":"<path>|null"}
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import { readFileSync, writeFileSync, renameSync, copyFileSync, existsSync } from 'node:fs';
|
|
32
|
+
import { join, dirname, basename } from 'node:path';
|
|
33
|
+
import { validateSession, normalizeSession, ValidationError } from './lib/session-schema.mjs';
|
|
34
|
+
|
|
35
|
+
const DEFAULT_FILE = '.orchestrator/metrics/sessions.jsonl';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Line numbers (1-indexed) known to be unmappable per D1 inventory. These
|
|
39
|
+
* entries have fundamentally divergent shape (e.g., `waves: null` scalar
|
|
40
|
+
* counts, alternative telemetry models) that cannot be deterministically
|
|
41
|
+
* converted without data loss. Marked `_deprecated: true` on apply.
|
|
42
|
+
*
|
|
43
|
+
* The logic below also independently detects unmappable entries by shape,
|
|
44
|
+
* so this list is informational — identification is structural, not
|
|
45
|
+
* positional. If the file is truncated or reordered, the shape-check still
|
|
46
|
+
* fires.
|
|
47
|
+
*/
|
|
48
|
+
const KNOWN_UNMAPPABLE_SHAPES = Object.freeze({
|
|
49
|
+
// `waves` is null (scalar counts via waves_completed/waves_total — array
|
|
50
|
+
// cannot be reconstructed):
|
|
51
|
+
wavesIsNull: (e) => e.waves === null,
|
|
52
|
+
// `waves` is a number (integer count, not an array):
|
|
53
|
+
wavesIsNumber: (e) => typeof e.waves === 'number',
|
|
54
|
+
// `waves_executed` scalar instead of `waves` array — array not reconstructible:
|
|
55
|
+
wavesExecutedScalar: (e) => typeof e.waves_executed === 'number' && !('waves' in e),
|
|
56
|
+
// head_ref + metrics-object variant (windows-native experiment) — no
|
|
57
|
+
// canonical mapping for the `metrics` bag:
|
|
58
|
+
metricsObjectShape: (e) => 'metrics' in e && !('agent_summary' in e),
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Value-transforming aliases not safe for session-schema.mjs (which only
|
|
63
|
+
* does same-shape renames). Applied here because backfill has full context.
|
|
64
|
+
*/
|
|
65
|
+
function applyValueAliases(e) {
|
|
66
|
+
const next = { ...e };
|
|
67
|
+
// duration_min / duration_minutes → duration_seconds (multiply by 60)
|
|
68
|
+
if ('duration_min' in next && !('duration_seconds' in next)) {
|
|
69
|
+
if (typeof next.duration_min === 'number' && next.duration_min >= 0) {
|
|
70
|
+
next.duration_seconds = next.duration_min * 60;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
if ('duration_minutes' in next && !('duration_seconds' in next)) {
|
|
74
|
+
if (typeof next.duration_minutes === 'number' && next.duration_minutes >= 0) {
|
|
75
|
+
next.duration_seconds = next.duration_minutes * 60;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
// agent_summary reconstruction from agents_complete/partial/failed/dispatched
|
|
79
|
+
// ONLY when waves scalar does NOT indicate a fundamentally different shape.
|
|
80
|
+
// Ambiguous without `spiral` — synthesize as 0 if reconstruction is attempted.
|
|
81
|
+
if (
|
|
82
|
+
!('agent_summary' in next) &&
|
|
83
|
+
typeof next.agents_complete === 'number' &&
|
|
84
|
+
typeof next.agents_partial === 'number' &&
|
|
85
|
+
typeof next.agents_failed === 'number'
|
|
86
|
+
) {
|
|
87
|
+
next.agent_summary = {
|
|
88
|
+
complete: next.agents_complete,
|
|
89
|
+
partial: next.agents_partial,
|
|
90
|
+
failed: next.agents_failed,
|
|
91
|
+
spiral: typeof next.agents_spiral === 'number' ? next.agents_spiral : 0,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
// total_agents: derive from agents_dispatched if missing
|
|
95
|
+
if (!('total_agents' in next) && typeof next.agents_dispatched === 'number') {
|
|
96
|
+
next.total_agents = next.agents_dispatched;
|
|
97
|
+
}
|
|
98
|
+
// total_files_changed: derive from files_changed scalar (if a number)
|
|
99
|
+
if (!('total_files_changed' in next) && typeof next.files_changed === 'number') {
|
|
100
|
+
next.total_files_changed = next.files_changed;
|
|
101
|
+
}
|
|
102
|
+
// total_waves: derive from waves_total or waves_completed scalar
|
|
103
|
+
if (!('total_waves' in next)) {
|
|
104
|
+
if (typeof next.waves_total === 'number') next.total_waves = next.waves_total;
|
|
105
|
+
else if (typeof next.waves_completed === 'number') next.total_waves = next.waves_completed;
|
|
106
|
+
}
|
|
107
|
+
return next;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
function detectUnmappableReason(entry) {
|
|
111
|
+
for (const [name, check] of Object.entries(KNOWN_UNMAPPABLE_SHAPES)) {
|
|
112
|
+
if (check(entry)) return name;
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function parseArgs(argv) {
|
|
118
|
+
const args = { file: DEFAULT_FILE, mode: 'dry-run' };
|
|
119
|
+
for (let i = 0; i < argv.length; i++) {
|
|
120
|
+
const a = argv[i];
|
|
121
|
+
if (a === '--file') args.file = argv[++i];
|
|
122
|
+
else if (a === '--apply') args.mode = 'apply';
|
|
123
|
+
else if (a === '--dry-run') args.mode = 'dry-run';
|
|
124
|
+
else if (a === '--mark-deprecated-only') args.mode = 'mark-deprecated-only';
|
|
125
|
+
else if (a === '--help' || a === '-h') {
|
|
126
|
+
process.stdout.write(
|
|
127
|
+
'Usage: node scripts/backfill-sessions.mjs [--file PATH] [--apply|--dry-run|--mark-deprecated-only]\n' +
|
|
128
|
+
' --file target sessions.jsonl (default: .orchestrator/metrics/sessions.jsonl)\n' +
|
|
129
|
+
' --dry-run report rewrites; do not modify file (default)\n' +
|
|
130
|
+
' --apply rewrite file atomically with .bak-<ISO> backup\n' +
|
|
131
|
+
' --mark-deprecated-only tag only unmappable entries with _deprecated:true\n'
|
|
132
|
+
);
|
|
133
|
+
process.exit(0);
|
|
134
|
+
} else {
|
|
135
|
+
process.stderr.write(`backfill-sessions: unknown argument: ${a}\n`);
|
|
136
|
+
process.exit(2);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return args;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Process a single entry. Returns { outcome, entry } where outcome is one
|
|
144
|
+
* of: 'unchanged' | 'rewritten' | 'deprecated'.
|
|
145
|
+
*
|
|
146
|
+
* - 'unchanged': transforms are a no-op (entry already canonical byte-equivalent)
|
|
147
|
+
* - 'rewritten': entry was transformed (safe aliases + value-transforming aliases)
|
|
148
|
+
* and the result validates as v1
|
|
149
|
+
* - 'deprecated': structurally unmappable OR validation still fails after
|
|
150
|
+
* transforms; tagged with _deprecated:true
|
|
151
|
+
*/
|
|
152
|
+
function processEntry(raw) {
|
|
153
|
+
const structuralReason = detectUnmappableReason(raw);
|
|
154
|
+
if (structuralReason) {
|
|
155
|
+
return {
|
|
156
|
+
outcome: 'deprecated',
|
|
157
|
+
entry: { ...raw, _deprecated: true, _deprecation_reason: `structural: ${structuralReason}` },
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Always run the transform pipeline so value-alias reconstruction happens
|
|
162
|
+
// even on entries that would pass validation without it.
|
|
163
|
+
let transformed = normalizeSession(raw);
|
|
164
|
+
transformed = applyValueAliases(transformed);
|
|
165
|
+
// Drop normalizeSession's read-path schema_version:0 stamp so validateSession
|
|
166
|
+
// stamps the canonical 1 on successful rewrites.
|
|
167
|
+
if (transformed.schema_version === 0 && !('schema_version' in raw)) {
|
|
168
|
+
delete transformed.schema_version;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
let validated;
|
|
172
|
+
try {
|
|
173
|
+
validated = validateSession(transformed);
|
|
174
|
+
} catch (err) {
|
|
175
|
+
if (err instanceof ValidationError) {
|
|
176
|
+
return {
|
|
177
|
+
outcome: 'deprecated',
|
|
178
|
+
entry: { ...raw, _deprecated: true, _deprecation_reason: err.message },
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
throw err;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// "Unchanged" means: the serialized form matches the serialized original.
|
|
185
|
+
// This preserves byte-identical behavior for already-canonical entries
|
|
186
|
+
// while still classifying entries that needed any transform as rewritten.
|
|
187
|
+
if (JSON.stringify(validated) === JSON.stringify(raw)) {
|
|
188
|
+
return { outcome: 'unchanged', entry: raw };
|
|
189
|
+
}
|
|
190
|
+
return { outcome: 'rewritten', entry: validated };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
async function main() {
|
|
194
|
+
const args = parseArgs(process.argv.slice(2));
|
|
195
|
+
|
|
196
|
+
if (!existsSync(args.file)) {
|
|
197
|
+
process.stderr.write(`backfill-sessions: file not found: ${args.file}\n`);
|
|
198
|
+
process.exit(1);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
let lines;
|
|
202
|
+
try {
|
|
203
|
+
lines = readFileSync(args.file, 'utf8').split('\n').filter((l) => l.length > 0);
|
|
204
|
+
} catch (err) {
|
|
205
|
+
process.stderr.write(`backfill-sessions: read failed: ${err.message}\n`);
|
|
206
|
+
process.exit(1);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const perLine = [];
|
|
210
|
+
const rewritten = [];
|
|
211
|
+
const counts = { unchanged: 0, rewritten: 0, deprecated: 0, parseError: 0 };
|
|
212
|
+
|
|
213
|
+
for (let i = 0; i < lines.length; i++) {
|
|
214
|
+
const lineNum = i + 1;
|
|
215
|
+
let raw;
|
|
216
|
+
try {
|
|
217
|
+
raw = JSON.parse(lines[i]);
|
|
218
|
+
} catch (err) {
|
|
219
|
+
counts.parseError++;
|
|
220
|
+
perLine.push({ line: lineNum, outcome: 'parse-error', reason: err.message });
|
|
221
|
+
rewritten.push(lines[i]); // preserve original line verbatim
|
|
222
|
+
continue;
|
|
223
|
+
}
|
|
224
|
+
const { outcome, entry } = processEntry(raw);
|
|
225
|
+
counts[outcome]++;
|
|
226
|
+
perLine.push({ line: lineNum, outcome, session_id: raw.session_id ?? null });
|
|
227
|
+
if (args.mode === 'mark-deprecated-only' && outcome === 'rewritten') {
|
|
228
|
+
// Conservative: leave mappable entries as-is.
|
|
229
|
+
rewritten.push(lines[i]);
|
|
230
|
+
} else {
|
|
231
|
+
rewritten.push(JSON.stringify(entry));
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const summary = {
|
|
236
|
+
mode: args.mode,
|
|
237
|
+
file: args.file,
|
|
238
|
+
total: lines.length,
|
|
239
|
+
unchanged: counts.unchanged,
|
|
240
|
+
rewritten: counts.rewritten,
|
|
241
|
+
deprecated: counts.deprecated,
|
|
242
|
+
parse_errors: counts.parseError,
|
|
243
|
+
backup: null,
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
if (args.mode === 'dry-run') {
|
|
247
|
+
process.stdout.write(JSON.stringify(summary) + '\n');
|
|
248
|
+
process.stdout.write(JSON.stringify({ detail: perLine }) + '\n');
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Apply path — POSIX atomic replace (tmp → rename-over-canonical) preceded
|
|
253
|
+
// by a pre-write copy-to-backup. This eliminates the crash window where the
|
|
254
|
+
// canonical path could be briefly missing if the rename dance were split:
|
|
255
|
+
// 1. copyFileSync: canonical → .bak-<ISO> (backup; canonical still present)
|
|
256
|
+
// 2. writeFileSync: rewritten → tmp (new content; canonical still present)
|
|
257
|
+
// 3. renameSync: tmp → canonical (atomic replace per POSIX)
|
|
258
|
+
// At every point between steps, the canonical file exists and holds either
|
|
259
|
+
// the old content or the new content. Recovery artifact: .bak-<ISO>.
|
|
260
|
+
const iso = new Date().toISOString().replace(/[:.]/g, '-');
|
|
261
|
+
const backupPath = `${args.file}.bak-${iso}`;
|
|
262
|
+
const tmpPath = join(dirname(args.file), `.${basename(args.file)}.tmp-${process.pid}`);
|
|
263
|
+
try {
|
|
264
|
+
copyFileSync(args.file, backupPath);
|
|
265
|
+
writeFileSync(tmpPath, rewritten.join('\n') + '\n', 'utf8');
|
|
266
|
+
renameSync(tmpPath, args.file);
|
|
267
|
+
summary.backup = backupPath;
|
|
268
|
+
} catch (err) {
|
|
269
|
+
const recoveryHint = existsSync(backupPath)
|
|
270
|
+
? `Canonical file is intact; backup at ${backupPath}.`
|
|
271
|
+
: 'Backup was not created.';
|
|
272
|
+
process.stderr.write(`backfill-sessions: write failed: ${err.message}. ${recoveryHint}\n`);
|
|
273
|
+
process.exit(1);
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
process.stdout.write(JSON.stringify(summary) + '\n');
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
main().catch((err) => {
|
|
280
|
+
process.stderr.write(`backfill-sessions: unexpected error: ${err?.stack ?? err}\n`);
|
|
281
|
+
process.exit(1);
|
|
282
|
+
});
|