@cassiomc1/forgeloop 1.10.0 → 1.10.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/DOCS_INDEX.md +12 -1
- package/LOOP_ENGINEERING.md +4 -4
- package/LOOP_SYSTEM_DESIGN.md +23 -0
- package/QUALITY_SCORECARD.md +2 -2
- package/README.md +22 -8
- package/docs/ADVISORY_CONTEXT.md +24 -0
- package/docs/AGENT_PROTOCOL_SUMMARY.md +1 -1
- package/docs/ARTIFACT_REFERENCE.md +15 -0
- package/docs/CODE_ATTESTATION.md +9 -0
- package/docs/DOCUMENTATION_GUIDE.md +4 -4
- package/docs/EXECUTION_PROFILE_BENCHMARKS.md +10 -0
- package/docs/MCP.md +13 -1
- package/docs/PACKAGE_CONTENTS.md +88 -0
- package/docs/RELEASE_CHECKLIST.md +10 -2
- package/docs/REVISION_PROVIDERS.md +9 -0
- package/docs/RIPWIRE_ADAPTER.md +189 -0
- package/docs/TROUBLESHOOTING.md +12 -0
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.html +13 -2
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.receipt.json +6 -6
- package/docs/assets/diagrams/forgeloop-code-attestation-flow.svg +10 -1
- package/docs/assets/diagrams/forgeloop-engineering-flow.html +22 -11
- package/docs/assets/diagrams/forgeloop-engineering-flow.receipt.json +6 -6
- package/docs/assets/diagrams/forgeloop-engineering-flow.svg +17 -8
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.html +14 -3
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.receipt.json +6 -6
- package/docs/assets/diagrams/forgeloop-verification-trust-flow.svg +10 -1
- package/docs/diagrams/README.md +26 -0
- package/docs/diagrams/forgeloop-code-attestation-flow.workflow.json +383 -57
- package/docs/diagrams/forgeloop-engineering-flow.workflow.json +374 -55
- package/docs/diagrams/forgeloop-verification-trust-flow.workflow.json +328 -47
- 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/package.json +14 -4
- package/scripts/CI_VALIDATORS.md +24 -0
- package/scripts/check-critical-coverage.mjs +9 -0
- package/src/adapters/ripwire/normalize.js +352 -0
- package/src/adapters/ripwire/process.js +248 -0
- package/src/adapters/ripwire/provider.js +245 -0
- package/src/commands/doctor.js +11 -10
- package/src/core/actions.js +2 -2
- package/src/core/advisory-context/service.js +36 -14
- package/src/core/approvals.js +2 -2
- package/src/core/artifacts.js +3 -3
- package/src/core/checks.js +0 -33
- package/src/core/completion.js +2 -2
- package/src/core/events.js +5 -5
- package/src/core/execution-profile.js +18 -5
- package/src/core/handoff-acceptance.js +10 -1
- package/src/core/next-action-pending-actions.js +255 -0
- package/src/core/next-action-phases.js +26 -764
- package/src/core/next-action-planned-phase.js +51 -0
- package/src/core/next-action-quality-guidance.js +19 -0
- package/src/core/next-action-recovery-phases.js +97 -0
- package/src/core/next-action-refresh.js +20 -0
- package/src/core/next-action-review-phase.js +189 -0
- package/src/core/next-action-verification-phase.js +192 -0
- package/src/core/task-recovery.js +2 -2
- package/src/core/transaction-maintenance.js +70 -0
- package/src/core/transaction.js +31 -10
- package/src/core/work-state.js +20 -11
- package/src/integration.d.ts +30 -3
- package/src/integration.js +2 -0
- package/src/core/cli-metadata.js +0 -23
- package/src/core/decision-classification.js +0 -55
- package/src/core/gates.js +0 -57
- package/src/core/workflow-compatibility.js +0 -151
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { taskArtifactPath } from "./task-paths.js";
|
|
2
|
+
import { NEXT_ACTIONS, result } from "./next-action-model.js";
|
|
3
|
+
import { artifactError } from "./next-action-artifacts.js";
|
|
4
|
+
import { listActions } from "./actions.js";
|
|
5
|
+
import { listApprovals } from "./approvals.js";
|
|
6
|
+
import { loadPolicyIdentity } from "./policy-engine.js";
|
|
7
|
+
|
|
8
|
+
export async function resolvePendingActionGuidance({ target, packageRoot, state, context, eventsRel, authorityContext, runtimeContext, helpers }) {
|
|
9
|
+
const {
|
|
10
|
+
capabilityDecisionMetadata,
|
|
11
|
+
actionApprovalGuidance,
|
|
12
|
+
actionAuthorityGuidance,
|
|
13
|
+
actionApprovalResolutionGuidance,
|
|
14
|
+
actionDeniedGuidance,
|
|
15
|
+
actionPolicyEpochGuidance,
|
|
16
|
+
authorizeActionGuidance,
|
|
17
|
+
evaluateProposedActionCapability,
|
|
18
|
+
findApplicablePendingApproval,
|
|
19
|
+
} = helpers;
|
|
20
|
+
let actionsForApproval;
|
|
21
|
+
try {
|
|
22
|
+
actionsForApproval = await listActions(target, { packageRoot, taskId: state.taskId });
|
|
23
|
+
} catch (error) {
|
|
24
|
+
if (error.code === "E_ACTION_INVALID") {
|
|
25
|
+
return result({
|
|
26
|
+
...context,
|
|
27
|
+
nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER,
|
|
28
|
+
commands: [],
|
|
29
|
+
reasons: [artifactError(
|
|
30
|
+
error.code,
|
|
31
|
+
`A durable action artifact is invalid and cannot be offered for authorization: ${error.message}`,
|
|
32
|
+
[taskArtifactPath(state.taskId, "actions"), eventsRel],
|
|
33
|
+
)],
|
|
34
|
+
requiredArtifacts: [taskArtifactPath(state.taskId, "actions"), eventsRel],
|
|
35
|
+
});
|
|
36
|
+
}
|
|
37
|
+
throw error;
|
|
38
|
+
}
|
|
39
|
+
const ambiguousAction = actionsForApproval.find((action) => action.state === "COMMIT_UNKNOWN");
|
|
40
|
+
if (ambiguousAction) {
|
|
41
|
+
return result({ ...context, nextAction: NEXT_ACTIONS.RECONCILE_ACTION,
|
|
42
|
+
commands: [`forgeloop action-reconcile --task ${state.taskId} --action ${ambiguousAction.actionId} --outcome UNKNOWN`],
|
|
43
|
+
reasons: [artifactError("E_ACTION_RECONCILIATION_REQUIRED",
|
|
44
|
+
`Action ${ambiguousAction.actionId} has an unknown external commit outcome; retry is forbidden until reconciliation.`)],
|
|
45
|
+
reconciliationAuthorityRequired: {
|
|
46
|
+
outcomesRequiringTrust: ["COMMITTED", "NOT_COMMITTED"],
|
|
47
|
+
reason: "Recording an UNKNOWN observation is safe from any surface; settling COMMITTED or NOT_COMMITTED requires a trusted host boundary and evidence.",
|
|
48
|
+
},
|
|
49
|
+
requiredArtifacts: [taskArtifactPath(state.taskId, "actions"), eventsRel] });
|
|
50
|
+
}
|
|
51
|
+
// A PROPOSED required action ready for authorization should use the
|
|
52
|
+
// canonical authorization surface; host/approval blockers remain structured.
|
|
53
|
+
const authorizableAction = actionsForApproval.find((action) => action.requiredForCompletion
|
|
54
|
+
&& action.state === "PROPOSED");
|
|
55
|
+
if (authorizableAction) {
|
|
56
|
+
let policyIdentity;
|
|
57
|
+
try {
|
|
58
|
+
policyIdentity = await loadPolicyIdentity(target, packageRoot, state.taskId);
|
|
59
|
+
} catch (error) {
|
|
60
|
+
return actionPolicyEpochGuidance({
|
|
61
|
+
context,
|
|
62
|
+
state,
|
|
63
|
+
action: authorizableAction,
|
|
64
|
+
policyIdentity: {
|
|
65
|
+
code: error.code ?? "E_POLICY_INVALID",
|
|
66
|
+
message: `Capability policy epoch validation failed for required action ${authorizableAction.actionId}: ${error.message}`,
|
|
67
|
+
},
|
|
68
|
+
eventsRel,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
if (policyIdentity.status !== "VALID") {
|
|
72
|
+
return actionPolicyEpochGuidance({
|
|
73
|
+
context,
|
|
74
|
+
state,
|
|
75
|
+
action: authorizableAction,
|
|
76
|
+
policyIdentity,
|
|
77
|
+
eventsRel,
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
let capability;
|
|
82
|
+
try {
|
|
83
|
+
capability = await evaluateProposedActionCapability({
|
|
84
|
+
target,
|
|
85
|
+
packageRoot,
|
|
86
|
+
action: authorizableAction,
|
|
87
|
+
authorityContext,
|
|
88
|
+
});
|
|
89
|
+
} catch (error) {
|
|
90
|
+
return result({
|
|
91
|
+
...context,
|
|
92
|
+
nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER,
|
|
93
|
+
commands: [],
|
|
94
|
+
reasons: [artifactError(
|
|
95
|
+
error.code ?? "E_ACTION_CAPABILITY_INVALID",
|
|
96
|
+
`Capability policy evaluation failed for required action ${authorizableAction.actionId}: ${error.message}`,
|
|
97
|
+
[taskArtifactPath(state.taskId, "actions"), eventsRel],
|
|
98
|
+
)],
|
|
99
|
+
capabilityDecision: {
|
|
100
|
+
capability: authorizableAction.capability,
|
|
101
|
+
decision: "DENY",
|
|
102
|
+
reasonCode: error.code ?? "E_ACTION_CAPABILITY_INVALID",
|
|
103
|
+
policyFingerprint: null,
|
|
104
|
+
},
|
|
105
|
+
requiredArtifacts: [taskArtifactPath(state.taskId, "actions"), eventsRel],
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
if (capability.decision === "REQUIRE_AUTHORITY") {
|
|
109
|
+
return actionAuthorityGuidance({
|
|
110
|
+
context,
|
|
111
|
+
state,
|
|
112
|
+
action: authorizableAction,
|
|
113
|
+
capability,
|
|
114
|
+
eventsRel,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
if (capability.allowed) {
|
|
118
|
+
return authorizeActionGuidance({
|
|
119
|
+
context,
|
|
120
|
+
state,
|
|
121
|
+
action: authorizableAction,
|
|
122
|
+
capability,
|
|
123
|
+
eventsRel,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
if (capability.decision === "REQUIRE_APPROVAL") {
|
|
127
|
+
let approvals;
|
|
128
|
+
try {
|
|
129
|
+
approvals = await listApprovals(target, { packageRoot, taskId: state.taskId });
|
|
130
|
+
} catch (error) {
|
|
131
|
+
return result({
|
|
132
|
+
...context,
|
|
133
|
+
nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER,
|
|
134
|
+
commands: [],
|
|
135
|
+
reasons: [artifactError(
|
|
136
|
+
error.code ?? "E_APPROVAL_INVALID",
|
|
137
|
+
`Approval artifact inspection failed for required action ${authorizableAction.actionId}: ${error.message}`,
|
|
138
|
+
[taskArtifactPath(state.taskId, "approvals"), eventsRel],
|
|
139
|
+
)],
|
|
140
|
+
capabilityDecision: capabilityDecisionMetadata(authorizableAction, capability),
|
|
141
|
+
requiredArtifacts: [taskArtifactPath(state.taskId, "approvals"), eventsRel],
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
try {
|
|
145
|
+
capability = await evaluateProposedActionCapability({
|
|
146
|
+
target,
|
|
147
|
+
packageRoot,
|
|
148
|
+
action: authorizableAction,
|
|
149
|
+
approvals,
|
|
150
|
+
authorityContext,
|
|
151
|
+
});
|
|
152
|
+
} catch (error) {
|
|
153
|
+
return result({
|
|
154
|
+
...context,
|
|
155
|
+
nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER,
|
|
156
|
+
commands: [],
|
|
157
|
+
reasons: [artifactError(
|
|
158
|
+
error.code ?? "E_ACTION_CAPABILITY_INVALID",
|
|
159
|
+
`Capability policy evaluation failed for required action ${authorizableAction.actionId}: ${error.message}`,
|
|
160
|
+
[taskArtifactPath(state.taskId, "actions"), eventsRel],
|
|
161
|
+
)],
|
|
162
|
+
capabilityDecision: {
|
|
163
|
+
capability: authorizableAction.capability,
|
|
164
|
+
decision: "DENY",
|
|
165
|
+
reasonCode: error.code ?? "E_ACTION_CAPABILITY_INVALID",
|
|
166
|
+
policyFingerprint: null,
|
|
167
|
+
},
|
|
168
|
+
requiredArtifacts: [taskArtifactPath(state.taskId, "actions"), eventsRel],
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
if (capability.allowed) {
|
|
172
|
+
return authorizeActionGuidance({
|
|
173
|
+
context,
|
|
174
|
+
state,
|
|
175
|
+
action: authorizableAction,
|
|
176
|
+
capability,
|
|
177
|
+
eventsRel,
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
if (capability.decision !== "REQUIRE_APPROVAL") {
|
|
181
|
+
if (capability.decision === "REQUIRE_AUTHORITY") {
|
|
182
|
+
return actionAuthorityGuidance({
|
|
183
|
+
context,
|
|
184
|
+
state,
|
|
185
|
+
action: authorizableAction,
|
|
186
|
+
capability,
|
|
187
|
+
eventsRel,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
if (capability.allowed) {
|
|
191
|
+
return authorizeActionGuidance({
|
|
192
|
+
context,
|
|
193
|
+
state,
|
|
194
|
+
action: authorizableAction,
|
|
195
|
+
capability,
|
|
196
|
+
eventsRel,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
return actionDeniedGuidance({
|
|
200
|
+
context,
|
|
201
|
+
state,
|
|
202
|
+
action: authorizableAction,
|
|
203
|
+
capability,
|
|
204
|
+
eventsRel,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
const pendingApproval = await findApplicablePendingApproval({
|
|
208
|
+
target,
|
|
209
|
+
packageRoot,
|
|
210
|
+
taskId: state.taskId,
|
|
211
|
+
action: authorizableAction,
|
|
212
|
+
approvals,
|
|
213
|
+
});
|
|
214
|
+
if (pendingApproval) {
|
|
215
|
+
return actionApprovalResolutionGuidance({
|
|
216
|
+
context,
|
|
217
|
+
state,
|
|
218
|
+
action: authorizableAction,
|
|
219
|
+
approval: pendingApproval,
|
|
220
|
+
eventsRel,
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
return actionApprovalGuidance({
|
|
224
|
+
context,
|
|
225
|
+
state,
|
|
226
|
+
action: authorizableAction,
|
|
227
|
+
capability,
|
|
228
|
+
eventsRel,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
return actionDeniedGuidance({
|
|
232
|
+
context,
|
|
233
|
+
state,
|
|
234
|
+
action: authorizableAction,
|
|
235
|
+
capability,
|
|
236
|
+
eventsRel,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
// Committed-but-unverified required action needs canonical postcondition
|
|
240
|
+
// evidence before it can satisfy completion.
|
|
241
|
+
const committedActions = actionsForApproval;
|
|
242
|
+
const unverifiedRequired = committedActions.find((action) => action.requiredForCompletion
|
|
243
|
+
&& action.state === "COMMITTED");
|
|
244
|
+
if (unverifiedRequired) {
|
|
245
|
+
return result({ ...context, nextAction: NEXT_ACTIONS.VERIFY_EXTERNAL_ACTION,
|
|
246
|
+
commands: [
|
|
247
|
+
`forgeloop run-check --task ${state.taskId} --id check-postcondition --requirement <requirement> -- <independent verification argv>`,
|
|
248
|
+
`forgeloop action-verify --task ${state.taskId} --action ${unverifiedRequired.actionId} --evidence <execution-ref>`,
|
|
249
|
+
],
|
|
250
|
+
reasons: [artifactError("E_ACTION_VERIFICATION_REQUIRED",
|
|
251
|
+
`Committed action ${unverifiedRequired.actionId} requires independent canonical postcondition evidence; exit code 0 alone is not verification.`)],
|
|
252
|
+
requiredArtifacts: [taskArtifactPath(state.taskId, "actions"), eventsRel] });
|
|
253
|
+
}
|
|
254
|
+
return null;
|
|
255
|
+
}
|