@cassiomc1/forgeloop 1.0.0 → 1.1.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.
Files changed (95) hide show
  1. package/.cursor/rules/project-loop.mdc +3 -2
  2. package/.github/copilot-instructions.md +1 -0
  3. package/AGENTS.md +2 -1
  4. package/CLAUDE.md +1 -0
  5. package/DOCS_INDEX.md +36 -0
  6. package/ENG/design-code-eng.md +31 -0
  7. package/ENG/documentation-quality-eng.md +400 -0
  8. package/EXECUTION_STATE.md +23 -13
  9. package/GUIDE_ROUTER.md +23 -8
  10. package/LOOP_ENGINEERING.md +77 -12
  11. package/PROTOCOL_INTEGRATION.md +9 -6
  12. package/README.md +82 -39
  13. package/docs/ARTIFACT_REFERENCE.md +442 -0
  14. package/docs/CLI_REFERENCE.md +892 -0
  15. package/docs/CROSS_HARNESS_CONTINUITY.md +198 -0
  16. package/docs/DOCUMENTATION_GUIDE.md +161 -0
  17. package/docs/GETTING_STARTED.md +348 -0
  18. package/docs/RECIPES.md +250 -0
  19. package/docs/TROUBLESHOOTING.md +345 -0
  20. package/docs/assets/forgeloop-flow.svg +1 -1
  21. package/docs/forgeloop-flow.mmd +1 -1
  22. package/package.json +13 -2
  23. package/schemas/task-descriptor.schema.json +56 -0
  24. package/schemas/work-state.schema.json +18 -1
  25. package/scripts/CI_VALIDATORS.md +7 -0
  26. package/src/cli.js +280 -388
  27. package/src/commands/advance.js +5 -2
  28. package/src/commands/audit.js +11 -1
  29. package/src/commands/clear-continuity.js +5 -2
  30. package/src/commands/clear-state.js +5 -2
  31. package/src/commands/complete.js +9 -1
  32. package/src/commands/continuity.js +5 -2
  33. package/src/commands/inspect.js +10 -2
  34. package/src/commands/next.js +5 -2
  35. package/src/commands/preflight.js +9 -1
  36. package/src/commands/prepare-completion.js +5 -2
  37. package/src/commands/reconcile-continuity.js +5 -2
  38. package/src/commands/record-check.js +7 -1
  39. package/src/commands/record-continuity.js +21 -14
  40. package/src/commands/record-terminal-result.js +7 -1
  41. package/src/commands/route.js +22 -18
  42. package/src/commands/run-check.js +52 -44
  43. package/src/commands/status.js +18 -12
  44. package/src/commands/task-create.js +94 -0
  45. package/src/commands/task-list.js +48 -0
  46. package/src/commands/task-migrate.js +34 -0
  47. package/src/commands/task-scope.js +75 -0
  48. package/src/commands/task-show.js +81 -0
  49. package/src/commands/task-unlock.js +35 -0
  50. package/src/commands/validate-protocol.js +37 -20
  51. package/src/commands/validate-state.js +24 -18
  52. package/src/config/guides.json +42 -0
  53. package/src/core/activation.js +8 -4
  54. package/src/core/artifact-registry.js +166 -0
  55. package/src/core/audit.js +65 -12
  56. package/src/core/bundles.js +76 -50
  57. package/src/core/cli-command-definitions.js +611 -0
  58. package/src/core/cli-metadata.js +23 -0
  59. package/src/core/completion-artifacts.js +161 -74
  60. package/src/core/completion.js +134 -76
  61. package/src/core/continuity.js +20 -13
  62. package/src/core/contract.js +6 -3
  63. package/src/core/error-codes.js +197 -0
  64. package/src/core/events.js +19 -14
  65. package/src/core/execution.js +38 -6
  66. package/src/core/gate-artifact.js +12 -9
  67. package/src/core/gates.js +4 -2
  68. package/src/core/guide-metadata.js +7 -11
  69. package/src/core/guide-registry.js +29 -0
  70. package/src/core/inspect.js +7 -4
  71. package/src/core/native-adapters.js +6 -0
  72. package/src/core/phase.js +85 -33
  73. package/src/core/preflight-consistency.js +24 -14
  74. package/src/core/preflight-loaders.js +16 -11
  75. package/src/core/preflight.js +44 -25
  76. package/src/core/protocol.js +2 -11
  77. package/src/core/receipt.js +1 -1
  78. package/src/core/report.js +2 -2
  79. package/src/core/repository.js +46 -12
  80. package/src/core/resumability.js +6 -4
  81. package/src/core/route-artifact.js +9 -5
  82. package/src/core/router.js +11 -7
  83. package/src/core/schema-validation.js +1 -0
  84. package/src/core/task-command.js +41 -0
  85. package/src/core/task-context.js +126 -0
  86. package/src/core/task-descriptor.js +81 -0
  87. package/src/core/task-discovery.js +116 -0
  88. package/src/core/task-identity.js +76 -0
  89. package/src/core/task-lock.js +209 -0
  90. package/src/core/task-migration-validation.js +140 -0
  91. package/src/core/task-migration.js +361 -0
  92. package/src/core/task-paths.js +96 -0
  93. package/src/core/task-scope.js +179 -0
  94. package/src/core/templates.js +3 -9
  95. package/src/core/work-state.js +24 -13
@@ -0,0 +1,75 @@
1
+ import { resolveTaskContext } from "../core/task-context.js";
2
+ import { readTaskDescriptor, writeTaskDescriptor } from "../core/task-descriptor.js";
3
+ import { normalizeWriteClaims, assertNoScopeConflicts, assertScopeClean, assertScopeNotFrozen } from "../core/task-scope.js";
4
+ import { discoverTasks } from "../core/task-discovery.js";
5
+ import { readWorkState } from "../core/work-state.js";
6
+ import { withProjectClaimsLock, withTaskLock } from "../core/task-lock.js";
7
+ import { E_TASK_NOT_FOUND } from "../core/error-codes.js";
8
+
9
+ function taskError(code, message, artifacts = []) {
10
+ const error = new Error(message);
11
+ error.code = code;
12
+ error.artifacts = artifacts;
13
+ return error;
14
+ }
15
+
16
+ export async function runTaskScope({ target, packageRoot, taskId, claims } = {}) {
17
+ const context = await resolveTaskContext(target, { taskId, packageRoot, explicitRequired: true });
18
+ const effectiveTaskId = context.taskId;
19
+
20
+ let descriptorArtifact;
21
+ try {
22
+ descriptorArtifact = await readTaskDescriptor(target, effectiveTaskId, packageRoot);
23
+ } catch (error) {
24
+ if (error.code === "ARTIFACT_MISSING") {
25
+ throw taskError(E_TASK_NOT_FOUND, `Task descriptor not found for task: ${effectiveTaskId}`);
26
+ }
27
+ throw error;
28
+ }
29
+
30
+ const descriptor = descriptorArtifact.value;
31
+
32
+ if (claims !== undefined && claims !== null) {
33
+ return withProjectClaimsLock(target, async () => {
34
+ return withTaskLock(target, effectiveTaskId, "task-scope", async () => {
35
+ const state = await readWorkState(target, { packageRoot, taskId: effectiveTaskId });
36
+ if (state?.phase) {
37
+ assertScopeNotFrozen(state.phase);
38
+ }
39
+
40
+ const normalizedClaims = normalizeWriteClaims(claims);
41
+ const allTasks = await discoverTasks(target, packageRoot);
42
+ assertNoScopeConflicts(normalizedClaims, allTasks, effectiveTaskId);
43
+ if (normalizedClaims.length > 0) {
44
+ await assertScopeClean(target, normalizedClaims);
45
+ }
46
+
47
+ const updatedDescriptor = {
48
+ ...descriptor,
49
+ writeClaims: normalizedClaims,
50
+ updatedAt: new Date().toISOString(),
51
+ };
52
+ await writeTaskDescriptor(target, updatedDescriptor, packageRoot);
53
+ return {
54
+ taskId: effectiveTaskId,
55
+ taskKey: context.taskKey,
56
+ writeClaims: updatedDescriptor.writeClaims,
57
+ updated: true,
58
+ };
59
+ });
60
+ });
61
+ }
62
+
63
+ return {
64
+ taskId: effectiveTaskId,
65
+ taskKey: context.taskKey,
66
+ writeClaims: descriptor.writeClaims ?? [],
67
+ updated: false,
68
+ };
69
+ }
70
+
71
+ export function formatTaskScopeResult(result) {
72
+ const claims = result.writeClaims.length === 0 ? "none" : result.writeClaims.join(", ");
73
+ const action = result.updated ? "updated scope" : "current scope";
74
+ return `${action} for ${result.taskId}: ${claims}\n`;
75
+ }
@@ -0,0 +1,81 @@
1
+ import { resolveTaskContext } from "../core/task-context.js";
2
+ import { readTaskDescriptor } from "../core/task-descriptor.js";
3
+ import { readLockInfo } from "../core/task-lock.js";
4
+ import { taskDirectory, taskArtifactPath } from "../core/task-paths.js";
5
+ import { readWorkState } from "../core/work-state.js";
6
+ import { readContract } from "../core/contract.js";
7
+ import { fileExists, ensureWithin } from "../core/filesystem.js";
8
+ import { E_TASK_NOT_FOUND } from "../core/error-codes.js";
9
+
10
+ function taskError(code, message, artifacts = []) {
11
+ const error = new Error(message);
12
+ error.code = code;
13
+ error.artifacts = artifacts;
14
+ return error;
15
+ }
16
+
17
+ export async function runTaskShow({ target, packageRoot, taskId } = {}) {
18
+ const context = await resolveTaskContext(target, { taskId, packageRoot, explicitRequired: true });
19
+ const effectiveTaskId = context.taskId;
20
+
21
+ let descriptor = null;
22
+ try {
23
+ const descArtifact = await readTaskDescriptor(target, effectiveTaskId, packageRoot);
24
+ descriptor = descArtifact.value;
25
+ } catch (error) {
26
+ if (error.code === "ARTIFACT_MISSING") {
27
+ throw taskError(E_TASK_NOT_FOUND, `Task descriptor not found for task: ${effectiveTaskId}`);
28
+ }
29
+ throw error;
30
+ }
31
+
32
+ const state = await readWorkState(target, { packageRoot, taskId: effectiveTaskId });
33
+ let contract = null;
34
+ try {
35
+ const contractArtifact = await readContract(target, packageRoot, { taskId: effectiveTaskId });
36
+ contract = contractArtifact.value;
37
+ } catch {
38
+ // contract may not be present
39
+ }
40
+
41
+ const lockInfo = await readLockInfo(target, effectiveTaskId);
42
+ const artifacts = {};
43
+ for (const name of ["contract", "route", "state", "preflight", "receipt", "continuity", "events"]) {
44
+ const rel = taskArtifactPath(effectiveTaskId, name);
45
+ artifacts[name] = {
46
+ path: rel,
47
+ exists: await fileExists(ensureWithin(target, rel)),
48
+ };
49
+ }
50
+
51
+ return {
52
+ taskId: effectiveTaskId,
53
+ taskKey: context.taskKey,
54
+ directory: taskDirectory(effectiveTaskId),
55
+ phase: state?.phase ?? "UNINITIALIZED",
56
+ writeClaims: descriptor.writeClaims ?? [],
57
+ lock: lockInfo,
58
+ contract: contract ? { title: contract.title ?? null, taskType: contract.taskType ?? null } : null,
59
+ artifacts,
60
+ createdAt: descriptor.createdAt,
61
+ updatedAt: descriptor.updatedAt,
62
+ };
63
+ }
64
+
65
+ export function formatTaskShowResult(result) {
66
+ const claims = result.writeClaims.length === 0 ? "none" : result.writeClaims.join(", ");
67
+ const lockStatus = result.lock
68
+ ? `locked by PID ${result.lock.pid ?? "unknown"} since ${result.lock.acquiredAt ?? "unknown"}`
69
+ : "unlocked";
70
+ const lines = [
71
+ `Task: ${result.taskId}`,
72
+ `Key: ${result.taskKey}`,
73
+ `Directory: ${result.directory}`,
74
+ `Phase: ${result.phase}`,
75
+ `Write Claims: ${claims}`,
76
+ `Lock: ${lockStatus}`,
77
+ `Created: ${result.createdAt}`,
78
+ `Updated: ${result.updatedAt}`,
79
+ ];
80
+ return `${lines.join("\n")}\n`;
81
+ }
@@ -0,0 +1,35 @@
1
+ import { resolveTaskContext } from "../core/task-context.js";
2
+ import { forceUnlockTask, readLockInfo } from "../core/task-lock.js";
3
+
4
+ export async function runTaskUnlock({ target, packageRoot, taskId, force = false } = {}) {
5
+ const context = await resolveTaskContext(target, { taskId, packageRoot, explicitRequired: true });
6
+ const effectiveTaskId = context.taskId;
7
+
8
+ const lockInfo = await readLockInfo(target, effectiveTaskId);
9
+ if (!lockInfo) {
10
+ return {
11
+ taskId: effectiveTaskId,
12
+ taskKey: context.taskKey,
13
+ unlocked: false,
14
+ message: "Task is not locked",
15
+ };
16
+ }
17
+
18
+ if (!force) {
19
+ const error = new Error(`Task ${effectiveTaskId} is locked by operation "${lockInfo.operation ?? "unknown"}" (PID ${lockInfo.pid ?? "unknown"}). Use --force to unlock.`);
20
+ error.code = "E_TASK_LOCKED";
21
+ throw error;
22
+ }
23
+
24
+ await forceUnlockTask(target, effectiveTaskId);
25
+ return {
26
+ taskId: effectiveTaskId,
27
+ taskKey: context.taskKey,
28
+ unlocked: true,
29
+ message: `Released lock for task ${effectiveTaskId}`,
30
+ };
31
+ }
32
+
33
+ export function formatTaskUnlockResult(result) {
34
+ return `${result.message}\n`;
35
+ }
@@ -43,6 +43,9 @@ async function readArtifact(target, relativePath, label) {
43
43
  }
44
44
  }
45
45
 
46
+ import { taskArtifactPath } from "../core/task-paths.js";
47
+ import { withResolvedTask } from "../core/task-command.js";
48
+
46
49
  export async function runValidateProtocol({
47
50
  target,
48
51
  packageRoot,
@@ -53,12 +56,24 @@ export async function runValidateProtocol({
53
56
  continuityFile = null,
54
57
  taskBriefFiles = [],
55
58
  delegatedResultFiles = [],
59
+ taskId = null,
60
+ task = null,
56
61
  }) {
62
+ return withResolvedTask(target, { taskId: taskId ?? task, packageRoot }, async (ctx) => {
63
+ const effectiveTaskId = ctx?.taskId ?? null;
64
+ const effectiveRouteFile = routeFile ?? (effectiveTaskId ? taskArtifactPath(effectiveTaskId, "route") : ARTIFACT_PATHS.route);
65
+ const effectiveStateFile = stateFile ?? (effectiveTaskId ? taskArtifactPath(effectiveTaskId, "state") : ARTIFACT_PATHS.state);
66
+ const effectiveReceiptFile = receiptFile ?? (effectiveTaskId ? taskArtifactPath(effectiveTaskId, "receipt") : ARTIFACT_PATHS.receipt);
67
+ const effectiveContractFile = contractFile ?? (effectiveTaskId ? taskArtifactPath(effectiveTaskId, "contract") : null);
68
+ const effectiveContinuityFile = continuityFile ?? (effectiveTaskId ? taskArtifactPath(effectiveTaskId, "continuity") : null);
69
+ const effectiveEventsFile = effectiveTaskId ? taskArtifactPath(effectiveTaskId, "events") : ARTIFACT_PATHS.events;
70
+ const effectivePreflightFile = effectiveTaskId ? taskArtifactPath(effectiveTaskId, "preflight") : ARTIFACT_PATHS.preflight;
71
+
57
72
  const descriptors = [
58
- ["route", routeFile],
59
- ["state", stateFile],
60
- ["receipt", receiptFile],
61
- ["continuity", continuityFile],
73
+ ["route", effectiveRouteFile],
74
+ ["state", effectiveStateFile],
75
+ ["receipt", effectiveReceiptFile],
76
+ ["continuity", effectiveContinuityFile],
62
77
  ...taskBriefFiles.map((file) => [`task brief:${file}`, file]),
63
78
  ...delegatedResultFiles.map((file) => [`delegated result:${file}`, file]),
64
79
  ];
@@ -117,19 +132,20 @@ export async function runValidateProtocol({
117
132
  for (const item of loaded.filter((candidate) => candidate.label.startsWith("delegated result:"))) {
118
133
  await validateLoaded(item, "delegated-result", async (value) => validateDelegatedResult(value, packageRoot));
119
134
  }
120
- const stateValidationError = schemaErrors.some((error) => error.artifacts.includes(stateFile));
135
+ const stateValidationError = schemaErrors.some((error) => error.artifacts.includes(effectiveStateFile));
121
136
  const stateClassification = state && readErrors.length === 0 && !stateValidationError
122
- ? await classifyLoadedWorkState({ target, state, contractFile })
137
+ ? await classifyLoadedWorkState({ target, state, contractFile: effectiveContractFile, taskId: effectiveTaskId })
123
138
  : null;
124
139
  let readyConsistencyErrors = [];
125
140
  try {
126
- const persistedPreflight = await readJsonArtifact(target, ARTIFACT_PATHS.preflight, "preflight", packageRoot);
141
+ const persistedPreflight = await readJsonArtifact(target, effectivePreflightFile, "preflight", packageRoot);
127
142
  if (persistedPreflight.value.status === "READY") {
128
143
  readyConsistencyErrors = await validateReadyProtocolConsistency({
129
144
  target,
130
145
  packageRoot,
131
146
  persisted: persistedPreflight.value,
132
- current: await evaluatePreflight({ target, packageRoot }),
147
+ current: await evaluatePreflight({ target, packageRoot, taskId: effectiveTaskId }),
148
+ taskId: effectiveTaskId,
133
149
  });
134
150
  }
135
151
  } catch {
@@ -138,13 +154,13 @@ export async function runValidateProtocol({
138
154
  let ledgerEvents = [];
139
155
  let ledgerErrors = [];
140
156
  if (state && !stateValidationError) {
141
- const ledger = await validateEventLedger(target, packageRoot);
157
+ const ledger = await validateEventLedger(target, packageRoot, { taskId: effectiveTaskId, eventsPath: effectiveEventsFile });
142
158
  ledgerEvents = ledger.events ?? [];
143
159
  ledgerErrors = [
144
- ...ledger.errors.map((error) => ({ ...error, artifacts: [ARTIFACT_PATHS.events] })),
160
+ ...ledger.errors.map((error) => ({ ...error, artifacts: [effectiveEventsFile] })),
145
161
  ...validateStateLedgerCoherence(state, ledger.events).map((error) => ({
146
162
  ...error,
147
- artifacts: [ARTIFACT_PATHS.state, ARTIFACT_PATHS.events],
163
+ artifacts: [effectiveStateFile, effectiveEventsFile],
148
164
  })),
149
165
  ];
150
166
  }
@@ -166,15 +182,16 @@ export async function runValidateProtocol({
166
182
  delegatedResults,
167
183
  events: ledgerEvents,
168
184
  });
169
- if (readErrors.length > 0 || schemaErrors.length > 0 || readyConsistencyErrors.length > 0 || ledgerErrors.length > 0) {
170
- return {
171
- ...result,
172
- status: "INVALID",
173
- errors: [...result.errors, ...readErrors, ...schemaErrors, ...readyConsistencyErrors, ...ledgerErrors]
174
- .sort((left, right) => left.code.localeCompare(right.code) || left.message.localeCompare(right.message)),
175
- };
176
- }
177
- return result;
185
+ if (readErrors.length > 0 || schemaErrors.length > 0 || readyConsistencyErrors.length > 0 || ledgerErrors.length > 0) {
186
+ return {
187
+ ...result,
188
+ status: "INVALID",
189
+ errors: [...result.errors, ...readErrors, ...schemaErrors, ...readyConsistencyErrors, ...ledgerErrors]
190
+ .sort((left, right) => left.code.localeCompare(right.code) || left.message.localeCompare(right.message)),
191
+ };
192
+ }
193
+ return result;
194
+ });
178
195
  }
179
196
 
180
197
  export function formatValidateProtocolResult(result) {
@@ -1,27 +1,33 @@
1
1
  import { readWorkState, WORK_STATE_PATH } from "../core/work-state.js";
2
+ import { withResolvedTask } from "../core/task-command.js";
3
+ import { taskArtifactPath } from "../core/task-paths.js";
2
4
 
3
- export async function runValidateState({ target, packageRoot }) {
4
- try {
5
- const state = await readWorkState(target, packageRoot);
6
- if (!state) {
5
+ export async function runValidateState({ target, packageRoot, taskId, task } = {}) {
6
+ return withResolvedTask(target, { taskId: taskId ?? task, packageRoot }, async (ctx) => {
7
+ const effectiveTaskId = ctx?.taskId ?? null;
8
+ const path = effectiveTaskId ? taskArtifactPath(effectiveTaskId, "state") : WORK_STATE_PATH;
9
+ try {
10
+ const state = await readWorkState(target, { packageRoot, taskId: effectiveTaskId });
11
+ if (!state) {
12
+ return {
13
+ ok: true,
14
+ path,
15
+ state: null,
16
+ errors: [],
17
+ warnings: ["No work-state checkpoint is present."],
18
+ };
19
+ }
20
+ return { ok: true, path, state, errors: [], warnings: [] };
21
+ } catch (error) {
7
22
  return {
8
- ok: true,
9
- path: WORK_STATE_PATH,
23
+ ok: false,
24
+ path,
10
25
  state: null,
11
- errors: [],
12
- warnings: ["No work-state checkpoint is present."],
26
+ errors: [error.message],
27
+ warnings: [],
13
28
  };
14
29
  }
15
- return { ok: true, path: WORK_STATE_PATH, state, errors: [], warnings: [] };
16
- } catch (error) {
17
- return {
18
- ok: false,
19
- path: WORK_STATE_PATH,
20
- state: null,
21
- errors: [error.message],
22
- warnings: [],
23
- };
24
- }
30
+ });
25
31
  }
26
32
 
27
33
  export function formatValidateStateResult(result) {
@@ -0,0 +1,42 @@
1
+ {
2
+ "premium": {
3
+ "path": "ENG/premium-sites-studio-eng.md",
4
+ "install": true
5
+ },
6
+ "clean": {
7
+ "path": "ENG/clean-code-eng.md",
8
+ "install": true
9
+ },
10
+ "test": {
11
+ "path": "ENG/test-code-eng.md",
12
+ "install": true
13
+ },
14
+ "security": {
15
+ "path": "ENG/sec-code-eng.md",
16
+ "install": true
17
+ },
18
+ "design": {
19
+ "path": "ENG/design-code-eng.md",
20
+ "install": true
21
+ },
22
+ "taste": {
23
+ "path": "ENG/taste-frontend-eng.md",
24
+ "install": true
25
+ },
26
+ "performance": {
27
+ "path": "ENG/perf-code-eng.md",
28
+ "install": true
29
+ },
30
+ "accessibility": {
31
+ "path": "ENG/accessibility-eng.md",
32
+ "install": true
33
+ },
34
+ "games": {
35
+ "path": "ENG/games-code-design-web-eng.md",
36
+ "install": true
37
+ },
38
+ "documentation": {
39
+ "path": "ENG/documentation-quality-eng.md",
40
+ "install": true
41
+ }
42
+ }
@@ -2,15 +2,19 @@ import { randomUUID } from "node:crypto";
2
2
 
3
3
  import { ARTIFACT_PATHS, writeJsonArtifact } from "./artifacts.js";
4
4
  import { PROTOCOL_VERSION } from "./protocol.js";
5
+ import { sessionArtifactPath } from "./task-paths.js";
5
6
 
6
7
  export async function activateSession(target, packageRoot, options = {}) {
8
+ const sessionId = options.sessionId ?? randomUUID();
7
9
  const value = {
8
10
  schemaVersion: 1,
9
11
  protocolVersion: PROTOCOL_VERSION,
10
- sessionId: randomUUID(),
11
- activationMarker: `forgeloop-${randomUUID()}`,
12
+ sessionId,
13
+ activationMarker: options.activationMarker ?? `forgeloop-${randomUUID()}`,
12
14
  createdAt: options.createdAt ?? new Date().toISOString(),
13
15
  };
14
- const written = await writeJsonArtifact(target, ARTIFACT_PATHS.session, value, "activation", packageRoot, options);
15
- return written.value;
16
+ const relativePath = sessionArtifactPath(sessionId);
17
+ const written = await writeJsonArtifact(target, relativePath, value, "activation", packageRoot, options);
18
+ await writeJsonArtifact(target, ARTIFACT_PATHS.session, value, "activation", packageRoot, options).catch(() => {});
19
+ return { ...written.value, path: relativePath };
16
20
  }
@@ -0,0 +1,166 @@
1
+ import { TASK_STATE_ROOT, TASK_ARTIFACT_FILES, PROJECT_ARTIFACT_PATHS, SESSIONS_ROOT } from "./task-paths.js";
2
+
3
+ /**
4
+ * Canonical registry of ForgeLoop artifacts, defining filesystem paths,
5
+ * schema bindings, ownership, mutability, trust classifications, and scopes.
6
+ *
7
+ * Schema field shapes remain strictly owned by JSON schemas in `schemas/`.
8
+ */
9
+ export const ARTIFACT_REGISTRY = Object.freeze({
10
+ descriptor: Object.freeze({
11
+ key: "descriptor",
12
+ scope: "TASK",
13
+ path: `${TASK_STATE_ROOT}/<task-key>/${TASK_ARTIFACT_FILES.descriptor}`,
14
+ schema: "task-descriptor",
15
+ owner: "PROTOCOL_MANAGED",
16
+ mutability: "MUTABLE_BEFORE_EXECUTION",
17
+ trustRole: "TASK_DESCRIPTOR",
18
+ isPublic: true,
19
+ isPersisted: true,
20
+ description: "Canonical task descriptor declaring task ID, hash key, timestamps, and write claims.",
21
+ }),
22
+ contract: Object.freeze({
23
+ key: "contract",
24
+ scope: "TASK",
25
+ path: `${TASK_STATE_ROOT}/<task-key>/${TASK_ARTIFACT_FILES.contract}`,
26
+ schema: "current-contract",
27
+ owner: "AGENT_AUTHORED",
28
+ mutability: "MUTABLE_BEFORE_EXECUTION",
29
+ trustRole: "OPERATIONAL_SPECIFICATION",
30
+ isPublic: true,
31
+ isPersisted: true,
32
+ description: "Current task contract defining objectives, deliverables, constraints, and success criteria.",
33
+ }),
34
+ route: Object.freeze({
35
+ key: "route",
36
+ scope: "TASK",
37
+ path: `${TASK_STATE_ROOT}/<task-key>/${TASK_ARTIFACT_FILES.route}`,
38
+ schema: "routing-result",
39
+ owner: "PROTOCOL_GENERATED",
40
+ mutability: "MUTABLE_ON_REROUTE",
41
+ trustRole: "GUIDE_ROUTING_SPECIFICATION",
42
+ isPublic: true,
43
+ isPersisted: true,
44
+ description: "Deterministic engineering guide routing result bound to current contract.",
45
+ }),
46
+ preflight: Object.freeze({
47
+ key: "preflight",
48
+ scope: "TASK",
49
+ path: `${TASK_STATE_ROOT}/<task-key>/${TASK_ARTIFACT_FILES.preflight}`,
50
+ schema: "preflight",
51
+ owner: "PROTOCOL_GENERATED",
52
+ mutability: "OVERWRITTEN_ON_PREFLIGHT",
53
+ trustRole: "READINESS_ATTESTATION",
54
+ isPublic: true,
55
+ isPersisted: true,
56
+ description: "Preflight readiness report verifying profile, contract, routing, and gates.",
57
+ }),
58
+ sources: Object.freeze({
59
+ key: "sources",
60
+ scope: "PROJECT",
61
+ path: PROJECT_ARTIFACT_PATHS.sources,
62
+ schema: "source-registry",
63
+ owner: "OPERATOR_OR_AGENT",
64
+ mutability: "MUTABLE_ON_DISCOVERY",
65
+ trustRole: "SOURCE_ATTESTATION",
66
+ isPublic: true,
67
+ isPersisted: true,
68
+ description: "Registry of discovered project facts, platforms, runtimes, and dependencies.",
69
+ }),
70
+ events: Object.freeze({
71
+ key: "events",
72
+ scope: "TASK",
73
+ path: `${TASK_STATE_ROOT}/<task-key>/${TASK_ARTIFACT_FILES.events}`,
74
+ schema: "event",
75
+ owner: "PROTOCOL_APPENDED",
76
+ mutability: "APPEND_ONLY",
77
+ trustRole: "AUDIT_LEDGER",
78
+ isPublic: true,
79
+ isPersisted: true,
80
+ description: "Append-only cryptographic hash-chained event ledger.",
81
+ }),
82
+ session: Object.freeze({
83
+ key: "session",
84
+ scope: "SESSION",
85
+ path: `${SESSIONS_ROOT}/<session-id>.json`,
86
+ schema: "activation",
87
+ owner: "PROTOCOL_GENERATED",
88
+ mutability: "OVERWRITTEN_ON_ACTIVATE",
89
+ trustRole: "SESSION_MARKER",
90
+ isPublic: true,
91
+ isPersisted: true,
92
+ description: "Active harness session marker recording session ID and activation marker.",
93
+ }),
94
+ config: Object.freeze({
95
+ key: "config",
96
+ scope: "PROJECT",
97
+ path: PROJECT_ARTIFACT_PATHS.config,
98
+ schema: "config",
99
+ owner: "OPERATOR_OR_AGENT",
100
+ mutability: "MUTABLE_CONFIGURATION",
101
+ trustRole: "PROJECT_CONFIGURATION",
102
+ isPublic: true,
103
+ isPersisted: true,
104
+ description: "Local ForgeLoop configuration settings and overrides.",
105
+ }),
106
+ gates: Object.freeze({
107
+ key: "gates",
108
+ scope: "TASK",
109
+ path: `${TASK_STATE_ROOT}/<task-key>/gates/<gate>.json`,
110
+ schema: "gate",
111
+ owner: "AGENT_AUTHORED_OR_REVIEWER",
112
+ mutability: "OVERWRITTEN_ON_GATE_SATISFACTION",
113
+ trustRole: "GATE_APPROVAL_ATTESTATION",
114
+ isPublic: true,
115
+ isPersisted: true,
116
+ description: "Preflight gate satisfaction artifacts recording gate decisions, artifacts, and evidence.",
117
+ }),
118
+ state: Object.freeze({
119
+ key: "state",
120
+ scope: "TASK",
121
+ path: `${TASK_STATE_ROOT}/<task-key>/${TASK_ARTIFACT_FILES.state}`,
122
+ schema: "work-state",
123
+ owner: "PROTOCOL_MANAGED",
124
+ mutability: "ATOMIC_STATE_TRANSITIONS",
125
+ trustRole: "CANONICAL_LIFECYCLE_STATE",
126
+ isPublic: true,
127
+ isPersisted: true,
128
+ description: "Canonical lifecycle work state recording active phase, checks, blockers, and fingerprints.",
129
+ }),
130
+ continuity: Object.freeze({
131
+ key: "continuity",
132
+ scope: "TASK",
133
+ path: `${TASK_STATE_ROOT}/<task-key>/${TASK_ARTIFACT_FILES.continuity}`,
134
+ schema: "continuity",
135
+ owner: "AGENT_OR_HARNESS",
136
+ mutability: "MUTABLE_HANDOFF_NOTES",
137
+ trustRole: "NON_EVIDENCE_HANDOFF",
138
+ isPublic: true,
139
+ isPersisted: true,
140
+ description: "Cross-harness non-evidence handoff notes containing focus, remaining work, and inspect first targets.",
141
+ }),
142
+ receipt: Object.freeze({
143
+ key: "receipt",
144
+ scope: "TASK",
145
+ path: `${TASK_STATE_ROOT}/<task-key>/${TASK_ARTIFACT_FILES.receipt}`,
146
+ schema: "execution-receipt",
147
+ owner: "PROTOCOL_COMPILED",
148
+ mutability: "ATOMIC_COMPILATION",
149
+ trustRole: "EVIDENCE_COMPILATION",
150
+ isPublic: true,
151
+ isPersisted: true,
152
+ description: "Cryptographically bound verification receipt summarizing evidence coverage and publication readiness.",
153
+ }),
154
+ executions: Object.freeze({
155
+ key: "executions",
156
+ scope: "TASK",
157
+ path: `${TASK_STATE_ROOT}/<task-key>/executions/exec-<id>.json`,
158
+ schema: "execution",
159
+ owner: "PROTOCOL_EXECUTED",
160
+ mutability: "IMMUTABLE_ONCE_WRITTEN",
161
+ trustRole: "EXECUTION_PROVENANCE",
162
+ isPublic: true,
163
+ isPersisted: true,
164
+ description: "Attested command execution provenance recording argv, cwd, resolution, status, and exit code.",
165
+ }),
166
+ });