@dommaker/harness 1.8.0 → 1.9.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 (79) hide show
  1. package/CHANGELOG.md +18 -1
  2. package/README.md +1 -1
  3. package/dist/cli/commands/check.d.ts +8 -0
  4. package/dist/cli/commands/check.d.ts.map +1 -1
  5. package/dist/cli/commands/check.js +10 -11
  6. package/dist/cli/commands/check.js.map +1 -1
  7. package/dist/cli/commands/status.d.ts +8 -0
  8. package/dist/cli/commands/status.d.ts.map +1 -1
  9. package/dist/cli/commands/status.js +5 -7
  10. package/dist/cli/commands/status.js.map +1 -1
  11. package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts +22 -0
  12. package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts.map +1 -1
  13. package/dist/cli/commands/sync-docs/capabilities-syncer.js +113 -8
  14. package/dist/cli/commands/sync-docs/capabilities-syncer.js.map +1 -1
  15. package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
  16. package/dist/cli/commands/sync-docs/index.js +36 -4
  17. package/dist/cli/commands/sync-docs/index.js.map +1 -1
  18. package/dist/cli/state-io.d.ts +34 -0
  19. package/dist/cli/state-io.d.ts.map +1 -0
  20. package/dist/cli/state-io.js +71 -0
  21. package/dist/cli/state-io.js.map +1 -0
  22. package/dist/hooks/bootstrap.d.ts +9 -12
  23. package/dist/hooks/bootstrap.d.ts.map +1 -1
  24. package/dist/hooks/bootstrap.js +9 -31
  25. package/dist/hooks/bootstrap.js.map +1 -1
  26. package/dist/hooks/index.d.ts +5 -7
  27. package/dist/hooks/index.d.ts.map +1 -1
  28. package/dist/hooks/index.js +4 -10
  29. package/dist/hooks/index.js.map +1 -1
  30. package/dist/index.d.ts +2 -2
  31. package/dist/index.d.ts.map +1 -1
  32. package/dist/index.js +3 -7
  33. package/dist/index.js.map +1 -1
  34. package/package.json +1 -1
  35. package/src/CONTEXT.md +2 -2
  36. package/src/__tests__/public-exports.test.ts +30 -4
  37. package/src/__tests__/public-type-surface.test.ts +0 -8
  38. package/src/cli/__tests__/state-io.test.ts +50 -0
  39. package/src/cli/commands/CONTEXT.md +4 -0
  40. package/src/cli/commands/__tests__/check-read-count.test.ts +3 -0
  41. package/src/cli/commands/__tests__/check.test.ts +20 -8
  42. package/src/cli/commands/__tests__/registry.test.ts +39 -3
  43. package/src/cli/commands/__tests__/status-extra.test.ts +25 -8
  44. package/src/cli/commands/__tests__/status.test.ts +46 -18
  45. package/src/cli/commands/__tests__/sync-docs-table-layout.test.ts +259 -0
  46. package/src/cli/commands/check.ts +20 -18
  47. package/src/cli/commands/status.ts +12 -7
  48. package/src/cli/commands/sync-docs/capabilities-syncer.ts +139 -9
  49. package/src/cli/commands/sync-docs/index.ts +41 -4
  50. package/src/cli/state-io.ts +53 -0
  51. package/src/hooks/CONTEXT.md +12 -22
  52. package/src/hooks/__tests__/bootstrap.test.ts +21 -70
  53. package/src/hooks/bootstrap.ts +9 -48
  54. package/src/hooks/index.ts +5 -20
  55. package/src/index.ts +1 -13
  56. package/dist/hooks/config.d.ts +0 -30
  57. package/dist/hooks/config.d.ts.map +0 -1
  58. package/dist/hooks/config.js +0 -34
  59. package/dist/hooks/config.js.map +0 -1
  60. package/dist/hooks/pipeline.d.ts +0 -36
  61. package/dist/hooks/pipeline.d.ts.map +0 -1
  62. package/dist/hooks/pipeline.js +0 -133
  63. package/dist/hooks/pipeline.js.map +0 -1
  64. package/dist/hooks/registry.d.ts +0 -70
  65. package/dist/hooks/registry.d.ts.map +0 -1
  66. package/dist/hooks/registry.js +0 -141
  67. package/dist/hooks/registry.js.map +0 -1
  68. package/dist/hooks/types.d.ts +0 -111
  69. package/dist/hooks/types.d.ts.map +0 -1
  70. package/dist/hooks/types.js +0 -9
  71. package/dist/hooks/types.js.map +0 -1
  72. package/src/__tests__/hooks-pipeline.test.ts +0 -201
  73. package/src/hooks/__tests__/config.test.ts +0 -19
  74. package/src/hooks/__tests__/pipeline.test.ts +0 -90
  75. package/src/hooks/__tests__/registry.test.ts +0 -115
  76. package/src/hooks/config.ts +0 -33
  77. package/src/hooks/pipeline.ts +0 -155
  78. package/src/hooks/registry.ts +0 -166
  79. package/src/hooks/types.ts +0 -118
@@ -0,0 +1,53 @@
1
+ /**
2
+ * 状态文件 IO 接缝(StateIO,ADR-0026 / harness#148)
3
+ *
4
+ * `.harness/.state.json` 是 harness **自身**的运行期状态(与 RunEnv 观察的项目上行
5
+ * 数据不是一类东西,RunEnv 保持 ADR-0023 的只读契约)。本模块是它读-改-写的唯一
6
+ * 入口:`check` 的智能提示与 `status` 两个写者都经本接缝,命令函数以可选参数注入,
7
+ * 缺省 = 真实 fs 实现(照 `CommandIO` 的注入模式,但不扩 `CommandIO`——输出面与
8
+ * 状态面是两个概念)。
9
+ *
10
+ * 落点 cli 层:两个消费者都在 cli,不进 core、不进包根导出面(零公共面变化)。
11
+ */
12
+
13
+ import * as fs from 'fs';
14
+ import * as path from 'path';
15
+
16
+ /**
17
+ * `.harness/.state.json` 的具名类型(从 check.ts 内联类型提出,ADR-0026 决策 1)。
18
+ * `lastDiagnoseRun` 字段已删除——全仓零生产者零消费者(ADR-0022 口径);
19
+ * 已落盘旧文件里残留该键无害,不做迁移。
20
+ */
21
+ export interface HarnessState {
22
+ /** 已展示过的智能提示 id(去重依据) */
23
+ shownHints?: string[];
24
+ /** 最近一次 `harness status` 运行时刻(ISO 串) */
25
+ lastStatusRun?: string;
26
+ }
27
+
28
+ /** harness 自身运行期状态文件的可注入接缝(读-改-写的唯一入口) */
29
+ export interface StateIO {
30
+ /** 文件缺失/空 → `{}`;损坏 → 现状语义(JSON.parse 抛即抛,不兜底,ADR-0026 决策 4) */
31
+ read(): HarnessState;
32
+ write(state: HarnessState): void;
33
+ }
34
+
35
+ /**
36
+ * 缺省真实 fs 实现:锚 projectPath(与 trace 落点同锚,#139),
37
+ * 状态文件路径知识只在本模块一份。
38
+ */
39
+ export function fileStateIO(projectPath: string): StateIO {
40
+ const statePath = path.join(projectPath, '.harness', '.state.json');
41
+ return {
42
+ read(): HarnessState {
43
+ if (!fs.existsSync(statePath)) return {};
44
+ const content = fs.readFileSync(statePath, 'utf-8');
45
+ if (content.trim() === '') return {};
46
+ return JSON.parse(content);
47
+ },
48
+ write(state: HarnessState): void {
49
+ fs.mkdirSync(path.dirname(statePath), { recursive: true });
50
+ fs.writeFileSync(statePath, JSON.stringify(state, null, 2));
51
+ },
52
+ };
53
+ }
@@ -1,34 +1,24 @@
1
1
  # hooks/
2
2
 
3
3
  ## 职责
4
- 通用 Hook 管线:注册 → 排序 → 错误隔离 → 采样执行。无业务逻辑,consumer 自行定义 hook 名称和语义。
4
+ Harness 运行环境的组合根:一次调用装配起约束检查器、会话管理器与 trace 记录器,并加载项目配置。
5
5
 
6
- H5(#44)起增加两个机制(G2/G7):
7
- - **注册表闭环**:`assertHookRegistryClosed(configs, hooks)` 声明(HookConfig)↔ 实现(HookDefinition)双向校验——引用未注册/注册无定义/重复均抛错,复制 checker 闭环模式;断言限构建/测试期,不进运行时热路径
8
- - **配置归一**:`HookConfig { name, enabled, errorStrategy }` 为 per-hook 配置唯一真相,且自 #159 起是 `enabled` / `errorStrategy` 的**唯一声明点**——`HookDefinition` 不再携带这两个字段,注册环节以配置表填充有效值(`EffectiveHook`),声明一处、读取一处,两侧矛盾在构造上不可能出现;`toErrorStrategy(blocking)` 承载 studio `blocking` → errorStrategy 的无损映射
6
+ ADR-0027(#170)起本层只剩 bootstrap 一个面。原先的通用 hook 管线(`registry` / `pipeline` / `config` / `types` 四文件:注册 → 排序 → 错误隔离 → 采样执行)双仓零生产消费者,整体删除——裁决记录 `docs/adr/0027-hooks-pipeline-surface-trim.md`,事实前提 studio#562(studio 侧 hooks 层删除)。目录名沿用历史,不再表示「提供 hook 能力」。
9
7
 
10
8
  ## 核心导出
11
- - `HookRegistry` — Hook 注册表(register/registerAll/unregister/get/getEnabled/listNames/listAll/setEnabled/clear;register HookConfig 填充有效值)
12
- - `HookPipeline` — Hook 执行管线(注册/排序/错误隔离/采样;errorStrategy block/warn)
13
- - `assertHookRegistryClosed` 注册表闭环双向校验(构建/测试期)
14
- - `HookConfig`(type)— per-hook 配置声明(enabled / errorStrategy 唯一声明点)
15
- - `EffectiveHook`(type)— HookDefinition + 配置填充的有效 enabled / errorStrategy(管线与注册表判定只读它)
16
- - `toErrorStrategy` — blocking → errorStrategy 无损映射(G7)
17
- - `bootstrapHarness` / `bootstrapHarnessSync` — Harness 启动引导;也是 trace 记录器的组合根:`new ConstraintChecker(new TraceCollector({ projectPath }))`(harness#88 接线,core 不上行依赖 monitoring,故由本层接线;#139 收根:两个入口本就收 projectPath,落点随之锚定,不再取 cwd 锚定的 `getTraceCollector()` 单例)
9
+ - `bootstrapHarness` — 异步组合根(S9:配置异步加载,不阻塞事件循环);也是 trace 记录器的接线点:`new ConstraintChecker(new TraceCollector({ projectPath }))`(harness#88:core 不上行依赖 monitoring,故由本层接线;#139 收根:两个入口本就收 projectPath,落点随之锚定,不再取 cwd 锚定的 `getTraceCollector()` 单例)
10
+ - `bootstrapHarnessSync` — 同形状的同步版,配置走 `readFileSync`,供不支持 top-level await 的环境与 `bootstrapHarness` 失败时的回落路径
11
+ - `HarnessBootstrap`(type)— 返回值形状 `{ checker, sessions, projectPath, mergedConstraints }`
18
12
 
19
13
  ## 依赖关系
20
- - 类型正本在**本模块** `src/hooks/types.ts`(`HookPhase`/`HookErrorStrategy`/`HookConfig`/`HookDefinition`/`EffectiveHook`/`HookResult`/`PipelineResult`);`src/types/` 里没有 hook 类型,本层只从 `src/types/project-config` 取 `MergedConstraintsConfig` 类型
21
- - 向下依赖(仅 `bootstrap.ts` 组合根):`core/constraints/checker`、`core/project-config-loader`、`context/session-manager`、`monitoring/traces`——`registry/pipeline/config/types` 四文件零上层依赖,是纯管线
22
- - 消费方:包根 `src/index.ts` 的 `./hooks` 出口与下游项目(studio 的 hook 装配)。**harness 内部无生产消费方**——core/cli 侧的调用边已随零消费者清账删除(harness#141 同判据,ADR-0022),「被 core 核心引擎/CLI 初始化流程消费」的旧说法已失效(harness#142 核对)
14
+ - 向下依赖:`core/constraints/checker`、`core/project-config-loader`、`context/session-manager`、`monitoring/traces`;类型面只从 `src/types/project-config` 取 `MergedConstraintsConfig`
15
+ - 消费方:包根 `src/index.ts` 的 bootstrap 三符号出口,与下游项目的运行环境初始化。生产唯一调用方是 studio(经 studio-shared `runtime/bootstrap.ts`,**无参调用**、只以类型持有 `HarnessBootstrap`)。**harness 内部无生产消费方**——core/cli 侧的调用边已随零消费者清账删除(harness#141 同判据,ADR-0022)
23
16
 
24
17
  ## 约定
25
- - 无业务逻辑,只提供管线能力
26
- - Consumer 自行定义 hook 名称和触发时机
27
- - **闭环**:声明配置与注册实现必须一一对应,缺一抛错(消灭「hook 定义不注册 = 死代码」类人记规矩)
28
- - **映射语义**:blocking=true → 'block'(失败阻断管线,停止后续 hook、passed=false);blocking=false → 'warn'(记录警告继续);有效策略集合仅 'block' | 'warn'(#159 起原 'ignore' 与「未声明 strategy」形态退出有效面——策略由 HookConfig 必填声明)
29
- - 错误隔离:单个 hook 失败不影响其他 hook
18
+ - 参数面只有 `projectPath`(缺省 `process.cwd()`):不给 hook 定义、不给配置表,`hookDefinitions` / `hookConfigs` 与注册闭环语义随管线面一同退场
19
+ - 组合根只做装配不做判定,`mergedConstraints` 原样透出,用不用由调用方决定
20
+ - 本层无内置 hook 定义,也不再提供 hook 管线能力;provider 侧的 PreToolUse 执法入口是 `src/pretool-use-hook.ts`(门禁 CLI 的另一条路),与本目录无关
30
21
 
31
22
  ## 注意事项
32
- - 通用管线设计,不绑定特定生命周期
33
- - 采样执行用于高频 hook(减少性能影响)
34
- - 闭环断言是纯函数,由 consumer 在其注册点/测试中调用;harness 无内置 hook 定义,不自动断言
23
+ - 无 unload/dispose:`SessionManager` 与 `TraceCollector` 随进程生命周期,本层不提供逆操作(口径同 `src/CONTEXT.md` 术语「文件驱动 CLI」)
24
+ - 删除属公共面 breaking:包根不再可达的符号有 4 个值符号(注册表、管线、闭环断言、blocking 映射)与 8 个类型,迁移路径 = 删引用;可达性负钉在 `src/__tests__/public-exports.test.ts`,两道全量清单闸(`public-exports` / `public-type-surface`)的条目已同步收缩
@@ -5,17 +5,17 @@
5
5
  * bootstrapHarnessSync uses synchronous fs reads and is more straightforward.
6
6
  *
7
7
  * Uses temp directories to provide harness config.
8
+ *
9
+ * ADR-0027(#170):hooks 管线面删除后本层只剩组合根职责——加载项目配置、
10
+ * 装配 checker / SessionManager / TraceCollector。`hookDefinitions` / `hookConfigs`
11
+ * 两参数与 `hooks` / `pipeline` 两字段随之消失,形状由编译期 + 运行期双钉冻结
12
+ * (手法照 ADR-0022 对 AC-007 的改写)。
8
13
  */
9
14
 
10
15
  import * as fs from 'fs';
11
16
  import * as path from 'path';
12
17
  import * as yaml from 'js-yaml';
13
18
  import { bootstrapHarnessSync, bootstrapHarness } from '../bootstrap';
14
- import type { HookConfig, HookDefinition } from '../types';
15
-
16
- function makeHookConfig(name: string, overrides: Partial<HookConfig> = {}): HookConfig {
17
- return { name, enabled: true, errorStrategy: 'warn', ...overrides };
18
- }
19
19
 
20
20
  function setupTempDir(dir: string): string {
21
21
  const harnessDir = path.join(dir, '.harness');
@@ -28,6 +28,16 @@ function setupTempDir(dir: string): string {
28
28
  return harnessDir;
29
29
  }
30
30
 
31
+ /** 已删字段的编译期钉:回灌(重新声明该字段)即 TS2578 红。纯读,不产生任何落盘副作用。 */
32
+ function assertPipelineSurfaceUnreachable(result: ReturnType<typeof bootstrapHarnessSync>): void {
33
+ // @ts-expect-error hooks 字段随 ADR-0027 管线面删除
34
+ const gone = result.hooks;
35
+ expect(gone).toBeUndefined();
36
+ // @ts-expect-error pipeline 字段随 ADR-0027 管线面删除
37
+ const gone2 = result.pipeline;
38
+ expect(gone2).toBeUndefined();
39
+ }
40
+
31
41
  describe('bootstrapHarness', () => {
32
42
  let tempDir: string;
33
43
 
@@ -44,10 +54,11 @@ describe('bootstrapHarness', () => {
44
54
  const result = await bootstrapHarness(tempDir);
45
55
  expect(result).toHaveProperty('checker');
46
56
  expect(result).toHaveProperty('sessions');
47
- expect(result).toHaveProperty('hooks');
48
- expect(result).toHaveProperty('pipeline');
49
57
  expect(result).toHaveProperty('projectPath', tempDir);
50
58
  expect(result).toHaveProperty('mergedConstraints');
59
+ expect(Object.keys(result).sort()).toEqual(
60
+ ['checker', 'mergedConstraints', 'projectPath', 'sessions'],
61
+ );
51
62
  });
52
63
 
53
64
  it('initializes with the provided project path', async () => {
@@ -55,33 +66,8 @@ describe('bootstrapHarness', () => {
55
66
  expect(result.projectPath).toBe(tempDir);
56
67
  });
57
68
 
58
- it('registers hook definitions when provided (with configs)', async () => {
59
- const hookDef: HookDefinition = {
60
- name: 'async-hook',
61
- phase: 'before',
62
- execute: async () => ({ passed: true }),
63
- };
64
- const result = await bootstrapHarness(tempDir, [hookDef], [makeHookConfig('async-hook')]);
65
- expect(result.hooks.listNames()).toContain('async-hook');
66
- expect(result.hooks.get('async-hook')?.errorStrategy).toBe('warn');
67
- });
68
-
69
- it('throws when definitions are provided without configs (#159)', async () => {
70
- const hookDef: HookDefinition = {
71
- name: 'async-hook',
72
- phase: 'before',
73
- execute: async () => ({ passed: true }),
74
- };
75
- await expect(bootstrapHarness(tempDir, [hookDef])).rejects.toThrow(/HookConfig/);
76
- });
77
-
78
- it('does not register hooks when no definitions provided', async () => {
79
- const result = await bootstrapHarness(tempDir, []);
80
- expect(result.hooks.listNames()).toEqual([]);
81
- });
82
-
83
69
  it('uses process.cwd() when no project path is given', async () => {
84
- const result = await bootstrapHarness(undefined, []);
70
+ const result = await bootstrapHarness();
85
71
  expect(result).toHaveProperty('projectPath');
86
72
  expect(result.projectPath).toBeDefined();
87
73
  });
@@ -122,10 +108,9 @@ describe('bootstrapHarnessSync', () => {
122
108
  const result = bootstrapHarnessSync(tempDir);
123
109
  expect(result).toHaveProperty('checker');
124
110
  expect(result).toHaveProperty('sessions');
125
- expect(result).toHaveProperty('hooks');
126
- expect(result).toHaveProperty('pipeline');
127
111
  expect(result).toHaveProperty('projectPath', tempDir);
128
112
  expect(result).toHaveProperty('mergedConstraints');
113
+ assertPipelineSurfaceUnreachable(result);
129
114
  });
130
115
 
131
116
  it('initializes with the provided project path', () => {
@@ -140,34 +125,10 @@ describe('bootstrapHarnessSync', () => {
140
125
  expect(result.mergedConstraints).toHaveProperty('guidelines');
141
126
  });
142
127
 
143
- it('registers hook definitions when provided (with configs)', () => {
144
- const hookDef: HookDefinition = {
145
- name: 'test-hook',
146
- phase: 'before',
147
- execute: async () => ({ passed: true }),
148
- };
149
- const result = bootstrapHarnessSync(tempDir, [hookDef], [makeHookConfig('test-hook')]);
150
- expect(result.hooks.listNames()).toContain('test-hook');
151
- });
152
-
153
- it('throws when definitions are provided without configs (#159)', () => {
154
- const hookDef: HookDefinition = {
155
- name: 'test-hook',
156
- phase: 'before',
157
- execute: async () => ({ passed: true }),
158
- };
159
- expect(() => bootstrapHarnessSync(tempDir, [hookDef])).toThrow(/HookConfig/);
160
- });
161
-
162
- it('does not register hooks when no definitions provided', () => {
163
- const result = bootstrapHarnessSync(tempDir, []);
164
- expect(result.hooks.listNames()).toEqual([]);
165
- });
166
-
167
128
  it('uses process.cwd() when no project path is given', () => {
168
129
  // We cannot easily test process.cwd() fallback without mocking cwd,
169
130
  // but we can verify the function accepts undefined
170
- const result = bootstrapHarnessSync(undefined, []);
131
+ const result = bootstrapHarnessSync(undefined);
171
132
  // Should not throw with process.cwd() — harness directory may not exist
172
133
  expect(result).toHaveProperty('projectPath');
173
134
  expect(result.projectPath).toBeDefined();
@@ -182,16 +143,6 @@ describe('bootstrapHarnessSync', () => {
182
143
  expect(fs.existsSync(sessionDir)).toBe(true);
183
144
  });
184
145
 
185
- it('creates a working HookPipeline', () => {
186
- const hookDef: HookDefinition = {
187
- name: 'pipeline-hook',
188
- phase: 'after',
189
- execute: async () => ({ passed: true, data: 'ok' }),
190
- };
191
- const result = bootstrapHarnessSync(tempDir, [hookDef], [makeHookConfig('pipeline-hook')]);
192
- expect(result.pipeline).toBeDefined();
193
- });
194
-
195
146
  it('loads from .harness/config.yml when present', () => {
196
147
  // Write a custom config with specific settings
197
148
  const harnessDir = path.join(tempDir, '.harness');
@@ -8,21 +8,22 @@
8
8
  * Consumer 用法:
9
9
  * ```typescript
10
10
  * const harness = await bootstrapHarness('/path/to/project');
11
- * // harness.checker, harness.config, harness.hooks, harness.sessions
11
+ * // harness.checker, harness.sessions, harness.mergedConstraints
12
12
  * ```
13
13
  *
14
14
  * checker 的 trace 记录器在此接线(harness#88:core 不上行依赖 monitoring),
15
15
  * 且锚在本函数收到的 projectPath 上(#139:落点跟根走,不落调用方 cwd)。
16
+ *
17
+ * ADR-0027(#170):hooks 管线面(registry/pipeline/config/types)双仓零生产消费者,
18
+ * 整体删除;本层的 `hookDefinitions` / `hookConfigs` 两参数与 `hooks` / `pipeline`
19
+ * 两字段随之消失——生产唯一调用方本就无参调用、不访问被删字段。
16
20
  */
17
21
 
18
22
  import { ConstraintChecker } from '../core/constraints/checker';
19
23
  import { SessionManager } from '../context/session-manager';
20
24
  import { ProjectConfigLoader } from '../core/project-config-loader';
21
25
  import { TraceCollector } from '../monitoring/traces';
22
- import { HookRegistry } from './registry';
23
- import { HookPipeline } from './pipeline';
24
26
  import type { MergedConstraintsConfig } from '../types/project-config';
25
- import type { HookConfig, HookDefinition } from './types';
26
27
 
27
28
  /**
28
29
  * 异步加载项目配置(S9:异步 I/O)
@@ -50,46 +51,19 @@ export interface HarnessBootstrap {
50
51
  checker: ConstraintChecker;
51
52
  /** 会话管理器 */
52
53
  sessions: SessionManager;
53
- /** Hook 注册表 */
54
- hooks: HookRegistry;
55
- /** Hook 管线 */
56
- pipeline: HookPipeline;
57
54
  /** 项目路径 */
58
55
  projectPath: string;
59
56
  /** 合并后的约束配置 */
60
57
  mergedConstraints: MergedConstraintsConfig;
61
58
  }
62
59
 
63
- /**
64
- * 注册初始 hook(定义必须配对配置声明表——#159 起 HookConfig 是
65
- * enabled / errorStrategy 的唯一声明点,缺表即抛错)
66
- */
67
- function registerInitialHooks(
68
- hooks: HookRegistry,
69
- hookDefinitions: HookDefinition[] | undefined,
70
- hookConfigs: HookConfig[] | undefined
71
- ): void {
72
- if (!hookDefinitions || hookDefinitions.length === 0) return;
73
- if (!hookConfigs) {
74
- throw new Error(
75
- '[harness] bootstrap 失败:注册 hook 定义必须同时提供 HookConfig 声明表' +
76
- '(enabled / errorStrategy 的唯一声明点)。'
77
- );
78
- }
79
- hooks.registerAll(hookDefinitions, hookConfigs);
80
- }
81
-
82
60
  /**
83
61
  * 初始化 harness 运行环境(异步,不阻塞事件循环)
84
62
  *
85
63
  * @param projectPath 项目根路径
86
- * @param hookDefinitions 可选,初始化时注册的 hook
87
- * @param hookConfigs 可选,与 hookDefinitions 配对的配置声明表(注册定义时必填)
88
64
  */
89
65
  export async function bootstrapHarness(
90
- projectPath?: string,
91
- hookDefinitions?: HookDefinition[],
92
- hookConfigs?: HookConfig[]
66
+ projectPath?: string
93
67
  ): Promise<HarnessBootstrap> {
94
68
  const resolvedPath = projectPath || process.cwd();
95
69
 
@@ -101,17 +75,10 @@ export async function bootstrapHarness(
101
75
  const checker = new ConstraintChecker(new TraceCollector({ projectPath: resolvedPath }));
102
76
 
103
77
  const sessions = new SessionManager(resolvedPath);
104
- const hooks = new HookRegistry();
105
- const pipeline = new HookPipeline(hooks);
106
-
107
- // 3. 注册初始 hook
108
- registerInitialHooks(hooks, hookDefinitions, hookConfigs);
109
78
 
110
79
  return {
111
80
  checker,
112
81
  sessions,
113
- hooks,
114
- pipeline,
115
82
  projectPath: resolvedPath,
116
83
  mergedConstraints,
117
84
  };
@@ -121,11 +88,11 @@ export async function bootstrapHarness(
121
88
  * 同步 bootstrap(兼容不支持 top-level await 的环境)
122
89
  *
123
90
  * 配置加载仍为同步(readFileSync),其他组件初始化同上。
91
+ *
92
+ * @param projectPath 项目根路径
124
93
  */
125
94
  export function bootstrapHarnessSync(
126
- projectPath?: string,
127
- hookDefinitions?: HookDefinition[],
128
- hookConfigs?: HookConfig[]
95
+ projectPath?: string
129
96
  ): HarnessBootstrap {
130
97
  const resolvedPath = projectPath || process.cwd();
131
98
 
@@ -136,16 +103,10 @@ export function bootstrapHarnessSync(
136
103
  const checker = new ConstraintChecker(new TraceCollector({ projectPath: resolvedPath }));
137
104
 
138
105
  const sessions = new SessionManager(resolvedPath);
139
- const hooks = new HookRegistry();
140
- const pipeline = new HookPipeline(hooks);
141
-
142
- registerInitialHooks(hooks, hookDefinitions, hookConfigs);
143
106
 
144
107
  return {
145
108
  checker,
146
109
  sessions,
147
- hooks,
148
- pipeline,
149
110
  projectPath: resolvedPath,
150
111
  mergedConstraints,
151
112
  };
@@ -1,25 +1,10 @@
1
1
  /**
2
2
  * Hooks 模块
3
3
  *
4
- * 通用 hook 管线:注册 排序 → 错误隔离 → 采样执行。
5
- * 无业务逻辑,consumer 自行定义 hook 名称和语义。
4
+ * ADR-0027(#170)起本层只剩 `bootstrap.ts` 一个面:通用 hook 管线
5
+ * (registry / pipeline / config / types 四文件)双仓零生产消费者,整体删除。
6
+ * 目录名沿用历史,职责是 harness 运行环境的组合根。
6
7
  */
7
8
 
8
- export type {
9
- HookPhase,
10
- HookErrorStrategy,
11
- HookConfig,
12
- HookDefinition,
13
- EffectiveHook,
14
- HookResult,
15
- HookExecutionRecord,
16
- PipelineResult,
17
- } from './types';
18
- export { HookRegistry, assertHookRegistryClosed } from './registry';
19
- export { HookPipeline } from './pipeline';
20
- export { toErrorStrategy } from './config';
21
- export {
22
- bootstrapHarness,
23
- bootstrapHarnessSync,
24
- type HarnessBootstrap,
25
- } from './bootstrap';
9
+ export { bootstrapHarness, bootstrapHarnessSync } from './bootstrap';
10
+ export type { HarnessBootstrap } from './bootstrap';
package/src/index.ts CHANGED
@@ -342,25 +342,13 @@ export { getCriticalArtifacts, verifyReleaseArtifacts } from './release';
342
342
  export type { ArtifactIntegrityResult } from './release';
343
343
 
344
344
  // ========================================
345
- // Hooks 管线
345
+ // Harness 运行环境引导(bootstrap 单面;hooks 管线面已随 ADR-0027 删除)
346
346
  // ========================================
347
347
  export {
348
- HookRegistry,
349
- assertHookRegistryClosed,
350
- HookPipeline,
351
- toErrorStrategy,
352
348
  bootstrapHarness,
353
349
  bootstrapHarnessSync,
354
350
  } from './hooks';
355
351
  export type {
356
- HookDefinition,
357
- HookConfig,
358
- EffectiveHook,
359
- HookErrorStrategy,
360
- HookExecutionRecord,
361
- HookPhase,
362
- HookResult,
363
- PipelineResult,
364
352
  HarnessBootstrap,
365
353
  } from './hooks';
366
354
 
@@ -1,30 +0,0 @@
1
- /**
2
- * Hook 配置归一(G7)
3
- *
4
- * studio 侧 per-hook 运行时配置(`blocking: boolean`)与 harness 侧
5
- * `HookConfig.errorStrategy` 双轨归一:以 errorStrategy 为唯一真相,
6
- * `blocking` → errorStrategy 为无损映射。
7
- *
8
- * 映射语义(确认于 H5):
9
- * - blocking=true → 'block':hook 失败阻断管线(停止执行后续 hook,
10
- * 管线结果 passed=false、blockedBy 记名)——对应 studio safeCallHook 抛错
11
- * - blocking=false → 'warn':hook 失败记录警告并继续执行后续 hook
12
- * (管线结果 warnings 记名,passed 不受影响)——对应 studio safeCallHook
13
- * console.warn 继续
14
- *
15
- * #159 起 `HookConfig` 是 `enabled` / `errorStrategy` 的唯一声明点:
16
- * HookDefinition 不再携带这两个字段,注册环节以配置表填充有效值,
17
- * 有效策略集合收敛为 'block' | 'warn'(原 'ignore' 退出有效面)。
18
- *
19
- * studio 侧随动(studio #150 A4):hooks/config.ts 删 DEFAULTS/safeCallHook,
20
- * blocking→errorStrategy 无损映射进 toHookDef;HARNESS_HOOK_DISABLE 保留 studio。
21
- */
22
- import type { HookErrorStrategy } from './types';
23
- /**
24
- * blocking 布尔 → errorStrategy 无损映射(block/warn)
25
- *
26
- * @param blocking studio 侧「失败是否阻断」语义
27
- * @returns 'block'(阻断)| 'warn'(警告继续)
28
- */
29
- export declare function toErrorStrategy(blocking: boolean): HookErrorStrategy;
30
- //# sourceMappingURL=config.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/hooks/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,OAAO,GAAG,iBAAiB,CAEpE"}
@@ -1,34 +0,0 @@
1
- "use strict";
2
- /**
3
- * Hook 配置归一(G7)
4
- *
5
- * studio 侧 per-hook 运行时配置(`blocking: boolean`)与 harness 侧
6
- * `HookConfig.errorStrategy` 双轨归一:以 errorStrategy 为唯一真相,
7
- * `blocking` → errorStrategy 为无损映射。
8
- *
9
- * 映射语义(确认于 H5):
10
- * - blocking=true → 'block':hook 失败阻断管线(停止执行后续 hook,
11
- * 管线结果 passed=false、blockedBy 记名)——对应 studio safeCallHook 抛错
12
- * - blocking=false → 'warn':hook 失败记录警告并继续执行后续 hook
13
- * (管线结果 warnings 记名,passed 不受影响)——对应 studio safeCallHook
14
- * console.warn 继续
15
- *
16
- * #159 起 `HookConfig` 是 `enabled` / `errorStrategy` 的唯一声明点:
17
- * HookDefinition 不再携带这两个字段,注册环节以配置表填充有效值,
18
- * 有效策略集合收敛为 'block' | 'warn'(原 'ignore' 退出有效面)。
19
- *
20
- * studio 侧随动(studio #150 A4):hooks/config.ts 删 DEFAULTS/safeCallHook,
21
- * blocking→errorStrategy 无损映射进 toHookDef;HARNESS_HOOK_DISABLE 保留 studio。
22
- */
23
- Object.defineProperty(exports, "__esModule", { value: true });
24
- exports.toErrorStrategy = toErrorStrategy;
25
- /**
26
- * blocking 布尔 → errorStrategy 无损映射(block/warn)
27
- *
28
- * @param blocking studio 侧「失败是否阻断」语义
29
- * @returns 'block'(阻断)| 'warn'(警告继续)
30
- */
31
- function toErrorStrategy(blocking) {
32
- return blocking ? 'block' : 'warn';
33
- }
34
- //# sourceMappingURL=config.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/hooks/config.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;;AAUH,0CAEC;AARD;;;;;GAKG;AACH,SAAgB,eAAe,CAAC,QAAiB;IAC/C,OAAO,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC;AACrC,CAAC"}
@@ -1,36 +0,0 @@
1
- /**
2
- * HookPipeline — hook 有序执行、错误隔离、采样
3
- *
4
- * 执行顺序:before hooks → consumer operation → after hooks
5
- * 每个 hook 独立 try/catch,单个 hook 失败不影���其他 hook。
6
- */
7
- import type { HookPhase, PipelineResult } from './types';
8
- import type { HookRegistry } from './registry';
9
- export declare class HookPipeline<C = unknown> {
10
- private registry;
11
- constructor(registry: HookRegistry<C, unknown>);
12
- /**
13
- * 执行指定 phase 的所有 enabled hook
14
- *
15
- * @param phase hook 时机
16
- * @param context 传递给每个 hook 的上下文
17
- * @returns 管线执行结果
18
- */
19
- run(phase: HookPhase, context: C): Promise<PipelineResult>;
20
- /**
21
- * 执行 before + after 全套管线
22
- *
23
- * @param context 上下文
24
- * @param operation 业务操作(在 before 和 after 之间执行)
25
- * @returns 管线结果 + 操作结果
26
- */
27
- runFull<R>(context: C, operation: () => Promise<R>): Promise<{
28
- pipelineResult: PipelineResult;
29
- operationResult?: R;
30
- }>;
31
- /**
32
- * 执行单个 hook(带采样和错误隔离)
33
- */
34
- private executeOne;
35
- }
36
- //# sourceMappingURL=pipeline.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/hooks/pipeline.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAEV,SAAS,EAET,cAAc,EACf,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,qBAAa,YAAY,CAAC,CAAC,GAAG,OAAO;IACnC,OAAO,CAAC,QAAQ,CAA2B;gBAE/B,QAAQ,EAAE,YAAY,CAAC,CAAC,EAAE,OAAO,CAAC;IAI9C;;;;;;OAMG;IACG,GAAG,CACP,KAAK,EAAE,SAAS,EAChB,OAAO,EAAE,CAAC,GACT,OAAO,CAAC,cAAc,CAAC;IAyB1B;;;;;;OAMG;IACG,OAAO,CAAC,CAAC,EACb,OAAO,EAAE,CAAC,EACV,SAAS,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAC1B,OAAO,CAAC;QAAE,cAAc,EAAE,cAAc,CAAC;QAAC,eAAe,CAAC,EAAE,CAAC,CAAA;KAAE,CAAC;IAwCnE;;OAEG;YACW,UAAU;CA4CzB"}