@inflexa-ai/harness 0.31.0 → 0.32.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 +7 -0
- package/dist/agents/conversation-agent.js +1 -0
- package/dist/agents/report-session-agent.d.ts +9 -0
- package/dist/agents/report-session-agent.js +10 -2
- package/dist/app/chat-turn.d.ts +11 -0
- package/dist/app/chat-turn.js +17 -1
- package/dist/app/spawn-report-session.d.ts +12 -0
- package/dist/app/spawn-report-session.js +21 -1
- package/dist/contracts/report-blocks.d.ts +8 -8
- package/dist/contracts/schemas/chat-parts.d.ts +6 -6
- package/dist/index.d.ts +4 -1
- package/dist/index.js +8 -0
- package/dist/prompts/briefing.d.ts +12 -1
- package/dist/prompts/briefing.js +27 -1
- package/dist/prompts/sandbox-standards.d.ts +1 -1
- package/dist/prompts/sandbox-standards.js +27 -0
- package/dist/provenance/seam.d.ts +203 -0
- package/dist/provenance/seam.js +71 -0
- package/dist/report-model/authoring-grammar.d.ts +2 -2
- package/dist/report-render/assets.d.ts +10 -0
- package/dist/report-render/assets.js +14 -0
- package/dist/report-render/design.js +256 -0
- package/dist/report-render/page.d.ts +75 -0
- package/dist/report-render/page.js +793 -1
- package/dist/report-render/provenance-data.d.ts +42 -0
- package/dist/report-render/provenance-data.js +62 -0
- package/dist/report-render/render.d.ts +20 -1
- package/dist/report-render/render.js +23 -10
- package/dist/report-render/types.d.ts +3 -0
- package/dist/report-render/views/chart-view.d.ts +5 -1
- package/dist/report-render/views/chart-view.js +6 -2
- package/dist/report-render/views/lineage.d.ts +93 -0
- package/dist/report-render/views/lineage.js +86 -0
- package/dist/report-render/views/page-view.d.ts +22 -1
- package/dist/report-render/views/page-view.js +12 -3
- package/dist/report-render/views/prose.d.ts +7 -1
- package/dist/report-render/views/prose.js +10 -6
- package/dist/report-render/views/references-view.d.ts +17 -1
- package/dist/report-render/views/references-view.js +12 -4
- package/dist/report-render/views/values.d.ts +17 -4
- package/dist/report-render/views/values.js +21 -8
- package/dist/runtime/assemble.d.ts +22 -0
- package/dist/runtime/assemble.js +4 -0
- package/dist/sandbox/k8s-client.d.ts +0 -5
- package/dist/sandbox/k8s-client.js +9 -115
- package/dist/sandbox/thread-env.d.ts +18 -12
- package/dist/sandbox/thread-env.js +36 -25
- package/dist/schemas/artifact-manifest.d.ts +4 -4
- package/dist/tools/display/show-user.d.ts +1 -1
- package/dist/tools/report-authoring/authoring-tools.d.ts +16 -1
- package/dist/tools/report-authoring/authoring-tools.js +58 -16
- package/dist/tools/report-session/derive-table.d.ts +3 -0
- package/dist/tools/report-session/derive-table.js +15 -0
- package/dist/tools/report-session/preview-report.d.ts +13 -4
- package/dist/tools/report-session/preview-report.js +23 -6
- package/dist/tools/report-session/record-version.d.ts +3 -0
- package/dist/tools/report-session/record-version.js +7 -3
- package/dist/tools/start-report-session.d.ts +7 -0
- package/dist/tools/start-report-session.js +1 -0
- package/dist/workflows/execute-analysis.d.ts +15 -69
- package/dist/workflows/execute-analysis.js +5 -4
- package/package.json +2 -1
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The provenance seam -- the one surface through which the harness tells an embedder what happened.
|
|
3
|
+
*
|
|
4
|
+
* Three duties answer one host concern. A durable run states the facts of its own lifecycle. A session
|
|
5
|
+
* states the acts that make a report. The report page needs the signed document of the analysis back, as
|
|
6
|
+
* bytes. The embedder holds that document, thus the harness declares the vocabulary and the seam alone,
|
|
7
|
+
* and it keeps no ledger of its own.
|
|
8
|
+
*
|
|
9
|
+
* One surface carries the three, and each member is optional alone. Thus a composition binds the members
|
|
10
|
+
* that it records, and the harness reads an absent member as absence and never as an error. The run emit
|
|
11
|
+
* takes the `RunSession` of the run, because a durable run carries one and a persisting realization needs
|
|
12
|
+
* its credential. A session act carries no session, thus the session emit takes the event alone.
|
|
13
|
+
*
|
|
14
|
+
* Both emit members are fire-and-forget. Neither gives a result, and the harness reads none. A site emits
|
|
15
|
+
* after its act lands, thus a refused act and a failed act each emit nothing and the record states what
|
|
16
|
+
* happened.
|
|
17
|
+
*/
|
|
18
|
+
import type { RunSession } from "../auth/types.js";
|
|
19
|
+
import type { Logger } from "../lib/logger.js";
|
|
20
|
+
import type { ProvenanceExport } from "../report-render/provenance-data.js";
|
|
21
|
+
import type { AuthoringBlock } from "../report-model/authoring-grammar.js";
|
|
22
|
+
import type { ExecuteAnalysisFinalStatus } from "../workflows/execute-analysis.js";
|
|
23
|
+
export type { ProvenanceExport } from "../report-render/provenance-data.js";
|
|
24
|
+
/**
|
|
25
|
+
* A run-lifecycle provenance observation handed to an optional host observer.
|
|
26
|
+
* Harness-owned plain union — the harness stays tsprov-free and bus-free; the
|
|
27
|
+
* host maps these execution facts onto its own ledger vocabulary.
|
|
28
|
+
*
|
|
29
|
+
* Every `atMs` is epoch milliseconds read via `await DBOS.now()`, a checkpointed
|
|
30
|
+
* step: a body re-executed by DBOS recovery reads the recorded value, so a
|
|
31
|
+
* re-emitted event carries the identical timestamp and merges on the host's
|
|
32
|
+
* ledger without a value conflict. Never source these from a wall clock
|
|
33
|
+
* (`Date.now()`) — that would diverge across replays and defeat the merge.
|
|
34
|
+
*
|
|
35
|
+
* `run_completed.durationMs` is the terminal `atMs` minus the `run_started`
|
|
36
|
+
* `atMs` (both `DBOS.now()` reads) — the true workflow-observed run span.
|
|
37
|
+
*
|
|
38
|
+
* `step_completed` fires once at EVERY scheduler-loop settlement — the only site
|
|
39
|
+
* that observes every executed step (registration sees only artifact-producing
|
|
40
|
+
* steps, and a child cannot observe its own parent-driven cancel). Steps that
|
|
41
|
+
* were never dispatched (dependents of a failed sibling) emit nothing by design;
|
|
42
|
+
* the run's terminal status carries that outcome. `status` maps the settlement
|
|
43
|
+
* outcome: `complete` → `"completed"`, `canceled` → `"canceled"`,
|
|
44
|
+
* `failed`/`blocked`/child-error → `"failed"`.
|
|
45
|
+
*
|
|
46
|
+
* `run_completed` fires at BOTH terminal boundaries (success and failure); the
|
|
47
|
+
* `status` field distinguishes them.
|
|
48
|
+
*/
|
|
49
|
+
export type RunProvenanceEvent = {
|
|
50
|
+
type: "run_started";
|
|
51
|
+
analysisId: string;
|
|
52
|
+
runId: string;
|
|
53
|
+
planSummary: string;
|
|
54
|
+
stepCount: number;
|
|
55
|
+
atMs: number;
|
|
56
|
+
} | {
|
|
57
|
+
type: "step_completed";
|
|
58
|
+
analysisId: string;
|
|
59
|
+
runId: string;
|
|
60
|
+
stepId: string;
|
|
61
|
+
/** Settlement outcome mapped to a terminal step status. */
|
|
62
|
+
status: "completed" | "failed" | "canceled";
|
|
63
|
+
/** The child's durable execution duration; absent when the child settled by throwing. */
|
|
64
|
+
durationMs?: number;
|
|
65
|
+
atMs: number;
|
|
66
|
+
} | {
|
|
67
|
+
type: "run_completed";
|
|
68
|
+
analysisId: string;
|
|
69
|
+
runId: string;
|
|
70
|
+
/**
|
|
71
|
+
* The body's terminal status. Both boundary sites resolve it through `deriveFinalStatus`,
|
|
72
|
+
* which records a budget pause as `"canceled"` — so `"suspended_insufficient_funds"` (a
|
|
73
|
+
* `RunStatus` member) is never emitted here and is deliberately absent from this narrower
|
|
74
|
+
* `ExecuteAnalysisFinalStatus`-minus-`"running"` set.
|
|
75
|
+
*/
|
|
76
|
+
status: Exclude<ExecuteAnalysisFinalStatus, "running">;
|
|
77
|
+
atMs: number;
|
|
78
|
+
/** `atMs − run_started.atMs`: the workflow-observed run span in ms. */
|
|
79
|
+
durationMs: number;
|
|
80
|
+
};
|
|
81
|
+
/**
|
|
82
|
+
* One act on one block of a draft.
|
|
83
|
+
*
|
|
84
|
+
* The kind rides beside the id, because an id alone tells a reader nothing about what the act touched.
|
|
85
|
+
* The kind is the one that the document holds after the act: a change is permitted to change the kind,
|
|
86
|
+
* and a remove states the kind that the draft carried before it.
|
|
87
|
+
*/
|
|
88
|
+
type BlockAct = {
|
|
89
|
+
analysisId: string;
|
|
90
|
+
threadId: string;
|
|
91
|
+
blockId: string;
|
|
92
|
+
blockKind: AuthoringBlock["kind"];
|
|
93
|
+
};
|
|
94
|
+
/**
|
|
95
|
+
* One observation of a session.
|
|
96
|
+
*
|
|
97
|
+
* The union names one event for each act: the start of a session, the four block operations, the title,
|
|
98
|
+
* the derivation, the preview, and the record. Each event carries the analysis and the thread, thus a
|
|
99
|
+
* consumer places it with no lookup. The title sits on the document, thus the title event names no block.
|
|
100
|
+
*
|
|
101
|
+
* Each member carries what its own site holds at the moment that the act lands, and nothing more. The
|
|
102
|
+
* derivation carries the chain of the table, because the site pins it and a consumer cannot rebuild it
|
|
103
|
+
* from the path. The preview carries the page and the hash of the draft that it shows, thus a consumer
|
|
104
|
+
* ties a page to the document that made it.
|
|
105
|
+
*
|
|
106
|
+
* The `create-session` event names the kind of the new session, and it names the parent thread of a child
|
|
107
|
+
* session. An analysis holds sessions of more than one kind, and the thread id alone says neither. Thus
|
|
108
|
+
* the document of the analysis tells the whole tree of the sessions, and a reader walks it with no second
|
|
109
|
+
* read of the seam. A root session carries no parent, thus `parentThreadId` is absent on it.
|
|
110
|
+
*/
|
|
111
|
+
export type SessionProvenanceEvent = {
|
|
112
|
+
type: "create-session";
|
|
113
|
+
analysisId: string;
|
|
114
|
+
threadId: string;
|
|
115
|
+
sessionKind: "conversation" | "report";
|
|
116
|
+
parentThreadId?: string;
|
|
117
|
+
} | ({
|
|
118
|
+
type: "add-block";
|
|
119
|
+
} & BlockAct) | ({
|
|
120
|
+
type: "change-block";
|
|
121
|
+
} & BlockAct) | ({
|
|
122
|
+
type: "remove-block";
|
|
123
|
+
} & BlockAct) | ({
|
|
124
|
+
type: "move-block";
|
|
125
|
+
} & BlockAct) | {
|
|
126
|
+
type: "set-title";
|
|
127
|
+
analysisId: string;
|
|
128
|
+
threadId: string;
|
|
129
|
+
title: string;
|
|
130
|
+
} | {
|
|
131
|
+
type: "run-derivation";
|
|
132
|
+
analysisId: string;
|
|
133
|
+
threadId: string;
|
|
134
|
+
outputPath: string;
|
|
135
|
+
outputHash: string;
|
|
136
|
+
scriptHash: string;
|
|
137
|
+
sources: ReadonlyArray<{
|
|
138
|
+
path: string;
|
|
139
|
+
hash: string;
|
|
140
|
+
}>;
|
|
141
|
+
} | {
|
|
142
|
+
type: "preview";
|
|
143
|
+
analysisId: string;
|
|
144
|
+
threadId: string;
|
|
145
|
+
pagePath: string;
|
|
146
|
+
documentHash: string;
|
|
147
|
+
} | {
|
|
148
|
+
type: "record-version";
|
|
149
|
+
analysisId: string;
|
|
150
|
+
threadId: string;
|
|
151
|
+
versionId: string;
|
|
152
|
+
replaced: boolean;
|
|
153
|
+
};
|
|
154
|
+
/**
|
|
155
|
+
* The provenance seam of the harness. An embedder realizes the members that it records, and the harness
|
|
156
|
+
* never branches on which realization is bound.
|
|
157
|
+
*/
|
|
158
|
+
export type ProvenanceSeam = {
|
|
159
|
+
/**
|
|
160
|
+
* The run engine emits the facts of one run here.
|
|
161
|
+
*
|
|
162
|
+
* Synchronous by signature (`void`, never a promise): a host that needs input and output dispatches
|
|
163
|
+
* the work rather than making a workflow body wait behind it. The `RunSession` rides from the durable
|
|
164
|
+
* workflow input, because a persisting realization needs the credential, the scope, and the identity
|
|
165
|
+
* of the run. A realization can ignore the parameter.
|
|
166
|
+
*/
|
|
167
|
+
readonly emitRunEvent?: (event: RunProvenanceEvent, session: RunSession) => void;
|
|
168
|
+
/**
|
|
169
|
+
* A session emits one act here. A session act runs under no authorized run, thus this member takes
|
|
170
|
+
* the event alone. It is synchronous by signature for the same reason as the run emit.
|
|
171
|
+
*/
|
|
172
|
+
readonly emitSessionEvent?: (event: SessionProvenanceEvent) => void;
|
|
173
|
+
/**
|
|
174
|
+
* The page staging reads the signed document of one analysis here.
|
|
175
|
+
*
|
|
176
|
+
* The member can be async, because a source can read a file or ask a service. It gives the bytes and
|
|
177
|
+
* the attestation, or `undefined`. Absence is a normal result, not a fault: the page then carries no
|
|
178
|
+
* provenance asset, and each other part of the render is what it was.
|
|
179
|
+
*/
|
|
180
|
+
readonly readExport?: (analysisId: string) => ProvenanceExport | undefined | Promise<ProvenanceExport | undefined>;
|
|
181
|
+
};
|
|
182
|
+
/**
|
|
183
|
+
* Bind the optional session emit and the logger of one site into a total emit.
|
|
184
|
+
*
|
|
185
|
+
* An absent member gives a call that does nothing. Thus a site emits where its act lands with no test of
|
|
186
|
+
* its own, and absence stays a normal condition.
|
|
187
|
+
*
|
|
188
|
+
* The harness is host-agnostic, and it cannot assume that a host callback is total. Thus a throw of the
|
|
189
|
+
* realization reaches the log and stops there: an observation is a record of an act, and a defect in it
|
|
190
|
+
* must never undo the act that already landed.
|
|
191
|
+
*/
|
|
192
|
+
export declare function bindSessionEmit(seam: ProvenanceSeam | undefined, logger: Logger): (event: SessionProvenanceEvent) => void;
|
|
193
|
+
/**
|
|
194
|
+
* Bind the optional document read and the logger of one site into a total read.
|
|
195
|
+
*
|
|
196
|
+
* An absent member gives a call that answers absence. Thus a site asks at its one place with no test of
|
|
197
|
+
* its own, and an unbound composition needs no branch.
|
|
198
|
+
*
|
|
199
|
+
* A throw of the realization reaches the log and becomes absence, for the same reason that a throw of an
|
|
200
|
+
* emit stops at the log: the provenance of the page is an addition to the report, and a defect in it must
|
|
201
|
+
* never cost the render.
|
|
202
|
+
*/
|
|
203
|
+
export declare function bindReadExport(seam: ProvenanceSeam | undefined, logger: Logger): (analysisId: string) => Promise<ProvenanceExport | undefined>;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The provenance seam -- the one surface through which the harness tells an embedder what happened.
|
|
3
|
+
*
|
|
4
|
+
* Three duties answer one host concern. A durable run states the facts of its own lifecycle. A session
|
|
5
|
+
* states the acts that make a report. The report page needs the signed document of the analysis back, as
|
|
6
|
+
* bytes. The embedder holds that document, thus the harness declares the vocabulary and the seam alone,
|
|
7
|
+
* and it keeps no ledger of its own.
|
|
8
|
+
*
|
|
9
|
+
* One surface carries the three, and each member is optional alone. Thus a composition binds the members
|
|
10
|
+
* that it records, and the harness reads an absent member as absence and never as an error. The run emit
|
|
11
|
+
* takes the `RunSession` of the run, because a durable run carries one and a persisting realization needs
|
|
12
|
+
* its credential. A session act carries no session, thus the session emit takes the event alone.
|
|
13
|
+
*
|
|
14
|
+
* Both emit members are fire-and-forget. Neither gives a result, and the harness reads none. A site emits
|
|
15
|
+
* after its act lands, thus a refused act and a failed act each emit nothing and the record states what
|
|
16
|
+
* happened.
|
|
17
|
+
*/
|
|
18
|
+
/**
|
|
19
|
+
* Bind the optional session emit and the logger of one site into a total emit.
|
|
20
|
+
*
|
|
21
|
+
* An absent member gives a call that does nothing. Thus a site emits where its act lands with no test of
|
|
22
|
+
* its own, and absence stays a normal condition.
|
|
23
|
+
*
|
|
24
|
+
* The harness is host-agnostic, and it cannot assume that a host callback is total. Thus a throw of the
|
|
25
|
+
* realization reaches the log and stops there: an observation is a record of an act, and a defect in it
|
|
26
|
+
* must never undo the act that already landed.
|
|
27
|
+
*/
|
|
28
|
+
export function bindSessionEmit(seam, logger) {
|
|
29
|
+
const emit = seam?.emitSessionEvent;
|
|
30
|
+
if (emit === undefined) {
|
|
31
|
+
return () => undefined;
|
|
32
|
+
}
|
|
33
|
+
return (event) => {
|
|
34
|
+
try {
|
|
35
|
+
emit(event);
|
|
36
|
+
}
|
|
37
|
+
catch (cause) {
|
|
38
|
+
logger.error("the session emit of the provenance seam threw", {
|
|
39
|
+
analysisId: event.analysisId,
|
|
40
|
+
threadId: event.threadId,
|
|
41
|
+
event: event.type,
|
|
42
|
+
...logger.errorFields(cause),
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Bind the optional document read and the logger of one site into a total read.
|
|
49
|
+
*
|
|
50
|
+
* An absent member gives a call that answers absence. Thus a site asks at its one place with no test of
|
|
51
|
+
* its own, and an unbound composition needs no branch.
|
|
52
|
+
*
|
|
53
|
+
* A throw of the realization reaches the log and becomes absence, for the same reason that a throw of an
|
|
54
|
+
* emit stops at the log: the provenance of the page is an addition to the report, and a defect in it must
|
|
55
|
+
* never cost the render.
|
|
56
|
+
*/
|
|
57
|
+
export function bindReadExport(seam, logger) {
|
|
58
|
+
const read = seam?.readExport;
|
|
59
|
+
if (read === undefined) {
|
|
60
|
+
return () => Promise.resolve(undefined);
|
|
61
|
+
}
|
|
62
|
+
return async (analysisId) => {
|
|
63
|
+
try {
|
|
64
|
+
return await read(analysisId);
|
|
65
|
+
}
|
|
66
|
+
catch (cause) {
|
|
67
|
+
logger.error("the document read of the provenance seam threw", { analysisId, ...logger.errorFields(cause) });
|
|
68
|
+
return undefined;
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}
|
|
@@ -398,15 +398,15 @@ declare const AUTHORING_ATOM_SCHEMAS: readonly [z.ZodObject<{
|
|
|
398
398
|
x: z.ZodOptional<z.ZodObject<{
|
|
399
399
|
title: z.ZodOptional<z.ZodString>;
|
|
400
400
|
scale: z.ZodOptional<z.ZodEnum<{
|
|
401
|
-
log: "log";
|
|
402
401
|
linear: "linear";
|
|
402
|
+
log: "log";
|
|
403
403
|
}>>;
|
|
404
404
|
}, z.core.$strict>>;
|
|
405
405
|
y: z.ZodOptional<z.ZodObject<{
|
|
406
406
|
title: z.ZodOptional<z.ZodString>;
|
|
407
407
|
scale: z.ZodOptional<z.ZodEnum<{
|
|
408
|
-
log: "log";
|
|
409
408
|
linear: "linear";
|
|
409
|
+
log: "log";
|
|
410
410
|
}>>;
|
|
411
411
|
}, z.core.$strict>>;
|
|
412
412
|
}, z.core.$strict>>;
|
|
@@ -66,6 +66,16 @@ export declare const ECHARTS_ASSET: PageAsset;
|
|
|
66
66
|
* entries alone, thus this specifier resolves as a path under the installed package directory.
|
|
67
67
|
*/
|
|
68
68
|
export declare const AG_GRID_ASSET: PageAsset;
|
|
69
|
+
/**
|
|
70
|
+
* The provenance library. The page-side popover reads the `tsprov` global that this file declares.
|
|
71
|
+
*
|
|
72
|
+
* The entry names the browser bundle of the package. The package `exports` map publishes the module entries
|
|
73
|
+
* alone, thus this specifier resolves as a path under the installed package directory.
|
|
74
|
+
*
|
|
75
|
+
* The harness imports no API of the library. It stages these bytes and nothing more, thus the walk of a
|
|
76
|
+
* chain belongs to the page and the renderer stays free of every provenance API.
|
|
77
|
+
*/
|
|
78
|
+
export declare const TSPROV_ASSET: PageAsset;
|
|
69
79
|
/** The sans font. The file is the latin subset of the variable font, thus one file carries each weight. */
|
|
70
80
|
export declare const SANS_FONT_ASSET: PageAsset;
|
|
71
81
|
/**
|
|
@@ -77,6 +77,19 @@ export const AG_GRID_ASSET = {
|
|
|
77
77
|
file: `${DEPS_DIR}/ag-grid-community.min.js`,
|
|
78
78
|
specifier: "ag-grid-community/dist/ag-grid-community.min.js",
|
|
79
79
|
};
|
|
80
|
+
/**
|
|
81
|
+
* The provenance library. The page-side popover reads the `tsprov` global that this file declares.
|
|
82
|
+
*
|
|
83
|
+
* The entry names the browser bundle of the package. The package `exports` map publishes the module entries
|
|
84
|
+
* alone, thus this specifier resolves as a path under the installed package directory.
|
|
85
|
+
*
|
|
86
|
+
* The harness imports no API of the library. It stages these bytes and nothing more, thus the walk of a
|
|
87
|
+
* chain belongs to the page and the renderer stays free of every provenance API.
|
|
88
|
+
*/
|
|
89
|
+
export const TSPROV_ASSET = {
|
|
90
|
+
file: `${DEPS_DIR}/tsprov.min.js`,
|
|
91
|
+
specifier: "@inflexa-ai/tsprov/dist/browser/tsprov.min.js",
|
|
92
|
+
};
|
|
80
93
|
/** The sans font. The file is the latin subset of the variable font, thus one file carries each weight. */
|
|
81
94
|
export const SANS_FONT_ASSET = {
|
|
82
95
|
file: `${DEPS_DIR}/space-grotesk-latin-wght-normal.woff2`,
|
|
@@ -112,6 +125,7 @@ export const MONO_FONT_700_ASSET = {
|
|
|
112
125
|
export const PAGE_ASSETS = [
|
|
113
126
|
ECHARTS_ASSET,
|
|
114
127
|
AG_GRID_ASSET,
|
|
128
|
+
TSPROV_ASSET,
|
|
115
129
|
SANS_FONT_ASSET,
|
|
116
130
|
MONO_FONT_400_ASSET,
|
|
117
131
|
MONO_FONT_500_ASSET,
|
|
@@ -116,6 +116,7 @@ export const DESIGN_CSS = `${FONT_FACES}
|
|
|
116
116
|
|
|
117
117
|
/* Semantic tags */
|
|
118
118
|
--color-high-bg: #f0fdf4;
|
|
119
|
+
--color-high-border: #bbf7d0;
|
|
119
120
|
--color-high-text: #15803d;
|
|
120
121
|
--color-medium-bg: #fffbeb;
|
|
121
122
|
--color-medium-text: #b45309;
|
|
@@ -378,6 +379,251 @@ img {
|
|
|
378
379
|
text-decoration: underline;
|
|
379
380
|
}
|
|
380
381
|
|
|
382
|
+
/* ── Lineage control and popover ──────────────────────── */
|
|
383
|
+
/* The control sits beside the bracket marker. It reads as a quiet branch glyph and never as a second
|
|
384
|
+
marker, thus it takes the muted color until a hover. The button carries no browser chrome, because a
|
|
385
|
+
chrome box inside a line of prose would break the line. */
|
|
386
|
+
.report-lineage {
|
|
387
|
+
display: inline-flex;
|
|
388
|
+
align-items: center;
|
|
389
|
+
margin-left: 2px;
|
|
390
|
+
padding: 0;
|
|
391
|
+
line-height: 0;
|
|
392
|
+
vertical-align: -2px;
|
|
393
|
+
color: var(--color-text-muted);
|
|
394
|
+
background: none;
|
|
395
|
+
border: 0;
|
|
396
|
+
cursor: pointer;
|
|
397
|
+
}
|
|
398
|
+
.report-lineage:hover {
|
|
399
|
+
color: var(--color-primary-500);
|
|
400
|
+
}
|
|
401
|
+
/* The drawing strokes in the current color, thus the two color rules above reach it and the sheet holds one
|
|
402
|
+
color for the two states. */
|
|
403
|
+
.report-lineage-glyph {
|
|
404
|
+
display: block;
|
|
405
|
+
}
|
|
406
|
+
/* The panel floats over the page. A data card clips its own overflow, thus the page script places the panel
|
|
407
|
+
against the document and no card cuts it. The panel takes the width of its longest row, thus a name reads
|
|
408
|
+
whole and a cut is the exception. The cap bounds it against the design and against the viewport, and the
|
|
409
|
+
viewport half holds the same margin at each side as the clamp of the script. Thus a narrow window shows
|
|
410
|
+
the panel between the two margins. */
|
|
411
|
+
.report-lineage-popover {
|
|
412
|
+
position: absolute;
|
|
413
|
+
z-index: 50;
|
|
414
|
+
width: max-content;
|
|
415
|
+
max-width: min(48rem, calc(100vw - 24px));
|
|
416
|
+
background: var(--color-card);
|
|
417
|
+
border: 1px solid var(--color-border);
|
|
418
|
+
box-shadow: 0 12px 32px -12px rgba(15, 23, 42, 0.18);
|
|
419
|
+
animation: report-lineage-open 0.12s ease-out;
|
|
420
|
+
}
|
|
421
|
+
.report-lineage-header {
|
|
422
|
+
display: flex;
|
|
423
|
+
align-items: center;
|
|
424
|
+
gap: 10px;
|
|
425
|
+
padding: 12px 16px;
|
|
426
|
+
border-bottom: 1px solid var(--color-border-subtle);
|
|
427
|
+
}
|
|
428
|
+
.report-lineage-title {
|
|
429
|
+
font-family: var(--font-mono);
|
|
430
|
+
font-size: 10px;
|
|
431
|
+
font-weight: 600;
|
|
432
|
+
letter-spacing: 0.08em;
|
|
433
|
+
text-transform: uppercase;
|
|
434
|
+
color: var(--color-text-muted);
|
|
435
|
+
}
|
|
436
|
+
/* The header names the marker that opened the panel and the depth of the chain under it. Thus the reader
|
|
437
|
+
reads the size of the chain before the body scrolls. */
|
|
438
|
+
.report-lineage-count {
|
|
439
|
+
font-family: var(--font-mono);
|
|
440
|
+
font-size: 11px;
|
|
441
|
+
color: var(--color-text-secondary);
|
|
442
|
+
}
|
|
443
|
+
.report-lineage-close {
|
|
444
|
+
margin-left: auto;
|
|
445
|
+
padding: 0;
|
|
446
|
+
font-family: var(--font-mono);
|
|
447
|
+
font-size: 14px;
|
|
448
|
+
line-height: 1;
|
|
449
|
+
color: var(--color-text-muted);
|
|
450
|
+
background: none;
|
|
451
|
+
border: 0;
|
|
452
|
+
cursor: pointer;
|
|
453
|
+
}
|
|
454
|
+
.report-lineage-close:hover {
|
|
455
|
+
color: var(--color-heading);
|
|
456
|
+
}
|
|
457
|
+
/* A deep pipeline builds more rows than a window holds. The cap reads against the viewport, thus the body
|
|
458
|
+
scrolls inside the panel and the panel never grows the page. */
|
|
459
|
+
.report-lineage-body {
|
|
460
|
+
max-height: 60vh;
|
|
461
|
+
overflow-y: auto;
|
|
462
|
+
padding: 16px 16px 8px;
|
|
463
|
+
}
|
|
464
|
+
/* One base row carries each file of the rail. A modifier marks the pinned artifact and a raw input, and a
|
|
465
|
+
row with no modifier is an artifact that the rail continues past. */
|
|
466
|
+
.report-lineage-row {
|
|
467
|
+
display: flex;
|
|
468
|
+
align-items: center;
|
|
469
|
+
gap: 8px;
|
|
470
|
+
padding: 6px 10px;
|
|
471
|
+
background: var(--color-bg-alt);
|
|
472
|
+
border: 1px solid var(--color-border-subtle);
|
|
473
|
+
}
|
|
474
|
+
.report-lineage-row + .report-lineage-row {
|
|
475
|
+
margin-top: 4px;
|
|
476
|
+
}
|
|
477
|
+
.report-lineage-row-pin {
|
|
478
|
+
padding: 8px 10px;
|
|
479
|
+
background: var(--color-primary-50);
|
|
480
|
+
border-color: var(--color-primary-100);
|
|
481
|
+
}
|
|
482
|
+
.report-lineage-row-producer {
|
|
483
|
+
padding: 8px 10px;
|
|
484
|
+
background: var(--color-card);
|
|
485
|
+
border-color: var(--color-border);
|
|
486
|
+
}
|
|
487
|
+
/* The terminal tint marks a file that no command of this analysis made. It is where a branch ends, thus the
|
|
488
|
+
tint and the tag of the row state the same fact. */
|
|
489
|
+
.report-lineage-row-raw {
|
|
490
|
+
background: var(--color-high-bg);
|
|
491
|
+
border-color: var(--color-high-border);
|
|
492
|
+
}
|
|
493
|
+
.report-lineage-tag {
|
|
494
|
+
flex-shrink: 0;
|
|
495
|
+
padding: 2px 6px;
|
|
496
|
+
font-family: var(--font-mono);
|
|
497
|
+
font-size: 9px;
|
|
498
|
+
letter-spacing: 0.06em;
|
|
499
|
+
color: var(--color-text-secondary);
|
|
500
|
+
background: var(--color-border);
|
|
501
|
+
}
|
|
502
|
+
.report-lineage-tag-pin {
|
|
503
|
+
font-weight: 600;
|
|
504
|
+
color: var(--color-card);
|
|
505
|
+
background: var(--color-primary-500);
|
|
506
|
+
}
|
|
507
|
+
.report-lineage-tag-raw {
|
|
508
|
+
font-weight: 600;
|
|
509
|
+
color: var(--color-card);
|
|
510
|
+
background: var(--color-high-text);
|
|
511
|
+
}
|
|
512
|
+
/* The path takes the whole free width of the row, and it never wraps the row into two lines. The panel grows
|
|
513
|
+
to the longest path, thus a window that gives the panel its cap needs no cut. In a narrow window the page
|
|
514
|
+
script cuts a long path at its start, and it cuts an over-long file name in the middle of that name. Thus
|
|
515
|
+
the ellipsis of this rule is the last guard, for a row too narrow to hold even a cut name. */
|
|
516
|
+
.report-lineage-path {
|
|
517
|
+
flex: 1;
|
|
518
|
+
overflow: hidden;
|
|
519
|
+
font-family: var(--font-mono);
|
|
520
|
+
font-size: 12px;
|
|
521
|
+
color: var(--color-text);
|
|
522
|
+
text-overflow: ellipsis;
|
|
523
|
+
white-space: nowrap;
|
|
524
|
+
}
|
|
525
|
+
.report-lineage-row-pin .report-lineage-path,
|
|
526
|
+
.report-lineage-row-raw .report-lineage-path {
|
|
527
|
+
color: var(--color-text-strong);
|
|
528
|
+
}
|
|
529
|
+
/* The directory prefix dims, thus the file name reads first inside a long path. */
|
|
530
|
+
.report-lineage-dir {
|
|
531
|
+
color: var(--color-text-muted);
|
|
532
|
+
}
|
|
533
|
+
.report-lineage-hash {
|
|
534
|
+
flex-shrink: 0;
|
|
535
|
+
margin-left: auto;
|
|
536
|
+
font-family: var(--font-mono);
|
|
537
|
+
font-size: 10px;
|
|
538
|
+
color: var(--color-text-muted);
|
|
539
|
+
}
|
|
540
|
+
.report-lineage-prompt {
|
|
541
|
+
flex-shrink: 0;
|
|
542
|
+
color: var(--color-text-secondary);
|
|
543
|
+
}
|
|
544
|
+
.report-lineage-script {
|
|
545
|
+
overflow: hidden;
|
|
546
|
+
font-family: var(--font-mono);
|
|
547
|
+
font-size: 12px;
|
|
548
|
+
color: var(--color-text-strong);
|
|
549
|
+
text-overflow: ellipsis;
|
|
550
|
+
white-space: nowrap;
|
|
551
|
+
}
|
|
552
|
+
.report-lineage-meta {
|
|
553
|
+
flex-shrink: 0;
|
|
554
|
+
margin-left: auto;
|
|
555
|
+
font-family: var(--font-mono);
|
|
556
|
+
font-size: 10px;
|
|
557
|
+
color: var(--color-text-muted);
|
|
558
|
+
}
|
|
559
|
+
/* The connector sits between two levels of the rail. The page script gives it the indent of its level, thus
|
|
560
|
+
one rule serves each depth. */
|
|
561
|
+
.report-lineage-link {
|
|
562
|
+
display: flex;
|
|
563
|
+
gap: 10px;
|
|
564
|
+
padding: 2px 0;
|
|
565
|
+
}
|
|
566
|
+
.report-lineage-rail {
|
|
567
|
+
width: 1px;
|
|
568
|
+
background: var(--color-border);
|
|
569
|
+
}
|
|
570
|
+
.report-lineage-rail-pin {
|
|
571
|
+
background: var(--color-primary-100);
|
|
572
|
+
}
|
|
573
|
+
.report-lineage-link-label {
|
|
574
|
+
padding: 6px 0;
|
|
575
|
+
font-family: var(--font-mono);
|
|
576
|
+
font-size: 9px;
|
|
577
|
+
letter-spacing: 0.08em;
|
|
578
|
+
color: var(--color-text-muted);
|
|
579
|
+
}
|
|
580
|
+
/* The dashed edge marks a row that names files and shows none of them. Thus a count row never reads as one
|
|
581
|
+
more hop of the chain. */
|
|
582
|
+
.report-lineage-more {
|
|
583
|
+
margin-top: 4px;
|
|
584
|
+
padding: 5px 10px;
|
|
585
|
+
font-family: var(--font-mono);
|
|
586
|
+
font-size: 11px;
|
|
587
|
+
color: var(--color-text-muted);
|
|
588
|
+
/* One line always: a wrapped count row breaks the rail rhythm in a narrow
|
|
589
|
+
window, and a cut count loses nothing that the rows above do not show. */
|
|
590
|
+
white-space: nowrap;
|
|
591
|
+
overflow: hidden;
|
|
592
|
+
text-overflow: ellipsis;
|
|
593
|
+
border: 1px dashed var(--color-border);
|
|
594
|
+
}
|
|
595
|
+
.report-lineage-footer {
|
|
596
|
+
display: flex;
|
|
597
|
+
align-items: center;
|
|
598
|
+
gap: 8px;
|
|
599
|
+
padding: 10px 16px;
|
|
600
|
+
background: var(--color-bg-alt);
|
|
601
|
+
border-top: 1px solid var(--color-border-subtle);
|
|
602
|
+
}
|
|
603
|
+
.report-lineage-check {
|
|
604
|
+
flex-shrink: 0;
|
|
605
|
+
color: var(--color-high-text);
|
|
606
|
+
}
|
|
607
|
+
/* The footer states that the chain is complete, or it states the absence or the truncation that stopped it.
|
|
608
|
+
A reader must read the rail against what it does not hold, thus the note sits under the rail and never
|
|
609
|
+
beside one row of it. */
|
|
610
|
+
.report-lineage-note {
|
|
611
|
+
font-family: var(--font-mono);
|
|
612
|
+
font-size: 10px;
|
|
613
|
+
line-height: 1.5;
|
|
614
|
+
color: var(--color-text-secondary);
|
|
615
|
+
}
|
|
616
|
+
@keyframes report-lineage-open {
|
|
617
|
+
from {
|
|
618
|
+
opacity: 0;
|
|
619
|
+
transform: translateY(-4px);
|
|
620
|
+
}
|
|
621
|
+
to {
|
|
622
|
+
opacity: 1;
|
|
623
|
+
transform: translateY(0);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
381
627
|
/* ── Corner-accent card ───────────────────────────────── */
|
|
382
628
|
/* The data cards keep square corners. The L-shaped accents grow on hover. */
|
|
383
629
|
.corner-accents {
|
|
@@ -789,6 +1035,10 @@ a.report-citation-source:hover {
|
|
|
789
1035
|
opacity: 1;
|
|
790
1036
|
transform: none;
|
|
791
1037
|
}
|
|
1038
|
+
/* The panel stands at once, thus the open costs no motion. */
|
|
1039
|
+
.report-lineage-popover {
|
|
1040
|
+
animation: none;
|
|
1041
|
+
}
|
|
792
1042
|
}
|
|
793
1043
|
|
|
794
1044
|
/* ── Print ────────────────────────────────────────────── */
|
|
@@ -796,6 +1046,12 @@ a.report-citation-source:hover {
|
|
|
796
1046
|
.report-nav {
|
|
797
1047
|
display: none;
|
|
798
1048
|
}
|
|
1049
|
+
/* The control opens a panel, and paper opens nothing. The appendix carries the same references, thus the
|
|
1050
|
+
printed page loses no evidence. */
|
|
1051
|
+
.report-lineage,
|
|
1052
|
+
.report-lineage-popover {
|
|
1053
|
+
display: none;
|
|
1054
|
+
}
|
|
799
1055
|
/* The printer gives the page margin. Thus the screen padding of the container drops out. */
|
|
800
1056
|
body:has(#report-sidebar) {
|
|
801
1057
|
padding-left: 0;
|