@rulvar/plan 1.33.0 → 1.35.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.
Files changed (2) hide show
  1. package/dist/index.js +47 -0
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -923,6 +923,49 @@ function cascadeOf(plan, root) {
923
923
  }
924
924
  //#endregion
925
925
  //#region src/guards.ts
926
+ /**
927
+ * RevisionGuards, the oscillation detector, and hysteresis (M7-T06).
928
+ *
929
+ * Guide: https://docs.rulvar.com/guide/adaptive-orchestration
930
+ * (DEF-8; DEF-2/DEF-5 interactions). The guards are non-HITL and
931
+ * terminating: a human in the loop is NEVER required for a run to end.
932
+ * Every guard verdict is a decision entry written strictly BEFORE its
933
+ * effects; verdict state is a pure fold of those entries, so replay
934
+ * re-derives the same freezes with zero live calls.
935
+ *
936
+ * Three detectors:
937
+ *
938
+ * - droppedRevisionStreak: `effectiveDroppedStreak` (the hashed counter
939
+ * plus trailing bad_base entries) reaching `droppedRevisionLimit`
940
+ * (default 3) fires the configured terminating fallback
941
+ * (reject-revision -> finish-with-partial -> fail-run; default
942
+ * finish-with-partial).
943
+ * - Oscillation: keyed on approachSigCoarse ACROSS LogicalTaskId
944
+ * boundaries (a content-identical rebirth under a fresh lineage root
945
+ * is still flagged): a re-add after a severing cancel of the same
946
+ * coarse signature counts one oscillation; at the per-key limit
947
+ * further re-adds FREEZE (the per-SpawnKey osc_guard of DEF-5 lands
948
+ * in M7-T07 and keys the DedupIndex instead).
949
+ * - Stall replans: hard-bounded per run; the streak already excludes
950
+ * transient and environment classes.
951
+ *
952
+ * Hysteresis on almost-done nodes is structural: park and cancel against
953
+ * RUNNING nodes only ever land as boundary flags (requestOnly), so a
954
+ * nearly-finished child is never killed mid-turn, and park/unpark churn
955
+ * feeds the oscillation counter.
956
+ */
957
+ /**
958
+ * Local mirror of the core numeric-intake idiom (v1.34.0 review P2-3):
959
+ * guard limits are compared with `<` and `!==`, and every comparison
960
+ * with NaN is false, so an unvalidated NaN limit made the dropped and
961
+ * oscillation guards trip immediately and the stall cap never trip.
962
+ */
963
+ function requireCount(value, site, min) {
964
+ if (typeof value !== "number" || !Number.isInteger(value) || value < min) throw new ConfigError(`${site} must be ${min === 1 ? "a positive integer" : "a nonnegative integer"}; got ${String(value)}`);
965
+ }
966
+ function requireGuardFraction(value, site) {
967
+ if (typeof value !== "number" || !Number.isFinite(value) || value <= 0 || value > 1) throw new ConfigError(`${site} must be a fraction in (0, 1]; got ${String(value)}`);
968
+ }
926
969
  /** Appendix A: osc_guard reject threshold per key (shared default). */
927
970
  const DEFAULT_MAX_OSCILLATIONS_PER_KEY = 2;
928
971
  /** The hard per-run stall replan bound. */
@@ -947,6 +990,10 @@ var RevisionGuards = class {
947
990
  frozen = /* @__PURE__ */ new Set();
948
991
  stallReplans = 0;
949
992
  constructor(options) {
993
+ if (options?.droppedRevisionLimit !== void 0) requireCount(options.droppedRevisionLimit, "RevisionGuards droppedRevisionLimit", 1);
994
+ if (options?.maxOscillationsPerKey !== void 0) requireCount(options.maxOscillationsPerKey, "RevisionGuards maxOscillationsPerKey", 1);
995
+ if (options?.stallReplanCap !== void 0) requireCount(options.stallReplanCap, "RevisionGuards stallReplanCap", 0);
996
+ if (options?.maxAbandonedNetUsdFraction !== void 0) requireGuardFraction(options.maxAbandonedNetUsdFraction, "RevisionGuards maxAbandonedNetUsdFraction");
950
997
  this.fallback = options?.fallback ?? "finish-with-partial";
951
998
  this.droppedRevisionLimit = options?.droppedRevisionLimit ?? 3;
952
999
  this.maxOscillationsPerKey = options?.maxOscillationsPerKey ?? 2;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rulvar/plan",
3
- "version": "1.33.0",
3
+ "version": "1.35.0",
4
4
  "description": "Rulvar adaptive orchestration extension: PlanRunner, RunLedger, escalation extensions, ModelLadder configuration.",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
@@ -22,13 +22,13 @@
22
22
  "access": "public"
23
23
  },
24
24
  "dependencies": {
25
- "@rulvar/core": "1.33.0"
25
+ "@rulvar/core": "1.35.0"
26
26
  },
27
27
  "devDependencies": {
28
28
  "@types/node": "^22.20.0",
29
29
  "tsdown": "^0.22.3",
30
30
  "typescript": "~6.0.3",
31
- "@rulvar/store-sqlite": "1.33.0"
31
+ "@rulvar/store-sqlite": "1.35.0"
32
32
  },
33
33
  "repository": {
34
34
  "type": "git",