@dommaker/harness 1.8.1 → 1.10.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 +16 -0
- package/README.md +10 -13
- package/dist/cli/commands/check.d.ts +3 -3
- package/dist/cli/commands/check.d.ts.map +1 -1
- package/dist/cli/commands/check.js +41 -78
- package/dist/cli/commands/check.js.map +1 -1
- package/dist/cli/commands/constraints-report.d.ts +2 -10
- package/dist/cli/commands/constraints-report.d.ts.map +1 -1
- package/dist/cli/commands/constraints-report.js +4 -57
- package/dist/cli/commands/constraints-report.js.map +1 -1
- package/dist/cli/commands/constraints-retire.d.ts +9 -23
- package/dist/cli/commands/constraints-retire.d.ts.map +1 -1
- package/dist/cli/commands/constraints-retire.js +39 -121
- package/dist/cli/commands/constraints-retire.js.map +1 -1
- package/dist/cli/commands/constraints.d.ts +3 -8
- package/dist/cli/commands/constraints.d.ts.map +1 -1
- package/dist/cli/commands/constraints.js +8 -23
- package/dist/cli/commands/constraints.js.map +1 -1
- package/dist/cli/commands/definitions.js +2 -2
- package/dist/cli/commands/init.d.ts +0 -32
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +7 -149
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/report.d.ts.map +1 -1
- package/dist/cli/commands/report.js +14 -17
- package/dist/cli/commands/report.js.map +1 -1
- package/dist/cli/commands/scaffold-templates.d.ts +0 -2
- package/dist/cli/commands/scaffold-templates.d.ts.map +1 -1
- package/dist/cli/commands/scaffold-templates.js +2 -37
- package/dist/cli/commands/scaffold-templates.js.map +1 -1
- package/dist/cli/commands/scaffold.d.ts +0 -2
- package/dist/cli/commands/scaffold.d.ts.map +1 -1
- package/dist/cli/commands/scaffold.js +0 -10
- package/dist/cli/commands/scaffold.js.map +1 -1
- package/dist/cli/commands/status.js +10 -10
- package/dist/cli/commands/status.js.map +1 -1
- package/dist/cli/commands/sync-docs/agents-syncer.d.ts +0 -2
- package/dist/cli/commands/sync-docs/agents-syncer.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/agents-syncer.js +4 -23
- package/dist/cli/commands/sync-docs/agents-syncer.js.map +1 -1
- package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts +22 -0
- package/dist/cli/commands/sync-docs/capabilities-syncer.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/capabilities-syncer.js +113 -8
- package/dist/cli/commands/sync-docs/capabilities-syncer.js.map +1 -1
- package/dist/cli/commands/sync-docs/index.d.ts.map +1 -1
- package/dist/cli/commands/sync-docs/index.js +36 -4
- package/dist/cli/commands/sync-docs/index.js.map +1 -1
- package/dist/core/constraints/capabilities-parser.d.ts.map +1 -1
- package/dist/core/constraints/capabilities-parser.js +16 -4
- package/dist/core/constraints/capabilities-parser.js.map +1 -1
- package/dist/core/constraints/checker.d.ts +15 -24
- package/dist/core/constraints/checker.d.ts.map +1 -1
- package/dist/core/constraints/checker.js +33 -72
- package/dist/core/constraints/checker.js.map +1 -1
- package/dist/core/constraints/checkers/docs-freshness.js +1 -1
- package/dist/core/constraints/checkers/docs-freshness.js.map +1 -1
- package/dist/core/constraints/checkers/governance-presence.d.ts +2 -1
- package/dist/core/constraints/checkers/governance-presence.d.ts.map +1 -1
- package/dist/core/constraints/checkers/governance-presence.js +4 -3
- package/dist/core/constraints/checkers/governance-presence.js.map +1 -1
- package/dist/core/constraints/checkers/index.d.ts +1 -1
- package/dist/core/constraints/checkers/index.d.ts.map +1 -1
- package/dist/core/constraints/checkers/index.js +4 -8
- package/dist/core/constraints/checkers/index.js.map +1 -1
- package/dist/core/constraints/checkers/iron-flags.d.ts +3 -4
- package/dist/core/constraints/checkers/iron-flags.d.ts.map +1 -1
- package/dist/core/constraints/checkers/iron-flags.js +4 -5
- package/dist/core/constraints/checkers/iron-flags.js.map +1 -1
- package/dist/core/constraints/checkers/no-test-simplification.js +1 -1
- package/dist/core/constraints/checkers/no-test-simplification.js.map +1 -1
- package/dist/core/constraints/checkers/types.d.ts +1 -1
- package/dist/core/constraints/checkers/types.js +1 -1
- package/dist/core/constraints/definitions/guidelines.d.ts +4 -4
- package/dist/core/constraints/definitions/guidelines.d.ts.map +1 -1
- package/dist/core/constraints/definitions/guidelines.js +9 -25
- package/dist/core/constraints/definitions/guidelines.js.map +1 -1
- package/dist/core/constraints/definitions/iron-laws.d.ts +4 -4
- package/dist/core/constraints/definitions/iron-laws.d.ts.map +1 -1
- package/dist/core/constraints/definitions/iron-laws.js +10 -81
- package/dist/core/constraints/definitions/iron-laws.js.map +1 -1
- package/dist/core/constraints/definitions.d.ts +7 -9
- package/dist/core/constraints/definitions.d.ts.map +1 -1
- package/dist/core/constraints/definitions.js +13 -16
- package/dist/core/constraints/definitions.js.map +1 -1
- package/dist/core/constraints/index.d.ts +2 -5
- package/dist/core/constraints/index.d.ts.map +1 -1
- package/dist/core/constraints/index.js +3 -13
- package/dist/core/constraints/index.js.map +1 -1
- package/dist/core/constraints/injection-writer.d.ts +10 -38
- package/dist/core/constraints/injection-writer.d.ts.map +1 -1
- package/dist/core/constraints/injection-writer.js +17 -54
- package/dist/core/constraints/injection-writer.js.map +1 -1
- package/dist/core/constraints/run-env.d.ts +1 -10
- package/dist/core/constraints/run-env.d.ts.map +1 -1
- package/dist/core/constraints/run-env.js +1 -22
- package/dist/core/constraints/run-env.js.map +1 -1
- package/dist/core/constraints/usage-report.d.ts +5 -7
- package/dist/core/constraints/usage-report.d.ts.map +1 -1
- package/dist/core/constraints/usage-report.js +5 -10
- package/dist/core/constraints/usage-report.js.map +1 -1
- package/dist/core/effective-constraints.d.ts +12 -22
- package/dist/core/effective-constraints.d.ts.map +1 -1
- package/dist/core/effective-constraints.js +13 -30
- package/dist/core/effective-constraints.js.map +1 -1
- package/dist/core/index.d.ts +2 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -9
- package/dist/core/index.js.map +1 -1
- package/dist/core/project-config-loader.d.ts +4 -42
- package/dist/core/project-config-loader.d.ts.map +1 -1
- package/dist/core/project-config-loader.js +16 -125
- package/dist/core/project-config-loader.js.map +1 -1
- package/dist/core/validators/passes-gate.d.ts +2 -2
- package/dist/core/validators/passes-gate.js +6 -6
- package/dist/core/validators/passes-gate.js.map +1 -1
- package/dist/hooks/bootstrap.d.ts +9 -12
- package/dist/hooks/bootstrap.d.ts.map +1 -1
- package/dist/hooks/bootstrap.js +9 -31
- package/dist/hooks/bootstrap.js.map +1 -1
- package/dist/hooks/index.d.ts +5 -7
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +4 -10
- package/dist/hooks/index.js.map +1 -1
- package/dist/index.d.ts +9 -12
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +10 -23
- package/dist/index.js.map +1 -1
- package/dist/monitoring/trace-analyzer.d.ts.map +1 -1
- package/dist/monitoring/trace-analyzer.js +11 -12
- package/dist/monitoring/trace-analyzer.js.map +1 -1
- package/dist/monitoring/traces.d.ts +4 -4
- package/dist/monitoring/traces.d.ts.map +1 -1
- package/dist/monitoring/traces.js +8 -8
- package/dist/monitoring/traces.js.map +1 -1
- package/dist/presets/standard.d.ts +8 -10
- package/dist/presets/standard.d.ts.map +1 -1
- package/dist/presets/standard.js +10 -16
- package/dist/presets/standard.js.map +1 -1
- package/dist/types/constraint.d.ts +24 -35
- package/dist/types/constraint.d.ts.map +1 -1
- package/dist/types/constraint.js +8 -7
- package/dist/types/constraint.js.map +1 -1
- package/dist/types/passes-gate.d.ts +5 -5
- package/dist/types/passes-gate.d.ts.map +1 -1
- package/dist/types/project-config.d.ts +8 -66
- package/dist/types/project-config.d.ts.map +1 -1
- package/dist/types/project-config.js +3 -2
- package/dist/types/project-config.js.map +1 -1
- package/dist/types/trace.d.ts +8 -10
- package/dist/types/trace.d.ts.map +1 -1
- package/package.json +2 -2
- package/src/CONTEXT.md +3 -3
- package/src/__tests__/checker-extra.test.ts +37 -61
- package/src/__tests__/checker.test.ts +10 -115
- package/src/__tests__/effective-constraints.test.ts +18 -83
- package/src/__tests__/iron-laws.test.ts +71 -90
- package/src/__tests__/passes-gate-check.test.ts +2 -2
- package/src/__tests__/project-config-loader-extra.test.ts +22 -123
- package/src/__tests__/project-config-loader.test.ts +5 -33
- package/src/__tests__/public-exports.test.ts +31 -11
- package/src/__tests__/public-type-surface.test.ts +2 -13
- package/src/__tests__/public-value-surface.test.ts +1 -7
- package/src/__tests__/skip-semantics.test.ts +21 -37
- package/src/__tests__/trace-analyzer-extra.test.ts +14 -14
- package/src/__tests__/trace-analyzer.test.ts +21 -21
- package/src/__tests__/trace-collector.test.ts +22 -22
- package/src/__tests__/traces.test.ts +27 -28
- package/src/cli/commands/CONTEXT.md +12 -9
- package/src/cli/commands/__tests__/check-read-count.test.ts +4 -15
- package/src/cli/commands/__tests__/check-skip-output.test.ts +14 -14
- package/src/cli/commands/__tests__/check.test.ts +14 -31
- package/src/cli/commands/__tests__/constraints-report.test.ts +12 -80
- package/src/cli/commands/__tests__/constraints-retire.test.ts +42 -231
- package/src/cli/commands/__tests__/init-governance-io.test.ts +12 -58
- package/src/cli/commands/__tests__/init-ondisk.test.ts +12 -20
- package/src/cli/commands/__tests__/init.test.ts +0 -11
- package/src/cli/commands/__tests__/project-path-anchoring.test.ts +2 -2
- package/src/cli/commands/__tests__/registry.test.ts +39 -3
- package/src/cli/commands/__tests__/report-extra.test.ts +4 -4
- package/src/cli/commands/__tests__/report.test.ts +1 -1
- package/src/cli/commands/__tests__/scaffold.test.ts +4 -14
- package/src/cli/commands/__tests__/status-analysis-wiring.test.ts +3 -3
- package/src/cli/commands/__tests__/status-extra.test.ts +14 -14
- package/src/cli/commands/__tests__/status.test.ts +12 -12
- package/src/cli/commands/__tests__/sync-docs-agents.test.ts +1 -1
- package/src/cli/commands/__tests__/sync-docs-table-layout.test.ts +259 -0
- package/src/cli/commands/check.ts +43 -82
- package/src/cli/commands/constraints-report.ts +4 -59
- package/src/cli/commands/constraints-retire.ts +49 -154
- package/src/cli/commands/constraints.ts +11 -26
- package/src/cli/commands/definitions.ts +2 -2
- package/src/cli/commands/init.ts +8 -179
- package/src/cli/commands/report.ts +19 -23
- package/src/cli/commands/scaffold-templates.ts +1 -37
- package/src/cli/commands/scaffold.ts +0 -11
- package/src/cli/commands/status.ts +10 -10
- package/src/cli/commands/sync-docs/agents-syncer.ts +4 -25
- package/src/cli/commands/sync-docs/capabilities-syncer.ts +139 -9
- package/src/cli/commands/sync-docs/index.ts +41 -4
- package/src/core/CONTEXT.md +9 -9
- package/src/core/constraints/__tests__/capability-counts.test.ts +5 -5
- package/src/core/constraints/__tests__/collect-constraints.test.ts +13 -8
- package/src/core/constraints/__tests__/git-evidence.test.ts +4 -7
- package/src/core/constraints/__tests__/injection-writer.test.ts +8 -61
- package/src/core/constraints/__tests__/run-env.test.ts +5 -34
- package/src/core/constraints/__tests__/trace-injection.test.ts +1 -1
- package/src/core/constraints/capabilities-parser.ts +17 -5
- package/src/core/constraints/checker.ts +39 -88
- package/src/core/constraints/checkers/__tests__/check-outcome.test.ts +5 -8
- package/src/core/constraints/checkers/__tests__/governance-presence.test.ts +1 -53
- package/src/core/constraints/checkers/__tests__/iron-flags.test.ts +5 -25
- package/src/core/constraints/checkers/docs-freshness.ts +1 -1
- package/src/core/constraints/checkers/governance-presence.ts +4 -3
- package/src/core/constraints/checkers/index.ts +4 -10
- package/src/core/constraints/checkers/iron-flags.ts +3 -12
- package/src/core/constraints/checkers/no-test-simplification.ts +1 -1
- package/src/core/constraints/checkers/types.ts +1 -1
- package/src/core/constraints/definitions/guidelines.ts +8 -25
- package/src/core/constraints/definitions/iron-laws.ts +9 -82
- package/src/core/constraints/definitions.ts +14 -15
- package/src/core/constraints/index.ts +4 -19
- package/src/core/constraints/injection-writer.ts +16 -61
- package/src/core/constraints/run-env.ts +1 -33
- package/src/core/constraints/usage-report.ts +8 -15
- package/src/core/effective-constraints.ts +14 -45
- package/src/core/index.ts +3 -10
- package/src/core/project-config-loader.ts +20 -145
- package/src/core/validators/passes-gate.ts +6 -6
- package/src/failure/CONTEXT.md +1 -1
- package/src/hooks/CONTEXT.md +12 -22
- package/src/hooks/__tests__/bootstrap.test.ts +24 -75
- package/src/hooks/bootstrap.ts +9 -48
- package/src/hooks/index.ts +5 -20
- package/src/index.ts +9 -31
- package/src/monitoring/__tests__/trace-file-anchoring.test.ts +1 -1
- package/src/monitoring/__tests__/traces.test.ts +22 -22
- package/src/monitoring/trace-analyzer.ts +12 -13
- package/src/monitoring/traces.ts +8 -8
- package/src/presets/__tests__/standard.test.ts +12 -16
- package/src/presets/standard.ts +14 -23
- package/src/types/__tests__/types.test.ts +3 -3
- package/src/types/constraint.ts +26 -40
- package/src/types/passes-gate.ts +5 -5
- package/src/types/project-config.ts +8 -77
- package/src/types/trace.ts +8 -11
- package/dist/core/constraints/agent-prompt-renderer.d.ts +0 -32
- package/dist/core/constraints/agent-prompt-renderer.d.ts.map +0 -1
- package/dist/core/constraints/agent-prompt-renderer.js +0 -55
- package/dist/core/constraints/agent-prompt-renderer.js.map +0 -1
- package/dist/core/constraints/checkers/no-bypass-checkpoint.d.ts +0 -6
- package/dist/core/constraints/checkers/no-bypass-checkpoint.d.ts.map +0 -1
- package/dist/core/constraints/checkers/no-bypass-checkpoint.js +0 -40
- package/dist/core/constraints/checkers/no-bypass-checkpoint.js.map +0 -1
- package/dist/core/constraints/definitions/prompts.d.ts +0 -12
- package/dist/core/constraints/definitions/prompts.d.ts.map +0 -1
- package/dist/core/constraints/definitions/prompts.js +0 -400
- package/dist/core/constraints/definitions/prompts.js.map +0 -1
- package/dist/core/constraints/injection-drift.d.ts +0 -61
- package/dist/core/constraints/injection-drift.d.ts.map +0 -1
- package/dist/core/constraints/injection-drift.js +0 -131
- package/dist/core/constraints/injection-drift.js.map +0 -1
- package/dist/core/constraints/injection-renderer.d.ts +0 -23
- package/dist/core/constraints/injection-renderer.d.ts.map +0 -1
- package/dist/core/constraints/injection-renderer.js +0 -44
- package/dist/core/constraints/injection-renderer.js.map +0 -1
- package/dist/hooks/config.d.ts +0 -30
- package/dist/hooks/config.d.ts.map +0 -1
- package/dist/hooks/config.js +0 -34
- package/dist/hooks/config.js.map +0 -1
- package/dist/hooks/pipeline.d.ts +0 -54
- package/dist/hooks/pipeline.d.ts.map +0 -1
- package/dist/hooks/pipeline.js +0 -173
- package/dist/hooks/pipeline.js.map +0 -1
- package/dist/hooks/registry.d.ts +0 -70
- package/dist/hooks/registry.d.ts.map +0 -1
- package/dist/hooks/registry.js +0 -141
- package/dist/hooks/registry.js.map +0 -1
- package/dist/hooks/types.d.ts +0 -113
- package/dist/hooks/types.d.ts.map +0 -1
- package/dist/hooks/types.js +0 -9
- package/dist/hooks/types.js.map +0 -1
- package/src/__tests__/hooks-pipeline.test.ts +0 -201
- package/src/cli/commands/__tests__/check-drift.test.ts +0 -158
- package/src/cli/commands/__tests__/init-injection.test.ts +0 -466
- package/src/core/constraints/__tests__/agent-prompt-renderer.test.ts +0 -129
- package/src/core/constraints/__tests__/injection-drift.test.ts +0 -247
- package/src/core/constraints/agent-prompt-renderer.ts +0 -67
- package/src/core/constraints/checkers/__tests__/no-bypass-checkpoint.test.ts +0 -76
- package/src/core/constraints/checkers/no-bypass-checkpoint.ts +0 -40
- package/src/core/constraints/definitions/prompts.ts +0 -414
- package/src/core/constraints/injection-drift.ts +0 -142
- package/src/core/constraints/injection-renderer.ts +0 -44
- package/src/hooks/__tests__/config.test.ts +0 -19
- package/src/hooks/__tests__/pipeline.test.ts +0 -195
- package/src/hooks/__tests__/registry.test.ts +0 -115
- package/src/hooks/config.ts +0 -33
- package/src/hooks/pipeline.ts +0 -202
- package/src/hooks/registry.ts +0 -166
- package/src/hooks/types.ts +0 -120
|
@@ -31,11 +31,10 @@ describe('TraceCollector', () => {
|
|
|
31
31
|
test('should record a trace', () => {
|
|
32
32
|
collector.record({
|
|
33
33
|
constraintId: 'no_fix_without_root_cause',
|
|
34
|
-
|
|
34
|
+
severity: 'error',
|
|
35
35
|
timestamp: Date.now(),
|
|
36
36
|
result: 'fail',
|
|
37
37
|
operation: 'code_implementation',
|
|
38
|
-
severity: 'error',
|
|
39
38
|
});
|
|
40
39
|
|
|
41
40
|
const traces = collector.read();
|
|
@@ -46,7 +45,7 @@ describe('TraceCollector', () => {
|
|
|
46
45
|
|
|
47
46
|
test('should record multiple traces', () => {
|
|
48
47
|
for (let i = 0; i < 5; i++) {
|
|
49
|
-
collector.recordPass('test_constraint', '
|
|
48
|
+
collector.recordPass('test_constraint', 'warning');
|
|
50
49
|
}
|
|
51
50
|
|
|
52
51
|
const traces = collector.read();
|
|
@@ -55,9 +54,9 @@ describe('TraceCollector', () => {
|
|
|
55
54
|
});
|
|
56
55
|
|
|
57
56
|
test('should filter traces by constraint ID', () => {
|
|
58
|
-
collector.recordPass('constraint_a', '
|
|
59
|
-
collector.recordFail('constraint_b', '
|
|
60
|
-
collector.recordFail('constraint_a', '
|
|
57
|
+
collector.recordPass('constraint_a', 'error');
|
|
58
|
+
collector.recordFail('constraint_b', 'warning');
|
|
59
|
+
collector.recordFail('constraint_a', 'error');
|
|
61
60
|
|
|
62
61
|
const traces = collector.read({ constraintId: 'constraint_a' });
|
|
63
62
|
expect(traces.length).toBe(2);
|
|
@@ -70,14 +69,14 @@ describe('TraceCollector', () => {
|
|
|
70
69
|
|
|
71
70
|
collector.record({
|
|
72
71
|
constraintId: 'old_trace',
|
|
73
|
-
|
|
72
|
+
severity: 'warning',
|
|
74
73
|
timestamp: oneHourAgo - 1000, // 1小时前更早
|
|
75
74
|
result: 'pass',
|
|
76
75
|
});
|
|
77
76
|
|
|
78
77
|
collector.record({
|
|
79
78
|
constraintId: 'new_trace',
|
|
80
|
-
|
|
79
|
+
severity: 'warning',
|
|
81
80
|
timestamp: now,
|
|
82
81
|
result: 'pass',
|
|
83
82
|
});
|
|
@@ -95,7 +94,7 @@ describe('TraceCollector', () => {
|
|
|
95
94
|
for (let i = 0; i < 10; i++) {
|
|
96
95
|
collector.record({
|
|
97
96
|
constraintId: `constraint_${i}`,
|
|
98
|
-
|
|
97
|
+
severity: 'warning',
|
|
99
98
|
timestamp: Date.now() - i * 60 * 1000, // 每分钟一个
|
|
100
99
|
result: i % 2 === 0 ? 'pass' : 'fail',
|
|
101
100
|
});
|
|
@@ -108,7 +107,7 @@ describe('TraceCollector', () => {
|
|
|
108
107
|
|
|
109
108
|
test('should provide stats', () => {
|
|
110
109
|
for (let i = 0; i < 10; i++) {
|
|
111
|
-
collector.recordPass(`constraint_${i}`, '
|
|
110
|
+
collector.recordPass(`constraint_${i}`, 'warning');
|
|
112
111
|
}
|
|
113
112
|
|
|
114
113
|
const stats = collector.getStats();
|
|
@@ -117,8 +116,8 @@ describe('TraceCollector', () => {
|
|
|
117
116
|
});
|
|
118
117
|
|
|
119
118
|
test('should filter traces by result', () => {
|
|
120
|
-
collector.recordFail('constraint_a', '
|
|
121
|
-
collector.recordPass('constraint_b', '
|
|
119
|
+
collector.recordFail('constraint_a', 'warning');
|
|
120
|
+
collector.recordPass('constraint_b', 'warning');
|
|
122
121
|
|
|
123
122
|
const traces = collector.read({ result: 'fail' });
|
|
124
123
|
expect(traces).toHaveLength(1);
|
|
@@ -127,12 +126,12 @@ describe('TraceCollector', () => {
|
|
|
127
126
|
|
|
128
127
|
test('should filter traces by projectPath', () => {
|
|
129
128
|
collector.record({
|
|
130
|
-
constraintId: 'c1',
|
|
131
|
-
result: 'pass', projectPath: '/project/a',
|
|
129
|
+
constraintId: 'c1', severity: 'warning', timestamp: Date.now(),
|
|
130
|
+
result: 'pass', projectPath: '/project/a',
|
|
132
131
|
});
|
|
133
132
|
collector.record({
|
|
134
|
-
constraintId: 'c2',
|
|
135
|
-
result: 'pass', projectPath: '/project/b',
|
|
133
|
+
constraintId: 'c2', severity: 'warning', timestamp: Date.now(),
|
|
134
|
+
result: 'pass', projectPath: '/project/b',
|
|
136
135
|
});
|
|
137
136
|
|
|
138
137
|
const traces = collector.read({ projectPath: '/project/a' });
|
|
@@ -142,12 +141,12 @@ describe('TraceCollector', () => {
|
|
|
142
141
|
|
|
143
142
|
test('should filter traces by sessionId', () => {
|
|
144
143
|
collector.record({
|
|
145
|
-
constraintId: 'c1',
|
|
146
|
-
result: 'pass', sessionId: 'session-1',
|
|
144
|
+
constraintId: 'c1', severity: 'warning', timestamp: Date.now(),
|
|
145
|
+
result: 'pass', sessionId: 'session-1',
|
|
147
146
|
});
|
|
148
147
|
collector.record({
|
|
149
|
-
constraintId: 'c2',
|
|
150
|
-
result: 'pass', sessionId: 'session-2',
|
|
148
|
+
constraintId: 'c2', severity: 'warning', timestamp: Date.now(),
|
|
149
|
+
result: 'pass', sessionId: 'session-2',
|
|
151
150
|
});
|
|
152
151
|
|
|
153
152
|
const traces = collector.read({ sessionId: 'session-2' });
|
|
@@ -215,7 +214,7 @@ describe('TraceAnalyzer', () => {
|
|
|
215
214
|
for (let i = 0; i < 10; i++) {
|
|
216
215
|
collector.record({
|
|
217
216
|
constraintId: 'constraint_a',
|
|
218
|
-
|
|
217
|
+
severity: 'warning',
|
|
219
218
|
timestamp: Date.now() - i * 60 * 1000,
|
|
220
219
|
result: i < 7 ? 'pass' : 'fail',
|
|
221
220
|
});
|
|
@@ -224,7 +223,7 @@ describe('TraceAnalyzer', () => {
|
|
|
224
223
|
for (let i = 0; i < 5; i++) {
|
|
225
224
|
collector.record({
|
|
226
225
|
constraintId: 'constraint_b',
|
|
227
|
-
|
|
226
|
+
severity: 'error',
|
|
228
227
|
timestamp: Date.now() - i * 60 * 1000,
|
|
229
228
|
result: i < 3 ? 'pass' : 'fail',
|
|
230
229
|
});
|
|
@@ -253,7 +252,7 @@ describe('TraceAnalyzer', () => {
|
|
|
253
252
|
for (let i = 0; i < 20; i++) {
|
|
254
253
|
collector.record({
|
|
255
254
|
constraintId: 'rising_fail_constraint',
|
|
256
|
-
|
|
255
|
+
severity: 'error',
|
|
257
256
|
timestamp: now - (40 - i) * 60 * 1000,
|
|
258
257
|
result: 'pass',
|
|
259
258
|
});
|
|
@@ -263,7 +262,7 @@ describe('TraceAnalyzer', () => {
|
|
|
263
262
|
for (let i = 0; i < 20; i++) {
|
|
264
263
|
collector.record({
|
|
265
264
|
constraintId: 'rising_fail_constraint',
|
|
266
|
-
|
|
265
|
+
severity: 'error',
|
|
267
266
|
timestamp: now - i * 60 * 1000,
|
|
268
267
|
result: i < 15 ? 'fail' : 'pass', // 75% 失败率
|
|
269
268
|
});
|
|
@@ -284,7 +283,7 @@ describe('TraceAnalyzer', () => {
|
|
|
284
283
|
for (let i = 0; i < 10; i++) {
|
|
285
284
|
collector.record({
|
|
286
285
|
constraintId: 'stable_constraint',
|
|
287
|
-
|
|
286
|
+
severity: 'warning',
|
|
288
287
|
timestamp: now - i * 60 * 1000,
|
|
289
288
|
result: 'pass',
|
|
290
289
|
});
|
|
@@ -298,7 +297,7 @@ describe('TraceAnalyzer', () => {
|
|
|
298
297
|
|
|
299
298
|
test('should save and load summary', () => {
|
|
300
299
|
for (let i = 0; i < 5; i++) {
|
|
301
|
-
collector.recordPass('test_constraint', '
|
|
300
|
+
collector.recordPass('test_constraint', 'warning');
|
|
302
301
|
}
|
|
303
302
|
|
|
304
303
|
const summaries = analyzer.analyzeRecent(1);
|
|
@@ -312,8 +311,8 @@ describe('TraceAnalyzer', () => {
|
|
|
312
311
|
|
|
313
312
|
test('should generate report', () => {
|
|
314
313
|
for (let i = 0; i < 5; i++) {
|
|
315
|
-
collector.recordPass('constraint_a', '
|
|
316
|
-
collector.recordFail('constraint_b', '
|
|
314
|
+
collector.recordPass('constraint_a', 'warning');
|
|
315
|
+
collector.recordFail('constraint_b', 'error');
|
|
317
316
|
}
|
|
318
317
|
|
|
319
318
|
const summaries = analyzer.analyzeRecent(1);
|
|
@@ -12,13 +12,13 @@ H5(#44)起:
|
|
|
12
12
|
- 命令契约(`src/cli/command-contract.ts`,上层目录)— `CommandResult` / `CommandKind` / `CommandIO` / `processIO` / `captureIO` / `lastJsonOutput` / `log` / `logError`
|
|
13
13
|
- 状态文件接缝(`src/cli/state-io.ts`,上层目录)— `.harness/.state.json` 读-改-写的唯一入口 `StateIO`(`read()`/`write()`)+ 状态类型 `HarnessState` + 缺省真实 fs 实现 `fileStateIO(projectPath)`;`check` / `status` 经可选 `stateIO` 参数注入(ADR-0026,harness#148)
|
|
14
14
|
- 门禁命令共享面(`src/cli/gate-command.ts`,上层目录)— `GateDecision → CommandResult` 的唯一映射 `gateCommandResult`,加 ✓/✗ 输出骨架 `reportGateDecision` 与出错横幅 `reportGateError`。同一句失败措辞此前抄在 6 个 handler 里(「`<id>` gate denied」×6 / 「`<id>` gate error」×4),门禁特有的指标行经 `onPass`/`onFail` 闭包传入(架构评审候选1)
|
|
15
|
-
- 脚手架落盘面(`src/cli/commands/scaffold.ts`)— 受管文件(managed file)三态判定 `writeManagedFile` / 一组落盘 `runPlan` +
|
|
15
|
+
- 脚手架落盘面(`src/cli/commands/scaffold.ts`)— 受管文件(managed file)三态判定 `writeManagedFile` / 一组落盘 `runPlan` + 8 个站点工厂(含对外文案;ADR-0029:custom-constraints.yml 示例站随 custom 约束面退役移除);模板正文住 `scaffold-templates.ts`,落盘注入面 `ScaffoldFileSystem` 可换内存替身(harness#132);CI 站点工厂 `harnessCheckCiFile` 带平台维度(github / gitlab,harness#143);两道 Git hook 工厂 `preCommitHookFile` / `prePushHookFile`(后者 harness#144,带可执行位)
|
|
16
16
|
- knowledge 投影面(`src/cli/commands/knowledge-view.ts`)— 11 个 knowledge 子操作的**display model** 渲染与出口:`emitKnowledgeView`(json/人读唯一分派 + 退出码)、`announce`(取数期进度行,`--json` 下静默)、`TONE_STYLES` 角色→样式单表、维度/规则 label 与 `toneForMaturity`/`toneForScore`/`toneForSeverity` 三张映射、`resolveKnowledgeBaseDir` + `openKnowledgeStore`(路径兜底与 store 构造单点)(harness#133)
|
|
17
17
|
- 各命令文件:check / validate / passes-gate / init / report / status / spec / sync-docs / knowledge / sdd / failure / posteval-plan / release / constraints / spec-baseline-check
|
|
18
18
|
- 6 门禁命令实现在 `acceptance` / `command` / `contract` / `performance` / `review` / `security`(其 CLI 元数据在 `src/gates/definitions.ts`,形状同为 `CommandDefinition`,ADR-0007);**判定一律穿过统一接口 `evaluate()`**,成败与措辞由共享面映射。`command` 只复用映射、保留自己的单行 ✓/✗ 输出(已对外的机器友好形状);`security audit` / `acceptance list` / `contract validate-schema` / `review status` / `command --list/--level` 是只展示不判断的子命令,直读报告面(`scan()` 等),不套骨架
|
|
19
19
|
- CLI 子命令 `command` 的两个分支共用同一台 gate 实例(#135/ADR-0024):默认分支经 `evaluate()` 取裁决、`--level` 经同一实例的 `getRiskLevel()` 取等级,命令侧不再引用模块级单例出口——「配置传进去却到不了判定」的那条路已封。`--strict` 旗帜随 `CommandGateConfig.strict`(零消费者、从不生效)一并删除
|
|
20
20
|
|
|
21
|
-
`constraints` 下挂治理子命令:`constraints report`(使用统计 + 退役候选诊断 +
|
|
21
|
+
`constraints` 下挂治理子命令:`constraints report`(使用统计 + 退役候选诊断 + 配置健康,`--export` 脱敏)、`constraints retire`(交互选择 + 人确认退役;带 id 直达需显式 `--yes`(#24 人确认闸门),无 `--yes` 报错 + 非零退出码且不落盘;落 config.yml retired 元数据 + KnowledgeStore 沉淀。ADR-0029:注入漂移小节、prompt 注入清单、custom 落点与治理注入段同步已随文本注入层关停移除)。
|
|
22
22
|
|
|
23
23
|
## 依赖关系
|
|
24
24
|
- 依赖 `src/core/constraints/` 约束引擎
|
|
@@ -47,17 +47,19 @@ H5(#44)起:
|
|
|
47
47
|
- **一次命令运行内,同一份数据至多取一次(harness#146,#140 A 票;与 ADR-0023 同族,但不扩 `RunEnv` 公共面)**:`spec-baseline-check` 的三条验证路径(文件存在性 / 依赖 / 代码模式)一律从命令级共享索引 `createBaselineIndex(projectPath)` 取数,不再各自裸调 `fs`——全仓 `.ts`/`.js` 内容扫描一遍建成 `路径 → 内容` 表、`package.json` 读解析一次(**读取失败态同样入库**,不逐条重试)、同一落点的存在性探测只 stat 一次;逐条前置与每个关键词只在索引上重放判定。索引**懒建**(没有前置需要某类数据时该类读取一次都不发生)、**运行结束即弃**(不跨命令复用:本命令是独立命令而非 `harness check` 的子步骤,「同一口径取一次」命令内自足即够,#140 triage 裁决 5)。改前代价 = 前置条件数 × 关键词数 × 源文件数,改后与条数无关,判定/证据文案/stdout/退出码逐字不变。**驻留上界(harness#162)**:索引容量有双上限 `SOURCE_INDEX_MAX_ENTRIES = 5000` / `SOURCE_INDEX_MAX_BYTES = 64MB`——超任一上限即放弃驻留、**回落逐文件流式读**(每个关键词重扫一遍,读完即弃),峰值内存由 O(全仓源码) 回到 O(单文件),正常规模仓保留「一次遍历」收益;取数面相应由 `sourceContents(): Map` 改为 `countFilesContaining(keyword)`,容量策略全部收在索引内部。机器可检:`__tests__/spec-baseline-read-count.test.ts`(形状照 `check-read-count.test.ts`:`readFileSync`·只读 `openSync`·`existsSync`·`readdirSync` 记件 + 整张读取表与目录扫描表逐条冻结 + 「同一份数据的同一口径至多取一次」+ N×K 缩放不变式 + 懒建不扫 + 失败态入库)+ `__tests__/spec-baseline-index-cap.test.ts`(驻留上界闸:上限内每文件至多读一遍、超条目/字节上限回落流式且判定不变)
|
|
48
48
|
- **先判定「本次要什么」再决定「读什么」,且清单顺序是对外面(harness#147,#140 B 票)**:`sync-docs/index.ts` 把 CAPABILITIES.md 的**格式判定放在源码扫描之前**——capability-listing 格式的判定面是计数、不做条目级比对,模块清单在本次运行里无人消费,于是整棵源码树**一份 `.ts` 内容都不读**(改造前是无条件全树逐文件 `await readFile`,只为首行注释)。要描述的分支(file-table)照旧取描述,但 `project-reader.ts` 分两步:先按目录遍历序定清单,再经 `extractFileDescriptions()` **并发**取数(上限固定 16,按票下裁决不做成配置面)并**按原索引回填**。清单顺序对外可见(`--check` 逐行比对生成的表格、人看 diff),票下裁决是「顺序逐字保持现状、不改字典序」,故「按并发完成顺序 append」属行为漂移而非优化。读盘次数之外一切不变:判定、漂移检测、写回内容逐字保持。机器可检:`__tests__/sync-docs-read-count.test.ts`(形状照 `check-read-count.test.ts`——listing 模式 `.ts` 内容读 = 0 + file-table 反证每份文件恰读一次 + 整张读取表冻结)、`sync-docs/__tests__/module-descriptions.test.ts`(替身把「起始序」与「完成序」刻意错开:串位或重排即红,另钉上限)、`__tests__/sync-docs-module-order.test.ts`(生成表格行序 = 目录遍历序、两次运行逐字复现)
|
|
49
49
|
|
|
50
|
+
- **撤登记要整行删,排版判定与修复共用一份正本(harness#171)**:`capabilities-syncer.ts` 删幽灵行此前是 `content.replace(/^\|…$/gm, '')`——`m` 下 `^…$` 只框住行内容、**不含行尾换行**,删完留一个空行;CommonMark 以空行断表,于是每撤一次登记就把一张能力表多切一刀(studio 存量攒到 40 处)。同函数末尾的 `\n{3,}→\n\n` 救不了它:删一行只产两个换行,够不着阈值(删相邻两行也不变好——只是把两刀并成一刀)。现行形状:① 删除按行 split/filter/join,行尾随之消失(CRLF 一并处理,正则尾上加 `\r?` 保持与旧 `m` 相同的匹配面);② 排版收拢正本是 `normalizeCapabilitiesTableLayout(content)`——规则①删两侧都是表格行的空行段、规则②收掉无数据行的表头/分隔行,**表格外的段落空行不动**,且**两条规则互相制造触发点**(①吃掉两张空表之间那个空行后,②在同一轮只收得掉后一张),故**跑到不动点**:一次 `sync-docs` 必须把 `--check` 报出来的全清掉,否则下游 CI 修完还红;③ 原有的一处 `\n{3,}→\n\n` 由「增删之后」移到**收拢之后**(收掉一张夹在两段散文之间的空表本身会新产 `\n\n\n`,留在前面就漏折叠),并删掉增删分支内那份重复折叠。围栏代码块(``` / ~~~)内的行在**排版收拢一侧整体豁免**(CAPABILITIES.md 是手写文档,块内长得像表格的示例行不是脏行:不给豁免时一块「表头+分隔行」示例被判空表删掉正文、`--check` 还为此判红)——但**幽灵行删除一侧刻意不豁免**,两侧口径故意不同:登记条目由 `core/constraints/capabilities-parser` 全文扫描得出(ADR-0009),围栏内的行同样算登记项,只让删除认围栏而条目不认,块内示例引用的已删文件会永远报成幽灵条目且 `--check` 修不掉(机器可检:「围栏内示例行引用已删文件」用例同时钉收敛与「只吃掉那一行、块内其余正文留着」)。让两侧同认围栏 = 改 ADR-0009 的登记面并连带影响 docs_freshness 判定,另票裁决。`--check` 的判定面就是「它返回的内容与入参是否不同」,写模式在**增删之后**调用收拢(先收拢会误删「本轮要往空表里补新行」的表头),故 check 报得出、fix 必清得掉(不重演 2026-08-04 studio PR #44 的「check 永不收敛」)。机器可检:`__tests__/sync-docs-table-layout.test.ts`(13 例:中间行/末行/目录条目行/CRLF 四种撤登形状 + 存量脏行 check 判红后写模式转绿 + 空表收掉 + 两张空表相邻一次收干净 + 收拢后不留三连换行 + 四条反向闸「有新行不误删表头」「表格外空行不动」「代码块内空表不被收」「代码块内表格样式行不被收拢」)
|
|
51
|
+
|
|
50
52
|
## 注意事项
|
|
51
53
|
- 新增命令需同步更新 CLAUDE.md / CAPABILITIES.md / src/CONTEXT.md
|
|
52
|
-
- **scaffold 三态模型(harness#132 已落地)**:init/validate
|
|
53
|
-
-
|
|
54
|
-
- 可检面:`__tests__/scaffold.test.ts`(注入内存 fs 替身,零真实文件系统测全三态 +
|
|
54
|
+
- **scaffold 三态模型(harness#132 已落地)**:init/validate 的「目标在不在场→写或告知」判定(站点数历经 8→9→8:#132 收口时 8 处,harness#144 加 pre-push 成 9,ADR-0029 撤 custom-constraints.yml 示例站回 8 = init 6 + validate 2)收口为 scaffold 模块。术语:**managed file**(受管文件)= harness 模板拥有正本、用户可持有的落盘文件;三态 = `created`(不在场→落盘)/ `exists`(在场→告知跳过,**不细分内容是否等于模板**,内容比较是独立特性)/ `manual`(在场→打印片段请用户手工合并)。纪律:scaffold 不持有覆盖/跳过策略(`--no-git-hooks`/`--no-github-actions` 由命令层翻成 plan 列表,scaffold 只见 plan);模板留代码内住 `scaffold-templates.ts`,不落 `templates/`(其无运行时消费者、integrity 清单不覆盖);**打印给用户的片段必须是落盘正文的一部分**(GH Actions 站点冲突分支打印完整 workflow 全文而非 job 片段,#103 判据);`--print-snippets` 的片段视图同判据且**由正本裁剪派生**(harness#153:`GITHUB_ACTIONS_SNIPPET` = `HARNESS_CHECK_WORKFLOW` 的 `jobs:` 段之后,形状与 GitLab 侧「打印的是落盘正文的尾巴」对仗。它曾是手抄的第二份文本,比落盘正本少 `validate` / `passes-gate` 两道门禁——照抄的用户拿到比同版本 `harness init` 弱一半的 CI;引导语随之改准为「以下正文取自 harness-check.yml 的 job 段」,`--print-snippets` 旗帜本身保留);gitlab 形同判据由 harness#157 补齐——`printSnippets` 收 `governanceLevel`、打 `renderGitLabCiJobs(governanceLevel)`,与落盘 / 冲突分支同一个正本函数(此前打无 level 的 `GITLAB_CI_SNIPPET`,`-g` 档照抄的用户静默少拿治理与 docs 新鲜度两个任务);CLAUDE.md/AGENTS.md 标记化幂等写(读-改-写形状)不纳入 scaffold;死选项 `-t/--type` 已随本票删除。GitLab CI 接线决议见下条;pre-push 站点已随 harness#144 落地,见「本地防线两道」条
|
|
55
|
+
- 站点的落点(#132 收口时 8 处,pre-push 是 harness#144 加的第 9 处,ADR-0029 撤 custom-constraints.yml 示例站后回到 8 处):`scaffold.ts` 存 8 个 `ManagedFile` 工厂(目标路径 + 三态文案 + 冲突片段),`init.ts` 只留「前置告知(无 .git / 目录不在场 / 已有 workflow 覆盖治理命令)+ runPlan」;原住 `validate.ts` 的 `createExampleCheckpoint`/`createExampleResolutions` 随工厂迁入 scaffold,`init.ts` 对 `validate` 的反向 import 因此消失,`validate.ts` 反向消费 `scaffold-templates` 的检查点文件路径约定。新增受管文件 = 一个工厂 + 一条 plan。**#132 口径外的第 9 处同形写手**(`sync-docs/context-syncer.ts:createContextMd` 自带一份 CONTEXT.md 正文、与正本已漂移)已于 harness#150 归位——现走 `contextDocFile` + `runPlan`,与 init 共用同一模板正本(`scaffold-templates.ts:renderContextDoc`)
|
|
56
|
+
- 可检面:`__tests__/scaffold.test.ts`(注入内存 fs 替身,零真实文件系统测全三态 + 8 站点文案逐字冻结 + 同源闸及其反证探针(两道 hook 各一条)+ 片段视图的「是正本裁出来的一段」包含闸)、`__tests__/init-ondisk.test.ts`(临时目录真跑 init:第一遍逐字节断言落盘内容并对齐改造前的整屏输出(#149 起这份整屏多出治理段 writer 的一行落盘提示——那行本就一直在真机上,只是此前 io 被丢、不进捕获面),第二遍断言 8 站点第三态与「打印即磁盘正文」;另有「`--print-snippets` 的 GitHub Actions 片段视图」两道闸——同源闸对 `harness-check.yml` 的字节级冻结基线、steps 集合闸按 YAML 解析枚举 job 的 `run:` 命令;harness#157 起「`--print-snippets` 的 GitLab CI 片段视图」四道闸——`-g standard` 同源闸(打印正文逐字等于 `GITLAB_GOVERNED` 基线的任务段 + 与真落盘 `.gitlab-ci.yml` 按 YAML 解析比对 harness-* 任务集合,正本删一个任务即红)、script 集合闸、不传 `-g` 单任务回归、`-g` 非法值 usage-error 零打印)
|
|
55
57
|
- 同族不同面:#150(已落地)是同一份 CONTEXT.md 模板被 `init` 与 `sync-docs` 两个命令各抄一份(`sync-docs/context-syncer.ts:createContextMd` 自带一份、与正本 `renderContextDoc` 已漂移),本条 #153 是同一命令的两个出口(打印 / 落盘)各抄一份——判据同一个(#103),落点不同,两票不并。#150 收口后 `createContextMd` 走 `contextDocFile` + `runPlan`(含 io / 注入 fs,正常只落 `created`),可检面 `__tests__/context-syncer.test.ts`(对撞闸:sync-docs 落点与 init 站点的骨架逐字相同;正本并入两行引导 + 注释形占位钉住;present→exists 不覆盖;#158 补骨架正文逐字冻结字面量——对撞闸只钉「两侧同源」,正本被改由冻结字面量撞红)与 `__tests__/init-ondisk.test.ts`(#158 起 `src/CONTEXT.md` 落盘从 `toContain` 升为 `CONTEXT_MD_BYTES` 字节级基线,同 `PRE_PUSH_HOOK_BYTES` 手法)
|
|
56
58
|
- **CI 平台维度(harness#143 已落地)**:init 的服务端 CI 接线带平台维度。术语:**CI 平台(ci platform)** = 服务端门禁的接线目标(`github` / `gitlab` / `none`,`none` = 不接线,即旧 `--no-github-actions`),一次平台选择贯通三个面(harness-check 站点、治理 CI 面、`--print-snippets` 的 CI 片段)。决议 ①–⑥ 的落地形状:① 显式 `--ci <github|gitlab|none>`、缺省 `github`、**不做 remote URL 自动检测**(init 常跑在首次 push 前,检测错是静默写错文件);② 最小参数化——CI 站点工厂 `harnessCheckCiFile(projectPath, platform, existingCiWorkflows, governanceLevel?)` 收平台参数,GitLab 正文住 `scaffold-templates.ts`(`GITLAB_CI_SNIPPET` / `renderGitLabCiJobs` / `renderGitLabCiFile`),`renderGovernanceWorkflow(level, platform)` 同步参数化,不建注册表抽象;③ 解析链 = flag > `.harness/config.yml` 的 `ci.platform` > `github`(读点 `readConfiguredCiPlatform`,缺失/脏值按未配置),**只有非默认平台才写 `ci.platform`**——github 不写键,而 init 每次都重写 config.yml,故「不写」即等于清除,旧配置零迁移、validate 不报错;④ `--no-github-actions` 保留为 `--ci none` 别名并打 deprecation 警告,与显式 `--ci <非 none>` 同时出现判 `usage-error` 且完全不落盘(不猜意图);⑤ `.gitlab-ci.yml` 三态:不在场 → `created`、在场 → `manual`(打印 harness 任务正文,它是落盘全文的尾巴 → #103 判据成立),GH 侧冲突判定逐字不变;⑥ GitLab 模板与 GH 对仗(同三条命令 + `image: node:20` + `stage: test` + `rules:` 触发 MR 与 main/master push + 无缓存),治理档 `sync-docs --check` 的非阻断对应 `allow_failure: true`
|
|
57
59
|
- 平台维度带来的两处形状差(GH 侧行为逐字节不变):**一个平台一份 CI 文件**——GitLab 没有「第二个 CI 文件位」,`-g` 档的治理任务并入同一份 `.gitlab-ci.yml` 正文,gitlab 形下 `governanceWorkflowFile` 无站点;**`none` = 不接线任何服务端 CI、不创建任何 CI 文件,治理 CI 面随之不建**(harness#156 裁决 F1,推翻 #143「`none` 只摘 harness-check 站点、`-g` 档照建 GH 治理 workflow」的有意保留——旧边界让 `config.yml` 写 `ci.platform: none` 而磁盘上躺着一份 GitHub Actions 文件,状态自相矛盾);治理文档面(CHANGELOG、约束段、CONTEXT.md)由 `-g` 独立决定,`none` 下照常生成。`--print-snippets` 照出 GH 片段(非落盘面,#156 未动)。切回 `github` 时 init 不删既有的 `.gitlab-ci.yml`——scaffold 只有「落盘 / 告知」两种动作,删除不是它的能力
|
|
58
60
|
- `-g` 值域校验在命令入口(harness#156 裁决 F2,与 #152 脏输入 fail-loud 同判据):合法值域 = `GOVERNANCE_PRESETS` 键集合(入口校验与装配共用同一份表),非法值判 `usage-error` 退 1、先于任何落盘,github / gitlab / none 三平台同判(旧行为:gitlab 侧非法值透传模板落三个任务、github 侧查表 miss 静默不建——同一输入两种后果)。隶属判定用 `Object.hasOwn` 而非 `in`(harness#164):`in` 走原型链,`-g constructor`/`toString` 曾穿透校验、把 `Object` 构造函数写进 `configData` 并在 `yaml.dump` 抛 `YAMLException`
|
|
59
61
|
- 可检面:`__tests__/scaffold.test.ts`「CI 站点的平台维度」+ 同源闸两形覆盖、`__tests__/init-ondisk.test.ts`「init --ci 平台维度」(`.gitlab-ci.yml` 逐字节 / 第二趟 manual / 治理并入 / 持久化回读 / 别名与冲突与非法值)与「init --ci × -g 组合边界(harness#156)」(`--ci none -g standard` 落盘文件集合 + config.yml 两投影逐字冻结、`-g` 非法值三平台一致性钉、合法值回归)
|
|
60
|
-
- **本地防线两道(harness#144 已落地)**:术语:**本地防线** = init 落的两个 Git hook 站点,分工是设计而非冗余——**pre-commit** 查暂存的(`check --staged` + plan 覆盖,增量、快反馈),**pre-push**
|
|
62
|
+
- **本地防线两道(harness#144 已落地)**:术语:**本地防线** = init 落的两个 Git hook 站点,分工是设计而非冗余——**pre-commit** 查暂存的(`check --staged` + plan 覆盖,增量、快反馈),**pre-push** 查整仓的(`prePushHookFile` → `.git/hooks/pre-push`,`mode 0o755`,全量 `harness check` 不带 `--staged` + `harness validate`,任一失败 `exit 1` 挡住 push),兜的是 `git commit --no-verify` 绕出去的内容。三道决定按 #132 grilling 定论钉死:① **不做增量**——不解析 pre-push 从 stdin 收到的 `<local_ref> <local_oid> <remote_ref> <remote_oid>` 清单,无论推什么(含删远端分支这种 stdin 空清单的场景)整仓过一遍,钩子内子进程一律 `< /dev/null`、不让它吃掉 ref 清单;② **不内建逃生机制**——无环境变量开关、无超时、无「慢则降级」,唯一口子是 git 原生 `git push --no-verify`;③ **定位不变**——本地 hook = 自检与提醒,服务端 CI(见「CI 平台维度」条)才是门禁,pre-push 不替代它。`--no-git-hooks` 一并覆盖本站点(策略仍在命令层翻成 plan,scaffold 不持策略、不新增跳过旗帜);`--print-snippets` 与冲突分支打印的片段 = 落盘正文去 shebang 头(#103 判据);init 收尾提示与本站点引导语写明「查暂存的 / 查整仓的,重复是设计使然」。仓库自身的 `bin/install-precommit-hook.sh`(`npm run hooks:install`)仍是 pre-commit 单道,本票不动它
|
|
61
63
|
- **knowledge view 收口模型(harness#133 已落地)**:knowledge 11 个子操作的「取数 + json 投影 + 人读排版 + 退出码」四件事拆开——命令模块只产 `{ data, human() }`(`data` 是 `--json` 正文的唯一正本,11 个 `knowledgeXxxView` 具名导出即投影本体),`knowledge-view.ts` 收口排版渲染、上色、label 映射、json/人读分派与退出码、路径解析与 store 构造。术语:**display model** = `human()` 返回的结构化人读视图 `{ sections: [{ title, rows: [{ cells }] }] }`,一格(cell)三选一:`label`(纯文案)/ `field`(点分路径声明它投影自 json 面哪个字段,数组下标与 `.length` 合法)/ `derived`(json 面没有的派生量,须在测试豁免表登记理由)。空行是显式的一行(`blankLine()`),不再靠 `\n` 前后缀藏排版。颜色只经 `DisplayTone` 角色查 `TONE_STYLES` 一张表,命令侧零 `chalk` 调用。可检面:`__tests__/knowledge-view.test.ts`(四道闸:源形状 / 11·11 json 字段清单冻结 / 两投影一致性 + 派生量双向对撞 / 满态与空态人读逐行冻结,基线取自改造前实现实测捕获)+ `__tests__/knowledge-tone-mapping.test.ts`(#151 加:tone 映射的纯函数级冻结,判据见下两条)。
|
|
62
64
|
- **冻结类测试的两条判据(#133 收尾须知自报第一条,#151 补完其推论)**:
|
|
63
65
|
- **fixture 走不到的分支须逐个建形,不共用一个空根**。闸 4b 的 `emptyRoot()` 是裸 `mkdtemp` 目录,天然没有 `.harness/knowledge-docs/`,于是 `syncRagRows` 的两个空态分支只有第一个(`!docsPresent`)进过基线,第二个(目录在场、无 `.md`)在测试里走不到——文案改字全绿。而 json 面两分支恒同形(`data = {directory, files}`),`--json` 断言钉不住人读第二行。落点:闸 4c 的平行表 `EXTRA_EMPTY_HUMAN_CASES`(每 case 自带 `makeRoot` + 人读逐行基线 + json 形状;不改 `EMPTY_HUMAN_BASELINE: Record<SubName, string[]>` 的形状,一个子命令只装一种形状的约束留在原表)。
|
|
@@ -65,14 +67,15 @@ H5(#44)起:
|
|
|
65
67
|
- **json 面现状冻结**:11 个顶层形状逐字不动(双仓核实 `knowledge --json` 零程序化消费者,studio 走 library 直调;统一包壳是纯 churn,删除 `--json` 死面按 ADR-0022 口径另票评估)。三处有意偏离,均不改字段集合:① `snapshot`/`index` 的 `--json` 由单行紧凑改为两空格缩进(分派单点化的直接结果,与其余 9 个子操作同形);② `sync-rag` 的两个空态此前把人类文案直接打到 `--json` 的 stdout(吐出的不是 JSON),现由 display model 承担,json 面恒为 `{directory, files}`;③ `stats` 人读面 `verified` 成熟度的颜色随 `toneForMaturity` 单表变为青色(此前 list 青 / stats 黄两处手抄不一致,文字不变)。
|
|
66
68
|
- 与 #134(KnowledgeAudit 收 store)独立落地,唯一交点 `knowledgeAudit` 命令函数——#134 是后到方并已适配:`knowledgeAuditView` 经 `openKnowledgeStore` 取 store 作引擎第一形参、`shortContentThreshold` 作第二形参(构造期不再自建 store)。审计后重建索引「只在人读路径发生」是本票保留的既有策略(`--json` 不写盘),也是 `knowledge.ts` 内被逐行冻结的唯一 `options.json` 分支。
|
|
67
69
|
- init 治理约束段写入落点(studio #302,ADR 2026-08-21 落点模型):新仓 → AGENTS.md `PRESERVE:governance` 段(sync-docs 重新生成时保留);旧模型仓(CLAUDE.md 已有 HARNESS_CONSTRAINTS 标记或 `## Governance Rules` 块)→ 续写 CLAUDE.md,幂等重跑不制造双份正本。治理段「在场守护」由 core 侧 governance_presence checker 承担(段缺失/为空时 check 报警)
|
|
68
|
-
- PRESERVE
|
|
69
|
-
-
|
|
70
|
+
- ADR-0029(文本注入层关停):init 的约束注入段 writer(setupAgentsMdConstraints / setupClaudeMdConstraints / setupGovernanceConstraints)、check 的注入漂移警告块、report 的漂移小节与 prompt 注入清单、retire 的注入段同步全部移除;sync-docs 不再生成 HARNESS_CONSTRAINTS 注入段。PRESERVE 标记保留机制本身保留(通用机制,非约束注入);历史 HARNESS_CONSTRAINTS 段在 `hasGovernanceContract` 中仅作治理契约在场的历史兼容判据
|
|
71
|
+
- marker-range 替换与治理正本探测收口在 `core/constraints/injection-writer`(ADR-0011):init 的 Output Style 段 writer 为「渲染 body + 调 writer」,半标记(单边/乱序)一律拒写告警;注入段落点路由(resolveInjectionTarget / resolveGovernanceLanding)已随 ADR-0029 删除
|
|
70
72
|
- knowledge 命令包含 11 个子操作(list/search/import/decay/stats/sync-rag/audit/snapshot/migrate/index/health);upsert/sync-status 已迁至 studio CLI(harness#110,二者硬编码 localhost Studio 内部端点,不属 harness「通用框架、文件驱动」定位)
|
|
71
73
|
- `stats` / `health` 的飞轮数字不在 CLI 内计算:分子口径唯一实现是 `knowledge/flywheel-metrics.ts`(`evaluateFlywheel`,ADR-0013),本层只做百分比取整/一位小数与字段名映射;`.consumption-stats.json` 的读取留在各命令(module 零 IO)
|
|
72
74
|
- 特殊路由(选项条件、子命令兜底、裸跑语义)表达在定义表的 optionRoutes / subcommands / subcommandStrict / bareRunsAction 字段,bin 是纯通用引擎、不含单命令知识
|
|
73
75
|
- **数值旗帜装配口径(harness#152)**:`<n>` / `<rate>` 这类数值旗帜经 commander 到命令侧**恒是字符串**(`defaultValue: '50'` 也是),所以入参声明必须跟着运行时形状走——装配点之前声明 `string`(或双形 `string | number`,如 `failure --limit`),数值只在装配点转一次。装配点有两处:**有 `mapActionArgs` 的命令在定义表装配**(`status --hours`、`constraints report` 三阈值、门禁 `--coverage-threshold`/`--bundle-threshold`/`--min-reviewers`),**knowledge 族是「纯投影条目」故编组归命令模块**(`knowledgeSearchCommand` 的 `--limit`、`knowledgeAudit` 的 `--threshold`);两形都对,判据是「声明与运行时一致」,因此 `as any` 不该出现在这条链上(它替编译器记住了旗帜的实际形状,去掉 `parseInt` 就是往数值槽塞字符串)。**转不出来必须 fail-loud**(`logError` + `usage-error`,非零退出):下游兜底写的多是 `?? 缺省值`,而 `??` 只兜 `null`/`undefined`、NaN 照单全收,于是一句 `len < NaN` 恒 false 就把一道质量判定静默关掉、退出码仍是 0(#152 病灶正本:`knowledge audit --threshold abc`)。上游按口径挡住的,引擎侧不再重复兜(库面向的守卫已落 harness#163:`KnowledgeAudit` 构造器对 `shortContentThreshold`/`staleDays`/`promotionBlockDays` 三槽的显式脏值(NaN/±Infinity/负数)构造期抛 `TypeError`,未传/显式 `undefined` 走缺省、显式 `0` 合法)。可检面:`__tests__/knowledge-audit-threshold.test.ts`(脏值 fail-loud / 缺省 '50' 与未传同果 / 显式 '0' 不被当未传 / `knowledge.ts` 零 `as any`)+ `__tests__/bin-exit-mapping.test.ts`(对外退出码 1 的端到端证据);knowledge 的 `kind:` 逐行冻结豁免表在 `__tests__/knowledge-view.test.ts` 闸 1(#152 起两行、#154 起三行,理由随表注)。其余数值旗帜的脏输入后果(fail-closed 与静默按缺省两类)逐项判定见 #152 票面,#152 只收 `knowledge audit --threshold` 一处;**harness#154 已把口径推广到全部数值旗帜**(已推广清单如下),**唯一解析器 = `src/utils/numeric-flag.ts`**(本条目指名的正本:`parseNumericFlag` 判别 + `numericFlagMessage` 文案模板 + `requireNumericFlag` 抛出 + `NumericFlagError`;`parseInt`/`parseFloat` 的前缀截断 `'30abc'→30`、`'1e2'→1` 同属静默误读,禁回)。两消费形同一条判定规则:定义表 `mapActionArgs` 站点(`constraints report` 三阈值、`performance --coverage-threshold`/`--bundle-threshold`、`review --min-reviewers`)走 `requireNumericFlag` 抛出、bin 在 `runDefinition` 捕获映射 usage-error——定义表是纯数据模块返回不了 CommandResult,装配失败的通道只此一条,bin 不因此新增单命令知识;命令模块站点(`knowledge search --limit`、`failure list --limit`、`passes-gate --coverage-threshold` 的 `coverageCheck`)走 `parseNumericFlag` 就地 `logError` + `usage-error`,且都先于 store/recorder 构造与任何执行(脏值零落盘)。#152 的 `assembleShortContentThreshold` 改为委派 `parseNumericFlag`(函数面与 #152 报错文案保留、冻结测试钉死,未新建第二套解析器)。**#152 判为 fail-closed 的两处不保留、一并收敛 fail-loud**(#154 裁定):`passes-gate --coverage-threshold` 的「覆盖率不足 (x% < NaN%)」自证异常文案与 `review --min-reviewers` 的恒不通过——脏输入属用法错误、自证异常的文案即缺陷;合法输入路径逐字不变,退出码面 review 1→1、passes-gate coverage 脏值 0→1 属缺陷修正(定级与 CHANGELOG 入账见该票)。边界不动:门禁/引擎构造器侧的 falsy/`??` 约定是库面向语义(显式 `0` 在 performance 门禁内仍 = 不检查该项),与 #163 同例不重复兜;`status --hours` 是死旗帜,另族另票。可检面新增:`src/utils/__tests__/numeric-flag.test.ts`(解析规则矩阵)+ `src/cli/commands/__tests__/numeric-flag-assembly.test.ts`(定义表逐旗帜装配 + 命令入口 + 端到端 spawn:脏输入 / 合法路径对拍 / 显式 `0`)
|
|
74
76
|
- **命令 interface = `CommandResult` + 注入 io(架构评审候选7)**:命令实现一律声明 `Promise<CommandResult>`(判别联合 `ok|skip|fail|usage-error`,`fail`/`usage-error` 必附可定位的 `reason`,多闸门命令 reason 含 `gate <id>`),末位可选形参 `io: CommandIO = processIO`;类型与写入面在 `src/cli/command-contract.ts`
|
|
75
|
-
- **writer 层 `io` 必传,缺省只留在对外命令入口(harness#149)**:`= processIO` 的缺省是给 bin 不注入时的对外入口用的(每个命令模块一处,如 `init()`)。命令内部的落盘 writer 与它们的路由一律收**必传** `io`——缺省留在链路上时,调用点漏传不报错,只是静默把提示兜到真实 stdout:`captureIO()` 的测试面因此天然缺那批行,`⚠️ …标记残缺…请人工修复` 这类唯一提示写坏了没有测试会红。init
|
|
77
|
+
- **writer 层 `io` 必传,缺省只留在对外命令入口(harness#149)**:`= processIO` 的缺省是给 bin 不注入时的对外入口用的(每个命令模块一处,如 `init()`)。命令内部的落盘 writer 与它们的路由一律收**必传** `io`——缺省留在链路上时,调用点漏传不报错,只是静默把提示兜到真实 stdout:`captureIO()` 的测试面因此天然缺那批行,`⚠️ …标记残缺…请人工修复` 这类唯一提示写坏了没有测试会红。init 治理段的 writer 已按此收口(ADR-0029 后只剩 Output Style 段一个 writer);机器可检:`__tests__/init-governance-io.test.ts`(源形状闸:`init.ts` 内该缺省只剩 `init()` 一处 + writer 签名必传 + `setupGovernance` 调用点逐个传 io)与行为面(临时目录真跑 init,断言 Output Style 段提示与其 `⚠️ …标记残缺…请人工修复` 告警落进注入面)
|
|
76
78
|
- **本目录零 `process.exit` / `process.exitCode`**:kind → 退出码的唯一映射在 `bin/harness.js`(`ok`/`skip` → 0,`fail`/`usage-error` → 1,未知 kind fail-closed)。历史上两处条件式(`command --level` 按严重级、门禁按 passed)已由命令侧译成 kind;`sync-docs --check` 的漂移改由实现返回 `fail`(定义表的 `afterRun` 逃生门随之废除)
|
|
77
79
|
- **输出不得直接用 console**:流式打印走 `log(io, …)` / `logError(io, …)`(`util.format` + 换行,与 console 逐字节等价,见 `src/cli/__tests__/command-contract.test.ts`);测试断言输出用 `captureIO()`,不再 `spyOn(process, 'exit')`;`--json` 输出断言用 `lastJsonOutput(io)`(JSON.parse 正本,harness#108)。例外:`constraints retire` 交互正文仍走 console(其 `RetireIO` 只注入 readline 流),退役结果打印已接注入流
|
|
78
80
|
- **#95 同型扫荡结论**:两门禁站点已修(`passes-gate` 执行/证据落 projectPath、`acceptance` 去掉 `tasksPath: './tasks.yml'` 相对默认值)。逐点判定后的豁免四处,理由与站点原文一起冻结在 `__tests__/project-path-convention.test.ts` 的豁免表:`release` 的 `pkgPath`(定义表本无 `-p`,pkgPath 就是该命令唯一的根且已逐个传给每条 `run(cmd, pkgPath)`,不构成半失效;给发布流水线新增 `-p` 属新能力)、`command` 的 `evaluate({ projectPath: process.cwd(), … })`(`CommandGate` 只做命令串正则判定、零文件读写,且该命令定义表里没有 `-p`,取 cwd 仅为满足 `GateContext` 形状,架构评审候选1)、`constraints` 的 `join(process.cwd(), 'package.json')`(报的是 harness 自身包版本,主锚 `__dirname`,cwd 仅兜底)、`core/spec/validator.ts` 的 `schemaPath: './specs/schemas'`(**确是同型病灶**:`validateAll(projectPath)` 用 projectPath 找 spec 文件、却用 cwd 找 schema;但 `validateFile`/`loadSchema` 签名里没有根,补齐要穿透整个 spec 域,留待 spec 单票收口)
|
|
81
|
+
- **另知:CAPABILITIES.md 排版面两处已知限制(harness#171 未修)**:① 收拢的围栏豁免只认 ` ``` ` / `~~~` 标记行,**4 空格以上缩进的代码块不豁免**(触发条件:手写 CAPABILITIES.md 用缩进而非围栏放表格示例,块内空行会被当排版脏行收掉)——缩进 ≥4 在 CommonMark 里与「缩进表格」本身歧义,现有豁免取保守侧(宁可少动),要收口得连 `TABLE_ROW_REGEX` 的缩进上界一起定;② **module 模式下整张表被收空后,下一次写会走「无表格」分支自动补一整张目录表**(`updateCapabilitiesFile` 的 `existingFiles.length === 0` else 分支),与同文件「目录条目需人工策划登记」的约定冲突——非 #171 引入(撤登记前留下的「空行 + 表头」形状经 `parseCapabilitiesFiles` 同样解析为空集,实测两版行为一致),且登记面认不认围栏要连 ADR-0009 一起裁决,另票。
|
|
@@ -68,7 +68,7 @@ function write(dir: string, rel: string, content: string): void {
|
|
|
68
68
|
|
|
69
69
|
const TABLE = ['| 能力 | 路径 | 说明 |', '|---|---|---|'];
|
|
70
70
|
|
|
71
|
-
/** 一个「两 checker 都会真评估」的项目:源码根下两个已登记文件 +
|
|
71
|
+
/** 一个「两 checker 都会真评估」的项目:源码根下两个已登记文件 + 一条 trace */
|
|
72
72
|
function fixtureRepo(): string {
|
|
73
73
|
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'harness-read-count-'));
|
|
74
74
|
git(dir, 'init', '-q');
|
|
@@ -76,11 +76,6 @@ function fixtureRepo(): string {
|
|
|
76
76
|
git(dir, 'config', 'user.name', 'Fixture');
|
|
77
77
|
write(dir, 'README.md', '# fixture\n');
|
|
78
78
|
write(dir, '.harness/config.yml', 'preset: standard\n');
|
|
79
|
-
write(
|
|
80
|
-
dir,
|
|
81
|
-
'.harness/custom-constraints.yml',
|
|
82
|
-
'custom_constraints:\n ab_custom_rule:\n level: guideline\n rule: 自定义规则\n message: 自定义提示\n trigger: manual\n'
|
|
83
|
-
);
|
|
84
79
|
write(
|
|
85
80
|
dir,
|
|
86
81
|
'CAPABILITIES.md',
|
|
@@ -131,25 +126,21 @@ describe('一次 check 的文件读取计数闸(ADR-0023 决策 5 ①)', ()
|
|
|
131
126
|
const result = await check({ staged: true, projectPath: dir }, io);
|
|
132
127
|
expect(result.kind).toBe('ok');
|
|
133
128
|
// 正对照:两个吃能力表的 checker 确实跑了(否则下面的断言是空跑)
|
|
134
|
-
expect(io.outText()).toContain('自定义约束: 1 条');
|
|
135
129
|
expect(io.outText()).toContain('触发条件: module_modification');
|
|
136
130
|
|
|
137
131
|
expect([...readCounts().entries()].sort()).toEqual(
|
|
138
132
|
[
|
|
139
133
|
// —— 一次运行一份:本 ADR 的承诺面 ——
|
|
140
134
|
['.harness/config.yml', 1],
|
|
141
|
-
['.harness/custom-constraints.yml', 1],
|
|
142
135
|
['CAPABILITIES.md', 1],
|
|
143
136
|
['.harness/logs/traces.log', 2],
|
|
144
137
|
['.harness/.state.json', 1], // ADR-0026:智能提示经 StateIO 读状态,恰一次
|
|
138
|
+
['AGENTS.md', 1],
|
|
145
139
|
// —— 已知例外(改动需明写理由)——
|
|
146
140
|
// traces.log = 2:head 50(智能提示的阈值判定,决策 3)与 tail 20(有无失败证据,
|
|
147
141
|
// 决策 4)是两个不同窗口的**有界**读;并成一个窗口就等于回到整读。
|
|
148
|
-
// AGENTS.md =
|
|
149
|
-
//
|
|
150
|
-
// 各尝试读一次(读失败的尝试同样计入)。
|
|
151
|
-
['AGENTS.md', 3],
|
|
152
|
-
['CLAUDE.md', 2],
|
|
142
|
+
// AGENTS.md = 1:ADR-0029 注入漂移检测与 custom 约束面关停后,治理文档只剩
|
|
143
|
+
// governance_presence 一处读者(夹具 CLAUDE.md 不存在且不再被探测,归零)。
|
|
153
144
|
].sort()
|
|
154
145
|
);
|
|
155
146
|
});
|
|
@@ -159,8 +150,6 @@ describe('一次 check 的文件读取计数闸(ADR-0023 决策 5 ①)', ()
|
|
|
159
150
|
|
|
160
151
|
const except = new Set([
|
|
161
152
|
path.join('.harness', 'logs', 'traces.log'), // 两个窗口的有界读,见上表理由
|
|
162
|
-
'AGENTS.md', // 治理文档,ADR-0023 决策表明示不处理
|
|
163
|
-
'CLAUDE.md',
|
|
164
153
|
]);
|
|
165
154
|
const offenders = [...readCounts()].filter(
|
|
166
155
|
([file, n]) => !except.has(file) && n > 1
|
|
@@ -62,13 +62,13 @@ jest.mock('chalk', () => ({
|
|
|
62
62
|
red: jest.fn((str: string) => str),
|
|
63
63
|
}));
|
|
64
64
|
|
|
65
|
-
function fakeConstraint(id: string,
|
|
65
|
+
function fakeConstraint(id: string, severity: 'error' | 'warning') {
|
|
66
66
|
return {
|
|
67
67
|
kind: 'check' as const,
|
|
68
68
|
id,
|
|
69
69
|
rule: 'test',
|
|
70
70
|
message: `msg-${id}`,
|
|
71
|
-
|
|
71
|
+
severity,
|
|
72
72
|
trigger: 'code_implementation',
|
|
73
73
|
enforcement: 'test',
|
|
74
74
|
};
|
|
@@ -83,21 +83,21 @@ describe('check 命令 skip 输出', () => {
|
|
|
83
83
|
jest.clearAllMocks();
|
|
84
84
|
});
|
|
85
85
|
|
|
86
|
-
it('skipped
|
|
86
|
+
it('skipped 单独列示,不计入 error 级通过条数', async () => {
|
|
87
87
|
mockChecker.checkConstraints.mockResolvedValue({
|
|
88
88
|
passed: true,
|
|
89
|
-
|
|
90
|
-
{ id: 'law_pass',
|
|
91
|
-
{ id: 'law_skip',
|
|
89
|
+
errors: [
|
|
90
|
+
{ id: 'law_pass', severity: 'error', satisfied: true, checkedAt: new Date(), constraint: fakeConstraint('law_pass', 'error') },
|
|
91
|
+
{ id: 'law_skip', severity: 'error', satisfied: true, skipped: true, checkedAt: new Date(), constraint: fakeConstraint('law_skip', 'error') },
|
|
92
92
|
],
|
|
93
|
-
|
|
93
|
+
warnings: [],
|
|
94
94
|
warningCount: 0,
|
|
95
95
|
});
|
|
96
96
|
|
|
97
97
|
await check({ preset: 'default', staged: false }, io);
|
|
98
98
|
|
|
99
99
|
// 通过条数只计实际评估的 1 条
|
|
100
|
-
expect(io.outText()).toContain('
|
|
100
|
+
expect(io.outText()).toContain('error 级约束: 全部通过 (1 条)');
|
|
101
101
|
// skipped 单独列示
|
|
102
102
|
expect(io.outText()).toContain('跳过评估: 1 条');
|
|
103
103
|
expect(io.outText()).toContain('- law_skip');
|
|
@@ -105,20 +105,20 @@ describe('check 命令 skip 输出', () => {
|
|
|
105
105
|
expect(io.outText()).toContain('约束检查通过');
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
-
it('
|
|
108
|
+
it('warning 级通过计数排除 skipped', async () => {
|
|
109
109
|
mockChecker.checkConstraints.mockResolvedValue({
|
|
110
110
|
passed: true,
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
{ id: 'g_pass',
|
|
114
|
-
{ id: 'g_skip',
|
|
111
|
+
errors: [],
|
|
112
|
+
warnings: [
|
|
113
|
+
{ id: 'g_pass', severity: 'warning', satisfied: true, checkedAt: new Date(), constraint: fakeConstraint('g_pass', 'warning') },
|
|
114
|
+
{ id: 'g_skip', severity: 'warning', satisfied: true, skipped: true, checkedAt: new Date(), constraint: fakeConstraint('g_skip', 'warning') },
|
|
115
115
|
],
|
|
116
116
|
warningCount: 0,
|
|
117
117
|
});
|
|
118
118
|
|
|
119
119
|
await check({ preset: 'default', staged: false }, io);
|
|
120
120
|
|
|
121
|
-
expect(io.outText()).toContain('
|
|
121
|
+
expect(io.outText()).toContain('warning 级约束: 1/1 通过');
|
|
122
122
|
expect(io.outText()).toContain('跳过评估: 1 条');
|
|
123
123
|
});
|
|
124
124
|
});
|
|
@@ -155,7 +155,7 @@ describe('check command(真 git fixture)', () => {
|
|
|
155
155
|
});
|
|
156
156
|
|
|
157
157
|
describe('判定与输出形状', () => {
|
|
158
|
-
it('
|
|
158
|
+
it('真证据全部满足时逐层通过', async () => {
|
|
159
159
|
const dir = gitRepo();
|
|
160
160
|
stageChange(dir, 'src/existing.ts', 'export const a = 2;\n');
|
|
161
161
|
passTraces(dir);
|
|
@@ -165,16 +165,13 @@ describe('check command(真 git fixture)', () => {
|
|
|
165
165
|
io
|
|
166
166
|
);
|
|
167
167
|
|
|
168
|
-
|
|
169
|
-
expect(io.outText()).toContain('✅
|
|
170
|
-
expect(io.outText()).toContain('✅ 指导原则: 2/2 通过');
|
|
171
|
-
expect(io.outText()).toContain('⏭️ 跳过评估: 1 条(约定未采用或证据未接线,不计通过/失败)');
|
|
172
|
-
expect(io.outText()).toContain('- incremental_progress');
|
|
168
|
+
expect(io.outText()).toContain('✅ error 级约束: 全部通过 (1 条)');
|
|
169
|
+
expect(io.outText()).toContain('✅ warning 级约束: 1/1 通过');
|
|
173
170
|
expect(io.outText()).toContain('✅ 约束检查通过');
|
|
174
171
|
expect(result).toEqual({ kind: 'ok' });
|
|
175
172
|
});
|
|
176
173
|
|
|
177
|
-
it('无验证证据 →
|
|
174
|
+
it('无验证证据 → 真 error 级拦截,经异常外溢为 fail', async () => {
|
|
178
175
|
const dir = gitRepo();
|
|
179
176
|
stageChange(dir, 'src/existing.ts', 'export const a = 2;\n');
|
|
180
177
|
write(dir, '.harness/logs/traces.log', '{"constraintId":"fixture","result":"fail"}\n');
|
|
@@ -207,7 +204,7 @@ describe('check command(真 git fixture)', () => {
|
|
|
207
204
|
expect(result.kind).toBe('fail');
|
|
208
205
|
});
|
|
209
206
|
|
|
210
|
-
it('staged 硬编码凭证 →
|
|
207
|
+
it('staged 硬编码凭证 → warning 级警告但不阻断', async () => {
|
|
211
208
|
const dir = gitRepo();
|
|
212
209
|
stageChange(
|
|
213
210
|
dir,
|
|
@@ -223,7 +220,7 @@ describe('check command(真 git fixture)', () => {
|
|
|
223
220
|
io
|
|
224
221
|
);
|
|
225
222
|
|
|
226
|
-
expect(io.outText()).toContain('⚠️
|
|
223
|
+
expect(io.outText()).toContain('⚠️ warning 级约束警告: 1 条');
|
|
227
224
|
expect(io.outText()).toContain('- no_hardcoded_credentials');
|
|
228
225
|
expect(io.outText()).toContain('✅ 约束检查通过');
|
|
229
226
|
expect(result).toEqual({ kind: 'ok' });
|
|
@@ -313,8 +310,8 @@ describe('check command(真 git fixture)', () => {
|
|
|
313
310
|
);
|
|
314
311
|
|
|
315
312
|
const out = io.outText();
|
|
316
|
-
expect(out).toContain('✅
|
|
317
|
-
expect(out).not.toContain('
|
|
313
|
+
expect(out).toContain('✅ warning 级约束: 1/1 通过');
|
|
314
|
+
expect(out).not.toContain('warning 级约束警告');
|
|
318
315
|
expect(out).toContain('💡 提示: 1 条(不判违规,供参考)');
|
|
319
316
|
expect(out).toContain('- capability_sync:');
|
|
320
317
|
expect(out).toContain('src/nested/deep.ts');
|
|
@@ -398,7 +395,7 @@ describe('check command(真 git fixture)', () => {
|
|
|
398
395
|
const traces = projectTraces(dir).slice(seeded);
|
|
399
396
|
|
|
400
397
|
expect(traces.map(t => t.constraintId)).toEqual(
|
|
401
|
-
expect.arrayContaining(['
|
|
398
|
+
expect.arrayContaining(['no_completion_without_verification', 'no_hardcoded_credentials'])
|
|
402
399
|
);
|
|
403
400
|
expect(traces.every(t => ['pass', 'fail', 'skip'].includes(t.result))).toBe(true);
|
|
404
401
|
expect(traces.every(t => t.projectPath === dir)).toBe(true);
|
|
@@ -462,7 +459,7 @@ describe('check command(真 git fixture)', () => {
|
|
|
462
459
|
});
|
|
463
460
|
|
|
464
461
|
describe('生效集来自真 .harness/config.yml', () => {
|
|
465
|
-
it('
|
|
462
|
+
it('报告禁用与未知约束 id', async () => {
|
|
466
463
|
const dir = gitRepo();
|
|
467
464
|
stageChange(dir, 'src/existing.ts', 'export const a = 2;\n');
|
|
468
465
|
passTraces(dir);
|
|
@@ -470,12 +467,6 @@ describe('check command(真 git fixture)', () => {
|
|
|
470
467
|
dir,
|
|
471
468
|
'.harness/config.yml',
|
|
472
469
|
[
|
|
473
|
-
'custom_constraints:',
|
|
474
|
-
' fixture_rule:',
|
|
475
|
-
' id: fixture_rule',
|
|
476
|
-
' level: guideline',
|
|
477
|
-
' trigger: manual',
|
|
478
|
-
' message: fixture 自定义约束',
|
|
479
470
|
'constraints:',
|
|
480
471
|
' capability_sync:',
|
|
481
472
|
' enabled: false',
|
|
@@ -490,7 +481,6 @@ describe('check command(真 git fixture)', () => {
|
|
|
490
481
|
io
|
|
491
482
|
);
|
|
492
483
|
|
|
493
|
-
expect(io.outText()).toContain('自定义约束: 1 条');
|
|
494
484
|
expect(io.outText()).toContain('已禁用约束: capability_sync, no_such_rule');
|
|
495
485
|
expect(io.outText()).toContain('配置中存在未知约束 id(已忽略,可清理): no_such_rule');
|
|
496
486
|
});
|
|
@@ -546,24 +536,17 @@ describe('check command(真 git fixture)', () => {
|
|
|
546
536
|
expect(result).toEqual({ kind: 'ok' });
|
|
547
537
|
});
|
|
548
538
|
|
|
549
|
-
it('
|
|
539
|
+
it('应该列出 error 级约束', () => {
|
|
550
540
|
const result = listLaws({}, io);
|
|
551
541
|
|
|
552
|
-
expect(io.outText()).toContain('
|
|
542
|
+
expect(io.outText()).toContain('error 级');
|
|
553
543
|
expect(result).toEqual({ kind: 'ok' });
|
|
554
544
|
});
|
|
555
545
|
|
|
556
|
-
it('
|
|
546
|
+
it('应该列出 warning 级约束', () => {
|
|
557
547
|
const result = listLaws({}, io);
|
|
558
548
|
|
|
559
|
-
expect(io.outText()).toContain('
|
|
560
|
-
expect(result).toEqual({ kind: 'ok' });
|
|
561
|
-
});
|
|
562
|
-
|
|
563
|
-
it('应该列出提示', () => {
|
|
564
|
-
const result = listLaws({}, io);
|
|
565
|
-
|
|
566
|
-
expect(io.outText()).toContain('提示');
|
|
549
|
+
expect(io.outText()).toContain('warning 级');
|
|
567
550
|
expect(result).toEqual({ kind: 'ok' });
|
|
568
551
|
});
|
|
569
552
|
});
|