session-orchestrator 3.24.0 → 4.0.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 +18 -0
- package/.agents/skills/autopilot/SKILL.md +17 -0
- package/.agents/skills/bootstrap/SKILL.md +20 -0
- package/.agents/skills/brainstorm/SKILL.md +22 -0
- package/.agents/skills/claude-md-drift-check/SKILL.md +15 -0
- package/.agents/skills/convergence-monitoring/SKILL.md +22 -0
- package/.agents/skills/debug/SKILL.md +22 -0
- package/.agents/skills/discovery/SKILL.md +20 -0
- package/.agents/skills/dispatcher/SKILL.md +15 -0
- package/.agents/skills/docs-orchestrator/SKILL.md +18 -0
- package/.agents/skills/ecosystem-health/SKILL.md +20 -0
- package/.agents/skills/eli5/SKILL.md +20 -0
- package/.agents/skills/eval/SKILL.md +21 -0
- package/.agents/skills/evolve/SKILL.md +21 -0
- package/.agents/skills/frontmatter-guard/SKILL.md +15 -0
- package/.agents/skills/gitlab-ops/SKILL.md +20 -0
- package/.agents/skills/gitlab-portfolio/SKILL.md +15 -0
- package/.agents/skills/grill/SKILL.md +22 -0
- package/.agents/skills/hook-development/SKILL.md +15 -0
- package/.agents/skills/mcp-builder/SKILL.md +15 -0
- package/.agents/skills/memory-cleanup/SKILL.md +21 -0
- package/.agents/skills/mode-selector/SKILL.md +17 -0
- package/.agents/skills/npm-publish/SKILL.md +16 -0
- package/.agents/skills/peekaboo-driver/SKILL.md +18 -0
- package/.agents/skills/persona-panel/SKILL.md +17 -0
- package/.agents/skills/plan/SKILL.md +20 -0
- package/.agents/skills/playwright-driver/SKILL.md +20 -0
- package/.agents/skills/quality-gates/SKILL.md +20 -0
- package/.agents/skills/reconcile/SKILL.md +21 -0
- package/.agents/skills/remote-offload/SKILL.md +20 -0
- package/.agents/skills/repo-audit/SKILL.md +16 -0
- package/.agents/skills/session-end/SKILL.md +20 -0
- package/.agents/skills/session-plan/SKILL.md +20 -0
- package/.agents/skills/session-start/SKILL.md +20 -0
- package/.agents/skills/spinout/SKILL.md +16 -0
- package/.agents/skills/sunset-review/SKILL.md +16 -0
- package/.agents/skills/test-runner/SKILL.md +20 -0
- package/.agents/skills/tmux-layout/SKILL.md +21 -0
- package/.agents/skills/using-orchestrator/SKILL.md +17 -0
- package/.agents/skills/vault-mirror/SKILL.md +15 -0
- package/.agents/skills/vault-sync/SKILL.md +15 -0
- package/.agents/skills/wave-executor/SKILL.md +20 -0
- package/.agents/skills/write-executable-plan/SKILL.md +22 -0
- 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.md +2 -2
- package/.cursor/commands/bootstrap.md +1 -1
- package/.cursor/commands/brainstorm.md +1 -1
- package/.cursor/commands/debug.md +1 -1
- package/.cursor/commands/discovery.md +1 -1
- package/.cursor/commands/dispatcher.md +2 -2
- package/.cursor/commands/eli5.md +2 -2
- package/.cursor/commands/eval.md +2 -2
- package/.cursor/commands/evolve.md +1 -1
- package/.cursor/commands/go.md +1 -1
- package/.cursor/commands/grill.md +2 -2
- package/.cursor/commands/memory-cleanup.md +2 -2
- package/.cursor/commands/persona-panel.md +1 -1
- package/.cursor/commands/plan.md +1 -1
- package/.cursor/commands/portfolio.md +1 -1
- package/.cursor/commands/reconcile.md +2 -2
- package/.cursor/commands/release.md +2 -2
- package/.cursor/commands/session.md +2 -2
- package/.cursor/commands/spinout.md +2 -2
- package/.cursor/commands/sunset-review.md +2 -2
- package/.cursor/commands/templates-ack.md +2 -2
- package/.cursor/commands/test.md +2 -2
- package/.cursor/skills/brainstorm/SKILL.md +1 -1
- package/.cursor/skills/eval/SKILL.md +1 -1
- package/.cursor/skills/quality-gates/SKILL.md +1 -1
- package/.cursor/skills/remote-offload/SKILL.md +1 -1
- package/.orchestrator/policy/blocked-commands.json +121 -0
- package/.orchestrator/policy/ecosystem.schema.json +66 -0
- package/.orchestrator/policy/quality-gates.example.json +16 -0
- package/.orchestrator/policy/quality-gates.schema.json +38 -0
- package/.orchestrator/policy/templates-policy.json +27 -0
- package/.orchestrator/policy/test-profiles.json +47 -0
- package/AGENTS.md +225 -0
- package/CHANGELOG.md +1125 -2
- package/NOTICE +11 -6
- package/README.md +127 -94
- package/agents/eval-judge.md +1 -1
- package/agents/skill-applied-judge.md +1 -1
- package/assets/wave-lifecycle.svg +98 -0
- package/commands/release.md +6 -3
- package/commands/session.md +18 -3
- package/docs/README.md +4 -0
- package/{agents/AGENTS.md → docs/agent-authoring.md} +19 -26
- package/docs/baseline.md +67 -0
- package/docs/ci-setup.md +108 -62
- package/docs/codex-setup.md +65 -21
- package/docs/components.md +36 -15
- package/docs/cursor-setup.md +6 -2
- package/docs/events-schema.md +9 -6
- package/docs/instruction-delivery.md +62 -0
- package/{agents/memory-proposal-collector.md → docs/memory-proposal-flow.md} +1 -8
- package/docs/migration-v4.md +341 -0
- package/docs/pi-setup.md +6 -1
- package/docs/plugin-architecture-v3.md +1 -1
- package/docs/rule-authoring.md +85 -19
- package/docs/scope-collision-guard.md +5 -5
- package/docs/session-config-reference.md +57 -56
- package/docs/session-config-template.md +6 -29
- package/docs/telemetry.md +157 -3
- package/docs/vault-docs-architecture.md +50 -11
- package/hooks/_lib/hook-import-set.json +1487 -0
- package/hooks/_lib/subagent-transcript.mjs +562 -0
- package/hooks/config-protection.mjs +2 -2
- package/hooks/cwd-change-restore.mjs +2 -2
- package/hooks/enforce-commands.mjs +69 -0
- package/hooks/hooks-codex.json +1 -1
- package/hooks/hooks-cursor.json +10 -0
- package/hooks/hooks-pi.json +5 -0
- package/hooks/hooks.json +6 -1
- package/hooks/loop-guard.mjs +3 -3
- package/hooks/on-session-end.mjs +2 -2
- package/hooks/on-session-start.mjs +103 -2
- package/hooks/on-stop.mjs +36 -11
- package/hooks/operator-steer.mjs +2 -2
- package/hooks/post-bash-write-verify.mjs +85 -0
- package/hooks/post-edit-import-probe.mjs +344 -0
- package/hooks/post-subagent-discovery-validator.mjs +187 -431
- package/hooks/post-tool-batch-wave-signal.mjs +118 -4
- package/hooks/post-tool-failure-corrective-context.mjs +2 -2
- package/hooks/post-tooluse-frontend-slop.mjs +3 -3
- package/hooks/pre-bash-destructive-guard.mjs +39 -13
- package/hooks/skill-invocation-telemetry.mjs +17 -5
- package/hooks/subagent-telemetry.mjs +13 -4
- package/monitors/monitors.json +3 -3
- package/package.json +9 -1
- package/pi/prompts/session.md +2 -2
- package/plugin.json +27 -0
- package/scripts/backfill-abandoned-sessions.mjs +50 -4
- package/scripts/backfill-learnings-from-vault.mjs +9 -3
- package/scripts/dialectic-deriver.mjs +73 -8
- package/scripts/export-hw-learnings.mjs +113 -1
- package/scripts/generate-agents-skills.mjs +378 -0
- package/scripts/generate-cursor-adapter.mjs +45 -8
- package/scripts/generate-hook-import-set.mjs +249 -0
- package/scripts/lib/agent-status.mjs +13 -2
- package/scripts/lib/auto-dream.mjs +38 -36
- package/scripts/lib/autonomy/suitability.mjs +6 -0
- package/scripts/lib/autopilot/loop.mjs +2 -2
- package/scripts/lib/ci-status-banner.mjs +220 -75
- package/scripts/lib/codex/plugin-contract.mjs +82 -6
- package/scripts/lib/config/auto-dream.mjs +2 -1
- package/scripts/lib/config/block-header.mjs +8 -0
- package/scripts/lib/config/block-preprocess.mjs +177 -0
- package/scripts/lib/config/broken-window.mjs +2 -1
- package/scripts/lib/config/cold-start.mjs +2 -1
- package/scripts/lib/config/config-protection.mjs +22 -2
- package/scripts/lib/config/context-coverage.mjs +2 -1
- package/scripts/lib/config/cross-repo.mjs +2 -1
- package/scripts/lib/config/custom-phases.mjs +2 -1
- package/scripts/lib/config/dialectic.mjs +2 -1
- package/scripts/lib/config/discovery-validator.mjs +2 -1
- package/scripts/lib/config/dispatcher-autonomy-capture.mjs +24 -1
- package/scripts/lib/config/dispatcher-autonomy.mjs +2 -1
- package/scripts/lib/config/docs-orchestrator.mjs +2 -1
- package/scripts/lib/config/docs-staleness.mjs +2 -1
- package/scripts/lib/config/drift-check.mjs +2 -1
- package/scripts/lib/config/eval.mjs +2 -1
- package/scripts/lib/config/events-rotation.mjs +2 -1
- package/scripts/lib/config/evolve.mjs +8 -2
- package/scripts/lib/config/frontend-slop-hook.mjs +7 -3
- package/scripts/lib/config/gitlab-portfolio.mjs +2 -1
- package/scripts/lib/config/handover-gate.mjs +2 -1
- package/scripts/lib/config/health-endpoints.mjs +7 -2
- package/scripts/lib/config/issue-budget.mjs +2 -1
- package/scripts/lib/config/loop-guard.mjs +2 -1
- package/scripts/lib/config/memory.mjs +2 -1
- package/scripts/lib/config/moc-staleness.mjs +2 -1
- package/scripts/lib/config/persona-gate-wave.mjs +2 -1
- package/scripts/lib/config/private-config-dir.mjs +67 -0
- package/scripts/lib/config/reconcile.mjs +2 -1
- package/scripts/lib/config/remote-hosts.mjs +2 -1
- package/scripts/lib/config/section-extractor.mjs +7 -1
- package/scripts/lib/config/skill-evolution.mjs +2 -1
- package/scripts/lib/config/slopcheck.mjs +2 -1
- package/scripts/lib/config/state-md-lock.mjs +2 -1
- package/scripts/lib/config/templates-first.mjs +2 -1
- package/scripts/lib/config/test.mjs +2 -1
- package/scripts/lib/config/vault-integration.mjs +7 -1
- package/scripts/lib/config/vault-mirror-quality.mjs +2 -1
- package/scripts/lib/config/vault-staleness.mjs +2 -1
- package/scripts/lib/config/vault-sync.mjs +2 -1
- package/scripts/lib/config/verification-auto-fix.mjs +2 -1
- package/scripts/lib/config/wave-reviewers.mjs +2 -1
- package/scripts/lib/config/worktree-orphans.mjs +2 -1
- package/scripts/lib/convergence-monitor.mjs +82 -16
- package/scripts/lib/dispatcher/rank.mjs +124 -48
- package/scripts/lib/ecosystem-health.mjs +16 -2
- package/scripts/lib/eval/engine.mjs +9 -1
- package/scripts/lib/eval/session-resolve.mjs +23 -4
- package/scripts/lib/events.mjs +22 -6
- package/scripts/lib/frontmatter-guard.mjs +131 -13
- package/scripts/lib/gates/gate-full.mjs +26 -0
- package/scripts/lib/gates/gate-helpers.mjs +76 -0
- package/scripts/lib/hardware-pattern-detector.mjs +18 -1
- package/scripts/lib/harness-audit/categories/category4.mjs +31 -11
- package/scripts/lib/host-identity.mjs +50 -11
- package/scripts/lib/instruction-budget-guard.mjs +171 -5
- package/scripts/lib/learnings/evolve-telemetry.mjs +178 -0
- package/scripts/lib/learnings/io.mjs +60 -6
- package/scripts/lib/memory-proposals/store.mjs +30 -22
- package/scripts/lib/owner-config-banner.mjs +43 -6
- package/scripts/lib/owner-config-loader.mjs +21 -10
- package/scripts/lib/owner-interview.mjs +3 -3
- package/scripts/lib/owner-yaml.mjs +207 -14
- package/scripts/lib/platform.mjs +108 -15
- package/scripts/lib/plugin-update-banner.mjs +406 -0
- package/scripts/lib/project-hygiene.mjs +38 -2
- package/scripts/lib/qg-command-drift-banner.mjs +50 -12
- package/scripts/lib/quality-gate.mjs +133 -44
- package/scripts/lib/reconcile/emitter.mjs +68 -6
- package/scripts/lib/reconcile/engine.mjs +13 -4
- package/scripts/lib/reconcile/idempotency.mjs +37 -4
- package/scripts/lib/reconcile/writer.mjs +40 -18
- package/scripts/lib/session-close-backfill.mjs +67 -9
- package/scripts/lib/session-id.mjs +12 -23
- package/scripts/lib/session-identity/own-session.mjs +125 -10
- package/scripts/lib/session-lock-shape.mjs +43 -0
- package/scripts/lib/session-lock.mjs +5 -10
- package/scripts/lib/session-registry.mjs +25 -9
- package/scripts/lib/session-schema/constants.mjs +36 -2
- package/scripts/lib/session-schema/validator.mjs +38 -4
- package/scripts/lib/session-start-probes.mjs +18 -1
- package/scripts/lib/sessions-staleness-banner.mjs +18 -11
- package/scripts/lib/skill-health/join.mjs +17 -4
- package/scripts/lib/state-md.mjs +78 -0
- package/scripts/lib/sunset/walker.mjs +6 -0
- package/scripts/lib/telemetry/schema.mjs +181 -9
- package/scripts/lib/telemetry/sync.mjs +368 -12
- package/scripts/lib/validate/check-agents-skills.mjs +327 -0
- package/scripts/lib/validate/check-agents.mjs +3 -3
- package/scripts/lib/validate/check-cursor-adapter.mjs +234 -72
- package/scripts/lib/validate/check-hooks-symmetry.mjs +45 -16
- package/scripts/lib/validate/check-owner-leakage.mjs +281 -20
- package/scripts/lib/validate/check-skill-links.mjs +163 -0
- package/scripts/lib/validate/check-skill-script-paths.mjs +47 -28
- package/scripts/lib/validate/check-unwired-features.mjs +0 -2
- package/scripts/lib/validate/check-validator-registration.mjs +10 -4
- package/scripts/lib/validate/enumerate-repo-files.mjs +317 -0
- package/scripts/lib/vault-backfill/template.mjs +63 -6
- package/scripts/lib/vault-mirror/process.mjs +165 -42
- package/scripts/lib/vault-mirror/telemetry.mjs +2 -2
- package/scripts/lib/vault-status/narrative-mirror.mjs +127 -18
- package/scripts/lib/wave-executor/dispatch-common.mjs +164 -0
- package/scripts/lib/wave-executor/foreign-dispatch.mjs +7 -142
- package/scripts/lib/wave-executor/remote-dispatch.mjs +5 -7
- package/scripts/lib/wave-resource-gate.mjs +8 -2
- package/scripts/lib/wave-sizing.mjs +4 -1
- package/scripts/lib/wave-transcript-tail.mjs +118 -4
- package/scripts/materialize-wave-scope.mjs +12 -5
- package/scripts/memory-propose.mjs +19 -5
- package/scripts/migrate-cold-start-seed.mjs +4 -1
- package/scripts/parse-config.mjs +60 -3
- package/scripts/release.mjs +337 -29
- package/scripts/repair-invalid-sessions.mjs +3 -3
- package/scripts/run-quality-gate.mjs +128 -11
- package/scripts/sweep-expired-learnings.mjs +90 -0
- package/scripts/sync-vault-schema.mjs +3 -1
- package/scripts/telemetry.mjs +2 -2
- package/scripts/validate-plugin.mjs +161 -0
- package/scripts/validate-wave-scope.mjs +28 -8
- package/scripts/wave-scope-binding.mjs +215 -0
- package/skills/_shared/instruction-file-resolution.md +10 -0
- package/skills/_shared/parallel-aware-preamble.md +1 -0
- package/skills/_shared/platform-tools.md +1 -1
- package/skills/_shared/state-ownership.md +1 -1
- package/skills/architecture/SKILL.md +7 -5
- package/skills/{domain-model/SKILL.md → architecture/references/domain-model.md} +9 -9
- package/skills/autopilot/SKILL.md +4 -18
- package/skills/claude-md-drift-check/SKILL.md +5 -1
- package/skills/claude-md-drift-check/checker.mjs +62 -2
- package/skills/convergence-monitoring/SIGNALS.md +55 -0
- package/skills/discovery/probes/vault-staleness.mjs +37 -13
- package/skills/discovery/probes-arch.md +20 -18
- package/skills/dispatcher/SKILL.md +3 -2
- package/skills/evolve/SKILL.md +65 -26
- package/skills/frontmatter-guard/SKILL.md +11 -5
- package/skills/npm-publish/SKILL.md +1 -1
- package/skills/reconcile/SKILL.md +33 -0
- package/skills/remote-offload/SKILL.md +1 -1
- package/skills/session-end/SKILL.md +18 -905
- package/skills/session-end/phase-3-6-tail.md +10 -3
- package/skills/session-end/plan-verification.md +221 -155
- package/skills/session-end/references/phase-2-quality-gate.md +93 -0
- package/skills/session-end/references/phase-3-documentation-updates.md +229 -0
- package/skills/session-end/references/phase-4a-worktree-cleanup.md +120 -0
- package/skills/session-end/references/phase-4b-worktree-orphan-sweep.md +58 -0
- package/skills/session-end/references/phase-5-issue-cleanup.md +104 -0
- package/skills/session-end/references/session-summary-template.md +62 -0
- package/skills/session-plan/SKILL.md +49 -0
- package/skills/session-start/SKILL.md +22 -904
- package/skills/session-start/phase-8-5-express-path.md +1 -1
- package/skills/session-start/references/phase-1-1-dispatcher-autonomy-capture.md +55 -0
- package/skills/session-start/references/phase-1-2-session-lock.md +140 -0
- package/skills/session-start/references/phase-1-5-session-continuity.md +254 -0
- package/skills/session-start/references/phase-1-7-vault-status-board.md +53 -0
- package/skills/session-start/references/phase-2-7-portfolio-snapshot.md +75 -0
- package/skills/session-start/references/phase-4-ssot-environment-check.md +155 -0
- package/skills/session-start/references/phase-6-5-forced-reads.md +75 -0
- package/skills/session-start/references/phase-6-6-project-intelligence.md +81 -0
- package/skills/session-start/references/phase-6-7-memory-banner-telemetry-consent.md +103 -0
- package/skills/vault-sync/validator.mjs +21 -27
- package/skills/wave-executor/SKILL.md +15 -1
- package/skills/wave-executor/references/wave-loop-dispatch.md +612 -0
- package/skills/wave-executor/references/wave-loop-review.md +570 -0
- package/skills/wave-executor/references/wave-loop-scope-manifest.md +162 -0
- package/skills/wave-executor/wave-loop.md +14 -1309
- package/templates/_shared/journey-manifest.md +10 -6
- package/.cursor/commands/autopilot-multi.md +0 -14
- package/.cursor/commands/contract-version-bump.md +0 -14
- package/.cursor/commands/journey-audit.md +0 -14
- package/.cursor/skills/contract-version-bump/SKILL.md +0 -12
- package/.cursor/skills/daily/SKILL.md +0 -12
- package/.cursor/skills/domain-model/SKILL.md +0 -13
- package/.cursor/skills/journey-audit/SKILL.md +0 -13
- package/.cursor/skills/skill-creator/SKILL.md +0 -13
- package/.cursor/skills/ubiquitous-language/SKILL.md +0 -13
- package/commands/autopilot-multi.md +0 -74
- package/commands/contract-version-bump.md +0 -28
- package/commands/journey-audit.md +0 -43
- package/pi/prompts/autopilot-multi.md +0 -12
- package/pi/prompts/contract-version-bump.md +0 -12
- package/pi/prompts/journey-audit.md +0 -12
- package/scripts/autopilot-multi.mjs +0 -885
- package/scripts/backfill-learnings-expires.mjs +0 -196
- package/scripts/backfill-learnings.mjs +0 -203
- package/scripts/fleet-instruction-scan.mjs +0 -141
- package/scripts/lib/autopilot/dep-graph.mjs +0 -417
- package/scripts/lib/autopilot/multi-killswitch.mjs +0 -184
- package/scripts/lib/webhook-url.mjs +0 -105
- package/scripts/lifecycle-sim-v6.mjs +0 -347
- package/scripts/migrate-learnings-jsonl.mjs +0 -189
- package/scripts/migrate-subagents-jsonl.mjs +0 -196
- package/scripts/upload-social-preview.mjs +0 -316
- package/skills/_shared/model-selection.md +0 -64
- package/skills/contract-version-bump/SKILL.md +0 -219
- package/skills/daily/SKILL.md +0 -222
- package/skills/daily/generate.sh +0 -92
- package/skills/daily/templates/daily.md.tpl +0 -36
- package/skills/journey-audit/SKILL.md +0 -270
- package/skills/skill-creator/SKILL.md +0 -168
- package/skills/ubiquitous-language/SKILL.md +0 -97
- package/skills/vault-sync/package-lock.json +0 -40
- /package/skills/{domain-model → architecture/references}/ADR-FORMAT.md +0 -0
- /package/skills/{domain-model → architecture/references}/CONTEXT-FORMAT.md +0 -0
|
@@ -1,347 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* scripts/lifecycle-sim-v6.mjs — Lifecycle simulation v6
|
|
4
|
-
*
|
|
5
|
-
* Issue #86 — measure the interaction between SO's enforcement layer and the
|
|
6
|
-
* downstream Claude Code agent layer. Real telemetry (GL#84) is still blocked,
|
|
7
|
-
* so v6 falls back to a discrete-event simulation parameterised from
|
|
8
|
-
* directional intuition codified in the issue and the existing v3.2 docs.
|
|
9
|
-
*
|
|
10
|
-
* The sim does NOT claim statistical authority. It produces a directional
|
|
11
|
-
* comparison between `enforcement: warn` and `enforcement: strict` over N
|
|
12
|
-
* synthetic sessions with a fixed RNG seed. The output is meant to seed a
|
|
13
|
-
* follow-up real-run experiment, not to drive a policy flip on its own.
|
|
14
|
-
*
|
|
15
|
-
* Usage:
|
|
16
|
-
* node scripts/lifecycle-sim-v6.mjs [--enforcement warn|strict|both]
|
|
17
|
-
* [--sessions N] [--seed K]
|
|
18
|
-
* [--json]
|
|
19
|
-
*
|
|
20
|
-
* Defaults: --enforcement both --sessions 100 --seed 42
|
|
21
|
-
*
|
|
22
|
-
* Exit codes (per cli-design.md):
|
|
23
|
-
* 0 — sim ran successfully
|
|
24
|
-
* 1 — bad CLI args
|
|
25
|
-
* 2 — internal error
|
|
26
|
-
*/
|
|
27
|
-
|
|
28
|
-
import { parseArgs } from 'node:util';
|
|
29
|
-
|
|
30
|
-
// -----------------------------------------------------------------------------
|
|
31
|
-
// Deterministic RNG — mulberry32 (small, well-known, seedable)
|
|
32
|
-
// -----------------------------------------------------------------------------
|
|
33
|
-
function mulberry32(seed) {
|
|
34
|
-
let a = seed >>> 0;
|
|
35
|
-
return function rng() {
|
|
36
|
-
a = (a + 0x6d2b79f5) >>> 0;
|
|
37
|
-
let t = a;
|
|
38
|
-
t = Math.imul(t ^ (t >>> 15), t | 1);
|
|
39
|
-
t ^= t + Math.imul(t ^ (t >>> 7), t | 61);
|
|
40
|
-
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
/** Box-Muller normal sample, clamped to [min, max], rounded to int. */
|
|
45
|
-
function gaussianInt(rng, mean, stddev, min, max) {
|
|
46
|
-
// two uniforms → standard normal
|
|
47
|
-
let u1 = rng();
|
|
48
|
-
if (u1 < 1e-9) u1 = 1e-9;
|
|
49
|
-
const u2 = rng();
|
|
50
|
-
const z = Math.sqrt(-2 * Math.log(u1)) * Math.cos(2 * Math.PI * u2);
|
|
51
|
-
const value = Math.round(mean + z * stddev);
|
|
52
|
-
return Math.max(min, Math.min(max, value));
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
// -----------------------------------------------------------------------------
|
|
56
|
-
// Simulation parameters (kept as an explicit object so the report can echo them)
|
|
57
|
-
// -----------------------------------------------------------------------------
|
|
58
|
-
const PARAMS = Object.freeze({
|
|
59
|
-
// Per-session planned issues sampled from a clipped Gaussian.
|
|
60
|
-
plannedMean: 12,
|
|
61
|
-
plannedStddev: 5,
|
|
62
|
-
plannedMin: 5,
|
|
63
|
-
plannedMax: 25,
|
|
64
|
-
|
|
65
|
-
// Base completion rate (fraction of planned issues an idealised session
|
|
66
|
-
// completes when nothing is blocked). Per-session jitter applied on top.
|
|
67
|
-
baseCompletion: 0.78,
|
|
68
|
-
completionJitter: 0.08, // ±8 percentage points
|
|
69
|
-
|
|
70
|
-
// Probability that a planned issue triggers a scope-friction event during
|
|
71
|
-
// its first edit attempt. Independent of enforcement level — the friction
|
|
72
|
-
// exists regardless; what differs is what the harness *does* about it.
|
|
73
|
-
scopeFrictionProb: 0.12,
|
|
74
|
-
|
|
75
|
-
// Carryover into next session: fraction of unfinished issues that roll over.
|
|
76
|
-
// 1.0 = everything carries over; lower values model dropped scope.
|
|
77
|
-
carryoverFraction: 1.0,
|
|
78
|
-
|
|
79
|
-
// Stagnation threshold: a session is "stagnant" when carryover_ratio > this.
|
|
80
|
-
stagnationCarryoverRatio: 0.5,
|
|
81
|
-
|
|
82
|
-
// Stagnation streak length triggering the "stagnant streak" bucket.
|
|
83
|
-
stagnationStreakLength: 3,
|
|
84
|
-
|
|
85
|
-
// Enforcement-mode-specific multipliers — see the report for the rationale
|
|
86
|
-
// and limits of these values.
|
|
87
|
-
modes: {
|
|
88
|
-
warn: {
|
|
89
|
-
// warn: friction is logged but the agent is allowed to retry, often
|
|
90
|
-
// converging on a partial solution. Friction events drag completion
|
|
91
|
-
// down moderately (each event costs ~1.5% completion) and a small
|
|
92
|
-
// fraction of friction events become permanent blocks.
|
|
93
|
-
frictionCompletionPenalty: 0.015,
|
|
94
|
-
blockConversionRate: 0.18,
|
|
95
|
-
},
|
|
96
|
-
strict: {
|
|
97
|
-
// strict: every scope-violation hard-aborts the agent's current step.
|
|
98
|
-
// The agent has to re-plan. This hurts completion more per event but
|
|
99
|
-
// converts fewer events into permanent blocks because the agent never
|
|
100
|
-
// produces partial-but-wrong output.
|
|
101
|
-
frictionCompletionPenalty: 0.025,
|
|
102
|
-
blockConversionRate: 0.08,
|
|
103
|
-
},
|
|
104
|
-
},
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
// -----------------------------------------------------------------------------
|
|
108
|
-
// Single-mode simulation
|
|
109
|
-
// -----------------------------------------------------------------------------
|
|
110
|
-
function simulateMode({ mode, sessions, seed }) {
|
|
111
|
-
if (!PARAMS.modes[mode]) {
|
|
112
|
-
throw new Error(`Unknown enforcement mode: ${mode}`);
|
|
113
|
-
}
|
|
114
|
-
const { frictionCompletionPenalty, blockConversionRate } = PARAMS.modes[mode];
|
|
115
|
-
|
|
116
|
-
// Seed each mode independently of the other so order doesn't matter, but
|
|
117
|
-
// derive the seed from the user seed so re-runs with the same --seed are
|
|
118
|
-
// identical.
|
|
119
|
-
const modeSeed = seed ^ (mode === 'warn' ? 0xa5a5a5a5 : 0x5a5a5a5a);
|
|
120
|
-
const rng = mulberry32(modeSeed);
|
|
121
|
-
|
|
122
|
-
let carryoverIssues = 0;
|
|
123
|
-
let stagnationStreak = 0;
|
|
124
|
-
let longestStagnationStreak = 0;
|
|
125
|
-
let stagnantSessionCount = 0;
|
|
126
|
-
let totalPlanned = 0;
|
|
127
|
-
let totalCompleted = 0;
|
|
128
|
-
let totalBlocked = 0;
|
|
129
|
-
const carryoverRatios = [];
|
|
130
|
-
|
|
131
|
-
for (let i = 0; i < sessions; i++) {
|
|
132
|
-
const fresh = gaussianInt(
|
|
133
|
-
rng,
|
|
134
|
-
PARAMS.plannedMean,
|
|
135
|
-
PARAMS.plannedStddev,
|
|
136
|
-
PARAMS.plannedMin,
|
|
137
|
-
PARAMS.plannedMax,
|
|
138
|
-
);
|
|
139
|
-
const planned = fresh + carryoverIssues;
|
|
140
|
-
totalPlanned += planned;
|
|
141
|
-
|
|
142
|
-
// Friction events: one Bernoulli draw per planned issue.
|
|
143
|
-
let frictionEvents = 0;
|
|
144
|
-
let permanentBlocks = 0;
|
|
145
|
-
for (let j = 0; j < planned; j++) {
|
|
146
|
-
if (rng() < PARAMS.scopeFrictionProb) {
|
|
147
|
-
frictionEvents += 1;
|
|
148
|
-
if (rng() < blockConversionRate) {
|
|
149
|
-
permanentBlocks += 1;
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
// Effective completion rate — base, jitter, friction penalty.
|
|
155
|
-
const jitter = (rng() * 2 - 1) * PARAMS.completionJitter;
|
|
156
|
-
let completionRate = PARAMS.baseCompletion + jitter;
|
|
157
|
-
completionRate -= frictionEvents * frictionCompletionPenalty;
|
|
158
|
-
completionRate = Math.max(0, Math.min(1, completionRate));
|
|
159
|
-
|
|
160
|
-
const completable = planned - permanentBlocks;
|
|
161
|
-
const completed = Math.min(completable, Math.round(completable * completionRate));
|
|
162
|
-
totalCompleted += completed;
|
|
163
|
-
totalBlocked += permanentBlocks;
|
|
164
|
-
|
|
165
|
-
const unfinished = planned - completed;
|
|
166
|
-
const carryoverRatio = planned === 0 ? 0 : unfinished / planned;
|
|
167
|
-
carryoverRatios.push(carryoverRatio);
|
|
168
|
-
|
|
169
|
-
if (carryoverRatio > PARAMS.stagnationCarryoverRatio) {
|
|
170
|
-
stagnationStreak += 1;
|
|
171
|
-
if (stagnationStreak >= PARAMS.stagnationStreakLength) {
|
|
172
|
-
stagnantSessionCount += 1;
|
|
173
|
-
}
|
|
174
|
-
if (stagnationStreak > longestStagnationStreak) {
|
|
175
|
-
longestStagnationStreak = stagnationStreak;
|
|
176
|
-
}
|
|
177
|
-
} else {
|
|
178
|
-
stagnationStreak = 0;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
carryoverIssues = Math.round(unfinished * PARAMS.carryoverFraction);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
carryoverRatios.sort((a, b) => a - b);
|
|
185
|
-
const meanCarryover =
|
|
186
|
-
carryoverRatios.reduce((s, v) => s + v, 0) / carryoverRatios.length;
|
|
187
|
-
const p95Index = Math.min(
|
|
188
|
-
carryoverRatios.length - 1,
|
|
189
|
-
Math.floor(0.95 * carryoverRatios.length),
|
|
190
|
-
);
|
|
191
|
-
const p95Carryover = carryoverRatios[p95Index];
|
|
192
|
-
|
|
193
|
-
return {
|
|
194
|
-
mode,
|
|
195
|
-
sessions,
|
|
196
|
-
totals: {
|
|
197
|
-
planned: totalPlanned,
|
|
198
|
-
completed: totalCompleted,
|
|
199
|
-
blocked: totalBlocked,
|
|
200
|
-
},
|
|
201
|
-
completionRatio: totalPlanned === 0 ? 0 : totalCompleted / totalPlanned,
|
|
202
|
-
carryover: {
|
|
203
|
-
meanRatio: meanCarryover,
|
|
204
|
-
p95Ratio: p95Carryover,
|
|
205
|
-
},
|
|
206
|
-
stagnation: {
|
|
207
|
-
streakThreshold: PARAMS.stagnationStreakLength,
|
|
208
|
-
carryoverThreshold: PARAMS.stagnationCarryoverRatio,
|
|
209
|
-
stagnantSessions: stagnantSessionCount,
|
|
210
|
-
stagnationRate: stagnantSessionCount / sessions,
|
|
211
|
-
longestStreak: longestStagnationStreak,
|
|
212
|
-
},
|
|
213
|
-
};
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
// -----------------------------------------------------------------------------
|
|
217
|
-
// CLI
|
|
218
|
-
// -----------------------------------------------------------------------------
|
|
219
|
-
function parseCli(argv) {
|
|
220
|
-
const { values } = parseArgs({
|
|
221
|
-
args: argv,
|
|
222
|
-
options: {
|
|
223
|
-
enforcement: { type: 'string', default: 'both' },
|
|
224
|
-
sessions: { type: 'string', default: '100' },
|
|
225
|
-
seed: { type: 'string', default: '42' },
|
|
226
|
-
json: { type: 'boolean', default: false },
|
|
227
|
-
help: { type: 'boolean', short: 'h', default: false },
|
|
228
|
-
},
|
|
229
|
-
allowPositionals: false,
|
|
230
|
-
});
|
|
231
|
-
return values;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
const HELP = `lifecycle-sim-v6 — issue #86 directional simulation
|
|
235
|
-
|
|
236
|
-
Usage:
|
|
237
|
-
node scripts/lifecycle-sim-v6.mjs [--enforcement warn|strict|both]
|
|
238
|
-
[--sessions N] [--seed K]
|
|
239
|
-
[--json]
|
|
240
|
-
|
|
241
|
-
Options:
|
|
242
|
-
--enforcement warn | strict | both (default: both)
|
|
243
|
-
--sessions N number of simulated sessions per mode (default: 100)
|
|
244
|
-
--seed K RNG seed for reproducibility (default: 42)
|
|
245
|
-
--json emit JSON to stdout (default: human text)
|
|
246
|
-
-h, --help show this help
|
|
247
|
-
|
|
248
|
-
Exit codes:
|
|
249
|
-
0 success
|
|
250
|
-
1 bad arguments
|
|
251
|
-
2 internal error
|
|
252
|
-
`;
|
|
253
|
-
|
|
254
|
-
function humanReport(results, opts) {
|
|
255
|
-
const lines = [];
|
|
256
|
-
lines.push('lifecycle-sim-v6 — directional results (NOT statistical)');
|
|
257
|
-
lines.push(`seed=${opts.seed} sessions=${opts.sessions} per mode`);
|
|
258
|
-
lines.push('');
|
|
259
|
-
for (const r of results) {
|
|
260
|
-
lines.push(`mode: ${r.mode}`);
|
|
261
|
-
lines.push(` planned issues: ${r.totals.planned}`);
|
|
262
|
-
lines.push(` completed: ${r.totals.completed}`);
|
|
263
|
-
lines.push(` permanently blocked: ${r.totals.blocked}`);
|
|
264
|
-
lines.push(` completion ratio: ${r.completionRatio.toFixed(3)}`);
|
|
265
|
-
lines.push(` mean carryover ratio: ${r.carryover.meanRatio.toFixed(3)}`);
|
|
266
|
-
lines.push(` p95 carryover ratio: ${r.carryover.p95Ratio.toFixed(3)}`);
|
|
267
|
-
lines.push(
|
|
268
|
-
` stagnant sessions: ${r.stagnation.stagnantSessions} ` +
|
|
269
|
-
`(rate ${r.stagnation.stagnationRate.toFixed(3)})`,
|
|
270
|
-
);
|
|
271
|
-
lines.push(` longest streak: ${r.stagnation.longestStreak}`);
|
|
272
|
-
lines.push('');
|
|
273
|
-
}
|
|
274
|
-
return lines.join('\n');
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
function main() {
|
|
278
|
-
let opts;
|
|
279
|
-
try {
|
|
280
|
-
opts = parseCli(process.argv.slice(2));
|
|
281
|
-
} catch (err) {
|
|
282
|
-
process.stderr.write(`bad args: ${err.message}\n`);
|
|
283
|
-
return 1;
|
|
284
|
-
}
|
|
285
|
-
if (opts.help) {
|
|
286
|
-
process.stdout.write(HELP);
|
|
287
|
-
return 0;
|
|
288
|
-
}
|
|
289
|
-
const sessions = Number.parseInt(opts.sessions, 10);
|
|
290
|
-
const seed = Number.parseInt(opts.seed, 10);
|
|
291
|
-
if (!Number.isFinite(sessions) || sessions <= 0) {
|
|
292
|
-
process.stderr.write('--sessions must be a positive integer\n');
|
|
293
|
-
return 1;
|
|
294
|
-
}
|
|
295
|
-
if (!Number.isFinite(seed)) {
|
|
296
|
-
process.stderr.write('--seed must be an integer\n');
|
|
297
|
-
return 1;
|
|
298
|
-
}
|
|
299
|
-
|
|
300
|
-
let modes;
|
|
301
|
-
if (opts.enforcement === 'both') {
|
|
302
|
-
modes = ['warn', 'strict'];
|
|
303
|
-
} else if (opts.enforcement === 'warn' || opts.enforcement === 'strict') {
|
|
304
|
-
modes = [opts.enforcement];
|
|
305
|
-
} else {
|
|
306
|
-
process.stderr.write(`--enforcement must be warn|strict|both\n`);
|
|
307
|
-
return 1;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
let results;
|
|
311
|
-
try {
|
|
312
|
-
results = modes.map((mode) => simulateMode({ mode, sessions, seed }));
|
|
313
|
-
} catch (err) {
|
|
314
|
-
process.stderr.write(`sim error: ${err.message}\n`);
|
|
315
|
-
return 2;
|
|
316
|
-
}
|
|
317
|
-
|
|
318
|
-
if (opts.json) {
|
|
319
|
-
const out = {
|
|
320
|
-
seed,
|
|
321
|
-
sessions,
|
|
322
|
-
params: PARAMS,
|
|
323
|
-
results,
|
|
324
|
-
};
|
|
325
|
-
process.stdout.write(`${JSON.stringify(out, null, 2)}\n`);
|
|
326
|
-
} else {
|
|
327
|
-
process.stdout.write(humanReport(results, { sessions, seed }));
|
|
328
|
-
}
|
|
329
|
-
return 0;
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
// Entry point — only run when invoked as a script (not when imported by tests).
|
|
333
|
-
const isDirectRun = (() => {
|
|
334
|
-
try {
|
|
335
|
-
const argv1 = process.argv[1] ? new URL(`file://${process.argv[1]}`).href : '';
|
|
336
|
-
return import.meta.url === argv1;
|
|
337
|
-
} catch {
|
|
338
|
-
return false;
|
|
339
|
-
}
|
|
340
|
-
})();
|
|
341
|
-
|
|
342
|
-
if (isDirectRun) {
|
|
343
|
-
process.exit(main());
|
|
344
|
-
}
|
|
345
|
-
|
|
346
|
-
// Named exports for tests.
|
|
347
|
-
export { mulberry32, gaussianInt, simulateMode, PARAMS, parseCli, main };
|
|
@@ -1,189 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* migrate-learnings-jsonl.mjs — one-shot migration helper for learnings.jsonl.
|
|
4
|
-
*
|
|
5
|
-
* Fixes schema drift described in issue #303:
|
|
6
|
-
* - description | recommendation → insight
|
|
7
|
-
* - missing id → crypto.randomUUID()
|
|
8
|
-
* - missing schema_version → 1
|
|
9
|
-
*
|
|
10
|
-
* Usage:
|
|
11
|
-
* node scripts/migrate-learnings-jsonl.mjs [--source <path>] [--dry-run] [--apply]
|
|
12
|
-
*
|
|
13
|
-
* Flags:
|
|
14
|
-
* --source <path> Path to learnings.jsonl (default: .orchestrator/metrics/learnings.jsonl)
|
|
15
|
-
* --dry-run Preview changes without writing (DEFAULT)
|
|
16
|
-
* --apply Write migrated records back to the file
|
|
17
|
-
*
|
|
18
|
-
* Exit codes:
|
|
19
|
-
* 0 Success (including no-op if everything is already canonical)
|
|
20
|
-
* 1 Input/validation error
|
|
21
|
-
* 2 I/O error
|
|
22
|
-
*
|
|
23
|
-
* Idempotent: running --apply a second time produces no further changes.
|
|
24
|
-
*
|
|
25
|
-
* Output (stdout — one JSON object per line):
|
|
26
|
-
* {"action":"migrated"|"already-canonical","id":"...","changes":[...]}
|
|
27
|
-
*
|
|
28
|
-
* Summary line (stderr):
|
|
29
|
-
* migrate-learnings: N migrated, M already-canonical, K failed-validation [dry-run|applied]
|
|
30
|
-
*/
|
|
31
|
-
|
|
32
|
-
import { existsSync, readFileSync, writeFileSync, renameSync } from 'node:fs';
|
|
33
|
-
import { mkdirSync } from 'node:fs';
|
|
34
|
-
import { dirname } from 'node:path';
|
|
35
|
-
import {
|
|
36
|
-
migrateLegacyLearning,
|
|
37
|
-
validateLearning,
|
|
38
|
-
ValidationError,
|
|
39
|
-
} from './lib/learnings.mjs';
|
|
40
|
-
|
|
41
|
-
// ---------------------------------------------------------------------------
|
|
42
|
-
// Arg parsing
|
|
43
|
-
// ---------------------------------------------------------------------------
|
|
44
|
-
|
|
45
|
-
const args = process.argv.slice(2);
|
|
46
|
-
const helpFlag = args.includes('--help') || args.includes('-h');
|
|
47
|
-
if (helpFlag) {
|
|
48
|
-
process.stdout.write(`Usage: migrate-learnings-jsonl.mjs [--source <path>] [--dry-run] [--apply]
|
|
49
|
-
|
|
50
|
-
Options:
|
|
51
|
-
--source <path> Path to learnings.jsonl (default: .orchestrator/metrics/learnings.jsonl)
|
|
52
|
-
--dry-run Preview changes without writing (DEFAULT)
|
|
53
|
-
--apply Write migrated records back to the file
|
|
54
|
-
|
|
55
|
-
Exit codes: 0 success 1 input error 2 I/O error
|
|
56
|
-
`);
|
|
57
|
-
process.exit(0);
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
const applyFlag = args.includes('--apply');
|
|
61
|
-
const dryRun = !applyFlag; // default is --dry-run
|
|
62
|
-
|
|
63
|
-
const sourceIdx = args.indexOf('--source');
|
|
64
|
-
const sourcePath =
|
|
65
|
-
sourceIdx !== -1 && args[sourceIdx + 1]
|
|
66
|
-
? args[sourceIdx + 1]
|
|
67
|
-
: '.orchestrator/metrics/learnings.jsonl';
|
|
68
|
-
|
|
69
|
-
// ---------------------------------------------------------------------------
|
|
70
|
-
// Load
|
|
71
|
-
// ---------------------------------------------------------------------------
|
|
72
|
-
|
|
73
|
-
if (!existsSync(sourcePath)) {
|
|
74
|
-
process.stderr.write(`migrate-learnings: source file not found: ${sourcePath}\n`);
|
|
75
|
-
process.exit(1);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
let raw;
|
|
79
|
-
try {
|
|
80
|
-
raw = readFileSync(sourcePath, 'utf8');
|
|
81
|
-
} catch (err) {
|
|
82
|
-
process.stderr.write(`migrate-learnings: failed to read ${sourcePath}: ${err.message}\n`);
|
|
83
|
-
process.exit(2);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const lines = raw.split('\n').filter((l) => l.trim().length > 0);
|
|
87
|
-
|
|
88
|
-
// ---------------------------------------------------------------------------
|
|
89
|
-
// Migrate
|
|
90
|
-
// ---------------------------------------------------------------------------
|
|
91
|
-
|
|
92
|
-
let countMigrated = 0;
|
|
93
|
-
let countCanonical = 0;
|
|
94
|
-
let countFailedValidation = 0;
|
|
95
|
-
let countMalformed = 0;
|
|
96
|
-
|
|
97
|
-
const outputLines = [];
|
|
98
|
-
|
|
99
|
-
for (const line of lines) {
|
|
100
|
-
let parsed;
|
|
101
|
-
try {
|
|
102
|
-
parsed = JSON.parse(line);
|
|
103
|
-
} catch {
|
|
104
|
-
countMalformed++;
|
|
105
|
-
process.stderr.write(`migrate-learnings: WARN skipping malformed JSON line: ${line.slice(0, 80)}\n`);
|
|
106
|
-
// Preserve the original malformed line as-is (do not discard data)
|
|
107
|
-
outputLines.push(line);
|
|
108
|
-
continue;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
// Detect which fields need migration
|
|
112
|
-
const changes = [];
|
|
113
|
-
if (!parsed.id) changes.push('id:added');
|
|
114
|
-
if (!parsed.insight) {
|
|
115
|
-
if (typeof parsed.description === 'string') changes.push('description→insight');
|
|
116
|
-
else if (typeof parsed.recommendation === 'string') changes.push('recommendation→insight');
|
|
117
|
-
}
|
|
118
|
-
if (parsed.schema_version === undefined || parsed.schema_version === null) {
|
|
119
|
-
changes.push('schema_version:1');
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const migrated = migrateLegacyLearning(parsed);
|
|
123
|
-
|
|
124
|
-
// Validate after migration — records that still fail after migration are
|
|
125
|
-
// preserved in-place (no data loss) but flagged in the summary.
|
|
126
|
-
let validated;
|
|
127
|
-
try {
|
|
128
|
-
// Stamp schema_version before validation so appendLearning-style stamping
|
|
129
|
-
// is mimicked (migrateLegacyLearning already does this, but be explicit).
|
|
130
|
-
validated = validateLearning({
|
|
131
|
-
...migrated,
|
|
132
|
-
schema_version: migrated.schema_version ?? 1,
|
|
133
|
-
});
|
|
134
|
-
} catch (err) {
|
|
135
|
-
countFailedValidation++;
|
|
136
|
-
const reason = err instanceof ValidationError ? err.message : String(err);
|
|
137
|
-
process.stderr.write(
|
|
138
|
-
`migrate-learnings: WARN record id=${parsed.id ?? '<no id>'} still fails validation after migration: ${reason}\n`
|
|
139
|
-
);
|
|
140
|
-
// Preserve original line — do not destroy unmigratable records
|
|
141
|
-
outputLines.push(line);
|
|
142
|
-
process.stdout.write(
|
|
143
|
-
JSON.stringify({ action: 'failed-validation', id: parsed.id ?? null, reason, changes }) + '\n'
|
|
144
|
-
);
|
|
145
|
-
continue;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
if (changes.length === 0) {
|
|
149
|
-
countCanonical++;
|
|
150
|
-
outputLines.push(JSON.stringify(validated));
|
|
151
|
-
process.stdout.write(JSON.stringify({ action: 'already-canonical', id: validated.id }) + '\n');
|
|
152
|
-
} else {
|
|
153
|
-
countMigrated++;
|
|
154
|
-
outputLines.push(JSON.stringify(validated));
|
|
155
|
-
process.stdout.write(JSON.stringify({ action: 'migrated', id: validated.id, changes }) + '\n');
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// ---------------------------------------------------------------------------
|
|
160
|
-
// Write (--apply only)
|
|
161
|
-
// ---------------------------------------------------------------------------
|
|
162
|
-
|
|
163
|
-
const modeLabel = dryRun ? 'dry-run' : 'applied';
|
|
164
|
-
|
|
165
|
-
if (!dryRun) {
|
|
166
|
-
const body = outputLines.join('\n') + '\n';
|
|
167
|
-
const tmp = `${sourcePath}.migrate-tmp-${process.pid}-${Date.now()}`;
|
|
168
|
-
try {
|
|
169
|
-
mkdirSync(dirname(sourcePath), { recursive: true });
|
|
170
|
-
writeFileSync(tmp, body, 'utf8');
|
|
171
|
-
renameSync(tmp, sourcePath);
|
|
172
|
-
} catch (err) {
|
|
173
|
-
process.stderr.write(`migrate-learnings: ERROR failed to write ${sourcePath}: ${err.message}\n`);
|
|
174
|
-
// Attempt cleanup of tmp
|
|
175
|
-
try { if (existsSync(tmp)) renameSync(tmp, tmp + '.failed'); } catch { /* ignore */ }
|
|
176
|
-
process.exit(2);
|
|
177
|
-
}
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
// ---------------------------------------------------------------------------
|
|
181
|
-
// Summary
|
|
182
|
-
// ---------------------------------------------------------------------------
|
|
183
|
-
|
|
184
|
-
process.stderr.write(
|
|
185
|
-
`migrate-learnings: ${countMigrated} migrated, ${countCanonical} already-canonical, ` +
|
|
186
|
-
`${countFailedValidation} failed-validation, ${countMalformed} malformed [${modeLabel}]\n`
|
|
187
|
-
);
|
|
188
|
-
|
|
189
|
-
process.exit(countFailedValidation > 0 ? 1 : 0);
|