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,507 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* memory-proposals/sink.mjs — Promotion + archival sink for AUQ-reviewed proposals.
|
|
3
|
+
*
|
|
4
|
+
* Converts AUQ-approved proposals into learnings.jsonl records and archives
|
|
5
|
+
* rejected proposals to .orchestrator/proposals.rejected.log. Clears
|
|
6
|
+
* proposals.jsonl at end-of-cycle.
|
|
7
|
+
*
|
|
8
|
+
* Responsibilities:
|
|
9
|
+
* - writeApproved: promote approved ProposalRecords → learnings.jsonl
|
|
10
|
+
* - archiveRejected: append rejected ProposalRecords → proposals.rejected.log
|
|
11
|
+
* - clearProposalsJsonl: atomically clear proposals.jsonl (tmp + rename),
|
|
12
|
+
* archiving the pre-clear content to a recovery sidecar first, AND
|
|
13
|
+
* reset every per-wave proposals-summary-*.json sidecar in the same
|
|
14
|
+
* metrics directory (issue #723 B3 — see function docstring for the
|
|
15
|
+
* fleet-wide desync bug this closes).
|
|
16
|
+
* - promoteAndClear: compose writeApproved + clearProposalsJsonl into a
|
|
17
|
+
* single verified-write-then-clear call (issue #828 — see function
|
|
18
|
+
* docstring for the incident class this closes: a caller that ran
|
|
19
|
+
* writeApproved() then clearProposalsJsonl() unconditionally could drain
|
|
20
|
+
* the queue even when writeApproved returned written:0).
|
|
21
|
+
*
|
|
22
|
+
* All three exported functions:
|
|
23
|
+
* - Never throw on individual record errors — collect into errors[] and continue.
|
|
24
|
+
* - Use the canonical two-phase path-safety guard validatePathInsideProject
|
|
25
|
+
* (from ../path-utils.mjs) with canonicalizeRoot:true on every write target.
|
|
26
|
+
* - Use the appendLearning() atomic-append pattern from learnings/io.mjs.
|
|
27
|
+
*
|
|
28
|
+
* writeApproved additionally guards against a caller-mistake class (#797):
|
|
29
|
+
* calling `writeApproved({ proposals: [...] })` instead of
|
|
30
|
+
* `writeApproved({ approved: [...] })` previously returned a silent
|
|
31
|
+
* `{ written: 0, errors: [] }` no-op — indistinguishable from "nothing was
|
|
32
|
+
* approved". A subsequent `clearProposalsJsonl()` then drained the queue
|
|
33
|
+
* anyway, permanently losing the approved proposals. `writeApproved` now
|
|
34
|
+
* throws a `TypeError` when `approved` is `undefined` but the caller passed
|
|
35
|
+
* OTHER (unrecognised) keys — the signature that fingerprints an arg-name
|
|
36
|
+
* typo rather than a legitimate no-op call.
|
|
37
|
+
*
|
|
38
|
+
* Issue: #501 (F2.1 Memory-Proposals); #544 M3 (path-utils canonicalization);
|
|
39
|
+
* #723 B3 (clearProposalsJsonl atomicity + summary-reset fix); #797
|
|
40
|
+
* (writeApproved fail-silent guard + clearProposalsJsonl archive-before-clear);
|
|
41
|
+
* #828 (promoteAndClear — clear the queue ONLY after a verified write).
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
import { appendFile, mkdir, readdir, rm } from 'node:fs/promises';
|
|
45
|
+
import { writeFileSync, renameSync, readFileSync, existsSync } from 'node:fs';
|
|
46
|
+
import path from 'node:path';
|
|
47
|
+
import crypto from 'node:crypto';
|
|
48
|
+
import { appendLearning } from '../learnings/io.mjs';
|
|
49
|
+
import { validatePathInsideProject } from '../path-utils.mjs';
|
|
50
|
+
|
|
51
|
+
// ---------------------------------------------------------------------------
|
|
52
|
+
// Path constants (relative to repoRoot)
|
|
53
|
+
// ---------------------------------------------------------------------------
|
|
54
|
+
|
|
55
|
+
const LEARNINGS_REL = path.join('.orchestrator', 'metrics', 'learnings.jsonl');
|
|
56
|
+
const PROPOSALS_REL = path.join('.orchestrator', 'metrics', 'proposals.jsonl');
|
|
57
|
+
const REJECTED_LOG_REL = path.join('.orchestrator', 'proposals.rejected.log');
|
|
58
|
+
// #797 recovery sidecar: pre-clear proposals.jsonl content is appended here
|
|
59
|
+
// before every truncate, so a downstream clear that follows a botched
|
|
60
|
+
// writeApproved call (or any other pre-clear mistake) is recoverable.
|
|
61
|
+
const PROPOSALS_ARCHIVE_REL = path.join('.orchestrator', 'runtime', 'proposals-archive.jsonl');
|
|
62
|
+
|
|
63
|
+
// ---------------------------------------------------------------------------
|
|
64
|
+
// Internal helpers
|
|
65
|
+
// ---------------------------------------------------------------------------
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Build a learning record from an approved proposal.
|
|
69
|
+
*
|
|
70
|
+
* Adds the 6 required extra fields:
|
|
71
|
+
* schema_version, source_session, expires_at (via appendLearning),
|
|
72
|
+
* scope, occurrences, _provenance
|
|
73
|
+
*
|
|
74
|
+
* Strips proposed_by_agent (audit-only, not persisted in learnings — privacy).
|
|
75
|
+
*
|
|
76
|
+
* NOTE: expires_at derivation is delegated to appendLearning() in io.mjs,
|
|
77
|
+
* which calls deriveExpiresAt(createdAt, type) from learnings/schema.mjs.
|
|
78
|
+
* We do not pre-compute it here to avoid duplicating that logic.
|
|
79
|
+
*
|
|
80
|
+
* NOTE: scope 'project' from the task spec is not a valid VALID_SCOPES value
|
|
81
|
+
* in learnings/schema.mjs (valid: 'local' | 'private' | 'public'). We use
|
|
82
|
+
* 'local' as the correct project-scoped equivalent to avoid a ValidationError.
|
|
83
|
+
*
|
|
84
|
+
* @param {object} proposal - full ProposalRecord
|
|
85
|
+
* @param {string} sessionId - e.g. 'main-2026-05-23-1249-deep'
|
|
86
|
+
* @returns {object} learning record ready for appendLearning()
|
|
87
|
+
*/
|
|
88
|
+
function _proposalToLearning(proposal, sessionId) {
|
|
89
|
+
// Destructure to strip proposed_by_agent (privacy — not persisted)
|
|
90
|
+
const { proposed_by_agent: _strip, ...base } = proposal;
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
...base,
|
|
94
|
+
schema_version: 1,
|
|
95
|
+
source_session: sessionId,
|
|
96
|
+
scope: 'local',
|
|
97
|
+
occurrences: 1,
|
|
98
|
+
_provenance: `agent-proposed@${proposal.wave_id}`,
|
|
99
|
+
// expires_at: intentionally omitted — appendLearning() derives it via
|
|
100
|
+
// deriveExpiresAt(created_at, type) from learnings/schema.mjs
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
// Exports
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Promote AUQ-approved proposals to learnings.jsonl.
|
|
110
|
+
*
|
|
111
|
+
* For each approved proposal:
|
|
112
|
+
* 1. Builds the learning record (adds 6 extra fields, strips proposed_by_agent).
|
|
113
|
+
* 2. Appends to .orchestrator/metrics/learnings.jsonl via appendLearning().
|
|
114
|
+
*
|
|
115
|
+
* Individual record errors are collected into errors[] — the function never
|
|
116
|
+
* throws on a per-record basis.
|
|
117
|
+
*
|
|
118
|
+
* Argument contract (#797 fail-silent guard):
|
|
119
|
+
* - `approved` is an array (possibly empty) → normal path / legitimate no-op.
|
|
120
|
+
* - `approved` is `undefined` and no OTHER (unrecognised) key was passed →
|
|
121
|
+
* legitimate no-op (e.g. an empty-selection AUQ round) → `{written:0, errors:[]}`.
|
|
122
|
+
* - `approved` is `undefined` BUT the caller passed unrecognised key(s)
|
|
123
|
+
* (e.g. `{ proposals: [...] }` instead of `{ approved: [...] }`) → throws
|
|
124
|
+
* `TypeError`, since this is almost certainly an arg-name typo that would
|
|
125
|
+
* otherwise silently no-op and lose the proposals on the next
|
|
126
|
+
* `clearProposalsJsonl()` call.
|
|
127
|
+
* - `approved` is defined but not an array → throws `TypeError` (clear
|
|
128
|
+
* contract violation, not a legitimate call shape).
|
|
129
|
+
*
|
|
130
|
+
* @param {object} opts
|
|
131
|
+
* @param {object[]} [opts.approved] - ProposalRecord[] selected by user via AUQ
|
|
132
|
+
* @param {string} opts.repoRoot - absolute project root path
|
|
133
|
+
* @param {string} opts.sessionId - e.g. 'main-2026-05-23-1249-deep'
|
|
134
|
+
* @returns {Promise<{ written: number, errors: string[] }>}
|
|
135
|
+
* @throws {TypeError} When `approved` is missing alongside unrecognised keys,
|
|
136
|
+
* or when `approved` is present but not an array.
|
|
137
|
+
*/
|
|
138
|
+
export async function writeApproved({ approved, repoRoot, sessionId, ...rest }) {
|
|
139
|
+
const errors = [];
|
|
140
|
+
let written = 0;
|
|
141
|
+
|
|
142
|
+
if (approved === undefined) {
|
|
143
|
+
const unknownKeys = Object.keys(rest);
|
|
144
|
+
if (unknownKeys.length > 0) {
|
|
145
|
+
throw new TypeError(
|
|
146
|
+
`writeApproved: missing "approved" — got unknown key(s): ${unknownKeys.join(', ')}. Did you mean approved:?`
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
return { written: 0, errors: [] };
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!Array.isArray(approved)) {
|
|
153
|
+
throw new TypeError(
|
|
154
|
+
`writeApproved: "approved" must be an array of ProposalRecord objects (got ${typeof approved})`
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (approved.length === 0) {
|
|
159
|
+
return { written: 0, errors: [] };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const learningsResult = validatePathInsideProject(LEARNINGS_REL, repoRoot, { canonicalizeRoot: true });
|
|
163
|
+
if (!learningsResult.ok) {
|
|
164
|
+
return { written: 0, errors: [`path-safety: ${learningsResult.reason} (${LEARNINGS_REL})`] };
|
|
165
|
+
}
|
|
166
|
+
const learningsPath = learningsResult.realPath ?? learningsResult.lexicalPath;
|
|
167
|
+
|
|
168
|
+
for (const proposal of approved) {
|
|
169
|
+
try {
|
|
170
|
+
const learningRecord = _proposalToLearning(proposal, sessionId);
|
|
171
|
+
await appendLearning(learningsPath, learningRecord);
|
|
172
|
+
written++;
|
|
173
|
+
} catch (err) {
|
|
174
|
+
const id = (proposal && proposal.id) ? proposal.id : '<unknown>';
|
|
175
|
+
errors.push(`proposal ${id}: ${err.message}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return { written, errors };
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Archive AUQ-rejected proposals to .orchestrator/proposals.rejected.log.
|
|
184
|
+
*
|
|
185
|
+
* For each rejected proposal, appends a JSONL line:
|
|
186
|
+
* { ...originalProposal, _rejected_reason, _rejected_at }
|
|
187
|
+
*
|
|
188
|
+
* Individual record errors are collected into errors[] — the function never
|
|
189
|
+
* throws on a per-record basis.
|
|
190
|
+
*
|
|
191
|
+
* @param {object} opts
|
|
192
|
+
* @param {object[]} opts.rejected - ProposalRecord[] declined by user
|
|
193
|
+
* @param {string} opts.repoRoot - absolute project root path
|
|
194
|
+
* @param {string} opts.reason - 'user-declined' | 'overflow-truncate' | 'manual-skip'
|
|
195
|
+
* @returns {Promise<{ archived: number, errors: string[] }>}
|
|
196
|
+
*/
|
|
197
|
+
export async function archiveRejected({ rejected, repoRoot, reason }) {
|
|
198
|
+
const errors = [];
|
|
199
|
+
let archived = 0;
|
|
200
|
+
|
|
201
|
+
if (!Array.isArray(rejected) || rejected.length === 0) {
|
|
202
|
+
return { archived: 0, errors: [] };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const rejectedResult = validatePathInsideProject(REJECTED_LOG_REL, repoRoot, { canonicalizeRoot: true });
|
|
206
|
+
if (!rejectedResult.ok) {
|
|
207
|
+
return { archived: 0, errors: [`path-safety: ${rejectedResult.reason} (${REJECTED_LOG_REL})`] };
|
|
208
|
+
}
|
|
209
|
+
const rejectedLogPath = rejectedResult.realPath ?? rejectedResult.lexicalPath;
|
|
210
|
+
|
|
211
|
+
// Ensure parent directory exists
|
|
212
|
+
try {
|
|
213
|
+
await mkdir(path.dirname(rejectedLogPath), { recursive: true });
|
|
214
|
+
} catch (err) {
|
|
215
|
+
return { archived: 0, errors: [`mkdir failed: ${err.message}`] };
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
const rejectedAt = new Date().toISOString();
|
|
219
|
+
|
|
220
|
+
for (const proposal of rejected) {
|
|
221
|
+
try {
|
|
222
|
+
const archiveRecord = {
|
|
223
|
+
...proposal,
|
|
224
|
+
_rejected_reason: reason,
|
|
225
|
+
_rejected_at: rejectedAt,
|
|
226
|
+
};
|
|
227
|
+
const line = JSON.stringify(archiveRecord) + '\n';
|
|
228
|
+
await appendFile(rejectedLogPath, line, 'utf8');
|
|
229
|
+
archived++;
|
|
230
|
+
} catch (err) {
|
|
231
|
+
const id = (proposal && proposal.id) ? proposal.id : '<unknown>';
|
|
232
|
+
errors.push(`proposal ${id}: ${err.message}`);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return { archived, errors };
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* Atomically clear proposals.jsonl AND reset every per-wave summary sidecar
|
|
241
|
+
* (end-of-cycle clear).
|
|
242
|
+
*
|
|
243
|
+
* Root cause this fixes (#723 B3, reproduced fleet-wide 3x): the previous
|
|
244
|
+
* implementation truncated ONLY proposals.jsonl via a direct `writeFileSync`
|
|
245
|
+
* — it never reset the per-wave `proposals-summary-<wave-id>.json` sidecars
|
|
246
|
+
* written by `store.mjs` `incrementSummary()`. `collector.mjs`
|
|
247
|
+
* `accumulateSummaryStats()` reads `stats.queued` exclusively from those
|
|
248
|
+
* summary files, and its short-circuit (return zero stats when
|
|
249
|
+
* proposals.jsonl does not exist) never engages after a clear because the
|
|
250
|
+
* clear leaves a 0-byte-but-EXISTING file behind. Net effect: every
|
|
251
|
+
* session-end after the first non-empty cycle reported a stale
|
|
252
|
+
* `queued > 0` count against a genuinely empty (0-byte) proposals.jsonl.
|
|
253
|
+
*
|
|
254
|
+
* Three-step fix:
|
|
255
|
+
* 0. (#797) Archive the pre-clear content of proposals.jsonl (if non-empty)
|
|
256
|
+
* by appending it verbatim to `.orchestrator/runtime/proposals-archive.jsonl`
|
|
257
|
+
* BEFORE truncating. This is the recovery path for the drain-after-
|
|
258
|
+
* fail-silent-write class of bug: `clearProposalsJsonl()` unconditionally
|
|
259
|
+
* drains the queue regardless of whether the preceding `writeApproved()`
|
|
260
|
+
* call actually wrote anything, so a caller-side mistake (wrong arg name,
|
|
261
|
+
* partial write failure) no longer means the queued proposals are gone
|
|
262
|
+
* for good — they are recoverable from the archive sidecar. Best-effort:
|
|
263
|
+
* an archive failure never blocks the clear itself, and an empty/missing
|
|
264
|
+
* proposals.jsonl produces no archive append (nothing to preserve).
|
|
265
|
+
* 1. Clear proposals.jsonl via tmp-file + rename (POSIX-atomic on the same
|
|
266
|
+
* filesystem) instead of an in-place `writeFileSync` truncate — this also
|
|
267
|
+
* brings the implementation in line with the documented contract in
|
|
268
|
+
* `agents/memory-proposal-collector.md` ("atomic clear ... write empty
|
|
269
|
+
* content to a tmp file, then rename over the target"), so a concurrent
|
|
270
|
+
* reader never observes a partially-truncated file.
|
|
271
|
+
* 2. Remove every `proposals-summary-*.json` sidecar in the same metrics
|
|
272
|
+
* directory so the NEXT `collectProposals()` call starts from zero
|
|
273
|
+
* instead of re-summing a prior cycle's counters.
|
|
274
|
+
*
|
|
275
|
+
* Contract (skills/session-end/SKILL.md § 3.6 failure modes): this function
|
|
276
|
+
* must NEVER throw. Step 1 failures short-circuit with
|
|
277
|
+
* `{ cleared: false, summariesCleared: 0 }`. Step 2 is best-effort per file —
|
|
278
|
+
* a single summary file that fails to delete is skipped (not fatal) so a
|
|
279
|
+
* transient fs error on one sidecar cannot block the whole clear.
|
|
280
|
+
*
|
|
281
|
+
* Creates the file (with empty content) if it does not yet exist.
|
|
282
|
+
*
|
|
283
|
+
* @param {object} opts
|
|
284
|
+
* @param {string} opts.repoRoot - absolute project root path
|
|
285
|
+
* @returns {Promise<{ cleared: boolean, summariesCleared: number }>}
|
|
286
|
+
*/
|
|
287
|
+
export async function clearProposalsJsonl({ repoRoot }) {
|
|
288
|
+
const proposalsResult = validatePathInsideProject(PROPOSALS_REL, repoRoot, { canonicalizeRoot: true });
|
|
289
|
+
if (!proposalsResult.ok) {
|
|
290
|
+
return { cleared: false, summariesCleared: 0 };
|
|
291
|
+
}
|
|
292
|
+
const proposalsPath = proposalsResult.realPath ?? proposalsResult.lexicalPath;
|
|
293
|
+
const metricsDirPath = path.dirname(proposalsPath);
|
|
294
|
+
|
|
295
|
+
// Step 0 (#797): archive pre-clear content before truncating. Best-effort
|
|
296
|
+
// — never lets an archive-side failure block the clear (same never-throw
|
|
297
|
+
// contract as the rest of this function).
|
|
298
|
+
if (existsSync(proposalsPath)) {
|
|
299
|
+
try {
|
|
300
|
+
const preClearContent = readFileSync(proposalsPath, 'utf8');
|
|
301
|
+
if (preClearContent.length > 0) {
|
|
302
|
+
const archiveResult = validatePathInsideProject(PROPOSALS_ARCHIVE_REL, repoRoot, { canonicalizeRoot: true });
|
|
303
|
+
if (archiveResult.ok) {
|
|
304
|
+
const archivePath = archiveResult.realPath ?? archiveResult.lexicalPath;
|
|
305
|
+
await mkdir(path.dirname(archivePath), { recursive: true });
|
|
306
|
+
const normalized = preClearContent.endsWith('\n') ? preClearContent : `${preClearContent}\n`;
|
|
307
|
+
await appendFile(archivePath, normalized, 'utf8');
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
} catch {
|
|
311
|
+
// Best-effort — a read/append failure on the archive sidecar must not
|
|
312
|
+
// block the clear itself (function-level never-throw contract).
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
try {
|
|
317
|
+
// Ensure parent directory exists before clear/create.
|
|
318
|
+
await mkdir(metricsDirPath, { recursive: true });
|
|
319
|
+
// Atomic clear: write empty content to a tmp file, then rename over the
|
|
320
|
+
// target — a concurrent hook reading proposals.jsonl mid-clear observes
|
|
321
|
+
// either the pre-clear content or the fully-cleared file, never a
|
|
322
|
+
// partial truncate.
|
|
323
|
+
const tmpSuffix = crypto.randomBytes(6).toString('hex');
|
|
324
|
+
const tmpFile = path.join(metricsDirPath, `.proposals.jsonl.tmp.${tmpSuffix}`);
|
|
325
|
+
writeFileSync(tmpFile, '', 'utf8');
|
|
326
|
+
renameSync(tmpFile, proposalsPath);
|
|
327
|
+
} catch {
|
|
328
|
+
return { cleared: false, summariesCleared: 0 };
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Reset per-wave summaries so a subsequent collectProposals() does not
|
|
332
|
+
// re-report a prior cycle's queued/dropped/below_floor counts against the
|
|
333
|
+
// now-empty proposals.jsonl (the #723 B3 root cause described above).
|
|
334
|
+
let summariesCleared = 0;
|
|
335
|
+
try {
|
|
336
|
+
const names = await readdir(metricsDirPath);
|
|
337
|
+
const summaryFiles = names.filter(
|
|
338
|
+
(n) => n.startsWith('proposals-summary-') && n.endsWith('.json')
|
|
339
|
+
);
|
|
340
|
+
for (const filename of summaryFiles) {
|
|
341
|
+
try {
|
|
342
|
+
await rm(path.join(metricsDirPath, filename), { force: true });
|
|
343
|
+
summariesCleared++;
|
|
344
|
+
} catch {
|
|
345
|
+
// Best-effort per file — a single sidecar failing to delete must not
|
|
346
|
+
// fail the overall clear (function-level never-throw contract).
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
} catch {
|
|
350
|
+
// Metrics dir became unreadable between mkdir and readdir (e.g. removed
|
|
351
|
+
// concurrently) — non-fatal, the JSONL clear above already succeeded.
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
return { cleared: true, summariesCleared };
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Promote approved proposals AND clear the queue — but ONLY when the write
|
|
359
|
+
* is verified complete. Composes writeApproved() + clearProposalsJsonl()
|
|
360
|
+
* behind a single mechanical guard so a caller can no longer reproduce the
|
|
361
|
+
* incident class this closes (#828, 2nd occurrence 2026-07-18): a caller
|
|
362
|
+
* ran writeApproved() (which returned `written: 0` — e.g. every record
|
|
363
|
+
* failed round-trip validation because `sessionId` was missing/wrong) and
|
|
364
|
+
* then ran clearProposalsJsonl() anyway, unconditionally draining the queue
|
|
365
|
+
* even though nothing had actually been promoted.
|
|
366
|
+
*
|
|
367
|
+
* writeApproved() and clearProposalsJsonl() themselves are untouched by this
|
|
368
|
+
* function — both keep their existing signatures and never-throw-per-record
|
|
369
|
+
* contracts. promoteAndClear() is a NEW, additive orchestration layer; it
|
|
370
|
+
* does not replace direct calls to either lower-level function for callers
|
|
371
|
+
* that need finer-grained control (e.g. archiveRejected() must still run as
|
|
372
|
+
* a separate, caller-driven step between writeApproved() and this call — see
|
|
373
|
+
* below).
|
|
374
|
+
*
|
|
375
|
+
* Guard (mechanical, not semantic): the clear proceeds if and only if
|
|
376
|
+
* `writeApproved()` wrote exactly as many records as were requested
|
|
377
|
+
* (`written === expected`) AND reported zero per-record errors. This is a
|
|
378
|
+
* purely mechanical count/error check — it does NOT distinguish "operator
|
|
379
|
+
* approved nothing this cycle" from "operator approved everything and it all
|
|
380
|
+
* wrote cleanly". Both are `expected === 0` or `written === expected` with no
|
|
381
|
+
* errors, and BOTH legitimately clear the queue. The only case the guard
|
|
382
|
+
* blocks is a MISMATCH — some or all approved records failed to write — which
|
|
383
|
+
* is precisely the #828 incident class.
|
|
384
|
+
*
|
|
385
|
+
* `approved: []` (or `approved` omitted) is a legitimate clear-with-nothing-
|
|
386
|
+
* approved call: `expected` computes to 0, `writeApproved()` returns
|
|
387
|
+
* `{written: 0, errors: []}` without touching the filesystem, the guard
|
|
388
|
+
* evaluates `0 === 0 && no errors` → true, and the clear proceeds. This is
|
|
389
|
+
* intentional — a cycle where the operator approved nothing must still be
|
|
390
|
+
* able to drain a queue of now-rejected/stale proposals once the caller has
|
|
391
|
+
* separately archived them via archiveRejected().
|
|
392
|
+
*
|
|
393
|
+
* archiveRejected() is INTENTIONALLY NOT folded into this function — it
|
|
394
|
+
* operates on a disjoint subset of proposals (the rejected ones) and has no
|
|
395
|
+
* bearing on whether the write of the APPROVED subset succeeded. Callers
|
|
396
|
+
* should sequence: writeApproved-relevant work → archiveRejected(rejected) →
|
|
397
|
+
* promoteAndClear(approved) (or call promoteAndClear() first, then
|
|
398
|
+
* archiveRejected() — order between the two does not matter, since
|
|
399
|
+
* archiveRejected() never reads or clears proposals.jsonl itself).
|
|
400
|
+
*
|
|
401
|
+
* Own argument-typo guard (own-level #797 mirror; fixes a reintroduction
|
|
402
|
+
* found by session-reviewer): the original implementation destructured only
|
|
403
|
+
* its three known keys and forwarded a FRESH `{ approved, repoRoot,
|
|
404
|
+
* sessionId }` literal to writeApproved() — so a caller typo like
|
|
405
|
+
* `promoteAndClear({ proposals: [...], sessionId, repoRoot })` silently
|
|
406
|
+
* dropped the unrecognised `proposals` key. writeApproved() then received
|
|
407
|
+
* `approved: undefined` with NO unknown keys (the fresh literal has none),
|
|
408
|
+
* so writeApproved()'s OWN #797 rest-based typo guard could never fire —
|
|
409
|
+
* it took the legitimate-no-op branch and returned `{written: 0, errors:
|
|
410
|
+
* []}`, `expected` computed to 0, the mechanical guard evaluated
|
|
411
|
+
* `0 === 0 && no errors` → true, and the queue was drained with
|
|
412
|
+
* `cleared: true, skippedReason: null`. That is the exact #828 incident
|
|
413
|
+
* class, reintroduced one layer up. Fix: promoteAndClear() now captures
|
|
414
|
+
* `...rest` itself and throws its OWN `TypeError` when `approved ===
|
|
415
|
+
* undefined` alongside unrecognised key(s) — before ever calling
|
|
416
|
+
* writeApproved() — using the same detection shape and message wording as
|
|
417
|
+
* writeApproved()'s guard (lines ~136-144 above). An explicit local guard
|
|
418
|
+
* was chosen over forwarding `...rest` into writeApproved() so the typo is
|
|
419
|
+
* caught (and named) at the layer the caller actually invoked, rather than
|
|
420
|
+
* relying on an internal delegate to surface it.
|
|
421
|
+
*
|
|
422
|
+
* @param {object} opts
|
|
423
|
+
* @param {object[]} [opts.approved] - ProposalRecord[] selected by user via
|
|
424
|
+
* AUQ. Same contract as writeApproved()'s `approved` param:
|
|
425
|
+
* omitted/`undefined` WITH NO other (unrecognised) keys present is a
|
|
426
|
+
* legitimate no-op batch (computes `expected: 0`, clear proceeds — mirrors
|
|
427
|
+
* writeApproved()'s own no-op semantics exactly, see the guard above);
|
|
428
|
+
* omitted/`undefined` WITH unrecognised key(s) present throws `TypeError`
|
|
429
|
+
* (arg-name-typo detection, see above); present-but-not-an-array is a
|
|
430
|
+
* caller-mistake and is NOT pre-checked here — it is left to propagate as
|
|
431
|
+
* the `TypeError` writeApproved() already throws for that shape (avoids
|
|
432
|
+
* duplicating that validation in two places).
|
|
433
|
+
* @param {string} opts.sessionId - e.g. 'main-2026-05-23-1249-deep'. MUST be
|
|
434
|
+
* a non-empty string — this function throws `TypeError` immediately
|
|
435
|
+
* (before calling writeApproved()) when it is not, since a missing/blank
|
|
436
|
+
* sessionId is a caller bug (every record would fail writeApproved's
|
|
437
|
+
* round-trip validation with "missing required field: source_session",
|
|
438
|
+
* guaranteeing the write-verification guard below blocks the clear anyway
|
|
439
|
+
* — this throws earlier and louder rather than silently skipping to a
|
|
440
|
+
* `skippedReason`).
|
|
441
|
+
* @param {string} opts.repoRoot - absolute project root path
|
|
442
|
+
* @returns {Promise<{
|
|
443
|
+
* written: number,
|
|
444
|
+
* expected: number,
|
|
445
|
+
* errors: string[],
|
|
446
|
+
* cleared: boolean,
|
|
447
|
+
* summariesCleared: number,
|
|
448
|
+
* skippedReason: string|null,
|
|
449
|
+
* }>}
|
|
450
|
+
* @throws {TypeError} When `approved` is missing alongside unrecognised
|
|
451
|
+
* key(s) (own-level arg-typo guard), when `sessionId` is not a non-empty
|
|
452
|
+
* string, or (propagated) when `approved` is present but not an array.
|
|
453
|
+
*/
|
|
454
|
+
export async function promoteAndClear({ approved, sessionId, repoRoot, ...rest }) {
|
|
455
|
+
if (approved === undefined) {
|
|
456
|
+
const unknownKeys = Object.keys(rest);
|
|
457
|
+
if (unknownKeys.length > 0) {
|
|
458
|
+
throw new TypeError(
|
|
459
|
+
`promoteAndClear: missing "approved" — got unknown key(s): ${unknownKeys.join(', ')}. Did you mean approved:?`
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (typeof sessionId !== 'string' || sessionId.trim().length === 0) {
|
|
465
|
+
throw new TypeError(
|
|
466
|
+
`promoteAndClear: "sessionId" must be a non-empty string (got ${typeof sessionId})`
|
|
467
|
+
);
|
|
468
|
+
}
|
|
469
|
+
|
|
470
|
+
// `expected` mirrors writeApproved()'s own no-op semantics: a non-array
|
|
471
|
+
// `approved` (including `undefined`, already guarded above against the
|
|
472
|
+
// arg-typo shape) computes as 0 here. When `approved` is present but
|
|
473
|
+
// genuinely not an array, writeApproved() below throws its own TypeError
|
|
474
|
+
// before this value is ever consulted by the guard.
|
|
475
|
+
const expected = Array.isArray(approved) ? approved.length : 0;
|
|
476
|
+
|
|
477
|
+
const w = await writeApproved({ approved, repoRoot, sessionId });
|
|
478
|
+
|
|
479
|
+
const ok = w.written === expected && w.errors.length === 0;
|
|
480
|
+
|
|
481
|
+
if (!ok) {
|
|
482
|
+
const skippedReason =
|
|
483
|
+
w.errors.length > 0
|
|
484
|
+
? 'write-errors'
|
|
485
|
+
: `partial-write: ${w.written}/${expected} written`;
|
|
486
|
+
// Clear is SKIPPED entirely — proposals.jsonl (and its summary sidecars)
|
|
487
|
+
// remain untouched on this path. This is the mechanical fix for #828:
|
|
488
|
+
// the queue is never drained on an unverified/incomplete write.
|
|
489
|
+
return {
|
|
490
|
+
...w,
|
|
491
|
+
expected,
|
|
492
|
+
cleared: false,
|
|
493
|
+
summariesCleared: 0,
|
|
494
|
+
skippedReason,
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
const c = await clearProposalsJsonl({ repoRoot });
|
|
499
|
+
|
|
500
|
+
return {
|
|
501
|
+
...w,
|
|
502
|
+
expected,
|
|
503
|
+
cleared: c.cleared,
|
|
504
|
+
summariesCleared: c.summariesCleared,
|
|
505
|
+
skippedReason: null,
|
|
506
|
+
};
|
|
507
|
+
}
|