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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor/rules/030-wave-execution.mdc +10 -8
- package/CHANGELOG.md +494 -0
- package/README.md +16 -11
- package/agents/analyst.md +1 -1
- package/agents/architect-reviewer.md +1 -1
- package/agents/code-implementer.md +4 -2
- package/agents/db-specialist.md +1 -1
- package/agents/dialectic-deriver.md +1 -1
- package/agents/docs-writer.md +1 -1
- package/agents/memory-proposal-collector.md +1 -1
- package/agents/qa-strategist.md +1 -1
- package/agents/security-reviewer.md +1 -1
- package/agents/session-reviewer.md +42 -1
- package/agents/skill-applied-judge.md +1 -1
- package/agents/test-writer.md +1 -1
- package/agents/ui-developer.md +1 -1
- package/agents/ux-evaluator.md +1 -1
- package/commands/release.md +60 -0
- package/commands/session.md +6 -2
- package/docs/USER-GUIDE.md +1 -1
- package/docs/instruction-delivery.md +350 -0
- package/docs/migration-v3.md +9 -6
- package/docs/persona-panel.md +3 -1
- package/docs/scope-collision-guard.md +167 -0
- package/docs/session-config-reference.md +1 -41
- package/docs/session-config-template.md +0 -23
- package/hooks/_lib/guard-source-loader.mjs +304 -91
- package/hooks/enforce-commands.mjs +216 -17
- package/hooks/enforce-scope.mjs +236 -12
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks.json +11 -1
- package/hooks/on-session-end.mjs +52 -5
- package/hooks/on-session-start.mjs +7 -4
- package/hooks/on-stop.mjs +127 -12
- package/hooks/post-bash-write-verify.mjs +8 -32
- package/hooks/pre-bash-destructive-guard.mjs +146 -59
- package/hooks/pre-bash-sessions-ledger-guard.mjs +493 -66
- package/hooks/pre-task-scope-disjoint.mjs +1042 -0
- package/package.json +2 -2
- package/pi/prompts/release.md +12 -0
- package/scripts/autopilot.mjs +3 -1
- package/scripts/backfill-learnings-from-vault.mjs +967 -0
- package/scripts/emit-session.mjs +45 -40
- package/scripts/export-hw-learnings.mjs +61 -2
- package/scripts/lib/autopilot/worktree-pipeline.mjs +5 -5
- package/scripts/lib/backlog-scan.mjs +106 -15
- package/scripts/lib/build-live-signals.mjs +7 -3
- package/scripts/lib/ci-status-banner.mjs +207 -23
- package/scripts/lib/command-blocker.mjs +322 -62
- package/scripts/lib/git-config-drift.mjs +471 -0
- package/scripts/lib/hardening.mjs +9 -9
- package/scripts/lib/harness-audit/categories/category6.mjs +65 -12
- package/scripts/lib/io.mjs +193 -7
- package/scripts/lib/learnings/affinity.mjs +434 -0
- package/scripts/lib/learnings/candidates.mjs +736 -0
- package/scripts/lib/learnings/expiry-sweep.mjs +408 -53
- package/scripts/lib/learnings/judgment.mjs +782 -0
- package/scripts/lib/learnings/kebab.mjs +128 -0
- package/scripts/lib/learnings/select.mjs +704 -0
- package/scripts/lib/memory-cleanup-stamp.mjs +132 -8
- package/scripts/lib/mirror-issues-banner.mjs +266 -0
- package/scripts/lib/named-vault-resolver.mjs +105 -16
- package/scripts/lib/peer-cards/schema.mjs +6 -2
- package/scripts/lib/reconcile/emitter.mjs +107 -22
- package/scripts/lib/reconcile/engine.mjs +9 -15
- package/scripts/lib/reconcile/renderer.mjs +141 -25
- package/scripts/lib/reconcile/sanitize.mjs +518 -0
- package/scripts/lib/reconcile/writer.mjs +134 -1
- package/scripts/lib/redact-spans.mjs +89 -0
- package/scripts/lib/scope-baseline.mjs +77 -17
- package/scripts/lib/scope-gate.mjs +852 -72
- package/scripts/lib/secret-masker.mjs +262 -0
- package/scripts/lib/session-close-backfill.mjs +2 -2
- package/scripts/lib/session-lock.mjs +34 -10
- package/scripts/lib/session-record-repair.mjs +551 -0
- package/scripts/lib/session-registry.mjs +9 -1
- package/scripts/lib/session-schema/serializer.mjs +54 -0
- package/scripts/lib/session-schema.mjs +1 -0
- package/scripts/lib/session-token-rollup.mjs +68 -6
- package/scripts/lib/soul-resolve.mjs +12 -0
- package/scripts/lib/state-md/mission-status.mjs +21 -12
- package/scripts/lib/tmux-layout/telemetry.mjs +43 -10
- package/scripts/lib/tmux-layout/vcs-detector.mjs +108 -4
- package/scripts/lib/validate/check-agents.mjs +77 -5
- package/scripts/lib/validate/check-banner-parity.mjs +376 -0
- package/scripts/lib/validate/check-commands.mjs +2 -20
- package/scripts/lib/validate/check-doc-cli-commands.mjs +514 -0
- package/scripts/lib/validate/check-guard-requires-parity.mjs +1148 -0
- package/scripts/lib/validate/check-hooks-symmetry.mjs +18 -0
- package/scripts/lib/validate/check-learning-provenance.mjs +511 -0
- package/scripts/lib/validate/check-owner-leakage.mjs +188 -20
- package/scripts/lib/validate/check-rules.mjs +31 -5
- package/scripts/lib/validate/check-skills.mjs +191 -0
- package/scripts/lib/validate/check-test-git-config-target.mjs +665 -0
- package/scripts/lib/validate/check-unicode-safety.mjs +22 -2
- package/scripts/lib/validate/check-untracked-test-deps.mjs +925 -0
- package/scripts/lib/validate/check-unwired-features.mjs +757 -0
- package/scripts/lib/validate/check-vcs-repo-flag.mjs +965 -0
- package/scripts/lib/validate/frontmatter-block.mjs +61 -0
- package/scripts/lib/validate/tier-inference.mjs +46 -8
- package/scripts/lib/vault-mirror/namespace.mjs +146 -1
- package/scripts/lib/vault-mirror/process.mjs +264 -31
- package/scripts/lib/vault-mirror/render-sessions.mjs +115 -4
- package/scripts/lib/vault-status/board-writer.mjs +300 -56
- package/scripts/lib/vault-status/narrative-mirror.mjs +119 -5
- package/scripts/lib/vcs-repo-spec.mjs +500 -19
- package/scripts/print-applicable-rules.mjs +170 -7
- package/scripts/print-learnings-index.mjs +501 -0
- package/scripts/release.mjs +616 -61
- package/scripts/repair-invalid-sessions.mjs +209 -0
- package/scripts/site-numbers.mjs +1049 -0
- package/scripts/sweep-expired-learnings.mjs +192 -32
- package/scripts/validate-plugin.mjs +82 -0
- package/scripts/validate-wave-scope.mjs +281 -12
- package/scripts/vault-mirror.mjs +26 -1
- package/skills/_shared/monitor-patterns.md +24 -4
- package/skills/_shared/state-ownership.md +17 -0
- package/skills/brainstorm/soul.md +47 -1
- package/skills/claude-md-drift-check/SKILL.md +9 -1
- package/skills/debug/SKILL.md +4 -1
- package/skills/discovery/issue-templates.md +4 -4
- package/skills/discovery/probes-code.md +2 -2
- package/skills/discovery/probes-feature.md +6 -6
- package/skills/discovery/probes-infra.md +2 -2
- package/skills/discovery/probes-session.md +5 -5
- package/skills/dispatcher/SKILL.md +10 -1
- package/skills/evolve/SKILL.md +116 -18
- package/skills/frontmatter-guard/SKILL.md +9 -1
- package/skills/gitlab-ops/SKILL.md +54 -39
- package/skills/gitlab-portfolio/SKILL.md +10 -1
- package/skills/grill/soul.md +44 -1
- package/skills/memory-cleanup/SKILL.md +18 -5
- package/skills/npm-publish/SKILL.md +22 -50
- package/skills/persona-panel/SKILL.md +3 -1
- package/skills/plan/mode-new.md +23 -5
- package/skills/plan/soul.md +46 -3
- package/skills/repo-audit/SKILL.md +10 -1
- package/skills/session-end/SKILL.md +45 -26
- package/skills/session-end/metrics-collection.md +1 -1
- package/skills/session-end/phase-3-6-tail.md +30 -1
- package/skills/session-end/plan-verification.md +1 -5
- package/skills/session-end/session-metrics-write.md +6 -10
- package/skills/session-plan/SKILL.md +2 -2
- package/skills/session-plan/wave-template.md +1 -1
- package/skills/session-start/SKILL.md +15 -1
- package/skills/session-start/soul.md +41 -1
- package/skills/spinout/SKILL.md +5 -1
- package/skills/sunset-review/SKILL.md +11 -1
- package/skills/tmux-layout/SKILL.md +7 -2
- package/skills/vault-mirror/SKILL.md +10 -1
- package/skills/vault-sync/SKILL.md +10 -1
- package/skills/vault-sync/validator.mjs +55 -6
- package/skills/wave-executor/SKILL.md +1 -5
- package/skills/wave-executor/wave-loop.md +77 -82
- 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
|
+
}
|