@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.
Files changed (248) hide show
  1. package/CONTRIBUTING.md +11 -7
  2. package/DOCS_INDEX.md +2 -1
  3. package/ENG/c-development-eng.md +2 -2
  4. package/ENG/cpp-development-eng.md +2 -2
  5. package/ENG/dotnet-aspnetcore-development-eng.md +7 -1
  6. package/ENG/flutter-development-eng.md +4 -2
  7. package/ENG/go-development-eng.md +2 -2
  8. package/ENG/java-development-eng.md +3 -3
  9. package/ENG/nodejs-backend-development-eng.md +8 -4
  10. package/ENG/php-development-eng.md +3 -3
  11. package/ENG/python-development-eng.md +205 -0
  12. package/ENG/rust-development-eng.md +42 -1
  13. package/ENG/sql-development-eng.md +4 -4
  14. package/ENG/swift-development-eng.md +5 -4
  15. package/ENG/test-code-eng.md +1 -1
  16. package/ENG/typescript-development-eng.md +4 -4
  17. package/GUIDE_ROUTER.md +6 -0
  18. package/LICENSE +133 -21
  19. package/LOOP_SYSTEM_DESIGN.md +10 -10
  20. package/README.md +28 -39
  21. package/THIRD_PARTY_NOTICES.md +3 -4
  22. package/completions/_forgeloop +11 -3
  23. package/completions/forgeloop.bash +19 -3
  24. package/completions/forgeloop.fish +61 -1
  25. package/docs/AGENT_PROTOCOL_SUMMARY.md +50 -40
  26. package/docs/ARTIFACT_REFERENCE.md +50 -37
  27. package/docs/CLI_REFERENCE.md +147 -3
  28. package/docs/DOCUMENTATION_GUIDE.md +6 -4
  29. package/docs/GETTING_STARTED.md +3 -3
  30. package/docs/PACKAGE_CONTENTS.md +14 -13
  31. package/docs/RELEASE_CHECKLIST.md +81 -10
  32. package/docs/SELF_HOSTED_TESTS.md +121 -0
  33. package/docs/documentation-manifest.json +25 -12
  34. package/package.json +15 -10
  35. package/schemas/task-bundle.schema.json +7 -2
  36. package/scripts/generate-agent-protocol-summary.mjs +2 -2
  37. package/scripts/run-mcp-clean-tests.mjs +30 -0
  38. package/src/adapters/agent-browser/provider.js +14 -11
  39. package/src/adapters/emulated-services/provider.js +11 -4
  40. package/src/adapters/temporary-paths.js +18 -0
  41. package/src/cli.js +146 -210
  42. package/src/commands/action-show.js +5 -2
  43. package/src/commands/advance.js +5 -1
  44. package/src/commands/baseline.js +13 -11
  45. package/src/commands/checkpoint-revalidate.js +24 -8
  46. package/src/commands/complete.js +19 -3
  47. package/src/commands/contract-create.js +3 -1
  48. package/src/commands/doctor.js +36 -13
  49. package/src/commands/gate-record.js +20 -31
  50. package/src/commands/gate-revalidate.js +11 -13
  51. package/src/commands/handoff-list.js +6 -1
  52. package/src/commands/handoff-show.js +6 -1
  53. package/src/commands/migrate-protocol.js +2 -2
  54. package/src/commands/model-route.js +9 -1
  55. package/src/commands/next.js +7 -1
  56. package/src/commands/policy-diff.js +25 -4
  57. package/src/commands/progress.js +17 -11
  58. package/src/commands/record-diagnosis.js +3 -0
  59. package/src/commands/semantic-plan.js +26 -2
  60. package/src/commands/status.js +6 -1
  61. package/src/commands/storage-backup.js +15 -0
  62. package/src/commands/storage-migrate.js +5 -0
  63. package/src/commands/storage-migration-resume.js +5 -0
  64. package/src/commands/storage-migration-status.js +44 -0
  65. package/src/commands/storage-restore-resume.js +7 -0
  66. package/src/commands/storage-restore.js +8 -0
  67. package/src/commands/task-abandon.js +1 -1
  68. package/src/commands/task-list.js +8 -43
  69. package/src/commands/task-migrate.js +3 -3
  70. package/src/commands/task-recover.js +10 -2
  71. package/src/commands/task-scope.js +18 -2
  72. package/src/commands/task-show.js +16 -2
  73. package/src/commands/validate-protocol.js +29 -14
  74. package/src/commands/validate-receipt.js +8 -1
  75. package/src/config/guides.json +4 -0
  76. package/src/core/action-ledger-projection.js +10 -3
  77. package/src/core/actions.js +38 -75
  78. package/src/core/activation.js +11 -3
  79. package/src/core/approvals.js +17 -51
  80. package/src/core/artifact-registry.js +59 -2
  81. package/src/core/artifacts.js +81 -27
  82. package/src/core/attachment-paths.js +45 -0
  83. package/src/core/attestation-coverage.js +6 -1
  84. package/src/core/attestation-verifier.js +21 -2
  85. package/src/core/attestation.js +17 -6
  86. package/src/core/audit.js +6 -1
  87. package/src/core/bundles.js +160 -66
  88. package/src/core/checkpoint-revalidation.js +12 -10
  89. package/src/core/cli-command-definitions.js +109 -7
  90. package/src/core/code-manifest.js +9 -3
  91. package/src/core/command-executors.js +182 -126
  92. package/src/core/command-input.js +21 -1
  93. package/src/core/completion-artifacts.js +71 -29
  94. package/src/core/completion-ownership.js +19 -8
  95. package/src/core/completion-recovery-rebind.js +11 -1
  96. package/src/core/completion.js +60 -9
  97. package/src/core/conformance.js +2 -1
  98. package/src/core/continuity-lint.js +13 -2
  99. package/src/core/continuity-reconciliation.js +16 -3
  100. package/src/core/continuity.js +16 -8
  101. package/src/core/contract-bootstrap-recovery.js +42 -41
  102. package/src/core/contract-revision.js +8 -9
  103. package/src/core/decision/artifact.js +2 -1
  104. package/src/core/decision/events.js +9 -6
  105. package/src/core/decision/resolver.js +19 -11
  106. package/src/core/decision/service.js +63 -38
  107. package/src/core/decision/task-bindings.js +6 -1
  108. package/src/core/diagnosis-model.js +11 -0
  109. package/src/core/diagnosis.js +10 -1
  110. package/src/core/diagnostic-projection.js +29 -12
  111. package/src/core/diagnostic-record.js +28 -3
  112. package/src/core/efficiency.js +6 -1
  113. package/src/core/events.js +316 -97
  114. package/src/core/execution-prerequisites.js +22 -13
  115. package/src/core/execution.js +26 -40
  116. package/src/core/filesystem.js +11 -0
  117. package/src/core/gate-artifact.js +105 -19
  118. package/src/core/gate-provenance.js +6 -5
  119. package/src/core/handoff.js +26 -14
  120. package/src/core/history.js +39 -35
  121. package/src/core/hypothesis-projection.js +32 -0
  122. package/src/core/information-gain-projection.js +104 -100
  123. package/src/core/inspect.js +12 -3
  124. package/src/core/integration-invocation-policy.js +10 -0
  125. package/src/core/integration-resources.js +41 -34
  126. package/src/core/ledger-event-collection.js +189 -0
  127. package/src/core/ledger-relations.js +23 -0
  128. package/src/core/native-storage.js +12 -0
  129. package/src/core/next-action-continuity.js +31 -9
  130. package/src/core/next-action-phases.js +1 -1
  131. package/src/core/next-action-review-phase.js +2 -2
  132. package/src/core/next-action-verification-phase.js +2 -1
  133. package/src/core/next-action.js +21 -2
  134. package/src/core/phase.js +138 -93
  135. package/src/core/policy-engine.js +12 -13
  136. package/src/core/preflight-consistency.js +72 -59
  137. package/src/core/preflight-loaders.js +19 -12
  138. package/src/core/preflight-model.js +6 -1
  139. package/src/core/preflight.js +29 -12
  140. package/src/core/progress.js +21 -10
  141. package/src/core/protocol-info.js +3 -0
  142. package/src/core/protocol-migration.js +7 -6
  143. package/src/core/reconcile-closure.js +162 -24
  144. package/src/core/recovery-history.js +66 -47
  145. package/src/core/reflection.js +72 -77
  146. package/src/core/responsibility.js +25 -13
  147. package/src/core/route-artifact.js +13 -3
  148. package/src/core/runtime-context.js +5 -0
  149. package/src/core/schema-validation.js +8 -3
  150. package/src/core/settlement.js +12 -1
  151. package/src/core/signing/bundle-file.js +53 -0
  152. package/src/core/signing/bundle-reference.js +15 -0
  153. package/src/core/signing/sigstore.js +60 -33
  154. package/src/core/signing/statement-file.js +27 -0
  155. package/src/core/storage-runtime-registry.js +41 -0
  156. package/src/core/structural-quality/artifacts.js +20 -14
  157. package/src/core/structural-quality/service.js +81 -56
  158. package/src/core/target-layout.js +1 -1
  159. package/src/core/task-claim-state.js +52 -18
  160. package/src/core/task-command.js +21 -1
  161. package/src/core/task-conflict-inspection.js +112 -107
  162. package/src/core/task-descriptor.js +29 -22
  163. package/src/core/task-discovery.js +288 -90
  164. package/src/core/task-lock.js +66 -306
  165. package/src/core/task-migration-validation.js +5 -5
  166. package/src/core/task-migration.js +17 -287
  167. package/src/core/task-snapshot.js +51 -8
  168. package/src/core/task-storage-migration.js +18 -0
  169. package/src/core/templates.js +20 -8
  170. package/src/core/test-intelligence/prune.js +5 -1
  171. package/src/core/trace.js +154 -99
  172. package/src/core/trajectory-evaluation.js +15 -3
  173. package/src/core/trajectory-metrics.js +47 -32
  174. package/src/core/transaction.js +16 -288
  175. package/src/core/verification-scope.js +20 -12
  176. package/src/core/work-state.js +100 -59
  177. package/src/repository-index/binary-manager.js +7 -5
  178. package/src/storage/artifact-bytes.js +22 -0
  179. package/src/storage/attachment-binding.js +11 -0
  180. package/src/storage/attachment-files.js +82 -0
  181. package/src/storage/attachment-references.js +36 -0
  182. package/src/storage/backup.js +53 -0
  183. package/src/storage/capabilities.js +31 -0
  184. package/src/storage/connection-owner.js +119 -0
  185. package/src/storage/connection.js +376 -0
  186. package/src/storage/existing-project-scope.js +45 -0
  187. package/src/storage/export-destination.js +44 -0
  188. package/src/storage/exporter.js +284 -0
  189. package/src/storage/file-durability.js +46 -0
  190. package/src/storage/importer.js +461 -0
  191. package/src/storage/index.js +74 -0
  192. package/src/storage/leases.js +63 -0
  193. package/src/storage/ledger-event-snapshot.js +150 -0
  194. package/src/storage/ledger-relations.js +220 -0
  195. package/src/storage/legacy-maintenance-boundary.js +18 -0
  196. package/src/storage/legacy-signature.js +115 -0
  197. package/src/storage/maintenance-handoff.js +129 -0
  198. package/src/storage/maintenance-owner.js +42 -0
  199. package/src/storage/maintenance.js +310 -0
  200. package/src/storage/metadata-json.js +35 -0
  201. package/src/storage/migration-activate.js +59 -0
  202. package/src/storage/migration-archive.js +57 -0
  203. package/src/storage/migration-attachment-root.js +61 -0
  204. package/src/storage/migration-candidate.js +398 -0
  205. package/src/storage/migration-publication.js +143 -0
  206. package/src/storage/migration-rollback-admission.js +54 -0
  207. package/src/storage/migration-rollback-preparation.js +132 -0
  208. package/src/storage/migration-rollback-publication.js +183 -0
  209. package/src/storage/migration-rollback-release.js +66 -0
  210. package/src/storage/migration-rollback-source-stage.js +103 -0
  211. package/src/storage/migration-rollback.js +43 -0
  212. package/src/storage/migration-source-partition.js +56 -0
  213. package/src/storage/migration-source.js +255 -0
  214. package/src/storage/migration-terminal.js +39 -0
  215. package/src/storage/migration-validation.js +75 -0
  216. package/src/storage/migration.js +43 -0
  217. package/src/storage/operational-attachments.js +49 -0
  218. package/src/storage/operational-context.js +50 -0
  219. package/src/storage/project-backup.js +98 -0
  220. package/src/storage/project-bootstrap.js +24 -0
  221. package/src/storage/project-boundary.js +79 -0
  222. package/src/storage/project-read-snapshot.js +18 -0
  223. package/src/storage/project-replacement.js +137 -0
  224. package/src/storage/project-restore.js +203 -0
  225. package/src/storage/record-definitions.js +45 -0
  226. package/src/storage/repository.js +507 -0
  227. package/src/storage/restore-journal.js +62 -0
  228. package/src/storage/restore-layout.js +30 -0
  229. package/src/storage/restore-preparation.js +84 -0
  230. package/src/storage/restore-replacement-archive.js +162 -0
  231. package/src/storage/restore-replacement-owner.js +31 -0
  232. package/src/storage/restore-replacement-preparation.js +128 -0
  233. package/src/storage/restore-replacement.js +142 -0
  234. package/src/storage/restore-terminal.js +32 -0
  235. package/src/storage/runtime.js +31 -0
  236. package/src/storage/schema.js +214 -0
  237. package/src/storage/singleton-import.js +84 -0
  238. package/src/storage/snapshot-continuity.js +16 -0
  239. package/src/storage/snapshot.js +36 -0
  240. package/src/storage/storage-marker.js +79 -0
  241. package/src/storage/task-guards.js +107 -0
  242. package/src/storage/topology.js +42 -0
  243. package/src/storage/transaction.js +179 -0
  244. package/src/storage/unit-of-work.js +663 -0
  245. package/src/storage/windows-process-incarnation.js +206 -0
  246. package/.forgeloop/forgeloop.gitignore +0 -4
  247. package/LICENSE-DOCS.md +0 -13
  248. package/src/core/transaction-maintenance.js +0 -70
@@ -0,0 +1,189 @@
1
+ /**
2
+ * Explicit immutable-source event sequence. This is not an Array or a Proxy.
3
+ * Readers decode on demand; historical views share only source position identity.
4
+ * The source owner must hold one immutable snapshot for the sequence's lifetime.
5
+ * Array-returning filter/map operations deliberately materialize selected proofs;
6
+ * they do not establish bounded memory for arbitrarily large relation state.
7
+ */
8
+ const collections = new WeakSet();
9
+ const viewToken = Symbol("ledger-source-view");
10
+
11
+ function sourceError(message) {
12
+ return Object.assign(new Error(message), { code: "E_LEDGER_SOURCE_INVALID" });
13
+ }
14
+
15
+ function integer(value) {
16
+ const number = Number(value);
17
+ return Number.isNaN(number) ? 0 : Math.trunc(number);
18
+ }
19
+
20
+ function bound(value, length) {
21
+ const index = integer(value);
22
+ return index < 0 ? Math.max(length + index, 0) : Math.min(index, length);
23
+ }
24
+
25
+ export class LedgerEventCollection {
26
+ #source;
27
+ #start;
28
+ #end;
29
+
30
+ constructor({ length, readAt, iterateRange = null, iterateTypes = null }, view = null, token = null) {
31
+ if (view) {
32
+ if (token !== viewToken) throw sourceError("Ledger views must come from their source collection");
33
+ this.#source = view.source;
34
+ this.#start = view.start;
35
+ this.#end = view.end;
36
+ } else {
37
+ if (!Number.isSafeInteger(length) || length < 0 || typeof readAt !== "function"
38
+ || (iterateRange !== null && typeof iterateRange !== "function")
39
+ || (iterateTypes !== null && typeof iterateTypes !== "function")) {
40
+ throw sourceError("Ledger source requires a finite length and synchronous readers");
41
+ }
42
+ this.#source = { length, readAt, iterateRange, iterateTypes, positions: new WeakMap() };
43
+ this.#start = 0;
44
+ this.#end = length;
45
+ }
46
+ collections.add(this);
47
+ Object.freeze(this);
48
+ }
49
+
50
+ get length() { return this.#end - this.#start; }
51
+
52
+ #observe(event, index) {
53
+ if (!event || typeof event !== "object" || Array.isArray(event)) {
54
+ throw sourceError("Ledger source returned a non-object event");
55
+ }
56
+ const known = this.#source.positions.get(event);
57
+ if (known !== undefined && known !== index) {
58
+ throw sourceError("Ledger source reused one event object at distinct positions");
59
+ }
60
+ this.#source.positions.set(event, index);
61
+ return event;
62
+ }
63
+
64
+ at(index) {
65
+ const position = integer(index);
66
+ const local = position < 0 ? this.length + position : position;
67
+ if (local < 0 || local >= this.length) return undefined;
68
+ const absolute = this.#start + local;
69
+ return this.#observe(this.#source.readAt(absolute), absolute);
70
+ }
71
+
72
+ *values() {
73
+ if (!this.#source.iterateRange) {
74
+ for (let index = 0; index < this.length; index += 1) yield this.at(index);
75
+ return;
76
+ }
77
+ let index = this.#start;
78
+ for (const event of this.#source.iterateRange(this.#start, this.#end)) {
79
+ if (index >= this.#end) throw sourceError("Ledger source exceeded its declared range");
80
+ yield this.#observe(event, index);
81
+ index += 1;
82
+ }
83
+ if (index !== this.#end) throw sourceError("Ledger source truncated its declared range");
84
+ }
85
+
86
+ [Symbol.iterator]() { return this.values(); }
87
+
88
+ *entries() {
89
+ let index = 0;
90
+ for (const event of this) { yield [index, event]; index += 1; }
91
+ }
92
+
93
+ *entriesOfTypes(types) {
94
+ const selected = new Set(types);
95
+ if (!this.#source.iterateTypes) {
96
+ for (const [index, event] of this.entries()) if (selected.has(event.event)) yield [index, event];
97
+ return;
98
+ }
99
+ let previous = this.#start - 1;
100
+ for (const { index, event } of this.#source.iterateTypes(this.#start, this.#end, [...selected])) {
101
+ if (!Number.isSafeInteger(index) || index <= previous || index < this.#start || index >= this.#end || !selected.has(event?.event)) {
102
+ throw sourceError("Ledger typed source returned an invalid position or event type");
103
+ }
104
+ previous = index;
105
+ yield [index - this.#start, this.#observe(event, index)];
106
+ }
107
+ }
108
+
109
+ slice(start = 0, end = this.length) {
110
+ const first = bound(start, this.length);
111
+ const last = Math.max(first, bound(end, this.length));
112
+ return new LedgerEventCollection({}, { source: this.#source, start: this.#start + first, end: this.#start + last }, viewToken);
113
+ }
114
+
115
+ indexOf(event) {
116
+ const absolute = event && typeof event === "object" ? this.#source.positions.get(event) : undefined;
117
+ return absolute === undefined || absolute < this.#start || absolute >= this.#end ? -1 : absolute - this.#start;
118
+ }
119
+
120
+ findIndex(predicate) {
121
+ for (const [index, event] of this.entries()) if (predicate(event, index, this)) return index;
122
+ return -1;
123
+ }
124
+
125
+ find(predicate) {
126
+ for (const [index, event] of this.entries()) if (predicate(event, index, this)) return event;
127
+ return undefined;
128
+ }
129
+
130
+ findLast(predicate) {
131
+ for (let index = this.length - 1; index >= 0; index -= 1) {
132
+ const event = this.at(index);
133
+ if (predicate(event, index, this)) return event;
134
+ }
135
+ return undefined;
136
+ }
137
+
138
+ some(predicate) { return this.findIndex(predicate) !== -1; }
139
+ every(predicate) { return this.findIndex((event, index) => !predicate(event, index, this)) === -1; }
140
+
141
+ filter(predicate) {
142
+ const selected = [];
143
+ for (const [index, event] of this.entries()) if (predicate(event, index, this)) selected.push(event);
144
+ return selected;
145
+ }
146
+
147
+ map(project) {
148
+ const values = [];
149
+ for (const [index, event] of this.entries()) values.push(project(event, index, this));
150
+ return values;
151
+ }
152
+ }
153
+
154
+ export function isLedgerEventCollection(events) {
155
+ return Array.isArray(events) || collections.has(events);
156
+ }
157
+
158
+ export function ledgerEventAt(events, index) {
159
+ return Array.isArray(events) ? events[index] : (Number.isSafeInteger(index) && index >= 0 ? events.at(index) : undefined);
160
+ }
161
+
162
+ /** Preserve strict Array identity; decoded collections prove private source position. */
163
+ export function ledgerEventAtIs(events, index, event) {
164
+ if (Array.isArray(events)) return events[index] === event;
165
+ return Number.isSafeInteger(index) && index >= 0 && index < events.length && events.indexOf(event) === index;
166
+ }
167
+
168
+ /** Typed selection preserves canonical source positions without retaining selected payload arrays. */
169
+ export function* ledgerEntriesOfTypes(events, types) {
170
+ if (!Array.isArray(events)) { yield* events.entriesOfTypes(types); return; }
171
+ const selected = new Set(types);
172
+ for (const [index, event] of events.entries()) if (selected.has(event.event)) yield [index, event];
173
+ }
174
+
175
+ export function* ledgerEventsOfTypes(events, types) {
176
+ for (const [, event] of ledgerEntriesOfTypes(events, types)) yield event;
177
+ }
178
+
179
+ export function ledgerTypeSummary(events, type) {
180
+ let count = 0;
181
+ let first;
182
+ let latest;
183
+ for (const event of ledgerEventsOfTypes(events, [type])) {
184
+ if (!count) first = event;
185
+ latest = event;
186
+ count += 1;
187
+ }
188
+ return { count, first, latest };
189
+ }
@@ -0,0 +1,23 @@
1
+ import { AsyncLocalStorage } from "node:async_hooks";
2
+
3
+ const owners = new AsyncLocalStorage();
4
+
5
+ /** Storage supplies an owned scratch backing; pure/array callers retain native collections. */
6
+ export function withLedgerRelationOwner(owner, callback) { return owners.run(owner, callback); }
7
+ export function ledgerRelationMap() { return owners.getStore()?.map() ?? new Map(); }
8
+ export function ledgerRelationSet() { return owners.getStore()?.set() ?? new Set(); }
9
+
10
+
11
+ /** Primitive grouped membership; storage may index groups without nested resident Sets. */
12
+ export function ledgerGroupedSet() {
13
+ const owner = owners.getStore();
14
+ if (owner?.groupedSet) return owner.groupedSet();
15
+ const groups = new Map();
16
+ return {
17
+ add(group, value) {
18
+ if (!groups.has(group)) groups.set(group, new Set());
19
+ groups.get(group).add(value);
20
+ },
21
+ *values(group) { yield* groups.get(group) ?? []; },
22
+ };
23
+ }
@@ -0,0 +1,12 @@
1
+ import { needsExistingProjectScope, withExistingProjectScope } from "../storage/existing-project-scope.js";
2
+ import { getOperationalStore } from "../storage/operational-context.js";
3
+
4
+ export async function withNativeReadScope(target, callback) {
5
+ return (await needsExistingProjectScope(target)) ? withExistingProjectScope(target, callback, { readOnly: true }) : callback();
6
+ }
7
+
8
+ export function requireNativeStore(target, errorFactory, refusalMessage) {
9
+ const store = getOperationalStore(target);
10
+ if (!store) throw errorFactory("E_STORAGE_MIGRATION_REQUIRED", refusalMessage);
11
+ return store;
12
+ }
@@ -1,15 +1,31 @@
1
1
  import { ARTIFACT_PATHS } from "./artifacts.js";
2
2
  import { reconcileContinuity } from "./continuity-reconciliation.js";
3
- import { NEXT_ACTIONS, result } from "./next-action-model.js";
3
+ import { directCommandSpec, NEXT_ACTIONS, result } from "./next-action-model.js";
4
+ import { taskArtifactPath } from "./task-paths.js";
4
5
 
5
- const REQUIRED_ARTIFACTS = Object.freeze([ARTIFACT_PATHS.state, ARTIFACT_PATHS.continuity]);
6
+ const SINGLETON_REQUIRED_ARTIFACTS = Object.freeze([ARTIFACT_PATHS.state, ARTIFACT_PATHS.continuity]);
6
7
 
7
- function reason(code, message, artifacts = REQUIRED_ARTIFACTS) {
8
+ function reason(code, message, artifacts = SINGLETON_REQUIRED_ARTIFACTS) {
8
9
  return { code, message, artifacts };
9
10
  }
10
11
 
12
+ function continuityArtifacts(taskId) {
13
+ if (!taskId) {
14
+ return {
15
+ continuity: ARTIFACT_PATHS.continuity,
16
+ required: SINGLETON_REQUIRED_ARTIFACTS,
17
+ };
18
+ }
19
+ return {
20
+ continuity: taskArtifactPath(taskId, "continuity"),
21
+ required: [taskArtifactPath(taskId, "state"), taskArtifactPath(taskId, "continuity")],
22
+ };
23
+ }
24
+
11
25
  export function nextActionForContinuity({ context, continuity } = {}) {
12
26
  if (!continuity || ["ABSENT", "NOT_APPLICABLE"].includes(continuity.classification)) return null;
27
+ const taskId = context?.taskId ?? null;
28
+ const artifacts = continuityArtifacts(taskId);
13
29
 
14
30
  if (continuity.classification === "FRESH") {
15
31
  const remaining = continuity.continuity?.remainingWork ?? [];
@@ -20,22 +36,27 @@ export function nextActionForContinuity({ context, continuity } = {}) {
20
36
  reasons: [reason(
21
37
  "CONTINUITY_REMAINING_WORK",
22
38
  `Execution continuity records ${remaining.length} remaining implementation item${remaining.length === 1 ? "" : "s"}.`,
23
- [ARTIFACT_PATHS.continuity],
39
+ [artifacts.continuity],
24
40
  )],
25
- requiredArtifacts: REQUIRED_ARTIFACTS,
41
+ requiredArtifacts: artifacts.required,
26
42
  });
27
43
  }
28
44
 
29
45
  if (continuity.classification === "RECONCILIATION_REQUIRED") {
46
+ const command = taskId
47
+ ? `forgeloop reconcile-continuity --task ${taskId}`
48
+ : "forgeloop reconcile-continuity";
30
49
  return result({
31
50
  ...context,
32
51
  nextAction: NEXT_ACTIONS.RESOLVE_BLOCKER,
33
- commands: ["forgeloop reconcile-continuity"],
52
+ commands: [command],
53
+ ...(taskId ? { commandSpecs: [directCommandSpec("reconcile-continuity", taskId)] } : {}),
34
54
  reasons: [reason(
35
55
  "E_CONTINUITY_RECONCILIATION_REQUIRED",
36
56
  "Execution continuity no longer matches canonical state or the current checkout; reconcile it before advancing verification.",
57
+ artifacts.required,
37
58
  )],
38
- requiredArtifacts: REQUIRED_ARTIFACTS,
59
+ requiredArtifacts: artifacts.required,
39
60
  });
40
61
  }
41
62
 
@@ -49,8 +70,9 @@ export function nextActionForContinuity({ context, continuity } = {}) {
49
70
  reasons: codes.map((code, index) => reason(
50
71
  code,
51
72
  continuity.reasons?.[index] ?? `Execution continuity is ${continuity.classification.toLowerCase()}.`,
73
+ artifacts.required,
52
74
  )),
53
- requiredArtifacts: REQUIRED_ARTIFACTS,
75
+ requiredArtifacts: artifacts.required,
54
76
  });
55
77
  }
56
78
 
@@ -60,6 +82,6 @@ export function nextActionForContinuity({ context, continuity } = {}) {
60
82
  export async function evaluateContinuityNextAction({ target, packageRoot, context } = {}) {
61
83
  return nextActionForContinuity({
62
84
  context,
63
- continuity: await reconcileContinuity({ target, packageRoot }),
85
+ continuity: await reconcileContinuity({ target, packageRoot, taskId: context?.taskId ?? null }),
64
86
  });
65
87
  }
@@ -311,7 +311,7 @@ export async function resolveNextActionPhase({
311
311
  const preflightArtifacts = [...requiredArtifacts, preflightRel];
312
312
  const persistedPreflightErrors = phaseNeedsChronology
313
313
  ? []
314
- : validatePersistedPreflight(preflightArtifact.value?.value, preflight);
314
+ : validatePersistedPreflight(preflightArtifact.value?.value, preflight, preflightArtifact.error);
315
315
 
316
316
  if (["ROUTED", "DESIGNING", "PLANNED"].includes(state.phase) && missingGates.length > 0) {
317
317
  const missingGatePaths = missingGates.map((gate) => taskGatePath(state.taskId, gate));
@@ -1,5 +1,5 @@
1
1
  import { readJsonArtifact } from "./artifacts.js";
2
- import { evaluateCompletion } from "./completion.js";
2
+ import { evaluateCompletion, receiptReadFailure } from "./completion.js";
3
3
  import { validateReceipt } from "./receipt.js";
4
4
  import { completionRelationshipErrors } from "./completion-relationships.js";
5
5
  import { evaluateRequiredEvidence, classifyRequirement, terminalRequirementsForContract } from "./evidence-readiness.js";
@@ -82,7 +82,7 @@ export async function resolveReviewingPhase({ state, context, requiredArtifacts,
82
82
  return decision(
83
83
  context,
84
84
  NEXT_ACTIONS.RESOLVE_BLOCKER,
85
- artifactError("E_RECEIPT_INVALID", `Repair or remove the invalid execution receipt before continuing: ${receipt.error.message}`, [receiptRel]),
85
+ receiptReadFailure(receipt.error, receiptRel),
86
86
  [...requiredArtifacts, receiptRel],
87
87
  );
88
88
  }
@@ -1,3 +1,4 @@
1
+ import { receiptReadFailure } from "./completion.js";
1
2
  import { readJsonArtifact } from "./artifacts.js";
2
3
  import { taskArtifactPath, taskStructuralQualityDirectory } from "./task-paths.js";
3
4
  import { validateReceipt } from "./receipt.js";
@@ -100,7 +101,7 @@ export async function resolveVerifyingPhase({ target, packageRoot, explicitTaskI
100
101
  return decision(
101
102
  context,
102
103
  NEXT_ACTIONS.RESOLVE_BLOCKER,
103
- artifactError("E_RECEIPT_INVALID", `Repair or remove the invalid execution receipt before continuing: ${receipt.error.message}`, [receiptRel]),
104
+ receiptReadFailure(receipt.error, receiptRel),
104
105
  [...requiredArtifacts, receiptRel],
105
106
  );
106
107
  }
@@ -15,6 +15,9 @@ import { resolveNextActionPhase } from "./next-action-phases.js";
15
15
  import { criterionForDecision } from "./settlement-model.js";
16
16
  import { validateApprovalForAction } from "./approvals.js";
17
17
  import { evaluateActionCapability } from "./capability-policy.js";
18
+ import { resolveTaskContext, TASK_SELECTION_MODES } from "./task-context.js";
19
+ import { getOperationalStore } from "../storage/operational-context.js";
20
+ import { withProjectReadSnapshot } from "../storage/project-read-snapshot.js";
18
21
 
19
22
  export { NEXT_ACTIONS } from "./next-action-model.js";
20
23
 
@@ -355,9 +358,25 @@ async function computeNextAction(targetOrOptions = {}, packageRootOption) {
355
358
  }
356
359
 
357
360
  export async function getNextAction(targetOrOptions = {}, packageRootOption) {
358
- const res = await computeNextAction(targetOrOptions, packageRootOption);
361
+ const target = typeof targetOrOptions === "string" ? targetOrOptions : targetOrOptions?.target;
362
+ const read = () => getSelectedNextAction(targetOrOptions, packageRootOption);
363
+ return target ? withProjectReadSnapshot(target, read) : read();
364
+ }
365
+
366
+ async function getSelectedNextAction(targetOrOptions, packageRootOption) {
367
+ let normalized = typeof targetOrOptions === "string"
368
+ ? { target: targetOrOptions, packageRoot: packageRootOption }
369
+ : { ...targetOrOptions };
370
+ if (getOperationalStore(normalized.target)) {
371
+ const context = await resolveTaskContext(normalized.target, {
372
+ taskId: normalized.taskId,
373
+ packageRoot: normalized.packageRoot,
374
+ selectionMode: TASK_SELECTION_MODES.READ,
375
+ });
376
+ if (context) normalized = { ...normalized, taskId: context.taskId };
377
+ }
378
+ const res = await computeNextAction(normalized);
359
379
  if (res && res.reasons && res.reasons.some((r) => r.code === "E_CONTRACT_UNRESOLVED_DECISION" || r.code === "E_UNRESOLVED_DECISION")) {
360
- const normalized = typeof targetOrOptions === "string" ? { target: targetOrOptions, packageRoot: packageRootOption } : targetOrOptions;
361
380
  const { target, packageRoot, taskId } = normalized ?? {};
362
381
  try {
363
382
  const explicitTaskId = taskId ?? null;