@cleocode/mcp-server 0.86.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/LICENSE +21 -0
- package/README.md +306 -0
- package/dist/domains/index.d.ts +16 -0
- package/dist/domains/index.d.ts.map +1 -0
- package/dist/domains/index.js +16 -0
- package/dist/domains/index.js.map +1 -0
- package/dist/domains/lifecycle.d.ts +147 -0
- package/dist/domains/lifecycle.d.ts.map +1 -0
- package/dist/domains/lifecycle.js +452 -0
- package/dist/domains/lifecycle.js.map +1 -0
- package/dist/domains/orchestrate.d.ts +133 -0
- package/dist/domains/orchestrate.d.ts.map +1 -0
- package/dist/domains/orchestrate.js +465 -0
- package/dist/domains/orchestrate.js.map +1 -0
- package/dist/domains/release.d.ts +109 -0
- package/dist/domains/release.d.ts.map +1 -0
- package/dist/domains/release.js +400 -0
- package/dist/domains/release.js.map +1 -0
- package/dist/domains/research.d.ts +139 -0
- package/dist/domains/research.d.ts.map +1 -0
- package/dist/domains/research.js +606 -0
- package/dist/domains/research.js.map +1 -0
- package/dist/domains/session.d.ts +129 -0
- package/dist/domains/session.d.ts.map +1 -0
- package/dist/domains/session.js +433 -0
- package/dist/domains/session.js.map +1 -0
- package/dist/domains/system.d.ts +92 -0
- package/dist/domains/system.d.ts.map +1 -0
- package/dist/domains/system.js +473 -0
- package/dist/domains/system.js.map +1 -0
- package/dist/domains/tasks.d.ts +180 -0
- package/dist/domains/tasks.d.ts.map +1 -0
- package/dist/domains/tasks.js +704 -0
- package/dist/domains/tasks.js.map +1 -0
- package/dist/domains/validate.d.ts +150 -0
- package/dist/domains/validate.d.ts.map +1 -0
- package/dist/domains/validate.js +568 -0
- package/dist/domains/validate.js.map +1 -0
- package/dist/gateways/mutate.d.ts +100 -0
- package/dist/gateways/mutate.d.ts.map +1 -0
- package/dist/gateways/mutate.js +937 -0
- package/dist/gateways/mutate.js.map +1 -0
- package/dist/gateways/query.d.ts +91 -0
- package/dist/gateways/query.d.ts.map +1 -0
- package/dist/gateways/query.js +245 -0
- package/dist/gateways/query.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +299 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/audit.d.ts +118 -0
- package/dist/lib/audit.d.ts.map +1 -0
- package/dist/lib/audit.js +311 -0
- package/dist/lib/audit.js.map +1 -0
- package/dist/lib/background-jobs.d.ts +86 -0
- package/dist/lib/background-jobs.d.ts.map +1 -0
- package/dist/lib/background-jobs.js +183 -0
- package/dist/lib/background-jobs.js.map +1 -0
- package/dist/lib/cache.d.ts +78 -0
- package/dist/lib/cache.d.ts.map +1 -0
- package/dist/lib/cache.js +204 -0
- package/dist/lib/cache.js.map +1 -0
- package/dist/lib/command-builder.d.ts +52 -0
- package/dist/lib/command-builder.d.ts.map +1 -0
- package/dist/lib/command-builder.js +280 -0
- package/dist/lib/command-builder.js.map +1 -0
- package/dist/lib/config.d.ts +42 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +248 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/defaults.d.ts +115 -0
- package/dist/lib/defaults.d.ts.map +1 -0
- package/dist/lib/defaults.js +61 -0
- package/dist/lib/defaults.js.map +1 -0
- package/dist/lib/error-handler.d.ts +101 -0
- package/dist/lib/error-handler.d.ts.map +1 -0
- package/dist/lib/error-handler.js +277 -0
- package/dist/lib/error-handler.js.map +1 -0
- package/dist/lib/executor.d.ts +110 -0
- package/dist/lib/executor.d.ts.map +1 -0
- package/dist/lib/executor.js +362 -0
- package/dist/lib/executor.js.map +1 -0
- package/dist/lib/exit-codes.d.ts +190 -0
- package/dist/lib/exit-codes.d.ts.map +1 -0
- package/dist/lib/exit-codes.js +1027 -0
- package/dist/lib/exit-codes.js.map +1 -0
- package/dist/lib/formatter.d.ts +196 -0
- package/dist/lib/formatter.d.ts.map +1 -0
- package/dist/lib/formatter.js +260 -0
- package/dist/lib/formatter.js.map +1 -0
- package/dist/lib/gate-validators.d.ts +103 -0
- package/dist/lib/gate-validators.d.ts.map +1 -0
- package/dist/lib/gate-validators.js +689 -0
- package/dist/lib/gate-validators.js.map +1 -0
- package/dist/lib/manifest-parser.d.ts +61 -0
- package/dist/lib/manifest-parser.d.ts.map +1 -0
- package/dist/lib/manifest-parser.js +338 -0
- package/dist/lib/manifest-parser.js.map +1 -0
- package/dist/lib/manifest.d.ts +177 -0
- package/dist/lib/manifest.d.ts.map +1 -0
- package/dist/lib/manifest.js +301 -0
- package/dist/lib/manifest.js.map +1 -0
- package/dist/lib/protocol-enforcement.d.ts +105 -0
- package/dist/lib/protocol-enforcement.d.ts.map +1 -0
- package/dist/lib/protocol-enforcement.js +331 -0
- package/dist/lib/protocol-enforcement.js.map +1 -0
- package/dist/lib/protocol-rules.d.ts +55 -0
- package/dist/lib/protocol-rules.d.ts.map +1 -0
- package/dist/lib/protocol-rules.js +760 -0
- package/dist/lib/protocol-rules.js.map +1 -0
- package/dist/lib/rate-limiter.d.ts +110 -0
- package/dist/lib/rate-limiter.d.ts.map +1 -0
- package/dist/lib/rate-limiter.js +208 -0
- package/dist/lib/rate-limiter.js.map +1 -0
- package/dist/lib/router.d.ts +126 -0
- package/dist/lib/router.d.ts.map +1 -0
- package/dist/lib/router.js +276 -0
- package/dist/lib/router.js.map +1 -0
- package/dist/lib/schema.d.ts +55 -0
- package/dist/lib/schema.d.ts.map +1 -0
- package/dist/lib/schema.js +70 -0
- package/dist/lib/schema.js.map +1 -0
- package/dist/lib/security.d.ts +156 -0
- package/dist/lib/security.d.ts.map +1 -0
- package/dist/lib/security.js +347 -0
- package/dist/lib/security.js.map +1 -0
- package/dist/lib/verification-gates.d.ts +287 -0
- package/dist/lib/verification-gates.d.ts.map +1 -0
- package/dist/lib/verification-gates.js +548 -0
- package/dist/lib/verification-gates.js.map +1 -0
- package/dist/types/domain.d.ts +29 -0
- package/dist/types/domain.d.ts.map +1 -0
- package/dist/types/domain.js +7 -0
- package/dist/types/domain.js.map +1 -0
- package/dist/types/error.d.ts +101 -0
- package/dist/types/error.d.ts.map +1 -0
- package/dist/types/error.js +61 -0
- package/dist/types/error.js.map +1 -0
- package/dist/types/gateway.d.ts +78 -0
- package/dist/types/gateway.d.ts.map +1 -0
- package/dist/types/gateway.js +7 -0
- package/dist/types/gateway.js.map +1 -0
- package/dist/types/index.d.ts +21 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +11 -0
- package/dist/types/index.js.map +1 -0
- package/dist/types/operations/lifecycle.d.ts +140 -0
- package/dist/types/operations/lifecycle.d.ts.map +1 -0
- package/dist/types/operations/lifecycle.js +8 -0
- package/dist/types/operations/lifecycle.js.map +1 -0
- package/dist/types/operations/orchestrate.d.ts +140 -0
- package/dist/types/operations/orchestrate.d.ts.map +1 -0
- package/dist/types/operations/orchestrate.js +8 -0
- package/dist/types/operations/orchestrate.js.map +1 -0
- package/dist/types/operations/release.d.ts +97 -0
- package/dist/types/operations/release.d.ts.map +1 -0
- package/dist/types/operations/release.js +7 -0
- package/dist/types/operations/release.js.map +1 -0
- package/dist/types/operations/research.d.ts +122 -0
- package/dist/types/operations/research.d.ts.map +1 -0
- package/dist/types/operations/research.js +8 -0
- package/dist/types/operations/research.js.map +1 -0
- package/dist/types/operations/session.d.ts +108 -0
- package/dist/types/operations/session.d.ts.map +1 -0
- package/dist/types/operations/session.js +8 -0
- package/dist/types/operations/session.js.map +1 -0
- package/dist/types/operations/system.d.ts +147 -0
- package/dist/types/operations/system.d.ts.map +1 -0
- package/dist/types/operations/system.js +8 -0
- package/dist/types/operations/system.js.map +1 -0
- package/dist/types/operations/tasks.d.ts +186 -0
- package/dist/types/operations/tasks.d.ts.map +1 -0
- package/dist/types/operations/tasks.js +8 -0
- package/dist/types/operations/tasks.js.map +1 -0
- package/dist/types/operations/validate.d.ts +170 -0
- package/dist/types/operations/validate.d.ts.map +1 -0
- package/dist/types/operations/validate.js +8 -0
- package/dist/types/operations/validate.js.map +1 -0
- package/package.json +67 -0
- package/schemas/IMPLEMENTATION-SUMMARY.md +250 -0
- package/schemas/README.md +284 -0
- package/schemas/common/error.schema.json +54 -0
- package/schemas/common/meta.schema.json +39 -0
- package/schemas/common/pagination.schema.json +32 -0
- package/schemas/index.json +159 -0
- package/schemas/requests/lifecycle/check.schema.json +20 -0
- package/schemas/requests/lifecycle/gate.fail.schema.json +25 -0
- package/schemas/requests/lifecycle/gate.pass.schema.json +28 -0
- package/schemas/requests/lifecycle/gates.schema.json +15 -0
- package/schemas/requests/lifecycle/history.schema.json +15 -0
- package/schemas/requests/lifecycle/prerequisites.schema.json +15 -0
- package/schemas/requests/lifecycle/progress.schema.json +29 -0
- package/schemas/requests/lifecycle/reset.schema.json +25 -0
- package/schemas/requests/lifecycle/skip.schema.json +25 -0
- package/schemas/requests/lifecycle/status.schema.json +23 -0
- package/schemas/requests/orchestrate/analyze.schema.json +15 -0
- package/schemas/requests/orchestrate/context.schema.json +13 -0
- package/schemas/requests/orchestrate/next.schema.json +15 -0
- package/schemas/requests/orchestrate/parallel.end.schema.json +20 -0
- package/schemas/requests/orchestrate/parallel.start.schema.json +20 -0
- package/schemas/requests/orchestrate/ready.schema.json +15 -0
- package/schemas/requests/orchestrate/skill.list.schema.json +13 -0
- package/schemas/requests/orchestrate/spawn.schema.json +25 -0
- package/schemas/requests/orchestrate/startup.schema.json +15 -0
- package/schemas/requests/orchestrate/status.schema.json +15 -0
- package/schemas/requests/orchestrate/validate.schema.json +15 -0
- package/schemas/requests/orchestrate/waves.schema.json +15 -0
- package/schemas/requests/release/changelog.schema.json +23 -0
- package/schemas/requests/release/commit.schema.json +22 -0
- package/schemas/requests/release/gates.run.schema.json +17 -0
- package/schemas/requests/release/prepare.schema.json +20 -0
- package/schemas/requests/release/push.schema.json +20 -0
- package/schemas/requests/release/rollback.schema.json +20 -0
- package/schemas/requests/release/tag.schema.json +19 -0
- package/schemas/requests/research/inject.schema.json +24 -0
- package/schemas/requests/research/link.schema.json +25 -0
- package/schemas/requests/research/list.schema.json +19 -0
- package/schemas/requests/research/manifest.append.schema.json +20 -0
- package/schemas/requests/research/manifest.archive.schema.json +19 -0
- package/schemas/requests/research/manifest.read.schema.json +21 -0
- package/schemas/requests/research/pending.schema.json +14 -0
- package/schemas/requests/research/query.schema.json +21 -0
- package/schemas/requests/research/show.schema.json +14 -0
- package/schemas/requests/research/stats.schema.json +14 -0
- package/schemas/requests/session/end.schema.json +13 -0
- package/schemas/requests/session/focus.clear.schema.json +7 -0
- package/schemas/requests/session/focus.get.schema.json +7 -0
- package/schemas/requests/session/focus.set.schema.json +15 -0
- package/schemas/requests/session/gc.schema.json +14 -0
- package/schemas/requests/session/history.schema.json +16 -0
- package/schemas/requests/session/list.schema.json +13 -0
- package/schemas/requests/session/resume.schema.json +14 -0
- package/schemas/requests/session/show.schema.json +14 -0
- package/schemas/requests/session/start.schema.json +23 -0
- package/schemas/requests/session/status.schema.json +7 -0
- package/schemas/requests/session/suspend.schema.json +13 -0
- package/schemas/requests/system/backup.schema.json +19 -0
- package/schemas/requests/system/cleanup.schema.json +20 -0
- package/schemas/requests/system/config.get.schema.json +14 -0
- package/schemas/requests/system/config.set.schema.json +24 -0
- package/schemas/requests/system/context.schema.json +7 -0
- package/schemas/requests/system/doctor.schema.json +7 -0
- package/schemas/requests/system/init.schema.json +18 -0
- package/schemas/requests/system/migrate.schema.json +19 -0
- package/schemas/requests/system/restore.schema.json +14 -0
- package/schemas/requests/system/stats.schema.json +7 -0
- package/schemas/requests/system/sync.schema.json +15 -0
- package/schemas/requests/system/version.schema.json +7 -0
- package/schemas/requests/tasks/analyze.schema.json +14 -0
- package/schemas/requests/tasks/archive.schema.json +19 -0
- package/schemas/requests/tasks/blockers.schema.json +15 -0
- package/schemas/requests/tasks/complete.schema.json +24 -0
- package/schemas/requests/tasks/create.schema.json +48 -0
- package/schemas/requests/tasks/delete.schema.json +20 -0
- package/schemas/requests/tasks/deps.schema.json +21 -0
- package/schemas/requests/tasks/exists.schema.json +15 -0
- package/schemas/requests/tasks/find.schema.json +22 -0
- package/schemas/requests/tasks/get.schema.json +15 -0
- package/schemas/requests/tasks/list.schema.json +26 -0
- package/schemas/requests/tasks/next.schema.json +21 -0
- package/schemas/requests/tasks/promote.schema.json +15 -0
- package/schemas/requests/tasks/reopen.schema.json +15 -0
- package/schemas/requests/tasks/reorder.schema.json +20 -0
- package/schemas/requests/tasks/reparent.schema.json +20 -0
- package/schemas/requests/tasks/tree.schema.json +21 -0
- package/schemas/requests/tasks/unarchive.schema.json +15 -0
- package/schemas/requests/tasks/update.schema.json +41 -0
- package/schemas/requests/validate/compliance.record.schema.json +20 -0
- package/schemas/requests/validate/compliance.summary.schema.json +18 -0
- package/schemas/requests/validate/compliance.violations.schema.json +19 -0
- package/schemas/requests/validate/manifest.schema.json +23 -0
- package/schemas/requests/validate/output.schema.json +19 -0
- package/schemas/requests/validate/protocol.schema.json +20 -0
- package/schemas/requests/validate/schema.schema.json +19 -0
- package/schemas/requests/validate/task.schema.json +21 -0
- package/schemas/requests/validate/test.coverage.schema.json +14 -0
- package/schemas/requests/validate/test.run.schema.json +22 -0
- package/schemas/requests/validate/test.status.schema.json +14 -0
- package/schemas/responses/common-error.schema.json +20 -0
- package/schemas/responses/common-success.schema.json +21 -0
- package/schemas/responses/lifecycle/check.schema.json +18 -0
- package/schemas/responses/lifecycle/gate.fail.schema.json +18 -0
- package/schemas/responses/lifecycle/gate.pass.schema.json +18 -0
- package/schemas/responses/lifecycle/gates.schema.json +18 -0
- package/schemas/responses/lifecycle/history.schema.json +18 -0
- package/schemas/responses/lifecycle/prerequisites.schema.json +18 -0
- package/schemas/responses/lifecycle/progress.schema.json +18 -0
- package/schemas/responses/lifecycle/reset.schema.json +18 -0
- package/schemas/responses/lifecycle/skip.schema.json +18 -0
- package/schemas/responses/lifecycle/status.schema.json +18 -0
- package/schemas/responses/orchestrate/analyze.schema.json +18 -0
- package/schemas/responses/orchestrate/context.schema.json +18 -0
- package/schemas/responses/orchestrate/next.schema.json +18 -0
- package/schemas/responses/orchestrate/parallel.end.schema.json +18 -0
- package/schemas/responses/orchestrate/parallel.start.schema.json +18 -0
- package/schemas/responses/orchestrate/ready.schema.json +18 -0
- package/schemas/responses/orchestrate/skill.list.schema.json +18 -0
- package/schemas/responses/orchestrate/spawn.schema.json +18 -0
- package/schemas/responses/orchestrate/startup.schema.json +18 -0
- package/schemas/responses/orchestrate/status.schema.json +18 -0
- package/schemas/responses/orchestrate/validate.schema.json +18 -0
- package/schemas/responses/orchestrate/waves.schema.json +18 -0
- package/schemas/responses/release/changelog.schema.json +18 -0
- package/schemas/responses/release/commit.schema.json +18 -0
- package/schemas/responses/release/gates.run.schema.json +18 -0
- package/schemas/responses/release/prepare.schema.json +18 -0
- package/schemas/responses/release/push.schema.json +18 -0
- package/schemas/responses/release/rollback.schema.json +18 -0
- package/schemas/responses/release/tag.schema.json +18 -0
- package/schemas/responses/research/inject.schema.json +18 -0
- package/schemas/responses/research/link.schema.json +18 -0
- package/schemas/responses/research/list.schema.json +18 -0
- package/schemas/responses/research/manifest.append.schema.json +18 -0
- package/schemas/responses/research/manifest.archive.schema.json +18 -0
- package/schemas/responses/research/manifest.read.schema.json +18 -0
- package/schemas/responses/research/pending.schema.json +18 -0
- package/schemas/responses/research/query.schema.json +18 -0
- package/schemas/responses/research/show.schema.json +18 -0
- package/schemas/responses/research/stats.schema.json +18 -0
- package/schemas/responses/session/end.schema.json +18 -0
- package/schemas/responses/session/focus.clear.schema.json +18 -0
- package/schemas/responses/session/focus.get.schema.json +18 -0
- package/schemas/responses/session/focus.set.schema.json +18 -0
- package/schemas/responses/session/gc.schema.json +18 -0
- package/schemas/responses/session/history.schema.json +18 -0
- package/schemas/responses/session/list.schema.json +18 -0
- package/schemas/responses/session/resume.schema.json +18 -0
- package/schemas/responses/session/show.schema.json +18 -0
- package/schemas/responses/session/start.schema.json +18 -0
- package/schemas/responses/session/status.schema.json +18 -0
- package/schemas/responses/session/suspend.schema.json +18 -0
- package/schemas/responses/system/backup.schema.json +18 -0
- package/schemas/responses/system/cleanup.schema.json +18 -0
- package/schemas/responses/system/config.get.schema.json +18 -0
- package/schemas/responses/system/config.set.schema.json +18 -0
- package/schemas/responses/system/context.schema.json +18 -0
- package/schemas/responses/system/doctor.schema.json +18 -0
- package/schemas/responses/system/init.schema.json +18 -0
- package/schemas/responses/system/migrate.schema.json +18 -0
- package/schemas/responses/system/restore.schema.json +18 -0
- package/schemas/responses/system/stats.schema.json +18 -0
- package/schemas/responses/system/sync.schema.json +18 -0
- package/schemas/responses/system/version.schema.json +18 -0
- package/schemas/responses/tasks/analyze.schema.json +18 -0
- package/schemas/responses/tasks/archive.schema.json +18 -0
- package/schemas/responses/tasks/blockers.schema.json +18 -0
- package/schemas/responses/tasks/complete.schema.json +18 -0
- package/schemas/responses/tasks/create.schema.json +18 -0
- package/schemas/responses/tasks/delete.schema.json +18 -0
- package/schemas/responses/tasks/deps.schema.json +18 -0
- package/schemas/responses/tasks/exists.schema.json +18 -0
- package/schemas/responses/tasks/find.schema.json +18 -0
- package/schemas/responses/tasks/get.schema.json +87 -0
- package/schemas/responses/tasks/list.schema.json +18 -0
- package/schemas/responses/tasks/next.schema.json +18 -0
- package/schemas/responses/tasks/promote.schema.json +18 -0
- package/schemas/responses/tasks/reopen.schema.json +18 -0
- package/schemas/responses/tasks/reorder.schema.json +18 -0
- package/schemas/responses/tasks/reparent.schema.json +18 -0
- package/schemas/responses/tasks/tree.schema.json +18 -0
- package/schemas/responses/tasks/unarchive.schema.json +18 -0
- package/schemas/responses/tasks/update.schema.json +18 -0
- package/schemas/responses/validate/compliance.record.schema.json +18 -0
- package/schemas/responses/validate/compliance.summary.schema.json +18 -0
- package/schemas/responses/validate/compliance.violations.schema.json +18 -0
- package/schemas/responses/validate/manifest.schema.json +18 -0
- package/schemas/responses/validate/output.schema.json +18 -0
- package/schemas/responses/validate/protocol.schema.json +18 -0
- package/schemas/responses/validate/schema.schema.json +18 -0
- package/schemas/responses/validate/task.schema.json +18 -0
- package/schemas/responses/validate/test.coverage.schema.json +18 -0
- package/schemas/responses/validate/test.run.schema.json +18 -0
- package/schemas/responses/validate/test.status.schema.json +18 -0
- package/server.json +53 -0
|
@@ -0,0 +1,689 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Gate Validators for 4-Layer Verification System
|
|
3
|
+
*
|
|
4
|
+
* @task T2936
|
|
5
|
+
* @task T3138
|
|
6
|
+
* @epic T2908
|
|
7
|
+
*
|
|
8
|
+
* Individual validator functions for each gate layer:
|
|
9
|
+
* - Layer 1: Schema validation (JSON Schema, format constraints)
|
|
10
|
+
* - Layer 2: Semantic validation (business rules, anti-hallucination)
|
|
11
|
+
* - Layer 3: Referential validation (cross-entity integrity)
|
|
12
|
+
* - Layer 4: Protocol validation (RCSD-IVTR lifecycle)
|
|
13
|
+
*
|
|
14
|
+
* Reference: docs/specs/MCP-SERVER-SPECIFICATION.md Section 8
|
|
15
|
+
*/
|
|
16
|
+
import { GateLayer, GateStatus, WORKFLOW_GATE_SEQUENCE, isValidWorkflowGateName, } from './verification-gates.js';
|
|
17
|
+
import { ErrorSeverity } from './exit-codes.js';
|
|
18
|
+
/**
|
|
19
|
+
* Layer 1: Schema Validation
|
|
20
|
+
*
|
|
21
|
+
* Validates operation parameters against JSON Schema definitions.
|
|
22
|
+
* Checks required fields, data types, and format constraints.
|
|
23
|
+
*/
|
|
24
|
+
export async function validateLayer1Schema(context) {
|
|
25
|
+
const violations = [];
|
|
26
|
+
// Task ID validation (if present)
|
|
27
|
+
if (context.params?.taskId) {
|
|
28
|
+
const taskId = context.params.taskId;
|
|
29
|
+
if (!taskId.match(/^T[0-9]+$/)) {
|
|
30
|
+
violations.push({
|
|
31
|
+
layer: GateLayer.SCHEMA,
|
|
32
|
+
severity: ErrorSeverity.ERROR,
|
|
33
|
+
code: 'E_INVALID_TASK_ID',
|
|
34
|
+
message: `Invalid task ID format: ${taskId}`,
|
|
35
|
+
field: 'taskId',
|
|
36
|
+
value: taskId,
|
|
37
|
+
constraint: 'Must match pattern ^T[0-9]+$',
|
|
38
|
+
fix: 'Use format T followed by digits (e.g., T1234)',
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
// Title validation (for create/update operations)
|
|
43
|
+
if (context.params?.title !== undefined) {
|
|
44
|
+
const title = context.params.title;
|
|
45
|
+
if (typeof title !== 'string' || title.length < 5 || title.length > 100) {
|
|
46
|
+
violations.push({
|
|
47
|
+
layer: GateLayer.SCHEMA,
|
|
48
|
+
severity: ErrorSeverity.ERROR,
|
|
49
|
+
code: 'E_INVALID_TITLE',
|
|
50
|
+
message: 'Title must be 5-100 characters',
|
|
51
|
+
field: 'title',
|
|
52
|
+
value: title,
|
|
53
|
+
constraint: 'length: 5-100',
|
|
54
|
+
fix: 'Provide a title between 5 and 100 characters',
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Description validation (Section 8.1: 10-1000 characters)
|
|
59
|
+
if (context.params?.description !== undefined) {
|
|
60
|
+
const description = context.params.description;
|
|
61
|
+
if (typeof description !== 'string' || description.length < 10 || description.length > 1000) {
|
|
62
|
+
violations.push({
|
|
63
|
+
layer: GateLayer.SCHEMA,
|
|
64
|
+
severity: ErrorSeverity.ERROR,
|
|
65
|
+
code: 'E_INVALID_DESCRIPTION',
|
|
66
|
+
message: 'Description must be 10-1000 characters',
|
|
67
|
+
field: 'description',
|
|
68
|
+
value: typeof description === 'string' ? `${description.substring(0, 50)}...` : description,
|
|
69
|
+
constraint: 'length: 10-1000',
|
|
70
|
+
fix: 'Provide a description between 10 and 1000 characters',
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
// Manifest ID format validation (Section 8.2: ^T\d{3,}-[a-z0-9-]+$)
|
|
75
|
+
if (context.params?.manifestEntry) {
|
|
76
|
+
const entry = context.params.manifestEntry;
|
|
77
|
+
if (entry.id) {
|
|
78
|
+
const manifestId = entry.id;
|
|
79
|
+
if (!manifestId.match(/^T\d{3,}-[a-z0-9-]+$/)) {
|
|
80
|
+
violations.push({
|
|
81
|
+
layer: GateLayer.SCHEMA,
|
|
82
|
+
severity: ErrorSeverity.ERROR,
|
|
83
|
+
code: 'E_INVALID_MANIFEST_ID',
|
|
84
|
+
message: `Invalid manifest ID format: ${manifestId}`,
|
|
85
|
+
field: 'manifestEntry.id',
|
|
86
|
+
value: manifestId,
|
|
87
|
+
constraint: 'Must match ^T\\d{3,}-[a-z0-9-]+$',
|
|
88
|
+
fix: 'Use format T####-slug (e.g., T1234-research-output)',
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// Date format validation (Section 8.2: ISO 8601 YYYY-MM-DD)
|
|
93
|
+
if (entry.date) {
|
|
94
|
+
const date = entry.date;
|
|
95
|
+
if (!date.match(/^\d{4}-\d{2}-\d{2}$/)) {
|
|
96
|
+
violations.push({
|
|
97
|
+
layer: GateLayer.SCHEMA,
|
|
98
|
+
severity: ErrorSeverity.ERROR,
|
|
99
|
+
code: 'E_INVALID_DATE_FORMAT',
|
|
100
|
+
message: `Invalid date format: ${date}`,
|
|
101
|
+
field: 'manifestEntry.date',
|
|
102
|
+
value: date,
|
|
103
|
+
constraint: 'Must be ISO 8601 YYYY-MM-DD',
|
|
104
|
+
fix: 'Use date format YYYY-MM-DD (e.g., 2026-02-06)',
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
// Agent type validation (Section 8.2: known protocol type)
|
|
109
|
+
if (entry.agent_type) {
|
|
110
|
+
const agentType = entry.agent_type;
|
|
111
|
+
const validAgentTypes = [
|
|
112
|
+
'research', 'analysis', 'specification', 'implementation',
|
|
113
|
+
'testing', 'validation', 'documentation', 'release',
|
|
114
|
+
];
|
|
115
|
+
if (!validAgentTypes.includes(agentType)) {
|
|
116
|
+
violations.push({
|
|
117
|
+
layer: GateLayer.SCHEMA,
|
|
118
|
+
severity: ErrorSeverity.ERROR,
|
|
119
|
+
code: 'E_INVALID_AGENT_TYPE',
|
|
120
|
+
message: `Invalid agent type: ${agentType}`,
|
|
121
|
+
field: 'manifestEntry.agent_type',
|
|
122
|
+
value: agentType,
|
|
123
|
+
constraint: `Must be one of: ${validAgentTypes.join(', ')}`,
|
|
124
|
+
fix: `Use one of: ${validAgentTypes.join(', ')}`,
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
// Status validation
|
|
130
|
+
if (context.params?.status) {
|
|
131
|
+
const status = context.params.status;
|
|
132
|
+
const validStatuses = ['pending', 'active', 'blocked', 'done'];
|
|
133
|
+
if (!validStatuses.includes(status)) {
|
|
134
|
+
violations.push({
|
|
135
|
+
layer: GateLayer.SCHEMA,
|
|
136
|
+
severity: ErrorSeverity.ERROR,
|
|
137
|
+
code: 'E_INVALID_STATUS',
|
|
138
|
+
message: `Invalid status: ${status}`,
|
|
139
|
+
field: 'status',
|
|
140
|
+
value: status,
|
|
141
|
+
constraint: `Must be one of: ${validStatuses.join(', ')}`,
|
|
142
|
+
fix: `Use one of: ${validStatuses.join(', ')}`,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Priority validation
|
|
147
|
+
if (context.params?.priority !== undefined) {
|
|
148
|
+
const priority = context.params.priority;
|
|
149
|
+
if (typeof priority !== 'number' || priority < 1 || priority > 9) {
|
|
150
|
+
violations.push({
|
|
151
|
+
layer: GateLayer.SCHEMA,
|
|
152
|
+
severity: ErrorSeverity.ERROR,
|
|
153
|
+
code: 'E_INVALID_PRIORITY',
|
|
154
|
+
message: 'Priority must be 1-9',
|
|
155
|
+
field: 'priority',
|
|
156
|
+
value: priority,
|
|
157
|
+
constraint: 'range: 1-9',
|
|
158
|
+
fix: 'Set priority between 1 (highest) and 9 (lowest)',
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return {
|
|
163
|
+
layer: GateLayer.SCHEMA,
|
|
164
|
+
status: violations.length > 0 ? GateStatus.FAILED : GateStatus.PASSED,
|
|
165
|
+
passed: violations.length === 0,
|
|
166
|
+
violations,
|
|
167
|
+
duration_ms: 0,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Layer 2: Semantic Validation
|
|
172
|
+
*
|
|
173
|
+
* Validates business rules and logical constraints.
|
|
174
|
+
* Checks hierarchy depth, sibling limits, title/description uniqueness.
|
|
175
|
+
*/
|
|
176
|
+
export async function validateLayer2Semantic(context) {
|
|
177
|
+
const violations = [];
|
|
178
|
+
// Title and description must be different (anti-hallucination)
|
|
179
|
+
if (context.params?.title && context.params?.description) {
|
|
180
|
+
const title = context.params.title;
|
|
181
|
+
const description = context.params.description;
|
|
182
|
+
if (title === description) {
|
|
183
|
+
violations.push({
|
|
184
|
+
layer: GateLayer.SEMANTIC,
|
|
185
|
+
severity: ErrorSeverity.ERROR,
|
|
186
|
+
code: 'E_TITLE_DESCRIPTION_SAME',
|
|
187
|
+
message: 'Title and description must be different',
|
|
188
|
+
field: 'description',
|
|
189
|
+
constraint: 'must differ from title',
|
|
190
|
+
fix: 'Provide a unique description that explains the task',
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
// No future timestamps (Section 8.1: created/updated <= now)
|
|
195
|
+
if (context.params?.created || context.params?.updated) {
|
|
196
|
+
const now = new Date();
|
|
197
|
+
for (const field of ['created', 'updated']) {
|
|
198
|
+
const value = context.params?.[field];
|
|
199
|
+
if (value) {
|
|
200
|
+
const timestamp = new Date(value);
|
|
201
|
+
if (!isNaN(timestamp.getTime()) && timestamp > now) {
|
|
202
|
+
violations.push({
|
|
203
|
+
layer: GateLayer.SEMANTIC,
|
|
204
|
+
severity: ErrorSeverity.ERROR,
|
|
205
|
+
code: 'E_FUTURE_TIMESTAMP',
|
|
206
|
+
message: `Timestamp ${field} cannot be in the future: ${value}`,
|
|
207
|
+
field,
|
|
208
|
+
value,
|
|
209
|
+
constraint: `${field} <= current time`,
|
|
210
|
+
fix: `Set ${field} to current or past timestamp`,
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
// Manifest date must not be future (Section 8.1)
|
|
217
|
+
if (context.params?.manifestEntry) {
|
|
218
|
+
const entry = context.params.manifestEntry;
|
|
219
|
+
if (entry.date) {
|
|
220
|
+
const dateStr = entry.date;
|
|
221
|
+
const today = new Date().toISOString().split('T')[0];
|
|
222
|
+
if (dateStr > today) {
|
|
223
|
+
violations.push({
|
|
224
|
+
layer: GateLayer.SEMANTIC,
|
|
225
|
+
severity: ErrorSeverity.ERROR,
|
|
226
|
+
code: 'E_FUTURE_TIMESTAMP',
|
|
227
|
+
message: `Manifest date cannot be in the future: ${dateStr}`,
|
|
228
|
+
field: 'manifestEntry.date',
|
|
229
|
+
value: dateStr,
|
|
230
|
+
constraint: 'date <= today',
|
|
231
|
+
fix: 'Set date to today or earlier',
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// Circular dependency check (if depends specified)
|
|
237
|
+
if (context.params?.depends) {
|
|
238
|
+
const depends = context.params.depends;
|
|
239
|
+
const taskId = context.params?.taskId;
|
|
240
|
+
if (taskId && depends.includes(taskId)) {
|
|
241
|
+
violations.push({
|
|
242
|
+
layer: GateLayer.SEMANTIC,
|
|
243
|
+
severity: ErrorSeverity.ERROR,
|
|
244
|
+
code: 'E_CIRCULAR_DEPENDENCY',
|
|
245
|
+
message: 'Task cannot depend on itself',
|
|
246
|
+
field: 'depends',
|
|
247
|
+
constraint: 'no self-reference',
|
|
248
|
+
fix: 'Remove self-reference from dependencies',
|
|
249
|
+
});
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
// Session scope validation
|
|
253
|
+
if (context.domain === 'session' && context.operation === 'start') {
|
|
254
|
+
const scope = context.params?.scope;
|
|
255
|
+
if (scope && !scope.match(/^(epic|task|global):/)) {
|
|
256
|
+
violations.push({
|
|
257
|
+
layer: GateLayer.SEMANTIC,
|
|
258
|
+
severity: ErrorSeverity.ERROR,
|
|
259
|
+
code: 'E_INVALID_SCOPE',
|
|
260
|
+
message: `Invalid session scope format: ${scope}`,
|
|
261
|
+
field: 'scope',
|
|
262
|
+
constraint: 'Must be epic:<id>, task:<id>, or global',
|
|
263
|
+
fix: 'Use format: epic:T1234, task:T5678, or global',
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
// Notes required for completion (if configured)
|
|
268
|
+
if (context.operation === 'complete' && context.params?.notes === undefined) {
|
|
269
|
+
violations.push({
|
|
270
|
+
layer: GateLayer.SEMANTIC,
|
|
271
|
+
severity: ErrorSeverity.WARNING,
|
|
272
|
+
code: 'E_NOTES_RECOMMENDED',
|
|
273
|
+
message: 'Completion notes are recommended',
|
|
274
|
+
field: 'notes',
|
|
275
|
+
constraint: 'should be present',
|
|
276
|
+
fix: 'Add --notes "..." to document completion',
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
return {
|
|
280
|
+
layer: GateLayer.SEMANTIC,
|
|
281
|
+
status: violations.filter((v) => v.severity === ErrorSeverity.ERROR).length > 0
|
|
282
|
+
? GateStatus.FAILED
|
|
283
|
+
: GateStatus.PASSED,
|
|
284
|
+
passed: violations.filter((v) => v.severity === ErrorSeverity.ERROR).length === 0,
|
|
285
|
+
violations,
|
|
286
|
+
duration_ms: 0,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Layer 3: Referential Validation
|
|
291
|
+
*
|
|
292
|
+
* Validates cross-entity references and relationships.
|
|
293
|
+
* Checks task existence, parent/child relationships, dependencies.
|
|
294
|
+
*/
|
|
295
|
+
export async function validateLayer3Referential(context) {
|
|
296
|
+
const violations = [];
|
|
297
|
+
// Parent task validation (if specified)
|
|
298
|
+
if (context.params?.parent) {
|
|
299
|
+
const parent = context.params.parent;
|
|
300
|
+
// Note: In production, this would call CLIExecutor to check existence
|
|
301
|
+
// For now, we validate format
|
|
302
|
+
if (!parent.match(/^T[0-9]+$/)) {
|
|
303
|
+
violations.push({
|
|
304
|
+
layer: GateLayer.REFERENTIAL,
|
|
305
|
+
severity: ErrorSeverity.ERROR,
|
|
306
|
+
code: 'E_INVALID_PARENT_REF',
|
|
307
|
+
message: `Invalid parent reference: ${parent}`,
|
|
308
|
+
field: 'parent',
|
|
309
|
+
value: parent,
|
|
310
|
+
constraint: 'Must be valid task ID',
|
|
311
|
+
fix: 'Verify parent task exists with: cleo exists <id>',
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
// Dependency validation
|
|
316
|
+
if (context.params?.depends) {
|
|
317
|
+
const depends = context.params.depends;
|
|
318
|
+
for (const depId of depends) {
|
|
319
|
+
if (!depId.match(/^T[0-9]+$/)) {
|
|
320
|
+
violations.push({
|
|
321
|
+
layer: GateLayer.REFERENTIAL,
|
|
322
|
+
severity: ErrorSeverity.ERROR,
|
|
323
|
+
code: 'E_INVALID_DEPENDENCY_REF',
|
|
324
|
+
message: `Invalid dependency reference: ${depId}`,
|
|
325
|
+
field: 'depends',
|
|
326
|
+
value: depId,
|
|
327
|
+
constraint: 'Must be valid task ID',
|
|
328
|
+
fix: 'Verify dependency exists with: cleo find --id <id>',
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
// Task existence validation (for update/complete/delete operations)
|
|
334
|
+
if (['update', 'complete', 'delete', 'archive', 'reopen'].includes(context.operation)) {
|
|
335
|
+
const taskId = context.params?.taskId;
|
|
336
|
+
if (!taskId) {
|
|
337
|
+
violations.push({
|
|
338
|
+
layer: GateLayer.REFERENTIAL,
|
|
339
|
+
severity: ErrorSeverity.ERROR,
|
|
340
|
+
code: 'E_TASK_ID_REQUIRED',
|
|
341
|
+
message: 'Task ID is required for this operation',
|
|
342
|
+
field: 'taskId',
|
|
343
|
+
constraint: 'required',
|
|
344
|
+
fix: 'Provide task ID with --taskId T####',
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
// Session validation (for focus operations)
|
|
349
|
+
if (context.domain === 'session' && context.operation === 'focus.set') {
|
|
350
|
+
const taskId = context.params?.taskId;
|
|
351
|
+
if (taskId && !taskId.match(/^T[0-9]+$/)) {
|
|
352
|
+
violations.push({
|
|
353
|
+
layer: GateLayer.REFERENTIAL,
|
|
354
|
+
severity: ErrorSeverity.ERROR,
|
|
355
|
+
code: 'E_INVALID_FOCUS_REF',
|
|
356
|
+
message: `Invalid focus task reference: ${taskId}`,
|
|
357
|
+
field: 'taskId',
|
|
358
|
+
value: taskId,
|
|
359
|
+
constraint: 'Must be valid task ID',
|
|
360
|
+
fix: 'Use valid task ID format: T####',
|
|
361
|
+
});
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
// Hierarchy depth validation (Section 8.1: max 3 levels)
|
|
365
|
+
if (context.params?.depth !== undefined) {
|
|
366
|
+
const depth = context.params.depth;
|
|
367
|
+
if (typeof depth === 'number' && depth > VALIDATION_RULES.MAX_DEPTH) {
|
|
368
|
+
violations.push({
|
|
369
|
+
layer: GateLayer.REFERENTIAL,
|
|
370
|
+
severity: ErrorSeverity.ERROR,
|
|
371
|
+
code: 'E_DEPTH_EXCEEDED',
|
|
372
|
+
message: `Hierarchy depth ${depth} exceeds maximum of ${VALIDATION_RULES.MAX_DEPTH}`,
|
|
373
|
+
field: 'depth',
|
|
374
|
+
value: depth,
|
|
375
|
+
constraint: `max depth: ${VALIDATION_RULES.MAX_DEPTH}`,
|
|
376
|
+
fix: 'Flatten hierarchy to max 3 levels (epic -> task -> subtask)',
|
|
377
|
+
});
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
// Sibling limit validation (Section 8.1: max 7 per parent)
|
|
381
|
+
if (context.params?.siblingCount !== undefined) {
|
|
382
|
+
const siblingCount = context.params.siblingCount;
|
|
383
|
+
if (typeof siblingCount === 'number' && siblingCount >= VALIDATION_RULES.MAX_SIBLINGS) {
|
|
384
|
+
violations.push({
|
|
385
|
+
layer: GateLayer.REFERENTIAL,
|
|
386
|
+
severity: ErrorSeverity.ERROR,
|
|
387
|
+
code: 'E_SIBLING_LIMIT',
|
|
388
|
+
message: `Parent already has ${siblingCount} children (max ${VALIDATION_RULES.MAX_SIBLINGS})`,
|
|
389
|
+
field: 'siblingCount',
|
|
390
|
+
value: siblingCount,
|
|
391
|
+
constraint: `max siblings: ${VALIDATION_RULES.MAX_SIBLINGS}`,
|
|
392
|
+
fix: 'Create a new parent to group related tasks',
|
|
393
|
+
});
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
// Manifest file reference validation (Section 8.2: referenced file readable)
|
|
397
|
+
if (context.params?.manifestEntry) {
|
|
398
|
+
const entry = context.params.manifestEntry;
|
|
399
|
+
if (typeof entry.file === 'string' && entry.file.length === 0) {
|
|
400
|
+
violations.push({
|
|
401
|
+
layer: GateLayer.REFERENTIAL,
|
|
402
|
+
severity: ErrorSeverity.ERROR,
|
|
403
|
+
code: 'E_EMPTY_FILE_REF',
|
|
404
|
+
message: 'Manifest entry file reference cannot be empty',
|
|
405
|
+
field: 'manifestEntry.file',
|
|
406
|
+
value: entry.file,
|
|
407
|
+
constraint: 'must be non-empty file path',
|
|
408
|
+
fix: 'Provide a valid file path in manifest entry',
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return {
|
|
413
|
+
layer: GateLayer.REFERENTIAL,
|
|
414
|
+
status: violations.length > 0 ? GateStatus.FAILED : GateStatus.PASSED,
|
|
415
|
+
passed: violations.length === 0,
|
|
416
|
+
violations,
|
|
417
|
+
duration_ms: 0,
|
|
418
|
+
};
|
|
419
|
+
}
|
|
420
|
+
/**
|
|
421
|
+
* Layer 4: Protocol Validation
|
|
422
|
+
*
|
|
423
|
+
* Validates RCSD-IVTR lifecycle compliance and protocol requirements.
|
|
424
|
+
* Checks lifecycle gates, protocol-specific rules, provenance tags.
|
|
425
|
+
*/
|
|
426
|
+
export async function validateLayer4Protocol(context, enforcer) {
|
|
427
|
+
const violations = [];
|
|
428
|
+
// Skip protocol validation for non-protocol operations
|
|
429
|
+
if (!context.protocolType) {
|
|
430
|
+
return {
|
|
431
|
+
layer: GateLayer.PROTOCOL,
|
|
432
|
+
status: GateStatus.SKIPPED,
|
|
433
|
+
passed: true,
|
|
434
|
+
violations: [],
|
|
435
|
+
duration_ms: 0,
|
|
436
|
+
};
|
|
437
|
+
}
|
|
438
|
+
// Lifecycle gate validation (for spawn/complete operations)
|
|
439
|
+
if (context.operation === 'spawn' || context.operation === 'complete') {
|
|
440
|
+
const taskId = context.params?.taskId;
|
|
441
|
+
const protocolType = context.protocolType;
|
|
442
|
+
// Check lifecycle prerequisites
|
|
443
|
+
const lifecycleGates = {
|
|
444
|
+
research: [],
|
|
445
|
+
consensus: ['research'],
|
|
446
|
+
specification: ['research', 'consensus'],
|
|
447
|
+
decomposition: ['research', 'consensus', 'specification'],
|
|
448
|
+
implementation: ['research', 'consensus', 'specification', 'decomposition'],
|
|
449
|
+
validation: ['implementation'],
|
|
450
|
+
testing: ['implementation', 'validation'],
|
|
451
|
+
release: ['implementation', 'validation', 'testing'],
|
|
452
|
+
};
|
|
453
|
+
const requiredGates = lifecycleGates[protocolType] || [];
|
|
454
|
+
if (requiredGates.length > 0) {
|
|
455
|
+
// Note: In production, check actual gate status via CLIExecutor
|
|
456
|
+
// For now, we document the requirement
|
|
457
|
+
violations.push({
|
|
458
|
+
layer: GateLayer.PROTOCOL,
|
|
459
|
+
severity: ErrorSeverity.INFO,
|
|
460
|
+
code: 'E_LIFECYCLE_PREREQUISITES',
|
|
461
|
+
message: `This operation requires prior stages: ${requiredGates.join(', ')}`,
|
|
462
|
+
field: 'lifecycle',
|
|
463
|
+
constraint: `prerequisites: ${requiredGates.join(', ')}`,
|
|
464
|
+
fix: 'Ensure prior lifecycle stages are complete',
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
// Manifest validation (for complete operations)
|
|
469
|
+
if (context.operation === 'complete') {
|
|
470
|
+
const manifestEntry = context.params?.manifestEntry;
|
|
471
|
+
if (manifestEntry) {
|
|
472
|
+
// Required manifest fields
|
|
473
|
+
const requiredFields = ['id', 'file', 'title', 'date', 'status', 'agent_type'];
|
|
474
|
+
for (const field of requiredFields) {
|
|
475
|
+
if (!manifestEntry[field]) {
|
|
476
|
+
violations.push({
|
|
477
|
+
layer: GateLayer.PROTOCOL,
|
|
478
|
+
severity: ErrorSeverity.ERROR,
|
|
479
|
+
code: 'E_MANIFEST_FIELD_MISSING',
|
|
480
|
+
message: `Manifest entry missing required field: ${field}`,
|
|
481
|
+
field: `manifestEntry.${field}`,
|
|
482
|
+
constraint: 'required',
|
|
483
|
+
fix: `Add ${field} to manifest entry`,
|
|
484
|
+
});
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
// Status enum validation
|
|
488
|
+
const status = manifestEntry.status;
|
|
489
|
+
if (!['complete', 'partial', 'blocked'].includes(status)) {
|
|
490
|
+
violations.push({
|
|
491
|
+
layer: GateLayer.PROTOCOL,
|
|
492
|
+
severity: ErrorSeverity.ERROR,
|
|
493
|
+
code: 'E_INVALID_MANIFEST_STATUS',
|
|
494
|
+
message: `Invalid manifest status: ${status}`,
|
|
495
|
+
field: 'manifestEntry.status',
|
|
496
|
+
value: status,
|
|
497
|
+
constraint: 'Must be: complete, partial, or blocked',
|
|
498
|
+
fix: 'Set status to complete, partial, or blocked',
|
|
499
|
+
});
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
// Key findings validation for research protocol (Section 8.2: 3-7 items)
|
|
504
|
+
if (context.protocolType === 'research' && context.operation === 'complete') {
|
|
505
|
+
const manifestEntry = context.params?.manifestEntry;
|
|
506
|
+
if (manifestEntry?.key_findings) {
|
|
507
|
+
const findings = manifestEntry.key_findings;
|
|
508
|
+
if (!Array.isArray(findings) || findings.length < VALIDATION_RULES.KEY_FINDINGS_MIN || findings.length > VALIDATION_RULES.KEY_FINDINGS_MAX) {
|
|
509
|
+
violations.push({
|
|
510
|
+
layer: GateLayer.PROTOCOL,
|
|
511
|
+
severity: ErrorSeverity.ERROR,
|
|
512
|
+
code: 'E_KEY_FINDINGS_COUNT',
|
|
513
|
+
message: `Research must have ${VALIDATION_RULES.KEY_FINDINGS_MIN}-${VALIDATION_RULES.KEY_FINDINGS_MAX} key findings, got ${Array.isArray(findings) ? findings.length : 0}`,
|
|
514
|
+
field: 'manifestEntry.key_findings',
|
|
515
|
+
value: Array.isArray(findings) ? findings.length : findings,
|
|
516
|
+
constraint: `count: ${VALIDATION_RULES.KEY_FINDINGS_MIN}-${VALIDATION_RULES.KEY_FINDINGS_MAX}`,
|
|
517
|
+
fix: 'Adjust key findings to have 3-7 items',
|
|
518
|
+
});
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
// Provenance validation (for implementation protocol)
|
|
523
|
+
if (context.protocolType === 'implementation') {
|
|
524
|
+
const files = context.params?.files;
|
|
525
|
+
if (files && files.length > 0) {
|
|
526
|
+
violations.push({
|
|
527
|
+
layer: GateLayer.PROTOCOL,
|
|
528
|
+
severity: ErrorSeverity.WARNING,
|
|
529
|
+
code: 'E_PROVENANCE_CHECK',
|
|
530
|
+
message: 'Implementation files should include @task provenance tags',
|
|
531
|
+
field: 'files',
|
|
532
|
+
constraint: 'should include @task comments',
|
|
533
|
+
fix: 'Add @task T#### comments to new functions/classes',
|
|
534
|
+
});
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return {
|
|
538
|
+
layer: GateLayer.PROTOCOL,
|
|
539
|
+
status: violations.filter((v) => v.severity === ErrorSeverity.ERROR).length > 0
|
|
540
|
+
? GateStatus.FAILED
|
|
541
|
+
: GateStatus.PASSED,
|
|
542
|
+
passed: violations.filter((v) => v.severity === ErrorSeverity.ERROR).length === 0,
|
|
543
|
+
violations,
|
|
544
|
+
duration_ms: 0,
|
|
545
|
+
};
|
|
546
|
+
}
|
|
547
|
+
/**
|
|
548
|
+
* Validation rule definitions for reuse
|
|
549
|
+
*/
|
|
550
|
+
export const VALIDATION_RULES = {
|
|
551
|
+
TASK_ID_PATTERN: /^T[0-9]+$/,
|
|
552
|
+
MANIFEST_ID_PATTERN: /^T\d{3,}-[a-z0-9-]+$/,
|
|
553
|
+
DATE_FORMAT_PATTERN: /^\d{4}-\d{2}-\d{2}$/,
|
|
554
|
+
TITLE_MIN_LENGTH: 5,
|
|
555
|
+
TITLE_MAX_LENGTH: 100,
|
|
556
|
+
DESCRIPTION_MIN_LENGTH: 10,
|
|
557
|
+
DESCRIPTION_MAX_LENGTH: 1000,
|
|
558
|
+
VALID_STATUSES: ['pending', 'active', 'blocked', 'done'],
|
|
559
|
+
VALID_MANIFEST_STATUSES: ['complete', 'partial', 'blocked'],
|
|
560
|
+
VALID_AGENT_TYPES: [
|
|
561
|
+
'research', 'analysis', 'specification', 'implementation',
|
|
562
|
+
'testing', 'validation', 'documentation', 'release',
|
|
563
|
+
],
|
|
564
|
+
PRIORITY_MIN: 1,
|
|
565
|
+
PRIORITY_MAX: 9,
|
|
566
|
+
MAX_DEPTH: 3,
|
|
567
|
+
MAX_SIBLINGS: 7,
|
|
568
|
+
KEY_FINDINGS_MIN: 3,
|
|
569
|
+
KEY_FINDINGS_MAX: 7,
|
|
570
|
+
};
|
|
571
|
+
/**
|
|
572
|
+
* Helper to check if a field is required for an operation
|
|
573
|
+
*/
|
|
574
|
+
export function isFieldRequired(domain, operation, field) {
|
|
575
|
+
const requirements = {
|
|
576
|
+
tasks: {
|
|
577
|
+
create: ['title', 'description'],
|
|
578
|
+
update: ['taskId'],
|
|
579
|
+
complete: ['taskId'],
|
|
580
|
+
delete: ['taskId'],
|
|
581
|
+
},
|
|
582
|
+
session: {
|
|
583
|
+
start: ['scope'],
|
|
584
|
+
'focus.set': ['taskId'],
|
|
585
|
+
},
|
|
586
|
+
};
|
|
587
|
+
return requirements[domain]?.[operation]?.includes(field) ?? false;
|
|
588
|
+
}
|
|
589
|
+
// ============================================================================
|
|
590
|
+
// Section 7: Workflow Gate Validators
|
|
591
|
+
// ============================================================================
|
|
592
|
+
/**
|
|
593
|
+
* Valid workflow gate agent names per Section 7.2
|
|
594
|
+
*/
|
|
595
|
+
export const VALID_WORKFLOW_AGENTS = ['coder', 'testing', 'qa', 'cleanup', 'security', 'docs'];
|
|
596
|
+
/**
|
|
597
|
+
* Valid workflow gate status values per Section 7.3
|
|
598
|
+
*/
|
|
599
|
+
export const VALID_WORKFLOW_GATE_STATUSES = [null, 'passed', 'failed', 'blocked'];
|
|
600
|
+
/**
|
|
601
|
+
* Validate a workflow gate name
|
|
602
|
+
*
|
|
603
|
+
* @task T3141
|
|
604
|
+
*/
|
|
605
|
+
export function validateWorkflowGateName(name) {
|
|
606
|
+
return isValidWorkflowGateName(name);
|
|
607
|
+
}
|
|
608
|
+
/**
|
|
609
|
+
* Validate a workflow gate status value per Section 7.3
|
|
610
|
+
*
|
|
611
|
+
* @task T3141
|
|
612
|
+
*/
|
|
613
|
+
export function validateWorkflowGateStatus(status) {
|
|
614
|
+
return status === null || status === 'passed' || status === 'failed' || status === 'blocked';
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* Validate a gate update operation.
|
|
618
|
+
*
|
|
619
|
+
* Checks that:
|
|
620
|
+
* - Gate name is valid
|
|
621
|
+
* - Status value is valid
|
|
622
|
+
* - Agent matches expected agent for the gate
|
|
623
|
+
* - Dependencies are met for pass operations
|
|
624
|
+
*
|
|
625
|
+
* @task T3141
|
|
626
|
+
*/
|
|
627
|
+
export function validateWorkflowGateUpdate(gateName, status, agent, tracker) {
|
|
628
|
+
const violations = [];
|
|
629
|
+
// Validate gate name
|
|
630
|
+
if (!isValidWorkflowGateName(gateName)) {
|
|
631
|
+
violations.push({
|
|
632
|
+
layer: GateLayer.PROTOCOL,
|
|
633
|
+
severity: ErrorSeverity.ERROR,
|
|
634
|
+
code: 'E_INVALID_GATE',
|
|
635
|
+
message: `Invalid workflow gate name: ${gateName}`,
|
|
636
|
+
field: 'gateName',
|
|
637
|
+
value: gateName,
|
|
638
|
+
constraint: `Must be one of: ${WORKFLOW_GATE_SEQUENCE.join(', ')}`,
|
|
639
|
+
fix: `Use a valid gate name: ${WORKFLOW_GATE_SEQUENCE.join(', ')}`,
|
|
640
|
+
});
|
|
641
|
+
return violations;
|
|
642
|
+
}
|
|
643
|
+
// Validate status
|
|
644
|
+
if (status !== 'passed' && status !== 'failed') {
|
|
645
|
+
violations.push({
|
|
646
|
+
layer: GateLayer.PROTOCOL,
|
|
647
|
+
severity: ErrorSeverity.ERROR,
|
|
648
|
+
code: 'E_INVALID_GATE_STATUS',
|
|
649
|
+
message: `Invalid gate status for update: ${status}`,
|
|
650
|
+
field: 'status',
|
|
651
|
+
value: status,
|
|
652
|
+
constraint: 'Must be "passed" or "failed" for update operations',
|
|
653
|
+
fix: 'Use status "passed" or "failed"',
|
|
654
|
+
});
|
|
655
|
+
}
|
|
656
|
+
// Validate agent match (if tracker provided for context)
|
|
657
|
+
if (agent && tracker) {
|
|
658
|
+
const gateState = tracker.getGateState(gateName);
|
|
659
|
+
if (gateState && agent !== gateState.agent) {
|
|
660
|
+
violations.push({
|
|
661
|
+
layer: GateLayer.PROTOCOL,
|
|
662
|
+
severity: ErrorSeverity.ERROR,
|
|
663
|
+
code: 'E_INVALID_AGENT',
|
|
664
|
+
message: `Agent "${agent}" is not authorized for gate "${gateName}" (expected: ${gateState.agent})`,
|
|
665
|
+
field: 'agent',
|
|
666
|
+
value: agent,
|
|
667
|
+
constraint: `Must be agent: ${gateState.agent}`,
|
|
668
|
+
fix: `Use the correct agent: ${gateState.agent}`,
|
|
669
|
+
});
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
// Validate dependencies are met for pass operations
|
|
673
|
+
if (status === 'passed' && tracker) {
|
|
674
|
+
if (!tracker.canAttempt(gateName)) {
|
|
675
|
+
violations.push({
|
|
676
|
+
layer: GateLayer.PROTOCOL,
|
|
677
|
+
severity: ErrorSeverity.ERROR,
|
|
678
|
+
code: 'E_GATE_DEPENDENCY',
|
|
679
|
+
message: `Cannot pass gate "${gateName}": dependencies not met`,
|
|
680
|
+
field: 'gateName',
|
|
681
|
+
value: gateName,
|
|
682
|
+
constraint: 'All dependency gates must be passed first',
|
|
683
|
+
fix: 'Pass all prerequisite gates first',
|
|
684
|
+
});
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
return violations;
|
|
688
|
+
}
|
|
689
|
+
//# sourceMappingURL=gate-validators.js.map
|