@cyning/harness 0.3.2 → 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +41 -0
- package/LICENSE +21 -0
- package/README.md +61 -13
- package/docs/ARCHITECTURE.md +1 -1
- package/docs/BENCHMARK_REPORT_TEMPLATE.md +135 -0
- package/docs/ETCLOVG_MAPPING_v1_zh.md +65 -0
- package/docs/ONBOARDING.md +29 -0
- package/docs/PUSH_AUDIT_a3_v1.md +52 -0
- package/docs/RELEASE_v0.4.0.md +89 -0
- package/docs/RELEASE_v1.0.0.md +90 -0
- package/docs/ROADMAP_TO_AGENT_GOVERNANCE.md +209 -0
- package/docs/methodology/AUDIT_doc_consistency_2026-06-15_zh.md +5 -3
- package/docs/methodology/README.md +7 -6
- package/docs/methodology/ROADMAP_v1_zh.md +23 -11
- package/docs/methodology/execution/PILOT_EVIDENCE_B2_v1_zh.md +45 -37
- package/docs/methodology/execution/TEMPLATE_HG_RELEASE_v1_zh.md +57 -0
- package/docs/methodology/graph/HARNESS_GRAPH_MODEL_design_v0_zh.md +24 -23
- package/docs/methodology/graph/HARNESS_GRAPH_MODEL_dialogue_archive_v1_zh.md +2 -2
- package/docs/methodology/graph/README.md +2 -2
- package/docs/methodology/product/DESIGN_ONTOLOGY_v1_zh.md +2 -2
- package/docs/methodology/prompts/PROMPT_article_theory_roundtable_v1_zh.md +3 -3
- package/docs/methodology/prompts/PROMPT_doc_consistency_audit_v1_zh.md +4 -4
- package/examples/compliance_bench/README.md +28 -0
- package/examples/compliance_bench/S1_r1_pending/task.md +30 -0
- package/examples/compliance_bench/S2_r1_no_review/task.md +31 -0
- package/examples/compliance_bench/S3_r1_with_review/reviews/s3_r1_with_review_audit_R1_20260616.md +18 -0
- package/examples/compliance_bench/S3_r1_with_review/task.md +31 -0
- package/examples/compliance_bench/S4_sync_domain/profile.json +16 -0
- package/examples/demo_checkout/ACCEPTANCE.md +16 -3
- package/examples/oss-fork/README.md +3 -4
- package/golden/POINTER_gold_epic_serial.md +11 -50
- package/golden/README.md +6 -6
- package/harness/prompts/README.md +4 -4
- package/lib/audit.js +168 -0
- package/lib/cli.js +50 -0
- package/ontology.yaml +93 -0
- package/package.json +3 -1
- package/schema/invoke_index.v1.schema.json +53 -0
- package/standards/POINTER_workspace_truth_v1_zh.md +11 -26
- package/wizard/compliance-bench.sh +156 -0
- package/wizard/gate-check.sh +139 -19
- package/wizard/harness-sync.sh +16 -3
- package/wizard/lib/generate-invoke-index.js +60 -0
package/lib/audit.js
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { loadTaskSidecar, validateTaskSidecar } from './task.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* 在目标仓运行 ICVO audit(D3/D5/S5 子集)。
|
|
8
|
+
*/
|
|
9
|
+
export function auditTarget(target, options = {}) {
|
|
10
|
+
const taskFile = options.taskFile;
|
|
11
|
+
|
|
12
|
+
const gate = runGateCheck(target, taskFile);
|
|
13
|
+
const testCheck = runTestCheck(target, taskFile);
|
|
14
|
+
const gitCheck = runGitCleanCheck(target);
|
|
15
|
+
|
|
16
|
+
const ok = gate.ok && testCheck.ok;
|
|
17
|
+
|
|
18
|
+
return {
|
|
19
|
+
ok,
|
|
20
|
+
target,
|
|
21
|
+
taskFile,
|
|
22
|
+
gate,
|
|
23
|
+
test: testCheck,
|
|
24
|
+
git: gitCheck,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function runGateCheck(target, taskFile) {
|
|
29
|
+
const script = path.join(
|
|
30
|
+
process.env.CYNING_HARNESS || process.cwd(),
|
|
31
|
+
'wizard',
|
|
32
|
+
'gate-check.sh',
|
|
33
|
+
);
|
|
34
|
+
const args = [script, '--target', target];
|
|
35
|
+
if (taskFile) args.push('--task', taskFile);
|
|
36
|
+
|
|
37
|
+
const result = spawnSync('bash', args, {
|
|
38
|
+
encoding: 'utf8',
|
|
39
|
+
env: process.env,
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
return {
|
|
43
|
+
ok: result.status === 0,
|
|
44
|
+
status: result.status,
|
|
45
|
+
stdout: result.stdout,
|
|
46
|
+
stderr: result.stderr,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function runTestCheck(target, taskFile) {
|
|
51
|
+
// 未指定 task 文件时,D5 跳过(在 active task 上跑 gate-check 已覆盖 D3)
|
|
52
|
+
if (!taskFile) {
|
|
53
|
+
return { ok: true, skipped: true, reason: '未指定 --task,跳过 D5' };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const absTask = path.isAbsolute(taskFile) ? taskFile : path.join(target, taskFile);
|
|
57
|
+
if (!fs.existsSync(absTask)) {
|
|
58
|
+
return { ok: true, skipped: true, reason: 'task 文件不存在,跳过 D5' };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const sidecar = resolveTaskSidecar(absTask, target);
|
|
62
|
+
if (!sidecar) {
|
|
63
|
+
return { ok: true, skipped: true, reason: '未找到 task sidecar,跳过 D5' };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let data;
|
|
67
|
+
try {
|
|
68
|
+
data = loadTaskSidecar(sidecar);
|
|
69
|
+
} catch (err) {
|
|
70
|
+
return { ok: false, reason: `sidecar 解析失败: ${err.message}` };
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const validation = validateTaskSidecar(data, path.basename(sidecar));
|
|
74
|
+
if (!validation.ok) {
|
|
75
|
+
return { ok: false, reason: validation.errors.join('; ') };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (data.test_strategy !== 'required') {
|
|
79
|
+
return {
|
|
80
|
+
ok: true,
|
|
81
|
+
skipped: true,
|
|
82
|
+
reason: `test_strategy=${data.test_strategy},无需 D5 强检查`,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const hasTest = hasTestArtifacts(target);
|
|
87
|
+
if (!hasTest) {
|
|
88
|
+
return {
|
|
89
|
+
ok: false,
|
|
90
|
+
reason: 'test_strategy=required 但目标仓未声明测试路径或 CI 引用',
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return { ok: true, reason: 'test_strategy=required 且检测到测试/CI 制品' };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
function resolveTaskSidecar(taskMarkdownPath, target) {
|
|
98
|
+
const dir = path.dirname(taskMarkdownPath);
|
|
99
|
+
const base = path.basename(taskMarkdownPath, '.md');
|
|
100
|
+
|
|
101
|
+
// 1. 同目录 <name>.harness.json
|
|
102
|
+
const sameDir = path.join(dir, `${base}.harness.json`);
|
|
103
|
+
if (fs.existsSync(sameDir)) return sameDir;
|
|
104
|
+
|
|
105
|
+
// 2. 目标仓根 task.harness.v1.json(legacy / 单 task 仓)
|
|
106
|
+
const rootSidecar = path.join(target, 'task.harness.v1.json');
|
|
107
|
+
if (fs.existsSync(rootSidecar)) return rootSidecar;
|
|
108
|
+
|
|
109
|
+
return null;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function hasTestArtifacts(target) {
|
|
113
|
+
const probes = [
|
|
114
|
+
'test',
|
|
115
|
+
'tests',
|
|
116
|
+
'spec',
|
|
117
|
+
'specs',
|
|
118
|
+
'__tests__',
|
|
119
|
+
'jest.config.js',
|
|
120
|
+
'jest.config.ts',
|
|
121
|
+
'vitest.config.js',
|
|
122
|
+
'vitest.config.ts',
|
|
123
|
+
'playwright.config.js',
|
|
124
|
+
'playwright.config.ts',
|
|
125
|
+
'cypress.config.js',
|
|
126
|
+
'pytest.ini',
|
|
127
|
+
'pyproject.toml',
|
|
128
|
+
'setup.py',
|
|
129
|
+
];
|
|
130
|
+
|
|
131
|
+
for (const p of probes) {
|
|
132
|
+
const full = path.join(target, p);
|
|
133
|
+
if (fs.existsSync(full)) return true;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// 任意 *.test.js / *.spec.js / *.test.ts / *.spec.ts
|
|
137
|
+
const jsTests = spawnSync(
|
|
138
|
+
'find',
|
|
139
|
+
[target, '-maxdepth', '3', '-type', 'f', '(', '-name', '*.test.js', '-o', '-name', '*.spec.js', '-o', '-name', '*.test.ts', '-o', '-name', '*.spec.ts', '-o', '-name', '*_test.py', '-o', '-name', 'test_*.py', ')'],
|
|
140
|
+
{ encoding: 'utf8' },
|
|
141
|
+
);
|
|
142
|
+
if (jsTests.status === 0 && jsTests.stdout.trim()) return true;
|
|
143
|
+
|
|
144
|
+
// CI 工作流引用
|
|
145
|
+
const ciDir = path.join(target, '.github', 'workflows');
|
|
146
|
+
if (fs.existsSync(ciDir)) {
|
|
147
|
+
const files = fs.readdirSync(ciDir);
|
|
148
|
+
if (files.some((f) => f.endsWith('.yml') || f.endsWith('.yaml'))) return true;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return false;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function runGitCleanCheck(target) {
|
|
155
|
+
const result = spawnSync('git', ['status', '--porcelain'], {
|
|
156
|
+
encoding: 'utf8',
|
|
157
|
+
cwd: target,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const dirty = result.status === 0 && result.stdout.trim().length > 0;
|
|
161
|
+
|
|
162
|
+
return {
|
|
163
|
+
ok: true, // audit 不因此失败,仅 warn
|
|
164
|
+
dirty,
|
|
165
|
+
stdout: result.stdout,
|
|
166
|
+
warning: dirty ? '工作区未 clean(S5):建议 commit 后再执行 apply' : null,
|
|
167
|
+
};
|
|
168
|
+
}
|
package/lib/cli.js
CHANGED
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
} from './paths.js';
|
|
8
8
|
import { runBash } from './run.js';
|
|
9
9
|
import { checkTaskFile } from './task.js';
|
|
10
|
+
import { auditTarget } from './audit.js';
|
|
10
11
|
|
|
11
12
|
function usage() {
|
|
12
13
|
console.log(`@cyning/harness · cyning-harness CLI (v0.3+)
|
|
@@ -15,12 +16,14 @@ function usage() {
|
|
|
15
16
|
npx @cyning/harness init [--preset NAME] [--ide LIST] [--target PATH] [--yes]
|
|
16
17
|
npx @cyning/harness upgrade [--target PATH] [--ide LIST] [--yes] [--force] [--gate-check]
|
|
17
18
|
npx @cyning/harness check [--target PATH]
|
|
19
|
+
npx @cyning/harness audit [--target PATH] [--task FILE]
|
|
18
20
|
npx @cyning/harness task check --file PATH [--no-circular] [--registry DIR]...
|
|
19
21
|
|
|
20
22
|
说明:
|
|
21
23
|
init 首次接入 · 内部调用 wizard/install.sh + harness-sync.sh apply
|
|
22
24
|
upgrade 升级已接入仓 · 等价 wizard/upgrade.sh --yes
|
|
23
25
|
check 读 manifest.json · 对比当前包版本是否有可升级版本
|
|
26
|
+
audit ICVO 机械审计:复用 gate-check · D5 测试声明检查 · S5 git-clean 提醒
|
|
24
27
|
task check 校验 task.harness.v1.json sidecar · --no-circular 检测 depends_on 环
|
|
25
28
|
|
|
26
29
|
环境:
|
|
@@ -62,6 +65,10 @@ export async function runCli(argv) {
|
|
|
62
65
|
await cmdCheck(rest, harnessRoot, pkgVersion);
|
|
63
66
|
return;
|
|
64
67
|
}
|
|
68
|
+
if (cmd === 'audit') {
|
|
69
|
+
await cmdAudit(rest);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
65
72
|
if (cmd === 'task') {
|
|
66
73
|
await cmdTask(rest);
|
|
67
74
|
return;
|
|
@@ -186,6 +193,49 @@ async function cmdCheck(args, harnessRoot, pkgVersion) {
|
|
|
186
193
|
}
|
|
187
194
|
}
|
|
188
195
|
|
|
196
|
+
async function cmdAudit(args) {
|
|
197
|
+
let rest = args;
|
|
198
|
+
const { value: targetArg, rest: r1 } = takeOption(rest, '--target');
|
|
199
|
+
rest = r1;
|
|
200
|
+
const { value: taskFile, rest: r2 } = takeOption(rest, '--task');
|
|
201
|
+
rest = r2;
|
|
202
|
+
|
|
203
|
+
if (rest.length > 0) {
|
|
204
|
+
const err = new Error(`audit 未知参数: ${rest.join(' ')}`);
|
|
205
|
+
err.exitCode = 1;
|
|
206
|
+
throw err;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const target = resolveTarget(process.cwd(), targetArg);
|
|
210
|
+
const result = auditTarget(target, { taskFile });
|
|
211
|
+
|
|
212
|
+
console.log(`目标: ${target}`);
|
|
213
|
+
if (taskFile) console.log(`task: ${taskFile}`);
|
|
214
|
+
console.log(`audit: ${result.ok ? 'PASS' : 'FAIL'}`);
|
|
215
|
+
|
|
216
|
+
// D3 gate-check
|
|
217
|
+
console.log(` gate-check: ${result.gate.ok ? 'PASS' : 'FAIL'}`);
|
|
218
|
+
if (!result.gate.ok && result.gate.stdout) {
|
|
219
|
+
for (const line of result.gate.stdout.split('\n')) {
|
|
220
|
+
if (line.trim()) console.log(` ${line}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// D5 test
|
|
225
|
+
console.log(` test-check: ${result.test.ok ? 'PASS' : 'FAIL'}`);
|
|
226
|
+
if (result.test.reason) console.log(` ${result.test.reason}`);
|
|
227
|
+
|
|
228
|
+
// S5 git-clean
|
|
229
|
+
console.log(` git-clean: ${result.git.dirty ? 'DIRTY' : 'CLEAN'}`);
|
|
230
|
+
if (result.git.warning) console.log(` ${result.git.warning}`);
|
|
231
|
+
|
|
232
|
+
if (!result.ok) {
|
|
233
|
+
const err = new Error('ICVO audit 未通过');
|
|
234
|
+
err.exitCode = 2;
|
|
235
|
+
throw err;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
189
239
|
async function cmdTask(args) {
|
|
190
240
|
const [sub, ...rest] = args;
|
|
191
241
|
if (sub !== 'check') {
|
package/ontology.yaml
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# cyning-harness 产品设计本体 · 机器可读抽取(草案 v1.0.0)
|
|
2
|
+
# 人类真值:docs/methodology/product/DESIGN_ONTOLOGY_v1_zh.md
|
|
3
|
+
# 冲突时以 Markdown 为准 · 供未来 harness ontology-check 使用
|
|
4
|
+
|
|
5
|
+
version: "1.2"
|
|
6
|
+
product_semver: "1.0.0"
|
|
7
|
+
license: MIT
|
|
8
|
+
|
|
9
|
+
classes:
|
|
10
|
+
- id: Track
|
|
11
|
+
domain: Package
|
|
12
|
+
subclasses: [GraphTrack, WikiTrack, StandardsTrack, ProcessTrack, VerifyTrack, IDETrack]
|
|
13
|
+
- id: Template
|
|
14
|
+
domain: Package
|
|
15
|
+
- id: Preset
|
|
16
|
+
domain: Package
|
|
17
|
+
- id: WizardTool
|
|
18
|
+
domain: Package
|
|
19
|
+
- id: IDEFragment
|
|
20
|
+
domain: Package
|
|
21
|
+
- id: AdoptedProfile
|
|
22
|
+
domain: Instance
|
|
23
|
+
- id: VersionManifest
|
|
24
|
+
domain: Instance
|
|
25
|
+
- id: Task
|
|
26
|
+
domain: Instance
|
|
27
|
+
- id: Hat
|
|
28
|
+
domain: Instance
|
|
29
|
+
- id: HumanGate
|
|
30
|
+
domain: Instance
|
|
31
|
+
- id: InvokeSnapshot
|
|
32
|
+
domain: Instance
|
|
33
|
+
- id: AuditReview
|
|
34
|
+
domain: Instance
|
|
35
|
+
- id: InformArtifact
|
|
36
|
+
domain: Instance
|
|
37
|
+
- id: ConstrainArtifact
|
|
38
|
+
domain: Instance
|
|
39
|
+
- id: VerifyArtifact
|
|
40
|
+
domain: Instance
|
|
41
|
+
|
|
42
|
+
relations:
|
|
43
|
+
- id: embedsInto
|
|
44
|
+
subject: DisciplinePackage
|
|
45
|
+
object: BusinessRepository
|
|
46
|
+
cardinality: "1..*"
|
|
47
|
+
- id: blocks
|
|
48
|
+
subject: HumanGate
|
|
49
|
+
object: Hat
|
|
50
|
+
cardinality: "1..*"
|
|
51
|
+
- id: governedBy
|
|
52
|
+
subject: Task
|
|
53
|
+
object: Hat
|
|
54
|
+
cardinality: "1..*"
|
|
55
|
+
- id: produces
|
|
56
|
+
subject: Hat
|
|
57
|
+
object: TraceArtifact
|
|
58
|
+
cardinality: "0..*"
|
|
59
|
+
|
|
60
|
+
axioms:
|
|
61
|
+
- id: P1
|
|
62
|
+
text: "纪律包不含业务代码与 LLM Runtime"
|
|
63
|
+
- id: S2
|
|
64
|
+
text: "禁止 sync 覆盖 docs/tasks、reviews、invokes/by-task"
|
|
65
|
+
- id: S5
|
|
66
|
+
text: "harness-sync apply 前须 git-clean(可 --force 跳过)"
|
|
67
|
+
- id: D2
|
|
68
|
+
text: "HG-AUDIT-R1 pending 时 22 不得附 30 Prompt"
|
|
69
|
+
- id: D7
|
|
70
|
+
text: "public push 须 HG-RELEASE 人闸 checklist 全勾"
|
|
71
|
+
|
|
72
|
+
starter_hats:
|
|
73
|
+
- hat_id: "10-requirements"
|
|
74
|
+
role: RequirementsHat
|
|
75
|
+
- hat_id: "22-task-audit"
|
|
76
|
+
role: TaskAuditHat
|
|
77
|
+
- hat_id: "30-execute-code"
|
|
78
|
+
role: ExecuteHat
|
|
79
|
+
- hat_id: "40-self-check"
|
|
80
|
+
role: SelfCheckHat
|
|
81
|
+
|
|
82
|
+
human_gates:
|
|
83
|
+
- id: HG-TASK-DRAFT
|
|
84
|
+
blocks_hats: ["22-task-audit", "30-execute-code"]
|
|
85
|
+
- id: HG-AUDIT-R1
|
|
86
|
+
blocks_hats: ["30-execute-code"]
|
|
87
|
+
- id: HG-RELEASE
|
|
88
|
+
blocks_hats: []
|
|
89
|
+
note: "维护者 CLOSE · npm/GitHub public · 非 Agent 自动签"
|
|
90
|
+
|
|
91
|
+
schemas:
|
|
92
|
+
manifest: "./schema/manifest.v1.schema.json"
|
|
93
|
+
task_sidecar: "./schema/task.harness.v1.schema.json"
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cyning/harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "1.0.0",
|
|
4
4
|
"description": "cyning-harness discipline package · init / upgrade / check CLI",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -26,6 +26,8 @@
|
|
|
26
26
|
"docs/",
|
|
27
27
|
"README.md",
|
|
28
28
|
"CHANGELOG.md",
|
|
29
|
+
"LICENSE",
|
|
30
|
+
"ontology.yaml",
|
|
29
31
|
"AGENTS.md"
|
|
30
32
|
],
|
|
31
33
|
"scripts": {
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://cyning.me/harness/schemas/invoke_index.v1.schema.json",
|
|
4
|
+
"title": "Harness InvokeSnapshot Index v1",
|
|
5
|
+
"description": "由 harness-sync --index 生成的 invokes/by-task 机器索引 · 只读聚合 · 不覆盖 S2 域",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["schema_version", "generated_at", "index"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"schema_version": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"const": "1"
|
|
12
|
+
},
|
|
13
|
+
"generated_at": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"format": "date-time"
|
|
16
|
+
},
|
|
17
|
+
"index": {
|
|
18
|
+
"type": "object",
|
|
19
|
+
"description": "task_slug → 该 task 的 invoke 列表",
|
|
20
|
+
"additionalProperties": {
|
|
21
|
+
"type": "object",
|
|
22
|
+
"required": ["task_slug", "invokes"],
|
|
23
|
+
"properties": {
|
|
24
|
+
"task_slug": {
|
|
25
|
+
"type": "string",
|
|
26
|
+
"pattern": "^[a-z0-9][a-z0-9_-]*$"
|
|
27
|
+
},
|
|
28
|
+
"invokes": {
|
|
29
|
+
"type": "array",
|
|
30
|
+
"items": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"required": ["path", "mtime", "hat_id"],
|
|
33
|
+
"properties": {
|
|
34
|
+
"path": {
|
|
35
|
+
"type": "string",
|
|
36
|
+
"description": "相对业务仓根的 invoke 文件路径"
|
|
37
|
+
},
|
|
38
|
+
"mtime": {
|
|
39
|
+
"type": "integer",
|
|
40
|
+
"description": "文件修改时间戳(秒)"
|
|
41
|
+
},
|
|
42
|
+
"hat_id": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"description": "从文件名解析的帽 ID,如 30 / 40 / CLOSE / HG-RELEASE"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
# POINTER ·
|
|
1
|
+
# POINTER · 工作区编码规范真值(示例 · 不复制全文)
|
|
2
2
|
|
|
3
3
|
| 项 | 内容 |
|
|
4
4
|
| --- | --- |
|
|
5
5
|
| **状态** | `active` |
|
|
6
|
-
| **日期** | 2026-06-
|
|
7
|
-
| **性质** | **只读指针**;Starter 交付 TEMPLATE
|
|
6
|
+
| **日期** | 2026-06-15(A3 脱敏) |
|
|
7
|
+
| **性质** | **只读指针**;Starter 交付 TEMPLATE,各业务仓维护 L1/L2 **active** 条文 |
|
|
8
8
|
|
|
9
9
|
---
|
|
10
10
|
|
|
@@ -12,42 +12,26 @@
|
|
|
12
12
|
|
|
13
13
|
| 规则 | 说明 |
|
|
14
14
|
| --- | --- |
|
|
15
|
-
| **禁止双维护** |
|
|
16
|
-
| **嵌入用户仓** | 从本目录 **TEMPLATE_*** 复制生成用户仓 `docs/standards/`;按需 **POINTER**
|
|
15
|
+
| **禁止双维护** | 不得将某业务仓 `docs/standards/` L1/L2 **全文**复制进 `cyning-harness` 产品仓 |
|
|
16
|
+
| **嵌入用户仓** | 从本目录 **TEMPLATE_*** 复制生成用户仓 `docs/standards/`;按需 **POINTER** 回链源仓 |
|
|
17
17
|
| **冲突** | task + 图谱 + PROJECT_CONFIG > 用户仓已嵌入 L1/L2 > 本 POINTER |
|
|
18
18
|
|
|
19
19
|
---
|
|
20
20
|
|
|
21
|
-
##
|
|
21
|
+
## 使用方式
|
|
22
22
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
| --- | --- | --- |
|
|
27
|
-
| **L1 基线** | `docs/standards/CODING_BASELINE_L1_v1_zh.md` | `active` · R1 2026-06-09 |
|
|
28
|
-
| **L2 前端** | `ai-ink-brain/docs/standards/CODING_FRONTEND_L2_v1_zh.md` | `active` |
|
|
29
|
-
| **L2 后端** | `ai-ink-brain-api-python/docs/standards/CODING_BACKEND_L2_v1_zh.md` | `active` |
|
|
30
|
-
| **外部参考** | `docs/standards/SOURCES_编码规范外部参考_v1_zh.md` | `draft` |
|
|
31
|
-
| **改进大纲** | `docs/standards/00_OUTLINE_工程编码规范改进_v1_zh.md` | 规划 |
|
|
32
|
-
| **Harness 衔接** | `docs/harness/guides/GUIDANCE_standards_in_harness_starter_m2_v1_zh.md` | M2 打包真值 |
|
|
23
|
+
1. **默认**:仅用本目录 [`TEMPLATE_CODING_BASELINE_L1_v1_zh.md`](TEMPLATE_CODING_BASELINE_L1_v1_zh.md) 等模板
|
|
24
|
+
2. **有上游规范仓时**:在用户仓 README 追加 POINTER 链(GitHub 路径 · 非本地绝对路径)
|
|
25
|
+
3. **禁止** 将私有工作区 invoke / task 正文 bulk 复制进产品包
|
|
33
26
|
|
|
34
27
|
---
|
|
35
28
|
|
|
36
|
-
## 治理仓(可选 ·
|
|
29
|
+
## 治理仓(可选 · 公开)
|
|
37
30
|
|
|
38
31
|
| 仓 | 用途 |
|
|
39
32
|
| --- | --- |
|
|
40
33
|
| [`cyning-ai-coding-governance`](https://github.com/Cyning12/cyning-ai-coding-governance) | 方法论 · L3/L2/L1 整合导读 · 对比研究 |
|
|
41
34
|
|
|
42
|
-
本地镜像(若有):`Projects/ai_coding_governance/methodology/`
|
|
43
|
-
|
|
44
|
-
---
|
|
45
|
-
|
|
46
|
-
## M2 金样来源(后续 T6)
|
|
47
|
-
|
|
48
|
-
- 编码规范 Epic:`ai-ink-brain-api-python/docs/tasks/active/task_standards_backend_api_modularization_manifest_v1.md`
|
|
49
|
-
- 过程 invoke:工作区 `docs/harness/invokes/by-task/`(**不**复制进产品仓)
|
|
50
|
-
|
|
51
35
|
---
|
|
52
36
|
|
|
53
37
|
## 修订记录
|
|
@@ -55,3 +39,4 @@
|
|
|
55
39
|
| 日期 | 说明 |
|
|
56
40
|
| --- | --- |
|
|
57
41
|
| 2026-06-09 | M2 T2:TEMPLATE + POINTER 首版 |
|
|
42
|
+
| 2026-06-15 | A3 public push:移除私有工作区路径与 Ink 内部 task 枚举 |
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# SDD-Compliance micro-bench · v1 · S1–S4
|
|
3
|
+
# 输出合规率 %;不测 LLM 解题,只测 Orchestrate/Verify 可机械部分。
|
|
4
|
+
set -euo pipefail
|
|
5
|
+
|
|
6
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
7
|
+
HARNESS_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
|
|
8
|
+
BENCH_DIR="$HARNESS_ROOT/examples/compliance_bench"
|
|
9
|
+
|
|
10
|
+
SCENARIOS=()
|
|
11
|
+
QUIET=0
|
|
12
|
+
|
|
13
|
+
usage() {
|
|
14
|
+
cat <<'EOF'
|
|
15
|
+
用法:compliance-bench.sh [--all | S1 S2 S3 S4] [--quiet]
|
|
16
|
+
|
|
17
|
+
--all 运行 S1–S4
|
|
18
|
+
--quiet 仅输出最终合规率
|
|
19
|
+
EOF
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
while [[ $# -gt 0 ]]; do
|
|
23
|
+
case "$1" in
|
|
24
|
+
--all) SCENARIOS=(S1 S2 S3 S4); shift ;;
|
|
25
|
+
--quiet) QUIET=1; shift ;;
|
|
26
|
+
S1|S2|S3|S4) SCENARIOS+=("$1"); shift ;;
|
|
27
|
+
-h|--help) usage; exit 0 ;;
|
|
28
|
+
*) echo "未知参数: $1" >&2; usage; exit 1 ;;
|
|
29
|
+
esac
|
|
30
|
+
done
|
|
31
|
+
|
|
32
|
+
[[ ${#SCENARIOS[@]} -gt 0 ]] || { usage; exit 1; }
|
|
33
|
+
|
|
34
|
+
log() { [[ "$QUIET" == "1" ]] || echo "$@"; }
|
|
35
|
+
|
|
36
|
+
PASS=0
|
|
37
|
+
FAIL=0
|
|
38
|
+
RESULTS=()
|
|
39
|
+
|
|
40
|
+
run_gate() {
|
|
41
|
+
local target="$1" task="$2"
|
|
42
|
+
"$HARNESS_ROOT/wizard/gate-check.sh" --target "$target" --task "$task" >/dev/null 2>&1 || true
|
|
43
|
+
# 返回实际 exit code
|
|
44
|
+
"$HARNESS_ROOT/wizard/gate-check.sh" --target "$target" --task "$task" >/dev/null 2>&1
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
scenario_S1() {
|
|
48
|
+
local dir="$BENCH_DIR/S1_r1_pending"
|
|
49
|
+
log "=== S1 · R1 pending 即 30(应拒) ==="
|
|
50
|
+
if ! "$HARNESS_ROOT/wizard/gate-check.sh" --target "$dir" --task task.md >/dev/null 2>&1; then
|
|
51
|
+
log "S1 PASS · gate-check 非零,30 被拒"
|
|
52
|
+
PASS=$((PASS+1))
|
|
53
|
+
RESULTS+=("S1|PASS")
|
|
54
|
+
else
|
|
55
|
+
log "S1 FAIL · gate-check exit 0,未拦截 pending R1"
|
|
56
|
+
FAIL=$((FAIL+1))
|
|
57
|
+
RESULTS+=("S1|FAIL")
|
|
58
|
+
fi
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
scenario_S2() {
|
|
62
|
+
local dir="$BENCH_DIR/S2_r1_no_review"
|
|
63
|
+
log "=== S2 · R1 approved 但无 review 文件(应非合规) ==="
|
|
64
|
+
local gate_ok=0
|
|
65
|
+
"$HARNESS_ROOT/wizard/gate-check.sh" --target "$dir" --task task.md >/dev/null 2>&1 && gate_ok=1 || true
|
|
66
|
+
local review_exists=0
|
|
67
|
+
[[ -n "$(find "$dir/reviews" -type f -name '*_audit_R1_*' 2>/dev/null)" ]] && review_exists=1 || true
|
|
68
|
+
|
|
69
|
+
if [[ "$review_exists" == "0" ]]; then
|
|
70
|
+
log "S2 PASS · review 文件缺失 -> 非合规 (gate-check exit=$gate_ok)"
|
|
71
|
+
PASS=$((PASS+1))
|
|
72
|
+
RESULTS+=("S2|PASS")
|
|
73
|
+
else
|
|
74
|
+
log "S2 FAIL · 不应存在 review 文件"
|
|
75
|
+
FAIL=$((FAIL+1))
|
|
76
|
+
RESULTS+=("S2|FAIL")
|
|
77
|
+
fi
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
scenario_S3() {
|
|
81
|
+
local dir="$BENCH_DIR/S3_r1_with_review"
|
|
82
|
+
log "=== S3 · R1 approved + review 落盘(应合规) ==="
|
|
83
|
+
local gate_ok=0
|
|
84
|
+
"$HARNESS_ROOT/wizard/gate-check.sh" --target "$dir" --task task.md >/dev/null 2>&1 && gate_ok=1 || true
|
|
85
|
+
local review_exists=0
|
|
86
|
+
[[ -n "$(find "$dir/reviews" -type f -name '*_audit_R1_*' 2>/dev/null)" ]] && review_exists=1 || true
|
|
87
|
+
|
|
88
|
+
if [[ "$gate_ok" == "1" && "$review_exists" == "1" ]]; then
|
|
89
|
+
log "S3 PASS · gate-check exit 0 且 review 文件存在"
|
|
90
|
+
PASS=$((PASS+1))
|
|
91
|
+
RESULTS+=("S3|PASS")
|
|
92
|
+
else
|
|
93
|
+
log "S3 FAIL · gate_ok=$gate_ok review_exists=$review_exists"
|
|
94
|
+
FAIL=$((FAIL+1))
|
|
95
|
+
RESULTS+=("S3|FAIL")
|
|
96
|
+
fi
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
scenario_S4() {
|
|
100
|
+
local dir="$BENCH_DIR/S4_sync_domain"
|
|
101
|
+
log "=== S4 · sync plan 不得含 task/reviews 路径 ==="
|
|
102
|
+
# 构造临时目标仓结构
|
|
103
|
+
local tmp
|
|
104
|
+
tmp="$(mktemp -d)"
|
|
105
|
+
mkdir -p "$tmp/.cyning-harness"
|
|
106
|
+
cp "$dir/profile.json" "$tmp/.cyning-harness/profile.json"
|
|
107
|
+
mkdir -p "$tmp/docs/harness/prompts" "$tmp/docs/harness/invokes" "$tmp/.cursor/rules"
|
|
108
|
+
|
|
109
|
+
local plan
|
|
110
|
+
plan="$tmp/plan.txt"
|
|
111
|
+
"$HARNESS_ROOT/wizard/harness-sync.sh" plan --target "$tmp" >"$plan" 2>&1 || true
|
|
112
|
+
|
|
113
|
+
local bad=0
|
|
114
|
+
if grep -E 'docs/tasks/|docs/harness/reviews/' "$plan" >/dev/null 2>&1; then
|
|
115
|
+
bad=1
|
|
116
|
+
fi
|
|
117
|
+
rm -rf "$tmp"
|
|
118
|
+
|
|
119
|
+
if [[ "$bad" == "0" ]]; then
|
|
120
|
+
log "S4 PASS · sync plan 未包含 task/reviews 路径"
|
|
121
|
+
PASS=$((PASS+1))
|
|
122
|
+
RESULTS+=("S4|PASS")
|
|
123
|
+
else
|
|
124
|
+
log "S4 FAIL · sync plan 包含不应同步的 task/reviews 路径"
|
|
125
|
+
FAIL=$((FAIL+1))
|
|
126
|
+
RESULTS+=("S4|FAIL")
|
|
127
|
+
fi
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
for s in "${SCENARIOS[@]}"; do
|
|
131
|
+
case "$s" in
|
|
132
|
+
S1) scenario_S1 ;;
|
|
133
|
+
S2) scenario_S2 ;;
|
|
134
|
+
S3) scenario_S3 ;;
|
|
135
|
+
S4) scenario_S4 ;;
|
|
136
|
+
esac
|
|
137
|
+
done
|
|
138
|
+
|
|
139
|
+
TOTAL=$((PASS+FAIL))
|
|
140
|
+
RATE=0
|
|
141
|
+
[[ "$TOTAL" -gt 0 ]] && RATE=$((PASS*100/TOTAL))
|
|
142
|
+
|
|
143
|
+
log ""
|
|
144
|
+
log "=== SDD-Compliance bench summary ==="
|
|
145
|
+
for r in "${RESULTS[@]}"; do
|
|
146
|
+
IFS='|' read -r id status <<<"$r"
|
|
147
|
+
log "$id: $status"
|
|
148
|
+
done
|
|
149
|
+
log "合规率: $PASS/$TOTAL = $RATE%"
|
|
150
|
+
|
|
151
|
+
# 静默模式仅输出数字
|
|
152
|
+
if [[ "$QUIET" == "1" ]]; then
|
|
153
|
+
echo "$RATE"
|
|
154
|
+
fi
|
|
155
|
+
|
|
156
|
+
[[ "$FAIL" -eq 0 ]]
|