@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,208 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { readFile } from "node:fs/promises";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
import { createHash } from "node:crypto";
|
|
6
|
+
import { resolve } from "node:path";
|
|
7
|
+
import { pathToFileURL } from "node:url";
|
|
8
|
+
|
|
9
|
+
const exec = promisify(execFile);
|
|
10
|
+
export const sha256 = value => createHash("sha256").update(value).digest("hex");
|
|
11
|
+
const must = (value, message) => { if (!value) throw new Error(message); };
|
|
12
|
+
const commentFields = value => ({ ...value, author: value.user ?? value.author, createdAt: value.created_at ?? value.createdAt, updatedAt: value.updated_at ?? value.updatedAt, url: value.html_url ?? value.url });
|
|
13
|
+
function required(o, keys) { for (const k of keys) must(typeof o[k] === "string" && o[k].trim(), "missing --" + k.replaceAll("_", "-")); }
|
|
14
|
+
function validateTarget(o) {
|
|
15
|
+
must(/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/.test(o.repo ?? ""), "invalid repo");
|
|
16
|
+
if (o.number !== undefined) must(/^[1-9]\d*$/.test(String(o.number)), "invalid number");
|
|
17
|
+
if (o.marker !== undefined) must(/^[a-zA-Z0-9:_./-]+$/.test(o.marker), "invalid marker");
|
|
18
|
+
}
|
|
19
|
+
export function createTransport(run = async args => {
|
|
20
|
+
try { return (await exec("gh", args, { encoding: "utf8", maxBuffer: 64 * 1024 * 1024 })).stdout.trim(); }
|
|
21
|
+
catch { throw new Error("GitHub operation failed; remote outcome may be unknown. Re-read before retrying (raw credential-bearing stderr omitted)."); }
|
|
22
|
+
}, readText = path => readFile(path, "utf8")) {
|
|
23
|
+
const json = async args => JSON.parse(await run(args));
|
|
24
|
+
const api = path => json(["api", path]);
|
|
25
|
+
const pages = async path => {
|
|
26
|
+
const value = await json(["api", path + (path.includes("?") ? "&" : "?") + "per_page=100", "--paginate", "--slurp"]);
|
|
27
|
+
must(Array.isArray(value) && value.every(Array.isArray), "incomplete or invalid pagination");
|
|
28
|
+
return value.flat();
|
|
29
|
+
};
|
|
30
|
+
async function readIssue(repo, number) {
|
|
31
|
+
const item = await api("repos/" + repo + "/issues/" + number);
|
|
32
|
+
must(!item.pull_request, "target is a pull request; use pr-read");
|
|
33
|
+
const comments = await pages("repos/" + repo + "/issues/" + number + "/comments");
|
|
34
|
+
must(comments.length === item.comments, "issue comments changed or pagination incomplete");
|
|
35
|
+
const after = await api("repos/" + repo + "/issues/" + number);
|
|
36
|
+
must(after.updated_at === item.updated_at && after.body === item.body && after.comments === item.comments && after.state === item.state, "issue drift during read");
|
|
37
|
+
return { ...item, author: item.user, state: item.state.toUpperCase(), url: item.html_url, comments: comments.map(commentFields),
|
|
38
|
+
createdAt: item.created_at, updatedAt: item.updated_at,
|
|
39
|
+
pagination_complete: true, fetched_at: new Date().toISOString() };
|
|
40
|
+
}
|
|
41
|
+
async function readPull(repo, number) {
|
|
42
|
+
const root = "repos/" + repo + "/pulls/" + number;
|
|
43
|
+
const before = await api(root);
|
|
44
|
+
const [comments, reviews, review_comments, files, diff] = await Promise.all([
|
|
45
|
+
pages("repos/" + repo + "/issues/" + number + "/comments"), pages(root + "/reviews"),
|
|
46
|
+
pages(root + "/comments"), pages(root + "/files"),
|
|
47
|
+
run(["api", root, "-H", "Accept: application/vnd.github.diff"]),
|
|
48
|
+
]);
|
|
49
|
+
const after = await api(root);
|
|
50
|
+
must(before.base.sha === after.base.sha && before.head.sha === after.head.sha && before.updated_at === after.updated_at &&
|
|
51
|
+
before.body === after.body && before.comments === after.comments && before.review_comments === after.review_comments, "PR drift during read");
|
|
52
|
+
must(files.length === after.changed_files && comments.length === after.comments && review_comments.length === after.review_comments,
|
|
53
|
+
"PR pagination incomplete");
|
|
54
|
+
must(typeof diff === "string" && (after.changed_files === 0 || diff.includes("diff --git")), "missing PR diff");
|
|
55
|
+
return { number: after.number, title: after.title, body: after.body ?? "", state: after.merged_at ? "MERGED" : after.state.toUpperCase(),
|
|
56
|
+
url: after.html_url, author: after.user, labels: after.labels, createdAt: after.created_at, updatedAt: after.updated_at,
|
|
57
|
+
baseRefName: after.base.ref, headRefName: after.head.ref, baseRefOid: after.base.sha, headRefOid: after.head.sha,
|
|
58
|
+
isDraft: after.draft, comments: comments.map(commentFields), reviews: reviews.map(commentFields), review_comments: review_comments.map(commentFields),
|
|
59
|
+
files: files.map(file => ({ ...file, path: file.filename ?? file.path })), diff, pagination_complete: true,
|
|
60
|
+
body_sha256: sha256(after.body ?? ""), fetched_at: new Date().toISOString() };
|
|
61
|
+
}
|
|
62
|
+
function fixed(o, p) {
|
|
63
|
+
for (const k of ["base_sha", "head_sha"]) must(/^[a-f0-9]{40,64}$/.test(o[k] ?? ""), "invalid " + k);
|
|
64
|
+
must(p.baseRefOid === o.base_sha && p.headRefOid === o.head_sha, "PR SHA drift");
|
|
65
|
+
if (o.expected_body_sha256 !== undefined) must(p.body_sha256 === o.expected_body_sha256, "PR body drift");
|
|
66
|
+
}
|
|
67
|
+
async function candidate(o) {
|
|
68
|
+
const qualifiedHead = o.head.includes(":") ? o.head : o.repo.split("/")[0] + ":" + o.head;
|
|
69
|
+
const found = await pages("repos/" + o.repo + "/pulls?state=all&head=" + encodeURIComponent(qualifiedHead) + "&base=" + encodeURIComponent(o.base));
|
|
70
|
+
const matches = found.filter(p => String(p.body ?? "").includes("<!-- " + o.marker + " -->"));
|
|
71
|
+
must(matches.length < 2, "multiple PRs with marker");
|
|
72
|
+
if (matches.length) return readPull(o.repo, String(matches[0].number));
|
|
73
|
+
must(found.every(p => p.state !== "open"), "existing open PR for branches needs explicit update");
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
async function createPr(o, plan) {
|
|
77
|
+
required(o, ["base", "head", "base_sha", "head_sha", "title", "body_file", "marker"]);
|
|
78
|
+
const raw = await readText(o.body_file);
|
|
79
|
+
const marker = "<!-- " + o.marker + " -->";
|
|
80
|
+
const body = raw.includes(marker) ? raw : raw.trimEnd() + "\n\n" + marker;
|
|
81
|
+
const existing = await candidate(o);
|
|
82
|
+
if (existing) {
|
|
83
|
+
fixed(o, existing);
|
|
84
|
+
must(existing.body === body && existing.title === o.title, "existing PR content differs; use explicit update");
|
|
85
|
+
return { ...plan, status: "already-present", ...existing };
|
|
86
|
+
}
|
|
87
|
+
const base = await api("repos/" + o.repo + "/commits/" + encodeURIComponent(o.base));
|
|
88
|
+
const colon = o.head.indexOf(":");
|
|
89
|
+
if (colon >= 0) required(o, ["head_repo"]);
|
|
90
|
+
const headRepo = colon >= 0 ? o.head_repo : o.repo;
|
|
91
|
+
must(/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/.test(headRepo), "invalid head-repo");
|
|
92
|
+
if (colon >= 0) must(headRepo.split("/")[0] === o.head.slice(0, colon), "head owner disagrees with head-repo");
|
|
93
|
+
const head = await api("repos/" + headRepo + "/commits/" + encodeURIComponent(colon >= 0 ? o.head.slice(colon + 1) : o.head));
|
|
94
|
+
fixed({ ...o, expected_body_sha256: undefined }, { baseRefOid: base.sha, headRefOid: head.sha });
|
|
95
|
+
if (!o.apply) return { ...plan, body_sha256: sha256(body), draft: true };
|
|
96
|
+
let failure;
|
|
97
|
+
try { await run(["pr", "create", "--repo", o.repo, "--base", o.base, "--head", o.head, "--title", o.title, "--body", body, "--draft"]); }
|
|
98
|
+
catch (e) { failure = e; }
|
|
99
|
+
const after = await candidate(o);
|
|
100
|
+
if (!after) throw failure ?? new Error("PR create result unknown; query marker before retry");
|
|
101
|
+
fixed(o, after);
|
|
102
|
+
must(after.body === body && after.title === o.title && after.isDraft, "PR creation readback mismatch");
|
|
103
|
+
return { ...plan, status: "created", ...after };
|
|
104
|
+
}
|
|
105
|
+
return async function transport(operation, options) {
|
|
106
|
+
const o = { labels: [], ...options }; validateTarget(o);
|
|
107
|
+
const plan = { operation, provider: "github", repo: o.repo, mode: o.apply ? "apply" : "dry-run" };
|
|
108
|
+
if (operation === "issue-read") { required(o, ["number"]); return { ...plan, kind: "issue", ...await readIssue(o.repo, o.number) }; }
|
|
109
|
+
if (operation === "pr-read") { required(o, ["number"]); return { ...plan, kind: "pull-request", ...await readPull(o.repo, o.number) }; }
|
|
110
|
+
if (operation === "issue-search") {
|
|
111
|
+
required(o, ["query"]);
|
|
112
|
+
const issues = await json(["issue", "list", "--repo", o.repo, "--search", o.query, "--state", o.state || "all", "--limit", o.limit || "20", "--json", "number,title,state,url,labels,updatedAt"]);
|
|
113
|
+
return { ...plan, issues, exhaustive: false };
|
|
114
|
+
}
|
|
115
|
+
if (operation === "issue-create") {
|
|
116
|
+
required(o, ["title", "body_file"]);
|
|
117
|
+
Object.assign(plan, { title: o.title, body_file: o.body_file, labels: o.labels });
|
|
118
|
+
if (!o.apply) return plan;
|
|
119
|
+
const body = await readText(o.body_file);
|
|
120
|
+
const args = ["issue", "create", "--repo", o.repo, "--title", o.title, "--body-file", o.body_file];
|
|
121
|
+
for (const label of o.labels) args.push("--label", label);
|
|
122
|
+
// Optional marker enables exact retry recovery for callers with durable ledgers.
|
|
123
|
+
const find = async () => {
|
|
124
|
+
if (!o.marker) return null;
|
|
125
|
+
must(body.includes("<!-- " + o.marker + " -->"), "body must include marker");
|
|
126
|
+
const list = await pages("repos/" + o.repo + "/issues?state=all");
|
|
127
|
+
const found = list.filter(i => !i.pull_request && String(i.body ?? "").includes("<!-- " + o.marker + " -->"));
|
|
128
|
+
must(found.length < 2, "multiple issues with marker");
|
|
129
|
+
return found.length ? readIssue(o.repo, String(found[0].number)) : null;
|
|
130
|
+
};
|
|
131
|
+
const previous = await find();
|
|
132
|
+
if (previous) { must(previous.body === body && previous.title === o.title, "existing issue content drift"); return { ...plan, status: "already-present", url: previous.url }; }
|
|
133
|
+
let url;
|
|
134
|
+
try { url = await run(args); } catch (error) { const recovered = await find(); if (!recovered) throw error; url = recovered.url; }
|
|
135
|
+
const number = /\/issues\/(\d+)$/.exec(url)?.[1]; must(number, "unknown issue create result");
|
|
136
|
+
const after = await readIssue(o.repo, number);
|
|
137
|
+
must(after.body === body && after.title === o.title, "issue readback mismatch");
|
|
138
|
+
return { ...plan, url: after.url, status: "created" };
|
|
139
|
+
}
|
|
140
|
+
if (operation === "issue-comment-close") {
|
|
141
|
+
required(o, ["number", "comment_file", "marker"]);
|
|
142
|
+
const comment = (await readText(o.comment_file)).trim(), marker = "<!-- " + o.marker + " -->";
|
|
143
|
+
const before = await readIssue(o.repo, o.number);
|
|
144
|
+
const exists = before.comments.some(c => String(c.body).includes(marker));
|
|
145
|
+
const result = { ...plan, number: Number(o.number), url: before.url, state_before: before.state, marker: o.marker,
|
|
146
|
+
comment_required: !exists, close_required: before.state !== "CLOSED", steps: [] };
|
|
147
|
+
if (!o.apply) return result;
|
|
148
|
+
if (!exists) { await run(["issue", "comment", o.number, "--repo", o.repo, "--body", comment + "\n\n" + marker]); result.steps.push("commented"); }
|
|
149
|
+
else result.steps.push("comment-already-present");
|
|
150
|
+
if (before.state !== "CLOSED") { await run(["issue", "close", o.number, "--repo", o.repo, "--reason", o.reason || "completed"]); result.steps.push("closed"); }
|
|
151
|
+
const after = await readIssue(o.repo, o.number);
|
|
152
|
+
must(after.state === "CLOSED" && after.comments.some(c => String(c.body).includes(marker)), "issue close readback mismatch");
|
|
153
|
+
return { ...result, state_after: after.state, status: "closed" };
|
|
154
|
+
}
|
|
155
|
+
if (operation === "pr-create") return createPr(o, plan);
|
|
156
|
+
if (["pr-update", "pr-ready"].includes(operation)) {
|
|
157
|
+
required(o, ["number", "base_sha", "head_sha", "expected_body_sha256"]);
|
|
158
|
+
must(/^[a-f0-9]{64}$/.test(o.expected_body_sha256), "invalid expected-body-sha256");
|
|
159
|
+
const before = await readPull(o.repo, o.number); fixed(o, before);
|
|
160
|
+
must(before.state === "OPEN", "PR is not open");
|
|
161
|
+
if (operation === "pr-ready") {
|
|
162
|
+
if (!o.apply) return { ...plan, number: before.number, ready_required: before.isDraft };
|
|
163
|
+
if (before.isDraft) await run(["pr", "ready", o.number, "--repo", o.repo]);
|
|
164
|
+
const after = await readPull(o.repo, o.number); fixed(o, after);
|
|
165
|
+
must(!after.isDraft, "PR remained draft");
|
|
166
|
+
return { ...plan, status: "ready", ...after };
|
|
167
|
+
}
|
|
168
|
+
required(o, ["title", "body_file", "marker"]);
|
|
169
|
+
const body = await readText(o.body_file), marker = "<!-- " + o.marker + " -->";
|
|
170
|
+
must(before.body.includes(marker) && body.includes(marker), "PR marker mismatch");
|
|
171
|
+
if (!o.apply) return { ...plan, number: before.number, body_sha256: sha256(body) };
|
|
172
|
+
await run(["pr", "edit", o.number, "--repo", o.repo, "--title", o.title, "--body", body]);
|
|
173
|
+
const after = await readPull(o.repo, o.number);
|
|
174
|
+
fixed({ ...o, expected_body_sha256: sha256(body) }, after);
|
|
175
|
+
must(after.title === o.title, "PR title readback mismatch");
|
|
176
|
+
return { ...plan, status: "updated", ...after };
|
|
177
|
+
}
|
|
178
|
+
throw new Error("unknown operation: " + operation);
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
export function parseArgs(argv) {
|
|
182
|
+
const [operation, ...rest] = argv, options = { labels: [] };
|
|
183
|
+
const allowed = new Set(["repo", "number", "query", "state", "limit", "title", "body_file", "comment_file", "marker", "reason", "base", "head", "head_repo", "base_sha", "head_sha", "expected_body_sha256", "label"]);
|
|
184
|
+
for (let i = 0; i < rest.length; i++) {
|
|
185
|
+
if (rest[i] === "--apply") { options.apply = true; continue; }
|
|
186
|
+
must(rest[i].startsWith("--"), "unexpected argument");
|
|
187
|
+
const k = rest[i].slice(2).replaceAll("-", "_"), value = rest[++i];
|
|
188
|
+
must(allowed.has(k) && value !== undefined && !value.startsWith("--"), "invalid option or missing value");
|
|
189
|
+
if (k === "label") options.labels.push(value); else { must(!(k in options), "duplicate option"); options[k] = value; }
|
|
190
|
+
}
|
|
191
|
+
return { operation, options };
|
|
192
|
+
}
|
|
193
|
+
export const usage = `github-transport.mjs <operation> --repo OWNER/REPO [options]
|
|
194
|
+
issue-read | pr-read --number N
|
|
195
|
+
issue-search --query TEXT [--state all] [--limit 20]
|
|
196
|
+
issue-create --title TEXT --body-file PATH [--label LABEL] [--marker ID] [--apply]
|
|
197
|
+
issue-comment-close --number N --comment-file PATH --marker ID [--reason completed] [--apply]
|
|
198
|
+
pr-create --base BRANCH --head BRANCH|OWNER:BRANCH [--head-repo OWNER/REPO] --base-sha SHA --head-sha SHA --title TEXT --body-file PATH --marker ID [--apply]
|
|
199
|
+
pr-update --number N --base-sha SHA --head-sha SHA --expected-body-sha256 HASH --title TEXT --body-file PATH --marker ID [--apply]
|
|
200
|
+
pr-ready --number N --base-sha SHA --head-sha SHA --expected-body-sha256 HASH [--apply]
|
|
201
|
+
Defaults to dry-run; caller owns authorization and state. --apply never implies push, merge or release.`;
|
|
202
|
+
if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) {
|
|
203
|
+
try {
|
|
204
|
+
const { operation, options } = parseArgs(process.argv.slice(2));
|
|
205
|
+
if (!operation || ["--help", "-h", "help"].includes(operation)) console.log(usage);
|
|
206
|
+
else console.log(JSON.stringify(await createTransport()(operation, options), null, 2));
|
|
207
|
+
} catch (error) { console.error("ERROR: " + error.message); process.exitCode = 1; }
|
|
208
|
+
}
|
|
@@ -20,6 +20,9 @@ close_capability: not-applicable
|
|
|
20
20
|
- **Labels or classification supplied by source:** none / ...
|
|
21
21
|
- **Attachments:** none / URL references only
|
|
22
22
|
- **Redactions:** none / description of omitted sensitive material
|
|
23
|
+
- **Pagination / fetched at:** GitHub 来源填完整性、每类条数及读取时间;其他来源不适用
|
|
24
|
+
- **Base / head SHA:** PR 填固定值,其他来源不适用
|
|
25
|
+
- **Supersedes:** 新快照在 frontmatter 填 supersedes_source 指向前一不可变快照;初次来源不适用
|
|
23
26
|
|
|
24
27
|
## Original Content
|
|
25
28
|
|
|
@@ -28,3 +31,7 @@ close_capability: not-applicable
|
|
|
28
31
|
## Source Comments
|
|
29
32
|
|
|
30
33
|
<按来源顺序保存评论;没有评论时写“无”。>
|
|
34
|
+
|
|
35
|
+
## PR Evidence
|
|
36
|
+
|
|
37
|
+
PR 来源保存 reviews、review comments、文件清单和完整 diff;分页缺失或 SHA 漂移阻塞分诊。非 PR 来源写不适用。
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
schema_version: 1
|
|
3
|
+
artifact: triage-run
|
|
4
|
+
id: TRI-001
|
|
5
|
+
mode: release-preflight
|
|
6
|
+
status: planned
|
|
7
|
+
repo: owner/repo
|
|
8
|
+
change: null
|
|
9
|
+
commit_sha: null
|
|
10
|
+
npm_target: unknown
|
|
11
|
+
targets: []
|
|
12
|
+
updated_at: <ISO-8601>
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
# 独立操作记录
|
|
16
|
+
|
|
17
|
+
## Targets
|
|
18
|
+
明确 repo、run/alert、workflow、SHA、tag;发布时逐包列 package、registry、version、dist-tag、required/not-required。
|
|
19
|
+
同步填写 frontmatter targets 数组,每项为 {"kind":"npm|github-release|ci|security","identity":"准确包名/tag/run/alert","required":true,"version":null,"registry":null,"dist_tag":null,"observed":"pending|verified|failed|unknown","receipt":null}。npm required 项必须填 version、registry、dist_tag;回读后填写 receipt 证据引用。预检通过允许 pending;release completed 要求每个 required 项 verified 且有 receipt。
|
|
20
|
+
|
|
21
|
+
## Plan
|
|
22
|
+
操作步骤与完成标准。恢复沿用本记录,保留先前尝试。
|
|
23
|
+
|
|
24
|
+
## Evidence
|
|
25
|
+
源证据、命令/退出码、脱敏日志、预检与目标判定理由;无法读取为 unknown。
|
|
26
|
+
|
|
27
|
+
## Authorization
|
|
28
|
+
每个副作用的用户授权出处、目标和内容;尚无授权时只准备计划。
|
|
29
|
+
|
|
30
|
+
## Receipts
|
|
31
|
+
逐步回执与执行后读回;completed 必须列出每个所需目标的确认结果。
|
|
32
|
+
|
|
33
|
+
## Recovery
|
|
34
|
+
失败点、已完成动作、状态未知项、漂移与恢复条件。
|
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
---
|
|
2
|
-
schema_version:
|
|
2
|
+
schema_version: 2
|
|
3
3
|
artifact: triage
|
|
4
4
|
change: <YYYY-MM-DD-topic>
|
|
5
5
|
mode: intake
|
|
6
|
+
disposition: needs-triage
|
|
7
|
+
verification: pending
|
|
8
|
+
remote_actions: []
|
|
6
9
|
source: <Path>{roots.state}/specdev/changes/{change}/source.md</Path>
|
|
7
10
|
classification: investigation
|
|
8
11
|
risk: medium
|
|
@@ -88,3 +88,10 @@ W 的 `<Path>{roots.state}/specdev/changes/{change}/initiative.json</Path>` 只
|
|
|
88
88
|
新 Ticket/普通 Map 保持原 schema_version,并使用 plan_contract_version: 1 扩展。Ticket 拥有经过核实的 Skill 调用绑定、语义资源和执行计划;Map 路由是其投影。规则为 `<Path>{roots.workflows}/specdev/common/rules/skill-invocation.md</Path>`。计划、产物数量与完成证据漂移必须由对应 owner 修订;不能仅改 map 状态。
|
|
89
89
|
|
|
90
90
|
父 `<Path>{roots.state}/specdev/changes/{change}/tickets-map.md</Path>` 是 goal-tickets-map 无状态入口,只引用现有 Implementation Map/Plan;它不能拥有第二份 status、owner 或任务清单。统一 P 拥有生命周期,旧 O 仅保留入口与恢复键。
|
|
91
|
+
|
|
92
|
+
## 新增工件 owner
|
|
93
|
+
|
|
94
|
+
- T:<Path>{roots.state}/specdev/changes/{change}/pull-requests/PR-###.md</Path> 拥有 PR 交付回执;<Path>{roots.state}/specdev/triage-runs/TRI-###.md</Path> 拥有独立远程操作,绝不复用 external_action/publish_action/capture。
|
|
95
|
+
- R-retro:<Path>{roots.state}/specdev/changes/{change}/retro/RETRO-###.md</Path> 拥有活动复盘建议和来源;不成为永久知识。
|
|
96
|
+
- P:<Path>{roots.state}/specdev/changes/{change}/prototypes/LOGIC-NNN/logic.md</Path> 记录逻辑模型与浏览器证据;index.html 是可运行原型,ready 时 hash 与记录一致。
|
|
97
|
+
- 来源更新:<Path>{roots.state}/specdev/changes/{change}/source.md</Path> 冻结后保持不变。T 将后续读取写入 <Path>{roots.state}/specdev/changes/{change}/sources/SRC-###.md</Path>,记录 replaces、固定 SHA/hash、完整性和原 locator,triage 正文选择有效快照;不得把新远程事实自动升级为已接受 Spec。
|
|
@@ -32,3 +32,9 @@ Owner 原子更新 `<Path>{roots.state}/specdev/changes/{change}/.status.json</P
|
|
|
32
32
|
远程动作不参与本地完成判定。Triage `external_action` 为 `pending-close`/`close-failed` 时先 reconcile;`closed`、`waived` 或 `not-applicable` 才允许 Archive。Triage `publish_action` 为 `pending`/`publish-failed` 时先恢复或结束 publish;`not-requested`、`published` 或 `waived` 才允许 Archive。`not-requested` 是默认,未点过 publish 的 change 不被新模式绑架。归档后工件只读。
|
|
33
33
|
|
|
34
34
|
**完成标准**:完成声明可由本地工件、Git 与验证重建;只有一个 owner 命中;失败 candidate 不污染父分支。
|
|
35
|
+
|
|
36
|
+
## 非实现复盘终点与 PR 归档门
|
|
37
|
+
|
|
38
|
+
仅有 R-retro(可先经 T intake)的 review-only change,以 completed 的 RETRO 报告作为等价验收;不能同时存在 Spec、Ticket、Map、Goal Plan、implementation 工件、Evidence 或 worktree。报告 sources 非空、结果诚实、全部验收完成,owner 按原完成转换规则更新状态;存在实现工件时仍走原实现完成门。insufficient-evidence 仅表示报告完整,不表示问题已解决。
|
|
39
|
+
|
|
40
|
+
PR requested=true 且 status=planned/failed 时阻塞归档;delivery_target=ready 时 draft 也保持未闭合。draft 目标可由 draft/ready 满足,ready 目标只由 ready 满足;waived 须记录放弃交付的授权与原因。创建成功不等于已授权 merge。独立 TRI 不因关联 change 就参与完成/归档门。
|
|
@@ -24,3 +24,9 @@
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
本地已清晰请求不强制绕行 Triage。完成后要记账才选 publish;尚未成 Change、只想先留 inbox 记录才选 capture。publish、reconcile 与 capture 写不同远程对象,可先后执行。G 是单 change Grill,P-prototype 是 UI 设计,不与统一 P-goal-plan 混淆。W 发现候选后逐个交 G/S/T;单 change 拆票后可交 P 规划,多个 child 创建父 Goal 前仍须全部 Ready。原缺陷诊断 D、风险分诊、远程 reconcile、完成后发布投影和记事项捕获职责不删除。
|
|
27
|
+
|
|
28
|
+
## 复盘与交付路由
|
|
29
|
+
|
|
30
|
+
开发环境/流程复盘 → <Path>{roots.workflows}/specdev/R-retro/R-retro.md</Path>。代码讲解仍为 L;Speculo 产品反馈仍为 retro command。CI/安全/PR/包发布 → <Path>{roots.workflows}/specdev/T-triage/T-triage.md</Path> 的对应模式;确定性修复回到 D/I,不由 T 接管代码。
|
|
31
|
+
|
|
32
|
+
长任务在阶段边界按产物与待决问题选择继续、压缩上下文或 handoff;交接只携带准确 owner、SHA、输入和未闭合动作,不硬编码特定模型的上下文阈值,不自动启动其他会话。
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
|
|
58
58
|
## 状态字段
|
|
59
59
|
|
|
60
|
-
`<Path>{roots.state}/specdev/status.json</Path>` 使用全局 schema v5;Spec/Ticket/Tickets Map 保留各自 schema v3;新 Ticket/Map 使用附加 plan_contract_version: 1,config 使用 schema
|
|
60
|
+
`<Path>{roots.state}/specdev/status.json</Path>` 使用全局 schema v5;Spec/Ticket/Tickets Map 保留各自 schema v3;新 Ticket/Map 使用附加 plan_contract_version: 1,config 使用 schema v6,Goal Plan 使用 schema v6,Implementation Map/Plan 使用 schema v1,`<Path>{roots.state}/specdev/changes/{change}/.status.json</Path>` 使用 schema v6:
|
|
61
61
|
|
|
62
62
|
- `schema_version`(数字):全局状态 schema 版本,固定为 `5`。
|
|
63
63
|
- `workflow`(字符串):workflow 标识,固定为 `"specdev"`。
|
|
@@ -1,58 +1,169 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"$id": "urn:speculo:specdev:config:
|
|
3
|
+
"$id": "urn:speculo:specdev:config:v6",
|
|
4
4
|
"title": "SpecDev Configuration",
|
|
5
5
|
"type": "object",
|
|
6
|
-
"required": [
|
|
6
|
+
"required": [
|
|
7
|
+
"schema_version",
|
|
8
|
+
"interaction_language",
|
|
9
|
+
"artifact_language",
|
|
10
|
+
"git",
|
|
11
|
+
"execution",
|
|
12
|
+
"verification",
|
|
13
|
+
"planning",
|
|
14
|
+
"github"
|
|
15
|
+
],
|
|
7
16
|
"properties": {
|
|
8
|
-
"schema_version": {
|
|
9
|
-
|
|
10
|
-
|
|
17
|
+
"schema_version": {
|
|
18
|
+
"const": 6
|
|
19
|
+
},
|
|
20
|
+
"interaction_language": {
|
|
21
|
+
"type": "string",
|
|
22
|
+
"minLength": 1
|
|
23
|
+
},
|
|
24
|
+
"artifact_language": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"minLength": 1
|
|
27
|
+
},
|
|
11
28
|
"git": {
|
|
12
29
|
"type": "object",
|
|
13
|
-
"required": [
|
|
30
|
+
"required": [
|
|
31
|
+
"default_branch"
|
|
32
|
+
],
|
|
14
33
|
"properties": {
|
|
15
|
-
"default_branch": {
|
|
34
|
+
"default_branch": {
|
|
35
|
+
"type": [
|
|
36
|
+
"string",
|
|
37
|
+
"null"
|
|
38
|
+
]
|
|
39
|
+
}
|
|
16
40
|
},
|
|
17
41
|
"additionalProperties": false
|
|
18
42
|
},
|
|
19
43
|
"execution": {
|
|
20
44
|
"type": "object",
|
|
21
|
-
"required": [
|
|
45
|
+
"required": [
|
|
46
|
+
"max_implementation_agents",
|
|
47
|
+
"max_integration_attempts",
|
|
48
|
+
"deep_ticket_human_approval",
|
|
49
|
+
"shared_path_owner"
|
|
50
|
+
],
|
|
22
51
|
"properties": {
|
|
23
|
-
"max_implementation_agents": {
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
52
|
+
"max_implementation_agents": {
|
|
53
|
+
"type": "integer",
|
|
54
|
+
"minimum": 1
|
|
55
|
+
},
|
|
56
|
+
"max_integration_attempts": {
|
|
57
|
+
"type": "integer",
|
|
58
|
+
"minimum": 1
|
|
59
|
+
},
|
|
60
|
+
"deep_ticket_human_approval": {
|
|
61
|
+
"type": "boolean"
|
|
62
|
+
},
|
|
63
|
+
"shared_path_owner": {
|
|
64
|
+
"type": "string",
|
|
65
|
+
"minLength": 1
|
|
66
|
+
}
|
|
27
67
|
},
|
|
28
68
|
"additionalProperties": false
|
|
29
69
|
},
|
|
30
70
|
"verification": {
|
|
31
71
|
"type": "object",
|
|
32
|
-
"required": [
|
|
72
|
+
"required": [
|
|
73
|
+
"test",
|
|
74
|
+
"typecheck",
|
|
75
|
+
"lint",
|
|
76
|
+
"build"
|
|
77
|
+
],
|
|
33
78
|
"properties": {
|
|
34
|
-
"test": {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
79
|
+
"test": {
|
|
80
|
+
"type": [
|
|
81
|
+
"string",
|
|
82
|
+
"null"
|
|
83
|
+
]
|
|
84
|
+
},
|
|
85
|
+
"typecheck": {
|
|
86
|
+
"type": [
|
|
87
|
+
"string",
|
|
88
|
+
"null"
|
|
89
|
+
]
|
|
90
|
+
},
|
|
91
|
+
"lint": {
|
|
92
|
+
"type": [
|
|
93
|
+
"string",
|
|
94
|
+
"null"
|
|
95
|
+
]
|
|
96
|
+
},
|
|
97
|
+
"build": {
|
|
98
|
+
"type": [
|
|
99
|
+
"string",
|
|
100
|
+
"null"
|
|
101
|
+
]
|
|
102
|
+
}
|
|
38
103
|
},
|
|
39
104
|
"additionalProperties": true
|
|
40
105
|
},
|
|
41
106
|
"planning": {
|
|
42
107
|
"type": "object",
|
|
43
|
-
"required": [
|
|
108
|
+
"required": [
|
|
109
|
+
"default_depth",
|
|
110
|
+
"require_ready_gate",
|
|
111
|
+
"require_evidence",
|
|
112
|
+
"ui_design_default_candidates",
|
|
113
|
+
"ui_design_max_candidates"
|
|
114
|
+
],
|
|
44
115
|
"properties": {
|
|
45
|
-
"default_depth": {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
116
|
+
"default_depth": {
|
|
117
|
+
"enum": [
|
|
118
|
+
"lite",
|
|
119
|
+
"standard",
|
|
120
|
+
"deep"
|
|
121
|
+
]
|
|
122
|
+
},
|
|
123
|
+
"require_ready_gate": {
|
|
124
|
+
"type": "boolean"
|
|
125
|
+
},
|
|
126
|
+
"require_evidence": {
|
|
127
|
+
"type": "boolean"
|
|
128
|
+
},
|
|
129
|
+
"ui_design_default_candidates": {
|
|
130
|
+
"type": "integer",
|
|
131
|
+
"minimum": 2,
|
|
132
|
+
"maximum": 4
|
|
133
|
+
},
|
|
134
|
+
"ui_design_max_candidates": {
|
|
135
|
+
"type": "integer",
|
|
136
|
+
"minimum": 2,
|
|
137
|
+
"maximum": 4
|
|
138
|
+
}
|
|
50
139
|
},
|
|
51
140
|
"additionalProperties": true
|
|
141
|
+
},
|
|
142
|
+
"github": {
|
|
143
|
+
"type": "object",
|
|
144
|
+
"required": [
|
|
145
|
+
"include_external_prs",
|
|
146
|
+
"labels"
|
|
147
|
+
],
|
|
148
|
+
"properties": {
|
|
149
|
+
"include_external_prs": {
|
|
150
|
+
"type": "boolean"
|
|
151
|
+
},
|
|
152
|
+
"labels": {
|
|
153
|
+
"type": "object",
|
|
154
|
+
"additionalProperties": {
|
|
155
|
+
"type": "string",
|
|
156
|
+
"minLength": 1
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
"additionalProperties": false
|
|
52
161
|
}
|
|
53
162
|
},
|
|
54
|
-
"allOf": [
|
|
55
|
-
|
|
56
|
-
|
|
163
|
+
"allOf": [
|
|
164
|
+
{
|
|
165
|
+
"$comment": "ui_design_default_candidates <= ui_design_max_candidates is enforced by validate-specdev.mjs because JSON Schema cannot compare sibling numeric values."
|
|
166
|
+
}
|
|
167
|
+
],
|
|
57
168
|
"additionalProperties": false
|
|
58
169
|
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "urn:speculo:specdev:logic-prototype:v1",
|
|
4
|
+
"type": "object",
|
|
5
|
+
"required": [
|
|
6
|
+
"schema_version",
|
|
7
|
+
"artifact",
|
|
8
|
+
"change",
|
|
9
|
+
"id",
|
|
10
|
+
"status",
|
|
11
|
+
"verification",
|
|
12
|
+
"html_sha256",
|
|
13
|
+
"updated_at"
|
|
14
|
+
],
|
|
15
|
+
"properties": {
|
|
16
|
+
"schema_version": {
|
|
17
|
+
"const": 1
|
|
18
|
+
},
|
|
19
|
+
"artifact": {
|
|
20
|
+
"const": "logic-prototype"
|
|
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": "^LOGIC-[0-9]{3,}$"
|
|
29
|
+
},
|
|
30
|
+
"status": {
|
|
31
|
+
"enum": [
|
|
32
|
+
"draft",
|
|
33
|
+
"blocked",
|
|
34
|
+
"ready"
|
|
35
|
+
]
|
|
36
|
+
},
|
|
37
|
+
"verification": {
|
|
38
|
+
"enum": [
|
|
39
|
+
"pending",
|
|
40
|
+
"passed",
|
|
41
|
+
"failed",
|
|
42
|
+
"unknown"
|
|
43
|
+
]
|
|
44
|
+
},
|
|
45
|
+
"html_sha256": {
|
|
46
|
+
"anyOf": [
|
|
47
|
+
{
|
|
48
|
+
"type": "null"
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
"type": "string",
|
|
52
|
+
"pattern": "^[a-f0-9]{64}$"
|
|
53
|
+
}
|
|
54
|
+
]
|
|
55
|
+
},
|
|
56
|
+
"updated_at": {
|
|
57
|
+
"type": "string",
|
|
58
|
+
"format": "date-time"
|
|
59
|
+
}
|
|
60
|
+
},
|
|
61
|
+
"additionalProperties": false
|
|
62
|
+
}
|