@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.
- package/.cursor/rules/project-loop.mdc +3 -2
- package/.github/copilot-instructions.md +1 -0
- package/AGENTS.md +2 -1
- package/CLAUDE.md +1 -0
- package/DOCS_INDEX.md +36 -0
- package/ENG/design-code-eng.md +31 -0
- package/ENG/documentation-quality-eng.md +400 -0
- package/EXECUTION_STATE.md +23 -13
- package/GUIDE_ROUTER.md +23 -8
- package/LOOP_ENGINEERING.md +77 -12
- package/PROTOCOL_INTEGRATION.md +9 -6
- package/README.md +82 -39
- package/docs/ARTIFACT_REFERENCE.md +442 -0
- package/docs/CLI_REFERENCE.md +892 -0
- package/docs/CROSS_HARNESS_CONTINUITY.md +198 -0
- package/docs/DOCUMENTATION_GUIDE.md +161 -0
- package/docs/GETTING_STARTED.md +348 -0
- package/docs/RECIPES.md +250 -0
- package/docs/TROUBLESHOOTING.md +345 -0
- package/docs/assets/forgeloop-flow.svg +1 -1
- package/docs/forgeloop-flow.mmd +1 -1
- package/package.json +13 -2
- package/schemas/task-descriptor.schema.json +56 -0
- package/schemas/work-state.schema.json +18 -1
- package/scripts/CI_VALIDATORS.md +7 -0
- package/src/cli.js +280 -388
- package/src/commands/advance.js +5 -2
- package/src/commands/audit.js +11 -1
- package/src/commands/clear-continuity.js +5 -2
- package/src/commands/clear-state.js +5 -2
- package/src/commands/complete.js +9 -1
- package/src/commands/continuity.js +5 -2
- package/src/commands/inspect.js +10 -2
- package/src/commands/next.js +5 -2
- package/src/commands/preflight.js +9 -1
- package/src/commands/prepare-completion.js +5 -2
- package/src/commands/reconcile-continuity.js +5 -2
- package/src/commands/record-check.js +7 -1
- package/src/commands/record-continuity.js +21 -14
- package/src/commands/record-terminal-result.js +7 -1
- package/src/commands/route.js +22 -18
- package/src/commands/run-check.js +52 -44
- package/src/commands/status.js +18 -12
- package/src/commands/task-create.js +94 -0
- package/src/commands/task-list.js +48 -0
- package/src/commands/task-migrate.js +34 -0
- package/src/commands/task-scope.js +75 -0
- package/src/commands/task-show.js +81 -0
- package/src/commands/task-unlock.js +35 -0
- package/src/commands/validate-protocol.js +37 -20
- package/src/commands/validate-state.js +24 -18
- package/src/config/guides.json +42 -0
- package/src/core/activation.js +8 -4
- package/src/core/artifact-registry.js +166 -0
- package/src/core/audit.js +65 -12
- package/src/core/bundles.js +76 -50
- package/src/core/cli-command-definitions.js +611 -0
- package/src/core/cli-metadata.js +23 -0
- package/src/core/completion-artifacts.js +161 -74
- package/src/core/completion.js +134 -76
- package/src/core/continuity.js +20 -13
- package/src/core/contract.js +6 -3
- package/src/core/error-codes.js +197 -0
- package/src/core/events.js +19 -14
- package/src/core/execution.js +38 -6
- package/src/core/gate-artifact.js +12 -9
- package/src/core/gates.js +4 -2
- package/src/core/guide-metadata.js +7 -11
- package/src/core/guide-registry.js +29 -0
- package/src/core/inspect.js +7 -4
- package/src/core/native-adapters.js +6 -0
- package/src/core/phase.js +85 -33
- package/src/core/preflight-consistency.js +24 -14
- package/src/core/preflight-loaders.js +16 -11
- package/src/core/preflight.js +44 -25
- package/src/core/protocol.js +2 -11
- package/src/core/receipt.js +1 -1
- package/src/core/report.js +2 -2
- package/src/core/repository.js +46 -12
- package/src/core/resumability.js +6 -4
- package/src/core/route-artifact.js +9 -5
- package/src/core/router.js +11 -7
- package/src/core/schema-validation.js +1 -0
- package/src/core/task-command.js +41 -0
- package/src/core/task-context.js +126 -0
- package/src/core/task-descriptor.js +81 -0
- package/src/core/task-discovery.js +116 -0
- package/src/core/task-identity.js +76 -0
- package/src/core/task-lock.js +209 -0
- package/src/core/task-migration-validation.js +140 -0
- package/src/core/task-migration.js +361 -0
- package/src/core/task-paths.js +96 -0
- package/src/core/task-scope.js +179 -0
- package/src/core/templates.js +3 -9
- package/src/core/work-state.js +24 -13
package/src/commands/advance.js
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { advanceWorkState } from "../core/phase.js";
|
|
2
|
+
import { withTaskMutation } from "../core/task-command.js";
|
|
2
3
|
|
|
3
4
|
export { advanceWorkState };
|
|
4
5
|
|
|
5
|
-
export async function runAdvance({ target, packageRoot, to }) {
|
|
6
|
+
export async function runAdvance({ target, packageRoot, to, taskId, task }) {
|
|
6
7
|
if (!to) throw new Error("--to is required for advance");
|
|
7
|
-
return
|
|
8
|
+
return withTaskMutation(target, { taskId: taskId ?? task, packageRoot }, "advance", async (ctx) => {
|
|
9
|
+
return advanceWorkState(target, to, { packageRoot, taskId: ctx?.taskId ?? null });
|
|
10
|
+
});
|
|
8
11
|
}
|
|
9
12
|
|
|
10
13
|
export function formatAdvanceResult(result) {
|
package/src/commands/audit.js
CHANGED
|
@@ -1,6 +1,16 @@
|
|
|
1
1
|
import { evaluateAudit } from "../core/audit.js";
|
|
2
|
+
import { withResolvedTask } from "../core/task-command.js";
|
|
2
3
|
|
|
3
|
-
export { evaluateAudit
|
|
4
|
+
export { evaluateAudit };
|
|
5
|
+
|
|
6
|
+
export async function runAudit(options = {}) {
|
|
7
|
+
const target = options.target;
|
|
8
|
+
const packageRoot = options.packageRoot;
|
|
9
|
+
const taskId = options.taskId ?? options.task ?? null;
|
|
10
|
+
return withResolvedTask(target, { taskId, packageRoot }, async (ctx) => {
|
|
11
|
+
return evaluateAudit({ ...options, taskId: ctx?.taskId ?? null });
|
|
12
|
+
});
|
|
13
|
+
}
|
|
4
14
|
|
|
5
15
|
export function formatAuditResult(result) {
|
|
6
16
|
const lines = [`FORGELOOP AUDIT: ${result.status}`];
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { clearContinuity } from "../core/continuity.js";
|
|
2
|
+
import { withTaskMutation } from "../core/task-command.js";
|
|
2
3
|
|
|
3
|
-
export async function runClearContinuity({ target } = {}) {
|
|
4
|
-
return
|
|
4
|
+
export async function runClearContinuity({ target, taskId, task, packageRoot } = {}) {
|
|
5
|
+
return withTaskMutation(target, { taskId: taskId ?? task, packageRoot }, "clear-continuity", async (ctx) => {
|
|
6
|
+
return clearContinuity(target, { taskId: ctx?.taskId ?? null });
|
|
7
|
+
});
|
|
5
8
|
}
|
|
6
9
|
|
|
7
10
|
export function formatClearContinuityResult(result) {
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { clearWorkState } from "../core/work-state.js";
|
|
2
|
+
import { withTaskMutation } from "../core/task-command.js";
|
|
2
3
|
|
|
3
4
|
export { clearWorkState };
|
|
4
5
|
|
|
5
|
-
export async function runClearState({ target }) {
|
|
6
|
-
return
|
|
6
|
+
export async function runClearState({ target, taskId, task, packageRoot } = {}) {
|
|
7
|
+
return withTaskMutation(target, { taskId: taskId ?? task, packageRoot }, "clear-state", async (ctx) => {
|
|
8
|
+
return clearWorkState(target, { taskId: ctx?.taskId ?? null });
|
|
9
|
+
});
|
|
7
10
|
}
|
|
8
11
|
|
|
9
12
|
export function formatClearStateResult(result) {
|
package/src/commands/complete.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { runComplete as evaluateAndComplete } from "../core/completion.js";
|
|
2
|
+
import { withTaskMutation } from "../core/task-command.js";
|
|
2
3
|
|
|
3
|
-
export
|
|
4
|
+
export async function runComplete(options = {}) {
|
|
5
|
+
const target = options.target;
|
|
6
|
+
const packageRoot = options.packageRoot;
|
|
7
|
+
const taskId = options.taskId ?? options.task ?? null;
|
|
8
|
+
return withTaskMutation(target, { taskId, packageRoot }, "complete", async (ctx) => {
|
|
9
|
+
return evaluateAndComplete({ ...options, taskId: ctx?.taskId ?? null });
|
|
10
|
+
});
|
|
11
|
+
}
|
|
4
12
|
|
|
5
13
|
export function formatCompleteResult(result) {
|
|
6
14
|
const lines = [`FORGELOOP COMPLETE: ${result.status}`];
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { reconcileContinuity } from "../core/continuity-reconciliation.js";
|
|
2
|
+
import { withResolvedTask } from "../core/task-command.js";
|
|
2
3
|
|
|
3
|
-
export async function runContinuity({ target, packageRoot } = {}) {
|
|
4
|
-
return
|
|
4
|
+
export async function runContinuity({ target, packageRoot, taskId, task } = {}) {
|
|
5
|
+
return withResolvedTask(target, { taskId: taskId ?? task, packageRoot }, async (ctx) => {
|
|
6
|
+
return reconcileContinuity({ target, packageRoot, taskId: ctx?.taskId ?? null });
|
|
7
|
+
});
|
|
5
8
|
}
|
|
6
9
|
|
|
7
10
|
export function formatContinuityResult(result) {
|
package/src/commands/inspect.js
CHANGED
|
@@ -1,6 +1,14 @@
|
|
|
1
|
-
import { inspectTarget } from "../core/inspect.js";
|
|
1
|
+
import { inspectTarget as coreInspectTarget } from "../core/inspect.js";
|
|
2
|
+
import { withResolvedTask } from "../core/task-command.js";
|
|
2
3
|
|
|
3
|
-
export
|
|
4
|
+
export async function inspectTarget(options = {}) {
|
|
5
|
+
const target = options.target;
|
|
6
|
+
const packageRoot = options.packageRoot;
|
|
7
|
+
const taskId = options.taskId ?? options.task ?? null;
|
|
8
|
+
return withResolvedTask(target, { taskId, packageRoot }, async (ctx) => {
|
|
9
|
+
return coreInspectTarget({ ...options, taskId: ctx?.taskId ?? null });
|
|
10
|
+
});
|
|
11
|
+
}
|
|
4
12
|
|
|
5
13
|
export function formatInspectResult(report) {
|
|
6
14
|
const lines = [
|
package/src/commands/next.js
CHANGED
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { getNextAction } from "../core/next-action.js";
|
|
2
|
+
import { withResolvedTask } from "../core/task-command.js";
|
|
2
3
|
|
|
3
|
-
export async function runNext({ target, packageRoot }) {
|
|
4
|
-
return
|
|
4
|
+
export async function runNext({ target, packageRoot, taskId, task }) {
|
|
5
|
+
return withResolvedTask(target, { taskId: taskId ?? task, packageRoot }, async (ctx) => {
|
|
6
|
+
return getNextAction({ target, packageRoot, taskId: ctx?.taskId ?? null });
|
|
7
|
+
});
|
|
5
8
|
}
|
|
6
9
|
|
|
7
10
|
export function formatNextActionResult(result) {
|
|
@@ -1,6 +1,14 @@
|
|
|
1
1
|
import { runPreflight as evaluateAndPersistPreflight } from "../core/preflight.js";
|
|
2
|
+
import { withTaskMutation } from "../core/task-command.js";
|
|
2
3
|
|
|
3
|
-
export
|
|
4
|
+
export async function runPreflight(options = {}) {
|
|
5
|
+
const target = options.target;
|
|
6
|
+
const packageRoot = options.packageRoot;
|
|
7
|
+
const taskId = options.taskId ?? options.task ?? null;
|
|
8
|
+
return withTaskMutation(target, { taskId, packageRoot }, "preflight", async (ctx) => {
|
|
9
|
+
return evaluateAndPersistPreflight({ ...options, taskId: ctx?.taskId ?? null });
|
|
10
|
+
});
|
|
11
|
+
}
|
|
4
12
|
|
|
5
13
|
export function formatPreflightResult(result) {
|
|
6
14
|
const lines = [`FORGELOOP PREFLIGHT: ${result.status}`];
|
|
@@ -1,9 +1,12 @@
|
|
|
1
1
|
import { prepareCompletion as prepareCompletionArtifacts } from "../core/completion-artifacts.js";
|
|
2
|
+
import { withTaskMutation } from "../core/task-command.js";
|
|
2
3
|
|
|
3
4
|
export { prepareCompletionArtifacts as prepareCompletion };
|
|
4
5
|
|
|
5
|
-
export async function runPrepareCompletion({ target, packageRoot, authorityContext, runtimeContext }) {
|
|
6
|
-
return
|
|
6
|
+
export async function runPrepareCompletion({ target, packageRoot, authorityContext, runtimeContext, taskId, task, ...rest } = {}) {
|
|
7
|
+
return withTaskMutation(target, { taskId: taskId ?? task, packageRoot }, "prepare-completion", async (ctx) => {
|
|
8
|
+
return prepareCompletionArtifacts({ target, packageRoot, authorityContext, runtimeContext, taskId: ctx?.taskId ?? null, ...rest });
|
|
9
|
+
});
|
|
7
10
|
}
|
|
8
11
|
|
|
9
12
|
export function formatPrepareCompletionResult(result) {
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { reconcileContinuity } from "../core/continuity-reconciliation.js";
|
|
2
|
+
import { withResolvedTask } from "../core/task-command.js";
|
|
2
3
|
|
|
3
|
-
export async function runReconcileContinuity({ target, packageRoot } = {}) {
|
|
4
|
-
return
|
|
4
|
+
export async function runReconcileContinuity({ target, packageRoot, taskId, task } = {}) {
|
|
5
|
+
return withResolvedTask(target, { taskId: taskId ?? task, packageRoot }, async (ctx) => {
|
|
6
|
+
return reconcileContinuity({ target, packageRoot, taskId: ctx?.taskId ?? null });
|
|
7
|
+
});
|
|
5
8
|
}
|
|
6
9
|
|
|
7
10
|
export function formatReconcileContinuityResult(result) {
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { recordCheck as recordCheckArtifact } from "../core/completion-artifacts.js";
|
|
2
|
+
import { withTaskMutation } from "../core/task-command.js";
|
|
2
3
|
|
|
3
4
|
export { recordCheckArtifact as recordCheck };
|
|
4
5
|
|
|
5
6
|
export async function runRecordCheck(input) {
|
|
6
|
-
|
|
7
|
+
const target = input.target;
|
|
8
|
+
const packageRoot = input.packageRoot;
|
|
9
|
+
const taskId = input.taskId ?? input.task ?? null;
|
|
10
|
+
return withTaskMutation(target, { taskId, packageRoot }, "record-check", async (ctx) => {
|
|
11
|
+
return recordCheckArtifact({ ...input, taskId: ctx?.taskId ?? null });
|
|
12
|
+
});
|
|
7
13
|
}
|
|
8
14
|
|
|
9
15
|
export function formatRecordCheckResult(result) {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { writeContinuity } from "../core/continuity.js";
|
|
2
|
+
import { withTaskMutation } from "../core/task-command.js";
|
|
2
3
|
|
|
3
4
|
function parseWorkItem(value, label) {
|
|
4
5
|
if (typeof value !== "string") throw new Error(`${label} must use <id>:<summary>`);
|
|
@@ -27,24 +28,30 @@ export async function runRecordContinuity({
|
|
|
27
28
|
repositoryFingerprint,
|
|
28
29
|
now,
|
|
29
30
|
dryRun = false,
|
|
31
|
+
taskId,
|
|
32
|
+
task,
|
|
30
33
|
} = {}) {
|
|
31
34
|
if ((focusId && !focusSummary) || (!focusId && focusSummary)) {
|
|
32
35
|
throw new Error("record-continuity requires --focus-id and --focus-summary together");
|
|
33
36
|
}
|
|
34
|
-
return
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
37
|
+
return withTaskMutation(target, { taskId: taskId ?? task, packageRoot }, "record-continuity", async (ctx) => {
|
|
38
|
+
const effectiveTaskId = ctx?.taskId ?? null;
|
|
39
|
+
return writeContinuity(target, {
|
|
40
|
+
...(focusId ? { currentFocus: { id: focusId, summary: focusSummary } } : {}),
|
|
41
|
+
remainingWork: remaining.map((item) => parseWorkItem(item, "--remaining")),
|
|
42
|
+
knownIssues: knownIssues.map((item) => parseWorkItem(item, "--known-issue")),
|
|
43
|
+
changedAreas,
|
|
44
|
+
inspectFirst,
|
|
45
|
+
...(resumeNote ? { resumeNote } : {}),
|
|
46
|
+
}, {
|
|
47
|
+
packageRoot,
|
|
48
|
+
...(state ? { state } : {}),
|
|
49
|
+
...(contract ? { contract } : {}),
|
|
50
|
+
...(repositoryFingerprint ? { repositoryFingerprint } : {}),
|
|
51
|
+
...(now ? { now } : {}),
|
|
52
|
+
dryRun,
|
|
53
|
+
taskId: effectiveTaskId,
|
|
54
|
+
});
|
|
48
55
|
});
|
|
49
56
|
}
|
|
50
57
|
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { recordTerminalResult as recordTerminalResultArtifact } from "../core/completion-artifacts.js";
|
|
2
|
+
import { withTaskMutation } from "../core/task-command.js";
|
|
2
3
|
|
|
3
4
|
export { recordTerminalResultArtifact as recordTerminalResult };
|
|
4
5
|
|
|
5
6
|
export async function runRecordTerminalResult(input) {
|
|
6
|
-
|
|
7
|
+
const target = input.target;
|
|
8
|
+
const packageRoot = input.packageRoot;
|
|
9
|
+
const taskId = input.taskId ?? input.task ?? null;
|
|
10
|
+
return withTaskMutation(target, { taskId, packageRoot }, "record-terminal-result", async (ctx) => {
|
|
11
|
+
return recordTerminalResultArtifact({ ...input, taskId: ctx?.taskId ?? null });
|
|
12
|
+
});
|
|
7
13
|
}
|
|
8
14
|
|
|
9
15
|
export function formatRecordTerminalResult(result) {
|
package/src/commands/route.js
CHANGED
|
@@ -1,26 +1,30 @@
|
|
|
1
1
|
import { evaluateRoute } from "../core/router.js";
|
|
2
2
|
import { persistRoute } from "../core/route-artifact.js";
|
|
3
3
|
import { readContract } from "../core/contract.js";
|
|
4
|
+
import { withTaskMutation } from "../core/task-command.js";
|
|
4
5
|
|
|
5
|
-
export async function runRoute({ target, packageRoot, workType, surfaces, risks, platforms, behaviorChange, executableChange }) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
contractFingerprint
|
|
18
|
-
|
|
19
|
-
|
|
6
|
+
export async function runRoute({ target, packageRoot, workType, surfaces, risks, platforms, behaviorChange, executableChange, taskId, task }) {
|
|
7
|
+
return withTaskMutation(target, { taskId: taskId ?? task, packageRoot }, "route", async (ctx) => {
|
|
8
|
+
const effectiveTaskId = ctx?.taskId ?? null;
|
|
9
|
+
const route = evaluateRoute({
|
|
10
|
+
workType,
|
|
11
|
+
surfaces,
|
|
12
|
+
risks,
|
|
13
|
+
platforms,
|
|
14
|
+
behaviorChange,
|
|
15
|
+
executableChange,
|
|
16
|
+
});
|
|
17
|
+
if (target && packageRoot) {
|
|
18
|
+
let contractFingerprint;
|
|
19
|
+
try {
|
|
20
|
+
contractFingerprint = (await readContract(target, packageRoot, { taskId: effectiveTaskId })).fingerprint;
|
|
21
|
+
} catch {
|
|
22
|
+
contractFingerprint = undefined;
|
|
23
|
+
}
|
|
24
|
+
await persistRoute(target, route, packageRoot, { contractFingerprint, taskId: effectiveTaskId });
|
|
20
25
|
}
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
return route;
|
|
26
|
+
return route;
|
|
27
|
+
});
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
export function formatRouteResult(result) {
|
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
recordCheck as recordCheckArtifact,
|
|
4
4
|
} from "../core/completion-artifacts.js";
|
|
5
5
|
import { runCommandExecution } from "../core/execution.js";
|
|
6
|
+
import { withTaskMutation } from "../core/task-command.js";
|
|
6
7
|
|
|
7
8
|
export async function runCheck({
|
|
8
9
|
target,
|
|
@@ -13,57 +14,64 @@ export async function runCheck({
|
|
|
13
14
|
details,
|
|
14
15
|
authorityContext,
|
|
15
16
|
runtimeContext,
|
|
17
|
+
taskId,
|
|
18
|
+
task,
|
|
16
19
|
}) {
|
|
17
20
|
if (typeof id !== "string" || id.trim() === "" || typeof requirement !== "string" || requirement.trim() === "") {
|
|
18
21
|
const error = new Error("run-check requires non-empty id and requirement");
|
|
19
22
|
error.code = "E_CHECK_INVALID";
|
|
20
23
|
throw error;
|
|
21
24
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
return withTaskMutation(target, { taskId: taskId ?? task, packageRoot }, "run-check", async (ctx) => {
|
|
26
|
+
const effectiveTaskId = ctx?.taskId ?? null;
|
|
27
|
+
const ready = await assertRecordCheckPrerequisites({
|
|
28
|
+
target,
|
|
29
|
+
packageRoot,
|
|
30
|
+
requirement,
|
|
31
|
+
status: "passed",
|
|
32
|
+
evidenceKind: "OBSERVED",
|
|
33
|
+
authorityContext,
|
|
34
|
+
runtimeContext,
|
|
35
|
+
taskId: effectiveTaskId,
|
|
36
|
+
});
|
|
37
|
+
const verificationCycle = ready.state.verificationCycle ?? 1;
|
|
38
|
+
const execution = await runCommandExecution({
|
|
39
|
+
target,
|
|
40
|
+
packageRoot,
|
|
41
|
+
taskId: ready.contract.value.taskId,
|
|
42
|
+
checkId: id,
|
|
43
|
+
requirement,
|
|
44
|
+
verificationCycle,
|
|
45
|
+
argv,
|
|
46
|
+
details,
|
|
47
|
+
authorityContext,
|
|
48
|
+
runtimeContext,
|
|
49
|
+
});
|
|
50
|
+
const status = execution.execution.status === "passed" ? "passed" : "failed";
|
|
51
|
+
const recorded = await recordCheckArtifact({
|
|
52
|
+
target,
|
|
53
|
+
packageRoot,
|
|
54
|
+
id,
|
|
55
|
+
kind: "command",
|
|
56
|
+
requirement,
|
|
57
|
+
status,
|
|
58
|
+
evidenceKind: "OBSERVED",
|
|
59
|
+
command: execution.execution.argv.join(" "),
|
|
60
|
+
result: execution.result,
|
|
61
|
+
...(execution.execution.exitCode === null ? {} : { exitCode: execution.execution.exitCode }),
|
|
62
|
+
details,
|
|
63
|
+
executionRef: execution.execution.executionId,
|
|
64
|
+
provenance: "FORGELOOP_EXECUTED",
|
|
65
|
+
authorityContext,
|
|
66
|
+
runtimeContext,
|
|
67
|
+
taskId: effectiveTaskId,
|
|
68
|
+
});
|
|
69
|
+
return {
|
|
70
|
+
...recorded,
|
|
71
|
+
execution: execution.execution,
|
|
72
|
+
executionPath: execution.path,
|
|
73
|
+
};
|
|
30
74
|
});
|
|
31
|
-
const verificationCycle = ready.state.verificationCycle ?? 1;
|
|
32
|
-
const execution = await runCommandExecution({
|
|
33
|
-
target,
|
|
34
|
-
packageRoot,
|
|
35
|
-
taskId: ready.contract.value.taskId,
|
|
36
|
-
checkId: id,
|
|
37
|
-
requirement,
|
|
38
|
-
verificationCycle,
|
|
39
|
-
argv,
|
|
40
|
-
details,
|
|
41
|
-
authorityContext,
|
|
42
|
-
runtimeContext,
|
|
43
|
-
});
|
|
44
|
-
const status = execution.execution.status === "passed" ? "passed" : "failed";
|
|
45
|
-
const recorded = await recordCheckArtifact({
|
|
46
|
-
target,
|
|
47
|
-
packageRoot,
|
|
48
|
-
id,
|
|
49
|
-
kind: "command",
|
|
50
|
-
requirement,
|
|
51
|
-
status,
|
|
52
|
-
evidenceKind: "OBSERVED",
|
|
53
|
-
command: execution.execution.argv.join(" "),
|
|
54
|
-
result: execution.result,
|
|
55
|
-
...(execution.execution.exitCode === null ? {} : { exitCode: execution.execution.exitCode }),
|
|
56
|
-
details,
|
|
57
|
-
executionRef: execution.execution.executionId,
|
|
58
|
-
provenance: "FORGELOOP_EXECUTED",
|
|
59
|
-
authorityContext,
|
|
60
|
-
runtimeContext,
|
|
61
|
-
});
|
|
62
|
-
return {
|
|
63
|
-
...recorded,
|
|
64
|
-
execution: execution.execution,
|
|
65
|
-
executionPath: execution.path,
|
|
66
|
-
};
|
|
67
75
|
}
|
|
68
76
|
|
|
69
77
|
export function formatRunCheckResult(result) {
|
package/src/commands/status.js
CHANGED
|
@@ -1,19 +1,25 @@
|
|
|
1
1
|
import { readAndClassifyWorkState } from "../core/work-state.js";
|
|
2
2
|
import { inspectSchemaHealth } from "../core/schema-validation.js";
|
|
3
3
|
import { reconcileContinuity } from "../core/continuity-reconciliation.js";
|
|
4
|
+
import { withResolvedTask } from "../core/task-command.js";
|
|
4
5
|
|
|
5
|
-
export async function runStatus({ target, packageRoot, contractFile = null }) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
6
|
+
export async function runStatus({ target, packageRoot, contractFile = null, taskId, task } = {}) {
|
|
7
|
+
return withResolvedTask(target, { taskId: taskId ?? task, packageRoot }, async (ctx) => {
|
|
8
|
+
const effectiveTaskId = ctx?.taskId ?? null;
|
|
9
|
+
const state = await readAndClassifyWorkState({ target, packageRoot, contractFile, taskId: effectiveTaskId });
|
|
10
|
+
const [protocol, continuity] = await Promise.all([
|
|
11
|
+
inspectSchemaHealth(target),
|
|
12
|
+
reconcileContinuity({ target, packageRoot, taskId: effectiveTaskId }),
|
|
13
|
+
]);
|
|
14
|
+
return {
|
|
15
|
+
...state,
|
|
16
|
+
taskId: effectiveTaskId,
|
|
17
|
+
taskKey: ctx?.taskKey ?? null,
|
|
18
|
+
protocol,
|
|
19
|
+
continuity,
|
|
20
|
+
evidence: [...(state.evidence ?? []), ...(protocol.evidence ?? [])],
|
|
21
|
+
};
|
|
22
|
+
});
|
|
17
23
|
}
|
|
18
24
|
|
|
19
25
|
export function formatStatusResult(result) {
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { assertTaskId } from "../core/task-identity.js";
|
|
3
|
+
import { createTaskDescriptor, writeTaskDescriptor } from "../core/task-descriptor.js";
|
|
4
|
+
import { normalizeWriteClaims, assertNoScopeConflicts, assertScopeClean } from "../core/task-scope.js";
|
|
5
|
+
import { discoverTasks, findTaskById } from "../core/task-discovery.js";
|
|
6
|
+
import { withProjectClaimsLock, withTaskLock } from "../core/task-lock.js";
|
|
7
|
+
import { taskDirectory } from "../core/task-paths.js";
|
|
8
|
+
import { ensureWithin, fileExists } from "../core/filesystem.js";
|
|
9
|
+
import { validateContract, writeContract } from "../core/contract.js";
|
|
10
|
+
import { appendProtocolEvent } from "../core/events.js";
|
|
11
|
+
import { E_TASK_REQUIRED, E_TASK_ALREADY_EXISTS, E_TASK_DESCRIPTOR_INVALID } from "../core/error-codes.js";
|
|
12
|
+
|
|
13
|
+
function taskError(code, message, artifacts = []) {
|
|
14
|
+
const error = new Error(message);
|
|
15
|
+
error.code = code;
|
|
16
|
+
error.artifacts = artifacts;
|
|
17
|
+
return error;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function runTaskCreate({ target, packageRoot, taskId, claims = [], contractFile = null } = {}) {
|
|
21
|
+
if (!taskId) {
|
|
22
|
+
throw taskError(E_TASK_REQUIRED, "--task is required for task-create");
|
|
23
|
+
}
|
|
24
|
+
assertTaskId(taskId);
|
|
25
|
+
|
|
26
|
+
const existing = await findTaskById(target, taskId, packageRoot);
|
|
27
|
+
if (existing) {
|
|
28
|
+
throw taskError(E_TASK_ALREADY_EXISTS, `Task already exists: ${taskId}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const normalizedClaims = normalizeWriteClaims(claims ?? []);
|
|
32
|
+
|
|
33
|
+
return withProjectClaimsLock(target, async () => {
|
|
34
|
+
const allTasks = await discoverTasks(target, packageRoot);
|
|
35
|
+
assertNoScopeConflicts(normalizedClaims, allTasks, taskId);
|
|
36
|
+
if (normalizedClaims.length > 0) {
|
|
37
|
+
await assertScopeClean(target, normalizedClaims);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return withTaskLock(target, taskId, "task-create", async () => {
|
|
41
|
+
const descriptor = createTaskDescriptor({
|
|
42
|
+
taskId,
|
|
43
|
+
writeClaims: normalizedClaims,
|
|
44
|
+
});
|
|
45
|
+
const written = await writeTaskDescriptor(target, descriptor, packageRoot);
|
|
46
|
+
|
|
47
|
+
let contractCopied = false;
|
|
48
|
+
if (contractFile) {
|
|
49
|
+
const sourcePath = ensureWithin(target, contractFile);
|
|
50
|
+
if (!(await fileExists(sourcePath))) {
|
|
51
|
+
throw taskError("E_CONTRACT_MISSING", `Specified contract file not found: ${contractFile}`);
|
|
52
|
+
}
|
|
53
|
+
const raw = await readFile(sourcePath, "utf8");
|
|
54
|
+
let parsed;
|
|
55
|
+
try {
|
|
56
|
+
parsed = JSON.parse(raw);
|
|
57
|
+
} catch {
|
|
58
|
+
throw taskError("E_CONTRACT_INVALID", `Specified contract file is not valid JSON: ${contractFile}`);
|
|
59
|
+
}
|
|
60
|
+
await validateContract(parsed, packageRoot);
|
|
61
|
+
if (parsed.taskId && parsed.taskId !== taskId) {
|
|
62
|
+
throw taskError(
|
|
63
|
+
E_TASK_DESCRIPTOR_INVALID,
|
|
64
|
+
`Contract taskId "${parsed.taskId}" does not match requested taskId "${taskId}"`,
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
parsed.taskId = taskId;
|
|
68
|
+
await writeContract(target, parsed, packageRoot, { taskId });
|
|
69
|
+
contractCopied = true;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
await appendProtocolEvent(target, {
|
|
73
|
+
taskId,
|
|
74
|
+
event: "TASK_RECEIVED",
|
|
75
|
+
details: { createdAt: descriptor.createdAt },
|
|
76
|
+
}, packageRoot, { taskId });
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
taskId: descriptor.taskId,
|
|
80
|
+
taskKey: descriptor.taskKey,
|
|
81
|
+
directory: taskDirectory(taskId),
|
|
82
|
+
descriptorPath: written.path,
|
|
83
|
+
writeClaims: descriptor.writeClaims,
|
|
84
|
+
contractCopied,
|
|
85
|
+
createdAt: descriptor.createdAt,
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function formatTaskCreateResult(result) {
|
|
92
|
+
const claims = result.writeClaims.length === 0 ? "none" : result.writeClaims.join(", ");
|
|
93
|
+
return `created task: ${result.taskId}\nkey: ${result.taskKey}\ndirectory: ${result.directory}\nclaims: ${claims}\n`;
|
|
94
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { discoverTasks } from "../core/task-discovery.js";
|
|
2
|
+
|
|
3
|
+
export async function runTaskList({ target, packageRoot } = {}) {
|
|
4
|
+
const tasks = await discoverTasks(target, packageRoot);
|
|
5
|
+
return {
|
|
6
|
+
tasks: tasks.map((task) => {
|
|
7
|
+
if (task.healthy === false) {
|
|
8
|
+
return {
|
|
9
|
+
taskId: task.taskId ?? null,
|
|
10
|
+
taskKey: task.taskKey,
|
|
11
|
+
directory: task.directory,
|
|
12
|
+
healthy: false,
|
|
13
|
+
error: task.error,
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
taskId: task.taskId,
|
|
18
|
+
taskKey: task.taskKey,
|
|
19
|
+
directory: task.directory,
|
|
20
|
+
healthy: true,
|
|
21
|
+
phase: task.phase,
|
|
22
|
+
writeClaims: task.writeClaims ?? [],
|
|
23
|
+
locked: task.locked,
|
|
24
|
+
hasContinuity: task.hasContinuity,
|
|
25
|
+
hasReceipt: task.hasReceipt,
|
|
26
|
+
createdAt: task.createdAt,
|
|
27
|
+
updatedAt: task.updatedAt,
|
|
28
|
+
};
|
|
29
|
+
}),
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function formatTaskListResult(result) {
|
|
34
|
+
if (result.tasks.length === 0) {
|
|
35
|
+
return "no tasks found\n";
|
|
36
|
+
}
|
|
37
|
+
const lines = ["Tasks:"];
|
|
38
|
+
for (const task of result.tasks) {
|
|
39
|
+
if (task.healthy === false) {
|
|
40
|
+
lines.push(`- ${task.taskKey} [CORRUPT]: ${task.error?.message ?? "unhealthy task namespace"}`);
|
|
41
|
+
} else {
|
|
42
|
+
const lockStr = task.locked ? " [LOCKED]" : "";
|
|
43
|
+
const claimsStr = task.writeClaims.length > 0 ? ` (claims: ${task.writeClaims.join(", ")})` : "";
|
|
44
|
+
lines.push(`- ${task.taskId}: ${task.phase ?? "UNINITIALIZED"}${lockStr}${claimsStr}`);
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return `${lines.join("\n")}\n`;
|
|
48
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { migrateLegacyLayout } from "../core/task-migration.js";
|
|
2
|
+
|
|
3
|
+
export async function runTaskMigrate({ target, packageRoot, dryRun = false } = {}) {
|
|
4
|
+
const result = await migrateLegacyLayout(target, { packageRoot, dryRun });
|
|
5
|
+
return result;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export function formatTaskMigrateResult(result) {
|
|
9
|
+
if (!result.migrated) {
|
|
10
|
+
if (result.dryRun) {
|
|
11
|
+
const lines = [
|
|
12
|
+
`[dry-run] task: ${result.taskId}`,
|
|
13
|
+
`key: ${result.taskKey}`,
|
|
14
|
+
`destination: ${result.targetDirectory}`,
|
|
15
|
+
"legacy artifacts:",
|
|
16
|
+
...(result.legacyFiles ?? []).map((artifact) => ` - ${artifact}`),
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
return `${lines.join("\n")}\n`;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return `${result.message ?? "no legacy layout detected; nothing to migrate"}\n`;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const lines = [
|
|
26
|
+
`migrated task: ${result.taskId}`,
|
|
27
|
+
`key: ${result.taskKey}`,
|
|
28
|
+
`destination: ${result.targetDirectory}`,
|
|
29
|
+
"migrated artifacts:",
|
|
30
|
+
...(result.migratedArtifacts ?? []).map((artifact) => ` - ${artifact}`),
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
return `${lines.join("\n")}\n`;
|
|
34
|
+
}
|