session-orchestrator 3.19.0 → 3.21.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.
Files changed (158) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.codex-plugin/plugin.json +1 -1
  4. package/.cursor/rules/030-wave-execution.mdc +10 -8
  5. package/CHANGELOG.md +494 -0
  6. package/README.md +16 -11
  7. package/agents/analyst.md +1 -1
  8. package/agents/architect-reviewer.md +1 -1
  9. package/agents/code-implementer.md +4 -2
  10. package/agents/db-specialist.md +1 -1
  11. package/agents/dialectic-deriver.md +1 -1
  12. package/agents/docs-writer.md +1 -1
  13. package/agents/memory-proposal-collector.md +1 -1
  14. package/agents/qa-strategist.md +1 -1
  15. package/agents/security-reviewer.md +1 -1
  16. package/agents/session-reviewer.md +42 -1
  17. package/agents/skill-applied-judge.md +1 -1
  18. package/agents/test-writer.md +1 -1
  19. package/agents/ui-developer.md +1 -1
  20. package/agents/ux-evaluator.md +1 -1
  21. package/commands/release.md +60 -0
  22. package/commands/session.md +6 -2
  23. package/docs/USER-GUIDE.md +1 -1
  24. package/docs/instruction-delivery.md +350 -0
  25. package/docs/migration-v3.md +9 -6
  26. package/docs/persona-panel.md +3 -1
  27. package/docs/scope-collision-guard.md +167 -0
  28. package/docs/session-config-reference.md +1 -41
  29. package/docs/session-config-template.md +0 -23
  30. package/hooks/_lib/guard-source-loader.mjs +304 -91
  31. package/hooks/enforce-commands.mjs +216 -17
  32. package/hooks/enforce-scope.mjs +236 -12
  33. package/hooks/hooks-codex.json +1 -1
  34. package/hooks/hooks.json +11 -1
  35. package/hooks/on-session-end.mjs +52 -5
  36. package/hooks/on-session-start.mjs +7 -4
  37. package/hooks/on-stop.mjs +127 -12
  38. package/hooks/post-bash-write-verify.mjs +8 -32
  39. package/hooks/pre-bash-destructive-guard.mjs +146 -59
  40. package/hooks/pre-bash-sessions-ledger-guard.mjs +493 -66
  41. package/hooks/pre-task-scope-disjoint.mjs +1042 -0
  42. package/package.json +2 -2
  43. package/pi/prompts/release.md +12 -0
  44. package/scripts/autopilot.mjs +3 -1
  45. package/scripts/backfill-learnings-from-vault.mjs +967 -0
  46. package/scripts/emit-session.mjs +45 -40
  47. package/scripts/export-hw-learnings.mjs +61 -2
  48. package/scripts/lib/autopilot/worktree-pipeline.mjs +5 -5
  49. package/scripts/lib/backlog-scan.mjs +106 -15
  50. package/scripts/lib/build-live-signals.mjs +7 -3
  51. package/scripts/lib/ci-status-banner.mjs +207 -23
  52. package/scripts/lib/command-blocker.mjs +322 -62
  53. package/scripts/lib/git-config-drift.mjs +471 -0
  54. package/scripts/lib/hardening.mjs +9 -9
  55. package/scripts/lib/harness-audit/categories/category6.mjs +65 -12
  56. package/scripts/lib/io.mjs +193 -7
  57. package/scripts/lib/learnings/affinity.mjs +434 -0
  58. package/scripts/lib/learnings/candidates.mjs +736 -0
  59. package/scripts/lib/learnings/expiry-sweep.mjs +408 -53
  60. package/scripts/lib/learnings/judgment.mjs +782 -0
  61. package/scripts/lib/learnings/kebab.mjs +128 -0
  62. package/scripts/lib/learnings/select.mjs +704 -0
  63. package/scripts/lib/memory-cleanup-stamp.mjs +132 -8
  64. package/scripts/lib/mirror-issues-banner.mjs +266 -0
  65. package/scripts/lib/named-vault-resolver.mjs +105 -16
  66. package/scripts/lib/peer-cards/schema.mjs +6 -2
  67. package/scripts/lib/reconcile/emitter.mjs +107 -22
  68. package/scripts/lib/reconcile/engine.mjs +9 -15
  69. package/scripts/lib/reconcile/renderer.mjs +141 -25
  70. package/scripts/lib/reconcile/sanitize.mjs +518 -0
  71. package/scripts/lib/reconcile/writer.mjs +134 -1
  72. package/scripts/lib/redact-spans.mjs +89 -0
  73. package/scripts/lib/scope-baseline.mjs +77 -17
  74. package/scripts/lib/scope-gate.mjs +852 -72
  75. package/scripts/lib/secret-masker.mjs +262 -0
  76. package/scripts/lib/session-close-backfill.mjs +2 -2
  77. package/scripts/lib/session-lock.mjs +34 -10
  78. package/scripts/lib/session-record-repair.mjs +551 -0
  79. package/scripts/lib/session-registry.mjs +9 -1
  80. package/scripts/lib/session-schema/serializer.mjs +54 -0
  81. package/scripts/lib/session-schema.mjs +1 -0
  82. package/scripts/lib/session-token-rollup.mjs +68 -6
  83. package/scripts/lib/soul-resolve.mjs +12 -0
  84. package/scripts/lib/state-md/mission-status.mjs +21 -12
  85. package/scripts/lib/tmux-layout/telemetry.mjs +43 -10
  86. package/scripts/lib/tmux-layout/vcs-detector.mjs +108 -4
  87. package/scripts/lib/validate/check-agents.mjs +77 -5
  88. package/scripts/lib/validate/check-banner-parity.mjs +376 -0
  89. package/scripts/lib/validate/check-commands.mjs +2 -20
  90. package/scripts/lib/validate/check-doc-cli-commands.mjs +514 -0
  91. package/scripts/lib/validate/check-guard-requires-parity.mjs +1148 -0
  92. package/scripts/lib/validate/check-hooks-symmetry.mjs +18 -0
  93. package/scripts/lib/validate/check-learning-provenance.mjs +511 -0
  94. package/scripts/lib/validate/check-owner-leakage.mjs +188 -20
  95. package/scripts/lib/validate/check-rules.mjs +31 -5
  96. package/scripts/lib/validate/check-skills.mjs +191 -0
  97. package/scripts/lib/validate/check-test-git-config-target.mjs +665 -0
  98. package/scripts/lib/validate/check-unicode-safety.mjs +22 -2
  99. package/scripts/lib/validate/check-untracked-test-deps.mjs +925 -0
  100. package/scripts/lib/validate/check-unwired-features.mjs +757 -0
  101. package/scripts/lib/validate/check-vcs-repo-flag.mjs +965 -0
  102. package/scripts/lib/validate/frontmatter-block.mjs +61 -0
  103. package/scripts/lib/validate/tier-inference.mjs +46 -8
  104. package/scripts/lib/vault-mirror/namespace.mjs +146 -1
  105. package/scripts/lib/vault-mirror/process.mjs +264 -31
  106. package/scripts/lib/vault-mirror/render-sessions.mjs +115 -4
  107. package/scripts/lib/vault-status/board-writer.mjs +300 -56
  108. package/scripts/lib/vault-status/narrative-mirror.mjs +119 -5
  109. package/scripts/lib/vcs-repo-spec.mjs +500 -19
  110. package/scripts/print-applicable-rules.mjs +170 -7
  111. package/scripts/print-learnings-index.mjs +501 -0
  112. package/scripts/release.mjs +616 -61
  113. package/scripts/repair-invalid-sessions.mjs +209 -0
  114. package/scripts/site-numbers.mjs +1049 -0
  115. package/scripts/sweep-expired-learnings.mjs +192 -32
  116. package/scripts/validate-plugin.mjs +82 -0
  117. package/scripts/validate-wave-scope.mjs +281 -12
  118. package/scripts/vault-mirror.mjs +26 -1
  119. package/skills/_shared/monitor-patterns.md +24 -4
  120. package/skills/_shared/state-ownership.md +17 -0
  121. package/skills/brainstorm/soul.md +47 -1
  122. package/skills/claude-md-drift-check/SKILL.md +9 -1
  123. package/skills/debug/SKILL.md +4 -1
  124. package/skills/discovery/issue-templates.md +4 -4
  125. package/skills/discovery/probes-code.md +2 -2
  126. package/skills/discovery/probes-feature.md +6 -6
  127. package/skills/discovery/probes-infra.md +2 -2
  128. package/skills/discovery/probes-session.md +5 -5
  129. package/skills/dispatcher/SKILL.md +10 -1
  130. package/skills/evolve/SKILL.md +116 -18
  131. package/skills/frontmatter-guard/SKILL.md +9 -1
  132. package/skills/gitlab-ops/SKILL.md +54 -39
  133. package/skills/gitlab-portfolio/SKILL.md +10 -1
  134. package/skills/grill/soul.md +44 -1
  135. package/skills/memory-cleanup/SKILL.md +18 -5
  136. package/skills/npm-publish/SKILL.md +22 -50
  137. package/skills/persona-panel/SKILL.md +3 -1
  138. package/skills/plan/mode-new.md +23 -5
  139. package/skills/plan/soul.md +46 -3
  140. package/skills/repo-audit/SKILL.md +10 -1
  141. package/skills/session-end/SKILL.md +45 -26
  142. package/skills/session-end/metrics-collection.md +1 -1
  143. package/skills/session-end/phase-3-6-tail.md +30 -1
  144. package/skills/session-end/plan-verification.md +1 -5
  145. package/skills/session-end/session-metrics-write.md +6 -10
  146. package/skills/session-plan/SKILL.md +2 -2
  147. package/skills/session-plan/wave-template.md +1 -1
  148. package/skills/session-start/SKILL.md +15 -1
  149. package/skills/session-start/soul.md +41 -1
  150. package/skills/spinout/SKILL.md +5 -1
  151. package/skills/sunset-review/SKILL.md +11 -1
  152. package/skills/tmux-layout/SKILL.md +7 -2
  153. package/skills/vault-mirror/SKILL.md +10 -1
  154. package/skills/vault-sync/SKILL.md +10 -1
  155. package/skills/vault-sync/validator.mjs +55 -6
  156. package/skills/wave-executor/SKILL.md +1 -5
  157. package/skills/wave-executor/wave-loop.md +77 -82
  158. package/scripts/lib/mission-status-schema.mjs +0 -114
@@ -0,0 +1,209 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * repair-invalid-sessions.mjs — one-time repair CLI for schema-invalid session
4
+ * ledger records (GitLab #1004).
5
+ *
6
+ * Thin driver: argument parsing, output rendering, exit codes. All repair logic
7
+ * lives in `scripts/lib/session-record-repair.mjs` so tests import functions
8
+ * instead of spawning a process.
9
+ *
10
+ * node scripts/repair-invalid-sessions.mjs [--dry-run|--apply] [--json]
11
+ * [--file PATH] [--repo-root PATH]
12
+ *
13
+ * SAFETY: `--dry-run` is the DEFAULT — nothing is written unless `--apply` is
14
+ * passed. Under `--apply` the original is ALWAYS copied to `<file>.bak-<stamp>`
15
+ * BEFORE the swap (the backup is mandatory — there is no opt-out, because this
16
+ * is a write path to a protected ledger and the `.bak` is its only forensic
17
+ * trail; MED-3), the new content is written to `<file>.tmp-<pid>` and renamed
18
+ * over the target (atomic), and the result is then re-verified against BOTH
19
+ * `validateSession` and `checkSessionsIntegrity`. A failed verification restores
20
+ * the backup byte-identically and exits 3 — the ledger is never left in a state
21
+ * worse than the one this CLI found.
22
+ *
23
+ * TARGET CONSTRAINT (MED-3): `--file` MUST resolve inside
24
+ * `<repo-root>/.orchestrator/metrics/`. Any target outside it — including a
25
+ * `..` traversal or a symlinked parent that escapes the dir — is refused with
26
+ * exit 1 BEFORE any read or write. This CLI is a write path to a protected
27
+ * ledger that the pre-bash-sessions-ledger-guard does not see (it resolves to
28
+ * verb `node`), so the target it may touch is bounded here.
29
+ *
30
+ * Exit codes (`.claude/rules/cli-design.md`):
31
+ * 0 — completed (dry-run or apply); the ledger is clean or unchanged
32
+ * 1 — user/input error (bad flag, unknown argument)
33
+ * 2 — system error (unreadable/unwritable ledger, I/O failure)
34
+ * 3 — post-verification failed; the backup was restored and nothing was kept
35
+ */
36
+
37
+ import fs from 'node:fs';
38
+ import { parseArgs } from 'node:util';
39
+ import path from 'node:path';
40
+ import { fileURLToPath } from 'node:url';
41
+
42
+ import { repairLedger, CANONICAL_LEDGER_REL } from './lib/session-record-repair.mjs';
43
+ import { SO_PROJECT_DIR } from './lib/platform.mjs';
44
+
45
+ const USAGE =
46
+ 'Usage: node scripts/repair-invalid-sessions.mjs [--dry-run|--apply] [--json]\n' +
47
+ ' [--file PATH] [--repo-root PATH]\n' +
48
+ ' --dry-run preview only (DEFAULT — nothing is written)\n' +
49
+ ' --apply rewrite the ledger in place (mandatory backup + atomic rename)\n' +
50
+ ' --json emit the summary as JSON to stdout\n' +
51
+ ' --file ledger path (default: <repo-root>/' +
52
+ CANONICAL_LEDGER_REL +
53
+ ');\n' +
54
+ ' must resolve inside <repo-root>/.orchestrator/metrics/\n' +
55
+ ' --repo-root project root (default: resolved SO_PROJECT_DIR)\n' +
56
+ 'Exit codes: 0 completed, 1 arg error, 2 system error, 3 post-verification failed\n';
57
+
58
+ /**
59
+ * Resolve `--file` and REFUSE any target outside
60
+ * `<repoRoot>/.orchestrator/metrics/` (MED-3). Symlinks are resolved on the
61
+ * metrics dir and on the target's PARENT (the file itself may not exist yet),
62
+ * so a symlinked parent cannot escape the dir; `path.resolve` collapses any
63
+ * `..` traversal before the prefix check.
64
+ *
65
+ * @param {string} file - the requested target (default or `--file` value).
66
+ * @param {string} repoRoot
67
+ * @returns {{ok: true, file: string}|{ok: false, metricsDir: string, resolved: string}}
68
+ */
69
+ function resolveLedgerTarget(file, repoRoot) {
70
+ const metricsDir = path.resolve(repoRoot, '.orchestrator', 'metrics');
71
+ // `absFile` (normalized, `..` collapsed) is what the repair uses — NOT the
72
+ // symlink-resolved form, so the canonical-path equality in `verifyWritten`
73
+ // (which compares against a non-realpath'd repoRoot) still recognises it.
74
+ const absFile = path.resolve(file);
75
+
76
+ // The CONTAINMENT decision resolves symlinks on both the metrics dir and the
77
+ // target's PARENT (the file itself may not exist yet), so a symlinked parent
78
+ // cannot smuggle a path that textually sits under metrics but physically
79
+ // escapes it (`metrics/link -> /etc`, then `--file metrics/link/passwd`).
80
+ let realMetricsDir = metricsDir;
81
+ try {
82
+ realMetricsDir = fs.realpathSync(metricsDir);
83
+ } catch {
84
+ /* not created yet — fall back to the normalized path */
85
+ }
86
+ let realParent = path.dirname(absFile);
87
+ try {
88
+ realParent = fs.realpathSync(path.dirname(absFile));
89
+ } catch {
90
+ /* parent may not exist yet — fall back to the normalized parent */
91
+ }
92
+ const realFile = path.join(realParent, path.basename(absFile));
93
+
94
+ const prefix = realMetricsDir.endsWith(path.sep) ? realMetricsDir : realMetricsDir + path.sep;
95
+ if (!realFile.startsWith(prefix)) {
96
+ return { ok: false, metricsDir: realMetricsDir, resolved: realFile };
97
+ }
98
+ return { ok: true, file: absFile };
99
+ }
100
+
101
+ /**
102
+ * Human-readable summary — mirrors `scripts/backfill-abandoned-sessions.mjs`
103
+ * `renderHuman()` so the two migration CLIs read the same way.
104
+ *
105
+ * @param {object} s summary from `repairLedger`
106
+ * @returns {string}
107
+ */
108
+ export function renderHuman(s) {
109
+ const lines = [];
110
+ lines.push(`Repair invalid session records — ${s.mode}`);
111
+ lines.push(` file: ${s.file}`);
112
+ lines.push(` lines: ${s.total}`);
113
+ lines.push(` invalid before: ${s.invalid_before}`);
114
+ lines.push(` ${s.mode === 'apply' ? 'repaired: ' : 'would repair: '} ${s.repaired}`);
115
+ lines.push(` invalid after: ${s.invalid_after}${s.mode === 'apply' ? '' : ' (projected)'}`);
116
+ if (s.unparseable > 0) {
117
+ lines.push(` unparseable: ${s.unparseable} (passed through untouched)`);
118
+ }
119
+ if (s.duplicate_ids_observed.length > 0) {
120
+ const ids = s.duplicate_ids_observed.map((d) => `${d.session_id} x${d.count}`).join(', ');
121
+ lines.push(` duplicate ids: ${s.duplicate_ids_observed.length} (${ids}) — preserved, never deduped`);
122
+ }
123
+ const classes = Object.entries(s.defects_by_class);
124
+ if (classes.length > 0) {
125
+ lines.push(' defects by class:');
126
+ for (const [cls, n] of classes.sort((a, b) => b[1] - a[1])) lines.push(` ${cls}: ${n}`);
127
+ }
128
+ if (s.backup_path) lines.push(` backup: ${s.backup_path}`);
129
+ if (s.errors.length > 0) {
130
+ lines.push(` errors: ${s.errors.length} (original line kept)`);
131
+ for (const e of s.errors) lines.push(` line ${e.line} ${e.session_id ?? '<no session_id>'}: ${e.error}`);
132
+ }
133
+ if (s.post_verify && s.post_verify.integrity !== 'clean' && s.post_verify.integrity !== 'skipped-not-canonical-path') {
134
+ lines.push(` integrity: ${s.post_verify.integrity.message ?? 'FAILED'}`);
135
+ }
136
+ if (s.ok === false) {
137
+ lines.push(' POST-VERIFICATION FAILED — backup restored, ledger unchanged.');
138
+ }
139
+ return lines.join('\n') + '\n';
140
+ }
141
+
142
+ async function main() {
143
+ let parsed;
144
+ try {
145
+ parsed = parseArgs({
146
+ args: process.argv.slice(2),
147
+ options: {
148
+ apply: { type: 'boolean', default: false },
149
+ 'dry-run': { type: 'boolean', default: false },
150
+ json: { type: 'boolean', default: false },
151
+ file: { type: 'string' },
152
+ 'repo-root': { type: 'string' },
153
+ help: { type: 'boolean', short: 'h', default: false },
154
+ },
155
+ allowPositionals: false,
156
+ });
157
+ } catch (err) {
158
+ process.stderr.write(`repair-invalid-sessions: ${err.message}\n${USAGE}`);
159
+ process.exit(1);
160
+ }
161
+
162
+ const { values } = parsed;
163
+
164
+ if (values.help) {
165
+ process.stdout.write(USAGE);
166
+ process.exit(0);
167
+ }
168
+
169
+ // --apply is an explicit opt-in; absent it (or with --dry-run) we never write.
170
+ const apply = values.apply === true && values['dry-run'] !== true;
171
+ const repoRoot = values['repo-root'] || SO_PROJECT_DIR;
172
+ const requestedFile = values.file || path.join(repoRoot, CANONICAL_LEDGER_REL);
173
+
174
+ // MED-3: bound the target to <repoRoot>/.orchestrator/metrics/ BEFORE any I/O.
175
+ const target = resolveLedgerTarget(requestedFile, repoRoot);
176
+ if (!target.ok) {
177
+ process.stderr.write(
178
+ `repair-invalid-sessions: --file must resolve inside ${target.metricsDir}${path.sep} ` +
179
+ `(got ${target.resolved})\n${USAGE}`
180
+ );
181
+ process.exit(1);
182
+ }
183
+ const file = target.file;
184
+
185
+ let summary;
186
+ try {
187
+ // The backup is mandatory (MED-3): this is a write path to a protected
188
+ // ledger and the `.bak` is its only forensic trail — there is no opt-out.
189
+ summary = repairLedger({ file, repoRoot, apply, backup: true });
190
+ } catch (err) {
191
+ process.stderr.write(`repair-invalid-sessions: ${err?.message ?? String(err)}\n`);
192
+ process.exit(2);
193
+ }
194
+
195
+ if (values.json) {
196
+ process.stdout.write(JSON.stringify(summary) + '\n');
197
+ } else {
198
+ process.stdout.write(renderHuman(summary));
199
+ }
200
+ process.exit(summary.ok === false ? 3 : 0);
201
+ }
202
+
203
+ const isDirectRun = process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1];
204
+ if (isDirectRun) {
205
+ main().catch((err) => {
206
+ process.stderr.write(`repair-invalid-sessions: unexpected error: ${err?.stack ?? err}\n`);
207
+ process.exit(2);
208
+ });
209
+ }