@peterxiaoyang/superspec 0.1.10 → 0.1.12

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.
Files changed (45) hide show
  1. package/README.md +8 -10
  2. package/dist/src/apply_worker_chain.d.ts +6 -3
  3. package/dist/src/apply_worker_chain.js +125 -30
  4. package/dist/src/apply_worker_chain_lifecycle.d.ts +7 -5
  5. package/dist/src/apply_worker_chain_lifecycle.js +153 -22
  6. package/dist/src/cli_args.d.ts +1 -0
  7. package/dist/src/cli_args.js +33 -15
  8. package/dist/src/disclosure.js +2 -2
  9. package/dist/src/evidence.js +104 -8
  10. package/dist/src/gates.d.ts +1 -0
  11. package/dist/src/gates.js +160 -27
  12. package/dist/src/hooks/adapter.js +43 -7
  13. package/dist/src/hooks/guard_api.js +2 -2
  14. package/dist/src/hooks/health.js +0 -12
  15. package/dist/src/hooks/policy_event.js +11 -1
  16. package/dist/src/i18n.js +63 -3
  17. package/dist/src/init_cli.js +1 -1
  18. package/dist/src/install_engine.js +64 -0
  19. package/dist/src/openspec.d.ts +2 -0
  20. package/dist/src/openspec.js +31 -0
  21. package/dist/src/packet_render.d.ts +1 -0
  22. package/dist/src/packet_render.js +248 -31
  23. package/dist/src/packet_schema.d.ts +2 -0
  24. package/dist/src/tasks.d.ts +5 -0
  25. package/dist/src/tasks.js +62 -0
  26. package/dist/src/util.d.ts +4 -0
  27. package/dist/src/util.js +13 -0
  28. package/dist/superspec.js +4 -4
  29. package/package.json +3 -3
  30. package/templates/hooks/codex-hooks.json +0 -26
  31. package/templates/sidecar/discovery.md +9 -0
  32. package/templates/sidecar/test-contract.md +2 -1
  33. package/templates/workflow/prompts/code-reviewer.md +1 -1
  34. package/templates/workflow/prompts/executor.md +1 -1
  35. package/templates/workflow/prompts/test-engineer.md +1 -0
  36. package/templates/workflow/prompts/test-runner.md +3 -1
  37. package/templates/workflow/prompts/verifier.md +9 -9
  38. package/templates/workflow/skills/superspec-apply/SKILL.md +58 -40
  39. package/templates/workflow/skills/superspec-archive/SKILL.md +5 -13
  40. package/templates/workflow/skills/superspec-explore/SKILL.md +17 -18
  41. package/templates/workflow/skills/superspec-propose/SKILL.md +26 -22
  42. package/templates/workflow/skills/superspec-review/SKILL.md +17 -24
  43. package/dist/src/packet_measure.d.ts +0 -43
  44. package/dist/src/packet_measure.js +0 -382
  45. /package/bin/{superspec-guard.js → superspec-check.js} +0 -0
@@ -1,382 +0,0 @@
1
- import { existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, statSync, writeFileSync } from "node:fs";
2
- import { tmpdir } from "node:os";
3
- import { dirname, join } from "node:path";
4
- import { load_install_map } from "./install_engine.js";
5
- import { dispatch_packet } from "./packet_render.js";
6
- import { GuardError, runtime, } from "./util.js";
7
- import { file_blob_sha } from "./git.js";
8
- const REPRESENTATIVE_SCENARIOS = [
9
- {
10
- name: "explore_complete",
11
- description: "Package payload surface for discovery authoring plus proposal-entry critic review.",
12
- files: [
13
- "templates/workflow/skills/superspec-explore/SKILL.md",
14
- "templates/workflow/prompts/critic.md",
15
- "adapters/codex/agents/critic.toml",
16
- ],
17
- },
18
- {
19
- name: "proposal_reviewed",
20
- description: "Package payload surface for proposal authoring and proposal critic review.",
21
- files: [
22
- "templates/workflow/skills/superspec-propose/SKILL.md",
23
- "templates/workflow/prompts/critic.md",
24
- "adapters/codex/agents/critic.toml",
25
- ],
26
- },
27
- {
28
- name: "design_complete",
29
- description: "Package payload surface for design authoring and the architect/critic/test-engineer review bundle.",
30
- files: [
31
- "templates/workflow/skills/superspec-propose/SKILL.md",
32
- "templates/workflow/prompts/architect.md",
33
- "templates/workflow/prompts/critic.md",
34
- "templates/workflow/prompts/test-engineer.md",
35
- "adapters/codex/agents/architect.toml",
36
- "adapters/codex/agents/critic.toml",
37
- "adapters/codex/agents/test-engineer.toml",
38
- ],
39
- },
40
- {
41
- name: "test_contract_drafted",
42
- description: "Package payload surface for test-contract authoring and its review lanes.",
43
- files: [
44
- "templates/workflow/skills/superspec-propose/SKILL.md",
45
- "templates/workflow/prompts/critic.md",
46
- "templates/workflow/prompts/test-engineer.md",
47
- "adapters/codex/agents/critic.toml",
48
- "adapters/codex/agents/test-engineer.toml",
49
- ],
50
- },
51
- {
52
- name: "apply_ready",
53
- description: "Package payload surface for apply orchestration and bounded executor handoff.",
54
- files: [
55
- "templates/workflow/skills/superspec-apply/SKILL.md",
56
- "templates/workflow/prompts/executor.md",
57
- "adapters/codex/agents/executor.toml",
58
- ],
59
- },
60
- {
61
- name: "review_complete_allow",
62
- description: "Package payload surface for merged review + final verification on the allow path.",
63
- files: [
64
- "templates/workflow/skills/superspec-review/SKILL.md",
65
- "templates/workflow/prompts/code-reviewer.md",
66
- "templates/workflow/prompts/architect.md",
67
- "templates/workflow/prompts/critic.md",
68
- "templates/workflow/prompts/verifier.md",
69
- "adapters/codex/agents/code-reviewer.toml",
70
- "adapters/codex/agents/architect.toml",
71
- "adapters/codex/agents/critic.toml",
72
- "adapters/codex/agents/verifier.toml",
73
- ],
74
- },
75
- {
76
- name: "archive_ready",
77
- description: "Package payload surface for archive handoff after review passes.",
78
- files: [
79
- "templates/workflow/skills/superspec-archive/SKILL.md",
80
- ],
81
- },
82
- {
83
- name: "round2_reviewer_prompt",
84
- description: "Package payload surface for a round>1 reviewer lane.",
85
- files: [
86
- "templates/workflow/prompts/critic.md",
87
- "adapters/codex/agents/critic.toml",
88
- ],
89
- },
90
- {
91
- name: "request_changes_reopen_tasks",
92
- description: "Package payload surface for a review round that routes back to apply via reopen_tasks.",
93
- files: [
94
- "templates/workflow/skills/superspec-review/SKILL.md",
95
- "templates/workflow/prompts/code-reviewer.md",
96
- "templates/workflow/prompts/architect.md",
97
- "templates/workflow/prompts/critic.md",
98
- "adapters/codex/agents/code-reviewer.toml",
99
- "adapters/codex/agents/architect.toml",
100
- "adapters/codex/agents/critic.toml",
101
- ],
102
- },
103
- {
104
- name: "task_reopen_to_resolved",
105
- description: "Package payload surface for reopened apply work from revert through successor executor completion.",
106
- files: [
107
- "templates/workflow/skills/superspec-apply/SKILL.md",
108
- "templates/workflow/prompts/executor.md",
109
- "adapters/codex/agents/executor.toml",
110
- ],
111
- },
112
- {
113
- name: "scope_expansion",
114
- description: "Package payload surface for apply-side user confirmation when task scope expands.",
115
- files: [
116
- "templates/workflow/skills/superspec-apply/SKILL.md",
117
- ],
118
- },
119
- ];
120
- const MATERIALIZED_WORKFLOW_PACKET_SAMPLES = [
121
- {
122
- name: "apply_ready_blocked",
123
- description: "workflow-packet agent output for apply_ready before all required evidence is present.",
124
- args: { command: "workflow-packet", change: "demo-change", gate: "apply_ready", packet_format: "agent" },
125
- },
126
- {
127
- name: "task_complete_blocked",
128
- description: "workflow-packet agent output for a task-scoped apply completion packet.",
129
- args: { command: "workflow-packet", change: "demo-change", gate: "task_complete", task_id: "TASK-001", packet_format: "agent" },
130
- },
131
- ];
132
- const MATERIALIZED_REVIEW_PACKET_SAMPLES = [
133
- {
134
- name: "proposal_reviewed_critic",
135
- description: "review-packet agent output for a disclosure reviewer lane.",
136
- args: { command: "review-packet", change: "demo-change", gate: "proposal_reviewed", role: "critic", round: 1, packet_format: "agent" },
137
- },
138
- {
139
- name: "review_complete_verifier",
140
- description: "review-packet agent output for the final verification lane.",
141
- args: { command: "review-packet", change: "demo-change", gate: "review_complete", role: "verifier", round: 1, packet_format: "agent" },
142
- },
143
- ];
144
- const MATERIALIZED_REVIEW_PROMPT_SAMPLES = [
145
- {
146
- name: "proposal_reviewed_round2_critic",
147
- description: "review-packet prompt output for a round>1 disclosure reviewer lane with ledger injection.",
148
- args: { command: "review-packet", change: "demo-change", gate: "proposal_reviewed", role: "critic", round: 2, packet_format: "prompt" },
149
- requiredMarkers: ["[SUPERSPEC-FINDING-LEDGER gate=proposal_reviewed findings=1]", "PROP-SCOPE-001", "proposal silently widens scope beyond discovery"],
150
- },
151
- {
152
- name: "review_complete_critic_verification",
153
- description: "review-packet prompt output for the critic verification_review lane.",
154
- args: { command: "review-packet", change: "demo-change", gate: "review_complete", role: "critic", round: 1, evidence_kind: "verification_review", packet_format: "prompt" },
155
- },
156
- ];
157
- const LEDGER_BLOCK_SAMPLES = [
158
- {
159
- name: "proposal_reviewed_round2_ledger",
160
- description: "ledger-render output used by round>1 reviewer prompts.",
161
- args: { command: "ledger-render", change: "demo-change", gate: "proposal_reviewed", round: 2 },
162
- requiredMarkers: ["[SUPERSPEC-FINDING-LEDGER gate=proposal_reviewed findings=1]", "PROP-SCOPE-001", "proposal silently widens scope beyond discovery"],
163
- },
164
- ];
165
- function ensureReadableTextFile(repoRoot, relPath) {
166
- const absPath = join(repoRoot, relPath);
167
- if (existsSync(absPath) && statSync(absPath).isFile()) {
168
- return readFileSync(absPath, "utf8");
169
- }
170
- throw new GuardError(`packet_measure_missing_file: ${relPath}`);
171
- }
172
- function dedupePaths(paths) {
173
- return [...new Set(paths)].sort();
174
- }
175
- function measuredFiles(repoRoot, relPaths) {
176
- return dedupePaths(relPaths).map((relPath) => ({
177
- path: relPath,
178
- chars: ensureReadableTextFile(repoRoot, relPath).length,
179
- }));
180
- }
181
- function measureSurface(repoRoot, relPaths) {
182
- const files = measuredFiles(repoRoot, relPaths);
183
- return {
184
- total_chars: files.reduce((sum, item) => sum + item.chars, 0),
185
- files,
186
- };
187
- }
188
- function writeSyntheticText(repoRoot, relPath, text) {
189
- const absPath = join(repoRoot, relPath);
190
- mkdirSync(dirname(absPath), { recursive: true });
191
- writeFileSync(absPath, text, "utf8");
192
- }
193
- function syntheticRef(root, relPath) {
194
- return {
195
- path: relPath,
196
- blob_sha: file_blob_sha(join(root, relPath)),
197
- };
198
- }
199
- function syntheticStatus(repoRoot, changeRoot) {
200
- return {
201
- changeRoot,
202
- planningHome: { root: repoRoot },
203
- schemaName: "spec-driven",
204
- applyRequires: ["tasks"],
205
- artifacts: ["proposal", "specs", "design", "tasks"].map((id) => ({ id, status: "done", missingDeps: [] })),
206
- };
207
- }
208
- function materializeSyntheticEvidence(changeRoot, relPath, evidence) {
209
- const absPath = join(changeRoot, relPath);
210
- mkdirSync(dirname(absPath), { recursive: true });
211
- writeFileSync(absPath, `${JSON.stringify(evidence, null, 2)}\n`, "utf8");
212
- return { ...evidence, _path: relPath };
213
- }
214
- function syntheticDisclosureEvidences(changeRoot) {
215
- const promptRef = ".superspec/reports/proposal-r1-critic-prompt.md";
216
- const outputRef = ".superspec/reports/proposal-r1-critic.md";
217
- mkdirSync(dirname(join(changeRoot, promptRef)), { recursive: true });
218
- writeFileSync(join(changeRoot, promptRef), "critic prompt for proposal round 1\n", "utf8");
219
- writeFileSync(join(changeRoot, outputRef), "critic found a scope issue\n", "utf8");
220
- const finding = {
221
- finding_id: "PROP-SCOPE-001",
222
- finding_uid: "proposal_reviewed:EV-proposal-r1-critic:PROP-SCOPE-001",
223
- finding_type: "blocker",
224
- category: "scope",
225
- material_categories: ["scope"],
226
- decision_scope_key: "demo-change:proposal:hidden-scope",
227
- summary: "proposal silently widens scope beyond discovery",
228
- };
229
- const targetRefs = [
230
- syntheticRef(changeRoot, "proposal.md"),
231
- syntheticRef(changeRoot, ".superspec/artifacts/discovery.md"),
232
- ];
233
- const review = materializeSyntheticEvidence(changeRoot, ".superspec/evidence/reviews/EV-proposal-r1-critic.json", {
234
- schema_version: 1,
235
- evidence_id: "EV-proposal-r1-critic",
236
- change_id: "demo-change",
237
- gate: "proposal_reviewed",
238
- kind: "review",
239
- created_at: "2026-06-08T00:00:00Z",
240
- created_by: "measure",
241
- status: "pass",
242
- agent_role: "critic",
243
- execution_mode: "native_subagent",
244
- agent_id: "agent-critic",
245
- prompt_ref: promptRef,
246
- output_ref: outputRef,
247
- source_anchors: [],
248
- target_refs: targetRefs,
249
- review_round_id: "proposal_reviewed-r1",
250
- findings: [finding],
251
- });
252
- const digest = materializeSyntheticEvidence(changeRoot, ".superspec/evidence/reviews/EV-proposal-r1-digest.json", {
253
- schema_version: 1,
254
- evidence_id: "EV-proposal-r1-digest",
255
- change_id: "demo-change",
256
- gate: "proposal_reviewed",
257
- kind: "main_review_digest",
258
- created_at: "2026-06-08T00:01:00Z",
259
- created_by: "main-thread",
260
- status: "blocked",
261
- review_round_id: "proposal_reviewed-r1",
262
- target_refs: targetRefs,
263
- source_review_evidence_refs: ["EV-proposal-r1-critic"],
264
- previous_digest_refs: [],
265
- finding_dispositions: [{
266
- ...finding,
267
- origin_review_evidence_id: "EV-proposal-r1-critic",
268
- disposition: "needs_user_decision",
269
- rationale: "material scope finding requires user confirmation",
270
- route: "stay_same_gate_user_decision",
271
- route_reason: "material scope finding requires user confirmation",
272
- }],
273
- });
274
- return [review, digest];
275
- }
276
- function createMaterializedMeasureContext() {
277
- const repoRoot = mkdtempSync(join(tmpdir(), "superspec-packet-measure-"));
278
- const changeRoot = join(repoRoot, "openspec", "changes", "demo-change");
279
- writeSyntheticText(repoRoot, "openspec/changes/demo-change/.superspec/artifacts/discovery.md", "discovery facts\n");
280
- writeSyntheticText(repoRoot, "openspec/changes/demo-change/.superspec/artifacts/business-invariants.md", "## Business Invariants\n\n| id | description |\n| --- | --- |\n| INV-001 | Preserve behavior |\n");
281
- writeSyntheticText(repoRoot, "openspec/changes/demo-change/.superspec/artifacts/test-contract.md", "## Test Contract\n\n| test_id | invariant_refs |\n| --- | --- |\n| TEST-001 | INV-001 |\n");
282
- writeSyntheticText(repoRoot, "openspec/changes/demo-change/proposal.md", "proposal\n");
283
- writeSyntheticText(repoRoot, "openspec/changes/demo-change/design.md", "design\n");
284
- writeSyntheticText(repoRoot, "openspec/changes/demo-change/specs/demo/spec.md", "#### Scenario: Demo\n");
285
- writeSyntheticText(repoRoot, "openspec/changes/demo-change/tasks.md", "- [ ] TASK-001 Implement\n - invariant_refs: INV-001\n - test_refs: TEST-001\n");
286
- const evidences = syntheticDisclosureEvidences(changeRoot);
287
- return {
288
- repoRoot,
289
- changeRoot,
290
- status: syntheticStatus(repoRoot, changeRoot),
291
- evidences,
292
- cleanup: () => rmSync(repoRoot, { recursive: true, force: true }),
293
- };
294
- }
295
- function withMaterializedMeasureRuntime(ctx, fn) {
296
- const overrides = {
297
- load_context: () => [ctx.status, ctx.repoRoot, ctx.changeRoot, ctx.evidences],
298
- dirty_worktree_paths: () => [],
299
- file_blob_sha,
300
- };
301
- const previous = new Map();
302
- for (const [key, value] of Object.entries(overrides)) {
303
- previous.set(key, { had: Object.prototype.hasOwnProperty.call(runtime, key), value: runtime[key] });
304
- runtime[key] = value;
305
- }
306
- try {
307
- return fn();
308
- }
309
- finally {
310
- for (const [key, item] of previous) {
311
- if (item.had)
312
- runtime[key] = item.value;
313
- else
314
- delete runtime[key];
315
- }
316
- }
317
- }
318
- function packetOutputText(args) {
319
- const result = dispatch_packet(args);
320
- if (result.output_format === "prompt") {
321
- const text = String(result.payload);
322
- return text.endsWith("\n") ? text : `${text}\n`;
323
- }
324
- return `${JSON.stringify(result.payload, null, 2)}\n`;
325
- }
326
- function measureMaterializedSamples(specs) {
327
- const ctx = createMaterializedMeasureContext();
328
- try {
329
- const samples = withMaterializedMeasureRuntime(ctx, () => specs.map((spec) => {
330
- const text = packetOutputText(spec.args);
331
- const missingMarkers = (spec.requiredMarkers ?? []).filter((marker) => !text.includes(marker));
332
- if (missingMarkers.length > 0) {
333
- throw new GuardError(`packet_measure_missing_marker: ${spec.name}: ${missingMarkers.join(", ")}`);
334
- }
335
- return {
336
- name: spec.name,
337
- description: spec.description,
338
- chars: text.length,
339
- ...(spec.requiredMarkers && spec.requiredMarkers.length > 0 ? { matched_markers: [...spec.requiredMarkers] } : {}),
340
- };
341
- }));
342
- return {
343
- total_chars: samples.reduce((sum, item) => sum + item.chars, 0),
344
- samples,
345
- };
346
- }
347
- finally {
348
- ctx.cleanup();
349
- }
350
- }
351
- function installPayloadSourcePaths(repoRoot) {
352
- const { mappings, problems } = load_install_map(repoRoot);
353
- if (problems.length > 0)
354
- throw new GuardError(`packet_measure_install_map_invalid: ${problems.join("; ")}`);
355
- return mappings
356
- .filter((mapping) => mapping.kind === "skill" || mapping.kind === "prompt" || mapping.kind === "agent")
357
- .map((mapping) => mapping.source);
358
- }
359
- export function representative_scenarios() {
360
- return REPRESENTATIVE_SCENARIOS;
361
- }
362
- export function measure_packet_surface_report(repoRoot) {
363
- const packagePayloadSourcePaths = installPayloadSourcePaths(repoRoot);
364
- const fixedSurfaceInstallUpperBound = measureSurface(repoRoot, packagePayloadSourcePaths);
365
- const fixedSurfaceRuntimeRequiredSubset = measureSurface(repoRoot, packagePayloadSourcePaths);
366
- const scenarios = REPRESENTATIVE_SCENARIOS.map((scenario) => ({
367
- name: scenario.name,
368
- description: scenario.description,
369
- ...measureSurface(repoRoot, scenario.files),
370
- }));
371
- return {
372
- fixed_surface_chars_install_upper_bound: fixedSurfaceInstallUpperBound,
373
- fixed_surface_chars_runtime_required_subset: fixedSurfaceRuntimeRequiredSubset,
374
- materialized_workflow_packet_chars: measureMaterializedSamples(MATERIALIZED_WORKFLOW_PACKET_SAMPLES),
375
- materialized_review_packet_chars: measureMaterializedSamples(MATERIALIZED_REVIEW_PACKET_SAMPLES),
376
- materialized_review_prompt_chars: measureMaterializedSamples(MATERIALIZED_REVIEW_PROMPT_SAMPLES),
377
- ledger_block_chars: measureMaterializedSamples(LEDGER_BLOCK_SAMPLES),
378
- representative_loaded_surface_chars: {
379
- scenarios,
380
- },
381
- };
382
- }
File without changes