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,571 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* owner-yaml.mjs — owner.yaml schema, validator, parser, writer (Issue #161, D1).
|
|
3
|
+
*
|
|
4
|
+
* Implements the public Owner Persona Layer API: schema validation, disk I/O,
|
|
5
|
+
* and sensible defaults. Intentionally separate from `owner-config.mjs` (which
|
|
6
|
+
* ships the richer D2-era schema with schema-version, metadata, and extended
|
|
7
|
+
* fields). This module targets the simpler D1 surface defined in the #161 epic.
|
|
8
|
+
*
|
|
9
|
+
* ── Schema (schema-version: 1) ───────────────────────────────────────────────
|
|
10
|
+
*
|
|
11
|
+
* owner:
|
|
12
|
+
* name: string (required, non-empty)
|
|
13
|
+
* language: 'de' | 'en' (required)
|
|
14
|
+
* tone:
|
|
15
|
+
* style: 'direct' | 'neutral' | 'friendly' (required)
|
|
16
|
+
* tonality: string (optional, free text)
|
|
17
|
+
* efficiency:
|
|
18
|
+
* output-level: 'lite' | 'full' | 'ultra' (required)
|
|
19
|
+
* preamble: 'minimal' | 'verbose' (required)
|
|
20
|
+
* hardware-sharing:
|
|
21
|
+
* enabled: boolean (required)
|
|
22
|
+
* hash-salt: string (required when enabled=true)
|
|
23
|
+
* paths: (optional; host-local path overrides — #653)
|
|
24
|
+
* vault-dir: string ('' = no override; mirrors Session Config vault-integration.vault-dir)
|
|
25
|
+
* baseline-path: string ('' = no override; maps to Session Config plan-baseline-path / projects-baseline)
|
|
26
|
+
* namespace-map-path: string ('' = no override; #725 D5 — points at a host-local JSON
|
|
27
|
+
* { "real-slug": "pseudonym-slug" } map that vault-mirror uses to
|
|
28
|
+
* give owner-leaky repos stable pseudonyms instead of collapsing
|
|
29
|
+
* them all to 'redacted-repo'. Never committed; env SO_NAMESPACE_MAP overrides.)
|
|
30
|
+
* confidential-names-file: string ('' = no override; #728a — points at a host-local FLAT JSON
|
|
31
|
+
* array-of-strings ["customer-name", "private-repo", …] the
|
|
32
|
+
* owner-leakage scanner's CP11 rule matches against tracked files.
|
|
33
|
+
* The names live in the referenced file ONLY — NEVER inline in
|
|
34
|
+
* owner.yaml. Never committed; env SO_CONFIDENTIAL_NAMES_FILE overrides.)
|
|
35
|
+
* dispatcher: (optional; host-local cross-repo dispatcher autonomy override — #679)
|
|
36
|
+
* autonomy: string ('' = no override; resolver enum off | advisory | autonomous-gated;
|
|
37
|
+
* precedence env SO_DISPATCHER_AUTONOMY > this > committed > off)
|
|
38
|
+
*
|
|
39
|
+
* ── Exports ───────────────────────────────────────────────────────────────────
|
|
40
|
+
*
|
|
41
|
+
* OWNER_YAML_PATH — default file path on disk
|
|
42
|
+
* validateOwnerSections(obj) — pure validation, no I/O; bucketed per section (#820)
|
|
43
|
+
* validateOwnerConfig(obj) — pure validation, no I/O; thin wrapper over validateOwnerSections
|
|
44
|
+
* loadOwnerConfig({path?}) — reads file; per-section tolerance for OPTIONAL
|
|
45
|
+
* sections (paths/dispatcher/vaults/baselines) — #820
|
|
46
|
+
* writeOwnerConfig(config, {path?}) — validates, writes YAML, creates dir
|
|
47
|
+
* getDefaults() — returns sensible default config object
|
|
48
|
+
*/
|
|
49
|
+
|
|
50
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
|
|
51
|
+
import { join, dirname } from 'node:path';
|
|
52
|
+
import { homedir } from 'node:os';
|
|
53
|
+
import yaml from 'js-yaml';
|
|
54
|
+
|
|
55
|
+
// ---------------------------------------------------------------------------
|
|
56
|
+
// Constants
|
|
57
|
+
// ---------------------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
/** Default path for the owner persona config file. */
|
|
60
|
+
export const OWNER_YAML_PATH = join(homedir(), '.config', 'session-orchestrator', 'owner.yaml');
|
|
61
|
+
|
|
62
|
+
const VALID_LANGUAGES = /** @type {const} */ (['de', 'en']);
|
|
63
|
+
const VALID_TONE_STYLES = /** @type {const} */ (['direct', 'neutral', 'friendly']);
|
|
64
|
+
const VALID_OUTPUT_LEVELS = /** @type {const} */ (['lite', 'full', 'ultra']);
|
|
65
|
+
const VALID_PREAMBLE_LEVELS = /** @type {const} */ (['minimal', 'verbose']);
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* REQUIRED sections (#820) — an invalid entry here keeps the legacy
|
|
69
|
+
* whole-file-discard behaviour of `loadOwnerConfig` unchanged.
|
|
70
|
+
*/
|
|
71
|
+
const REQUIRED_SECTIONS = /** @type {const} */ (['owner', 'tone', 'efficiency', 'hardware-sharing']);
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* OPTIONAL object sections (#820) — a malformed entry is replaced by its
|
|
75
|
+
* `getDefaults()` value and reported via `droppedSections` + a stderr WARN,
|
|
76
|
+
* but does NOT discard the rest of the file.
|
|
77
|
+
*/
|
|
78
|
+
const OPTIONAL_OBJECT_SECTIONS = /** @type {const} */ (['paths', 'dispatcher']);
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* OPTIONAL list sections (#820) — malformed entries are passed through
|
|
82
|
+
* UNTOUCHED (their consumers run a lenient parse-at-point-of-use pass —
|
|
83
|
+
* `parseNamedVaults`/`parseBaselines` — that already drops bad entries with
|
|
84
|
+
* its own WARN). Surfaced here only via `sectionWarnings`, never dropped.
|
|
85
|
+
*/
|
|
86
|
+
const OPTIONAL_LIST_SECTIONS = /** @type {const} */ (['vaults', 'baselines']);
|
|
87
|
+
|
|
88
|
+
/** Canonical validation-order — MUST match the pre-#820 inline validation order. */
|
|
89
|
+
const SECTION_ORDER = /** @type {const} */ ([
|
|
90
|
+
...REQUIRED_SECTIONS,
|
|
91
|
+
...OPTIONAL_OBJECT_SECTIONS,
|
|
92
|
+
...OPTIONAL_LIST_SECTIONS,
|
|
93
|
+
]);
|
|
94
|
+
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// Helpers
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
function isPlainObject(v) {
|
|
100
|
+
return v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Defaults
|
|
105
|
+
// ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Returns the sensible default owner config. `owner.name` is left as an empty
|
|
109
|
+
* string — callers (e.g. D2 bootstrap) must fill it in from user input.
|
|
110
|
+
*
|
|
111
|
+
* @returns {object}
|
|
112
|
+
*/
|
|
113
|
+
export function getDefaults() {
|
|
114
|
+
return {
|
|
115
|
+
owner: {
|
|
116
|
+
name: '',
|
|
117
|
+
language: 'en',
|
|
118
|
+
},
|
|
119
|
+
tone: {
|
|
120
|
+
style: 'neutral',
|
|
121
|
+
tonality: '',
|
|
122
|
+
},
|
|
123
|
+
efficiency: {
|
|
124
|
+
'output-level': 'full',
|
|
125
|
+
preamble: 'minimal',
|
|
126
|
+
},
|
|
127
|
+
'hardware-sharing': {
|
|
128
|
+
enabled: false,
|
|
129
|
+
'hash-salt': '',
|
|
130
|
+
},
|
|
131
|
+
paths: {
|
|
132
|
+
'vault-dir': '',
|
|
133
|
+
'baseline-path': '',
|
|
134
|
+
'namespace-map-path': '',
|
|
135
|
+
'confidential-names-file': '',
|
|
136
|
+
},
|
|
137
|
+
dispatcher: {
|
|
138
|
+
autonomy: '',
|
|
139
|
+
},
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ---------------------------------------------------------------------------
|
|
144
|
+
// Validation
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Validate a raw owner config object, bucketed per top-level section (#820).
|
|
149
|
+
* Pure function — never throws, never reads or writes any file. Reuses the
|
|
150
|
+
* EXACT validation rules `validateOwnerConfig` has always applied; only the
|
|
151
|
+
* bucketing is new, so error message text and (within a section) ordering
|
|
152
|
+
* are unchanged.
|
|
153
|
+
*
|
|
154
|
+
* @param {unknown} obj
|
|
155
|
+
* @returns {{ sections: Record<string, { valid: boolean, errors: string[] }>, errors: string[] }}
|
|
156
|
+
*/
|
|
157
|
+
export function validateOwnerSections(obj) {
|
|
158
|
+
if (!isPlainObject(obj)) {
|
|
159
|
+
return { sections: {}, errors: ['config must be a plain object'] };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const sections = {};
|
|
163
|
+
|
|
164
|
+
// ── owner ──────────────────────────────────────────────────────────────────
|
|
165
|
+
{
|
|
166
|
+
const errors = [];
|
|
167
|
+
const owner = obj.owner;
|
|
168
|
+
if (!isPlainObject(owner)) {
|
|
169
|
+
errors.push('owner must be an object');
|
|
170
|
+
} else {
|
|
171
|
+
if (typeof owner.name !== 'string' || owner.name.trim().length === 0) {
|
|
172
|
+
errors.push('owner.name is required and must be a non-empty string');
|
|
173
|
+
}
|
|
174
|
+
if (!VALID_LANGUAGES.includes(owner.language)) {
|
|
175
|
+
errors.push(
|
|
176
|
+
`owner.language must be one of ${VALID_LANGUAGES.join(', ')}, got: ${JSON.stringify(owner.language)}`,
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
sections.owner = { valid: errors.length === 0, errors };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ── tone ───────────────────────────────────────────────────────────────────
|
|
184
|
+
{
|
|
185
|
+
const errors = [];
|
|
186
|
+
const tone = obj.tone;
|
|
187
|
+
if (!isPlainObject(tone)) {
|
|
188
|
+
errors.push('tone must be an object');
|
|
189
|
+
} else {
|
|
190
|
+
if (!VALID_TONE_STYLES.includes(tone.style)) {
|
|
191
|
+
errors.push(
|
|
192
|
+
`tone.style must be one of ${VALID_TONE_STYLES.join(', ')}, got: ${JSON.stringify(tone.style)}`,
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
// tonality is optional; if present must be a string
|
|
196
|
+
if (tone.tonality !== undefined && tone.tonality !== null && typeof tone.tonality !== 'string') {
|
|
197
|
+
errors.push('tone.tonality must be a string or absent');
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
sections.tone = { valid: errors.length === 0, errors };
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// ── efficiency ─────────────────────────────────────────────────────────────
|
|
204
|
+
{
|
|
205
|
+
const errors = [];
|
|
206
|
+
const efficiency = obj.efficiency;
|
|
207
|
+
if (!isPlainObject(efficiency)) {
|
|
208
|
+
errors.push('efficiency must be an object');
|
|
209
|
+
} else {
|
|
210
|
+
if (!VALID_OUTPUT_LEVELS.includes(efficiency['output-level'])) {
|
|
211
|
+
errors.push(
|
|
212
|
+
`efficiency.output-level must be one of ${VALID_OUTPUT_LEVELS.join(', ')}, got: ${JSON.stringify(efficiency['output-level'])}`,
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
if (!VALID_PREAMBLE_LEVELS.includes(efficiency.preamble)) {
|
|
216
|
+
errors.push(
|
|
217
|
+
`efficiency.preamble must be one of ${VALID_PREAMBLE_LEVELS.join(', ')}, got: ${JSON.stringify(efficiency.preamble)}`,
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
sections.efficiency = { valid: errors.length === 0, errors };
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ── hardware-sharing ───────────────────────────────────────────────────────
|
|
225
|
+
{
|
|
226
|
+
const errors = [];
|
|
227
|
+
const hw = obj['hardware-sharing'];
|
|
228
|
+
if (!isPlainObject(hw)) {
|
|
229
|
+
errors.push('hardware-sharing must be an object');
|
|
230
|
+
} else {
|
|
231
|
+
if (typeof hw.enabled !== 'boolean') {
|
|
232
|
+
errors.push(`hardware-sharing.enabled must be a boolean, got: ${typeof hw.enabled}`);
|
|
233
|
+
}
|
|
234
|
+
// hash-salt is required when enabled=true
|
|
235
|
+
if (hw.enabled === true) {
|
|
236
|
+
if (typeof hw['hash-salt'] !== 'string' || hw['hash-salt'].length === 0) {
|
|
237
|
+
errors.push('hardware-sharing.hash-salt is required (non-empty string) when hardware-sharing.enabled is true');
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
// if present and non-empty, must be a string
|
|
241
|
+
if (
|
|
242
|
+
hw['hash-salt'] !== undefined &&
|
|
243
|
+
hw['hash-salt'] !== null &&
|
|
244
|
+
hw['hash-salt'] !== '' &&
|
|
245
|
+
typeof hw['hash-salt'] !== 'string'
|
|
246
|
+
) {
|
|
247
|
+
errors.push('hardware-sharing.hash-salt must be a string');
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
sections['hardware-sharing'] = { valid: errors.length === 0, errors };
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// ── paths (optional; host-local path overrides — #653) ───────────────────────
|
|
254
|
+
{
|
|
255
|
+
const errors = [];
|
|
256
|
+
const paths = obj.paths;
|
|
257
|
+
if (paths !== undefined && paths !== null) {
|
|
258
|
+
if (!isPlainObject(paths)) {
|
|
259
|
+
errors.push('paths must be an object when present');
|
|
260
|
+
} else {
|
|
261
|
+
for (const key of ['vault-dir', 'baseline-path', 'namespace-map-path', 'confidential-names-file']) {
|
|
262
|
+
const v = paths[key];
|
|
263
|
+
if (v !== undefined && v !== null && typeof v !== 'string') {
|
|
264
|
+
errors.push(`paths.${key} must be a string`);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
sections.paths = { valid: errors.length === 0, errors };
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
// ── dispatcher (optional; host-local dispatcher autonomy override — #679) ─────
|
|
273
|
+
// Loose validation only (mirrors `paths`): the resolver enum-validates the
|
|
274
|
+
// value (resolveDispatcherAutonomy in config/dispatcher-autonomy.mjs), so an
|
|
275
|
+
// invalid string here falls through to the next precedence tier rather than
|
|
276
|
+
// failing the whole owner.yaml load.
|
|
277
|
+
{
|
|
278
|
+
const errors = [];
|
|
279
|
+
const dispatcher = obj.dispatcher;
|
|
280
|
+
if (dispatcher !== undefined && dispatcher !== null) {
|
|
281
|
+
if (!isPlainObject(dispatcher)) {
|
|
282
|
+
errors.push('dispatcher must be an object when present');
|
|
283
|
+
} else if (
|
|
284
|
+
dispatcher.autonomy !== undefined &&
|
|
285
|
+
dispatcher.autonomy !== null &&
|
|
286
|
+
typeof dispatcher.autonomy !== 'string'
|
|
287
|
+
) {
|
|
288
|
+
errors.push('dispatcher.autonomy must be a string');
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
sections.dispatcher = { valid: errors.length === 0, errors };
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
// ── vaults (optional; N named vaults for walk-up resolution — #700) ──────────
|
|
295
|
+
// Drop-and-WARN on malformed entries is handled by parseNamedVaults() in
|
|
296
|
+
// named-vault-resolver.mjs. Here we only validate the container shape to
|
|
297
|
+
// prevent clearly invalid config from passing silently.
|
|
298
|
+
// Absent or null → backward-compat no-op (no validation errors).
|
|
299
|
+
{
|
|
300
|
+
const errors = [];
|
|
301
|
+
const vaults = obj.vaults;
|
|
302
|
+
if (vaults !== undefined && vaults !== null) {
|
|
303
|
+
if (!Array.isArray(vaults)) {
|
|
304
|
+
errors.push('vaults must be an array when present');
|
|
305
|
+
} else {
|
|
306
|
+
for (let i = 0; i < vaults.length; i++) {
|
|
307
|
+
const entry = vaults[i];
|
|
308
|
+
if (entry === null || entry === undefined) {
|
|
309
|
+
errors.push(`vaults[${i}] must not be null`);
|
|
310
|
+
continue;
|
|
311
|
+
}
|
|
312
|
+
if (!isPlainObject(entry)) {
|
|
313
|
+
errors.push(`vaults[${i}] must be an object`);
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
// Required string fields: name, suffix, root
|
|
317
|
+
for (const field of ['name', 'suffix', 'root']) {
|
|
318
|
+
if (typeof entry[field] !== 'string' || entry[field].trim() === '') {
|
|
319
|
+
errors.push(`vaults[${i}].${field} must be a non-empty string`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
// Optional match sub-object
|
|
323
|
+
if (entry.match !== undefined && entry.match !== null) {
|
|
324
|
+
if (!isPlainObject(entry.match)) {
|
|
325
|
+
errors.push(`vaults[${i}].match must be an object when present`);
|
|
326
|
+
} else if (
|
|
327
|
+
entry.match['org-prefix'] !== undefined &&
|
|
328
|
+
typeof entry.match['org-prefix'] !== 'string'
|
|
329
|
+
) {
|
|
330
|
+
errors.push(`vaults[${i}].match.org-prefix must be a string`);
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
sections.vaults = { valid: errors.length === 0, errors };
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// ── baselines (optional; N named plan-baselines for per-context resolution — #819) ──
|
|
340
|
+
// Cousin of vaults: above. Drop-and-WARN on malformed entries is handled by
|
|
341
|
+
// parseBaselines() in named-baseline-resolver.mjs; here we validate the container
|
|
342
|
+
// + entry shape to prevent clearly invalid config from passing silently.
|
|
343
|
+
// Divergences from vaults: the match key is `path-prefix` (a local FILESYSTEM
|
|
344
|
+
// directory tree, not a git-remote org slug), `path` replaces root+suffix, and
|
|
345
|
+
// `match` is REQUIRED (a baseline with no path-prefix can never be selected).
|
|
346
|
+
// Absent or null → backward-compat no-op (no validation errors).
|
|
347
|
+
{
|
|
348
|
+
const errors = [];
|
|
349
|
+
const baselines = obj.baselines;
|
|
350
|
+
if (baselines !== undefined && baselines !== null) {
|
|
351
|
+
if (!Array.isArray(baselines)) {
|
|
352
|
+
errors.push('baselines must be an array when present');
|
|
353
|
+
} else {
|
|
354
|
+
for (let i = 0; i < baselines.length; i++) {
|
|
355
|
+
const entry = baselines[i];
|
|
356
|
+
if (entry === null || entry === undefined) {
|
|
357
|
+
errors.push(`baselines[${i}] must not be null`);
|
|
358
|
+
continue;
|
|
359
|
+
}
|
|
360
|
+
if (!isPlainObject(entry)) {
|
|
361
|
+
errors.push(`baselines[${i}] must be an object`);
|
|
362
|
+
continue;
|
|
363
|
+
}
|
|
364
|
+
// Required string fields: name, path
|
|
365
|
+
for (const field of ['name', 'path']) {
|
|
366
|
+
if (typeof entry[field] !== 'string' || entry[field].trim() === '') {
|
|
367
|
+
errors.push(`baselines[${i}].${field} must be a non-empty string`);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
// Required match sub-object with a non-empty path-prefix string.
|
|
371
|
+
if (!isPlainObject(entry.match)) {
|
|
372
|
+
errors.push(`baselines[${i}].match must be an object`);
|
|
373
|
+
} else if (
|
|
374
|
+
typeof entry.match['path-prefix'] !== 'string' ||
|
|
375
|
+
entry.match['path-prefix'].trim() === ''
|
|
376
|
+
) {
|
|
377
|
+
errors.push(`baselines[${i}].match.path-prefix must be a non-empty string`);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
sections.baselines = { valid: errors.length === 0, errors };
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
const errors = SECTION_ORDER.flatMap((name) => sections[name].errors);
|
|
386
|
+
return { sections, errors };
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Validate a raw owner config object. Pure function — never throws, never reads
|
|
391
|
+
* or writes any file. Thin wrapper over `validateOwnerSections` (#820) —
|
|
392
|
+
* preserves the original `{ valid, errors }` contract unchanged.
|
|
393
|
+
*
|
|
394
|
+
* @param {unknown} obj
|
|
395
|
+
* @returns {{ valid: boolean, errors: string[] }}
|
|
396
|
+
*/
|
|
397
|
+
export function validateOwnerConfig(obj) {
|
|
398
|
+
const { errors } = validateOwnerSections(obj);
|
|
399
|
+
return { valid: errors.length === 0, errors };
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// ---------------------------------------------------------------------------
|
|
403
|
+
// Loader
|
|
404
|
+
// ---------------------------------------------------------------------------
|
|
405
|
+
|
|
406
|
+
/**
|
|
407
|
+
* Load owner.yaml from disk. Returns defaults when the file is absent or
|
|
408
|
+
* contains invalid content, populating `errors` in the latter case.
|
|
409
|
+
*
|
|
410
|
+
* Per-section tolerance (#820): an invalid REQUIRED section (owner, tone,
|
|
411
|
+
* efficiency, hardware-sharing) still discards the whole file (legacy
|
|
412
|
+
* behaviour, unchanged). An invalid OPTIONAL object section (paths,
|
|
413
|
+
* dispatcher) is instead replaced by its default value — the rest of the
|
|
414
|
+
* file survives, `source` becomes `'partial'`, and the drop is reported via
|
|
415
|
+
* `droppedSections` + a stderr WARN. OPTIONAL list sections (vaults,
|
|
416
|
+
* baselines) are passed through UNTOUCHED even when strict-invalid — their
|
|
417
|
+
* consumers already run a lenient parse-at-point-of-use pass — and are
|
|
418
|
+
* surfaced only via `sectionWarnings` (never dropped, never counted towards
|
|
419
|
+
* `'partial'`).
|
|
420
|
+
*
|
|
421
|
+
* Defensive — never throws.
|
|
422
|
+
*
|
|
423
|
+
* @param {{ path?: string }} [opts]
|
|
424
|
+
* @returns {{
|
|
425
|
+
* config: object,
|
|
426
|
+
* source: 'file'|'defaults'|'partial',
|
|
427
|
+
* errors: string[],
|
|
428
|
+
* droppedSections?: Array<{ section: string, errors: string[] }>,
|
|
429
|
+
* sectionWarnings?: Array<{ section: string, errors: string[] }>,
|
|
430
|
+
* }}
|
|
431
|
+
*/
|
|
432
|
+
export function loadOwnerConfig(opts = {}) {
|
|
433
|
+
const filePath = opts.path ?? OWNER_YAML_PATH;
|
|
434
|
+
|
|
435
|
+
if (!existsSync(filePath)) {
|
|
436
|
+
return { config: getDefaults(), source: 'defaults', errors: [] };
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
let raw;
|
|
440
|
+
try {
|
|
441
|
+
raw = readFileSync(filePath, 'utf8');
|
|
442
|
+
} catch (err) {
|
|
443
|
+
return {
|
|
444
|
+
config: getDefaults(),
|
|
445
|
+
source: 'defaults',
|
|
446
|
+
errors: [`failed to read owner.yaml: ${err.message}`],
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
let parsed;
|
|
451
|
+
try {
|
|
452
|
+
parsed = yaml.load(raw);
|
|
453
|
+
} catch (err) {
|
|
454
|
+
return {
|
|
455
|
+
config: getDefaults(),
|
|
456
|
+
source: 'defaults',
|
|
457
|
+
errors: [`YAML parse error: ${err.message}`],
|
|
458
|
+
};
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
if (!isPlainObject(parsed)) {
|
|
462
|
+
return {
|
|
463
|
+
config: getDefaults(),
|
|
464
|
+
source: 'defaults',
|
|
465
|
+
errors: ['owner.yaml must contain a YAML mapping at the top level'],
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
const { sections, errors: allErrors } = validateOwnerSections(parsed);
|
|
470
|
+
|
|
471
|
+
// Any REQUIRED section invalid → legacy whole-file-discard, unchanged (#820).
|
|
472
|
+
const requiredInvalid = REQUIRED_SECTIONS.some((name) => !sections[name]?.valid);
|
|
473
|
+
if (requiredInvalid) {
|
|
474
|
+
return {
|
|
475
|
+
config: getDefaults(),
|
|
476
|
+
source: 'defaults',
|
|
477
|
+
errors: allErrors,
|
|
478
|
+
};
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// All REQUIRED sections valid — tolerate malformed OPTIONAL sections instead
|
|
482
|
+
// of discarding the whole file (#820).
|
|
483
|
+
const defaults = getDefaults();
|
|
484
|
+
const config = { ...parsed };
|
|
485
|
+
const droppedSections = [];
|
|
486
|
+
const sectionWarnings = [];
|
|
487
|
+
|
|
488
|
+
for (const name of OPTIONAL_OBJECT_SECTIONS) {
|
|
489
|
+
const sec = sections[name];
|
|
490
|
+
if (sec && !sec.valid) {
|
|
491
|
+
config[name] = defaults[name];
|
|
492
|
+
droppedSections.push({ section: name, errors: sec.errors });
|
|
493
|
+
const firstError = sec.errors[0] ?? 'invalid section';
|
|
494
|
+
console.warn(
|
|
495
|
+
`WARN owner-yaml: dropping owner.yaml section "${name}" (${firstError}); using defaults`,
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
for (const name of OPTIONAL_LIST_SECTIONS) {
|
|
501
|
+
const sec = sections[name];
|
|
502
|
+
if (sec && !sec.valid) {
|
|
503
|
+
// Raw pass-through — deliberate defense-in-depth; parseNamedVaults()/
|
|
504
|
+
// parseBaselines() run their own lenient drop-and-WARN at point-of-use.
|
|
505
|
+
sectionWarnings.push({ section: name, errors: sec.errors });
|
|
506
|
+
const firstError = sec.errors[0] ?? 'invalid entries';
|
|
507
|
+
console.warn(
|
|
508
|
+
`WARN owner-yaml: owner.yaml section "${name}" has invalid entries (${firstError}); ` +
|
|
509
|
+
'lenient consumers will drop bad entries at point-of-use',
|
|
510
|
+
);
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
const result = {
|
|
515
|
+
config,
|
|
516
|
+
source: droppedSections.length > 0 ? 'partial' : 'file',
|
|
517
|
+
errors: allErrors,
|
|
518
|
+
};
|
|
519
|
+
if (droppedSections.length > 0) result.droppedSections = droppedSections;
|
|
520
|
+
if (sectionWarnings.length > 0) result.sectionWarnings = sectionWarnings;
|
|
521
|
+
return result;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
// ---------------------------------------------------------------------------
|
|
525
|
+
// Writer
|
|
526
|
+
// ---------------------------------------------------------------------------
|
|
527
|
+
|
|
528
|
+
/**
|
|
529
|
+
* Validate `config` and write it as YAML to disk. Creates parent directories
|
|
530
|
+
* if they do not exist. Validates before writing — returns errors without
|
|
531
|
+
* touching the filesystem when validation fails.
|
|
532
|
+
*
|
|
533
|
+
* Synchronous. Defensive — never throws.
|
|
534
|
+
*
|
|
535
|
+
* @param {object} config
|
|
536
|
+
* @param {{ path?: string }} [opts]
|
|
537
|
+
* @returns {{ written: boolean, errors: string[] }}
|
|
538
|
+
*/
|
|
539
|
+
export function writeOwnerConfig(config, opts = {}) {
|
|
540
|
+
const filePath = opts.path ?? OWNER_YAML_PATH;
|
|
541
|
+
|
|
542
|
+
const validation = validateOwnerConfig(config);
|
|
543
|
+
if (!validation.valid) {
|
|
544
|
+
return { written: false, errors: validation.errors };
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
const dir = dirname(filePath);
|
|
548
|
+
try {
|
|
549
|
+
mkdirSync(dir, { recursive: true });
|
|
550
|
+
} catch (err) {
|
|
551
|
+
return {
|
|
552
|
+
written: false,
|
|
553
|
+
errors: [`failed to create directory ${dir}: ${err.message}`],
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
let yamlStr;
|
|
558
|
+
try {
|
|
559
|
+
yamlStr = yaml.dump(config, { lineWidth: 120, noRefs: true });
|
|
560
|
+
} catch (err) {
|
|
561
|
+
return { written: false, errors: [`failed to serialise config to YAML: ${err.message}`] };
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
try {
|
|
565
|
+
writeFileSync(filePath, yamlStr, 'utf8');
|
|
566
|
+
} catch (err) {
|
|
567
|
+
return { written: false, errors: [`failed to write owner.yaml: ${err.message}`] };
|
|
568
|
+
}
|
|
569
|
+
|
|
570
|
+
return { written: true, errors: [] };
|
|
571
|
+
}
|