loopforge 3.9.2 → 4.0.7
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 +236 -112
- package/dist/audit.d.ts +73 -15
- package/dist/audit.d.ts.map +1 -1
- package/dist/audit.js +131 -61
- package/dist/audit.js.map +1 -1
- package/dist/canonical-state.d.ts +152 -40
- package/dist/canonical-state.d.ts.map +1 -1
- package/dist/canonical-state.js +382 -206
- package/dist/canonical-state.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +41 -12
- package/dist/cli.js.map +1 -1
- package/dist/cognitive-governance.d.ts +0 -2
- package/dist/cognitive-governance.d.ts.map +1 -1
- package/dist/cognitive-governance.js +0 -4
- package/dist/cognitive-governance.js.map +1 -1
- package/dist/committed-round.d.ts +105 -141
- package/dist/committed-round.d.ts.map +1 -1
- package/dist/committed-round.js +249 -372
- package/dist/committed-round.js.map +1 -1
- package/dist/constraint-lifecycle.d.ts +73 -0
- package/dist/constraint-lifecycle.d.ts.map +1 -0
- package/dist/constraint-lifecycle.js +174 -0
- package/dist/constraint-lifecycle.js.map +1 -0
- package/dist/context-boundary.d.ts +83 -0
- package/dist/context-boundary.d.ts.map +1 -0
- package/dist/context-boundary.js +101 -0
- package/dist/context-boundary.js.map +1 -0
- package/dist/contract-items.d.ts +12 -8
- package/dist/contract-items.d.ts.map +1 -1
- package/dist/contract-items.js +31 -26
- package/dist/contract-items.js.map +1 -1
- package/dist/contract-requirement.d.ts +47 -0
- package/dist/contract-requirement.d.ts.map +1 -0
- package/dist/contract-requirement.js +51 -0
- package/dist/contract-requirement.js.map +1 -0
- package/dist/criterion-status.d.ts +35 -0
- package/dist/criterion-status.d.ts.map +1 -0
- package/dist/criterion-status.js +89 -0
- package/dist/criterion-status.js.map +1 -0
- package/dist/enforcement-gate.d.ts +32 -67
- package/dist/enforcement-gate.d.ts.map +1 -1
- package/dist/enforcement-gate.js +203 -439
- package/dist/enforcement-gate.js.map +1 -1
- package/dist/engine.d.ts +9 -103
- package/dist/engine.d.ts.map +1 -1
- package/dist/engine.js +28 -887
- package/dist/engine.js.map +1 -1
- package/dist/errors.d.ts +41 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +69 -0
- package/dist/errors.js.map +1 -0
- package/dist/evidence-claims.d.ts +33 -31
- package/dist/evidence-claims.d.ts.map +1 -1
- package/dist/evidence-claims.js +58 -72
- package/dist/evidence-claims.js.map +1 -1
- package/dist/evidence-posture-view.d.ts +19 -0
- package/dist/evidence-posture-view.d.ts.map +1 -0
- package/dist/evidence-posture-view.js +116 -0
- package/dist/evidence-posture-view.js.map +1 -0
- package/dist/evidence-posture.d.ts +73 -0
- package/dist/evidence-posture.d.ts.map +1 -0
- package/dist/evidence-posture.js +281 -0
- package/dist/evidence-posture.js.map +1 -0
- package/dist/evidence-provider.d.ts +55 -37
- package/dist/evidence-provider.d.ts.map +1 -1
- package/dist/evidence-provider.js +182 -332
- package/dist/evidence-provider.js.map +1 -1
- package/dist/explain.d.ts +167 -27
- package/dist/explain.d.ts.map +1 -1
- package/dist/explain.js +373 -58
- package/dist/explain.js.map +1 -1
- package/dist/field-accounting.d.ts.map +1 -1
- package/dist/field-accounting.js +3 -22
- package/dist/field-accounting.js.map +1 -1
- package/dist/generate-schema.js +13 -8
- package/dist/generate-schema.js.map +1 -1
- package/dist/git-boundary.d.ts +128 -0
- package/dist/git-boundary.d.ts.map +1 -0
- package/dist/git-boundary.js +625 -0
- package/dist/git-boundary.js.map +1 -0
- package/dist/git-facts.d.ts +22 -0
- package/dist/git-facts.d.ts.map +1 -0
- package/dist/git-facts.js +29 -0
- package/dist/git-facts.js.map +1 -0
- package/dist/git-motion.d.ts +37 -0
- package/dist/git-motion.d.ts.map +1 -0
- package/dist/git-motion.js +59 -0
- package/dist/git-motion.js.map +1 -0
- package/dist/history.d.ts +44 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +77 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +19 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -4
- package/dist/index.js.map +1 -1
- package/dist/init.d.ts +16 -0
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +80 -14
- package/dist/init.js.map +1 -1
- package/dist/loop-compiler.d.ts +51 -19
- package/dist/loop-compiler.d.ts.map +1 -1
- package/dist/loop-compiler.js +423 -507
- package/dist/loop-compiler.js.map +1 -1
- package/dist/loop-store.d.ts +6 -5
- package/dist/loop-store.d.ts.map +1 -1
- package/dist/loop-store.js +153 -35
- package/dist/loop-store.js.map +1 -1
- package/dist/mcp/round-lifecycle.d.ts +285 -81
- package/dist/mcp/round-lifecycle.d.ts.map +1 -1
- package/dist/mcp/round-lifecycle.js +1629 -574
- package/dist/mcp/round-lifecycle.js.map +1 -1
- package/dist/mcp/server.d.ts +5 -0
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +61 -19
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/session.d.ts +81 -39
- package/dist/mcp/session.d.ts.map +1 -1
- package/dist/mcp/session.js +963 -271
- package/dist/mcp/session.js.map +1 -1
- package/dist/mcp/submission-boundary.d.ts +23 -31
- package/dist/mcp/submission-boundary.d.ts.map +1 -1
- package/dist/mcp/submission-boundary.js +242 -82
- package/dist/mcp/submission-boundary.js.map +1 -1
- package/dist/mcp/submission-handle.d.ts +66 -0
- package/dist/mcp/submission-handle.d.ts.map +1 -0
- package/dist/mcp/submission-handle.js +173 -0
- package/dist/mcp/submission-handle.js.map +1 -0
- package/dist/mcp/tools.d.ts +17 -7
- package/dist/mcp/tools.d.ts.map +1 -1
- package/dist/mcp/tools.js +763 -244
- package/dist/mcp/tools.js.map +1 -1
- package/dist/open-question.d.ts +7 -0
- package/dist/open-question.d.ts.map +1 -0
- package/dist/open-question.js +14 -0
- package/dist/open-question.js.map +1 -0
- package/dist/policy-metrics.d.ts +31 -21
- package/dist/policy-metrics.d.ts.map +1 -1
- package/dist/policy-metrics.js +117 -64
- package/dist/policy-metrics.js.map +1 -1
- package/dist/policy.d.ts +15 -40
- package/dist/policy.d.ts.map +1 -1
- package/dist/policy.js +13 -13
- package/dist/policy.js.map +1 -1
- package/dist/progress.d.ts +45 -0
- package/dist/progress.d.ts.map +1 -0
- package/dist/progress.js +136 -0
- package/dist/progress.js.map +1 -0
- package/dist/prompt-assembler.d.ts +28 -9
- package/dist/prompt-assembler.d.ts.map +1 -1
- package/dist/prompt-assembler.js +592 -603
- package/dist/prompt-assembler.js.map +1 -1
- package/dist/prompt-coverage.d.ts +39 -0
- package/dist/prompt-coverage.d.ts.map +1 -0
- package/dist/prompt-coverage.js +80 -0
- package/dist/prompt-coverage.js.map +1 -0
- package/dist/prompt-freshness.d.ts +25 -0
- package/dist/prompt-freshness.d.ts.map +1 -0
- package/dist/prompt-freshness.js +88 -0
- package/dist/prompt-freshness.js.map +1 -0
- package/dist/prompt-policy.d.ts +14 -4
- package/dist/prompt-policy.d.ts.map +1 -1
- package/dist/prompt-policy.js +29 -10
- package/dist/prompt-policy.js.map +1 -1
- package/dist/protocol.d.ts +875 -419
- package/dist/protocol.d.ts.map +1 -1
- package/dist/protocol.js +73 -99
- package/dist/protocol.js.map +1 -1
- package/dist/recovery-guidance.d.ts +13 -0
- package/dist/recovery-guidance.d.ts.map +1 -0
- package/dist/recovery-guidance.js +15 -0
- package/dist/recovery-guidance.js.map +1 -0
- package/dist/recovery.d.ts +68 -0
- package/dist/recovery.d.ts.map +1 -0
- package/dist/recovery.js +234 -0
- package/dist/recovery.js.map +1 -0
- package/dist/replay.d.ts +42 -8
- package/dist/replay.d.ts.map +1 -1
- package/dist/replay.js +311 -84
- package/dist/replay.js.map +1 -1
- package/dist/result-facts.d.ts +45 -0
- package/dist/result-facts.d.ts.map +1 -0
- package/dist/result-facts.js +86 -0
- package/dist/result-facts.js.map +1 -0
- package/dist/round-contract.d.ts +38 -8
- package/dist/round-contract.d.ts.map +1 -1
- package/dist/round-contract.js +71 -31
- package/dist/round-contract.js.map +1 -1
- package/dist/round-coordinator.d.ts +45 -91
- package/dist/round-coordinator.d.ts.map +1 -1
- package/dist/round-coordinator.js +254 -271
- package/dist/round-coordinator.js.map +1 -1
- package/dist/round-driver.d.ts +56 -16
- package/dist/round-driver.d.ts.map +1 -1
- package/dist/round-driver.js +225 -39
- package/dist/round-driver.js.map +1 -1
- package/dist/round-facts.d.ts +33 -52
- package/dist/round-facts.d.ts.map +1 -1
- package/dist/round-facts.js +54 -157
- package/dist/round-facts.js.map +1 -1
- package/dist/round-guidance.d.ts +56 -0
- package/dist/round-guidance.d.ts.map +1 -0
- package/dist/round-guidance.js +161 -0
- package/dist/round-guidance.js.map +1 -0
- package/dist/round-outcome.d.ts +21 -0
- package/dist/round-outcome.d.ts.map +1 -0
- package/dist/round-outcome.js +31 -0
- package/dist/round-outcome.js.map +1 -0
- package/dist/round-transaction.d.ts +168 -21
- package/dist/round-transaction.d.ts.map +1 -1
- package/dist/round-transaction.js +856 -39
- package/dist/round-transaction.js.map +1 -1
- package/dist/self-eval.d.ts +40 -60
- package/dist/self-eval.d.ts.map +1 -1
- package/dist/self-eval.js +455 -384
- package/dist/self-eval.js.map +1 -1
- package/dist/storage.d.ts +16 -0
- package/dist/storage.d.ts.map +1 -1
- package/dist/storage.js +54 -4
- package/dist/storage.js.map +1 -1
- package/dist/subgoal-state.d.ts +2 -3
- package/dist/subgoal-state.d.ts.map +1 -1
- package/dist/subgoal-state.js +2 -3
- package/dist/subgoal-state.js.map +1 -1
- package/dist/submission-guidance.d.ts +43 -21
- package/dist/submission-guidance.d.ts.map +1 -1
- package/dist/submission-guidance.js +368 -262
- package/dist/submission-guidance.js.map +1 -1
- package/dist/submission-receipt.d.ts +16 -8
- package/dist/submission-receipt.d.ts.map +1 -1
- package/dist/submission-receipt.js +146 -51
- package/dist/submission-receipt.js.map +1 -1
- package/dist/token-utils.d.ts +47 -7
- package/dist/token-utils.d.ts.map +1 -1
- package/dist/token-utils.js +66 -15
- package/dist/token-utils.js.map +1 -1
- package/dist/transaction-identity.d.ts +9 -0
- package/dist/transaction-identity.d.ts.map +1 -0
- package/dist/transaction-identity.js +69 -0
- package/dist/transaction-identity.js.map +1 -0
- package/dist/verification-actions.d.ts +13 -0
- package/dist/verification-actions.d.ts.map +1 -0
- package/dist/verification-actions.js +44 -0
- package/dist/verification-actions.js.map +1 -0
- package/dist/verification-gate.d.ts +26 -46
- package/dist/verification-gate.d.ts.map +1 -1
- package/dist/verification-gate.js +218 -427
- package/dist/verification-gate.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/working-memory.d.ts +187 -0
- package/dist/working-memory.d.ts.map +1 -0
- package/dist/working-memory.js +767 -0
- package/dist/working-memory.js.map +1 -0
- package/dist/workspace-checkpoint.d.ts +88 -0
- package/dist/workspace-checkpoint.d.ts.map +1 -0
- package/dist/workspace-checkpoint.js +326 -0
- package/dist/workspace-checkpoint.js.map +1 -0
- package/dist/workspace-restore.d.ts +68 -0
- package/dist/workspace-restore.d.ts.map +1 -0
- package/dist/workspace-restore.js +604 -0
- package/dist/workspace-restore.js.map +1 -0
- package/dist/workspace.d.ts +0 -9
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +0 -17
- package/dist/workspace.js.map +1 -1
- package/loop_policy.json +5 -19
- package/package.json +2 -2
- package/skills/forge/SKILL.md +212 -0
- package/skills/forge/references/evidence-and-contracts.md +204 -0
- package/skills/forge/references/recovery-and-views.md +188 -0
- package/dist/cognitive-facts.d.ts +0 -30
- package/dist/cognitive-facts.d.ts.map +0 -1
- package/dist/cognitive-facts.js +0 -113
- package/dist/cognitive-facts.js.map +0 -1
- package/dist/loop-extras-parser.d.ts +0 -81
- package/dist/loop-extras-parser.d.ts.map +0 -1
- package/dist/loop-extras-parser.js +0 -213
- package/dist/loop-extras-parser.js.map +0 -1
- package/dist/loop-projection.d.ts +0 -6
- package/dist/loop-projection.d.ts.map +0 -1
- package/dist/loop-projection.js +0 -28
- package/dist/loop-projection.js.map +0 -1
- package/skills/perception/SKILL.md +0 -370
package/dist/committed-round.js
CHANGED
|
@@ -4,12 +4,16 @@
|
|
|
4
4
|
* place that interprets their transaction envelope or the compiler's merged
|
|
5
5
|
* lineage representation. Consumers receive one stable, read-only view.
|
|
6
6
|
*/
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
7
|
+
import { observationVersionIssue } from "./evidence-provider.js";
|
|
8
|
+
import { promptContextPairIssue } from "./context-boundary.js";
|
|
9
|
+
import { PROMPT_ARTIFACT_SCHEMA_VERSION, isPromptContextMode, isPromptContextReason, makeLoopRoundResult, } from "./protocol.js";
|
|
10
|
+
import { ROUND_TRANSACTION_SCHEMA_VERSION, deriveRoundObservationDelta, isProcessResult, parseRoundTransactionSnapshot, rootBranchId, transactionSchemaVersionOf, } from "./round-transaction.js";
|
|
11
|
+
import { effectiveOutcome, parseRoundContract, } from "./self-eval.js";
|
|
10
12
|
import { entryRound, isRecord } from "./token-utils.js";
|
|
13
|
+
import { transactionIdentityIssue } from "./transaction-identity.js";
|
|
11
14
|
function committedAction(value) {
|
|
12
|
-
return value === "continue" || value === "stop" ||
|
|
15
|
+
return value === "continue" || value === "stop" ||
|
|
16
|
+
value === "recovery" || value === "backtrack"
|
|
13
17
|
? value
|
|
14
18
|
: null;
|
|
15
19
|
}
|
|
@@ -19,56 +23,11 @@ function snapshots(value) {
|
|
|
19
23
|
function flags(value) {
|
|
20
24
|
return Array.isArray(value) ? value : [];
|
|
21
25
|
}
|
|
22
|
-
function roundOutcome(value) {
|
|
23
|
-
return value === "success" || value === "partial" ||
|
|
24
|
-
value === "failed" || value === "blocked"
|
|
25
|
-
? value
|
|
26
|
-
: null;
|
|
27
|
-
}
|
|
28
26
|
function strings(value) {
|
|
29
27
|
return Array.isArray(value)
|
|
30
28
|
? value.filter((item) => typeof item === "string")
|
|
31
29
|
: [];
|
|
32
30
|
}
|
|
33
|
-
function numbers(value) {
|
|
34
|
-
return Array.isArray(value)
|
|
35
|
-
? value.filter((item) => typeof item === "number")
|
|
36
|
-
: [];
|
|
37
|
-
}
|
|
38
|
-
function stringMap(value) {
|
|
39
|
-
if (!isRecord(value))
|
|
40
|
-
return {};
|
|
41
|
-
const out = {};
|
|
42
|
-
for (const [key, entry] of Object.entries(value)) {
|
|
43
|
-
if (typeof entry === "string")
|
|
44
|
-
out[key] = entry;
|
|
45
|
-
}
|
|
46
|
-
return out;
|
|
47
|
-
}
|
|
48
|
-
/** v3.8.1: decode a rollback directive's facts from either carrier —
|
|
49
|
-
* `result.*` on a durable :feedback entry, `lineage.*` on a hydrated merged
|
|
50
|
-
* entry. Returns null when no restore point is recorded, which is also the
|
|
51
|
-
* signal that the record cannot serve as recovery guidance. */
|
|
52
|
-
export function decodeBacktrackRecord(source) {
|
|
53
|
-
const value = isRecord(source) ? source : {};
|
|
54
|
-
const target = typeof value.backtrackTarget === "number" ? value.backtrackTarget : null;
|
|
55
|
-
if (target === null)
|
|
56
|
-
return null;
|
|
57
|
-
return {
|
|
58
|
-
target,
|
|
59
|
-
triggerRule: typeof value.backtrackTriggerRule === "string"
|
|
60
|
-
? value.backtrackTriggerRule
|
|
61
|
-
: "",
|
|
62
|
-
failedRounds: numbers(value.backtrackFailedRounds),
|
|
63
|
-
approaches: strings(value.backtrackApproaches),
|
|
64
|
-
wrongAssumptions: strings(value.backtrackWrongAssumptions),
|
|
65
|
-
skippedFiles: strings(value.backtrackSkippedFiles),
|
|
66
|
-
skippedFingerprints: stringMap(value.backtrackSkippedFingerprints),
|
|
67
|
-
targetGitHead: typeof value.backtrackTargetGitHead === "string"
|
|
68
|
-
? value.backtrackTargetGitHead
|
|
69
|
-
: undefined,
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
31
|
/** Normalize a raw subgoal_updates value into typed entries. Decode stays
|
|
73
32
|
* tolerant of any shape (parser caps apply at ingestion; this is the
|
|
74
33
|
* read-model view of what was committed). */
|
|
@@ -114,7 +73,7 @@ function evidenceIncomplete(after, deltas) {
|
|
|
114
73
|
* feedback entry and the engine's hydrated merged entry call it, so there is
|
|
115
74
|
* no second evaluation/history interpretation. */
|
|
116
75
|
function viewFromSnapshot(input) {
|
|
117
|
-
const { snapshot, result
|
|
76
|
+
const { snapshot, result } = input;
|
|
118
77
|
if (snapshot.phase !== "committed")
|
|
119
78
|
return null;
|
|
120
79
|
const action = committedAction(result.action);
|
|
@@ -129,12 +88,27 @@ function viewFromSnapshot(input) {
|
|
|
129
88
|
const beforeEvidence = snapshots(snapshot.beforeEvidence);
|
|
130
89
|
const afterEvidence = snapshots(snapshot.afterEvidence);
|
|
131
90
|
const observationDelta = deriveRoundObservationDelta(beforeEvidence, afterEvidence);
|
|
91
|
+
const contractProposal = evaluation?.round_contract
|
|
92
|
+
? parseRoundContract(evaluation.round_contract) ?? null
|
|
93
|
+
: null;
|
|
132
94
|
return {
|
|
133
95
|
source: input.source,
|
|
134
96
|
sourceEntry: input.sourceEntry,
|
|
135
97
|
loopId,
|
|
136
98
|
round,
|
|
137
99
|
roundId: snapshot.roundId,
|
|
100
|
+
transactionId: snapshot.transactionId,
|
|
101
|
+
branchId: snapshot.branchId,
|
|
102
|
+
kind: snapshot.kind,
|
|
103
|
+
origin: snapshot.kind === "rollback"
|
|
104
|
+
? (snapshot.recovery?.origin ?? "machine")
|
|
105
|
+
: "evaluation",
|
|
106
|
+
recovery: snapshot.recovery ?? null,
|
|
107
|
+
beforeCheckpoint: snapshot.beforeCheckpoint ?? null,
|
|
108
|
+
checkpoints: snapshot.checkpoints ?? [],
|
|
109
|
+
restore: snapshot.restore ?? null,
|
|
110
|
+
operatorAcceptance: snapshot.operatorAcceptance ?? null,
|
|
111
|
+
compiled: snapshot.compiled ?? null,
|
|
138
112
|
sequence: input.sequence,
|
|
139
113
|
attempt: snapshot.attempt,
|
|
140
114
|
promptArtifact: snapshot.promptArtifact ?? null,
|
|
@@ -142,12 +116,16 @@ function viewFromSnapshot(input) {
|
|
|
142
116
|
executionReport: evaluation?.execution_report ?? null,
|
|
143
117
|
verificationFlags: flags(result.verificationFlags),
|
|
144
118
|
result,
|
|
119
|
+
evidencePosture: result.evidencePosture ?? null,
|
|
145
120
|
action,
|
|
146
|
-
|
|
147
|
-
outcome,
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
:
|
|
121
|
+
agentSuccessClaim: result.agentSuccessClaim === true,
|
|
122
|
+
agentOutcome: result.agentOutcome ?? outcome,
|
|
123
|
+
...(evaluation && typeof evaluation.objective_refinement === "string" &&
|
|
124
|
+
evaluation.objective_refinement.trim().length > 0
|
|
125
|
+
? { objectiveRefinement: evaluation.objective_refinement }
|
|
126
|
+
: {}),
|
|
127
|
+
contractProposal,
|
|
128
|
+
contractRedirect: contractProposal?.supersedes ?? null,
|
|
151
129
|
contractBinding: snapshot.contractBinding ?? null,
|
|
152
130
|
beforeEvidence,
|
|
153
131
|
afterEvidence,
|
|
@@ -158,13 +136,17 @@ function viewFromSnapshot(input) {
|
|
|
158
136
|
outputSummary: evaluation?.output_summary,
|
|
159
137
|
constraintViolations: strings(evaluation?.constraint_violations),
|
|
160
138
|
discoveredConstraints: strings(evaluation?.discovered_constraints),
|
|
161
|
-
activeConstraints:
|
|
139
|
+
activeConstraints: snapshot.compiled
|
|
140
|
+
? [...snapshot.compiled.constraintsActive]
|
|
141
|
+
: [],
|
|
142
|
+
compiledRetirements: snapshot.compiled
|
|
143
|
+
? [...snapshot.compiled.constraintsRetired]
|
|
144
|
+
: [],
|
|
162
145
|
retractedConstraints: strings(evaluation?.retracted_constraints),
|
|
146
|
+
openQuestions: strings(evaluation?.open_questions),
|
|
147
|
+
resolvedOpenQuestionIds: strings(evaluation?.resolved_open_question_ids),
|
|
163
148
|
emergedSubtasks: strings(evaluation?.emerged_subtasks),
|
|
164
149
|
subgoalUpdates: subGoalUpdates(evaluation?.subgoal_updates),
|
|
165
|
-
backtrack: action === "backtrack"
|
|
166
|
-
? decodeBacktrackRecord(result)
|
|
167
|
-
: null,
|
|
168
150
|
};
|
|
169
151
|
}
|
|
170
152
|
export function decodeCommittedRound(entry) {
|
|
@@ -181,66 +163,107 @@ export function decodeCommittedRound(entry) {
|
|
|
181
163
|
// legacyTransactionRounds() reports the loss explicitly; it is never
|
|
182
164
|
// silently decoded into a partial view.
|
|
183
165
|
const snapshot = parseRoundTransactionSnapshot(transaction.snapshot);
|
|
184
|
-
if (!snapshot)
|
|
166
|
+
if (!snapshot || snapshot.kind !== "evaluation")
|
|
167
|
+
return null;
|
|
168
|
+
const identityIssue = transactionIdentityIssue(entry);
|
|
169
|
+
if (identityIssue)
|
|
185
170
|
return null;
|
|
186
171
|
const resultValue = isRecord(transaction.result)
|
|
187
172
|
? transaction.result
|
|
188
173
|
: snapshot.result;
|
|
189
|
-
|
|
174
|
+
// v4.0.3/v4.0.6: a current-schema (9) committed result must carry a
|
|
175
|
+
// well-formed Evidence Posture and a well-formed Agent outcome. A missing or
|
|
176
|
+
// corrupt posture/result is a HARD break, never re-derived at read time.
|
|
177
|
+
if (!isRecord(resultValue) || !isProcessResult(resultValue))
|
|
190
178
|
return null;
|
|
191
179
|
return viewFromSnapshot({
|
|
192
180
|
source: "feedback",
|
|
193
181
|
sourceEntry: entry,
|
|
194
|
-
lineage,
|
|
195
182
|
snapshot,
|
|
196
183
|
result: resultValue,
|
|
197
184
|
sequence: typeof entry.sequence === "number" ? entry.sequence : undefined,
|
|
198
185
|
});
|
|
199
186
|
}
|
|
200
|
-
/**
|
|
201
|
-
*
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
export function decodeMergedRound(entry) {
|
|
205
|
-
if (!isRecord(entry))
|
|
187
|
+
/** v3.9.6: decode a committed rollback directive event. It is a typed event
|
|
188
|
+
* in the round document, never a normal feedback round. */
|
|
189
|
+
export function decodeRollbackEvent(entry) {
|
|
190
|
+
if (entry.task_type !== "rollback_directive")
|
|
206
191
|
return null;
|
|
207
|
-
const
|
|
208
|
-
const
|
|
209
|
-
const action = committedAction(lineage.committed_action ?? raw.committed_action);
|
|
210
|
-
if (!action)
|
|
211
|
-
return null;
|
|
212
|
-
const transaction = isRecord(lineage.round_transaction)
|
|
192
|
+
const lineage = isRecord(entry.loop_lineage) ? entry.loop_lineage : null;
|
|
193
|
+
const transaction = lineage && isRecord(lineage.round_transaction)
|
|
213
194
|
? lineage.round_transaction
|
|
214
|
-
:
|
|
215
|
-
? raw.round_transaction
|
|
216
|
-
: null;
|
|
195
|
+
: null;
|
|
217
196
|
if (!transaction)
|
|
218
197
|
return null;
|
|
219
198
|
const snapshot = parseRoundTransactionSnapshot(transaction.snapshot);
|
|
220
|
-
if (!snapshot)
|
|
199
|
+
if (!snapshot || snapshot.kind !== "rollback" || !snapshot.recovery)
|
|
200
|
+
return null;
|
|
201
|
+
const identityIssue = transactionIdentityIssue(entry);
|
|
202
|
+
if (identityIssue)
|
|
221
203
|
return null;
|
|
222
204
|
const resultValue = isRecord(transaction.result)
|
|
223
205
|
? transaction.result
|
|
224
206
|
: snapshot.result;
|
|
225
|
-
if (!isRecord(resultValue))
|
|
207
|
+
if (!isRecord(resultValue) || !isProcessResult(resultValue))
|
|
208
|
+
return null;
|
|
209
|
+
const result = resultValue;
|
|
210
|
+
if (result.action !== "recovery" && result.action !== "backtrack")
|
|
226
211
|
return null;
|
|
227
212
|
return viewFromSnapshot({
|
|
228
|
-
source: "
|
|
229
|
-
sourceEntry:
|
|
230
|
-
lineage,
|
|
213
|
+
source: "rollback",
|
|
214
|
+
sourceEntry: entry,
|
|
231
215
|
snapshot,
|
|
232
|
-
result
|
|
233
|
-
sequence: typeof
|
|
216
|
+
result,
|
|
217
|
+
sequence: typeof entry.sequence === "number" ? entry.sequence : undefined,
|
|
234
218
|
});
|
|
235
219
|
}
|
|
236
220
|
/** Normalize committed history: rollback directives disappear, later records
|
|
237
221
|
* replace earlier records for the same logical round, and output is ascending. */
|
|
238
|
-
/**
|
|
239
|
-
* merged
|
|
240
|
-
*
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
222
|
+
/** v4.0.1: decode either an ordinary committed round or a rollback event.
|
|
223
|
+
* There is no merged-lineage representation any more: a committed round is
|
|
224
|
+
* exactly its durable :feedback envelope, and a rollback is its event. */
|
|
225
|
+
export function decodeCommittedRecord(entry) {
|
|
226
|
+
return decodeCommittedRound(entry) ??
|
|
227
|
+
decodeRollbackEvent(entry);
|
|
228
|
+
}
|
|
229
|
+
/** THE committed-view → compiler-request projection.
|
|
230
|
+
*
|
|
231
|
+
* The compiler once received these fields from the caller's mutable
|
|
232
|
+
* `last_round_result`, which existed on the live path and was absent on
|
|
233
|
+
* resume/unpause/backtrack compiles. This projection lets one committed
|
|
234
|
+
* fact source feed every consumer. Optional declarations are read from the
|
|
235
|
+
* decoded view where it exposes them. */
|
|
236
|
+
export function roundResultFromCommittedRound(view) {
|
|
237
|
+
const evaluation = view.evaluation;
|
|
238
|
+
return makeLoopRoundResult({
|
|
239
|
+
round: view.round,
|
|
240
|
+
agentSuccessClaim: view.agentSuccessClaim,
|
|
241
|
+
output_summary: evaluation?.output_summary ?? "",
|
|
242
|
+
constraint_violations: [...(evaluation?.constraint_violations ?? [])],
|
|
243
|
+
discovered_constraints: [...(view.discoveredConstraints ?? [])],
|
|
244
|
+
emerged_subtasks: [...(view.emergedSubtasks ?? [])],
|
|
245
|
+
subgoal_updates: [...(view.subgoalUpdates ?? [])],
|
|
246
|
+
execution_report: view.executionReport ?? undefined,
|
|
247
|
+
retracted_constraints: [...(view.retractedConstraints ?? [])],
|
|
248
|
+
wrong_assumptions: [...(evaluation?.wrong_assumptions ?? [])],
|
|
249
|
+
open_questions: [...(evaluation?.open_questions ?? [])],
|
|
250
|
+
resolved_open_question_ids: [...(evaluation?.resolved_open_question_ids ?? [])],
|
|
251
|
+
worker_results: [...(evaluation?.worker_results ?? [])],
|
|
252
|
+
subtask_boundary: evaluation?.subtask_boundary === true,
|
|
253
|
+
subtask_boundary_label: evaluation?.subtask_boundary_label ?? "",
|
|
254
|
+
agentOutcome: evaluation
|
|
255
|
+
? effectiveOutcome(evaluation)
|
|
256
|
+
: view.agentOutcome ?? undefined,
|
|
257
|
+
blocker: evaluation?.blocker,
|
|
258
|
+
retroactiveClaims: [...(evaluation?.retroactiveClaims ?? [])],
|
|
259
|
+
no_change_reason: evaluation?.no_change_reason,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
/** The ONE pending-directive lookup for a redo round. The directive's
|
|
263
|
+
* `target.redo_round` is the branch fence, so a deep restore and a
|
|
264
|
+
* same-round redo match the same way. */
|
|
265
|
+
export function pendingBacktrackRecord(entries, redoRound) {
|
|
266
|
+
return pendingRollbackDirective(entries, redoRound);
|
|
244
267
|
}
|
|
245
268
|
/** Lineage sub-object of an entry (`loop_lineage`; v3.7: the legacy
|
|
246
269
|
* top-level `lineage` alias was removed), or {} when absent. */
|
|
@@ -250,188 +273,114 @@ export function entryLineage(entry) {
|
|
|
250
273
|
const value = entry.loop_lineage;
|
|
251
274
|
return isRecord(value) ? value : {};
|
|
252
275
|
}
|
|
253
|
-
/**
|
|
254
|
-
*
|
|
255
|
-
* a
|
|
256
|
-
*
|
|
257
|
-
export function
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
const
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
.
|
|
297
|
-
|
|
298
|
-
}
|
|
299
|
-
/** Read constraints_active from an entry's lineage. */
|
|
300
|
-
export function entryActiveConstraints(entry) {
|
|
301
|
-
const view = decodeRound(entry);
|
|
302
|
-
if (view)
|
|
303
|
-
return view.activeConstraints ?? [];
|
|
304
|
-
const arr = entryLineage(entry).constraints_active;
|
|
305
|
-
return Array.isArray(arr) ? arr.filter((v) => typeof v === "string") : [];
|
|
306
|
-
}
|
|
307
|
-
/** Read retracted_constraints from an entry (direct field, else nested in
|
|
308
|
-
* the lineage). */
|
|
309
|
-
export function entryRetractedConstraints(entry) {
|
|
310
|
-
const view = decodeRound(entry);
|
|
311
|
-
if (view)
|
|
312
|
-
return view.retractedConstraints ?? [];
|
|
313
|
-
const record = isRecord(entry) ? entry : {};
|
|
314
|
-
const direct = record.retracted_constraints;
|
|
315
|
-
if (Array.isArray(direct))
|
|
316
|
-
return direct.filter((v) => typeof v === "string");
|
|
317
|
-
const nested = entryLineage(entry).retracted_constraints;
|
|
318
|
-
if (Array.isArray(nested))
|
|
319
|
-
return nested.filter((v) => typeof v === "string");
|
|
320
|
-
return [];
|
|
321
|
-
}
|
|
322
|
-
/** Read emerged_subtasks from an entry (handles direct + lineage nesting). */
|
|
323
|
-
export function entryEmergedSubtasks(entry) {
|
|
324
|
-
const view = decodeRound(entry);
|
|
325
|
-
if (view)
|
|
326
|
-
return view.emergedSubtasks ?? [];
|
|
327
|
-
const record = isRecord(entry) ? entry : {};
|
|
328
|
-
const direct = record.emerged_subtasks;
|
|
329
|
-
if (Array.isArray(direct))
|
|
330
|
-
return direct.filter((v) => typeof v === "string");
|
|
331
|
-
const nested = entryLineage(entry).emerged_subtasks;
|
|
332
|
-
if (Array.isArray(nested))
|
|
333
|
-
return nested.filter((v) => typeof v === "string");
|
|
334
|
-
return [];
|
|
335
|
-
}
|
|
336
|
-
/** Read subgoal_updates from an entry (handles direct + lineage nesting).
|
|
337
|
-
* v3.7.1: committed status transitions are replayed in round order by the
|
|
338
|
-
* compiler — every derivation replays ALL committed rounds, not just the
|
|
339
|
-
* last one. */
|
|
340
|
-
export function entrySubGoalUpdates(entry) {
|
|
341
|
-
const view = decodeRound(entry);
|
|
342
|
-
if (view)
|
|
343
|
-
return view.subgoalUpdates ?? [];
|
|
344
|
-
const record = isRecord(entry) ? entry : {};
|
|
345
|
-
const direct = record.subgoal_updates;
|
|
346
|
-
if (Array.isArray(direct))
|
|
347
|
-
return subGoalUpdates(direct);
|
|
348
|
-
const nested = entryLineage(entry).subgoal_updates;
|
|
349
|
-
if (Array.isArray(nested))
|
|
350
|
-
return subGoalUpdates(nested);
|
|
351
|
-
return [];
|
|
352
|
-
}
|
|
353
|
-
/** Read constraint_violations from an entry (entry-level, stored from the
|
|
354
|
-
* previous round's last_round_result at persist time — distinct from the
|
|
355
|
-
* evaluation-level violations on the committed view). Moved from
|
|
356
|
-
* verification-gate so lineage-shape reads live in this module only. */
|
|
357
|
-
export function entryViolations(entry) {
|
|
358
|
-
const record = isRecord(entry) ? entry : {};
|
|
359
|
-
const viols = record.constraint_violations;
|
|
360
|
-
if (Array.isArray(viols))
|
|
361
|
-
return viols.filter((v) => typeof v === "string");
|
|
362
|
-
return [];
|
|
363
|
-
}
|
|
364
|
-
export function historyRounds(views, beforeRound = Number.POSITIVE_INFINITY) {
|
|
276
|
+
/** Resolve the current branch from already-decoded committed views. This is
|
|
277
|
+
* the branch walker itself; `resolveBranch` is only the entry-decoding
|
|
278
|
+
* wrapper, so a caller that already holds the views (the history loader)
|
|
279
|
+
* never decodes the same entries twice. */
|
|
280
|
+
export function resolveBranchViews(records) {
|
|
281
|
+
const kindOf = (view) => view.kind ?? "evaluation";
|
|
282
|
+
const first = records.find((view) => kindOf(view) === "evaluation") ?? records[0];
|
|
283
|
+
if (!first) {
|
|
284
|
+
return { loopId: "", branchId: "", rounds: [], directives: [], pendingDirective: null };
|
|
285
|
+
}
|
|
286
|
+
const loopId = first.loopId;
|
|
287
|
+
const branchOf = (view) => view.branchId ?? rootBranchId(loopId);
|
|
288
|
+
let branchId = rootBranchId(loopId);
|
|
289
|
+
let rounds = records
|
|
290
|
+
.filter((view) => kindOf(view) === "evaluation" && branchOf(view) === branchId)
|
|
291
|
+
.sort((a, b) => a.round - b.round);
|
|
292
|
+
const directives = [];
|
|
293
|
+
const seen = new Set();
|
|
294
|
+
for (;;) {
|
|
295
|
+
const candidates = records
|
|
296
|
+
.filter((view) => kindOf(view) === "rollback" &&
|
|
297
|
+
branchOf(view) === branchId &&
|
|
298
|
+
view.recovery !== null && view.recovery !== undefined)
|
|
299
|
+
.sort((a, b) => a.round - b.round ||
|
|
300
|
+
(a.transactionId ?? "").localeCompare(b.transactionId ?? ""));
|
|
301
|
+
if (candidates.length === 0)
|
|
302
|
+
break;
|
|
303
|
+
// A branch closes exactly once. If corrupt data has more, the latest one
|
|
304
|
+
// is the effective directive; the others remain visible in the raw log.
|
|
305
|
+
const directive = candidates[candidates.length - 1];
|
|
306
|
+
const directiveId = directive.transactionId ?? directive.roundId;
|
|
307
|
+
if (seen.has(directiveId))
|
|
308
|
+
break;
|
|
309
|
+
seen.add(directiveId);
|
|
310
|
+
const recovery = directive.recovery;
|
|
311
|
+
// v3.9.7: the fence is the explicit redo round, not a nominal target
|
|
312
|
+
// round. A before/mid checkpoint's redo round is its owning round.
|
|
313
|
+
rounds = rounds.filter((view) => view.round < recovery.target.redo_round);
|
|
314
|
+
const child = recovery.transaction_id;
|
|
315
|
+
const childRounds = records
|
|
316
|
+
.filter((view) => kindOf(view) === "evaluation" && branchOf(view) === child)
|
|
317
|
+
.sort((a, b) => a.round - b.round);
|
|
318
|
+
rounds = [...rounds, ...childRounds];
|
|
319
|
+
directives.push(directive);
|
|
320
|
+
branchId = child;
|
|
321
|
+
}
|
|
365
322
|
const byRound = new Map();
|
|
366
|
-
for (const view of
|
|
367
|
-
if (!view || view.action === "backtrack" || view.round >= beforeRound)
|
|
368
|
-
continue;
|
|
323
|
+
for (const view of rounds)
|
|
369
324
|
byRound.set(view.round, view);
|
|
325
|
+
rounds = [...byRound.values()].sort((a, b) => a.round - b.round);
|
|
326
|
+
let pendingDirective = null;
|
|
327
|
+
const latest = directives[directives.length - 1];
|
|
328
|
+
if (latest && latest.recovery) {
|
|
329
|
+
const child = latest.recovery.transaction_id;
|
|
330
|
+
const hasChildRound = records.some((view) => kindOf(view) === "evaluation" && branchOf(view) === child);
|
|
331
|
+
if (!hasChildRound)
|
|
332
|
+
pendingDirective = latest;
|
|
333
|
+
}
|
|
334
|
+
return { loopId, branchId, rounds, directives, pendingDirective };
|
|
335
|
+
}
|
|
336
|
+
/** Resolve the current branch from committed records in the raw entry view.
|
|
337
|
+
* Decoding happens once here; the walker itself is `resolveBranchViews`. */
|
|
338
|
+
export function resolveBranch(entries) {
|
|
339
|
+
return resolveBranchViews(entries
|
|
340
|
+
.map(decodeCommittedRecord)
|
|
341
|
+
.filter((view) => view !== null));
|
|
342
|
+
}
|
|
343
|
+
/** v3.9.6: current-branch evaluation rounds. */
|
|
344
|
+
export function branchRounds(entries) {
|
|
345
|
+
return resolveBranch(entries).rounds;
|
|
346
|
+
}
|
|
347
|
+
/** v3.9.6: rollback directive chain. */
|
|
348
|
+
export function branchDirectives(entries) {
|
|
349
|
+
return resolveBranch(entries).directives;
|
|
350
|
+
}
|
|
351
|
+
/** v3.9.6: the pending directive controlling a redo round. */
|
|
352
|
+
export function pendingRollbackDirective(entries, redoRound) {
|
|
353
|
+
const pending = resolveBranch(entries).pendingDirective;
|
|
354
|
+
if (!pending?.recovery)
|
|
355
|
+
return null;
|
|
356
|
+
if (redoRound !== undefined && pending.recovery.target.redo_round !== redoRound) {
|
|
357
|
+
return null;
|
|
370
358
|
}
|
|
371
|
-
return
|
|
359
|
+
return pending;
|
|
360
|
+
}
|
|
361
|
+
/** v3.9.6: machine deadlock guard. Agent-origin directives never consume it. */
|
|
362
|
+
export function hasCommittedMachineBacktrack(entries, currentRound) {
|
|
363
|
+
return branchDirectives(entries).some((view) => {
|
|
364
|
+
const recovery = view.recovery;
|
|
365
|
+
return view.origin === "machine" &&
|
|
366
|
+
recovery !== null && recovery !== undefined &&
|
|
367
|
+
recovery.target.redo_round <= currentRound &&
|
|
368
|
+
view.round >= currentRound;
|
|
369
|
+
});
|
|
372
370
|
}
|
|
373
|
-
/** v3.
|
|
374
|
-
*
|
|
375
|
-
* `decodeRound` (hydrated merged lineage first, then the durable feedback
|
|
376
|
-
* entry) plus the rollback handling in `historyRounds` — so the compile path,
|
|
377
|
-
* the projection, the live coordinator, audit and explain can no longer
|
|
378
|
-
* disagree about which rounds are this branch's history, nor about how their
|
|
379
|
-
* envelope was interpreted.
|
|
380
|
-
*
|
|
381
|
-
* `currentRound` bounds the window exactly as before (default: unbounded). It
|
|
382
|
-
* is NOT a rollback fence: the redo re-commits the round numbers the abandoned
|
|
383
|
-
* rounds occupied, so abandonment is temporal, not numeric, and cannot be
|
|
384
|
-
* expressed as a bound here. */
|
|
371
|
+
/** v3.9.6: live history window = current branch below the round being
|
|
372
|
+
* compiled. `currentRound` is the round being prepared/submitted. */
|
|
385
373
|
export function derivationRounds(entries, currentRound = Number.POSITIVE_INFINITY) {
|
|
386
|
-
return
|
|
387
|
-
}
|
|
388
|
-
/** v3.8.1: the loop's durable round frontier — the session document's
|
|
389
|
-
* `current_round`. Null when no session document is in the entry set, so
|
|
390
|
-
* callers fail OPEN rather than inventing a bound. */
|
|
391
|
-
export function persistedFrontier(entries) {
|
|
392
|
-
for (const entry of entries) {
|
|
393
|
-
if (!isRecord(entry) || entry.task_type !== "session_state")
|
|
394
|
-
continue;
|
|
395
|
-
const round = entryLineage(entry).current_round;
|
|
396
|
-
return typeof round === "number" && Number.isFinite(round) && round >= 1
|
|
397
|
-
? round
|
|
398
|
-
: null;
|
|
399
|
-
}
|
|
400
|
-
return null;
|
|
374
|
+
return branchRounds(entries).filter((view) => view.round < currentRound);
|
|
401
375
|
}
|
|
402
|
-
/** v3.
|
|
403
|
-
*
|
|
404
|
-
* as `derivationRounds`, plus the bound those callers cannot compute
|
|
405
|
-
* themselves.
|
|
406
|
-
*
|
|
407
|
-
* The live paths bound their window at the round being compiled, and that
|
|
408
|
-
* bound is what keeps a backtracked branch out of this branch's history. A
|
|
409
|
-
* read-only view has no compile round, so it reads the SAME frontier from the
|
|
410
|
-
* durable session document and applies it only while a committed rollback
|
|
411
|
-
* directive is still in effect — a directive stops being history the moment
|
|
412
|
-
* the redo commit replaces its round's record, which is exactly when the
|
|
413
|
-
* abandoned rounds have been re-committed and must count again.
|
|
414
|
-
*
|
|
415
|
-
* With no directive in effect the window stays UNBOUNDED on purpose: after a
|
|
416
|
-
* stop, terminate or max_rounds the session's `current_round` sits ON the
|
|
417
|
-
* round it just committed, so bounding there would silently drop the loop's
|
|
418
|
-
* final round. An unobservable frontier fails open the same way — a read-only
|
|
419
|
-
* view never invents a fence, it only applies the one the runtime recorded. */
|
|
376
|
+
/** v3.9.6: read-only history = current branch rounds. No session frontier,
|
|
377
|
+
* no numeric fence, no second cursor. */
|
|
420
378
|
export function readOnlyRounds(entries) {
|
|
421
|
-
|
|
422
|
-
const rollbackPending = views.some((view) => view?.action === "backtrack");
|
|
423
|
-
if (!rollbackPending)
|
|
424
|
-
return historyRounds(views);
|
|
425
|
-
return historyRounds(views, persistedFrontier(entries) ?? Number.POSITIVE_INFINITY);
|
|
379
|
+
return branchRounds(entries);
|
|
426
380
|
}
|
|
427
|
-
/**
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
* over these facts, not a second observation collection. */
|
|
431
|
-
export function machineEvidenceForRound(view) {
|
|
432
|
-
if (view.afterEvidence.length > 0)
|
|
433
|
-
return view.afterEvidence;
|
|
434
|
-
return view.beforeEvidence;
|
|
381
|
+
/** v3.9.6: read-only rollback events = committed directive chain. */
|
|
382
|
+
export function readOnlyRollbacks(entries) {
|
|
383
|
+
return branchDirectives(entries);
|
|
435
384
|
}
|
|
436
385
|
export function legacyTransactionRounds(entries) {
|
|
437
386
|
const out = [];
|
|
@@ -459,12 +408,42 @@ export function legacyTransactionRounds(entries) {
|
|
|
459
408
|
// silently, which is the one thing this function exists to prevent.
|
|
460
409
|
const snapshot = envelope.snapshot;
|
|
461
410
|
if (isRecord(snapshot.promptArtifact)) {
|
|
462
|
-
const
|
|
411
|
+
const artifact = snapshot.promptArtifact;
|
|
412
|
+
const artifactVersion = artifact.schemaVersion;
|
|
463
413
|
if (typeof artifactVersion === "number" &&
|
|
464
414
|
artifactVersion !== PROMPT_ARTIFACT_SCHEMA_VERSION) {
|
|
465
415
|
out.push({ round, schemaVersion: artifactVersion, envelope: "prompt_artifact" });
|
|
466
416
|
continue;
|
|
467
417
|
}
|
|
418
|
+
// v4.0.5: the same runtime whitelists the decoder uses. A malformed
|
|
419
|
+
// optional invocation instruction is a legacy artifact, not accepted
|
|
420
|
+
// silently.
|
|
421
|
+
const validInstruction = artifact.currentUserInstruction === undefined ||
|
|
422
|
+
(typeof artifact.currentUserInstruction === "string" &&
|
|
423
|
+
artifact.currentUserInstruction.trim().length > 0);
|
|
424
|
+
const validContextFields = (artifact.level === "l0" || artifact.level === "l1" || artifact.level === "l2") &&
|
|
425
|
+
isPromptContextMode(artifact.contextMode) &&
|
|
426
|
+
isPromptContextReason(artifact.contextReason) &&
|
|
427
|
+
validInstruction &&
|
|
428
|
+
typeof artifact.attempt === "number" &&
|
|
429
|
+
typeof artifact.round === "number";
|
|
430
|
+
const contextIssue = validContextFields
|
|
431
|
+
? promptContextPairIssue({
|
|
432
|
+
level: artifact.level,
|
|
433
|
+
mode: artifact.contextMode,
|
|
434
|
+
reason: artifact.contextReason,
|
|
435
|
+
attempt: artifact.attempt,
|
|
436
|
+
round: artifact.round,
|
|
437
|
+
})
|
|
438
|
+
: "missing or malformed context boundary fields";
|
|
439
|
+
if (contextIssue !== null) {
|
|
440
|
+
out.push({
|
|
441
|
+
round,
|
|
442
|
+
schemaVersion: typeof artifactVersion === "number" ? artifactVersion : -1,
|
|
443
|
+
envelope: "prompt_context",
|
|
444
|
+
});
|
|
445
|
+
continue;
|
|
446
|
+
}
|
|
468
447
|
}
|
|
469
448
|
// v3.9.1: an observation schema mismatch is also a hard break. The
|
|
470
449
|
// transaction parser drops the whole round on it; report the loss here
|
|
@@ -474,16 +453,16 @@ export function legacyTransactionRounds(entries) {
|
|
|
474
453
|
...(Array.isArray(snapshot.afterEvidence) ? snapshot.afterEvidence : []),
|
|
475
454
|
];
|
|
476
455
|
for (const item of observations) {
|
|
477
|
-
if (!isRecord(item)
|
|
456
|
+
if (!isRecord(item)) {
|
|
478
457
|
out.push({ round, schemaVersion: -1, envelope: "observation" });
|
|
479
458
|
break;
|
|
480
459
|
}
|
|
481
|
-
|
|
482
|
-
const
|
|
483
|
-
if (
|
|
460
|
+
// v3.9.9: the SAME version judgment the transaction parser uses.
|
|
461
|
+
const issue = observationVersionIssue(item.kind, item.schemaVersion, item.data, item.status);
|
|
462
|
+
if (issue) {
|
|
484
463
|
out.push({
|
|
485
464
|
round,
|
|
486
|
-
schemaVersion:
|
|
465
|
+
schemaVersion: issue.schemaVersion,
|
|
487
466
|
envelope: "observation",
|
|
488
467
|
});
|
|
489
468
|
break;
|
|
@@ -492,106 +471,4 @@ export function legacyTransactionRounds(entries) {
|
|
|
492
471
|
}
|
|
493
472
|
return out.sort((a, b) => a.round - b.round);
|
|
494
473
|
}
|
|
495
|
-
/** The ONE definition of machine Git motion for a single round.
|
|
496
|
-
*
|
|
497
|
-
* Motion is a non-empty file list, a HEAD that moved across the round, or a
|
|
498
|
-
* HEAD that landed on the rollback's restore point — restoring the workspace
|
|
499
|
-
* IS that round's work, and would otherwise read as a round that did
|
|
500
|
-
* nothing. A clean worktree is not the same thing as no progress:
|
|
501
|
-
* commit-per-slice workflows leave no file delta even though HEAD advanced.
|
|
502
|
-
*
|
|
503
|
-
* Observability stays the caller's concern: this answers "did this round
|
|
504
|
-
* move", never "could the round be observed". The restore-point arm compares
|
|
505
|
-
* short hashes, the rule `checkBacktrackGitHeadRestore` uses; the
|
|
506
|
-
* before/after arm compares the observations themselves. */
|
|
507
|
-
export function gitMotionSignal(input) {
|
|
508
|
-
if (input.changedFiles !== null && input.changedFiles.length > 0)
|
|
509
|
-
return true;
|
|
510
|
-
const { headBefore, headAfter, restoreTargetHead } = input;
|
|
511
|
-
if (headAfter && headBefore && headAfter !== headBefore)
|
|
512
|
-
return true;
|
|
513
|
-
if (headAfter && restoreTargetHead &&
|
|
514
|
-
headAfter.slice(0, 12) === restoreTargetHead.slice(0, 12))
|
|
515
|
-
return true;
|
|
516
|
-
if (input.changedFiles === null)
|
|
517
|
-
return null;
|
|
518
|
-
// An observed empty change set is no motion only when both HEADs are known;
|
|
519
|
-
// otherwise a commit-only slice could still have moved HEAD.
|
|
520
|
-
if (!headBefore || !headAfter)
|
|
521
|
-
return null;
|
|
522
|
-
return false;
|
|
523
|
-
}
|
|
524
|
-
/** Git HEAD as a machine observation exposes it, or null when it is silent. */
|
|
525
|
-
export function observedGitHead(observation) {
|
|
526
|
-
return observation && isRecord(observation.data) &&
|
|
527
|
-
typeof observation.data.head === "string" && observation.data.head
|
|
528
|
-
? observation.data.head
|
|
529
|
-
: null;
|
|
530
|
-
}
|
|
531
|
-
function observedGitObservation(observation) {
|
|
532
|
-
return observation && observation.kind === "git" && observation.status === "observed"
|
|
533
|
-
? observation
|
|
534
|
-
: null;
|
|
535
|
-
}
|
|
536
|
-
function observedGitIn(observations) {
|
|
537
|
-
return observations.find((observation) => observation.kind === "git" && observation.status === "observed") ?? null;
|
|
538
|
-
}
|
|
539
|
-
/** The in-flight round's own motion, from its derived Git delta. A Git
|
|
540
|
-
* boundary that cannot be compared yields `null` unless HEAD movement or the
|
|
541
|
-
* restore point independently proves motion. */
|
|
542
|
-
export function inFlightGitMotion(deltas, restoreTargetHead) {
|
|
543
|
-
const git = deltas.find((delta) => delta.kind === "git");
|
|
544
|
-
if (!git)
|
|
545
|
-
return null;
|
|
546
|
-
const before = observedGitObservation(git.before);
|
|
547
|
-
const after = observedGitObservation(git.after);
|
|
548
|
-
return gitMotionSignal({
|
|
549
|
-
changedFiles: git.changedFiles,
|
|
550
|
-
headBefore: observedGitHead(before ?? undefined),
|
|
551
|
-
headAfter: observedGitHead(after ?? undefined),
|
|
552
|
-
restoreTargetHead: restoreTargetHead ?? null,
|
|
553
|
-
});
|
|
554
|
-
}
|
|
555
|
-
/** Machine-observed git motion for the most recent contiguous window.
|
|
556
|
-
* A clean worktree is not the same thing as no progress: commit-per-slice
|
|
557
|
-
* workflows leave no file delta even though HEAD advanced. Unknown motion in
|
|
558
|
-
* any round makes the window undecidable rather than falsely empty. */
|
|
559
|
-
export function machineGitMotionSeries(rounds, currentRound, lookback) {
|
|
560
|
-
if (!Number.isInteger(lookback) || lookback < 1 || currentRound <= lookback)
|
|
561
|
-
return null;
|
|
562
|
-
const byRound = new Map();
|
|
563
|
-
for (const round of rounds) {
|
|
564
|
-
if (round.round < 1 || round.round >= currentRound)
|
|
565
|
-
continue;
|
|
566
|
-
byRound.set(round.round, round);
|
|
567
|
-
}
|
|
568
|
-
const recent = [];
|
|
569
|
-
for (let number = currentRound - lookback; number < currentRound; number++) {
|
|
570
|
-
const round = byRound.get(number);
|
|
571
|
-
if (!round)
|
|
572
|
-
return null;
|
|
573
|
-
if (!round.observationDelta.some((delta) => delta.kind === "git"))
|
|
574
|
-
return null;
|
|
575
|
-
recent.push(round);
|
|
576
|
-
}
|
|
577
|
-
const motion = [];
|
|
578
|
-
for (const round of recent) {
|
|
579
|
-
const git = round.observationDelta.find((delta) => delta.kind === "git");
|
|
580
|
-
// The round's baseline: its own before observation, else the previous
|
|
581
|
-
// round's after observation. Older Git observations do not carry a HEAD
|
|
582
|
-
// field; their change set is still a valid motion signal.
|
|
583
|
-
const baseline = observedGitObservation(git.before)
|
|
584
|
-
?? observedGitIn(byRound.get(round.round - 1)?.afterEvidence ?? []);
|
|
585
|
-
const after = observedGitObservation(git.after);
|
|
586
|
-
const signal = gitMotionSignal({
|
|
587
|
-
changedFiles: git.changedFiles,
|
|
588
|
-
headBefore: observedGitHead(baseline ?? undefined),
|
|
589
|
-
headAfter: observedGitHead(after ?? undefined),
|
|
590
|
-
});
|
|
591
|
-
if (signal === null)
|
|
592
|
-
return null;
|
|
593
|
-
motion.push(signal);
|
|
594
|
-
}
|
|
595
|
-
return motion;
|
|
596
|
-
}
|
|
597
474
|
//# sourceMappingURL=committed-round.js.map
|