oh-my-knowledge 0.48.0 → 0.49.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/README.md +50 -18
- package/README.zh.md +55 -23
- package/dist/analysis/coverage-analyzer.d.ts +1 -0
- package/dist/analysis/coverage-analyzer.js +125 -62
- package/dist/analysis/failure-clusterer.js +2 -1
- package/dist/analysis/gap-analyzer.d.ts +2 -2
- package/dist/analysis/gap-analyzer.js +13 -3
- package/dist/analysis/hedging-classifier.d.ts +2 -2
- package/dist/analysis/hedging-classifier.js +3 -4
- package/dist/analysis/report-diagnostics.js +9 -7
- package/dist/analysis/sample-diagnostics.js +6 -6
- package/dist/artifact-graph/doctor.js +15 -7
- package/dist/assets/agent-skills/omk/SKILL.md +27 -7
- package/dist/assets/agent-skills/omk/references/commands.md +18 -17
- package/dist/authoring/evolver.d.ts +10 -6
- package/dist/authoring/evolver.js +496 -83
- package/dist/authoring/generator.d.ts +3 -3
- package/dist/authoring/generator.js +5 -10
- package/dist/authoring/sample-fixer.d.ts +8 -6
- package/dist/authoring/sample-fixer.js +76 -5
- package/dist/cli/commands/doctor.js +31 -14
- package/dist/cli/commands/eval/index.d.ts +3 -0
- package/dist/cli/commands/eval/index.js +163 -17
- package/dist/cli/commands/evolve.d.ts +4 -4
- package/dist/cli/commands/evolve.js +27 -13
- package/dist/cli/commands/init.js +16 -3
- package/dist/cli/commands/observe/inbox.js +28 -21
- package/dist/cli/commands/observe/index.js +20 -11
- package/dist/cli/commands/observe/ingest.d.ts +3 -0
- package/dist/cli/commands/observe/ingest.js +30 -2
- package/dist/cli/commands/sample.d.ts +6 -3
- package/dist/cli/commands/sample.js +72 -68
- package/dist/cli/lib/codex-model-hint.d.ts +9 -0
- package/dist/cli/lib/codex-model-hint.js +45 -0
- package/dist/cli/lib/generation-failure-hint.d.ts +2 -0
- package/dist/cli/lib/generation-failure-hint.js +61 -0
- package/dist/cli/lib/i18n-dict/common.d.ts +1 -1
- package/dist/cli/lib/i18n-dict/common.js +4 -0
- package/dist/cli/lib/i18n-dict/gen.d.ts +1 -1
- package/dist/cli/lib/i18n-dict/gen.js +38 -6
- package/dist/cli/lib/i18n-dict/help.js +6 -6
- package/dist/cli/lib/i18n-dict/init.d.ts +1 -1
- package/dist/cli/lib/i18n-dict/init.js +13 -9
- package/dist/cli/lib/i18n-dict/run.d.ts +1 -1
- package/dist/cli/lib/i18n-dict/run.js +34 -2
- package/dist/cli/lib/llm-failure-classifier.d.ts +2 -0
- package/dist/cli/lib/llm-failure-classifier.js +8 -0
- package/dist/cli/lib/parse-run-config.d.ts +6 -5
- package/dist/cli/lib/parse-run-config.js +16 -9
- package/dist/cli/lib/runtime-defaults.d.ts +21 -0
- package/dist/cli/lib/runtime-defaults.js +79 -0
- package/dist/diagnosis/observe-mapper.js +14 -15
- package/dist/diagnosis/observe-producer.js +3 -1
- package/dist/diagnosis/studio-projection.js +14 -7
- package/dist/diagnosis/types.d.ts +2 -0
- package/dist/diagnosis/types.js +12 -0
- package/dist/doctor/endpoint-rule.js +2 -1
- package/dist/eval-core/artifact-file-names.js +18 -1
- package/dist/eval-core/artifact-index.d.ts +7 -11
- package/dist/eval-core/artifact-index.js +139 -80
- package/dist/eval-core/cache.d.ts +12 -3
- package/dist/eval-core/cache.js +89 -29
- package/dist/eval-core/comparability.js +10 -6
- package/dist/eval-core/evaluation-execution.d.ts +2 -1
- package/dist/eval-core/evaluation-execution.js +122 -37
- package/dist/eval-core/evaluation-job.d.ts +4 -1
- package/dist/eval-core/evaluation-job.js +4 -1
- package/dist/eval-core/evaluation-reporting.d.ts +15 -13
- package/dist/eval-core/evaluation-reporting.js +54 -52
- package/dist/eval-core/execution-strategy.d.ts +2 -0
- package/dist/eval-core/execution-strategy.js +11 -9
- package/dist/eval-core/fact-checker.js +15 -7
- package/dist/eval-core/holdout.js +3 -2
- package/dist/eval-core/judge-independence.d.ts +2 -2
- package/dist/eval-core/mock-hook.cjs +23 -6
- package/dist/eval-core/mocks-runtime.js +30 -8
- package/dist/eval-core/report-document.d.ts +12 -0
- package/dist/eval-core/report-document.js +1151 -0
- package/dist/eval-core/report-extensions.d.ts +4 -0
- package/dist/eval-core/report-extensions.js +500 -0
- package/dist/eval-core/report-file-migration.js +7 -2
- package/dist/eval-core/resume-compatibility.d.ts +31 -0
- package/dist/eval-core/resume-compatibility.js +141 -0
- package/dist/eval-core/sample-fingerprint.d.ts +12 -0
- package/dist/eval-core/sample-fingerprint.js +193 -0
- package/dist/eval-core/schema.js +86 -31
- package/dist/eval-core/verdict.d.ts +8 -4
- package/dist/eval-core/verdict.js +24 -10
- package/dist/eval-workflows/batch-evaluation-workflow.d.ts +2 -1
- package/dist/eval-workflows/batch-evaluation-workflow.js +25 -12
- package/dist/eval-workflows/evaluation-pipeline/preflight-warnings.d.ts +10 -5
- package/dist/eval-workflows/evaluation-pipeline/preflight-warnings.js +58 -21
- package/dist/eval-workflows/evaluation-pipeline/report-finalize.js +3 -1
- package/dist/eval-workflows/evaluation-pipeline/run-state.d.ts +4 -1
- package/dist/eval-workflows/evaluation-pipeline/run-state.js +4 -1
- package/dist/eval-workflows/evaluation-pipeline/test-set-hash.d.ts +6 -5
- package/dist/eval-workflows/evaluation-pipeline/test-set-hash.js +17 -10
- package/dist/eval-workflows/evaluation-pipeline.js +12 -7
- package/dist/eval-workflows/run-evaluation.d.ts +9 -7
- package/dist/eval-workflows/run-evaluation.js +79 -51
- package/dist/executors/anthropic-api.js +65 -9
- package/dist/executors/claude-cli.js +16 -79
- package/dist/executors/claude-protocol.d.ts +28 -0
- package/dist/executors/claude-protocol.js +180 -0
- package/dist/executors/claude-sdk-trace.js +56 -28
- package/dist/executors/claude-sdk.d.ts +1 -0
- package/dist/executors/claude-sdk.js +39 -93
- package/dist/executors/codex-cli-trace.js +166 -31
- package/dist/executors/codex-cli.d.ts +6 -8
- package/dist/executors/codex-cli.js +49 -151
- package/dist/executors/codex-protocol.d.ts +24 -0
- package/dist/executors/codex-protocol.js +234 -0
- package/dist/executors/codex-sdk.js +68 -120
- package/dist/executors/gemini.js +88 -13
- package/dist/executors/index.d.ts +2 -3
- package/dist/executors/index.js +5 -3
- package/dist/executors/openai-api.js +70 -9
- package/dist/executors/runtime-fingerprint.js +88 -11
- package/dist/executors/script-command.d.ts +8 -0
- package/dist/executors/script-command.js +87 -0
- package/dist/executors/script.js +202 -29
- package/dist/executors/shared.d.ts +35 -3
- package/dist/executors/shared.js +113 -15
- package/dist/grading/assertions.d.ts +1 -1
- package/dist/grading/assertions.js +19 -9
- package/dist/grading/diagnostic.d.ts +9 -2
- package/dist/grading/diagnostic.js +25 -2
- package/dist/grading/index.js +10 -4
- package/dist/grading/judge.js +19 -6
- package/dist/grading/layered-scores.d.ts +2 -3
- package/dist/grading/layered-scores.js +2 -3
- package/dist/inputs/load-samples.d.ts +1 -2
- package/dist/inputs/load-samples.js +23 -1
- package/dist/inputs/mcp-resolver.js +6 -3
- package/dist/inputs/sample-document.d.ts +11 -0
- package/dist/inputs/sample-document.js +96 -0
- package/dist/managed/evidence.d.ts +1 -0
- package/dist/managed/evidence.js +1 -1
- package/dist/managed/store.js +200 -91
- package/dist/observability/codex-trace-adapter.d.ts +5 -0
- package/dist/observability/codex-trace-adapter.js +850 -0
- package/dist/observability/experience.d.ts +32 -6
- package/dist/observability/experience.js +2695 -459
- package/dist/observability/feedback-matchers.js +16 -1
- package/dist/observability/inbox-view-model.d.ts +1 -1
- package/dist/observability/inbox-view-model.js +19 -14
- package/dist/observability/inbox.d.ts +7 -1
- package/dist/observability/inbox.js +632 -124
- package/dist/observability/problem-patterns.js +2 -0
- package/dist/observability/review-state.d.ts +6 -0
- package/dist/observability/review-state.js +235 -63
- package/dist/observability/skill-chain-advisories.js +1 -1
- package/dist/observability/skill-chain.js +17 -4
- package/dist/observability/skill-health-analyzer.d.ts +32 -7
- package/dist/observability/skill-health-analyzer.js +194 -121
- package/dist/observability/skill-health-report.d.ts +10 -0
- package/dist/observability/skill-health-report.js +620 -0
- package/dist/observability/soft-standards/constants.d.ts +0 -1
- package/dist/observability/soft-standards/constants.js +0 -1
- package/dist/observability/soft-standards/index.d.ts +1 -1
- package/dist/observability/soft-standards/index.js +1 -1
- package/dist/observability/soft-standards/llm-extractor.js +8 -10
- package/dist/observability/soft-standards/skill-standards-store.d.ts +2 -1
- package/dist/observability/soft-standards/skill-standards-store.js +59 -18
- package/dist/observability/soft-standards/types.d.ts +2 -2
- package/dist/observability/trace-adapter.d.ts +12 -7
- package/dist/observability/trace-adapter.js +11 -9
- package/dist/observability/trace-attribution.d.ts +13 -5
- package/dist/observability/trace-attribution.js +315 -21
- package/dist/observability/trace-ingestion.d.ts +9 -0
- package/dist/observability/trace-ingestion.js +80 -0
- package/dist/observability/trace-ir.d.ts +113 -0
- package/dist/observability/trace-ir.js +87 -0
- package/dist/observability/trace-segmenter.d.ts +19 -6
- package/dist/observability/trace-segmenter.js +377 -196
- package/dist/observability/trace-session-index.d.ts +19 -0
- package/dist/observability/trace-session-index.js +68 -0
- package/dist/observability/trace-source.d.ts +12 -4
- package/dist/observability/trace-source.js +939 -215
- package/dist/renderer/html-renderer.js +37 -6
- package/dist/renderer/icons.js +3 -0
- package/dist/renderer/observation-inbox-renderer.js +208 -90
- package/dist/renderer/skill-detail-renderer.js +452 -109
- package/dist/renderer/skill-health-renderer.js +69 -12
- package/dist/renderer/summary.js +28 -7
- package/dist/renderer/table.js +21 -4
- package/dist/renderer/test-view.d.ts +1 -0
- package/dist/renderer/test-view.js +44 -9
- package/dist/server/indexed-report-store.js +14 -18
- package/dist/server/job-store.js +64 -26
- package/dist/server/report-server.js +190 -78
- package/dist/server/report-store.js +57 -80
- package/dist/server/skill-index.js +143 -49
- package/dist/server/skill-insights.js +44 -5
- package/dist/shared/artifact-graph.d.ts +3 -0
- package/dist/shared/artifact-graph.js +224 -0
- package/dist/shared/assertion-types.d.ts +8 -0
- package/dist/shared/assertion-types.js +46 -0
- package/dist/shared/atomic-json.d.ts +8 -0
- package/dist/shared/atomic-json.js +33 -0
- package/dist/shared/diagnosis-schema.d.ts +9 -0
- package/dist/shared/diagnosis-schema.js +181 -0
- package/dist/shared/doctor-report.d.ts +3 -0
- package/dist/shared/doctor-report.js +103 -0
- package/dist/shared/evaluation-job.d.ts +6 -0
- package/dist/shared/evaluation-job.js +217 -0
- package/dist/shared/executor-result.d.ts +17 -0
- package/dist/shared/executor-result.js +221 -0
- package/dist/shared/file-lock.d.ts +12 -0
- package/dist/shared/file-lock.js +129 -0
- package/dist/shared/json-value.d.ts +5 -0
- package/dist/shared/json-value.js +36 -0
- package/dist/shared/keyed-mutex.d.ts +7 -0
- package/dist/shared/keyed-mutex.js +24 -0
- package/dist/shared/record-count.d.ts +8 -0
- package/dist/shared/record-count.js +43 -0
- package/dist/shared/sample-contract.d.ts +3 -0
- package/dist/shared/sample-contract.js +332 -0
- package/dist/shared/timestamp.d.ts +6 -0
- package/dist/shared/timestamp.js +64 -0
- package/dist/shared/token-usage.d.ts +19 -0
- package/dist/shared/token-usage.js +50 -0
- package/dist/shared/tool-call-status.d.ts +8 -0
- package/dist/shared/tool-call-status.js +28 -0
- package/dist/shared/tool-identity.d.ts +21 -0
- package/dist/shared/tool-identity.js +84 -0
- package/dist/shared/tool-search.js +73 -16
- package/dist/shared/trace-projection.d.ts +5 -0
- package/dist/shared/trace-projection.js +20 -0
- package/dist/shared/trace-source-kind.d.ts +3 -0
- package/dist/shared/trace-source-kind.js +12 -0
- package/dist/types/diagnosis.d.ts +2 -0
- package/dist/types/eval.d.ts +4 -0
- package/dist/types/executor.d.ts +32 -5
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.js +1 -0
- package/dist/types/judge.d.ts +2 -0
- package/dist/types/observability.d.ts +116 -9
- package/dist/types/report.d.ts +58 -6
- package/dist/types/skill-index.d.ts +7 -0
- package/dist/types/trace.d.ts +2 -0
- package/dist/types/trace.js +1 -0
- package/package.json +9 -5
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { readFileSync, readdirSync, statSync } from 'node:fs';
|
|
2
2
|
import { dirname, join, resolve } from 'node:path';
|
|
3
3
|
import yaml from 'js-yaml';
|
|
4
|
+
import { dependencyRequirementsValidationError, sampleContractValidationError, } from '../shared/sample-contract.js';
|
|
5
|
+
import { setOwnRecordValue } from '../shared/record-count.js';
|
|
4
6
|
export function parseYaml(text) {
|
|
5
7
|
try {
|
|
6
8
|
return yaml.load(text);
|
|
@@ -70,7 +72,7 @@ function loadSamplesFromDir(dir) {
|
|
|
70
72
|
`defined in both "${prev}" and "${f}"`);
|
|
71
73
|
}
|
|
72
74
|
seenIds.set(s.sample_id, f);
|
|
73
|
-
sampleSourceById
|
|
75
|
+
setOwnRecordValue(sampleSourceById, s.sample_id, path);
|
|
74
76
|
}
|
|
75
77
|
allSamples.push(...single.samples);
|
|
76
78
|
mergedRequires = mergeRequires(mergedRequires, single.requires);
|
|
@@ -118,10 +120,20 @@ export function validateSamples(samples) {
|
|
|
118
120
|
'workflow',
|
|
119
121
|
'workflow_node',
|
|
120
122
|
]);
|
|
123
|
+
const firstIndexBySampleId = new Map();
|
|
121
124
|
for (const [i, sample] of samples.entries()) {
|
|
125
|
+
if (!sample || typeof sample !== 'object' || Array.isArray(sample)) {
|
|
126
|
+
throw new Error(`samples[${i}] invalid sample contract: sample must be an object`);
|
|
127
|
+
}
|
|
122
128
|
if (!sample.sample_id || typeof sample.sample_id !== 'string') {
|
|
123
129
|
throw new Error(`samples[${i}] missing or invalid required field: sample_id (must be a non-empty string)`);
|
|
124
130
|
}
|
|
131
|
+
const firstIndex = firstIndexBySampleId.get(sample.sample_id);
|
|
132
|
+
if (firstIndex !== undefined) {
|
|
133
|
+
throw new Error(`duplicate sample_id "${sample.sample_id}" at samples[${i}] `
|
|
134
|
+
+ `(first defined at samples[${firstIndex}])`);
|
|
135
|
+
}
|
|
136
|
+
firstIndexBySampleId.set(sample.sample_id, i);
|
|
125
137
|
if (!sample.prompt || typeof sample.prompt !== 'string') {
|
|
126
138
|
throw new Error(`samples[${i}] (${sample.sample_id}) missing or invalid required field: prompt (must be a non-empty string)`);
|
|
127
139
|
}
|
|
@@ -247,6 +259,10 @@ export function validateSamples(samples) {
|
|
|
247
259
|
emitViolation(`${label}: regex pattern 含 CJK 字符 — 同样的语义匹配应走 rubric → judge,而不是字面正则: ${JSON.stringify(a.pattern)}`);
|
|
248
260
|
}
|
|
249
261
|
}
|
|
262
|
+
const contractError = sampleContractValidationError(sample);
|
|
263
|
+
if (contractError) {
|
|
264
|
+
throw new Error(`samples[${i}] (${sample.sample_id}) invalid sample contract: ${contractError}`);
|
|
265
|
+
}
|
|
250
266
|
}
|
|
251
267
|
}
|
|
252
268
|
function loadSampleFile(samplesPath) {
|
|
@@ -264,6 +280,12 @@ function loadSampleFile(samplesPath) {
|
|
|
264
280
|
const wrapper = parsed;
|
|
265
281
|
samples = wrapper.samples;
|
|
266
282
|
requires = wrapper.requires;
|
|
283
|
+
if (requires !== undefined) {
|
|
284
|
+
const requiresError = dependencyRequirementsValidationError(requires);
|
|
285
|
+
if (requiresError) {
|
|
286
|
+
throw new Error(`invalid samples file: ${samplesPath}: ${requiresError}`);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
267
289
|
}
|
|
268
290
|
else {
|
|
269
291
|
throw new Error(`invalid samples file shape: ${samplesPath} (expected an array or an object with a 'samples' field)`);
|
|
@@ -35,6 +35,7 @@ import { resolve } from 'node:path';
|
|
|
35
35
|
import { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
36
36
|
import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
|
|
37
37
|
import { isPlaceholderUrl } from './url-fetcher.js';
|
|
38
|
+
import { ownRecordValue, setOwnRecordValue } from '../shared/record-count.js';
|
|
38
39
|
// Reuse the same URL regex from url-fetcher.mjs
|
|
39
40
|
const URL_REGEX = /https?:\/\/[A-Za-z0-9\-._~:/?#[\]@!$&'()*+,;=%]+/g;
|
|
40
41
|
const MCP_CALL_TIMEOUT_MS = 30_000;
|
|
@@ -83,7 +84,7 @@ export function loadMcpConfig(configPath) {
|
|
|
83
84
|
const eligible = {};
|
|
84
85
|
for (const [name, def] of Object.entries(serverDefs)) {
|
|
85
86
|
if (isMcpServerDef(def) && def.urlPatterns.length > 0) {
|
|
86
|
-
eligible
|
|
87
|
+
setOwnRecordValue(eligible, name, def);
|
|
87
88
|
}
|
|
88
89
|
}
|
|
89
90
|
if (Object.keys(eligible).length === 0)
|
|
@@ -145,7 +146,9 @@ export async function resolveMcpUrls(samples, mcpServers) {
|
|
|
145
146
|
const fetched = new Map(); // url -> { ok, content, error }
|
|
146
147
|
const fetchPromises = [];
|
|
147
148
|
for (const [serverName, urls] of serverUrlGroups) {
|
|
148
|
-
|
|
149
|
+
const server = ownRecordValue(mcpServers, serverName);
|
|
150
|
+
if (server)
|
|
151
|
+
fetchPromises.push(fetchFromServer(serverName, server, urls, fetched));
|
|
149
152
|
}
|
|
150
153
|
await Promise.allSettled(fetchPromises);
|
|
151
154
|
// 4. Report failures
|
|
@@ -208,7 +211,7 @@ function buildToolArgs(fetchTool, url) {
|
|
|
208
211
|
return null;
|
|
209
212
|
const args = {};
|
|
210
213
|
for (const [key, template] of Object.entries(params)) {
|
|
211
|
-
args
|
|
214
|
+
setOwnRecordValue(args, key, template.replace(/\$(\d+)/g, (_, i) => match[Number(i)] || ''));
|
|
212
215
|
}
|
|
213
216
|
return args;
|
|
214
217
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { Sample } from '../types/index.js';
|
|
2
|
+
import { type LoadSamplesResult } from './load-samples.js';
|
|
3
|
+
export declare function parseSampleDocument(filePath: string): unknown;
|
|
4
|
+
export declare function getSamplesArray(document: unknown, filePath: string): Sample[];
|
|
5
|
+
export declare function stringifySampleDocument(filePath: string, document: unknown): string;
|
|
6
|
+
/**
|
|
7
|
+
* Persist only changed samples back to the source file that originally owned each
|
|
8
|
+
* sample_id. JSON/YAML and array/wrapper shapes are retained, including `requires`
|
|
9
|
+
* and any other wrapper metadata.
|
|
10
|
+
*/
|
|
11
|
+
export declare function writeFixedSamplesToSources(loaded: Pick<LoadSamplesResult, 'sourceFiles' | 'sampleSourceById'>, samples: Sample[], changedIds: ReadonlySet<string>): string[];
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { lstatSync, readFileSync, realpathSync, renameSync, statSync, unlinkSync, writeFileSync, } from 'node:fs';
|
|
2
|
+
import { dirname, join } from 'node:path';
|
|
3
|
+
import yaml from 'js-yaml';
|
|
4
|
+
import { withFileLock } from '../shared/file-lock.js';
|
|
5
|
+
import { ownRecordValue } from '../shared/record-count.js';
|
|
6
|
+
import { parseYaml, validateSamples } from './load-samples.js';
|
|
7
|
+
function isRecord(value) {
|
|
8
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
9
|
+
}
|
|
10
|
+
function isYamlPath(filePath) {
|
|
11
|
+
return /\.(ya?ml)$/i.test(filePath);
|
|
12
|
+
}
|
|
13
|
+
export function parseSampleDocument(filePath) {
|
|
14
|
+
const raw = readFileSync(filePath, 'utf-8');
|
|
15
|
+
return isYamlPath(filePath) ? parseYaml(raw) : JSON.parse(raw);
|
|
16
|
+
}
|
|
17
|
+
export function getSamplesArray(document, filePath) {
|
|
18
|
+
if (Array.isArray(document))
|
|
19
|
+
return document;
|
|
20
|
+
if (isRecord(document) && Array.isArray(document.samples)) {
|
|
21
|
+
return document.samples;
|
|
22
|
+
}
|
|
23
|
+
throw new Error(`invalid samples file shape: ${filePath} `
|
|
24
|
+
+ `(expected an array or an object with a 'samples' field)`);
|
|
25
|
+
}
|
|
26
|
+
export function stringifySampleDocument(filePath, document) {
|
|
27
|
+
if (isYamlPath(filePath)) {
|
|
28
|
+
return yaml.dump(document, { lineWidth: -1, noRefs: true });
|
|
29
|
+
}
|
|
30
|
+
return JSON.stringify(document, null, 2);
|
|
31
|
+
}
|
|
32
|
+
function writableFilePath(filePath) {
|
|
33
|
+
return lstatSync(filePath).isSymbolicLink() ? realpathSync(filePath) : filePath;
|
|
34
|
+
}
|
|
35
|
+
function atomicWriteFile(filePath, content) {
|
|
36
|
+
const targetPath = writableFilePath(filePath);
|
|
37
|
+
const tempPath = join(dirname(targetPath), `.${process.pid}.${Date.now()}.${Math.random().toString(36).slice(2)}.tmp`);
|
|
38
|
+
try {
|
|
39
|
+
writeFileSync(tempPath, content, { mode: statSync(targetPath).mode & 0o777 });
|
|
40
|
+
renameSync(tempPath, targetPath);
|
|
41
|
+
}
|
|
42
|
+
catch (error) {
|
|
43
|
+
try {
|
|
44
|
+
unlinkSync(tempPath);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// The rename may already have consumed the temporary file.
|
|
48
|
+
}
|
|
49
|
+
throw error;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Persist only changed samples back to the source file that originally owned each
|
|
54
|
+
* sample_id. JSON/YAML and array/wrapper shapes are retained, including `requires`
|
|
55
|
+
* and any other wrapper metadata.
|
|
56
|
+
*/
|
|
57
|
+
export function writeFixedSamplesToSources(loaded, samples, changedIds) {
|
|
58
|
+
if (changedIds.size === 0)
|
|
59
|
+
return [];
|
|
60
|
+
const fixedById = new Map(samples.map((sample) => [sample.sample_id, sample]));
|
|
61
|
+
const idsByFile = new Map();
|
|
62
|
+
for (const sampleId of changedIds) {
|
|
63
|
+
const filePath = ownRecordValue(loaded.sampleSourceById, sampleId);
|
|
64
|
+
if (!filePath)
|
|
65
|
+
throw new Error(`sample ${sampleId} source file not found`);
|
|
66
|
+
if (!loaded.sourceFiles.includes(filePath)) {
|
|
67
|
+
throw new Error(`sample ${sampleId} source file is outside the loaded sample set`);
|
|
68
|
+
}
|
|
69
|
+
if (!fixedById.has(sampleId)) {
|
|
70
|
+
throw new Error(`fixed sample ${sampleId} is missing from the update set`);
|
|
71
|
+
}
|
|
72
|
+
const ids = idsByFile.get(filePath) ?? new Set();
|
|
73
|
+
ids.add(sampleId);
|
|
74
|
+
idsByFile.set(filePath, ids);
|
|
75
|
+
}
|
|
76
|
+
for (const [filePath, ids] of idsByFile.entries()) {
|
|
77
|
+
const targetPath = writableFilePath(filePath);
|
|
78
|
+
withFileLock(`${targetPath}.omk.lock`, () => {
|
|
79
|
+
const document = parseSampleDocument(filePath);
|
|
80
|
+
const fileSamples = getSamplesArray(document, filePath);
|
|
81
|
+
const existingIds = new Set(fileSamples.map((sample) => sample.sample_id));
|
|
82
|
+
for (const sampleId of ids) {
|
|
83
|
+
if (!existingIds.has(sampleId)) {
|
|
84
|
+
throw new Error(`sample ${sampleId} is no longer present in ${filePath}`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
const nextSamples = fileSamples.map((sample) => (ids.has(sample.sample_id) ? fixedById.get(sample.sample_id) : sample));
|
|
88
|
+
validateSamples(nextSamples);
|
|
89
|
+
const nextDocument = Array.isArray(document)
|
|
90
|
+
? nextSamples
|
|
91
|
+
: { ...document, samples: nextSamples };
|
|
92
|
+
atomicWriteFile(filePath, stringifySampleDocument(filePath, nextDocument));
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
return [...idsByFile.keys()];
|
|
96
|
+
}
|
|
@@ -8,6 +8,7 @@ export declare function buildEvidenceRef(report: EvaluationReport, variant: stri
|
|
|
8
8
|
export interface RecordedEvidence {
|
|
9
9
|
recordId: string;
|
|
10
10
|
name: string;
|
|
11
|
+
variant: string;
|
|
11
12
|
contentHash: string;
|
|
12
13
|
/** true ⇒ evidence.contentHash 等于记录当前 contentHash → deriveManagedState 计为当前证据 →
|
|
13
14
|
* measurable。false ⇒ 测的是旧内容(已 drift),证据留存但不绑当前版本。 */
|
package/dist/managed/evidence.js
CHANGED
|
@@ -149,7 +149,7 @@ export function recordEvalEvidence(report, verdict, recordedAt, opts = {}) {
|
|
|
149
149
|
const merged = appendManagedEvidence(dir, rec.id, ref);
|
|
150
150
|
if (!merged)
|
|
151
151
|
continue;
|
|
152
|
-
out.push({ recordId: rec.id, name: rec.name, contentHash: ref.contentHash, bound: ref.contentHash === merged.contentHash });
|
|
152
|
+
out.push({ recordId: rec.id, name: rec.name, variant: variantKey, contentHash: ref.contentHash, bound: ref.contentHash === merged.contentHash });
|
|
153
153
|
}
|
|
154
154
|
return out;
|
|
155
155
|
}
|