@inflexa-ai/harness 0.17.0 → 0.18.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/dist/agents/report-builder.d.ts +2 -0
- package/dist/agents/report-builder.js +2 -0
- package/dist/app/spawn-report-session.d.ts +85 -0
- package/dist/app/spawn-report-session.js +83 -0
- package/dist/contracts/chat-events.d.ts +9 -0
- package/dist/contracts/index.d.ts +6 -2
- package/dist/contracts/index.js +3 -1
- package/dist/contracts/organ-system.d.ts +49 -0
- package/dist/contracts/organ-system.js +67 -0
- package/dist/contracts/report-blocks.d.ts +564 -0
- package/dist/contracts/report-blocks.js +117 -0
- package/dist/contracts/report-reference.d.ts +459 -0
- package/dist/contracts/report-reference.js +256 -0
- package/dist/contracts/schemas/chat-events.d.ts +2 -0
- package/dist/contracts/schemas/chat-events.js +2 -0
- package/dist/contracts/severity.d.ts +15 -0
- package/dist/contracts/severity.js +10 -0
- package/dist/contracts/target-dossier.d.ts +8675 -12972
- package/dist/contracts/target-dossier.js +534 -591
- package/dist/data/safety-panel-schema.d.ts +18 -5
- package/dist/data/safety-panel-schema.js +4 -4
- package/dist/execution/artifact-metadata.js +1 -0
- package/dist/execution/report-runner.js +1 -3
- package/dist/execution/run-synthesis.js +2 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.js +19 -0
- package/dist/lib/async-utils.d.ts +16 -4
- package/dist/lib/async-utils.js +20 -15
- package/dist/lib/host-concurrency.js +1 -0
- package/dist/loop/__fixtures__/scripted-provider.js +23 -2
- package/dist/loop/run-agent.js +58 -10
- package/dist/loop/tool-detail.d.ts +40 -0
- package/dist/loop/tool-detail.js +35 -3
- package/dist/loop/types.d.ts +13 -0
- package/dist/memory/thread-history.d.ts +10 -0
- package/dist/memory/thread-history.js +12 -1
- package/dist/prompts/target-assessment/briefs/executive-recommendation.d.ts +1 -1
- package/dist/prompts/target-assessment/briefs/executive-recommendation.js +5 -5
- package/dist/prompts/target-assessment/briefs/liability-bullets.d.ts +1 -1
- package/dist/prompts/target-assessment/briefs/liability-bullets.js +1 -1
- package/dist/prompts/target-assessment/briefs/target-organ-liabilities.d.ts +1 -1
- package/dist/prompts/target-assessment/briefs/target-organ-liabilities.js +11 -3
- package/dist/prompts/target-assessment/investigation/adversarial-critique.d.ts +1 -0
- package/dist/prompts/target-assessment/investigation/adversarial-critique.js +67 -0
- package/dist/prompts/target-assessment/investigation/claim-reverification.d.ts +1 -0
- package/dist/prompts/target-assessment/investigation/claim-reverification.js +49 -0
- package/dist/prompts/target-assessment/investigation/index.d.ts +3 -0
- package/dist/prompts/target-assessment/investigation/index.js +3 -0
- package/dist/prompts/target-assessment/investigation/mechanism-proposal.d.ts +1 -0
- package/dist/prompts/target-assessment/investigation/mechanism-proposal.js +49 -0
- package/dist/prompts/target-assessment/tox-voice/index.d.ts +1 -1
- package/dist/prompts/target-assessment/tox-voice/vocabulary.d.ts +8 -5
- package/dist/prompts/target-assessment/tox-voice/vocabulary.js +9 -20
- package/dist/providers/__fixtures__/session.d.ts +5 -4
- package/dist/providers/__fixtures__/session.js +4 -4
- package/dist/report-model/block-walk.d.ts +56 -0
- package/dist/report-model/block-walk.js +89 -0
- package/dist/report-model/draft-finish.d.ts +60 -0
- package/dist/report-model/draft-finish.js +56 -0
- package/dist/report-model/draft-operations.d.ts +125 -0
- package/dist/report-model/draft-operations.js +341 -0
- package/dist/report-model/draft-read.d.ts +59 -0
- package/dist/report-model/draft-read.js +109 -0
- package/dist/report-model/draft.d.ts +51 -0
- package/dist/report-model/draft.js +32 -0
- package/dist/report-model/fixture-resolver.d.ts +8 -0
- package/dist/report-model/fixture-resolver.js +252 -0
- package/dist/report-model/mint-snapshot.d.ts +30 -0
- package/dist/report-model/mint-snapshot.js +40 -0
- package/dist/report-model/reference-resolver.d.ts +113 -0
- package/dist/report-model/reference-resolver.js +60 -0
- package/dist/report-model/structural-validation.d.ts +22 -0
- package/dist/report-model/structural-validation.js +68 -0
- package/dist/report-model/validate.d.ts +40 -0
- package/dist/report-model/validate.js +80 -0
- package/dist/report-render/chart.d.ts +33 -0
- package/dist/report-render/chart.js +538 -0
- package/dist/report-render/page.d.ts +127 -0
- package/dist/report-render/page.js +307 -0
- package/dist/report-render/references.d.ts +32 -0
- package/dist/report-render/references.js +44 -0
- package/dist/report-render/render.d.ts +25 -0
- package/dist/report-render/render.js +125 -0
- package/dist/report-render/script-json.d.ts +13 -0
- package/dist/report-render/script-json.js +15 -0
- package/dist/report-render/types.d.ts +39 -0
- package/dist/report-render/types.js +10 -0
- package/dist/report-render/views/chart-view.d.ts +11 -0
- package/dist/report-render/views/chart-view.js +23 -0
- package/dist/report-render/views/page-view.d.ts +20 -0
- package/dist/report-render/views/page-view.js +49 -0
- package/dist/report-render/views/prose.d.ts +28 -0
- package/dist/report-render/views/prose.js +75 -0
- package/dist/report-render/views/references-view.d.ts +20 -0
- package/dist/report-render/views/references-view.js +64 -0
- package/dist/report-render/views/values.d.ts +42 -0
- package/dist/report-render/views/values.js +43 -0
- package/dist/runtime/boot.d.ts +3 -3
- package/dist/runtime/boot.js +15 -4
- package/dist/runtime/dbos.d.ts +4 -4
- package/dist/state/artifacts.d.ts +16 -0
- package/dist/state/artifacts.js +18 -0
- package/dist/state/init.js +31 -0
- package/dist/state/purge-analysis.js +1 -0
- package/dist/state/report-versions.d.ts +132 -0
- package/dist/state/report-versions.js +194 -0
- package/dist/tasks/data-profile.js +1 -0
- package/dist/tools/bio/chembl.js +1 -0
- package/dist/tools/bio/comptox.js +1 -0
- package/dist/tools/bio/drug-gene-interactions.js +1 -0
- package/dist/tools/bio/gene-disease-evidence.js +1 -0
- package/dist/tools/bio/gene-preclinical-profile.js +1 -0
- package/dist/tools/bio/keys.d.ts +3 -2
- package/dist/tools/bio/keys.js +3 -2
- package/dist/tools/bio/lookup-annotation.js +1 -0
- package/dist/tools/bio/opentargets.d.ts +3 -3
- package/dist/tools/bio/opentargets.js +4 -3
- package/dist/tools/bio/pubchem.js +1 -0
- package/dist/tools/bio/search-clinical-trials.js +1 -0
- package/dist/tools/bio/search-faers.js +1 -0
- package/dist/tools/bio/search-geo-datasets.js +1 -0
- package/dist/tools/bio/search-interactions.js +1 -0
- package/dist/tools/bio/target-safety.d.ts +4 -6
- package/dist/tools/bio/target-safety.js +7 -6
- package/dist/tools/define-tool.d.ts +19 -12
- package/dist/tools/define-tool.js +4 -2
- package/dist/tools/display/show-user.js +1 -0
- package/dist/tools/iterate-report.js +2 -0
- package/dist/tools/lib/chembl-client.d.ts +2 -0
- package/dist/tools/lib/chembl-client.js +1 -0
- package/dist/tools/lib/monarch-client.d.ts +42 -0
- package/dist/tools/lib/monarch-client.js +100 -0
- package/dist/tools/report/build-report.js +1 -0
- package/dist/tools/report/mint-preview-url.js +1 -0
- package/dist/tools/report/preview-snapshot.js +1 -0
- package/dist/tools/report/submit-report.js +1 -0
- package/dist/tools/report/version-fs.js +4 -0
- package/dist/tools/report-authoring/authoring-tools.d.ts +134 -0
- package/dist/tools/report-authoring/authoring-tools.js +352 -0
- package/dist/tools/research/context7-docs.js +2 -0
- package/dist/tools/research/generate-analogy-report.js +1 -0
- package/dist/tools/research/generate-plan.js +3 -0
- package/dist/tools/research/inspect-data-profile.js +4 -0
- package/dist/tools/research/literature-reviewer.js +1 -0
- package/dist/tools/research/search-arxiv.js +1 -0
- package/dist/tools/research/search-github-repos.js +1 -0
- package/dist/tools/research/search-semantic-scholar.js +1 -0
- package/dist/tools/sandbox/list-available-packages.js +23 -0
- package/dist/tools/sandbox/list-available-refs.js +36 -0
- package/dist/tools/sandbox/report-blocker.js +1 -0
- package/dist/tools/sandbox/skills.js +2 -0
- package/dist/tools/workspace/file-stat.js +1 -0
- package/dist/tools/workspace/show-file.js +2 -0
- package/dist/tools/workspace/show-plan.js +1 -0
- package/dist/workflows/execute-analysis.d.ts +5 -1
- package/dist/workflows/execute-analysis.js +5 -5
- package/dist/workflows/execute-target-assessment.d.ts +13 -2
- package/dist/workflows/execute-target-assessment.js +78 -16
- package/dist/workflows/target-assessment/assemblers/index.d.ts +1 -1
- package/dist/workflows/target-assessment/assemblers/index.js +1 -1
- package/dist/workflows/target-assessment/assemblers/literature.d.ts +9 -27
- package/dist/workflows/target-assessment/assemblers/literature.js +32 -48
- package/dist/workflows/target-assessment/assemblers/orchestrator.d.ts +17 -9
- package/dist/workflows/target-assessment/assemblers/orchestrator.js +195 -85
- package/dist/workflows/target-assessment/assemblers/safety.d.ts +70 -48
- package/dist/workflows/target-assessment/assemblers/safety.js +125 -42
- package/dist/workflows/target-assessment/collectors/index.d.ts +5 -2
- package/dist/workflows/target-assessment/collectors/index.js +48 -5
- package/dist/workflows/target-assessment/coverage.d.ts +43 -27
- package/dist/workflows/target-assessment/coverage.js +33 -29
- package/dist/workflows/target-assessment/fanout/index.js +1 -0
- package/dist/workflows/target-assessment/investigation/index.d.ts +97 -0
- package/dist/workflows/target-assessment/investigation/index.js +419 -0
- package/dist/workflows/target-assessment/lib/approval-precedents.d.ts +26 -5
- package/dist/workflows/target-assessment/lib/approval-precedents.js +95 -29
- package/dist/workflows/target-assessment/lib/citation-validator.js +1 -1
- package/dist/workflows/target-assessment/lib/clinical-consequence-annotator.d.ts +2 -2
- package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.d.ts +2 -3
- package/dist/workflows/target-assessment/lib/clinical-evidence-attribution.js +0 -4
- package/dist/workflows/target-assessment/lib/compute-derived.d.ts +6 -5
- package/dist/workflows/target-assessment/lib/compute-derived.js +14 -5
- package/dist/workflows/target-assessment/lib/family-complex-supplement.d.ts +5 -6
- package/dist/workflows/target-assessment/lib/family-complex-supplement.js +2 -3
- package/dist/workflows/target-assessment/lib/fda-label-safety.d.ts +72 -0
- package/dist/workflows/target-assessment/lib/fda-label-safety.js +166 -0
- package/dist/workflows/target-assessment/lib/heterodimer-filter.d.ts +0 -9
- package/dist/workflows/target-assessment/lib/heterodimer-filter.js +0 -10
- package/dist/workflows/target-assessment/lib/hpo-organ-map.d.ts +34 -0
- package/dist/workflows/target-assessment/lib/hpo-organ-map.js +63 -0
- package/dist/workflows/target-assessment/lib/impc-organ-map.d.ts +19 -0
- package/dist/workflows/target-assessment/lib/impc-organ-map.js +39 -0
- package/dist/workflows/target-assessment/lib/meddra-organ-map.d.ts +24 -14
- package/dist/workflows/target-assessment/lib/meddra-organ-map.js +59 -47
- package/dist/workflows/target-assessment/lib/recommendation-sanitizer.js +1 -1
- package/dist/workflows/target-assessment/lib/regulatory-actions.d.ts +2 -3
- package/dist/workflows/target-assessment/lib/regulatory-actions.js +33 -4
- package/dist/workflows/target-assessment/lib/safety-corroboration.d.ts +44 -0
- package/dist/workflows/target-assessment/lib/safety-corroboration.js +273 -0
- package/dist/workflows/target-assessment/lib/target-identity-filter.d.ts +5 -7
- package/dist/workflows/target-assessment/lib/target-identity-filter.js +5 -7
- package/dist/workflows/target-assessment/metrics.d.ts +1 -6
- package/dist/workflows/target-assessment/metrics.js +1 -13
- package/dist/workflows/target-assessment/phase4-assemble.d.ts +2147 -807
- package/dist/workflows/target-assessment/phase4-assemble.js +19 -19
- package/dist/workflows/target-assessment/phase5-persist.d.ts +20 -21
- package/dist/workflows/target-assessment/phase5-persist.js +56 -173
- package/dist/workflows/target-assessment/schemas.d.ts +46 -1
- package/dist/workflows/target-assessment/schemas.js +19 -0
- package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.d.ts +1 -0
- package/dist/workflows/target-assessment/steps/fanout/polypharm-for-one-modulator-step.js +2 -0
- package/dist/workflows/target-assessment/steps/phase2-aggregate.d.ts +26 -62
- package/dist/workflows/target-assessment/steps/phase2-aggregate.js +0 -4
- package/dist/workflows/target-assessment/steps/phase3-aggregate.d.ts +27 -154
- package/dist/workflows/target-assessment/steps/phase3-aggregate.js +0 -6
- package/dist/workflows/target-assessment/steps/synthesis/diagnostics.d.ts +0 -6
- package/dist/workflows/target-assessment/steps/synthesis/diagnostics.js +0 -18
- package/dist/workflows/target-assessment/sub-workflows/per-modulator-polypharm.d.ts +1 -0
- package/dist/workflows/target-assessment/synthesis/index.d.ts +52 -5
- package/dist/workflows/target-assessment/synthesis/index.js +4 -1
- package/dist/workflows/target-assessment/synthesis-probes.d.ts +3 -3
- package/package.json +4 -1
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The seam that turns a report reference into a concrete value, and the value model that it gives back.
|
|
3
|
+
*
|
|
4
|
+
* A reference is authored belief. A resolver reads the pinned evidence and gives the value that the
|
|
5
|
+
* belief points at. The harness declares the seam here, thus a production realization can read storage,
|
|
6
|
+
* and a fixture realization can read an in-memory snapshot. The two obey the same contract.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Give back each name in `columns` that no row holds, in the order of `columns`.
|
|
10
|
+
*
|
|
11
|
+
* A table tolerates a ragged row, thus a column that only some rows hold is present. A column that no
|
|
12
|
+
* row holds is a name that addresses nothing, and it is the difference between a sparse table and a
|
|
13
|
+
* column that does not exist.
|
|
14
|
+
*
|
|
15
|
+
* A table with no rows gives back nothing. There is no evidence to contradict any name, and an empty
|
|
16
|
+
* result table is a real scientific outcome that must not fail for the shape of its emptiness.
|
|
17
|
+
*/
|
|
18
|
+
export function columnsHeldByNoRow(rows, columns) {
|
|
19
|
+
if (rows.length === 0) {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
return columns.filter((column) => !rows.some((row) => column in row));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The file type of an entry that holds no cell for a reference to address.
|
|
26
|
+
*
|
|
27
|
+
* The `satisfies` clause ties each literal to a member of `ArtifactType`. A renamed member of
|
|
28
|
+
* `ArtifactType` becomes a compile error here. A new member passes with no error, because the rule
|
|
29
|
+
* refuses a kind and never confirms one.
|
|
30
|
+
*/
|
|
31
|
+
const FILE_TYPES_WITH_NO_CELL = new Set(["figure", "script", "log", "notebook"]);
|
|
32
|
+
/**
|
|
33
|
+
* Give back `true` for a file type that holds no cell for a reference to address.
|
|
34
|
+
*
|
|
35
|
+
* The file type states a role, and it does not state a data format. Thus the rule runs one way only. It
|
|
36
|
+
* refuses a kind, and it never confirms one. An `output` gives back `false`, because it covers a table and
|
|
37
|
+
* an image alike. Only a read of the artifact settles which one it is. An absent file type gives back
|
|
38
|
+
* `false` for the same reason.
|
|
39
|
+
*
|
|
40
|
+
* The structural tier and a resolver both read this one predicate. Thus the two tiers can never disagree
|
|
41
|
+
* about one reference.
|
|
42
|
+
*/
|
|
43
|
+
export function fileTypeHoldsNoCell(fileType) {
|
|
44
|
+
return fileType !== undefined && fileType !== null && FILE_TYPES_WITH_NO_CELL.has(fileType);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Give back the snapshot entry at `path`, or `undefined` when the map holds no such own key.
|
|
48
|
+
*
|
|
49
|
+
* An agent authors a report reference, thus the path is untrusted text. The `artifacts` map can be a
|
|
50
|
+
* plain object: a fixture literal, or a `JSON.parse` of a stored snapshot. A bracket lookup on a plain
|
|
51
|
+
* object with a path such as `constructor` finds an inherited member. Then the refusal reads
|
|
52
|
+
* `hash-mismatch`, but the true answer is `artifact-missing`. The `Object.hasOwn` guard admits an own
|
|
53
|
+
* key only, thus an inherited member reads as absent.
|
|
54
|
+
*
|
|
55
|
+
* The structural tier and the fixture resolver both read this one lookup. Thus the two tiers can never
|
|
56
|
+
* disagree about membership.
|
|
57
|
+
*/
|
|
58
|
+
export function snapshotEntry(snapshot, path) {
|
|
59
|
+
return Object.hasOwn(snapshot.artifacts, path) ? snapshot.artifacts[path] : undefined;
|
|
60
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The structural tier of reference resolution.
|
|
3
|
+
*
|
|
4
|
+
* The tier answers from the pinned snapshot alone, and it opens no file. Thus an authoring operation can
|
|
5
|
+
* run it on each change, and it stays instant while a document grows.
|
|
6
|
+
*
|
|
7
|
+
* The value tier gives the value that a reference points at, and only a read of the artifact gives that
|
|
8
|
+
* value. As a result the costly read happens one time for each report version, and never for each edit.
|
|
9
|
+
*/
|
|
10
|
+
import { type Result } from "neverthrow";
|
|
11
|
+
import type { Reference, UnresolvedReference } from "../contracts/report-reference.js";
|
|
12
|
+
import { type ReportSnapshot } from "./reference-resolver.js";
|
|
13
|
+
/**
|
|
14
|
+
* Validate one reference against the snapshot.
|
|
15
|
+
*
|
|
16
|
+
* The `Ok` channel carries no value. The tier answers membership, identity, and the file type, and a
|
|
17
|
+
* value comes from a read of the artifact. An `assert` is the authored belief about that value, thus this
|
|
18
|
+
* tier matches no assertion and a reference that carries one passes on its pin alone.
|
|
19
|
+
*
|
|
20
|
+
* The answer is synchronous, because the snapshot is already in memory.
|
|
21
|
+
*/
|
|
22
|
+
export declare function validateReferenceStructure(reference: Reference, snapshot: ReportSnapshot): Result<void, UnresolvedReference>;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The structural tier of reference resolution.
|
|
3
|
+
*
|
|
4
|
+
* The tier answers from the pinned snapshot alone, and it opens no file. Thus an authoring operation can
|
|
5
|
+
* run it on each change, and it stays instant while a document grows.
|
|
6
|
+
*
|
|
7
|
+
* The value tier gives the value that a reference points at, and only a read of the artifact gives that
|
|
8
|
+
* value. As a result the costly read happens one time for each report version, and never for each edit.
|
|
9
|
+
*/
|
|
10
|
+
import { err, ok } from "neverthrow";
|
|
11
|
+
import { fileTypeHoldsNoCell, snapshotEntry } from "./reference-resolver.js";
|
|
12
|
+
/** Build an `Err` that carries the unresolved reference. The `detail` key is present only when there is a detail to carry. */
|
|
13
|
+
function fail(reference, reason, detail) {
|
|
14
|
+
const failure = detail !== undefined ? { reference, reason, detail } : { reference, reason };
|
|
15
|
+
return err(failure);
|
|
16
|
+
}
|
|
17
|
+
/** Validate the artifact pin of one reference: its membership in the snapshot, its identity, and the file type of its entry. */
|
|
18
|
+
function validatePin(reference, snapshot) {
|
|
19
|
+
const entry = snapshotEntry(snapshot, reference.path);
|
|
20
|
+
if (entry === undefined) {
|
|
21
|
+
return fail(reference, "artifact-missing", `no artifact at ${reference.path}`);
|
|
22
|
+
}
|
|
23
|
+
if (entry.hash !== reference.hash) {
|
|
24
|
+
return fail(reference, "hash-mismatch", `expected ${reference.hash} but the artifact hash is ${entry.hash}`);
|
|
25
|
+
}
|
|
26
|
+
// An `artifact-file` pins the bytes of a whole file, thus each file type is valid for it. A figure
|
|
27
|
+
// block binds through `artifact-file`, and a rule that refused a `figure` here would fail each figure
|
|
28
|
+
// of each report.
|
|
29
|
+
const readsACell = reference.kind === "artifact-value" || reference.kind === "artifact-table";
|
|
30
|
+
const fileType = entry.fileType;
|
|
31
|
+
if (readsACell && fileTypeHoldsNoCell(fileType)) {
|
|
32
|
+
return fail(reference, "unreadable-artifact", `the ${fileType} at ${reference.path} holds no cell to read`);
|
|
33
|
+
}
|
|
34
|
+
return ok();
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Validate one reference against the snapshot.
|
|
38
|
+
*
|
|
39
|
+
* The `Ok` channel carries no value. The tier answers membership, identity, and the file type, and a
|
|
40
|
+
* value comes from a read of the artifact. An `assert` is the authored belief about that value, thus this
|
|
41
|
+
* tier matches no assertion and a reference that carries one passes on its pin alone.
|
|
42
|
+
*
|
|
43
|
+
* The answer is synchronous, because the snapshot is already in memory.
|
|
44
|
+
*/
|
|
45
|
+
export function validateReferenceStructure(reference, snapshot) {
|
|
46
|
+
switch (reference.kind) {
|
|
47
|
+
case "artifact-value":
|
|
48
|
+
case "artifact-table":
|
|
49
|
+
case "artifact-file":
|
|
50
|
+
return validatePin(reference, snapshot);
|
|
51
|
+
case "derivation": {
|
|
52
|
+
// A derivation holds no pin of its own, thus it is grounded exactly when both of its inputs
|
|
53
|
+
// are grounded. The failure keeps the reason of the input, because the derivation itself did
|
|
54
|
+
// not fail on its own terms.
|
|
55
|
+
for (const input of reference.inputs) {
|
|
56
|
+
const inputResult = validatePin(input, snapshot);
|
|
57
|
+
if (inputResult.isErr()) {
|
|
58
|
+
return fail(reference, inputResult.error.reason, inputResult.error.detail);
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return ok();
|
|
62
|
+
}
|
|
63
|
+
case "citation":
|
|
64
|
+
// A citation holds an external id and no path, thus the snapshot has nothing to say about it.
|
|
65
|
+
// The citation authorities settle its truth at the value tier.
|
|
66
|
+
return ok();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The mechanical validator of a report document.
|
|
3
|
+
*
|
|
4
|
+
* The schemas already carry the grammar: a strict object rejects a forbidden field, a binding is present
|
|
5
|
+
* by construction, and a metric value slot admits one scalar reference only. Thus a schema parse failure
|
|
6
|
+
* is the grammar rejection, and the validator does not re-implement it. The validator adds the three
|
|
7
|
+
* checks that a schema cannot make: it makes sure that each block id occurs one time only, it resolves
|
|
8
|
+
* each reference against the pinned evidence, and it warns about a free numeral in prose.
|
|
9
|
+
*/
|
|
10
|
+
import type { UnresolvedReference } from "../contracts/report-reference.js";
|
|
11
|
+
import { type ReportWarning } from "./block-walk.js";
|
|
12
|
+
import { type ReferenceResolver, type ReportSnapshot } from "./reference-resolver.js";
|
|
13
|
+
export type { ReportWarning };
|
|
14
|
+
/** One schema conformance problem, reduced to a path and a message. */
|
|
15
|
+
export interface SchemaIssue {
|
|
16
|
+
path: string;
|
|
17
|
+
message: string;
|
|
18
|
+
}
|
|
19
|
+
/** One reference that did not resolve, tied to the block that carries it. */
|
|
20
|
+
export interface ResolutionFailure {
|
|
21
|
+
blockId: string;
|
|
22
|
+
failure: UnresolvedReference;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* The result of validation. `valid` is false when the schema failed, a block id repeats, or a reference
|
|
26
|
+
* did not resolve. `duplicateIds` names each repeated id one time, in sorted order. The warnings ride
|
|
27
|
+
* along in either case.
|
|
28
|
+
*/
|
|
29
|
+
export type ReportValidation = {
|
|
30
|
+
valid: true;
|
|
31
|
+
warnings: ReportWarning[];
|
|
32
|
+
} | {
|
|
33
|
+
valid: false;
|
|
34
|
+
schemaIssues?: SchemaIssue[];
|
|
35
|
+
duplicateIds?: string[];
|
|
36
|
+
resolutionFailures?: ResolutionFailure[];
|
|
37
|
+
warnings: ReportWarning[];
|
|
38
|
+
};
|
|
39
|
+
/** Resolve each reference, collect the schema conformance and the resolution outcomes, and warn on prose. */
|
|
40
|
+
export declare function validateReport(document: unknown, snapshot: ReportSnapshot, resolver: ReferenceResolver): Promise<ReportValidation>;
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The mechanical validator of a report document.
|
|
3
|
+
*
|
|
4
|
+
* The schemas already carry the grammar: a strict object rejects a forbidden field, a binding is present
|
|
5
|
+
* by construction, and a metric value slot admits one scalar reference only. Thus a schema parse failure
|
|
6
|
+
* is the grammar rejection, and the validator does not re-implement it. The validator adds the three
|
|
7
|
+
* checks that a schema cannot make: it makes sure that each block id occurs one time only, it resolves
|
|
8
|
+
* each reference against the pinned evidence, and it warns about a free numeral in prose.
|
|
9
|
+
*/
|
|
10
|
+
import { ReportDocumentSchema } from "../contracts/report-blocks.js";
|
|
11
|
+
import { allWithConcurrency } from "../lib/async-utils.js";
|
|
12
|
+
import { walkBlocks } from "./block-walk.js";
|
|
13
|
+
import { columnsHeldByNoRow } from "./reference-resolver.js";
|
|
14
|
+
/**
|
|
15
|
+
* The cap on how many references resolve at the same time.
|
|
16
|
+
*
|
|
17
|
+
* A production resolver reads storage for each reference, and a large report holds hundreds of them. An
|
|
18
|
+
* unbounded fan-out would open one read for each reference at the same moment.
|
|
19
|
+
*/
|
|
20
|
+
const RESOLUTION_CONCURRENCY = 8;
|
|
21
|
+
/**
|
|
22
|
+
* Match each column that a chart encoding names against the table that its binding resolved to.
|
|
23
|
+
*
|
|
24
|
+
* The binding schema admits a table reference only, thus a resolved value of another type means that the
|
|
25
|
+
* bound resolver broke its own contract. That is reported and never ignored, because a chart with no
|
|
26
|
+
* table behind it renders nothing and a silent skip would pass it as grounded.
|
|
27
|
+
*/
|
|
28
|
+
function checkChartEncoding(entry, value) {
|
|
29
|
+
const encodingColumns = entry.encodingColumns;
|
|
30
|
+
if (encodingColumns === undefined || encodingColumns.length === 0) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
if (value.type !== "table") {
|
|
34
|
+
return { reference: entry.reference, reason: "locator-out-of-range", detail: `the chart binding resolved to a ${value.type} and not to a table` };
|
|
35
|
+
}
|
|
36
|
+
const absent = columnsHeldByNoRow(value.rows, encodingColumns);
|
|
37
|
+
if (absent.length === 0) {
|
|
38
|
+
return undefined;
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
reference: entry.reference,
|
|
42
|
+
reason: "locator-out-of-range",
|
|
43
|
+
detail: `the encoding names column ${absent.join(", ")}, which the bound table does not hold`,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
/** Resolve each reference, collect the schema conformance and the resolution outcomes, and warn on prose. */
|
|
47
|
+
export async function validateReport(document, snapshot, resolver) {
|
|
48
|
+
const parsed = ReportDocumentSchema.safeParse(document);
|
|
49
|
+
if (!parsed.success) {
|
|
50
|
+
const schemaIssues = parsed.error.issues.map((issue) => ({
|
|
51
|
+
path: issue.path.map((segment) => String(segment)).join("."),
|
|
52
|
+
message: issue.message,
|
|
53
|
+
}));
|
|
54
|
+
return { valid: false, schemaIssues, warnings: [] };
|
|
55
|
+
}
|
|
56
|
+
const { references, repeatedIds, warnings } = walkBlocks(parsed.data.sections);
|
|
57
|
+
const resolved = await allWithConcurrency(references.map((entry) => () => resolver.resolve(entry.reference, snapshot).then((result) => ({ entry, result }))), RESOLUTION_CONCURRENCY);
|
|
58
|
+
// Collect every failure. A reviewer must see each block that broke, thus the walk does not stop at
|
|
59
|
+
// the first unresolved reference. The `Err` channel carries the unresolved reference itself.
|
|
60
|
+
const resolutionFailures = [];
|
|
61
|
+
for (const { entry, result } of resolved) {
|
|
62
|
+
if (result.isErr()) {
|
|
63
|
+
resolutionFailures.push({ blockId: entry.blockId, failure: result.error });
|
|
64
|
+
continue;
|
|
65
|
+
}
|
|
66
|
+
const encodingFailure = checkChartEncoding(entry, result.value);
|
|
67
|
+
if (encodingFailure !== undefined) {
|
|
68
|
+
resolutionFailures.push({ blockId: entry.blockId, failure: encodingFailure });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
if (repeatedIds.length > 0 || resolutionFailures.length > 0) {
|
|
72
|
+
return {
|
|
73
|
+
valid: false,
|
|
74
|
+
...(repeatedIds.length > 0 ? { duplicateIds: repeatedIds } : {}),
|
|
75
|
+
...(resolutionFailures.length > 0 ? { resolutionFailures } : {}),
|
|
76
|
+
warnings,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
return { valid: true, warnings };
|
|
80
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The chart derivation of the report page.
|
|
3
|
+
*
|
|
4
|
+
* A chart block binds one whole-table artifact and a channel mapping. `deriveChartOption` turns the
|
|
5
|
+
* chart type, the encoding, and the resolved rows into one ECharts option object. The chart container
|
|
6
|
+
* markup lives beside this file, and it wraps the option that this derivation gives.
|
|
7
|
+
*
|
|
8
|
+
* The renderer computes no aggregate. Each plotted number stays traceable to one cell of the evidence
|
|
9
|
+
* artifact. A pie or a heatmap arrives one row per category or per cell, thus a repeated category or a
|
|
10
|
+
* repeated pair is a refusal and not a silent sum.
|
|
11
|
+
*
|
|
12
|
+
* The derivation is deterministic. Every object builds in one fixed key order, and the code reads no
|
|
13
|
+
* clock, no random value, and no locale. A string comparison uses the code-unit order (`<`) and never
|
|
14
|
+
* `localeCompare`, thus the same rows give the same bytes on every host.
|
|
15
|
+
*/
|
|
16
|
+
import { type Result } from "neverthrow";
|
|
17
|
+
import type { ChartBlock } from "../contracts/report-blocks.js";
|
|
18
|
+
import type { RenderProblem } from "./types.js";
|
|
19
|
+
/** One cell of a resolved row. A cell is one string or one number. */
|
|
20
|
+
type Cell = string | number;
|
|
21
|
+
/** One resolved row of the bound table, keyed by column name. */
|
|
22
|
+
export type ChartRow = Record<string, Cell>;
|
|
23
|
+
/** The derived option object, ready for `normalizeEchartSpec` and the inline JSON. */
|
|
24
|
+
export type EchartOption = Record<string, unknown>;
|
|
25
|
+
/**
|
|
26
|
+
* Derive the ECharts option for a chart block, then pass it through `normalizeEchartSpec`.
|
|
27
|
+
*
|
|
28
|
+
* The normalizer owns the bottom legend, the save action, and the axis-label discipline. Thus the
|
|
29
|
+
* derivation sets no `title`, no `legend`, no `toolbox`, and no explicit series color. The theme palette
|
|
30
|
+
* assigns the series colors by their order.
|
|
31
|
+
*/
|
|
32
|
+
export declare function deriveChartOption(block: ChartBlock, rows: readonly ChartRow[], columns?: readonly string[]): Result<EchartOption, RenderProblem>;
|
|
33
|
+
export {};
|