@inflexa-ai/harness 0.20.0 → 0.21.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/conversation-agent.d.ts +26 -14
- package/dist/agents/conversation-agent.js +12 -23
- package/dist/agents/report-session-agent.d.ts +31 -5
- package/dist/agents/report-session-agent.js +31 -6
- package/dist/app/chat-turn.js +3 -1
- package/dist/app/message-assembly.d.ts +15 -1
- package/dist/app/message-assembly.js +22 -8
- package/dist/app/report-session-runtime.d.ts +21 -4
- package/dist/app/report-session-runtime.js +53 -6
- package/dist/app/spawn-report-session.d.ts +174 -17
- package/dist/app/spawn-report-session.js +233 -21
- package/dist/contracts/chat-events.d.ts +7 -2
- package/dist/contracts/message.d.ts +10 -0
- package/dist/contracts/report-blocks.d.ts +743 -19
- package/dist/contracts/report-blocks.js +186 -11
- package/dist/contracts/report-reference.d.ts +88 -0
- package/dist/contracts/report-reference.js +48 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.js +20 -0
- package/dist/lib/chrome.d.ts +26 -6
- package/dist/lib/chrome.js +99 -32
- package/dist/lib/eyes.d.ts +57 -0
- package/dist/lib/eyes.js +45 -0
- package/dist/lib/page-capture.d.ts +14 -0
- package/dist/lib/page-capture.js +56 -2
- package/dist/loop/run-agent.js +58 -23
- package/dist/loop/tool-detail.d.ts +19 -2
- package/dist/loop/tool-detail.js +35 -2
- package/dist/memory/content-to-cortex.d.ts +3 -2
- package/dist/memory/content-to-cortex.js +21 -12
- package/dist/memory/conversation-display-replay.js +14 -7
- package/dist/memory/thread-history.d.ts +60 -3
- package/dist/memory/thread-history.js +55 -16
- package/dist/memory/thread-store.d.ts +11 -1
- package/dist/memory/thread-store.js +12 -2
- package/dist/prompts/conversation.d.ts +1 -1
- package/dist/prompts/conversation.js +23 -6
- package/dist/prompts/report-session.d.ts +1 -1
- package/dist/prompts/report-session.js +183 -9
- package/dist/report-model/authoring-grammar.d.ts +463 -0
- package/dist/report-model/authoring-grammar.js +77 -0
- package/dist/report-model/block-walk.d.ts +57 -14
- package/dist/report-model/block-walk.js +116 -11
- package/dist/report-model/draft-finish.d.ts +22 -4
- package/dist/report-model/draft-finish.js +39 -7
- package/dist/report-model/draft-operations.d.ts +3 -0
- package/dist/report-model/draft-operations.js +19 -5
- package/dist/report-model/draft-read.d.ts +8 -5
- package/dist/report-model/draft-read.js +21 -5
- package/dist/report-model/fixture-resolver.js +15 -4
- package/dist/report-model/pin-snapshot.d.ts +24 -6
- package/dist/report-model/pin-snapshot.js +204 -6
- package/dist/report-model/production-resolver.js +15 -4
- package/dist/report-model/reference-resolver.d.ts +50 -1
- package/dist/report-model/reference-resolver.js +94 -1
- package/dist/report-model/reference-stamp.d.ts +45 -0
- package/dist/report-model/reference-stamp.js +173 -0
- package/dist/report-model/structural-validation.d.ts +4 -1
- package/dist/report-model/structural-validation.js +48 -3
- package/dist/report-model/validate.d.ts +1 -1
- package/dist/report-model/validate.js +1 -1
- package/dist/report-render/asset-lookup.d.ts +19 -0
- package/dist/report-render/asset-lookup.js +37 -0
- package/dist/report-render/assets.d.ts +45 -1
- package/dist/report-render/assets.js +65 -6
- package/dist/report-render/chart-presets.d.ts +102 -0
- package/dist/report-render/chart-presets.js +205 -0
- package/dist/report-render/chart.d.ts +112 -10
- package/dist/report-render/chart.js +1164 -41
- package/dist/report-render/design.d.ts +99 -0
- package/dist/report-render/design.js +289 -107
- package/dist/report-render/number-format.d.ts +117 -0
- package/dist/report-render/number-format.js +479 -0
- package/dist/report-render/page.d.ts +121 -5
- package/dist/report-render/page.js +689 -7
- package/dist/report-render/references.d.ts +70 -16
- package/dist/report-render/references.js +54 -22
- package/dist/report-render/render.d.ts +23 -5
- package/dist/report-render/render.js +81 -22
- package/dist/report-render/table-data.d.ts +92 -0
- package/dist/report-render/table-data.js +129 -0
- package/dist/report-render/types.d.ts +15 -0
- package/dist/report-render/value-bridge.js +6 -2
- package/dist/report-render/views/chart-view.d.ts +12 -5
- package/dist/report-render/views/chart-view.js +14 -8
- package/dist/report-render/views/page-view.d.ts +36 -15
- package/dist/report-render/views/page-view.js +45 -23
- package/dist/report-render/views/prose.d.ts +12 -2
- package/dist/report-render/views/prose.js +29 -7
- package/dist/report-render/views/references-view.d.ts +22 -8
- package/dist/report-render/views/references-view.js +101 -13
- package/dist/report-render/views/values.d.ts +128 -13
- package/dist/report-render/views/values.js +199 -18
- package/dist/runtime/assemble.d.ts +39 -5
- package/dist/runtime/assemble.js +47 -6
- package/dist/sandbox/client.d.ts +10 -1
- package/dist/sandbox/create-sandbox.d.ts +9 -4
- package/dist/sandbox/create-sandbox.js +22 -16
- package/dist/sandbox/docker-client.js +10 -7
- package/dist/sandbox/mount-plan.d.ts +45 -7
- package/dist/sandbox/mount-plan.js +43 -5
- package/dist/sandbox/types.d.ts +6 -0
- package/dist/state/init.js +38 -9
- package/dist/state/report-session-state.d.ts +42 -1
- package/dist/state/report-session-state.js +70 -2
- package/dist/state/report-versions.d.ts +22 -17
- package/dist/state/report-versions.js +48 -35
- package/dist/state/snapshot-parse.js +16 -1
- package/dist/tools/define-tool.d.ts +32 -0
- package/dist/tools/define-tool.js +4 -0
- package/dist/tools/detail-resolver.d.ts +7 -2
- package/dist/tools/detail-resolver.js +7 -2
- package/dist/tools/iterate-report.d.ts +1 -1
- package/dist/tools/report/preview-snapshot.js +1 -0
- package/dist/tools/report-authoring/authoring-tools.d.ts +15 -6
- package/dist/tools/report-authoring/authoring-tools.js +99 -18
- package/dist/tools/report-session/derive-table.d.ts +162 -0
- package/dist/tools/report-session/derive-table.js +459 -0
- package/dist/tools/report-session/examine-page.d.ts +48 -18
- package/dist/tools/report-session/examine-page.js +204 -24
- package/dist/tools/report-session/index.d.ts +2 -0
- package/dist/tools/report-session/index.js +2 -0
- package/dist/tools/report-session/list-artifacts.d.ts +97 -0
- package/dist/tools/report-session/list-artifacts.js +197 -0
- package/dist/tools/report-session/preview-report.d.ts +6 -0
- package/dist/tools/report-session/preview-report.js +180 -28
- package/dist/tools/report-session/record-version.d.ts +22 -10
- package/dist/tools/report-session/record-version.js +99 -14
- package/dist/tools/start-report-session.d.ts +115 -0
- package/dist/tools/start-report-session.js +179 -0
- package/dist/workspace/paths.d.ts +50 -0
- package/dist/workspace/paths.js +63 -0
- package/package.json +2 -1
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The one walk over a block tree.
|
|
3
3
|
*
|
|
4
|
-
* A block kind decides where a reference sits, which
|
|
5
|
-
* holds children. Three consumers need that same knowledge: the mechanical
|
|
6
|
-
* and the draft operations. A switch for each consumer is a switch that a
|
|
7
|
-
* silence, because each arm returns and none of them fails to build. Thus the
|
|
4
|
+
* A block kind decides where a reference sits, which text carries a free numeral or a drifted exponent
|
|
5
|
+
* form, and whether the block holds children. Three consumers need that same knowledge: the mechanical
|
|
6
|
+
* validator, the draft finish, and the draft operations. A switch for each consumer is a switch that a
|
|
7
|
+
* ninth block kind can miss in silence, because each arm returns and none of them fails to build. Thus the
|
|
8
|
+
* walk lives here one time.
|
|
8
9
|
*
|
|
9
10
|
* The walk reads a finished `Block` tree and a relaxed `DraftBlock` tree alike. The two differ only in the
|
|
10
11
|
* section rules, and the section arm reads the children of either one.
|
|
@@ -12,6 +13,8 @@
|
|
|
12
13
|
* The walk resolves nothing and it opens no file. It reports what the tree holds, and a caller decides
|
|
13
14
|
* which part it needs.
|
|
14
15
|
*/
|
|
16
|
+
import { channelColumn } from "../contracts/report-blocks.js";
|
|
17
|
+
import { holdsADriftedExponent } from "../report-render/number-format.js";
|
|
15
18
|
/**
|
|
16
19
|
* A free-standing token that looks like a number, with an optional decimal part and an optional percent
|
|
17
20
|
* sign.
|
|
@@ -25,6 +28,13 @@
|
|
|
25
28
|
* the report.
|
|
26
29
|
*/
|
|
27
30
|
const NUMERAL_PATTERN = /(?<![A-Za-z0-9])-?\d+(?:\.\d+)?%?/g;
|
|
31
|
+
/**
|
|
32
|
+
* A free-standing token that states a number in an exponent form, for example `4.3e-05`.
|
|
33
|
+
*
|
|
34
|
+
* The left boundary is the one of the numeral scan, thus a token inside a name never matches. The exponent
|
|
35
|
+
* itself carries an optional sign, because a written form gives one and a shown form never does.
|
|
36
|
+
*/
|
|
37
|
+
const EXPONENT_PATTERN = /(?<![A-Za-z0-9])-?\d+(?:\.\d+)?[eE][+-]?\d+/g;
|
|
28
38
|
/** Warn for each free-standing numeral that the prose of one block carries. */
|
|
29
39
|
export function numeralWarnings(blockId, prose) {
|
|
30
40
|
const warnings = [];
|
|
@@ -33,6 +43,73 @@ export function numeralWarnings(blockId, prose) {
|
|
|
33
43
|
}
|
|
34
44
|
return warnings;
|
|
35
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Warn for each exponent form of one block that the page never prints.
|
|
48
|
+
*
|
|
49
|
+
* The page owns the notation of a value. Thus a sentence that writes a second notation of the same number
|
|
50
|
+
* disagrees with the card beside it. The renderer answers which forms it prints, thus this scan states no
|
|
51
|
+
* rule of its own.
|
|
52
|
+
*
|
|
53
|
+
* The check is advisory, exactly as the numeral scan is. A prose exponent can be a quoted threshold that no
|
|
54
|
+
* cell carries, thus a hit warns and it never fails the report.
|
|
55
|
+
*/
|
|
56
|
+
export function exponentWarnings(blockId, prose) {
|
|
57
|
+
const warnings = [];
|
|
58
|
+
for (const match of prose.matchAll(EXPONENT_PATTERN)) {
|
|
59
|
+
if (holdsADriftedExponent(match[0])) {
|
|
60
|
+
warnings.push({ blockId, kind: "exponent-form", detail: match[0] });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return warnings;
|
|
64
|
+
}
|
|
65
|
+
/** Each advisory warning that the prose of one block carries. */
|
|
66
|
+
function proseWarnings(blockId, prose) {
|
|
67
|
+
return [...numeralWarnings(blockId, prose), ...exponentWarnings(blockId, prose)];
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Each column that the grammar of one chart names.
|
|
71
|
+
*
|
|
72
|
+
* The quick path names its four channels and its label. A composition names the channels of each series,
|
|
73
|
+
* the lower bound of a band, the label of each series, and the column of each rank rule. A transform rides
|
|
74
|
+
* beside its column, thus a transformed channel names the same column as a plain one.
|
|
75
|
+
*
|
|
76
|
+
* A name comes back one time, in the order that the grammar states it. Thus a refusal names each absent
|
|
77
|
+
* column one time.
|
|
78
|
+
*/
|
|
79
|
+
function chartColumns(block) {
|
|
80
|
+
const columns = [];
|
|
81
|
+
const add = (column) => {
|
|
82
|
+
if (column !== undefined && !columns.includes(column))
|
|
83
|
+
columns.push(column);
|
|
84
|
+
};
|
|
85
|
+
const addChannel = (channel) => {
|
|
86
|
+
if (channel !== undefined)
|
|
87
|
+
add(channelColumn(channel));
|
|
88
|
+
};
|
|
89
|
+
const encoding = block.encoding;
|
|
90
|
+
if (encoding !== undefined) {
|
|
91
|
+
addChannel(encoding.x);
|
|
92
|
+
addChannel(encoding.y);
|
|
93
|
+
addChannel(encoding.group);
|
|
94
|
+
addChannel(encoding.value);
|
|
95
|
+
add(encoding.label);
|
|
96
|
+
}
|
|
97
|
+
const composition = block.composition;
|
|
98
|
+
if (composition !== undefined) {
|
|
99
|
+
for (const series of composition.series) {
|
|
100
|
+
addChannel(series.encoding.x);
|
|
101
|
+
addChannel(series.encoding.y);
|
|
102
|
+
addChannel(series.encoding.y0);
|
|
103
|
+
addChannel(series.encoding.group);
|
|
104
|
+
add(series.encoding.label);
|
|
105
|
+
}
|
|
106
|
+
for (const annotation of composition.annotations ?? []) {
|
|
107
|
+
if (annotation.kind === "point-labels")
|
|
108
|
+
add(annotation.column);
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
return columns;
|
|
112
|
+
}
|
|
36
113
|
/**
|
|
37
114
|
* Walk a block tree, and report each reference, each repeated id, and each free numeral.
|
|
38
115
|
*
|
|
@@ -58,10 +135,15 @@ export function walkBlocks(blocks) {
|
|
|
58
135
|
}
|
|
59
136
|
return;
|
|
60
137
|
case "text":
|
|
61
|
-
warnings.push(...
|
|
138
|
+
warnings.push(...proseWarnings(block.id, block.content.prose));
|
|
139
|
+
// An item of a list states a point, the same as a sentence of the prose. Thus a free
|
|
140
|
+
// figure inside an item carries the same honesty concern, and the walk reads both.
|
|
141
|
+
for (const item of block.content.list?.items ?? []) {
|
|
142
|
+
warnings.push(...proseWarnings(block.id, item));
|
|
143
|
+
}
|
|
62
144
|
return;
|
|
63
145
|
case "claim":
|
|
64
|
-
warnings.push(...
|
|
146
|
+
warnings.push(...proseWarnings(block.id, block.content.prose));
|
|
65
147
|
for (const binding of block.bindings) {
|
|
66
148
|
references.push({ blockId: block.id, reference: binding });
|
|
67
149
|
}
|
|
@@ -69,12 +151,9 @@ export function walkBlocks(blocks) {
|
|
|
69
151
|
case "metric":
|
|
70
152
|
references.push({ blockId: block.id, reference: block.value });
|
|
71
153
|
return;
|
|
72
|
-
case "chart":
|
|
73
|
-
|
|
74
|
-
const encodingColumns = [encoding.x, encoding.y, encoding.group, encoding.value].filter((column) => column !== undefined);
|
|
75
|
-
references.push({ blockId: block.id, reference: block.binding, encodingColumns });
|
|
154
|
+
case "chart":
|
|
155
|
+
references.push({ blockId: block.id, reference: block.binding, encodingColumns: chartColumns(block) });
|
|
76
156
|
return;
|
|
77
|
-
}
|
|
78
157
|
case "table":
|
|
79
158
|
case "figure":
|
|
80
159
|
case "citation":
|
|
@@ -87,3 +166,29 @@ export function walkBlocks(blocks) {
|
|
|
87
166
|
}
|
|
88
167
|
return { references, repeatedIds: [...repeated].sort(), warnings };
|
|
89
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Each artifact path that a set of collected references names.
|
|
171
|
+
*
|
|
172
|
+
* An artifact binding names one path. A derivation reference computes over two inputs, thus it names the
|
|
173
|
+
* path of each input and a table that feeds a derived scalar counts as named. A citation names no path.
|
|
174
|
+
*
|
|
175
|
+
* The set answers one question: which files does this document use. The finish reads it against the
|
|
176
|
+
* derivations of the session, and the record reads it again over the recorded document.
|
|
177
|
+
*/
|
|
178
|
+
export function referencedPaths(references) {
|
|
179
|
+
const paths = new Set();
|
|
180
|
+
for (const entry of references) {
|
|
181
|
+
const reference = entry.reference;
|
|
182
|
+
if (reference.kind === "citation") {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (reference.kind === "derivation") {
|
|
186
|
+
for (const input of reference.inputs) {
|
|
187
|
+
paths.add(input.path);
|
|
188
|
+
}
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
paths.add(reference.path);
|
|
192
|
+
}
|
|
193
|
+
return paths;
|
|
194
|
+
}
|
|
@@ -5,14 +5,23 @@
|
|
|
5
5
|
* draft one time: the full document schema, the unique ids, and the structural tier over every reference.
|
|
6
6
|
*
|
|
7
7
|
* The finish opens no file, and it reads the snapshot alone. Thus it runs the structural tier only, and
|
|
8
|
-
* the value tier stays in the gate of the report pipeline. The
|
|
9
|
-
*
|
|
8
|
+
* the value tier stays in the gate of the report pipeline. The structural tier matches the columns of a
|
|
9
|
+
* chart wherever the snapshot can answer, and the value tier matches them again over the rows that it
|
|
10
|
+
* reads.
|
|
10
11
|
*
|
|
11
12
|
* The free-numeral scan needs no file, thus the finish runs it and reports each hit as a warning. A
|
|
12
13
|
* warning never makes a draft invalid, and it rides beside the outcome in either case. Without the
|
|
13
14
|
* channel a caller could not read the one signal that catches a figure typed into prose with no metric
|
|
14
15
|
* block behind it.
|
|
15
16
|
*
|
|
17
|
+
* The exponent-form scan rides the same channel. A sentence can state a number in a notation that the page
|
|
18
|
+
* never prints, and such a sentence disagrees with the card beside it. Thus the prose notation and the page
|
|
19
|
+
* notation cannot drift in silence.
|
|
20
|
+
*
|
|
21
|
+
* The unused-derivation scan rides the same channel. The records and the draft both sit in memory, thus the
|
|
22
|
+
* scan opens no file either. A derivation whose output path no binding names warns, and the derived bytes
|
|
23
|
+
* stay until the record prunes them.
|
|
24
|
+
*
|
|
16
25
|
* The result is plain data. A gap is an expected outcome, not an error, thus the finish never throws. On
|
|
17
26
|
* a pass the finish gives the valid document, and it does not change the draft.
|
|
18
27
|
*/
|
|
@@ -53,8 +62,17 @@ export type FinishResult = {
|
|
|
53
62
|
gaps: FinishGap[];
|
|
54
63
|
warnings: ReportWarning[];
|
|
55
64
|
};
|
|
65
|
+
/**
|
|
66
|
+
* One derivation of the session, as the finish reads it. The finish needs the output path alone, because
|
|
67
|
+
* the used set is the paths that the bindings name.
|
|
68
|
+
*/
|
|
69
|
+
export interface SessionDerivation {
|
|
70
|
+
readonly outputPath: string;
|
|
71
|
+
}
|
|
56
72
|
/**
|
|
57
73
|
* Finish a draft. The finish validates the schema, the unique ids, and the structural resolution of each
|
|
58
|
-
* reference, and it reports each gap as data. It warns about each free numeral in prose
|
|
74
|
+
* reference, and it reports each gap as data. It warns about each free numeral in prose, and about each
|
|
75
|
+
* exponent form that the page never prints. It warns about each derivation of the session that no binding
|
|
76
|
+
* names.
|
|
59
77
|
*/
|
|
60
|
-
export declare function finishDraft(draft: DraftDocument, snapshot: ReportSnapshot): FinishResult;
|
|
78
|
+
export declare function finishDraft(draft: DraftDocument, snapshot: ReportSnapshot, derivations?: readonly SessionDerivation[]): FinishResult;
|
|
@@ -5,25 +5,56 @@
|
|
|
5
5
|
* draft one time: the full document schema, the unique ids, and the structural tier over every reference.
|
|
6
6
|
*
|
|
7
7
|
* The finish opens no file, and it reads the snapshot alone. Thus it runs the structural tier only, and
|
|
8
|
-
* the value tier stays in the gate of the report pipeline. The
|
|
9
|
-
*
|
|
8
|
+
* the value tier stays in the gate of the report pipeline. The structural tier matches the columns of a
|
|
9
|
+
* chart wherever the snapshot can answer, and the value tier matches them again over the rows that it
|
|
10
|
+
* reads.
|
|
10
11
|
*
|
|
11
12
|
* The free-numeral scan needs no file, thus the finish runs it and reports each hit as a warning. A
|
|
12
13
|
* warning never makes a draft invalid, and it rides beside the outcome in either case. Without the
|
|
13
14
|
* channel a caller could not read the one signal that catches a figure typed into prose with no metric
|
|
14
15
|
* block behind it.
|
|
15
16
|
*
|
|
17
|
+
* The exponent-form scan rides the same channel. A sentence can state a number in a notation that the page
|
|
18
|
+
* never prints, and such a sentence disagrees with the card beside it. Thus the prose notation and the page
|
|
19
|
+
* notation cannot drift in silence.
|
|
20
|
+
*
|
|
21
|
+
* The unused-derivation scan rides the same channel. The records and the draft both sit in memory, thus the
|
|
22
|
+
* scan opens no file either. A derivation whose output path no binding names warns, and the derived bytes
|
|
23
|
+
* stay until the record prunes them.
|
|
24
|
+
*
|
|
16
25
|
* The result is plain data. A gap is an expected outcome, not an error, thus the finish never throws. On
|
|
17
26
|
* a pass the finish gives the valid document, and it does not change the draft.
|
|
18
27
|
*/
|
|
19
28
|
import { ReportDocumentSchema } from "../contracts/report-blocks.js";
|
|
20
|
-
import { walkBlocks } from "./block-walk.js";
|
|
29
|
+
import { referencedPaths, walkBlocks } from "./block-walk.js";
|
|
21
30
|
import { validateReferenceStructure } from "./structural-validation.js";
|
|
31
|
+
/**
|
|
32
|
+
* Warn for each derivation that no binding of the draft names.
|
|
33
|
+
*
|
|
34
|
+
* A derivation is used when a binding names its output path. Thus the scan is a set difference over the
|
|
35
|
+
* records and the collected references, and it reads no file. A warning names the output path, and it
|
|
36
|
+
* decides no outcome.
|
|
37
|
+
*/
|
|
38
|
+
function unusedDerivationWarnings(derivations, references) {
|
|
39
|
+
if (derivations.length === 0) {
|
|
40
|
+
return [];
|
|
41
|
+
}
|
|
42
|
+
const named = referencedPaths(references);
|
|
43
|
+
const warnings = [];
|
|
44
|
+
for (const record of derivations) {
|
|
45
|
+
if (!named.has(record.outputPath)) {
|
|
46
|
+
warnings.push({ kind: "unused-derivation", detail: record.outputPath });
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
return warnings;
|
|
50
|
+
}
|
|
22
51
|
/**
|
|
23
52
|
* Finish a draft. The finish validates the schema, the unique ids, and the structural resolution of each
|
|
24
|
-
* reference, and it reports each gap as data. It warns about each free numeral in prose
|
|
53
|
+
* reference, and it reports each gap as data. It warns about each free numeral in prose, and about each
|
|
54
|
+
* exponent form that the page never prints. It warns about each derivation of the session that no binding
|
|
55
|
+
* names.
|
|
25
56
|
*/
|
|
26
|
-
export function finishDraft(draft, snapshot) {
|
|
57
|
+
export function finishDraft(draft, snapshot, derivations = []) {
|
|
27
58
|
const gaps = [];
|
|
28
59
|
const parsed = ReportDocumentSchema.safeParse(draft);
|
|
29
60
|
if (!parsed.success) {
|
|
@@ -37,12 +68,13 @@ export function finishDraft(draft, snapshot) {
|
|
|
37
68
|
}
|
|
38
69
|
// The walk reads the draft tree, not the parse result. The draft is already typed, thus the walk stays
|
|
39
70
|
// well-defined even when the schema parse fails, and a caller still gets the id and reference gaps.
|
|
40
|
-
const { references, repeatedIds, warnings } = walkBlocks(draft.sections);
|
|
71
|
+
const { references, repeatedIds, warnings: proseWarnings } = walkBlocks(draft.sections);
|
|
72
|
+
const warnings = [...proseWarnings, ...unusedDerivationWarnings(derivations, references)];
|
|
41
73
|
for (const id of repeatedIds) {
|
|
42
74
|
gaps.push({ kind: "duplicate-id", id });
|
|
43
75
|
}
|
|
44
76
|
for (const entry of references) {
|
|
45
|
-
const result = validateReferenceStructure(entry.reference, snapshot);
|
|
77
|
+
const result = validateReferenceStructure(entry.reference, snapshot, entry.encodingColumns);
|
|
46
78
|
if (result.isErr()) {
|
|
47
79
|
gaps.push({ kind: "unresolved-reference", blockId: entry.blockId, failure: result.error });
|
|
48
80
|
}
|
|
@@ -88,6 +88,9 @@ export declare function setTitle(draft: DraftDocument, title: string): DraftDocu
|
|
|
88
88
|
*
|
|
89
89
|
* The root admits a section only, thus an atom at the root refuses. The validation covers the payload and
|
|
90
90
|
* each of its descendants.
|
|
91
|
+
*
|
|
92
|
+
* The stamp runs before the parse. Thus a reference that names a path of the snapshot and no hash carries
|
|
93
|
+
* the hash of that path when the grammar reads it.
|
|
91
94
|
*/
|
|
92
95
|
export declare function addBlock(draft: DraftDocument, operation: AddOperation, snapshot: ReportSnapshot): Result<DraftDocument, DraftRefusal>;
|
|
93
96
|
/**
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
import { err, ok } from "neverthrow";
|
|
19
19
|
import { walkBlocks } from "./block-walk.js";
|
|
20
20
|
import { DraftBlockSchema } from "./draft.js";
|
|
21
|
+
import { stampReferenceHashes } from "./reference-stamp.js";
|
|
21
22
|
import { validateReferenceStructure } from "./structural-validation.js";
|
|
22
23
|
/**
|
|
23
24
|
* Set the title of the document.
|
|
@@ -33,9 +34,16 @@ export function setTitle(draft, title) {
|
|
|
33
34
|
*
|
|
34
35
|
* The root admits a section only, thus an atom at the root refuses. The validation covers the payload and
|
|
35
36
|
* each of its descendants.
|
|
37
|
+
*
|
|
38
|
+
* The stamp runs before the parse. Thus a reference that names a path of the snapshot and no hash carries
|
|
39
|
+
* the hash of that path when the grammar reads it.
|
|
36
40
|
*/
|
|
37
41
|
export function addBlock(draft, operation, snapshot) {
|
|
38
|
-
const
|
|
42
|
+
const stamped = stampReferenceHashes(operation.block, snapshot);
|
|
43
|
+
if (stamped.isErr()) {
|
|
44
|
+
return err(stamped.error);
|
|
45
|
+
}
|
|
46
|
+
const parsed = DraftBlockSchema.safeParse(stamped.value);
|
|
39
47
|
if (!parsed.success) {
|
|
40
48
|
return err(malformed(parsed.error));
|
|
41
49
|
}
|
|
@@ -75,10 +83,16 @@ export function changeBlock(draft, operation, snapshot) {
|
|
|
75
83
|
if (payload === null || typeof payload !== "object") {
|
|
76
84
|
return err({ reason: "malformed-block", detail: "the payload is not an object" });
|
|
77
85
|
}
|
|
78
|
-
// The stamp sets the target id on the payload before the parse. Thus an id mismatch is
|
|
86
|
+
// The id stamp sets the target id on the payload before the parse. Thus an id mismatch is
|
|
79
87
|
// unrepresentable, and a replacement always keeps the id of its target.
|
|
80
|
-
const
|
|
81
|
-
|
|
88
|
+
const withId = { ...payload, id: operation.targetId };
|
|
89
|
+
// The hash stamp then fills each absent reference hash from the snapshot, thus the grammar reads a
|
|
90
|
+
// complete pin. The two stamps are independent, because one reads the block and one reads a reference.
|
|
91
|
+
const hashed = stampReferenceHashes(withId, snapshot);
|
|
92
|
+
if (hashed.isErr()) {
|
|
93
|
+
return err(hashed.error);
|
|
94
|
+
}
|
|
95
|
+
const parsed = DraftBlockSchema.safeParse(hashed.value);
|
|
82
96
|
if (!parsed.success) {
|
|
83
97
|
return err(malformed(parsed.error));
|
|
84
98
|
}
|
|
@@ -203,7 +217,7 @@ function commit(candidate, added, snapshot) {
|
|
|
203
217
|
}
|
|
204
218
|
const unresolved = [];
|
|
205
219
|
for (const entry of walkBlocks([added]).references) {
|
|
206
|
-
validateReferenceStructure(entry.reference, snapshot).match(() => { }, (failure) => {
|
|
220
|
+
validateReferenceStructure(entry.reference, snapshot, entry.encodingColumns).match(() => { }, (failure) => {
|
|
207
221
|
unresolved.push(failure);
|
|
208
222
|
});
|
|
209
223
|
}
|
|
@@ -9,8 +9,11 @@
|
|
|
9
9
|
* section gives its own fields and the ids of its children, and never the subtree. The outline already
|
|
10
10
|
* names every descendant, thus a subtree here would return the whole tree that the outline exists to keep
|
|
11
11
|
* out.
|
|
12
|
+
*
|
|
13
|
+
* A read gives each binding with no hash. The pinned snapshot owns that value, thus an echoed hash would
|
|
14
|
+
* give the agent a value to mistype.
|
|
12
15
|
*/
|
|
13
|
-
import type {
|
|
16
|
+
import type { AuthoringAtomBlock } from "./authoring-grammar.js";
|
|
14
17
|
import type { DraftBlock, DraftDocument } from "./draft.js";
|
|
15
18
|
/**
|
|
16
19
|
* One outline entry. It carries the id, the kind, the nesting depth, and a short label. A top-level
|
|
@@ -35,8 +38,8 @@ export interface ShallowSection {
|
|
|
35
38
|
title: string;
|
|
36
39
|
childIds: string[];
|
|
37
40
|
}
|
|
38
|
-
/** One block as the read surface gives it. An atom reads in full, and a section reads shallow. */
|
|
39
|
-
export type ReadableBlock =
|
|
41
|
+
/** One block as the read surface gives it. An atom reads in full and hash-free, and a section reads shallow. */
|
|
42
|
+
export type ReadableBlock = AuthoringAtomBlock | ShallowSection;
|
|
40
43
|
/**
|
|
41
44
|
* Build the outline of a draft. The walk is pre-order, thus the entries follow the document order. A
|
|
42
45
|
* section comes before its children, and a child sits one depth deeper than its parent.
|
|
@@ -53,7 +56,7 @@ export declare function buildOutline(draft: DraftDocument): OutlineEntry[];
|
|
|
53
56
|
*/
|
|
54
57
|
export declare function childOutline(blocks: readonly DraftBlock[], depth: number): OutlineEntry[];
|
|
55
58
|
/**
|
|
56
|
-
* Read one block by its id. An atom gives the block
|
|
57
|
-
* fields and the id of each child. A block that no id holds gives `undefined`.
|
|
59
|
+
* Read one block by its id. An atom gives the block with its bindings, and each binding gives no hash. A
|
|
60
|
+
* section gives its own fields and the id of each child. A block that no id holds gives `undefined`.
|
|
58
61
|
*/
|
|
59
62
|
export declare function readBlock(draft: DraftDocument, id: string): ReadableBlock | undefined;
|
|
@@ -9,8 +9,12 @@
|
|
|
9
9
|
* section gives its own fields and the ids of its children, and never the subtree. The outline already
|
|
10
10
|
* names every descendant, thus a subtree here would return the whole tree that the outline exists to keep
|
|
11
11
|
* out.
|
|
12
|
+
*
|
|
13
|
+
* A read gives each binding with no hash. The pinned snapshot owns that value, thus an echoed hash would
|
|
14
|
+
* give the agent a value to mistype.
|
|
12
15
|
*/
|
|
13
16
|
import { locate } from "./draft-operations.js";
|
|
17
|
+
import { stripReferenceHashes } from "./reference-stamp.js";
|
|
14
18
|
/** The label keeps a prose head of at most this many code points. A clipped label ends with the marker. */
|
|
15
19
|
const PROSE_CLIP = 80;
|
|
16
20
|
/** The marker that a clipped label ends with. It counts toward `PROSE_CLIP`. */
|
|
@@ -40,6 +44,9 @@ function clip(text) {
|
|
|
40
44
|
* a note. A text and a claim carry prose. Every one of them clips: a caption, a note, and a title are free
|
|
41
45
|
* prose too, thus an unclipped arm would put a paragraph into each outline that names the block.
|
|
42
46
|
*
|
|
47
|
+
* A text block carries its enumeration in the list, and the lead sentences above it can be empty. Such a
|
|
48
|
+
* block gives the first item, thus the outline names it and the agent reads which block it is.
|
|
49
|
+
*
|
|
43
50
|
* A block with an absent optional field gives an empty label.
|
|
44
51
|
*/
|
|
45
52
|
function labelOf(block) {
|
|
@@ -56,8 +63,15 @@ function labelOf(block) {
|
|
|
56
63
|
return clip(block.caption ?? "");
|
|
57
64
|
case "citation":
|
|
58
65
|
return clip(block.note ?? "");
|
|
59
|
-
case "text":
|
|
60
|
-
|
|
66
|
+
case "text": {
|
|
67
|
+
const prose = block.content.prose;
|
|
68
|
+
if (prose.trim().length > 0) {
|
|
69
|
+
return clip(prose);
|
|
70
|
+
}
|
|
71
|
+
// An item passes the grammar with one space in it, thus the first item can name nothing. The
|
|
72
|
+
// walk takes the first item that holds text, and a list of blank items gives an empty label.
|
|
73
|
+
return clip(block.content.list?.items.find((item) => item.trim().length > 0) ?? "");
|
|
74
|
+
}
|
|
61
75
|
case "claim":
|
|
62
76
|
return clip(block.content.prose);
|
|
63
77
|
}
|
|
@@ -94,8 +108,8 @@ export function childOutline(blocks, depth) {
|
|
|
94
108
|
return blocks.map((block) => ({ id: block.id, kind: block.kind, depth, label: labelOf(block) }));
|
|
95
109
|
}
|
|
96
110
|
/**
|
|
97
|
-
* Read one block by its id. An atom gives the block
|
|
98
|
-
* fields and the id of each child. A block that no id holds gives `undefined`.
|
|
111
|
+
* Read one block by its id. An atom gives the block with its bindings, and each binding gives no hash. A
|
|
112
|
+
* section gives its own fields and the id of each child. A block that no id holds gives `undefined`.
|
|
99
113
|
*/
|
|
100
114
|
export function readBlock(draft, id) {
|
|
101
115
|
const found = locate(draft, id)?.block;
|
|
@@ -105,5 +119,7 @@ export function readBlock(draft, id) {
|
|
|
105
119
|
if (found.kind === "section") {
|
|
106
120
|
return { kind: "section", id: found.id, title: found.title, childIds: found.blocks.map((child) => child.id) };
|
|
107
121
|
}
|
|
108
|
-
|
|
122
|
+
// The stored atom and the read atom differ in the stamped hash alone, and the walk removes that field.
|
|
123
|
+
// Thus the assertion states what the walk did.
|
|
124
|
+
return stripReferenceHashes(found);
|
|
109
125
|
}
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
*/
|
|
6
6
|
import { err, ok } from "neverthrow";
|
|
7
7
|
import { asFiniteNumber, cellMatchesFilterValue, checkCitationAssertion, checkValueAssertion } from "./assert-rules.js";
|
|
8
|
-
import { columnsHeldByNoRow, fileTypeHoldsNoCell, snapshotEntry, } from "./reference-resolver.js";
|
|
8
|
+
import { applyRowBound, columnsHeldByNoRow, fileTypeHoldsNoCell, snapshotEntry, } from "./reference-resolver.js";
|
|
9
9
|
/** Build an `Err` that carries the unresolved reference. The `detail` key is present only when there is a detail to carry. */
|
|
10
10
|
function fail(reference, reason, detail) {
|
|
11
11
|
const failure = detail !== undefined ? { reference, reason, detail } : { reference, reason };
|
|
@@ -79,10 +79,21 @@ function resolveArtifactTable(reference, snapshot) {
|
|
|
79
79
|
if (fileTypeHoldsNoCell(tableFileType)) {
|
|
80
80
|
return fail(reference, "unreadable-artifact", `the ${tableFileType} at ${reference.path} holds no cell to read`);
|
|
81
81
|
}
|
|
82
|
-
|
|
82
|
+
// The bound ranks the rows as the artifact holds them, thus a bound over a column that the subset
|
|
83
|
+
// below leaves out still ranks them. An unknown bound column addresses nothing, exactly as an unknown
|
|
84
|
+
// subset column does.
|
|
85
|
+
const bound = reference.rowBound;
|
|
86
|
+
const allRows = artifact.rows ?? [];
|
|
87
|
+
if (bound !== undefined && columnsHeldByNoRow(allRows, [bound.column]).length > 0) {
|
|
88
|
+
return fail(reference, "locator-out-of-range", `the row bound names column ${bound.column}, which the table at ${reference.path} does not hold`);
|
|
89
|
+
}
|
|
90
|
+
const rows = bound === undefined ? allRows : applyRowBound(allRows, bound);
|
|
91
|
+
// The page states the shown count against the count of the artifact. This read is the one place that
|
|
92
|
+
// holds both, thus a bounded table carries the pre-bound total out with its rows.
|
|
93
|
+
const total = bound === undefined ? {} : { total: allRows.length };
|
|
83
94
|
const columns = reference.columns;
|
|
84
95
|
if (columns === undefined) {
|
|
85
|
-
return ok({ type: "table", rows });
|
|
96
|
+
return ok({ type: "table", rows, ...total });
|
|
86
97
|
}
|
|
87
98
|
// A name that no row holds addresses nothing. Without this check a projection onto an invented column
|
|
88
99
|
// gives an empty cell for each row and still resolves, which is the fabrication that grounding exists
|
|
@@ -102,7 +113,7 @@ function resolveArtifactTable(reference, snapshot) {
|
|
|
102
113
|
}
|
|
103
114
|
return projected;
|
|
104
115
|
});
|
|
105
|
-
return ok({ type: "table", rows: projectedRows, columns });
|
|
116
|
+
return ok({ type: "table", rows: projectedRows, columns, ...total });
|
|
106
117
|
}
|
|
107
118
|
function resolveArtifactFile(reference, snapshot) {
|
|
108
119
|
const artifact = snapshotEntry(snapshot, reference.path);
|
|
@@ -7,24 +7,42 @@
|
|
|
7
7
|
*
|
|
8
8
|
* The artifact ledger holds one row for each path, and it keeps no history. Thus the set at a past
|
|
9
9
|
* moment is not recoverable later, and the pin must run at the anchor itself.
|
|
10
|
+
*
|
|
11
|
+
* The snapshot carries the citation evidence beside the artifact map. The pinned set is the literature
|
|
12
|
+
* that the synthesis engaged, and two fields of a run synthesis carry it: the key references and the
|
|
13
|
+
* references of each finding. The records live in the run tree on disk. Thus the pin reads that tree
|
|
14
|
+
* through the workspace-root seam of the caller.
|
|
15
|
+
*
|
|
16
|
+
* A reference that carries a short citation also pins a bibliography record beside its key. The key list
|
|
17
|
+
* keeps the membership role, thus the record map is optional and it changes no resolution.
|
|
10
18
|
*/
|
|
11
19
|
import { type Result } from "neverthrow";
|
|
12
20
|
import type { Querier } from "../state/db.js";
|
|
21
|
+
import { type ResolveWorkspaceRoot } from "../workspace/paths.js";
|
|
13
22
|
import type { ReportSnapshot } from "./reference-resolver.js";
|
|
14
23
|
/**
|
|
15
|
-
* The reason that the pin gave no snapshot. The read
|
|
16
|
-
* fail. An analysis with no registered artifact
|
|
17
|
-
* this set. `cause` carries the underlying fault for a
|
|
24
|
+
* The reason that the pin gave no snapshot. The ledger read and the run listing are the two operations
|
|
25
|
+
* that can fail. An analysis with no registered artifact and an analysis with no synthesis on disk are
|
|
26
|
+
* normal answers, thus absence is not a member of this set. `cause` carries the underlying fault for a
|
|
27
|
+
* log.
|
|
18
28
|
*/
|
|
19
29
|
export type PinSnapshotError = {
|
|
20
|
-
kind: "ledger-read-failed";
|
|
30
|
+
kind: "ledger-read-failed" | "run-listing-failed";
|
|
21
31
|
cause: unknown;
|
|
22
32
|
};
|
|
33
|
+
/** The construction options of the pin. */
|
|
34
|
+
export interface PinReportSnapshotOptions {
|
|
35
|
+
/**
|
|
36
|
+
* The workspace-root seam of the caller. The citation evidence sits on disk, thus a composition
|
|
37
|
+
* that binds no seam pins the artifact map alone.
|
|
38
|
+
*/
|
|
39
|
+
readonly resolveWorkspaceRoot?: ResolveWorkspaceRoot;
|
|
40
|
+
}
|
|
23
41
|
/**
|
|
24
|
-
* Pin the snapshot of one analysis from the artifact ledger.
|
|
42
|
+
* Pin the snapshot of one analysis from the artifact ledger and from the synthesis records of its runs.
|
|
25
43
|
*
|
|
26
44
|
* The pin copies no cell and no byte. An entry pins identity alone, thus the snapshot grows with the
|
|
27
45
|
* count of artifacts and never with the size of the data. A read of an artifact belongs to the value
|
|
28
46
|
* tier, which runs one time for each report version.
|
|
29
47
|
*/
|
|
30
|
-
export declare function pinReportSnapshot(pool: Querier, analysisId: string): Promise<Result<ReportSnapshot, PinSnapshotError>>;
|
|
48
|
+
export declare function pinReportSnapshot(pool: Querier, analysisId: string, options?: PinReportSnapshotOptions): Promise<Result<ReportSnapshot, PinSnapshotError>>;
|