@workflow-cannon/workspace-kit 0.16.0 → 0.17.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/dist/cli/doctor-planning-issues.d.ts +6 -0
- package/dist/cli/doctor-planning-issues.js +37 -0
- package/dist/cli.js +3 -0
- package/dist/core/config-metadata.js +69 -1
- package/dist/core/workspace-kit-config.js +2 -1
- package/dist/modules/task-engine/doctor-planning-persistence.d.ts +9 -0
- package/dist/modules/task-engine/doctor-planning-persistence.js +77 -0
- package/dist/modules/task-engine/index.js +272 -8
- package/dist/modules/task-engine/planning-config.d.ts +3 -0
- package/dist/modules/task-engine/planning-config.js +7 -0
- package/dist/modules/task-engine/strict-task-validation.d.ts +3 -0
- package/dist/modules/task-engine/strict-task-validation.js +52 -0
- package/dist/modules/task-engine/task-type-validation.d.ts +10 -0
- package/dist/modules/task-engine/task-type-validation.js +26 -0
- package/dist/modules/task-engine/types.d.ts +1 -1
- package/package.json +3 -2
- package/schemas/compatibility-matrix.schema.json +64 -0
- package/schemas/parity-evidence.schema.json +60 -0
- package/schemas/task-engine-run-contracts.schema.json +560 -0
- package/schemas/task-engine-state.schema.json +41 -0
- package/schemas/workspace-kit-profile.schema.json +55 -0
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { validateKnownTaskTypeRequirements } from "./task-type-validation.js";
|
|
2
|
+
const TASK_ID_RE = /^T\d+$/;
|
|
3
|
+
const ALLOWED_STATUS = new Set(["proposed", "ready", "in_progress", "blocked", "completed", "cancelled"]);
|
|
4
|
+
function isStringArray(value) {
|
|
5
|
+
return Array.isArray(value) && value.every((entry) => typeof entry === "string");
|
|
6
|
+
}
|
|
7
|
+
function isIsoDateLike(value) {
|
|
8
|
+
return typeof value === "string" && value.length > 0 && Number.isFinite(Date.parse(value));
|
|
9
|
+
}
|
|
10
|
+
export function validateTaskEntityForStrictMode(task) {
|
|
11
|
+
if (!TASK_ID_RE.test(task.id)) {
|
|
12
|
+
return `task '${task.id}' has invalid id format`;
|
|
13
|
+
}
|
|
14
|
+
if (typeof task.title !== "string" || task.title.trim().length === 0) {
|
|
15
|
+
return `task '${task.id}' has empty title`;
|
|
16
|
+
}
|
|
17
|
+
if (!ALLOWED_STATUS.has(task.status)) {
|
|
18
|
+
return `task '${task.id}' has unsupported status '${String(task.status)}'`;
|
|
19
|
+
}
|
|
20
|
+
if (typeof task.type !== "string" || task.type.trim().length === 0) {
|
|
21
|
+
return `task '${task.id}' has empty type`;
|
|
22
|
+
}
|
|
23
|
+
if (!isIsoDateLike(task.createdAt) || !isIsoDateLike(task.updatedAt)) {
|
|
24
|
+
return `task '${task.id}' has invalid createdAt/updatedAt timestamps`;
|
|
25
|
+
}
|
|
26
|
+
if (task.dependsOn !== undefined && !isStringArray(task.dependsOn)) {
|
|
27
|
+
return `task '${task.id}' has invalid dependsOn values`;
|
|
28
|
+
}
|
|
29
|
+
if (task.unblocks !== undefined && !isStringArray(task.unblocks)) {
|
|
30
|
+
return `task '${task.id}' has invalid unblocks values`;
|
|
31
|
+
}
|
|
32
|
+
if (task.technicalScope !== undefined && !isStringArray(task.technicalScope)) {
|
|
33
|
+
return `task '${task.id}' has invalid technicalScope values`;
|
|
34
|
+
}
|
|
35
|
+
if (task.acceptanceCriteria !== undefined && !isStringArray(task.acceptanceCriteria)) {
|
|
36
|
+
return `task '${task.id}' has invalid acceptanceCriteria values`;
|
|
37
|
+
}
|
|
38
|
+
const knownTypeValidation = validateKnownTaskTypeRequirements(task);
|
|
39
|
+
if (knownTypeValidation) {
|
|
40
|
+
return `task '${task.id}': ${knownTypeValidation.message}`;
|
|
41
|
+
}
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
export function validateTaskSetForStrictMode(tasks) {
|
|
45
|
+
for (const task of tasks) {
|
|
46
|
+
const issue = validateTaskEntityForStrictMode(task);
|
|
47
|
+
if (issue) {
|
|
48
|
+
return issue;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { TaskEntity } from "./types.js";
|
|
2
|
+
export type KnownTaskTypeValidationError = {
|
|
3
|
+
code: "invalid-task-type-requirements";
|
|
4
|
+
message: string;
|
|
5
|
+
};
|
|
6
|
+
/**
|
|
7
|
+
* Optional strictness for known task types.
|
|
8
|
+
* Unknown/custom task types remain passthrough for compatibility.
|
|
9
|
+
*/
|
|
10
|
+
export declare function validateKnownTaskTypeRequirements(task: TaskEntity): KnownTaskTypeValidationError | null;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
function nonEmptyStringArray(value) {
|
|
2
|
+
return Array.isArray(value) && value.some((entry) => typeof entry === "string" && entry.trim().length > 0);
|
|
3
|
+
}
|
|
4
|
+
/**
|
|
5
|
+
* Optional strictness for known task types.
|
|
6
|
+
* Unknown/custom task types remain passthrough for compatibility.
|
|
7
|
+
*/
|
|
8
|
+
export function validateKnownTaskTypeRequirements(task) {
|
|
9
|
+
if (task.type !== "improvement") {
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
const missing = [];
|
|
13
|
+
if (!nonEmptyStringArray(task.acceptanceCriteria)) {
|
|
14
|
+
missing.push("acceptanceCriteria");
|
|
15
|
+
}
|
|
16
|
+
if (!nonEmptyStringArray(task.technicalScope)) {
|
|
17
|
+
missing.push("technicalScope");
|
|
18
|
+
}
|
|
19
|
+
if (missing.length === 0) {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
code: "invalid-task-type-requirements",
|
|
24
|
+
message: `Type '${task.type}' requires non-empty fields: ${missing.join(", ")}`
|
|
25
|
+
};
|
|
26
|
+
}
|
|
@@ -65,7 +65,7 @@ export type TaskEngineError = {
|
|
|
65
65
|
code: TaskEngineErrorCode;
|
|
66
66
|
message: string;
|
|
67
67
|
};
|
|
68
|
-
export type TaskEngineErrorCode = "invalid-transition" | "guard-rejected" | "dependency-unsatisfied" | "task-not-found" | "duplicate-task-id" | "invalid-task-schema" | "invalid-task-update" | "invalid-task-id-format" | "task-archived" | "dependency-cycle" | "duplicate-dependency" | "storage-read-error" | "storage-write-error" | "invalid-adapter" | "import-parse-error";
|
|
68
|
+
export type TaskEngineErrorCode = "invalid-transition" | "guard-rejected" | "dependency-unsatisfied" | "task-not-found" | "duplicate-task-id" | "invalid-task-schema" | "invalid-task-type-requirements" | "invalid-task-update" | "invalid-task-id-format" | "task-archived" | "dependency-cycle" | "duplicate-dependency" | "storage-read-error" | "storage-write-error" | "invalid-adapter" | "import-parse-error";
|
|
69
69
|
export type TaskAdapter = {
|
|
70
70
|
name: string;
|
|
71
71
|
supports: () => TaskAdapterCapability[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@workflow-cannon/workspace-kit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"packageManager": "pnpm@10.0.0",
|
|
6
6
|
"license": "MIT",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"scripts": {
|
|
21
21
|
"build": "tsc -p tsconfig.json",
|
|
22
|
-
"check": "tsc -p tsconfig.json --noEmit",
|
|
22
|
+
"check": "tsc -p tsconfig.json --noEmit && node scripts/check-task-engine-run-contracts.mjs",
|
|
23
23
|
"clean": "rm -rf dist",
|
|
24
24
|
"test": "pnpm run build && node --test test/**/*.test.mjs",
|
|
25
25
|
"pack:dry-run": "pnpm run build && pnpm pack --pack-destination ./artifacts/workspace-kit-pack",
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"files": [
|
|
49
49
|
"dist",
|
|
50
50
|
"src/modules/documentation",
|
|
51
|
+
"schemas",
|
|
51
52
|
"package.json"
|
|
52
53
|
],
|
|
53
54
|
"dependencies": {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://workflow-cannon.dev/schemas/compatibility-matrix.schema.json",
|
|
4
|
+
"title": "Workflow Cannon Compatibility Matrix",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["schemaVersion", "generatedAt", "runtime", "modules", "channels"],
|
|
7
|
+
"properties": {
|
|
8
|
+
"schemaVersion": {
|
|
9
|
+
"type": "integer",
|
|
10
|
+
"minimum": 1
|
|
11
|
+
},
|
|
12
|
+
"generatedAt": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"format": "date-time"
|
|
15
|
+
},
|
|
16
|
+
"runtime": {
|
|
17
|
+
"type": "object",
|
|
18
|
+
"required": ["runtimeVersion", "moduleContractVersion", "configSchema", "policyTraceSchema"],
|
|
19
|
+
"properties": {
|
|
20
|
+
"runtimeVersion": { "type": "string", "minLength": 1 },
|
|
21
|
+
"moduleContractVersion": { "type": "string", "enum": ["1"] },
|
|
22
|
+
"configSchema": { "type": "integer", "minimum": 1 },
|
|
23
|
+
"policyTraceSchema": { "type": "integer", "minimum": 1 }
|
|
24
|
+
},
|
|
25
|
+
"additionalProperties": false
|
|
26
|
+
},
|
|
27
|
+
"modules": {
|
|
28
|
+
"type": "array",
|
|
29
|
+
"minItems": 1,
|
|
30
|
+
"items": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"required": ["id", "version", "contractVersion", "compatibilityLevel", "supportedRuntime", "severity"],
|
|
33
|
+
"properties": {
|
|
34
|
+
"id": { "type": "string", "minLength": 1 },
|
|
35
|
+
"version": { "type": "string", "pattern": "^\\d+\\.\\d+\\.\\d+$" },
|
|
36
|
+
"contractVersion": { "type": "string", "enum": ["1"] },
|
|
37
|
+
"compatibilityLevel": { "type": "string", "enum": ["strict", "compatible", "warn-only"] },
|
|
38
|
+
"supportedRuntime": { "type": "string", "minLength": 1 },
|
|
39
|
+
"severity": { "type": "string", "enum": ["error", "warn"] },
|
|
40
|
+
"notes": { "type": "string" }
|
|
41
|
+
},
|
|
42
|
+
"additionalProperties": false
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"channels": {
|
|
46
|
+
"type": "array",
|
|
47
|
+
"minItems": 3,
|
|
48
|
+
"items": {
|
|
49
|
+
"type": "object",
|
|
50
|
+
"required": ["name", "npmDistTag", "releaseLabel", "tagPrefix", "allowPrerelease"],
|
|
51
|
+
"properties": {
|
|
52
|
+
"name": { "type": "string", "enum": ["canary", "stable", "lts"] },
|
|
53
|
+
"npmDistTag": { "type": "string", "minLength": 1 },
|
|
54
|
+
"releaseLabel": { "type": "string", "minLength": 1 },
|
|
55
|
+
"tagPrefix": { "type": "string", "minLength": 1 },
|
|
56
|
+
"allowPrerelease": { "type": "boolean" },
|
|
57
|
+
"rollback": { "type": "string" }
|
|
58
|
+
},
|
|
59
|
+
"additionalProperties": false
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
},
|
|
63
|
+
"additionalProperties": false
|
|
64
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/NJLaPrell/workflow-cannon/schemas/parity-evidence.schema.json",
|
|
4
|
+
"title": "Parity Evidence",
|
|
5
|
+
"description": "Machine-readable evidence artifact from parity validation runs.",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["schemaVersion", "runner", "timestamp", "overall", "steps"],
|
|
8
|
+
"additionalProperties": false,
|
|
9
|
+
"properties": {
|
|
10
|
+
"schemaVersion": {
|
|
11
|
+
"type": "integer",
|
|
12
|
+
"const": 1,
|
|
13
|
+
"description": "Schema version for forward compatibility."
|
|
14
|
+
},
|
|
15
|
+
"runner": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": "Path to the runner script that produced this evidence."
|
|
18
|
+
},
|
|
19
|
+
"timestamp": {
|
|
20
|
+
"type": "string",
|
|
21
|
+
"format": "date-time",
|
|
22
|
+
"description": "ISO 8601 timestamp of when the run completed."
|
|
23
|
+
},
|
|
24
|
+
"overall": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"enum": ["pass", "fail"],
|
|
27
|
+
"description": "Aggregate outcome of the parity run."
|
|
28
|
+
},
|
|
29
|
+
"steps": {
|
|
30
|
+
"type": "array",
|
|
31
|
+
"minItems": 1,
|
|
32
|
+
"description": "Ordered results for each step in the parity command chain.",
|
|
33
|
+
"items": {
|
|
34
|
+
"type": "object",
|
|
35
|
+
"required": ["name", "status", "durationMs"],
|
|
36
|
+
"additionalProperties": false,
|
|
37
|
+
"properties": {
|
|
38
|
+
"name": {
|
|
39
|
+
"type": "string",
|
|
40
|
+
"description": "Step identifier matching the canonical command chain."
|
|
41
|
+
},
|
|
42
|
+
"status": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"enum": ["pass", "fail"],
|
|
45
|
+
"description": "Outcome for this step."
|
|
46
|
+
},
|
|
47
|
+
"durationMs": {
|
|
48
|
+
"type": "integer",
|
|
49
|
+
"minimum": 0,
|
|
50
|
+
"description": "Wall-clock duration of this step in milliseconds."
|
|
51
|
+
},
|
|
52
|
+
"error": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"description": "First 500 characters of stderr on failure. Absent on pass."
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|