session-orchestrator 5.11.0 → 5.13.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 +12 -1
- package/.claude-plugin/plugin.json +4 -1
- package/.codex-plugin/agents/explorer.toml +1 -1
- package/.codex-plugin/agents/session-reviewer.toml +2 -2
- package/.codex-plugin/agents/wave-worker.toml +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/.orchestrator/policy/blocked-commands.json +8 -0
- package/.orchestrator/policy/public-export.json +30 -0
- package/CHANGELOG.md +180 -3
- package/README.md +126 -117
- package/agents/analyst.md +7 -2
- package/agents/architect-reviewer.md +7 -2
- package/agents/code-implementer.md +4 -2
- package/agents/db-specialist.md +9 -2
- package/agents/dialectic-deriver.md +9 -3
- package/agents/docs-writer.md +7 -2
- package/agents/eval-judge.md +7 -1
- package/agents/qa-strategist.md +7 -2
- package/agents/security-reviewer.md +9 -2
- package/agents/session-reviewer.md +18 -2
- package/agents/skill-applied-judge.md +7 -1
- package/agents/test-writer.md +9 -2
- package/agents/ui-developer.md +9 -2
- package/agents/ux-evaluator.md +6 -1
- package/commands/session.md +4 -2
- package/commands/templates-ack.md +89 -18
- package/docs/agent-authoring.md +30 -11
- package/docs/ci-setup.md +38 -2
- package/docs/codex-setup.md +33 -9
- package/docs/components.md +13 -2
- package/docs/cursor-setup.md +3 -3
- package/docs/events-schema.md +5 -4
- package/docs/evidence-candidates.md +63 -0
- package/docs/fleet-checkin-audit.md +7 -4
- package/docs/github-mirror-protection.md +150 -3
- package/docs/install.md +102 -25
- package/docs/instruction-delivery.md +1 -1
- package/docs/migration-v4.md +2 -2
- package/docs/mods.md +118 -0
- package/docs/owner-config-schema.md +1 -0
- package/docs/plugin-architecture-v3.md +1 -1
- package/docs/pm-skills-marketplace.md +4 -3
- package/docs/rule-authoring.md +24 -13
- package/docs/scope-collision-guard.md +2 -1
- package/docs/session-config-reference.md +55 -21
- package/docs/session-config-template.md +32 -24
- package/docs/sessions-ledger-repair.md +137 -0
- package/docs/vault-docs-architecture.md +1 -1
- package/hooks/_lib/hook-import-set.json +406 -73
- package/hooks/_lib/hook-root.mjs +301 -0
- package/hooks/_lib/lock-bootstrap.mjs +434 -4
- package/hooks/_lib/orchestrator-ignore.mjs +3 -0
- package/hooks/_lib/subagent-transcript.mjs +2 -2
- package/hooks/_lib/vcs-create-matcher.mjs +13 -17
- package/hooks/cwd-change-restore.mjs +5 -1
- package/hooks/enforce-commands.mjs +4 -3
- package/hooks/enforce-scope.mjs +11 -1
- package/hooks/hooks-codex.json +48 -2
- package/hooks/hooks.json +31 -31
- package/hooks/on-session-end.mjs +38 -15
- package/hooks/on-session-start.mjs +158 -59
- package/hooks/on-stop.mjs +11 -6
- package/hooks/post-bash-issue-budget-refund.mjs +6 -1
- package/hooks/post-bash-write-verify.mjs +8 -4
- package/hooks/post-edit-validate.mjs +45 -52
- package/hooks/post-subagent-discovery-validator.mjs +2 -2
- package/hooks/post-tool-batch-wave-signal.mjs +8 -4
- package/hooks/post-tool-failure-corrective-context.mjs +5 -1
- package/hooks/pre-bash-destructive-guard.mjs +220 -8
- package/hooks/pre-bash-issue-budget.mjs +6 -1
- package/hooks/pre-bash-sessions-ledger-guard.mjs +691 -123
- package/hooks/pre-bash-staging-fence.mjs +258 -14
- package/hooks/pre-bash-templates-first.mjs +28 -27
- package/hooks/run-node.sh +1 -1
- package/hooks/skill-invocation-telemetry.mjs +15 -8
- package/hooks/subagent-telemetry.mjs +77 -14
- package/hooks/wave-scope-commit-guard.mjs +143 -14
- package/mods/so-hud/.claude-plugin/plugin.json +11 -0
- package/mods/so-hud/README.md +57 -0
- package/mods/so-hud/hooks/beacon-io.mjs +101 -0
- package/mods/so-hud/hooks/heredoc-scan.ts +394 -0
- package/mods/so-hud/hooks/hooks.json +1 -0
- package/mods/so-hud/hooks/logic.ts +182 -0
- package/mods/so-hud/hooks/register.tsx +670 -0
- package/mods/so-hud/hooks/rules.ts +2998 -0
- package/mods/so-hud/tsconfig.json +8 -0
- package/mods/so-hud/types/index.d.ts +36 -0
- package/package.json +4 -3
- package/rules/_index.md +1 -1
- package/rules/always-on/ask-via-tool.md +1 -1
- package/rules/always-on/loop-and-monitor.md +17 -194
- package/rules/always-on/parallel-sessions.md +3 -1
- package/rules/opt-in-stack/backend-data.md +1 -1
- package/rules/opt-in-stack/backend.md +1 -1
- package/rules/opt-in-stack/security-web.md +5 -1
- package/scripts/agent-usage-census.mjs +79 -0
- package/scripts/apply-session-learnings.mjs +34 -6
- package/scripts/archive-closed-prds.mjs +21 -5
- package/scripts/autopilot.mjs +11 -5
- package/scripts/backfill-abandoned-sessions.mjs +13 -3
- package/scripts/backfill-evidence-digest.mjs +2 -1
- package/scripts/backfill-learnings-from-vault.mjs +61 -6
- package/scripts/backfill-sessions.mjs +16 -2
- package/scripts/check-package-manager.mjs +26 -8
- package/scripts/check-sessions-integrity.mjs +4 -1
- package/scripts/ci/assert-vitest-green.mjs +3 -0
- package/scripts/ci/windows-smoke.mjs +201 -0
- package/scripts/close-precheck.mjs +55 -0
- package/scripts/dialectic-deriver.mjs +20 -12
- package/scripts/emit-event.mjs +2 -2
- package/scripts/emit-session.mjs +239 -20
- package/scripts/eval-session.mjs +17 -3
- package/scripts/export-hw-learnings.mjs +29 -2
- package/scripts/fleet-checkin-audit.mjs +8 -4
- package/scripts/fleet-checkin.mjs +73 -3
- package/scripts/gc-stale-worktrees.mjs +6 -1
- package/scripts/github-export.mjs +128 -0
- package/scripts/github-protection-audit.mjs +3 -1
- package/scripts/handover-queue-check.mjs +209 -0
- package/scripts/harness-audit.mjs +6 -4
- package/scripts/lib/agent-frontmatter.mjs +57 -10
- package/scripts/lib/agent-status.mjs +125 -7
- package/scripts/lib/agent-usage-census.mjs +394 -0
- package/scripts/lib/auto-dialectic.mjs +17 -10
- package/scripts/lib/auto-dream.mjs +22 -14
- package/scripts/lib/autopilot/loop.mjs +3 -1
- package/scripts/lib/autopilot/recent-runs.mjs +4 -2
- package/scripts/lib/autopilot/stall-sampler.mjs +2 -1
- package/scripts/lib/autopilot/telemetry.mjs +5 -1
- package/scripts/lib/autopilot/worktree-pipeline.mjs +5 -5
- package/scripts/lib/baseline-archetypes.mjs +10 -2
- package/scripts/lib/blocked-commands-policy.mjs +4 -3
- package/scripts/lib/build-live-signals.mjs +13 -9
- package/scripts/lib/ci-status-banner.mjs +329 -24
- package/scripts/lib/codex/plugin-contract.mjs +8 -0
- package/scripts/lib/cold-start-detector.mjs +5 -2
- package/scripts/lib/command-blocker.mjs +603 -43
- package/scripts/lib/common.mjs +3 -2
- package/scripts/lib/config/cross-repo.mjs +74 -7
- package/scripts/lib/config/host-paths.mjs +6 -2
- package/scripts/lib/config/remote-hosts.mjs +2 -2
- package/scripts/lib/config/section-extractor.mjs +65 -11
- package/scripts/lib/config/template-keys.mjs +153 -0
- package/scripts/lib/config/vault-integration.mjs +1 -0
- package/scripts/lib/config.mjs +3 -3
- package/scripts/lib/coordinator-snapshot.mjs +1 -1
- package/scripts/lib/discovery/triage-state.mjs +2 -1
- package/scripts/lib/dispatcher/rank.mjs +5 -7
- package/scripts/lib/ecosystem-health.mjs +2 -2
- package/scripts/lib/eval/engine.mjs +3 -2
- package/scripts/lib/eval/session-resolve.mjs +6 -2
- package/scripts/lib/eval/sink.mjs +4 -3
- package/scripts/lib/events-retention-banner.mjs +3 -2
- package/scripts/lib/events-schema.mjs +10 -0
- package/scripts/lib/events.mjs +23 -12
- package/scripts/lib/evolve/autonomy-verdict.mjs +4 -9
- package/scripts/lib/evolve/autopilot-effectiveness.mjs +8 -18
- package/scripts/lib/fleet-protocol.mjs +21 -0
- package/scripts/lib/gates/gate-full.mjs +172 -11
- package/scripts/lib/gates/gate-helpers.mjs +236 -23
- package/scripts/lib/git-config-drift.mjs +141 -7
- package/scripts/lib/github-export.mjs +447 -0
- package/scripts/lib/gitlab-portfolio/cli.mjs +16 -5
- package/scripts/lib/handover-queue-marks.mjs +159 -0
- package/scripts/lib/hardware-pattern-detector.mjs +103 -13
- package/scripts/lib/harness-audit/categories/category1.mjs +16 -4
- package/scripts/lib/harness-audit/categories/category4.mjs +11 -8
- package/scripts/lib/harness-audit/categories/category6.mjs +25 -5
- package/scripts/lib/harness-audit/categories/category9.mjs +10 -4
- package/scripts/lib/host-identity.mjs +9 -5
- package/scripts/lib/http-request.mjs +64 -0
- package/scripts/lib/instruction-budget-guard.mjs +30 -22
- package/scripts/lib/io.mjs +6 -3
- package/scripts/lib/issue-budget-reconcile.mjs +3 -2
- package/scripts/lib/issue-budget.mjs +36 -7
- package/scripts/lib/learnings/evidence-candidates.mjs +154 -0
- package/scripts/lib/learnings/expiry-sweep.mjs +99 -7
- package/scripts/lib/learnings/io.mjs +289 -15
- package/scripts/lib/learnings/select.mjs +53 -4
- package/scripts/lib/learnings/store-role.mjs +209 -0
- package/scripts/lib/learnings/surface.mjs +46 -7
- package/scripts/lib/ledger-path.mjs +113 -0
- package/scripts/lib/maintenance-due-banner.mjs +63 -12
- package/scripts/lib/memory-banner.mjs +5 -6
- package/scripts/lib/memory-proposals/collector.mjs +5 -2
- package/scripts/lib/memory-proposals/sink.mjs +7 -4
- package/scripts/lib/memory-proposals/store.mjs +22 -5
- package/scripts/lib/mode-selector-accuracy.mjs +2 -1
- package/scripts/lib/no-follow-open.mjs +57 -0
- package/scripts/lib/orphan-reaper/reaper-audit.mjs +4 -2
- package/scripts/lib/own-lock-root.mjs +95 -0
- package/scripts/lib/owner-interview.mjs +2 -1
- package/scripts/lib/owner-yaml.mjs +70 -7
- package/scripts/lib/platform.mjs +161 -9
- package/scripts/lib/plugin-update-banner.mjs +15 -11
- package/scripts/lib/pre-push-local-partial.mjs +117 -0
- package/scripts/lib/pre-push-remote-gate.mjs +151 -25
- package/scripts/lib/process-group.mjs +270 -34
- package/scripts/lib/project-hygiene.mjs +28 -0
- package/scripts/lib/quality-gate.mjs +33 -9
- package/scripts/lib/reconcile/backlog.mjs +29 -15
- package/scripts/lib/reconcile/engine.mjs +26 -2
- package/scripts/lib/reconcile/idempotency.mjs +65 -3
- package/scripts/lib/reconcile/rule-expiry-sweep.mjs +53 -12
- package/scripts/lib/reconcile/verdict-ledger.mjs +224 -0
- package/scripts/lib/reconcile-nudge-banner.mjs +40 -6
- package/scripts/lib/resource-probe/evaluate.mjs +32 -9
- package/scripts/lib/resource-probe/probe-platform.mjs +3 -1
- package/scripts/lib/rule-loader.mjs +20 -10
- package/scripts/lib/scope-baseline.mjs +123 -8
- package/scripts/lib/scope-echo.mjs +5 -2
- package/scripts/lib/scope-gate.mjs +55 -20
- package/scripts/lib/session-close-backfill.mjs +75 -7
- package/scripts/lib/session-end/close-precheck.mjs +151 -0
- package/scripts/lib/session-end/phase-skip.mjs +8 -7
- package/scripts/lib/session-end/tail-runner.mjs +7 -2
- package/scripts/lib/session-end/vault-orphans.mjs +205 -0
- package/scripts/lib/session-id.mjs +9 -1
- package/scripts/lib/session-identity/own-session.mjs +63 -0
- package/scripts/lib/session-invocation.mjs +14 -3
- package/scripts/lib/session-lock.mjs +114 -0
- package/scripts/lib/session-record-repair.mjs +245 -35
- package/scripts/lib/session-registry.mjs +85 -12
- package/scripts/lib/session-schema/constants.mjs +9 -0
- package/scripts/lib/session-schema/filters.mjs +56 -1
- package/scripts/lib/session-schema/validator.mjs +15 -5
- package/scripts/lib/session-start-probes.mjs +149 -22
- package/scripts/lib/session-token-rollup.mjs +2 -1
- package/scripts/lib/session-work-trees.mjs +166 -0
- package/scripts/lib/sessions-canonical.mjs +61 -3
- package/scripts/lib/sessions-integrity-banner.mjs +17 -39
- package/scripts/lib/sessions-staleness-banner.mjs +8 -5
- package/scripts/lib/sh-quote.mjs +1 -0
- package/scripts/lib/skill-evidence-window.mjs +5 -1
- package/scripts/lib/skill-health/join.mjs +10 -9
- package/scripts/lib/skill-invocations-schema.mjs +13 -0
- package/scripts/lib/skill-judgments-schema.mjs +6 -2
- package/scripts/lib/snapshot-probe.mjs +414 -0
- package/scripts/lib/so-hud/collect.mjs +174 -0
- package/scripts/lib/so-hud/runtime.mjs +87 -0
- package/scripts/lib/spiral-carryover.mjs +8 -1
- package/scripts/lib/state-md/frontmatter-mutators.mjs +266 -8
- package/scripts/lib/state-md/idle-reset.mjs +248 -0
- package/scripts/lib/state-md/mission-status.mjs +5 -1
- package/scripts/lib/state-md/recommendations.mjs +26 -4
- package/scripts/lib/state-md-peer-guard.mjs +2 -0
- package/scripts/lib/state-md.mjs +31 -8
- package/scripts/lib/sunset/walker.mjs +4 -3
- package/scripts/lib/telemetry/consent.mjs +4 -2
- package/scripts/lib/telemetry/queue.mjs +3 -3
- package/scripts/lib/telemetry/sync.mjs +45 -9
- package/scripts/lib/test-runner/artifact-paths.mjs +10 -2
- package/scripts/lib/tmux-layout/layouts.mjs +20 -3
- package/scripts/lib/ux-grill/paths.mjs +5 -1
- package/scripts/lib/validate/check-agents.mjs +37 -6
- package/scripts/lib/validate/check-component-paths.mjs +25 -1
- package/scripts/lib/validate/check-guard-requires-parity.mjs +3 -3
- package/scripts/lib/validate/check-hooks-symmetry.mjs +53 -32
- package/scripts/lib/validate/check-json-files.mjs +3 -1
- package/scripts/lib/validate/check-learning-provenance.mjs +3 -2
- package/scripts/lib/validate/check-learnings-shell-writes.mjs +10 -4
- package/scripts/lib/validate/check-metrics-ledger-root.mjs +234 -0
- package/scripts/lib/validate/check-owner-leakage.mjs +159 -11
- package/scripts/lib/validate/check-test-posix-shell.mjs +246 -0
- package/scripts/lib/validate/check-unwired-features.mjs +4 -2
- package/scripts/lib/validate/check-vcs-repo-flag-positional.mjs +58 -0
- package/scripts/lib/validate/check-vcs-repo-flag.mjs +259 -73
- package/scripts/lib/validate/check-workflow-continue-on-error.mjs +63 -0
- package/scripts/lib/validate/confidential-names.mjs +3 -1
- package/scripts/lib/vault-mirror/migrate-ids.mjs +321 -0
- package/scripts/lib/vault-mirror/namespace.mjs +22 -9
- package/scripts/lib/vault-mirror/process.mjs +197 -65
- package/scripts/lib/vault-mirror/prune.mjs +5 -2
- package/scripts/lib/vault-mirror/render-learnings.mjs +8 -7
- package/scripts/lib/vault-mirror/render-sessions.mjs +36 -53
- package/scripts/lib/vault-mirror/retention.mjs +2 -2
- package/scripts/lib/vault-mirror/utils.mjs +106 -13
- package/scripts/lib/vault-staleness-banner.mjs +4 -7
- package/scripts/lib/vault-status/board-writer.mjs +34 -5
- package/scripts/lib/vault-status/narrative-mirror.mjs +66 -7
- package/scripts/lib/wave-executor/foreign-dispatch.mjs +20 -0
- package/scripts/lib/win-process-tree.mjs +137 -0
- package/scripts/lib/workspace.mjs +1 -1
- package/scripts/lib/worktree/constants.mjs +1 -1
- package/scripts/lib/worktree/lifecycle.mjs +1 -1
- package/scripts/lib/worktree/listing.mjs +1 -1
- package/scripts/lib/zx.mjs +50 -0
- package/scripts/mcp-server.mjs +16 -7
- package/scripts/mcp-server.sh +13 -83
- package/scripts/measure-policy-cache-effectiveness.mjs +7 -5
- package/scripts/memory-propose.mjs +10 -3
- package/scripts/migrate-cold-start-seed.mjs +7 -0
- package/scripts/migrate-legacy-learnings.sh +22 -0
- package/scripts/migrate-sessions-jsonl.mjs +16 -2
- package/scripts/migrate-vault-paths.mjs +29 -13
- package/scripts/parse-config.mjs +71 -14
- package/scripts/print-learnings-index.mjs +25 -2
- package/scripts/release-session-lock.mjs +94 -7
- package/scripts/release.mjs +451 -24
- package/scripts/relocate-vault-corpus.mjs +11 -7
- package/scripts/repair-invalid-sessions.mjs +68 -3
- package/scripts/resolve-session-invocation.mjs +57 -8
- package/scripts/run-migrate-v2-cross-repo.mjs +56 -1
- package/scripts/run-quality-gate.mjs +33 -7
- package/scripts/self-update.mjs +56 -7
- package/scripts/session-commit-files.mjs +133 -0
- package/scripts/site-numbers.mjs +52 -9
- package/scripts/snapshot-probe.mjs +156 -0
- package/scripts/so-hud.mjs +68 -0
- package/scripts/state-md-idle-reset.mjs +82 -0
- package/scripts/sweep-expired-learnings.mjs +54 -6
- package/scripts/sweep-expired-rules.mjs +4 -1
- package/scripts/validate-plugin.mjs +22 -0
- package/scripts/vault-integration-watcher.mjs +54 -6
- package/scripts/vault-mirror.mjs +9 -0
- package/scripts/vault-orphans.mjs +171 -0
- package/skills/_shared/fleet-protocol.md +166 -3
- package/skills/_shared/instruction-file-resolution.md +1 -1
- package/skills/_shared/monitor-patterns.md +44 -25
- package/skills/_shared/platform-tools.md +7 -0
- package/skills/_shared/state-ownership.md +5 -4
- package/skills/architecture/references/domain-model.md +1 -1
- package/skills/autopilot/SKILL.md +3 -2
- package/skills/bootstrap/SKILL.md +3 -1
- package/skills/bootstrap/_shared-template.md +4 -4
- package/skills/bootstrap/fast-template.md +1 -1
- package/skills/bootstrap/private-contract.md +1 -1
- package/skills/bootstrap/references/bootstrap-retroactive-flow.md +17 -9
- package/skills/bootstrap/templates/agents/project-code-review.md +5 -1
- package/skills/bootstrap/templates/agents/project-discovery.md +5 -1
- package/skills/bootstrap/templates/agents/project-quality-gate.md +5 -1
- package/skills/claude-md-drift-check/SKILL.md +3 -3
- package/skills/claude-md-drift-check/checker.mjs +61 -101
- package/skills/close/SKILL.md +20 -7
- package/skills/discovery/probes/docs-staleness.mjs +7 -5
- package/skills/discovery/probes/frontend-slop.mjs +7 -3
- package/skills/discovery/probes/ssot-code-diff.mjs +5 -3
- package/skills/discovery/probes/supply-chain-slopcheck.mjs +10 -8
- package/skills/discovery/probes/vault-narrative-staleness.mjs +30 -10
- package/skills/discovery/probes/vault-staleness.mjs +5 -3
- package/skills/discovery/probes-session.md +3 -0
- package/skills/discovery/probes-vault.md +2 -0
- package/skills/dispatcher/SKILL.md +1 -1
- package/skills/eval/SKILL.md +3 -1
- package/skills/evolve/SKILL.md +2 -0
- package/skills/evolve/references/evolve-analyze-mode.md +3 -3
- package/skills/evolve/references/evolve-dialectic-mode.md +1 -1
- package/skills/gitlab-ops/SKILL.md +7 -6
- package/skills/gitlab-portfolio/SKILL.md +2 -2
- package/skills/go/SKILL.md +13 -1
- package/skills/npm-publish/SKILL.md +5 -2
- package/skills/plan/SKILL.md +23 -10
- package/skills/plan/mode-feature.md +13 -8
- package/skills/plan/mode-new.md +6 -14
- package/skills/plan/prd-feature-template.md +19 -0
- package/skills/plan/prd-full-template.md +12 -5
- package/skills/plan/prd-premortem-panel.md +48 -0
- package/skills/plan/prd-reviewer-prompt.md +11 -3
- package/skills/plan/soul.md +1 -1
- package/skills/portfolio/SKILL.md +5 -6
- package/skills/quality-gates/SKILL.md +2 -4
- package/skills/reconcile/SKILL.md +27 -4
- package/skills/release/SKILL.md +16 -8
- package/skills/session-end/SKILL.md +97 -13
- package/skills/session-end/phase-3-2-docs-verification.md +1 -0
- package/skills/session-end/phase-3-6-tail.md +11 -7
- package/skills/session-end/phase-3-7a-recommendations.md +9 -2
- package/skills/session-end/plan-verification.md +16 -1
- package/skills/session-end/references/phase-3-7e-vault-orphans.md +63 -0
- package/skills/session-end/references/phase-3-documentation-updates.md +25 -13
- package/skills/session-end/references/phase-5-issue-cleanup.md +1 -1
- package/skills/session-end/references/session-summary-template.md +8 -0
- package/skills/session-end/session-metrics-write.md +7 -4
- package/skills/session-plan/SKILL.md +5 -3
- package/skills/session-start/SKILL.md +10 -4
- package/skills/session-start/phase-4-5-resource-health.md +11 -5
- package/skills/session-start/phase-7-5-mode-selector.md +3 -1
- package/skills/session-start/phase-8-5-express-path.md +4 -3
- package/skills/session-start/references/phase-1-2-session-lock.md +7 -5
- package/skills/session-start/references/phase-1-5-session-continuity.md +13 -5
- package/skills/session-start/references/phase-1-7-vault-status-board.md +1 -1
- package/skills/session-start/references/phase-4-ssot-environment-check.md +3 -2
- package/skills/tmux-layout/SKILL.md +2 -2
- package/skills/vault-sync/validator.mjs +3 -2
- package/skills/wave-executor/SKILL.md +5 -4
- package/skills/wave-executor/references/wave-executor-state-init.md +3 -3
- package/skills/wave-executor/references/wave-loop-dispatch.md +11 -5
- package/skills/wave-executor/references/wave-loop-review.md +13 -5
- package/skills/wave-executor/references/wave-loop-scope-manifest.md +1 -1
- package/AGENTS.md +0 -207
- package/assets/og-card.png +0 -0
- package/assets/og-card.svg +0 -47
- package/assets/wave-lifecycle.svg +0 -98
|
@@ -48,10 +48,14 @@ For session-end specifically: the preamble is DETECTION-ONLY. The lock-release p
|
|
|
48
48
|
|
|
49
49
|
```javascript
|
|
50
50
|
try {
|
|
51
|
+
const { pathToFileURL } = await import('node:url');
|
|
51
52
|
const { appendSkillInvocation, DEFAULT_SKILL_INVOCATIONS_PATH } =
|
|
52
|
-
await import('${PLUGIN_ROOT}/scripts/lib/skill-invocations-schema.mjs');
|
|
53
|
-
const
|
|
54
|
-
|
|
53
|
+
await import(pathToFileURL('${PLUGIN_ROOT}/scripts/lib/skill-invocations-schema.mjs').href);
|
|
54
|
+
const { resolveLedgerPath } =
|
|
55
|
+
await import(pathToFileURL('${PLUGIN_ROOT}/scripts/lib/ledger-path.mjs').href);
|
|
56
|
+
// In a linked worktree the main checkout's ledger when it is gitignored there,
|
|
57
|
+
// so the record survives the worktree (#1722).
|
|
58
|
+
await appendSkillInvocation(resolveLedgerPath(DEFAULT_SKILL_INVOCATIONS_PATH, process.cwd()), {
|
|
55
59
|
timestamp: new Date().toISOString(),
|
|
56
60
|
event: 'selected',
|
|
57
61
|
skill: 'session-orchestrator:session-end',
|
|
@@ -63,7 +67,7 @@ try {
|
|
|
63
67
|
|
|
64
68
|
## Phase 1: Plan Verification
|
|
65
69
|
|
|
66
|
-
> Always runs, first. Reads back the agreed plan and dispositions every item: 1.1 Done (verify with evidence) + 1.1a File-Level Grounding, 1.2 / 1.3 / 1.4 which COLLECT carryover candidates and file nothing (#769), 1.5 Discovery Scan, 1.6 Safety Review + 1.6.6 "What Not To Retry", then **1.65 Handover Alignment Gate** — the only place `[Carryover]` filing is authorized to originate, fail-open to "everything carries" — followed by 1.7 Metrics Collection, 1.8 Session Review, and 1.9 / 1.10 Mission-Status classification + breakdown. Full procedure: [`plan-verification.md`](plan-verification.md).
|
|
70
|
+
> Always runs, first. Reads back the agreed plan and dispositions every item: 1.1 Done (verify with evidence) + 1.1a File-Level Grounding + 1.1b External Acceptance (open `[Abnahme]` issues, closed only on oracle evidence), 1.2 / 1.3 / 1.4 which COLLECT carryover candidates and file nothing (#769), 1.5 Discovery Scan, 1.6 Safety Review + 1.6.6 "What Not To Retry", then **1.65 Handover Alignment Gate** — the only place `[Carryover]` filing is authorized to originate, fail-open to "everything carries" — followed by 1.7 Metrics Collection, 1.8 Session Review, and 1.9 / 1.10 Mission-Status classification + breakdown. Full procedure: [`plan-verification.md`](plan-verification.md).
|
|
67
71
|
|
|
68
72
|
## Phase 2: Quality Gate
|
|
69
73
|
|
|
@@ -145,7 +149,7 @@ discipline as `createSpiralCarryoverIssue`).
|
|
|
145
149
|
|
|
146
150
|
## Phase 3: Documentation Updates
|
|
147
151
|
|
|
148
|
-
> Always runs. Refreshes the session-lock heartbeat at entry (`updateHeartbeat`, #590-3 — skip when `persistence: false`) so a long close-out cannot lapse the 4h TTL, then walks 3.0 Defensive Cleanup → 3.1 SSOT files → 3.2 Docs Verification → 3.2a Session Handover → 3.3 Claude-rules freshness → 3.4/3.4a STATE.md write + coordinator-snapshot cleanup → 3.45 Telemetry Flush → 3.5/3.5a/3.6.x session memory, learning extraction and the mechanical skip-plan tail → 3.7/3.7a/3.7b/3.7c/3.7d metrics write, recommendations, durable commit (#490), vault board → closed, session-eval. Full procedure: [`references/phase-3-documentation-updates.md`](references/phase-3-documentation-updates.md).
|
|
152
|
+
> Always runs. Refreshes the session-lock heartbeat at entry (`updateHeartbeat`, #590-3 — skip when `persistence: false`) so a long close-out cannot lapse the 4h TTL, then walks 3.0 Defensive Cleanup → 3.1 SSOT files → 3.2 Docs Verification → 3.2a Session Handover → 3.3 Claude-rules freshness → 3.4/3.4a STATE.md write + coordinator-snapshot cleanup → 3.45 Telemetry Flush → 3.5/3.5a/3.6.x session memory, learning extraction and the mechanical skip-plan tail → 3.7/3.7a/3.7b/3.7c/3.7d/3.7e metrics write, recommendations, durable commit (#490), vault board → closed, session-eval, vault orphans named (#1563). Full procedure: [`references/phase-3-documentation-updates.md`](references/phase-3-documentation-updates.md).
|
|
149
153
|
|
|
150
154
|
## Phase 3.8: Session Lock Release (#330)
|
|
151
155
|
|
|
@@ -159,7 +163,9 @@ node scripts/release-session-lock.mjs --session-id "<raw session_id>" --json
|
|
|
159
163
|
|
|
160
164
|
`--session-id` is the PHYSICAL raw value established by session-start Phase 1.2 and stored in `.orchestrator/session.lock` `session_id` — never STATE.md `session:` and never `semantic_session_id`, both of which are attribution labels.
|
|
161
165
|
|
|
162
|
-
|
|
166
|
+
No `--repo-root`: the command looks for the checkout whose lock carries that id itself (#1730) — the registry's `session_root`, the cwd's checkout, its main checkout, a registered linked worktree — and names it in the JSON (`repo_root`, `root_source`). Before, it read only the cwd, answered `absent` with exit 0 when the lock sat in the other checkout, and the lock stayed.
|
|
167
|
+
|
|
168
|
+
The command releases the lock only when that raw id owns it, emits the terminal `orchestrator.session.lock.released` breadcrumb, and then RE-READS the lock path: **exit 0 means the lock is provably gone.** It replaces the hand-executed `release()` this phase used to prescribe, which deleted the lock silently — the SessionEnd hook then found `status: 'absent'` and emitted nothing either, so a lock lifecycle ended with no terminal event at all (#1395: 5 `lock.acquired` against 0 `lock.released` in this repo). `outcome: "absent"` is a normal exit-0 result (idempotent re-run; nothing was released, so no event is written). A verified release also leaves `.orchestrator/runtime/lock-released/<raw session_id>.json` (`release_marker: true` in the JSON), so a later `compact` or `resume` of the same raw session does not re-create the lock (#1651); `startup` and `clear` take a lock as before, and the session's next `acquire()` removes the marker.
|
|
163
169
|
|
|
164
170
|
A non-zero exit is a WARN, never a blocker — log `⚠ session-lock: <outcome>` and continue; the TTL provides automatic expiry for the next session:
|
|
165
171
|
|
|
@@ -187,7 +193,7 @@ Use Conventional Commits format:
|
|
|
187
193
|
type(scope): description
|
|
188
194
|
|
|
189
195
|
- [bullet points of what changed]
|
|
190
|
-
- Closes #IID1, #IID2 (if applicable)
|
|
196
|
+
- Closes #IID1, #IID2 (if applicable — never an `[Abnahme]` IID, plan-verification 1.1b)
|
|
191
197
|
|
|
192
198
|
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
|
193
199
|
```
|
|
@@ -218,7 +224,37 @@ between the markers and executes it, so no second copy of this command may exist
|
|
|
218
224
|
# so a consumer repo (even one without a Session Config) skips with exit 0.
|
|
219
225
|
# With a remote, Session Config `mirror` decides (#1034), read through parse-config.mjs:
|
|
220
226
|
# `mirror: none` → never push, even with a 'github' remote (explicit opt-out)
|
|
221
|
-
# `mirror: github` or key absent (`mirror: null` counts as absent) → push
|
|
227
|
+
# `mirror: github` or key absent (`mirror: null` counts as absent) → push HEAD,
|
|
228
|
+
# but only after `github-export.mjs --plain-guard` (#1645):
|
|
229
|
+
# when HEAD or origin's main (ls-remote, fetched if not
|
|
230
|
+
# local) carries .orchestrator/policy/public-export.json,
|
|
231
|
+
# the repo publishes curated only — a checkout whose
|
|
232
|
+
# CLAUDE.md predates `mirror: curated` must not push its
|
|
233
|
+
# full private history → WARN, exit 1, nothing pushed.
|
|
234
|
+
# origin unreadable → same refusal (fail closed); no
|
|
235
|
+
# 'origin' remote → refused unless `mirror: github` is
|
|
236
|
+
# set explicitly (nothing to check the policy against).
|
|
237
|
+
# `mirror: curated` → publish ONE squash snapshot (scripts/github-export.mjs,
|
|
238
|
+
# #1631) on top of the verified public github main,
|
|
239
|
+
# never HEAD. Source anchor: only when HEAD is on branch
|
|
240
|
+
# `main` AND equals origin's main (`git ls-remote`, after
|
|
241
|
+
# the 4.3 push) — GitHub main deploys production, so a
|
|
242
|
+
# feature branch or unpushed/unreviewed local commits
|
|
243
|
+
# skip (exit 0, nothing pushed); origin unreadable →
|
|
244
|
+
# WARN, exit 1. Export exit 3 = nothing to publish (no
|
|
245
|
+
# push), other non-zero → WARN, exit 1. Bound to the
|
|
246
|
+
# scanned sha (read from the export's stdout only):
|
|
247
|
+
# push URL must equal fetch URL, then `--verify <sha>`
|
|
248
|
+
# re-checks it right before the push (one commit on the
|
|
249
|
+
# ls-remote-verified github main, trailer
|
|
250
|
+
# `Source-Commit: <origin main>`, no excluded path in its
|
|
251
|
+
# tree), else WARN, exit 1; the push sends that sha, not
|
|
252
|
+
# the export ref. Plain push, no lease: the commit's
|
|
253
|
+
# parent IS the verified sha, and a plain push is
|
|
254
|
+
# fast-forward-only, so GitHub itself rejects it if its
|
|
255
|
+
# main moved to anything that is not an ancestor of that
|
|
256
|
+
# sha — the same anchor a lease would enforce, without a
|
|
257
|
+
# force flag (blocked by the destructive-command guard).
|
|
222
258
|
# any other value / config unreadable → loud WARN, no push, exit 1
|
|
223
259
|
# The plugin root is resolved, not assumed: a fresh /close shell often lacks
|
|
224
260
|
# PLUGIN_ROOT. Order per skills/_shared/config-reading.md (native PLUGIN_ROOT,
|
|
@@ -260,8 +296,49 @@ elif ! mirror_cfg=$(printf '%s' "$cfg_json" | node -e 'let s="";process.stdin.on
|
|
|
260
296
|
exit 1
|
|
261
297
|
elif [ "$mirror_cfg" = "none" ]; then
|
|
262
298
|
echo "GitHub mirror: 'mirror: none' in Session Config — skipping (not an error)."
|
|
263
|
-
elif [ -n "$mirror_cfg" ] && [ "$mirror_cfg" != "github" ]; then
|
|
264
|
-
echo "WARN GitHub mirror: unknown 'mirror: ${mirror_cfg}' (expected none | github) — nothing pushed." >&2
|
|
299
|
+
elif [ -n "$mirror_cfg" ] && [ "$mirror_cfg" != "github" ] && [ "$mirror_cfg" != "curated" ]; then
|
|
300
|
+
echo "WARN GitHub mirror: unknown 'mirror: ${mirror_cfg}' (expected none | github | curated) — nothing pushed." >&2
|
|
301
|
+
exit 1
|
|
302
|
+
elif [ "$mirror_cfg" = "curated" ]; then
|
|
303
|
+
if ! origin_ls=$(git ls-remote origin refs/heads/main 2>&1); then
|
|
304
|
+
echo "WARN GitHub mirror: cannot read origin's main — curated source not verifiable, nothing pushed." >&2
|
|
305
|
+
echo " git said: ${origin_ls}" >&2
|
|
306
|
+
exit 1
|
|
307
|
+
fi
|
|
308
|
+
origin_main=$(printf '%s\n' "$origin_ls" | awk '$2 == "refs/heads/main" { print $1 }')
|
|
309
|
+
if [ "$(git symbolic-ref --short -q HEAD)" != "main" ] || [ -z "$origin_main" ] || [ "$(git rev-parse HEAD)" != "$origin_main" ]; then
|
|
310
|
+
echo "GitHub mirror: 'mirror: curated' publishes only a HEAD on main that equals origin's main (HEAD $(git symbolic-ref --short -q HEAD || echo detached) $(git rev-parse --short HEAD), origin main ${origin_main:-none}) — skipping (not an error)."
|
|
311
|
+
else
|
|
312
|
+
# stdout carries ONLY the sha (stderr goes straight to the operator), so the
|
|
313
|
+
# sha pushed below is the one the export scanned — never a ref name that could
|
|
314
|
+
# be moved in between.
|
|
315
|
+
export_sha=$(GIT_NO_REPLACE_OBJECTS=1 node "$so_root/scripts/github-export.mjs" --source "$origin_main")
|
|
316
|
+
export_rc=$?
|
|
317
|
+
if [ "$export_rc" -eq 3 ]; then
|
|
318
|
+
echo "GitHub mirror: curated snapshot of ${origin_main} unchanged — nothing to publish."
|
|
319
|
+
elif [ "$export_rc" -ne 0 ]; then
|
|
320
|
+
echo "WARN GitHub mirror: curated export REFUSED (exit ${export_rc}, reason above) — nothing pushed." >&2
|
|
321
|
+
exit 1
|
|
322
|
+
elif ! printf '%s' "$export_sha" | grep -Eqx '[0-9a-f]{40}([0-9a-f]{24})?'; then
|
|
323
|
+
echo "WARN GitHub mirror: the export printed no commit sha — nothing pushed." >&2
|
|
324
|
+
exit 1
|
|
325
|
+
elif [ "$(git remote get-url github)" != "$(git remote get-url --push github)" ]; then
|
|
326
|
+
echo "WARN GitHub mirror: the push URL of 'github' differs from its fetch URL (pushurl or pushInsteadOf) — the verified main would not describe the push target; nothing pushed." >&2
|
|
327
|
+
exit 1
|
|
328
|
+
elif ! GIT_NO_REPLACE_OBJECTS=1 node "$so_root/scripts/github-export.mjs" --verify "$export_sha" --source "$origin_main"; then
|
|
329
|
+
echo "WARN GitHub mirror: ${export_sha} is not one clean snapshot of ${origin_main} on the verified github main — nothing pushed." >&2
|
|
330
|
+
exit 1
|
|
331
|
+
elif push_out=$(git push github "${export_sha}:refs/heads/main" 2>&1); then
|
|
332
|
+
echo "GitHub mirror: pushed curated snapshot ${export_sha} (source ${origin_main}) -> ${mirror_url}"
|
|
333
|
+
else
|
|
334
|
+
echo "WARN GitHub mirror PUSH FAILED: curated snapshot ${export_sha} is NOT on ${mirror_url}" >&2
|
|
335
|
+
echo "${push_out}" >&2
|
|
336
|
+
echo "WARN Mirror is stale — anything wired to it (site deploy) will not update." >&2
|
|
337
|
+
exit 1
|
|
338
|
+
fi
|
|
339
|
+
fi
|
|
340
|
+
elif ! GIT_NO_REPLACE_OBJECTS=1 node "$so_root/scripts/github-export.mjs" --plain-guard --mirror-key "${mirror_cfg:-unset}"; then
|
|
341
|
+
echo "WARN GitHub mirror: plain push of HEAD REFUSED (reason above) — this repo publishes curated only; nothing pushed." >&2
|
|
265
342
|
exit 1
|
|
266
343
|
elif push_out=$(git push github HEAD 2>&1); then
|
|
267
344
|
echo "GitHub mirror: pushed $(git rev-parse HEAD) -> ${mirror_url}"
|
|
@@ -278,6 +355,12 @@ State 3 exits non-zero on purpose: it is the only machine-readable signal that t
|
|
|
278
355
|
behind. Report it to the operator in the session summary; do not retry silently and do not
|
|
279
356
|
swallow it with `|| true`.
|
|
280
357
|
|
|
358
|
+
### 4.5 Only a Pipeline or Release Train Left
|
|
359
|
+
|
|
360
|
+
Do not wait for a pipeline or release train inside session-end. If the session has no other work left, apply `skills/_shared/fleet-protocol.md` § Rücklauf an den Navigator (Pflicht) with the wait case `zug-<name>`: exactly one report in this `/close` turn, only with fleet integration (`flotte: true`). That the waiting answer before `/close` already reported the train is intended — every waiting turn triggered by an input (`/close` is one) reports once (#1611); turns woken by `/loop`, a Monitor event, an own background agent or shell, or ScheduleWakeup do not. Write NO handover or train manifest here — a coordinator only acts on one written with the waiting answer before `/close` (same section, Übergabe und Zug-Manifest) — and never invoke `/close` or another close yourself. Then continue with Phase 4a; never gate the close on a reply (CSM-004).
|
|
361
|
+
|
|
362
|
+
**Queue marks on the own handover (#1639).** With fleet integration — whether or not a train is left — re-check the newest own handover or train manifest in the fleet folder (fleet-protocol § Flotten-Ordner und Session-Name gives `<name>`, `<cwd>` and `<startedAt>`): `node "$PLUGIN_ROOT/scripts/handover-queue-check.mjs" --name <name> --fleet-dir <cwd>/.orchestrator/runtime/fleet --since <startedAt> --apply --repo <repo> [--navigator-cli <koordinator-cli>]`, same `<repo>` and CLI as fleet-protocol Schritt 1a. It files every open point that still lacks a `queue:#`/`issue:#` mark and writes the id into the file; this edits the existing file and is not the forbidden new handover. `--since` keeps a predecessor's file of the same session name out (names repeat across days). Exit 3 (points left unmarked, e.g. the CLI is unreachable): name `Übergabe ohne Queue-IDs: <n> Punkte` with the listed points in the session summary. It never blocks the close; without fleet integration or without an own file it is a silent no-op (exit 0).
|
|
363
|
+
|
|
281
364
|
## Phase 4a: Auto-Promoted Worktree Cleanup (#575 P3.2)
|
|
282
365
|
|
|
283
366
|
> Skip when `persistence: false`; skip silently unless the CURRENT worktree is an auto-promoted sibling — `detectAutoPromotedWorktree()` from `scripts/lib/session-end/worktree-cleanup.mjs` (marker `.orchestrator/promoted-from.json` first, legacy basename match as fallback, #1069). Runs AFTER the Phase 4 commit+push, never before (#490 durableCommit ordering). Clean worktree → auto-remove with WARN; dirty → the 3-option AUQ (`Behalten` / `Löschen` / `Manuell`) before any destructive action, per PSA-003. Full procedure: [`references/phase-4a-worktree-cleanup.md`](references/phase-4a-worktree-cleanup.md).
|
|
@@ -292,7 +375,7 @@ swallow it with `|| true`.
|
|
|
292
375
|
|
|
293
376
|
## Phase 6: Final Report
|
|
294
377
|
|
|
295
|
-
Present to the user the **Session Summary**: Completed / Carried Over / Dropped at Handover Gate (#769) / New Issues Created / Unresolved Review Findings (MED-LOW, #617) / Metrics — including the Docs Health line rendered from the Phase 2.3 result and the Custom Phases line from Phase 2.5 — / Next Session Recommendations. Full template plus the Test-delta and Documentation-Coverage anchors: [`references/session-summary-template.md`](references/session-summary-template.md).
|
|
378
|
+
Present to the user the **Session Summary**: Completed / Abnahme offen (open `[Abnahme]` issues from Phase 1.1b — never counted as Completed, #1678) / Carried Over / Dropped at Handover Gate (#769) / New Issues Created / Unresolved Review Findings (MED-LOW, #617) / Metrics — including the Docs Health line rendered from the Phase 2.3 result and the Custom Phases line from Phase 2.5 — / Next Session Recommendations. Full template plus the Test-delta and Documentation-Coverage anchors: [`references/session-summary-template.md`](references/session-summary-template.md).
|
|
296
379
|
|
|
297
380
|
## Sub-File Reference
|
|
298
381
|
|
|
@@ -305,7 +388,7 @@ Present to the user the **Session Summary**: Completed / Carried Over / Dropped
|
|
|
305
388
|
| `metrics-collection.md` | Phase 1.7 JSONL schema and conditional field rules |
|
|
306
389
|
| `vault-operations.md` | Phase 2.1 validator bash contract and reporting matrix |
|
|
307
390
|
| `drift-operations.md` | Phase 2.2 drift-checker bash contract and reporting matrix |
|
|
308
|
-
| `references/phase-3-documentation-updates.md` | Phase 3 full procedural body — final heartbeat (#590-3), 3.0 Defensive Cleanup, 3.1 SSOT files, 3.2/3.2a docs + handover, 3.3 rules freshness, 3.4/3.4a STATE.md write + snapshot cleanup, 3.45 Telemetry Flush, 3.5/3.5a/3.6.x memory + learnings + tail dispatcher, 3.7/3.7a/3.7b/3.7c/3.7d metrics, recommendations, durable commit, vault board, session-eval |
|
|
391
|
+
| `references/phase-3-documentation-updates.md` | Phase 3 full procedural body — final heartbeat (#590-3), 3.0 Defensive Cleanup, 3.1 SSOT files, 3.2/3.2a docs + handover, 3.3 rules freshness, 3.4/3.4a STATE.md write + snapshot cleanup, 3.45 Telemetry Flush, 3.5/3.5a/3.6.x memory + learnings + tail dispatcher, 3.7/3.7a/3.7b/3.7c/3.7d/3.7e metrics, recommendations, durable commit, vault board, session-eval, vault orphans |
|
|
309
392
|
| `phase-3-2-docs-verification.md` | Phase 3.2 full procedural body — docs-tasks load, SESSION_START_REF, per-task loop, mode-gated report, Documentation Coverage block |
|
|
310
393
|
| `learning-patterns.md` | Phases 3.5a + 3.6 extraction heuristics, confidence updates, passive decay, and JSONL write procedure |
|
|
311
394
|
| `phase-3-6-tail.md` | Phase 3.6.x tail — detail procedures for the tail phases: 3.6.3 Memory-Proposals Collection (`collectProposals` + AUQ multiSelect + `promoteAndClear`, composing `writeApproved` + `clearProposalsJsonl` behind a mechanical write-before-clear guard, #828), **3.6.4 Expired-Learnings Sweep — MECHANICAL since 2026-09-09**: after `planTailPhases()`, call `runTailPhases({ repoRoot, plan })` from `scripts/lib/session-end/tail-runner.mjs` (delegating to `runExpiredSweep`) and report `result['3.6.4']` (`ran`, `scanned`, `archived`); the event `orchestrator.learnings.sweep_applied` is the proof it ran (Epic #723 B4), **3.6.5 Auto-Dream — RETIRED** and **3.6.7 Auto-Dialectic — RETIRED** (both replaced by the session-start `maintenance-due` probe, `checkMaintenanceDue` in `scripts/lib/maintenance-due-banner.mjs`; headings kept as two-line stubs because other docs cite them), 3.6.6 Skill-Applied Judge (#645 L3 — two calls, not one: `buildSkillEvidence` from `scripts/lib/skill-evidence-window.mjs` renders this session's transcript into the evidence window, then `runSkillJudge` judges it and the coordinator writes each judgment via `appendSkillJudgment`; on empty evidence text `runSkillJudge` returns `status: 'no-evidence'` and does NOT dispatch, #1399 — the exact call shape is in the sub-file, do not reconstruct it here), 3.6.8 Reconciliation Rule Proposals (#696 FA3 — `runReconcile` + AUQ + `writeApprovedRules`). Loaded on demand by the SKILL.md skip-plan dispatcher (#724) — only phases with `run: true` in the `planTailPhases()` plan execute |
|
|
@@ -316,8 +399,9 @@ Present to the user the **Session Summary**: Completed / Carried Over / Dropped
|
|
|
316
399
|
| `phase-3-7a-recommendations.md` § 3.7b | Phase 3.7b full procedural body — `withDurableCommit` invocation for `sessions.jsonl` + `STATE.md` (#490 AC2), `enabled:false` local no-op, autopilot.jsonl exclusion note |
|
|
317
400
|
| `references/phase-3-documentation-updates.md` § 3.7c | Vault Board → Closed (#674) — `mirrorBoard({ explicitStatus: 'closed' })` transitions this repo's board row to `closed`; gated on `vault-integration.enabled`, generator-marked + idempotent, non-blocking, ordered after 3.7b and before 3.7d/3.4/3.8 |
|
|
318
401
|
| `references/phase-3-documentation-updates.md` § 3.7d | Session-Eval (opt-in — #803) — `node scripts/eval-session.mjs --json` scores the just-closed session; gated on `eval.enabled` + `eval.mode != off` (parsed by `scripts/lib/config/eval.mjs`), optional `eval-judge` dispatch + `writeEvalReport`, advisory/never-blocks-close, ordered after 3.7 (record must exist) and before 3.4/Phase 4 (record committed with the session). Full flow in `skills/eval/SKILL.md` |
|
|
402
|
+
| `references/phase-3-7e-vault-orphans.md` | Phase 3.7e Vault Orphans (#1563) — `node scripts/vault-orphans.mjs --since <started_at> --json` (`findVaultOrphans` in `scripts/lib/session-end/vault-orphans.mjs`, read-only `git ls-files --others`) names untracked vault files modified since the session start, minus `_generator: session-orchestrator-` files; candidates only (mtime is no proof), one Open Question via `appendOpenQuestionOnDisk`, never commits/moves/deletes in the vault; `unavailable` = "could not measure", never "no orphans"; gated on `vault-integration.enabled`, ordered after 3.7d and before 3.4 |
|
|
319
403
|
| (inline) Phase 3.8 | Session Lock Release — `release()` uses the physical raw `session_id`; raw mismatch/absent is non-fatal but never repaired with semantic labels or proof (live ambiguity remains for TTL/Reaper); fs-errors are non-fatal; runs after STATE.md writes and before Phase 4 commit staging |
|
|
320
|
-
| (inline) Phase 4 | Commit & Push — stage individually (PSA-004), commit, push to origin, then the `github-mirror-push` block (4 states: not-a-repo / no `github` remote / pushed / push failed) |
|
|
404
|
+
| (inline) Phase 4 | Commit & Push — stage individually (PSA-004), commit, push to origin, then the `github-mirror-push` block (4 states: not-a-repo / no `github` remote / pushed / push failed), then 4.5 (only a pipeline or train left → one fleet Rücklauf `zug-<name>` in this turn, fleet integration only; no manifest, no self-close) |
|
|
321
405
|
| `references/phase-4a-worktree-cleanup.md` | Phase 4a full procedural body — auto-promoted-worktree detection (`detectAutoPromotedWorktree`, marker-keyed since #1069), clean-check, clean auto-remove path, dirty 3-option AUQ (`Behalten`/`Löschen`/`Manuell`), PSA-003 + #490 ordering rationale |
|
|
322
406
|
| `references/phase-4b-worktree-orphan-sweep.md` | Phase 4b full procedural body — `checkWorktreeOrphans()` read-only proposal set, the coordinator-rendered AUQ, opt-in `worktree-orphans.enabled` gate |
|
|
323
407
|
| `references/phase-5-issue-cleanup.md` | Phase 5 full procedural body — close resolved issues via `node "$PLUGIN_ROOT/scripts/lib/issue-close-strip-labels.mjs" --close` (strip `status:*` → close → re-read; `closed: false` is a finding, not done, #308), Step 3 filing of the Phase 1.65 carry-list incl. the deferred `createSpiralCarryoverIssue` and `markOpenQuestionAnsweredOnDisk`, Step 3b `[Backlog-Sammel]` overflow, discovery-issue creation |
|
|
@@ -36,6 +36,7 @@ process.stdout.write(JSON.stringify(tasks));
|
|
|
36
36
|
Run the accessor in `plan-verification.md § SESSION_START_REF accessor`; it exports `$SESSION_RANGE`, one complete diff range. Summary:
|
|
37
37
|
- Primary: the pinned `session-start-ref` from STATE.md frontmatter → `<sha>..HEAD`.
|
|
38
38
|
- Field absent: the baseline `resolveBaselineRange()` resolves → `<base>...HEAD` (never a hard-coded `origin/main`).
|
|
39
|
+
- Multi-tree (#1659): also run `node scripts/session-commit-files.mjs` and treat its union as additional changed files (commits in the session's other own worktrees). The own-tree range above stays authoritative; no diff across trees is computed; skipped trees (stderr) are unmeasured, not unchanged.
|
|
39
40
|
- Nothing resolves: `$SESSION_RANGE` is empty and the accessor names `no-baseline-ref` or `unresolvable-ref` — log `⚠ Phase 3.2: no session range (<reason>). Docs verification skipped.` and skip Phase 3.2. Never treat the empty range as "no docs changed".
|
|
40
41
|
|
|
41
42
|
If `git diff` itself fails (network issue, corrupt repo), log:
|
|
@@ -147,12 +147,12 @@ import { runTailPhases } from '${PLUGIN_ROOT}/scripts/lib/session-end/tail-runne
|
|
|
147
147
|
|
|
148
148
|
const tail = await runTailPhases({ repoRoot: process.cwd(), plan });
|
|
149
149
|
const sweep = tail['3.6.4'];
|
|
150
|
-
// { ran: true, scanned, archived, archivePath } | { ran: false, reason: 'plan-skip' | 'no-plan' | 'error', error? }
|
|
150
|
+
// { ran: true, scanned, archived, archivePath } | { ran: false, reason: 'plan-skip' | 'no-plan' | 'store-role-inbox' | 'error', error? }
|
|
151
151
|
```
|
|
152
152
|
|
|
153
153
|
- `runTailPhases` delegates to `runExpiredSweep({ repoRoot, plan, now })` — the same module's single-phase entry point — and returns a KEYED shape so a caller keeps working when a second phase becomes mechanical. Today exactly one phase is: 3.6.3, 3.6.5–3.6.8 stay coordinator-executed because they are AUQ-gated or need a subagent dispatch a library function cannot make.
|
|
154
154
|
- **Never throws, fails CLOSED.** Any error yields `{ ran: false, reason: 'error' }` and the close proceeds. Stale-past-grace entries move into `.orchestrator/metrics/learnings-archive.jsonl` (append-only, never deleted).
|
|
155
|
-
- **Report** `sweep.ran`, `sweep.scanned` and `sweep.archived` in the Phase 6 Final Report, e.g. `expired-sweep: 12 archived of 640 scanned`. When `ran: false`, report the `reason` instead — a skipped sweep is a stated outcome, never silence.
|
|
155
|
+
- **Report** `sweep.ran`, `sweep.scanned` and `sweep.archived` in the Phase 6 Final Report, e.g. `expired-sweep: 12 archived of 640 scanned`. When `ran: false`, report the `reason` instead — a skipped sweep is a stated outcome, never silence. `store-role-inbox` means this host only appends to `learnings-inbox.jsonl` (owner.yaml `learnings.store-role: inbox`, #1516); the writer host sweeps.
|
|
156
156
|
- **The proof it ran is the event `orchestrator.learnings.sweep_applied`** in `.orchestrator/metrics/events.jsonl` (payload source `session-end-3.6.4`, which separates it from the standalone CLI). A close claiming a sweep with no such event did not sweep.
|
|
157
157
|
|
|
158
158
|
The standalone `node scripts/sweep-expired-learnings.mjs --apply --json` CLI remains available for manual/out-of-session use; it is no longer the session-end path.
|
|
@@ -180,17 +180,19 @@ After learnings are written (Phase 3.6), and when the judge is enabled, run a **
|
|
|
180
180
|
|
|
181
181
|
2. Determine the **judged set** — only THIS session's selected skills.
|
|
182
182
|
|
|
183
|
-
> **Join on the RAW UUID, never on the semantic session id.** The writer is the `PreToolUse` hook `hooks/skill-invocation-telemetry.mjs`, which stamps `session_id` straight from the hook payload (`:190`) — that is the harness UUID. Measured 2026-09-19 over `.orchestrator/metrics/skill-invocations.jsonl` (841 lines): **767 raw UUIDs, 55 semantic ids, 17 null**. Joining on `main-<date>-session-N` therefore matches ~0 rows for a current session, the judged set comes back empty, and the phase reports `empty-input` — a silent no-op indistinguishable from "this session used no skills". The raw id is `process.env.CLAUDE_CODE_SESSION_ID
|
|
183
|
+
> **Join on the RAW UUID, never on the semantic session id.** The writer is the `PreToolUse` hook `hooks/skill-invocation-telemetry.mjs`, which stamps `session_id` straight from the hook payload (`:190`) — that is the harness UUID. Measured 2026-09-19 over `.orchestrator/metrics/skill-invocations.jsonl` (841 lines): **767 raw UUIDs, 55 semantic ids, 17 null**. Joining on `main-<date>-session-N` therefore matches ~0 rows for a current session, the judged set comes back empty, and the phase reports `empty-input` — a silent no-op indistinguishable from "this session used no skills". The raw id is `process.env.CLAUDE_CODE_SESSION_ID`; it is also the id that names the transcript file in step 3. The repo-global `.orchestrator/session.lock` may name a PEER session (#1664, same class as #1663), so its `session_id` counts only via `resolveSessionIds()`, which returns it only when it is among this process's own ids (`readProcessLocalSessionIds()`) and `null` otherwise. No provable id → empty judged set → `empty-input`, never a judgment of the peer's transcript.
|
|
184
184
|
|
|
185
185
|
```javascript
|
|
186
186
|
import { readFileSync } from 'node:fs';
|
|
187
|
-
import {
|
|
187
|
+
import { resolveSessionIds } from '${PLUGIN_ROOT}/scripts/lib/state-md.mjs';
|
|
188
188
|
|
|
189
|
+
// Lock id only when provably OWN — never a peer's (#1664).
|
|
189
190
|
const rawSessionId =
|
|
190
191
|
(process.env.CLAUDE_CODE_SESSION_ID || '').trim() ||
|
|
191
|
-
(
|
|
192
|
+
resolveSessionIds({ repoRoot: process.cwd() }).session_id;
|
|
192
193
|
|
|
193
|
-
|
|
194
|
+
// No id → no join: `null` would otherwise match the null-session rows.
|
|
195
|
+
const selectedSkills = !rawSessionId ? [] : [...new Set(
|
|
194
196
|
readFileSync('.orchestrator/metrics/skill-invocations.jsonl', 'utf8')
|
|
195
197
|
.split('\n').filter(Boolean)
|
|
196
198
|
.flatMap((l) => { try { return [JSON.parse(l)]; } catch { return []; } })
|
|
@@ -207,6 +209,7 @@ After learnings are written (Phase 3.6), and when the judge is enabled, run a **
|
|
|
207
209
|
import { buildSkillEvidence } from '${PLUGIN_ROOT}/scripts/lib/skill-evidence-window.mjs';
|
|
208
210
|
import { runSkillJudge, evidenceBudgetChars } from '${PLUGIN_ROOT}/scripts/lib/skill-judge.mjs';
|
|
209
211
|
import { appendSkillJudgment } from '${PLUGIN_ROOT}/scripts/lib/skill-judgments-schema.mjs';
|
|
212
|
+
import { resolveLedgerPath } from '${PLUGIN_ROOT}/scripts/lib/ledger-path.mjs';
|
|
210
213
|
import path from 'node:path';
|
|
211
214
|
|
|
212
215
|
const budgetTokens = config['skill-evolution']['judge-budget-tokens'] ?? 8000;
|
|
@@ -253,7 +256,8 @@ After learnings are written (Phase 3.6), and when the judge is enabled, run a **
|
|
|
253
256
|
4. On `result.status === 'ok'`: the **COORDINATOR** writes each returned judgment to the sidecar. Stamp the per-record metadata (`timestamp`, `event: 'judged'`, `session_id`, `advisory: true`, `model`, `schema_version: 1`) and append:
|
|
254
257
|
|
|
255
258
|
```javascript
|
|
256
|
-
|
|
259
|
+
// In a linked worktree the main checkout's ledger when it is gitignored there (#1722).
|
|
260
|
+
const judgmentsPath = resolveLedgerPath('.orchestrator/metrics/skill-judgments.jsonl', process.cwd());
|
|
257
261
|
const nowIso = new Date().toISOString();
|
|
258
262
|
for (const j of result.judgments) {
|
|
259
263
|
await appendSkillJudgment(
|
|
@@ -24,6 +24,10 @@ try {
|
|
|
24
24
|
// parallel-session CWD drift). Passing an undefined repoRoot here sends every close down
|
|
25
25
|
// the fail-open catch below and silently omits all 5 fields (GitLab #1036).
|
|
26
26
|
const repoRoot = execSync('git rev-parse --show-toplevel', {encoding: 'utf8'}).trim();
|
|
27
|
+
// SAME path Phase 3.4 writes `status: completed` into (#1640): the STATE.md
|
|
28
|
+
// this session's state-init wrote. Pass it explicitly — never let the
|
|
29
|
+
// resolver re-guess, or a foreign .claude/STATE.md can take the fields.
|
|
30
|
+
const statePath = '<state-dir>/STATE.md';
|
|
27
31
|
|
|
28
32
|
// In-memory session metrics — pulled from the session's running state,
|
|
29
33
|
// NOT re-read from sessions.jsonl (which was just-written in Phase 3.7).
|
|
@@ -41,8 +45,9 @@ try {
|
|
|
41
45
|
'rationale': rec.rationale,
|
|
42
46
|
};
|
|
43
47
|
|
|
44
|
-
await updateFrontmatterFieldsOnDisk(repoRoot, fields);
|
|
45
|
-
|
|
48
|
+
const res = await updateFrontmatterFieldsOnDisk(repoRoot, fields, {statePath});
|
|
49
|
+
if (res.reason) throw new Error('STATE.md not written (' + res.reason + '): ' + res.path);
|
|
50
|
+
console.log('Recommendations written to ' + res.path + ': ' + rec.mode + ' (' + rec.rationale + ')');
|
|
46
51
|
} catch (err) {
|
|
47
52
|
// AC3: defensive — exception must NOT block Phase 3.4 status: completed.
|
|
48
53
|
mkdirSync('.orchestrator/metrics', {recursive: true});
|
|
@@ -63,6 +68,8 @@ try {
|
|
|
63
68
|
|
|
64
69
|
**Repo-root contract (#1036 / Kanevry#65):** `updateFrontmatterFieldsOnDisk(repoRoot, fields)` takes the repo root as its FIRST argument and rejects a missing one via `requireRepoRoot()` — there is no implicit `process.cwd()` fallback, deliberately (parallel-session CWD drift, PSA rules). Run the snippet verbatim, including the `repoRoot` binding: an `undefined` first argument throws *before* STATE.md is touched, and the AC3 catch in the snippet turns that throw into a green close with all 5 fields missing. That failure is invisible in STATE.md — it looks exactly like "this session produced no recommendation".
|
|
65
70
|
|
|
71
|
+
**Shared path source (#1640):** `statePath` is the one `<state-dir>/STATE.md` value this close uses for BOTH Phase 3.7a and Phase 3.4 — substitute the identical path in both snippets. The writer still refuses a target that is another session's COMPLETED record (`reason: 'foreign-state-md'`, stderr names the file); it never overwrites such a record and never redirects the write. Phase 3.4 re-stamps `session-id` to the closing process's own id, so a re-run of this phase after 3.4 still finds its own file.
|
|
72
|
+
|
|
66
73
|
**Data source guarantee:** The three inputs (`completionRate`, `carryoverRatio`, `carryoverIssues`) MUST come from the in-memory session metrics object built in Phase 1.7, NOT from a re-read of `.orchestrator/metrics/sessions.jsonl`. Reading the just-written JSONL would introduce a circular dependency and risk reading a truncated line if Phase 3.7's `appendJsonl` was mid-flush.
|
|
67
74
|
|
|
68
75
|
**Field precision:**
|
|
@@ -43,6 +43,7 @@ Read back the session plan that was agreed at the start. For EACH planned item:
|
|
|
43
43
|
- **Verify with evidence**: read the changed files, check git diff, run relevant test
|
|
44
44
|
- Confirm acceptance criteria are met
|
|
45
45
|
- Mark as completed
|
|
46
|
+
- An `[Abnahme]` issue is never a Done item, even when its implementation landed this session — it goes to 1.1b (#1678)
|
|
46
47
|
|
|
47
48
|
### 1.1a File-Level Grounding
|
|
48
49
|
|
|
@@ -51,7 +52,7 @@ Read back the session plan that was agreed at the start. For EACH planned item:
|
|
|
51
52
|
Compare the files the plan said would be touched against the files actually changed in the session. Catches both **scope creep** (files changed that were not in any agent's prompt scope) and **incomplete coverage** (files in the plan that were never edited).
|
|
52
53
|
|
|
53
54
|
1. **Planned files** = union of all file paths from agent prompt scopes across all waves. Source: STATE.md Wave History, falling back to the original session plan's per-agent "Files:" specs. Glob patterns are expanded against the working tree at session-start time.
|
|
54
|
-
2. **Actual files** = `git diff --name-only "$SESSION_RANGE"` (accessor above: the pinned `session-start-ref`, else the resolved baseline range). Empty `$SESSION_RANGE` → skip 1.1a with the accessor's reason.
|
|
55
|
+
2. **Actual files** = `git diff --name-only "$SESSION_RANGE"` (accessor above: the pinned `session-start-ref`, else the resolved baseline range). Empty `$SESSION_RANGE` → skip 1.1a with the accessor's reason. **Multi-tree (#1659):** also run `node scripts/session-commit-files.mjs` (session id defaults to `$CLAUDE_CODE_SESSION_ID`; `--json` prints `{ trees, perTree, skipped, union, malformed }`) and add its union — files committed in the session's OTHER worktrees, recorded by `hooks/pre-bash-staging-fence.mjs` as `orchestrator.session.work_tree` events — to Actual. The own-tree `session-start-ref` range stays authoritative for the own tree; the CLI diffs each recorded tree only inside itself, never across trees. Trees it lists as skipped (stderr) were not measured: report them, do not read them as "no changes".
|
|
55
56
|
3. **Compute discrepancies:**
|
|
56
57
|
- **Touched** = files in both Planned and Actual
|
|
57
58
|
- **Unplanned (scope creep)** = files in Actual but not in Planned
|
|
@@ -83,6 +84,20 @@ Compare the files the plan said would be touched against the files actually chan
|
|
|
83
84
|
```
|
|
84
85
|
The metrics field is conditional on `grounding-check: true` — when the gate is off, omit the field entirely (do not write `null`).
|
|
85
86
|
|
|
87
|
+
### 1.1b External Acceptance (`[Abnahme]` issues, #1678)
|
|
88
|
+
|
|
89
|
+
`/plan` files every PRD § External Acceptance row as its own issue `[Abnahme] <Kriterium>` — real-world acceptance by the owner or an external oracle, which the implementation issue does not close. List the OPEN ones, separate from 1.1:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
glab issue list --search "[Abnahme]" --in title --per-page 100 # GitLab (open is the default)
|
|
93
|
+
gh issue list --search "[Abnahme] in:title" --state open --limit 100 # GitHub
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
- **Title filter:** keep only titles that START with `[Abnahme]` — GitHub search ignores punctuation and also matches e.g. "Abnahme-Checkliste".
|
|
97
|
+
- **Scope:** keep only issues linked to this session's planned issues or their epic (issue links, `Epic`/`Parent` reference in the body, shared epic label). If the link is not determinable, list all open `[Abnahme]` issues and say so in the report (`scope: all open — link not determinable`).
|
|
98
|
+
- **Per issue:** `#<IID> <Kriterium> — owner: <from the body, else assignee, else "Owner"> — not-before: <date from the body or a label, else —>`. `/plan` writes owner, not-before, pass condition, fallback and oracle command into the body (`skills/plan/SKILL.md` § 6.1), so read the body first.
|
|
99
|
+
- **Closing rule:** an `[Abnahme]` issue is closed ONLY with oracle evidence in the closing comment — the command plus its result, or the owner's observation, quoted and dated. A green test alone is not enough. session-end never closes one on its own because the implementation landed, and files no carryover candidate for it (it stays open by design); it reports it in Phase 6 under **Abnahme offen**.
|
|
100
|
+
|
|
86
101
|
### 1.2 Partially Done Items
|
|
87
102
|
- Document what was completed and what remains
|
|
88
103
|
- **Do NOT file the carryover issue here (#769).** Collect a carryover **candidate** instead — append it to the in-memory candidate list that the Phase 1.65 Handover Alignment Gate consumes. The issue is filed (only if the gate confirms it) in Phase 5 Step 3. Candidate record (JS keys as `routeCandidates` / `normalizeCandidate` read them — `source-phase`→`sourcePhase`, `origin-issue`→`originIssue`; see `plan-verification.md § Candidate Record Format`):
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
# Phase 3.7e: Vault Orphans (#1563)
|
|
2
|
+
|
|
3
|
+
> Sub-file of the session-end skill (#1157 - agentskills.io: SKILL.md core < 500 lines, procedure in `references/`). `SKILL.md` and `references/phase-3-documentation-updates.md` keep a short stub naming this phase and its gate condition.
|
|
4
|
+
|
|
5
|
+
## Why
|
|
6
|
+
|
|
7
|
+
A session that writes into the Meta-Vault (an audit, a report, a review, a handover) leaves an untracked file in ANOTHER repo. Its own close commits only in its own repo, and vault-mirror auto-commits only its own generator-marked files in `40-learnings/` and `50-sessions/` (`scripts/lib/vault-mirror/auto-commit.mjs`). Nothing else ever looks. Measured 2026-10-11: 268 untracked files had piled up in one vault checkout over five days. The writing session is the only one that knows it wrote them, so this phase makes it look before it closes.
|
|
8
|
+
|
|
9
|
+
## Gate
|
|
10
|
+
|
|
11
|
+
- Skip silently unless `vault-integration.enabled: true` in Session Config.
|
|
12
|
+
- Skip silently when `<state-dir>/STATE.md` does not exist or has no `started_at` in its frontmatter (`persistence: false`): there is no session window to measure against and no place to record the result.
|
|
13
|
+
|
|
14
|
+
## Ordering
|
|
15
|
+
|
|
16
|
+
Runs AFTER Phase 3.7d (Session-Eval) and BEFORE Phase 3.4 (`status: completed`), so the Open Question below is written while STATE.md is still `status: active`. See the Phase 3.4 Runtime Ordering Note.
|
|
17
|
+
|
|
18
|
+
## Procedure
|
|
19
|
+
|
|
20
|
+
1. Read `started_at` from the STATE.md frontmatter (the same value Phase 3.7 writes into `sessions.jsonl`).
|
|
21
|
+
2. Run the read-only probe from the repo root, so the host-local vault-dir resolution (`SO_VAULT_DIR` > owner.yaml > committed `vault-integration.vault-dir`) sees this repo's Session Config:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
node "$PLUGIN_ROOT/scripts/vault-orphans.mjs" --since "<STATE.md started_at>" --json
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Exit `0` = measured (the JSON may hold zero candidates), `1` = bad arguments (fix the call), `2` = could not measure. The JSON is the result of `findVaultOrphans()` in `scripts/lib/session-end/vault-orphans.mjs`:
|
|
28
|
+
|
|
29
|
+
- `{ status: 'ok', candidates: [{ path, mtimeMs }], total, truncated, untracked }` - `path` is vault-relative; `candidates` holds at most 200 entries sorted by path, `total` counts all of them, `untracked` is everything git listed before the filters.
|
|
30
|
+
- `{ status: 'unavailable', reason, detail?, candidates: [] }` - no vault dir, not a git repo, or git failed.
|
|
31
|
+
|
|
32
|
+
A candidate is an untracked, not gitignored vault file whose mtime is at or after `started_at` and whose first 4 KB carry no `_generator: session-orchestrator-` frontmatter line (those files belong to vault-mirror, the narrative mirror, the active-sessions board or the portfolio writer).
|
|
33
|
+
|
|
34
|
+
3. Disposition:
|
|
35
|
+
|
|
36
|
+
- **`total > 0`** - NAME the candidates in the Final Report (see `session-summary-template.md`, line `Vault orphans`), then record ONE Open Question in STATE.md, listing up to 10 paths and the count:
|
|
37
|
+
|
|
38
|
+
```javascript
|
|
39
|
+
import { appendOpenQuestionOnDisk } from '${PLUGIN_ROOT}/scripts/lib/state-md.mjs';
|
|
40
|
+
|
|
41
|
+
const shown = result.candidates.slice(0, 10).map((c) => c.path);
|
|
42
|
+
const more = result.total - shown.length;
|
|
43
|
+
await appendOpenQuestionOnDisk(process.cwd(), {
|
|
44
|
+
question:
|
|
45
|
+
`Vault: ${result.total} untracked file(s) modified during this session: ` +
|
|
46
|
+
`${shown.join(', ')}${more > 0 ? ` (+${more} more)` : ''}. ` +
|
|
47
|
+
'Commit, move or delete them in the vault - mtime is a hint, not proof this session wrote them.',
|
|
48
|
+
source: 'session-end/3.7e',
|
|
49
|
+
priority: 'medium',
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The question survives the next session-start Idle Reset and is surfaced there as a forced read (`skills/_shared/state-ownership.md` Open Questions), so the next session or the vault owner sorts the files in.
|
|
54
|
+
- **`total === 0`** - report `none since session start` in the Final Report. No Open Question.
|
|
55
|
+
- **`status: unavailable`** (exit 2) - report `could not measure (<reason>)`. NEVER report it as "no orphans": a missing measurement is not a zero. No Open Question.
|
|
56
|
+
|
|
57
|
+
## What this phase never does
|
|
58
|
+
|
|
59
|
+
It does NOT commit into the vault, does NOT delete, does NOT move anything (PSA-003). The mtime window does not prove that this session wrote a file - a concurrent session of another repo may have written the same file in the same window - and the vault is another repo with its own rules. The phase reports; the vault owner disposes.
|
|
60
|
+
|
|
61
|
+
**Non-blocking:** a probe failure, a non-zero exit or a failed STATE.md write MUST NOT block the close. Log one `WARNING: vault orphans - <reason>; continuing close` line and proceed.
|
|
62
|
+
|
|
63
|
+
No Session Config key and no event type belong to this phase; it is a report.
|
|
@@ -11,7 +11,9 @@
|
|
|
11
11
|
> // so a long-idle deep session's 4h-TTL lock does not lapse mid-close.
|
|
12
12
|
> // BEFORE Phase 3.8 lock-release (which deletes the lock).
|
|
13
13
|
> import { updateHeartbeat } from 'scripts/lib/session-lock.mjs';
|
|
14
|
-
>
|
|
14
|
+
> import { resolveOwnLockRootFor } from 'scripts/lib/own-lock-root.mjs';
|
|
15
|
+
> // The checkout holding this session's lock, not the cwd's (#1730) — at the wrong root nothing is written.
|
|
16
|
+
> updateHeartbeat({ sessionId, repoRoot: resolveOwnLockRootFor({ sessionId }).root });
|
|
15
17
|
> ```
|
|
16
18
|
>
|
|
17
19
|
> Skip silently if `persistence: false` in Session Config (no session.lock exists in that mode).
|
|
@@ -63,24 +65,24 @@ Review `<state-dir>/rules/` files that are relevant to this session's work:
|
|
|
63
65
|
|
|
64
66
|
### 3.4 Update STATE.md
|
|
65
67
|
|
|
66
|
-
> **Ownership Reference:** See `skills/_shared/state-ownership.md`. session-end is authorized to set `status: completed` plus the optional `updated` timestamp (#184), and — as of Phase A of Epic #271 — the 5 Recommendation fields written by Phase 3.7a. No other fields.
|
|
68
|
+
> **Ownership Reference:** See `skills/_shared/state-ownership.md`. session-end is authorized to set `status: completed` plus the optional `updated` timestamp (#184), the final `current-wave`, the re-stamped `session-id` (#1640, see step 1), and — as of Phase A of Epic #271 — the 5 Recommendation fields written by Phase 3.7a. No other fields.
|
|
67
69
|
|
|
68
|
-
> **Runtime Ordering Note (Epic #271 Phase A):** Phase 3.4's `status: completed` write executes LAST in Phase 3, AFTER Phase 3.7 (sessions.jsonl) and Phase 3.7a (Compute and Write Recommendations). The ordinal position here (3.4) is kept for historical compatibility; the canonical runtime order is `3.1 → 3.2 → 3.3 → 3.4a → 3.5 → 3.5a → 3.6 → 3.6.3 → 3.6.4 → 3.6.6 → 3.6.8 → 3.7 → 3.45 → 3.7a → 3.7b → 3.7c → 3.7d → 3.4` (3.6.3/3.6.4/3.6.6 were missing from this note pre-#724; 3.6.5 Auto-Dream and 3.6.7 Auto-Dialectic were RETIRED 2026-09-09 — see `phase-3-6-tail.md` — leaving four live tail phases, not six; the Tail-Diät skip-plan dispatcher dispatches exactly those four mechanically). Rationale: Phase 3.7a reads in-memory session metrics and writes the 5 Recommendation fields via `updateFrontmatterFields`; that write must complete BEFORE the STATE.md frontmatter is finalized with `status: completed` so the Recommendation fields are visible to the next session-start while STATE.md is still `status: active`. Crash-resilience: if `/close` aborts between 3.7a and 3.4, STATE.md carries `status: active` + Recommendations; session-start Phase 1.5 offers resume (and the banner renders). If the reverse ordering were used (status: completed first), a crash would leave `status: completed` without Recommendations — the Reader would silently no-op the banner, losing the handoff. Phase 3.45 (Telemetry Flush, #844) sits AFTER Phase 3.7 because it drains the send-queue with the just-written `sessions.jsonl` record already included, and BEFORE Phase 3.7a because it is a fire-and-forget side-effect with no dependency on the Recommendation-write ordering below it. Phase 3.7d (Session-Eval, #803) sits AFTER Phase 3.7 because it scores the `sessions.jsonl` record that phase just wrote — the record must exist first — and BEFORE Phase 3.4 because its `eval.jsonl` output is advisory and must never block the close.
|
|
70
|
+
> **Runtime Ordering Note (Epic #271 Phase A):** Phase 3.4's `status: completed` write executes LAST in Phase 3, AFTER Phase 3.7 (sessions.jsonl) and Phase 3.7a (Compute and Write Recommendations). The ordinal position here (3.4) is kept for historical compatibility; the canonical runtime order is `3.1 → 3.2 → 3.3 → 3.4a → 3.5 → 3.5a → 3.6 → 3.6.3 → 3.6.4 → 3.6.6 → 3.6.8 → 3.7 → 3.45 → 3.7a → 3.7b → 3.7c → 3.7d → 3.7e → 3.4` (3.6.3/3.6.4/3.6.6 were missing from this note pre-#724; 3.6.5 Auto-Dream and 3.6.7 Auto-Dialectic were RETIRED 2026-09-09 — see `phase-3-6-tail.md` — leaving four live tail phases, not six; the Tail-Diät skip-plan dispatcher dispatches exactly those four mechanically). Rationale: Phase 3.7a reads in-memory session metrics and writes the 5 Recommendation fields via `updateFrontmatterFields`; that write must complete BEFORE the STATE.md frontmatter is finalized with `status: completed` so the Recommendation fields are visible to the next session-start while STATE.md is still `status: active`. Crash-resilience: if `/close` aborts between 3.7a and 3.4, STATE.md carries `status: active` + Recommendations; session-start Phase 1.5 offers resume (and the banner renders). If the reverse ordering were used (status: completed first), a crash would leave `status: completed` without Recommendations — the Reader would silently no-op the banner, losing the handoff. Phase 3.45 (Telemetry Flush, #844) sits AFTER Phase 3.7 because it drains the send-queue with the just-written `sessions.jsonl` record already included, and BEFORE Phase 3.7a because it is a fire-and-forget side-effect with no dependency on the Recommendation-write ordering below it. Phase 3.7d (Session-Eval, #803) sits AFTER Phase 3.7 because it scores the `sessions.jsonl` record that phase just wrote — the record must exist first — and BEFORE Phase 3.4 because its `eval.jsonl` output is advisory and must never block the close.
|
|
69
71
|
|
|
70
72
|
> Gate: Only run if `persistence` is enabled in Session Config and `<state-dir>/STATE.md` exists.
|
|
71
|
-
1.
|
|
72
|
-
2. Record final wave count and completion time in the frontmatter
|
|
73
|
-
3. Touch `updated: <ISO 8601 UTC>` in the frontmatter (issue #184). Use `scripts/lib/state-md.mjs` → `touchUpdatedField` for safety:
|
|
73
|
+
1. Finalize the frontmatter in ONE lock-guarded write, into the SAME file Phase 3.7a wrote (#1640 — identical `statePath`, passed explicitly): `status: completed`, `updated: <ISO 8601 UTC>` (#184), the final wave count as `current-wave`, and `session-id` re-stamped to this process's own raw id when one is measurable (after a mid-session `/clear` the record still names the pre-`/clear` id; the re-stamp keeps it this session's). Do not hand-edit any of these fields:
|
|
74
74
|
```bash
|
|
75
75
|
node --input-type=module -e "
|
|
76
|
-
import {
|
|
77
|
-
import {
|
|
78
|
-
const
|
|
79
|
-
|
|
76
|
+
import {execSync} from 'node:child_process';
|
|
77
|
+
import {markStateMdCompletedOnDisk} from '${PLUGIN_ROOT}/scripts/lib/state-md/frontmatter-mutators.mjs';
|
|
78
|
+
const repoRoot = execSync('git rev-parse --show-toplevel', {encoding: 'utf8'}).trim();
|
|
79
|
+
const statePath = '<state-dir>/STATE.md'; // same value as Phase 3.7a
|
|
80
|
+
const res = await markStateMdCompletedOnDisk(repoRoot, {statePath, currentWave: <final wave number>});
|
|
81
|
+
console.log(res.written ? 'STATE.md completed: ' + res.path : 'STATE.md NOT written (' + (res.reason ?? 'no change') + '): ' + res.path);
|
|
80
82
|
"
|
|
81
83
|
```
|
|
82
|
-
|
|
83
|
-
|
|
84
|
+
No-op if the file has no frontmatter. `reason: 'foreign-state-md'` means the path holds another session's completed record — report it as a Deviation, never hand-edit it.
|
|
85
|
+
2. Keep the file as a record — do NOT delete it (next session-start reads it)
|
|
84
86
|
|
|
85
87
|
If STATE.md doesn't exist, skip this subsection.
|
|
86
88
|
|
|
@@ -233,9 +235,19 @@ const boardResult = await mirrorBoard({
|
|
|
233
235
|
|
|
234
236
|
Evaluate the just-closed session deterministically and, when configured, with an advisory LLM judge. This phase is a thin hook — the full evaluation flow lives in `skills/eval/SKILL.md`; only the close-out integration is described here.
|
|
235
237
|
|
|
236
|
-
1. **Deterministic run.** Invoke `node scripts/eval-session.mjs --json` — with no `--session`, the cascade (`resolveSession`, revised #822) walks records newest-to-oldest (source order) and evaluates the first one that is either `status:'completed'` or non-abandoned with evidence of completed work (typically the record Phase 3.7 just appended). Model capture: the coordinator passes `--model-id <id> --model-source self-report`; the `$ANTHROPIC_MODEL` env var wins automatically when set. Pass the configured pseudonym through with `--handle <eval.handle>` (omit when `null`). The CLI appends the eval record to `.orchestrator/metrics/eval.jsonl` (`appendEvalRecord` is never-throw).
|
|
238
|
+
1. **Deterministic run.** Invoke `node scripts/eval-session.mjs --json` — with no `--session`, the cascade (`resolveSession`, revised #822) walks records newest-to-oldest (source order) and evaluates the first one that is either `status:'completed'` or non-abandoned with evidence of completed work (typically the record Phase 3.7 just appended). Model capture: the coordinator passes `--model-id <id> --model-source self-report`; the `$ANTHROPIC_MODEL` env var wins automatically when set. Pass the configured pseudonym through with `--handle <eval.handle>` (omit when `null`). The CLI appends the eval record to `.orchestrator/metrics/eval.jsonl` (`appendEvalRecord` is never-throw); run from a linked worktree, that is the main checkout's journal when the worktree ignores it (`resolveLedgerPath`, #1722), so the record survives the worktree.
|
|
237
239
|
2. **Advisory judge (opt-in).** When `eval.judge` is not `off`, run the judge flow per `skills/eval/SKILL.md` § Phase 3: the coordinator dispatches the read-only `eval-judge` agent (DI'd dispatch, untrusted-data nonce fence), merges the advisory judge dimensions (`method: "judge"`, `advisory: true`, `calibration_status: "uncalibrated"`) into the record, and appends the merged record. When `eval.judge: off`, no agent is dispatched and no judge dimensions are produced.
|
|
238
240
|
3. **HTML report (opt-in).** When `eval.report: html` (the default), call `writeEvalReport(record, …)` from `scripts/lib/eval/report.mjs` to emit the self-contained run report under `.orchestrator/eval/reports/<run-id>.html` (gitignored, regenerable from the record). When `eval.report: none`, skip the report.
|
|
239
241
|
|
|
240
242
|
**Advisory — never blocks the close (FA6):** an eval failure — a non-zero `eval-session.mjs` exit, a judge-dispatch error, or a report-write error — MUST NOT abort `/close`. Under `mode: warn`, log a single `WARNING: session-eval failed — <reason>; continuing close` line to stderr and proceed to Phase 3.8 / Phase 4. There is NO exit-code gate on this phase. See `skills/eval/SKILL.md` for the full deterministic-engine + judge + report detail flow.
|
|
241
243
|
|
|
244
|
+
### Phase 3.7e: Vault Orphans (#1563)
|
|
245
|
+
|
|
246
|
+
> Gate: Skip silently unless `vault-integration.enabled: true` in Session Config, and when STATE.md has no `started_at` (no session window to measure).
|
|
247
|
+
|
|
248
|
+
> **Ordering:** Runs AFTER Phase 3.7d and BEFORE Phase 3.4 (`status: completed`), so its one Open Question lands while STATE.md is still `status: active`. See the Phase 3.4 Runtime Ordering Note canonical order.
|
|
249
|
+
|
|
250
|
+
Names the untracked vault files this session may have left behind: `node "$PLUGIN_ROOT/scripts/vault-orphans.mjs" --since <STATE.md started_at> --json` lists untracked vault files modified since the session start, minus files carrying a `_generator: session-orchestrator-` marker. The results are candidates — mtime is a hint, not proof of authorship. The coordinator names them in the Final Report and records ONE Open Question via `appendOpenQuestionOnDisk` (up to 10 paths plus the count). It never commits, moves or deletes anything in the vault (PSA-003). Exit 2 / `status: unavailable` is reported as "could not measure", never as "no orphans". Non-blocking.
|
|
251
|
+
|
|
252
|
+
**See `phase-3-7e-vault-orphans.md` for full details.**
|
|
253
|
+
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
> **VCS Reference:** Use CLI commands per the "Common CLI Commands" section of the gitlab-ops skill.
|
|
8
8
|
|
|
9
|
-
1. **Close resolved issues — one command strips, closes and verifies (#308):** run the CLI ONCE with every resolved issue ID, `--vcs` taken from Session Config:
|
|
9
|
+
1. **Close resolved issues — one command strips, closes and verifies (#308):** run the CLI ONCE with every resolved issue ID, `--vcs` taken from Session Config. An `[Abnahme]` issue is never in that list: it closes only on oracle evidence (`plan-verification.md` § 1.1b, #1678).
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
12
|
node "$PLUGIN_ROOT/scripts/lib/issue-close-strip-labels.mjs" --close --vcs <gitlab|github> <iid> [<iid> ...]
|
|
@@ -11,6 +11,10 @@ Present to the user:
|
|
|
11
11
|
- [x] Issue #N: [description] — [evidence: tests passing, files changed]
|
|
12
12
|
- [x] Issue #M: [description]
|
|
13
13
|
|
|
14
|
+
### Abnahme offen (implemented ≠ accepted — never counted as Completed) [#1678]
|
|
15
|
+
- [ ] #<IID> [Abnahme] <Kriterium> — owner: <body | assignee | Owner> — not-before: <date | —>
|
|
16
|
+
- scope: [linked to this session's plan/epic | all open — link not determinable]
|
|
17
|
+
|
|
14
18
|
### Carried Over
|
|
15
19
|
- [ ] Issue #P: [what's left] — new issue #Q created
|
|
16
20
|
- [ ] [description] — blocked by [reason]
|
|
@@ -46,6 +50,10 @@ Present to the user:
|
|
|
46
50
|
- Custom Phases: [render based on Phase 2.5 result — omit the line entirely if `custom-phases` was absent/empty]
|
|
47
51
|
- Per phase: `<name>: <pass|FAIL> (exit <code>, mode=<mode>)[ — review: <path>]`
|
|
48
52
|
- None ran (all filtered out by `when`): `none applicable for session-type=<type>.`
|
|
53
|
+
- Vault orphans: [render based on Phase 3.7e — omit the line entirely when `vault-integration.enabled` is not `true`]
|
|
54
|
+
- Candidates present: `<N> untracked vault file(s) modified since session start (candidates, mtime is no proof): <path>, <path>, ... — recorded as Open Question, nothing committed or deleted.`
|
|
55
|
+
- None: `none since session start (<U> untracked in total).`
|
|
56
|
+
- Could not measure (exit 2 / `status: unavailable`): `could not measure (<reason>)` — never "none".
|
|
49
57
|
- Enforcement: [N violations blocked / M warnings] (or "N/A" if enforcement off)
|
|
50
58
|
- Circuit breaker: [N agents hit limits, M spirals detected] (or "none")
|
|
51
59
|
- Metrics written to: `.orchestrator/metrics/sessions.jsonl`
|
|
@@ -13,9 +13,10 @@
|
|
|
13
13
|
>
|
|
14
14
|
> Minimal invocation:
|
|
15
15
|
> ```bash
|
|
16
|
-
> printf '%s' "$METRICS_ENTRY" | node "$PLUGIN_ROOT/scripts/emit-session.mjs"
|
|
16
|
+
> printf '%s' "$METRICS_ENTRY" | node "$PLUGIN_ROOT/scripts/emit-session.mjs"
|
|
17
17
|
> ```
|
|
18
18
|
> See step 2 below for the full invocation including exit-code handling.
|
|
19
|
+
> Pass no `--file`: the default is the main checkout's `sessions.jsonl` (#1584). A relative `--file` is taken verbatim and, run from a linked worktree, writes the worktree's own copy, which is gitignored here and lost with the worktree (#1722).
|
|
19
20
|
|
|
20
21
|
1. Ensure `.orchestrator/metrics/` directory exists: `mkdir -p .orchestrator/metrics`
|
|
21
22
|
|
|
@@ -29,7 +30,7 @@
|
|
|
29
30
|
|
|
30
31
|
> **#701.2 DOC NOTE — `completed_at >= started_at` guard:** This invariant is enforced mechanically by `scripts/emit-session.mjs`. The writer applies `clampTimestampsMonotonic()` (from `scripts/lib/session-schema/timestamps.mjs`) before `validateSession()`, clamping any inversion of `completed_at < started_at` to `started_at` and recording forensics in `_clamped: true` / `_original_completed_at`. Previously-inverted entries (e.g. `main-2026-06-21-session-4`) are already corrected. **No per-session coordinator action is needed** — the writer enforces the invariant at write time. Do not add defensive clamping logic here; the canonical guard lives in `emit-session.mjs`.
|
|
31
32
|
|
|
32
|
-
1a. **Token Rollup (#644, #1244 — mechanical since #1436)** — no coordinator action. `scripts/emit-session.mjs` (step 2) runs `rollupSessionTokens()` itself whenever the incoming record carries no `total_tokens`: it resolves the session's raw UUID from `.orchestrator/current-session.json` (adopted only when its `semantic_session_id` equals the record's `session_id` and no process-local session id contradicts it), joins the sibling `subagents.jsonl` on `parent_session_id`, and fills `total_tokens`, `total_token_input`, `total_token_output`, the three cache buckets, `total_cost_usd`, `subagents_with_tokens`, `matched_records`, `cost_records_priced`, `cost_records_total` and `_token_schema` for every key the record does not already carry — plus `raw_session_id`. No matching records, an unreadable ledger or a foreign marker omits the fields with a WARN on stderr, never a fabricated `0`. An explicit key on `$METRICS_ENTRY` still wins. The same UUID also backs `session_start_ref` when STATE.md has none (the `head_sha` of the session's own `orchestrator.session.started` event).
|
|
33
|
+
1a. **Token Rollup (#644, #1244 — mechanical since #1436)** — no coordinator action. `scripts/emit-session.mjs` (step 2) runs `rollupSessionTokens()` itself whenever the incoming record carries no `total_tokens`: it resolves the session's raw UUID from `.orchestrator/current-session.json` (adopted only when its `semantic_session_id` equals the record's `session_id` and no process-local session id contradicts it), joins the sibling `subagents.jsonl` on `parent_session_id`, and fills `total_tokens`, `total_token_input`, `total_token_output`, the three cache buckets, `total_cost_usd`, `subagents_with_tokens`, `matched_records`, `cost_records_priced`, `cost_records_total` and `_token_schema` for every key the record does not already carry — plus `raw_session_id`. No matching records, an unreadable ledger or a foreign marker omits the fields with a WARN on stderr, never a fabricated `0`. An explicit key on `$METRICS_ENTRY` still wins. The same UUID also backs `session_start_ref` when STATE.md has none (the `head_sha` of the session's own `orchestrator.session.started` event) — except when the session's own registry entry shows `work_root` ≠ `session_root` (#1652): that sha was measured in the SessionStart root, not the tree the session works in, so the field is omitted with a WARN.
|
|
33
34
|
|
|
34
35
|
**Semantics:** `null` totals mean "no token data was captured for this session" — this is NOT the same as zero cost. Do NOT coerce null to 0 when displaying or summing across sessions. The same holds for `total_cost_usd`: it is omitted whenever `cost_records_priced < cost_records_total` (#1475) — at least one cost-relevant subagent record could not be priced (a model the price table does not know, a token-bearing turn naming no model, a transcript that yielded no tokens, e.g. one over the hook's size cap, or an agent with a start record whose stop found no transcript of its own, mostly `workflow-subagent`, or an agent known only from `schema_version` 1 records — #1487 Pkt 1) — and also when `cost_records_total` is 0, because there was nothing to price. It is never "$0" for an unknown cost and never a partial sum over the v2 agents the ledger can see. Phantom stops (#939, no start record) are no agents and never count. The two counters are persisted beside it, so a missing cost says whether part of the session was unpriceable or nothing was priceable. Blind spots remain: an agent whose start record fell outside the hook's tail window looks like a phantom, an agent with neither start nor stop record is invisible. A session spanning the 2026-09-09 schema boundary therefore reports no cost (its v1 agents are unpriced candidates since 2026-10-02; before, it was priced over its v2 records only — 4 distinct fleet sessions carried such a partial sum). A subagent whose turns span several models carries a per-model `models_usage` breakdown in `subagents.jsonl` (#1470) and is priced part by part. A written `total_cost_usd` is the **API list-price equivalent** of the metered tokens, never a payment: subscription quota, purchased credits and the amount actually billed are invisible to the rollup, and no record states any of them. Since #1470 emit-session marks this with `cost_basis: "api-list-equivalent"` beside every `total_cost_usd` it writes, and writes `pricing_table_date` (the `PRICING_TABLE_DATE` of `scripts/lib/telemetry/pricing.mjs` that priced the session) beside the cost counters. A record without these fields predates #1470; its cost is a list-price estimate too, priced by an older table, not a verified bill. The emit-session warning names the reason via `match_status` (ledger absent, ledger with no readable records, or records present but none for the own raw UUID); none of these means zero cost.
|
|
35
36
|
|
|
@@ -39,7 +40,7 @@
|
|
|
39
40
|
|
|
40
41
|
2. Append the prepared JSONL entry (from Phase 1.7; the writer itself adds the token fields — step 1a) via the validating writer `scripts/emit-session.mjs` (issue #249):
|
|
41
42
|
```bash
|
|
42
|
-
printf '%s' "$METRICS_ENTRY" | node "$PLUGIN_ROOT/scripts/emit-session.mjs"
|
|
43
|
+
printf '%s' "$METRICS_ENTRY" | node "$PLUGIN_ROOT/scripts/emit-session.mjs"
|
|
43
44
|
EMIT_EXIT=$?
|
|
44
45
|
if [[ $EMIT_EXIT -eq 1 ]]; then
|
|
45
46
|
echo "ERROR: session-end validation failed — entry rejected by scripts/emit-session.mjs. See stderr above. Session metrics NOT written." >&2
|
|
@@ -199,7 +200,9 @@
|
|
|
199
200
|
# gate. Non-blocking: a failure surfaces a warning and never blocks close,
|
|
200
201
|
# using the same strict/warn degradation idiom as the vault-mirror step above.
|
|
201
202
|
NM_OUTPUT=$(node -e "
|
|
202
|
-
import('
|
|
203
|
+
import('node:url').then(({ pathToFileURL }) =>
|
|
204
|
+
import(pathToFileURL('$PLUGIN_ROOT/scripts/lib/vault-status/narrative-mirror.mjs').href)
|
|
205
|
+
).then(async (m) => {
|
|
203
206
|
const r = await m.mirrorNarrative({ repoRoot: process.cwd() });
|
|
204
207
|
process.stdout.write(JSON.stringify(r));
|
|
205
208
|
}).catch((e) => { process.stderr.write(String(e && e.message || e)); process.exit(3); });
|