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,112 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* worktree-cleanup.mjs — Phase 4a Auto-Promoted Worktree Cleanup helpers (#575 P3.2).
|
|
3
|
+
*
|
|
4
|
+
* Public API:
|
|
5
|
+
* - detectAutoPromotedWorktree(repoRoot, sessionId, opts): { wtPath, sessionId, branch } | null
|
|
6
|
+
* - isWorktreeClean(wtPath, opts): boolean
|
|
7
|
+
* (opts.execFileFn — injectable execFileSync seam for tests; #577 HARDEN-001)
|
|
8
|
+
*
|
|
9
|
+
* Closes #575 — Epic #568 Phase 3.2 (Parallel-Aware Sessions Auto-Promoted Worktree Cleanup)
|
|
10
|
+
* PRD: "Parallel-aware sessions" (#568; archived in the private Meta-Vault) §3 P3 Gherkin rows 2-3
|
|
11
|
+
*
|
|
12
|
+
* Lifted verbatim from skills/session-end/SKILL.md Phase 4a so that the helpers
|
|
13
|
+
* are unit-testable and reusable (instruction-text → executable extraction).
|
|
14
|
+
*
|
|
15
|
+
* DI seam (#580-DI-001): this module uses a SYNCHRONOUS `opts.execFileFn`
|
|
16
|
+
* (default `execFileSync`) because session-end Phase 4a runs in a synchronous
|
|
17
|
+
* coordinator step. Its sibling helper `scripts/lib/memory-cleanup/worktree-sweep.mjs`
|
|
18
|
+
* shares the same sync `execFileFn` seam; the autopilot worktree driver
|
|
19
|
+
* (`scripts/lib/autopilot/worktree-pipeline.mjs`) deliberately uses an ASYNC
|
|
20
|
+
* `opts.$` (zx) seam instead because `enterWorktree()` is async. The seams are
|
|
21
|
+
* kept divergent on purpose — unifying them would break the sync/async boundary.
|
|
22
|
+
*/
|
|
23
|
+
import path from 'node:path';
|
|
24
|
+
import { execFileSync } from 'node:child_process';
|
|
25
|
+
import { parseSessionId } from '../session-id.mjs';
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Detect whether the given repoRoot is an auto-promoted sibling worktree
|
|
29
|
+
* created by `enterWorktree()` during the Phase 0.5 PROMOTION_OFFER path.
|
|
30
|
+
*
|
|
31
|
+
* Auto-promoted layout: <basePath>/<repo-name>-<sessionId>/
|
|
32
|
+
*
|
|
33
|
+
* Returns:
|
|
34
|
+
* { wtPath, sessionId, branch } on match
|
|
35
|
+
* null on non-match (UUID session, non-promoted path, or git error)
|
|
36
|
+
*
|
|
37
|
+
* @param {string} repoRoot - Absolute path to the candidate worktree
|
|
38
|
+
* @param {string} sessionId - Session ID (semantic or UUID)
|
|
39
|
+
* @returns {{wtPath: string, sessionId: string, branch: string} | null}
|
|
40
|
+
*/
|
|
41
|
+
export function detectAutoPromotedWorktree(repoRoot, sessionId, opts = {}) {
|
|
42
|
+
// #577 HARDEN-001: execFileSync + args ARRAY (no shell) is structurally
|
|
43
|
+
// injection-proof — repoRoot can never be interpreted as shell metacharacters.
|
|
44
|
+
const execFileFn = opts.execFileFn ?? execFileSync;
|
|
45
|
+
const parsed = parseSessionId(sessionId);
|
|
46
|
+
if (!parsed || parsed.format !== 'semantic') return null; // UUID-format sessions are never auto-promoted
|
|
47
|
+
|
|
48
|
+
// Derive the MAIN checkout root from `git worktree list --porcelain` (first entry).
|
|
49
|
+
// Bug-fix (W3 T2 finding): the original logic compared `path.basename(repoRoot) === ${repoName}-${sessionId}`
|
|
50
|
+
// where repoName WAS basename(repoRoot) itself — structurally impossible. The correct repo-name comes
|
|
51
|
+
// from the main checkout (worktree list entry 0), not from the promoted worktree we're checking.
|
|
52
|
+
let mainCheckoutRoot;
|
|
53
|
+
try {
|
|
54
|
+
const out = execFileFn('git', ['-C', repoRoot, 'worktree', 'list', '--porcelain'], {
|
|
55
|
+
encoding: 'utf8',
|
|
56
|
+
});
|
|
57
|
+
const lines = out.split('\n');
|
|
58
|
+
const firstWorktreeLine = lines.find((l) => l.startsWith('worktree '));
|
|
59
|
+
if (!firstWorktreeLine) return null;
|
|
60
|
+
mainCheckoutRoot = firstWorktreeLine.slice('worktree '.length);
|
|
61
|
+
} catch {
|
|
62
|
+
return null; // not a git repo, treat as not promoted
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
// If repoRoot IS the main checkout, this session is NOT in an auto-promoted worktree.
|
|
66
|
+
if (path.resolve(repoRoot) === path.resolve(mainCheckoutRoot)) return null;
|
|
67
|
+
|
|
68
|
+
// Auto-promoted layout: <basePath>/<main-repo-name>-<sessionId>/
|
|
69
|
+
// Where <basePath> = path.dirname(mainCheckoutRoot).
|
|
70
|
+
const mainRepoName = path.basename(mainCheckoutRoot);
|
|
71
|
+
const expectedBasename = `${mainRepoName}-${sessionId}`;
|
|
72
|
+
const isPromotedPath = path.basename(repoRoot) === expectedBasename;
|
|
73
|
+
|
|
74
|
+
if (isPromotedPath) {
|
|
75
|
+
return { wtPath: repoRoot, sessionId, branch: parsed.branch };
|
|
76
|
+
}
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Determine whether the worktree at the given path is clean.
|
|
82
|
+
*
|
|
83
|
+
* A worktree is clean iff ALL three conditions hold:
|
|
84
|
+
* 1. No uncommitted changes (`git status --porcelain` is empty)
|
|
85
|
+
* 2. No untracked files (implicit in #1 — porcelain includes `??` entries)
|
|
86
|
+
* 3. No unpushed commits (`git status --short --branch` lacks `ahead`)
|
|
87
|
+
*
|
|
88
|
+
* On any git error, returns `false` (safer per PSA-003 — conservative default
|
|
89
|
+
* means we never auto-remove a worktree we couldn't verify).
|
|
90
|
+
*
|
|
91
|
+
* @param {string} wtPath - Absolute path to the worktree
|
|
92
|
+
* @returns {boolean}
|
|
93
|
+
*/
|
|
94
|
+
export function isWorktreeClean(wtPath, opts = {}) {
|
|
95
|
+
// #577 HARDEN-001: execFileSync + args ARRAY (no shell) — wtPath cannot inject.
|
|
96
|
+
const execFileFn = opts.execFileFn ?? execFileSync;
|
|
97
|
+
try {
|
|
98
|
+
const status = execFileFn('git', ['-C', wtPath, 'status', '--porcelain'], {
|
|
99
|
+
encoding: 'utf8',
|
|
100
|
+
});
|
|
101
|
+
if (status.trim().length > 0) return false; // dirty (modified, untracked, or staged)
|
|
102
|
+
|
|
103
|
+
const branchStatus = execFileFn('git', ['-C', wtPath, 'status', '--short', '--branch'], {
|
|
104
|
+
encoding: 'utf8',
|
|
105
|
+
});
|
|
106
|
+
if (branchStatus.match(/\bahead\b/)) return false; // unpushed
|
|
107
|
+
|
|
108
|
+
return true;
|
|
109
|
+
} catch {
|
|
110
|
+
return false; // any error → treat as dirty (safer per PSA-003)
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* session-id.mjs — Semantic session-ID generation and dual-format parsing.
|
|
3
|
+
*
|
|
4
|
+
* Public API:
|
|
5
|
+
* - resolveSemanticSessionId({ branch, mode, activeSessions, repoRoot, history }): Promise<string>
|
|
6
|
+
* - parseSessionId(id): { format: 'semantic'|'uuid', ...fields, raw } | null
|
|
7
|
+
* - SEMANTIC_ID_RE — source-of-truth regex for semantic session IDs
|
|
8
|
+
* - UUID_V4_RE — regex for UUID-v4 format session IDs
|
|
9
|
+
*
|
|
10
|
+
* Closes #572 — Epic #568 Phase 2.1 (Parallel-Aware Sessions Semantic ID)
|
|
11
|
+
* Closes #585 — Epic #583 W2-I2 (history-aware n-increment) per audit
|
|
12
|
+
* .orchestrator/audits/W1-D2-resolveSemanticSessionId.md
|
|
13
|
+
*
|
|
14
|
+
* PRD refs:
|
|
15
|
+
* - "Parallel-aware sessions" (#568; archived in the private Meta-Vault) §3 P2 + §3.A P2
|
|
16
|
+
* - Epic #583 (Parallel-Session Detection Wiring Hardening)
|
|
17
|
+
*
|
|
18
|
+
* Design notes:
|
|
19
|
+
* - resolveSemanticSessionId wraps its body in withStateMdLock so two
|
|
20
|
+
* concurrent preambles cannot assign duplicate n values (PSA-005).
|
|
21
|
+
* - parseSessionId is a pure synchronous function — no I/O, no side effects.
|
|
22
|
+
* - History-aware (#585): the n-increment consults three candidate sources:
|
|
23
|
+
* 1. activeSessions (the legacy source — live lockfiles + registry).
|
|
24
|
+
* 2. sessions.jsonl history (closed sessions; appended on session-end).
|
|
25
|
+
* 3. STATE.md frontmatter `session:` (last-resort survivor of crashed sessions).
|
|
26
|
+
* Sources 2 and 3 are read inside the existing withStateMdLock so their
|
|
27
|
+
* visibility is consistent with the n-claim that follows. Both are opt-out
|
|
28
|
+
* via opts.history.{consultHistory,consultStateMd} and DI-overridable via
|
|
29
|
+
* opts.history.{readHistoryImpl,readStateMdSessionImpl} for tests.
|
|
30
|
+
* - Reader helpers never throw: missing files, malformed JSONL lines, and
|
|
31
|
+
* unparseable frontmatter are all treated as "no signal" (empty/null).
|
|
32
|
+
* - Production code is silent: no console.log, no console.warn.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import { readFile } from 'node:fs/promises';
|
|
36
|
+
import path from 'node:path';
|
|
37
|
+
|
|
38
|
+
import { withStateMdLock } from './session-lock.mjs';
|
|
39
|
+
import { resolveStateMdPath } from './state-md/frontmatter-mutators.mjs';
|
|
40
|
+
import { parseStateMd } from './state-md/yaml-parser.mjs';
|
|
41
|
+
|
|
42
|
+
// ---------------------------------------------------------------------------
|
|
43
|
+
// Exported regexes (source-of-truth, also consumed by tests/consumers)
|
|
44
|
+
// ---------------------------------------------------------------------------
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Source-of-truth regex for the semantic session-ID format.
|
|
48
|
+
*
|
|
49
|
+
* Groups:
|
|
50
|
+
* 1 — branch (`[a-z0-9._/-]+`, git branch characters including slashes)
|
|
51
|
+
* 2 — date (`YYYY-MM-DD`, UTC)
|
|
52
|
+
* 3 — mode (`[a-z-]+`, lowercase + hyphens)
|
|
53
|
+
* 4 — n monotonic counter (decimal digits)
|
|
54
|
+
*
|
|
55
|
+
* Note: the branch group is case-insensitive at the regex level, but
|
|
56
|
+
* callers must normalise branches to the form they store in activeSessions.
|
|
57
|
+
*
|
|
58
|
+
* @type {RegExp}
|
|
59
|
+
*/
|
|
60
|
+
export const SEMANTIC_ID_RE = /^([a-z0-9._/-]+)-(\d{4}-\d{2}-\d{2})-([a-z-]+)-(\d+)$/;
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Regex for UUID-v4 session IDs.
|
|
64
|
+
*
|
|
65
|
+
* Matches: 8-4-4-4-12 hex digits, version nibble = '4', variant nibble in {8,9,a,b}.
|
|
66
|
+
* Case-insensitive to accept both uppercase and lowercase hex.
|
|
67
|
+
*
|
|
68
|
+
* @type {RegExp}
|
|
69
|
+
*/
|
|
70
|
+
export const UUID_V4_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;
|
|
71
|
+
|
|
72
|
+
// ---------------------------------------------------------------------------
|
|
73
|
+
// Internal helpers
|
|
74
|
+
// ---------------------------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Validate a git branch name for use in a semantic session ID.
|
|
78
|
+
*
|
|
79
|
+
* Allows the characters that git itself accepts: alphanumerics, dots, dashes,
|
|
80
|
+
* underscores, and forward-slashes (for namespaced branches like feature/foo).
|
|
81
|
+
*
|
|
82
|
+
* @param {string} branch
|
|
83
|
+
* @returns {boolean}
|
|
84
|
+
*/
|
|
85
|
+
function isValidBranch(branch) {
|
|
86
|
+
return typeof branch === 'string' && branch.length > 0 && /^[a-zA-Z0-9._/-]+$/.test(branch);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Validate a session mode name.
|
|
91
|
+
*
|
|
92
|
+
* Modes are lowercase ASCII letters and hyphens (e.g. "deep", "feature",
|
|
93
|
+
* "house-keeping"). No uppercase, no underscores, no digits.
|
|
94
|
+
*
|
|
95
|
+
* @param {string} mode
|
|
96
|
+
* @returns {boolean}
|
|
97
|
+
*/
|
|
98
|
+
function isValidMode(mode) {
|
|
99
|
+
return typeof mode === 'string' && mode.length > 0 && /^[a-z-]+$/.test(mode);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Read closed-session IDs from `<repoRoot>/.orchestrator/metrics/sessions.jsonl`.
|
|
104
|
+
*
|
|
105
|
+
* Robustness contract (W1-D2 audit §3.1):
|
|
106
|
+
* - Missing file (ENOENT) → returns [] (fresh repo, no history yet).
|
|
107
|
+
* - Permission/FS error → returns [] (fail-open; this helper must never
|
|
108
|
+
* block the n-claim it feeds).
|
|
109
|
+
* - Malformed JSONL line → silently skipped (per-line try/catch).
|
|
110
|
+
* - Lines without a string `session_id` field → filtered out.
|
|
111
|
+
*
|
|
112
|
+
* Performance note: sessions.jsonl is line-oriented but typically <100 KB.
|
|
113
|
+
* A single readFile is faster than line-streaming at this size. Should the
|
|
114
|
+
* file grow past ~5 MB a future change can swap to a `readline` stream with
|
|
115
|
+
* early-exit; not a launch blocker.
|
|
116
|
+
*
|
|
117
|
+
* @param {string} repoRoot
|
|
118
|
+
* @returns {Promise<string[]>} Array of session_id strings (may include duplicates).
|
|
119
|
+
*/
|
|
120
|
+
async function readSessionIdsFromHistory(repoRoot) {
|
|
121
|
+
const filePath = path.join(repoRoot, '.orchestrator', 'metrics', 'sessions.jsonl');
|
|
122
|
+
let raw;
|
|
123
|
+
try {
|
|
124
|
+
raw = await readFile(filePath, 'utf8');
|
|
125
|
+
} catch {
|
|
126
|
+
return [];
|
|
127
|
+
}
|
|
128
|
+
const ids = [];
|
|
129
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
130
|
+
const trimmed = line.trim();
|
|
131
|
+
if (trimmed === '') continue;
|
|
132
|
+
try {
|
|
133
|
+
const parsed = JSON.parse(trimmed);
|
|
134
|
+
if (typeof parsed?.session_id === 'string') ids.push(parsed.session_id);
|
|
135
|
+
} catch {
|
|
136
|
+
// Malformed line — skip silently (audit §3.1 robustness contract).
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return ids;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Read the `session:` field from `<repoRoot>/.claude/STATE.md` (or `.codex`,
|
|
144
|
+
* `.cursor`, `.pi` — `resolveStateMdPath` picks the active/existing candidate).
|
|
145
|
+
*
|
|
146
|
+
* Robustness contract (W1-D2 audit §3.2):
|
|
147
|
+
* - Missing STATE.md → returns null.
|
|
148
|
+
* - Unparseable frontmatter → returns null (parseStateMd already returns
|
|
149
|
+
* null on bad input).
|
|
150
|
+
* - `session:` field absent or non-string → returns null.
|
|
151
|
+
* - I/O error → returns null (fail-open).
|
|
152
|
+
*
|
|
153
|
+
* Lock invariant: this helper is called from inside `withStateMdLock`, so we
|
|
154
|
+
* are reading our own write-lock domain — no mid-write races.
|
|
155
|
+
*
|
|
156
|
+
* @param {string} repoRoot
|
|
157
|
+
* @returns {Promise<string|null>} The session_id string, or null when absent.
|
|
158
|
+
*/
|
|
159
|
+
async function readSessionIdFromStateMd(repoRoot) {
|
|
160
|
+
const filePath = resolveStateMdPath(repoRoot);
|
|
161
|
+
let raw;
|
|
162
|
+
try {
|
|
163
|
+
raw = await readFile(filePath, 'utf8');
|
|
164
|
+
} catch {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
const parsed = parseStateMd(raw);
|
|
168
|
+
if (parsed === null) return null;
|
|
169
|
+
const sessionField = parsed.frontmatter?.session;
|
|
170
|
+
return typeof sessionField === 'string' ? sessionField : null;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// ---------------------------------------------------------------------------
|
|
174
|
+
// Public API
|
|
175
|
+
// ---------------------------------------------------------------------------
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Parse a session ID string into a structured object.
|
|
179
|
+
*
|
|
180
|
+
* Accepts two formats:
|
|
181
|
+
*
|
|
182
|
+
* 1. Semantic: `<branch>-<YYYY-MM-DD>-<mode>-<n>`
|
|
183
|
+
* Returns `{ format: 'semantic', branch, date, mode, n, raw }`.
|
|
184
|
+
*
|
|
185
|
+
* 2. UUID-v4: `xxxxxxxx-xxxx-4xxx-[89ab]xxx-xxxxxxxxxxxx`
|
|
186
|
+
* Returns `{ format: 'uuid', uuid, raw }`.
|
|
187
|
+
*
|
|
188
|
+
* Returns `null` for any input that is not a non-empty string or does not
|
|
189
|
+
* match either known format. Never throws.
|
|
190
|
+
*
|
|
191
|
+
* PRD §3 P2 row 3: "Given an existing STATE.md with UUID-v4 session-id
|
|
192
|
+
* (pre-P2 vintage), when any post-P2 code reads STATE.md frontmatter, then
|
|
193
|
+
* both formats are accepted."
|
|
194
|
+
*
|
|
195
|
+
* @param {unknown} id - The session ID to parse.
|
|
196
|
+
* @returns {{ format: 'semantic', branch: string, date: string, mode: string, n: number, raw: string }
|
|
197
|
+
* | { format: 'uuid', uuid: string, raw: string }
|
|
198
|
+
* | null}
|
|
199
|
+
*/
|
|
200
|
+
export function parseSessionId(id) {
|
|
201
|
+
if (typeof id !== 'string' || id.length === 0) return null;
|
|
202
|
+
|
|
203
|
+
// Try semantic format first.
|
|
204
|
+
const m = id.match(SEMANTIC_ID_RE);
|
|
205
|
+
if (m) {
|
|
206
|
+
return {
|
|
207
|
+
format: 'semantic',
|
|
208
|
+
branch: m[1],
|
|
209
|
+
date: m[2],
|
|
210
|
+
mode: m[3],
|
|
211
|
+
n: parseInt(m[4], 10),
|
|
212
|
+
raw: id,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
// Try UUID-v4.
|
|
217
|
+
if (UUID_V4_RE.test(id)) {
|
|
218
|
+
return { format: 'uuid', uuid: id, raw: id };
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Generate the next semantic session ID for this (branch, date, mode) tuple.
|
|
226
|
+
*
|
|
227
|
+
* The generated ID matches the regex:
|
|
228
|
+
* `^[a-z0-9._/-]+-\d{4}-\d{2}-\d{2}-[a-z-]+-\d+$`
|
|
229
|
+
*
|
|
230
|
+
* Counter semantics (PRD §3.A P2 Ubiquitous + #585 history-aware extension):
|
|
231
|
+
* n = max(existing n values for same branch+date+mode across THREE sources) + 1
|
|
232
|
+
* When no matching sessions exist anywhere → n = 1.
|
|
233
|
+
* Gaps are never filled: if existing n = [1, 3] the next is 4, not 2.
|
|
234
|
+
*
|
|
235
|
+
* Sources consulted (all merged into a single candidate set):
|
|
236
|
+
* A. opts.activeSessions — live sessions (lockfiles + host-wide registry).
|
|
237
|
+
* B. sessions.jsonl — closed-session history (opt-out via opts.history.consultHistory=false).
|
|
238
|
+
* C. STATE.md `session:` — last-resort survivor (opt-out via opts.history.consultStateMd=false).
|
|
239
|
+
*
|
|
240
|
+
* Defaults for B and C are ON — historically only source A was consulted,
|
|
241
|
+
* which caused n to reset to 1 once the previous session deregistered itself
|
|
242
|
+
* (root-cause of duplicate-ID incidents documented in #585).
|
|
243
|
+
*
|
|
244
|
+
* Concurrency safety (PSA-005):
|
|
245
|
+
* All three reads and the n-claim are wrapped in `withStateMdLock` so two
|
|
246
|
+
* concurrent preambles in parallel worktrees observe a consistent view and
|
|
247
|
+
* cannot assign the same n.
|
|
248
|
+
*
|
|
249
|
+
* UUID-v4 entries (in any source) are silently dropped (parseSessionId returns
|
|
250
|
+
* format:'uuid' which the filter excludes). Malformed semantic-looking IDs are
|
|
251
|
+
* also dropped (SEMANTIC_ID_RE rejects them).
|
|
252
|
+
*
|
|
253
|
+
* @param {object} opts
|
|
254
|
+
* @param {string} opts.branch - Current git branch (e.g. "main", "feature/foo").
|
|
255
|
+
* Must match `/^[a-zA-Z0-9._/-]+$/`. Required.
|
|
256
|
+
* @param {string} opts.mode - Session type (e.g. "deep", "feature", "housekeeping").
|
|
257
|
+
* Must match `/^[a-z-]+$/`. Required.
|
|
258
|
+
* @param {Array<{sessionId: string}>} [opts.activeSessions=[]] - Active sessions
|
|
259
|
+
* array from session-discovery. Each element must have a `.sessionId` string.
|
|
260
|
+
* Defaults to an empty array when omitted or undefined.
|
|
261
|
+
* @param {string} [opts.repoRoot] - Absolute path to the repo root. Used by
|
|
262
|
+
* `withStateMdLock`, the sessions.jsonl reader, and the STATE.md reader.
|
|
263
|
+
* Defaults to `process.cwd()` when omitted.
|
|
264
|
+
* @param {object} [opts.history] - Opt-out + DI controls for the history-aware
|
|
265
|
+
* sources introduced in #585. All fields optional.
|
|
266
|
+
* @param {boolean} [opts.history.consultHistory=true] - When false, the
|
|
267
|
+
* sessions.jsonl reader is skipped entirely (legacy-only behaviour).
|
|
268
|
+
* @param {boolean} [opts.history.consultStateMd=true] - When false, the
|
|
269
|
+
* STATE.md `session:` reader is skipped entirely (legacy-only behaviour).
|
|
270
|
+
* @param {(repoRoot: string) => Promise<string[]>} [opts.history.readHistoryImpl]
|
|
271
|
+
* Test/DI override for the sessions.jsonl reader. Signature must mirror the
|
|
272
|
+
* internal helper: returns an array of session_id strings (no throws).
|
|
273
|
+
* @param {(repoRoot: string) => Promise<string|null>} [opts.history.readStateMdSessionImpl]
|
|
274
|
+
* Test/DI override for the STATE.md reader. Signature must mirror the
|
|
275
|
+
* internal helper: returns a session_id string or null (no throws).
|
|
276
|
+
* @returns {Promise<string>} The next semantic session ID, e.g. "main-2026-05-27-deep-2".
|
|
277
|
+
* @throws {TypeError} When `branch` is missing, empty, or contains invalid characters.
|
|
278
|
+
* @throws {TypeError} When `mode` is missing, empty, or contains characters other than
|
|
279
|
+
* lowercase letters and hyphens.
|
|
280
|
+
* @throws {Error} When the STATE.md write-lock cannot be acquired (timeout or fs-error).
|
|
281
|
+
*/
|
|
282
|
+
export async function resolveSemanticSessionId({
|
|
283
|
+
branch,
|
|
284
|
+
mode,
|
|
285
|
+
activeSessions,
|
|
286
|
+
repoRoot,
|
|
287
|
+
history,
|
|
288
|
+
} = {}) {
|
|
289
|
+
// Input validation — validate before acquiring the lock to fail fast.
|
|
290
|
+
if (!isValidBranch(branch)) {
|
|
291
|
+
throw new TypeError(
|
|
292
|
+
`resolveSemanticSessionId: 'branch' must be a non-empty string matching /^[a-zA-Z0-9._/-]+$/, got: ${JSON.stringify(branch)}`,
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
if (!isValidMode(mode)) {
|
|
296
|
+
throw new TypeError(
|
|
297
|
+
`resolveSemanticSessionId: 'mode' must be a non-empty string matching /^[a-z-]+$/, got: ${JSON.stringify(mode)}`,
|
|
298
|
+
);
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
// Normalise the history opts bag. Both flags default ON — see audit §2.3.
|
|
302
|
+
const consultHistory = history?.consultHistory !== false;
|
|
303
|
+
const consultStateMd = history?.consultStateMd !== false;
|
|
304
|
+
const historyImpl = history?.readHistoryImpl ?? readSessionIdsFromHistory;
|
|
305
|
+
const stateMdImpl = history?.readStateMdSessionImpl ?? readSessionIdFromStateMd;
|
|
306
|
+
const effectiveRoot = repoRoot ?? process.cwd();
|
|
307
|
+
|
|
308
|
+
return withStateMdLock(repoRoot, async () => {
|
|
309
|
+
// Derive the current UTC date as YYYY-MM-DD.
|
|
310
|
+
const today = new Date().toISOString().slice(0, 10);
|
|
311
|
+
|
|
312
|
+
// Read the two history-aware sources in parallel. Errors from either source
|
|
313
|
+
// are swallowed (the .catch() guards belt-and-braces; helpers already
|
|
314
|
+
// never throw, but a third-party DI impl might).
|
|
315
|
+
const [historicalIds, stateMdId] = await Promise.all([
|
|
316
|
+
consultHistory ? historyImpl(effectiveRoot).catch(() => []) : Promise.resolve([]),
|
|
317
|
+
consultStateMd ? stateMdImpl(effectiveRoot).catch(() => null) : Promise.resolve(null),
|
|
318
|
+
]);
|
|
319
|
+
|
|
320
|
+
// Build a single candidate stream. Duplicates are fine — Math.max handles them.
|
|
321
|
+
const candidateIds = [
|
|
322
|
+
...(activeSessions ?? []).map((s) => s?.sessionId),
|
|
323
|
+
...historicalIds,
|
|
324
|
+
...(stateMdId !== null ? [stateMdId] : []),
|
|
325
|
+
];
|
|
326
|
+
|
|
327
|
+
// Match against (branch, date, mode) and project to n.
|
|
328
|
+
// UUID entries, unknown-format entries, and non-matching tuples are excluded.
|
|
329
|
+
const matchingNs = candidateIds
|
|
330
|
+
.map((id) => parseSessionId(id))
|
|
331
|
+
.filter(
|
|
332
|
+
(parsed) =>
|
|
333
|
+
parsed !== null &&
|
|
334
|
+
parsed.format === 'semantic' &&
|
|
335
|
+
parsed.branch === branch &&
|
|
336
|
+
parsed.date === today &&
|
|
337
|
+
parsed.mode === mode,
|
|
338
|
+
)
|
|
339
|
+
.map((parsed) => parsed.n);
|
|
340
|
+
|
|
341
|
+
const maxN = matchingNs.length > 0 ? Math.max(...matchingNs) : 0;
|
|
342
|
+
const nextN = maxN + 1;
|
|
343
|
+
|
|
344
|
+
return _formatSemanticId(branch, today, mode, nextN);
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Format a semantic session ID from its components.
|
|
350
|
+
*
|
|
351
|
+
* Exposed for testing only — the leading underscore marks this as an internal
|
|
352
|
+
* helper that production callers should NOT use directly.
|
|
353
|
+
*
|
|
354
|
+
* @param {string} branch - Git branch name.
|
|
355
|
+
* @param {string} date - ISO date string (YYYY-MM-DD).
|
|
356
|
+
* @param {string} mode - Session mode.
|
|
357
|
+
* @param {number} n - Monotonic counter value.
|
|
358
|
+
* @returns {string} The formatted session ID, e.g. "main-2026-05-27-deep-1".
|
|
359
|
+
*/
|
|
360
|
+
export function _formatSemanticId(branch, date, mode, n) {
|
|
361
|
+
return `${branch}-${date}-${mode}-${n}`;
|
|
362
|
+
}
|