openone-workflow-kit 0.1.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.
Files changed (109) hide show
  1. package/CHANGELOG.md +21 -0
  2. package/CODE_OF_CONDUCT.md +25 -0
  3. package/CONTRIBUTING.md +51 -0
  4. package/INIT.md +81 -0
  5. package/LICENSE +201 -0
  6. package/NOTICE +6 -0
  7. package/README.md +195 -0
  8. package/SECURITY.md +39 -0
  9. package/bin/check-contract.cjs +137 -0
  10. package/bin/check-sanitized.cjs +93 -0
  11. package/bin/init-workspace.cjs +966 -0
  12. package/docs/assets/PROVENANCE.md +20 -0
  13. package/docs/assets/architecture.svg +112 -0
  14. package/docs/assets/hero.svg +83 -0
  15. package/docs/assets/quick-demo.svg +74 -0
  16. package/docs/assets/social-preview.svg +57 -0
  17. package/docs/assets/visual-manifest.json +52 -0
  18. package/docs/definition-of-done.md +80 -0
  19. package/docs/dual-track-workflow.md +90 -0
  20. package/docs/local-merge-notes.md +21 -0
  21. package/docs/maintainer-handoff.md +150 -0
  22. package/docs/manual-publish.md +119 -0
  23. package/docs/private-denylist.example.txt +11 -0
  24. package/docs/publication-decisions.md +36 -0
  25. package/docs/release-checklist.md +72 -0
  26. package/docs/releases/v0.1.0.md +70 -0
  27. package/docs/shareable-install.md +51 -0
  28. package/docs/tool-install-recipes.md +103 -0
  29. package/examples/ecommerce/team-profile.example.yaml +141 -0
  30. package/examples/education/team-profile.example.yaml +143 -0
  31. package/examples/saas-to-b/team-profile.example.yaml +130 -0
  32. package/examples/team-profile.example.yaml +69 -0
  33. package/install.sh +27 -0
  34. package/package.json +65 -0
  35. package/scripts/build-release.cjs +141 -0
  36. package/scripts/release-source-state.cjs +38 -0
  37. package/templates/AGENTS.template.md +26 -0
  38. package/test/release-readiness.cjs +214 -0
  39. package/test/smoke.cjs +272 -0
  40. package/workflow/adapters/README.md +16 -0
  41. package/workflow/core/README.md +38 -0
  42. package/workflow/core/capabilities/README.md +61 -0
  43. package/workflow/core/capabilities/acceptance-oracle-tracker.md +45 -0
  44. package/workflow/core/capabilities/branch-gatekeeper.md +57 -0
  45. package/workflow/core/capabilities/channel-experiment-tracker.md +32 -0
  46. package/workflow/core/capabilities/ci-cd-automation-governor.md +123 -0
  47. package/workflow/core/capabilities/contract-tracer.md +53 -0
  48. package/workflow/core/capabilities/data-change-safety-checker.md +52 -0
  49. package/workflow/core/capabilities/definition-lint.md +81 -0
  50. package/workflow/core/capabilities/deployment-readiness-checker.md +57 -0
  51. package/workflow/core/capabilities/impact-scope-analyzer.md +50 -0
  52. package/workflow/core/capabilities/knowledge-capture-maintainer.md +32 -0
  53. package/workflow/core/capabilities/market-evidence-grader.md +33 -0
  54. package/workflow/core/capabilities/memory-curator.md +48 -0
  55. package/workflow/core/capabilities/personal-git-operator.md +41 -0
  56. package/workflow/core/capabilities/personal-release-checklist.md +49 -0
  57. package/workflow/core/capabilities/prd-code-diff-checker.md +53 -0
  58. package/workflow/core/capabilities/protocol-state-machine-checker.md +57 -0
  59. package/workflow/core/capabilities/release-safety-checker.md +61 -0
  60. package/workflow/core/capabilities/repo-baseline-scanner.md +48 -0
  61. package/workflow/core/capabilities/rule-extractor.md +48 -0
  62. package/workflow/core/capabilities/runtime-evidence-triage.md +53 -0
  63. package/workflow/core/capabilities/security-reviewer.md +48 -0
  64. package/workflow/core/capabilities/test-evidence-reviewer.md +50 -0
  65. package/workflow/core/capabilities/ui-baseline-reviewer.md +50 -0
  66. package/workflow/core/capabilities/verify-app.md +51 -0
  67. package/workflow/core/capabilities/worktree-isolator.md +53 -0
  68. package/workflow/core/commands/01-/351/234/200/346/261/202/350/256/250/350/256/272.md +36 -0
  69. package/workflow/core/commands/02-/344/272/247/345/223/201/346/226/207/346/241/243.md +35 -0
  70. package/workflow/core/commands/02B-UI/350/256/276/350/256/241.md +75 -0
  71. package/workflow/core/commands/03-06-/347/240/224/345/217/221/345/207/206/345/244/207.md +30 -0
  72. package/workflow/core/commands/03-/346/212/200/346/234/257/346/236/266/346/236/204.md +32 -0
  73. package/workflow/core/commands/04-/344/273/243/347/240/201/345/256/236/347/216/260.md +32 -0
  74. package/workflow/core/commands/04A-/345/211/215/347/253/257/344/273/243/347/240/201/345/256/236/347/216/260.md +36 -0
  75. package/workflow/core/commands/04B-/345/220/216/347/253/257/344/273/243/347/240/201/345/256/236/347/216/260.md +31 -0
  76. package/workflow/core/commands/05-/344/273/243/347/240/201/345/256/241/346/237/245.md +31 -0
  77. package/workflow/core/commands/06-/346/265/213/350/257/225/347/224/250/344/276/213.md +34 -0
  78. package/workflow/core/commands/07-/346/265/213/350/257/225/346/211/247/350/241/214.md +33 -0
  79. package/workflow/core/commands/08-/345/217/221/345/270/203/345/207/206/345/244/207.md +44 -0
  80. package/workflow/core/commands/09-/345/217/221/345/270/203/346/211/247/350/241/214.md +39 -0
  81. package/workflow/core/commands/10-/345/244/215/347/233/230/346/200/273/347/273/223.md +41 -0
  82. package/workflow/core/commands/B1-B8-/345/225/206/344/270/232/345/214/226/345/207/206/345/244/207.md +30 -0
  83. package/workflow/core/commands/B1-/344/270/232/345/212/241/345/256/232/344/275/215.md +38 -0
  84. package/workflow/core/commands/B2-/345/225/206/344/270/232/346/250/241/345/274/217.md +38 -0
  85. package/workflow/core/commands/B3-PMF/344/270/216/345/256/242/346/210/267/347/224/273/345/203/217.md +39 -0
  86. package/workflow/core/commands/B4-/345/234/272/346/231/257/344/270/216/350/264/255/344/271/260/346/227/205/347/250/213.md +37 -0
  87. package/workflow/core/commands/B5-/346/270/240/351/201/223/346/274/217/346/226/227/346/230/240/345/260/204.md +39 -0
  88. package/workflow/core/commands/B6-/350/220/245/351/224/200/350/216/267/345/256/242/347/255/226/347/225/245.md +38 -0
  89. package/workflow/core/commands/B7-/350/220/245/351/224/200/351/242/204/347/256/227.md +36 -0
  90. package/workflow/core/commands/B8-/346/270/240/351/201/223/346/211/247/350/241/214/347/255/226/347/225/245.md +39 -0
  91. package/workflow/core/commands/B9-/347/255/226/347/225/245/345/244/215/347/233/230.md +38 -0
  92. package/workflow/core/commands/README.md +56 -0
  93. package/workflow/core/commands/init-workspace.md +29 -0
  94. package/workflow/core/commands/new-feature.md +33 -0
  95. package/workflow/core/commands/new-product.md +28 -0
  96. package/workflow/core/commands/workflow-status.md +30 -0
  97. package/workflow/core/commands//344/270/200/350/207/264/346/200/247/346/243/200/346/237/245.md +35 -0
  98. package/workflow/core/commands//344/272/244/344/273/230/350/207/263/345/256/214/346/210/220.md +40 -0
  99. package/workflow/core/commands//345/256/232/344/271/211/345/256/214/346/210/220.md +41 -0
  100. package/workflow/core/commands//346/276/204/346/270/205.md +31 -0
  101. package/workflow/core/templates/00-business-status.md +43 -0
  102. package/workflow/core/templates/00-workflow-status.md +50 -0
  103. package/workflow/core/templates/README.md +12 -0
  104. package/workflow/core/templates/business-stage-document.md +49 -0
  105. package/workflow/core/templates/completion-contract.md +114 -0
  106. package/workflow/core/templates/constitution.template.md +50 -0
  107. package/workflow/core/templates/living-spec.md +35 -0
  108. package/workflow/core/templates/stage-document.md +42 -0
  109. package/workflow/core/templates/team-profile.template.yaml +104 -0
@@ -0,0 +1,137 @@
1
+ #!/usr/bin/env node
2
+ // 完成合同结构校验器:node bin/check-contract.cjs <features/xxx/00-完成合同.md>
3
+ // 校验分档必填节、Oracle 表、冻结前置条件与模糊词,退出码 0=通过 1=存在错误。
4
+ const fs = require('fs');
5
+ const path = require('path');
6
+
7
+ const STAR_SECTIONS = ['★ 目标与非目标', '★ 术语表', '★ 验收 Oracle', '★ 待澄清项'];
8
+ const FULL_SECTIONS = [
9
+ ...STAR_SECTIONS,
10
+ '数据流与状态机',
11
+ '失败路径闭环',
12
+ '质量预算',
13
+ '影响边界',
14
+ 'Definition Lint 结果',
15
+ '修订记录'
16
+ ];
17
+ const ORACLE_STATES = new Set(['NOT_RUN', 'PASS', 'FAIL', 'STALE', 'WAIVED']);
18
+ const FUZZY_WORDS = /(流畅|稳定|好用|优雅|高性能|健壮|无缝|丝滑|尽快|大量)/;
19
+
20
+ main();
21
+
22
+ function main() {
23
+ const file = process.argv[2];
24
+ if (!file) {
25
+ console.error('用法: node bin/check-contract.cjs <完成合同.md 路径>');
26
+ process.exit(1);
27
+ }
28
+ const full = path.resolve(process.cwd(), file);
29
+ if (!fs.existsSync(full)) {
30
+ console.error(`文件不存在: ${file}`);
31
+ process.exit(1);
32
+ }
33
+ const text = fs.readFileSync(full, 'utf8');
34
+ const errors = [];
35
+ const warnings = [];
36
+
37
+ const tier = pickTableValue(text, '复杂度档位');
38
+ const tierChosen = tier && /^(S|M|L)$/.test(tier.trim());
39
+ if (!tierChosen) errors.push(`复杂度档位未选择(当前: ${tier || '缺失'};应为 S、M 或 L 单值)`);
40
+
41
+ const status = pickTableValue(text, '状态');
42
+ const statusChosen = status && /^(草稿|已冻结|修订中)$/.test(status.trim());
43
+ if (!statusChosen) errors.push(`合同状态未选择(当前: ${status || '缺失'};应为 草稿、已冻结 或 修订中 单值)`);
44
+ const frozen = statusChosen && status.trim() === '已冻结';
45
+
46
+ const sections = splitSections(text);
47
+ const required = tierChosen && tier.trim() === 'S' ? STAR_SECTIONS : FULL_SECTIONS;
48
+ for (const name of required) {
49
+ if (findSection(sections, name) === null) errors.push(`缺少必填章节: ## ${name}`);
50
+ }
51
+
52
+ const oracleBody = findSection(sections, '★ 验收 Oracle') || '';
53
+ const rows = oracleBody.split(/\r?\n/).filter((line) => /^\|\s*O-\d+/.test(line.trim()));
54
+ if (!rows.length) {
55
+ errors.push('验收 Oracle 表为空:至少需要一条以 O- 编号的 Oracle');
56
+ } else {
57
+ let blockingCount = 0;
58
+ for (const row of rows) {
59
+ const cells = row.split('|').map((cell) => cell.trim());
60
+ // ['', ID, 标准, 验证方法, 类型, blocking, 状态, 证据, 时间, '']
61
+ if (cells.length < 9) {
62
+ errors.push(`Oracle 行列数不足: ${cells[1] || row.slice(0, 30)}`);
63
+ continue;
64
+ }
65
+ const [id, criteria, method, type, blocking, state] = cells.slice(1, 7);
66
+ if (!criteria || /待填写/.test(criteria)) errors.push(`${id} 验收标准未填写`);
67
+ if (!method || method === '-' || /待填写/.test(method)) errors.push(`${id} 缺少可复现验证方法`);
68
+ if (!/^(auto|manual)$/.test(type)) errors.push(`${id} 类型应为 auto 或 manual(当前: ${type})`);
69
+ if (blocking === '是') blockingCount++;
70
+ if (!ORACLE_STATES.has(state)) errors.push(`${id} 状态非法(当前: ${state};应为 ${[...ORACLE_STATES].join('/')})`);
71
+ if (frozen === false && (state === 'PASS' || state === 'FAIL')) {
72
+ warnings.push(`${id} 在合同冻结前已有执行状态 ${state},请确认证据对应当前草稿`);
73
+ }
74
+ if (FUZZY_WORDS.test(criteria) && !/\d/.test(criteria)) {
75
+ warnings.push(`${id} 验收标准包含模糊词且未绑定数字,请确认已在术语表消歧`);
76
+ }
77
+ }
78
+ if (!blockingCount) errors.push('没有任何 blocking Oracle:至少一条 blocking=是');
79
+ }
80
+
81
+ const clarifySection = findSection(sections, '★ 待澄清项') || '';
82
+ const pendingMarks = (text.match(/\[待澄清/g) || []).length;
83
+ const pendingItems = clarifySection
84
+ .split(/\r?\n/)
85
+ .filter((line) => /^\s*-\s+\S/.test(line) && !/^\s*-\s+待填写/.test(line) && !/^\s*-\s+无/.test(line)).length;
86
+ if (frozen && (pendingMarks > 0 || pendingItems > 0)) {
87
+ errors.push(`已冻结合同仍存在待澄清项(标记 ${pendingMarks} 处,条目 ${pendingItems} 条):冻结前必须清零或降级为已记录假设`);
88
+ }
89
+
90
+ if (frozen) {
91
+ const lint = pickTableValue(text, 'Definition Lint');
92
+ if (!lint || lint.trim() !== '通过') errors.push(`已冻结合同的 Definition Lint 必须为「通过」(当前: ${lint || '缺失'})`);
93
+ const confirm = pickTableValue(text, '冻结确认');
94
+ if (!confirm || /待用户确认/.test(confirm)) errors.push('已冻结合同缺少用户冻结确认记录');
95
+ }
96
+
97
+ const goalsBody = findSection(sections, '★ 目标与非目标') || '';
98
+ for (const line of goalsBody.split(/\r?\n/)) {
99
+ if (/^\s*-\s+\S/.test(line) && FUZZY_WORDS.test(line) && !/\d/.test(line)) {
100
+ warnings.push(`目标含模糊词且无数字约束: ${line.trim().slice(0, 50)}`);
101
+ }
102
+ }
103
+
104
+ for (const w of warnings) console.log(`警告 ${w}`);
105
+ for (const e of errors) console.error(`错误 ${e}`);
106
+ if (errors.length) {
107
+ console.error(`合同校验未通过:${errors.length} 个错误,${warnings.length} 个警告。`);
108
+ process.exit(1);
109
+ }
110
+ console.log(`合同校验通过:0 个错误,${warnings.length} 个警告。`);
111
+ }
112
+
113
+ function pickTableValue(text, key) {
114
+ const re = new RegExp(`^\\|\\s*${key}\\s*\\|([^|]+)\\|`, 'm');
115
+ const match = text.match(re);
116
+ return match ? match[1].trim() : null;
117
+ }
118
+
119
+ function splitSections(text) {
120
+ const map = new Map();
121
+ const parts = text.split(/^## /m).slice(1);
122
+ for (const part of parts) {
123
+ const newline = part.indexOf('\n');
124
+ const title = (newline === -1 ? part : part.slice(0, newline)).trim();
125
+ map.set(title, newline === -1 ? '' : part.slice(newline + 1));
126
+ }
127
+ return map;
128
+ }
129
+
130
+ // 章节按前缀匹配:允许用户在标题后加说明后缀,例如「★ 术语表(歧义消融)」。
131
+ function findSection(sections, name) {
132
+ if (sections.has(name)) return sections.get(name);
133
+ for (const [title, body] of sections) {
134
+ if (title.startsWith(name)) return body;
135
+ }
136
+ return null;
137
+ }
@@ -0,0 +1,93 @@
1
+ #!/usr/bin/env node
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ const kitRoot = path.resolve(__dirname, '..');
6
+ const args = parseArgs(process.argv.slice(2));
7
+ const banned = loadBannedTerms(args.extraBanned);
8
+ const genericPatterns = [
9
+ { name: 'private key marker', regex: /-----BEGIN (RSA |EC |OPENSSH |DSA )?PRIVATE KEY-----/ },
10
+ { name: 'access token assignment', regex: /\b(access[_-]?token|api[_-]?key|secret[_-]?key|password)\b\s*[:=]\s*["'][^"']{8,}["']/i },
11
+ { name: 'private intranet URL', regex: /https?:\/\/(10\.|172\.(1[6-9]|2\d|3[01])\.|192\.168\.|localhost|127\.0\.0\.1)/i }
12
+ ];
13
+
14
+ const skipDirs = new Set(['.git', 'node_modules', 'dist', 'build', 'target']);
15
+ const checkedExt = new Set([
16
+ '.md',
17
+ '.json',
18
+ '.cjs',
19
+ '.js',
20
+ '.yaml',
21
+ '.yml',
22
+ '.txt',
23
+ '.sh'
24
+ ]);
25
+
26
+ const hits = [];
27
+
28
+ function walk(dir) {
29
+ for (const name of fs.readdirSync(dir)) {
30
+ const full = path.join(dir, name);
31
+ const rel = path.relative(kitRoot, full);
32
+ const stat = fs.statSync(full);
33
+ if (stat.isDirectory()) {
34
+ if (!skipDirs.has(name)) walk(full);
35
+ continue;
36
+ }
37
+ if (!checkedExt.has(path.extname(name))) continue;
38
+ const text = fs.readFileSync(full, 'utf8');
39
+ for (const term of banned) {
40
+ const idx = text.indexOf(term);
41
+ if (idx >= 0) {
42
+ const line = text.slice(0, idx).split(/\r?\n/).length;
43
+ hits.push({ rel, line, term });
44
+ }
45
+ }
46
+ for (const pattern of genericPatterns) {
47
+ const match = text.match(pattern.regex);
48
+ if (match && match.index != null) {
49
+ const line = text.slice(0, match.index).split(/\r?\n/).length;
50
+ hits.push({ rel, line, term: pattern.name });
51
+ }
52
+ }
53
+ }
54
+ }
55
+
56
+ walk(kitRoot);
57
+
58
+ if (hits.length) {
59
+ console.error('脱敏检查失败。对外分发前请移除项目特定词和敏感内容。');
60
+ for (const hit of hits) {
61
+ console.error(`- ${hit.rel}:${hit.line} 包含 ${JSON.stringify(hit.term)}`);
62
+ }
63
+ process.exit(1);
64
+ }
65
+
66
+ console.log('脱敏检查通过。');
67
+
68
+ function parseArgs(argv) {
69
+ const parsed = { extraBanned: '' };
70
+ for (let i = 0; i < argv.length; i++) {
71
+ const arg = argv[i];
72
+ if (arg === '--extra-banned') parsed.extraBanned = argv[++i] || '';
73
+ else if (arg === '--help' || arg === '-h') {
74
+ console.log(`用法: node bin/check-sanitized.cjs [--extra-banned file]
75
+
76
+ 内置扫描会检查通用密钥和私有 URL 模式。对外分发前,请使用 --extra-banned
77
+ 指定本地私有 denylist。该私有 denylist 不得提交到 starter kit。`);
78
+ process.exit(0);
79
+ } else {
80
+ throw new Error(`未知参数: ${arg}`);
81
+ }
82
+ }
83
+ return parsed;
84
+ }
85
+
86
+ function loadBannedTerms(file) {
87
+ if (!file) return [];
88
+ const full = path.resolve(process.cwd(), file);
89
+ const text = fs.readFileSync(full, 'utf8');
90
+ return text.split(/\r?\n/)
91
+ .map((line) => line.trim())
92
+ .filter((line) => line && !line.startsWith('#'));
93
+ }