@fro.bot/systematic 3.5.0 → 3.5.1
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/dist/index.js +164 -23
- package/dist/lib/workflow-guard.d.ts +14 -0
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -3320,9 +3320,20 @@ function applyUnitStart(marker, epoch, context) {
|
|
|
3320
3320
|
return;
|
|
3321
3321
|
}
|
|
3322
3322
|
if (current.unitId === marker.unitId) {
|
|
3323
|
-
if (
|
|
3323
|
+
if (current.state === "completed")
|
|
3324
|
+
return "out-of-order";
|
|
3325
|
+
if (!sameUnitDeclaration(current, marker) && unitHasMintedEvidence(current, context)) {
|
|
3326
|
+
return "out-of-order";
|
|
3327
|
+
}
|
|
3328
|
+
if (sameUnitDeclaration(current, marker) && current.transitionDigest !== marker.transitionDigest) {
|
|
3324
3329
|
return "conflicting-marker";
|
|
3325
|
-
|
|
3330
|
+
}
|
|
3331
|
+
if (!unitDeclarationExtends(current, marker))
|
|
3332
|
+
return "conflicting-marker";
|
|
3333
|
+
if (sameUnitDeclaration(current, marker))
|
|
3334
|
+
return;
|
|
3335
|
+
context.progression = { epoch, unit: snapshot };
|
|
3336
|
+
return;
|
|
3326
3337
|
}
|
|
3327
3338
|
if (current.state !== "completed")
|
|
3328
3339
|
return "out-of-order";
|
|
@@ -3346,9 +3357,26 @@ function applyUnitComplete(marker, epoch, context) {
|
|
|
3346
3357
|
};
|
|
3347
3358
|
return;
|
|
3348
3359
|
}
|
|
3360
|
+
function unitHasMintedEvidence(current, context) {
|
|
3361
|
+
return [...context.mintByReceipt.values()].some((envelope) => envelope.canonical.epochDigest === current.epochDigest && envelope.canonical.unitDigest === current.unitDigest);
|
|
3362
|
+
}
|
|
3349
3363
|
function sameUnitDeclaration(current, marker) {
|
|
3350
3364
|
return current.epochDigest === marker.epochDigest && current.unitDigest === marker.unitDigest && current.family === marker.family && JSON.stringify(current.requiredOperations) === JSON.stringify(marker.requiredOperations) && JSON.stringify(current.resourceScopes) === JSON.stringify(marker.resourceScopes);
|
|
3351
3365
|
}
|
|
3366
|
+
function unitDeclarationExtends(current, marker) {
|
|
3367
|
+
if (current.epochDigest !== marker.epochDigest || current.unitDigest !== marker.unitDigest || current.family !== marker.family) {
|
|
3368
|
+
return false;
|
|
3369
|
+
}
|
|
3370
|
+
const nextOperations = new Set(marker.requiredOperations);
|
|
3371
|
+
if (current.requiredOperations.some((operation) => !nextOperations.has(operation))) {
|
|
3372
|
+
return false;
|
|
3373
|
+
}
|
|
3374
|
+
const nextScopes = new Map(marker.resourceScopes.map((scope) => [
|
|
3375
|
+
scope.operation,
|
|
3376
|
+
scope.resourceIdentity
|
|
3377
|
+
]));
|
|
3378
|
+
return [...current.resourceScopes].every((scope) => nextScopes.get(scope.operation) === scope.resourceIdentity);
|
|
3379
|
+
}
|
|
3352
3380
|
function foldProgressionMarker(marker, context) {
|
|
3353
3381
|
return marker.target === "epoch" ? applyEpochProgression(marker, context) : applyUnitProgression(marker, context);
|
|
3354
3382
|
}
|
|
@@ -8473,7 +8501,11 @@ function cloneUnit(unit) {
|
|
|
8473
8501
|
requiredOperations: unit.requiredOperations,
|
|
8474
8502
|
requiredResourceOperations: Object.freeze([
|
|
8475
8503
|
...unit.declaredResourceOperations
|
|
8476
|
-
])
|
|
8504
|
+
]),
|
|
8505
|
+
resourceScopes: Object.freeze([...unit.resourceScopes].map(([operation, resourceIdentity]) => ({
|
|
8506
|
+
operation,
|
|
8507
|
+
resourceIdentity
|
|
8508
|
+
})))
|
|
8477
8509
|
});
|
|
8478
8510
|
}
|
|
8479
8511
|
function familyForSkill(skill) {
|
|
@@ -8900,6 +8932,13 @@ function createWorkflowGuard(options) {
|
|
|
8900
8932
|
return "receipt-mismatch";
|
|
8901
8933
|
return context.worktreeIdentity !== currentWorktreeIdentity ? "receipt-mismatch" : undefined;
|
|
8902
8934
|
}
|
|
8935
|
+
function currentOperationContext() {
|
|
8936
|
+
return Object.freeze({
|
|
8937
|
+
workspaceIdentity: currentWorkspaceIdentity,
|
|
8938
|
+
...currentRepositoryIdentity === undefined ? {} : { repositoryIdentity: currentRepositoryIdentity },
|
|
8939
|
+
...currentWorktreeIdentity === undefined ? {} : { worktreeIdentity: currentWorktreeIdentity }
|
|
8940
|
+
});
|
|
8941
|
+
}
|
|
8903
8942
|
function resourceBeforeReason(input, unit) {
|
|
8904
8943
|
if (!operationUsesResource(input.operation))
|
|
8905
8944
|
return;
|
|
@@ -9591,9 +9630,10 @@ function createWorkflowGuard(options) {
|
|
|
9591
9630
|
}
|
|
9592
9631
|
return;
|
|
9593
9632
|
}
|
|
9594
|
-
function mergeResourceScopes(trustedPolicy, modelScopes) {
|
|
9595
|
-
const result = new Map(runtimeScopes);
|
|
9596
|
-
|
|
9633
|
+
function mergeResourceScopes(trustedPolicy, modelScopes, existingScopes) {
|
|
9634
|
+
const result = new Map(existingScopes ?? runtimeScopes);
|
|
9635
|
+
const scopesToMerge = existingScopes ? [runtimeScopes, trustedPolicy.resourceScopes, modelScopes] : [trustedPolicy.resourceScopes, modelScopes];
|
|
9636
|
+
for (const scopes of scopesToMerge) {
|
|
9597
9637
|
for (const [operation, resource] of scopes) {
|
|
9598
9638
|
const existing = result.get(operation);
|
|
9599
9639
|
if (existing && existing !== resource)
|
|
@@ -9603,22 +9643,71 @@ function createWorkflowGuard(options) {
|
|
|
9603
9643
|
}
|
|
9604
9644
|
return result;
|
|
9605
9645
|
}
|
|
9606
|
-
function requiredOperationsFor(trustedPolicy, model, resourceScopes) {
|
|
9646
|
+
function requiredOperationsFor(trustedPolicy, model, resourceScopes, existingOperations = []) {
|
|
9607
9647
|
return Object.freeze([
|
|
9608
9648
|
...new Set([
|
|
9609
9649
|
...MANDATORY_OPERATIONS,
|
|
9610
9650
|
...runtimeRequired,
|
|
9651
|
+
...existingOperations,
|
|
9611
9652
|
...trustedPolicy.expectedOperations,
|
|
9612
9653
|
...model.expectedOperations,
|
|
9613
9654
|
...resourceScopes.keys()
|
|
9614
9655
|
])
|
|
9615
9656
|
]);
|
|
9616
9657
|
}
|
|
9658
|
+
function resourceIdentitiesMatchUnit(unit) {
|
|
9659
|
+
const expected = new Map(runtimeScopes);
|
|
9660
|
+
for (const [operation, resource] of unit.resourceScopes) {
|
|
9661
|
+
expected.set(operation, resource);
|
|
9662
|
+
}
|
|
9663
|
+
if (currentResourceIdentities.size !== expected.size)
|
|
9664
|
+
return false;
|
|
9665
|
+
for (const [operation, resource] of expected) {
|
|
9666
|
+
if (currentResourceIdentities.get(operation) !== resource)
|
|
9667
|
+
return false;
|
|
9668
|
+
}
|
|
9669
|
+
return true;
|
|
9670
|
+
}
|
|
9671
|
+
function pristineActiveUnit(unit) {
|
|
9672
|
+
return unit.evidence.size === 0 && unit.issues.size === 0 && unit.staleReceiptIds.size === 0 && unit.recoveredReceiptIds.size === 0 && unit.operationStates.size === 0 && unit.ledgerContexts.size === 0 && globalIssue === undefined && transitionsByCall.size === 0 && terminalOperationCalls.size === 0 && currentResourceRevisionIdentities.size === 0 && currentPullRequestFingerprint === undefined && currentWorkspaceIdentity === initialWorkspaceIdentity && currentRepositoryIdentity === initialRepositoryIdentity && currentWorktreeIdentity === initialWorktreeIdentity && resourceIdentitiesMatchUnit(unit);
|
|
9673
|
+
}
|
|
9674
|
+
function declarationChanged(unit, requiredOperations, resourceScopes) {
|
|
9675
|
+
if (JSON.stringify(unit.requiredOperations) !== JSON.stringify(requiredOperations)) {
|
|
9676
|
+
return true;
|
|
9677
|
+
}
|
|
9678
|
+
if (unit.resourceScopes.size !== resourceScopes.size)
|
|
9679
|
+
return true;
|
|
9680
|
+
for (const [operation, resource] of resourceScopes) {
|
|
9681
|
+
if (unit.resourceScopes.get(operation) !== resource)
|
|
9682
|
+
return true;
|
|
9683
|
+
}
|
|
9684
|
+
return false;
|
|
9685
|
+
}
|
|
9686
|
+
function startActiveUnit(parsed, trustedPolicy, unit) {
|
|
9687
|
+
if (!pristineActiveUnit(unit)) {
|
|
9688
|
+
return { status: "rejected", reasonCode: "unit-active" };
|
|
9689
|
+
}
|
|
9690
|
+
const resourceScopes = mergeResourceScopes(trustedPolicy, parsed.resourceScopes, unit.resourceScopes);
|
|
9691
|
+
if (!resourceScopes) {
|
|
9692
|
+
return { status: "rejected", reasonCode: "runtime-scope-conflict" };
|
|
9693
|
+
}
|
|
9694
|
+
const requiredOperations = requiredOperationsFor(trustedPolicy, parsed, resourceScopes, unit.requiredOperations);
|
|
9695
|
+
if (!declarationChanged(unit, requiredOperations, resourceScopes)) {
|
|
9696
|
+
return { status: "rejected", reasonCode: "unit-active" };
|
|
9697
|
+
}
|
|
9698
|
+
unit.requiredOperations = requiredOperations;
|
|
9699
|
+
unit.declaredResourceOperations = Object.freeze([...resourceScopes.keys()]);
|
|
9700
|
+
unit.resourceScopes = resourceScopes;
|
|
9701
|
+
for (const [operation, resource] of resourceScopes) {
|
|
9702
|
+
currentResourceIdentities.set(operation, resource);
|
|
9703
|
+
}
|
|
9704
|
+
return { status: "started", unit: cloneUnit(unit) };
|
|
9705
|
+
}
|
|
9617
9706
|
function startParsedUnit(parsed, trustedPolicy) {
|
|
9618
9707
|
if (!epoch)
|
|
9619
9708
|
return { status: "rejected", reasonCode: "no-active-epoch" };
|
|
9620
9709
|
if (epoch.unit?.status === "active") {
|
|
9621
|
-
return
|
|
9710
|
+
return startActiveUnit(parsed, trustedPolicy, epoch.unit);
|
|
9622
9711
|
}
|
|
9623
9712
|
const resourceScopes = mergeResourceScopes(trustedPolicy, parsed.resourceScopes);
|
|
9624
9713
|
if (!resourceScopes) {
|
|
@@ -9706,7 +9795,7 @@ function createWorkflowGuard(options) {
|
|
|
9706
9795
|
reasonCode: existing.state === "abandoned" ? "abandoned-transition" : "transition-terminal"
|
|
9707
9796
|
};
|
|
9708
9797
|
}
|
|
9709
|
-
async function processOperation(input, parsed, unit) {
|
|
9798
|
+
async function processOperation(input, parsed, unit, trustedClassification) {
|
|
9710
9799
|
if (!unit.requiredOperations.includes(parsed.operation)) {
|
|
9711
9800
|
markTerminalOperation(parsed, input);
|
|
9712
9801
|
return { status: "rejected", reasonCode: "operation-not-required" };
|
|
@@ -9725,7 +9814,7 @@ function createWorkflowGuard(options) {
|
|
|
9725
9814
|
if (prepared.status !== "prepared") {
|
|
9726
9815
|
return prepared.reasonCode === "call-context-conflict" ? { status: "rejected", reasonCode: "call-context-conflict" } : { status: "rejected", reasonCode: "rejected-operation" };
|
|
9727
9816
|
}
|
|
9728
|
-
const classification = await classifyPreparedOperation(parsed, input, unit);
|
|
9817
|
+
const classification = trustedClassification ?? await classifyPreparedOperation(parsed, input, unit);
|
|
9729
9818
|
return classification ? finalizeOperation(parsed, input, unit, classification) : { status: "rejected", reasonCode: "guard-unavailable" };
|
|
9730
9819
|
}
|
|
9731
9820
|
function rejectParsedOperation(parsed, input, unit, reasonCode) {
|
|
@@ -10231,12 +10320,44 @@ function createWorkflowGuard(options) {
|
|
|
10231
10320
|
}
|
|
10232
10321
|
return processOperation(input, parsed, epoch.unit);
|
|
10233
10322
|
},
|
|
10323
|
+
async observeTrustedRecoveredOperation(input) {
|
|
10324
|
+
const modeResult = evidenceModeResult();
|
|
10325
|
+
if (modeResult)
|
|
10326
|
+
return modeResult;
|
|
10327
|
+
if (!epoch)
|
|
10328
|
+
return { status: "rejected", reasonCode: "no-active-epoch" };
|
|
10329
|
+
if (!epoch.unit)
|
|
10330
|
+
return { status: "rejected", reasonCode: "no-active-unit" };
|
|
10331
|
+
if (epoch.unit.status === "completed") {
|
|
10332
|
+
return { status: "rejected", reasonCode: "unit-completed" };
|
|
10333
|
+
}
|
|
10334
|
+
const terminalResult = terminalOperationResult(input);
|
|
10335
|
+
if (terminalResult)
|
|
10336
|
+
return terminalResult;
|
|
10337
|
+
const parsed = parseReceiptOperationObservation(input);
|
|
10338
|
+
if (!parsed) {
|
|
10339
|
+
markTerminalOperation(undefined, input);
|
|
10340
|
+
return recordGlobalEvidenceIssue("invalid-receipt");
|
|
10341
|
+
}
|
|
10342
|
+
const classification = {
|
|
10343
|
+
outcome: "accepted",
|
|
10344
|
+
category: parsed.operation,
|
|
10345
|
+
attribution: "runtime-verified",
|
|
10346
|
+
result: "success",
|
|
10347
|
+
sideEffect: parsed.operation === "verification" || parsed.operation === "check-readback" || parsed.operation === "review-readback" ? "not-required" : "required",
|
|
10348
|
+
reasonCode: "recognized-command"
|
|
10349
|
+
};
|
|
10350
|
+
return processOperation(input, parsed, epoch.unit, classification);
|
|
10351
|
+
},
|
|
10234
10352
|
observeReadback(input) {
|
|
10235
10353
|
const parsed = parseReadback(input);
|
|
10236
10354
|
if (!parsed)
|
|
10237
10355
|
return { status: "rejected", reasonCode: "invalid-receipt" };
|
|
10238
10356
|
return observeRevision(parsed, epoch?.unit);
|
|
10239
10357
|
},
|
|
10358
|
+
currentOperationContext() {
|
|
10359
|
+
return currentOperationContext();
|
|
10360
|
+
},
|
|
10240
10361
|
status() {
|
|
10241
10362
|
return projection();
|
|
10242
10363
|
},
|
|
@@ -11156,6 +11277,7 @@ function createSessionRuntime(options) {
|
|
|
11156
11277
|
const rollupKey = `${host.sessionID}:${host.callID}:${childSessionID}`;
|
|
11157
11278
|
if (rolledUpChildren.has(rollupKey))
|
|
11158
11279
|
return;
|
|
11280
|
+
const parentBefore = guard.currentOperationContext();
|
|
11159
11281
|
const children = await options.hostReadback.listChildren(host.sessionID);
|
|
11160
11282
|
if (!children.some((child) => child.sessionId === childSessionID && child.parentID === host.sessionID)) {
|
|
11161
11283
|
markUnavailable();
|
|
@@ -11203,18 +11325,27 @@ function createSessionRuntime(options) {
|
|
|
11203
11325
|
const currentStatus = guard.status();
|
|
11204
11326
|
if (!currentStatus.epoch || !currentStatus.unit)
|
|
11205
11327
|
return;
|
|
11206
|
-
const expectedWorkspace =
|
|
11207
|
-
const expectedRepository =
|
|
11208
|
-
const expectedWorktree =
|
|
11209
|
-
|
|
11328
|
+
const expectedWorkspace = childLedger.digestIdentity("workspace", parentBefore.workspaceIdentity);
|
|
11329
|
+
const expectedRepository = childLedger.digestIdentity("repository", current.snapshot.repositoryRevisionDigest);
|
|
11330
|
+
const expectedWorktree = childLedger.digestIdentity("worktree", current.snapshot.worktreeRevisionDigest);
|
|
11331
|
+
const candidates = [];
|
|
11210
11332
|
for (const childReceipt of recovered.receipts) {
|
|
11211
11333
|
const operation = childReceipt.canonical.operation;
|
|
11212
11334
|
if (!localOperation(operation))
|
|
11213
11335
|
continue;
|
|
11214
|
-
if (childReceipt.canonical.workspaceDigest !== expectedWorkspace
|
|
11336
|
+
if (childReceipt.canonical.workspaceDigest !== expectedWorkspace) {
|
|
11215
11337
|
markUnavailable();
|
|
11216
11338
|
return;
|
|
11217
11339
|
}
|
|
11340
|
+
if (childReceipt.canonical.repositoryDigest !== expectedRepository || childReceipt.canonical.worktreeDigest !== expectedWorktree) {
|
|
11341
|
+
continue;
|
|
11342
|
+
}
|
|
11343
|
+
candidates.push(childReceipt);
|
|
11344
|
+
}
|
|
11345
|
+
let minted = false;
|
|
11346
|
+
for (const childReceipt of candidates) {
|
|
11347
|
+
const operation = childReceipt.canonical.operation;
|
|
11348
|
+
const parentContext = guard.currentOperationContext();
|
|
11218
11349
|
const callID = `task-${host.callID}-${childReceipt.canonical.receiptId}`;
|
|
11219
11350
|
const observation = {
|
|
11220
11351
|
callId: callID,
|
|
@@ -11223,12 +11354,12 @@ function createSessionRuntime(options) {
|
|
|
11223
11354
|
context: {
|
|
11224
11355
|
epochId: currentStatus.epoch.epochId,
|
|
11225
11356
|
unitId: currentStatus.unit.unitId,
|
|
11226
|
-
workspaceIdentity:
|
|
11227
|
-
repositoryIdentity
|
|
11228
|
-
worktreeIdentity
|
|
11357
|
+
workspaceIdentity: parentContext.workspaceIdentity,
|
|
11358
|
+
...parentContext.repositoryIdentity ? { repositoryIdentity: parentContext.repositoryIdentity } : {},
|
|
11359
|
+
...parentContext.worktreeIdentity ? { worktreeIdentity: parentContext.worktreeIdentity } : {}
|
|
11229
11360
|
},
|
|
11230
11361
|
after: {
|
|
11231
|
-
workspaceIdentity:
|
|
11362
|
+
workspaceIdentity: parentContext.workspaceIdentity,
|
|
11232
11363
|
repositoryIdentity: current.snapshot.repositoryRevisionDigest,
|
|
11233
11364
|
worktreeIdentity: current.snapshot.worktreeRevisionDigest
|
|
11234
11365
|
},
|
|
@@ -11238,7 +11369,7 @@ function createSessionRuntime(options) {
|
|
|
11238
11369
|
noOp: false
|
|
11239
11370
|
}
|
|
11240
11371
|
};
|
|
11241
|
-
const result = await guard.
|
|
11372
|
+
const result = await guard.observeTrustedRecoveredOperation(observation);
|
|
11242
11373
|
if (result.status === "accepted") {
|
|
11243
11374
|
const receipt = receiptForOperation(callID, operation);
|
|
11244
11375
|
if (receipt)
|
|
@@ -11433,13 +11564,21 @@ function createSessionRuntime(options) {
|
|
|
11433
11564
|
}
|
|
11434
11565
|
return result;
|
|
11435
11566
|
}
|
|
11567
|
+
function progressionResourceScopes(unit) {
|
|
11568
|
+
return [...unit.resourceScopes].sort((first, second) => first.operation.localeCompare(second.operation)).map((scope) => ({
|
|
11569
|
+
operation: scope.operation,
|
|
11570
|
+
resourceIdentity: ledger.digestIdentity("resource", scope.resourceIdentity)
|
|
11571
|
+
}));
|
|
11572
|
+
}
|
|
11436
11573
|
function writeStartResult(output, result) {
|
|
11437
11574
|
if (!isRecord7(output))
|
|
11438
11575
|
return;
|
|
11576
|
+
const existingMetadata = isRecord7(output.metadata) ? output.metadata : {};
|
|
11439
11577
|
if (result.status === "started") {
|
|
11440
11578
|
output.title = "Workflow unit started";
|
|
11441
11579
|
output.output = JSON.stringify({ status: "started" });
|
|
11442
11580
|
output.metadata = {
|
|
11581
|
+
...existingMetadata,
|
|
11443
11582
|
...metadata2(),
|
|
11444
11583
|
workflowGuard: { status: "started" }
|
|
11445
11584
|
};
|
|
@@ -11452,6 +11591,7 @@ function createSessionRuntime(options) {
|
|
|
11452
11591
|
reasonCode
|
|
11453
11592
|
});
|
|
11454
11593
|
output.metadata = {
|
|
11594
|
+
...existingMetadata,
|
|
11455
11595
|
...metadata2(),
|
|
11456
11596
|
workflowGuard: {
|
|
11457
11597
|
status: "rejected",
|
|
@@ -11783,7 +11923,7 @@ function createSessionRuntime(options) {
|
|
|
11783
11923
|
unitId: status.unit.unitId,
|
|
11784
11924
|
family: status.epoch.family,
|
|
11785
11925
|
requiredOperations: status.unit.requiredOperations,
|
|
11786
|
-
resourceScopes:
|
|
11926
|
+
resourceScopes: progressionResourceScopes(status.unit),
|
|
11787
11927
|
state: "started",
|
|
11788
11928
|
transitionDigest: ledger.digestIdentity("call", host.callID)
|
|
11789
11929
|
}));
|
|
@@ -11840,7 +11980,7 @@ function createSessionRuntime(options) {
|
|
|
11840
11980
|
unitId: unit.unitId,
|
|
11841
11981
|
family: epoch.family,
|
|
11842
11982
|
requiredOperations: unit.requiredOperations,
|
|
11843
|
-
resourceScopes:
|
|
11983
|
+
resourceScopes: progressionResourceScopes(unit),
|
|
11844
11984
|
state: "started",
|
|
11845
11985
|
transitionDigest: ledger.digestIdentity("call", host.callID)
|
|
11846
11986
|
}));
|
|
@@ -12205,9 +12345,10 @@ function createSessionRuntime(options) {
|
|
|
12205
12345
|
const marker = projectReceiptMintMarker(receipt, ledger.getSessionSalt());
|
|
12206
12346
|
if (!marker)
|
|
12207
12347
|
return;
|
|
12348
|
+
const existing = output.metadata[SYSTEMATIC_WORKFLOW_RECEIPT_METADATA_KEY];
|
|
12208
12349
|
output.metadata = {
|
|
12209
12350
|
...output.metadata,
|
|
12210
|
-
[SYSTEMATIC_WORKFLOW_RECEIPT_METADATA_KEY]: marker
|
|
12351
|
+
[SYSTEMATIC_WORKFLOW_RECEIPT_METADATA_KEY]: existing ? Array.isArray(existing) ? [...existing, marker] : [existing, marker] : marker
|
|
12211
12352
|
};
|
|
12212
12353
|
}
|
|
12213
12354
|
function mergeProgressionMarker(output, marker) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type ReceiptClassifier } from './receipt-classifier.js';
|
|
2
2
|
import type { ReceiptLedger, ReceiptOperation } from './receipt-ledger.js';
|
|
3
|
+
import type { ReceiptResourceScope } from './receipt-readback.js';
|
|
3
4
|
export type WorkflowMode = 'protected' | 'disabled' | 'unavailable';
|
|
4
5
|
export type WorkflowState = 'protected' | 'waiting' | 'rejected' | 'disabled' | 'unavailable';
|
|
5
6
|
export type RepairKind = 'fresh-readback' | 'rerun-operation' | 'question-attestation';
|
|
@@ -34,6 +35,12 @@ export interface UnitSnapshot {
|
|
|
34
35
|
status: 'active' | 'completed';
|
|
35
36
|
requiredOperations: readonly ReceiptOperation[];
|
|
36
37
|
requiredResourceOperations: readonly ReceiptOperation[];
|
|
38
|
+
resourceScopes: readonly ReceiptResourceScope[];
|
|
39
|
+
}
|
|
40
|
+
export interface CurrentOperationContext {
|
|
41
|
+
readonly workspaceIdentity: string;
|
|
42
|
+
readonly repositoryIdentity?: string;
|
|
43
|
+
readonly worktreeIdentity?: string;
|
|
37
44
|
}
|
|
38
45
|
export interface WorkflowStatus {
|
|
39
46
|
state: WorkflowState;
|
|
@@ -125,7 +132,14 @@ export interface WorkflowGuard {
|
|
|
125
132
|
observeReceipt(input: unknown): EvidenceObservationResult;
|
|
126
133
|
observeAttempt(input: unknown): EvidenceObservationResult;
|
|
127
134
|
observeOperation(input: unknown): Promise<EvidenceObservationResult>;
|
|
135
|
+
/**
|
|
136
|
+
* Internal recovery seam. Callers MUST validate host lineage, own
|
|
137
|
+
* registration/seed/readback, stable workspace, and current mutable
|
|
138
|
+
* revisions before using this classifier-bypassing path.
|
|
139
|
+
*/
|
|
140
|
+
observeTrustedRecoveredOperation(input: unknown): Promise<EvidenceObservationResult>;
|
|
128
141
|
observeReadback(input: unknown): ReadbackObservationResult;
|
|
142
|
+
currentOperationContext(): CurrentOperationContext;
|
|
129
143
|
status(): WorkflowStatus;
|
|
130
144
|
prepareTransition(input: unknown): TransitionPrepareResult;
|
|
131
145
|
finalizeTransition(input: unknown): TransitionFinalizeResult;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fro.bot/systematic",
|
|
3
|
-
"version": "3.5.
|
|
3
|
+
"version": "3.5.1",
|
|
4
4
|
"description": "Compound-engineering loops for OpenCode, Pi, and Claude Code",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"homepage": "https://fro.bot/systematic",
|
|
@@ -90,10 +90,10 @@
|
|
|
90
90
|
}
|
|
91
91
|
},
|
|
92
92
|
"devDependencies": {
|
|
93
|
-
"@biomejs/biome": "2.5.
|
|
93
|
+
"@biomejs/biome": "2.5.6",
|
|
94
94
|
"@earendil-works/pi-coding-agent": "0.80.10",
|
|
95
|
-
"@opencode-ai/plugin": "1.18.
|
|
96
|
-
"@opencode-ai/sdk": "1.18.
|
|
95
|
+
"@opencode-ai/plugin": "1.18.8",
|
|
96
|
+
"@opencode-ai/sdk": "1.18.8",
|
|
97
97
|
"@semantic-release/exec": "7.1.0",
|
|
98
98
|
"@tintinweb/pi-subagents": "0.14.3",
|
|
99
99
|
"@types/bun": "latest",
|