session-orchestrator 3.23.0 → 4.0.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/.agents/skills/architecture/SKILL.md +18 -0
- package/.agents/skills/autopilot/SKILL.md +17 -0
- package/.agents/skills/bootstrap/SKILL.md +20 -0
- package/.agents/skills/brainstorm/SKILL.md +22 -0
- package/.agents/skills/claude-md-drift-check/SKILL.md +15 -0
- package/.agents/skills/convergence-monitoring/SKILL.md +22 -0
- package/.agents/skills/debug/SKILL.md +22 -0
- package/.agents/skills/discovery/SKILL.md +20 -0
- package/.agents/skills/dispatcher/SKILL.md +15 -0
- package/.agents/skills/docs-orchestrator/SKILL.md +18 -0
- package/.agents/skills/ecosystem-health/SKILL.md +20 -0
- package/.agents/skills/eli5/SKILL.md +20 -0
- package/.agents/skills/eval/SKILL.md +21 -0
- package/.agents/skills/evolve/SKILL.md +21 -0
- package/.agents/skills/frontmatter-guard/SKILL.md +15 -0
- package/.agents/skills/gitlab-ops/SKILL.md +20 -0
- package/.agents/skills/gitlab-portfolio/SKILL.md +15 -0
- package/.agents/skills/grill/SKILL.md +22 -0
- package/.agents/skills/hook-development/SKILL.md +15 -0
- package/.agents/skills/mcp-builder/SKILL.md +15 -0
- package/.agents/skills/memory-cleanup/SKILL.md +21 -0
- package/.agents/skills/mode-selector/SKILL.md +17 -0
- package/.agents/skills/npm-publish/SKILL.md +16 -0
- package/.agents/skills/peekaboo-driver/SKILL.md +18 -0
- package/.agents/skills/persona-panel/SKILL.md +17 -0
- package/.agents/skills/plan/SKILL.md +20 -0
- package/.agents/skills/playwright-driver/SKILL.md +20 -0
- package/.agents/skills/quality-gates/SKILL.md +20 -0
- package/.agents/skills/reconcile/SKILL.md +21 -0
- package/.agents/skills/remote-offload/SKILL.md +20 -0
- package/.agents/skills/repo-audit/SKILL.md +16 -0
- package/.agents/skills/session-end/SKILL.md +20 -0
- package/.agents/skills/session-plan/SKILL.md +20 -0
- package/.agents/skills/session-start/SKILL.md +20 -0
- package/.agents/skills/spinout/SKILL.md +16 -0
- package/.agents/skills/sunset-review/SKILL.md +16 -0
- package/.agents/skills/test-runner/SKILL.md +20 -0
- package/.agents/skills/tmux-layout/SKILL.md +21 -0
- package/.agents/skills/using-orchestrator/SKILL.md +17 -0
- package/.agents/skills/vault-mirror/SKILL.md +15 -0
- package/.agents/skills/vault-sync/SKILL.md +15 -0
- package/.agents/skills/wave-executor/SKILL.md +20 -0
- package/.agents/skills/write-executable-plan/SKILL.md +22 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor/commands/autopilot.md +2 -2
- package/.cursor/commands/bootstrap.md +1 -1
- package/.cursor/commands/brainstorm.md +1 -1
- package/.cursor/commands/debug.md +1 -1
- package/.cursor/commands/discovery.md +1 -1
- package/.cursor/commands/dispatcher.md +2 -2
- package/.cursor/commands/eli5.md +2 -2
- package/.cursor/commands/eval.md +2 -2
- package/.cursor/commands/evolve.md +1 -1
- package/.cursor/commands/go.md +1 -1
- package/.cursor/commands/grill.md +2 -2
- package/.cursor/commands/memory-cleanup.md +2 -2
- package/.cursor/commands/persona-panel.md +1 -1
- package/.cursor/commands/plan.md +1 -1
- package/.cursor/commands/portfolio.md +1 -1
- package/.cursor/commands/reconcile.md +2 -2
- package/.cursor/commands/release.md +2 -2
- package/.cursor/commands/session.md +2 -2
- package/.cursor/commands/spinout.md +2 -2
- package/.cursor/commands/sunset-review.md +2 -2
- package/.cursor/commands/templates-ack.md +2 -2
- package/.cursor/commands/test.md +2 -2
- package/.cursor/skills/brainstorm/SKILL.md +1 -1
- package/.cursor/skills/eval/SKILL.md +1 -1
- package/.cursor/skills/quality-gates/SKILL.md +1 -1
- package/.cursor/skills/remote-offload/SKILL.md +13 -0
- package/.orchestrator/policy/blocked-commands.json +121 -0
- package/.orchestrator/policy/ecosystem.schema.json +66 -0
- package/.orchestrator/policy/quality-gates.example.json +16 -0
- package/.orchestrator/policy/quality-gates.schema.json +38 -0
- package/.orchestrator/policy/templates-policy.json +27 -0
- package/.orchestrator/policy/test-profiles.json +47 -0
- package/AGENTS.md +225 -0
- package/CHANGELOG.md +1401 -0
- package/NOTICE +11 -6
- package/README.md +127 -92
- package/agents/db-specialist.md +0 -1
- package/agents/eval-judge.md +1 -1
- package/agents/skill-applied-judge.md +1 -1
- package/assets/wave-lifecycle.svg +98 -0
- package/commands/release.md +6 -3
- package/commands/session.md +18 -3
- package/docs/README.md +4 -0
- package/{agents/AGENTS.md → docs/agent-authoring.md} +19 -26
- package/docs/baseline.md +67 -0
- package/docs/ci-setup.md +249 -48
- package/docs/codex-setup.md +66 -22
- package/docs/components.md +37 -16
- package/docs/cursor-setup.md +6 -2
- package/docs/events-schema.md +51 -10
- package/docs/instruction-delivery.md +62 -0
- package/{agents/memory-proposal-collector.md → docs/memory-proposal-flow.md} +1 -8
- package/docs/migration-v4.md +341 -0
- package/docs/pi-setup.md +6 -1
- package/docs/plugin-architecture-v3.md +1 -1
- package/docs/rule-authoring.md +85 -19
- package/docs/scope-collision-guard.md +8 -8
- package/docs/session-config-reference.md +120 -61
- package/docs/session-config-template.md +40 -33
- package/docs/telemetry/telemetry-claims.md +11 -10
- package/docs/telemetry.md +187 -4
- package/docs/vault-docs-architecture.md +50 -11
- package/hooks/_lib/atomic-json.mjs +111 -0
- package/hooks/_lib/hook-import-set.json +1487 -0
- package/hooks/_lib/subagent-paths.mjs +143 -0
- package/hooks/_lib/subagent-transcript.mjs +562 -0
- package/hooks/config-protection.mjs +2 -2
- package/hooks/cwd-change-restore.mjs +11 -31
- package/hooks/enforce-commands.mjs +69 -0
- package/hooks/enforce-scope.mjs +35 -6
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks-cursor.json +10 -0
- package/hooks/hooks-pi.json +5 -0
- package/hooks/hooks.json +6 -1
- package/hooks/loop-guard.mjs +3 -3
- package/hooks/on-session-end.mjs +280 -14
- package/hooks/on-session-start.mjs +153 -4
- package/hooks/on-stop.mjs +371 -17
- package/hooks/operator-steer.mjs +2 -2
- package/hooks/post-bash-write-verify.mjs +189 -4
- package/hooks/post-edit-import-probe.mjs +344 -0
- package/hooks/post-subagent-discovery-validator.mjs +278 -392
- package/hooks/post-tool-batch-wave-signal.mjs +272 -44
- package/hooks/post-tool-failure-corrective-context.mjs +11 -34
- package/hooks/post-tooluse-frontend-slop.mjs +3 -3
- package/hooks/pre-bash-destructive-guard.mjs +39 -13
- package/hooks/pre-bash-memory-propose-audit.mjs +13 -7
- package/hooks/skill-invocation-telemetry.mjs +17 -5
- package/hooks/subagent-telemetry.mjs +24 -30
- package/monitors/monitors.json +3 -3
- package/package.json +9 -1
- package/pi/prompts/session.md +2 -2
- package/plugin.json +27 -0
- package/scripts/autopilot.mjs +26 -12
- package/scripts/backfill-abandoned-sessions.mjs +130 -15
- package/scripts/backfill-learnings-from-vault.mjs +9 -3
- package/scripts/dialectic-deriver.mjs +73 -8
- package/scripts/emit-event.mjs +10 -2
- package/scripts/export-hw-learnings.mjs +113 -1
- package/scripts/generate-agents-skills.mjs +378 -0
- package/scripts/generate-cursor-adapter.mjs +45 -8
- package/scripts/generate-hook-import-set.mjs +249 -0
- package/scripts/lib/agent-status.mjs +13 -2
- package/scripts/lib/auq/parse.mjs +5 -29
- package/scripts/lib/auto-dialectic.mjs +68 -0
- package/scripts/lib/auto-dream.mjs +38 -36
- package/scripts/lib/autonomy/suitability.mjs +6 -0
- package/scripts/lib/autopilot/loop.mjs +2 -2
- package/scripts/lib/autopilot/worktree-pipeline.mjs +82 -6
- package/scripts/lib/build-live-signals.mjs +25 -22
- package/scripts/lib/ci-status-banner.mjs +220 -75
- package/scripts/lib/codex/plugin-contract.mjs +82 -6
- package/scripts/lib/cold-start-detector.mjs +23 -14
- package/scripts/lib/config/auto-dream.mjs +2 -1
- package/scripts/lib/config/block-header.mjs +63 -0
- package/scripts/lib/config/block-preprocess.mjs +177 -0
- package/scripts/lib/config/broken-window.mjs +2 -1
- package/scripts/lib/config/cold-start.mjs +2 -1
- package/scripts/lib/config/config-protection.mjs +22 -2
- package/scripts/lib/config/context-coverage.mjs +2 -1
- package/scripts/lib/config/cross-repo.mjs +2 -1
- package/scripts/lib/config/custom-phases.mjs +2 -1
- package/scripts/lib/config/dialectic.mjs +2 -1
- package/scripts/lib/config/discovery-validator.mjs +9 -3
- package/scripts/lib/config/dispatcher-autonomy-capture.mjs +24 -1
- package/scripts/lib/config/dispatcher-autonomy.mjs +2 -1
- package/scripts/lib/config/docs-orchestrator.mjs +2 -1
- package/scripts/lib/config/docs-staleness.mjs +2 -1
- package/scripts/lib/config/drift-check.mjs +2 -1
- package/scripts/lib/config/eval.mjs +2 -1
- package/scripts/lib/config/events-rotation.mjs +2 -1
- package/scripts/lib/config/evolve.mjs +8 -2
- package/scripts/lib/config/frontend-slop-hook.mjs +7 -3
- package/scripts/lib/config/gitlab-portfolio.mjs +2 -1
- package/scripts/lib/config/handover-gate.mjs +2 -1
- package/scripts/lib/config/health-endpoints.mjs +388 -0
- package/scripts/lib/config/issue-budget.mjs +2 -1
- package/scripts/lib/config/loop-guard.mjs +2 -1
- package/scripts/lib/config/memory.mjs +2 -1
- package/scripts/lib/config/moc-staleness.mjs +2 -1
- package/scripts/lib/config/persona-gate-wave.mjs +2 -1
- package/scripts/lib/config/private-config-dir.mjs +67 -0
- package/scripts/lib/config/reconcile.mjs +2 -1
- package/scripts/lib/config/remote-hosts.mjs +234 -0
- package/scripts/lib/config/section-extractor.mjs +7 -1
- package/scripts/lib/config/skill-evolution.mjs +2 -1
- package/scripts/lib/config/slopcheck.mjs +2 -1
- package/scripts/lib/config/state-md-lock.mjs +2 -1
- package/scripts/lib/config/templates-first.mjs +2 -1
- package/scripts/lib/config/test.mjs +2 -1
- package/scripts/lib/config/vault-integration.mjs +7 -1
- package/scripts/lib/config/vault-mirror-quality.mjs +2 -1
- package/scripts/lib/config/vault-staleness.mjs +2 -1
- package/scripts/lib/config/vault-sync.mjs +2 -1
- package/scripts/lib/config/verification-auto-fix.mjs +2 -1
- package/scripts/lib/config/wave-reviewers.mjs +2 -1
- package/scripts/lib/config/worktree-orphans.mjs +2 -1
- package/scripts/lib/config.mjs +31 -3
- package/scripts/lib/convergence-monitor.mjs +82 -16
- package/scripts/lib/dispatcher/enumerate.mjs +2 -17
- package/scripts/lib/dispatcher/rank.mjs +124 -48
- package/scripts/lib/ecosystem-health.mjs +16 -2
- package/scripts/lib/eval/engine.mjs +9 -1
- package/scripts/lib/eval/session-resolve.mjs +23 -4
- package/scripts/lib/events-schema.mjs +48 -0
- package/scripts/lib/events.mjs +256 -7
- package/scripts/lib/evolve/autonomy-verdict.mjs +9 -4
- package/scripts/lib/evolve/autopilot-effectiveness.mjs +18 -1
- package/scripts/lib/frontmatter-guard.mjs +131 -13
- package/scripts/lib/gates/gate-full.mjs +26 -0
- package/scripts/lib/gates/gate-helpers.mjs +76 -0
- package/scripts/lib/gitlab-portfolio/cli.mjs +3 -15
- package/scripts/lib/hardware-pattern-detector.mjs +18 -1
- package/scripts/lib/harness-audit/categories/category1.mjs +17 -6
- package/scripts/lib/harness-audit/categories/category4.mjs +31 -11
- package/scripts/lib/host-identity.mjs +50 -11
- package/scripts/lib/instruction-budget-guard.mjs +171 -5
- package/scripts/lib/learnings/evolve-telemetry.mjs +178 -0
- package/scripts/lib/learnings/io.mjs +60 -6
- package/scripts/lib/memory-banner.mjs +20 -8
- package/scripts/lib/memory-proposals/store.mjs +30 -22
- package/scripts/lib/owner-config-banner.mjs +43 -6
- package/scripts/lib/owner-config-loader.mjs +21 -10
- package/scripts/lib/owner-interview.mjs +3 -3
- package/scripts/lib/owner-yaml.mjs +207 -14
- package/scripts/lib/peer-discovery.mjs +20 -2
- package/scripts/lib/platform.mjs +108 -15
- package/scripts/lib/plugin-update-banner.mjs +406 -0
- package/scripts/lib/project-hygiene.mjs +38 -2
- package/scripts/lib/qg-command-drift-banner.mjs +50 -12
- package/scripts/lib/quality-gate.mjs +133 -44
- package/scripts/lib/reconcile/emitter.mjs +68 -6
- package/scripts/lib/reconcile/engine.mjs +249 -9
- package/scripts/lib/reconcile/idempotency.mjs +37 -4
- package/scripts/lib/reconcile/writer.mjs +40 -18
- package/scripts/lib/scope-gate.mjs +36 -0
- package/scripts/lib/session-close-backfill.mjs +125 -18
- package/scripts/lib/session-discovery.mjs +57 -3
- package/scripts/lib/session-end/phase-skip.mjs +2 -2
- package/scripts/lib/session-id.mjs +12 -23
- package/scripts/lib/session-identity/own-session.mjs +187 -11
- package/scripts/lib/session-lock-shape.mjs +43 -0
- package/scripts/lib/session-lock.mjs +5 -10
- package/scripts/lib/session-registry.mjs +25 -9
- package/scripts/lib/session-schema/constants.mjs +36 -2
- package/scripts/lib/session-schema/validator.mjs +38 -4
- package/scripts/lib/session-start-probes.mjs +18 -1
- package/scripts/lib/session-transition.mjs +1 -1
- package/scripts/lib/sessions-canonical.mjs +446 -0
- package/scripts/lib/sessions-staleness-banner.mjs +18 -11
- package/scripts/lib/skill-health/join.mjs +17 -4
- package/scripts/lib/state-md.mjs +78 -0
- package/scripts/lib/sunset/walker.mjs +6 -0
- package/scripts/lib/telemetry/schema.mjs +255 -17
- package/scripts/lib/telemetry/sync.mjs +417 -24
- package/scripts/lib/tmux-layout/telemetry.mjs +14 -2
- package/scripts/lib/validate/check-agents-skills.mjs +327 -0
- package/scripts/lib/validate/check-agents.mjs +3 -3
- package/scripts/lib/validate/check-cursor-adapter.mjs +234 -72
- package/scripts/lib/validate/check-doc-cli-commands.mjs +9 -33
- package/scripts/lib/validate/check-hooks-emit-event-guard.mjs +370 -0
- package/scripts/lib/validate/check-hooks-symmetry.mjs +45 -16
- package/scripts/lib/validate/check-owner-leakage.mjs +281 -20
- package/scripts/lib/validate/check-skill-links.mjs +163 -0
- package/scripts/lib/validate/check-skill-script-paths.mjs +455 -0
- package/scripts/lib/validate/check-untracked-test-deps.mjs +10 -0
- package/scripts/lib/validate/check-unwired-features.mjs +0 -9
- package/scripts/lib/validate/check-validator-registration.mjs +254 -0
- package/scripts/lib/validate/check-vcs-repo-flag.mjs +6 -28
- package/scripts/lib/validate/enumerate-repo-files.mjs +317 -0
- package/scripts/lib/validate/markdown-fences.mjs +196 -0
- package/scripts/lib/vault-backfill/template.mjs +63 -6
- package/scripts/lib/vault-mirror/process.mjs +165 -42
- package/scripts/lib/vault-mirror/telemetry.mjs +2 -2
- package/scripts/lib/vault-status/board-lock.mjs +185 -0
- package/scripts/lib/vault-status/board-writer.mjs +174 -135
- package/scripts/lib/vault-status/narrative-mirror.mjs +129 -37
- package/scripts/lib/wave-executor/dispatch-common.mjs +164 -0
- package/scripts/lib/wave-executor/foreign-dispatch.mjs +7 -142
- package/scripts/lib/wave-executor/remote-dispatch.mjs +502 -0
- package/scripts/lib/wave-resource-gate.mjs +133 -7
- package/scripts/lib/wave-sizing.mjs +4 -1
- package/scripts/lib/wave-transcript-tail.mjs +142 -8
- package/scripts/materialize-wave-scope.mjs +32 -9
- package/scripts/memory-propose.mjs +146 -8
- package/scripts/migrate-cold-start-seed.mjs +4 -1
- package/scripts/parse-config.mjs +60 -3
- package/scripts/promote-vault-strict.mjs +4 -15
- package/scripts/release.mjs +337 -29
- package/scripts/repair-invalid-sessions.mjs +3 -3
- package/scripts/run-quality-gate.mjs +128 -11
- package/scripts/site-numbers.mjs +36 -4
- package/scripts/sweep-expired-learnings.mjs +90 -0
- package/scripts/sync-vault-schema.mjs +3 -1
- package/scripts/telemetry.mjs +2 -2
- package/scripts/validate-plugin.mjs +187 -0
- package/scripts/validate-wave-scope.mjs +28 -8
- package/scripts/vault-consolidate.mjs +3 -11
- package/scripts/vault-integration-watcher.mjs +2 -4
- package/scripts/vault-mirror.mjs +111 -26
- package/scripts/wave-scope-binding.mjs +215 -0
- package/skills/_shared/instruction-file-resolution.md +10 -0
- package/skills/_shared/parallel-aware-auq.md +31 -2
- package/skills/_shared/parallel-aware-preamble.md +18 -4
- package/skills/_shared/platform-tools.md +1 -1
- package/skills/_shared/state-ownership.md +1 -1
- package/skills/architecture/SKILL.md +7 -5
- package/skills/{domain-model/SKILL.md → architecture/references/domain-model.md} +9 -9
- package/skills/autopilot/SKILL.md +4 -18
- package/skills/claude-md-drift-check/SKILL.md +5 -1
- package/skills/claude-md-drift-check/checker.mjs +62 -2
- package/skills/convergence-monitoring/SIGNALS.md +55 -0
- package/skills/discovery/probes/vault-staleness.mjs +37 -13
- package/skills/discovery/probes-arch.md +20 -18
- package/skills/dispatcher/SKILL.md +3 -2
- package/skills/ecosystem-health/SKILL.md +4 -1
- package/skills/ecosystem-health/wizard.md +5 -0
- package/skills/evolve/SKILL.md +87 -11
- package/skills/frontmatter-guard/SKILL.md +11 -5
- package/skills/npm-publish/SKILL.md +1 -1
- package/skills/reconcile/SKILL.md +38 -2
- package/skills/remote-offload/SKILL.md +89 -0
- package/skills/session-end/SKILL.md +18 -905
- package/skills/session-end/phase-3-6-tail.md +19 -9
- package/skills/session-end/plan-verification.md +221 -155
- package/skills/session-end/references/phase-2-quality-gate.md +93 -0
- package/skills/session-end/references/phase-3-documentation-updates.md +229 -0
- package/skills/session-end/references/phase-4a-worktree-cleanup.md +120 -0
- package/skills/session-end/references/phase-4b-worktree-orphan-sweep.md +58 -0
- package/skills/session-end/references/phase-5-issue-cleanup.md +104 -0
- package/skills/session-end/references/session-summary-template.md +62 -0
- package/skills/session-plan/SKILL.md +49 -0
- package/skills/session-start/SKILL.md +41 -900
- package/skills/session-start/phase-8-5-express-path.md +1 -1
- package/skills/session-start/references/phase-1-1-dispatcher-autonomy-capture.md +55 -0
- package/skills/session-start/references/phase-1-2-session-lock.md +140 -0
- package/skills/session-start/references/phase-1-5-session-continuity.md +254 -0
- package/skills/session-start/references/phase-1-7-vault-status-board.md +53 -0
- package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +75 -0
- package/skills/session-start/references/phase-4-ssot-environment-check.md +155 -0
- package/skills/session-start/references/phase-6-5-forced-reads.md +75 -0
- package/skills/session-start/references/phase-6-6-project-intelligence.md +81 -0
- package/skills/session-start/references/phase-6-7-memory-banner-telemetry-consent.md +103 -0
- package/skills/vault-sync/validator.mjs +21 -27
- package/skills/wave-executor/SKILL.md +16 -2
- package/skills/wave-executor/references/wave-loop-dispatch.md +612 -0
- package/skills/wave-executor/references/wave-loop-review.md +570 -0
- package/skills/wave-executor/references/wave-loop-scope-manifest.md +162 -0
- package/skills/wave-executor/wave-loop.md +14 -1271
- package/templates/_shared/journey-manifest.md +10 -6
- package/.cursor/commands/autopilot-multi.md +0 -14
- package/.cursor/commands/contract-version-bump.md +0 -14
- package/.cursor/commands/journey-audit.md +0 -14
- package/.cursor/skills/contract-version-bump/SKILL.md +0 -12
- package/.cursor/skills/daily/SKILL.md +0 -12
- package/.cursor/skills/domain-model/SKILL.md +0 -13
- package/.cursor/skills/journey-audit/SKILL.md +0 -13
- package/.cursor/skills/skill-creator/SKILL.md +0 -13
- package/.cursor/skills/ubiquitous-language/SKILL.md +0 -13
- package/commands/autopilot-multi.md +0 -74
- package/commands/contract-version-bump.md +0 -28
- package/commands/journey-audit.md +0 -43
- package/pi/prompts/autopilot-multi.md +0 -12
- package/pi/prompts/contract-version-bump.md +0 -12
- package/pi/prompts/journey-audit.md +0 -12
- package/scripts/autopilot-multi.mjs +0 -885
- package/scripts/backfill-learnings-expires.mjs +0 -196
- package/scripts/backfill-learnings.mjs +0 -203
- package/scripts/fleet-instruction-scan.mjs +0 -141
- package/scripts/lib/autopilot/dep-graph.mjs +0 -417
- package/scripts/lib/autopilot/multi-killswitch.mjs +0 -184
- package/scripts/lib/webhook-url.mjs +0 -105
- package/scripts/lifecycle-sim-v6.mjs +0 -347
- package/scripts/migrate-learnings-jsonl.mjs +0 -189
- package/scripts/migrate-subagents-jsonl.mjs +0 -196
- package/scripts/upload-social-preview.mjs +0 -316
- package/skills/_shared/model-selection.md +0 -64
- package/skills/contract-version-bump/SKILL.md +0 -219
- package/skills/daily/SKILL.md +0 -222
- package/skills/daily/generate.sh +0 -92
- package/skills/daily/templates/daily.md.tpl +0 -36
- package/skills/journey-audit/SKILL.md +0 -269
- package/skills/skill-creator/SKILL.md +0 -168
- package/skills/ubiquitous-language/SKILL.md +0 -97
- package/skills/vault-sync/package-lock.json +0 -40
- /package/skills/{domain-model → architecture/references}/ADR-FORMAT.md +0 -0
- /package/skills/{domain-model → architecture/references}/CONTEXT-FORMAT.md +0 -0
package/docs/telemetry.md
CHANGED
|
@@ -39,18 +39,55 @@ projection unit test enforces the drop of any non-whitelisted input field.
|
|
|
39
39
|
| `arch` | CPU architecture (e.g. `arm64`, `x64`). |
|
|
40
40
|
| `node_major` | Major Node.js version in use. |
|
|
41
41
|
| `ci` | Boolean — whether the run was detected as a CI environment. |
|
|
42
|
-
| `fleet` | Boolean —
|
|
43
|
-
| `
|
|
42
|
+
| `fleet` | Boolean — **DEPRECATED since 2026-09-06, removal 2027-03-06.** Identical in value to `fleet_self_declared` for the whole deprecation generation; kept so the server's existing `fleet` column stays comparable across the rename. |
|
|
43
|
+
| `fleet_self_declared` | Boolean, optional — the client's own claim that this send came from an operator host. **Self-declared, and the name says so on purpose:** the authoritative classification is server-side (see below). Derived from the *resolved consent state* (`enabled-fleet` from an `owner.yaml` opt-in, or `enabled-env` from `SO_TELEMETRY=1`), no longer from a raw `owner.yaml` read. |
|
|
44
|
+
| `session_profile` | Optional — the STATE.md frontmatter `session-profile`, and **whitelisted profile names only** (today exactly `ultradeep`). Anything else — a value your repo invented, a client name, a typo — is **omitted from the ping entirely**: never sent verbatim, and never flattened to `other` either. A SECOND axis beside `session_type`, never a substitute for it: an ultradeep session is `session_type: "deep"` PLUS `session_profile: "ultradeep"`. **Absent when no profile is set or the profile is not on the whitelist** (the key is omitted, never `null`), including on derived pings, which never invent one. The whitelist is enforced twice — client-side before the send, and again server-side, which rejects a record carrying an unlisted profile rather than storing it. |
|
|
45
|
+
| `session_record` | Optional — WHICH source the session facts in this ping came from: `ledger` (a matching `sessions.jsonl` record), `derived` (reconstructed from `events.jsonl`), `absent` (neither). When `absent`, `session_type` is `unknown` and `duration_bucket` is **not a measurement**. |
|
|
46
|
+
| `session_type` | One of `housekeeping`, `feature`, `deep`, `other`, `unknown`. `other` means MEASURED but not one of the three modes; `unknown` means NOT MEASURED. Before 2026-09-06 both collapsed to `other`. |
|
|
44
47
|
| `duration_bucket` | One of `<15m`, `15-60m`, `1-3h`, `>3h` — a coarse bucket, never an exact duration. |
|
|
45
48
|
| `skills[]` | Names of invoked skills, filtered against the shipped plugin roster — any name not in that roster becomes `"other"`. |
|
|
46
|
-
| `commands[]` |
|
|
49
|
+
| `commands[]` | Names of invoked slash-commands, same filtering rule as `skills[]`. |
|
|
50
|
+
|
|
51
|
+
### How a name lands in `skills[]` or `commands[]`
|
|
52
|
+
|
|
53
|
+
Both buckets are fed from one local ledger of invocations
|
|
54
|
+
(`.orchestrator/metrics/skill-invocations.jsonl`), so a single classification
|
|
55
|
+
rule decides which bucket a name reaches — and it is deliberately biased
|
|
56
|
+
towards anonymizing rather than towards attributing:
|
|
57
|
+
|
|
58
|
+
- Shipped **skills** are recorded plugin-prefixed
|
|
59
|
+
(`session-orchestrator:session-end`); shipped **commands** are recorded bare
|
|
60
|
+
(`session`). The Skill tool surfaces a slash-command that has no backing
|
|
61
|
+
`skills/` directory under the *prefixed* form too, so a prefixed name whose
|
|
62
|
+
bare form is a shipped command is reported in `commands[]` under that bare
|
|
63
|
+
name.
|
|
64
|
+
- A name is only ever reported as one of our commands when it carries the
|
|
65
|
+
plugin prefix. A **bare** name is never credited to a command, even when it
|
|
66
|
+
collides with one of our command names — a third-party or personal skill
|
|
67
|
+
invoked bare as `test` would otherwise be reported as our `/test` command.
|
|
68
|
+
Bare unknown names take the skills path and are reduced to `"other"`. The one
|
|
69
|
+
exception is a name arriving in the ledger's `.command` **field**: that field
|
|
70
|
+
is itself the "this is one of ours" provenance signal a bare `.skill` arrival
|
|
71
|
+
lacks, so `buildUsagePing` prefixes every `.command` value before
|
|
72
|
+
classification (`scripts/lib/telemetry/schema.mjs`). Without that step the
|
|
73
|
+
`.command` producer would be wired but dead — every record it writes would
|
|
74
|
+
silently become `"other"`.
|
|
75
|
+
- On a spelling collision (`memory-cleanup` exists as both a skill and a
|
|
76
|
+
command) the skill roster wins, so exactly one bucket is credited. Counting
|
|
77
|
+
distinct surfaces across `skills[]` and `commands[]` therefore never
|
|
78
|
+
double-counts a single one.
|
|
47
79
|
|
|
48
80
|
## What we never collect
|
|
49
81
|
|
|
50
82
|
This list is a hard invariant, not a deferral:
|
|
51
83
|
|
|
52
84
|
- No repository names, no file paths, no git remotes.
|
|
53
|
-
- No prompts, no session transcripts, no free-form text of any kind.
|
|
85
|
+
- No prompts, no session transcripts, no free-form text of any kind. Every
|
|
86
|
+
field on the wire is either a number, a boolean, or a value from a closed
|
|
87
|
+
set this repository ships. The last free-text field, `session_profile`, was
|
|
88
|
+
closed on 2026-09-06: it now carries whitelisted profile names only, and an
|
|
89
|
+
unlisted value is dropped before the payload is built (and refused again by
|
|
90
|
+
the server, so it cannot be stored even if some other client sent it).
|
|
54
91
|
- No command arguments — only whitelisted command/skill *names*, and only
|
|
55
92
|
from the shipped roster (anything else is reduced to `"other"`).
|
|
56
93
|
- No hostnames.
|
|
@@ -151,6 +188,38 @@ the offline queue is non-empty or a session has completed since — the latter
|
|
|
151
188
|
clause is what lets the fallback originate a ping instead of only retrying a
|
|
152
189
|
failed one (#1138).
|
|
153
190
|
|
|
191
|
+
## The other thing that leaves the host: the update check
|
|
192
|
+
|
|
193
|
+
Telemetry is not the only outbound request this plugin can make, so the second
|
|
194
|
+
one is documented here rather than somewhere an egress audit would miss it.
|
|
195
|
+
|
|
196
|
+
On **SessionStart**, `hooks/on-session-start.mjs` calls the plugin-update banner
|
|
197
|
+
(`scripts/lib/plugin-update-banner.mjs`), which asks npm whether a newer release
|
|
198
|
+
exists:
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
GET https://registry.npmjs.org/session-orchestrator/latest
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
What it is, precisely:
|
|
205
|
+
|
|
206
|
+
- **No payload and no identifier.** It is a plain `GET` of a constant, public
|
|
207
|
+
URL — no body, no query string, no `anon_id`, no headers this plugin adds.
|
|
208
|
+
npm sees a request for a public package's metadata, as `npm view` would.
|
|
209
|
+
- **At most once per 24 h.** The answer is cached
|
|
210
|
+
(`plugin-latest.json`, `CACHE_TTL_MS = 24 h`); within the TTL no request is
|
|
211
|
+
made at all. The request has a short timeout and every failure is silent.
|
|
212
|
+
- **Independent of telemetry consent.** It is not a ping and sends nothing about
|
|
213
|
+
you — but it is still traffic, so it honours the offline flags below.
|
|
214
|
+
|
|
215
|
+
**Kill switches** (any one of them, set to anything other than empty / `0` /
|
|
216
|
+
`false`, turns the whole check off — not merely the request; the SessionStart
|
|
217
|
+
record then also omits `plugin_version_latest`):
|
|
218
|
+
|
|
219
|
+
- `SO_DISABLE_UPDATE_CHECK` — this probe's own switch.
|
|
220
|
+
- `DO_NOT_TRACK` — the standard flag, also honoured by the telemetry path.
|
|
221
|
+
- `CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC`.
|
|
222
|
+
|
|
154
223
|
## Retention
|
|
155
224
|
|
|
156
225
|
- **Raw records:** kept 24 months, then pruned. The retention window exists
|
|
@@ -161,6 +230,99 @@ failed one (#1138).
|
|
|
161
230
|
- **Anonymous ID rotation:** every 90 days, independent of retention — a
|
|
162
231
|
rotated ID cannot be linked back to the one it replaced.
|
|
163
232
|
|
|
233
|
+
## When a ping is sent
|
|
234
|
+
|
|
235
|
+
Two triggers, deliberately independent of each other:
|
|
236
|
+
|
|
237
|
+
1. **SessionEnd** (`hooks/on-session-end.mjs`) — the mechanical close-time
|
|
238
|
+
flush.
|
|
239
|
+
2. **SessionStart** (`backfillOnSessionStart` in
|
|
240
|
+
`scripts/backfill-abandoned-sessions.mjs`) — drains whatever the PREVIOUS
|
|
241
|
+
session left queued. Since 4.0.0 every queued record is re-projected and
|
|
242
|
+
`session_profile`-whitelisted at the transport boundary before it is sent,
|
|
243
|
+
and a batch the server rejects with HTTP 400/422 is EVICTED (breadcrumb
|
|
244
|
+
`reason: rejected-evicted`) instead of being re-queued forever — a poison
|
|
245
|
+
record can no longer block every later flush.
|
|
246
|
+
|
|
247
|
+
Trigger 2 exists because trigger 1 fires only on a REGULAR close, and most
|
|
248
|
+
sessions do not have one: measured 2026-09-06 over 90 fleet days, **429 clean
|
|
249
|
+
closes against 2.016 distinct `session.started` ids = 21,3 %**. Roughly four
|
|
250
|
+
sessions in five never reached the only code path that sends. SessionStart is
|
|
251
|
+
the trigger that survives whatever killed the previous session — the same
|
|
252
|
+
argument the abandoned-session backfill already makes for the ledger.
|
|
253
|
+
|
|
254
|
+
The start-time flush is bounded (1,5 s POST budget), lazily imported, gated by
|
|
255
|
+
the same consent check, and swallows every error: it can never delay or break a
|
|
256
|
+
session start. A timeout is lossless — the batch lands in the offline queue.
|
|
257
|
+
|
|
258
|
+
A ping no longer depends on `sessions.jsonl`. When the ledger has no matching
|
|
259
|
+
record, `session_type` and `duration_bucket` are reconstructed from
|
|
260
|
+
`events.jsonl` and the ping is stamped `session_record: "derived"`; when neither
|
|
261
|
+
source has a type, it is `session_type: "unknown"` with `session_record:
|
|
262
|
+
"absent"` — never a measured-looking `other`.
|
|
263
|
+
|
|
264
|
+
### Sandbox guard
|
|
265
|
+
|
|
266
|
+
The sender refuses to send when it is not running in a real operator session.
|
|
267
|
+
This is not a nicety: on 2026-09-06 six agent sandboxes ran the SessionEnd hook
|
|
268
|
+
from a repo checkout and sent **six real pings to the production ingest server**,
|
|
269
|
+
minted against the operator's real `anon_id`, because `telemetry/paths.mjs`
|
|
270
|
+
resolves `~/.config/session-orchestrator/` from `homedir()` and does not honour
|
|
271
|
+
`SO_CONFIG_HOME` — faking the source never faked the destination.
|
|
272
|
+
|
|
273
|
+
A send is refused (no network, no queue write, no anon-ID mint) when **any** of:
|
|
274
|
+
|
|
275
|
+
- `SO_TELEMETRY_DISABLED=1` or `DO_NOT_TRACK` is set;
|
|
276
|
+
- `SO_CONFIG_HOME` / `XDG_CONFIG_HOME` points somewhere other than the directory
|
|
277
|
+
the telemetry state is actually read from (unless the caller redirected the
|
|
278
|
+
state path too — that redirect succeeded, which is the opposite of the leak);
|
|
279
|
+
- `CLAUDE_PROJECT_DIR`, or the cwd, sits under the OS temp directory or `/tmp`.
|
|
280
|
+
|
|
281
|
+
The guard also **fails closed**: if any of its own probes throws, the send is
|
|
282
|
+
refused with `sandbox:probe-failed` rather than permitted. An environment the
|
|
283
|
+
guard could not classify is treated as one it would have refused; nothing is
|
|
284
|
+
lost, because the next session re-probes from scratch.
|
|
285
|
+
|
|
286
|
+
If you invoke any telemetry writer by hand, export `SO_TELEMETRY_DISABLED=1`.
|
|
287
|
+
|
|
288
|
+
## Server-side fleet attribution
|
|
289
|
+
|
|
290
|
+
The `fleet` flag on the wire is **self-declared and was measurably wrong**.
|
|
291
|
+
Until 2026-09-06 the client derived it as `ownerConfig?.telemetry?.enabled
|
|
292
|
+
=== true` — a statement about a FILE, not about a person. The operator's
|
|
293
|
+
second Mac has consent granted but no `telemetry:` block in `owner.yaml`,
|
|
294
|
+
so it declared itself external: **394 of 490 server records (80,4 %)**
|
|
295
|
+
counted the operator as an external user, and every week's
|
|
296
|
+
`fleet_vs_external` was wrong by that margin.
|
|
297
|
+
|
|
298
|
+
Two independent repairs, because the client alone cannot close this:
|
|
299
|
+
|
|
300
|
+
1. **Client-side** — `fleet_self_declared` is derived from the resolved
|
|
301
|
+
consent state (`enabled-fleet` / `enabled-env`), so a host opted in via
|
|
302
|
+
`SO_TELEMETRY=1` is no longer mistaken for an external install. The name
|
|
303
|
+
states the limit: a sandbox, or a host whose `owner.yaml` is unreachable,
|
|
304
|
+
still declares `false` however honest it is.
|
|
305
|
+
2. **Server-side (authoritative)** — set `SO_INGEST_FLEET_ANON_IDS` on the
|
|
306
|
+
ingest server to a comma-separated list of the operator's own `anon_id`
|
|
307
|
+
values. A matching record is **stored** as fleet regardless of what it
|
|
308
|
+
claims. The allowlist can only PROMOTE, never demote: a host that honestly
|
|
309
|
+
declares itself fleet stays fleet even if the operator forgot to list it.
|
|
310
|
+
|
|
311
|
+
```
|
|
312
|
+
SO_INGEST_FLEET_ANON_IDS=a3bb4907-…,c29cac99-…
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
The record survives verbatim in `raw_json`, including its own `fleet` /
|
|
316
|
+
`fleet_self_declared` claim, so the client's declaration and the server's
|
|
317
|
+
verdict remain separable forever and the disagreement rate stays measurable.
|
|
318
|
+
`fleet_vs_external` in the weekly digest reads the stored column, i.e. the
|
|
319
|
+
server verdict. **The allowlist applies at INSERT time**, so it cannot repair
|
|
320
|
+
rows already written — a `fleet_vs_external` computed over a range that
|
|
321
|
+
predates the change is known-wrong and re-running the digest will not fix it.
|
|
322
|
+
|
|
323
|
+
Unset by default: with no `SO_INGEST_FLEET_ANON_IDS`, storage takes the
|
|
324
|
+
client's word exactly as it did before.
|
|
325
|
+
|
|
164
326
|
## Schema evolution
|
|
165
327
|
|
|
166
328
|
The schema is **additive-only** within a given `schema_version`: new
|
|
@@ -169,6 +331,27 @@ without a version bump. The server accepts both the current and the
|
|
|
169
331
|
immediately previous `schema_version`, so a slightly-outdated client is
|
|
170
332
|
never hard-broken by a server-side schema update.
|
|
171
333
|
|
|
334
|
+
Unknown top-level fields are accepted by the server and preserved verbatim
|
|
335
|
+
inside `raw_json`, so an additive field round-trips through a server that
|
|
336
|
+
predates it — which is what makes a *rename* safe: emit both names for one
|
|
337
|
+
generation, then drop the old one.
|
|
338
|
+
|
|
339
|
+
**In flight now (added 2026-09-06, schema v1, additive):**
|
|
340
|
+
|
|
341
|
+
| Field | Status | Removal |
|
|
342
|
+
|---|---|---|
|
|
343
|
+
| `fleet_self_declared` | new name for `fleet` | — |
|
|
344
|
+
| `fleet` | deprecated alias, same value | **2027-03-06** |
|
|
345
|
+
| `session_record` | new (`ledger` \| `derived` \| `absent`) | — |
|
|
346
|
+
| `session_profile` | new (STATE.md `session-profile`, whitelisted names only; omitted when unset or unlisted) | — |
|
|
347
|
+
|
|
348
|
+
Client-side the frozen whitelist is split in two: `USAGE_PING_FIELDS` (the
|
|
349
|
+
REQUIRED v1 contract, which `tests/telemetry/parity.test.mjs` asserts the
|
|
350
|
+
server independently requires field by field) and
|
|
351
|
+
`USAGE_PING_OPTIONAL_FIELDS`. `projectUsagePing` projects the UNION, so the
|
|
352
|
+
data-minimization tripwire still holds — a field must be on a reviewed list
|
|
353
|
+
before it can reach the wire.
|
|
354
|
+
|
|
172
355
|
## Relationship to `telemetry-claims.md`
|
|
173
356
|
|
|
174
357
|
This page describes the **opt-in, client-side usage-telemetry pipeline**
|
|
@@ -1,12 +1,19 @@
|
|
|
1
1
|
# Vault & Docs Architecture — Umbrella Narrative
|
|
2
2
|
|
|
3
3
|
**Audience:** Plugin contributors (Dev). New contributors who need to understand
|
|
4
|
-
how the
|
|
5
|
-
discovery probes fit together — what fires when, who owns which file, and how
|
|
4
|
+
how the three documentation skills, one orchestrator skill, one agent, two
|
|
5
|
+
vault-status writers, and two discovery probes fit together — what fires when, who owns which file, and how
|
|
6
6
|
to recover when something breaks.
|
|
7
7
|
|
|
8
8
|
**Status:** Living document. Tracks Epic #229 (Vault & Docs Orchestration).
|
|
9
9
|
|
|
10
|
+
**Last verified:** 2026-09-06 at `e4674109`. The vault-file layout below was
|
|
11
|
+
re-measured against the code that writes it — `scripts/lib/vault-status/narrative-mirror.mjs`
|
|
12
|
+
(`resolveNarrativePath`) and `scripts/lib/vault-status/board-writer.mjs` (`resolveBoardPath`) —
|
|
13
|
+
after the pre-#673/#674/#675 layout in this file was found stale by the 2026-09-06
|
|
14
|
+
360°-Audit (`docs/audits/2026-09-06-360-audit/w1/d6-docs-drift.md`). The `daily` skill
|
|
15
|
+
rows are gone with the skill itself (§ 5A of that audit).
|
|
16
|
+
|
|
10
17
|
---
|
|
11
18
|
|
|
12
19
|
## 1. Purpose
|
|
@@ -48,6 +55,8 @@ Data flow within a single `/session feature → /go → /close` cycle:
|
|
|
48
55
|
↓
|
|
49
56
|
┌──────────────────────────────────────────────────────────────────┐
|
|
50
57
|
│ session-start │
|
|
58
|
+
│ Phase 1.7 vault-status board (opt-in) │
|
|
59
|
+
│ └─ this repo → in-progress in _active-sessions.md │
|
|
51
60
|
│ Phase 2.5 docs-orchestrator (opt-in) │
|
|
52
61
|
│ └─ audience detection → docs-tasks block in STATE.md │
|
|
53
62
|
│ Source: skills/session-start/phase-2-5-docs-planning.md │
|
|
@@ -78,6 +87,8 @@ Data flow within a single `/session feature → /go → /close` cycle:
|
|
|
78
87
|
│ Phase 2.3 vault-staleness (opt-in: stale projects) │
|
|
79
88
|
│ Phase 3.2 docs-verify (per-task ok/partial/gap) │
|
|
80
89
|
│ Phase 3.7 vault-mirror (sessions.jsonl → 50-sessions/) │
|
|
90
|
+
│ Phase 3.7 narrative-mirror (STATE.md → _session-narrative) │
|
|
91
|
+
│ Phase 3.7c vault board (this repo's row → closed) │
|
|
81
92
|
│ Source: skills/session-end/SKILL.md (phase markers) │
|
|
82
93
|
└──────────────────────────────────────────────────────────────────┘
|
|
83
94
|
↓
|
|
@@ -86,7 +97,10 @@ Data flow within a single `/session feature → /go → /close` cycle:
|
|
|
86
97
|
│ 01-projects/<slug>/ ← context.md / decisions.md / people.md │
|
|
87
98
|
│ (docs-writer, Vault audience) │
|
|
88
99
|
│ 01-projects/<slug>/ ← _overview.md (vault-mirror, no humans) │
|
|
89
|
-
│
|
|
100
|
+
│ 01-projects/<slug>/ ← _session-narrative.md │
|
|
101
|
+
│ (narrative-mirror, session-end 3.7) │
|
|
102
|
+
│ 01-projects/_active-sessions.md │
|
|
103
|
+
│ (board-writer, start 1.7 / end 3.7c) │
|
|
90
104
|
│ 40-learnings/<slug>.md (vault-mirror, evolve hook) │
|
|
91
105
|
│ 50-sessions/<id>.md (vault-mirror, session-end Phase 3.7) │
|
|
92
106
|
└──────────────────────────────────────────────────────────────────┘
|
|
@@ -109,7 +123,8 @@ Data flow within a single `/session feature → /go → /close` cycle:
|
|
|
109
123
|
| `vault-staleness` probes | `skills/discovery/probes-vault.md` + `skills/discovery/probes/vault-staleness.mjs` | `/discovery vault` (on-demand) and session-end Phase 2.3 (opt-in close-time gate) | `VAULT_DIR/01-projects/*/` `_overview.md` + narrative files | JSONL findings under `.orchestrator/metrics/vault-staleness.jsonl` and `vault-narrative-staleness.jsonl` | Vault/Ops (telemetry) |
|
|
110
124
|
| `docs-orchestrator` | `skills/docs-orchestrator/SKILL.md` | session-start Phase 2.5, session-plan Step 1.5/1.8, session-end Phase 3.2 (all gated on `enabled: true`) | Session scope + Session Config audience list | `docs-tasks` block in STATE.md (write side); `### Documentation Coverage` block in final report (verify side) | All three (User / Dev / Vault) |
|
|
111
125
|
| `docs-writer` agent | `agents/docs-writer.md` | Dispatched by `wave-executor` for each `Docs`-classified task | `diff`, `git-log`, `session-memory`, `affected-files` | Audience-targeted Markdown writes (Edit/Write); `[docs-orchestrator] Docs task complete` report line | All three (per task) |
|
|
112
|
-
| `
|
|
126
|
+
| `narrative-mirror` | `scripts/lib/vault-status/narrative-mirror.mjs` (`mirrorNarrative`) | session-end Phase 3.7, gated on `vault-integration.enabled` | `.claude/STATE.md` narrative sections + the session record | `<vault>/01-projects/<repo-slug>/_session-narrative.md` (generator-marked) | Vault/Ops (durable per-repo narrative) |
|
|
127
|
+
| `board-writer` | `scripts/lib/vault-status/board-writer.mjs` (`sweepBoard` / `mirrorBoard`) | session-start Phase 1.7 (`in-progress`) and session-end Phase 3.7c (`closed`), gated on `vault-integration.enabled` | live session registry + this repo's root | `<vault>/01-projects/_active-sessions.md` — one row per repo (generator-marked, idempotent, never touches `_overview.md`) | Vault/Ops (cross-repo occupancy board) |
|
|
113
128
|
| `vault-mirror` | `skills/vault-mirror/SKILL.md` + `scripts/vault-mirror.mjs` | session-end Phase 3.7 (sessions); evolve Phase 3.5 (learnings) | `.orchestrator/metrics/sessions.jsonl`, `.orchestrator/metrics/learnings.jsonl` | `<vault>/50-sessions/<id>.md`, `<vault>/40-learnings/<slug>.md` (`_generator` marker `session-orchestrator-vault-mirror@1`) | Vault/Ops (telemetry → Markdown) |
|
|
114
129
|
| `vault-backfill` CLI | `scripts/vault-backfill.mjs` | Manual, also surfaced via `/plan retro vault-backfill` sub-mode | `vault-integration.gitlab-groups` config + GitLab API | `.vault.yaml` per repo + Vault stub directories | Vault/Ops (one-shot migration) |
|
|
115
130
|
|
|
@@ -131,6 +146,15 @@ which audience. Never inline this table elsewhere — always cross-link.
|
|
|
131
146
|
`<vault>/01-projects/<slug>/context.md`, `decisions.md`, `people.md`.
|
|
132
147
|
Source: same.
|
|
133
148
|
|
|
149
|
+
Those three are the **authored** half of a project folder and are the only vault
|
|
150
|
+
files docs-writer may touch. The other four under `01-projects/` are **generated**
|
|
151
|
+
and carry a `_` prefix plus a generator marker: `_overview.md` (vault-mirror),
|
|
152
|
+
`_session-narrative.md` (narrative-mirror, #675), and — one level up, per vault
|
|
153
|
+
rather than per project — `_active-sessions.md` (board-writer, #674). Editing a
|
|
154
|
+
generated file by hand is not forbidden by a rule; it is simply overwritten on the
|
|
155
|
+
next run. Source: the `resolveNarrativePath` / `resolveBoardPath` path builders in
|
|
156
|
+
`scripts/lib/vault-status/`.
|
|
157
|
+
|
|
134
158
|
The Session Config field `docs-orchestrator.audiences` accepts any subset of
|
|
135
159
|
`[user, dev, vault]`; narrowing it (e.g., `[user, dev]` on a project without a
|
|
136
160
|
Vault) suppresses Vault-targeted docs without disabling the orchestrator
|
|
@@ -169,7 +193,7 @@ silent REVIEW-marker-only output. Source:
|
|
|
169
193
|
|
|
170
194
|
## 6. Non-Overlap Discipline
|
|
171
195
|
|
|
172
|
-
|
|
196
|
+
Four forbidden cross-writes are enforced by the architecture, not just by
|
|
173
197
|
convention:
|
|
174
198
|
|
|
175
199
|
- **`<vault>/01-projects/*/_overview.md` is owned by `vault-mirror`.** The
|
|
@@ -179,11 +203,24 @@ convention:
|
|
|
179
203
|
`skills/docs-orchestrator/audience-mapping.md` § Non-Overlap (vault-mirror
|
|
180
204
|
row) and `skills/vault-mirror/SKILL.md` § Idempotency (the `_generator`
|
|
181
205
|
marker `session-orchestrator-vault-mirror@1` is the discriminator).
|
|
182
|
-
- **`<vault>/
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
`
|
|
206
|
+
- **`<vault>/01-projects/_active-sessions.md` and
|
|
207
|
+
`<vault>/01-projects/<slug>/_session-narrative.md` are owned by the two
|
|
208
|
+
vault-status writers.** Both are generator-marked and rewritten wholesale on
|
|
209
|
+
the next session-start / session-end, so a hand edit is silently lost rather
|
|
210
|
+
than merged. `board-writer` additionally holds a cross-repo mutex at
|
|
211
|
+
`<vault>/.orchestrator/board.lock` while writing
|
|
212
|
+
(`scripts/lib/vault-status/board-lock.mjs`, #1180 — deliberately not beside
|
|
213
|
+
the board) because the board is the one vault file several repos write
|
|
214
|
+
concurrently.
|
|
215
|
+
- **`<vault>/03-daily/*` remains a forbidden path for `docs-writer`, but no
|
|
216
|
+
plugin component writes it any more.** The `daily` skill was retired on
|
|
217
|
+
2026-09-06 (360°-Audit § 5A) and `templates/daily.md.tpl` is gone with it;
|
|
218
|
+
measured the same day, `03-daily/` survives in code only inside
|
|
219
|
+
`scripts/lib/frontmatter-guard.mjs`'s vault-subdirectory *detector*, which
|
|
220
|
+
reads paths and writes none. The forbidden-path entries in
|
|
221
|
+
`skills/docs-orchestrator/audience-mapping.md` § Non-Overlap and
|
|
222
|
+
`agents/docs-writer.md` still name `daily` as the owner; that is stale
|
|
223
|
+
attribution for a path the operator's own PKM now owns, not a live contract.
|
|
187
224
|
- **`CLAUDE.md` may be remediated by `docs-writer` (Dev audience), but
|
|
188
225
|
`claude-md-drift-check` only diagnoses it.** The two skills must not run
|
|
189
226
|
on `CLAUDE.md` in parallel within the same wave. Source:
|
|
@@ -201,6 +238,7 @@ Concrete answer to "when does each component fire":
|
|
|
201
238
|
|
|
202
239
|
| Phase | Skill / Probe | Gating |
|
|
203
240
|
|-------|---------------|--------|
|
|
241
|
+
| `/session` start, Phase 1.7 | `board-writer` marks this repo `in-progress` on `_active-sessions.md` | `vault-integration.enabled: true` |
|
|
204
242
|
| `/session` start, Phase 2.5 | `docs-orchestrator` audience detection | `docs-orchestrator.enabled: true` |
|
|
205
243
|
| `/session` start, Phase 4.5 | resource-health probe | always (env-aware) |
|
|
206
244
|
| session-plan Step 1.5/1.8 | `docs-writer` registered + Docs role classified | `docs-orchestrator.enabled: true` |
|
|
@@ -210,9 +248,10 @@ Concrete answer to "when does each component fire":
|
|
|
210
248
|
| `/close` Phase 2.3 | `vault-staleness` + `vault-narrative-staleness` probes | `vault-staleness.enabled: true` |
|
|
211
249
|
| `/close` Phase 3.2 | `docs-orchestrator` verification | `docs-orchestrator.enabled: true` AND `docs-tasks` block present |
|
|
212
250
|
| `/close` Phase 3.7 | `vault-mirror` (sessions) | `vault-integration.enabled: true` AND `mode != off` |
|
|
251
|
+
| `/close` Phase 3.7 | `narrative-mirror` → `_session-narrative.md` | `vault-integration.enabled: true`; `mode: strict` blocks the close on failure, otherwise WARN |
|
|
252
|
+
| `/close` Phase 3.7c | `board-writer` transitions this repo's row to `closed` | `vault-integration.enabled: true`; non-blocking |
|
|
213
253
|
| evolve Phase 3.5 | `vault-mirror` (learnings) | same as above |
|
|
214
254
|
| `/discovery vault` | `vault-staleness` probes (on-demand) | `.vault.yaml` present OR `vault-integration.enabled: true` |
|
|
215
|
-
| `/daily` | `daily` skill | user-invocable; no Session Config gate |
|
|
216
255
|
|
|
217
256
|
Sources: `skills/session-end/SKILL.md` (Phase markers), `docs/session-config-reference.md`
|
|
218
257
|
(per-skill enabled-flag semantics), `skills/discovery/probes-vault.md` (probe
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* atomic-json.mjs — shared atomic read-modify-write helper for JSON hook state.
|
|
3
|
+
*
|
|
4
|
+
* Extracted (issue #1197) from four byte-identical private copies of
|
|
5
|
+
* `atomicMutateJson` that had accumulated independently in
|
|
6
|
+
* `hooks/cwd-change-restore.mjs`, `hooks/on-session-end.mjs`,
|
|
7
|
+
* `hooks/post-tool-batch-wave-signal.mjs`, and
|
|
8
|
+
* `hooks/post-tool-failure-corrective-context.mjs` (measured 2026-09-02 @
|
|
9
|
+
* a019d5a4 via `rg -n "function atomicMutateJson" hooks/` — 4 hits, one per
|
|
10
|
+
* file, logic byte-identical modulo the per-file tmp-suffix and the
|
|
11
|
+
* `fs.`-namespace-vs-named-import style `on-session-end.mjs` uses).
|
|
12
|
+
*
|
|
13
|
+
* F-H fix (W3-reviewer finding, #1197): the pre-extraction copies treated
|
|
14
|
+
* EVERY read/parse failure as "file absent" and silently fell back to
|
|
15
|
+
* `defaultValue` — so an unparsable file, `EISDIR`, `EACCES`, or a file
|
|
16
|
+
* truncated mid-write by a concurrent writer was overwritten with
|
|
17
|
+
* `defaultValue`-derived content instead of being left alone. Only `ENOENT`
|
|
18
|
+
* (file genuinely does not exist yet) is a legitimate "start fresh" case;
|
|
19
|
+
* every other failure now aborts BEFORE the tmp-write/rename stage and
|
|
20
|
+
* reports `{ ok: false, reason }` — the original file is never touched.
|
|
21
|
+
*
|
|
22
|
+
* Return-object over throw (deliberate — see #1197 task note): all four
|
|
23
|
+
* callers already run under `main().catch(() => {}).finally(() =>
|
|
24
|
+
* process.exit(0))` — informational, never-deny hooks (verified 2026-09-02:
|
|
25
|
+
* none of the four match a deny/block pattern) — so a thrown error would be
|
|
26
|
+
* swallowed safely too. But in `post-tool-batch-wave-signal.mjs` a throw at
|
|
27
|
+
* the FIRST call site (line ~278, the `last_batch` write) would abort
|
|
28
|
+
* `main()` before it reaches the independent heartbeat-refresh block that
|
|
29
|
+
* follows — a real behavioural loss the thrown-error path would introduce
|
|
30
|
+
* silently. A result object lets every caller decide locally whether an RMW
|
|
31
|
+
* failure should short-circuit the rest of `main()` or just get logged and
|
|
32
|
+
* ignored, so no caller loses unrelated post-call behaviour by construction.
|
|
33
|
+
*
|
|
34
|
+
* NAMED CEILING (BV-004): tmp-file + rename makes each individual write
|
|
35
|
+
* atomic, but not the full read-modify-write — two concurrent callers can
|
|
36
|
+
* still interleave (both read the same `current`, both compute an update
|
|
37
|
+
* from it, the second rename wins and silently drops the first mutation).
|
|
38
|
+
* This module does not defend against that race. Revisit with a per-file
|
|
39
|
+
* lock (e.g. an flock-style sidecar or `session-lock.mjs`'s lease pattern)
|
|
40
|
+
* if `.orchestrator/current-session.json` writes start dropping fields
|
|
41
|
+
* under concurrent-session load — no such loss has been measured yet.
|
|
42
|
+
*
|
|
43
|
+
* @module hooks/_lib/atomic-json
|
|
44
|
+
*/
|
|
45
|
+
|
|
46
|
+
import { readFile, writeFile, rename, mkdir, unlink } from 'node:fs/promises';
|
|
47
|
+
import path from 'node:path';
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Atomic read-modify-write of a JSON file via temp-file + rename.
|
|
51
|
+
*
|
|
52
|
+
* Reads the existing file and parses it as JSON. When the file does not
|
|
53
|
+
* exist (`ENOENT`), starts from `defaultValue` — the only case in which
|
|
54
|
+
* "file absent" is legitimate. Any OTHER read or parse failure (directory
|
|
55
|
+
* at `filePath`, permission denied, truncated/corrupt JSON, …) aborts
|
|
56
|
+
* WITHOUT calling `mutate` and WITHOUT writing anything — the original file
|
|
57
|
+
* (if any) is left exactly as it was.
|
|
58
|
+
*
|
|
59
|
+
* On success, applies the synchronous `mutate` transformer, writes the
|
|
60
|
+
* result to a `${filePath}.tmp-<suffix>-<pid>-<ts>` sibling, then renames it
|
|
61
|
+
* over `filePath` (atomic on POSIX same-filesystem rename; best-effort on
|
|
62
|
+
* Windows). If the write/rename stage itself fails, the tmp file is
|
|
63
|
+
* best-effort unlinked so a failure never leaves an orphaned `.tmp-*`
|
|
64
|
+
* artifact behind.
|
|
65
|
+
*
|
|
66
|
+
* @param {string} filePath — absolute path to the JSON file.
|
|
67
|
+
* @param {object} defaultValue — starting value used ONLY when the file does
|
|
68
|
+
* not exist yet (`ENOENT`). Never applied on top of an unreadable-but-
|
|
69
|
+
* present file.
|
|
70
|
+
* @param {function(object): object} mutate — pure synchronous transformer;
|
|
71
|
+
* receives the parsed current value (or `defaultValue`), returns the next
|
|
72
|
+
* value to persist.
|
|
73
|
+
* @param {string} [tmpTag] — short tag folded into the tmp filename so
|
|
74
|
+
* concurrent callers targeting the same `filePath` from different hooks
|
|
75
|
+
* don't collide on the same tmp path (mirrors the per-caller suffixes the
|
|
76
|
+
* four pre-extraction copies used: `-cwd-`, `-ose-`, `-ptb-`, `-ptf-`).
|
|
77
|
+
* @returns {Promise<{ ok: true, value: object } | { ok: false, reason: string }>}
|
|
78
|
+
*/
|
|
79
|
+
export async function atomicMutateJson(filePath, defaultValue, mutate, tmpTag = 'ajs') {
|
|
80
|
+
let current = defaultValue;
|
|
81
|
+
try {
|
|
82
|
+
const raw = await readFile(filePath, 'utf8');
|
|
83
|
+
current = JSON.parse(raw);
|
|
84
|
+
} catch (err) {
|
|
85
|
+
if (err && err.code === 'ENOENT') {
|
|
86
|
+
// File genuinely does not exist yet — the only legitimate "fresh
|
|
87
|
+
// file" case. `current` already holds `defaultValue`.
|
|
88
|
+
} else {
|
|
89
|
+
// EISDIR, EACCES, a JSON.parse SyntaxError (unparsable/truncated
|
|
90
|
+
// content), or anything else — never silently treat as "absent".
|
|
91
|
+
// Abort before mutate/write; the original file is untouched.
|
|
92
|
+
return { ok: false, reason: (err && err.code) || 'parse-error' };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const updated = mutate(current);
|
|
97
|
+
const tmp = `${filePath}.tmp-${tmpTag}-${process.pid}-${Date.now()}`;
|
|
98
|
+
try {
|
|
99
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
100
|
+
await writeFile(tmp, JSON.stringify(updated, null, 2) + '\n', 'utf8');
|
|
101
|
+
await rename(tmp, filePath);
|
|
102
|
+
} catch (err) {
|
|
103
|
+
try {
|
|
104
|
+
await unlink(tmp);
|
|
105
|
+
} catch {
|
|
106
|
+
// tmp was never created, or is already gone — nothing to clean up.
|
|
107
|
+
}
|
|
108
|
+
return { ok: false, reason: (err && err.code) || 'write-error' };
|
|
109
|
+
}
|
|
110
|
+
return { ok: true, value: updated };
|
|
111
|
+
}
|