@cassiomc1/forgeloop 1.13.0 → 1.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENT_COMPATIBILITY.md +8 -0
- package/DOCS_INDEX.md +35 -3
- package/ENG/nodejs-backend-development-eng.md +2 -2
- package/ENG/sec-code-eng.md +7 -7
- package/EXECUTION_STATE.md +12 -0
- package/LOOP_ENGINEERING.md +28 -2
- package/ORCHESTRATOR_INTEGRATION.md +9 -5
- package/PROTOCOL_INTEGRATION.md +55 -2
- package/README.md +40 -25
- package/TERMINOLOGY.md +2 -0
- package/THREAT_MODEL.md +140 -1
- package/completions/_forgeloop +19 -1
- package/completions/forgeloop.bash +37 -1
- package/completions/forgeloop.fish +123 -1
- package/docs/ADVISORY_CONTEXT.md +25 -0
- package/docs/AGENT_BROWSER_ADAPTER.md +81 -0
- package/docs/AGENT_BROWSER_VERIFICATION.md +6 -0
- package/docs/AGENT_PROTOCOL_SUMMARY.md +27 -2
- package/docs/AGENT_SKILL.md +66 -0
- package/docs/ARTIFACT_REFERENCE.md +123 -0
- package/docs/AUDIT_UX.md +46 -0
- package/docs/BROWSER_VERIFICATION.md +136 -0
- package/docs/CLI_REFERENCE.md +366 -6
- package/docs/CODE_ATTESTATION.md +2 -2
- package/docs/DOCUMENTATION_GUIDE.md +32 -11
- package/docs/JEV_BENCHMARKS.md +31 -0
- package/docs/MODEL_ROUTING.md +37 -0
- package/docs/OPENSRC_ADAPTER.md +241 -0
- package/docs/PACKAGE_CONTENTS.md +35 -8
- package/docs/PROVIDERS.md +126 -0
- package/docs/PROVIDER_ARCHITECTURE.md +199 -0
- package/docs/RECIPES.md +9 -0
- package/docs/RELEASE_CHECKLIST.md +38 -5
- package/docs/SECURITY_REVIEW.md +71 -0
- package/docs/SEMANTIC_DECISION_PLANE.md +71 -0
- package/docs/TEST_INTELLIGENCE.md +29 -0
- package/docs/TEST_PRUNING.md +14 -0
- package/docs/TROUBLESHOOTING.md +198 -1
- package/docs/UNIVERSAL_INTEGRATION.md +31 -0
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.html +2 -2
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.receipt.json +5 -5
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.svg +1 -1
- package/docs/assets/diagrams/forgeloop-engineering-flow.html +39 -26
- package/docs/assets/diagrams/forgeloop-engineering-flow.receipt.json +6 -6
- package/docs/assets/diagrams/forgeloop-engineering-flow.svg +26 -26
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.html +2 -1
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.receipt.json +5 -5
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.svg +1 -1
- package/docs/diagrams/README.md +13 -9
- package/docs/diagrams/forgeloop-code-attestation-flow.workflow.json +1 -1
- package/docs/diagrams/forgeloop-engineering-flow.workflow.json +24 -19
- package/docs/diagrams/forgeloop-verification-trust-flow.workflow.json +1 -0
- package/docs/diagrams/reviews/forgeloop-code-attestation-flow.review.json +4 -4
- package/docs/diagrams/reviews/forgeloop-engineering-flow.review.json +4 -4
- package/docs/diagrams/reviews/forgeloop-verification-trust-flow.review.json +4 -4
- package/docs/documentation-manifest.json +750 -5
- package/docs/protocol-requirements.json +24 -0
- package/package.json +30 -3
- package/schemas/config.schema.json +14 -0
- package/schemas/context-plan.schema.json +18 -0
- package/schemas/semantic-decision.schema.json +46 -0
- package/schemas/test-utility.schema.json +44 -0
- package/scripts/CI_VALIDATORS.md +6 -6
- package/scripts/benchmark-jev.mjs +5 -0
- package/scripts/benchmark-test-intelligence.mjs +4 -0
- package/scripts/generate-agent-protocol-summary.mjs +4 -1
- package/scripts/generate-forgeloop-skill.mjs +133 -0
- package/scripts/jev-smoke.mjs +19 -0
- package/skills/forgeloop/README.md +9 -0
- package/skills/forgeloop/SKILL.md +77 -0
- package/skills/forgeloop/references/lifecycle.md +9 -0
- package/skills/forgeloop/references/recovery.md +7 -0
- package/skills/forgeloop/references/verification.md +7 -0
- package/src/adapters/agent-browser/assertions.js +47 -0
- package/src/adapters/agent-browser/commands.js +54 -0
- package/src/adapters/agent-browser/index.js +3 -0
- package/src/adapters/agent-browser/locator.js +40 -0
- package/src/adapters/agent-browser/process.js +215 -0
- package/src/adapters/agent-browser/provider.js +313 -0
- package/src/adapters/emulated-services/constants.js +24 -0
- package/src/adapters/emulated-services/index.js +7 -0
- package/src/adapters/emulated-services/process.js +162 -0
- package/src/adapters/emulated-services/provider.js +282 -0
- package/src/adapters/opensrc/normalize.js +90 -0
- package/src/adapters/opensrc/process.js +248 -0
- package/src/adapters/opensrc/provider.js +338 -0
- package/src/adapters/opensrc/search.js +264 -0
- package/src/adapters/typesafe/client.js +28 -0
- package/src/adapters/typesafe/engine.js +63 -0
- package/src/adapters/typesafe/normalize.js +41 -0
- package/src/cli.js +108 -0
- package/src/commands/checkpoint-revalidate.js +176 -0
- package/src/commands/context-plan.js +38 -0
- package/src/commands/contract-create.js +264 -0
- package/src/commands/contract-revise.js +236 -0
- package/src/commands/decision-show.js +14 -0
- package/src/commands/decision-status.js +22 -0
- package/src/commands/discover.js +41 -0
- package/src/commands/doctor.js +15 -0
- package/src/commands/gate-record.js +205 -0
- package/src/commands/gate-revalidate.js +137 -0
- package/src/commands/model-route.js +32 -0
- package/src/commands/route.js +146 -18
- package/src/commands/semantic-plan.js +17 -0
- package/src/commands/task-abandon.js +224 -0
- package/src/commands/task-migrate-contract-bootstrap-repair.js +288 -0
- package/src/commands/task-repair-contract-bootstrap.js +263 -0
- package/src/commands/test-inventory.js +5 -0
- package/src/commands/test-prune-plan.js +5 -0
- package/src/commands/test-prune-probe.js +5 -0
- package/src/commands/test-utility.js +5 -0
- package/src/commands/validate-protocol.js +10 -1
- package/src/core/artifact-registry.js +24 -0
- package/src/core/audit-ux.js +514 -0
- package/src/core/browser-verification/constants.js +149 -0
- package/src/core/browser-verification/normalize.js +254 -0
- package/src/core/browser-verification/provider.js +519 -0
- package/src/core/browser-verification/service.js +115 -0
- package/src/core/checkpoint-revalidation.js +319 -0
- package/src/core/cli-command-definitions.js +241 -0
- package/src/core/command-executors.js +110 -0
- package/src/core/command-input.js +115 -43
- package/src/core/completion-artifacts.js +14 -5
- package/src/core/completion.js +4 -6
- package/src/core/config.js +3 -0
- package/src/core/context-compiler/budget.js +9 -0
- package/src/core/context-compiler/candidates.js +39 -0
- package/src/core/context-compiler/compiler.js +63 -0
- package/src/core/context-compiler/fingerprint.js +11 -0
- package/src/core/context-compiler/policy.js +13 -0
- package/src/core/context-compiler/result.js +23 -0
- package/src/core/contract-bootstrap-recovery.js +655 -0
- package/src/core/contract-revision.js +210 -0
- package/src/core/decision/artifact.js +69 -0
- package/src/core/decision/benchmarks.js +103 -0
- package/src/core/decision/cache.js +27 -0
- package/src/core/decision/constants.js +58 -0
- package/src/core/decision/cutover.js +34 -0
- package/src/core/decision/engine.js +22 -0
- package/src/core/decision/errors.js +68 -0
- package/src/core/decision/events.js +101 -0
- package/src/core/decision/freshness.js +19 -0
- package/src/core/decision/normalizers/index.js +115 -0
- package/src/core/decision/policy.js +18 -0
- package/src/core/decision/projection.js +16 -0
- package/src/core/decision/question-registry.js +201 -0
- package/src/core/decision/request.js +26 -0
- package/src/core/decision/resolver.js +130 -0
- package/src/core/decision/result.js +58 -0
- package/src/core/decision/service.js +156 -0
- package/src/core/decision/state-builder.js +65 -0
- package/src/core/decision/task-bindings.js +30 -0
- package/src/core/decision/test-provider.js +32 -0
- package/src/core/decision/thresholds.js +15 -0
- package/src/core/error-codes.js +278 -0
- package/src/core/events.js +226 -57
- package/src/core/evidence-readiness.js +9 -0
- package/src/core/execution-prerequisites.js +14 -0
- package/src/core/execution-profile.js +63 -38
- package/src/core/gate-provenance.js +124 -0
- package/src/core/integration-invocation-policy.js +27 -4
- package/src/core/integration-resources.js +86 -61
- package/src/core/model-router/constants.js +10 -0
- package/src/core/model-router/policy.js +103 -0
- package/src/core/model-router/router.js +37 -0
- package/src/core/next-action-model.js +58 -0
- package/src/core/next-action-phases.js +130 -42
- package/src/core/next-action-refresh.js +43 -9
- package/src/core/next-action-review-phase.js +7 -2
- package/src/core/next-action.js +35 -7
- package/src/core/phase.js +128 -10
- package/src/core/preflight-consistency.js +23 -9
- package/src/core/preflight-loaders.js +37 -5
- package/src/core/protocol-info.js +65 -0
- package/src/core/protocol.js +20 -0
- package/src/core/reconcile-closure.js +128 -52
- package/src/core/recovery-history.js +1 -0
- package/src/core/resumability.js +154 -44
- package/src/core/route-artifact.js +15 -1
- package/src/core/router.js +67 -1
- package/src/core/runtime-context.js +118 -61
- package/src/core/schema-validation.js +3 -0
- package/src/core/security-review/constants.js +64 -0
- package/src/core/security-review/normalize.js +245 -0
- package/src/core/security-review/provider.js +204 -0
- package/src/core/security-review/service.js +134 -0
- package/src/core/semantic-planning/constants.js +19 -0
- package/src/core/semantic-planning/projection.js +94 -0
- package/src/core/semantic-planning/service.js +15 -0
- package/src/core/sources.js +37 -0
- package/src/core/task-claim-state.js +201 -1
- package/src/core/task-conflict-inspection.js +31 -5
- package/src/core/task-paths.js +13 -0
- package/src/core/task-recovery.js +1 -0
- package/src/core/templates.js +3 -0
- package/src/core/test-intelligence/benchmarks.js +68 -0
- package/src/core/test-intelligence/inventory.js +73 -0
- package/src/core/test-intelligence/prune.js +90 -0
- package/src/core/test-intelligence/semantic-state.js +15 -0
- package/src/core/test-intelligence/service.js +40 -0
- package/src/core/test-intelligence/utility.js +50 -0
- package/src/core/trace.js +11 -7
- package/src/core/transaction.js +1 -0
- package/src/integration.d.ts +492 -0
- package/src/integration.js +54 -0
- package/src/providers/README.md +47 -0
- package/src/providers/capabilities.js +46 -0
- package/src/providers/errors.js +15 -0
- package/src/providers/index.js +29 -0
- package/src/providers/json-snapshot.js +105 -0
- package/src/providers/registry.js +152 -0
|
@@ -80,6 +80,18 @@ function sameReadyPreflightEvent(event, result) {
|
|
|
80
80
|
&& sameStringSet(event.details?.satisfiedGates, result.satisfiedGates);
|
|
81
81
|
}
|
|
82
82
|
|
|
83
|
+
function latestContractRevisionSeq(events) {
|
|
84
|
+
return events.findLast((event) => event.event === "CONTRACT_REVISED")?.seq ?? 0;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function hasCurrentRevisionGate(events, taskId, gate) {
|
|
88
|
+
const revisionSeq = latestContractRevisionSeq(events);
|
|
89
|
+
return events.some((event) => event.event === "GATE_SATISFIED"
|
|
90
|
+
&& event.taskId === taskId
|
|
91
|
+
&& event.details?.gate === gate
|
|
92
|
+
&& event.seq > revisionSeq);
|
|
93
|
+
}
|
|
94
|
+
|
|
83
95
|
export async function validateReadyProtocolConsistency({
|
|
84
96
|
target,
|
|
85
97
|
packageRoot,
|
|
@@ -141,9 +153,7 @@ export async function validateReadyProtocolConsistency({
|
|
|
141
153
|
}
|
|
142
154
|
}
|
|
143
155
|
for (const gate of persisted.satisfiedGates ?? []) {
|
|
144
|
-
if (!events
|
|
145
|
-
&& event.taskId === persisted.taskId
|
|
146
|
-
&& event.details?.gate === gate)) {
|
|
156
|
+
if (!hasCurrentRevisionGate(events, persisted.taskId, gate)) {
|
|
147
157
|
errors.push(issue("E_PREFLIGHT_GATE_EVENT_MISSING", `READY preflight is missing lifecycle gate event: ${gate}`, [eventsRel, `${gatesRel}/${gate}.json`]));
|
|
148
158
|
}
|
|
149
159
|
}
|
|
@@ -189,9 +199,12 @@ export function assertExistingReadyLifecycleCompatibility(ledger, result) {
|
|
|
189
199
|
const existingReady = events.findLast((event) => event.event === "PREFLIGHT_READY" && event.taskId === result.taskId);
|
|
190
200
|
if (!existingReady) return;
|
|
191
201
|
const latestOutcome = latestPreflightOutcomeEvent(events, result.taskId);
|
|
202
|
+
const revisedAfterReady = events.some((event) => event.event === "CONTRACT_REVISED"
|
|
203
|
+
&& event.taskId === result.taskId
|
|
204
|
+
&& event.seq > existingReady.seq);
|
|
192
205
|
// A READY outcome superseded by a later BLOCKED outcome may be replaced by a
|
|
193
206
|
// fresh READY with different details once the blocked preflight is resolved.
|
|
194
|
-
if (latestOutcome?.event === "PREFLIGHT_BLOCKED") return;
|
|
207
|
+
if (latestOutcome?.event === "PREFLIGHT_BLOCKED" || revisedAfterReady) return;
|
|
195
208
|
if (!sameReadyPreflightEvent(existingReady, result)) {
|
|
196
209
|
throw preflightError(
|
|
197
210
|
"E_PHASE_CHRONOLOGY_INVALID",
|
|
@@ -217,9 +230,7 @@ export async function appendActivationEvents(target, packageRoot, ledger, result
|
|
|
217
230
|
await append({ taskId: result.taskId, event: "ROUTE_VALIDATED", fingerprint: result.fingerprints.routing });
|
|
218
231
|
}
|
|
219
232
|
for (const gate of result.satisfiedGates) {
|
|
220
|
-
if (!events
|
|
221
|
-
&& event.taskId === result.taskId
|
|
222
|
-
&& event.details?.gate === gate)) {
|
|
233
|
+
if (!hasCurrentRevisionGate(events, result.taskId, gate)) {
|
|
223
234
|
await append({ taskId: result.taskId, event: "GATE_SATISFIED", details: { gate } });
|
|
224
235
|
}
|
|
225
236
|
}
|
|
@@ -227,15 +238,18 @@ export async function appendActivationEvents(target, packageRoot, ledger, result
|
|
|
227
238
|
const existingReady = events.findLast((event) => event.event === "PREFLIGHT_READY" && event.taskId === result.taskId);
|
|
228
239
|
const latestOutcome = latestPreflightOutcomeEvent(events, result.taskId);
|
|
229
240
|
const readySupersededByBlocked = existingReady && latestOutcome?.event === "PREFLIGHT_BLOCKED";
|
|
241
|
+
const readySupersededByContractRevision = existingReady && events.some((event) => event.event === "CONTRACT_REVISED"
|
|
242
|
+
&& event.taskId === result.taskId
|
|
243
|
+
&& event.seq > existingReady.seq);
|
|
230
244
|
if (result.status === "READY") {
|
|
231
|
-
if (existingReady && !readySupersededByBlocked && !sameReadyPreflightEvent(existingReady, result)) {
|
|
245
|
+
if (existingReady && !readySupersededByBlocked && !readySupersededByContractRevision && !sameReadyPreflightEvent(existingReady, result)) {
|
|
232
246
|
throw preflightError(
|
|
233
247
|
"E_PHASE_CHRONOLOGY_INVALID",
|
|
234
248
|
"PREFLIGHT_READY already exists with different READY preflight details; repair the contract, route, or gate lifecycle before refreshing preflight",
|
|
235
249
|
[ARTIFACT_PATHS.preflight, ARTIFACT_PATHS.events, ARTIFACT_PATHS.contract, ARTIFACT_PATHS.route, ARTIFACT_PATHS.gates],
|
|
236
250
|
);
|
|
237
251
|
}
|
|
238
|
-
if (!existingReady || readySupersededByBlocked) {
|
|
252
|
+
if (!existingReady || readySupersededByBlocked || readySupersededByContractRevision) {
|
|
239
253
|
await append({
|
|
240
254
|
taskId: result.taskId,
|
|
241
255
|
event: "PREFLIGHT_READY",
|
|
@@ -2,13 +2,15 @@ import { ARTIFACT_PATHS, readJsonArtifact } from "./artifacts.js";
|
|
|
2
2
|
import { readContract } from "./contract.js";
|
|
3
3
|
import { readGateIfPresent, validateGateArtifacts } from "./gate-artifact.js";
|
|
4
4
|
import { requiredGatesForGuides } from "./guide-metadata.js";
|
|
5
|
-
import {
|
|
5
|
+
import { assertContractPresetRefs, assertContractSourceProvenance, externalContractSourceRefs } from "./sources.js";
|
|
6
6
|
import { readPersistedRoute } from "./route-artifact.js";
|
|
7
7
|
import { ensureWithin, readBytes } from "./filesystem.js";
|
|
8
8
|
import { sha256 } from "./manifest.js";
|
|
9
9
|
import { findProfilePath } from "./profile.js";
|
|
10
10
|
import { issue } from "./preflight-model.js";
|
|
11
11
|
import { taskGatePath, taskArtifactPath } from "./task-paths.js";
|
|
12
|
+
import { readEvents } from "./events.js";
|
|
13
|
+
import { hasCurrentGateEvidence } from "./gate-provenance.js";
|
|
12
14
|
|
|
13
15
|
export async function readProfile(target) {
|
|
14
16
|
const relativePath = await findProfilePath(target);
|
|
@@ -62,15 +64,25 @@ export async function loadRoute(target, packageRoot, errors, options = {}) {
|
|
|
62
64
|
|
|
63
65
|
export async function loadSources(target, contract, packageRoot, errors) {
|
|
64
66
|
if (!contract?.value?.sourceRefs?.length) return null;
|
|
65
|
-
|
|
67
|
+
const refs = contract.value.sourceRefs;
|
|
66
68
|
try {
|
|
67
|
-
|
|
69
|
+
assertContractPresetRefs(refs);
|
|
68
70
|
} catch (error) {
|
|
69
|
-
errors.push(issue(error.code
|
|
71
|
+
errors.push(issue(error.code ?? "E_PROFILE_SOURCE_UNKNOWN", error.message, [ARTIFACT_PATHS.sources]));
|
|
70
72
|
return null;
|
|
71
73
|
}
|
|
74
|
+
const externalRefs = externalContractSourceRefs(refs);
|
|
75
|
+
let registry;
|
|
76
|
+
if (externalRefs.length > 0) {
|
|
77
|
+
try {
|
|
78
|
+
registry = (await readJsonArtifact(target, ARTIFACT_PATHS.sources, "source-registry", packageRoot)).value;
|
|
79
|
+
} catch (error) {
|
|
80
|
+
errors.push(issue(error.code === "ARTIFACT_MISSING" ? "E_PROFILE_SOURCE_MISSING" : "E_PROFILE_SOURCE_UNKNOWN", error.message, [ARTIFACT_PATHS.sources]));
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
72
84
|
try {
|
|
73
|
-
|
|
85
|
+
assertContractSourceProvenance(registry, refs);
|
|
74
86
|
} catch (error) {
|
|
75
87
|
errors.push(issue(error.code ?? "E_PROFILE_SOURCE_UNKNOWN", error.message, [ARTIFACT_PATHS.sources]));
|
|
76
88
|
}
|
|
@@ -84,6 +96,15 @@ export async function inspectGates(target, contract, route, packageRoot, errors,
|
|
|
84
96
|
const satisfied = [];
|
|
85
97
|
const records = {};
|
|
86
98
|
const taskId = options.taskId ?? null;
|
|
99
|
+
let events = [];
|
|
100
|
+
if (taskId) {
|
|
101
|
+
try {
|
|
102
|
+
events = await readEvents(target, packageRoot, { taskId });
|
|
103
|
+
} catch {
|
|
104
|
+
events = [];
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
const latestContractRevisionSeq = events.findLast((event) => event.event === "CONTRACT_REVISED")?.seq ?? 0;
|
|
87
108
|
for (const gate of required) {
|
|
88
109
|
let artifact;
|
|
89
110
|
const defaultGateRel = taskId ? taskGatePath(taskId, gate) : `${ARTIFACT_PATHS.gates}/${gate}.json`;
|
|
@@ -111,6 +132,17 @@ export async function inspectGates(target, contract, route, packageRoot, errors,
|
|
|
111
132
|
errors.push(issue("E_GATE_STALE", `Gate ${gate} references stale artifacts`, [artifact.path], { gate, stale }));
|
|
112
133
|
continue;
|
|
113
134
|
}
|
|
135
|
+
const currentEpochSatisfied = latestContractRevisionSeq === 0
|
|
136
|
+
|| hasCurrentGateEvidence(events, taskId, gate, latestContractRevisionSeq);
|
|
137
|
+
if (!currentEpochSatisfied) {
|
|
138
|
+
errors.push(issue(
|
|
139
|
+
"E_GATE_UNVERIFIED",
|
|
140
|
+
`Required gate ${gate} was satisfied before the current contract revision`,
|
|
141
|
+
[defaultGateRel],
|
|
142
|
+
{ gate },
|
|
143
|
+
));
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
114
146
|
satisfied.push(gate);
|
|
115
147
|
}
|
|
116
148
|
return { required, satisfied: satisfied.sort(), records };
|
|
@@ -4,6 +4,8 @@ import { PUBLIC_ERROR_REGISTRY } from "./error-codes.js";
|
|
|
4
4
|
import { GUIDE_REGISTRY } from "./guide-registry.js";
|
|
5
5
|
import { PROTOCOL_VERSION, WORK_PHASES, WORK_TRANSITIONS } from "./protocol.js";
|
|
6
6
|
import { VERIFICATION_ISOLATION_MODES } from "./verification-execution.js";
|
|
7
|
+
import { PROVIDER_KINDS } from "../providers/capabilities.js";
|
|
8
|
+
import { DECISION_DEFAULT_POLICY, DECISION_ENGINE_ID, PINNED_JEV_MODEL } from "./decision/constants.js";
|
|
7
9
|
|
|
8
10
|
export const SCHEMA_COMPATIBILITY_POLICY = Object.freeze({
|
|
9
11
|
protocolVersion: PROTOCOL_VERSION,
|
|
@@ -36,6 +38,42 @@ export function protocolInfo({ packageVersion = null } = {}) {
|
|
|
36
38
|
writesSchemaVersions: schemaVersions,
|
|
37
39
|
compatibility: SCHEMA_COMPATIBILITY_POLICY,
|
|
38
40
|
features: {
|
|
41
|
+
semanticDecisionPlane: {
|
|
42
|
+
version: 1,
|
|
43
|
+
supported: true,
|
|
44
|
+
requiredForNewSemanticDecisions: true,
|
|
45
|
+
engine: DECISION_ENGINE_ID,
|
|
46
|
+
model: PINNED_JEV_MODEL,
|
|
47
|
+
policyVersion: DECISION_DEFAULT_POLICY.policyVersion,
|
|
48
|
+
authority: "SEMANTIC_DECISION",
|
|
49
|
+
evidenceAuthority: "NONE",
|
|
50
|
+
lifecycleAuthority: false,
|
|
51
|
+
completionAuthority: false,
|
|
52
|
+
ownershipAuthority: false,
|
|
53
|
+
installationAuthority: false,
|
|
54
|
+
commands: ["decision-status", "decision-show", "context-plan", "model-route", "semantic-plan", "test-inventory", "test-utility", "test-prune-plan", "test-prune-probe"],
|
|
55
|
+
resource: "task/decisions",
|
|
56
|
+
contextPlanResource: "task/context-plan",
|
|
57
|
+
modelRouteResource: "task/model-route",
|
|
58
|
+
testUtilityResource: "task/test-utility",
|
|
59
|
+
testPruneResource: "task/test-utility",
|
|
60
|
+
offlineInspection: true,
|
|
61
|
+
completePerformsLiveRequest: false,
|
|
62
|
+
modelRouting: {
|
|
63
|
+
version: 1,
|
|
64
|
+
tiers: ["NONE", "FAST", "STANDARD", "PRIMARY"],
|
|
65
|
+
deterministicFloor: true,
|
|
66
|
+
advisoryEscalationOnly: true,
|
|
67
|
+
vendorSelection: false,
|
|
68
|
+
},
|
|
69
|
+
semanticPlanning: {
|
|
70
|
+
version: 1,
|
|
71
|
+
questionSets: ["intake-v1", "contract-v1", "route-v1", "context-v1", "model-route-v1", "failure-v1", "diagnosis-v1", "review-v1", "task-overlap-v1", "test-utility-v1", "test-prune-v1"],
|
|
72
|
+
authority: "SEMANTIC_DECISION",
|
|
73
|
+
evidenceAuthority: "NONE",
|
|
74
|
+
lifecycleAuthority: false,
|
|
75
|
+
},
|
|
76
|
+
},
|
|
39
77
|
taskClaimRecovery: {
|
|
40
78
|
version: 1,
|
|
41
79
|
durableRecoveryState: true,
|
|
@@ -153,6 +191,18 @@ export function protocolInfo({ packageVersion = null } = {}) {
|
|
|
153
191
|
commands: ["next", "task-show"],
|
|
154
192
|
preservesDefaultOutput: true,
|
|
155
193
|
},
|
|
194
|
+
auditUx: {
|
|
195
|
+
version: 1,
|
|
196
|
+
supported: true,
|
|
197
|
+
readOnly: true,
|
|
198
|
+
resource: "task/audit-view",
|
|
199
|
+
timeline: true,
|
|
200
|
+
lifecycleAuthority: false,
|
|
201
|
+
evidenceAuthority: false,
|
|
202
|
+
completionAuthority: false,
|
|
203
|
+
mutationAuthority: false,
|
|
204
|
+
externalExecution: false,
|
|
205
|
+
},
|
|
156
206
|
usageTelemetry: {
|
|
157
207
|
version: 1,
|
|
158
208
|
supported: true,
|
|
@@ -205,6 +255,21 @@ export function protocolInfo({ packageVersion = null } = {}) {
|
|
|
205
255
|
evidenceAuthority: false,
|
|
206
256
|
executable: false,
|
|
207
257
|
},
|
|
258
|
+
providerExtensions: {
|
|
259
|
+
version: 1,
|
|
260
|
+
supported: true,
|
|
261
|
+
providerNeutral: true,
|
|
262
|
+
maturity: "experimental",
|
|
263
|
+
publicRegistryApi: false,
|
|
264
|
+
packageSubpathExported: false,
|
|
265
|
+
autoInstall: false,
|
|
266
|
+
lifecycleAuthority: false,
|
|
267
|
+
completionAuthority: false,
|
|
268
|
+
evidenceAuthority: false,
|
|
269
|
+
providerKinds: [...PROVIDER_KINDS],
|
|
270
|
+
resultBoundary: "STRICT_JSON_SNAPSHOT",
|
|
271
|
+
cancellation: "COOPERATIVE_ABORT_SIGNAL",
|
|
272
|
+
},
|
|
208
273
|
responsibilityConstraints: {
|
|
209
274
|
version: 1,
|
|
210
275
|
supported: true,
|
package/src/core/protocol.js
CHANGED
|
@@ -30,6 +30,9 @@ export const FAILURE_CODES = Object.freeze([
|
|
|
30
30
|
"E_GATE_REQUIRED",
|
|
31
31
|
"E_GATE_UNVERIFIED",
|
|
32
32
|
"E_GATE_STALE",
|
|
33
|
+
"E_GATE_NOT_REQUIRED",
|
|
34
|
+
"E_GATE_INVALID",
|
|
35
|
+
"E_PHASE_FREEZE",
|
|
33
36
|
"E_PHASE_TRANSITION_INVALID",
|
|
34
37
|
"E_PHASE_PREREQUISITE_MISSING",
|
|
35
38
|
"E_PHASE_CHRONOLOGY_INVALID",
|
|
@@ -113,6 +116,23 @@ export const FAILURE_CODES = Object.freeze([
|
|
|
113
116
|
"E_INTERVENTION_HYPOTHESIS_MISSING",
|
|
114
117
|
"E_INTERVENTION_REFERENCE_INVALID",
|
|
115
118
|
"E_STRATEGY_OSCILLATION",
|
|
119
|
+
"E_DECISION_ENGINE_UNAVAILABLE",
|
|
120
|
+
"E_DECISION_ENGINE_AUTH_REQUIRED",
|
|
121
|
+
"E_DECISION_ENGINE_AUTH_INVALID",
|
|
122
|
+
"E_DECISION_MODEL_UNSUPPORTED",
|
|
123
|
+
"E_DECISION_REQUEST_INVALID",
|
|
124
|
+
"E_DECISION_RESULT_INVALID",
|
|
125
|
+
"E_DECISION_TIMEOUT",
|
|
126
|
+
"E_DECISION_RATE_LIMITED",
|
|
127
|
+
"E_DECISION_STATE_UNSAFE",
|
|
128
|
+
"E_DECISION_STATE_LIMIT",
|
|
129
|
+
"E_DECISION_LOW_CONFIDENCE",
|
|
130
|
+
"E_DECISION_STALE",
|
|
131
|
+
"E_DECISION_POLICY_INVALID",
|
|
132
|
+
"E_DECISION_CACHE_INVALID",
|
|
133
|
+
"E_DECISION_QUESTION_SET_UNKNOWN",
|
|
134
|
+
"E_DECISION_QUESTION_SET_STALE",
|
|
135
|
+
"E_DECISION_REQUIRED",
|
|
116
136
|
"E_DECISION_CRITERION_INVALID",
|
|
117
137
|
"E_DECISION_NOT_UNRESOLVED",
|
|
118
138
|
]);
|
|
@@ -1,18 +1,17 @@
|
|
|
1
1
|
import { readContract } from "./contract.js";
|
|
2
2
|
import { canonicalFingerprint, readJsonArtifact, writeJsonArtifact } from "./artifacts.js";
|
|
3
|
-
import { appendProtocolEvent, validateEventLedger } from "./events.js";
|
|
3
|
+
import { appendProtocolEvent, validateCompletionRecoveryAuthorization, validateEventLedger } from "./events.js";
|
|
4
4
|
import { authorizeCompletionRecoveryOrRebind } from "./completion-recovery-rebind.js";
|
|
5
5
|
import { runCommandExecution } from "./execution.js";
|
|
6
6
|
import { createReceipt } from "./receipt.js";
|
|
7
7
|
import { currentRepositoryFingerprint } from "./repository.js";
|
|
8
8
|
import { taskArtifactPath } from "./task-paths.js";
|
|
9
|
+
import { resolveTaskClaimState } from "./task-claim-state.js";
|
|
9
10
|
import { classifyLoadedWorkState, readWorkState, mutateWorkState } from "./work-state.js";
|
|
10
11
|
import { classifyRequirement } from "./evidence-readiness.js";
|
|
11
12
|
|
|
12
13
|
export const RECONCILE_EVENT = "CHECKPOINT_RECONCILED";
|
|
13
14
|
|
|
14
|
-
const RECONCILABLE_DRIFT = new Set(["REPOSITORY_CHANGED"]);
|
|
15
|
-
|
|
16
15
|
const RECONCILABLE_PHASES = new Set(["EXECUTING", "VERIFYING", "REVIEWING"]);
|
|
17
16
|
|
|
18
17
|
function reconcileError(code, message, artifacts = []) {
|
|
@@ -22,14 +21,126 @@ function reconcileError(code, message, artifacts = []) {
|
|
|
22
21
|
return error;
|
|
23
22
|
}
|
|
24
23
|
|
|
24
|
+
function assertRepositoryOnlyFreshness(freshness, stateRel, contractRel) {
|
|
25
|
+
if (freshness.status !== "REVALIDATION_REQUIRED") {
|
|
26
|
+
throw reconcileError(
|
|
27
|
+
"E_RECONCILE_NOT_STALE",
|
|
28
|
+
`work-state checkpoint is ${freshness.status === "FRESH" ? "fresh" : "not revalidation-required"}; no reconciliation required`,
|
|
29
|
+
[stateRel, contractRel],
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
if (freshness.reasons.length === 1 && freshness.reasons[0] === "REPOSITORY_CHANGED") return;
|
|
33
|
+
throw reconcileError(
|
|
34
|
+
"E_RECONCILE_UNSUPPORTED_DRIFT",
|
|
35
|
+
`reconcile-closure only reconciles repository fingerprint drift; unresolved drift: ${freshness.reasons.join(", ")}`,
|
|
36
|
+
[stateRel, contractRel],
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function assertFreshReviewingRecoveryIsAuthorized({
|
|
41
|
+
target,
|
|
42
|
+
packageRoot,
|
|
43
|
+
taskId,
|
|
44
|
+
state,
|
|
45
|
+
eventsRel,
|
|
46
|
+
receiptRel,
|
|
47
|
+
}) {
|
|
48
|
+
if (state.phase !== "REVIEWING" || !Object.prototype.hasOwnProperty.call(state, "lastCompletionAttempt")) return;
|
|
49
|
+
const ledger = await validateEventLedger(target, packageRoot, { taskId });
|
|
50
|
+
if (!ledger.valid) return;
|
|
51
|
+
let receipt = null;
|
|
52
|
+
try {
|
|
53
|
+
receipt = (await readJsonArtifact(target, receiptRel, "execution-receipt", packageRoot)).value;
|
|
54
|
+
} catch (error) {
|
|
55
|
+
if (error.code !== "ARTIFACT_MISSING") throw error;
|
|
56
|
+
}
|
|
57
|
+
const recoveryAuth = validateCompletionRecoveryAuthorization({ state, receipt, events: ledger.events });
|
|
58
|
+
if (!recoveryAuth.authorized) {
|
|
59
|
+
const first = recoveryAuth.errors?.[0] ?? {};
|
|
60
|
+
throw reconcileError(
|
|
61
|
+
first.code ?? "E_COMPLETION_RECOVERY_UNAUTHORIZED",
|
|
62
|
+
`REVIEWING reconciliation requires authorized completion recovery: ${first.message ?? "unauthorized"}`,
|
|
63
|
+
[eventsRel, receiptRel],
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function validateReconciliationCheckpoint({
|
|
69
|
+
target,
|
|
70
|
+
packageRoot,
|
|
71
|
+
taskId,
|
|
72
|
+
state,
|
|
73
|
+
stateRel,
|
|
74
|
+
contractRel,
|
|
75
|
+
eventsRel,
|
|
76
|
+
receiptRel,
|
|
77
|
+
authorityContext,
|
|
78
|
+
runtimeContext,
|
|
79
|
+
}) {
|
|
80
|
+
const freshness = await classifyLoadedWorkState({ target, state, contractFile: contractRel });
|
|
81
|
+
if (freshness.status !== "REVALIDATION_REQUIRED") {
|
|
82
|
+
await assertFreshReviewingRecoveryIsAuthorized({
|
|
83
|
+
target,
|
|
84
|
+
packageRoot,
|
|
85
|
+
taskId,
|
|
86
|
+
state,
|
|
87
|
+
eventsRel,
|
|
88
|
+
receiptRel,
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
assertRepositoryOnlyFreshness(freshness, stateRel, contractRel);
|
|
92
|
+
|
|
93
|
+
const ledger = await validateEventLedger(target, packageRoot, { taskId });
|
|
94
|
+
if (!ledger.valid) {
|
|
95
|
+
const first = ledger.errors[0];
|
|
96
|
+
throw reconcileError(
|
|
97
|
+
"E_RECONCILE_LEDGER_INVALID",
|
|
98
|
+
`append-only event ledger must be valid before reconciliation: ${first?.message ?? "invalid ledger"}`,
|
|
99
|
+
[eventsRel],
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const ownership = await resolveTaskClaimState(target, { taskId, packageRoot });
|
|
104
|
+
if (!ownership.ownershipValid || !ownership.mutationAllowed || ownership.claimState !== "ACTIVE") {
|
|
105
|
+
const first = ownership.ownershipErrors?.[0] ?? ownership.errors?.[0] ?? {};
|
|
106
|
+
throw reconcileError(
|
|
107
|
+
first.code ?? "E_TASK_CLAIM_OWNERSHIP_INCONSISTENT",
|
|
108
|
+
`reconcile-closure requires active, valid task claim ownership: ${first.message ?? ownership.claimState}`,
|
|
109
|
+
[stateRel, eventsRel],
|
|
110
|
+
);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (state.phase !== "REVIEWING" || !Object.prototype.hasOwnProperty.call(state, "lastCompletionAttempt")) {
|
|
114
|
+
return state;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const recovery = await authorizeCompletionRecoveryOrRebind({
|
|
118
|
+
target,
|
|
119
|
+
packageRoot,
|
|
120
|
+
taskId,
|
|
121
|
+
authorityContext,
|
|
122
|
+
runtimeContext,
|
|
123
|
+
});
|
|
124
|
+
if (!recovery.recoveryAuth.authorized) {
|
|
125
|
+
const first = recovery.recoveryAuth.errors?.[0] ?? {};
|
|
126
|
+
throw reconcileError(
|
|
127
|
+
first.code ?? "E_COMPLETION_RECOVERY_UNAUTHORIZED",
|
|
128
|
+
`REVIEWING reconciliation requires authorized completion recovery: ${first.message ?? "unauthorized"}`,
|
|
129
|
+
[stateRel, receiptRel],
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
return recovery.rebound ? recovery.state : state;
|
|
133
|
+
}
|
|
134
|
+
|
|
25
135
|
/**
|
|
26
136
|
* Canonical recovery for an EXECUTING, VERIFYING, or REVIEWING task whose
|
|
27
137
|
* objective is already satisfied in the current repository but whose
|
|
28
138
|
* work-state checkpoint is stale because the repository fingerprint moved.
|
|
29
139
|
*
|
|
30
140
|
* The command refreshes the checkpoint repository fingerprint only after:
|
|
31
|
-
* - the task is EXECUTING, VERIFYING, or (with
|
|
32
|
-
*
|
|
141
|
+
* - the task is EXECUTING, VERIFYING, or REVIEWING (with either a
|
|
142
|
+
* persisted completion rejection or the narrow repository-only bootstrap
|
|
143
|
+
* path),
|
|
33
144
|
* - classification requires revalidation and the only drift is
|
|
34
145
|
* REPOSITORY_CHANGED,
|
|
35
146
|
* - the append-only event ledger is valid,
|
|
@@ -81,53 +192,18 @@ export async function runReconcileClosure({
|
|
|
81
192
|
[stateRel],
|
|
82
193
|
);
|
|
83
194
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
`REVIEWING reconciliation requires authorized completion recovery: ${first.message ?? "unauthorized"}`,
|
|
97
|
-
[stateRel, receiptRel],
|
|
98
|
-
);
|
|
99
|
-
}
|
|
100
|
-
if (recovery.rebound) {
|
|
101
|
-
state = recovery.state;
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
const freshness = await classifyLoadedWorkState({ target, state, contractFile: contractRel });
|
|
106
|
-
if (freshness.status !== "REVALIDATION_REQUIRED" || !freshness.reasons.includes("REPOSITORY_CHANGED")) {
|
|
107
|
-
throw reconcileError(
|
|
108
|
-
"E_RECONCILE_NOT_STALE",
|
|
109
|
-
`work-state checkpoint is ${freshness.status === "FRESH" ? "fresh" : "not revalidation-required"}; no reconciliation required`,
|
|
110
|
-
[stateRel, contractRel],
|
|
111
|
-
);
|
|
112
|
-
}
|
|
113
|
-
const unsupported = freshness.reasons.filter((reason) => !RECONCILABLE_DRIFT.has(reason));
|
|
114
|
-
if (unsupported.length > 0) {
|
|
115
|
-
throw reconcileError(
|
|
116
|
-
"E_RECONCILE_UNSUPPORTED_DRIFT",
|
|
117
|
-
`reconcile-closure only reconciles repository fingerprint drift; unresolved drift: ${unsupported.join(", ")}`,
|
|
118
|
-
[stateRel, contractRel],
|
|
119
|
-
);
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const ledger = await validateEventLedger(target, packageRoot, { taskId });
|
|
123
|
-
if (!ledger.valid) {
|
|
124
|
-
const first = ledger.errors[0];
|
|
125
|
-
throw reconcileError(
|
|
126
|
-
"E_RECONCILE_LEDGER_INVALID",
|
|
127
|
-
`append-only event ledger must be valid before reconciliation: ${first?.message ?? "invalid ledger"}`,
|
|
128
|
-
[eventsRel],
|
|
129
|
-
);
|
|
130
|
-
}
|
|
195
|
+
state = await validateReconciliationCheckpoint({
|
|
196
|
+
target,
|
|
197
|
+
packageRoot,
|
|
198
|
+
taskId,
|
|
199
|
+
state,
|
|
200
|
+
stateRel,
|
|
201
|
+
contractRel,
|
|
202
|
+
eventsRel,
|
|
203
|
+
receiptRel,
|
|
204
|
+
authorityContext,
|
|
205
|
+
runtimeContext,
|
|
206
|
+
});
|
|
131
207
|
|
|
132
208
|
const contract = await readContract(target, packageRoot, { taskId });
|
|
133
209
|
const verificationItem = (contract.value.verification ?? []).find((item) => {
|