@peterxiaoyang/superspec 0.1.10 → 0.1.11
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 +4 -6
- package/dist/src/hooks/adapter.js +43 -7
- package/dist/src/hooks/health.js +0 -12
- package/dist/src/install_engine.js +64 -0
- package/package.json +1 -1
- package/templates/hooks/codex-hooks.json +0 -26
- package/templates/workflow/skills/superspec-apply/SKILL.md +0 -7
- package/templates/workflow/skills/superspec-archive/SKILL.md +0 -8
- package/templates/workflow/skills/superspec-explore/SKILL.md +0 -7
- package/templates/workflow/skills/superspec-propose/SKILL.md +0 -7
- package/templates/workflow/skills/superspec-review/SKILL.md +0 -7
package/README.md
CHANGED
|
@@ -82,7 +82,7 @@ superspec init --scope project
|
|
|
82
82
|
|
|
83
83
|
这条命令的意思是:把 SuperSpec 当前可用的工作流入口安装到项目里。
|
|
84
84
|
|
|
85
|
-
初始化还会安装托管的 `.codex/hooks.json
|
|
85
|
+
初始化还会安装托管的 `.codex/hooks.json`。默认 manifest 只在子智能体启动和停止时调用 SuperSpec,记录 best-effort 审计信息;它不会拦截普通文件写入,也不会在测试命令后自动记录结果。
|
|
86
86
|
|
|
87
87
|
Windows PowerShell 如果拦截 npm 的 `.ps1` 脚本,请改用:
|
|
88
88
|
|
|
@@ -161,15 +161,13 @@ openspec/changes/<变更ID>/.superspec/
|
|
|
161
161
|
|
|
162
162
|
## Hook 会做什么
|
|
163
163
|
|
|
164
|
-
SuperSpec
|
|
164
|
+
SuperSpec 默认安装的 hook 只在子智能体启动和停止时运行:
|
|
165
165
|
|
|
166
|
-
- 写文件前:检查是否会改到 SuperSpec 的过程记录、提前归档、绕过任务检查,或写到当前任务不该写的地方
|
|
167
|
-
- 工具执行后:如果刚跑的是测试或验证命令,就记录这次结果
|
|
168
166
|
- 子智能体启动和停止时:记录这次子智能体运行的基本信息
|
|
169
167
|
|
|
170
168
|
这些 hook 的默认超时时间是 `120` 秒。这个时间限制的是 hook 自己的检查过程,不限制 `npm test`、构建命令或子智能体本身能运行多久。
|
|
171
169
|
|
|
172
|
-
hook
|
|
170
|
+
hook 不是安全沙箱。默认 hook 只留下子智能体审计线索;它不会机械阻止写入,也不能把记录变成不可伪造的安全证明。
|
|
173
171
|
|
|
174
172
|
## 重要边界
|
|
175
173
|
|
|
@@ -189,7 +187,7 @@ SuperSpec 能让流程更规范,但它不是安全锁。
|
|
|
189
187
|
- 阻止恶意伪造记录
|
|
190
188
|
- 替代正式的安全审计、合规审计或法律证明
|
|
191
189
|
|
|
192
|
-
也就是说,SuperSpec 目前是“流程纪律 +
|
|
190
|
+
也就是说,SuperSpec 目前是“流程纪律 + 审计辅助工具”,不是“强制安全系统”。默认 hook 只增强子智能体活动的可见性;显式/manual `PreToolUse` 才会进入保守写入策略,但仍然不能替代正式的安全控制。
|
|
193
191
|
|
|
194
192
|
## 常用命令
|
|
195
193
|
|
|
@@ -224,11 +224,32 @@ function postToolUseOutput(decision) {
|
|
|
224
224
|
},
|
|
225
225
|
};
|
|
226
226
|
}
|
|
227
|
+
function isSubagentHookEvent(eventName) {
|
|
228
|
+
return eventName === "SubagentStart" || eventName === "SubagentStop";
|
|
229
|
+
}
|
|
227
230
|
function subagentOutput(decision, eventName) {
|
|
231
|
+
if (!decision.allowed) {
|
|
232
|
+
return {
|
|
233
|
+
systemMessage: `SuperSpec ${eventName} runlog skipped; ${decision.block_reasons.map((item) => item.message).join("; ") || "audit telemetry unavailable"}`,
|
|
234
|
+
hookSpecificOutput: {
|
|
235
|
+
hookEventName: eventName,
|
|
236
|
+
additionalContext: "SuperSpec subagent telemetry is best-effort and did not block the workflow.",
|
|
237
|
+
},
|
|
238
|
+
};
|
|
239
|
+
}
|
|
228
240
|
return {
|
|
229
241
|
systemMessage: `SuperSpec ${eventName} runlog recorded as ${decision.trust}; strict_profile=${decision.strict_profile}`,
|
|
230
242
|
};
|
|
231
243
|
}
|
|
244
|
+
function inertSubagentOutput(eventName, message) {
|
|
245
|
+
return {
|
|
246
|
+
systemMessage: message,
|
|
247
|
+
hookSpecificOutput: {
|
|
248
|
+
hookEventName: eventName,
|
|
249
|
+
additionalContext: "SuperSpec subagent telemetry is best-effort and did not block the workflow.",
|
|
250
|
+
},
|
|
251
|
+
};
|
|
252
|
+
}
|
|
232
253
|
function writeTempEvent(event) {
|
|
233
254
|
const path = join(tmpdir(), `superspec-hook-event-${randomUUID()}.json`);
|
|
234
255
|
writeFileSync(path, `${JSON.stringify(event)}\n`, { encoding: "utf8", mode: 0o600, flag: "wx" });
|
|
@@ -241,8 +262,9 @@ function validateHookEvent(event) {
|
|
|
241
262
|
if (!["PreToolUse", "PostToolUse", "SubagentStart", "SubagentStop"].includes(eventName)) {
|
|
242
263
|
return `unsupported hook_event_name: ${eventName}`;
|
|
243
264
|
}
|
|
244
|
-
if (typeof event.cwd !== "string" || !event.cwd)
|
|
265
|
+
if ((eventName === "PreToolUse" || eventName === "PostToolUse") && (typeof event.cwd !== "string" || !event.cwd)) {
|
|
245
266
|
return "hook event missing required cwd";
|
|
267
|
+
}
|
|
246
268
|
if (eventName === "PreToolUse") {
|
|
247
269
|
if (typeof event.tool_name !== "string" || !event.tool_name)
|
|
248
270
|
return "PreToolUse hook event missing required tool_name";
|
|
@@ -254,6 +276,7 @@ function validateHookEvent(event) {
|
|
|
254
276
|
export function runHookAdapter(argv = process.argv.slice(2), stdin = readFileSync(0, "utf8")) {
|
|
255
277
|
const parsedArgs = parseArgs(argv);
|
|
256
278
|
let event;
|
|
279
|
+
let eventNameForFailure = "";
|
|
257
280
|
try {
|
|
258
281
|
if (!stdin.trim())
|
|
259
282
|
throw new Error("hook stdin is empty");
|
|
@@ -261,11 +284,16 @@ export function runHookAdapter(argv = process.argv.slice(2), stdin = readFileSyn
|
|
|
261
284
|
if (!isObject(parsed))
|
|
262
285
|
throw new Error("hook stdin must be a JSON object");
|
|
263
286
|
event = parsed;
|
|
287
|
+
eventNameForFailure = typeof event.hook_event_name === "string" ? event.hook_event_name : "";
|
|
264
288
|
const validationError = validateHookEvent(event);
|
|
265
289
|
if (validationError)
|
|
266
290
|
throw new Error(validationError);
|
|
267
291
|
}
|
|
268
292
|
catch (err) {
|
|
293
|
+
if (isSubagentHookEvent(eventNameForFailure)) {
|
|
294
|
+
const payload = inertSubagentOutput(eventNameForFailure, `SuperSpec ${eventNameForFailure} runlog skipped: ${err.message}`);
|
|
295
|
+
return { code: 0, stdout: `${JSON.stringify(payload)}\n`, stderr: "" };
|
|
296
|
+
}
|
|
269
297
|
return { code: 2, stdout: "", stderr: `SuperSpec hook event parse failed: ${err.message}\n` };
|
|
270
298
|
}
|
|
271
299
|
const inference = parsedArgs.change ? { state: "unique", change: parsedArgs.change } : inferChangeFromActiveSessions(event);
|
|
@@ -274,8 +302,9 @@ export function runHookAdapter(argv = process.argv.slice(2), stdin = readFileSyn
|
|
|
274
302
|
const payload = noChangeFallback(event, inference);
|
|
275
303
|
return { code: 0, stdout: `${JSON.stringify(payload)}\n`, stderr: "" };
|
|
276
304
|
}
|
|
277
|
-
|
|
305
|
+
let eventRef = null;
|
|
278
306
|
try {
|
|
307
|
+
eventRef = writeTempEvent(event);
|
|
279
308
|
const eventName = String(event.hook_event_name ?? "");
|
|
280
309
|
let payload;
|
|
281
310
|
if (eventName === "PreToolUse") {
|
|
@@ -298,14 +327,21 @@ export function runHookAdapter(argv = process.argv.slice(2), stdin = readFileSyn
|
|
|
298
327
|
return { code: 0, stdout: `${JSON.stringify(payload)}\n`, stderr: "" };
|
|
299
328
|
}
|
|
300
329
|
catch (err) {
|
|
330
|
+
if (isSubagentHookEvent(String(event.hook_event_name ?? ""))) {
|
|
331
|
+
const eventName = String(event.hook_event_name ?? "");
|
|
332
|
+
const payload = inertSubagentOutput(eventName, `SuperSpec ${eventName} runlog skipped: ${err.message}`);
|
|
333
|
+
return { code: 0, stdout: `${JSON.stringify(payload)}\n`, stderr: "" };
|
|
334
|
+
}
|
|
301
335
|
return { code: 2, stdout: "", stderr: `SuperSpec hook failed closed: ${err.message}\n` };
|
|
302
336
|
}
|
|
303
337
|
finally {
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
338
|
+
if (eventRef) {
|
|
339
|
+
try {
|
|
340
|
+
unlinkSync(resolve(eventRef));
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
// best effort temp cleanup
|
|
344
|
+
}
|
|
309
345
|
}
|
|
310
346
|
}
|
|
311
347
|
}
|
package/dist/src/hooks/health.js
CHANGED
|
@@ -4,18 +4,6 @@ import { reason, sha256_file } from "../util.js";
|
|
|
4
4
|
import { HOOK_ADAPTER_VERSION } from "./types.js";
|
|
5
5
|
const HOOK_COMMAND = 'superspec-hook --change "$SUPERSPEC_CHANGE"';
|
|
6
6
|
const EXPECTED_HOOK_MATRIX = [
|
|
7
|
-
{
|
|
8
|
-
eventName: "PreToolUse",
|
|
9
|
-
matcher: "Bash|apply_patch|Edit|Write|mcp__.*",
|
|
10
|
-
timeout: 120,
|
|
11
|
-
statusMessage: "SuperSpec 写入策略检查",
|
|
12
|
-
},
|
|
13
|
-
{
|
|
14
|
-
eventName: "PostToolUse",
|
|
15
|
-
matcher: "Bash",
|
|
16
|
-
timeout: 120,
|
|
17
|
-
statusMessage: "SuperSpec 运行证据记录",
|
|
18
|
-
},
|
|
19
7
|
{
|
|
20
8
|
eventName: "SubagentStart",
|
|
21
9
|
matcher: ".*",
|
|
@@ -29,6 +29,13 @@ const CODEX_CONFIG_ENTRIES = [
|
|
|
29
29
|
{ table: "agents", key: "max_threads", value: String(CODEX_NATIVE_AGENT_MAX_THREADS) },
|
|
30
30
|
{ table: "agents", key: "max_depth", value: String(CODEX_NATIVE_AGENT_MAX_DEPTH) },
|
|
31
31
|
];
|
|
32
|
+
const HOOK_COMMAND = 'superspec-hook --change "$SUPERSPEC_CHANGE"';
|
|
33
|
+
const LEGACY_FOUR_HOOK_MATRIX = [
|
|
34
|
+
{ eventName: "PreToolUse", matcher: "Bash|apply_patch|Edit|Write|mcp__.*", statusMessage: "SuperSpec 写入策略检查" },
|
|
35
|
+
{ eventName: "PostToolUse", matcher: "Bash", statusMessage: "SuperSpec 运行证据记录" },
|
|
36
|
+
{ eventName: "SubagentStart", matcher: ".*", statusMessage: "SuperSpec 子智能体启动记录" },
|
|
37
|
+
{ eventName: "SubagentStop", matcher: ".*", statusMessage: "SuperSpec 子智能体停止记录" },
|
|
38
|
+
];
|
|
32
39
|
function package_version(packageRoot) {
|
|
33
40
|
try {
|
|
34
41
|
const pkg = JSON.parse(readFileSync(join(packageRoot, "package.json"), "utf8"));
|
|
@@ -38,6 +45,51 @@ function package_version(packageRoot) {
|
|
|
38
45
|
return "0.0.0";
|
|
39
46
|
}
|
|
40
47
|
}
|
|
48
|
+
function has_exact_keys(value, keys) {
|
|
49
|
+
const actual = Object.keys(value).sort();
|
|
50
|
+
const expected = [...keys].sort();
|
|
51
|
+
return actual.length === expected.length && actual.every((key, idx) => key === expected[idx]);
|
|
52
|
+
}
|
|
53
|
+
function legacy_hook_entry_matches(entry, expected) {
|
|
54
|
+
if (!isObject(entry) || !has_exact_keys(entry, ["matcher", "hooks"]))
|
|
55
|
+
return false;
|
|
56
|
+
if (entry.matcher !== expected.matcher || !Array.isArray(entry.hooks) || entry.hooks.length !== 1)
|
|
57
|
+
return false;
|
|
58
|
+
const hook = entry.hooks[0];
|
|
59
|
+
return isObject(hook)
|
|
60
|
+
&& has_exact_keys(hook, ["type", "command", "timeout", "statusMessage"])
|
|
61
|
+
&& hook.type === "command"
|
|
62
|
+
&& hook.command === HOOK_COMMAND
|
|
63
|
+
&& hook.timeout === 120
|
|
64
|
+
&& hook.statusMessage === expected.statusMessage;
|
|
65
|
+
}
|
|
66
|
+
function is_legacy_managed_four_hook_manifest(parsed) {
|
|
67
|
+
if (!isObject(parsed) || !has_exact_keys(parsed, ["superspec", "hooks"]))
|
|
68
|
+
return false;
|
|
69
|
+
if (!isObject(parsed.superspec) || !has_exact_keys(parsed.superspec, ["managed", "adapter_version", "strict_profile_default"]))
|
|
70
|
+
return false;
|
|
71
|
+
if (parsed.superspec.managed !== true
|
|
72
|
+
|| parsed.superspec.adapter_version !== "superspec-hook@2"
|
|
73
|
+
|| parsed.superspec.strict_profile_default !== "audit-only-until-r1-provenance-passes") {
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
if (!isObject(parsed.hooks) || !has_exact_keys(parsed.hooks, LEGACY_FOUR_HOOK_MATRIX.map((entry) => entry.eventName)))
|
|
77
|
+
return false;
|
|
78
|
+
return LEGACY_FOUR_HOOK_MATRIX.every((expected) => {
|
|
79
|
+
const entries = parsed.hooks[expected.eventName];
|
|
80
|
+
return Array.isArray(entries)
|
|
81
|
+
&& entries.length === 1
|
|
82
|
+
&& legacy_hook_entry_matches(entries[0], expected);
|
|
83
|
+
});
|
|
84
|
+
}
|
|
85
|
+
function target_is_legacy_managed_four_hook_manifest(targetAbs) {
|
|
86
|
+
try {
|
|
87
|
+
return is_legacy_managed_four_hook_manifest(JSON.parse(readFileSync(targetAbs, "utf8")));
|
|
88
|
+
}
|
|
89
|
+
catch {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
41
93
|
export function load_install_map(packageRoot = PACKAGE_ROOT) {
|
|
42
94
|
const mapPath = join(packageRoot, INSTALL_MAP_REL);
|
|
43
95
|
if (!existsSync(mapPath))
|
|
@@ -315,6 +367,13 @@ export function install_workflow(repoRoot, opts = {}) {
|
|
|
315
367
|
files.push({ path: mapping.target, sha256: sourceSha, managed: true, preexisting: false });
|
|
316
368
|
actions.push({ action: `install ${mapping.target}`, status: "ok" });
|
|
317
369
|
}
|
|
370
|
+
else if (mapping.kind === "hook" && target_is_legacy_managed_four_hook_manifest(targetAbs)) {
|
|
371
|
+
// Previous SuperSpec default hooks installed PreToolUse/PostToolUse. Re-init must migrate
|
|
372
|
+
// that unmodified managed baseline so old blocking hooks do not stay in the workflow.
|
|
373
|
+
copyFileSync(sourceAbs, targetAbs);
|
|
374
|
+
files.push({ path: mapping.target, sha256: sourceSha, managed: true, preexisting: false });
|
|
375
|
+
actions.push({ action: `install ${mapping.target}`, status: "updated", detail: "legacy managed four-hook manifest migrated to subagent-only manifest" });
|
|
376
|
+
}
|
|
318
377
|
else if (opts.force) {
|
|
319
378
|
copyFileSync(targetAbs, `${targetAbs}.bak`);
|
|
320
379
|
copyFileSync(sourceAbs, targetAbs);
|
|
@@ -323,6 +382,11 @@ export function install_workflow(repoRoot, opts = {}) {
|
|
|
323
382
|
files.push({ path: mapping.target, sha256: sourceSha, managed: true, preexisting: false });
|
|
324
383
|
actions.push({ action: `install ${mapping.target}`, status: "updated", detail: `existing file backed up to ${mapping.target}.bak` });
|
|
325
384
|
}
|
|
385
|
+
else if (mapping.kind === "hook") {
|
|
386
|
+
copyFileSync(sourceAbs, `${targetAbs}.new`);
|
|
387
|
+
files.push({ path: mapping.target, sha256: targetSha, managed: false, preexisting: true });
|
|
388
|
+
actions.push({ action: `install ${mapping.target}`, status: "skipped", detail: `pre-existing hooks manifest kept; current SuperSpec manifest written to ${mapping.target}.new` });
|
|
389
|
+
}
|
|
326
390
|
else {
|
|
327
391
|
// Pre-existing different file: never overwrite, never delete (DISTRIBUTION §5 red line).
|
|
328
392
|
files.push({ path: mapping.target, sha256: targetSha, managed: false, preexisting: true });
|
package/package.json
CHANGED
|
@@ -5,32 +5,6 @@
|
|
|
5
5
|
"strict_profile_default": "audit-only-until-r1-provenance-passes"
|
|
6
6
|
},
|
|
7
7
|
"hooks": {
|
|
8
|
-
"PreToolUse": [
|
|
9
|
-
{
|
|
10
|
-
"matcher": "Bash|apply_patch|Edit|Write|mcp__.*",
|
|
11
|
-
"hooks": [
|
|
12
|
-
{
|
|
13
|
-
"type": "command",
|
|
14
|
-
"command": "superspec-hook --change \"$SUPERSPEC_CHANGE\"",
|
|
15
|
-
"timeout": 120,
|
|
16
|
-
"statusMessage": "SuperSpec 写入策略检查"
|
|
17
|
-
}
|
|
18
|
-
]
|
|
19
|
-
}
|
|
20
|
-
],
|
|
21
|
-
"PostToolUse": [
|
|
22
|
-
{
|
|
23
|
-
"matcher": "Bash",
|
|
24
|
-
"hooks": [
|
|
25
|
-
{
|
|
26
|
-
"type": "command",
|
|
27
|
-
"command": "superspec-hook --change \"$SUPERSPEC_CHANGE\"",
|
|
28
|
-
"timeout": 120,
|
|
29
|
-
"statusMessage": "SuperSpec 运行证据记录"
|
|
30
|
-
}
|
|
31
|
-
]
|
|
32
|
-
}
|
|
33
|
-
],
|
|
34
8
|
"SubagentStart": [
|
|
35
9
|
{
|
|
36
10
|
"matcher": ".*",
|
|
@@ -26,13 +26,6 @@ Apply 按 OpenSpec tasks 执行实现,负责 RED/GREEN 证据、任务勾选
|
|
|
26
26
|
|
|
27
27
|
## 第一条必跑命令
|
|
28
28
|
|
|
29
|
-
先尝试建立当前 change 的 SuperSpec hook session。R-1/provenance 未通过时该命令只会记录 audit-only lease 和降级诊断,不代表 mechanical enforcement 已启用:
|
|
30
|
-
|
|
31
|
-
```text
|
|
32
|
-
superspec guard hook-session-begin --change "<change>" --workflow superspec-apply --entrypoint-token "<fresh-entrypoint-token>" --format agent
|
|
33
|
-
superspec guard hook-session-status --change "<change>" --format agent
|
|
34
|
-
```
|
|
35
|
-
|
|
36
29
|
```text
|
|
37
30
|
superspec guard workflow-packet --change "<change>" --gate apply_ready --format agent
|
|
38
31
|
```
|
|
@@ -26,13 +26,6 @@ Archive 在 `review_complete` allowed 后收尾:确认 archive readiness、保
|
|
|
26
26
|
|
|
27
27
|
## 第一条必跑命令
|
|
28
28
|
|
|
29
|
-
先尝试建立当前 change 的 SuperSpec hook session。R-1/provenance 未通过时该命令只会记录 audit-only lease 和降级诊断,不代表 mechanical enforcement 已启用:
|
|
30
|
-
|
|
31
|
-
```text
|
|
32
|
-
superspec guard hook-session-begin --change "<change>" --workflow superspec-archive --entrypoint-token "<fresh-entrypoint-token>" --format agent
|
|
33
|
-
superspec guard hook-session-status --change "<change>" --format agent
|
|
34
|
-
```
|
|
35
|
-
|
|
36
29
|
```text
|
|
37
30
|
superspec guard workflow-packet --change "<change>" --gate archive_ready --format agent
|
|
38
31
|
```
|
|
@@ -67,7 +60,6 @@ openspec archive -y "<change>"
|
|
|
67
60
|
|
|
68
61
|
```text
|
|
69
62
|
superspec guard check-archived --change "<change>" --format agent
|
|
70
|
-
superspec guard hook-session-end --change "<change>" --reason archived --format agent
|
|
71
63
|
```
|
|
72
64
|
|
|
73
65
|
`.superspec/artifacts/business-invariants.md`、`.superspec/artifacts/test-contract.md`、review/verification evidence、RED/GREEN evidence 和 archive evidence 必须能从 preservation manifest 追溯。
|
|
@@ -26,13 +26,6 @@ Explore 只做需求澄清、代码事实调查、范围边界和风险记录。
|
|
|
26
26
|
|
|
27
27
|
## 第一条必跑命令
|
|
28
28
|
|
|
29
|
-
先尝试建立当前 change 的 SuperSpec hook session。R-1/provenance 未通过时该命令只会记录 audit-only lease 和降级诊断,不代表 mechanical enforcement 已启用:
|
|
30
|
-
|
|
31
|
-
```text
|
|
32
|
-
superspec guard hook-session-begin --change "<change>" --workflow superspec-explore --entrypoint-token "<fresh-entrypoint-token>" --format agent
|
|
33
|
-
superspec guard hook-session-status --change "<change>" --format agent
|
|
34
|
-
```
|
|
35
|
-
|
|
36
29
|
```text
|
|
37
30
|
superspec init --scope project --format agent
|
|
38
31
|
```
|
|
@@ -26,13 +26,6 @@ Propose 把 discovery 转成 OpenSpec proposal package,并补 SuperSpec 业务
|
|
|
26
26
|
|
|
27
27
|
## 第一条必跑命令
|
|
28
28
|
|
|
29
|
-
先尝试建立当前 change 的 SuperSpec hook session。R-1/provenance 未通过时该命令只会记录 audit-only lease 和降级诊断,不代表 mechanical enforcement 已启用:
|
|
30
|
-
|
|
31
|
-
```text
|
|
32
|
-
superspec guard hook-session-begin --change "<change>" --workflow superspec-propose --entrypoint-token "<fresh-entrypoint-token>" --format agent
|
|
33
|
-
superspec guard hook-session-status --change "<change>" --format agent
|
|
34
|
-
```
|
|
35
|
-
|
|
36
29
|
```text
|
|
37
30
|
superspec guard workflow-packet --change "<change>" --gate explore_complete --format agent
|
|
38
31
|
```
|
|
@@ -26,13 +26,6 @@ Review 合并实现审查、架构审查、SuperSpec critic、final verification
|
|
|
26
26
|
|
|
27
27
|
## 第一条必跑命令
|
|
28
28
|
|
|
29
|
-
先尝试建立当前 change 的 SuperSpec hook session。R-1/provenance 未通过时该命令只会记录 audit-only lease 和降级诊断,不代表 mechanical enforcement 已启用:
|
|
30
|
-
|
|
31
|
-
```text
|
|
32
|
-
superspec guard hook-session-begin --change "<change>" --workflow superspec-review --entrypoint-token "<fresh-entrypoint-token>" --format agent
|
|
33
|
-
superspec guard hook-session-status --change "<change>" --format agent
|
|
34
|
-
```
|
|
35
|
-
|
|
36
29
|
```text
|
|
37
30
|
superspec guard check-init --change "<change>" --format agent
|
|
38
31
|
```
|