@dommaker/harness 1.2.3 → 1.3.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 +7 -0
- package/dist/cli/commands/definitions.js +1 -1
- package/dist/cli/commands/definitions.js.map +1 -1
- package/dist/cli/commands/failure.d.ts.map +1 -1
- package/dist/cli/commands/failure.js +2 -1
- package/dist/cli/commands/failure.js.map +1 -1
- package/dist/cli/commands/knowledge.d.ts.map +1 -1
- package/dist/cli/commands/knowledge.js +24 -1
- package/dist/cli/commands/knowledge.js.map +1 -1
- package/dist/cli/commands/release.d.ts.map +1 -1
- package/dist/cli/commands/release.js +6 -20
- package/dist/cli/commands/release.js.map +1 -1
- package/dist/cli/commands/sync-docs/agents-syncer.js +26 -14
- package/dist/cli/commands/sync-docs/agents-syncer.js.map +1 -1
- package/dist/completion-checkers/index.d.ts +3 -3
- package/dist/completion-checkers/index.js +3 -3
- package/dist/completion-checkers/phase-format.d.ts +1 -1
- package/dist/completion-checkers/phase-format.js +2 -2
- package/dist/completion-checkers/phase-format.js.map +1 -1
- package/dist/completion-checkers/tdd-chain.d.ts +1 -1
- package/dist/completion-checkers/tdd-chain.js +2 -2
- package/dist/completion-checkers/tdd-chain.js.map +1 -1
- package/dist/completion-checkers/types.d.ts +5 -5
- package/dist/completion-checkers/types.d.ts.map +1 -1
- package/dist/completion-checkers/types.js +2 -2
- package/dist/failure/recorder.d.ts +1 -1
- package/dist/failure/recorder.js +1 -1
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +13 -4
- package/dist/index.js.map +1 -1
- package/dist/knowledge/index-generator.js +1 -1
- package/dist/knowledge/index-generator.js.map +1 -1
- package/dist/release/index.d.ts +3 -0
- package/dist/release/index.d.ts.map +1 -0
- package/dist/release/index.js +11 -0
- package/dist/release/index.js.map +1 -0
- package/dist/release/integrity.d.ts +65 -0
- package/dist/release/integrity.d.ts.map +1 -0
- package/dist/release/integrity.js +151 -0
- package/dist/release/integrity.js.map +1 -0
- package/dist/types/failure.d.ts +7 -0
- package/dist/types/failure.d.ts.map +1 -1
- package/dist/types/failure.js +8 -1
- package/dist/types/failure.js.map +1 -1
- package/package.json +1 -1
- package/src/__tests__/failure-recorder.test.ts +9 -0
- package/src/__tests__/public-exports.test.ts +3 -0
- package/src/cli/commands/__tests__/knowledge.test.ts +35 -3
- package/src/cli/commands/__tests__/release.test.ts +22 -1
- package/src/cli/commands/__tests__/sync-docs-agents.test.ts +33 -10
- package/src/cli/commands/definitions.ts +1 -1
- package/src/cli/commands/failure.ts +2 -1
- package/src/cli/commands/knowledge.ts +26 -1
- package/src/cli/commands/release.ts +6 -20
- package/src/cli/commands/sync-docs/agents-syncer.ts +26 -13
- package/src/completion-checkers/CONTEXT.md +1 -1
- package/src/completion-checkers/__tests__/contract-presence.test.ts +1 -1
- package/src/completion-checkers/__tests__/phase-format.test.ts +1 -1
- package/src/completion-checkers/__tests__/tdd-chain.test.ts +2 -2
- package/src/completion-checkers/index.ts +3 -3
- package/src/completion-checkers/phase-format.ts +2 -2
- package/src/completion-checkers/tdd-chain.ts +2 -2
- package/src/completion-checkers/types.ts +5 -5
- package/src/failure/recorder.ts +1 -1
- package/src/index.ts +11 -2
- package/src/knowledge/__tests__/index-generator.test.ts +2 -2
- package/src/knowledge/index-generator.ts +1 -1
- package/src/release/CONTEXT.md +23 -0
- package/src/release/__tests__/integrity.test.ts +157 -0
- package/src/release/index.ts +13 -0
- package/src/release/integrity.ts +141 -0
- package/src/types/failure.ts +8 -0
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"failure.js","sourceRoot":"","sources":["../../src/types/failure.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"failure.js","sourceRoot":"","sources":["../../src/types/failure.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -333,3 +333,12 @@ describe('FailureRecorder', () => {
|
|
|
333
333
|
});
|
|
334
334
|
});
|
|
335
335
|
});
|
|
336
|
+
|
|
337
|
+
describe('DEFAULT_FAILURE_LOG_FILE', () => {
|
|
338
|
+
it('锁口径:统一到 .harness/logs/(与 DEFAULT_TRACE_FILE 同目录约定),从 barrel 公开导出', async () => {
|
|
339
|
+
const { DEFAULT_FAILURE_LOG_FILE } = await import('../types/failure');
|
|
340
|
+
const barrel = await import('../index');
|
|
341
|
+
expect(DEFAULT_FAILURE_LOG_FILE).toBe('.harness/logs/failures.log');
|
|
342
|
+
expect(barrel.DEFAULT_FAILURE_LOG_FILE).toBe(DEFAULT_FAILURE_LOG_FILE);
|
|
343
|
+
});
|
|
344
|
+
});
|
|
@@ -29,6 +29,7 @@ const EXPECTED_RUNTIME_EXPORTS = [
|
|
|
29
29
|
'DEFAULT_COMMAND_BLACKLIST',
|
|
30
30
|
'DEFAULT_COMPACTION_CONFIG',
|
|
31
31
|
'DEFAULT_DECAY_CONFIG',
|
|
32
|
+
'DEFAULT_FAILURE_LOG_FILE',
|
|
32
33
|
'DEFAULT_LEVEL_MAPPING',
|
|
33
34
|
'DEFAULT_NONCODE_GLOBS',
|
|
34
35
|
'DEFAULT_TEST_GLOBS',
|
|
@@ -103,6 +104,7 @@ const EXPECTED_RUNTIME_EXPORTS = [
|
|
|
103
104
|
'getCommandGate',
|
|
104
105
|
'getCommandRiskLevel',
|
|
105
106
|
'getConstraint',
|
|
107
|
+
'getCriticalArtifacts',
|
|
106
108
|
'getEffectiveConstraints',
|
|
107
109
|
'getEffectiveGates',
|
|
108
110
|
'getFailureLevel',
|
|
@@ -127,6 +129,7 @@ const EXPECTED_RUNTIME_EXPORTS = [
|
|
|
127
129
|
'validateSpec',
|
|
128
130
|
'verifyContractPresence',
|
|
129
131
|
'verifyPhaseFormat',
|
|
132
|
+
'verifyReleaseArtifacts',
|
|
130
133
|
'verifyTddChain',
|
|
131
134
|
];
|
|
132
135
|
|
|
@@ -68,13 +68,20 @@ const MOCK_REPORT = {
|
|
|
68
68
|
};
|
|
69
69
|
|
|
70
70
|
describe('getKnowledgeDir', () => {
|
|
71
|
+
const fs = require('fs');
|
|
71
72
|
const os = require('os');
|
|
72
73
|
const path = require('path');
|
|
73
74
|
const storeModule = require('../../../knowledge/store');
|
|
74
75
|
let storeCtorSpy: jest.SpyInstance;
|
|
76
|
+
let homedirSpy: jest.SpyInstance;
|
|
77
|
+
let errorSpy: jest.SpyInstance;
|
|
78
|
+
let tmpHome: string;
|
|
75
79
|
|
|
76
80
|
beforeEach(() => {
|
|
77
81
|
delete process.env.KNOWLEDGE_BASE_DIR;
|
|
82
|
+
tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-kdir-'));
|
|
83
|
+
homedirSpy = jest.spyOn(os, 'homedir').mockReturnValue(tmpHome);
|
|
84
|
+
errorSpy = jest.spyOn(console, 'error').mockImplementation();
|
|
78
85
|
storeCtorSpy = jest.spyOn(storeModule, 'FileKnowledgeStore').mockImplementation(() => ({
|
|
79
86
|
list: jest.fn().mockReturnValue([]),
|
|
80
87
|
}));
|
|
@@ -82,7 +89,10 @@ describe('getKnowledgeDir', () => {
|
|
|
82
89
|
|
|
83
90
|
afterEach(() => {
|
|
84
91
|
storeCtorSpy.mockRestore();
|
|
92
|
+
homedirSpy.mockRestore();
|
|
93
|
+
errorSpy.mockRestore();
|
|
85
94
|
delete process.env.KNOWLEDGE_BASE_DIR;
|
|
95
|
+
fs.rmSync(tmpHome, { recursive: true, force: true });
|
|
86
96
|
});
|
|
87
97
|
|
|
88
98
|
it('should use KNOWLEDGE_BASE_DIR env var when set', async () => {
|
|
@@ -93,11 +103,33 @@ describe('getKnowledgeDir', () => {
|
|
|
93
103
|
}));
|
|
94
104
|
});
|
|
95
105
|
|
|
96
|
-
it('should default to ~/.
|
|
97
|
-
|
|
106
|
+
it('should default to ~/.harness/knowledge when no env var and no legacy data', async () => {
|
|
107
|
+
await knowledgeStats({ json: true });
|
|
108
|
+
expect(storeCtorSpy).toHaveBeenCalledWith(expect.objectContaining({
|
|
109
|
+
baseDir: path.join(tmpHome, '.harness', 'knowledge'),
|
|
110
|
+
}));
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it('should keep legacy ~/.studio/knowledge when it still has data, with deprecation notice shown once', async () => {
|
|
114
|
+
const legacyDir = path.join(tmpHome, '.studio', 'knowledge');
|
|
115
|
+
fs.mkdirSync(legacyDir, { recursive: true });
|
|
116
|
+
fs.writeFileSync(path.join(legacyDir, 'guideline-x.md'), 'data');
|
|
117
|
+
await knowledgeStats({ json: true });
|
|
118
|
+
expect(storeCtorSpy).toHaveBeenCalledWith(expect.objectContaining({
|
|
119
|
+
baseDir: legacyDir,
|
|
120
|
+
}));
|
|
121
|
+
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('旧目录'));
|
|
122
|
+
// 闩锁:同进程重复解析只提示一次
|
|
123
|
+
await knowledgeStats({ json: true });
|
|
124
|
+
const notices = errorSpy.mock.calls.filter((c: any[]) => typeof c[0] === 'string' && c[0].includes('旧目录'));
|
|
125
|
+
expect(notices).toHaveLength(1);
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('should ignore legacy dir when it exists but is empty', async () => {
|
|
129
|
+
fs.mkdirSync(path.join(tmpHome, '.studio', 'knowledge'), { recursive: true });
|
|
98
130
|
await knowledgeStats({ json: true });
|
|
99
131
|
expect(storeCtorSpy).toHaveBeenCalledWith(expect.objectContaining({
|
|
100
|
-
baseDir:
|
|
132
|
+
baseDir: path.join(tmpHome, '.harness', 'knowledge'),
|
|
101
133
|
}));
|
|
102
134
|
});
|
|
103
135
|
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import * as fs from 'fs';
|
|
15
|
+
import * as path from 'path';
|
|
15
16
|
import { execSync } from 'child_process';
|
|
16
17
|
import { release } from '../release';
|
|
17
18
|
|
|
@@ -53,7 +54,13 @@ jest.mock('chalk', () => {
|
|
|
53
54
|
const mockExecSync = execSync as jest.MockedFunction<typeof execSync>;
|
|
54
55
|
const mockFs = fs as jest.Mocked<typeof fs>;
|
|
55
56
|
|
|
56
|
-
const PKG_JSON = JSON.stringify({
|
|
57
|
+
const PKG_JSON = JSON.stringify({
|
|
58
|
+
name: '@dommaker/harness',
|
|
59
|
+
version: '0.18.0',
|
|
60
|
+
main: './dist/index.js',
|
|
61
|
+
bin: './bin/harness.js',
|
|
62
|
+
exports: { '.': './dist/index.js' },
|
|
63
|
+
});
|
|
57
64
|
|
|
58
65
|
describe('release command', () => {
|
|
59
66
|
let consoleSpy: jest.SpyInstance;
|
|
@@ -135,6 +142,20 @@ describe('release command', () => {
|
|
|
135
142
|
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('Uncommitted changes'));
|
|
136
143
|
});
|
|
137
144
|
|
|
145
|
+
test('dist 关键发布物缺失:发布前闸门 exit 1(不触 npm version)', async () => {
|
|
146
|
+
mockFs.existsSync.mockImplementation((p) => {
|
|
147
|
+
if (String(p).endsWith('package.json')) return true;
|
|
148
|
+
if (String(p).includes(`${path.sep}dist${path.sep}`)) return false;
|
|
149
|
+
return true;
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
await expect(release({ bumpType: 'patch' })).rejects.toThrow('__exit_1__');
|
|
153
|
+
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('dist missing critical artifacts'));
|
|
154
|
+
expect(errorSpy).toHaveBeenCalledWith(expect.stringContaining('dist/index.js'));
|
|
155
|
+
const commands = mockExecSync.mock.calls.map(c => String(c[0]));
|
|
156
|
+
expect(commands.some(c => c.startsWith('npm version'))).toBe(false);
|
|
157
|
+
});
|
|
158
|
+
|
|
138
159
|
test('dry-run patch:计算目标版本 0.18.1,exit 0', async () => {
|
|
139
160
|
await expect(release({ bumpType: 'patch', dryRun: 'true' })).rejects.toThrow('__exit_0__');
|
|
140
161
|
expect(consoleSpy).toHaveBeenCalledWith(
|
|
@@ -242,18 +242,18 @@ describe('sync-docs --agents', () => {
|
|
|
242
242
|
fs.rmSync(testDir, { recursive: true, force: true });
|
|
243
243
|
});
|
|
244
244
|
|
|
245
|
-
describe('
|
|
246
|
-
it('.
|
|
247
|
-
const testDir = path.join(tempDir, '
|
|
245
|
+
describe('知识入口行:模块上下文正本模型', () => {
|
|
246
|
+
it('.harness/CONTEXT.md 存在时生成正本行指向新正本,替代散置指引,--check 无漂移', async () => {
|
|
247
|
+
const testDir = path.join(tempDir, 'canonical-context-model');
|
|
248
248
|
createFixture(testDir, { pnpm: true, withCapabilities: true });
|
|
249
249
|
// 夹具已带散置 src/CONTEXT.md:正本在场时整行替代,与是否残留散置文件无关
|
|
250
|
-
fs.mkdirSync(path.join(testDir, '.
|
|
251
|
-
fs.writeFileSync(path.join(testDir, '.
|
|
250
|
+
fs.mkdirSync(path.join(testDir, '.harness'), { recursive: true });
|
|
251
|
+
fs.writeFileSync(path.join(testDir, '.harness', 'CONTEXT.md'), '# 模块上下文\n\n## src\n\n职责:夹具\n');
|
|
252
252
|
|
|
253
253
|
await syncDocs({ projectPath: testDir, agents: true });
|
|
254
254
|
|
|
255
255
|
const content = fs.readFileSync(path.join(testDir, 'AGENTS.md'), 'utf-8');
|
|
256
|
-
expect(content).toContain('模块上下文正本:`.
|
|
256
|
+
expect(content).toContain('模块上下文正本:`.harness/CONTEXT.md`(模块锚点组织),改动代码时同步更新');
|
|
257
257
|
expect(content).not.toContain('各源码目录的 `CONTEXT.md`');
|
|
258
258
|
expect(content).not.toContain('缺失目录可由 `harness sync-docs` 生成模板');
|
|
259
259
|
// 知识库行不受影响
|
|
@@ -267,7 +267,30 @@ describe('sync-docs --agents', () => {
|
|
|
267
267
|
fs.rmSync(testDir, { recursive: true, force: true });
|
|
268
268
|
});
|
|
269
269
|
|
|
270
|
-
it('.studio/CONTEXT.md
|
|
270
|
+
it('旧正本 .studio/CONTEXT.md 仍在场时兼容沿用(免迁移),两者同在以 .harness/ 为准', async () => {
|
|
271
|
+
const legacyDir = path.join(tempDir, 'legacy-context-model');
|
|
272
|
+
createFixture(legacyDir, { pnpm: true, withCapabilities: true });
|
|
273
|
+
fs.mkdirSync(path.join(legacyDir, '.studio'), { recursive: true });
|
|
274
|
+
fs.writeFileSync(path.join(legacyDir, '.studio', 'CONTEXT.md'), '# 模块上下文\n\n## src\n\n职责:夹具\n');
|
|
275
|
+
await syncDocs({ projectPath: legacyDir, agents: true });
|
|
276
|
+
expect(fs.readFileSync(path.join(legacyDir, 'AGENTS.md'), 'utf-8'))
|
|
277
|
+
.toContain('模块上下文正本:`.studio/CONTEXT.md`(模块锚点组织),改动代码时同步更新');
|
|
278
|
+
fs.rmSync(legacyDir, { recursive: true, force: true });
|
|
279
|
+
|
|
280
|
+
const bothDir = path.join(tempDir, 'both-context-model');
|
|
281
|
+
createFixture(bothDir, { pnpm: true, withCapabilities: true });
|
|
282
|
+
fs.mkdirSync(path.join(bothDir, '.studio'), { recursive: true });
|
|
283
|
+
fs.writeFileSync(path.join(bothDir, '.studio', 'CONTEXT.md'), '# 旧\n');
|
|
284
|
+
fs.mkdirSync(path.join(bothDir, '.harness'), { recursive: true });
|
|
285
|
+
fs.writeFileSync(path.join(bothDir, '.harness', 'CONTEXT.md'), '# 新\n');
|
|
286
|
+
await syncDocs({ projectPath: bothDir, agents: true });
|
|
287
|
+
const content = fs.readFileSync(path.join(bothDir, 'AGENTS.md'), 'utf-8');
|
|
288
|
+
expect(content).toContain('模块上下文正本:`.harness/CONTEXT.md`');
|
|
289
|
+
expect(content).not.toContain('.studio/CONTEXT.md');
|
|
290
|
+
fs.rmSync(bothDir, { recursive: true, force: true });
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('正本不存在时保持散置模型行,--check 无漂移', async () => {
|
|
271
294
|
const testDir = path.join(tempDir, 'scattered-context-model');
|
|
272
295
|
createFixture(testDir, { pnpm: true, withCapabilities: true });
|
|
273
296
|
|
|
@@ -282,10 +305,10 @@ describe('sync-docs --agents', () => {
|
|
|
282
305
|
fs.rmSync(testDir, { recursive: true, force: true });
|
|
283
306
|
});
|
|
284
307
|
|
|
285
|
-
it('
|
|
286
|
-
const testDir = path.join(tempDir, '
|
|
308
|
+
it('正本路径是目录而非文件时不视为正本', async () => {
|
|
309
|
+
const testDir = path.join(tempDir, 'context-doc-dir');
|
|
287
310
|
createFixture(testDir, { pnpm: true, withCapabilities: true });
|
|
288
|
-
fs.mkdirSync(path.join(testDir, '.
|
|
311
|
+
fs.mkdirSync(path.join(testDir, '.harness', 'CONTEXT.md'), { recursive: true });
|
|
289
312
|
|
|
290
313
|
await syncDocs({ projectPath: testDir, agents: true });
|
|
291
314
|
|
|
@@ -251,7 +251,7 @@ export const COMMAND_DEFINITIONS: CommandDefinition[] = [
|
|
|
251
251
|
{ flags: '--title <title>', description: '知识标题(用于 upsert)' },
|
|
252
252
|
{ flags: '--content <content>', description: '知识内容 Markdown(用于 upsert)' },
|
|
253
253
|
{ flags: '--file <path>', description: '从文件读取内容(用于 upsert)' },
|
|
254
|
-
{ flags: '--source <source>', description: '知识来源 (
|
|
254
|
+
{ flags: '--source <source>', description: '知识来源 (cli/design)', defaultValue: 'cli' },
|
|
255
255
|
{ flags: '--fix', description: '自动修复(用于 audit)', defaultValue: false },
|
|
256
256
|
{ flags: '--dry-run', description: '只输出报告不修改(用于 audit)', defaultValue: false },
|
|
257
257
|
{ flags: '--threshold <n>', description: '短内容阈值(字符数,用于 audit)', defaultValue: '50' },
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import chalk from 'chalk';
|
|
8
8
|
import * as path from 'path';
|
|
9
9
|
import { FailureRecorder } from '../../failure/recorder';
|
|
10
|
+
import { DEFAULT_FAILURE_LOG_FILE } from '../../types/failure';
|
|
10
11
|
|
|
11
12
|
export interface FailureOptions {
|
|
12
13
|
/** 项目路径 */
|
|
@@ -21,7 +22,7 @@ export interface FailureOptions {
|
|
|
21
22
|
function getRecorder(projectPath?: string): FailureRecorder {
|
|
22
23
|
const base = projectPath || process.cwd();
|
|
23
24
|
return new FailureRecorder({
|
|
24
|
-
logFile: path.join(base,
|
|
25
|
+
logFile: path.join(base, DEFAULT_FAILURE_LOG_FILE),
|
|
25
26
|
});
|
|
26
27
|
}
|
|
27
28
|
|
|
@@ -499,10 +499,35 @@ export async function knowledgeHealth(options: KnowledgeOptions & { dir?: string
|
|
|
499
499
|
}
|
|
500
500
|
}
|
|
501
501
|
|
|
502
|
+
/** 缺省知识库数据根(相对用户 home) */
|
|
503
|
+
const KNOWLEDGE_DATA_DIR = path.join('.harness', 'knowledge');
|
|
504
|
+
/** 旧缺省数据根(曾寄居 studio home),仍有数据时兼容沿用,免迁移 */
|
|
505
|
+
const LEGACY_KNOWLEDGE_DATA_DIR = path.join('.studio', 'knowledge');
|
|
506
|
+
|
|
507
|
+
let legacyKnowledgeDirNotified = false;
|
|
508
|
+
|
|
509
|
+
function hasKnowledgeData(dir: string): boolean {
|
|
510
|
+
try {
|
|
511
|
+
return fs.existsSync(dir) && fs.readdirSync(dir).length > 0;
|
|
512
|
+
} catch {
|
|
513
|
+
return false;
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
|
|
502
517
|
function getKnowledgeDir(projectPath?: string): string {
|
|
503
518
|
if (projectPath) return `${projectPath}/.harness/knowledge`;
|
|
504
519
|
if (process.env.KNOWLEDGE_BASE_DIR) return process.env.KNOWLEDGE_BASE_DIR;
|
|
505
|
-
|
|
520
|
+
const defaultDir = path.join(os.homedir(), KNOWLEDGE_DATA_DIR);
|
|
521
|
+
const legacyDir = path.join(os.homedir(), LEGACY_KNOWLEDGE_DATA_DIR);
|
|
522
|
+
if (hasKnowledgeData(legacyDir)) {
|
|
523
|
+
if (!legacyKnowledgeDirNotified) {
|
|
524
|
+
legacyKnowledgeDirNotified = true;
|
|
525
|
+
// stderr:不污染 --json 的 stdout 输出
|
|
526
|
+
console.error(chalk.yellow(`⚠ 缺省知识库根已改为 ${defaultDir};旧目录 ${legacyDir} 仍有数据,本次沿用(免迁移,可用 KNOWLEDGE_BASE_DIR 覆盖)`));
|
|
527
|
+
}
|
|
528
|
+
return legacyDir;
|
|
529
|
+
}
|
|
530
|
+
return defaultDir;
|
|
506
531
|
}
|
|
507
532
|
|
|
508
533
|
/**
|
|
@@ -21,6 +21,7 @@ import { execSync } from 'child_process';
|
|
|
21
21
|
import * as fs from 'fs';
|
|
22
22
|
import * as path from 'path';
|
|
23
23
|
import chalk from 'chalk';
|
|
24
|
+
import { verifyReleaseArtifacts } from '../../release';
|
|
24
25
|
|
|
25
26
|
export interface ReleaseOptions {
|
|
26
27
|
bumpType?: 'patch' | 'minor' | 'major';
|
|
@@ -92,28 +93,13 @@ export async function release(options: ReleaseOptions): Promise<void> {
|
|
|
92
93
|
}
|
|
93
94
|
console.log(chalk.green('✅ tsc: built'));
|
|
94
95
|
|
|
95
|
-
// ── 4. Verify dist
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
'dist/knowledge/store.js',
|
|
100
|
-
'dist/knowledge/ingest.js',
|
|
101
|
-
'dist/knowledge/query.js',
|
|
102
|
-
'dist/knowledge/lifecycle.js',
|
|
103
|
-
'dist/knowledge/lint.js',
|
|
104
|
-
'dist/knowledge/types.js',
|
|
105
|
-
'dist/knowledge/import.js',
|
|
106
|
-
'dist/knowledge/reference-tracker.js',
|
|
107
|
-
'dist/knowledge/lifecycle-hooks.js',
|
|
108
|
-
'dist/knowledge/doctor.js',
|
|
109
|
-
'dist/core/constraints/prompt-injection.js',
|
|
110
|
-
];
|
|
111
|
-
const missing = criticalFiles.filter(f => !fs.existsSync(path.join(pkgPath, f)));
|
|
112
|
-
if (missing.length > 0) {
|
|
113
|
-
console.error(chalk.red(`❌ dist missing critical files: ${missing.join(', ')}`));
|
|
96
|
+
// ── 4. Verify dist(发布物完整性自检,harness#77:清单随源码维护,不再本文件硬编码)──
|
|
97
|
+
const integrity = verifyReleaseArtifacts(pkgPath);
|
|
98
|
+
if (!integrity.ok) {
|
|
99
|
+
console.error(chalk.red(`❌ dist missing critical artifacts (${integrity.missing.length}): ${integrity.missing.join(', ')}`));
|
|
114
100
|
process.exit(1);
|
|
115
101
|
}
|
|
116
|
-
console.log(chalk.green(
|
|
102
|
+
console.log(chalk.green(`✅ dist: verified (${integrity.checked.length} critical artifacts)`));
|
|
117
103
|
|
|
118
104
|
if (dryRun) {
|
|
119
105
|
const [major, minor, patch] = oldVersion.split('.').map(Number);
|
|
@@ -60,11 +60,11 @@ export interface GovernanceInfo {
|
|
|
60
60
|
*/
|
|
61
61
|
export async function buildAgentsMd(projectPath: string, srcDirs: string[]): Promise<string> {
|
|
62
62
|
const pkg = await readPackageJsonLite(projectPath);
|
|
63
|
-
const [dirs, governance, hasKnowledge,
|
|
63
|
+
const [dirs, governance, hasKnowledge, contextDocPath, contextDocCount] = await Promise.all([
|
|
64
64
|
getTopLevelDirEntries(projectPath, srcDirs),
|
|
65
65
|
getGovernanceInfo(projectPath),
|
|
66
66
|
hasKnowledgeDir(projectPath),
|
|
67
|
-
|
|
67
|
+
findCanonicalContextDoc(projectPath),
|
|
68
68
|
countContextDocs(projectPath, srcDirs),
|
|
69
69
|
]);
|
|
70
70
|
|
|
@@ -128,10 +128,10 @@ export async function buildAgentsMd(projectPath: string, srcDirs: string[]): Pro
|
|
|
128
128
|
// 写进受 --check 漂移比对的文档会制造「必然过期」竞态(任何知识写入都使已提交文档过期)
|
|
129
129
|
lines.push('- `.harness/knowledge/`:项目知识库,用 `harness knowledge` 查询');
|
|
130
130
|
}
|
|
131
|
-
if (
|
|
132
|
-
//
|
|
133
|
-
// 散置 CONTEXT.md
|
|
134
|
-
lines.push(
|
|
131
|
+
if (contextDocPath) {
|
|
132
|
+
// 正本模型:模块上下文沉淀归并到业务仓单一正本(模块锚点组织),
|
|
133
|
+
// 散置 CONTEXT.md 指引与该模型矛盾,正本在场时整行替代
|
|
134
|
+
lines.push(`- 模块上下文正本:\`${contextDocPath}\`(模块锚点组织),改动代码时同步更新`);
|
|
135
135
|
} else {
|
|
136
136
|
lines.push(
|
|
137
137
|
contextDocCount > 0
|
|
@@ -254,17 +254,30 @@ async function hasKnowledgeDir(projectPath: string): Promise<boolean> {
|
|
|
254
254
|
}
|
|
255
255
|
}
|
|
256
256
|
|
|
257
|
+
/** 模块上下文正本候选路径(相对项目根):新正本优先,旧正本兼容沿用(免迁移) */
|
|
258
|
+
const CONTEXT_DOC_PATHS: string[][] = [
|
|
259
|
+
['.harness', 'CONTEXT.md'],
|
|
260
|
+
['.studio', 'CONTEXT.md'],
|
|
261
|
+
];
|
|
262
|
+
|
|
257
263
|
/**
|
|
258
|
-
*
|
|
264
|
+
* 定位模块上下文正本(正本模型):
|
|
259
265
|
* 模块上下文沉淀归并到业务仓单一正本(模块锚点组织)时,知识入口行改指正本,
|
|
260
|
-
* 不再输出散置 CONTEXT.md
|
|
266
|
+
* 不再输出散置 CONTEXT.md 指引。正本路径缺省 `.harness/CONTEXT.md`;
|
|
267
|
+
* 旧正本 `.studio/CONTEXT.md` 在场时兼容沿用,两者同时在场上以 `.harness/` 为准。
|
|
268
|
+
* 返回相对 projectPath 的正本路径(posix 分隔符),无正本返回 null。只判存在性,不解析内容。
|
|
261
269
|
*/
|
|
262
|
-
async function
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
270
|
+
async function findCanonicalContextDoc(projectPath: string): Promise<string | null> {
|
|
271
|
+
for (const segments of CONTEXT_DOC_PATHS) {
|
|
272
|
+
try {
|
|
273
|
+
if ((await fs.stat(path.join(projectPath, ...segments))).isFile()) {
|
|
274
|
+
return segments.join('/');
|
|
275
|
+
}
|
|
276
|
+
} catch {
|
|
277
|
+
// 该候选不存在,看下一个
|
|
278
|
+
}
|
|
267
279
|
}
|
|
280
|
+
return null;
|
|
268
281
|
}
|
|
269
282
|
|
|
270
283
|
/** 统计源码根内的 CONTEXT.md 数量(含源码根本身,跨根去重) */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# completion-checkers/
|
|
2
2
|
|
|
3
3
|
## 职责
|
|
4
|
-
|
|
4
|
+
提交集收尾软观测三纯判定函数:tdd-chain 引用链 / phase-format subject 结构 / contract-presence 契约在场。纯函数,不碰文件系统与 git,commits 由调用方(completion-gates 类守卫)供给。
|
|
5
5
|
|
|
6
6
|
## 核心导出
|
|
7
7
|
- `verifyTddChain(commits, config)`(`tdd-chain.ts`)——实现 commit 须带 trailer `Tested-By: <sha>`(`TESTED_BY_RE` 写死);被引 sha 须在本提交集内、位置在前、文件清单命中 test_globs;`Tests: none`(`TESTS_NONE_RE`)→ waiver;纯测试/纯非代码 commit 天然免检
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* verifyTddChain
|
|
2
|
+
* verifyTddChain 测试(伪造引用三形态 / Tests: none waiver / 免检分类)
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { verifyTddChain } from '../tdd-chain';
|
|
@@ -34,7 +34,7 @@ describe('verifyTddChain', () => {
|
|
|
34
34
|
const commits = [commit({ sha: sha(2), body: 'Tested-By: ' + sha(9) })];
|
|
35
35
|
const result = verifyTddChain(commits);
|
|
36
36
|
expect(result.verdict).toBe('violation');
|
|
37
|
-
expect(result.commits[0].reason).toContain('
|
|
37
|
+
expect(result.commits[0].reason).toContain('不在本提交集内');
|
|
38
38
|
});
|
|
39
39
|
|
|
40
40
|
it('伪造引用:被引 commit 位置在本 commit 之后 → violation', () => {
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Completion Checkers
|
|
2
|
+
* Completion Checkers
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* 提交集收尾软观测三件套——纯判定函数库:
|
|
5
5
|
* - verifyTddChain:实现 commit 的 Tested-By 引用链验证
|
|
6
6
|
* - verifyPhaseFormat:phase(...) subject 结构验证
|
|
7
7
|
* - verifyContractPresence:通用契约在场引擎
|
|
8
8
|
*
|
|
9
9
|
* 与 ConstraintCheck 闭环注册表无关:直接 export,不注册、不碰 checkers/index.ts。
|
|
10
|
-
* 函数不碰文件系统与 git;commits 由调用方(
|
|
10
|
+
* 函数不碰文件系统与 git;commits 由调用方(completion-gates 类守卫)供给。
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
13
|
export * from './types';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* phase-format
|
|
2
|
+
* phase-format:提交 subject 的 phase 结构验证(Q4 定稿)
|
|
3
3
|
*
|
|
4
4
|
* 协议(正则写死在函数里,不进配置):
|
|
5
5
|
* - 全部非 merge commit 的 subject 命中 `^phase\([a-z0-9-]+\):\s+\S`
|
|
@@ -32,7 +32,7 @@ export function verifyPhaseFormat(
|
|
|
32
32
|
|
|
33
33
|
const verdicts: CommitVerdict[] = commits.map((commit) => {
|
|
34
34
|
if (isMergeCommit(commit)) {
|
|
35
|
-
return { sha: commit.sha, verdict: 'violation', reason: '
|
|
35
|
+
return { sha: commit.sha, verdict: 'violation', reason: '提交集内出现 merge commit' };
|
|
36
36
|
}
|
|
37
37
|
if (!PHASE_SUBJECT_RE.test(commit.subject)) {
|
|
38
38
|
return { sha: commit.sha, verdict: 'violation', reason: `subject 不合规:${commit.subject}` };
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 协议(写死为机制本体,不进配置):
|
|
5
5
|
* - 实现 commit 必须带 trailer `Tested-By: <sha>`
|
|
6
|
-
* - 被引 sha 须 a)
|
|
6
|
+
* - 被引 sha 须 a) 在本提交集内 b) 序列位置在本 commit 之前(比位置不比时间戳)c) 其文件清单命中 test_globs
|
|
7
7
|
* - 豁免:trailer `Tests: none` → waiver(放行记台账);纯非代码/纯测试 commit 天然免检
|
|
8
8
|
*/
|
|
9
9
|
|
|
@@ -44,7 +44,7 @@ export function verifyTddChain(
|
|
|
44
44
|
const ref = m[1].toLowerCase();
|
|
45
45
|
const j = commits.findIndex((c) => c.sha.toLowerCase().startsWith(ref));
|
|
46
46
|
if (j === -1) {
|
|
47
|
-
return { sha: commit.sha, verdict: 'violation', reason: `Tested-By 引用 ${ref}
|
|
47
|
+
return { sha: commit.sha, verdict: 'violation', reason: `Tested-By 引用 ${ref} 不在本提交集内` };
|
|
48
48
|
}
|
|
49
49
|
if (j >= i) {
|
|
50
50
|
return { sha: commit.sha, verdict: 'violation', reason: `Tested-By 引用 ${ref} 位置不在本 commit 之前` };
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Completion Checkers
|
|
2
|
+
* Completion Checkers 类型定义
|
|
3
3
|
*
|
|
4
4
|
* 三个纯判定函数共享的输入/输出/配置类型。
|
|
5
5
|
* 与 ConstraintCheck 闭环注册表无关:直接 export,不注册、不碰 checkers/index.ts。
|
|
6
|
-
* 函数不碰文件系统与 git,commits 由调用方(
|
|
6
|
+
* 函数不碰文件系统与 git,commits 由调用方(git log)供给。
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
/**
|
|
9
|
+
/** 提交集内的单个提交输入(有序,base..HEAD 升序) */
|
|
10
10
|
export interface CommitInput {
|
|
11
11
|
/** 完整 sha */
|
|
12
12
|
sha: string;
|
|
@@ -49,7 +49,7 @@ export interface PhaseFormatResult {
|
|
|
49
49
|
|
|
50
50
|
/** verifyContractPresence 上下文(类型→字段的判定方法映射是代码不是配置) */
|
|
51
51
|
export interface ContractPresenceContext {
|
|
52
|
-
/** review
|
|
52
|
+
/** review 类型契约:调用方已解析的 metadata.reviewReport */
|
|
53
53
|
reviewReport?: unknown;
|
|
54
54
|
[key: string]: unknown;
|
|
55
55
|
}
|
|
@@ -62,7 +62,7 @@ export interface ContractPresenceResult {
|
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
|
-
* completion_checkers 配置(对应 yml 顶层键 `completion_checkers
|
|
65
|
+
* completion_checkers 配置(对应 yml 顶层键 `completion_checkers:`,消费方自解)。
|
|
66
66
|
* 只配开关/glob/契约类型清单;协议格式(Tested-By、Tests: none、phase 结构)写死为机制本体。
|
|
67
67
|
* 缺省 = 全开 + 默认 glob。
|
|
68
68
|
*/
|
package/src/failure/recorder.ts
CHANGED
|
@@ -25,7 +25,7 @@ export interface FailureRecorderConfig {
|
|
|
25
25
|
*
|
|
26
26
|
* 用法:
|
|
27
27
|
* ```typescript
|
|
28
|
-
* const recorder = new FailureRecorder({ logFile:
|
|
28
|
+
* const recorder = new FailureRecorder({ logFile: DEFAULT_FAILURE_LOG_FILE });
|
|
29
29
|
* await recorder.record({
|
|
30
30
|
* type: ErrorType.TEST_FAILED,
|
|
31
31
|
* level: FailureLevel.L1,
|
package/src/index.ts
CHANGED
|
@@ -235,6 +235,7 @@ export type { ContextAverages } from './monitoring';
|
|
|
235
235
|
export {
|
|
236
236
|
ErrorType,
|
|
237
237
|
FailureLevel,
|
|
238
|
+
DEFAULT_FAILURE_LOG_FILE,
|
|
238
239
|
DEFAULT_CLASSIFICATION_RULES,
|
|
239
240
|
DEFAULT_LEVEL_MAPPING,
|
|
240
241
|
ErrorClassifier,
|
|
@@ -339,8 +340,8 @@ export type {
|
|
|
339
340
|
} from './knowledge';
|
|
340
341
|
|
|
341
342
|
// ========================================
|
|
342
|
-
// Completion Checkers
|
|
343
|
-
//
|
|
343
|
+
// Completion Checkers
|
|
344
|
+
// 提交集收尾软观测三纯判定函数:tdd-chain / phase-format / contract-presence。
|
|
344
345
|
// 纯函数直接 export,不进 ConstraintCheck 闭环注册表。
|
|
345
346
|
// ========================================
|
|
346
347
|
export {
|
|
@@ -374,6 +375,14 @@ export type {
|
|
|
374
375
|
// ========================================
|
|
375
376
|
export { getRegistryPath, getToolsDir } from './tools';
|
|
376
377
|
|
|
378
|
+
// ========================================
|
|
379
|
+
// 发布物完整性自检(#75 N4 收编:harness 自己最知道自己发了什么)
|
|
380
|
+
// 清单 = 包声明面(package.json main/exports/bin)推导 + 运行时 extras 随源码维护;
|
|
381
|
+
// studio publishPackage 的 dist 校验改调本能力,不再硬编码 harness dist 内部清单。
|
|
382
|
+
// ========================================
|
|
383
|
+
export { getCriticalArtifacts, verifyReleaseArtifacts } from './release';
|
|
384
|
+
export type { ArtifactIntegrityResult } from './release';
|
|
385
|
+
|
|
377
386
|
// ========================================
|
|
378
387
|
// Hooks 管线
|
|
379
388
|
// ========================================
|
|
@@ -103,7 +103,7 @@ Extra text
|
|
|
103
103
|
it('handles files without frontmatter via best-effort', () => {
|
|
104
104
|
fs.writeFileSync(
|
|
105
105
|
path.join(tmpDir, 'agent-network-core-concepts.md'),
|
|
106
|
-
'#
|
|
106
|
+
'# 核心概念总览\n\n## 设计要点\nBody\n'
|
|
107
107
|
);
|
|
108
108
|
|
|
109
109
|
const gen = new KnowledgeIndexGenerator(tmpDir);
|
|
@@ -115,7 +115,7 @@ Extra text
|
|
|
115
115
|
// agent-network-* prefix maps to architecture
|
|
116
116
|
expect(fields[2]).toBe('architecture');
|
|
117
117
|
// H2 terms should still be extracted
|
|
118
|
-
expect(fields[6]).toContain('
|
|
118
|
+
expect(fields[6]).toContain('设计要点');
|
|
119
119
|
});
|
|
120
120
|
|
|
121
121
|
it('excludes .archive directory', () => {
|
|
@@ -59,7 +59,7 @@ export class KnowledgeIndexGenerator {
|
|
|
59
59
|
`# Types: ${Object.entries(byType).map(([t, n]) => `${t}:${n}`).join(' ')}`,
|
|
60
60
|
`#`,
|
|
61
61
|
`# Usage:`,
|
|
62
|
-
`# grep "<keyword>" ~/.
|
|
62
|
+
`# grep "<keyword>" ~/.harness/knowledge/_index.md`,
|
|
63
63
|
`# Then Read the matching file for full content.`,
|
|
64
64
|
`#`,
|
|
65
65
|
`# filename|id|type|title|maturity|tags|terms`,
|