session-orchestrator 5.6.0 → 5.8.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 +3 -0
- package/.agents/skills/architecture/agents/openai.yaml +5 -0
- package/.agents/skills/claude-md-drift-check/SKILL.md +1 -0
- package/.agents/skills/eval/SKILL.md +1 -1
- package/.agents/skills/frontmatter-guard/SKILL.md +1 -0
- package/.agents/skills/gitlab-portfolio/SKILL.md +1 -0
- package/.agents/skills/hook-development/SKILL.md +1 -0
- package/.agents/skills/mcp-builder/SKILL.md +1 -0
- package/.agents/skills/tmux-layout/SKILL.md +3 -0
- package/.agents/skills/tmux-layout/agents/openai.yaml +5 -0
- package/.agents/skills/vault-mirror/SKILL.md +1 -0
- package/.agents/skills/vault-sync/SKILL.md +1 -0
- package/.agents/skills/write-executable-plan/SKILL.md +3 -0
- package/.agents/skills/write-executable-plan/agents/openai.yaml +5 -0
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.codex-plugin/skills/architecture/SKILL.md +3 -0
- package/.codex-plugin/skills/architecture/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/claude-md-drift-check/SKILL.md +1 -0
- package/.codex-plugin/skills/eval/SKILL.md +1 -1
- package/.codex-plugin/skills/frontmatter-guard/SKILL.md +1 -0
- package/.codex-plugin/skills/gitlab-portfolio/SKILL.md +1 -0
- package/.codex-plugin/skills/hook-development/SKILL.md +1 -0
- package/.codex-plugin/skills/mcp-builder/SKILL.md +1 -0
- package/.codex-plugin/skills/tmux-layout/SKILL.md +3 -0
- package/.codex-plugin/skills/tmux-layout/agents/openai.yaml +5 -0
- package/.codex-plugin/skills/vault-mirror/SKILL.md +1 -0
- package/.codex-plugin/skills/vault-sync/SKILL.md +1 -0
- package/.codex-plugin/skills/write-executable-plan/SKILL.md +3 -0
- package/.codex-plugin/skills/write-executable-plan/agents/openai.yaml +5 -0
- package/.cursor/commands/architecture.md +13 -0
- package/.cursor/commands/eval.md +1 -1
- package/.cursor/commands/tmux-layout.md +13 -0
- package/.cursor/commands/write-executable-plan.md +13 -0
- package/.cursor/skills/architecture/SKILL.md +0 -1
- package/.cursor/skills/eval/SKILL.md +1 -1
- package/.cursor/skills/tmux-layout/SKILL.md +0 -1
- package/.cursor/skills/write-executable-plan/SKILL.md +0 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/CHANGELOG.md +75 -0
- package/README.md +10 -10
- package/agents/eval-judge.md +5 -5
- package/docs/USER-GUIDE.md +1 -1
- package/docs/baseline.md +4 -3
- package/docs/codex-setup.md +6 -4
- package/docs/components.md +5 -4
- package/docs/events-schema.md +4 -3
- package/docs/github-mirror-protection.md +3 -2
- package/docs/install.md +39 -3
- package/docs/pi-setup.md +1 -1
- package/docs/rule-authoring.md +12 -2
- package/docs/session-config-reference.md +8 -6
- package/docs/session-config-template.md +4 -3
- package/evals/README.md +60 -0
- package/evals/instruction-ablation/README.md +147 -0
- package/evals/instruction-ablation/cases/psa-003-foreign-work.json +34 -0
- package/evals/instruction-ablation/cases/sec-007-sql-parameterisation.json +32 -0
- package/evals/instruction-ablation/cases/vbc-001-verify-before-claiming.json +41 -0
- package/evals/instruction-ablation/run.mjs +336 -0
- package/evals/psa-003-foreign-work/case.yaml +16 -0
- package/evals/psa-003-foreign-work/fixture.sh +12 -0
- package/evals/psa-003-foreign-work/graders/foreign-file-intact.md +7 -0
- package/evals/psa-003-foreign-work/graders/names-foreign-work.md +6 -0
- package/evals/psa-003-foreign-work/graders/no-destructive-git.md +8 -0
- package/evals/psa-003-foreign-work/graders/ran-git.md +7 -0
- package/evals/psa-003-foreign-work/prompt.md +6 -0
- package/hooks/_lib/hook-import-set.json +10 -2
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks.json +1 -1
- package/hooks/post-tool-batch-wave-signal.mjs +1 -0
- package/hooks/post-tooluse-frontend-slop.mjs +11 -1
- package/hooks/pre-task-scope-disjoint.mjs +8 -6
- package/hooks/wave-scope-commit-guard.mjs +41 -10
- package/monitors/monitors.json +1 -1
- package/package.json +3 -1
- package/pi/prompts/architecture.md +11 -0
- package/pi/prompts/eval.md +1 -1
- package/pi/prompts/tmux-layout.md +11 -0
- package/pi/prompts/write-executable-plan.md +11 -0
- package/scripts/backfill-abandoned-sessions.mjs +6 -8
- package/scripts/backfill-learnings-from-vault.mjs +17 -6
- package/scripts/backfill-sessions.mjs +9 -7
- package/scripts/codex-install.mjs +108 -23
- package/scripts/emit-session.mjs +15 -2
- package/scripts/fleet-auflagen.mjs +54 -0
- package/scripts/generate-agents-skills.mjs +9 -16
- package/scripts/generate-codex-skills.mjs +8 -6
- package/scripts/generate-cursor-adapter.mjs +9 -74
- package/scripts/generate-pi-prompts.mjs +8 -74
- package/scripts/github-protection-audit.mjs +8 -0
- package/scripts/lib/auto-dialectic.mjs +10 -9
- package/scripts/lib/auto-dream.mjs +8 -10
- package/scripts/lib/autopilot/telemetry.mjs +12 -6
- package/scripts/lib/bootstrap-lock-refresh.mjs +8 -12
- package/scripts/lib/config/drift-check.mjs +1 -1
- package/scripts/lib/config/frontend-slop-hook.mjs +6 -0
- package/scripts/lib/config.mjs +5 -1
- package/scripts/lib/convergence-monitor.mjs +5 -3
- package/scripts/lib/dispatcher/rank.mjs +5 -1
- package/scripts/lib/ecosystem-health.mjs +5 -3
- package/scripts/lib/eval/engine.mjs +61 -27
- package/scripts/lib/eval/judge.mjs +14 -8
- package/scripts/lib/eval/session-resolve.mjs +7 -2
- package/scripts/lib/events-retention-banner.mjs +5 -2
- package/scripts/lib/events.mjs +81 -54
- package/scripts/lib/fleet-protocol.mjs +98 -1
- package/scripts/lib/instruction-budget-guard.mjs +25 -18
- package/scripts/lib/io.mjs +181 -39
- package/scripts/lib/learnings/candidates.mjs +14 -44
- package/scripts/lib/learnings/io.mjs +7 -2
- package/scripts/lib/learnings/judgment.mjs +5 -8
- package/scripts/lib/learnings/select.mjs +3 -3
- package/scripts/lib/maintenance-due-banner.mjs +2 -2
- package/scripts/lib/memory-proposals/sink.mjs +5 -6
- package/scripts/lib/memory-proposals/store.mjs +12 -7
- package/scripts/lib/mirror-issues-banner.mjs +11 -1
- package/scripts/lib/native-command.mjs +48 -0
- package/scripts/lib/orphan-reaper/scan.mjs +29 -3
- package/scripts/lib/peer-cards/writer.mjs +6 -18
- package/scripts/lib/platform.mjs +113 -4
- package/scripts/lib/plugin-package-stage.mjs +169 -0
- package/scripts/lib/plugin-surface.mjs +121 -0
- package/scripts/lib/quality-gate.mjs +30 -25
- package/scripts/lib/reconcile/idempotency.mjs +9 -10
- package/scripts/lib/reconcile/rule-expiry-sweep.mjs +97 -8
- package/scripts/lib/reconcile/writer.mjs +11 -17
- package/scripts/lib/rule-loader.mjs +32 -6
- package/scripts/lib/scope-echo.mjs +12 -2
- package/scripts/lib/scope-gate.mjs +2 -1
- package/scripts/lib/session-close-backfill.mjs +21 -4
- package/scripts/lib/session-id.mjs +2 -1
- package/scripts/lib/session-record-repair.mjs +9 -13
- package/scripts/lib/session-schema/constants.mjs +20 -5
- package/scripts/lib/session-start-probes.mjs +2 -2
- package/scripts/lib/sessions-staleness-banner.mjs +2 -3
- package/scripts/lib/skill-evolution/engine.mjs +6 -9
- package/scripts/lib/skill-evolution/idempotency.mjs +8 -10
- package/scripts/lib/slopcheck.mjs +9 -7
- package/scripts/lib/sunset/walker.mjs +3 -3
- package/scripts/lib/telemetry/sync.mjs +3 -1
- package/scripts/lib/telemetry-flush-health-banner.mjs +2 -2
- package/scripts/lib/test-sibling-coverage.mjs +167 -0
- package/scripts/lib/tmux-layout/telemetry-stats.mjs +5 -7
- package/scripts/lib/tmux-layout/telemetry.mjs +8 -10
- package/scripts/lib/user-invocable-skills.mjs +37 -10
- package/scripts/lib/ux-grill/run-record.mjs +5 -3
- package/scripts/lib/validate/check-banner-parity.mjs +1 -0
- package/scripts/lib/validate/check-skills.mjs +12 -2
- package/scripts/lib/validate/check-test-fixture-shapes.mjs +3 -3
- package/scripts/lib/validate/check-test-value-bans.mjs +12 -2
- package/scripts/lib/validate/check-unicode-safety.mjs +1 -1
- package/scripts/lib/validate/check-unwired-features.mjs +1 -1
- package/scripts/lib/vault-archive.mjs +2 -4
- package/scripts/lib/vault-mirror/process.mjs +585 -72
- package/scripts/lib/vault-mirror/prune.mjs +413 -0
- package/scripts/lib/vault-mirror/render-sessions.mjs +13 -5
- package/scripts/lib/vault-mirror/retention.mjs +388 -0
- package/scripts/lib/vault-mirror/utils.mjs +16 -1
- package/scripts/lib/vault-status/board-writer.mjs +6 -6
- package/scripts/lib/vault-sync-baseline.mjs +9 -8
- package/scripts/lib/wave-transcript-tail.mjs +19 -6
- package/scripts/lib/worktree/meta.mjs +7 -3
- package/scripts/mcp-server.mjs +158 -0
- package/scripts/migrate-sessions-jsonl.mjs +7 -5
- package/scripts/migrate-vault-paths.mjs +7 -4
- package/scripts/print-applicable-rules.mjs +1 -25
- package/scripts/print-learnings-index.mjs +2 -1
- package/scripts/release.mjs +4 -0
- package/scripts/relocate-vault-corpus.mjs +5 -3
- package/scripts/run-migrate-v2-cross-repo.mjs +9 -7
- package/scripts/self-update.mjs +179 -39
- package/scripts/site-numbers.mjs +4 -3
- package/scripts/sweep-expired-learnings.mjs +6 -4
- package/scripts/sync-vault-schema.mjs +3 -7
- package/scripts/vault-mirror-prune.mjs +145 -0
- package/scripts/vault-mirror.mjs +77 -12
- package/scripts/windows-setup.mjs +45 -0
- package/skills/_shared/fleet-protocol.md +8 -0
- package/skills/architecture/SKILL.md +1 -0
- package/skills/bootstrap/_shared-template.md +5 -2
- package/skills/claude-md-drift-check/SKILL.md +4 -1
- package/skills/claude-md-drift-check/checker.mjs +89 -26
- package/skills/discovery/probes/ssot-code-diff.mjs +3 -3
- package/skills/eval/SKILL.md +12 -13
- package/skills/eval/rubric-v3.md +521 -0
- package/skills/frontmatter-guard/SKILL.md +1 -0
- package/skills/gitlab-portfolio/SKILL.md +1 -0
- package/skills/hook-development/SKILL.md +1 -0
- package/skills/mcp-builder/SKILL.md +1 -0
- package/skills/repo-audit/SKILL.md +2 -2
- package/skills/session-end/SKILL.md +32 -1
- package/skills/session-end/vault-operations.md +12 -2
- package/skills/session-start/SKILL.md +2 -2
- package/skills/session-start/phase-7-1-premise-check.md +2 -1
- package/skills/session-start/references/operations-contract.md +4 -1
- package/skills/tmux-layout/SKILL.md +1 -0
- package/skills/vault-mirror/SKILL.md +28 -2
- package/skills/vault-sync/SKILL.md +6 -6
- package/skills/vault-sync/validator.sh +29 -10
- package/skills/wave-executor/references/wave-loop-scope-manifest.md +5 -3
- package/skills/write-executable-plan/SKILL.md +1 -0
- package/hooks/agent-teams-h3-test.sh +0 -362
- package/scripts/spikes/h3-agent-teams/preflight.sh +0 -53
- package/scripts/spikes/h3-agent-teams/run-h3.sh +0 -112
- package/scripts/spikes/h3-agent-teams/setup.sh +0 -137
- package/scripts/spikes/h3-agent-teams/toggle.sh +0 -38
package/scripts/site-numbers.mjs
CHANGED
|
@@ -178,8 +178,9 @@ export function countSkills(root) {
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
/**
|
|
181
|
-
* The product's "slash commands" number: `commands/*.md` ∪ skills
|
|
182
|
-
*
|
|
181
|
+
* The product's "slash commands" number: `commands/*.md` ∪ skills whose
|
|
182
|
+
* `user-invocable` is `true` or ABSENT (Claude Code lists a keyless skill in the
|
|
183
|
+
* `/` picker — #1515), deduplicated by name.
|
|
183
184
|
*
|
|
184
185
|
* NOT `ls commands/*.md | wc -l` any more, and the change is not cosmetic. After
|
|
185
186
|
* the 2026-09-16 fold (#1370) `commands/` holds two files — `session` and
|
|
@@ -435,7 +436,7 @@ export const METRIC_DEFS = Object.freeze([
|
|
|
435
436
|
id: 'commands',
|
|
436
437
|
provenance: false,
|
|
437
438
|
source:
|
|
438
|
-
'ls commands/*.md | wc -l + grep -
|
|
439
|
+
'ls commands/*.md | wc -l + grep -L "^user-invocable: *false" skills/*/SKILL.md | wc -l (union by name; a skill without the key counts, as in Claude Code — see countCommands)',
|
|
439
440
|
compute: (root) => fmtCount(countCommands(root)),
|
|
440
441
|
},
|
|
441
442
|
{
|
|
@@ -123,7 +123,7 @@
|
|
|
123
123
|
*/
|
|
124
124
|
|
|
125
125
|
import { existsSync, realpathSync, statSync } from 'node:fs';
|
|
126
|
-
import { mkdir, readFile
|
|
126
|
+
import { mkdir, readFile } from 'node:fs/promises';
|
|
127
127
|
import path from 'node:path';
|
|
128
128
|
import { isDeepStrictEqual } from 'node:util';
|
|
129
129
|
import {
|
|
@@ -135,6 +135,7 @@ import {
|
|
|
135
135
|
warnUnparseableLines,
|
|
136
136
|
} from './lib/learnings/expiry-sweep.mjs';
|
|
137
137
|
import { readLearningsSnapshot, parseLearningsText } from './lib/learnings/io.mjs';
|
|
138
|
+
import { atomicWriteText, envelopeToError } from './lib/io.mjs';
|
|
138
139
|
import { validateLearning } from './lib/learnings/schema.mjs';
|
|
139
140
|
import { validatePathInsideProject } from './lib/path-utils.mjs';
|
|
140
141
|
import { emitEvolveCompleted } from './lib/learnings/evolve-telemetry.mjs';
|
|
@@ -668,9 +669,10 @@ async function runSnapshot(args) {
|
|
|
668
669
|
...snap.entries.map((e) => JSON.stringify(e)),
|
|
669
670
|
];
|
|
670
671
|
await mkdir(path.dirname(args.snapshot), { recursive: true });
|
|
671
|
-
const
|
|
672
|
-
|
|
673
|
-
|
|
672
|
+
const res = await atomicWriteText(args.snapshot, lines.join('\n') + '\n', {
|
|
673
|
+
tmpPrefix: `.${path.basename(args.snapshot)}.tmp`,
|
|
674
|
+
});
|
|
675
|
+
if (!res.ok) throw envelopeToError(res);
|
|
674
676
|
} catch (err) {
|
|
675
677
|
process.stderr.write(`sweep-expired-learnings: snapshot failed: ${err.message}\n`);
|
|
676
678
|
process.exit(2);
|
|
@@ -26,13 +26,9 @@
|
|
|
26
26
|
* 2 — missing file
|
|
27
27
|
* 3 — malformed sentinels (only one of begin/end present)
|
|
28
28
|
*
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
32
|
-
* projects-baseline source. Until #503 lands upstream, `--check` will
|
|
33
|
-
* report drift; this is expected and is tracked as upstream-sync-debt
|
|
34
|
-
* in the #503 PRD (W5 F2 docs agent owns the PRD update). Once
|
|
35
|
-
* upstream catches up, `--write` will become a no-op (idempotent).
|
|
29
|
+
* The #503 vendor-ahead state (`peer-card` in validator.mjs before the
|
|
30
|
+
* canonical source carried it) is resolved: projects-baseline has
|
|
31
|
+
* `peer-card`, and `--check` passed against baseline `9108018` (2026-10-04).
|
|
36
32
|
*/
|
|
37
33
|
|
|
38
34
|
import { readFileSync, writeFileSync, existsSync } from 'node:fs';
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* vault-mirror-prune.mjs — CLI for the mirror-zone retention prune (#1513).
|
|
4
|
+
*
|
|
5
|
+
* Applies the vault-mirror retention rules to the notes the mirror wrote
|
|
6
|
+
* BEFORE those rules existed: expired / superseded / duplicate learnings,
|
|
7
|
+
* metrics-only session notes (rolled up first), namespace-alias folders and
|
|
8
|
+
* flat legacy notes. All decisions live in `scripts/lib/vault-mirror/prune.mjs`
|
|
9
|
+
* (rules table in its header) and `retention.mjs`; this file is argv parsing,
|
|
10
|
+
* the manifest, output and exit codes.
|
|
11
|
+
*
|
|
12
|
+
* SAFETY: dry run is the DEFAULT and writes nothing — not even the manifest
|
|
13
|
+
* unless `--manifest` names a path. `--apply` moves files (archive → under
|
|
14
|
+
* `90-archive/mirror/`), never deletes a note's content, never touches a note
|
|
15
|
+
* without the mirror's `_generator` marker.
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* node scripts/vault-mirror-prune.mjs --vault-dir PATH [--apply]
|
|
19
|
+
* [--manifest PATH] [--now ISO] [--min-narrative-chars N]
|
|
20
|
+
* [--alias FROM=TO ...] [--json]
|
|
21
|
+
*
|
|
22
|
+
* Exit codes: 0 success · 1 usage error · 2 apply finished with per-path errors
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { writeFileSync } from 'node:fs';
|
|
26
|
+
import { resolve } from 'node:path';
|
|
27
|
+
|
|
28
|
+
import { expandTilde, redactHomeDir } from './lib/common.mjs';
|
|
29
|
+
import { isMainModule } from './lib/is-main-module.mjs';
|
|
30
|
+
import { MANIFEST_SCHEMA, applyPrune, planPrune } from './lib/vault-mirror/prune.mjs';
|
|
31
|
+
|
|
32
|
+
const HELP = `Usage: node scripts/vault-mirror-prune.mjs --vault-dir PATH [--apply] [--manifest PATH]
|
|
33
|
+
[--now ISO] [--min-narrative-chars N] [--alias FROM=TO ...] [--json]
|
|
34
|
+
|
|
35
|
+
Plans (default) or applies the vault-mirror retention rules to 40-learnings/
|
|
36
|
+
and 50-sessions/ of a vault. Only notes carrying the vault-mirror _generator
|
|
37
|
+
marker are considered; hand-written notes are never touched.
|
|
38
|
+
|
|
39
|
+
Options:
|
|
40
|
+
--vault-dir PATH Vault root (required; no host-local default on purpose)
|
|
41
|
+
--apply Write: rollups, then archive moves to 90-archive/mirror/, then renames
|
|
42
|
+
--manifest PATH Write the JSON manifest (path, reason, action, target) there
|
|
43
|
+
--now ISO Injected clock for the expiry check (default: now)
|
|
44
|
+
--min-narrative-chars N Session narrative gate (default 400, as vault-mirror.quality)
|
|
45
|
+
--alias FROM=TO Explicit namespace alias (repeatable); the hyphen rule applies without it
|
|
46
|
+
--json Print the summary as one JSON line
|
|
47
|
+
|
|
48
|
+
Exit codes: 0 success 1 usage error 2 apply finished with per-path errors
|
|
49
|
+
`;
|
|
50
|
+
|
|
51
|
+
function usageError(msg) {
|
|
52
|
+
process.stderr.write(`vault-mirror-prune: ${msg}\n`);
|
|
53
|
+
process.exit(1);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function parseArgs(argv) {
|
|
57
|
+
const args = { vaultDir: null, apply: false, manifest: null, now: null, minNarrativeChars: 400, aliases: {}, json: false };
|
|
58
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
59
|
+
const a = argv[i];
|
|
60
|
+
const value = () => {
|
|
61
|
+
const v = argv[i + 1];
|
|
62
|
+
if (v === undefined || v.startsWith('--')) usageError(`${a} needs a value`);
|
|
63
|
+
i += 1;
|
|
64
|
+
return v;
|
|
65
|
+
};
|
|
66
|
+
if (a === '--help' || a === '-h') {
|
|
67
|
+
process.stdout.write(HELP);
|
|
68
|
+
process.exit(0);
|
|
69
|
+
} else if (a === '--vault-dir') args.vaultDir = value();
|
|
70
|
+
else if (a === '--apply') args.apply = true;
|
|
71
|
+
else if (a === '--dry-run') args.apply = false;
|
|
72
|
+
else if (a === '--manifest') args.manifest = value();
|
|
73
|
+
else if (a === '--now') {
|
|
74
|
+
args.now = value();
|
|
75
|
+
if (!Number.isFinite(Date.parse(args.now))) usageError(`--now is not a date: ${args.now}`);
|
|
76
|
+
} else if (a === '--min-narrative-chars') {
|
|
77
|
+
const raw = value();
|
|
78
|
+
if (!/^\d+$/.test(raw)) usageError(`--min-narrative-chars must be a non-negative integer, got ${raw}`);
|
|
79
|
+
args.minNarrativeChars = Number(raw);
|
|
80
|
+
} else if (a === '--alias') {
|
|
81
|
+
const m = /^([a-z0-9][a-z0-9-]*)=([a-z0-9][a-z0-9-]*)$/.exec(value());
|
|
82
|
+
if (!m) usageError('--alias expects FROM=TO with lowercase kebab namespaces');
|
|
83
|
+
args.aliases[m[1]] = m[2];
|
|
84
|
+
} else if (a === '--json') args.json = true;
|
|
85
|
+
else usageError(`unknown argument ${a}`);
|
|
86
|
+
}
|
|
87
|
+
if (!args.vaultDir) usageError('--vault-dir is required');
|
|
88
|
+
return args;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function main(argv = process.argv.slice(2)) {
|
|
92
|
+
const args = parseArgs(argv);
|
|
93
|
+
const vaultDir = resolve(expandTilde(args.vaultDir));
|
|
94
|
+
const today = new Date(args.now ? Date.parse(args.now) : Date.now()).toISOString().slice(0, 10);
|
|
95
|
+
const plan = planPrune({ vaultDir, today, minNarrativeChars: args.minNarrativeChars, aliases: args.aliases });
|
|
96
|
+
|
|
97
|
+
const manifest = {
|
|
98
|
+
schema: MANIFEST_SCHEMA,
|
|
99
|
+
generated_at: new Date().toISOString(),
|
|
100
|
+
today,
|
|
101
|
+
dry_run: !args.apply,
|
|
102
|
+
min_narrative_chars: args.minNarrativeChars,
|
|
103
|
+
aliases: plan.aliases,
|
|
104
|
+
skipped_handwritten: plan.handwritten,
|
|
105
|
+
counts: plan.counts,
|
|
106
|
+
rollups: Object.fromEntries(Object.entries(plan.rollups).map(([k, rows]) => [k, rows.length])),
|
|
107
|
+
actions: plan.actions,
|
|
108
|
+
};
|
|
109
|
+
let applied = null;
|
|
110
|
+
if (args.apply) {
|
|
111
|
+
applied = applyPrune(plan, { vaultDir });
|
|
112
|
+
// The apply result belongs in the manifest: notes written beside an
|
|
113
|
+
// occupied archive target (`suffixed`) and per-path errors.
|
|
114
|
+
manifest.applied = applied;
|
|
115
|
+
}
|
|
116
|
+
if (args.manifest) writeFileSync(resolve(expandTilde(args.manifest)), JSON.stringify(manifest, null, 2) + '\n', 'utf8');
|
|
117
|
+
|
|
118
|
+
const summary = {
|
|
119
|
+
dry_run: !args.apply,
|
|
120
|
+
counts: plan.counts,
|
|
121
|
+
rollups: Object.keys(plan.rollups).length,
|
|
122
|
+
aliases: plan.aliases,
|
|
123
|
+
skipped_handwritten: plan.handwritten,
|
|
124
|
+
...(applied ? { applied: { ...applied, suffixed: applied.suffixed.length, errors: applied.errors.length } } : {}),
|
|
125
|
+
};
|
|
126
|
+
if (args.json) process.stdout.write(JSON.stringify(summary) + '\n');
|
|
127
|
+
else {
|
|
128
|
+
process.stdout.write(`vault-mirror-prune ${args.apply ? 'APPLY' : 'dry run'} (today ${today})\n`);
|
|
129
|
+
for (const [k, n] of Object.entries(plan.counts).sort()) process.stdout.write(` ${k.padEnd(36)} ${n}\n`);
|
|
130
|
+
process.stdout.write(` rollup notes ${Object.keys(plan.rollups).length}\n`);
|
|
131
|
+
for (const [from, to] of Object.entries(plan.aliases)) process.stdout.write(` alias ${from} -> ${to}\n`);
|
|
132
|
+
if (applied) {
|
|
133
|
+
process.stdout.write(` applied: archived ${applied.archived}, moved ${applied.moved}, rollups written ${applied.rollupsWritten}, suffixed ${applied.suffixed.length}, errors ${applied.errors.length}\n`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
if (applied && applied.errors.length > 0) {
|
|
137
|
+
for (const e of applied.errors.slice(0, 20)) process.stderr.write(`vault-mirror-prune: ${e.path}: ${redactHomeDir(e.error)}\n`);
|
|
138
|
+
return 2;
|
|
139
|
+
}
|
|
140
|
+
return 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (isMainModule(import.meta.url)) {
|
|
144
|
+
process.exit(main());
|
|
145
|
+
}
|
package/scripts/vault-mirror.mjs
CHANGED
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
* `--repo-root` — #1503)
|
|
21
21
|
*
|
|
22
22
|
* Output: one JSON line per action on stdout:
|
|
23
|
-
* {"action":"created|updated|skipped-noop|skipped-handwritten|skipped-collision-resolved|skipped-foreign-owner|skipped-invalid|skipped-quality-low|skipped-abandoned|skipped-duplicate-session","path":"...","kind":"...","id":"..."}
|
|
23
|
+
* {"action":"created|updated|skipped-noop|skipped-handwritten|skipped-collision-resolved|skipped-foreign-owner|skipped-invalid|skipped-quality-low|skipped-abandoned|skipped-duplicate-session|skipped-metrics-only|skipped-expired|skipped-duplicate-insight","path":"...","kind":"...","id":"..."}
|
|
24
24
|
*
|
|
25
25
|
* Idempotency rules:
|
|
26
26
|
* 1. File does not exist → create.
|
|
@@ -45,8 +45,10 @@
|
|
|
45
45
|
*
|
|
46
46
|
* Quality gate (PRD F1.2):
|
|
47
47
|
* Learnings with confidence < --quality-min-confidence emit `skipped-quality-low`.
|
|
48
|
-
* Sessions
|
|
49
|
-
*
|
|
48
|
+
* Sessions whose free text (`notes`/`narrative`/`summary`) is shorter than
|
|
49
|
+
* --quality-min-narrative-chars get no note: `skipped-metrics-only` with one row
|
|
50
|
+
* in the month rollup `50-sessions/<repo>/_rollup-YYYY-MM.md` (#1513; rules in
|
|
51
|
+
* scripts/lib/vault-mirror/retention.mjs). Quality gate runs BEFORE --force; --force does NOT
|
|
50
52
|
* bypass the filter. Quality-skipped entries emit `path: null` and an
|
|
51
53
|
* additional `reason` field describing the violated threshold.
|
|
52
54
|
*
|
|
@@ -65,14 +67,16 @@
|
|
|
65
67
|
* Part of session-orchestrator vault-mirror (Issue #14).
|
|
66
68
|
*/
|
|
67
69
|
|
|
68
|
-
import { existsSync, realpathSync } from 'node:fs';
|
|
69
|
-
import { basename, dirname, resolve } from 'node:path';
|
|
70
|
+
import { existsSync, readFileSync, realpathSync } from 'node:fs';
|
|
71
|
+
import { basename, dirname, join, resolve } from 'node:path';
|
|
70
72
|
import { createInterface } from 'node:readline';
|
|
71
73
|
import { createReadStream } from 'node:fs';
|
|
72
74
|
|
|
73
75
|
import {
|
|
74
76
|
processLearning,
|
|
75
77
|
processSession,
|
|
78
|
+
processArchivedLearning,
|
|
79
|
+
learningNoteSlugs,
|
|
76
80
|
getMaskerStats,
|
|
77
81
|
emitAction,
|
|
78
82
|
} from './lib/vault-mirror/process.mjs';
|
|
@@ -81,6 +85,7 @@ import { emitEvent } from './lib/events.mjs';
|
|
|
81
85
|
import { autoCommitVaultMirror } from './lib/vault-mirror/auto-commit.mjs';
|
|
82
86
|
import { parseColumnFlags, CliFlagError } from './lib/cli-flags.mjs';
|
|
83
87
|
import { resolveRepoNamespace } from './lib/vault-mirror/namespace.mjs';
|
|
88
|
+
import { SESSION_NARRATIVE_FIELDS, canonicalNamespace } from './lib/vault-mirror/retention.mjs';
|
|
84
89
|
import { sessionSourceRecord } from './lib/vault-mirror/render-sessions.mjs';
|
|
85
90
|
import { checkCanonicalVault, describeOriginForLog, findRepoRoot, normalizeRemote } from './lib/named-vault-resolver.mjs';
|
|
86
91
|
import { loadOwnerConfig } from './lib/owner-yaml.mjs';
|
|
@@ -249,8 +254,9 @@ if (flagValues.help === true) {
|
|
|
249
254
|
' project holding --source (<root>/.orchestrator/metrics/), else',
|
|
250
255
|
' the git repo containing it, else the cwd. Refused (exit 2,',
|
|
251
256
|
' "source-repo-mismatch") when --source lies in a different repo.',
|
|
252
|
-
' --quality-min-narrative-chars <int> Sessions: minimum
|
|
253
|
-
'
|
|
257
|
+
' --quality-min-narrative-chars <int> Sessions: minimum free text in notes/narrative/summary',
|
|
258
|
+
' (default 400, 0 = off). Below it: "skipped-metrics-only",',
|
|
259
|
+
' one row in 50-sessions/<repo>/_rollup-YYYY-MM.md.',
|
|
254
260
|
' --quality-min-confidence <float> Learnings: minimum confidence threshold (default 0.5).',
|
|
255
261
|
' Entries below the threshold emit "skipped-quality-low".',
|
|
256
262
|
'',
|
|
@@ -641,6 +647,9 @@ async function main() {
|
|
|
641
647
|
// superseded duplicates included — filled while buffering below, read by
|
|
642
648
|
// processSession's owner guard (a key from this ledger is never foreign).
|
|
643
649
|
sourceRecordKeys: new Set(),
|
|
650
|
+
// #1513: note slugs of every LIVE learning in --source; the archive pass
|
|
651
|
+
// below never marks a note one of them still owns.
|
|
652
|
+
liveLearningSlugs: new Set(),
|
|
644
653
|
qualityMinNarrativeChars,
|
|
645
654
|
qualityMinConfidence,
|
|
646
655
|
};
|
|
@@ -662,14 +671,15 @@ async function main() {
|
|
|
662
671
|
* always supplies a real line number.
|
|
663
672
|
* @returns {Promise<void>}
|
|
664
673
|
*/
|
|
665
|
-
async function dispatchEntry(entry, entryLineNum) {
|
|
674
|
+
async function dispatchEntry(entry, entryLineNum, processor = null) {
|
|
666
675
|
try {
|
|
667
676
|
// Both processors return the `action` string they emitted (every one of
|
|
668
677
|
// their exit paths is an `emitAction` call), so the tally needs no second
|
|
669
678
|
// census of the 18 call sites in process.mjs — a census that would go
|
|
670
679
|
// stale the first time a branch is added.
|
|
671
|
-
const action =
|
|
672
|
-
|
|
680
|
+
const action = processor
|
|
681
|
+
? await processor(entry, entryLineNum, ctx)
|
|
682
|
+
: kind === 'learning'
|
|
673
683
|
? await processLearning(entry, entryLineNum, ctx)
|
|
674
684
|
: await processSession(entry, entryLineNum, ctx);
|
|
675
685
|
tally(action);
|
|
@@ -776,9 +786,39 @@ async function main() {
|
|
|
776
786
|
continue;
|
|
777
787
|
}
|
|
778
788
|
|
|
789
|
+
for (const slug of learningNoteSlugs(entry)) ctx.liveLearningSlugs.add(slug);
|
|
779
790
|
await dispatchEntry(entry, lineNum);
|
|
780
791
|
}
|
|
781
792
|
|
|
793
|
+
// #1513 ARCHIVE PASS (`--kind learning` only): records /evolve or the expiry
|
|
794
|
+
// sweep moved out of `learnings.jsonl` live on in the sibling
|
|
795
|
+
// `learnings-archive.jsonl`. Their vault notes used to stay `status: draft`
|
|
796
|
+
// forever, because the mirror only ever saw the live store. Each archived
|
|
797
|
+
// record now marks its existing note archived (never creates one). Counted
|
|
798
|
+
// into `total` like any other line, so the run partition still holds. A
|
|
799
|
+
// malformed archive line is reported and skipped — the archive is secondary
|
|
800
|
+
// input and must not abort a mirror run that already wrote the live store.
|
|
801
|
+
if (kind === 'learning' && basename(resolve(source)) === 'learnings.jsonl') {
|
|
802
|
+
const archivePath = join(dirname(resolve(source)), 'learnings-archive.jsonl');
|
|
803
|
+
if (existsSync(archivePath)) {
|
|
804
|
+
const archiveLines = readFileSync(archivePath, 'utf8').split('\n');
|
|
805
|
+
for (let i = 0; i < archiveLines.length; i += 1) {
|
|
806
|
+
const trimmed = archiveLines[i].trim();
|
|
807
|
+
if (!trimmed) continue;
|
|
808
|
+
let archived;
|
|
809
|
+
try {
|
|
810
|
+
archived = JSON.parse(trimmed);
|
|
811
|
+
} catch {
|
|
812
|
+
process.stderr.write(`WARN vault-mirror: malformed JSON on learnings-archive.jsonl line ${i + 1} — skipped\n`);
|
|
813
|
+
continue;
|
|
814
|
+
}
|
|
815
|
+
if (archived === null || typeof archived !== 'object') continue;
|
|
816
|
+
runState.total++;
|
|
817
|
+
await dispatchEntry(archived, i + 1, processArchivedLearning);
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
|
|
782
822
|
if (kind === 'session') {
|
|
783
823
|
// Only an OBJECT entry carrying a non-empty `session_id` is eligible for
|
|
784
824
|
// the dedup collapse — the same predicate canonicalizeSessions itself uses
|
|
@@ -803,6 +843,30 @@ async function main() {
|
|
|
803
843
|
// never a guess (scripts/lib/sessions-canonical.mjs header, "RULE ORDER").
|
|
804
844
|
const survivors = new Set(canonicalizeSessions(identifiable));
|
|
805
845
|
|
|
846
|
+
// #1513 review H2: "newest wins" must not cost a session its narrative. A
|
|
847
|
+
// re-appended record often carries only the metrics; the free text sits on
|
|
848
|
+
// the line it supersedes (vault ledger: 1.172 chars on line 101, none on the
|
|
849
|
+
// winning line 108). The winner inherits every narrative field it LACKS
|
|
850
|
+
// from the losers, latest loser first — its own fields always win.
|
|
851
|
+
const inherited = new Map();
|
|
852
|
+
for (const e of sessionEntries) {
|
|
853
|
+
if (!isIdentifiable(e) || survivors.has(e)) continue;
|
|
854
|
+
const carry = inherited.get(e.session_id) ?? {};
|
|
855
|
+
for (const field of SESSION_NARRATIVE_FIELDS) {
|
|
856
|
+
if (e[field] !== undefined && e[field] !== null && e[field] !== '') carry[field] = e[field];
|
|
857
|
+
}
|
|
858
|
+
inherited.set(e.session_id, carry);
|
|
859
|
+
}
|
|
860
|
+
const withNarrative = (e) => {
|
|
861
|
+
const carry = isIdentifiable(e) ? inherited.get(e.session_id) : undefined;
|
|
862
|
+
if (!carry) return e;
|
|
863
|
+
const merged = { ...e };
|
|
864
|
+
for (const [field, value] of Object.entries(carry)) {
|
|
865
|
+
if (merged[field] === undefined || merged[field] === null || merged[field] === '') merged[field] = value;
|
|
866
|
+
}
|
|
867
|
+
return merged;
|
|
868
|
+
};
|
|
869
|
+
|
|
806
870
|
for (let i = 0; i < sessionEntries.length; i++) {
|
|
807
871
|
const entry = sessionEntries[i];
|
|
808
872
|
if (isIdentifiable(entry) && !survivors.has(entry)) {
|
|
@@ -831,7 +895,7 @@ async function main() {
|
|
|
831
895
|
);
|
|
832
896
|
continue;
|
|
833
897
|
}
|
|
834
|
-
await dispatchEntry(entry, sessionLineNums[i]);
|
|
898
|
+
await dispatchEntry(withNarrative(entry), sessionLineNums[i]);
|
|
835
899
|
}
|
|
836
900
|
}
|
|
837
901
|
|
|
@@ -863,7 +927,8 @@ async function main() {
|
|
|
863
927
|
autoCommitVaultMirror(
|
|
864
928
|
resolve(vaultDir),
|
|
865
929
|
sessionIdArg,
|
|
866
|
-
|
|
930
|
+
// #1513 review M2: the folder the processors actually wrote into.
|
|
931
|
+
canonicalNamespace(resolve(vaultDir), resolveRepoNamespace({ vaultName, repoRoot: ctx.repoRoot })),
|
|
867
932
|
);
|
|
868
933
|
}
|
|
869
934
|
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** Opt-in native MCP configuration for an installed Windows plugin. */
|
|
3
|
+
import { copyFileSync, existsSync, readFileSync, realpathSync, renameSync, writeFileSync } from 'node:fs';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { isMainModule } from './lib/is-main-module.mjs';
|
|
6
|
+
|
|
7
|
+
export function configureNativeMcp(pluginRoot, { node = process.execPath, platform = process.platform } = {}) {
|
|
8
|
+
if (platform !== 'win32') throw new Error('This setup is for native Windows only.');
|
|
9
|
+
const root = realpathSync(pluginRoot);
|
|
10
|
+
if (existsSync(path.join(root, '.git'))) throw new Error('Refusing to configure a Git checkout.');
|
|
11
|
+
const segments = root.split(path.sep);
|
|
12
|
+
const inCache = segments.some((segment, index) =>
|
|
13
|
+
segment === 'plugins' && segments[index + 1] === 'cache' && index + 2 < segments.length);
|
|
14
|
+
if (!inCache) throw new Error('Expected an installed plugin inside plugins/cache.');
|
|
15
|
+
const pkg = JSON.parse(readFileSync(path.join(root, 'package.json'), 'utf8'));
|
|
16
|
+
if (pkg.name !== 'session-orchestrator') throw new Error('Expected a session-orchestrator plugin directory.');
|
|
17
|
+
const server = path.join(root, 'scripts', 'mcp-server.mjs');
|
|
18
|
+
if (!existsSync(server)) throw new Error('Native MCP server is missing; install the updated package first.');
|
|
19
|
+
const file = path.join(root, '.mcp.json');
|
|
20
|
+
const raw = readFileSync(file, 'utf8');
|
|
21
|
+
const config = JSON.parse(raw);
|
|
22
|
+
const current = config.mcpServers?.['session-orchestrator'];
|
|
23
|
+
if (!current || typeof current !== 'object') throw new Error('Expected session-orchestrator MCP configuration.');
|
|
24
|
+
if (current.command === node && current.args?.length === 1 && current.args[0] === server) {
|
|
25
|
+
return { changed: false, file };
|
|
26
|
+
}
|
|
27
|
+
const backup = `${file}.before-windows-setup`;
|
|
28
|
+
if (existsSync(backup)) throw new Error(`Backup already exists: ${backup}; inspect it before reconfiguring.`);
|
|
29
|
+
config.mcpServers['session-orchestrator'] = { ...current, command: node, args: [server] };
|
|
30
|
+
copyFileSync(file, backup);
|
|
31
|
+
const temporary = `${file}.windows-setup-${process.pid}`;
|
|
32
|
+
writeFileSync(temporary, `${JSON.stringify(config, null, 2)}\n`, { flag: 'wx' });
|
|
33
|
+
renameSync(temporary, file);
|
|
34
|
+
return { changed: true, file, backup };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (isMainModule(import.meta.url)) {
|
|
38
|
+
try {
|
|
39
|
+
if (process.argv.length !== 3) throw new Error('Usage: node scripts/windows-setup.mjs <installed-plugin-directory>');
|
|
40
|
+
process.stdout.write(`${JSON.stringify(configureNativeMcp(process.argv[2]))}\n`);
|
|
41
|
+
} catch (error) {
|
|
42
|
+
process.stderr.write(`${error.message}\n`);
|
|
43
|
+
process.exitCode = 1;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
@@ -103,6 +103,14 @@ Bedingungen, die der Navigator einer Session schreibt (z. B. Agent-Caps, volle P
|
|
|
103
103
|
Offload-Regel, Merge- und Zeitregeln, Besitzkonflikte); Inhalt und Form gehören dem Navigator. Die
|
|
104
104
|
Session **liest** die Datei; sie wartet auf keine Nachricht. Keine Auflage ist eine Freigabe.
|
|
105
105
|
|
|
106
|
+
**Pflichtfeld `repo`** (#1520, navigator#190): absoluter Pfad auf Toplevel oder git-common-dir des
|
|
107
|
+
Ziel-Repos. Der Dateiname ist die semantische Session-ID, und die kollidiert über Repos hinweg.
|
|
108
|
+
Gelesen wird über `node scripts/fleet-auflagen.mjs <session_id>` (Code: `readAuflagen()`); die
|
|
109
|
+
Datei gilt nur, wenn `repo` und das eigene Repo auf dasselbe `git rev-parse --path-format=absolute
|
|
110
|
+
--git-common-dir` auflösen, ein Worktree passt also zu seinem Haupt-Checkout. Fehlt das Feld, passt
|
|
111
|
+
es nicht oder ist die Datei unlesbar: fail-closed, WARN auf stderr, die Datei zählt als nicht
|
|
112
|
+
vorhanden, die Standard-Auflagen gelten.
|
|
113
|
+
|
|
106
114
|
**Standard-Auflagen (konservative Stufe).** Sie gelten ab `fallback_due_at` (stdout der
|
|
107
115
|
Check-in-CLI), wenn bis dahin keine Auflagen-Datei liegt. Die Frist rechnet `fallbackDueAt()`:
|
|
108
116
|
`CHECKIN_FALLBACK_MIN` = 10 Minuten nach `checkin.zeit`, solange ein Navigator aktiv ist; ohne
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: architecture
|
|
3
3
|
description: Use when the user asks to improve architecture, find refactoring opportunities, surface deepening opportunities, consolidate tightly-coupled modules, or make a codebase more testable and AI-navigable. Surfaces shallow modules and hypothetical seams using a precise vocabulary (Module / Interface / Implementation / Depth / Seam / Adapter / Leverage / Locality from LANGUAGE.md).
|
|
4
|
+
user-invocable: true
|
|
4
5
|
model: inherit
|
|
5
6
|
derived-from: mattpocock/skills@90ea8ee
|
|
6
7
|
license: MIT
|
|
@@ -342,7 +342,7 @@ On Codex CLI / Cursor IDE, substitute `.codex/` or `.cursor/` for `.claude/` per
|
|
|
342
342
|
|
|
343
343
|
Applied by Fast Step 3 (so by every tier), by the Upgrade Flow and by the Refresh-Lock Flow in `SKILL.md` — the last two because a repo bootstrapped before this step existed never gets it otherwise: both flows write no `.gitignore` of their own.
|
|
344
344
|
|
|
345
|
-
`.orchestrator/` itself stays versioned: `bootstrap.lock`, `policy/`, `steering/`, `peers/` and the `.orchestrator/metrics/*.jsonl` ledgers are durable project data. What the plugin writes there at runtime is per-process state or a per-run artifact that a `git add -A` would otherwise commit — the session lock is rewritten on every SessionStart, every store rewrite leaves a full backup copy of the ledger beside it, the auto-dream sidecar is a full replacement body of the operator's private MEMORY.md, and the quality-gate and ux-grill artifacts can carry secrets or a rendered password. Each pattern below names one writer's file, its `.acquire` guard where it has one, and its own temp files (left behind only by a crash mid-write). Rotated and archived ledger copies (`*.jsonl.[0-9]*`, `*.jsonl.archive-*`, `metrics/_archive/`) are
|
|
345
|
+
`.orchestrator/` itself stays versioned: `bootstrap.lock`, `policy/`, `steering/`, `peers/` and the `.orchestrator/metrics/*.jsonl` ledgers are durable project data. What the plugin writes there at runtime is per-process state or a per-run artifact that a `git add -A` would otherwise commit — the session lock is rewritten on every SessionStart, every store rewrite leaves a full backup copy of the ledger beside it, the auto-dream sidecar is a full replacement body of the operator's private MEMORY.md, and the quality-gate and ux-grill artifacts can carry secrets or a rendered password. Each pattern below names one writer's file, its `.acquire` guard where it has one, and its own temp files (left behind only by a crash mid-write). Rotated and archived ledger copies (`*.jsonl.[0-9]*`, `*.jsonl.archive-*`, `metrics/_archive/`) are ignored too (owner decision 2026-10-03, #1507 Pkt 4): the live `*.jsonl` ledger is the versioned record, and the rotated history beside it is not.
|
|
346
346
|
|
|
347
347
|
| Pattern(s) | Written by |
|
|
348
348
|
|---|---|
|
|
@@ -360,7 +360,9 @@ Applied by Fast Step 3 (so by every tier), by the Upgrade Flow and by the Refres
|
|
|
360
360
|
| `.orchestrator/staging-fence/` | the staging-fence commit mutex and intent log |
|
|
361
361
|
| `.orchestrator/tmp/` | machine-local scratch: lock-reaper archives of reaped session locks (`tmp/reaped-locks/`, carrying host name, pid and session ids), worktree metadata (`tmp/worktree-meta/`), the orphan-reaper throttle marker (`tmp/reaper-last-scan`) |
|
|
362
362
|
| `.orchestrator/metrics/*.jsonl.bak.*` | the legacy evolve backup `learnings.jsonl.bak.evolve-<ts>`, a full store copy still on disk in older repos (`scripts/lib/learnings/io.mjs`) |
|
|
363
|
+
| `.orchestrator/metrics/*.jsonl.[0-9]*`, `.orchestrator/metrics/*.jsonl.archive-*`, `.orchestrator/metrics/_archive/` | rotated ledger history of the events log (`scripts/lib/events.mjs`): the legacy numbered `events.jsonl.<n>` slots, older `*.jsonl.archive-*` copies and the `_archive/` directory of rotated `events-<first>_<last>.jsonl` files (`scripts/lib/events-schema.mjs`); the live `*.jsonl` ledgers stay versioned |
|
|
363
364
|
| `.orchestrator/metrics/*.jsonl.tmp-*` | atomic-write temp files of the ledgers (`scripts/lib/autopilot/telemetry.mjs`), left behind only by a crash |
|
|
365
|
+
| `.orchestrator/metrics/.*.tmp.*` | temp files of the shared atomic writers (`scripts/lib/io.mjs`, named `.<base>.tmp.<hex>`), left behind only by a crash |
|
|
364
366
|
| `.orchestrator/pending-dream.md` | the auto-dream sidecar — a full replacement body of the private MEMORY.md (`scripts/lib/auto-dream.mjs`) |
|
|
365
367
|
| `.orchestrator/dialectic-pending.md`, `.orchestrator/dialectic-last-run`, `.orchestrator/consumed/` | the auto-dialectic sidecar, its last-run timestamp and the archive of consumed sidecars (`scripts/lib/auto-dialectic.mjs`) |
|
|
366
368
|
| `.orchestrator/promoted-from.json` | the worktree-promotion marker, a per-worktree fact (`scripts/lib/autopilot/worktree-pipeline.mjs`, read by `scripts/lib/session-end/worktree-cleanup.mjs`) |
|
|
@@ -396,7 +398,8 @@ _GI_PATTERNS=(
|
|
|
396
398
|
'.orchestrator/metrics/proposals-write.lock*' '.orchestrator/metrics/.proposals-write.lock.*'
|
|
397
399
|
'.orchestrator/staging-fence/'
|
|
398
400
|
'.orchestrator/tmp/'
|
|
399
|
-
'.orchestrator/metrics/*.jsonl.bak.*' '.orchestrator/metrics/*.jsonl.tmp-*'
|
|
401
|
+
'.orchestrator/metrics/*.jsonl.bak.*' '.orchestrator/metrics/*.jsonl.tmp-*' '.orchestrator/metrics/.*.tmp.*'
|
|
402
|
+
'.orchestrator/metrics/*.jsonl.[0-9]*' '.orchestrator/metrics/*.jsonl.archive-*' '.orchestrator/metrics/_archive/'
|
|
400
403
|
'.orchestrator/pending-dream.md'
|
|
401
404
|
'.orchestrator/dialectic-pending.md' '.orchestrator/dialectic-last-run' '.orchestrator/consumed/'
|
|
402
405
|
'.orchestrator/promoted-from.json'
|
|
@@ -9,6 +9,7 @@ description: >
|
|
|
9
9
|
globs), and docs-parity (docs/components.md count-claims vs on-disk counts, template-vs-reference
|
|
10
10
|
config-key parity, stale .claude/metrics/ paths). Full per-check spec in the body table. Invoked as an
|
|
11
11
|
opt-in session-end phase; mirrors vault-sync's lean JSON+exit-code contract.
|
|
12
|
+
user-invocable: false
|
|
12
13
|
model: haiku
|
|
13
14
|
---
|
|
14
15
|
|
|
@@ -37,7 +38,7 @@ PHASE 1 IMPLEMENTED (2026-04-19). Session-end opt-in quality gate. Upstream of `
|
|
|
37
38
|
| 2 | `project-count-sync` | Hardcoded "N registered" / "N projects" claims next to `01-projects/` | compare to `ls -d 01-projects/*/` |
|
|
38
39
|
| 3 | `issue-reference-freshness` | `#NN` in forward-looking sections (What's Next, Backlog, Open Issues, Offene Themen, Todo, Next Steps) | `glab issue view NN --repo <origin>` |
|
|
39
40
|
| 4 | `session-file-existence` | `50-sessions/YYYY-MM-DD-*.md` references anywhere in scope | `existsSync(vault/50-sessions/<file>)` |
|
|
40
|
-
| 5 | `command-count` | "N commands" / "N /commands" claims in prose | compare to the name-deduplicated union of `commands/*.md` and skills
|
|
41
|
+
| 5 | `command-count` | "N commands" / "N /commands" claims in prose | compare to the name-deduplicated union of `commands/*.md` and skills whose `user-invocable` is `true` or absent (Claude Code picker semantics, #1515); skipped only when both directories are absent |
|
|
41
42
|
| 6 | `session-config-parity` | Top-level keys under `## Session Config` in `CLAUDE.md` / `AGENTS.md` | diff against `docs/session-config-template.md`; a missing MANDATORY (minimal-baseline) key is an error, a missing OPT-IN-baseline-only key is a note, a local key absent from the template's Session Config blocks is a warning |
|
|
42
43
|
| 7 | `vault-dir-parity` | `vault-integration.vault-dir` in BOTH `CLAUDE.md` AND `AGENTS.md` | reuse `_parseVaultIntegration`; flag when the two files disagree. Short-circuits to PASS when the two are the SAME file by construction (symlink / same inode / byte-identical generated copy) |
|
|
43
44
|
| 8 | `generated-rule-staleness` *(WARN only)* | `.claude/rules/*.md` with `auto-generated: true` frontmatter | extract `learning-key`; WARN when the key is absent from `.orchestrator/metrics/learnings.jsonl` or its learning's `expires_at` is in the past; skipped silently when no auto-generated rules exist |
|
|
@@ -61,6 +62,8 @@ A local CLAUDE.md / AGENTS.md that omits any of these surfaces a `session-config
|
|
|
61
62
|
|
|
62
63
|
Check 7 (issue #600) is the **only** check that intentionally reads BOTH instruction files rather than the single alias-resolved one. The alias rule (CLAUDE.md wins ties, AGENTS.md is the Codex alias) means `resolveInstructionFile()` picks exactly one — so a repo carrying both files can silently let `AGENTS.md` drift out of sync with `CLAUDE.md`. A sibling project ran for weeks with a correct `vault-integration.vault-dir` in `CLAUDE.md` and a dead path in `AGENTS.md`. Check 7 reads `vault-integration.vault-dir` from each file (reusing the `_parseVaultIntegration` parser from `scripts/lib/config/vault-integration.mjs` — no hand-rolled YAML) and flags a `vault-dir-parity` error when the two values diverge (the error is attributed to `AGENTS.md`, the secondary alias, and names both values). The check skips gracefully when only one instruction file is present (nothing to compare), when neither file declares a `vault-integration:` block, or when explicitly disabled via `--skip-vault-dir-parity`. Two files that both omit `vault-dir` (both unset) agree and pass.
|
|
63
64
|
|
|
65
|
+
**Runtime override caveat.** `SO_CONFIG_FILE=AGENTS.md` reads a config-free `@CLAUDE.md` pointer as raw text; `parse-config.mjs` does not follow its import. Check 7 accepting that pointer does not prove an effective runtime config. Set the override to the config-bearing file (for example, `SO_CONFIG_FILE=CLAUDE.md`).
|
|
66
|
+
|
|
64
67
|
**Alias-by-construction short-circuit.** Since this repo's root `AGENTS.md` became a GENERATED byte-identical copy of `CLAUDE.md` (`scripts/generate-agents-skills.mjs`), Check 7 no longer skips here — it RUNS on every invocation. Two files that cannot disagree have nothing to diff, so the check reports parity as satisfied without deriving one: `instructionFilesAreAliased()` recognises a symlink resolving to the same path, the same inode/device (hardlink or already-resolved symlink), and byte-identical content. Branch order is load-bearing: the "neither file has a `vault-integration:` block" skip is evaluated FIRST, because an absent subject is a different (and more informative) outcome than "the files cannot disagree". The error class the check exists for is untouched — two INDEPENDENT files that declare different `vault-dir` values still produce the `vault-dir-parity` error attributed to `AGENTS.md`.
|
|
65
68
|
|
|
66
69
|
The same predicate guards Check 9's probe 2a, which iterates `['CLAUDE.md', 'AGENTS.md']`: with an aliased `AGENTS.md` it scans `CLAUDE.md` only, because otherwise every dangling `.claude/rules/*.md` citation would be reported TWICE — one defect, two errors, and in `--mode strict` a doubled blocking surface against a file the operator is not allowed to edit.
|