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,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* learnings/kebab.mjs — the slug primitive behind every `learning_key`, and
|
|
3
|
+
* {@link learningKeyOf}, the whole-key derivation built on it.
|
|
4
|
+
*
|
|
5
|
+
* `learning_key` is not a stored field. It is DERIVED as
|
|
6
|
+
* `` `${type}/${kebab(title || subject)}` `` and is the logical identity the
|
|
7
|
+
* whole dedupe/idempotency layer keys on (`reconcile/idempotency.mjs` calls it
|
|
8
|
+
* "THE logical dedupe key"). That makes this a contract, not a formatting
|
|
9
|
+
* helper: two callers that derive differently do not produce two ugly slugs,
|
|
10
|
+
* they FORK the key space — the same learning renders under two identities,
|
|
11
|
+
* dedupe stops firing, and both halves look correct in isolation.
|
|
12
|
+
*
|
|
13
|
+
* Before this module existed there were four copies of `kebab` plus one inline
|
|
14
|
+
* expression, none exported, none shared (two of the four landed on the same
|
|
15
|
+
* day, from two different authors, each of whom flagged the duplication in
|
|
16
|
+
* their own report). They agreed on every reachable input — verified, not
|
|
17
|
+
* assumed — so consolidating here changed no existing key. See the byte-identity
|
|
18
|
+
* proof pinned in `tests/scripts/lib/learnings/kebab.test.mjs`.
|
|
19
|
+
*
|
|
20
|
+
* Consolidating the primitive left the SECOND half of the problem open, and it
|
|
21
|
+
* is the half that bites: what each call site FED the primitive. Four sites
|
|
22
|
+
* derived `${type}/…` verbatim while the writer (`reconcile/emitter.mjs`) alone
|
|
23
|
+
* derived `${kebab(type)}/…` — a divergence invisible while every live `type`
|
|
24
|
+
* is kebab-identical, and silent in BOTH directions the moment one is not.
|
|
25
|
+
* {@link learningKeyOf} exists so there is one derivation to agree with rather
|
|
26
|
+
* than five to keep in sync.
|
|
27
|
+
*
|
|
28
|
+
* ## Why this file lives under `learnings/` and not under `reconcile/`
|
|
29
|
+
*
|
|
30
|
+
* Dependency direction, measured rather than presumed (2026-08-13, HEAD
|
|
31
|
+
* 5d59e62): `grep -rn "from '../learnings/" scripts/lib/reconcile/` reports 6
|
|
32
|
+
* import edges across 4 files (`eligibility.mjs`, `engine.mjs` ×2,
|
|
33
|
+
* `emitter.mjs` ×2, `renderer.mjs` — three of them pulling THIS module, three
|
|
34
|
+
* pulling `learnings/schema.mjs`), and the reverse grep
|
|
35
|
+
* `from '../reconcile/'` over `scripts/lib/learnings/` reports 0. A primitive
|
|
36
|
+
* shared by both packages therefore belongs on the `learnings/` side; placing
|
|
37
|
+
* it under `reconcile/` would invert an edge and introduce a cycle.
|
|
38
|
+
*
|
|
39
|
+
* Deliberately NOT re-exported from the `scripts/lib/learnings.mjs` barrel —
|
|
40
|
+
* `surface.mjs` and `affinity.mjs` set that precedent: consumers import the
|
|
41
|
+
* leaf directly, so the barrel stays the historical schema/io/filters surface.
|
|
42
|
+
*
|
|
43
|
+
* ## What this module is NOT
|
|
44
|
+
*
|
|
45
|
+
* Not a general-purpose slugifier. Vault note ids, tag segments, and rule
|
|
46
|
+
* filenames have their own slug rules with their own length caps and charset
|
|
47
|
+
* contracts (`vault-mirror/utils.mjs`, `vault-archive.mjs`). Do not route those
|
|
48
|
+
* through here — a shared slugifier across unrelated identity spaces is how a
|
|
49
|
+
* cap added for one consumer silently re-keys another.
|
|
50
|
+
*
|
|
51
|
+
* Pure, stdlib-only, no imports, no clock, no fs.
|
|
52
|
+
*/
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Slugify a string into the stable kebab-case token used for learning keys.
|
|
56
|
+
*
|
|
57
|
+
* Lowercases, collapses every run of non-`[a-z0-9]` characters into a single
|
|
58
|
+
* `-`, and trims leading/trailing `-`.
|
|
59
|
+
*
|
|
60
|
+
* Three properties callers depend on:
|
|
61
|
+
*
|
|
62
|
+
* 1. **Total** — never throws. Non-string input is coerced via `String()`,
|
|
63
|
+
* so `null`/`undefined`/numbers yield `"null"`/`"undefined"`/`"12345"`
|
|
64
|
+
* rather than a `TypeError`. Callers pass values typed `unknown` at
|
|
65
|
+
* their trust boundary; a throw there would abort a reconcile run.
|
|
66
|
+
* 2. **Lossy on non-ASCII** — German umlauts and em-dashes in the corpus are
|
|
67
|
+
* collapsed to `-` (`"Größe"` → `"gr-e"`), NOT transliterated. Ugly, and
|
|
68
|
+
* deliberately frozen: every stamped key in `.claude/rules/*.md` and in
|
|
69
|
+
* `.orchestrator/runtime/reconcile-candidates.jsonl` was minted this way.
|
|
70
|
+
* Adding transliteration would re-key the entire corpus.
|
|
71
|
+
* 3. **May return the empty string** — an all-symbol input (`"!!!"`) yields
|
|
72
|
+
* `""`. `reconcile/renderer.mjs::deriveSlug` branches on exactly that to
|
|
73
|
+
* fall back to its hash suffix, so an "always return something non-empty"
|
|
74
|
+
* change here would silently disable that branch.
|
|
75
|
+
*
|
|
76
|
+
* @param {unknown} s Value to slugify; coerced with `String()`.
|
|
77
|
+
* @returns {string} Kebab token; `''` when the input holds no `[a-z0-9]`.
|
|
78
|
+
*/
|
|
79
|
+
export function kebab(s) {
|
|
80
|
+
return String(s)
|
|
81
|
+
.toLowerCase()
|
|
82
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
83
|
+
.replace(/^-+|-+$/g, '');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* THE derivation of a learning's logical key: `` `${type}/${kebab(title || subject)}` ``.
|
|
88
|
+
*
|
|
89
|
+
* One rule, four decisions, each of which was a divergence between call sites
|
|
90
|
+
* before this function existed:
|
|
91
|
+
*
|
|
92
|
+
* 1. **The TYPE half is verbatim (trimmed), never kebab'd.** The subject half
|
|
93
|
+
* is prose and must be slugged; the type half is an enum token a reader
|
|
94
|
+
* parses BACK OUT of the key — `backfill-learnings-from-vault.mjs` uses
|
|
95
|
+
* `learning_key.split('/')[0]` as the reconstructed `type` and
|
|
96
|
+
* cross-checks it against the vault note's type. Kebabbing it would make
|
|
97
|
+
* that half lossy, which is precisely the fidelity downgrade the backfill
|
|
98
|
+
* labels `derived:learning-key-slug (original prose not recoverable)` for
|
|
99
|
+
* the subject half. Frontmatter safety does not need it either: the key
|
|
100
|
+
* becomes an unquoted `learning-key:` scalar, and `reconcile/renderer.mjs`
|
|
101
|
+
* already asserts `LEARNING_KEY_RE` (`/^[a-z0-9/-]+$/`) on the whole value
|
|
102
|
+
* before rendering — an unsafe type is REJECTED loudly there rather than
|
|
103
|
+
* silently re-keyed here.
|
|
104
|
+
* 2. **`title` wins over `subject`**, and a whitespace-only `title` falls
|
|
105
|
+
* through to `subject` rather than yielding an empty slug.
|
|
106
|
+
* 3. **`null`, not a throw, for an unkeyable record.** Readers scan corpora
|
|
107
|
+
* that contain shape-foreign lines; an unkeyable record simply does not
|
|
108
|
+
* participate in key resolution. Writers that need a string check for
|
|
109
|
+
* `null` and reject the record with their own auditable reason.
|
|
110
|
+
* 4. **An empty slug is unkeyable, not a key.** `kebab('!!!')` is `''`, and
|
|
111
|
+
* `` `anti-pattern/` `` is not an identity — it is a bucket every
|
|
112
|
+
* all-symbol-subject record of that type would collide in.
|
|
113
|
+
*
|
|
114
|
+
* @param {unknown} record A learning record (or anything; non-records yield `null`).
|
|
115
|
+
* @returns {string|null} `` `${type}/${slug}` ``, or `null` when unkeyable.
|
|
116
|
+
*/
|
|
117
|
+
export function learningKeyOf(record) {
|
|
118
|
+
if (record === null || typeof record !== 'object' || Array.isArray(record)) return null;
|
|
119
|
+
const rec = /** @type {Record<string, unknown>} */ (record);
|
|
120
|
+
const type = typeof rec.type === 'string' ? rec.type.trim() : '';
|
|
121
|
+
const titleOrSubject =
|
|
122
|
+
(typeof rec.title === 'string' && rec.title.trim() !== '' ? rec.title : '') ||
|
|
123
|
+
(typeof rec.subject === 'string' && rec.subject.trim() !== '' ? rec.subject : '');
|
|
124
|
+
if (type === '' || titleOrSubject === '') return null;
|
|
125
|
+
const slug = kebab(titleOrSubject);
|
|
126
|
+
if (slug === '') return null;
|
|
127
|
+
return `${type}/${slug}`;
|
|
128
|
+
}
|