@xulthekl/team-flow 0.63.0 → 0.65.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/.claude/always/phase-guard.md +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/.codex-plugin/plugin.json +1 -1
- package/.cursor-plugin/marketplace.json +1 -1
- package/.cursor-plugin/plugin.json +1 -1
- package/.github/plugin/marketplace.json +2 -2
- package/.github/workflows/ci.yml +2 -0
- package/CHANGELOG.md +57 -0
- package/GEMINI.md +1 -1
- package/INSTALL.md +1 -1
- package/README.md +1 -1
- package/agents/architecture-design.md +1 -1
- package/agents/architecture-reviewer.md +2 -2
- package/docs/README_en.md +1 -1
- package/docs/state-machine.md +4 -1
- package/docs/team-flow /344/275/277/347/224/250/350/257/264/346/230/216/357/274/210/347/240/224/345/217/221/345/233/242/351/230/237/347/211/210/357/274/211.md" +2 -2
- package/gemini-extension.json +1 -1
- package/hooks/session-start +2 -2
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/plugin.json +1 -1
- package/scripts/guard/checks/_fs-utils.mjs +18 -0
- package/scripts/guard/checks/arch-design-light.mjs +39 -0
- package/scripts/guard/checks/arch-gate-exemptions.mjs +5 -3
- package/scripts/guard/checks/arch-merged-light.mjs +67 -0
- package/scripts/guard/checks/arch-readiness.mjs +1 -1
- package/scripts/guard/checks/arch-snapshot-light.mjs +30 -0
- package/scripts/guard/checks/arch-snapshot.mjs +5 -3
- package/scripts/guard/checks/artifacts-planned.mjs +38 -0
- package/scripts/guard/checks/compound-writeback-light.mjs +45 -0
- package/scripts/guard/checks/cross-change-consistency-light.mjs +75 -0
- package/scripts/guard/checks/direct-short-path.mjs +52 -0
- package/scripts/guard/checks/direct-test-result.mjs +30 -0
- package/scripts/guard/checks/execution-plan-ready.mjs +7 -1
- package/scripts/guard/checks/execution-reviews-passed-light.mjs +28 -0
- package/scripts/guard/checks/lightweight-completion-evidence.mjs +27 -0
- package/scripts/guard/checks/specs-merged.mjs +25 -1
- package/scripts/guard/checks/test-matrix-complete.mjs +27 -1
- package/scripts/guard/checks/test-matrix-ready.mjs +28 -1
- package/scripts/guard/checks/test-merged-light.mjs +36 -0
- package/scripts/guard/guard.mjs +102 -12
- package/scripts/infer-workflow.mjs +35 -4
- package/scripts/lib/arch-merge.mjs +404 -54
- package/scripts/lib/arch-parse.mjs +5 -2
- package/scripts/lib/arch-registry.mjs +523 -0
- package/scripts/lib/arch-scan-code.mjs +518 -0
- package/scripts/lib/cmd-arch.mjs +9 -1
- package/scripts/lib/cmd-doctor.mjs +1 -1
- package/scripts/lib/cmd-execution.mjs +44 -1
- package/scripts/lib/cmd-state.mjs +94 -4
- package/scripts/lib/config-loader.mjs +20 -0
- package/scripts/lib/execution-plan.mjs +3 -1
- package/scripts/lib/state-loader.mjs +43 -0
- package/scripts/lib/surface-scan.mjs +156 -0
- package/scripts/lib/test-merge.mjs +10 -2
- package/scripts/team-flow.mjs +6 -3
- package/skills/architecture-design/SKILL.md +29 -11
- package/skills/architecture-design/chapters/ch04-entity-to-aggregate.md +18 -7
- package/skills/architecture-design/chapters/ch06-integration.md +12 -3
- package/skills/architecture-design/glossary.md +5 -1
- package/skills/architecture-design/references/adr-templates.md +56 -0
- package/skills/architecture-design/references/context-map-8.md +47 -0
- package/skills/architecture-design/references/ddd-evented-playbook.md +41 -0
- package/skills/architecture-design/references/s3.5-architecture-template.md +36 -4
- package/skills/architecture-design/references/s3.5-loading-protocol.md +5 -4
- package/skills/architecture-design/references/s3.5-product-architecture.md +6 -6
- package/skills/architecture-design/templates/architecture.md +20 -0
- package/skills/ce-compound/references/concepts-vocabulary.md +1 -1
- package/skills/ce-compound/references/full-mode-workflow.md +2 -2
- package/skills/ce-compound/references/lightweight-mode.md +1 -1
- package/skills/clean-code/SKILL.md +1 -1
- package/skills/jarvis/SKILL.md +2 -0
- package/skills/release-archivist/SKILL.md +60 -13
- package/skills/release-archivist/references/closing-procedures.md +1 -1
- package/skills/session-handoff/SKILL.md +1 -0
- package/skills/test-strategy/SKILL.md +1 -1
- package/skills/workflow-orchestrator/SKILL.md +2 -2
- package/skills/workflow-start/SKILL.md +63 -5
- package/skills/workflow-start/references/routing-rules.md +4 -4
|
@@ -17,6 +17,13 @@ const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
|
17
17
|
// schema_version 不可设置(仅 init 打戳,防止伪造豁免身份,v0.13 §48.1)。
|
|
18
18
|
const SETTABLE_FIELDS = [
|
|
19
19
|
'workflow', 'batches_completed', 'spec_merged',
|
|
20
|
+
// v0.64.0 P0(实施计划 §3.3):前门双维度。三环齐全(本列表 + state-loader
|
|
21
|
+
// BUILTIN_DEFAULTS + writeState 序列化分支),缺一即「回显成功却零写入」。
|
|
22
|
+
// workflow_variant 走通用通道仅 exploring 态可写(下方 set 分支强制);
|
|
23
|
+
// 升档豁免 = tf state upgrade 专用子命令(不经本通道),写入打
|
|
24
|
+
// variant_source=upgrade + variant_direction=up,guard 据此 fail-closed。
|
|
25
|
+
'workflow_variant', 'variant_source', 'variant_direction',
|
|
26
|
+
'planned_arch', 'model_profile',
|
|
20
27
|
'dp_0_decisions', 'dp_0_confirmed', 'dp_0_timestamp', 'dp_0_result',
|
|
21
28
|
// v0.59.0(P4 实测):dp_{1,2,3,5,6,7}_decisions / _confirmed 共 12 个字段已移除——
|
|
22
29
|
// 它们虽在旧白名单内,但 writeState 无对应序列化分支,tf state set 会**回显成功却零写入**
|
|
@@ -49,6 +56,8 @@ const SETTABLE_FIELDS = [
|
|
|
49
56
|
// 三键必须与 state-loader BUILTIN_DEFAULTS + writeState 序列化分支同步注册,
|
|
50
57
|
// 缺任一环即「回显成功却零写入」(v0.59.0 同型教训,见本文件 :20-24 注释)。
|
|
51
58
|
'gates_probed_skipped', 'gates_probed_skip_reason', 'gates_probed_na',
|
|
59
|
+
// v0.64.0(§4 arch-design-light):轻架构说明显式跳过键(须附理由)
|
|
60
|
+
'arch_design_light_skipped', 'arch_design_light_skip_reason',
|
|
52
61
|
];
|
|
53
62
|
|
|
54
63
|
export async function run(args) {
|
|
@@ -66,7 +75,7 @@ export async function run(args) {
|
|
|
66
75
|
|
|
67
76
|
if (!changeDir) {
|
|
68
77
|
console.error('Usage: tf state <subcommand> <change-dir> [arg]');
|
|
69
|
-
console.error('Subcommands: init, check, transition, get, rebuild, set');
|
|
78
|
+
console.error('Subcommands: init, check, transition, get, rebuild, set, upgrade');
|
|
70
79
|
process.exit(2);
|
|
71
80
|
}
|
|
72
81
|
|
|
@@ -80,7 +89,7 @@ export async function run(args) {
|
|
|
80
89
|
// Unknown subcommand: report a usage error (exit 2) BEFORE the BUG-B
|
|
81
90
|
// state-file existence check, so a bad subcommand is not masked by the
|
|
82
91
|
// "No state file" error (which would return exit 1 instead of exit 2).
|
|
83
|
-
const KNOWN_SUBS = ['init', 'check', 'transition', 'get', 'rebuild', 'set'];
|
|
92
|
+
const KNOWN_SUBS = ['init', 'check', 'transition', 'get', 'rebuild', 'set', 'upgrade'];
|
|
84
93
|
if (!KNOWN_SUBS.includes(sub)) {
|
|
85
94
|
console.error(`Unknown subcommand: ${sub}. Valid: ${KNOWN_SUBS.join(', ')}`);
|
|
86
95
|
process.exit(2);
|
|
@@ -112,6 +121,12 @@ export async function run(args) {
|
|
|
112
121
|
const state = readState(changeDir);
|
|
113
122
|
if (!stateFileExisted) {
|
|
114
123
|
state.schema_version = 1;
|
|
124
|
+
// v0.64.0 P0(§4 扫描基线):init 打戳 base_sha(架构 surface 扫描的 git 基线)。
|
|
125
|
+
// 非 git 环境 / 尚无提交 → 保持 null,扫描时按 §4 退化参照(origin → FAIL)。
|
|
126
|
+
try {
|
|
127
|
+
const rev = spawnSync('git', ['rev-parse', 'HEAD'], { cwd: changeDir, encoding: 'utf-8', timeout: 3_000 });
|
|
128
|
+
if (rev.status === 0 && rev.stdout?.trim()) state.base_sha = rev.stdout.trim();
|
|
129
|
+
} catch { /* 保持 null */ }
|
|
115
130
|
}
|
|
116
131
|
state.artifacts_hash = hash;
|
|
117
132
|
state.contract_hash = ch;
|
|
@@ -170,7 +185,16 @@ export async function run(args) {
|
|
|
170
185
|
const rawWorkflow = state.workflow || 'full';
|
|
171
186
|
// Normalize: guard only accepts full/hotfix/tweak, not "auto"
|
|
172
187
|
const workflow = rawWorkflow === 'auto' ? 'full' : rawWorkflow;
|
|
173
|
-
|
|
188
|
+
// v0.64.0 P1(§3.3/§4.5 注记①):透传前门变体——guard 据此选 quick/planned 维度表。
|
|
189
|
+
// 这是 workflow_variant 到达 resolveDimensions 的唯一管道(workflow-start 是 skill,不是调用方)。
|
|
190
|
+
const guardArgs = [guardScript, 'check', changeDir, fromState, toState, '--json', '--workflow', workflow];
|
|
191
|
+
if (state.workflow_variant != null && state.workflow_variant !== 'null') {
|
|
192
|
+
guardArgs.push('--workflow-variant', String(state.workflow_variant));
|
|
193
|
+
}
|
|
194
|
+
if (state.planned_arch === true || state.planned_arch === 'true') {
|
|
195
|
+
guardArgs.push('--planned-arch', 'true');
|
|
196
|
+
}
|
|
197
|
+
const guardResult = spawnSync('node', guardArgs, {
|
|
174
198
|
cwd: join(__dirname, '..', '..'),
|
|
175
199
|
timeout: 10_000,
|
|
176
200
|
});
|
|
@@ -282,6 +306,28 @@ export async function run(args) {
|
|
|
282
306
|
}
|
|
283
307
|
process.exit(1);
|
|
284
308
|
}
|
|
309
|
+
// v0.64.0 P0 写限(§3.3/§5.3):workflow_variant 通用通道仅 exploring 态可写,
|
|
310
|
+
// 防关门段自降档清屏;升档唯一豁免 = tf state upgrade 专用子命令(不走本通道)。
|
|
311
|
+
if (field === 'workflow_variant') {
|
|
312
|
+
const cur = readState(changeDir);
|
|
313
|
+
if (cur.state !== 'exploring') {
|
|
314
|
+
console.error(
|
|
315
|
+
`⛔ 'workflow_variant' 仅可在 exploring 态通过本通道写入(当前: ${cur.state})。\n` +
|
|
316
|
+
` 升档唯一机械入口: tf state upgrade <change-dir> <planned|full>(原子改写 variant_source=upgrade + 回退 approved-for-build + 重算 hash;降档拒绝)`
|
|
317
|
+
);
|
|
318
|
+
process.exit(1);
|
|
319
|
+
}
|
|
320
|
+
if (value !== 'null' && !['direct', 'planned', 'legacy'].includes(value)) {
|
|
321
|
+
console.error(`⛔ Invalid workflow_variant: '${value}'. Must be one of: direct, planned, legacy, null`);
|
|
322
|
+
process.exit(1);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (field === 'model_profile') {
|
|
326
|
+
if (!['mechanical', 'standard', 'strong', 'review'].includes(value)) {
|
|
327
|
+
console.error(`⛔ Invalid model_profile: '${value}'. Must be one of: mechanical, standard, strong, review`);
|
|
328
|
+
process.exit(1);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
285
331
|
updateField(changeDir, field, value);
|
|
286
332
|
if (values.json) {
|
|
287
333
|
console.log(JSON.stringify({ ok: true, field, value }));
|
|
@@ -290,8 +336,52 @@ export async function run(args) {
|
|
|
290
336
|
}
|
|
291
337
|
break;
|
|
292
338
|
}
|
|
339
|
+
case 'upgrade': {
|
|
340
|
+
// v0.64.0(实施计划 §5.3 D9,升档唯一机械入口):
|
|
341
|
+
// tf state upgrade <change-dir> <planned|full>
|
|
342
|
+
// 校验方向向上 → 原子写 variant_source=upgrade + variant_direction=up →
|
|
343
|
+
// state 回退 approved-for-build(重跑目标档闸门)→ 重算 artifacts_hash(B-10)。
|
|
344
|
+
// 保留:磁盘代码/产物、test_result + test_evidence_path、test_matrix_hash。
|
|
345
|
+
const toVariant = arg;
|
|
346
|
+
if (!['planned', 'full'].includes(toVariant)) {
|
|
347
|
+
console.error('Usage: tf state upgrade <change-dir> <planned|full> (downgrades are forbidden)');
|
|
348
|
+
process.exit(2);
|
|
349
|
+
}
|
|
350
|
+
const ORDER = { direct: 0, planned: 1 };
|
|
351
|
+
const cur = readState(changeDir);
|
|
352
|
+
const from = cur.workflow_variant;
|
|
353
|
+
// 档位序:direct(0) < planned(1) < full/legacy(2)。upgrade full = 置 legacy + workflow full
|
|
354
|
+
//(variant 枚举无 'full',full 档由 workflow=full + legacy 全量仪式表达,方案 §5.3)。
|
|
355
|
+
const fromOrder = from === 'legacy' || from == null ? 2 : (ORDER[from] ?? 0);
|
|
356
|
+
const targetOrder = toVariant === 'full' ? 2 : ORDER[toVariant];
|
|
357
|
+
if (fromOrder >= targetOrder) {
|
|
358
|
+
console.error(`⛔ Upgrade ${from ?? 'null'} -> ${toVariant} is not upward (downgrades are forbidden)`);
|
|
359
|
+
process.exit(1);
|
|
360
|
+
}
|
|
361
|
+
cur.workflow_variant = toVariant === 'full' ? 'legacy' : toVariant;
|
|
362
|
+
cur.variant_source = 'upgrade';
|
|
363
|
+
cur.variant_direction = 'up';
|
|
364
|
+
if (toVariant === 'full') cur.workflow = 'full';
|
|
365
|
+
// 已过规划段则回退 approved-for-build 重跑目标档闸门;exploring 态无需回退
|
|
366
|
+
const ROLLBACK_STATES = ['specifying', 'bridging', 'executing', 'debugging', 'closing'];
|
|
367
|
+
if (ROLLBACK_STATES.includes(cur.state)) {
|
|
368
|
+
const prev = cur.state;
|
|
369
|
+
cur.state = 'approved-for-build';
|
|
370
|
+
cur.last_transition_from = prev;
|
|
371
|
+
cur.last_transition_to = 'approved-for-build';
|
|
372
|
+
cur.last_transition = new Date().toISOString();
|
|
373
|
+
}
|
|
374
|
+
cur.artifacts_hash = computeArtifactsHash(changeDir); // B-10 重算,防 doctor/check 恒报不一致
|
|
375
|
+
writeState(changeDir, cur);
|
|
376
|
+
if (values.json) {
|
|
377
|
+
console.log(JSON.stringify({ ok: true, from: from ?? 'null', to: cur.workflow_variant, state: cur.state, variant_source: 'upgrade' }));
|
|
378
|
+
} else {
|
|
379
|
+
console.log(`✅ Upgraded workflow_variant: ${from ?? 'null'} -> ${cur.workflow_variant} (source=upgrade, state=${cur.state}, artifacts_hash recomputed)`);
|
|
380
|
+
}
|
|
381
|
+
break;
|
|
382
|
+
}
|
|
293
383
|
default:
|
|
294
|
-
console.error(`Unknown subcommand: ${sub}. Valid: init, check, transition, get, rebuild, set`);
|
|
384
|
+
console.error(`Unknown subcommand: ${sub}. Valid: init, check, transition, get, rebuild, set, upgrade`);
|
|
295
385
|
process.exit(2);
|
|
296
386
|
}
|
|
297
387
|
}
|
|
@@ -47,6 +47,26 @@ const DEFAULTS = {
|
|
|
47
47
|
// 各 skill 上下文加载时按 phase 过滤注入
|
|
48
48
|
// 示例: { database: ".team-flow/conventions/db-design.md", ... }
|
|
49
49
|
},
|
|
50
|
+
arch: {
|
|
51
|
+
// C4 代码注解扫描配置(ddd-purity-and-arch-merge-design v1.4 §5.4-4b)。
|
|
52
|
+
// C4 是本机制**唯一**「A 级 · 真独立」锚:锚点 = 代码本体,不经 arch-merge 写路径。
|
|
53
|
+
scan: {
|
|
54
|
+
// 持久化注解名(默认 MyBatis-Plus)。非该框架的项目改此值。
|
|
55
|
+
annotation: 'TableName',
|
|
56
|
+
// 自定义正则源(优先于 annotation)。约定 capture group(1) 为表名。
|
|
57
|
+
pattern: null,
|
|
58
|
+
// 扫描的目标文件扩展名。
|
|
59
|
+
extensions: ['.java'],
|
|
60
|
+
// 多仓场景:扫描根(相对项目根);为空则扫项目根。
|
|
61
|
+
// emp-auth 恰把两仓放在同一工作树,故单根可跑;两仓分属不同 git 仓库时须配置。
|
|
62
|
+
repos: [],
|
|
63
|
+
// 排除目录(按 basename 匹配,任意层级生效)。
|
|
64
|
+
// ★ 必配:emp-auth 根下有 4 个 .worktrees/ 副本,不排除会把同一批注解重复计入。
|
|
65
|
+
// ★ 真相源说明(P3 MIN-4):**强制底线以 `arch-scan-code.mjs` 的 `DEFAULT_EXCLUDE_DIRS`
|
|
66
|
+
// 为准**(并集语义恒生效,配置只能追加)——此处仅是文档性默认值,漂移不影响强制性。
|
|
67
|
+
exclude: ['.worktrees', '.git', 'node_modules', 'target', 'build', 'dist'],
|
|
68
|
+
},
|
|
69
|
+
},
|
|
50
70
|
glaf4_dev: {
|
|
51
71
|
// glaf4-dev 运行时探测结果(v2.1 §4 接口①)
|
|
52
72
|
// contract-builder 产出 execution-contract 时实时探测写入;delegation_mode 由用户/编排开关
|
|
@@ -107,7 +107,9 @@ export function validatePlan(changeDir, plan) {
|
|
|
107
107
|
if (state.revision != null && plan?.revision !== state.revision) {
|
|
108
108
|
failures.push('execution plan revision does not match state');
|
|
109
109
|
}
|
|
110
|
-
|
|
110
|
+
// v0.64.0(§4.5 注记⑤配套):planned(workflow=full+variant=planned)无 recommendation/
|
|
111
|
+
// selection 仪式——plan 由 tasks.md 派生,不走 execution-recommendation 流程;豁免三件要求。
|
|
112
|
+
if (plan?.workflow !== 'tweak' && state.workflow_variant !== 'planned') {
|
|
111
113
|
if (plan?.recommendation === undefined) failures.push('execution plan recommendation is required for full/hotfix');
|
|
112
114
|
if (plan?.recommendation_receipt === undefined) failures.push('execution plan recommendation receipt is required for full/hotfix');
|
|
113
115
|
if (plan?.selection === undefined) failures.push('execution plan selection is required for full/hotfix');
|
|
@@ -15,6 +15,21 @@ export const VALID_STATES = [
|
|
|
15
15
|
const BUILTIN_DEFAULTS = {
|
|
16
16
|
state: 'exploring',
|
|
17
17
|
workflow: 'auto',
|
|
18
|
+
// spec-superflow 2.0 双维度(v0.64.0 P0;实施计划 §3.1):
|
|
19
|
+
// workflow 仍是内部档位 full|hotfix|tweak|quick|lightweight(+auto 归一化前);
|
|
20
|
+
// workflow_variant 是用户前门意图 null|direct|planned|legacy——两者不可混用。
|
|
21
|
+
// 旧 yaml 缺字段 → null,路由语义 = legacy 分支(见 workflow-start SKILL)。
|
|
22
|
+
workflow_variant: null,
|
|
23
|
+
// 升档标记(§5.3):variant_source ∈ null|start|upgrade;direction ∈ null|up|down。
|
|
24
|
+
// guard fail-closed 校验数据源——非 exploring 改 variant 必须 source=upgrade 且 direction=up。
|
|
25
|
+
variant_source: null,
|
|
26
|
+
variant_direction: null,
|
|
27
|
+
// planned 是否走轻架构表(arch-design-light + arch-snapshot-light,G3)
|
|
28
|
+
planned_arch: false,
|
|
29
|
+
// 角色级模型分配档(config-loader MODEL_PROFILES),仅解析不改流程(§3.5)
|
|
30
|
+
model_profile: 'standard',
|
|
31
|
+
// 架构 surface 扫描基线(§4):tf state init 打戳 git HEAD;缺失时扫描退化参照
|
|
32
|
+
base_sha: null,
|
|
18
33
|
revision: null,
|
|
19
34
|
artifacts_hash: null,
|
|
20
35
|
contract_hash: null,
|
|
@@ -90,6 +105,10 @@ const BUILTIN_DEFAULTS = {
|
|
|
90
105
|
gates_probed_skipped: null,
|
|
91
106
|
gates_probed_skip_reason: null,
|
|
92
107
|
gates_probed_na: null,
|
|
108
|
+
// v0.64.0(§4 arch-design-light):轻架构说明的显式跳过键(skip-with-reason 模式,
|
|
109
|
+
// 与 arch_merge_skipped / test_matrix_skipped 同型:跳过必须附理由留痕)
|
|
110
|
+
arch_design_light_skipped: null,
|
|
111
|
+
arch_design_light_skip_reason: null,
|
|
93
112
|
// 注意:schema_version 故意不在 BUILTIN_DEFAULTS 中(v0.13 §48.1)——
|
|
94
113
|
// 它只由 `tf state init` 在 change 创建时打戳,字段缺失本身就是"存量 change"信号。
|
|
95
114
|
};
|
|
@@ -135,6 +154,26 @@ export function writeState(changeDir, state) {
|
|
|
135
154
|
lines.push('# === Core state ===');
|
|
136
155
|
lines.push(`state: ${state.state || 'exploring'}`);
|
|
137
156
|
lines.push(`workflow: ${state.workflow || 'auto'}`);
|
|
157
|
+
// v0.64.0 P0:前门双维度字段。条件序列化(同 schema_version 模式)——
|
|
158
|
+
// 存量 change 无这些字段时不追加,避免全量 churn;readState 缺失回退 BUILTIN_DEFAULTS。
|
|
159
|
+
if (state.workflow_variant != null) {
|
|
160
|
+
lines.push(`workflow_variant: ${state.workflow_variant}`);
|
|
161
|
+
}
|
|
162
|
+
if (state.variant_source != null) {
|
|
163
|
+
lines.push(`variant_source: ${state.variant_source}`);
|
|
164
|
+
}
|
|
165
|
+
if (state.variant_direction != null) {
|
|
166
|
+
lines.push(`variant_direction: ${state.variant_direction}`);
|
|
167
|
+
}
|
|
168
|
+
if (state.planned_arch === true || state.planned_arch === 'true') {
|
|
169
|
+
lines.push(`planned_arch: true`);
|
|
170
|
+
}
|
|
171
|
+
if (state.model_profile != null && state.model_profile !== 'standard') {
|
|
172
|
+
lines.push(`model_profile: ${state.model_profile}`);
|
|
173
|
+
}
|
|
174
|
+
if (state.base_sha != null) {
|
|
175
|
+
lines.push(`base_sha: ${state.base_sha}`);
|
|
176
|
+
}
|
|
138
177
|
lines.push(`revision: ${state.revision ?? 'null'}`);
|
|
139
178
|
// v0.13 §48.1:schema_version 仅由 tf state init 在 change 创建时打戳;
|
|
140
179
|
// 缺失 = 存量 change(测试门禁豁免键)。rebuild/set 不追加,故条件序列化。
|
|
@@ -225,6 +264,10 @@ export function writeState(changeDir, state) {
|
|
|
225
264
|
lines.push(`gates_probed_skipped: ${state.gates_probed_skipped ?? 'null'}`);
|
|
226
265
|
lines.push(`gates_probed_skip_reason: ${state.gates_probed_skip_reason ?? 'null'}`);
|
|
227
266
|
lines.push(`gates_probed_na: ${state.gates_probed_na ?? 'null'}`);
|
|
267
|
+
lines.push('');
|
|
268
|
+
lines.push('# === Light architecture design gate (v0.64.0) ===');
|
|
269
|
+
lines.push(`arch_design_light_skipped: ${state.arch_design_light_skipped ?? 'null'}`);
|
|
270
|
+
lines.push(`arch_design_light_skip_reason: ${state.arch_design_light_skip_reason ?? 'null'}`);
|
|
228
271
|
|
|
229
272
|
fs.writeFileSync(filePath, lines.join('\n') + '\n', 'utf-8');
|
|
230
273
|
}
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
// scripts/lib/surface-scan.mjs — 架构 surface 扫描共享层(v0.64.0,实施计划 §4)
|
|
2
|
+
//
|
|
3
|
+
// 唯一真相源:direct-short-path / arch-merged-light / test-merged-light /
|
|
4
|
+
// cross-change-consistency-light 都从这里取「本次变更碰了哪些文件、是否命中架构 surface」。
|
|
5
|
+
// 判据统一为「架构 surface = API / DB / 聚合」(R2 A-17 统一,禁止各 check 自造变体)。
|
|
6
|
+
//
|
|
7
|
+
// 三源合并(B-05/B-06):已提交 diff + 工作区修改 + untracked;基线 = state.base_sha,
|
|
8
|
+
// 缺失退化 origin/<default> merge-base,再无 → no-baseline(fail-closed,禁 merge-base HEAD HEAD 空过)。
|
|
9
|
+
// 聚合清单(B-04):.team-flow/aggregate-dirs.txt 机器清单(每行一个目录前缀,# 注释,
|
|
10
|
+
// 空文件 = 显式确认无聚合);与 docs/architecture/baseline.md 皆缺失 → missing(fail-closed)。
|
|
11
|
+
import { execFileSync } from 'node:child_process';
|
|
12
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
13
|
+
import { dirname, join } from 'node:path';
|
|
14
|
+
|
|
15
|
+
const GIT_TIMEOUT_MS = 2_000; // §4 扫描预算 ≤2s(cmd-state spawnSync 10s 总超时)
|
|
16
|
+
|
|
17
|
+
// API/DB surface 路径模式(§4 路径模式表)——聚合只认清单目录,不做词义解析
|
|
18
|
+
const SURFACE_PATH_RE = /(^|\/)(api|apis|routes|controllers?)(\/|$)|mapper|migrations?(\/|$)|(^|\/)schema[._/-]|\.(sql)$/i;
|
|
19
|
+
// 测试文件(test-merged-light 用)
|
|
20
|
+
const TEST_PATH_RE = /(^|\/)(tests?|__tests__|spec)(\/|$)|\.(test|spec)\.[jt]sx?$|Test\.java$/i;
|
|
21
|
+
|
|
22
|
+
function git(args, cwd) {
|
|
23
|
+
return execFileSync('git', args, {
|
|
24
|
+
cwd,
|
|
25
|
+
encoding: 'utf-8',
|
|
26
|
+
timeout: GIT_TIMEOUT_MS,
|
|
27
|
+
maxBuffer: 8 * 1024 * 1024,
|
|
28
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** 由 changes/<name> 推项目根:优先含 changes/ 或 docs/ 的祖先 */
|
|
33
|
+
export function findProjectRoot(changeDir) {
|
|
34
|
+
const a = dirname(dirname(changeDir));
|
|
35
|
+
if (existsSync(join(a, 'changes')) || existsSync(join(a, 'docs'))) return a;
|
|
36
|
+
const b = dirname(changeDir);
|
|
37
|
+
if (existsSync(join(b, 'changes')) || existsSync(join(b, 'docs'))) return b;
|
|
38
|
+
return a;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* 解析聚合清单。返回 { missing, dirs[] }。
|
|
43
|
+
* missing=true 表示两处皆无 → 调用方 fail-closed。
|
|
44
|
+
*/
|
|
45
|
+
export function readAggregateList(projectRoot) {
|
|
46
|
+
const txt = join(projectRoot, '.team-flow', 'aggregate-dirs.txt');
|
|
47
|
+
const baseline = join(projectRoot, 'docs', 'architecture', 'baseline.md');
|
|
48
|
+
const txtExists = existsSync(txt);
|
|
49
|
+
const baselineExists = existsSync(baseline);
|
|
50
|
+
if (!txtExists && !baselineExists) return { missing: true, dirs: [] };
|
|
51
|
+
if (!txtExists) return { missing: false, dirs: [] }; // 有 baseline 但无机器清单:清单维度放行,聚合匹配按空表(见设计 §4)
|
|
52
|
+
const dirs = readFileSync(txt, 'utf-8')
|
|
53
|
+
.split('\n')
|
|
54
|
+
.map(l => l.trim())
|
|
55
|
+
.filter(l => l && !l.startsWith('#'));
|
|
56
|
+
return { missing: false, dirs };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function readScanIgnore(projectRoot) {
|
|
60
|
+
const p = join(projectRoot, '.team-flow', 'scan-ignore');
|
|
61
|
+
if (!existsSync(p)) return [];
|
|
62
|
+
return readFileSync(p, 'utf-8')
|
|
63
|
+
.split('\n')
|
|
64
|
+
.map(l => l.trim())
|
|
65
|
+
.filter(l => l && !l.startsWith('#'));
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* 三源合并的变更文件清单。
|
|
70
|
+
* 返回 { error: null|'no-baseline'|'diff-failed', files: string[], base }
|
|
71
|
+
*/
|
|
72
|
+
export function gitChangedFiles(changeDir, state) {
|
|
73
|
+
const files = new Set();
|
|
74
|
+
let base = state?.base_sha || null;
|
|
75
|
+
|
|
76
|
+
// 退化:base_sha 缺失 → origin/<default> merge-base(B-05:参照对象写死)
|
|
77
|
+
if (!base) {
|
|
78
|
+
try {
|
|
79
|
+
const ref = git(['symbolic-ref', 'refs/remotes/origin/HEAD'], changeDir).trim();
|
|
80
|
+
const remoteBranch = ref.replace('refs/remotes/', '');
|
|
81
|
+
base = git(['merge-base', 'HEAD', remoteBranch], changeDir).trim();
|
|
82
|
+
} catch {
|
|
83
|
+
return { error: 'no-baseline', files: [], base: null };
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// 源①:已提交 diff(base...HEAD)
|
|
88
|
+
try {
|
|
89
|
+
for (const f of git(['diff', '--name-only', `${base}...HEAD`], changeDir).split('\n')) {
|
|
90
|
+
if (f.trim()) files.add(f.trim());
|
|
91
|
+
}
|
|
92
|
+
} catch {
|
|
93
|
+
return { error: 'diff-failed', files: [], base };
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
// 源②:工作区已修改
|
|
97
|
+
try {
|
|
98
|
+
for (const f of git(['diff', '--name-only', 'HEAD'], changeDir).split('\n')) {
|
|
99
|
+
if (f.trim()) files.add(f.trim());
|
|
100
|
+
}
|
|
101
|
+
} catch { /* 无 HEAD 时忽略 */ }
|
|
102
|
+
|
|
103
|
+
// 源③:untracked(porcelain -uall;B-06:新建未 add 的文件只有此源可见)
|
|
104
|
+
try {
|
|
105
|
+
for (const line of git(['status', '--porcelain', '-uall'], changeDir).split('\n')) {
|
|
106
|
+
if (!line) continue;
|
|
107
|
+
let p = line.slice(3);
|
|
108
|
+
const arrow = p.indexOf(' -> ');
|
|
109
|
+
if (arrow !== -1) p = p.slice(arrow + 4); // rename 取新名
|
|
110
|
+
p = p.trim().replace(/^"|"$/g, '');
|
|
111
|
+
if (p) files.add(p);
|
|
112
|
+
}
|
|
113
|
+
} catch { /* 忽略 */ }
|
|
114
|
+
|
|
115
|
+
// 排除清单(B-06:防无关 WIP 误报)
|
|
116
|
+
const ignore = readScanIgnore(findProjectRoot(changeDir));
|
|
117
|
+
const out = [...files].filter(f => !ignore.some(prefix => f.startsWith(prefix)));
|
|
118
|
+
return { error: null, files: out, base };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* 文件 → 架构 surface 分类。
|
|
123
|
+
* 返回 { architectureSurface, apiFiles, dbFiles, aggregateFiles, testFiles }
|
|
124
|
+
*/
|
|
125
|
+
export function classifyFiles(files, aggregateDirs) {
|
|
126
|
+
const apiFiles = [];
|
|
127
|
+
const dbFiles = [];
|
|
128
|
+
const aggregateFiles = [];
|
|
129
|
+
const testFiles = [];
|
|
130
|
+
for (const f of files) {
|
|
131
|
+
if (TEST_PATH_RE.test(f)) testFiles.push(f);
|
|
132
|
+
if (SURFACE_PATH_RE.test(f)) {
|
|
133
|
+
apiFiles.push(f);
|
|
134
|
+
if (/migrations?(\/|$)|schema[._/-]|\.(sql)$/i.test(f)) dbFiles.push(f);
|
|
135
|
+
}
|
|
136
|
+
if (aggregateDirs.some(d => {
|
|
137
|
+
const norm = d.replace(/\/+$/, '');
|
|
138
|
+
return f === norm || f.startsWith(`${norm}/`);
|
|
139
|
+
})) {
|
|
140
|
+
aggregateFiles.push(f);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
const architectureSurface = [...new Set([...apiFiles, ...aggregateFiles])];
|
|
144
|
+
return { architectureSurface, apiFiles, dbFiles, aggregateFiles, testFiles };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** 便捷组合:扫描 + 清单 + 分类(各 light runner 统一入口) */
|
|
148
|
+
export function scanArchitectureSurface(changeDir, state) {
|
|
149
|
+
const changed = gitChangedFiles(changeDir, state);
|
|
150
|
+
if (changed.error) return { error: changed.error, projectRoot: findProjectRoot(changeDir) };
|
|
151
|
+
const projectRoot = findProjectRoot(changeDir);
|
|
152
|
+
const agg = readAggregateList(projectRoot);
|
|
153
|
+
if (agg.missing) return { error: 'aggregate-list-missing', projectRoot };
|
|
154
|
+
const cls = classifyFiles(changed.files, agg.dirs);
|
|
155
|
+
return { error: null, projectRoot, files: changed.files, ...cls };
|
|
156
|
+
}
|
|
@@ -40,6 +40,8 @@ function parseArgv(argv) {
|
|
|
40
40
|
parsed.projectRoot = argv[++i];
|
|
41
41
|
} else if (argv[i] === '--dry-run') {
|
|
42
42
|
parsed.dryRun = true;
|
|
43
|
+
} else if (argv[i] === '--light') {
|
|
44
|
+
parsed.light = true;
|
|
43
45
|
} else if (!argv[i].startsWith('--')) {
|
|
44
46
|
parsed._.push(argv[i]);
|
|
45
47
|
}
|
|
@@ -584,11 +586,13 @@ export async function run(args) {
|
|
|
584
586
|
async function main(argv, projectRoot) {
|
|
585
587
|
const changeDir = argv._[0];
|
|
586
588
|
const dryRun = argv.dryRun || false;
|
|
589
|
+
const light = argv.light || false; // v0.64.0 §4:planned 轻回写入口(同 rewriteIndex 单写)
|
|
587
590
|
|
|
588
591
|
if (!changeDir) {
|
|
589
|
-
console.error('Usage: tf test-merge <change-dir> [--project-root <path>] [--dry-run]');
|
|
592
|
+
console.error('Usage: tf test-merge <change-dir> [--project-root <path>] [--dry-run] [--light]');
|
|
590
593
|
process.exit(2);
|
|
591
594
|
}
|
|
595
|
+
if (light) console.log(' Mode: LIGHT(增量条目 + change:<name> 归因锚写入 changelog)');
|
|
592
596
|
|
|
593
597
|
const absChangeDir = resolve(changeDir);
|
|
594
598
|
const changeName = extractChangeName(absChangeDir);
|
|
@@ -643,7 +647,11 @@ async function main(argv, projectRoot) {
|
|
|
643
647
|
console.log(`${mark} Step 3: resolveDeferred — ${deferredResult.resolved} items resolved`);
|
|
644
648
|
|
|
645
649
|
// Step 4: appendChangelog
|
|
646
|
-
|
|
650
|
+
// v0.64.0 §4:light 模式在 changelog 首行写入归因锚 `change:<name>`——
|
|
651
|
+
// arch-merged-light 同型的 guard 归因判据(test-merged-light)依赖该锚,
|
|
652
|
+
// full 模式文件名即含 changeName,锚为显式加固不改变既有内容。
|
|
653
|
+
const changelogContent = light ? `<!-- change:${changeName} -->\n${matrixContent}` : matrixContent;
|
|
654
|
+
const changelogPath = appendChangelog(ledgerDir, changeName, changelogContent, dryRun);
|
|
647
655
|
console.log(`${mark} Step 4: appendChangelog — ${relative(projectRoot, changelogPath)}`);
|
|
648
656
|
|
|
649
657
|
// Step 5: rewriteIndex
|
package/scripts/team-flow.mjs
CHANGED
|
@@ -71,9 +71,12 @@ Commands:
|
|
|
71
71
|
arch scaffold Scaffold global docs/architecture/ ledger in generator format (v0.53.0 §102)
|
|
72
72
|
arch precheck <change-dir> [--json]
|
|
73
73
|
Emit deterministic architecture-gate evidence (v0.22 §88; evidence only, exit 0)
|
|
74
|
-
arch
|
|
74
|
+
arch scan-code [--root <path>|--project-root <path>] [--repos <a,b>] [--annotation <name>] [--exclude <a,b>] [--json]
|
|
75
|
+
Scan code for persistence annotations (C4 anchor; v1.4 §5.4-4b;
|
|
76
|
+
read-only, zero-dep, evidence only, exit 0; status=na N/A, status=partial partial coverage — neither is PASS)
|
|
77
|
+
arch-merge <change-dir> [--project-root <path>] [--dry-run] [--light]
|
|
75
78
|
Merge architecture delta into global docs/architecture/
|
|
76
|
-
test-merge <change-dir> [--project-root <path>] [--dry-run]
|
|
79
|
+
test-merge <change-dir> [--project-root <path>] [--dry-run] [--light]
|
|
77
80
|
Merge test matrix results into global docs/test-ledger/
|
|
78
81
|
test-matrix-export <input.json> <output.md> [--change-id <id>]
|
|
79
82
|
Convert glaf4 test-matrix.json to team-flow test-matrix.md
|
|
@@ -90,7 +93,7 @@ Commands:
|
|
|
90
93
|
pytest: terminal summary or junit XML (--junitxml)
|
|
91
94
|
config [options] Display or modify configuration
|
|
92
95
|
config --resolve-model <profile> Resolve a configured model profile without switching models
|
|
93
|
-
state <sub> <dir> Manage .team-flow.yaml state (init|check|transition|get|rebuild)
|
|
96
|
+
state <sub> <dir> Manage .team-flow.yaml state (init|check|transition|get|rebuild|set|upgrade)
|
|
94
97
|
inject <dir> Generate phase-guard artifacts; use --platforms <name|all> when platform is ambiguous
|
|
95
98
|
audit <dir> Generate decision-point-audit.md from .team-flow.yaml
|
|
96
99
|
checkpoint save <change-dir> --task <id> --next <text>
|
|
@@ -22,7 +22,7 @@ description: 基于 4A 企业架构 + DDD 领域驱动设计的架构/API/DB 设
|
|
|
22
22
|
实体(唯一标识)+值对象(无标识)+聚合根(唯一入口)+事务边界(聚合内一事务)。聚合仅存于业务服务;数据服务/技术服务无聚合。
|
|
23
23
|
|
|
24
24
|
### F5 · 限界上下文(Context Map)
|
|
25
|
-
语义边界=L3
|
|
25
|
+
语义边界=L3 应用服务;同术语异义须显式映射——**经典 8 模式**(Shared Kernel / Customer-Supplier / Conformist / Anti-Corruption Layer / Open Host Service / Separate Ways / Partnership / Published Language),选型走决策流(`references/context-map-8.md`,含 Mermaid;CML NO-GO)。
|
|
26
26
|
|
|
27
27
|
### F6 · CQRS 写读模型
|
|
28
28
|
事务型对象→写模型(聚合,Command/Read 操作);分析型对象→读模型(查询模型,Query 派生,无事务)。Command/Read→写模型;Query 经阻断测试分流。
|
|
@@ -37,7 +37,7 @@ description: 基于 4A 企业架构 + DDD 领域驱动设计的架构/API/DB 设
|
|
|
37
37
|
- ch01-4a-domains — 4A 四域定义与分叉依赖
|
|
38
38
|
- ch02-change-cascade — 变更级联与跨域一致性门禁
|
|
39
39
|
- ch03-architecture-outputs — 架构产出三层 + 治理三支柱
|
|
40
|
-
- ch04-entity-to-aggregate —
|
|
40
|
+
- ch04-entity-to-aggregate — 业务实体→聚合→子域→限界上下文
|
|
41
41
|
- ch05-cqrs — 写/读模型、指令分流、三维判定
|
|
42
42
|
- ch06-integration — 与 team-flow / compound-engineering 的集成
|
|
43
43
|
|
|
@@ -54,6 +54,11 @@ description: 基于 4A 企业架构 + DDD 领域驱动设计的架构/API/DB 设
|
|
|
54
54
|
- Command / Read / Query → ch05
|
|
55
55
|
- 阻断测试 / 三维判定 → ch05
|
|
56
56
|
- 增量设计 / As-Is 冻结 / 复利回写 / 全局锚点 → ch06
|
|
57
|
+
- 子域 / 问题空间·解空间 / 统一语言索引 → ch04, 产品级模板 §0/§1.1(O2/O3)
|
|
58
|
+
- Context Map 8 模式 / 决策流 / Mermaid → references/context-map-8.md(O7)
|
|
59
|
+
- 事件 schema 版本策略 / 领域事件表 → 产品级模板 §3.2 门禁段 + 变更级模板 §2.5(O4)
|
|
60
|
+
- Saga 补偿矩阵 / Projection 重建 / ADR 模板 → references/ddd-evented-playbook.md, references/adr-templates.md(O5/O6)
|
|
61
|
+
- DDD 深度 advisory / ddd_depth(lightweight≠skipped)→ 「DDD 深度 advisory」段 + 结构化输出契约(O1)
|
|
57
62
|
|
|
58
63
|
## Workflow Integration: 判断+执行一体化(v0.9 §26)
|
|
59
64
|
|
|
@@ -67,10 +72,19 @@ description: 基于 4A 企业架构 + DDD 领域驱动设计的架构/API/DB 设
|
|
|
67
72
|
|
|
68
73
|
- `change-brief.md`(scope / AC / 技术方向)
|
|
69
74
|
- `requirement/vN/plan.md` 高阶技术设计段(模块边界/技术选型/数据流/关键聚合划分)
|
|
70
|
-
- `docs/architecture/iterations/vN/architecture.md
|
|
75
|
+
- `docs/architecture/iterations/vN/architecture.md`(产品级架构快照,**设计期主输入**,v0.35.0)——BC 边界/聚合所有权/全局契约的设计输入(★ O8:**落地态权威 = 全局 ARCHITECTURE.md**(registry 渲染),快照 = grounding + seed 源 + 体检基准,不称唯一事实源);**Fast Path 下不读**(见 `### Fast Path`)
|
|
71
76
|
- 全局 `docs/architecture/`(As-Is 实际态基线,已落地部分)
|
|
72
77
|
- 现有 `specs/`(若有)
|
|
73
78
|
|
|
79
|
+
### DDD 深度 advisory(O1 · 在五项检查**之前**执行)
|
|
80
|
+
|
|
81
|
+
按**领域复杂度**输出 DDD 深度旗标(advisory,**绝不替代 `decision`**):
|
|
82
|
+
|
|
83
|
+
- **三判据**(禁「4 选 2」硬阈值,防 LLM 套用偏差放大):① 是否有丰富行为/不变量 ② 是否存在模型冲突(同词异义/多义) ③ 是否存在值得深建模的 Core Domain。
|
|
84
|
+
- 输出 `ddd_depth: full | lightweight`。吸收「3 Questions + 反模式红牌」(微服务过早 / CQRS / 事件溯源 / DDD / Repository 可能过度工程)。
|
|
85
|
+
- **★ 头号红牌:`lightweight ≠ skipped`**——lightweight 分支**仍须走完五项检查,且第 4/5 项(API / DB schema)不得短路**;只是战术建模从简(可省略部分 DDD 战术制品),判定与 API/DB 设计照做。
|
|
86
|
+
- **诚实边界**:LLM 是否把 lightweight 误当"可跳过"**无法机械观测**——由契约正交 lint 与步骤表静态检查收敛路径,行为层保留人审(§10 风险如实登记)。
|
|
87
|
+
|
|
74
88
|
### 五项检查(架构变更判定)
|
|
75
89
|
|
|
76
90
|
依次检查以下五项,**全部为否** → `decision: skipped`;**任一为是** → `decision: required`:
|
|
@@ -111,25 +125,23 @@ description: 基于 4A 企业架构 + DDD 领域驱动设计的架构/API/DB 设
|
|
|
111
125
|
**流程**(不硬阻断,显式确认 + 登记 deviation):
|
|
112
126
|
1. **呈现变更摘要**:涉及的产品级决策 + 影响范围(引用快照章节)
|
|
113
127
|
2. **用户确认**(阻塞问题工具):接受 → 执行变更;拒绝 → 保持产品级定义,change 内走增量
|
|
114
|
-
3. **登记 deviation**:确认后写入 `orchestrator.yaml` 的 `replan_log`(`seq/trigger: arch-deviation/before/after/approved_by
|
|
128
|
+
3. **登记 deviation**:确认后写入 `orchestrator.yaml` 的 `replan_log`(`seq/trigger: arch-deviation/before/after/approved_by`——**可机读唯一落点**);**Rationale/Consequences 按 `references/adr-templates.md` 选型**(五模板;仅「难逆转 ∧ 无上下文会困惑 ∧ 真实权衡」三条件全满足才写完整 ADR,否则 replan_log 一行即可,防泛滥;**不另起 `docs/adr/`**,防双轨漂移)+ 在 `iterations/vN/architecture.md` 演进日志追加修订记录(迭代收尾 S3.5 确认晋升)
|
|
115
129
|
4. **`new` 聚合 flag**:在 `iterations/vN/architecture.md` 聚合注册表标注 `[pending-promotion]`,待下一迭代产品级晋升
|
|
116
130
|
|
|
117
131
|
### 执行流程
|
|
118
132
|
|
|
119
133
|
```
|
|
120
134
|
1. 读取输入(brief + plan + specs + 全局 ARCHITECTURE.md)——**Fast Path 裁剪为「brief + precheck 输出」**
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
→ reason: 简述不涉及架构变更的理由
|
|
125
|
-
→ 返回结构化输出
|
|
135
|
+
1.5 DDD 深度 advisory(领域复杂度三判据 → ddd_depth: full | lightweight;advisory,不影响 decision)
|
|
136
|
+
2. 执行五项检查(ddd_depth: lightweight 分支**同样执行全部五项**,第 4/5 项不得短路)
|
|
137
|
+
3. 全部为否 → decision: skipped + reason(不涉及架构变更)→ 返回结构化输出(含 ddd_depth)
|
|
126
138
|
4. 任一为是:
|
|
127
139
|
→ decision: required
|
|
128
140
|
→ reason: 简述涉及的架构变更项
|
|
129
|
-
→
|
|
141
|
+
→ 执行 4A+DDD 增量设计(F1-F8;ddd_depth: lightweight 时战术制品从简,API/DB 产出不减)
|
|
130
142
|
→ 产出 architecture/architecture.md + database.md + api.md
|
|
131
143
|
→ artifacts: 产出路径列表
|
|
132
|
-
→
|
|
144
|
+
→ 返回结构化输出(含 ddd_depth)
|
|
133
145
|
```
|
|
134
146
|
|
|
135
147
|
### 结构化输出契约
|
|
@@ -137,6 +149,8 @@ description: 基于 4A 企业架构 + DDD 领域驱动设计的架构/API/DB 设
|
|
|
137
149
|
返回给 workflow-start 的结果**必须**包含以下字段:
|
|
138
150
|
|
|
139
151
|
```yaml
|
|
152
|
+
ddd_depth: full | lightweight # O1 advisory:与 decision **正交**——任意组合合法;
|
|
153
|
+
# 禁止从 lightweight 推导/默认出 skipped(契约正交 lint 锁定)
|
|
140
154
|
decision: required | skipped
|
|
141
155
|
reason: "..." # skipped 时:不涉及架构变更的理由
|
|
142
156
|
# required 时:涉及的变更项摘要
|
|
@@ -146,6 +160,10 @@ artifacts: # required 时必填,skipped 时为空
|
|
|
146
160
|
- architecture/api.md
|
|
147
161
|
```
|
|
148
162
|
|
|
163
|
+
> **schema 正交(O1 判据①,规则文本 lint)**:`ddd_depth ∈ {full, lightweight}` 与 `decision ∈ {required, skipped}`
|
|
164
|
+
> 同时存在、取值域独立、**任意组合合法**(lightweight+required / lightweight+skipped / full+required / full+skipped 全允许)——
|
|
165
|
+
> schema 层**不得**出现 `lightweight → skipped` 的推导或默认值。该 lint 校验的是**我们写下的规则文本自洽**,不校验 LLM 实际判定(诚实边界见 advisory 段)。
|
|
166
|
+
|
|
149
167
|
**职责边界**:architecture-design 负责**判断+产出**(五项检查判断是否涉及架构变更,涉及则产出架构设计文档),workflow-start 负责**reasonableness check + 状态写入**(确认判断合理性后写入 yaml)。
|
|
150
168
|
|
|
151
169
|
### 产出目录
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# ch04 ·
|
|
1
|
+
# ch04 · 业务实体→聚合→子域→限界上下文
|
|
2
2
|
|
|
3
3
|
## 业务实体(识别起点)
|
|
4
4
|
- 定义:BA 流程中的**表证单书**(订单/合同/工单/客户档案/库存记录),是业务概念而非数据库表。
|
|
@@ -15,12 +15,23 @@
|
|
|
15
15
|
- 事务边界:聚合内所有操作须在一个事务完成(如创建订单同时建头/行项目/算总价)。
|
|
16
16
|
- **硬规则**:聚合仅存在于业务服务;数据服务(跨聚合查询分析)、技术服务(消息队列)**无聚合**。
|
|
17
17
|
|
|
18
|
-
##
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
18
|
+
## 子域(问题空间 · 投资分级)
|
|
19
|
+
- **子域(Subdomain)**:业务问题空间的分区(Vernon《IDDD》),先于解空间存在;子域 → 限界上下文是**多对多映射**(一个 BC 常服务多个子域),不是 1:1。
|
|
20
|
+
- **三分类**:**Core**(核心竞争力,深建模)/ **Supporting**(保障性,最小定制)/ **Generic**(通用能力,买或复用)——**分类驱动 BC 投资**。
|
|
21
|
+
- **纪律**:子域描述只写**业务问题**,不列 service / 包名 / 类名(问题空间 ≠ 解空间;按实现载体划子域/BC 是已知失效模式,须人审)。
|
|
22
|
+
|
|
23
|
+
## 限界上下文 / Context Map(核心框架 F5 · 经典 8 模式,O7)
|
|
24
|
+
- 限界上下文=语义边界,对应 L3 应用服务;相关聚合组成上下文(**解空间,≠ 子域**)。
|
|
25
|
+
- 同术语异义须显式映射——**8 模式全集**(与产品级模板 §1.2 同步,教 = 用):
|
|
26
|
+
- **Separate Ways**:不集成(先问要不要集成再选型)。
|
|
27
|
+
- **Partnership**:对等团队共同演进、双向承诺。
|
|
28
|
+
- **Customer-Supplier**:供需协商契约。
|
|
29
|
+
- **Conformist**:下游全盘接受上游模型。
|
|
30
|
+
- **Open Host Service**:上游开放标准协议供多下游消费。
|
|
31
|
+
- **Anti-Corruption Layer**:下游翻译上游模型,防止概念泄漏。
|
|
32
|
+
- **Shared Kernel**:两上下文共享部分模型,变更须协商。
|
|
33
|
+
- **Published Language**:标准化语义词汇(与 OHS 天然配对)。
|
|
34
|
+
- **决策流与 Mermaid 生成**:见 `references/context-map-8.md`(选型顺序 Separate Ways → Partnership → Customer-Supplier → Conformist → OHS+PL → ACL → Shared Kernel;CML DSL 导出 = NO-GO)。
|
|
24
35
|
|
|
25
36
|
## 应用提示
|
|
26
37
|
- 每变更设计先画"涉及实体的活动对象矩阵";再定聚合根与事务边界;最后落到全局 Context Map。
|