@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
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { ledgerRelationSet, ledgerGroupedSet } from "./ledger-relations.js";
|
|
2
|
+
import { isLedgerEventCollection, ledgerEventsOfTypes } from "./ledger-event-collection.js";
|
|
3
|
+
import { normalizeDiagnosticSnapshot, compareDiagnosticCycles, classifyGain, isEffectiveGain } from "./information-gain.js";
|
|
3
4
|
import { computeFailureSignature } from "./failure-signature.js";
|
|
4
5
|
|
|
5
6
|
function snapshotFor(event) {
|
|
@@ -19,23 +20,43 @@ const sameSortedSet = (a, b) =>
|
|
|
19
20
|
// current diagnostic sequence inclusive.
|
|
20
21
|
// Events inside an interval belong to the *current* cycle's knowledge state;
|
|
21
22
|
// they are never attributed retroactively to the earlier diagnosis.
|
|
22
|
-
function intervalEvents(taskEvents, fromExclusive, toInclusive) {
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
function* intervalEvents(taskEvents, fromExclusive, toInclusive) {
|
|
24
|
+
for (const event of taskEvents) if (event.seq > fromExclusive && event.seq <= toInclusive) yield event;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function orderedTaskEvents(events, taskId) {
|
|
28
|
+
const belongs = event => !taskId || !event.taskId || event.taskId === taskId;
|
|
29
|
+
if (!Array.isArray(events) && isLedgerEventCollection(events)) {
|
|
30
|
+
let previous = -Infinity;
|
|
31
|
+
let ordered = true;
|
|
32
|
+
for (const event of events) {
|
|
33
|
+
if (!Number.isFinite(event.seq) || event.seq < previous) { ordered = false; break; }
|
|
34
|
+
previous = event.seq;
|
|
35
|
+
}
|
|
36
|
+
if (ordered) return { *[Symbol.iterator]() { for (const event of ledgerEventsOfTypes(events, ["DIAGNOSTIC_CASE_RECORDED", "DIAGNOSIS_RECORDED", "VERIFICATION_STARTED", "VERIFICATION_RECORDED", "INTERVENTION_RECORDED", "HYPOTHESIS_DISPOSITION_RECORDED"])) if (belongs(event)) yield event; } };
|
|
37
|
+
}
|
|
38
|
+
return [...(events ?? [])].filter(belongs).sort((a, b) => (a.seq ?? 0) - (b.seq ?? 0));
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function eliminatedHypothesis(previousDiagnostic, diagnostic) {
|
|
42
|
+
if (previousDiagnostic?.event !== "DIAGNOSTIC_CASE_RECORDED" || diagnostic.event !== "DIAGNOSTIC_CASE_RECORDED") return false;
|
|
43
|
+
const previous = previousDiagnostic.details?.hypotheses ?? [];
|
|
44
|
+
const current = diagnostic.details?.hypotheses ?? [];
|
|
45
|
+
const statements = new Set(current.map(hypothesis => `${hypothesis.statement}`.trim().toLowerCase()));
|
|
46
|
+
return previous.some(hypothesis => !current.some(candidate => candidate.id === hypothesis.id)
|
|
47
|
+
&& !statements.has(`${hypothesis.statement}`.trim().toLowerCase()));
|
|
25
48
|
}
|
|
26
49
|
|
|
27
50
|
function failureStateByCycle(taskEvents) {
|
|
28
|
-
const surfaces =
|
|
29
|
-
const signatures =
|
|
51
|
+
const surfaces = ledgerGroupedSet();
|
|
52
|
+
const signatures = ledgerGroupedSet();
|
|
30
53
|
const record = (cycle, details) => {
|
|
31
54
|
if (!Number.isInteger(cycle)) cycle = Number(cycle) || 1;
|
|
32
|
-
if (!surfaces.has(cycle)) surfaces.set(cycle, new Set());
|
|
33
|
-
if (!signatures.has(cycle)) signatures.set(cycle, new Set());
|
|
34
55
|
const requirement = details.requirement ?? details.id ?? details.checkId;
|
|
35
56
|
if (!requirement) return;
|
|
36
57
|
if (details.status === "failed" || details.status === "blocked") {
|
|
37
|
-
surfaces.
|
|
38
|
-
signatures.
|
|
58
|
+
surfaces.add(cycle, requirement);
|
|
59
|
+
signatures.add(cycle, computeFailureSignature({
|
|
39
60
|
requirement,
|
|
40
61
|
status: details.status,
|
|
41
62
|
exitCode: Number.isInteger(details.exitCode) ? details.exitCode : null,
|
|
@@ -44,13 +65,6 @@ function failureStateByCycle(taskEvents) {
|
|
|
44
65
|
}
|
|
45
66
|
};
|
|
46
67
|
for (const event of taskEvents) {
|
|
47
|
-
if (event.event === "VERIFICATION_STARTED") {
|
|
48
|
-
const cycle = event.details?.verificationCycle;
|
|
49
|
-
if (Number.isInteger(cycle)) {
|
|
50
|
-
if (!surfaces.has(cycle)) surfaces.set(cycle, new Set());
|
|
51
|
-
if (!signatures.has(cycle)) signatures.set(cycle, new Set());
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
68
|
if (event.event === "VERIFICATION_RECORDED") {
|
|
55
69
|
record(event.details?.verificationCycle ?? 1, event.details ?? {});
|
|
56
70
|
}
|
|
@@ -102,46 +116,45 @@ function snapshotHasContentDelta(previousDetails, currentDetails) {
|
|
|
102
116
|
|| differs(prev.evidence, cur.evidence);
|
|
103
117
|
}
|
|
104
118
|
|
|
105
|
-
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
119
|
+
function intervalKnowledge(taskEvents, previousDiagnostic, diagnostic) {
|
|
120
|
+
const intervalStart = previousDiagnostic ? previousDiagnostic.seq : -Infinity;
|
|
121
|
+
let hypothesisDispositionChanged = false;
|
|
122
|
+
const knownUpToPrev = ledgerRelationSet();
|
|
123
|
+
for (const event of taskEvents) {
|
|
124
|
+
if (event.event === "INTERVENTION_RECORDED" && previousDiagnostic && (event.seq ?? 0) <= previousDiagnostic.seq) {
|
|
125
|
+
const fingerprint = event.details?.interventionSemanticFingerprint
|
|
126
|
+
?? `${event.details?.intervention?.statement ?? ""}`.trim().toLowerCase();
|
|
127
|
+
if (fingerprint) knownUpToPrev.add(fingerprint);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
let novelIntervention = false;
|
|
131
|
+
for (const event of intervalEvents(taskEvents, intervalStart, diagnostic.seq)) {
|
|
132
|
+
if (event.event === "HYPOTHESIS_DISPOSITION_RECORDED") hypothesisDispositionChanged = true;
|
|
133
|
+
if (event.event === "INTERVENTION_RECORDED") {
|
|
134
|
+
const fingerprint = event.details?.interventionSemanticFingerprint
|
|
135
|
+
?? `${event.details?.intervention?.statement ?? ""}`.trim().toLowerCase();
|
|
136
|
+
if (fingerprint && !knownUpToPrev.has(fingerprint)) novelIntervention = true;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return { hypothesisDispositionChanged, novelIntervention };
|
|
140
|
+
}
|
|
109
141
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
142
|
+
function* iterateInformationGainProjection(events, taskId) {
|
|
143
|
+
const taskEvents = orderedTaskEvents(events, taskId);
|
|
144
|
+
const diagnosticEvents = { *[Symbol.iterator]() {
|
|
145
|
+
for (const event of taskEvents) {
|
|
146
|
+
if (["DIAGNOSTIC_CASE_RECORDED", "DIAGNOSIS_RECORDED"].includes(event.event)
|
|
147
|
+
&& (!taskId || event.taskId === taskId)) yield event;
|
|
148
|
+
}
|
|
149
|
+
} };
|
|
113
150
|
|
|
114
151
|
const failure = failureStateByCycle(taskEvents);
|
|
115
152
|
|
|
116
|
-
// Build final per-cycle entries first; effectiveGain is computed once at the
|
|
117
|
-
// end from fully final dimensions (no post-mutation anywhere).
|
|
118
|
-
const built = [];
|
|
119
153
|
let previousDiagnostic = null;
|
|
154
|
+
let previousSnapshot = null;
|
|
120
155
|
for (const diagnostic of diagnosticEvents) {
|
|
121
|
-
const
|
|
122
|
-
const interval = intervalEvents(taskEvents, intervalStart, diagnostic.seq);
|
|
123
|
-
|
|
124
|
-
const hypothesisDispositionChanged =
|
|
125
|
-
interval.some((event) => event.event === "HYPOTHESIS_DISPOSITION_RECORDED");
|
|
156
|
+
const { hypothesisDispositionChanged, novelIntervention } = intervalKnowledge(taskEvents, previousDiagnostic, diagnostic);
|
|
126
157
|
|
|
127
|
-
// Intervention deltas recognize only genuinely NEW semantic interventions:
|
|
128
|
-
// recorded after the previous diagnosis, attributed to the current
|
|
129
|
-
// correction cycle, and never attempted before. Repeating an already-known
|
|
130
|
-
// intervention — or merely having executed the previous cycle's corrective
|
|
131
|
-
// action — is not new information.
|
|
132
|
-
const knownUpToPrev = new Set(
|
|
133
|
-
taskEvents
|
|
134
|
-
.filter((event) => event.event === "INTERVENTION_RECORDED"
|
|
135
|
-
&& (previousDiagnostic ? (event.seq ?? 0) <= previousDiagnostic.seq : false))
|
|
136
|
-
.map((event) => event.details?.interventionSemanticFingerprint
|
|
137
|
-
?? `${event.details?.intervention?.statement ?? ""}`.trim().toLowerCase())
|
|
138
|
-
.filter(Boolean),
|
|
139
|
-
);
|
|
140
|
-
const novelInInterval = interval
|
|
141
|
-
.filter((event) => event.event === "INTERVENTION_RECORDED")
|
|
142
|
-
.map((event) => event.details?.interventionSemanticFingerprint
|
|
143
|
-
?? `${event.details?.intervention?.statement ?? ""}`.trim().toLowerCase())
|
|
144
|
-
.filter((fingerprint) => fingerprint && !knownUpToPrev.has(fingerprint));
|
|
145
158
|
// A genuinely new intervention counts as information only when the
|
|
146
159
|
// diagnosis itself moves: an identical re-proposal of the previous
|
|
147
160
|
// diagnosis after executing its already-known corrective action carries
|
|
@@ -149,18 +162,18 @@ export function buildInformationGainProjection(events, taskId) {
|
|
|
149
162
|
const sameSemanticsAsPrevious = Boolean(previousDiagnostic)
|
|
150
163
|
&& !snapshotHasContentDelta(previousDiagnostic.details ?? {}, diagnostic.details ?? {});
|
|
151
164
|
const interventionChanged = Boolean(previousDiagnostic)
|
|
152
|
-
&&
|
|
165
|
+
&& novelIntervention
|
|
153
166
|
&& !sameSemanticsAsPrevious;
|
|
154
167
|
|
|
155
168
|
const cycle = diagnostic.details?.verificationCycle ?? 1;
|
|
156
169
|
const previousCycle = previousDiagnostic?.details?.verificationCycle ?? null;
|
|
157
|
-
const surface = [...(failure.surfaces.
|
|
158
|
-
const signatures = [...(failure.signatures.
|
|
170
|
+
const surface = [...(failure.surfaces.values(cycle))].sort();
|
|
171
|
+
const signatures = [...(failure.signatures.values(cycle))].sort();
|
|
159
172
|
const previousSurface = previousCycle !== null && previousCycle !== undefined
|
|
160
|
-
? [...(failure.surfaces.
|
|
173
|
+
? [...(failure.surfaces.values(previousCycle))].sort()
|
|
161
174
|
: null;
|
|
162
175
|
const previousSignatures = previousCycle !== null && previousCycle !== undefined
|
|
163
|
-
? [...(failure.signatures.
|
|
176
|
+
? [...(failure.signatures.values(previousCycle))].sort()
|
|
164
177
|
: null;
|
|
165
178
|
const hasPreviousFailureState = previousSurface !== null;
|
|
166
179
|
const failureSurfaceChanged = hasPreviousFailureState
|
|
@@ -181,7 +194,7 @@ export function buildInformationGainProjection(events, taskId) {
|
|
|
181
194
|
|
|
182
195
|
const snapshot = snapshotFor(diagnostic);
|
|
183
196
|
|
|
184
|
-
|
|
197
|
+
const entry = {
|
|
185
198
|
verificationCycle: cycle,
|
|
186
199
|
sequence: diagnostic.seq ?? null,
|
|
187
200
|
diagnosticSequence: diagnostic.seq ?? null,
|
|
@@ -195,62 +208,53 @@ export function buildInformationGainProjection(events, taskId) {
|
|
|
195
208
|
failureSurfaceChanged,
|
|
196
209
|
interventionChanged,
|
|
197
210
|
strategyChanged,
|
|
198
|
-
hypothesisEliminated:
|
|
211
|
+
hypothesisEliminated: eliminatedHypothesis(previousDiagnostic, diagnostic),
|
|
199
212
|
},
|
|
200
213
|
evidence: {
|
|
201
214
|
semanticRefs: [...(snapshot.evidenceRefs ?? [])].sort(),
|
|
202
215
|
surface, signatures, strategyFingerprint,
|
|
203
216
|
},
|
|
204
|
-
}
|
|
205
|
-
|
|
217
|
+
};
|
|
218
|
+
const dimensions = compareDiagnosticCycles(previousSnapshot, snapshot, entry.dimensionsInput);
|
|
219
|
+
const classification = classifyGain(dimensions, { first: previousDiagnostic === null });
|
|
220
|
+
const effectiveGain = isEffectiveGain(dimensions, classification);
|
|
221
|
+
yield finalizeGainEntry(entry, { dimensions, classification, effectiveGain });
|
|
206
222
|
previousDiagnostic = diagnostic;
|
|
223
|
+
previousSnapshot = snapshot;
|
|
207
224
|
}
|
|
225
|
+
}
|
|
208
226
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
// churn is artificial novelty and must never create gain.
|
|
212
|
-
for (let i = 1; i < built.length; i++) {
|
|
213
|
-
if (built[i].sourceModel !== "STRUCTURED_DIAGNOSTIC_CASE_V1"
|
|
214
|
-
|| built[i - 1].sourceModel !== "STRUCTURED_DIAGNOSTIC_CASE_V1") continue;
|
|
215
|
-
const previousHypotheses = diagnosticEvents[i - 1]?.details?.hypotheses ?? [];
|
|
216
|
-
const currentHypotheses = diagnosticEvents[i]?.details?.hypotheses ?? [];
|
|
217
|
-
const currentStatements = new Set(currentHypotheses.map(
|
|
218
|
-
(hypothesis) => `${hypothesis.statement}`.trim().toLowerCase()));
|
|
219
|
-
built[i].dimensionsInput.hypothesisEliminated = previousHypotheses.some((hypothesis) => {
|
|
220
|
-
const survivedById = currentHypotheses.some((candidate) => candidate.id === hypothesis.id);
|
|
221
|
-
return !survivedById
|
|
222
|
-
&& !currentStatements.has(`${hypothesis.statement}`.trim().toLowerCase());
|
|
223
|
-
});
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// Final classification + single-point effectiveGain computation.
|
|
227
|
-
const entries = computeInformationGain(built.map((entry) => ({
|
|
227
|
+
function finalizeGainEntry(entry, { dimensions, classification, effectiveGain }) {
|
|
228
|
+
return Object.freeze({
|
|
228
229
|
verificationCycle: entry.verificationCycle,
|
|
229
230
|
sequence: entry.sequence,
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
evidence: Object.freeze({
|
|
242
|
-
semanticRefs: Object.freeze(entry.evidence.semanticRefs),
|
|
243
|
-
failureSurface: Object.freeze(entry.evidence.surface),
|
|
244
|
-
failureSignatures: Object.freeze(entry.evidence.signatures),
|
|
245
|
-
strategyFingerprint: entry.evidence.strategyFingerprint,
|
|
246
|
-
}),
|
|
247
|
-
dimensions: Object.freeze({ ...dimensions }),
|
|
248
|
-
classification,
|
|
249
|
-
effectiveGain,
|
|
250
|
-
});
|
|
231
|
+
diagnosticSequence: entry.diagnosticSequence,
|
|
232
|
+
sourceModel: entry.sourceModel,
|
|
233
|
+
evidence: Object.freeze({
|
|
234
|
+
semanticRefs: Object.freeze(entry.evidence.semanticRefs),
|
|
235
|
+
failureSurface: Object.freeze(entry.evidence.surface),
|
|
236
|
+
failureSignatures: Object.freeze(entry.evidence.signatures),
|
|
237
|
+
strategyFingerprint: entry.evidence.strategyFingerprint,
|
|
238
|
+
}),
|
|
239
|
+
dimensions: Object.freeze({ ...dimensions }),
|
|
240
|
+
classification,
|
|
241
|
+
effectiveGain,
|
|
251
242
|
});
|
|
252
243
|
}
|
|
253
244
|
|
|
245
|
+
export function buildInformationGainProjection(events, taskId) {
|
|
246
|
+
return [...iterateInformationGainProjection(events, taskId)];
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/** Internal latest-only use does not retain all diagnostic result objects. */
|
|
250
|
+
export function summarizeInformationGain(events, taskId, { verificationCycle = null } = {}) {
|
|
251
|
+
let latest = null;
|
|
252
|
+
for (const entry of iterateInformationGainProjection(events, taskId)) {
|
|
253
|
+
if (verificationCycle === null || verificationCycle === undefined || entry.verificationCycle === verificationCycle) latest = entry;
|
|
254
|
+
}
|
|
255
|
+
return evaluateStructuredDiagnosticStall(latest ? [latest] : []);
|
|
256
|
+
}
|
|
257
|
+
|
|
254
258
|
// One canonical structured-stall policy (fail-fast):
|
|
255
259
|
// The latest comparable diagnostic state that produces no effective
|
|
256
260
|
// information gain is stalled and may not trigger another blind correction
|
package/src/core/inspect.js
CHANGED
|
@@ -17,6 +17,8 @@ import { evaluateProgress } from "./progress.js";
|
|
|
17
17
|
import { readEvents } from "./events.js";
|
|
18
18
|
import { taskStructuralQualityDirectory } from "./task-paths.js";
|
|
19
19
|
import { projectStructuralQualityStatus } from "./structural-quality/status.js";
|
|
20
|
+
import { operationalArtifactExists } from "../storage/operational-context.js";
|
|
21
|
+
import { withProjectReadSnapshot } from "../storage/project-read-snapshot.js";
|
|
20
22
|
|
|
21
23
|
function reasonCodesFor({ state, trace, progress }) {
|
|
22
24
|
const codes = [];
|
|
@@ -169,7 +171,15 @@ function profileMetadata(bytes) {
|
|
|
169
171
|
|
|
170
172
|
import { taskArtifactPath } from "./task-paths.js";
|
|
171
173
|
|
|
172
|
-
|
|
174
|
+
async function inspectStatePresence(target, relativePath) {
|
|
175
|
+
return operationalArtifactExists(target, relativePath) ?? await fileExists(ensureWithin(target, relativePath));
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
export async function inspectTarget(options = {}) {
|
|
179
|
+
return withProjectReadSnapshot(options.target, () => inspectSelectedTarget(options));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async function inspectSelectedTarget({ target, packageRoot, contractFile = null, authorityContext, runtimeContext, taskId = null, stateFile = null } = {}) {
|
|
173
183
|
let manifest = null;
|
|
174
184
|
let manifestError = null;
|
|
175
185
|
try {
|
|
@@ -184,8 +194,7 @@ export async function inspectTarget({ target, packageRoot, contractFile = null,
|
|
|
184
194
|
? { ...profileMetadata(await readBytes(profilePath)), path: profileRelativePath }
|
|
185
195
|
: { mode: null, status: null };
|
|
186
196
|
const effectiveStateRel = stateFile ?? (taskId ? taskArtifactPath(taskId, "state") : WORK_STATE_PATH);
|
|
187
|
-
const
|
|
188
|
-
const statePresent = await fileExists(statePath);
|
|
197
|
+
const statePresent = await inspectStatePresence(target, effectiveStateRel);
|
|
189
198
|
const classifiedState = await readAndClassifyWorkState({ target, packageRoot, contractFile, taskId, stateFile: effectiveStateRel });
|
|
190
199
|
const rawState = classifiedState?.state ?? null;
|
|
191
200
|
const taskInfo = taskId ? await findTaskById(target, taskId, packageRoot) : null;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getStorageCapabilities } from "../storage/capabilities.js";
|
|
1
2
|
import { CLI_COMMAND_DEFINITIONS } from "./cli-command-definitions.js";
|
|
2
3
|
import { COMMAND_EXECUTORS } from "./command-executors.js";
|
|
3
4
|
import { PROTOCOL_VERSION } from "./protocol.js";
|
|
@@ -24,6 +25,7 @@ export const INTEGRATION_RISK_CLASSES = Object.freeze({
|
|
|
24
25
|
});
|
|
25
26
|
|
|
26
27
|
const READ_ONLY_COMMANDS = Object.freeze(new Set([
|
|
28
|
+
"storage-migration-status",
|
|
27
29
|
"protocol-info", "decision-status", "decision-show", "context-plan", "model-route", "semantic-plan", "test-inventory", "status", "next", "continuity", "reconcile-continuity",
|
|
28
30
|
"task-list", "task-show", "task-lock-status", "progress", "audit", "report",
|
|
29
31
|
"inspect", "validate-state", "validate-protocol", "validate-receipt",
|
|
@@ -69,6 +71,13 @@ const STATIC_RISK_CLASSES = Object.freeze({
|
|
|
69
71
|
"usage-record": INTEGRATION_RISK_CLASSES.LOOP_MUTATION,
|
|
70
72
|
init: INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
71
73
|
update: INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
74
|
+
"storage-migration-resume": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
75
|
+
"storage-rollback-resume": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
76
|
+
"storage-rollback": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
77
|
+
"storage-migrate": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
78
|
+
"storage-backup": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
79
|
+
"storage-restore": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
80
|
+
"storage-restore-resume": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
72
81
|
activate: INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
73
82
|
"task-migrate": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
74
83
|
"migrate-protocol": INTEGRATION_RISK_CLASSES.MAINTENANCE,
|
|
@@ -143,6 +152,7 @@ export function getForgeLoopCapabilities({ packageVersion = null } = {}) {
|
|
|
143
152
|
integrationApiVersion: FORGELOOP_INTEGRATION_RUNTIME_VERSION,
|
|
144
153
|
executorParity: Object.keys(COMMAND_EXECUTORS).length === Object.keys(CLI_COMMAND_DEFINITIONS).length,
|
|
145
154
|
features: {
|
|
155
|
+
operationalStorage: getStorageCapabilities(),
|
|
146
156
|
taskClaimRecovery: {
|
|
147
157
|
version: 1,
|
|
148
158
|
durableRecoveryState: true,
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
|
+
import { withProjectStorage } from "../storage/project-boundary.js";
|
|
3
|
+
import { withProjectReadSnapshot } from "../storage/project-read-snapshot.js";
|
|
2
4
|
import { runProtocolInfo } from "../commands/protocol-info.js";
|
|
3
5
|
import { readContract } from "./contract.js";
|
|
4
|
-
import {
|
|
6
|
+
import { discoverTaskSummaries } from "./task-discovery.js";
|
|
5
7
|
import { resolveTaskClaimState } from "./task-claim-state.js";
|
|
6
8
|
import { runStatus } from "../commands/status.js";
|
|
7
9
|
import { runContinuity } from "../commands/continuity.js";
|
|
@@ -10,11 +12,11 @@ import { listApprovals } from "./approvals.js";
|
|
|
10
12
|
import { buildTrajectoryMetrics } from "./trajectory-metrics.js";
|
|
11
13
|
import { loadCapabilityPolicy } from "./capability-policy.js";
|
|
12
14
|
import { readdir } from "node:fs/promises";
|
|
13
|
-
import {
|
|
15
|
+
import { getOperationalStore, listOperationalArtifactNames } from "../storage/operational-context.js";
|
|
16
|
+
import { readJsonArtifact, parseCapturedJsonArtifact } from "./artifacts.js";
|
|
14
17
|
import { taskDirectory } from "./task-paths.js";
|
|
15
18
|
import { resolveWorkspaceBindingStatus } from "./workspace-binding.js";
|
|
16
|
-
import {
|
|
17
|
-
import { readHandoffAcceptanceLedger, resolveHandoffAcceptance } from "./handoff-acceptance.js";
|
|
19
|
+
import { runHandoffList } from "../commands/handoff-list.js";
|
|
18
20
|
import { resolveResponsibilityStatus } from "./responsibility.js";
|
|
19
21
|
import { readVerificationScope } from "./verification-scope.js";
|
|
20
22
|
import { resolveAttestationStatus } from "./attestation.js";
|
|
@@ -118,6 +120,12 @@ function ownershipProjection(projection) {
|
|
|
118
120
|
};
|
|
119
121
|
}
|
|
120
122
|
|
|
123
|
+
async function resourceArtifactNames(projectPath, taskId, kind, directory) {
|
|
124
|
+
const names = listOperationalArtifactNames(projectPath, taskId, kind);
|
|
125
|
+
if (names !== null) return names;
|
|
126
|
+
try { return await readdir(directory); } catch { return []; }
|
|
127
|
+
}
|
|
128
|
+
|
|
121
129
|
async function readForgeLoopIntegrationResourceCore(uri, {
|
|
122
130
|
projectPath = ".",
|
|
123
131
|
packageRoot = undefined,
|
|
@@ -135,13 +143,8 @@ async function readForgeLoopIntegrationResourceCore(uri, {
|
|
|
135
143
|
|
|
136
144
|
if (uri === "protocol/info") return { uri, data: await runProtocolInfo({ packageVersion }) };
|
|
137
145
|
if (uri === "project/tasks") {
|
|
138
|
-
const tasks = await
|
|
139
|
-
return { uri, data: { count: tasks.length, tasks
|
|
140
|
-
taskId: task.taskId,
|
|
141
|
-
healthy: task.healthy !== false,
|
|
142
|
-
phase: task.phase ?? null,
|
|
143
|
-
mutationAllowed: task.mutationAllowed !== false,
|
|
144
|
-
})) } };
|
|
146
|
+
const tasks = await discoverTaskSummaries(projectPath, packageRoot);
|
|
147
|
+
return { uri, data: { count: tasks.length, tasks } };
|
|
145
148
|
}
|
|
146
149
|
if (TASK_RESOURCE_URIS.has(uri) && (typeof taskId !== "string" || !taskId)) {
|
|
147
150
|
const error = new Error(`Resource ${uri} requires a taskId`);
|
|
@@ -164,27 +167,7 @@ async function readForgeLoopIntegrationResourceCore(uri, {
|
|
|
164
167
|
return { uri, taskId, data: await resolveWorkspaceBindingStatus(projectPath, { packageRoot, taskId }) };
|
|
165
168
|
}
|
|
166
169
|
if (uri === "task/handoffs") {
|
|
167
|
-
|
|
168
|
-
const ledger = await readHandoffAcceptanceLedger(projectPath, packageRoot, { taskId });
|
|
169
|
-
const projectedHandoffs = handoffs.map((handoff) => {
|
|
170
|
-
const resolved = resolveHandoffAcceptance({
|
|
171
|
-
events: ledger.events,
|
|
172
|
-
handoff,
|
|
173
|
-
ledgerValid: ledger.valid,
|
|
174
|
-
ledgerErrors: ledger.errors,
|
|
175
|
-
});
|
|
176
|
-
return {
|
|
177
|
-
...handoff,
|
|
178
|
-
acceptance: {
|
|
179
|
-
status: resolved.status,
|
|
180
|
-
consumerId: resolved.consumerId ?? null,
|
|
181
|
-
harness: resolved.harness ?? null,
|
|
182
|
-
acceptedAt: resolved.acceptedAt ?? null,
|
|
183
|
-
...(resolved.reasonCodes ? { reasonCodes: [...resolved.reasonCodes] } : {}),
|
|
184
|
-
},
|
|
185
|
-
};
|
|
186
|
-
});
|
|
187
|
-
return { uri, taskId, data: { taskId, count: projectedHandoffs.length, handoffs: projectedHandoffs } };
|
|
170
|
+
return { uri, taskId, data: await runHandoffList({ target: projectPath, packageRoot, taskId }) };
|
|
188
171
|
}
|
|
189
172
|
if (uri === "task/responsibility") {
|
|
190
173
|
return { uri, taskId, data: await resolveResponsibilityStatus(projectPath, { packageRoot, taskId }) };
|
|
@@ -229,7 +212,7 @@ async function readForgeLoopIntegrationResourceCore(uri, {
|
|
|
229
212
|
}
|
|
230
213
|
if (uri === "task/evaluations") {
|
|
231
214
|
const dir = path.join(projectPath, taskDirectory(taskId), "evaluations");
|
|
232
|
-
|
|
215
|
+
const names = await resourceArtifactNames(projectPath, taskId, "evaluations", dir);
|
|
233
216
|
const evaluations = [];
|
|
234
217
|
for (const name of names.filter((entry) => /^eval-[A-Za-z0-9_-]+\.json$/.test(entry)).sort()) {
|
|
235
218
|
evaluations.push((await readJsonArtifact(projectPath, `${taskDirectory(taskId)}/evaluations/${name}`, "trajectory-evaluation", packageRoot)).value);
|
|
@@ -237,8 +220,13 @@ async function readForgeLoopIntegrationResourceCore(uri, {
|
|
|
237
220
|
return { uri, taskId, data: { evaluations } };
|
|
238
221
|
}
|
|
239
222
|
if (uri === "task/decisions") {
|
|
223
|
+
const store = getOperationalStore(projectPath);
|
|
224
|
+
if (store) {
|
|
225
|
+
const decisions = await readCapturedDecisions(store, taskId, packageRoot);
|
|
226
|
+
return { uri, taskId, data: { taskId, decisions } };
|
|
227
|
+
}
|
|
240
228
|
const dir = path.join(projectPath, taskDirectory(taskId), "decisions");
|
|
241
|
-
|
|
229
|
+
const names = await resourceArtifactNames(projectPath, taskId, "decisions", dir);
|
|
242
230
|
const decisions = [];
|
|
243
231
|
for (const name of names.filter((entry) => /^[A-Za-z0-9][A-Za-z0-9_-]{0,127}\.json$/.test(entry)).sort()) {
|
|
244
232
|
decisions.push((await readJsonArtifact(projectPath, `${taskDirectory(taskId)}/decisions/${name}`, "semantic-decision", packageRoot)).value);
|
|
@@ -297,6 +285,15 @@ async function readForgeLoopIntegrationResourceCore(uri, {
|
|
|
297
285
|
}
|
|
298
286
|
|
|
299
287
|
export async function readForgeLoopIntegrationResource(uri, options = {}) {
|
|
288
|
+
const target = options.projectPath ?? ".";
|
|
289
|
+
const read = () => readSelectedIntegrationResource(uri, options);
|
|
290
|
+
// Decisions capture their finite catalog and bytes synchronously; other
|
|
291
|
+
// task projections retain the complete committed project snapshot.
|
|
292
|
+
return withProjectStorage(target, () => TASK_RESOURCE_URIS.has(uri) && uri !== "task/decisions"
|
|
293
|
+
? withProjectReadSnapshot(target, read) : read(), { readOnly: true, runtimeContext: options.runtimeContext });
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
async function readSelectedIntegrationResource(uri, options) {
|
|
300
297
|
if (uri === "task/audit-view") {
|
|
301
298
|
if (typeof options.taskId !== "string" || !options.taskId) {
|
|
302
299
|
const error = new Error("Resource task/audit-view requires a taskId"); error.code = "E_TASK_REQUIRED"; throw error;
|
|
@@ -328,3 +325,13 @@ export async function readForgeLoopIntegrationResource(uri, options = {}) {
|
|
|
328
325
|
}
|
|
329
326
|
return readForgeLoopIntegrationResourceCore(uri, options);
|
|
330
327
|
}
|
|
328
|
+
|
|
329
|
+
|
|
330
|
+
async function readCapturedDecisions(store, taskId, packageRoot) {
|
|
331
|
+
const decisions = [];
|
|
332
|
+
for (const entry of store.captureDecisionTexts(taskId)) {
|
|
333
|
+
if (entry.error) throw entry.error;
|
|
334
|
+
decisions.push((await parseCapturedJsonArtifact(entry.text, entry.relativePath, "semantic-decision", packageRoot)).value);
|
|
335
|
+
}
|
|
336
|
+
return decisions;
|
|
337
|
+
}
|