session-orchestrator 3.22.0 → 3.23.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/commands/autopilot-multi.md +14 -0
- package/.cursor/commands/autopilot.md +14 -0
- package/.cursor/commands/bootstrap.md +14 -0
- package/.cursor/commands/brainstorm.md +14 -0
- package/.cursor/commands/close.md +13 -0
- package/.cursor/commands/contract-version-bump.md +14 -0
- package/.cursor/commands/debug.md +14 -0
- package/.cursor/commands/discovery.md +14 -0
- package/.cursor/commands/dispatcher.md +14 -0
- package/.cursor/commands/eli5.md +14 -0
- package/.cursor/commands/eval.md +14 -0
- package/.cursor/commands/evolve.md +14 -0
- package/.cursor/commands/go.md +14 -0
- package/.cursor/commands/grill.md +14 -0
- package/.cursor/commands/harness-audit.md +13 -0
- package/.cursor/commands/journey-audit.md +14 -0
- package/.cursor/commands/memory-cleanup.md +14 -0
- package/.cursor/commands/persona-panel.md +14 -0
- package/.cursor/commands/plan.md +14 -0
- package/.cursor/commands/portfolio.md +14 -0
- package/.cursor/commands/reconcile.md +14 -0
- package/.cursor/commands/release.md +14 -0
- package/.cursor/commands/repo-audit.md +13 -0
- package/.cursor/commands/session.md +14 -0
- package/.cursor/commands/spinout.md +14 -0
- package/.cursor/commands/sunset-review.md +14 -0
- package/.cursor/commands/templates-ack.md +14 -0
- package/.cursor/commands/test.md +14 -0
- package/.cursor/hooks.json +60 -0
- package/.cursor/rules/000-session-orchestrator.mdc +8 -0
- package/.cursor/rules/010-session-workflow.mdc +9 -1
- package/.cursor/rules/020-quality-gates.mdc +1 -1
- package/.cursor/rules/030-wave-execution.mdc +1 -1
- package/.cursor/rules/050-plan.mdc +2 -2
- package/.cursor/rules/070-gitlab-ops.mdc +73 -57
- package/.cursor/rules/080-ecosystem-health.mdc +7 -7
- package/.cursor/skills/architecture/SKILL.md +13 -0
- package/.cursor/skills/autopilot/SKILL.md +12 -0
- package/.cursor/skills/bootstrap/SKILL.md +12 -0
- package/.cursor/skills/brainstorm/SKILL.md +13 -0
- package/.cursor/skills/claude-md-drift-check/SKILL.md +13 -0
- package/.cursor/skills/contract-version-bump/SKILL.md +12 -0
- package/.cursor/skills/convergence-monitoring/SKILL.md +12 -0
- package/.cursor/skills/daily/SKILL.md +12 -0
- package/.cursor/skills/debug/SKILL.md +13 -0
- package/.cursor/skills/discovery/SKILL.md +13 -0
- package/.cursor/skills/dispatcher/SKILL.md +13 -0
- package/.cursor/skills/docs-orchestrator/SKILL.md +13 -0
- package/.cursor/skills/domain-model/SKILL.md +13 -0
- package/.cursor/skills/ecosystem-health/SKILL.md +13 -0
- package/.cursor/skills/eli5/SKILL.md +13 -0
- package/.cursor/skills/eval/SKILL.md +12 -0
- package/.cursor/skills/evolve/SKILL.md +13 -0
- package/.cursor/skills/frontmatter-guard/SKILL.md +13 -0
- package/.cursor/skills/gitlab-ops/SKILL.md +13 -0
- package/.cursor/skills/gitlab-portfolio/SKILL.md +13 -0
- package/.cursor/skills/grill/SKILL.md +13 -0
- package/.cursor/skills/hook-development/SKILL.md +13 -0
- package/.cursor/skills/journey-audit/SKILL.md +13 -0
- package/.cursor/skills/mcp-builder/SKILL.md +13 -0
- package/.cursor/skills/memory-cleanup/SKILL.md +12 -0
- package/.cursor/skills/mode-selector/SKILL.md +13 -0
- package/.cursor/skills/npm-publish/SKILL.md +12 -0
- package/.cursor/skills/peekaboo-driver/SKILL.md +13 -0
- package/.cursor/skills/persona-panel/SKILL.md +12 -0
- package/.cursor/skills/plan/SKILL.md +13 -0
- package/.cursor/skills/playwright-driver/SKILL.md +13 -0
- package/.cursor/skills/quality-gates/SKILL.md +13 -0
- package/.cursor/skills/reconcile/SKILL.md +12 -0
- package/.cursor/skills/repo-audit/SKILL.md +13 -0
- package/.cursor/skills/session-end/SKILL.md +13 -0
- package/.cursor/skills/session-plan/SKILL.md +13 -0
- package/.cursor/skills/session-start/SKILL.md +13 -0
- package/.cursor/skills/skill-creator/SKILL.md +13 -0
- package/.cursor/skills/spinout/SKILL.md +12 -0
- package/.cursor/skills/sunset-review/SKILL.md +13 -0
- package/.cursor/skills/test-runner/SKILL.md +13 -0
- package/.cursor/skills/tmux-layout/SKILL.md +13 -0
- package/.cursor/skills/ubiquitous-language/SKILL.md +13 -0
- package/.cursor/skills/using-orchestrator/SKILL.md +13 -0
- package/.cursor/skills/vault-mirror/SKILL.md +13 -0
- package/.cursor/skills/vault-sync/SKILL.md +13 -0
- package/.cursor/skills/wave-executor/SKILL.md +13 -0
- package/.cursor/skills/write-executable-plan/SKILL.md +13 -0
- package/.mcp.json +4 -1
- package/CHANGELOG.md +168 -0
- package/README.md +18 -15
- package/agents/AGENTS.md +23 -4
- package/agents/code-implementer.md +2 -1
- package/agents/db-specialist.md +2 -1
- package/agents/docs-writer.md +3 -1
- package/agents/eval-judge.md +1 -1
- package/agents/session-reviewer.md +7 -1
- package/agents/test-writer.md +2 -1
- package/agents/ui-developer.md +2 -1
- package/commands/bootstrap.md +2 -2
- package/commands/close.md +3 -1
- package/commands/go.md +1 -1
- package/commands/journey-audit.md +43 -0
- package/docs/USER-GUIDE.md +2 -2
- package/docs/ci-setup.md +14 -0
- package/docs/codex-setup.md +64 -0
- package/docs/components.md +6 -6
- package/docs/cursor-setup.md +26 -47
- package/docs/events-schema.md +76 -4
- package/docs/github-mirror-protection.md +197 -0
- package/docs/pi-setup.md +2 -0
- package/docs/rule-authoring.md +3 -1
- package/docs/scope-collision-guard.md +49 -2
- package/docs/session-config-reference.md +26 -4
- package/docs/session-config-template.md +4 -3
- package/docs/telemetry.md +22 -0
- package/hooks/_lib/lock-bootstrap.mjs +8 -4
- package/hooks/_lib/vcs-create-matcher.mjs +397 -38
- package/hooks/enforce-scope.mjs +64 -0
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks-cursor.json +201 -20
- package/hooks/hooks-pi.json +1 -1
- package/hooks/hooks.json +2 -2
- package/hooks/on-session-end.mjs +211 -10
- package/hooks/on-session-start.mjs +214 -11
- package/hooks/on-stop.mjs +48 -9
- package/hooks/post-subagent-discovery-validator.mjs +34 -3
- package/hooks/post-tool-batch-wave-signal.mjs +11 -2
- package/hooks/pre-bash-issue-budget.mjs +117 -4
- package/hooks/pre-bash-sessions-ledger-guard.mjs +159 -0
- package/hooks/pre-bash-staging-fence.mjs +4 -0
- package/hooks/pre-task-scope-disjoint.mjs +368 -35
- package/hooks/skill-invocation-telemetry.mjs +21 -10
- package/monitors/monitors.json +6 -0
- package/package.json +1 -1
- package/pi/prompts/journey-audit.md +12 -0
- package/rules/_index.md +9 -1
- package/rules/always-on/ask-via-tool.md +62 -0
- package/rules/always-on/bash-harness-pitfalls.md +168 -0
- package/rules/always-on/build-value.md +47 -0
- package/rules/always-on/cross-session-messaging.md +59 -0
- package/rules/always-on/loop-and-monitor.md +221 -0
- package/rules/always-on/parallel-sessions.md +142 -12
- package/rules/always-on/receiving-review.md +108 -0
- package/rules/always-on/test-value.md +40 -0
- package/rules/always-on/verification-before-completion.md +77 -0
- package/scripts/archive-closed-prds.mjs +258 -18
- package/scripts/autopilot.mjs +5 -0
- package/scripts/backfill-evidence-digest.mjs +376 -0
- package/scripts/cursor-install.mjs +89 -48
- package/scripts/export-hw-learnings.mjs +143 -2
- package/scripts/express-path.mjs +299 -0
- package/scripts/generate-cursor-adapter.mjs +253 -0
- package/scripts/github-protection-audit.mjs +358 -0
- package/scripts/lib/autopilot/worktree-pipeline.mjs +240 -16
- package/scripts/lib/build-live-signals.mjs +24 -5
- package/scripts/lib/ci-status-banner.mjs +158 -11
- package/scripts/lib/command-blocker.mjs +70 -0
- package/scripts/lib/config/reconcile.mjs +79 -4
- package/scripts/lib/config/section-extractor.mjs +235 -36
- package/scripts/lib/config-schema.mjs +9 -1
- package/scripts/lib/config.mjs +57 -6
- package/scripts/lib/convergence-monitor.mjs +13 -2
- package/scripts/lib/cursor-hook-bridge.mjs +443 -0
- package/scripts/lib/dispatcher/cli.mjs +2 -2
- package/scripts/lib/express-path.mjs +327 -0
- package/scripts/lib/file-lock.mjs +22 -4
- package/scripts/lib/gates/gate-full.mjs +81 -8
- package/scripts/lib/gates/gate-helpers.mjs +76 -15
- package/scripts/lib/git-config-drift.mjs +134 -5
- package/scripts/lib/host-identity.mjs +247 -2
- package/scripts/lib/instruction-budget-guard.mjs +31 -1
- package/scripts/lib/issue-budget.mjs +229 -30
- package/scripts/lib/learnings/io.mjs +55 -10
- package/scripts/lib/learnings/schema.mjs +95 -28
- package/scripts/lib/lock-reaper.mjs +7 -1
- package/scripts/lib/locks/staging-fence-lock.mjs +5 -1
- package/scripts/lib/locks/state-md-lock.mjs +8 -1
- package/scripts/lib/memory-banner.mjs +5 -2
- package/scripts/lib/memory-paths.mjs +15 -6
- package/scripts/lib/mode-selector/scoring.mjs +53 -6
- package/scripts/lib/platform.mjs +72 -9
- package/scripts/lib/plugin-root.mjs +143 -19
- package/scripts/lib/project-hygiene.mjs +43 -3
- package/scripts/lib/quality-gate.mjs +271 -13
- package/scripts/lib/reconcile/emitter.mjs +87 -19
- package/scripts/lib/reconcile/engine.mjs +281 -13
- package/scripts/lib/reconcile/idempotency.mjs +102 -1
- package/scripts/lib/reconcile/renderer.mjs +148 -3
- package/scripts/lib/reconcile/sanitize.mjs +40 -17
- package/scripts/lib/reconcile/writer.mjs +415 -84
- package/scripts/lib/rule-loader.mjs +37 -2
- package/scripts/lib/rules-sync.mjs +51 -8
- package/scripts/lib/scope-gate.mjs +90 -0
- package/scripts/lib/session-close-backfill.mjs +369 -28
- package/scripts/lib/session-discovery.mjs +13 -3
- package/scripts/lib/session-end/phase-skip.mjs +37 -4
- package/scripts/lib/session-end/worktree-cleanup.mjs +154 -7
- package/scripts/lib/session-id.mjs +30 -14
- package/scripts/lib/session-identity/own-session.mjs +159 -0
- package/scripts/lib/session-lock.mjs +85 -30
- package/scripts/lib/session-schema/normalizer.mjs +70 -3
- package/scripts/lib/session-schema/validator.mjs +40 -0
- package/scripts/lib/session-start-probes.mjs +608 -0
- package/scripts/lib/session-transition.mjs +277 -0
- package/scripts/lib/sessions-staleness-banner.mjs +124 -57
- package/scripts/lib/spiral-carryover.mjs +90 -9
- package/scripts/lib/state-md/frontmatter-mutators.mjs +41 -8
- package/scripts/lib/state-md/mission-status.mjs +350 -52
- package/scripts/lib/state-md/yaml-parser.mjs +145 -16
- package/scripts/lib/state-md.mjs +12 -2
- package/scripts/lib/telemetry/sync.mjs +46 -8
- package/scripts/lib/validate/check-agents.mjs +66 -0
- package/scripts/lib/validate/check-cursor-adapter.mjs +102 -0
- package/scripts/lib/validate/check-dead-bridge.mjs +24 -2
- package/scripts/lib/validate/check-doc-cli-commands.mjs +16 -32
- package/scripts/lib/validate/check-hooks-symmetry.mjs +29 -63
- package/scripts/lib/validate/check-playwright-mcp-canary.mjs +13 -22
- package/scripts/lib/validate/check-plugin-monitors.mjs +10 -4
- package/scripts/lib/validate/check-test-value-bans.mjs +165 -17
- package/scripts/lib/validate/check-unwired-features.mjs +340 -32
- package/scripts/lib/validate/repo-files.mjs +275 -0
- package/scripts/lib/validate-vendored-rules.mjs +229 -7
- package/scripts/lib/vault-mirror/process.mjs +99 -43
- package/scripts/lib/vault-mirror/telemetry.mjs +210 -0
- package/scripts/lib/vault-staleness-banner.mjs +76 -6
- package/scripts/lib/vault-status/board-writer.mjs +211 -10
- package/scripts/lib/vault-status/narrative-mirror.mjs +188 -8
- package/scripts/lib/wave-executor/foreign-dispatch.mjs +832 -0
- package/scripts/lib/wave-transcript-tail.mjs +869 -0
- package/scripts/materialize-wave-scope.mjs +209 -12
- package/scripts/mcp-server.sh +11 -2
- package/scripts/parse-config.mjs +65 -0
- package/scripts/token-audit.sh +9 -2
- package/scripts/validate-plugin.mjs +3 -0
- package/scripts/validate-wave-scope.mjs +67 -0
- package/scripts/vault-mirror.mjs +203 -34
- package/skills/_shared/monitor-patterns.md +31 -5
- package/skills/_shared/parallel-aware-auq.md +1 -1
- package/skills/_shared/parallel-aware-preamble.md +4 -2
- package/skills/_shared/platform-tools.md +11 -5
- package/skills/_shared/state-ownership.md +29 -2
- package/skills/autopilot/SKILL.md +5 -1
- package/skills/bootstrap/SKILL.md +3 -3
- package/skills/bootstrap/_shared-template.md +18 -10
- package/skills/bootstrap/deep-template.md +10 -6
- package/skills/bootstrap/fast-template.md +15 -8
- package/skills/bootstrap/standard-template.md +10 -6
- package/skills/claude-md-drift-check/checker.mjs +39 -11
- package/skills/dispatcher/SKILL.md +1 -1
- package/skills/journey-audit/SKILL.md +269 -0
- package/skills/peekaboo-driver/SKILL.md +15 -3
- package/skills/persona-panel/SKILL.md +1 -1
- package/skills/reconcile/SKILL.md +41 -1
- package/skills/session-end/SKILL.md +17 -4
- package/skills/session-end/metrics-collection.md +7 -4
- package/skills/session-end/phase-3-6-tail.md +11 -3
- package/skills/session-end/phase-3-7a-recommendations.md +16 -2
- package/skills/session-plan/SKILL.md +6 -1
- package/skills/session-plan/wave-template.md +1 -0
- package/skills/session-start/SKILL.md +30 -16
- package/skills/session-start/phase-7-5-mode-selector.md +15 -3
- package/skills/session-start/phase-8-5-express-path.md +77 -12
- package/skills/vault-sync/validator.mjs +31 -0
- package/skills/wave-executor/SKILL.md +4 -2
- package/skills/wave-executor/circuit-breaker.md +34 -9
- package/skills/wave-executor/wave-loop.md +102 -19
- package/templates/_shared/journey-manifest.md +110 -0
- package/templates/_shared/rules/parallel-sessions.md +0 -77
|
@@ -6,7 +6,8 @@
|
|
|
6
6
|
* aliases) or parent barrel.
|
|
7
7
|
*
|
|
8
8
|
* Exports: normalizeSession
|
|
9
|
-
* Module-private: _warnedMissingSchemaVersion (Set, per-process dedupe)
|
|
9
|
+
* Module-private: _warnedMissingSchemaVersion (Set, per-process dedupe),
|
|
10
|
+
* isPlainObject, _canonicalizeExpressPath
|
|
10
11
|
*/
|
|
11
12
|
|
|
12
13
|
import { SESSION_KEY_ALIASES } from './constants.mjs';
|
|
@@ -19,19 +20,82 @@ import { SESSION_KEY_ALIASES } from './constants.mjs';
|
|
|
19
20
|
// per process, preventing log-spam on large sessions.jsonl files.
|
|
20
21
|
const _warnedMissingSchemaVersion = new Set();
|
|
21
22
|
|
|
23
|
+
// ---------------------------------------------------------------------------
|
|
24
|
+
// Internal helper (intentional duplication — Option 1 submodule isolation,
|
|
25
|
+
// mirroring aliases.mjs / validator.mjs rather than adding a cross-import)
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
|
|
28
|
+
function isPlainObject(v) {
|
|
29
|
+
return v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Collapse the legacy OBJECT form of `express_path` onto the canonical
|
|
34
|
+
* BOOLEAN form, in place on the caller's already-copied `next` object.
|
|
35
|
+
*
|
|
36
|
+
* WHY BOOLEAN IS CANONICAL. The ledger carried the field in two forms with no
|
|
37
|
+
* arbiter: 20 boolean records (14 `false`, 6 `true`) and exactly ONE object,
|
|
38
|
+
* `{activated, tasks, notes}` on `main-2026-05-01-housekeeping-2` (measured
|
|
39
|
+
* 2026-08-23 over 271 records). That single record is a hand-composition
|
|
40
|
+
* artefact, not a richer designed variant: it is the only one of the 21 with
|
|
41
|
+
* no top-level `notes`, and its `express_path.notes` holds session-level
|
|
42
|
+
* narrative that every other record puts in the canonical top-level `notes`.
|
|
43
|
+
* It also carries the same era's ad-hoc vocabulary elsewhere
|
|
44
|
+
* (`waves_completed`, `issues_new`, `cross_repo_writes`), so the object form
|
|
45
|
+
* is that composition freedom, not a schema. The question the field exists to
|
|
46
|
+
* answer — "greift der Express Path?" (docs/prd/2026-08-22-framework-
|
|
47
|
+
* verschlankung.md § VS-1) — is a boolean question whose denominator is the
|
|
48
|
+
* 14 `false` records.
|
|
49
|
+
*
|
|
50
|
+
* NOTHING IS DISCARDED. The original object is preserved verbatim under
|
|
51
|
+
* `_express_path_detail`, following the module's own "original keys are
|
|
52
|
+
* preserved alongside their canonical alias" convention and the
|
|
53
|
+
* `_completed_at_conflict` forensics tag in aliases.mjs. The conversion is
|
|
54
|
+
* therefore deterministic AND reversible.
|
|
55
|
+
*
|
|
56
|
+
* A shape this function cannot recognise (string, number, array, or an object
|
|
57
|
+
* without a boolean `activated`) is passed through UNTOUCHED — normalizeSession
|
|
58
|
+
* never throws. Refusing such a shape is the write path's job; validator.mjs
|
|
59
|
+
* rejects it there.
|
|
60
|
+
*
|
|
61
|
+
* BOUNDED TOLERANCE — REVISIT TRIGGER: the legacy object form is accepted only
|
|
62
|
+
* because one historical record uses it. When that record leaves the ledger (or
|
|
63
|
+
* is rewritten by a migration), delete this function and the matching
|
|
64
|
+
* `express_path` object clause in validator.mjs — canonical-boolean-only.
|
|
65
|
+
*
|
|
66
|
+
* @param {Record<string, any>} next — mutable copy of the entry being normalized
|
|
67
|
+
*/
|
|
68
|
+
function _canonicalizeExpressPath(next) {
|
|
69
|
+
if (!('express_path' in next)) return;
|
|
70
|
+
const raw = next.express_path;
|
|
71
|
+
if (!isPlainObject(raw)) return; // boolean (canonical), null, or unrecognised
|
|
72
|
+
if (typeof raw.activated !== 'boolean') return; // not the legacy shape
|
|
73
|
+
|
|
74
|
+
// Non-clobber, matching the alias rule above: never overwrite a sidecar that
|
|
75
|
+
// a previous normalization pass (or a producer) already wrote.
|
|
76
|
+
if (!('_express_path_detail' in next)) {
|
|
77
|
+
next._express_path_detail = raw;
|
|
78
|
+
}
|
|
79
|
+
next.express_path = raw.activated;
|
|
80
|
+
}
|
|
81
|
+
|
|
22
82
|
// ---------------------------------------------------------------------------
|
|
23
83
|
// Public API
|
|
24
84
|
// ---------------------------------------------------------------------------
|
|
25
85
|
|
|
26
86
|
/**
|
|
27
|
-
* Normalize a session entry read from disk. Applies SAFE key aliases,
|
|
87
|
+
* Normalize a session entry read from disk. Applies SAFE key aliases, collapses
|
|
88
|
+
* the legacy object form of `express_path` onto its canonical boolean, and tags
|
|
28
89
|
* legacy entries without `schema_version` as 0 (distinct from
|
|
29
90
|
* CURRENT_SESSION_SCHEMA_VERSION=2 which is stamped on new writes; bumped
|
|
30
91
|
* 1 -> 2 via #372).
|
|
31
92
|
*
|
|
32
93
|
* Never throws. Malformed input (null, non-object, array) is passed through
|
|
33
94
|
* unchanged. Original keys are preserved alongside their canonical alias for
|
|
34
|
-
* debugging
|
|
95
|
+
* debugging; the pre-collapse `express_path` object is preserved under
|
|
96
|
+
* `_express_path_detail` for the same reason.
|
|
97
|
+
*
|
|
98
|
+
* Idempotent: normalize(normalize(x)) deep-equals normalize(x).
|
|
35
99
|
*
|
|
36
100
|
* @param {any} entry
|
|
37
101
|
* @returns {any} normalized entry (or original if non-object)
|
|
@@ -48,6 +112,9 @@ export function normalizeSession(entry) {
|
|
|
48
112
|
}
|
|
49
113
|
}
|
|
50
114
|
|
|
115
|
+
// express_path — same key, two shapes. Collapse onto the canonical boolean.
|
|
116
|
+
_canonicalizeExpressPath(next);
|
|
117
|
+
|
|
51
118
|
// schema_version — legacy entries tagged as 0 (NOT CURRENT_SESSION_SCHEMA_VERSION).
|
|
52
119
|
if ('schema_version' in next && next.schema_version !== undefined) {
|
|
53
120
|
// Preserve existing version.
|
|
@@ -431,6 +431,46 @@ function _validateOptionalFields(entry) {
|
|
|
431
431
|
}
|
|
432
432
|
}
|
|
433
433
|
}
|
|
434
|
+
|
|
435
|
+
// `express_path` — CANONICAL FORM IS BOOLEAN (`true` = the express path was
|
|
436
|
+
// taken, `false` = it was offered and declined). Until now the field had no
|
|
437
|
+
// arbiter at all: the ledger carried 20 booleans and 1 object
|
|
438
|
+
// (`{activated, tasks, notes}`) and nothing in the schema knew the key, so
|
|
439
|
+
// neither shape could be called wrong. See the rationale + the 2026-08-23
|
|
440
|
+
// census in normalizer.mjs `_canonicalizeExpressPath`.
|
|
441
|
+
//
|
|
442
|
+
// The legacy `{activated: boolean, ...}` object is a BOUNDED read tolerance,
|
|
443
|
+
// in the same spirit as `_validateSchemaVersion` accepting [0,1,2,3] while
|
|
444
|
+
// only CURRENT_SESSION_SCHEMA_VERSION is ever written. It is bounded in two
|
|
445
|
+
// ways: an object WITHOUT a boolean `activated` is refused (the tolerance
|
|
446
|
+
// cannot grow into "any object"), and `normalizeSession` collapses the
|
|
447
|
+
// legacy object to the canonical boolean on every read, so no consumer
|
|
448
|
+
// downstream of the read path ever observes two shapes. Rejecting the legacy
|
|
449
|
+
// object outright here would have cost exactly one historical record its
|
|
450
|
+
// clean bill of health in `checkSessionsIntegrity` for zero benefit —
|
|
451
|
+
// measured 2026-08-23: `validateSession` fails 0 of 271 records today.
|
|
452
|
+
//
|
|
453
|
+
// REVISIT TRIGGER: when no ledger record uses the object form any more, drop
|
|
454
|
+
// the `isPlainObject` branch below and require a boolean outright.
|
|
455
|
+
if (entry.express_path !== undefined && entry.express_path !== null) {
|
|
456
|
+
const ep = entry.express_path;
|
|
457
|
+
if (typeof ep !== 'boolean' && !(isPlainObject(ep) && typeof ep.activated === 'boolean')) {
|
|
458
|
+
const shape = Array.isArray(ep) ? 'array' : typeof ep;
|
|
459
|
+
throw new ValidationError(
|
|
460
|
+
`express_path must be a boolean (canonical), null, or the legacy {activated: boolean} object, got: ${shape}`
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
// `_express_path_detail` — forensic sidecar written by `normalizeSession`
|
|
466
|
+
// when it collapses a legacy object `express_path` onto its boolean. Holds
|
|
467
|
+
// the pre-collapse object verbatim so the conversion stays reversible.
|
|
468
|
+
// Same optional-object contract as `effectiveness` / `discovery_stats`.
|
|
469
|
+
if (entry._express_path_detail !== undefined && entry._express_path_detail !== null) {
|
|
470
|
+
if (!isPlainObject(entry._express_path_detail)) {
|
|
471
|
+
throw new ValidationError('_express_path_detail must be an object or null');
|
|
472
|
+
}
|
|
473
|
+
}
|
|
434
474
|
}
|
|
435
475
|
|
|
436
476
|
// ---------------------------------------------------------------------------
|