loopforge 3.6.0 → 3.7.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/README.md +12 -9
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js +3 -8
- package/dist/audit.js.map +1 -1
- package/dist/canonical-state.d.ts +0 -2
- package/dist/canonical-state.d.ts.map +1 -1
- package/dist/canonical-state.js +0 -20
- package/dist/canonical-state.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +1 -9
- package/dist/cli.js.map +1 -1
- package/dist/cognitive-governance.d.ts +0 -1
- package/dist/cognitive-governance.d.ts.map +1 -1
- package/dist/cognitive-governance.js +1 -4
- package/dist/cognitive-governance.js.map +1 -1
- package/dist/committed-round.d.ts +41 -0
- package/dist/committed-round.d.ts.map +1 -1
- package/dist/committed-round.js +129 -2
- package/dist/committed-round.js.map +1 -1
- package/dist/enforcement-gate.d.ts.map +1 -1
- package/dist/enforcement-gate.js +120 -130
- package/dist/enforcement-gate.js.map +1 -1
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +2 -1
- package/dist/engine.js.map +1 -1
- package/dist/evidence-claims.d.ts.map +1 -1
- package/dist/evidence-claims.js +3 -9
- package/dist/evidence-claims.js.map +1 -1
- package/dist/evidence-provider.d.ts +6 -17
- package/dist/evidence-provider.d.ts.map +1 -1
- package/dist/evidence-provider.js +12 -92
- package/dist/evidence-provider.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/loop-compiler.d.ts +0 -6
- package/dist/loop-compiler.d.ts.map +1 -1
- package/dist/loop-compiler.js +43 -265
- package/dist/loop-compiler.js.map +1 -1
- package/dist/loop-store.d.ts +11 -16
- package/dist/loop-store.d.ts.map +1 -1
- package/dist/loop-store.js +21 -63
- package/dist/loop-store.js.map +1 -1
- package/dist/mcp/round-lifecycle.d.ts +8 -3
- package/dist/mcp/round-lifecycle.d.ts.map +1 -1
- package/dist/mcp/round-lifecycle.js +36 -18
- package/dist/mcp/round-lifecycle.js.map +1 -1
- package/dist/mcp/server.js +1 -1
- package/dist/mcp/session.d.ts +3 -2
- package/dist/mcp/session.d.ts.map +1 -1
- package/dist/mcp/session.js +3 -2
- package/dist/mcp/session.js.map +1 -1
- package/dist/mcp/tools.js +1 -1
- package/dist/mcp/tools.js.map +1 -1
- package/dist/policy.d.ts +0 -11
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +0 -4
- package/dist/policy.js.map +1 -1
- package/dist/prompt-assembler.d.ts +0 -3
- package/dist/prompt-assembler.d.ts.map +1 -1
- package/dist/prompt-assembler.js +5 -52
- package/dist/prompt-assembler.js.map +1 -1
- package/dist/protocol.d.ts +5 -24
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +0 -22
- package/dist/protocol.js.map +1 -1
- package/dist/round-coordinator.d.ts.map +1 -1
- package/dist/round-coordinator.js +2 -1
- package/dist/round-coordinator.js.map +1 -1
- package/dist/round-driver.d.ts +0 -3
- package/dist/round-driver.d.ts.map +1 -1
- package/dist/round-driver.js +1 -10
- package/dist/round-driver.js.map +1 -1
- package/dist/token-utils.d.ts +7 -3
- package/dist/token-utils.d.ts.map +1 -1
- package/dist/token-utils.js +18 -3
- package/dist/token-utils.js.map +1 -1
- package/dist/verification-gate.d.ts +19 -9
- package/dist/verification-gate.d.ts.map +1 -1
- package/dist/verification-gate.js +102 -87
- package/dist/verification-gate.js.map +1 -1
- package/loop_policy.json +0 -4
- package/package.json +2 -2
- package/skills/perception/SKILL.md +5 -3
package/dist/loop-compiler.js
CHANGED
|
@@ -7,7 +7,7 @@ import { createHash } from "node:crypto";
|
|
|
7
7
|
import { getPolicy } from "./policy.js";
|
|
8
8
|
import { AgentStatus, makeLoopCompileResponse, makeLoopHealth, makeLoopObjective, makeConstraintMeta, makeMilestoneSummary, makeRollingSummary, makeSubGoal, makeTaskAlignment, } from "./protocol.js";
|
|
9
9
|
import { createCanonicalLoopState, renderCanonicalStateMarkdown, } from "./canonical-state.js";
|
|
10
|
-
import {
|
|
10
|
+
import { decodeRound, entryLineage, entryCriteriaMet, entryCriteriaRemaining, entryActiveConstraints, entryRetractedConstraints, entryProgressEstimate, entryEmergedSubtasks, machineGitMotionSeries, mergedRoundsFromEntries, } from "./committed-round.js";
|
|
11
11
|
import { contractRoundEvaluations, deriveActiveRoundContract, } from "./round-contract.js";
|
|
12
12
|
import { assemblePromptArtifact } from "./prompt-assembler.js";
|
|
13
13
|
import { decidePromptLevel, } from "./prompt-policy.js";
|
|
@@ -17,15 +17,9 @@ function contextEntries(context) {
|
|
|
17
17
|
return [];
|
|
18
18
|
return context.results.filter((value) => value !== null && typeof value === "object" && !Array.isArray(value));
|
|
19
19
|
}
|
|
20
|
-
function lineage(entry) {
|
|
21
|
-
const value = entry.loop_lineage ?? entry.lineage;
|
|
22
|
-
return value !== null && typeof value === "object" && !Array.isArray(value)
|
|
23
|
-
? value
|
|
24
|
-
: {};
|
|
25
|
-
}
|
|
26
20
|
function loopEntries(loopId, context) {
|
|
27
21
|
return contextEntries(context)
|
|
28
|
-
.filter((entry) => entry.loop_id === loopId ||
|
|
22
|
+
.filter((entry) => entry.loop_id === loopId || entryLineage(entry).loop_id === loopId)
|
|
29
23
|
.sort((a, b) => entryRound(a) - entryRound(b));
|
|
30
24
|
}
|
|
31
25
|
/** v3.3.1: The canonical entry for a round — its compile-time lineage entry
|
|
@@ -51,12 +45,6 @@ function roundCanonicalEntry(entries, round) {
|
|
|
51
45
|
}) ??
|
|
52
46
|
null);
|
|
53
47
|
}
|
|
54
|
-
/** Return the canonical read model when an entry is a committed round.
|
|
55
|
-
* Uncommitted compiler lineage has no durable decision yet and deliberately
|
|
56
|
-
* remains on the local compile path; it cannot be treated as history. */
|
|
57
|
-
function committedView(entry) {
|
|
58
|
-
return decodeMergedRound(entry) ?? decodeCommittedRound(entry);
|
|
59
|
-
}
|
|
60
48
|
export function computeGoalTextHash(text) {
|
|
61
49
|
const normalized = text.trim().replace(/\s+/g, " ").toLowerCase();
|
|
62
50
|
return createHash("sha256").update(normalized).digest("hex").slice(0, 12);
|
|
@@ -75,7 +63,7 @@ export function readPresentedBaseline(loopId, round, context) {
|
|
|
75
63
|
const entry = roundCanonicalEntry(loopEntries(loopId, context), round - 1);
|
|
76
64
|
if (!entry)
|
|
77
65
|
return null;
|
|
78
|
-
const data =
|
|
66
|
+
const data = entryLineage(entry);
|
|
79
67
|
const constraintIds = data.presented_constraint_ids;
|
|
80
68
|
const subGoals = data.presented_subgoals;
|
|
81
69
|
const milestoneRanges = data.presented_milestone_ranges;
|
|
@@ -97,7 +85,7 @@ export function getPreviousRound(loopId, round, context) {
|
|
|
97
85
|
const entry = roundCanonicalEntry(loopEntries(loopId, context), round);
|
|
98
86
|
if (!entry)
|
|
99
87
|
return null;
|
|
100
|
-
const data =
|
|
88
|
+
const data = entryLineage(entry);
|
|
101
89
|
return {
|
|
102
90
|
round,
|
|
103
91
|
goal_id: typeof data.goal_id === "string" ? data.goal_id : "",
|
|
@@ -188,7 +176,7 @@ function evolveConstraints(request, objective, previous, context) {
|
|
|
188
176
|
const lastRetractedRound = new Map();
|
|
189
177
|
if (window > 0) {
|
|
190
178
|
for (const entry of loopEntries(request.loop_id, context)) {
|
|
191
|
-
const retractedList =
|
|
179
|
+
const retractedList = entryLineage(entry).retracted_constraints;
|
|
192
180
|
if (!Array.isArray(retractedList))
|
|
193
181
|
continue;
|
|
194
182
|
const rnd = entryRound(entry);
|
|
@@ -221,47 +209,6 @@ function evolveConstraints(request, objective, previous, context) {
|
|
|
221
209
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
222
210
|
// Hierarchical Summary (v2.1)
|
|
223
211
|
// ═══════════════════════════════════════════════════════════════════════════
|
|
224
|
-
/** Extract execution_evidence from a vault entry, handling both direct-field
|
|
225
|
-
* and lineage-nested shapes. Returns null when absent. */
|
|
226
|
-
function entryExecutionEvidence(entry) {
|
|
227
|
-
const view = committedView(entry);
|
|
228
|
-
if (view)
|
|
229
|
-
return view.executionEvidence;
|
|
230
|
-
const direct = entry.execution_evidence;
|
|
231
|
-
if (direct && typeof direct === "object" && !Array.isArray(direct)) {
|
|
232
|
-
return direct;
|
|
233
|
-
}
|
|
234
|
-
const lin = lineage(entry);
|
|
235
|
-
const nested = lin.execution_evidence;
|
|
236
|
-
if (nested && typeof nested === "object" && !Array.isArray(nested)) {
|
|
237
|
-
return nested;
|
|
238
|
-
}
|
|
239
|
-
return null;
|
|
240
|
-
}
|
|
241
|
-
/** Read success_criteria_met from an entry's execution_evidence. */
|
|
242
|
-
function entryCriteriaMet(entry) {
|
|
243
|
-
const view = committedView(entry);
|
|
244
|
-
if (view)
|
|
245
|
-
return view.executionEvidence?.success_criteria_met
|
|
246
|
-
?.filter((value) => typeof value === "string") ?? [];
|
|
247
|
-
const ev = entryExecutionEvidence(entry);
|
|
248
|
-
if (!ev)
|
|
249
|
-
return [];
|
|
250
|
-
const arr = ev.success_criteria_met;
|
|
251
|
-
return Array.isArray(arr) ? arr.filter((v) => typeof v === "string") : [];
|
|
252
|
-
}
|
|
253
|
-
/** Read success_criteria_remaining from an entry's execution_evidence. */
|
|
254
|
-
function entryCriteriaRemaining(entry) {
|
|
255
|
-
const view = committedView(entry);
|
|
256
|
-
if (view)
|
|
257
|
-
return view.executionEvidence?.success_criteria_remaining
|
|
258
|
-
?.filter((value) => typeof value === "string") ?? [];
|
|
259
|
-
const ev = entryExecutionEvidence(entry);
|
|
260
|
-
if (!ev)
|
|
261
|
-
return [];
|
|
262
|
-
const arr = ev.success_criteria_remaining;
|
|
263
|
-
return Array.isArray(arr) ? arr.filter((v) => typeof v === "string") : [];
|
|
264
|
-
}
|
|
265
212
|
/** v3.2: Deterministic lessons learned — constraints violated repeatedly or
|
|
266
213
|
* verification checks failing repeatedly across rounds (full history, unlike
|
|
267
214
|
* the enforcement gate's R2 3-round window). Presentation only: the output
|
|
@@ -273,7 +220,7 @@ export function deriveLessons(loopId, context, currentRound) {
|
|
|
273
220
|
const warns = new Map();
|
|
274
221
|
for (const entry of entries) {
|
|
275
222
|
const rnd = entryRound(entry);
|
|
276
|
-
const view =
|
|
223
|
+
const view = decodeRound(entry);
|
|
277
224
|
const viols = view
|
|
278
225
|
? view.constraintViolations ?? []
|
|
279
226
|
: Array.isArray(entry.constraint_violations)
|
|
@@ -454,41 +401,10 @@ function detectNewCriteria(current, previous) {
|
|
|
454
401
|
return currMet;
|
|
455
402
|
return currMet.filter((curr) => prevMet.every((prev) => !criteriaMatch(curr, prev)));
|
|
456
403
|
}
|
|
457
|
-
/** Read constraints_active from an entry's lineage. */
|
|
458
|
-
function entryActiveConstraints(entry) {
|
|
459
|
-
const view = committedView(entry);
|
|
460
|
-
if (view)
|
|
461
|
-
return view.activeConstraints ?? [];
|
|
462
|
-
const lin = lineage(entry);
|
|
463
|
-
const arr = lin.constraints_active;
|
|
464
|
-
return Array.isArray(arr) ? arr.filter((v) => typeof v === "string") : [];
|
|
465
|
-
}
|
|
466
|
-
/** Read retracted_constraints from an entry. */
|
|
467
|
-
function entryRetractedConstraints(entry) {
|
|
468
|
-
const view = committedView(entry);
|
|
469
|
-
if (view)
|
|
470
|
-
return view.retractedConstraints ?? [];
|
|
471
|
-
const direct = entry.retracted_constraints;
|
|
472
|
-
if (Array.isArray(direct))
|
|
473
|
-
return direct.filter((v) => typeof v === "string");
|
|
474
|
-
const lin = lineage(entry);
|
|
475
|
-
const nested = lin.retracted_constraints;
|
|
476
|
-
if (Array.isArray(nested))
|
|
477
|
-
return nested.filter((v) => typeof v === "string");
|
|
478
|
-
return [];
|
|
479
|
-
}
|
|
480
|
-
/** Read progress_estimate from an entry's execution_evidence. */
|
|
481
|
-
function entryProgressEstimate(entry) {
|
|
482
|
-
const ev = entryExecutionEvidence(entry);
|
|
483
|
-
if (!ev)
|
|
484
|
-
return 0;
|
|
485
|
-
const pe = ev.progress_estimate;
|
|
486
|
-
return typeof pe === "number" ? pe : 0;
|
|
487
|
-
}
|
|
488
404
|
/** Build a single MilestoneSummary from a range of completed round entries. */
|
|
489
405
|
function buildMilestoneFromEntries(phaseEntries, startRound, endRound, label, kind) {
|
|
490
406
|
const last = phaseEntries.at(-1);
|
|
491
|
-
const lastView = last ?
|
|
407
|
+
const lastView = last ? decodeRound(last) : null;
|
|
492
408
|
const outcomeRaw = lastView
|
|
493
409
|
? (lastView.outputSummary ?? "")
|
|
494
410
|
: last && typeof last.output_summary === "string" ? last.output_summary : "";
|
|
@@ -572,8 +488,8 @@ export function buildRollingSummary(loopId, currentRound, context, sinceRound =
|
|
|
572
488
|
const outcomes = [];
|
|
573
489
|
const issues = [];
|
|
574
490
|
for (const entry of windowEntries) {
|
|
575
|
-
const view =
|
|
576
|
-
const data =
|
|
491
|
+
const view = decodeRound(entry);
|
|
492
|
+
const data = entryLineage(entry);
|
|
577
493
|
const round = entryRound(entry);
|
|
578
494
|
const success = view ? view.success : entry.success ?? data.success;
|
|
579
495
|
const summary = view
|
|
@@ -606,7 +522,7 @@ export function buildRollingSummary(loopId, currentRound, context, sinceRound =
|
|
|
606
522
|
for (let i = 0; i < completedEntries.length; i++) {
|
|
607
523
|
const entry = completedEntries[i];
|
|
608
524
|
const rnd = entryRound(entry);
|
|
609
|
-
const lin =
|
|
525
|
+
const lin = entryLineage(entry);
|
|
610
526
|
// Signal 1: Agent-declared checkpoint
|
|
611
527
|
const isAgentCheckpoint = lin.compression_checkpoint === true;
|
|
612
528
|
// Signal 2: New criteria met (compare with previous vault entry)
|
|
@@ -685,60 +601,13 @@ export function deriveConstraintId(text) {
|
|
|
685
601
|
export function deriveCriterionId(text) {
|
|
686
602
|
return "cr-" + deriveItemId(text);
|
|
687
603
|
}
|
|
688
|
-
/** v2.11: Check whether a user-provided reference looks like a constraint ID.
|
|
689
|
-
* Matches the pattern c-XXXXXXXX where X is a hex digit. */
|
|
690
|
-
export function isConstraintId(ref) {
|
|
691
|
-
return /^c-[a-f0-9]{8}$/.test(ref);
|
|
692
|
-
}
|
|
693
604
|
/** v2.11: Check whether a user-provided reference looks like a criterion ID.
|
|
694
|
-
* Matches the pattern cr-XXXXXXXX where X is a hex digit.
|
|
695
|
-
|
|
605
|
+
* Matches the pattern cr-XXXXXXXX where X is a hex digit. Module-local:
|
|
606
|
+
* consumers with the same need (round-contract.ts) keep their own copy to
|
|
607
|
+
* avoid an import cycle. */
|
|
608
|
+
function isCriterionId(ref) {
|
|
696
609
|
return /^cr-[a-f0-9]{8}$/.test(ref);
|
|
697
610
|
}
|
|
698
|
-
/** Read emerged_subtasks from a vault entry (handles direct + lineage nesting). */
|
|
699
|
-
function entryEmergedSubtasks(entry) {
|
|
700
|
-
const view = committedView(entry);
|
|
701
|
-
if (view)
|
|
702
|
-
return view.emergedSubtasks ?? [];
|
|
703
|
-
const direct = entry.emerged_subtasks;
|
|
704
|
-
if (Array.isArray(direct))
|
|
705
|
-
return direct.filter((v) => typeof v === "string");
|
|
706
|
-
const lin = lineage(entry);
|
|
707
|
-
const nested = lin.emerged_subtasks;
|
|
708
|
-
if (Array.isArray(nested))
|
|
709
|
-
return nested.filter((v) => typeof v === "string");
|
|
710
|
-
return [];
|
|
711
|
-
}
|
|
712
|
-
/** Read completed_subtasks from an entry. */
|
|
713
|
-
function entryCompletedSubtasks(entry) {
|
|
714
|
-
const view = committedView(entry);
|
|
715
|
-
if (view)
|
|
716
|
-
return view.completedSubtasks ?? [];
|
|
717
|
-
const direct = entry.completed_subtasks;
|
|
718
|
-
if (Array.isArray(direct))
|
|
719
|
-
return direct.filter((v) => typeof v === "string");
|
|
720
|
-
return [];
|
|
721
|
-
}
|
|
722
|
-
/** Read blocked_subtasks from an entry. */
|
|
723
|
-
function entryBlockedSubtasks(entry) {
|
|
724
|
-
const view = committedView(entry);
|
|
725
|
-
if (view)
|
|
726
|
-
return view.blockedSubtasks ?? [];
|
|
727
|
-
const direct = entry.blocked_subtasks;
|
|
728
|
-
if (Array.isArray(direct))
|
|
729
|
-
return direct.filter((v) => typeof v === "string");
|
|
730
|
-
return [];
|
|
731
|
-
}
|
|
732
|
-
/** Read canceled_subtasks from an entry. */
|
|
733
|
-
function entryCanceledSubtasks(entry) {
|
|
734
|
-
const view = committedView(entry);
|
|
735
|
-
if (view)
|
|
736
|
-
return view.canceledSubtasks ?? [];
|
|
737
|
-
const direct = entry.canceled_subtasks;
|
|
738
|
-
if (Array.isArray(direct))
|
|
739
|
-
return direct.filter((v) => typeof v === "string");
|
|
740
|
-
return [];
|
|
741
|
-
}
|
|
742
611
|
/**
|
|
743
612
|
* Match a user-provided sub-task reference against existing SubGoals.
|
|
744
613
|
*
|
|
@@ -846,35 +715,10 @@ function manageSubGoals(loopId, currentRound, lastRoundResult, vaultContext) {
|
|
|
846
715
|
subGoals[idx].status_changed_at_round = rnd;
|
|
847
716
|
}
|
|
848
717
|
}
|
|
849
|
-
//
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
if (sg.status === "pending") {
|
|
854
|
-
if (jaccardSimilarity(nextAction, sg.description) >= policy.subgoal_auto_in_progress_threshold) {
|
|
855
|
-
sg.status = "in_progress";
|
|
856
|
-
sg.status_changed_at_round = rnd;
|
|
857
|
-
break; // Only one sub-goal can be in_progress at a time
|
|
858
|
-
}
|
|
859
|
-
}
|
|
860
|
-
}
|
|
861
|
-
}
|
|
862
|
-
// Phase 4 — Auto-complete: new criteria match → done
|
|
863
|
-
const newCriteria = lastRoundResult.execution_evidence?.success_criteria_met ?? [];
|
|
864
|
-
if (newCriteria.length > 0) {
|
|
865
|
-
for (const sg of subGoals) {
|
|
866
|
-
if (sg.status === "pending" || sg.status === "in_progress") {
|
|
867
|
-
for (const crit of newCriteria) {
|
|
868
|
-
if (jaccardSimilarity(crit, sg.description) >= policy.subgoal_auto_complete_threshold) {
|
|
869
|
-
sg.status = "done";
|
|
870
|
-
sg.status_changed_at_round = rnd;
|
|
871
|
-
sg.completed_at_round = rnd;
|
|
872
|
-
break;
|
|
873
|
-
}
|
|
874
|
-
}
|
|
875
|
-
}
|
|
876
|
-
}
|
|
877
|
-
}
|
|
718
|
+
// v3.7: no auto status inference. The v2.3 fuzzy heuristics
|
|
719
|
+
// (next_action Jaccard → in_progress, criteria Jaccard → done) were
|
|
720
|
+
// removed — they fabricated dashboard motion the machine never
|
|
721
|
+
// verified. Statuses change only via the agent declarations above.
|
|
878
722
|
}
|
|
879
723
|
// Sort for display: in_progress first, then pending by age desc, then blocked, then done, then canceled
|
|
880
724
|
subGoals.sort((a, b) => {
|
|
@@ -915,40 +759,6 @@ function constraintSource(text, objective, constraintsFromPlan) {
|
|
|
915
759
|
return "criteria";
|
|
916
760
|
return "discovered";
|
|
917
761
|
}
|
|
918
|
-
/** Find the earliest round a constraint was discovered by scanning vault entries.
|
|
919
|
-
* v2.11: ID-first matching. Checks exact constraint ID match (c-XXXXXXXX)
|
|
920
|
-
* against the derived ID of each vault entry's discovered_constraints, then
|
|
921
|
-
* falls back to Jaccard similarity on text. */
|
|
922
|
-
function findDiscoveredRound(text, vaultEntries) {
|
|
923
|
-
const targetId = deriveConstraintId(text);
|
|
924
|
-
let earliest = 0;
|
|
925
|
-
for (const entry of vaultEntries) {
|
|
926
|
-
const rnd = entryRound(entry);
|
|
927
|
-
if (rnd < 1)
|
|
928
|
-
continue;
|
|
929
|
-
const view = committedView(entry);
|
|
930
|
-
const discovered = view
|
|
931
|
-
? view.discoveredConstraints ?? []
|
|
932
|
-
: Array.isArray(entry.discovered_constraints)
|
|
933
|
-
? entry.discovered_constraints.filter((v) => typeof v === "string")
|
|
934
|
-
: [];
|
|
935
|
-
for (const d of discovered) {
|
|
936
|
-
// Phase 1: exact ID match (v2.11)
|
|
937
|
-
if (d === targetId || deriveConstraintId(d) === targetId) {
|
|
938
|
-
if (earliest === 0 || rnd < earliest)
|
|
939
|
-
earliest = rnd;
|
|
940
|
-
break;
|
|
941
|
-
}
|
|
942
|
-
// Phase 2: Jaccard fallback for natural-language references.
|
|
943
|
-
if (jaccardSimilarity(text, d) >= getPolicy().evolution.constraint_match_threshold) {
|
|
944
|
-
if (earliest === 0 || rnd < earliest)
|
|
945
|
-
earliest = rnd;
|
|
946
|
-
break;
|
|
947
|
-
}
|
|
948
|
-
}
|
|
949
|
-
}
|
|
950
|
-
return earliest;
|
|
951
|
-
}
|
|
952
762
|
/** Find the latest round a constraint was violated by scanning vault entries.
|
|
953
763
|
* v2.11: ID-first matching. Checks exact constraint ID match (c-XXXXXXXX)
|
|
954
764
|
* against the derived ID of each vault entry's constraint_violations, then
|
|
@@ -969,7 +779,7 @@ function findLastViolatedRound(text, vaultEntries) {
|
|
|
969
779
|
const rnd = entryRound(entry);
|
|
970
780
|
if (rnd < 1)
|
|
971
781
|
continue;
|
|
972
|
-
const view =
|
|
782
|
+
const view = decodeRound(entry);
|
|
973
783
|
const violations = view
|
|
974
784
|
? view.constraintViolations ?? []
|
|
975
785
|
: Array.isArray(entry.constraint_violations)
|
|
@@ -986,15 +796,17 @@ function findLastViolatedRound(text, vaultEntries) {
|
|
|
986
796
|
return latest;
|
|
987
797
|
}
|
|
988
798
|
/**
|
|
989
|
-
* Manage constraint lifecycle
|
|
799
|
+
* Manage constraint lifecycle metadata for the active set.
|
|
990
800
|
*
|
|
991
|
-
*
|
|
992
|
-
*
|
|
993
|
-
*
|
|
994
|
-
*
|
|
801
|
+
* v3.7: the v2.3 time-aware decay (discovered constraints demoted to
|
|
802
|
+
* inactive after N quiet rounds, auto-reactivation on a fresh violation)
|
|
803
|
+
* is gone — every active constraint stays active. What remains is the
|
|
804
|
+
* display metadata the v3 prompt surface needs: per-text source
|
|
805
|
+
* classification (v3.3.1: criteria texts carry the cr-XXXXXXXX namespace),
|
|
806
|
+
* and last_violated_at_round for the L1 collapse "(violated this round)"
|
|
807
|
+
* annotation (diffConstraints compares it to the current round).
|
|
995
808
|
*/
|
|
996
809
|
function manageConstraintLifecycle(active, objective, constraintsFromPlan, vaultContext, currentRound, lastRoundViolations = []) {
|
|
997
|
-
const policy = getPolicy().evolution;
|
|
998
810
|
const allEntries = loopEntries(objective.loop_id || "", vaultContext);
|
|
999
811
|
const completedEntries = allEntries.filter((e) => entryRound(e) >= 1 && entryRound(e) < currentRound);
|
|
1000
812
|
const metadata = [];
|
|
@@ -1004,57 +816,25 @@ function manageConstraintLifecycle(active, objective, constraintsFromPlan, vault
|
|
|
1004
816
|
// as a violation at currentRound — the previous rounds' scan cannot see
|
|
1005
817
|
// it (this round is not committed yet). This makes the L1 collapse
|
|
1006
818
|
// render "(violated this round)" (diffConstraints compares
|
|
1007
|
-
// last_violated_at_round === round)
|
|
1008
|
-
// discovered constraints.
|
|
819
|
+
// last_violated_at_round === round).
|
|
1009
820
|
const violatedThisRound = lastRoundViolations.some((v) => matchesConstraintText(text, v));
|
|
1010
|
-
//
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
// and an agent echoing the active-list ID into success_criteria_met
|
|
1018
|
-
// could never match.
|
|
1019
|
-
metadata.push(makeConstraintMeta({
|
|
1020
|
-
id: source === "criteria" ? deriveCriterionId(text) : deriveConstraintId(text),
|
|
1021
|
-
text,
|
|
1022
|
-
discovered_at_round: objective.created_at_round || 1,
|
|
1023
|
-
last_violated_at_round: violatedThisRound
|
|
1024
|
-
? currentRound
|
|
1025
|
-
: findLastViolatedRound(text, completedEntries),
|
|
1026
|
-
source,
|
|
1027
|
-
status: "active",
|
|
1028
|
-
}));
|
|
1029
|
-
continue;
|
|
1030
|
-
}
|
|
1031
|
-
// Discovered constraints: check for inactivity
|
|
1032
|
-
const discoveredAt = findDiscoveredRound(text, completedEntries);
|
|
1033
|
-
const lastViolated = violatedThisRound
|
|
1034
|
-
? currentRound
|
|
1035
|
-
: findLastViolatedRound(text, completedEntries);
|
|
1036
|
-
const roundsSinceRelevant = lastViolated > 0
|
|
1037
|
-
? currentRound - lastViolated
|
|
1038
|
-
: currentRound - (discoveredAt || currentRound);
|
|
1039
|
-
const status = roundsSinceRelevant > policy.constraint_inactive_rounds
|
|
1040
|
-
? "inactive"
|
|
1041
|
-
: "active";
|
|
821
|
+
// v3.3.1: one text, one identity — a success-criterion text that also
|
|
822
|
+
// lives in the merged active set belongs to the cr-XXXXXXXX namespace
|
|
823
|
+
// (its own Success Criteria section and the verification gate's
|
|
824
|
+
// criteriaMatch are ID-first on cr-). Rendering it under c-XXXXXXXX in
|
|
825
|
+
// the active list meant the same text carried two IDs in one prompt,
|
|
826
|
+
// and an agent echoing the active-list ID into success_criteria_met
|
|
827
|
+
// could never match.
|
|
1042
828
|
metadata.push(makeConstraintMeta({
|
|
1043
|
-
id: deriveConstraintId(text),
|
|
829
|
+
id: source === "criteria" ? deriveCriterionId(text) : deriveConstraintId(text),
|
|
1044
830
|
text,
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
831
|
+
last_violated_at_round: violatedThisRound
|
|
832
|
+
? currentRound
|
|
833
|
+
: findLastViolatedRound(text, completedEntries),
|
|
834
|
+
source,
|
|
1049
835
|
}));
|
|
1050
836
|
}
|
|
1051
|
-
|
|
1052
|
-
.filter((m) => m.status === "active")
|
|
1053
|
-
.map((m) => m.text);
|
|
1054
|
-
const inactive = metadata
|
|
1055
|
-
.filter((m) => m.status === "inactive")
|
|
1056
|
-
.map((m) => m.text);
|
|
1057
|
-
return { active: newActive, inactive, metadata };
|
|
837
|
+
return { active, metadata };
|
|
1058
838
|
}
|
|
1059
839
|
export function alignTask(proposedTask, request, context) {
|
|
1060
840
|
const objective = request.loop_objective ?? latestObjective(request.loop_id, context);
|
|
@@ -1099,7 +879,7 @@ function levelDecision(request, context) {
|
|
|
1099
879
|
last?.revised_success_criteria?.length ||
|
|
1100
880
|
last?.wrong_assumptions?.length);
|
|
1101
881
|
const lastFullRound = loopEntries(request.loop_id, context)
|
|
1102
|
-
.filter((entry) =>
|
|
882
|
+
.filter((entry) => entryLineage(entry).recompile_level === "l2")
|
|
1103
883
|
.map(entryRound)
|
|
1104
884
|
.filter((round) => round < request.round)
|
|
1105
885
|
.at(-1) ?? 1;
|
|
@@ -1108,7 +888,7 @@ function levelDecision(request, context) {
|
|
|
1108
888
|
// rehydration (the decidePromptLevel recovery_boundary branch existed
|
|
1109
889
|
// but no caller ever set it).
|
|
1110
890
|
const recoveryBoundary = loopEntries(request.loop_id, context)
|
|
1111
|
-
.some((entry) =>
|
|
891
|
+
.some((entry) => entryLineage(entry).committed_action === "backtrack" &&
|
|
1112
892
|
entryRound(entry) === request.round);
|
|
1113
893
|
return decidePromptLevel({
|
|
1114
894
|
round: request.round,
|
|
@@ -1285,7 +1065,6 @@ export function compileLoop(request, context) {
|
|
|
1285
1065
|
lineage: [`${request.loop_id}:r${request.round}`],
|
|
1286
1066
|
constraints_active: constraintLifecycle.active,
|
|
1287
1067
|
constraints_retired: constraints.retired,
|
|
1288
|
-
constraints_inactive: constraintLifecycle.inactive,
|
|
1289
1068
|
constraint_metadata: constraintLifecycle.metadata,
|
|
1290
1069
|
loop_id: request.loop_id,
|
|
1291
1070
|
round: request.round,
|
|
@@ -1329,7 +1108,6 @@ export function compileLoop(request, context) {
|
|
|
1329
1108
|
state,
|
|
1330
1109
|
level: decision.level,
|
|
1331
1110
|
reasons: decision.reasons,
|
|
1332
|
-
mode: policy.prompt.injection_mode,
|
|
1333
1111
|
budgets: {
|
|
1334
1112
|
l0: policy.prompt.l0_max_chars,
|
|
1335
1113
|
l1: policy.prompt.l1_max_chars,
|