@namewta/speculo 1.0.19 → 1.0.21
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/README.md +8 -7
- package/THIRD-PARTY-NOTICES.md +33 -0
- package/dist/src/agent-files.js +2 -2
- package/dist/src/agent-files.js.map +1 -1
- package/dist/src/agent-skills.d.ts +81 -0
- package/dist/src/agent-skills.js +530 -0
- package/dist/src/agent-skills.js.map +1 -0
- package/dist/src/cli.js +11 -3
- package/dist/src/cli.js.map +1 -1
- package/dist/src/config.js +5 -3
- package/dist/src/config.js.map +1 -1
- package/dist/src/doctor.js +7 -2
- package/dist/src/doctor.js.map +1 -1
- package/dist/src/index.d.ts +8 -0
- package/dist/src/index.js +23 -3
- package/dist/src/index.js.map +1 -1
- package/dist/src/manifest.d.ts +9 -0
- package/dist/src/manifest.js +45 -7
- package/dist/src/manifest.js.map +1 -1
- package/dist/src/refresh.js +40 -0
- package/dist/src/refresh.js.map +1 -1
- package/dist/src/structured.js +15 -4
- package/dist/src/structured.js.map +1 -1
- package/dist/src/transaction.d.ts +24 -3
- package/dist/src/transaction.js +191 -12
- package/dist/src/transaction.js.map +1 -1
- package/package.json +7 -4
- package/scripts/validate-skills.mjs +56 -0
- package/template/AGENTS.md +1 -1
- package/template/CLAUDE.md +1 -1
- package/template/canonical/canonical-specdev-goal-plan.md +156 -28
- package/template/canonical/canonical-specdev-grill-with-docs.md +153 -31
- package/template/canonical/canonical-specdev-spec.md +153 -27
- package/template/canonical/canonical-specdev-tickets.md +153 -27
- package/template/canonical/canonical-specdev-wayfinder.md +142 -27
- package/template/commands/retro.md +11 -9
- package/template/skills/archive-and-consolidate/SKILL.md +163 -7
- package/template/skills/archive-and-consolidate/references/cleanup-rules.md +1 -1
- package/template/skills/docs-sync/references/agents/claude-redirect.md +8 -8
- package/template/skills/docs-sync/references/agents/content-contract.md +1 -1
- package/template/skills/docs-sync/references/agents/manifest-discovery.md +7 -7
- package/template/skills/docs-sync/references/agents/role-classification.md +4 -4
- package/template/skills/docs-sync/references/agents-contract.md +1 -1
- package/template/skills/engineering-standards-builder/SKILL.md +149 -9
- package/template/skills/engineering-standards-builder/agents/openai.yaml +2 -0
- package/template/skills/engineering-standards-builder/manifest.txt +2 -1
- package/template/skills/engineering-standards-builder/references/rules/09-mechanical-guardrails.md +9 -0
- package/template/skills/engineering-standards-builder/scripts/validate-builder.mjs +4 -7
- package/template/skills/git-history-squash/SKILL.md +87 -11
- package/template/skills/git-history-squash/agents/openai.yaml +2 -0
- package/template/skills/optimize-codex-config/SKILL.md +71 -11
- package/template/skills/{speculo-retro/references/entry-procedure.md → retrospective/SKILL.md} +12 -8
- package/template/skills/{speculo-retro → retrospective}/references/friction-taxonomy.md +22 -13
- package/template/skills/{speculo-retro → retrospective}/references/issue-drafting-sop.md +10 -20
- package/template/skills/source-code-zip/SKILL.md +44 -8
- package/template/skills/source-code-zip/references/{entry-procedure.md → cli-reference.md} +0 -43
- package/template/skills/upstream-fork-sync/SKILL.md +71 -8
- package/template/skills/{writing-great-skills → writing-for-agents}/GLOSSARY.md +1 -1
- package/template/skills/writing-for-agents/SKILL.md +22 -0
- package/template/skills/writing-for-agents/agents/openai.yaml +2 -0
- package/template/skills/{writing-great-skills/references/entry-procedure.md → writing-for-agents/references/authoring-principles.md} +2 -9
- package/template/workflows/ops/D-project-deploy/D-project-deploy.md +1 -1
- package/template/workflows/ops/H-host-manage/H-host-manage.md +4 -0
- package/template/workflows/ops/H-host-manage/references/host-profiles.md +4 -0
- package/template/workflows/ops/I-initialize/I-initialize.md +1 -1
- package/template/workflows/ops/INDEX.md +2 -0
- package/template/workflows/ops/README.md +7 -3
- package/template/workflows/ops/S-server-connect/S-server-connect.md +5 -5
- package/template/workflows/ops/V-inventory-view/V-inventory-view.md +2 -0
- package/template/workflows/ops/common/CAPABILITIES.md +2 -0
- package/template/workflows/ops/common/USAGE.md +5 -5
- package/template/workflows/ops/common/WORKSPACE-USAGE.md +29 -2
- package/template/workflows/ops/common/examples/README.md +1 -1
- package/template/workflows/ops/common/examples/register.example.json +7 -3
- package/template/workflows/ops/common/examples/server-connection.example.json +11 -0
- package/template/workflows/ops/common/rules/deployment-layout.md +1 -1
- package/template/workflows/ops/common/rules/host-root-and-onboarding.md +47 -0
- package/template/workflows/ops/common/rules/persistence-and-secrets.md +1 -1
- package/template/workflows/ops/common/rules/research-and-decisions.md +8 -0
- package/template/workflows/ops/common/rules/server-onboarding.md +3 -3
- package/template/workflows/ops/common/rules/workspace-and-authorization.md +2 -2
- package/template/workflows/ops/common/schemas/fleet-view.schema.json +64 -0
- package/template/workflows/ops/common/schemas/host.schema.json +19 -0
- package/template/workflows/ops/common/schemas/plan.schema.json +57 -0
- package/template/workflows/ops/common/schemas/root-confirmation.schema.json +118 -0
- package/template/workflows/ops/common/schemas/server-connection.schema.json +67 -0
- package/template/workflows/ops/common/schemas/server-discovery.schema.json +171 -0
- package/template/workflows/ops/common/schemas/spec.schema.json +19 -0
- package/template/workflows/ops/common/schemas/status.schema.json +19 -0
- package/template/workflows/ops/common/templates/FLEET.html +1 -1
- package/template/workflows/ops/common/templates/HOST-README.md +3 -1
- package/template/workflows/ops/common/tests/test_ops.mjs +8 -11
- package/template/workflows/ops/common/tests/test_ops_bootstrap.mjs +101 -16
- package/template/workflows/ops/common/tests/test_ops_onboarding.mjs +225 -0
- package/template/workflows/ops/common/tests/test_ops_workspace.mjs +17 -8
- package/template/workflows/ops/common/tools/demo-local.mjs +4 -0
- package/template/workflows/ops/common/tools/opslib/agent.mjs +26 -0
- package/template/workflows/ops/common/tools/opslib/bootstrap.mjs +52 -26
- package/template/workflows/ops/common/tools/opslib/cli.mjs +1 -0
- package/template/workflows/ops/common/tools/opslib/core.mjs +1 -1
- package/template/workflows/ops/common/tools/opslib/docs.mjs +1 -0
- package/template/workflows/ops/common/tools/opslib/execution.mjs +4 -0
- package/template/workflows/ops/common/tools/opslib/fleet.mjs +26 -4
- package/template/workflows/ops/common/tools/opslib/host_recipes.mjs +34 -0
- package/template/workflows/ops/common/tools/opslib/model.mjs +6 -0
- package/template/workflows/ops/common/tools/opslib/onboarding.mjs +215 -0
- package/template/workflows/ops/common/tools/opslib/planner.mjs +3 -0
- package/template/workflows/ops/common/tools/opslib/transport.mjs +10 -6
- package/template/workflows/ops/common/tools/opslib/workspace_cli.mjs +20 -1
- package/template/workflows/ops/common/tools/server-discover.sh +65 -0
- package/template/workflows/ops/common/tools/validate-ops.mjs +1 -1
- package/template/workflows/ops/manifest.json +12 -5
- package/template/workflows/specdev/A-archive-and-consolidate/consolidation-interview.md +3 -3
- package/template/workflows/specdev/C-code-review/C-code-review.md +1 -1
- package/template/workflows/specdev/D-diagnose-bugs/D-diagnose-bugs.md +6 -0
- package/template/workflows/specdev/G-grill-with-docs/references/interview-procedure.md +6 -6
- package/template/workflows/specdev/I-implement/references/implementation-procedure.md +1 -1
- package/template/workflows/specdev/I-implement/tdd-rules.md +4 -0
- package/template/workflows/specdev/I-init-setup/config-template.json +5 -1
- package/template/workflows/specdev/INDEX.md +2 -2
- package/template/workflows/specdev/P-goal-plan/planning-modes.md +1 -1
- package/template/workflows/specdev/P-prototype/P-prototype.md +7 -3
- package/template/workflows/specdev/P-prototype/logic-protocol.md +11 -0
- package/template/workflows/specdev/P-prototype/logic-template.md +27 -0
- package/template/workflows/specdev/P-prototype/project-preview-protocol.md +9 -0
- package/template/workflows/specdev/R-retro/R-retro.md +26 -0
- package/template/workflows/specdev/R-retro/retro-template.md +27 -0
- package/template/workflows/specdev/R-review-architecture/references/selected-design.md +1 -1
- package/template/workflows/specdev/README.md +13 -2
- package/template/workflows/specdev/S-spec/S-spec.md +4 -0
- package/template/workflows/specdev/T-tickets/T-tickets.md +4 -0
- package/template/workflows/specdev/T-triage/T-triage.md +31 -25
- package/template/workflows/specdev/T-triage/capture-protocol.md +1 -1
- package/template/workflows/specdev/T-triage/ci-security-protocol.md +11 -0
- package/template/workflows/specdev/T-triage/intake-protocol.md +10 -4
- package/template/workflows/specdev/T-triage/pr-delivery-protocol.md +13 -0
- package/template/workflows/specdev/T-triage/pr-record-template.md +39 -0
- package/template/workflows/specdev/T-triage/publish-protocol.md +1 -1
- package/template/workflows/specdev/T-triage/queue-protocol.md +7 -0
- package/template/workflows/specdev/T-triage/reconcile-protocol.md +1 -1
- package/template/workflows/specdev/T-triage/references/issue-pr-policy.md +23 -0
- package/template/workflows/specdev/T-triage/references/release-authentication.md +9 -0
- package/template/workflows/specdev/T-triage/references/release-notes-injection.md +21 -0
- package/template/workflows/specdev/T-triage/references/workflow-yaml-reference.md +37 -0
- package/template/workflows/specdev/T-triage/release-preflight.md +13 -0
- package/template/workflows/specdev/T-triage/release-protocol.md +12 -0
- package/template/workflows/specdev/T-triage/release-recovery.md +18 -0
- package/template/workflows/specdev/T-triage/remote-operations.md +25 -0
- package/template/workflows/specdev/T-triage/scripts/github-transport.mjs +208 -0
- package/template/workflows/specdev/T-triage/source-template.md +7 -0
- package/template/workflows/specdev/T-triage/triage-run-template.md +34 -0
- package/template/workflows/specdev/T-triage/triage-template.md +4 -1
- package/template/workflows/specdev/common/rules/artifact-contract.md +7 -0
- package/template/workflows/specdev/common/rules/change-completion.md +6 -0
- package/template/workflows/specdev/common/rules/workflow-routing.md +6 -0
- package/template/workflows/specdev/common/rules/workflow-state-and-lifecycle.md +1 -1
- package/template/workflows/specdev/common/schemas/config.schema.json +137 -26
- package/template/workflows/specdev/common/schemas/logic-prototype.schema.json +62 -0
- package/template/workflows/specdev/common/schemas/pull-request.schema.json +90 -0
- package/template/workflows/specdev/common/schemas/retrospective.schema.json +58 -0
- package/template/workflows/specdev/common/schemas/triage-run.schema.json +149 -0
- package/template/workflows/specdev/common/schemas/triage.schema.json +117 -15
- package/template/workflows/specdev/common/tools/delivery-artifacts.mjs +95 -0
- package/template/workflows/specdev/common/tools/validate-specdev.mjs +60 -19
- package/template/workflows/specdev/runtime-contract.json +4 -2
- package/template/skills/archive-and-consolidate/references/entry-procedure.md +0 -178
- package/template/skills/docs-sync/references/entry-procedure.md +0 -3
- package/template/skills/engineering-standards-builder/references/entry-procedure.md +0 -154
- package/template/skills/git-history-squash/references/entry-procedure.md +0 -94
- package/template/skills/github-npm-ops/SKILL.md +0 -19
- package/template/skills/github-npm-ops/references/ci-and-security-ops.md +0 -178
- package/template/skills/github-npm-ops/references/entry-procedure.md +0 -25
- package/template/skills/github-npm-ops/references/failure-recovery.md +0 -123
- package/template/skills/github-npm-ops/references/issue-pr-triage.md +0 -227
- package/template/skills/github-npm-ops/references/issue-transport.md +0 -26
- package/template/skills/github-npm-ops/references/package-json-checklist.md +0 -171
- package/template/skills/github-npm-ops/references/preflight-checklist.md +0 -39
- package/template/skills/github-npm-ops/references/publish-detection.md +0 -68
- package/template/skills/github-npm-ops/references/release-notes-injection.md +0 -238
- package/template/skills/github-npm-ops/references/release-pipeline.md +0 -108
- package/template/skills/github-npm-ops/references/setup-npm-token.md +0 -63
- package/template/skills/github-npm-ops/references/troubleshooting-playbook.md +0 -293
- package/template/skills/github-npm-ops/references/version-bump-flow.md +0 -217
- package/template/skills/github-npm-ops/references/workflow-yaml-reference.md +0 -268
- package/template/skills/github-npm-ops/scripts/issue-transport.mjs +0 -227
- package/template/skills/optimize-codex-config/references/entry-procedure.md +0 -78
- package/template/skills/speculo-retro/SKILL.md +0 -19
- package/template/skills/upstream-fork-sync/references/entry-procedure.md +0 -78
- package/template/skills/writing-great-skills/SKILL.md +0 -23
- /package/template/skills/{writing-great-skills → writing-for-agents}/references/document-contract.md +0 -0
- /package/template/workflows/specdev/{G-grill-with-docs → common/rules}/adr-format.md +0 -0
- /package/template/workflows/specdev/{G-grill-with-docs → common/rules}/context-format.md +0 -0
- /package/template/workflows/specdev/{I-implement → common/rules}/design-it-twice.md +0 -0
- /package/template/workflows/specdev/{G-grill-with-docs/domain-modeling-rules.md → common/rules/domain-modeling.md} +0 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "urn:speculo:specdev:pull-request:v1",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"required": [
|
|
6
|
+
"schema_version",
|
|
7
|
+
"artifact",
|
|
8
|
+
"change",
|
|
9
|
+
"id",
|
|
10
|
+
"requested",
|
|
11
|
+
"status",
|
|
12
|
+
"repo",
|
|
13
|
+
"base_sha",
|
|
14
|
+
"head_sha",
|
|
15
|
+
"body_sha256",
|
|
16
|
+
"verification",
|
|
17
|
+
"url",
|
|
18
|
+
"updated_at",
|
|
19
|
+
"delivery_target"
|
|
20
|
+
],
|
|
21
|
+
"properties": {
|
|
22
|
+
"schema_version": {
|
|
23
|
+
"const": 1
|
|
24
|
+
},
|
|
25
|
+
"artifact": {
|
|
26
|
+
"const": "pull-request"
|
|
27
|
+
},
|
|
28
|
+
"change": {
|
|
29
|
+
"type": "string",
|
|
30
|
+
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}-[a-z0-9]+(?:-[a-z0-9]+)*$"
|
|
31
|
+
},
|
|
32
|
+
"id": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"pattern": "^PR-[0-9]{3,}$"
|
|
35
|
+
},
|
|
36
|
+
"requested": {
|
|
37
|
+
"type": "boolean"
|
|
38
|
+
},
|
|
39
|
+
"status": {
|
|
40
|
+
"enum": [
|
|
41
|
+
"planned",
|
|
42
|
+
"draft",
|
|
43
|
+
"ready",
|
|
44
|
+
"failed",
|
|
45
|
+
"waived"
|
|
46
|
+
]
|
|
47
|
+
},
|
|
48
|
+
"repo": {
|
|
49
|
+
"type": "string",
|
|
50
|
+
"pattern": "^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$"
|
|
51
|
+
},
|
|
52
|
+
"base_sha": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"pattern": "^[a-f0-9]{40,64}$"
|
|
55
|
+
},
|
|
56
|
+
"head_sha": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"pattern": "^[a-f0-9]{40,64}$"
|
|
59
|
+
},
|
|
60
|
+
"body_sha256": {
|
|
61
|
+
"type": "string",
|
|
62
|
+
"pattern": "^[a-f0-9]{64}$"
|
|
63
|
+
},
|
|
64
|
+
"verification": {
|
|
65
|
+
"enum": [
|
|
66
|
+
"pending",
|
|
67
|
+
"passed",
|
|
68
|
+
"failed",
|
|
69
|
+
"unknown"
|
|
70
|
+
]
|
|
71
|
+
},
|
|
72
|
+
"url": {
|
|
73
|
+
"type": [
|
|
74
|
+
"string",
|
|
75
|
+
"null"
|
|
76
|
+
]
|
|
77
|
+
},
|
|
78
|
+
"updated_at": {
|
|
79
|
+
"type": "string",
|
|
80
|
+
"format": "date-time"
|
|
81
|
+
},
|
|
82
|
+
"delivery_target": {
|
|
83
|
+
"enum": [
|
|
84
|
+
"draft",
|
|
85
|
+
"ready"
|
|
86
|
+
]
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
"additionalProperties": false
|
|
90
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "urn:speculo:specdev:retrospective:v1",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"required": [
|
|
6
|
+
"schema_version",
|
|
7
|
+
"artifact",
|
|
8
|
+
"change",
|
|
9
|
+
"id",
|
|
10
|
+
"status",
|
|
11
|
+
"outcome",
|
|
12
|
+
"sources",
|
|
13
|
+
"updated_at"
|
|
14
|
+
],
|
|
15
|
+
"properties": {
|
|
16
|
+
"schema_version": {
|
|
17
|
+
"const": 1
|
|
18
|
+
},
|
|
19
|
+
"artifact": {
|
|
20
|
+
"const": "retrospective"
|
|
21
|
+
},
|
|
22
|
+
"change": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}-[a-z0-9]+(?:-[a-z0-9]+)*$"
|
|
25
|
+
},
|
|
26
|
+
"id": {
|
|
27
|
+
"type": "string",
|
|
28
|
+
"pattern": "^RETRO-[0-9]{3,}$"
|
|
29
|
+
},
|
|
30
|
+
"status": {
|
|
31
|
+
"enum": [
|
|
32
|
+
"draft",
|
|
33
|
+
"blocked",
|
|
34
|
+
"completed"
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
"outcome": {
|
|
38
|
+
"enum": [
|
|
39
|
+
"findings",
|
|
40
|
+
"no-findings",
|
|
41
|
+
"insufficient-evidence"
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
"sources": {
|
|
45
|
+
"type": "array",
|
|
46
|
+
"minItems": 1,
|
|
47
|
+
"items": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"minLength": 1
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"updated_at": {
|
|
53
|
+
"type": "string",
|
|
54
|
+
"format": "date-time"
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"additionalProperties": false
|
|
58
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "urn:speculo:specdev:triage-run:v1",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"required": [
|
|
6
|
+
"schema_version",
|
|
7
|
+
"artifact",
|
|
8
|
+
"id",
|
|
9
|
+
"mode",
|
|
10
|
+
"status",
|
|
11
|
+
"repo",
|
|
12
|
+
"change",
|
|
13
|
+
"commit_sha",
|
|
14
|
+
"npm_target",
|
|
15
|
+
"updated_at",
|
|
16
|
+
"targets"
|
|
17
|
+
],
|
|
18
|
+
"properties": {
|
|
19
|
+
"schema_version": {
|
|
20
|
+
"const": 1
|
|
21
|
+
},
|
|
22
|
+
"artifact": {
|
|
23
|
+
"const": "triage-run"
|
|
24
|
+
},
|
|
25
|
+
"id": {
|
|
26
|
+
"type": "string",
|
|
27
|
+
"pattern": "^TRI-[0-9]{3,}$"
|
|
28
|
+
},
|
|
29
|
+
"mode": {
|
|
30
|
+
"enum": [
|
|
31
|
+
"ci-security",
|
|
32
|
+
"release-preflight",
|
|
33
|
+
"release"
|
|
34
|
+
]
|
|
35
|
+
},
|
|
36
|
+
"status": {
|
|
37
|
+
"enum": [
|
|
38
|
+
"planned",
|
|
39
|
+
"blocked",
|
|
40
|
+
"failed",
|
|
41
|
+
"completed"
|
|
42
|
+
]
|
|
43
|
+
},
|
|
44
|
+
"repo": {
|
|
45
|
+
"type": "string",
|
|
46
|
+
"pattern": "^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$"
|
|
47
|
+
},
|
|
48
|
+
"change": {
|
|
49
|
+
"anyOf": [
|
|
50
|
+
{
|
|
51
|
+
"type": "null"
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
"type": "string",
|
|
55
|
+
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}-[a-z0-9]+(?:-[a-z0-9]+)*$"
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
},
|
|
59
|
+
"commit_sha": {
|
|
60
|
+
"anyOf": [
|
|
61
|
+
{
|
|
62
|
+
"type": "null"
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
"type": "string",
|
|
66
|
+
"pattern": "^[a-f0-9]{40,64}$"
|
|
67
|
+
}
|
|
68
|
+
]
|
|
69
|
+
},
|
|
70
|
+
"npm_target": {
|
|
71
|
+
"enum": [
|
|
72
|
+
"required",
|
|
73
|
+
"not-required",
|
|
74
|
+
"unknown"
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
"updated_at": {
|
|
78
|
+
"type": "string",
|
|
79
|
+
"format": "date-time"
|
|
80
|
+
},
|
|
81
|
+
"targets": {
|
|
82
|
+
"type": "array",
|
|
83
|
+
"items": {
|
|
84
|
+
"type": "object",
|
|
85
|
+
"required": [
|
|
86
|
+
"kind",
|
|
87
|
+
"identity",
|
|
88
|
+
"required",
|
|
89
|
+
"version",
|
|
90
|
+
"registry",
|
|
91
|
+
"dist_tag",
|
|
92
|
+
"observed",
|
|
93
|
+
"receipt"
|
|
94
|
+
],
|
|
95
|
+
"properties": {
|
|
96
|
+
"kind": {
|
|
97
|
+
"enum": [
|
|
98
|
+
"npm",
|
|
99
|
+
"github-release",
|
|
100
|
+
"ci",
|
|
101
|
+
"security"
|
|
102
|
+
]
|
|
103
|
+
},
|
|
104
|
+
"identity": {
|
|
105
|
+
"type": "string",
|
|
106
|
+
"minLength": 1
|
|
107
|
+
},
|
|
108
|
+
"required": {
|
|
109
|
+
"type": "boolean"
|
|
110
|
+
},
|
|
111
|
+
"version": {
|
|
112
|
+
"type": [
|
|
113
|
+
"string",
|
|
114
|
+
"null"
|
|
115
|
+
]
|
|
116
|
+
},
|
|
117
|
+
"registry": {
|
|
118
|
+
"type": [
|
|
119
|
+
"string",
|
|
120
|
+
"null"
|
|
121
|
+
]
|
|
122
|
+
},
|
|
123
|
+
"dist_tag": {
|
|
124
|
+
"type": [
|
|
125
|
+
"string",
|
|
126
|
+
"null"
|
|
127
|
+
]
|
|
128
|
+
},
|
|
129
|
+
"observed": {
|
|
130
|
+
"enum": [
|
|
131
|
+
"pending",
|
|
132
|
+
"verified",
|
|
133
|
+
"failed",
|
|
134
|
+
"unknown"
|
|
135
|
+
]
|
|
136
|
+
},
|
|
137
|
+
"receipt": {
|
|
138
|
+
"type": [
|
|
139
|
+
"string",
|
|
140
|
+
"null"
|
|
141
|
+
]
|
|
142
|
+
}
|
|
143
|
+
},
|
|
144
|
+
"additionalProperties": false
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
},
|
|
148
|
+
"additionalProperties": false
|
|
149
|
+
}
|
|
@@ -1,23 +1,125 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "urn:speculo:specdev:triage:
|
|
3
|
+
"$id": "urn:speculo:specdev:triage:v2",
|
|
4
4
|
"title": "SpecDev Triage Frontmatter",
|
|
5
5
|
"type": "object",
|
|
6
|
-
"required": [
|
|
6
|
+
"required": [
|
|
7
|
+
"schema_version",
|
|
8
|
+
"artifact",
|
|
9
|
+
"change",
|
|
10
|
+
"mode",
|
|
11
|
+
"source",
|
|
12
|
+
"classification",
|
|
13
|
+
"risk",
|
|
14
|
+
"route",
|
|
15
|
+
"ready_for_implementation",
|
|
16
|
+
"external_action",
|
|
17
|
+
"updated_at",
|
|
18
|
+
"disposition",
|
|
19
|
+
"verification",
|
|
20
|
+
"remote_actions"
|
|
21
|
+
],
|
|
7
22
|
"properties": {
|
|
8
|
-
"schema_version": {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
"
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"
|
|
19
|
-
|
|
20
|
-
|
|
23
|
+
"schema_version": {
|
|
24
|
+
"const": 2
|
|
25
|
+
},
|
|
26
|
+
"artifact": {
|
|
27
|
+
"const": "triage"
|
|
28
|
+
},
|
|
29
|
+
"change": {
|
|
30
|
+
"type": "string",
|
|
31
|
+
"pattern": "^[0-9]{4}-[0-9]{2}-[0-9]{2}-[a-z0-9]+(?:-[a-z0-9]+)*$"
|
|
32
|
+
},
|
|
33
|
+
"mode": {
|
|
34
|
+
"enum": [
|
|
35
|
+
"intake",
|
|
36
|
+
"reconcile",
|
|
37
|
+
"publish"
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
"source": {
|
|
41
|
+
"type": "string",
|
|
42
|
+
"minLength": 1
|
|
43
|
+
},
|
|
44
|
+
"classification": {
|
|
45
|
+
"enum": [
|
|
46
|
+
"bug",
|
|
47
|
+
"feature",
|
|
48
|
+
"refactor",
|
|
49
|
+
"investigation",
|
|
50
|
+
"operations",
|
|
51
|
+
"documentation",
|
|
52
|
+
"review",
|
|
53
|
+
"mixed"
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
"risk": {
|
|
57
|
+
"enum": [
|
|
58
|
+
"low",
|
|
59
|
+
"medium",
|
|
60
|
+
"high",
|
|
61
|
+
"critical"
|
|
62
|
+
]
|
|
63
|
+
},
|
|
64
|
+
"route": {
|
|
65
|
+
"type": "string",
|
|
66
|
+
"pattern": "^specdev/[a-z0-9]+(?:-[a-z0-9]+)*$"
|
|
67
|
+
},
|
|
68
|
+
"ready_for_implementation": {
|
|
69
|
+
"type": "boolean"
|
|
70
|
+
},
|
|
71
|
+
"external_action": {
|
|
72
|
+
"enum": [
|
|
73
|
+
"not-applicable",
|
|
74
|
+
"pending-close",
|
|
75
|
+
"closed",
|
|
76
|
+
"close-failed",
|
|
77
|
+
"waived"
|
|
78
|
+
]
|
|
79
|
+
},
|
|
80
|
+
"publish_action": {
|
|
81
|
+
"enum": [
|
|
82
|
+
"not-requested",
|
|
83
|
+
"pending",
|
|
84
|
+
"published",
|
|
85
|
+
"publish-failed",
|
|
86
|
+
"waived"
|
|
87
|
+
]
|
|
88
|
+
},
|
|
89
|
+
"publish": {
|
|
90
|
+
"type": [
|
|
91
|
+
"string",
|
|
92
|
+
"null"
|
|
93
|
+
]
|
|
94
|
+
},
|
|
95
|
+
"updated_at": {
|
|
96
|
+
"type": "string",
|
|
97
|
+
"minLength": 1
|
|
98
|
+
},
|
|
99
|
+
"disposition": {
|
|
100
|
+
"enum": [
|
|
101
|
+
"needs-triage",
|
|
102
|
+
"needs-info",
|
|
103
|
+
"ready-for-agent",
|
|
104
|
+
"ready-for-human",
|
|
105
|
+
"wontfix"
|
|
106
|
+
]
|
|
107
|
+
},
|
|
108
|
+
"verification": {
|
|
109
|
+
"enum": [
|
|
110
|
+
"pending",
|
|
111
|
+
"passed",
|
|
112
|
+
"failed",
|
|
113
|
+
"unknown"
|
|
114
|
+
]
|
|
115
|
+
},
|
|
116
|
+
"remote_actions": {
|
|
117
|
+
"type": "array",
|
|
118
|
+
"items": {
|
|
119
|
+
"type": "string"
|
|
120
|
+
},
|
|
121
|
+
"uniqueItems": true
|
|
122
|
+
}
|
|
21
123
|
},
|
|
22
124
|
"additionalProperties": true
|
|
23
125
|
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import { existsSync, lstatSync, readFileSync, readdirSync } from "node:fs";
|
|
2
|
+
import { basename, dirname, join } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
|
|
6
|
+
const schemas = join(dirname(fileURLToPath(import.meta.url)), "..", "schemas");
|
|
7
|
+
function check(value, rule, at, errors) {
|
|
8
|
+
if (rule.anyOf) {
|
|
9
|
+
if (!rule.anyOf.some(r => { const e=[]; check(value,r,at,e); return !e.length; })) errors.push(at + ": invalid value");
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
if (rule.const !== undefined && value !== rule.const) errors.push(at + ": invalid constant");
|
|
13
|
+
if (rule.enum && !rule.enum.includes(value)) errors.push(at + ": invalid enum");
|
|
14
|
+
if (rule.type) {
|
|
15
|
+
const types = Array.isArray(rule.type) ? rule.type : [rule.type];
|
|
16
|
+
const t = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
|
|
17
|
+
if (!types.includes(t)) { errors.push(at + ": invalid type"); return; }
|
|
18
|
+
}
|
|
19
|
+
if (typeof value === "string") {
|
|
20
|
+
if (rule.minLength && value.trim().length < rule.minLength) errors.push(at + ": empty");
|
|
21
|
+
if (rule.pattern && !new RegExp(rule.pattern).test(value)) errors.push(at + ": invalid format");
|
|
22
|
+
if (rule.format === "date-time" && (!/^\d{4}-\d{2}-\d{2}T/.test(value) || Number.isNaN(Date.parse(value)))) errors.push(at + ": invalid timestamp");
|
|
23
|
+
}
|
|
24
|
+
if (Array.isArray(value)) {
|
|
25
|
+
if (rule.minItems && value.length < rule.minItems) errors.push(at + ": missing entries");
|
|
26
|
+
if (rule.uniqueItems && new Set(value).size !== value.length) errors.push(at + ": duplicate entries");
|
|
27
|
+
value.forEach((v,i) => check(v,rule.items ?? {},at+"["+i+"]",errors));
|
|
28
|
+
} else if (value && typeof value === "object") {
|
|
29
|
+
for (const k of rule.required ?? []) if (!(k in value)) errors.push(at + ": missing " + k);
|
|
30
|
+
for (const [k,v] of Object.entries(value)) {
|
|
31
|
+
if (rule.properties?.[k]) check(v,rule.properties[k],at+"."+k,errors);
|
|
32
|
+
else if (rule.additionalProperties === false) errors.push(at + ": unknown " + k);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
const specs = {
|
|
37
|
+
"pull-request": ["pull-request.schema.json", ["Summary","Evidence","Before / After","Merge Risk","Authorization","Receipt","Recovery"]],
|
|
38
|
+
retrospective: ["retrospective.schema.json", ["Scope","Evidence","Findings","Acceptance","Routes"]],
|
|
39
|
+
"logic-prototype": ["logic-prototype.schema.json", ["Model","Scenarios","Evidence","Decisions","Handoff"]],
|
|
40
|
+
"triage-run": ["triage-run.schema.json", ["Targets","Plan","Evidence","Authorization","Receipts","Recovery"]],
|
|
41
|
+
};
|
|
42
|
+
export function validateRecord(path, kind, parse, expectedChange = null) {
|
|
43
|
+
const errors = [];
|
|
44
|
+
if (!existsSync(path) || !lstatSync(path).isFile() || lstatSync(path).isSymbolicLink()) return { errors: [path + ": missing or unsafe record"], meta: {} };
|
|
45
|
+
const {meta, body} = parse(path), spec = specs[kind];
|
|
46
|
+
check(meta, JSON.parse(readFileSync(join(schemas,spec[0]),"utf8")), basename(path), errors);
|
|
47
|
+
if (expectedChange && meta.change !== expectedChange) errors.push(path + ": change mismatch");
|
|
48
|
+
if (kind === "logic-prototype" ? basename(dirname(path)) !== meta.id : basename(path) !== meta.id + ".md") errors.push(path + ": ID/path mismatch");
|
|
49
|
+
for (const heading of spec[1]) {
|
|
50
|
+
const section = new RegExp("^## " + heading.replace(/[.*+?^${}()|[\]\\]/g,"\\$&") + "\\r?\\n([\\s\\S]*?)(?=^## |$(?![\\s\\S]))","m").exec(body)?.[1];
|
|
51
|
+
if (!section?.trim()) errors.push(path + ": missing or empty section " + heading);
|
|
52
|
+
}
|
|
53
|
+
if (kind === "pull-request") {
|
|
54
|
+
if (meta.status === "ready" && meta.verification !== "passed") errors.push(path + ": ready PR requires passed verification");
|
|
55
|
+
if (["draft","ready"].includes(meta.status) && !new RegExp("^https://github\\.com/" + String(meta.repo).replaceAll(".","\\.") + "/pull/[1-9]\\d*$").test(meta.url ?? "")) errors.push(path + ": missing matching PR URL");
|
|
56
|
+
}
|
|
57
|
+
if (kind === "retrospective" && (meta.sources ?? []).some(s => typeof s !== "string" || !s.trim() || /(?:[A-Za-z]:[\\/]|\b(?:token|secret)=)/i.test(s))) errors.push(path + ": unsafe source");
|
|
58
|
+
if (kind === "triage-run" && meta.status === "completed" && meta.mode !== "ci-security") {
|
|
59
|
+
if (meta.npm_target === "unknown") errors.push(path + ": completed release target cannot be unknown");
|
|
60
|
+
if (!meta.commit_sha) errors.push(path + ": completed release requires fixed commit");
|
|
61
|
+
const targets = Array.isArray(meta.targets) ? meta.targets : [];
|
|
62
|
+
if (!targets.some(t => t.required)) errors.push(path + ": completed preflight/release requires explicit targets");
|
|
63
|
+
if (meta.npm_target === "required" && !targets.some(t => t.kind === "npm" && t.required)) errors.push(path + ": missing required npm target");
|
|
64
|
+
if (meta.npm_target === "not-required" && targets.some(t => t.kind === "npm" && t.required)) errors.push(path + ": conflicting npm target decision");
|
|
65
|
+
for (const target of targets.filter(t => t.required)) {
|
|
66
|
+
if (target.kind === "npm" && (!target.version || !target.registry || !target.dist_tag)) errors.push(path + ": incomplete npm target metadata");
|
|
67
|
+
if (meta.mode === "release" && (target.observed !== "verified" || !target.receipt?.trim())) errors.push(path + ": required target has no verified receipt");
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (kind === "logic-prototype") {
|
|
71
|
+
const htmlPath = join(dirname(path),"index.html");
|
|
72
|
+
if (!existsSync(htmlPath) || lstatSync(htmlPath).isSymbolicLink()) errors.push(path + ": missing or unsafe index.html");
|
|
73
|
+
else {
|
|
74
|
+
const html = readFileSync(htmlPath,"utf8");
|
|
75
|
+
if (/<(?:script|iframe)\b[^>]*\bsrc\s*=|<link\b[^>]*\bhref\s*=|\b(?:fetch|WebSocket|XMLHttpRequest|sendBeacon)\s*\(|@import\b|url\(\s*["']?(?:https?:|\/\/)|\b(?:src|href)\s*=\s*["'](?:https?:|\/\/)/i.test(html)) errors.push(path + ": logic prototype must be standalone and offline");
|
|
76
|
+
if (!/reset/i.test(html) || !/<script\b/i.test(html)) errors.push(path + ": missing reset or inline logic");
|
|
77
|
+
if (meta.status === "ready") {
|
|
78
|
+
if (meta.verification !== "passed") errors.push(path + ": ready logic requires browser verification");
|
|
79
|
+
if (meta.html_sha256 !== createHash("sha256").update(html).digest("hex")) errors.push(path + ": HTML digest mismatch");
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return {errors,meta,body};
|
|
84
|
+
}
|
|
85
|
+
export function validateRecords(change, kind, parse) {
|
|
86
|
+
const folder = kind === "retrospective" ? "retro" : "pull-requests", root=join(change,folder);
|
|
87
|
+
const errors=[], records=[];
|
|
88
|
+
if (!existsSync(root)) return {errors,records};
|
|
89
|
+
if (lstatSync(root).isSymbolicLink() || !lstatSync(root).isDirectory()) return {errors:[root + ": unsafe directory"],records};
|
|
90
|
+
for (const file of readdirSync(root)) {
|
|
91
|
+
const result=validateRecord(join(root,file),kind,parse,basename(change));
|
|
92
|
+
errors.push(...result.errors); records.push(result);
|
|
93
|
+
}
|
|
94
|
+
return {errors,records};
|
|
95
|
+
}
|