agentfootprint 9.75.0 → 9.76.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +94 -0
- package/CLAUDE.md +22 -18
- package/dist/artifacts/index.js +3 -1
- package/dist/artifacts/index.js.map +1 -1
- package/dist/artifacts/recordingArtifact.js +47 -1
- package/dist/artifacts/recordingArtifact.js.map +1 -1
- package/dist/core/Agent.js +30 -0
- package/dist/core/Agent.js.map +1 -1
- package/dist/core/agent/stages/toolCalls.js +42 -0
- package/dist/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/core/agent/toolDispatch.js +101 -0
- package/dist/core/agent/toolDispatch.js.map +1 -0
- package/dist/core/flowchartAsTool.js +9 -2
- package/dist/core/flowchartAsTool.js.map +1 -1
- package/dist/core/runbook/coverage.js +162 -0
- package/dist/core/runbook/coverage.js.map +1 -0
- package/dist/core/runbook/dispatch.js +119 -0
- package/dist/core/runbook/dispatch.js.map +1 -0
- package/dist/core/runbook/index.js +24 -0
- package/dist/core/runbook/index.js.map +1 -0
- package/dist/core/runbook/report.js +69 -0
- package/dist/core/runbook/report.js.map +1 -0
- package/dist/core/runbook/runbookAsTool.js +381 -0
- package/dist/core/runbook/runbookAsTool.js.map +1 -0
- package/dist/core/runbook/types.js +22 -0
- package/dist/core/runbook/types.js.map +1 -0
- package/dist/core/runbook/verdicts.js +156 -0
- package/dist/core/runbook/verdicts.js.map +1 -0
- package/dist/core/runbook/walk.js +115 -0
- package/dist/core/runbook/walk.js.map +1 -0
- package/dist/core/tools.js +55 -1
- package/dist/core/tools.js.map +1 -1
- package/dist/esm/artifacts/index.d.ts +1 -1
- package/dist/esm/artifacts/index.js +1 -1
- package/dist/esm/artifacts/index.js.map +1 -1
- package/dist/esm/artifacts/recordingArtifact.d.ts +36 -0
- package/dist/esm/artifacts/recordingArtifact.js +45 -0
- package/dist/esm/artifacts/recordingArtifact.js.map +1 -1
- package/dist/esm/core/Agent.js +30 -0
- package/dist/esm/core/Agent.js.map +1 -1
- package/dist/esm/core/agent/stages/toolCalls.js +42 -0
- package/dist/esm/core/agent/stages/toolCalls.js.map +1 -1
- package/dist/esm/core/agent/toolDispatch.d.ts +50 -0
- package/dist/esm/core/agent/toolDispatch.js +97 -0
- package/dist/esm/core/agent/toolDispatch.js.map +1 -0
- package/dist/esm/core/flowchartAsTool.d.ts +6 -0
- package/dist/esm/core/flowchartAsTool.js +9 -2
- package/dist/esm/core/flowchartAsTool.js.map +1 -1
- package/dist/esm/core/runbook/coverage.d.ts +69 -0
- package/dist/esm/core/runbook/coverage.js +155 -0
- package/dist/esm/core/runbook/coverage.js.map +1 -0
- package/dist/esm/core/runbook/dispatch.d.ts +69 -0
- package/dist/esm/core/runbook/dispatch.js +112 -0
- package/dist/esm/core/runbook/dispatch.js.map +1 -0
- package/dist/esm/core/runbook/index.d.ts +9 -0
- package/dist/esm/core/runbook/index.js +9 -0
- package/dist/esm/core/runbook/index.js.map +1 -0
- package/dist/esm/core/runbook/report.d.ts +52 -0
- package/dist/esm/core/runbook/report.js +64 -0
- package/dist/esm/core/runbook/report.js.map +1 -0
- package/dist/esm/core/runbook/runbookAsTool.d.ts +90 -0
- package/dist/esm/core/runbook/runbookAsTool.js +377 -0
- package/dist/esm/core/runbook/runbookAsTool.js.map +1 -0
- package/dist/esm/core/runbook/types.d.ts +203 -0
- package/dist/esm/core/runbook/types.js +21 -0
- package/dist/esm/core/runbook/types.js.map +1 -0
- package/dist/esm/core/runbook/verdicts.d.ts +78 -0
- package/dist/esm/core/runbook/verdicts.js +148 -0
- package/dist/esm/core/runbook/verdicts.js.map +1 -0
- package/dist/esm/core/runbook/walk.d.ts +72 -0
- package/dist/esm/core/runbook/walk.js +110 -0
- package/dist/esm/core/runbook/walk.js.map +1 -0
- package/dist/esm/core/tools.d.ts +112 -0
- package/dist/esm/core/tools.js +52 -0
- package/dist/esm/core/tools.js.map +1 -1
- package/dist/esm/index.d.ts +4 -3
- package/dist/esm/index.js +19 -2
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/lib/mcp/toolExtras.d.ts +8 -0
- package/dist/esm/lib/mcp/toolExtras.js +7 -1
- package/dist/esm/lib/mcp/toolExtras.js.map +1 -1
- package/dist/index.js +97 -66
- package/dist/index.js.map +1 -1
- package/dist/lib/mcp/toolExtras.js +6 -0
- package/dist/lib/mcp/toolExtras.js.map +1 -1
- package/dist/types/artifacts/index.d.ts +1 -1
- package/dist/types/artifacts/index.d.ts.map +1 -1
- package/dist/types/artifacts/recordingArtifact.d.ts +36 -0
- package/dist/types/artifacts/recordingArtifact.d.ts.map +1 -1
- package/dist/types/core/Agent.d.ts.map +1 -1
- package/dist/types/core/agent/stages/toolCalls.d.ts.map +1 -1
- package/dist/types/core/agent/toolDispatch.d.ts +51 -0
- package/dist/types/core/agent/toolDispatch.d.ts.map +1 -0
- package/dist/types/core/flowchartAsTool.d.ts +6 -0
- package/dist/types/core/flowchartAsTool.d.ts.map +1 -1
- package/dist/types/core/runbook/coverage.d.ts +70 -0
- package/dist/types/core/runbook/coverage.d.ts.map +1 -0
- package/dist/types/core/runbook/dispatch.d.ts +70 -0
- package/dist/types/core/runbook/dispatch.d.ts.map +1 -0
- package/dist/types/core/runbook/index.d.ts +10 -0
- package/dist/types/core/runbook/index.d.ts.map +1 -0
- package/dist/types/core/runbook/report.d.ts +53 -0
- package/dist/types/core/runbook/report.d.ts.map +1 -0
- package/dist/types/core/runbook/runbookAsTool.d.ts +91 -0
- package/dist/types/core/runbook/runbookAsTool.d.ts.map +1 -0
- package/dist/types/core/runbook/types.d.ts +204 -0
- package/dist/types/core/runbook/types.d.ts.map +1 -0
- package/dist/types/core/runbook/verdicts.d.ts +79 -0
- package/dist/types/core/runbook/verdicts.d.ts.map +1 -0
- package/dist/types/core/runbook/walk.d.ts +73 -0
- package/dist/types/core/runbook/walk.d.ts.map +1 -0
- package/dist/types/core/tools.d.ts +112 -0
- package/dist/types/core/tools.d.ts.map +1 -1
- package/dist/types/index.d.ts +4 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/lib/mcp/toolExtras.d.ts +8 -0
- package/dist/types/lib/mcp/toolExtras.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* runbook/types — the vocabulary of the runbook bridge.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: one options bag, four concerns (declarations, procedure, evidence
|
|
5
|
+
* policy, walk policy) + one envelope type split into a MANDATORY
|
|
6
|
+
* SPINE and an OPTIONAL projection. Pure data, no behavior.
|
|
7
|
+
* Role: core/ layer. `runbookAsTool.ts` consumes; consumers read the
|
|
8
|
+
* envelope types when they assert on results.
|
|
9
|
+
* Emits: N/A.
|
|
10
|
+
*
|
|
11
|
+
* The SPINE / PROJECTION split is load-bearing honesty, not taste: the spine
|
|
12
|
+
* (coverage, provenance, rule version, the recorded walk) is what EVERY
|
|
13
|
+
* runbook ships whatever its shape, so an answer can never arrive without its
|
|
14
|
+
* boundary; the verdict/rowset projection is one shape of answer (a triage),
|
|
15
|
+
* selected by `resultKind: 'verdict/*'` — an action-taking runbook ships the
|
|
16
|
+
* spine plus its own `report` payload and no rowset. The spine's wire shape
|
|
17
|
+
* is explicitly PROVISIONAL until a second, differently-shaped consumer has
|
|
18
|
+
* shipped through it.
|
|
19
|
+
*/
|
|
20
|
+
import type { CombinedRecorder, FlowChart, RedactionPolicy } from 'footprintjs';
|
|
21
|
+
import type { CoverageItem } from '../agent/coverage/types.js';
|
|
22
|
+
import type { ToolDispatch, ToolOwner, ToolResultCeiling } from '../tools.js';
|
|
23
|
+
import type { ToolResultClass } from '../../lib/semantics/types.js';
|
|
24
|
+
import type { ToolWants } from '../../artifacts/wants.js';
|
|
25
|
+
/**
|
|
26
|
+
* The procedure: a factory invoked PER CALL with the run's own tool dispatch
|
|
27
|
+
* (`ctx.tools`), returning a FRESH chart whose stages close over it.
|
|
28
|
+
*
|
|
29
|
+
* Per call for two reasons that are really one: a chart shared between two
|
|
30
|
+
* runs is a chart whose closures belong to whichever run built it last, and
|
|
31
|
+
* the dispatch only exists at execute time — so the factory is both the
|
|
32
|
+
* fresh-chart law and the delivery mechanism.
|
|
33
|
+
*
|
|
34
|
+
* It is ALSO invoked once at definition time, with a probe dispatch whose
|
|
35
|
+
* `has` answers false and whose `call` refuses — to read the chart's declared
|
|
36
|
+
* contract (input schema, the named decider's branches). Stage bodies do not
|
|
37
|
+
* run at build, so a well-formed factory pays nothing; a factory with side
|
|
38
|
+
* effects at build time is a factory that lies about being a declaration.
|
|
39
|
+
*/
|
|
40
|
+
export type RunbookProcedure = (tools: ToolDispatch) => FlowChart;
|
|
41
|
+
/** Rule provenance — threaded bridge → envelope → coverage sentence, so an
|
|
42
|
+
* answer produced under one reading of the rules can be told apart from an
|
|
43
|
+
* answer produced under the next. */
|
|
44
|
+
export interface RunbookRules {
|
|
45
|
+
/** The rule set's name ('health-signal'). Non-empty. */
|
|
46
|
+
readonly name: string;
|
|
47
|
+
/** Its version stamp ('v1'). Non-empty; bump when the READING changes,
|
|
48
|
+
* never for wording. */
|
|
49
|
+
readonly version: string;
|
|
50
|
+
}
|
|
51
|
+
/** The verdict/rowset projection's dials. */
|
|
52
|
+
export interface RunbookVerdictsOptions {
|
|
53
|
+
/**
|
|
54
|
+
* The decider stage (by id or name) whose declared branch labels generate
|
|
55
|
+
* `verdict_meanings`. Branch descriptions come from the chart's own
|
|
56
|
+
* structure when the decider is statically declared; rule labels observed
|
|
57
|
+
* in this run's decide() evidence refine them (and are the only source
|
|
58
|
+
* when the decider lives inside a dynamically generated fan-out branch,
|
|
59
|
+
* where build-time structure cannot see it).
|
|
60
|
+
*/
|
|
61
|
+
readonly decider: string;
|
|
62
|
+
/** Cap on `verdicts` rows AND the rendered table — ONE number for both
|
|
63
|
+
* halves (a longer list beside a shorter table is an invitation to retype
|
|
64
|
+
* identifiers). Default 50. */
|
|
65
|
+
readonly maxRows?: number;
|
|
66
|
+
}
|
|
67
|
+
/** The walk policy. */
|
|
68
|
+
export interface RunbookWalkOptions {
|
|
69
|
+
/** Row cap on the minted walk (default 500). When the full walk does not
|
|
70
|
+
* fit, the CONTROL FLOW survives — see `walk.ts` for the projection law. */
|
|
71
|
+
readonly cap?: number;
|
|
72
|
+
}
|
|
73
|
+
/** Everything `runbookAsTool` accepts. Smallest legal call:
|
|
74
|
+
* `{ name, description, procedure }` — and it still yields the spine. */
|
|
75
|
+
export interface RunbookAsToolOptions {
|
|
76
|
+
/** Tool name the LLM dispatches by. */
|
|
77
|
+
readonly name: string;
|
|
78
|
+
/** REQUIRED — a description-less tool is invisible to the model. */
|
|
79
|
+
readonly description: string;
|
|
80
|
+
/** The procedure factory — see {@link RunbookProcedure}. */
|
|
81
|
+
readonly procedure: RunbookProcedure;
|
|
82
|
+
/** Selects the envelope projection (`'verdict/*'` gets the rowset) AND is
|
|
83
|
+
* the artifact kind a placed result is minted under. */
|
|
84
|
+
readonly resultKind?: string;
|
|
85
|
+
readonly resultClass?: ToolResultClass;
|
|
86
|
+
readonly owner?: ToolOwner;
|
|
87
|
+
readonly resultCeiling?: ToolResultCeiling;
|
|
88
|
+
readonly wants?: ToolWants;
|
|
89
|
+
readonly argumentsFrom?: readonly string[];
|
|
90
|
+
/** The named ingredient tools the procedure calls through `ctx.tools` —
|
|
91
|
+
* drift-checked at agent build. */
|
|
92
|
+
readonly composedOf?: readonly string[];
|
|
93
|
+
/** Explicit input schema wins; otherwise the chart's `.contract()` input
|
|
94
|
+
* is lifted when it is a plain JSON-Schema object (a parseable schema —
|
|
95
|
+
* zod et al. — cannot be serialized for the model and falls back to the
|
|
96
|
+
* empty-object default). */
|
|
97
|
+
readonly inputSchema?: Readonly<Record<string, unknown>>;
|
|
98
|
+
/** Rule provenance — see {@link RunbookRules}. */
|
|
99
|
+
readonly rules?: RunbookRules;
|
|
100
|
+
/** The verdict projection's dials — see {@link RunbookVerdictsOptions}. */
|
|
101
|
+
readonly verdicts?: RunbookVerdictsOptions;
|
|
102
|
+
/** The walk policy — see {@link RunbookWalkOptions}. */
|
|
103
|
+
readonly walk?: RunbookWalkOptions;
|
|
104
|
+
/** Observers attached to each invocation's fresh inner executor. */
|
|
105
|
+
readonly recorders?: ReadonlyArray<CombinedRecorder>;
|
|
106
|
+
/** Keep each invocation's inner record for `inspect_tool_run` descent. */
|
|
107
|
+
readonly keepRecord?: boolean;
|
|
108
|
+
/** Bounded LRU size for kept records (requires `keepRecord: true`). */
|
|
109
|
+
readonly keepRecordLimit?: number;
|
|
110
|
+
/** Redaction policy for the inner run (commit-time scrub). */
|
|
111
|
+
readonly redact?: RedactionPolicy;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* The recorded-walk descriptor — ALWAYS on the spine. The walk itself ships
|
|
115
|
+
* as an artifact ticket (`ref`), never as bytes in the envelope; with no
|
|
116
|
+
* store attached (or a failed mint) the descriptor still states its
|
|
117
|
+
* counters and the `note` names why there is no ticket — a missing walk must
|
|
118
|
+
* never be mistaken for a short run.
|
|
119
|
+
*/
|
|
120
|
+
export interface WalkDescriptor {
|
|
121
|
+
/** The claim-ticket ref of the minted walk artifact. Absent when no store
|
|
122
|
+
* is attached or the mint failed — `note` says which. */
|
|
123
|
+
readonly ref?: string;
|
|
124
|
+
/** The artifact kind (`'recording/chart-walk'`). Present with `ref`. */
|
|
125
|
+
readonly kind?: string;
|
|
126
|
+
/** Rows in the minted artifact. */
|
|
127
|
+
readonly rows: number;
|
|
128
|
+
/** Total execution steps the narrative recorder counted — spans isolated
|
|
129
|
+
* subflow logs, which the root commit log cannot. */
|
|
130
|
+
readonly steps_executed: number;
|
|
131
|
+
/** `'full'` — every narrative entry fit under the cap; `'control-flow'` —
|
|
132
|
+
* it did not, and the stages/forks/subflows/decisions survived while the
|
|
133
|
+
* per-key reads and writes were dropped. */
|
|
134
|
+
readonly projection: 'full' | 'control-flow';
|
|
135
|
+
readonly shown: number;
|
|
136
|
+
readonly total: number;
|
|
137
|
+
readonly complete: boolean;
|
|
138
|
+
/**
|
|
139
|
+
* WHICH SEGMENT of the run this walk covers. `'full'` for an un-gated run
|
|
140
|
+
* (all of phase 1). When approval gates land, a resumed run's recorders
|
|
141
|
+
* start empty on the fresh executor — its walk will say `'post-resume'`
|
|
142
|
+
* and its counters will count only that segment; the discriminant ships
|
|
143
|
+
* NOW so the wire does not break then.
|
|
144
|
+
*/
|
|
145
|
+
readonly walk_segment: 'full' | 'pre-pause' | 'post-resume';
|
|
146
|
+
/** The human sentence: what the walk is, and (when projected) what the
|
|
147
|
+
* control-flow projection kept and dropped, or why there is no ticket. */
|
|
148
|
+
readonly note: string;
|
|
149
|
+
}
|
|
150
|
+
/** One verdict row, as the chart wrote it. The bridge reads rows from the
|
|
151
|
+
* final state's `verdicts` key and requires only `verdict`; every other
|
|
152
|
+
* column is the app's own vocabulary. */
|
|
153
|
+
export interface VerdictRow {
|
|
154
|
+
readonly verdict: string;
|
|
155
|
+
readonly [column: string]: unknown;
|
|
156
|
+
}
|
|
157
|
+
/** The mandatory-spine + optional-projection envelope every runbook returns
|
|
158
|
+
* (unless an inner absence passed through — then the answer IS that absence,
|
|
159
|
+
* verbatim). Recognized by the framework's coverage funnel at the dispatch
|
|
160
|
+
* boundary like any `coverage()` ledger. */
|
|
161
|
+
export interface RunbookEnvelope {
|
|
162
|
+
readonly af_coverage: {
|
|
163
|
+
readonly checked?: readonly CoverageItem[];
|
|
164
|
+
readonly not_checked?: readonly CoverageItem[];
|
|
165
|
+
readonly cannot_cover?: readonly CoverageItem[];
|
|
166
|
+
/** The static coverage law sentence (the `coverage()` note). */
|
|
167
|
+
readonly note: string;
|
|
168
|
+
/** The run's own sentence — names the rule set and version. */
|
|
169
|
+
readonly sentence: string;
|
|
170
|
+
};
|
|
171
|
+
readonly result: {
|
|
172
|
+
/** Re-emitted FIRST: the carried inner provenance stamp (if any inner
|
|
173
|
+
* tool declared one — a LOCAL SEED confession survives composition)
|
|
174
|
+
* plus this call's own `{tool, toolCallId}`. */
|
|
175
|
+
readonly af_provenance: Readonly<Record<string, unknown>>;
|
|
176
|
+
/** `rules.version`, or the honest `'undeclared'` when no rules were
|
|
177
|
+
* declared — the field never silently vanishes. */
|
|
178
|
+
readonly rule_version: string;
|
|
179
|
+
/** The recorded walk — always present. */
|
|
180
|
+
readonly walk: WalkDescriptor;
|
|
181
|
+
readonly verdicts?: readonly VerdictRow[];
|
|
182
|
+
readonly rows_shown?: number;
|
|
183
|
+
readonly rows_total?: number;
|
|
184
|
+
readonly rows_complete?: boolean;
|
|
185
|
+
/** Pre-rendered markdown table over the SAME rows as `verdicts`. */
|
|
186
|
+
readonly table?: string;
|
|
187
|
+
/** The render law, stated to the model. Present with `table`. */
|
|
188
|
+
readonly render_note?: string;
|
|
189
|
+
/** branch → meaning, GENERATED from the decider's declared branches and
|
|
190
|
+
* the rule labels this run's evidence carried — never hand-restated. */
|
|
191
|
+
readonly verdict_meanings?: Readonly<Record<string, string>>;
|
|
192
|
+
/** Present ONLY when the chart's `report` spelled one of the envelope's
|
|
193
|
+
* own names: it names every discarded field and says the values under
|
|
194
|
+
* those names are the bridge's. Absent on the clean path. */
|
|
195
|
+
readonly report_note?: string;
|
|
196
|
+
/** Everything the chart put in its `report` state key — the app's own
|
|
197
|
+
* result fields, spread here verbatim BESIDE the spine. Spine keys win:
|
|
198
|
+
* a report field spelling `af_coverage`, `af_provenance`, `rule_version`,
|
|
199
|
+
* `walk`, `report_note`, or a projection key this run assembled is
|
|
200
|
+
* discarded and named in `report_note`. */
|
|
201
|
+
readonly [appField: string]: unknown;
|
|
202
|
+
};
|
|
203
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* runbook/types — the vocabulary of the runbook bridge.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: one options bag, four concerns (declarations, procedure, evidence
|
|
5
|
+
* policy, walk policy) + one envelope type split into a MANDATORY
|
|
6
|
+
* SPINE and an OPTIONAL projection. Pure data, no behavior.
|
|
7
|
+
* Role: core/ layer. `runbookAsTool.ts` consumes; consumers read the
|
|
8
|
+
* envelope types when they assert on results.
|
|
9
|
+
* Emits: N/A.
|
|
10
|
+
*
|
|
11
|
+
* The SPINE / PROJECTION split is load-bearing honesty, not taste: the spine
|
|
12
|
+
* (coverage, provenance, rule version, the recorded walk) is what EVERY
|
|
13
|
+
* runbook ships whatever its shape, so an answer can never arrive without its
|
|
14
|
+
* boundary; the verdict/rowset projection is one shape of answer (a triage),
|
|
15
|
+
* selected by `resultKind: 'verdict/*'` — an action-taking runbook ships the
|
|
16
|
+
* spine plus its own `report` payload and no rowset. The spine's wire shape
|
|
17
|
+
* is explicitly PROVISIONAL until a second, differently-shaped consumer has
|
|
18
|
+
* shipped through it.
|
|
19
|
+
*/
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../../src/core/runbook/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* runbook/verdicts — the OPTIONAL verdict/rowset projection.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: pure selection + rendering over the run's final state, plus one
|
|
5
|
+
* tiny recorder that harvests decide() evidence for generated
|
|
6
|
+
* meanings. Selected by `resultKind: 'verdict/*'`; a runbook of any
|
|
7
|
+
* other kind never sees this module.
|
|
8
|
+
* Role: core/runbook.
|
|
9
|
+
* Emits: N/A.
|
|
10
|
+
*
|
|
11
|
+
* THE ONE-NUMBER LAW: the rendered table and the structured `verdicts` list
|
|
12
|
+
* show the SAME rows under the SAME cap. A rendered table beside a longer
|
|
13
|
+
* structured list is an invitation that gets accepted — rows come back
|
|
14
|
+
* retyped with subtly wrong identifiers.
|
|
15
|
+
*
|
|
16
|
+
* GENERATED MEANINGS, never hand-restated: `verdict_meanings` is composed
|
|
17
|
+
* from (a) the named decider's declared branches in the chart's own structure
|
|
18
|
+
* (branch description, falling back to branch name), overlaid by (b) the rule
|
|
19
|
+
* LABELS this run's decide() evidence carried — the rule speaking for itself.
|
|
20
|
+
* A decider inside a dynamically generated fan-out branch is invisible to (a)
|
|
21
|
+
* by construction (the branch chart does not exist at build time); (b) still
|
|
22
|
+
* covers every verdict an executed rule produced.
|
|
23
|
+
*/
|
|
24
|
+
import type { CombinedRecorder } from 'footprintjs';
|
|
25
|
+
import type { FlowChart } from 'footprintjs';
|
|
26
|
+
import type { VerdictRow } from './types.js';
|
|
27
|
+
/** Default cap on `verdicts` rows and the rendered table. */
|
|
28
|
+
export declare const DEFAULT_MAX_ROWS = 50;
|
|
29
|
+
/** The render law, stated to the model beside every table. */
|
|
30
|
+
export declare const VERDICT_RENDER_NOTE: string;
|
|
31
|
+
/** The reserved verdict word for "no classification was reached" — rows
|
|
32
|
+
* carrying it are counted into the coverage ledger as not-checked ground
|
|
33
|
+
* (the three-outcome honesty: reached, reached-and-clear, DECLINED). */
|
|
34
|
+
export declare const DECLINED_VERDICT = "declined";
|
|
35
|
+
/** Read the rowset off the final state's `verdicts` key — an array of bags
|
|
36
|
+
* each carrying a string `verdict`. Anything else reads as "no rowset". */
|
|
37
|
+
export declare function verdictRowsOf(state: Readonly<Record<string, unknown>>): VerdictRow[];
|
|
38
|
+
/**
|
|
39
|
+
* Render the shown rows as one markdown table. Columns are the FIRST row's
|
|
40
|
+
* own keys in declaration order — the chart writes its rows, so the chart
|
|
41
|
+
* owns the column vocabulary; the bridge only renders it.
|
|
42
|
+
*/
|
|
43
|
+
export declare function renderVerdictTable(rows: readonly VerdictRow[]): string;
|
|
44
|
+
/** The two spellings of the configured decider, once the static walk has
|
|
45
|
+
* (maybe) resolved it — flow events report the decider by name. */
|
|
46
|
+
export interface DeciderIdentity {
|
|
47
|
+
readonly spellings: ReadonlySet<string>;
|
|
48
|
+
/** branch → declared meaning, from the chart's own structure. */
|
|
49
|
+
readonly declared: ReadonlyMap<string, string>;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Find the named decider in the chart's static structure (root graph plus
|
|
53
|
+
* statically declared subflows) and read its branches. Loop-ref stubs are
|
|
54
|
+
* skipped FIRST — they deliberately violate node-id uniqueness.
|
|
55
|
+
*/
|
|
56
|
+
export declare function resolveDecider(chart: FlowChart, decider: string): DeciderIdentity;
|
|
57
|
+
/** The evidence harvest: rule labels observed in this run, branch → label. */
|
|
58
|
+
export interface MeaningsHarvest {
|
|
59
|
+
readonly recorder: CombinedRecorder;
|
|
60
|
+
readonly observed: ReadonlyMap<string, string>;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* A tiny flow recorder capturing the named decider's decide() evidence as it
|
|
64
|
+
* fires — collected DURING the traversal, never reconstructed after. Rule
|
|
65
|
+
* labels are harvested for every rule the evidence lists (matched or not):
|
|
66
|
+
* a rule that was evaluated has spoken its label, whichever branch won.
|
|
67
|
+
*
|
|
68
|
+
* A decider inside a subflow (including a generated fan-out branch) reports
|
|
69
|
+
* itself PATH-PREFIXED (`per-subject~0/Protection posture`), so the match is
|
|
70
|
+
* on the LAST `/`-segment — the same last-delimiter reading every upstream
|
|
71
|
+
* parser of these paths uses, which is what keeps the generated-branch
|
|
72
|
+
* marker opaque here.
|
|
73
|
+
*/
|
|
74
|
+
export declare function meaningsRecorder(identity: DeciderIdentity): MeaningsHarvest;
|
|
75
|
+
/** Compose the final meanings: declared branches first, observed rule labels
|
|
76
|
+
* winning where both speak (the rule is the sharper sentence). Undefined
|
|
77
|
+
* when neither source produced anything — absent, never `{}`. */
|
|
78
|
+
export declare function composeMeanings(identity: DeciderIdentity, observed: ReadonlyMap<string, string>): Record<string, string> | undefined;
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* runbook/verdicts — the OPTIONAL verdict/rowset projection.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: pure selection + rendering over the run's final state, plus one
|
|
5
|
+
* tiny recorder that harvests decide() evidence for generated
|
|
6
|
+
* meanings. Selected by `resultKind: 'verdict/*'`; a runbook of any
|
|
7
|
+
* other kind never sees this module.
|
|
8
|
+
* Role: core/runbook.
|
|
9
|
+
* Emits: N/A.
|
|
10
|
+
*
|
|
11
|
+
* THE ONE-NUMBER LAW: the rendered table and the structured `verdicts` list
|
|
12
|
+
* show the SAME rows under the SAME cap. A rendered table beside a longer
|
|
13
|
+
* structured list is an invitation that gets accepted — rows come back
|
|
14
|
+
* retyped with subtly wrong identifiers.
|
|
15
|
+
*
|
|
16
|
+
* GENERATED MEANINGS, never hand-restated: `verdict_meanings` is composed
|
|
17
|
+
* from (a) the named decider's declared branches in the chart's own structure
|
|
18
|
+
* (branch description, falling back to branch name), overlaid by (b) the rule
|
|
19
|
+
* LABELS this run's decide() evidence carried — the rule speaking for itself.
|
|
20
|
+
* A decider inside a dynamically generated fan-out branch is invisible to (a)
|
|
21
|
+
* by construction (the branch chart does not exist at build time); (b) still
|
|
22
|
+
* covers every verdict an executed rule produced.
|
|
23
|
+
*/
|
|
24
|
+
/** Default cap on `verdicts` rows and the rendered table. */
|
|
25
|
+
export const DEFAULT_MAX_ROWS = 50;
|
|
26
|
+
/** The render law, stated to the model beside every table. */
|
|
27
|
+
export const VERDICT_RENDER_NOTE = 'table is PRE-RENDERED over the same rows as `verdicts` — output it VERBATIM. Never ' +
|
|
28
|
+
'retype an identifier from `verdicts`; a transcribed name that looks right and matches ' +
|
|
29
|
+
'nothing is the failure this note exists to stop.';
|
|
30
|
+
/** The reserved verdict word for "no classification was reached" — rows
|
|
31
|
+
* carrying it are counted into the coverage ledger as not-checked ground
|
|
32
|
+
* (the three-outcome honesty: reached, reached-and-clear, DECLINED). */
|
|
33
|
+
export const DECLINED_VERDICT = 'declined';
|
|
34
|
+
/** Read the rowset off the final state's `verdicts` key — an array of bags
|
|
35
|
+
* each carrying a string `verdict`. Anything else reads as "no rowset". */
|
|
36
|
+
export function verdictRowsOf(state) {
|
|
37
|
+
const raw = state.verdicts;
|
|
38
|
+
if (!Array.isArray(raw))
|
|
39
|
+
return [];
|
|
40
|
+
return raw.filter((row) => row !== null &&
|
|
41
|
+
typeof row === 'object' &&
|
|
42
|
+
!Array.isArray(row) &&
|
|
43
|
+
typeof row.verdict === 'string');
|
|
44
|
+
}
|
|
45
|
+
/** The one place a `—` is written for a value the source did not report — a
|
|
46
|
+
* blank cell reads as a zero; a dash reads as "not reported". */
|
|
47
|
+
function cell(value) {
|
|
48
|
+
if (value === null || value === undefined || value === '')
|
|
49
|
+
return '—';
|
|
50
|
+
if (value === true)
|
|
51
|
+
return 'yes';
|
|
52
|
+
if (value === false)
|
|
53
|
+
return 'no';
|
|
54
|
+
return String(value).replace(/\|/g, '\\|').replace(/\n/g, ' ');
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Render the shown rows as one markdown table. Columns are the FIRST row's
|
|
58
|
+
* own keys in declaration order — the chart writes its rows, so the chart
|
|
59
|
+
* owns the column vocabulary; the bridge only renders it.
|
|
60
|
+
*/
|
|
61
|
+
export function renderVerdictTable(rows) {
|
|
62
|
+
if (rows.length === 0)
|
|
63
|
+
return 'No rows in the projected set.';
|
|
64
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
65
|
+
const columns = Object.keys(rows[0]);
|
|
66
|
+
const head = `| ${columns.join(' | ')} |\n|${columns.map(() => '---').join('|')}|\n`;
|
|
67
|
+
return (head +
|
|
68
|
+
rows.map((row) => `| ${columns.map((column) => cell(row[column])).join(' | ')} |`).join('\n'));
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Find the named decider in the chart's static structure (root graph plus
|
|
72
|
+
* statically declared subflows) and read its branches. Loop-ref stubs are
|
|
73
|
+
* skipped FIRST — they deliberately violate node-id uniqueness.
|
|
74
|
+
*/
|
|
75
|
+
export function resolveDecider(chart, decider) {
|
|
76
|
+
const spellings = new Set([decider]);
|
|
77
|
+
const declared = new Map();
|
|
78
|
+
const visited = new Set();
|
|
79
|
+
const walk = (node) => {
|
|
80
|
+
if (node === undefined || node.isLoopRef === true || visited.has(node))
|
|
81
|
+
return;
|
|
82
|
+
visited.add(node);
|
|
83
|
+
if ((node.id === decider || node.name === decider) && (node.children?.length ?? 0) > 0) {
|
|
84
|
+
if (node.id !== undefined)
|
|
85
|
+
spellings.add(node.id);
|
|
86
|
+
if (node.name !== undefined)
|
|
87
|
+
spellings.add(node.name);
|
|
88
|
+
for (const child of node.children ?? []) {
|
|
89
|
+
const branch = child.branchId ?? child.id;
|
|
90
|
+
if (branch === undefined)
|
|
91
|
+
continue;
|
|
92
|
+
const meaning = child.description ?? child.name;
|
|
93
|
+
if (meaning !== undefined && !declared.has(branch))
|
|
94
|
+
declared.set(branch, meaning);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
for (const child of node.children ?? [])
|
|
98
|
+
walk(child);
|
|
99
|
+
walk(node.next);
|
|
100
|
+
};
|
|
101
|
+
walk(chart.root);
|
|
102
|
+
for (const subflow of Object.values(chart.subflows ?? {})) {
|
|
103
|
+
walk(subflow.root);
|
|
104
|
+
}
|
|
105
|
+
return { spellings, declared };
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* A tiny flow recorder capturing the named decider's decide() evidence as it
|
|
109
|
+
* fires — collected DURING the traversal, never reconstructed after. Rule
|
|
110
|
+
* labels are harvested for every rule the evidence lists (matched or not):
|
|
111
|
+
* a rule that was evaluated has spoken its label, whichever branch won.
|
|
112
|
+
*
|
|
113
|
+
* A decider inside a subflow (including a generated fan-out branch) reports
|
|
114
|
+
* itself PATH-PREFIXED (`per-subject~0/Protection posture`), so the match is
|
|
115
|
+
* on the LAST `/`-segment — the same last-delimiter reading every upstream
|
|
116
|
+
* parser of these paths uses, which is what keeps the generated-branch
|
|
117
|
+
* marker opaque here.
|
|
118
|
+
*/
|
|
119
|
+
export function meaningsRecorder(identity) {
|
|
120
|
+
const observed = new Map();
|
|
121
|
+
const lastSegment = (path) => path.slice(path.lastIndexOf('/') + 1);
|
|
122
|
+
const recorder = {
|
|
123
|
+
id: 'runbook-verdict-meanings',
|
|
124
|
+
onDecision(event) {
|
|
125
|
+
if (!identity.spellings.has(lastSegment(event.decider)))
|
|
126
|
+
return;
|
|
127
|
+
const evidence = event.evidence;
|
|
128
|
+
for (const rule of evidence?.rules ?? []) {
|
|
129
|
+
if (typeof rule.branch === 'string' && typeof rule.label === 'string') {
|
|
130
|
+
observed.set(rule.branch, rule.label);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
return { recorder, observed };
|
|
136
|
+
}
|
|
137
|
+
/** Compose the final meanings: declared branches first, observed rule labels
|
|
138
|
+
* winning where both speak (the rule is the sharper sentence). Undefined
|
|
139
|
+
* when neither source produced anything — absent, never `{}`. */
|
|
140
|
+
export function composeMeanings(identity, observed) {
|
|
141
|
+
const meanings = {};
|
|
142
|
+
for (const [branch, meaning] of identity.declared)
|
|
143
|
+
meanings[branch] = meaning;
|
|
144
|
+
for (const [branch, label] of observed)
|
|
145
|
+
meanings[branch] = label;
|
|
146
|
+
return Object.keys(meanings).length > 0 ? meanings : undefined;
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=verdicts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verdicts.js","sourceRoot":"","sources":["../../../../src/core/runbook/verdicts.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AAMH,6DAA6D;AAC7D,MAAM,CAAC,MAAM,gBAAgB,GAAG,EAAE,CAAC;AAEnC,8DAA8D;AAC9D,MAAM,CAAC,MAAM,mBAAmB,GAC9B,qFAAqF;IACrF,wFAAwF;IACxF,kDAAkD,CAAC;AAErD;;yEAEyE;AACzE,MAAM,CAAC,MAAM,gBAAgB,GAAG,UAAU,CAAC;AAE3C;4EAC4E;AAC5E,MAAM,UAAU,aAAa,CAAC,KAAwC;IACpE,MAAM,GAAG,GAAG,KAAK,CAAC,QAAQ,CAAC;IAC3B,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QAAE,OAAO,EAAE,CAAC;IACnC,OAAO,GAAG,CAAC,MAAM,CACf,CAAC,GAAG,EAAqB,EAAE,CACzB,GAAG,KAAK,IAAI;QACZ,OAAO,GAAG,KAAK,QAAQ;QACvB,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC;QACnB,OAAQ,GAA6B,CAAC,OAAO,KAAK,QAAQ,CAC7D,CAAC;AACJ,CAAC;AAED;kEACkE;AAClE,SAAS,IAAI,CAAC,KAAc;IAC1B,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,KAAK,EAAE;QAAE,OAAO,GAAG,CAAC;IACtE,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IACjC,IAAI,KAAK,KAAK,KAAK;QAAE,OAAO,IAAI,CAAC;IACjC,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AACjE,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAA2B;IAC5D,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,+BAA+B,CAAC;IAC9D,oEAAoE;IACpE,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAE,CAAC,CAAC;IACtC,MAAM,IAAI,GAAG,KAAK,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,QAAQ,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;IACrF,OAAO,CACL,IAAI;QACJ,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,KAAK,OAAO,CAAC,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAC9F,CAAC;AACJ,CAAC;AAwBD;;;;GAIG;AACH,MAAM,UAAU,cAAc,CAAC,KAAgB,EAAE,OAAe;IAC9D,MAAM,SAAS,GAAG,IAAI,GAAG,CAAS,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7C,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,MAAM,OAAO,GAAG,IAAI,GAAG,EAAY,CAAC;IACpC,MAAM,IAAI,GAAG,CAAC,IAA0B,EAAQ,EAAE;QAChD,IAAI,IAAI,KAAK,SAAS,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI,IAAI,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC;YAAE,OAAO;QAC/E,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAClB,IAAI,CAAC,IAAI,CAAC,EAAE,KAAK,OAAO,IAAI,IAAI,CAAC,IAAI,KAAK,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC;YACvF,IAAI,IAAI,CAAC,EAAE,KAAK,SAAS;gBAAE,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;YAClD,IAAI,IAAI,CAAC,IAAI,KAAK,SAAS;gBAAE,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YACtD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,IAAI,EAAE,EAAE,CAAC;gBACxC,MAAM,MAAM,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,CAAC;gBAC1C,IAAI,MAAM,KAAK,SAAS;oBAAE,SAAS;gBACnC,MAAM,OAAO,GAAG,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,IAAI,CAAC;gBAChD,IAAI,OAAO,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,MAAM,CAAC;oBAAE,QAAQ,CAAC,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YACpF,CAAC;QACH,CAAC;QACD,KAAK,MAAM,KAAK,IAAI,IAAI,CAAC,QAAQ,IAAI,EAAE;YAAE,IAAI,CAAC,KAAK,CAAC,CAAC;QACrD,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAClB,CAAC,CAAC;IACF,IAAI,CAAC,KAAK,CAAC,IAAgB,CAAC,CAAC;IAC7B,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,IAAI,EAAE,CAAC,EAAE,CAAC;QAC1D,IAAI,CAAC,OAAO,CAAC,IAAgB,CAAC,CAAC;IACjC,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,CAAC;AACjC,CAAC;AAQD;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,gBAAgB,CAAC,QAAyB;IACxD,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC3C,MAAM,WAAW,GAAG,CAAC,IAAY,EAAU,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC;IACpF,MAAM,QAAQ,GAAG;QACf,EAAE,EAAE,0BAA0B;QAC9B,UAAU,CAAC,KAAwB;YACjC,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;gBAAE,OAAO;YAChE,MAAM,QAAQ,GAAG,KAAK,CAAC,QAEV,CAAC;YACd,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,KAAK,IAAI,EAAE,EAAE,CAAC;gBACzC,IAAI,OAAO,IAAI,CAAC,MAAM,KAAK,QAAQ,IAAI,OAAO,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;oBACtE,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;gBACxC,CAAC;YACH,CAAC;QACH,CAAC;KAC6B,CAAC;IACjC,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC;AAChC,CAAC;AAED;;kEAEkE;AAClE,MAAM,UAAU,eAAe,CAC7B,QAAyB,EACzB,QAAqC;IAErC,MAAM,QAAQ,GAA2B,EAAE,CAAC;IAC5C,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,QAAQ,CAAC,QAAQ;QAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,OAAO,CAAC;IAC9E,KAAK,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,IAAI,QAAQ;QAAE,QAAQ,CAAC,MAAM,CAAC,GAAG,KAAK,CAAC;IACjE,OAAO,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,SAAS,CAAC;AACjE,CAAC"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* runbook/walk — the recorded walk: projection law, counters, mint.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: pure projection (`projectWalk`) + one guarded side effect
|
|
5
|
+
* (`mintWalk`, which files the artifact and NEVER fails the answer).
|
|
6
|
+
* Role: core/runbook. The walk is not a debugging extra — a procedure that
|
|
7
|
+
* cannot show how it reached a verdict is a procedure somebody has
|
|
8
|
+
* to take on trust.
|
|
9
|
+
* Emits: nothing (the artifact capability emits its own `artifacts.*`).
|
|
10
|
+
*
|
|
11
|
+
* THE CAP LAW, and why a head slice is the wrong cap: in a chart walk the
|
|
12
|
+
* per-key reads and writes come FIRST and the decisions come LAST, so a
|
|
13
|
+
* naive head slice over a fleet sweep keeps four hundred writes and drops
|
|
14
|
+
* every decision — a walk with the walking taken out. When the whole thing
|
|
15
|
+
* does not fit, the CONTROL FLOW survives (stages, forks, subflows, and
|
|
16
|
+
* every `condition` entry carrying its decide() evidence) and the projection
|
|
17
|
+
* is DECLARED, so a reader knows which of the two they are holding.
|
|
18
|
+
*/
|
|
19
|
+
import type { ToolExecutionContext } from '../tools.js';
|
|
20
|
+
import type { WalkDescriptor } from './types.js';
|
|
21
|
+
/** The default row cap — a readable artifact, not an archive. The count is
|
|
22
|
+
* always reported, so a truncated walk says it is truncated instead of
|
|
23
|
+
* looking like a short run. */
|
|
24
|
+
export declare const DEFAULT_WALK_CAP = 500;
|
|
25
|
+
/** A narrative entry as this module reads it. `rawValue` is deliberately NOT
|
|
26
|
+
* in the list: it is a LIVE reference into engine memory and has no business
|
|
27
|
+
* in a value checked into an artifact store. */
|
|
28
|
+
export interface NarrativeEntryView {
|
|
29
|
+
readonly type: string;
|
|
30
|
+
readonly text: string;
|
|
31
|
+
readonly depth: number;
|
|
32
|
+
readonly stageName?: string;
|
|
33
|
+
readonly stageId?: string;
|
|
34
|
+
readonly runtimeStageId?: string;
|
|
35
|
+
readonly subflowId?: string;
|
|
36
|
+
}
|
|
37
|
+
/** One walk row — plain data by construction (every field projected). */
|
|
38
|
+
export interface WalkRow {
|
|
39
|
+
readonly step: number;
|
|
40
|
+
readonly type: string;
|
|
41
|
+
readonly depth: number;
|
|
42
|
+
readonly stage: string | null;
|
|
43
|
+
readonly stage_id: string | null;
|
|
44
|
+
readonly runtime_stage_id: string | null;
|
|
45
|
+
readonly subflow: string | null;
|
|
46
|
+
readonly text: string;
|
|
47
|
+
}
|
|
48
|
+
/** The pure projection result — rows plus truthful counters. */
|
|
49
|
+
export interface ProjectedWalk {
|
|
50
|
+
readonly rows: readonly WalkRow[];
|
|
51
|
+
readonly projection: 'full' | 'control-flow';
|
|
52
|
+
readonly shown: number;
|
|
53
|
+
readonly total: number;
|
|
54
|
+
readonly complete: boolean;
|
|
55
|
+
}
|
|
56
|
+
/** Apply the cap law. Counters are about the WHOLE narrative (`total`), so a
|
|
57
|
+
* projected walk cannot read as a short run. */
|
|
58
|
+
export declare function projectWalk(entries: readonly NarrativeEntryView[], cap: number): ProjectedWalk;
|
|
59
|
+
/** What the mint needs from the call. */
|
|
60
|
+
export interface WalkMintFacts {
|
|
61
|
+
readonly toolName: string;
|
|
62
|
+
readonly toolCallId: string;
|
|
63
|
+
readonly runId?: string;
|
|
64
|
+
readonly stepsExecuted: number;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* File the walk and build the descriptor. THE SPINE ALWAYS GETS A
|
|
68
|
+
* DESCRIPTOR: with no store attached, or when the mint fails, the counters
|
|
69
|
+
* still travel and the note names why there is no ticket — a failed mint
|
|
70
|
+
* costs the TICKET, never the answer and never the counts.
|
|
71
|
+
*/
|
|
72
|
+
export declare function mintWalk(ctx: ToolExecutionContext, projected: ProjectedWalk, facts: WalkMintFacts): Promise<WalkDescriptor>;
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* runbook/walk — the recorded walk: projection law, counters, mint.
|
|
3
|
+
*
|
|
4
|
+
* Pattern: pure projection (`projectWalk`) + one guarded side effect
|
|
5
|
+
* (`mintWalk`, which files the artifact and NEVER fails the answer).
|
|
6
|
+
* Role: core/runbook. The walk is not a debugging extra — a procedure that
|
|
7
|
+
* cannot show how it reached a verdict is a procedure somebody has
|
|
8
|
+
* to take on trust.
|
|
9
|
+
* Emits: nothing (the artifact capability emits its own `artifacts.*`).
|
|
10
|
+
*
|
|
11
|
+
* THE CAP LAW, and why a head slice is the wrong cap: in a chart walk the
|
|
12
|
+
* per-key reads and writes come FIRST and the decisions come LAST, so a
|
|
13
|
+
* naive head slice over a fleet sweep keeps four hundred writes and drops
|
|
14
|
+
* every decision — a walk with the walking taken out. When the whole thing
|
|
15
|
+
* does not fit, the CONTROL FLOW survives (stages, forks, subflows, and
|
|
16
|
+
* every `condition` entry carrying its decide() evidence) and the projection
|
|
17
|
+
* is DECLARED, so a reader knows which of the two they are holding.
|
|
18
|
+
*/
|
|
19
|
+
import { CHART_WALK_ARTIFACT_KIND, chartWalkPutInput } from '../../artifacts/recordingArtifact.js';
|
|
20
|
+
/** The default row cap — a readable artifact, not an archive. The count is
|
|
21
|
+
* always reported, so a truncated walk says it is truncated instead of
|
|
22
|
+
* looking like a short run. */
|
|
23
|
+
export const DEFAULT_WALK_CAP = 500;
|
|
24
|
+
function toWalkRow(entry, step) {
|
|
25
|
+
return {
|
|
26
|
+
step,
|
|
27
|
+
type: entry.type,
|
|
28
|
+
depth: entry.depth,
|
|
29
|
+
stage: entry.stageName ?? null,
|
|
30
|
+
stage_id: entry.stageId ?? null,
|
|
31
|
+
runtime_stage_id: entry.runtimeStageId ?? null,
|
|
32
|
+
subflow: entry.subflowId ?? null,
|
|
33
|
+
text: entry.text,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
/** Apply the cap law. Counters are about the WHOLE narrative (`total`), so a
|
|
37
|
+
* projected walk cannot read as a short run. */
|
|
38
|
+
export function projectWalk(entries, cap) {
|
|
39
|
+
const full = entries.length <= cap;
|
|
40
|
+
const projection = full ? 'full' : 'control-flow';
|
|
41
|
+
const chosen = full ? entries : entries.filter((entry) => entry.type !== 'step');
|
|
42
|
+
const rows = chosen.slice(0, cap).map(toWalkRow);
|
|
43
|
+
return {
|
|
44
|
+
rows,
|
|
45
|
+
projection,
|
|
46
|
+
shown: rows.length,
|
|
47
|
+
total: entries.length,
|
|
48
|
+
complete: rows.length === entries.length,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/** The descriptor's standing sentence, plus the projection clause when the
|
|
52
|
+
* control flow is what survived. */
|
|
53
|
+
function walkNote(projection) {
|
|
54
|
+
return ("The chart's own walk, one row per execution step. The `condition` rows carry the " +
|
|
55
|
+
'rule that matched, the values it compared and the branch it chose — that is where a ' +
|
|
56
|
+
'verdict can be checked rather than taken on trust.' +
|
|
57
|
+
(projection === 'control-flow'
|
|
58
|
+
? ' This one is the CONTROL-FLOW projection: the walk did not fit, so the stages, ' +
|
|
59
|
+
'forks, subflows and decisions are here and the per-key reads and writes are not.'
|
|
60
|
+
: ''));
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* File the walk and build the descriptor. THE SPINE ALWAYS GETS A
|
|
64
|
+
* DESCRIPTOR: with no store attached, or when the mint fails, the counters
|
|
65
|
+
* still travel and the note names why there is no ticket — a failed mint
|
|
66
|
+
* costs the TICKET, never the answer and never the counts.
|
|
67
|
+
*/
|
|
68
|
+
export async function mintWalk(ctx, projected, facts) {
|
|
69
|
+
const base = {
|
|
70
|
+
rows: projected.rows.length,
|
|
71
|
+
steps_executed: facts.stepsExecuted,
|
|
72
|
+
projection: projected.projection,
|
|
73
|
+
shown: projected.shown,
|
|
74
|
+
total: projected.total,
|
|
75
|
+
complete: projected.complete,
|
|
76
|
+
walk_segment: 'full',
|
|
77
|
+
};
|
|
78
|
+
if (!ctx.hasArtifacts || projected.rows.length === 0) {
|
|
79
|
+
return {
|
|
80
|
+
...base,
|
|
81
|
+
note: projected.rows.length === 0
|
|
82
|
+
? 'The run produced no narrative entries, so there was no walk to file.'
|
|
83
|
+
: walkNote(projected.projection) +
|
|
84
|
+
' No artifact store is attached to this run, so the walk was recorded but not ' +
|
|
85
|
+
'filed — the counters above are still the truth about it.',
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
const meta = await ctx.artifacts.put(chartWalkPutInput([...projected.rows], {
|
|
90
|
+
toolName: facts.toolName,
|
|
91
|
+
toolCallId: facts.toolCallId,
|
|
92
|
+
...(facts.runId !== undefined && { runId: facts.runId }),
|
|
93
|
+
}));
|
|
94
|
+
return {
|
|
95
|
+
ref: meta.ref,
|
|
96
|
+
kind: CHART_WALK_ARTIFACT_KIND,
|
|
97
|
+
...base,
|
|
98
|
+
note: walkNote(projected.projection),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
catch (err) {
|
|
102
|
+
return {
|
|
103
|
+
...base,
|
|
104
|
+
note: walkNote(projected.projection) +
|
|
105
|
+
` The walk could not be filed (${err instanceof Error ? err.message : String(err)}) — ` +
|
|
106
|
+
'the mint failure cost the ticket, never the answer.',
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
//# sourceMappingURL=walk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"walk.js","sourceRoot":"","sources":["../../../../src/core/runbook/walk.ts"],"names":[],"mappings":"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"}
|