@cassiomc1/forgeloop 1.14.0 → 2.0.0-rc.2
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/CONTRIBUTING.md +11 -7
- package/DOCS_INDEX.md +2 -1
- package/ENG/c-development-eng.md +2 -2
- package/ENG/cpp-development-eng.md +2 -2
- package/ENG/dotnet-aspnetcore-development-eng.md +7 -1
- package/ENG/flutter-development-eng.md +4 -2
- package/ENG/go-development-eng.md +2 -2
- package/ENG/java-development-eng.md +3 -3
- package/ENG/nodejs-backend-development-eng.md +8 -4
- package/ENG/php-development-eng.md +3 -3
- package/ENG/python-development-eng.md +205 -0
- package/ENG/rust-development-eng.md +42 -1
- package/ENG/sql-development-eng.md +4 -4
- package/ENG/swift-development-eng.md +5 -4
- package/ENG/test-code-eng.md +1 -1
- package/ENG/typescript-development-eng.md +4 -4
- package/GUIDE_ROUTER.md +6 -0
- package/LICENSE +133 -21
- package/LOOP_SYSTEM_DESIGN.md +10 -10
- package/README.md +28 -39
- package/THIRD_PARTY_NOTICES.md +3 -4
- package/completions/_forgeloop +11 -3
- package/completions/forgeloop.bash +19 -3
- package/completions/forgeloop.fish +61 -1
- package/docs/AGENT_PROTOCOL_SUMMARY.md +50 -40
- package/docs/ARTIFACT_REFERENCE.md +50 -37
- package/docs/CLI_REFERENCE.md +147 -3
- package/docs/DOCUMENTATION_GUIDE.md +6 -4
- package/docs/GETTING_STARTED.md +3 -3
- package/docs/PACKAGE_CONTENTS.md +14 -13
- package/docs/RELEASE_CHECKLIST.md +81 -10
- package/docs/SELF_HOSTED_TESTS.md +121 -0
- package/docs/documentation-manifest.json +25 -12
- package/package.json +15 -10
- package/schemas/task-bundle.schema.json +7 -2
- package/scripts/generate-agent-protocol-summary.mjs +2 -2
- package/scripts/run-mcp-clean-tests.mjs +30 -0
- package/src/adapters/agent-browser/provider.js +14 -11
- package/src/adapters/emulated-services/provider.js +11 -4
- package/src/adapters/temporary-paths.js +18 -0
- package/src/cli.js +146 -210
- package/src/commands/action-show.js +5 -2
- package/src/commands/advance.js +5 -1
- package/src/commands/baseline.js +13 -11
- package/src/commands/checkpoint-revalidate.js +24 -8
- package/src/commands/complete.js +19 -3
- package/src/commands/contract-create.js +3 -1
- package/src/commands/doctor.js +36 -13
- package/src/commands/gate-record.js +20 -31
- package/src/commands/gate-revalidate.js +11 -13
- package/src/commands/handoff-list.js +6 -1
- package/src/commands/handoff-show.js +6 -1
- package/src/commands/migrate-protocol.js +2 -2
- package/src/commands/model-route.js +9 -1
- package/src/commands/next.js +7 -1
- package/src/commands/policy-diff.js +25 -4
- package/src/commands/progress.js +17 -11
- package/src/commands/record-diagnosis.js +3 -0
- package/src/commands/semantic-plan.js +26 -2
- package/src/commands/status.js +6 -1
- package/src/commands/storage-backup.js +15 -0
- package/src/commands/storage-migrate.js +5 -0
- package/src/commands/storage-migration-resume.js +5 -0
- package/src/commands/storage-migration-status.js +44 -0
- package/src/commands/storage-restore-resume.js +7 -0
- package/src/commands/storage-restore.js +8 -0
- package/src/commands/task-abandon.js +1 -1
- package/src/commands/task-list.js +8 -43
- package/src/commands/task-migrate.js +3 -3
- package/src/commands/task-recover.js +10 -2
- package/src/commands/task-scope.js +18 -2
- package/src/commands/task-show.js +16 -2
- package/src/commands/validate-protocol.js +29 -14
- package/src/commands/validate-receipt.js +8 -1
- package/src/config/guides.json +4 -0
- package/src/core/action-ledger-projection.js +10 -3
- package/src/core/actions.js +38 -75
- package/src/core/activation.js +11 -3
- package/src/core/approvals.js +17 -51
- package/src/core/artifact-registry.js +59 -2
- package/src/core/artifacts.js +81 -27
- package/src/core/attachment-paths.js +45 -0
- package/src/core/attestation-coverage.js +6 -1
- package/src/core/attestation-verifier.js +21 -2
- package/src/core/attestation.js +17 -6
- package/src/core/audit.js +6 -1
- package/src/core/bundles.js +160 -66
- package/src/core/checkpoint-revalidation.js +12 -10
- package/src/core/cli-command-definitions.js +109 -7
- package/src/core/code-manifest.js +9 -3
- package/src/core/command-executors.js +182 -126
- package/src/core/command-input.js +21 -1
- package/src/core/completion-artifacts.js +71 -29
- package/src/core/completion-ownership.js +19 -8
- package/src/core/completion-recovery-rebind.js +11 -1
- package/src/core/completion.js +60 -9
- package/src/core/conformance.js +2 -1
- package/src/core/continuity-lint.js +13 -2
- package/src/core/continuity-reconciliation.js +16 -3
- package/src/core/continuity.js +16 -8
- package/src/core/contract-bootstrap-recovery.js +42 -41
- package/src/core/contract-revision.js +8 -9
- package/src/core/decision/artifact.js +2 -1
- package/src/core/decision/events.js +9 -6
- package/src/core/decision/resolver.js +19 -11
- package/src/core/decision/service.js +63 -38
- package/src/core/decision/task-bindings.js +6 -1
- package/src/core/diagnosis-model.js +11 -0
- package/src/core/diagnosis.js +10 -1
- package/src/core/diagnostic-projection.js +29 -12
- package/src/core/diagnostic-record.js +28 -3
- package/src/core/efficiency.js +6 -1
- package/src/core/events.js +316 -97
- package/src/core/execution-prerequisites.js +22 -13
- package/src/core/execution.js +26 -40
- package/src/core/filesystem.js +11 -0
- package/src/core/gate-artifact.js +105 -19
- package/src/core/gate-provenance.js +6 -5
- package/src/core/handoff.js +26 -14
- package/src/core/history.js +39 -35
- package/src/core/hypothesis-projection.js +32 -0
- package/src/core/information-gain-projection.js +104 -100
- package/src/core/inspect.js +12 -3
- package/src/core/integration-invocation-policy.js +10 -0
- package/src/core/integration-resources.js +41 -34
- package/src/core/ledger-event-collection.js +189 -0
- package/src/core/ledger-relations.js +23 -0
- package/src/core/native-storage.js +12 -0
- package/src/core/next-action-continuity.js +31 -9
- package/src/core/next-action-phases.js +1 -1
- package/src/core/next-action-review-phase.js +2 -2
- package/src/core/next-action-verification-phase.js +2 -1
- package/src/core/next-action.js +21 -2
- package/src/core/phase.js +138 -93
- package/src/core/policy-engine.js +12 -13
- package/src/core/preflight-consistency.js +72 -59
- package/src/core/preflight-loaders.js +19 -12
- package/src/core/preflight-model.js +6 -1
- package/src/core/preflight.js +29 -12
- package/src/core/progress.js +21 -10
- package/src/core/protocol-info.js +3 -0
- package/src/core/protocol-migration.js +7 -6
- package/src/core/reconcile-closure.js +162 -24
- package/src/core/recovery-history.js +66 -47
- package/src/core/reflection.js +72 -77
- package/src/core/responsibility.js +25 -13
- package/src/core/route-artifact.js +13 -3
- package/src/core/runtime-context.js +5 -0
- package/src/core/schema-validation.js +8 -3
- package/src/core/settlement.js +12 -1
- package/src/core/signing/bundle-file.js +53 -0
- package/src/core/signing/bundle-reference.js +15 -0
- package/src/core/signing/sigstore.js +60 -33
- package/src/core/signing/statement-file.js +27 -0
- package/src/core/storage-runtime-registry.js +41 -0
- package/src/core/structural-quality/artifacts.js +20 -14
- package/src/core/structural-quality/service.js +81 -56
- package/src/core/target-layout.js +1 -1
- package/src/core/task-claim-state.js +52 -18
- package/src/core/task-command.js +21 -1
- package/src/core/task-conflict-inspection.js +112 -107
- package/src/core/task-descriptor.js +29 -22
- package/src/core/task-discovery.js +288 -90
- package/src/core/task-lock.js +66 -306
- package/src/core/task-migration-validation.js +5 -5
- package/src/core/task-migration.js +17 -287
- package/src/core/task-snapshot.js +51 -8
- package/src/core/task-storage-migration.js +18 -0
- package/src/core/templates.js +20 -8
- package/src/core/test-intelligence/prune.js +5 -1
- package/src/core/trace.js +154 -99
- package/src/core/trajectory-evaluation.js +15 -3
- package/src/core/trajectory-metrics.js +47 -32
- package/src/core/transaction.js +16 -288
- package/src/core/verification-scope.js +20 -12
- package/src/core/work-state.js +100 -59
- package/src/repository-index/binary-manager.js +7 -5
- package/src/storage/artifact-bytes.js +22 -0
- package/src/storage/attachment-binding.js +11 -0
- package/src/storage/attachment-files.js +82 -0
- package/src/storage/attachment-references.js +36 -0
- package/src/storage/backup.js +53 -0
- package/src/storage/capabilities.js +31 -0
- package/src/storage/connection-owner.js +119 -0
- package/src/storage/connection.js +376 -0
- package/src/storage/existing-project-scope.js +45 -0
- package/src/storage/export-destination.js +44 -0
- package/src/storage/exporter.js +284 -0
- package/src/storage/file-durability.js +46 -0
- package/src/storage/importer.js +461 -0
- package/src/storage/index.js +74 -0
- package/src/storage/leases.js +63 -0
- package/src/storage/ledger-event-snapshot.js +150 -0
- package/src/storage/ledger-relations.js +220 -0
- package/src/storage/legacy-maintenance-boundary.js +18 -0
- package/src/storage/legacy-signature.js +115 -0
- package/src/storage/maintenance-handoff.js +129 -0
- package/src/storage/maintenance-owner.js +42 -0
- package/src/storage/maintenance.js +310 -0
- package/src/storage/metadata-json.js +35 -0
- package/src/storage/migration-activate.js +59 -0
- package/src/storage/migration-archive.js +57 -0
- package/src/storage/migration-attachment-root.js +61 -0
- package/src/storage/migration-candidate.js +398 -0
- package/src/storage/migration-publication.js +143 -0
- package/src/storage/migration-rollback-admission.js +54 -0
- package/src/storage/migration-rollback-preparation.js +132 -0
- package/src/storage/migration-rollback-publication.js +183 -0
- package/src/storage/migration-rollback-release.js +66 -0
- package/src/storage/migration-rollback-source-stage.js +103 -0
- package/src/storage/migration-rollback.js +43 -0
- package/src/storage/migration-source-partition.js +56 -0
- package/src/storage/migration-source.js +255 -0
- package/src/storage/migration-terminal.js +39 -0
- package/src/storage/migration-validation.js +75 -0
- package/src/storage/migration.js +43 -0
- package/src/storage/operational-attachments.js +49 -0
- package/src/storage/operational-context.js +50 -0
- package/src/storage/project-backup.js +98 -0
- package/src/storage/project-bootstrap.js +24 -0
- package/src/storage/project-boundary.js +79 -0
- package/src/storage/project-read-snapshot.js +18 -0
- package/src/storage/project-replacement.js +137 -0
- package/src/storage/project-restore.js +203 -0
- package/src/storage/record-definitions.js +45 -0
- package/src/storage/repository.js +507 -0
- package/src/storage/restore-journal.js +62 -0
- package/src/storage/restore-layout.js +30 -0
- package/src/storage/restore-preparation.js +84 -0
- package/src/storage/restore-replacement-archive.js +162 -0
- package/src/storage/restore-replacement-owner.js +31 -0
- package/src/storage/restore-replacement-preparation.js +128 -0
- package/src/storage/restore-replacement.js +142 -0
- package/src/storage/restore-terminal.js +32 -0
- package/src/storage/runtime.js +31 -0
- package/src/storage/schema.js +214 -0
- package/src/storage/singleton-import.js +84 -0
- package/src/storage/snapshot-continuity.js +16 -0
- package/src/storage/snapshot.js +36 -0
- package/src/storage/storage-marker.js +79 -0
- package/src/storage/task-guards.js +107 -0
- package/src/storage/topology.js +42 -0
- package/src/storage/transaction.js +179 -0
- package/src/storage/unit-of-work.js +663 -0
- package/src/storage/windows-process-incarnation.js +206 -0
- package/.forgeloop/forgeloop.gitignore +0 -4
- package/LICENSE-DOCS.md +0 -13
- package/src/core/transaction-maintenance.js +0 -70
package/src/core/trace.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { withTaskSnapshot } from "./task-snapshot.js";
|
|
2
|
+
import { ledgerEventsOfTypes } from "./ledger-event-collection.js";
|
|
3
3
|
import {
|
|
4
4
|
assertDiagnosticCaseDetails,
|
|
5
5
|
assertInterventionDetails,
|
|
@@ -201,23 +201,6 @@ export function normalizeProtocolEvent(event, context = {}) {
|
|
|
201
201
|
};
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
-
function lifecycleTransitions(events) {
|
|
205
|
-
const transitions = [];
|
|
206
|
-
let lastPhaseEvent = null;
|
|
207
|
-
for (const event of events) {
|
|
208
|
-
if (!LIFECYCLE_TRANSITIONS.includes(event.event)) continue;
|
|
209
|
-
transitions.push({
|
|
210
|
-
sequence: event.seq,
|
|
211
|
-
at: event.at,
|
|
212
|
-
type: event.event,
|
|
213
|
-
details: event.details ?? {},
|
|
214
|
-
previous: lastPhaseEvent?.type ?? null,
|
|
215
|
-
});
|
|
216
|
-
lastPhaseEvent = event;
|
|
217
|
-
}
|
|
218
|
-
return transitions;
|
|
219
|
-
}
|
|
220
|
-
|
|
221
204
|
function projectChecks(snapshot) {
|
|
222
205
|
const state = snapshot.state;
|
|
223
206
|
const attemptsByCheck = new Map();
|
|
@@ -289,7 +272,8 @@ function projectChecks(snapshot) {
|
|
|
289
272
|
}
|
|
290
273
|
|
|
291
274
|
function legacyDiagnoses(events, taskId) {
|
|
292
|
-
return
|
|
275
|
+
return [...ledgerEventsOfTypes(events, ["DIAGNOSIS_RECORDED"])]
|
|
276
|
+
.filter(event => !taskId || event.taskId === taskId).map((event) => ({
|
|
293
277
|
sequence: event.seq,
|
|
294
278
|
at: event.at,
|
|
295
279
|
sourceModel: "LEGACY_DIAGNOSIS_V1",
|
|
@@ -357,35 +341,53 @@ const PHASE_DERIVATIONS = Object.freeze({
|
|
|
357
341
|
INTERVENTION_RECORDED: () => "CORRECTING",
|
|
358
342
|
});
|
|
359
343
|
|
|
360
|
-
function
|
|
344
|
+
function phaseStep(event, currentPhase) {
|
|
345
|
+
const milestone = PHASE_MILESTONES[event.event];
|
|
346
|
+
if (milestone) return { phase: milestone.phase, quality: milestone.quality };
|
|
347
|
+
const derive = PHASE_DERIVATIONS[event.event];
|
|
348
|
+
const derived = derive ? derive(event.details ?? {}) : null;
|
|
349
|
+
return { phase: derived ?? currentPhase, quality: derived || currentPhase ? "derived" : "unknown" };
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
function orderedSequences(events) {
|
|
353
|
+
let previous = -Infinity;
|
|
354
|
+
return events.every(event => {
|
|
355
|
+
if (!Number.isFinite(event.seq) || event.seq <= previous) return false;
|
|
356
|
+
previous = event.seq;
|
|
357
|
+
return true;
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
function* phasedEvents(events, validatedOrder = false) {
|
|
361
362
|
let currentPhase = null;
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
currentPhase = milestone.phase;
|
|
368
|
-
qualityBySequence.set(event.seq, milestone.quality);
|
|
369
|
-
} else {
|
|
370
|
-
const derive = PHASE_DERIVATIONS[event.event];
|
|
371
|
-
const derived = derive ? derive(event.details ?? {}) : null;
|
|
372
|
-
if (derived) {
|
|
373
|
-
currentPhase = derived;
|
|
374
|
-
qualityBySequence.set(event.seq, "derived");
|
|
375
|
-
} else {
|
|
376
|
-
qualityBySequence.set(event.seq, currentPhase ? "derived" : "unknown");
|
|
377
|
-
}
|
|
363
|
+
if (validatedOrder || orderedSequences(events)) {
|
|
364
|
+
for (const event of events) {
|
|
365
|
+
const step = phaseStep(event, currentPhase);
|
|
366
|
+
currentPhase = step.phase;
|
|
367
|
+
yield { event, ...step };
|
|
378
368
|
}
|
|
379
|
-
|
|
369
|
+
return;
|
|
370
|
+
}
|
|
371
|
+
// Legacy duplicate sequence keys retain the original last-write projection.
|
|
372
|
+
const phases = new Map();
|
|
373
|
+
for (const event of events) {
|
|
374
|
+
const step = phaseStep(event, currentPhase);
|
|
375
|
+
currentPhase = step.phase;
|
|
376
|
+
phases.set(event.seq, step);
|
|
380
377
|
}
|
|
381
|
-
|
|
378
|
+
for (const event of events) yield { event, ...phases.get(event.seq) };
|
|
382
379
|
}
|
|
383
380
|
|
|
384
381
|
export function historyQualityFor({ snapshot, normalizedEvents }) {
|
|
382
|
+
return historyQualityFromSummary(snapshot, normalizedEvents.length,
|
|
383
|
+
normalizedEvents.some((event) => event.timestampQuality !== "authoritative"));
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function historyQualityFromSummary(snapshot, eventCount, hasLegacyTimestamp) {
|
|
385
387
|
const reasons = [];
|
|
386
|
-
const hasLedger =
|
|
388
|
+
const hasLedger = eventCount > 0;
|
|
387
389
|
if (!hasLedger) reasons.push("LEDGER_ABSENT");
|
|
388
|
-
if (
|
|
390
|
+
if (hasLegacyTimestamp) {
|
|
389
391
|
reasons.push("LEGACY_PHASE_TIMESTAMPS_UNAVAILABLE");
|
|
390
392
|
}
|
|
391
393
|
if (snapshot.state && Number.isInteger(snapshot.state.verificationCycle) && snapshot.state.verificationCycle > 1
|
|
@@ -396,54 +398,69 @@ export function historyQualityFor({ snapshot, normalizedEvents }) {
|
|
|
396
398
|
return { level, reasons };
|
|
397
399
|
}
|
|
398
400
|
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
401
|
+
// History supplies an output collector only. Complete snapshot auditing and
|
|
402
|
+
// every diagnostic/action projection below remain independent of its selection.
|
|
403
|
+
export async function buildTaskTrace({ target, packageRoot, taskId = null, eventsPath = null } = {}, eventProjection = null) {
|
|
404
|
+
return withTaskSnapshot({ target, packageRoot, taskId, eventsPath }, snapshot =>
|
|
405
|
+
buildTraceFromSnapshot({ target, packageRoot, taskId, eventsPath, snapshot, eventProjection }));
|
|
406
|
+
}
|
|
402
407
|
|
|
403
|
-
|
|
404
|
-
const { phaseBySequence, qualityBySequence } = reconstructPhaseChronology(taskEvents);
|
|
408
|
+
function projectTraceEvents(taskEvents, artifactPath, eventProjection, validatedOrder) {
|
|
405
409
|
const normalizedEvents = [];
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
phase: phaseBySequence.get(event.seq) ?? null,
|
|
409
|
-
artifactPath,
|
|
410
|
-
phaseQuality: qualityBySequence.get(event.seq) ?? "unknown",
|
|
411
|
-
}));
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
const integrity = {
|
|
415
|
-
valid: snapshot.integrity.valid,
|
|
416
|
-
errors: snapshot.integrity.errors,
|
|
417
|
-
};
|
|
418
|
-
const historyQuality = historyQualityFor({ snapshot, normalizedEvents });
|
|
419
|
-
const diagnostics = structuredDiagnostics(taskEvents);
|
|
420
|
-
|
|
421
|
-
const failureSignatures = projectFailureSignatures({ state: snapshot.state, events: taskEvents });
|
|
422
|
-
const failureSurfaces = projectFailureSurfaces({ state: snapshot.state, events: taskEvents });
|
|
423
|
-
|
|
410
|
+
const transitions = [];
|
|
411
|
+
let lastPhaseEvent = null;
|
|
424
412
|
const executions = [];
|
|
425
413
|
const seenExecutions = new Set();
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
414
|
+
const evidenceSources = new Map();
|
|
415
|
+
const recovery = [];
|
|
416
|
+
const completionAttempts = [];
|
|
417
|
+
const continuity = [];
|
|
418
|
+
let validatedAt = null;
|
|
419
|
+
let rejectedAt = null;
|
|
420
|
+
let actionEventCount = 0;
|
|
421
|
+
let reconciliationCount = 0;
|
|
422
|
+
let totalEventCount = 0;
|
|
423
|
+
let hasLegacyTimestamp = false;
|
|
424
|
+
for (const { event, phase, quality } of phasedEvents(taskEvents, validatedOrder)) {
|
|
425
|
+
const normalized = normalizeProtocolEvent(event, {
|
|
426
|
+
phase: phase ?? null,
|
|
427
|
+
artifactPath,
|
|
428
|
+
phaseQuality: quality ?? "unknown",
|
|
438
429
|
});
|
|
439
|
-
|
|
430
|
+
totalEventCount += 1;
|
|
431
|
+
hasLegacyTimestamp ||= normalized.timestampQuality !== "authoritative";
|
|
432
|
+
if (eventProjection) eventProjection.add(normalized);
|
|
433
|
+
else normalizedEvents.push(normalized);
|
|
434
|
+
|
|
435
|
+
if (LIFECYCLE_TRANSITIONS.includes(event.event)) {
|
|
436
|
+
transitions.push({
|
|
437
|
+
sequence: event.seq,
|
|
438
|
+
at: event.at,
|
|
439
|
+
type: event.event,
|
|
440
|
+
details: event.details ?? {},
|
|
441
|
+
previous: lastPhaseEvent?.type ?? null,
|
|
442
|
+
});
|
|
443
|
+
lastPhaseEvent = event;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
const details = event.details ?? {};
|
|
447
|
+
const executionId = details.executionId ?? details.executionRef ?? null;
|
|
448
|
+
if (executionId && !seenExecutions.has(executionId)) {
|
|
449
|
+
seenExecutions.add(executionId);
|
|
450
|
+
executions.push({
|
|
451
|
+
executionId,
|
|
452
|
+
sequence: event.seq,
|
|
453
|
+
at: event.at,
|
|
454
|
+
status: details.status ?? null,
|
|
455
|
+
exitCode: details.exitCode ?? null,
|
|
456
|
+
resolution: details.resolution ?? null,
|
|
457
|
+
});
|
|
458
|
+
}
|
|
440
459
|
|
|
441
|
-
const evidenceSources = new Map();
|
|
442
|
-
for (const event of taskEvents) {
|
|
443
460
|
const refs = [
|
|
444
|
-
...(Array.isArray(
|
|
445
|
-
...(Array.isArray(
|
|
446
|
-
?
|
|
461
|
+
...(Array.isArray(details.evidenceRefs) ? details.evidenceRefs : []),
|
|
462
|
+
...(Array.isArray(details.hypotheses)
|
|
463
|
+
? details.hypotheses.flatMap((hypothesis) => hypothesis.evidenceRefs ?? [])
|
|
447
464
|
: []),
|
|
448
465
|
];
|
|
449
466
|
for (const ref of refs) {
|
|
@@ -451,19 +468,59 @@ export async function buildTaskTrace({ target, packageRoot, taskId = null, event
|
|
|
451
468
|
if (!evidenceSources.has(ref)) evidenceSources.set(ref, { ref, sources: [] });
|
|
452
469
|
if (!evidenceSources.get(ref).sources.includes(event.event)) evidenceSources.get(ref).sources.push(event.event);
|
|
453
470
|
}
|
|
471
|
+
|
|
472
|
+
if (["TASK_RECOVERY_RECORDED", "TASK_RECOVERY_RESUMED", "OPERATOR_RECOVERY_RECORDED", "TASK_ABANDONED", "LEGACY_RECOVERY_MIGRATION_RECORDED"].includes(event.event)) {
|
|
473
|
+
recovery.push({ sequence: event.seq, at: event.at, type: event.event, details });
|
|
474
|
+
}
|
|
475
|
+
if (event.event === "COMPLETION_VALIDATED" || event.event === "COMPLETION_REJECTED") {
|
|
476
|
+
completionAttempts.push({ sequence: event.seq, at: event.at, type: event.event });
|
|
477
|
+
if (event.event === "COMPLETION_VALIDATED") validatedAt = event.at ?? null;
|
|
478
|
+
else rejectedAt = event.at ?? null;
|
|
479
|
+
}
|
|
480
|
+
if (event.event.startsWith("ACTION_") || event.event.startsWith("APPROVAL_")) actionEventCount += 1;
|
|
481
|
+
if (event.event === "ACTION_RECONCILED") reconciliationCount += 1;
|
|
482
|
+
if (event.event === "CONTINUITY_RECORDED" || event.event === "CHECKPOINT_RECONCILED") {
|
|
483
|
+
continuity.push({ sequence: event.seq, at: event.at, type: event.event });
|
|
484
|
+
}
|
|
454
485
|
}
|
|
455
|
-
const evidence = [...evidenceSources.values()].sort((a, b) => a.ref.localeCompare(b.ref));
|
|
456
486
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
487
|
+
return {
|
|
488
|
+
normalizedEvents,
|
|
489
|
+
totalEventCount,
|
|
490
|
+
hasLegacyTimestamp,
|
|
491
|
+
transitions,
|
|
492
|
+
executions,
|
|
493
|
+
evidence: [...evidenceSources.values()].sort((a, b) => a.ref.localeCompare(b.ref)),
|
|
494
|
+
recovery,
|
|
495
|
+
completion: { validatedAt, rejectedAt, attempts: completionAttempts },
|
|
496
|
+
continuity,
|
|
497
|
+
actionEventCount,
|
|
498
|
+
reconciliationCount,
|
|
499
|
+
};
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
async function buildTraceFromSnapshot({ target, packageRoot, taskId, eventsPath, snapshot, eventProjection }) {
|
|
503
|
+
const artifactPath = eventsPath ?? ".forgeloop/task-state/<task-key>/events.ndjson";
|
|
460
504
|
|
|
461
|
-
const
|
|
462
|
-
const
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
505
|
+
const belongsToTask = event => !event.taskId || event.taskId === taskId;
|
|
506
|
+
const taskEvents = taskId && !snapshot.events.every(belongsToTask)
|
|
507
|
+
? snapshot.events.filter(belongsToTask) : snapshot.events;
|
|
508
|
+
// The private snapshot audit already proved seq === index + 1. Invalid
|
|
509
|
+
// ledgers retain the original duplicate/order compatibility projection.
|
|
510
|
+
const {
|
|
511
|
+
normalizedEvents, totalEventCount, hasLegacyTimestamp, transitions,
|
|
512
|
+
executions, evidence, recovery, completion, continuity, actionEventCount, reconciliationCount,
|
|
513
|
+
} = projectTraceEvents(taskEvents, artifactPath, eventProjection, snapshot.integrity.valid);
|
|
514
|
+
|
|
515
|
+
const integrity = {
|
|
516
|
+
valid: snapshot.integrity.valid,
|
|
517
|
+
errors: snapshot.integrity.errors,
|
|
466
518
|
};
|
|
519
|
+
const historyQuality = historyQualityFromSummary(snapshot, totalEventCount, hasLegacyTimestamp);
|
|
520
|
+
const diagnostics = structuredDiagnostics(taskEvents);
|
|
521
|
+
|
|
522
|
+
const failureSignatures = projectFailureSignatures({ state: snapshot.state, events: taskEvents });
|
|
523
|
+
const failureSurfaces = projectFailureSurfaces({ state: snapshot.state, events: taskEvents });
|
|
467
524
|
|
|
468
525
|
const actions = snapshot.taskId
|
|
469
526
|
? await listActions(target, { packageRoot, taskId: snapshot.taskId })
|
|
@@ -481,7 +538,6 @@ export async function buildTaskTrace({ target, packageRoot, taskId = null, event
|
|
|
481
538
|
artifact: action,
|
|
482
539
|
}));
|
|
483
540
|
}
|
|
484
|
-
const actionEvents = taskEvents.filter((event) => event.event.startsWith("ACTION_") || event.event.startsWith("APPROVAL_"));
|
|
485
541
|
const byState = Object.fromEntries([...new Set(actions.map((action) => action.state))].sort()
|
|
486
542
|
.map((state) => [state, actions.filter((action) => action.state === state).length]));
|
|
487
543
|
const byCapability = Object.fromEntries([...new Set(actions.map((action) => action.capability))].sort()
|
|
@@ -505,8 +561,8 @@ export async function buildTaskTrace({ target, packageRoot, taskId = null, event
|
|
|
505
561
|
|| (projection.state === "VERIFIED" && !(projection.authorization.valid && projection.verification.valid))
|
|
506
562
|
).length,
|
|
507
563
|
repeatedIdempotencyAttempts,
|
|
508
|
-
reconciliationCount
|
|
509
|
-
eventCount:
|
|
564
|
+
reconciliationCount,
|
|
565
|
+
eventCount: actionEventCount,
|
|
510
566
|
};
|
|
511
567
|
|
|
512
568
|
return {
|
|
@@ -530,8 +586,9 @@ export async function buildTaskTrace({ target, packageRoot, taskId = null, event
|
|
|
530
586
|
historyQuality,
|
|
531
587
|
integrity,
|
|
532
588
|
artifacts: {},
|
|
533
|
-
events: normalizedEvents,
|
|
534
|
-
|
|
589
|
+
events: eventProjection ? eventProjection.result() : normalizedEvents,
|
|
590
|
+
...(eventProjection ? { totalEventCount } : {}),
|
|
591
|
+
transitions,
|
|
535
592
|
executions,
|
|
536
593
|
checks: projectChecks(snapshot),
|
|
537
594
|
evidence,
|
|
@@ -541,9 +598,7 @@ export async function buildTaskTrace({ target, packageRoot, taskId = null, event
|
|
|
541
598
|
},
|
|
542
599
|
failureSignatures,
|
|
543
600
|
failureSurfaces,
|
|
544
|
-
continuity
|
|
545
|
-
.filter((event) => ["CONTINUITY_RECORDED", "CHECKPOINT_RECONCILED"].includes(event.event))
|
|
546
|
-
.map((event) => ({ sequence: event.seq, at: event.at, type: event.event })),
|
|
601
|
+
continuity,
|
|
547
602
|
recovery,
|
|
548
603
|
policy: {},
|
|
549
604
|
audit: {},
|
|
@@ -9,6 +9,8 @@ import { taskEvaluationPath } from "./task-paths.js";
|
|
|
9
9
|
import { appendProtocolEvent } from "./events.js";
|
|
10
10
|
import { assertSafePath, ensureWithin } from "./filesystem.js";
|
|
11
11
|
import { withTaskTransaction } from "./transaction.js";
|
|
12
|
+
import { withProjectReadSnapshot } from "../storage/project-read-snapshot.js";
|
|
13
|
+
import { needsExistingProjectScope, withExistingProjectScope } from "../storage/existing-project-scope.js";
|
|
12
14
|
|
|
13
15
|
function evaluationError(code, message) { const error = new Error(message); error.code = code; return error; }
|
|
14
16
|
|
|
@@ -25,8 +27,18 @@ export async function evaluateTrajectory({ target, packageRoot, taskId, scenario
|
|
|
25
27
|
}
|
|
26
28
|
assertSchema(scenario, await readSchema("trajectory-scenario", packageRoot), "trajectory scenario");
|
|
27
29
|
if (!/^eval-[A-Za-z0-9_-]+$/.test(evaluationId)) throw evaluationError("E_TRAJECTORY_SCENARIO_INVALID", "evaluationId is invalid");
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
+
const options = { target, packageRoot, taskId, scenarioPath, evaluationId, runtimeContext };
|
|
31
|
+
if (await needsExistingProjectScope(target)) {
|
|
32
|
+
return withExistingProjectScope(target, () => evaluateValidatedTrajectory(options, scenario));
|
|
33
|
+
}
|
|
34
|
+
return evaluateValidatedTrajectory(options, scenario);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async function evaluateValidatedTrajectory({ target, packageRoot, taskId, evaluationId, runtimeContext }, scenario) {
|
|
38
|
+
const { trace, metrics } = await withProjectReadSnapshot(target, async () => ({
|
|
39
|
+
trace: await buildTaskTrace({ target, packageRoot, taskId }),
|
|
40
|
+
metrics: await buildTrajectoryMetrics({ target, packageRoot, taskId, runtimeContext }),
|
|
41
|
+
}));
|
|
30
42
|
const milestoneSet = new Set(trace.events.map((event) => event.type));
|
|
31
43
|
const missingMilestones = (scenario.requiredMilestones ?? []).filter((milestone) => !milestoneSet.has(milestone));
|
|
32
44
|
const completionEvent = trace.events.find((event) => event.type === "COMPLETION_VALIDATED");
|
|
@@ -62,7 +74,7 @@ export async function evaluateTrajectory({ target, packageRoot, taskId, scenario
|
|
|
62
74
|
};
|
|
63
75
|
const evaluation = { ...base, evaluationFingerprint: canonicalFingerprint(base) };
|
|
64
76
|
assertSchema(evaluation, await readSchema("trajectory-evaluation", packageRoot), "trajectory evaluation");
|
|
65
|
-
await withTaskTransaction({ target, taskId, operation: "trajectory-evaluated" }, async () => {
|
|
77
|
+
await withTaskTransaction({ target, packageRoot, taskId, operation: "trajectory-evaluated" }, async () => {
|
|
66
78
|
await writeJsonArtifact(target, taskEvaluationPath(taskId, evaluationId), evaluation, "trajectory-evaluation", packageRoot);
|
|
67
79
|
await appendProtocolEvent(target, { taskId, event: "TRAJECTORY_EVALUATED", fingerprint: evaluation.evaluationFingerprint,
|
|
68
80
|
details: { evaluationId, scenarioId: scenario.scenarioId, evaluationFingerprint: evaluation.evaluationFingerprint } }, packageRoot, { taskId });
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { withEventLedgerAudit } from "./events.js";
|
|
2
|
+
import { getOperationalStore } from "../storage/operational-context.js";
|
|
3
|
+
import { withNativeReadScope } from "./native-storage.js";
|
|
1
4
|
import { buildTaskTrace } from "./trace.js";
|
|
2
5
|
import { buildTaskReflection } from "./reflection.js";
|
|
3
6
|
import { readPersistedRoute } from "./route-artifact.js";
|
|
@@ -9,7 +12,44 @@ export const COMPARABLE_WORK_EVENTS = new Set([
|
|
|
9
12
|
"INTERVENTION_RECORDED", "ACTION_STARTED", "ACTION_RECONCILED", "REVIEW_STARTED",
|
|
10
13
|
]);
|
|
11
14
|
|
|
12
|
-
export async function buildTrajectoryMetrics(
|
|
15
|
+
export async function buildTrajectoryMetrics(options) {
|
|
16
|
+
const { target, packageRoot, taskId } = options;
|
|
17
|
+
return withNativeReadScope(target, () => {
|
|
18
|
+
if (getOperationalStore(target)) {
|
|
19
|
+
return withEventLedgerAudit(target, packageRoot, { taskId }, () => projectTrajectoryMetrics(options));
|
|
20
|
+
}
|
|
21
|
+
return projectTrajectoryMetrics(options);
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async function readActionReadiness(options) {
|
|
26
|
+
try {
|
|
27
|
+
const { evaluateRequiredActionReadiness } = await import("./action-readiness.js");
|
|
28
|
+
const readiness = await evaluateRequiredActionReadiness(options);
|
|
29
|
+
return { satisfied: readiness.satisfied, unresolved: readiness.unresolved };
|
|
30
|
+
} catch { return null; }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async function readExecutionProfile(target, packageRoot, taskId) {
|
|
34
|
+
try {
|
|
35
|
+
const route = await readPersistedRoute(target, packageRoot, { taskId });
|
|
36
|
+
return projectExecutionProfile(route.value);
|
|
37
|
+
} catch {
|
|
38
|
+
// Legacy tasks and incomplete task namespaces may not have a route.
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function readMetricsUsage(target, packageRoot, taskId, runtimeContext) {
|
|
44
|
+
const usage = await readTaskUsage(target, packageRoot, taskId);
|
|
45
|
+
const usageProvider = runtimeContext?.usageProvider;
|
|
46
|
+
if (usageProvider && typeof usageProvider.getTaskUsage === "function") {
|
|
47
|
+
return providerUsage(await usageProvider.getTaskUsage({ projectPath: target, taskId }));
|
|
48
|
+
}
|
|
49
|
+
return usage;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
async function projectTrajectoryMetrics({ target, packageRoot, taskId, runtimeContext = null }) {
|
|
13
53
|
const trace = await buildTaskTrace({ target, packageRoot, taskId });
|
|
14
54
|
const reflection = await buildTaskReflection({ target, packageRoot, taskId });
|
|
15
55
|
const events = trace.events;
|
|
@@ -17,22 +57,13 @@ export async function buildTrajectoryMetrics({ target, packageRoot, taskId, runt
|
|
|
17
57
|
.map((event) => event.data?.verificationCycle).filter(Number.isInteger));
|
|
18
58
|
const diagnosticCycles = new Set(trace.diagnostics.cases.map((item) => item.verificationCycle));
|
|
19
59
|
const firstEventAt = events.find((event) => event.timestampQuality === "authoritative")?.timestamp ?? null;
|
|
20
|
-
const lastEventAt =
|
|
60
|
+
const lastEventAt = events.findLast((event) => event.timestampQuality === "authoritative")?.timestamp ?? null;
|
|
21
61
|
const firstMs = firstEventAt ? Date.parse(firstEventAt) : NaN;
|
|
22
62
|
const lastMs = lastEventAt ? Date.parse(lastEventAt) : NaN;
|
|
23
63
|
const interventions = reflection.interventions ?? { count: 0, informative: 0, nonInformative: 0 };
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
executionProfile = projectExecutionProfile(route.value);
|
|
28
|
-
} catch {
|
|
29
|
-
// Legacy tasks and incomplete task namespaces may not have a route.
|
|
30
|
-
}
|
|
31
|
-
let usage = await readTaskUsage(target, packageRoot, taskId);
|
|
32
|
-
const usageProvider = runtimeContext?.usageProvider;
|
|
33
|
-
if (usageProvider && typeof usageProvider.getTaskUsage === "function") {
|
|
34
|
-
usage = providerUsage(await usageProvider.getTaskUsage({ projectPath: target, taskId }));
|
|
35
|
-
}
|
|
64
|
+
const executionProfile = await readExecutionProfile(target, packageRoot, taskId);
|
|
65
|
+
const usage = await readMetricsUsage(target, packageRoot, taskId, runtimeContext);
|
|
66
|
+
const readiness = await readActionReadiness({ target, packageRoot, taskId });
|
|
36
67
|
return {
|
|
37
68
|
schemaVersion: 1,
|
|
38
69
|
taskId,
|
|
@@ -58,24 +89,8 @@ export async function buildTrajectoryMetrics({ target, packageRoot, taskId, runt
|
|
|
58
89
|
verified: trace.actions?.verified ?? 0,
|
|
59
90
|
// Canonical trust counts come from the action-readiness projection;
|
|
60
91
|
// raw VERIFIED labels are observability only.
|
|
61
|
-
trustedSatisfied:
|
|
62
|
-
|
|
63
|
-
const { evaluateRequiredActionReadiness } = await import("./action-readiness.js");
|
|
64
|
-
const readiness = await evaluateRequiredActionReadiness({ target, packageRoot, taskId });
|
|
65
|
-
return readiness.satisfied;
|
|
66
|
-
} catch {
|
|
67
|
-
return null;
|
|
68
|
-
}
|
|
69
|
-
})(),
|
|
70
|
-
unresolvedRequired: await (async () => {
|
|
71
|
-
try {
|
|
72
|
-
const { evaluateRequiredActionReadiness } = await import("./action-readiness.js");
|
|
73
|
-
const readiness = await evaluateRequiredActionReadiness({ target, packageRoot, taskId });
|
|
74
|
-
return readiness.unresolved;
|
|
75
|
-
} catch {
|
|
76
|
-
return null;
|
|
77
|
-
}
|
|
78
|
-
})(),
|
|
92
|
+
trustedSatisfied: readiness ? readiness.satisfied : null,
|
|
93
|
+
unresolvedRequired: readiness ? readiness.unresolved : null,
|
|
79
94
|
failed: trace.actions?.failed ?? 0,
|
|
80
95
|
ambiguous: trace.actions?.ambiguous ?? 0,
|
|
81
96
|
reconciliations: trace.actions?.reconciliationCount ?? 0,
|