@typedstandards/civic-typed-harness 0.5.0 → 0.7.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/CHANGELOG.md +130 -0
- package/README.md +32 -4
- package/dist/capture/data-sources.d.ts +24 -21
- package/dist/capture/data-sources.js +19 -22
- package/dist/capture/provenance.d.ts +80 -35
- package/dist/capture/provenance.js +76 -68
- package/dist/capture/run-attributes.d.ts +35 -0
- package/dist/capture/run-attributes.js +105 -0
- package/dist/capture/trace.d.ts +2 -4
- package/dist/capture/trace.js +3 -9
- package/dist/civic/data-sources.d.ts +58 -0
- package/dist/civic/data-sources.js +63 -0
- package/dist/civic/provenance.d.ts +63 -0
- package/dist/civic/provenance.js +86 -0
- package/dist/civic/run-servers.d.ts +11 -0
- package/dist/civic/run-servers.js +35 -0
- package/dist/civic/trace.d.ts +4 -0
- package/dist/civic/trace.js +13 -0
- package/dist/format/dathere.d.ts +99 -8
- package/dist/format/dathere.js +158 -13
- package/dist/format/sources.d.ts +12 -2
- package/dist/format/sources.js +12 -2
- package/dist/format/vocabulary.d.ts +21 -0
- package/dist/format/vocabulary.js +26 -3
- package/dist/index.d.ts +5 -2
- package/dist/index.js +19 -4
- package/package.json +3 -3
- /package/dist/{capture → civic}/data-sources.assert.d.ts +0 -0
- /package/dist/{capture → civic}/data-sources.assert.js +0 -0
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,136 @@
|
|
|
3
3
|
Factual record of what changed per published version. Section references are
|
|
4
4
|
to the Typed Standards specification unless noted otherwise.
|
|
5
5
|
|
|
6
|
+
## 0.7.0 — 2026-10-15
|
|
7
|
+
|
|
8
|
+
- **The seam inside the harness** ([civic-ai-tools#244](https://github.com/npstorey/civic-ai-tools/issues/244) P2).
|
|
9
|
+
`src/capture/provenance.ts` and `src/capture/data-sources.ts` import
|
|
10
|
+
nothing from `src/format/`, type-only imports included. The capture
|
|
11
|
+
builders take the vocabulary, the sixteen `civic:` term names, the source
|
|
12
|
+
registry, the fallback and skill source ids and the dataset-keyed fact as
|
|
13
|
+
required parameters.
|
|
14
|
+
- **What moved.** `buildProvenanceGraph`, `ProvenanceConfig` and
|
|
15
|
+
`CIVICAITOOLS_PROVENANCE_CONFIG` moved to `src/civic/provenance.ts`;
|
|
16
|
+
`buildDataSources`, `resolveToolSource` and `DataSourceOptions` moved to
|
|
17
|
+
`src/civic/data-sources.ts`. These modules apply the civic defaults
|
|
18
|
+
(`CIVIC_VOCABULARY`, `CIVIC_SOURCE_REGISTRY`, `FALLBACK_SOURCE_ID`,
|
|
19
|
+
`civicToolSourceResolver`, `isDatasetKeyedSource`) and call the capture
|
|
20
|
+
builders. The compile-only pin `data-sources.assert.ts` moved with
|
|
21
|
+
`buildDataSources`, byte-unchanged.
|
|
22
|
+
- **The API is unchanged.** The package entry exports the same names with
|
|
23
|
+
the same signatures, and the capture builders are not exported. The golden
|
|
24
|
+
suites pass with no fixture edited.
|
|
25
|
+
- `src/purity.test.ts` gains the rule: a capture module imports no module from
|
|
26
|
+
outside `src/capture/`. A package import, such as
|
|
27
|
+
`@typedstandards/produce-core`, is allowed.
|
|
28
|
+
|
|
29
|
+
The record fields of [ADR-0033](../../docs/adr/0033-composed-harness-record-fields.md)
|
|
30
|
+
(sprint R2, H2). Signed bytes move only where one of these inputs is passed,
|
|
31
|
+
where a call's source is not in the registry, and in a datHere environment
|
|
32
|
+
block that no longer states two placeholders.
|
|
33
|
+
|
|
34
|
+
- **Every call is attributed to its own source** (ADR-0033 §A). A call tagged
|
|
35
|
+
with a source the registry lacks was attributed to Socrata's agent — in a
|
|
36
|
+
run where Socrata made no call, an agent the graph did not contain — and is
|
|
37
|
+
now attributed to its own source's agent. An untagged span resolves by its
|
|
38
|
+
tool name, as `dataSources` already did (`ProvenanceConfig.resolver`, new,
|
|
39
|
+
default `civicToolSourceResolver`). A span neither identifies is attributed
|
|
40
|
+
to the new `UNKNOWN_SOURCE_ID` (`'unknown'`), the value the reference app
|
|
41
|
+
writes for a tool it cannot resolve; `resolveToolSource` and
|
|
42
|
+
`buildDataSources` default to it too, so an unknown call mints no
|
|
43
|
+
`dataSources` entry. `FALLBACK_SOURCE_ID` (`'socrata'`) stays exported and is
|
|
44
|
+
no longer a default for attribution. The capture builder's
|
|
45
|
+
`ProvenanceBuildConfig` gains a required `resolver`.
|
|
46
|
+
- **Each call states its upstream name, alias and server** (§B): the span
|
|
47
|
+
attributes `tool.upstream_name`, `mcp.alias` and `mcp.server.id` become
|
|
48
|
+
three new terms, `civic:upstreamToolName`, `civic:sourceAlias` and
|
|
49
|
+
`civic:serverId`, appended to the tool-call activity when present.
|
|
50
|
+
`ProvenanceTerms` gains the three keys.
|
|
51
|
+
- **The environment block states `temperature` and `toolDefinitions` only
|
|
52
|
+
when passed** (§C; [civic-ai-tools#240](https://github.com/npstorey/civic-ai-tools/issues/240)):
|
|
53
|
+
`buildDatHereEnvironment` takes an optional fourth argument and
|
|
54
|
+
`DatHerePolicyInput` two optional fields. Both keep their 0.6.0 key
|
|
55
|
+
positions, so passing the recorded `0` and `[]` reproduces an earlier
|
|
56
|
+
package byte for byte. Specification §8.7.1 requirement 3 is amended in
|
|
57
|
+
v0.1.11 to match.
|
|
58
|
+
- **The run-time servers win** (§D; civic-ai-tools-website#495):
|
|
59
|
+
`DatHerePolicyInput.runMcpServers` is preferred over `mcpServers` and
|
|
60
|
+
`skillMcpServerUrl`; `withRunServerAddresses(registry, servers)` (new) gives
|
|
61
|
+
the graph's source agents the run's addresses.
|
|
62
|
+
- **A new `org.civicaitools.composition` block** (§E;
|
|
63
|
+
civic-ai-tools-website#475): `assertedDate`, `sourcesOffered` and
|
|
64
|
+
`components`, under every content profile, emitted only when one is
|
|
65
|
+
passed. `COMPOSITION_EXTENSION_KEY`, `SOURCE_OFFERED_REASONS`,
|
|
66
|
+
`GUIDANCE_ORIGINS`, `buildCompositionExtension` and `RecordFieldError`
|
|
67
|
+
(new). A value outside the record's form is refused, never dropped.
|
|
68
|
+
- **`extractRunAttributes(trace)`** (new, capture): reads `analysis.asserted_date`,
|
|
69
|
+
`analysis.mcp_servers`, `analysis.tool_definitions`,
|
|
70
|
+
`analysis.sources_offered` and `analysis.components` from the trace's root
|
|
71
|
+
span; a present attribute that does not parse throws `RunAttributeError`.
|
|
72
|
+
- **`CIVICAITOOLS_TRACE_CONFIG` moved to `src/civic/trace.ts`**
|
|
73
|
+
([civic-ai-tools#246](https://github.com/npstorey/civic-ai-tools/issues/246)),
|
|
74
|
+
re-exported under the same name. `src/purity.test.ts` gains the rule: no
|
|
75
|
+
capture module exports a value binding.
|
|
76
|
+
- **The goldens.** No frozen fixture is edited, and `src/frozen-fixtures.test.ts`
|
|
77
|
+
(new) pins the sha256 of `website-golden.json` and `reference-golden.json`.
|
|
78
|
+
The website golden's derived graph moves in one field (tool-4's
|
|
79
|
+
`prov:wasAssociatedWith`), reached by a second named lift with its count
|
|
80
|
+
asserted; `span-carrying-golden.json`'s two recorded hashes are regenerated
|
|
81
|
+
with a dated note. `reference-golden.json`'s eight cases reproduce byte for
|
|
82
|
+
byte given their recorded environment values, and a new-default leg pins
|
|
83
|
+
the bytes without them.
|
|
84
|
+
- `capture/data-sources.test.ts`'s "Unknown tool with no trace span defaults
|
|
85
|
+
to socrata (pre-M9.1 backward compat)" is rewritten: the tool is `unknown`.
|
|
86
|
+
|
|
87
|
+
The dependency floor (sprint R2, G4).
|
|
88
|
+
|
|
89
|
+
- **Dependency floor raised:** `@typedstandards/produce-core` `^0.7.0` →
|
|
90
|
+
`^0.8.0`, and the `@typedstandards/verify-core` devDependency `^0.12.0` →
|
|
91
|
+
`^0.13.0`, the version produce-core 0.8.0 resolves. Under 0.x semver
|
|
92
|
+
`^0.7.0` excludes 0.8.0, so a consumer that pins produce-core `^0.8.0`
|
|
93
|
+
would install a second copy of each core beside this package's.
|
|
94
|
+
- **What the two releases bring.** produce-core 0.8.0 emits four more
|
|
95
|
+
attestation sub-types and adds `EnvelopeInput.vcsRef` and raw-bytes/v1 over
|
|
96
|
+
a BlobRef; an input that supplies none of them builds the same bytes.
|
|
97
|
+
verify-core 0.13.0 adds the `superseded` lifecycle status, the key binding
|
|
98
|
+
in `verifyLifecycleChain` and `checkAttestationNode`. The harness reads none
|
|
99
|
+
of these.
|
|
100
|
+
- The lockfile resolves produce-core 0.8.0 and verify-core 0.13.0, one copy
|
|
101
|
+
each, and no other entry moves. The golden byte-compat suite passes
|
|
102
|
+
unchanged.
|
|
103
|
+
- A consumer that pins produce-core 0.7.x no longer shares its copy with this
|
|
104
|
+
package.
|
|
105
|
+
- **README:** the runtime-dependency line names `^0.8.0`.
|
|
106
|
+
|
|
107
|
+
## 0.6.0 — 2026-09-26
|
|
108
|
+
|
|
109
|
+
The list is derived from
|
|
110
|
+
`git log 65e8a1d..<this release> -- packages/civic-typed-harness/` — `65e8a1d`
|
|
111
|
+
is the 0.5.0 release's last commit, its dating fix. No harness source changes
|
|
112
|
+
in that range.
|
|
113
|
+
|
|
114
|
+
- **Dependency floor raised:** `@typedstandards/produce-core` `^0.3.0 ||
|
|
115
|
+
^0.4.0` → `^0.7.0`, and the `@typedstandards/verify-core` devDependency
|
|
116
|
+
`^0.9.0` → `^0.12.0`, the version produce-core 0.7.0 resolves. The two move
|
|
117
|
+
together so the install holds one copy of verify-core: with only the
|
|
118
|
+
devDependency moved, produce-core 0.3.0 kept verify-core 0.9.0 and the
|
|
119
|
+
harness tests imported a second copy. The raise went to `^0.6.0` /
|
|
120
|
+
`^0.11.0` first ([civic-ai-tools#232](https://github.com/npstorey/civic-ai-tools/issues/232))
|
|
121
|
+
and was not published at that range.
|
|
122
|
+
- **What the two releases bring.** verify-core 0.11.0 is the first release
|
|
123
|
+
that exports `KNOWN_TYPE_URIS`, which the hub's `check:verifier-subtype-set`
|
|
124
|
+
compares with the specification's §8.12.1 table. verify-core 0.12.0 (the
|
|
125
|
+
certificate-chain split, typedstandards#100) adds a member to
|
|
126
|
+
`ChainFailReason` and to `Rfc3161FailReason`; the harness reads neither.
|
|
127
|
+
produce-core 0.7.0 changes none of its own source.
|
|
128
|
+
- The lockfile resolves produce-core 0.7.0 and verify-core 0.12.0, one copy
|
|
129
|
+
each, and no other entry moves. The golden byte-compat suite passes
|
|
130
|
+
unchanged.
|
|
131
|
+
- A consumer that pins produce-core 0.3.x through 0.6.x no longer shares its
|
|
132
|
+
copy with this package: under 0.x semver `^0.7.0` excludes them.
|
|
133
|
+
- **README:** the runtime-dependency line named produce-core `^0.2.0`, a range
|
|
134
|
+
this package has not declared since 0.3.0. It names `^0.7.0`.
|
|
135
|
+
|
|
6
136
|
## 0.5.0 — 2026-09-18
|
|
7
137
|
|
|
8
138
|
The list is derived from
|
package/README.md
CHANGED
|
@@ -9,10 +9,11 @@ The civic **domain harness** for [Typed Standards](../../docs/architecture/typed
|
|
|
9
9
|
| Module group | Modules | What it is |
|
|
10
10
|
|---|---|---|
|
|
11
11
|
| **`src/format/`** — the civic format-extension | `vocabulary.ts`, `sources.ts`, `dathere.ts`, `profiles.ts` | What extends the *standard*: the `civic:` JSON-LD namespace and `urn:civic-record:` id scheme (both in two eras — new emissions mint the settlement-era terms, the prior-era terms stay exported and reproducible because they are frozen inside already-signed records; spec Appendix J), the civic source registry, the datHere envelope-policy derivations (producerProfile, canonicalization rule, summary emission, environment extension), and the captureMethod vocabulary surface. |
|
|
12
|
-
| **`src/capture/`** — the capture machinery | `trace.ts`, `skill-metadata.ts`, `data-sources.ts`, `provenance.ts` | What *produces* under it: the OTel `TraceBuilder`, skill-metadata extraction, data-source population, and the provenance **builder
|
|
12
|
+
| **`src/capture/`** — the capture machinery | `trace.ts`, `skill-metadata.ts`, `run-attributes.ts`, `data-sources.ts`, `provenance.ts` | What *produces* under it: the OTel `TraceBuilder`, skill-metadata extraction, run-attribute extraction (`extractRunAttributes`), data-source population, and the provenance **builder**, which walks the trace. Capture imports no module from outside `src/capture/`; its one import beyond it is the `@typedstandards/produce-core` package. The vocabulary, the term names, the source registry and their defaults arrive as parameters, and capture defines no deployment value (it exports no `const`). |
|
|
13
|
+
| **`src/civic/`** — the civic defaults over capture | `data-sources.ts`, `provenance.ts`, `run-servers.ts`, `trace.ts` | The package's `buildDataSources`, `resolveToolSource` and `buildProvenanceGraph`, with `DataSourceOptions`, `ProvenanceConfig` and `CIVICAITOOLS_PROVENANCE_CONFIG`: they pass the format group's vocabulary, terms and registry to the capture builders and apply the civic defaults. Also `withRunServerAddresses` and the reference deployment's trace identity, `CIVICAITOOLS_TRACE_CONFIG`. |
|
|
13
14
|
| **`src/rubric/`** — the adversarial rubric | `adversarial-eval-core.ts` | The civic six-criterion evaluation rubric (pure core: rubric text, pinned version hash, prompt builder, response parser). The model runner and result emission stay implementation-side. |
|
|
14
15
|
|
|
15
|
-
**The internal boundary is load-bearing:** no capture module defines vocabulary, and no format-extension module walks a trace — this sentence reserves a future split of the format-extension group into its own package (a civic extension *of Typed Standards*, distinct from the civic *harness* proper) as a mechanical move rather than a re-partition. The boundary is enforced by `src/purity.test.ts`.
|
|
16
|
+
**The internal boundary is load-bearing:** no capture module defines vocabulary or imports a module from outside `src/capture/` (a package import, `@typedstandards/produce-core`, is allowed), and no format-extension module walks a trace — this sentence reserves a future split of the format-extension group into its own package (a civic extension *of Typed Standards*, distinct from the civic *harness* proper) as a mechanical move rather than a re-partition. The boundary is enforced by `src/purity.test.ts`.
|
|
16
17
|
|
|
17
18
|
## Config, not constants — and required, never defaulted
|
|
18
19
|
|
|
@@ -45,17 +46,44 @@ const graph = buildProvenanceGraph(trace, input, CIVICAITOOLS_PROVENANCE_CONFIG)
|
|
|
45
46
|
|
|
46
47
|
Likewise: `buildDatHereEnvironment` / `deriveDatHereEnvelopeFields` take `CIVICAITOOLS_ENVIRONMENT_CONFIG`, `new TraceBuilder(...)` takes `CIVICAITOOLS_TRACE_CONFIG`, and `isDatasetKeyedSource` / `displayNameForSource` / `formatDataSourcesSummary` take `CIVIC_SOURCE_REGISTRY`. The acceptance condition is encoded in `src/required-config.assert.ts`: reintroducing any of the removed defaults fails `npm run typecheck`.
|
|
47
48
|
|
|
49
|
+
## What a record states about its run (0.7.0, [ADR-0033](../../docs/adr/0033-composed-harness-record-fields.md))
|
|
50
|
+
|
|
51
|
+
**Every call is attributed to its own source.** A tool span's source is its `mcp.source` tag; an untagged span resolves by its tool name through the resolver (`ProvenanceConfig.resolver`, default `civicToolSourceResolver`); a span neither identifies is attributed to `UNKNOWN_SOURCE_ID` (`'unknown'`), never to Socrata. The same order holds in `dataSources[]`, where an unknown call mints no entry. A caller may still pass `fallbackSourceId` explicitly, for example `'socrata'` to reproduce an earlier record of an untagged span.
|
|
52
|
+
|
|
53
|
+
**Each call states both its names, its alias and its server**, when the span carries them: `tool.upstream_name`, `mcp.alias` and `mcp.server.id` become `civic:upstreamToolName`, `civic:sourceAlias` and `civic:serverId` on the tool-call activity. `civic:toolName` stays the name the model called.
|
|
54
|
+
|
|
55
|
+
**The environment block states only what is known.** `temperature` and `toolDefinitions` are emitted only when passed (`DatHerePolicyInput.temperature`, `.toolDefinitions`; spec §8.7.1 requirement 3 as amended in v0.1.11). To reproduce a package made before 0.7.0, pass the values it recorded (`temperature: 0`, `toolDefinitions: []`); its bytes come back exactly.
|
|
56
|
+
|
|
57
|
+
**Run-time facts come from the trace.** The producer writes them on the trace's root span at run time, and `extractRunAttributes(trace)` returns them under the derivation's input names:
|
|
58
|
+
|
|
59
|
+
| Root-span attribute (JSON text unless noted) | Input | Lands in |
|
|
60
|
+
|---|---|---|
|
|
61
|
+
| `analysis.asserted_date` (plain `YYYY-MM-DD`) | `assertedDate` | `extensions["org.civicaitools.composition"].assertedDate` |
|
|
62
|
+
| `analysis.mcp_servers` | `runMcpServers` | `org.civicaitools.environment.mcpServers` (wins over `mcpServers` and `skillMcpServerUrl`); `withRunServerAddresses(registry, servers)` gives the graph's agents the run's addresses |
|
|
63
|
+
| `analysis.tool_definitions` | `toolDefinitions` | `org.civicaitools.environment.toolDefinitions` |
|
|
64
|
+
| `analysis.sources_offered` | `sourcesOffered` | `org.civicaitools.composition.sourcesOffered` |
|
|
65
|
+
| `analysis.components` | `components` | `org.civicaitools.composition.components` |
|
|
66
|
+
|
|
67
|
+
```ts
|
|
68
|
+
const fields = deriveDatHereEnvelopeFields(
|
|
69
|
+
{ model, contentProfile, summary, extensions, ...extractRunAttributes(trace) },
|
|
70
|
+
{ host },
|
|
71
|
+
);
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
An absent attribute is an absent field. A present attribute that does not parse is refused (`RunAttributeError`), and a value outside the record's form — a date that is not a calendar date, a reason outside `configured` / `not-configured` / `selected` / `not-selected`, a guidance hash that is not lowercase hex SHA-256, an origin outside `endpoint` / `inline` / `fallback` — is refused by the derivation (`RecordFieldError`), never dropped. The `org.civicaitools.composition` block is emitted under every content profile, appended after the other extensions, and only when one of its fields is passed: a package built with none of these inputs has 0.6.0's extensions bytes, except that a datHere environment block no longer states the two placeholders.
|
|
75
|
+
|
|
48
76
|
## Purity contract (harness-grade)
|
|
49
77
|
|
|
50
78
|
No I/O, no network, no environment reads, no Node built-ins, browser-safe — anywhere in the package. Clock and RNG are permitted **in capture modules only** (span timestamps and ids are what capture *is*) and are injectable for deterministic tests. Enforced mechanically twice: `eslint.config.mjs` (`no-restricted-imports` / `-globals` / `-properties` / `-syntax`) and `src/purity.test.ts` (browser-safety + determinism + module-boundary checks under `node --test`).
|
|
51
79
|
|
|
52
|
-
Runtime dependency: **`@typedstandards/produce-core` (`^0.
|
|
80
|
+
Runtime dependency: **`@typedstandards/produce-core` (`^0.8.0`) only.** The verify-core primitives the harness consumes (`sha256Hex`, `isBlobRef`, the canonicalization-rule URIs, the Q32 vocabulary table, the `CaptureMethod` type) are reached through produce-core's 0.2.0 re-exports rather than imported from `@typedstandards/verify-core` directly — one declared dependency, no phantom, and still one hashing implementation across producer, harness, and verifier by construction (produce-core re-exports the same bindings verify-core defines, verified by reference identity).
|
|
53
81
|
|
|
54
82
|
> **Q32 adjacency** (open-questions registry): the captureMethod vocabulary re-exported from `src/format/profiles.ts` is verify-core's hardcoded fallback table, pending versioned, content-addressed guidance bundles. Flagged, not solved, here.
|
|
55
83
|
|
|
56
84
|
## Byte-compatibility
|
|
57
85
|
|
|
58
|
-
The port is tested for **byte parity** against golden outputs captured from the reference implementation (`src/__fixtures__/website-golden.json`): with the reference config passed explicitly, `buildProvenanceGraph` and `buildDataSources` reproduce the app's output byte-for-byte, and `RUBRIC_VERSION_SHA256` is asserted against the exact reference digest (moving the rubric changes no hashes). Both captured fixtures predate the 2026-08-19 vocabulary settlement, so their byte-parity legs run with `PRIOR_ERA_CIVIC_VOCABULARY` injected — the fixtures are never rewritten — beside a settlement-era leg asserting the same bytes with exactly the two Appendix J literals substituted. Fixture-level byte-compat against produce-core's `reference-golden.json` (`src/golden-reproduction.test.ts`) and the produce→verify composition round-trip (`src/composition-roundtrip.test.ts`) landed in S2 P2 and run with the suite.
|
|
86
|
+
The port is tested for **byte parity** against golden outputs captured from the reference implementation (`src/__fixtures__/website-golden.json`): with the reference config passed explicitly, `buildProvenanceGraph` and `buildDataSources` reproduce the app's output byte-for-byte, lifted over the two behaviours the harness has deliberately changed since (the #205 address fallback in 0.5.0 and the fallback attribution in 0.7.0, each a named transform of the frozen bytes with its node count asserted; both frozen fixtures' sha256 is asserted by `src/frozen-fixtures.test.ts`), and `RUBRIC_VERSION_SHA256` is asserted against the exact reference digest (moving the rubric changes no hashes). Both captured fixtures predate the 2026-08-19 vocabulary settlement, so their byte-parity legs run with `PRIOR_ERA_CIVIC_VOCABULARY` injected — the fixtures are never rewritten — beside a settlement-era leg asserting the same bytes with exactly the two Appendix J literals substituted. Fixture-level byte-compat against produce-core's `reference-golden.json` (`src/golden-reproduction.test.ts`) and the produce→verify composition round-trip (`src/composition-roundtrip.test.ts`) landed in S2 P2 and run with the suite.
|
|
59
87
|
|
|
60
88
|
## Develop
|
|
61
89
|
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import type { DataSourceEntry } from '@typedstandards/produce-core';
|
|
2
|
-
import { type CivicSourceRegistry, type ToolSourceResolver } from '../format/sources.ts';
|
|
3
2
|
export type { DataSourceEntry };
|
|
4
3
|
/** The caller-supplied per-tool-call summary the population walks alongside
|
|
5
4
|
* the trace. */
|
|
@@ -23,7 +22,9 @@ export interface ToolCallSummary {
|
|
|
23
22
|
* rejection. Added 0.4.0. */
|
|
24
23
|
failureKind?: string;
|
|
25
24
|
}
|
|
26
|
-
|
|
25
|
+
/** The span shape the resolver inspects. Exported for the civic layer's
|
|
26
|
+
* signatures; the package entry does not export it. */
|
|
27
|
+
export interface TraceSpan {
|
|
27
28
|
name: string;
|
|
28
29
|
attributes?: Array<{
|
|
29
30
|
key: string;
|
|
@@ -34,18 +35,23 @@ interface TraceSpan {
|
|
|
34
35
|
};
|
|
35
36
|
}>;
|
|
36
37
|
}
|
|
37
|
-
/**
|
|
38
|
-
*
|
|
39
|
-
export interface
|
|
38
|
+
/** Every input the population reads besides the calls and the trace, each
|
|
39
|
+
* one explicit. */
|
|
40
|
+
export interface DataSourceBuildConfig {
|
|
40
41
|
/** Tool-name → source-id resolver (fallback when a span carries no
|
|
41
|
-
* `mcp.source` attribute).
|
|
42
|
-
resolver
|
|
43
|
-
/** Source
|
|
44
|
-
*
|
|
45
|
-
registry
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
42
|
+
* `mcp.source` attribute). */
|
|
43
|
+
resolver: (toolName: string) => string | undefined;
|
|
44
|
+
/** Source id → catalog type and, for an aggregate source, the portal URL of
|
|
45
|
+
* its single entry. Insertion order is emission order. */
|
|
46
|
+
registry: Record<string, {
|
|
47
|
+
catalogType: string;
|
|
48
|
+
aggregatePortalUrl?: string;
|
|
49
|
+
}>;
|
|
50
|
+
/** Source id for calls neither the trace nor the resolver can identify. */
|
|
51
|
+
fallbackSourceId: string;
|
|
52
|
+
/** Is this source dataset-keyed (one entry per dataset)? Supplied by the
|
|
53
|
+
* caller, consistent with `registry`. */
|
|
54
|
+
isDatasetKeyed: (sourceId: string) => boolean;
|
|
49
55
|
}
|
|
50
56
|
/**
|
|
51
57
|
* Resolve the MCP source for a tool call. Prefers the `mcp.source` attribute
|
|
@@ -58,7 +64,7 @@ export interface DataSourceOptions {
|
|
|
58
64
|
* flow. When the counts diverge, the static resolver still identifies the
|
|
59
65
|
* source.
|
|
60
66
|
*/
|
|
61
|
-
export declare function
|
|
67
|
+
export declare function resolveToolSourceWith(toolCall: ToolCallSummary, span: TraceSpan | undefined, resolver: (toolName: string) => string | undefined, fallbackSourceId: string): string;
|
|
62
68
|
/**
|
|
63
69
|
* Build the per-source evidence-package `dataSources` array.
|
|
64
70
|
*
|
|
@@ -81,11 +87,8 @@ export declare function resolveToolSource(toolCall: ToolCallSummary, span: Trace
|
|
|
81
87
|
* activities and in the caller's own `queries[]` — what it is not is an
|
|
82
88
|
* assertion, inside signed bytes, that a source was reached at a timestamp.
|
|
83
89
|
*
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
* positional
|
|
87
|
-
* it also accepts `undefined`, which is what a caller that has stopped
|
|
88
|
-
* consulting it should pass. Dropping it is a breaking change and waits for
|
|
89
|
-
* a major.
|
|
90
|
+
* The run's portal is not a parameter: an entry states the portal the call
|
|
91
|
+
* carried, never the run's (the civic layer's `buildDataSources` keeps its
|
|
92
|
+
* inert `fallbackPortal` positional for existing callers).
|
|
90
93
|
*/
|
|
91
|
-
export declare function
|
|
94
|
+
export declare function buildDataSourcesWith(toolCalls: ToolCallSummary[], trace: Record<string, unknown>, now: string, config: DataSourceBuildConfig): DataSourceEntry[];
|
|
@@ -2,17 +2,19 @@
|
|
|
2
2
|
// spans plus the caller-supplied tool-call summary to produce one
|
|
3
3
|
// `dataSources` entry per (source, datasetId) tuple. Relocated from
|
|
4
4
|
// civic-ai-tools-website `src/lib/evidence/data-sources.ts:78–185` per the S2
|
|
5
|
-
// brief §1
|
|
6
|
-
//
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
5
|
+
// brief §1.
|
|
6
|
+
//
|
|
7
|
+
// THE SEAM (civic-ai-tools#244 P2). This module imports nothing civic: the
|
|
8
|
+
// tool-name → source-id resolver, the source registry (catalog types,
|
|
9
|
+
// endpoints, the dataset-keyed vs aggregate split), the fallback source id
|
|
10
|
+
// and the dataset-keyed fact all arrive as parameters, every one of them
|
|
11
|
+
// required. The civic defaults are applied one layer up, in
|
|
12
|
+
// `src/civic/data-sources.ts`, which exports the package's `buildDataSources`,
|
|
13
|
+
// `resolveToolSource` and `DataSourceOptions` under their existing names and
|
|
14
|
+
// signatures.
|
|
12
15
|
//
|
|
13
16
|
// `DataSourceEntry` is produce-core's envelope input shape — the harness
|
|
14
17
|
// populates it, never redefines it.
|
|
15
|
-
import { CIVIC_SOURCE_REGISTRY, FALLBACK_SOURCE_ID, civicToolSourceResolver, isDatasetKeyedSource, } from "../format/sources.js";
|
|
16
18
|
function getToolSpans(trace) {
|
|
17
19
|
try {
|
|
18
20
|
// Untyped walk over the caller's trace shape.
|
|
@@ -42,7 +44,7 @@ function spanAttr(span, key) {
|
|
|
42
44
|
* flow. When the counts diverge, the static resolver still identifies the
|
|
43
45
|
* source.
|
|
44
46
|
*/
|
|
45
|
-
export function
|
|
47
|
+
export function resolveToolSourceWith(toolCall, span, resolver, fallbackSourceId) {
|
|
46
48
|
return spanAttr(span, 'mcp.source')
|
|
47
49
|
?? resolver(toolCall.name)
|
|
48
50
|
?? fallbackSourceId;
|
|
@@ -69,17 +71,12 @@ export function resolveToolSource(toolCall, span, resolver = civicToolSourceReso
|
|
|
69
71
|
* activities and in the caller's own `queries[]` — what it is not is an
|
|
70
72
|
* assertion, inside signed bytes, that a source was reached at a timestamp.
|
|
71
73
|
*
|
|
72
|
-
*
|
|
73
|
-
*
|
|
74
|
-
* positional
|
|
75
|
-
* it also accepts `undefined`, which is what a caller that has stopped
|
|
76
|
-
* consulting it should pass. Dropping it is a breaking change and waits for
|
|
77
|
-
* a major.
|
|
74
|
+
* The run's portal is not a parameter: an entry states the portal the call
|
|
75
|
+
* carried, never the run's (the civic layer's `buildDataSources` keeps its
|
|
76
|
+
* inert `fallbackPortal` positional for existing callers).
|
|
78
77
|
*/
|
|
79
|
-
export function
|
|
80
|
-
const resolver
|
|
81
|
-
const registry = options.registry ?? CIVIC_SOURCE_REGISTRY;
|
|
82
|
-
const fallbackSourceId = options.fallbackSourceId ?? FALLBACK_SOURCE_ID;
|
|
78
|
+
export function buildDataSourcesWith(toolCalls, trace, now, config) {
|
|
79
|
+
const { resolver, registry, fallbackSourceId } = config;
|
|
83
80
|
const toolSpans = getToolSpans(trace);
|
|
84
81
|
const datasetKeyed = new Map();
|
|
85
82
|
const aggregateAccessed = new Set();
|
|
@@ -87,12 +84,12 @@ export function buildDataSources(toolCalls, trace, fallbackPortal, now, options
|
|
|
87
84
|
const tc = toolCalls[i];
|
|
88
85
|
// A call the producer recorded as rejected reached no data, so it mints
|
|
89
86
|
// no entry on either branch below. The walk keeps its index rather than
|
|
90
|
-
// filtering the list: `
|
|
87
|
+
// filtering the list: `resolveToolSourceWith` pairs a call to `toolSpans[i]`,
|
|
91
88
|
// and a filtered list would shift every later call onto the wrong span.
|
|
92
89
|
if (tc.failed)
|
|
93
90
|
continue;
|
|
94
|
-
const source =
|
|
95
|
-
if (
|
|
91
|
+
const source = resolveToolSourceWith(tc, toolSpans[i], resolver, fallbackSourceId);
|
|
92
|
+
if (config.isDatasetKeyed(source)) {
|
|
96
93
|
const datasetId = tc.args.dataset_id;
|
|
97
94
|
const portal = tc.args.portal;
|
|
98
95
|
// An entry is minted only from what the call carried: a dataset id AND
|
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { type ProvGraph, type ProvNode } from '@typedstandards/produce-core';
|
|
2
|
-
import { type CivicVocabulary, type PlatformAgentConfig } from '../format/vocabulary.ts';
|
|
3
|
-
import { type CivicSourceRegistry } from '../format/sources.ts';
|
|
4
2
|
export type { ProvGraph, ProvNode };
|
|
5
3
|
/** Package-level inputs to the graph build. */
|
|
6
4
|
export interface ProvenanceInput {
|
|
@@ -27,41 +25,88 @@ export interface ProvenanceInput {
|
|
|
27
25
|
* literal an excess-property error, so removal waits for a major. */
|
|
28
26
|
portal?: string;
|
|
29
27
|
}
|
|
30
|
-
/**
|
|
31
|
-
|
|
28
|
+
/** The id scheme and `@context` the builder emits under. Structural: the
|
|
29
|
+
* format group's vocabulary objects satisfy it, and this module never
|
|
30
|
+
* imports them. */
|
|
31
|
+
export interface ProvenanceVocabulary {
|
|
32
|
+
/** The graph's JSON-LD `@context`, in emission order. */
|
|
33
|
+
context(): Record<string, string>;
|
|
34
|
+
/** Package-scoped node id. */
|
|
35
|
+
urn(packageId: string, type: string, id: string): string;
|
|
36
|
+
/** Model-agent id. */
|
|
37
|
+
modelUrn(model: string): string;
|
|
38
|
+
/** MCP source-agent id. The builder encodes an UNKNOWN id with
|
|
39
|
+
* `encodeURIComponent` before passing it. */
|
|
40
|
+
sourceAgentUrn(sourceId: string): string;
|
|
41
|
+
/** Platform-agent id. */
|
|
42
|
+
platformUrn(platformId: string): string;
|
|
43
|
+
}
|
|
44
|
+
/** The property names the builder keys its domain-specific properties with,
|
|
45
|
+
* one per property. The builder spells none of them; the civic layer passes
|
|
46
|
+
* the format group's declared terms. */
|
|
47
|
+
export interface ProvenanceTerms {
|
|
48
|
+
readonly contentHash: string;
|
|
49
|
+
readonly serverUrl: string;
|
|
50
|
+
readonly sourceId: string;
|
|
51
|
+
readonly url: string;
|
|
52
|
+
readonly promptTokens: string;
|
|
53
|
+
readonly completionTokens: string;
|
|
54
|
+
readonly toolName: string;
|
|
55
|
+
readonly operationType: string;
|
|
56
|
+
readonly datasetId: string;
|
|
57
|
+
readonly portalDomain: string;
|
|
58
|
+
readonly datasetUrl: string;
|
|
59
|
+
readonly croissantMetadataUrl: string;
|
|
60
|
+
readonly responseRows: string;
|
|
61
|
+
readonly durationMs: string;
|
|
62
|
+
readonly failed: string;
|
|
63
|
+
readonly failureKind: string;
|
|
64
|
+
/** The wire name of a call whose exposed name is aliased (ADR-0033 §B). */
|
|
65
|
+
readonly upstreamToolName: string;
|
|
66
|
+
/** The alias of the source that answered a call. */
|
|
67
|
+
readonly sourceAlias: string;
|
|
68
|
+
/** The identity of the server that answered a call. */
|
|
69
|
+
readonly serverId: string;
|
|
70
|
+
}
|
|
71
|
+
/** One source the builder can name as an agent. Structural: a format-group
|
|
72
|
+
* registry entry satisfies it. */
|
|
73
|
+
export interface ProvenanceSourceInfo {
|
|
74
|
+
/** The agent's `dcterms:title`. */
|
|
75
|
+
agentTitle: string;
|
|
76
|
+
/** The agent's server URL. Absent means the graph states no address for
|
|
77
|
+
* the source (civic-ai-tools#205). */
|
|
78
|
+
serverUrl?: string;
|
|
79
|
+
}
|
|
80
|
+
/** Every input the builder reads, each one explicit. */
|
|
81
|
+
export interface ProvenanceBuildConfig {
|
|
32
82
|
/** Platform-agent identity — the deployment publishing the record. */
|
|
33
|
-
platformAgent:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
83
|
+
platformAgent: {
|
|
84
|
+
id: string;
|
|
85
|
+
title: string;
|
|
86
|
+
url: string;
|
|
87
|
+
};
|
|
88
|
+
/** Source id → agent coordinates. Insertion order is agent order. */
|
|
89
|
+
sourceRegistry: Record<string, ProvenanceSourceInfo>;
|
|
90
|
+
/** Is this source dataset-keyed (its data responses carry the dataset id,
|
|
91
|
+
* portal and dataset URL)? Supplied by the caller, consistent with
|
|
92
|
+
* `sourceRegistry`. */
|
|
93
|
+
isDatasetKeyed: (sourceId: string) => boolean;
|
|
94
|
+
/** Tool-name → source-id resolver, consulted for a span that carries no
|
|
95
|
+
* `mcp.source` tag. */
|
|
96
|
+
resolver: (toolName: string) => string | undefined;
|
|
97
|
+
/** Source id for a span neither its tag nor the resolver identifies. */
|
|
98
|
+
fallbackSourceId: string;
|
|
99
|
+
/** Source whose agent server URL the trace's skill-fetch span URL
|
|
100
|
+
* overrides when present. */
|
|
101
|
+
skillSourceId: string;
|
|
102
|
+
/** `dcterms:description` of the model agent; omitted from the graph when
|
|
103
|
+
* unset, never filled with a fallback. */
|
|
49
104
|
modelAgentDescription?: string;
|
|
50
|
-
/**
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
* hash it was signed with. Unlike the other fields here this is not
|
|
55
|
-
* deployment identity, so it is defaulted rather than required: emitting
|
|
56
|
-
* the current vocabulary is never the silent-attribution hazard ADR-0024
|
|
57
|
-
* guards against. */
|
|
58
|
-
vocabulary?: CivicVocabulary;
|
|
105
|
+
/** Id scheme and `@context`. */
|
|
106
|
+
vocabulary: ProvenanceVocabulary;
|
|
107
|
+
/** Property names. */
|
|
108
|
+
terms: ProvenanceTerms;
|
|
59
109
|
}
|
|
60
|
-
/** The civicaitools.org reference deployment's values. Passed explicitly by
|
|
61
|
-
* the reference app — never applied as a default, and never spread into
|
|
62
|
-
* another instance's config, which would assert infrastructure that
|
|
63
|
-
* instance doesn't run. */
|
|
64
|
-
export declare const CIVICAITOOLS_PROVENANCE_CONFIG: ProvenanceConfig;
|
|
65
110
|
/**
|
|
66
111
|
* Build a W3C PROV-O JSON-LD graph from an OTel trace and package metadata.
|
|
67
112
|
*
|
|
@@ -71,4 +116,4 @@ export declare const CIVICAITOOLS_PROVENANCE_CONFIG: ProvenanceConfig;
|
|
|
71
116
|
* - Prompt, skill guidance, data responses, output → prov:Entity
|
|
72
117
|
* - LLM model, MCP server, platform → prov:Agent
|
|
73
118
|
*/
|
|
74
|
-
export declare function
|
|
119
|
+
export declare function buildProvenanceGraphWith(trace: Record<string, unknown>, input: ProvenanceInput, config: ProvenanceBuildConfig): ProvGraph;
|