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,232 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* state-md-peer-guard.mjs — Issue #588 (STATE.md peer-guard)
|
|
3
|
+
*
|
|
4
|
+
* Belt-and-suspenders defense for session-start Phase 1b.
|
|
5
|
+
*
|
|
6
|
+
* Even with parallel-aware-preamble (Phase 0.5) and session-lock acquire
|
|
7
|
+
* (Phase 1.2) in place, a session can miss an active peer when:
|
|
8
|
+
* - The peer's session.lock was force-deleted (stale-PID sweep) but
|
|
9
|
+
* STATE.md still shows status:active.
|
|
10
|
+
* - A registry entry was orphaned but not yet swept.
|
|
11
|
+
*
|
|
12
|
+
* `checkPeerStateMd` reads the repo's STATE.md frontmatter and reports
|
|
13
|
+
* whether a *different* active session currently owns it. Callers
|
|
14
|
+
* (session-start Phase 1b, skill body) use the result to decide whether to
|
|
15
|
+
* fire the Worktree-Promotion AUQ before overwriting STATE.md.
|
|
16
|
+
*
|
|
17
|
+
* Never throws — all errors are surfaced via the `reason` field with value
|
|
18
|
+
* `'malformed-state-md'` so callers can always act on the return value.
|
|
19
|
+
*
|
|
20
|
+
* @module state-md-peer-guard
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
24
|
+
import { join } from 'node:path';
|
|
25
|
+
import { parseStateMd } from './state-md.mjs';
|
|
26
|
+
|
|
27
|
+
// ---------------------------------------------------------------------------
|
|
28
|
+
// Canonical STATE.md path candidates (same order as frontmatter-mutators.mjs)
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
const STATE_MD_CANDIDATES = ['.claude/STATE.md', '.codex/STATE.md', '.cursor/STATE.md', '.pi/STATE.md'];
|
|
32
|
+
|
|
33
|
+
function preferredStateMdCandidate() {
|
|
34
|
+
switch (process.env.SO_PLATFORM) {
|
|
35
|
+
case 'codex': return '.codex/STATE.md';
|
|
36
|
+
case 'cursor': return '.cursor/STATE.md';
|
|
37
|
+
case 'pi': return '.pi/STATE.md';
|
|
38
|
+
default: return '.claude/STATE.md';
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function orderedStateMdCandidates() {
|
|
43
|
+
const preferred = preferredStateMdCandidate();
|
|
44
|
+
return [
|
|
45
|
+
preferred,
|
|
46
|
+
...STATE_MD_CANDIDATES.filter((candidate) => candidate !== preferred),
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Resolve the first existing STATE.md path under repoRoot, checking the active
|
|
52
|
+
* platform first. Falls back to the active platform path when none exist.
|
|
53
|
+
*
|
|
54
|
+
* @param {string} repoRoot
|
|
55
|
+
* @returns {string} Absolute path (may not exist on disk).
|
|
56
|
+
*/
|
|
57
|
+
function _resolveStateMdPath(repoRoot) {
|
|
58
|
+
const candidates = orderedStateMdCandidates();
|
|
59
|
+
for (const candidate of candidates) {
|
|
60
|
+
const abs = join(repoRoot, candidate);
|
|
61
|
+
if (existsSync(abs)) return abs;
|
|
62
|
+
}
|
|
63
|
+
return join(repoRoot, candidates[0]);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
// Age calculation helper
|
|
68
|
+
// ---------------------------------------------------------------------------
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Return age in decimal hours from an ISO-8601 `started_at` string to now.
|
|
72
|
+
* Returns Infinity on unparseable input so callers treat malformed dates as
|
|
73
|
+
* "very old" (safe-side: allow overwrite).
|
|
74
|
+
*
|
|
75
|
+
* DIVERGENCE NOTE: `_ageHoursFrom` in peer-discovery.mjs returns undefined on
|
|
76
|
+
* unparseable input so callers can omit the ageHours field rather than emit a
|
|
77
|
+
* misleading number. This function returns Infinity instead — a fail-safe
|
|
78
|
+
* semantics: a malformed date is treated as "infinitely old/definitely-stale"
|
|
79
|
+
* which permits the overwrite rather than blocking on an indeterminate age.
|
|
80
|
+
* The divergence is intentional: peer-guard needs a conservative yes/no
|
|
81
|
+
* decision, so Infinity is the correct safe default; peer-discovery emits
|
|
82
|
+
* structured data, so undefined (field omission) is correct. Do NOT merge
|
|
83
|
+
* or unify these helpers.
|
|
84
|
+
*
|
|
85
|
+
* @param {string|null|undefined} startedAt
|
|
86
|
+
* @returns {number} Hours elapsed (may be Infinity).
|
|
87
|
+
*/
|
|
88
|
+
function _ageHoursFromStartedAt(startedAt) {
|
|
89
|
+
if (typeof startedAt !== 'string' || startedAt.trim() === '') return Infinity;
|
|
90
|
+
const ms = Date.now() - new Date(startedAt).getTime();
|
|
91
|
+
if (!Number.isFinite(ms)) return Infinity;
|
|
92
|
+
return ms / (1000 * 60 * 60);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// ---------------------------------------------------------------------------
|
|
96
|
+
// Public API
|
|
97
|
+
// ---------------------------------------------------------------------------
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Detect whether a peer session owns the current repo's STATE.md.
|
|
101
|
+
*
|
|
102
|
+
* Decision tree:
|
|
103
|
+
* 1. File missing → { peer: null, reason: 'no STATE.md' }
|
|
104
|
+
* 2. Malformed frontmatter → { peer: null, reason: 'malformed-state-md' }
|
|
105
|
+
* 3. status completed|idle → { peer: null, reason: 'status: <X>' }
|
|
106
|
+
* 4. status active + session === mySessionId
|
|
107
|
+
* → { peer: null, reason: 'own session' }
|
|
108
|
+
* 5. status active + different session + age > maxAgeHours
|
|
109
|
+
* → { peer: null, reason: 'ABANDONED (age > maxAgeHours)' }
|
|
110
|
+
* 6. status active + different session + age ≤ maxAgeHours
|
|
111
|
+
* → { peer: { sessionId, startedAt, currentWave, mode, ageHours },
|
|
112
|
+
* reason: 'ACTIVE peer detected' }
|
|
113
|
+
*
|
|
114
|
+
* @param {string} repoRoot Absolute path to the repository root.
|
|
115
|
+
* @param {string|null} mySessionId Current session's id (UUID or semantic);
|
|
116
|
+
* null means "no session yet" — any active
|
|
117
|
+
* session counts as a peer.
|
|
118
|
+
* @param {object} [opts]
|
|
119
|
+
* @param {number} [opts.maxAgeHours=4] STATE.md older than this is
|
|
120
|
+
* treated as abandoned (allow overwrite).
|
|
121
|
+
* @returns {{
|
|
122
|
+
* peer: {
|
|
123
|
+
* sessionId: string,
|
|
124
|
+
* startedAt: string,
|
|
125
|
+
* currentWave: number,
|
|
126
|
+
* mode: string|null,
|
|
127
|
+
* ageHours: number
|
|
128
|
+
* } | null,
|
|
129
|
+
* reason: string | null
|
|
130
|
+
* }}
|
|
131
|
+
*/
|
|
132
|
+
export function checkPeerStateMd(repoRoot, mySessionId, opts = {}) {
|
|
133
|
+
const maxAgeHours =
|
|
134
|
+
typeof opts.maxAgeHours === 'number' && opts.maxAgeHours > 0 ? opts.maxAgeHours : 4;
|
|
135
|
+
|
|
136
|
+
// ------------------------------------------------------------------
|
|
137
|
+
// Step 1: resolve + read STATE.md
|
|
138
|
+
// ------------------------------------------------------------------
|
|
139
|
+
const stateMdPath = _resolveStateMdPath(repoRoot);
|
|
140
|
+
|
|
141
|
+
let raw;
|
|
142
|
+
try {
|
|
143
|
+
raw = readFileSync(stateMdPath, 'utf8');
|
|
144
|
+
} catch (err) {
|
|
145
|
+
// ENOENT is expected for fresh repos; other FS errors also treated as
|
|
146
|
+
// "no STATE.md" — fail-open (allow overwrite) rather than blocking.
|
|
147
|
+
if (err.code === 'ENOENT') {
|
|
148
|
+
return { peer: null, reason: 'no STATE.md' };
|
|
149
|
+
}
|
|
150
|
+
return { peer: null, reason: 'no STATE.md' };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// ------------------------------------------------------------------
|
|
154
|
+
// Step 2: parse frontmatter (parseStateMd never throws per its contract)
|
|
155
|
+
// ------------------------------------------------------------------
|
|
156
|
+
let parsed;
|
|
157
|
+
try {
|
|
158
|
+
parsed = parseStateMd(raw);
|
|
159
|
+
} catch {
|
|
160
|
+
return { peer: null, reason: 'malformed-state-md' };
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (parsed === null || parsed.frontmatter === null) {
|
|
164
|
+
return { peer: null, reason: 'malformed-state-md' };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const fm = parsed.frontmatter;
|
|
168
|
+
|
|
169
|
+
// ------------------------------------------------------------------
|
|
170
|
+
// Step 3: check status field
|
|
171
|
+
// ------------------------------------------------------------------
|
|
172
|
+
const status = typeof fm.status === 'string' ? fm.status.toLowerCase().trim() : '';
|
|
173
|
+
|
|
174
|
+
if (status === 'completed' || status === 'idle') {
|
|
175
|
+
return { peer: null, reason: `status: ${status}` };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Any non-active value (missing, unknown) → treat as non-blocking.
|
|
179
|
+
if (status !== 'active') {
|
|
180
|
+
return { peer: null, reason: `status: ${status || 'unknown'}` };
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// ------------------------------------------------------------------
|
|
184
|
+
// Step 4: check session identity
|
|
185
|
+
// ------------------------------------------------------------------
|
|
186
|
+
// PRECONDITION (invariant): `mySessionId` and `fm.session` MUST be expressed
|
|
187
|
+
// in the SAME id-space (both semantic OR both UUID). The equality check below
|
|
188
|
+
// is plain string-equality — a semantic-vs-UUID mismatch for the SAME logical
|
|
189
|
+
// session would be misclassified as a foreign peer (fail-safe: extra AUQ, never
|
|
190
|
+
// a missed peer). When `mySessionId === null` ("no session yet"), the ownership
|
|
191
|
+
// short-circuit is skipped and any active session with a `session` field counts
|
|
192
|
+
// as a peer.
|
|
193
|
+
const sessionField = typeof fm.session === 'string' ? fm.session : null;
|
|
194
|
+
|
|
195
|
+
// No session field → cannot identify a peer; allow overwrite.
|
|
196
|
+
if (sessionField === null) {
|
|
197
|
+
return { peer: null, reason: 'no session field' };
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// Session matches ours → we own STATE.md already.
|
|
201
|
+
if (mySessionId !== null && sessionField === mySessionId) {
|
|
202
|
+
return { peer: null, reason: 'own session' };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
// ------------------------------------------------------------------
|
|
206
|
+
// Step 5: check age of the active entry
|
|
207
|
+
// ------------------------------------------------------------------
|
|
208
|
+
const startedAt = typeof fm['started_at'] === 'string' ? fm['started_at'] : null;
|
|
209
|
+
const ageHours = _ageHoursFromStartedAt(startedAt);
|
|
210
|
+
|
|
211
|
+
if (ageHours > maxAgeHours) {
|
|
212
|
+
return { peer: null, reason: 'ABANDONED (age > maxAgeHours)' };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
// ------------------------------------------------------------------
|
|
216
|
+
// Step 6: active peer detected
|
|
217
|
+
// ------------------------------------------------------------------
|
|
218
|
+
const currentWave = typeof fm['current-wave'] === 'number' ? fm['current-wave'] : 0;
|
|
219
|
+
// `session-type` is the frontmatter key used for mode (e.g. 'deep', 'feature').
|
|
220
|
+
const mode = typeof fm['session-type'] === 'string' ? fm['session-type'] : null;
|
|
221
|
+
|
|
222
|
+
return {
|
|
223
|
+
peer: {
|
|
224
|
+
sessionId: sessionField,
|
|
225
|
+
startedAt: startedAt ?? '',
|
|
226
|
+
currentWave,
|
|
227
|
+
mode,
|
|
228
|
+
ageHours,
|
|
229
|
+
},
|
|
230
|
+
reason: 'ACTIVE peer detected',
|
|
231
|
+
};
|
|
232
|
+
}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* STATE.md helpers (issue #184).
|
|
3
|
+
*
|
|
4
|
+
* Thin barrel — re-exports all 25 public symbols from the split submodules.
|
|
5
|
+
* Implementation lives in scripts/lib/state-md/*.mjs.
|
|
6
|
+
*
|
|
7
|
+
* @see scripts/lib/state-md/yaml-parser.mjs parseStateMd, serializeStateMd
|
|
8
|
+
* @see scripts/lib/state-md/frontmatter-mutators.mjs touchUpdatedField, updateFrontmatterFields
|
|
9
|
+
* @see scripts/lib/state-md/body-sections.mjs readCurrentTask, appendDeviation, markExpressPathComplete, appendWhatNotToRetry, readWhatNotToRetry, readOpenQuestions, appendOpenQuestion, markOpenQuestionAnswered
|
|
10
|
+
* @see scripts/lib/state-md/mission-status.mjs parseMissionStatus, writeMissionStatus, setMissionStatus, readMissionStatus
|
|
11
|
+
* @see scripts/lib/state-md/recommendations.mjs parseRecommendations
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
export { parseStateMd, serializeStateMd } from './state-md/yaml-parser.mjs';
|
|
15
|
+
|
|
16
|
+
export {
|
|
17
|
+
touchUpdatedField,
|
|
18
|
+
updateFrontmatterFields,
|
|
19
|
+
resolveStateMdPath,
|
|
20
|
+
writeStateMd,
|
|
21
|
+
updateFrontmatterFieldsOnDisk,
|
|
22
|
+
touchUpdatedFieldOnDisk,
|
|
23
|
+
} from './state-md/frontmatter-mutators.mjs';
|
|
24
|
+
|
|
25
|
+
export {
|
|
26
|
+
readCurrentTask,
|
|
27
|
+
appendDeviation,
|
|
28
|
+
markExpressPathComplete,
|
|
29
|
+
recordAutoCommit,
|
|
30
|
+
appendDeviationOnDisk,
|
|
31
|
+
recordAutoCommitOnDisk,
|
|
32
|
+
markExpressPathCompleteOnDisk,
|
|
33
|
+
appendWhatNotToRetry,
|
|
34
|
+
readWhatNotToRetry,
|
|
35
|
+
appendWhatNotToRetryOnDisk,
|
|
36
|
+
readOpenQuestions,
|
|
37
|
+
appendOpenQuestion,
|
|
38
|
+
markOpenQuestionAnswered,
|
|
39
|
+
appendOpenQuestionOnDisk,
|
|
40
|
+
markOpenQuestionAnsweredOnDisk,
|
|
41
|
+
MAX_OPEN_QUESTIONS_STORED,
|
|
42
|
+
} from './state-md/body-sections.mjs';
|
|
43
|
+
|
|
44
|
+
export {
|
|
45
|
+
parseMissionStatus,
|
|
46
|
+
writeMissionStatus,
|
|
47
|
+
setMissionStatus,
|
|
48
|
+
readMissionStatus,
|
|
49
|
+
writeMissionStatusOnDisk,
|
|
50
|
+
setMissionStatusOnDisk,
|
|
51
|
+
} from './state-md/mission-status.mjs';
|
|
52
|
+
|
|
53
|
+
export { parseRecommendations } from './state-md/recommendations.mjs';
|
|
@@ -0,0 +1,309 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* subagents-schema.mjs — schema module for subagent lifecycle records.
|
|
3
|
+
*
|
|
4
|
+
* Mirrors learnings.mjs style: Zod-free, custom ValidationError, validate /
|
|
5
|
+
* normalize / migrate / append / read functions. No top-level side effects.
|
|
6
|
+
*
|
|
7
|
+
* Part of issue #342 (PostToolUseFailure + PostToolBatch + SubagentStart/Stop
|
|
8
|
+
* hook handlers). W1-Part1 scope: schema module only.
|
|
9
|
+
*
|
|
10
|
+
* Canonical schema (schema_version: 1):
|
|
11
|
+
* timestamp ISO 8601 string — when the event occurred
|
|
12
|
+
* event 'start' | 'stop' — lifecycle event type
|
|
13
|
+
* agent_id string — unique identifier for the subagent instance
|
|
14
|
+
* schema_version 1 (integer)
|
|
15
|
+
*
|
|
16
|
+
* Required for event='stop':
|
|
17
|
+
* duration_ms positive integer — wall-clock time from start to stop
|
|
18
|
+
*
|
|
19
|
+
* Optional:
|
|
20
|
+
* agent_type string | null — e.g. 'explore', 'writer', 'test-writer'
|
|
21
|
+
* parent_session_id string | null — session that spawned this subagent
|
|
22
|
+
* token_input integer | null — prompt token count for this subagent
|
|
23
|
+
* token_output integer | null — completion token count for this subagent
|
|
24
|
+
* total_cost_usd number | null — native total cost in USD (#624, fractional,
|
|
25
|
+
* best-effort: null when the harness does not
|
|
26
|
+
* expose it; no rate table is applied)
|
|
27
|
+
*
|
|
28
|
+
* OTel aliases (optional, stop-only, additive — #411, schema_version=1 backwards-compat):
|
|
29
|
+
* gen_ai.usage.input_tokens integer | null — alias of token_input
|
|
30
|
+
* gen_ai.usage.output_tokens integer | null — alias of token_output
|
|
31
|
+
* gen_ai.system 'anthropic' — AI provider identifier
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
import { readFile, appendFile, mkdir } from 'node:fs/promises';
|
|
35
|
+
import { existsSync } from 'node:fs';
|
|
36
|
+
import path from 'node:path';
|
|
37
|
+
|
|
38
|
+
// ---------------------------------------------------------------------------
|
|
39
|
+
// Constants
|
|
40
|
+
// ---------------------------------------------------------------------------
|
|
41
|
+
|
|
42
|
+
/** Current subagent-record schema version. */
|
|
43
|
+
export const CURRENT_SCHEMA_VERSION = 1;
|
|
44
|
+
|
|
45
|
+
/** Allowed event values. */
|
|
46
|
+
export const VALID_EVENTS = Object.freeze(['start', 'stop']);
|
|
47
|
+
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
// ValidationError
|
|
50
|
+
// ---------------------------------------------------------------------------
|
|
51
|
+
|
|
52
|
+
export class ValidationError extends Error {
|
|
53
|
+
/**
|
|
54
|
+
* @param {string} message
|
|
55
|
+
* @param {string|undefined} field — optional field name that triggered the error
|
|
56
|
+
*/
|
|
57
|
+
constructor(message, field) {
|
|
58
|
+
super(message);
|
|
59
|
+
this.name = 'ValidationError';
|
|
60
|
+
this.field = field;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// ---------------------------------------------------------------------------
|
|
65
|
+
// Validate
|
|
66
|
+
// ---------------------------------------------------------------------------
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Validate a subagent record. Throws ValidationError on any constraint
|
|
70
|
+
* violation. Does NOT mutate the input.
|
|
71
|
+
*
|
|
72
|
+
* Required fields: timestamp, event, agent_id, schema_version.
|
|
73
|
+
* Additional requirement when event='stop': duration_ms (positive integer).
|
|
74
|
+
* Optional: agent_type, parent_session_id, token_input, token_output.
|
|
75
|
+
*
|
|
76
|
+
* @param {object} entry
|
|
77
|
+
* @returns {object} the entry (unchanged) — validation is side-effect-free
|
|
78
|
+
* @throws {ValidationError}
|
|
79
|
+
*/
|
|
80
|
+
export function validateSubagent(entry) {
|
|
81
|
+
if (!entry || typeof entry !== 'object') {
|
|
82
|
+
throw new ValidationError('subagent record must be a non-null object');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// schema_version
|
|
86
|
+
if (entry.schema_version !== CURRENT_SCHEMA_VERSION) {
|
|
87
|
+
throw new ValidationError(
|
|
88
|
+
`schema_version must be ${CURRENT_SCHEMA_VERSION}, got: ${entry.schema_version}`,
|
|
89
|
+
'schema_version',
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// timestamp
|
|
94
|
+
if (typeof entry.timestamp !== 'string' || !entry.timestamp.trim()) {
|
|
95
|
+
throw new ValidationError('timestamp must be a non-empty ISO 8601 string', 'timestamp');
|
|
96
|
+
}
|
|
97
|
+
if (Number.isNaN(Date.parse(entry.timestamp))) {
|
|
98
|
+
throw new ValidationError(
|
|
99
|
+
`timestamp is not a valid ISO 8601 date: ${entry.timestamp}`,
|
|
100
|
+
'timestamp',
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// event
|
|
105
|
+
if (!VALID_EVENTS.includes(entry.event)) {
|
|
106
|
+
throw new ValidationError(
|
|
107
|
+
`event must be one of ${VALID_EVENTS.join('|')}, got: ${entry.event}`,
|
|
108
|
+
'event',
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// agent_id
|
|
113
|
+
if (typeof entry.agent_id !== 'string' || !entry.agent_id.trim()) {
|
|
114
|
+
throw new ValidationError('agent_id must be a non-empty string', 'agent_id');
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// duration_ms — required for stop events
|
|
118
|
+
if (entry.event === 'stop') {
|
|
119
|
+
if (
|
|
120
|
+
typeof entry.duration_ms !== 'number' ||
|
|
121
|
+
!Number.isInteger(entry.duration_ms) ||
|
|
122
|
+
entry.duration_ms < 0
|
|
123
|
+
) {
|
|
124
|
+
throw new ValidationError(
|
|
125
|
+
'duration_ms must be a non-negative integer when event=stop',
|
|
126
|
+
'duration_ms',
|
|
127
|
+
);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// agent_type (optional)
|
|
132
|
+
if (entry.agent_type !== undefined && entry.agent_type !== null && typeof entry.agent_type !== 'string') {
|
|
133
|
+
throw new ValidationError('agent_type must be a string or null', 'agent_type');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// parent_session_id (optional)
|
|
137
|
+
if (
|
|
138
|
+
entry.parent_session_id !== undefined &&
|
|
139
|
+
entry.parent_session_id !== null &&
|
|
140
|
+
typeof entry.parent_session_id !== 'string'
|
|
141
|
+
) {
|
|
142
|
+
throw new ValidationError('parent_session_id must be a string or null', 'parent_session_id');
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// token_input (optional)
|
|
146
|
+
if (entry.token_input !== undefined && entry.token_input !== null) {
|
|
147
|
+
if (typeof entry.token_input !== 'number' || !Number.isInteger(entry.token_input) || entry.token_input < 0) {
|
|
148
|
+
throw new ValidationError('token_input must be a non-negative integer or null', 'token_input');
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// token_output (optional)
|
|
153
|
+
if (entry.token_output !== undefined && entry.token_output !== null) {
|
|
154
|
+
if (typeof entry.token_output !== 'number' || !Number.isInteger(entry.token_output) || entry.token_output < 0) {
|
|
155
|
+
throw new ValidationError('token_output must be a non-negative integer or null', 'token_output');
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// total_cost_usd (optional, #624) — fractional number (not integer), best-effort.
|
|
160
|
+
if (entry.total_cost_usd !== undefined && entry.total_cost_usd !== null) {
|
|
161
|
+
if (typeof entry.total_cost_usd !== 'number' || !Number.isFinite(entry.total_cost_usd) || entry.total_cost_usd < 0) {
|
|
162
|
+
throw new ValidationError('total_cost_usd must be a non-negative number or null', 'total_cost_usd');
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// OTel alias — #411 additive, schema_version=1 backwards-compat
|
|
167
|
+
const otelInputTokens = entry['gen_ai.usage.input_tokens'];
|
|
168
|
+
if (otelInputTokens !== undefined && otelInputTokens !== null) {
|
|
169
|
+
if (typeof otelInputTokens !== 'number' || !Number.isInteger(otelInputTokens) || otelInputTokens < 0) {
|
|
170
|
+
throw new ValidationError('gen_ai.usage.input_tokens must be a non-negative integer or null', 'gen_ai.usage.input_tokens');
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// OTel alias — #411 additive, schema_version=1 backwards-compat
|
|
175
|
+
const otelOutputTokens = entry['gen_ai.usage.output_tokens'];
|
|
176
|
+
if (otelOutputTokens !== undefined && otelOutputTokens !== null) {
|
|
177
|
+
if (typeof otelOutputTokens !== 'number' || !Number.isInteger(otelOutputTokens) || otelOutputTokens < 0) {
|
|
178
|
+
throw new ValidationError('gen_ai.usage.output_tokens must be a non-negative integer or null', 'gen_ai.usage.output_tokens');
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// OTel alias — #411 additive, schema_version=1 backwards-compat
|
|
183
|
+
if (entry['gen_ai.system'] !== undefined && entry['gen_ai.system'] !== null) {
|
|
184
|
+
if (typeof entry['gen_ai.system'] !== 'string') {
|
|
185
|
+
throw new ValidationError('gen_ai.system must be a string or null', 'gen_ai.system');
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
return entry;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
// ---------------------------------------------------------------------------
|
|
193
|
+
// Normalize
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Normalize a subagent record read from disk. Applies defaults for optional
|
|
198
|
+
* fields so callers can treat legacy and new entries uniformly.
|
|
199
|
+
* Does NOT throw — malformed entries are passed through unchanged.
|
|
200
|
+
*
|
|
201
|
+
* @param {object} entry
|
|
202
|
+
* @returns {object} entry with optional fields defaulted to null / CURRENT_SCHEMA_VERSION
|
|
203
|
+
*/
|
|
204
|
+
export function normalizeSubagent(entry) {
|
|
205
|
+
if (!entry || typeof entry !== 'object') return entry;
|
|
206
|
+
return {
|
|
207
|
+
...entry,
|
|
208
|
+
schema_version: entry.schema_version ?? CURRENT_SCHEMA_VERSION,
|
|
209
|
+
agent_type: entry.agent_type ?? null,
|
|
210
|
+
parent_session_id: entry.parent_session_id ?? null,
|
|
211
|
+
token_input: entry.token_input ?? null,
|
|
212
|
+
token_output: entry.token_output ?? null,
|
|
213
|
+
// total_cost_usd — #624 additive, best-effort native cost (null when absent)
|
|
214
|
+
total_cost_usd: entry.total_cost_usd ?? null,
|
|
215
|
+
// OTel alias — #411 additive, schema_version=1 backwards-compat
|
|
216
|
+
'gen_ai.usage.input_tokens': entry['gen_ai.usage.input_tokens'] ?? null,
|
|
217
|
+
'gen_ai.usage.output_tokens': entry['gen_ai.usage.output_tokens'] ?? null,
|
|
218
|
+
'gen_ai.system': entry['gen_ai.system'] ?? null,
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// ---------------------------------------------------------------------------
|
|
223
|
+
// Migrate
|
|
224
|
+
// ---------------------------------------------------------------------------
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Migrate a legacy / partial subagent record to the canonical v1 shape.
|
|
228
|
+
* Idempotent — calling it on an already-canonical record is a safe no-op.
|
|
229
|
+
* No-op for v1; reserved for future schema bumps.
|
|
230
|
+
*
|
|
231
|
+
* The caller MUST still run validateSubagent() on the result before writing.
|
|
232
|
+
*
|
|
233
|
+
* @param {object} entry
|
|
234
|
+
* @returns {object} record with schema_version stamped
|
|
235
|
+
*/
|
|
236
|
+
export function migrateLegacySubagent(entry) {
|
|
237
|
+
if (!entry || typeof entry !== 'object') return entry;
|
|
238
|
+
const out = { ...entry };
|
|
239
|
+
if (out.schema_version === undefined || out.schema_version === null) {
|
|
240
|
+
out.schema_version = CURRENT_SCHEMA_VERSION;
|
|
241
|
+
}
|
|
242
|
+
return out;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// ---------------------------------------------------------------------------
|
|
246
|
+
// Append
|
|
247
|
+
// ---------------------------------------------------------------------------
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Append a single subagent record to a JSONL file.
|
|
251
|
+
*
|
|
252
|
+
* Steps:
|
|
253
|
+
* 1. Stamp schema_version if missing.
|
|
254
|
+
* 2. Normalize (apply optional-field defaults).
|
|
255
|
+
* 3. Validate — throws ValidationError on bad input.
|
|
256
|
+
* 4. Serialize to JSON + newline.
|
|
257
|
+
* 5. mkdir(dirname, recursive: true).
|
|
258
|
+
* 6. appendFile (POSIX append ≤ PIPE_BUF is atomic for typical JSONL lines).
|
|
259
|
+
*
|
|
260
|
+
* @param {string} filePath — absolute path to target .jsonl file
|
|
261
|
+
* @param {object} entry — candidate subagent record
|
|
262
|
+
* @returns {Promise<object>} the validated + normalized entry that was written
|
|
263
|
+
* @throws {ValidationError} when the entry fails schema validation
|
|
264
|
+
*/
|
|
265
|
+
export async function appendSubagent(filePath, entry) {
|
|
266
|
+
const stamped = {
|
|
267
|
+
...entry,
|
|
268
|
+
schema_version: entry?.schema_version ?? CURRENT_SCHEMA_VERSION,
|
|
269
|
+
};
|
|
270
|
+
const normalized = normalizeSubagent(stamped);
|
|
271
|
+
const validated = validateSubagent(normalized);
|
|
272
|
+
const line = JSON.stringify(validated) + '\n';
|
|
273
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
274
|
+
await appendFile(filePath, line, 'utf8');
|
|
275
|
+
return validated;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// ---------------------------------------------------------------------------
|
|
279
|
+
// Read
|
|
280
|
+
// ---------------------------------------------------------------------------
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Read all valid subagent records from a JSONL file. Malformed lines are
|
|
284
|
+
* skipped with a stderr warning — never throws.
|
|
285
|
+
*
|
|
286
|
+
* @param {string} filePath — absolute path to target .jsonl file
|
|
287
|
+
* @returns {Promise<object[]>} array of normalized subagent records
|
|
288
|
+
*/
|
|
289
|
+
export async function readSubagents(filePath) {
|
|
290
|
+
if (!existsSync(filePath)) return [];
|
|
291
|
+
let raw;
|
|
292
|
+
try {
|
|
293
|
+
raw = await readFile(filePath, 'utf8');
|
|
294
|
+
} catch (err) {
|
|
295
|
+
process.stderr.write(`[subagents-schema] WARN: could not read ${filePath}: ${err?.message ?? err}\n`);
|
|
296
|
+
return [];
|
|
297
|
+
}
|
|
298
|
+
const lines = raw.split('\n').filter((l) => l.trim().length > 0);
|
|
299
|
+
const entries = [];
|
|
300
|
+
for (const line of lines) {
|
|
301
|
+
try {
|
|
302
|
+
const parsed = JSON.parse(line);
|
|
303
|
+
entries.push(normalizeSubagent(parsed));
|
|
304
|
+
} catch {
|
|
305
|
+
process.stderr.write(`[subagents-schema] WARN: skipping malformed JSONL line: ${line.slice(0, 120)}\n`);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
return entries;
|
|
309
|
+
}
|