@typedstandards/civic-typed-harness 0.2.0 → 0.3.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 CHANGED
@@ -3,6 +3,64 @@
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.3.0 — 2026-08-20
7
+
8
+ **Settlement-era civic vocabulary + produce-core 0.3.0**
9
+ ([civic-ai-tools#160](https://github.com/npstorey/civic-ai-tools/issues/160) —
10
+ the 2026-08-19 vocabulary settlement, [ADR-0025](../../docs/adr/0025-vocabulary-settlement-evidence-excision.md);
11
+ canonical mapping in the specification's **Appendix J**, migration class
12
+ *frozen-in-signed-artifacts*). Minor, not breaking: no existing export is
13
+ removed or retyped, and no already-published package changes.
14
+
15
+ - **New emissions mint the settlement-era vocabulary.** `CIVIC_NS` becomes
16
+ `https://civicaitools.org/ns/civic/` (was
17
+ `https://civicaitools.org/ns/evidence/`) and `CIVIC_URN_PREFIX` becomes
18
+ `urn:civic-record` (was `urn:civic-evidence`). Every id emitted by
19
+ `buildProvenanceGraph` and every `civic:` `@context` entry moves with them.
20
+ **A package produced by this version is not byte-identical to one produced
21
+ by 0.2.0 from the same inputs** — the graph is inside the hashed envelope,
22
+ so its envelope hash differs. Packages already signed under 0.2.0 are
23
+ untouched and remain verifiable exactly as published.
24
+ - **The prior era stays available, exported, and reproducible.** New:
25
+ `PRIOR_ERA_CIVIC_NS`, `PRIOR_ERA_CIVIC_URN_PREFIX`, the `CivicVocabulary`
26
+ type, `makeCivicVocabulary(ns, urnPrefix)`, and the two bound eras
27
+ `CIVIC_VOCABULARY` / `PRIOR_ERA_CIVIC_VOCABULARY`. `ProvenanceConfig` gains
28
+ an optional `vocabulary` field defaulting to `CIVIC_VOCABULARY`. Supplying
29
+ `PRIOR_ERA_CIVIC_VOCABULARY` reproduces a pre-settlement record
30
+ byte-for-byte — the only sanctioned use; it is never for new emissions.
31
+ Unlike the other `ProvenanceConfig` fields this one is defaulted rather than
32
+ required: emitting the current vocabulary is not the silent-attribution
33
+ hazard ADR-0024 guards against.
34
+ - **Unchanged signatures.** `civicUrn`, `civicModelUrn`, `civicSourceAgentUrn`,
35
+ `civicPlatformUrn`, and `makeCivicProvContext` keep their exact signatures
36
+ and now delegate to `CIVIC_VOCABULARY`, so callers of the settlement era need
37
+ no edits.
38
+ - **`buildEvaluationPrompt` takes `RecordPackage`** — produce-core 0.3.0's
39
+ settlement-era name for the same object (Appendix J, *alias-and-deprecate*).
40
+ `EvidencePackage` remains a deprecated upstream alias of that exact type, so
41
+ existing callers still compile; the rubric test suite deliberately stays on
42
+ the prior name as the executable alias proof.
43
+ - **Dependency floor raised:** `@typedstandards/produce-core` `^0.2.0` →
44
+ `^0.3.0`, and the `@typedstandards/verify-core` devDependency `^0.8.0` →
45
+ `^0.9.0` to match the version produce-core 0.3.0 resolves (same-commit
46
+ manifest bump across a minor boundary). No behavior-affecting patch bumps
47
+ ride along.
48
+ - **Both eras are under test.** The two captured fixtures
49
+ (`reference-golden.json`, `website-golden.json`) predate the settlement and
50
+ are not edited: their byte- and hash-parity legs now run with the prior-era
51
+ vocabulary injected, and a settlement-era leg asserts the same reference
52
+ bytes with exactly the two Appendix J literals substituted, with the hashes
53
+ recomputed from those substituted bytes by verify-core's shared chain. The
54
+ per-canonicalization-rule consequence is pinned explicitly: under
55
+ `legacy-json/v1` the era flip moves `contentHash` (the whole package is
56
+ fingerprinted, provenance included); under `dathere-ag-jupyter/v1` it does
57
+ not (only the executed notebook is). The envelope hash moves under both.
58
+ - **Purity guard widened:** the capture/format boundary test now bars
59
+ vocabulary literals of **both** eras from `src/capture/**`. The invariant is
60
+ "vocabulary lives only in `format/vocabulary.ts`", not "the prior-era strings
61
+ are gone" — both eras are real vocabulary now, and either could be redefined
62
+ in the wrong module.
63
+
6
64
  ## 0.2.0 — 2026-08-17
7
65
 
8
66
  **Breaking: identity-bearing config is now required**
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @typedstandards/civic-typed-harness
2
2
 
3
- The civic **domain harness** for [Typed Standards](../../docs/architecture/typed-standards-specification.md) evidence packages — the DOMAIN side of the format/domain line drawn in [ADR-0021](../../docs/adr/0021-produce-core-extraction.md), relocated from the reference app as an installable package ([ADR-0022](../../docs/adr/0022-civic-typed-harness-packaging.md)). The operating rule: **the harness derives, the core assembles** — everything here produces domain-derived values (civic vocabulary terms, datHere policy fields, provenance graphs, capture artifacts) that feed [`@typedstandards/produce-core`](https://www.npmjs.com/package/@typedstandards/produce-core)'s neutral envelope assembly.
3
+ The civic **domain harness** for [Typed Standards](../../docs/architecture/typed-standards-specification.md) record packages — the DOMAIN side of the format/domain line drawn in [ADR-0021](../../docs/adr/0021-produce-core-extraction.md), relocated from the reference app as an installable package ([ADR-0022](../../docs/adr/0022-civic-typed-harness-packaging.md)). The operating rule: **the harness derives, the core assembles** — everything here produces domain-derived values (civic vocabulary terms, datHere policy fields, provenance graphs, capture artifacts) that feed [`@typedstandards/produce-core`](https://www.npmjs.com/package/@typedstandards/produce-core)'s neutral envelope assembly.
4
4
 
5
5
  > **Name.** The package landed in-repo under the working name `civic-typed-harness` with the final npm name deferred (ADR-0022 §B). It publishes as **`@typedstandards/civic-typed-harness`** — the standard's scope with the full layer-3 basename; the rationale, the alternatives scored, and the named reversal condition are recorded in the planning-side naming memo and summarized in the [ADR-0022 addendum](../../docs/adr/0022-civic-typed-harness-packaging.md#addendum-2026-08-02--final-npm-name).
6
6
 
@@ -8,7 +8,7 @@ The civic **domain harness** for [Typed Standards](../../docs/architecture/typed
8
8
 
9
9
  | Module group | Modules | What it is |
10
10
  |---|---|---|
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-evidence:` id scheme, the civic source registry, the datHere envelope-policy derivations (producerProfile, canonicalization rule, summary emission, environment extension), and the captureMethod vocabulary surface. |
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
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** — which imports its vocabulary from the format group and walks the trace. |
13
13
  | **`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
14
 
@@ -55,7 +55,7 @@ Runtime dependency: **`@typedstandards/produce-core` (`^0.2.0`) only.** The veri
55
55
 
56
56
  ## Byte-compatibility
57
57
 
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). 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.
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.
59
59
 
60
60
  ## Develop
61
61
 
@@ -1,5 +1,5 @@
1
1
  import { type ProvGraph, type ProvNode } from '@typedstandards/produce-core';
2
- import { type PlatformAgentConfig } from '../format/vocabulary.ts';
2
+ import { type CivicVocabulary, type PlatformAgentConfig } from '../format/vocabulary.ts';
3
3
  import { type CivicSourceRegistry } from '../format/sources.ts';
4
4
  export type { ProvGraph, ProvNode };
5
5
  /** Package-level inputs to the graph build. */
@@ -19,7 +19,7 @@ export interface ProvenanceInput {
19
19
  }
20
20
  /** Instance configuration for the graph build (config-not-constants). */
21
21
  export interface ProvenanceConfig {
22
- /** Platform-agent identity — the deployment publishing the evidence. */
22
+ /** Platform-agent identity — the deployment publishing the record. */
23
23
  platformAgent: PlatformAgentConfig;
24
24
  /** Source registry supplying agent titles and MCP server URLs. */
25
25
  sourceRegistry: CivicSourceRegistry;
@@ -34,6 +34,15 @@ export interface ProvenanceConfig {
34
34
  * carries no `dcterms:description` at all — the field is omitted from the
35
35
  * graph (honest omission), never filled with a fallback. */
36
36
  modelAgentDescription?: string;
37
+ /** Vocabulary era to emit (spec Appendix J). Defaults to the settlement-era
38
+ * `CIVIC_VOCABULARY` — the only value a new emission may use. Supply
39
+ * `PRIOR_ERA_CIVIC_VOCABULARY` ONLY to reproduce or verify a record signed
40
+ * before the 2026-08-19 settlement, whose identifiers are frozen under the
41
+ * hash it was signed with. Unlike the other fields here this is not
42
+ * deployment identity, so it is defaulted rather than required: emitting
43
+ * the current vocabulary is never the silent-attribution hazard ADR-0024
44
+ * guards against. */
45
+ vocabulary?: CivicVocabulary;
37
46
  }
38
47
  /** The civicaitools.org reference deployment's values. Passed explicitly by
39
48
  * the reference app — never applied as a default. */
@@ -17,7 +17,7 @@
17
17
  // order is preserved throughout — the legacy hash chain's byte contract).
18
18
  import { makeProvGraph, makeEntityNode, makeActivityNode, makeAgentNode, provRef, provUsed, provWasGeneratedBy, provWasDerivedFrom, provWasAssociatedWith, xsdDateTime, } from '@typedstandards/produce-core';
19
19
  import { hash } from "./trace.js";
20
- import { makeCivicProvContext, civicUrn, civicModelUrn, civicSourceAgentUrn, civicPlatformUrn, CIVICAITOOLS_PLATFORM_AGENT, } from "../format/vocabulary.js";
20
+ import { CIVIC_VOCABULARY, CIVICAITOOLS_PLATFORM_AGENT, } from "../format/vocabulary.js";
21
21
  import { CIVIC_SOURCE_REGISTRY, FALLBACK_SOURCE_ID, isDatasetKeyedSource, } from "../format/sources.js";
22
22
  /** The civicaitools.org reference deployment's values. Passed explicitly by
23
23
  * the reference app — never applied as a default. */
@@ -53,10 +53,14 @@ export function buildProvenanceGraph(trace, input, config) {
53
53
  const registry = config.sourceRegistry;
54
54
  const fallbackSourceId = config.fallbackSourceId ?? FALLBACK_SOURCE_ID;
55
55
  const skillSourceId = config.skillSourceId ?? FALLBACK_SOURCE_ID;
56
+ // Vocabulary era — settlement-era unless a prior-era reproduction asks
57
+ // otherwise. Every id and the `@context` below route through it, so a graph
58
+ // never mixes eras.
59
+ const vocab = config.vocabulary ?? CIVIC_VOCABULARY;
56
60
  const graph = [];
57
61
  // --- Entities ---
58
62
  // User prompt
59
- graph.push(makeEntityNode(civicUrn(packageId, 'prompt', promptHash), {
63
+ graph.push(makeEntityNode(vocab.urn(packageId, 'prompt', promptHash), {
60
64
  'civic:contentHash': `sha256:${promptHash}`,
61
65
  'dcterms:description': 'User query prompt',
62
66
  ...(promptText ? { 'prov:value': promptText } : {}),
@@ -69,13 +73,13 @@ export function buildProvenanceGraph(trace, input, config) {
69
73
  const skillHash = skillSpan ? getAttr(skillSpan.attributes, 'skill.text_hash') : undefined;
70
74
  const skillServerUrl = skillSpan ? getAttr(skillSpan.attributes, 'skill.mcp_server_url') : undefined;
71
75
  if (skillHash) {
72
- graph.push(makeEntityNode(civicUrn(packageId, 'skill', skillHash), {
76
+ graph.push(makeEntityNode(vocab.urn(packageId, 'skill', skillHash), {
73
77
  'civic:contentHash': `sha256:${skillHash}`,
74
78
  'dcterms:description': 'Composed MCP skill guidance (system prompt)',
75
79
  }, ['prov:Plan']));
76
80
  }
77
81
  // Final output
78
- graph.push(makeEntityNode(civicUrn(packageId, 'output', outputHash), {
82
+ graph.push(makeEntityNode(vocab.urn(packageId, 'output', outputHash), {
79
83
  'civic:contentHash': `sha256:${outputHash}`,
80
84
  'dcterms:description': 'AI-generated analysis output',
81
85
  }));
@@ -83,7 +87,7 @@ export function buildProvenanceGraph(trace, input, config) {
83
87
  // LLM model. The description is emitted only when the config supplies one —
84
88
  // absent otherwise (honest omission, same idiom as deriveProducerProfile /
85
89
  // deriveSummaryEmission in ../format/dathere.ts).
86
- const modelUrn = civicModelUrn(model);
90
+ const modelUrn = vocab.modelUrn(model);
87
91
  graph.push(makeAgentNode(modelUrn, {
88
92
  'dcterms:title': model,
89
93
  ...(config.modelAgentDescription
@@ -91,7 +95,7 @@ export function buildProvenanceGraph(trace, input, config) {
91
95
  : {}),
92
96
  }, ['prov:SoftwareAgent']));
93
97
  // MCP server agents — one per distinct data source that appears in the
94
- // trace. Tool spans carry `mcp.source`; evidence records captured before
98
+ // trace. Tool spans carry `mcp.source`; records captured before
95
99
  // source tagging have no source attribute, so they fall back to the
96
100
  // configured fallback source (socrata — the only source at the time) for
97
101
  // backwards compatibility. Agent coordinates come from the source registry;
@@ -100,7 +104,7 @@ export function buildProvenanceGraph(trace, input, config) {
100
104
  const sourceAgentMap = {};
101
105
  for (const [id, info] of Object.entries(registry)) {
102
106
  sourceAgentMap[id] = {
103
- urn: civicSourceAgentUrn(id),
107
+ urn: vocab.sourceAgentUrn(id),
104
108
  title: info.agentTitle,
105
109
  serverUrl: id === skillSourceId ? skillServerUrl || info.serverUrl : info.serverUrl,
106
110
  };
@@ -112,7 +116,7 @@ export function buildProvenanceGraph(trace, input, config) {
112
116
  }
113
117
  for (const sourceId of sourcesInTrace) {
114
118
  const meta = sourceAgentMap[sourceId] ?? {
115
- urn: civicSourceAgentUrn(encodeURIComponent(sourceId)),
119
+ urn: vocab.sourceAgentUrn(encodeURIComponent(sourceId)),
116
120
  title: `${sourceId} MCP Server`,
117
121
  serverUrl: sourceId,
118
122
  };
@@ -129,10 +133,10 @@ export function buildProvenanceGraph(trace, input, config) {
129
133
  const id = sourceId || fallbackSourceId;
130
134
  return (sourceAgentMap[id]?.urn
131
135
  ?? sourceAgentMap[fallbackSourceId]?.urn
132
- ?? civicSourceAgentUrn(encodeURIComponent(id)));
136
+ ?? vocab.sourceAgentUrn(encodeURIComponent(id)));
133
137
  }
134
138
  // Platform
135
- graph.push(makeAgentNode(civicPlatformUrn(config.platformAgent.id), {
139
+ graph.push(makeAgentNode(vocab.platformUrn(config.platformAgent.id), {
136
140
  'dcterms:title': config.platformAgent.title,
137
141
  'civic:url': config.platformAgent.url,
138
142
  }, ['prov:SoftwareAgent']));
@@ -141,11 +145,11 @@ export function buildProvenanceGraph(trace, input, config) {
141
145
  const inferenceSpans = spans.filter(s => s.name === 'llm_inference');
142
146
  const inferenceUrns = [];
143
147
  for (const span of inferenceSpans) {
144
- const spanUrn = civicUrn(packageId, 'inference', span.spanId);
148
+ const spanUrn = vocab.urn(packageId, 'inference', span.spanId);
145
149
  inferenceUrns.push(spanUrn);
146
- const used = [provRef(civicUrn(packageId, 'prompt', promptHash))];
150
+ const used = [provRef(vocab.urn(packageId, 'prompt', promptHash))];
147
151
  if (skillHash) {
148
- used.push(provRef(civicUrn(packageId, 'skill', skillHash)));
152
+ used.push(provRef(vocab.urn(packageId, 'skill', skillHash)));
149
153
  }
150
154
  const promptTokens = getAttr(span.attributes, 'gen_ai.response.prompt_tokens');
151
155
  const completionTokens = getAttr(span.attributes, 'gen_ai.response.completion_tokens');
@@ -166,7 +170,7 @@ export function buildProvenanceGraph(trace, input, config) {
166
170
  // MCP tool call spans
167
171
  const dataResponseUrns = [];
168
172
  for (const span of toolSpans) {
169
- const toolCallUrn = civicUrn(packageId, 'tool-call', span.spanId);
173
+ const toolCallUrn = vocab.urn(packageId, 'tool-call', span.spanId);
170
174
  const argsStr = getAttr(span.attributes, 'tool.arguments') || '{}';
171
175
  const queryHash = hash(argsStr);
172
176
  const responseHash = getAttr(span.attributes, 'tool.response_hash');
@@ -183,19 +187,19 @@ export function buildProvenanceGraph(trace, input, config) {
183
187
  const precedingInference = inferenceSpans
184
188
  .filter(is => Number(is.endTimeUnixNano || '0') <= toolStart)
185
189
  .sort((a, b) => Number(b.endTimeUnixNano || '0') - Number(a.endTimeUnixNano || '0'))[0];
186
- const queryUrn = civicUrn(packageId, 'query', queryHash);
190
+ const queryUrn = vocab.urn(packageId, 'query', queryHash);
187
191
  graph.push(makeEntityNode(queryUrn, {
188
192
  'civic:contentHash': `sha256:${queryHash}`,
189
193
  'civic:toolName': toolName,
190
194
  'civic:operationType': opType,
191
195
  'dcterms:description': `MCP tool arguments (${opType})`,
192
196
  ...(precedingInference
193
- ? provWasGeneratedBy(civicUrn(packageId, 'inference', precedingInference.spanId))
197
+ ? provWasGeneratedBy(vocab.urn(packageId, 'inference', precedingInference.spanId))
194
198
  : {}),
195
199
  }));
196
200
  // Data response entity
197
201
  if (responseHash) {
198
- const dataUrn = civicUrn(packageId, 'data', responseHash);
202
+ const dataUrn = vocab.urn(packageId, 'data', responseHash);
199
203
  dataResponseUrns.push(dataUrn);
200
204
  // Description varies by source — dataset-keyed sources (Socrata) have
201
205
  // a portal domain; aggregate/unknown sources are described by their
@@ -249,8 +253,8 @@ export function buildProvenanceGraph(trace, input, config) {
249
253
  const generatorSpan = synthesisSpan || lastInference;
250
254
  if (generatorSpan) {
251
255
  const generatorUrn = synthesisSpan
252
- ? civicUrn(packageId, 'synthesis', synthesisSpan.spanId)
253
- : civicUrn(packageId, 'inference', generatorSpan.spanId);
256
+ ? vocab.urn(packageId, 'synthesis', synthesisSpan.spanId)
257
+ : vocab.urn(packageId, 'inference', generatorSpan.spanId);
254
258
  // Add synthesis activity if it exists
255
259
  if (synthesisSpan) {
256
260
  graph.push(makeActivityNode(generatorUrn, {
@@ -265,7 +269,7 @@ export function buildProvenanceGraph(trace, input, config) {
265
269
  }));
266
270
  }
267
271
  // Output wasGeneratedBy
268
- graph.push(makeEntityNode(civicUrn(packageId, 'output', outputHash), {
272
+ graph.push(makeEntityNode(vocab.urn(packageId, 'output', outputHash), {
269
273
  ...provWasGeneratedBy(generatorUrn),
270
274
  ...(dataResponseUrns.length > 0
271
275
  ? provWasDerivedFrom(dataResponseUrns)
@@ -288,9 +292,9 @@ export function buildProvenanceGraph(trace, input, config) {
288
292
  'prov:qualifiedAssociation': {
289
293
  '@type': 'prov:Association',
290
294
  'prov:agent': provRef(modelUrn),
291
- 'prov:hadPlan': provRef(civicUrn(packageId, 'skill', skillHash)),
295
+ 'prov:hadPlan': provRef(vocab.urn(packageId, 'skill', skillHash)),
292
296
  },
293
297
  }));
294
298
  }
295
- return makeProvGraph(makeCivicProvContext(), graph);
299
+ return makeProvGraph(vocab.context(), graph);
296
300
  }
@@ -1,24 +1,68 @@
1
1
  /** The `civic:` JSON-LD namespace URI. Part of the civic vocabulary itself
2
2
  * (it names the term set, not a deployment), so it is a constant, not a
3
- * config input. */
4
- export declare const CIVIC_NS = "https://civicaitools.org/ns/evidence/";
5
- /** The civic evidence-package JSON-LD `@context`: prov / xsd / civic /
3
+ * config input. Settlement-era value (Appendix J). */
4
+ export declare const CIVIC_NS = "https://civicaitools.org/ns/civic/";
5
+ /** Root of the `urn:civic-record:` id scheme. Settlement-era value
6
+ * (Appendix J). */
7
+ export declare const CIVIC_URN_PREFIX = "urn:civic-record";
8
+ /** The prior-era `civic:` namespace URI, frozen inside records signed before
9
+ * the 2026-08-19 settlement. Never emitted for new packages. */
10
+ export declare const PRIOR_ERA_CIVIC_NS = "https://civicaitools.org/ns/evidence/";
11
+ /** The prior-era id-scheme root, frozen inside records signed before the
12
+ * 2026-08-19 settlement. Never emitted for new packages. */
13
+ export declare const PRIOR_ERA_CIVIC_URN_PREFIX = "urn:civic-evidence";
14
+ /**
15
+ * One era of the civic vocabulary: the two literals plus the emitters bound
16
+ * to them. The vocabulary is not deployment configuration (a deployment does
17
+ * not choose its own term set); this seam exists only so a prior-era package
18
+ * can be reproduced with the exact vocabulary it was signed under.
19
+ */
20
+ export interface CivicVocabulary {
21
+ /** The `civic:` JSON-LD namespace URI for this era. */
22
+ readonly ns: string;
23
+ /** The id-scheme root for this era. */
24
+ readonly urnPrefix: string;
25
+ /** The civic record-package JSON-LD `@context`: prov / xsd / civic /
26
+ * dcterms, in the reference emission order (legacy-chain byte discipline —
27
+ * insertion order is the byte contract). */
28
+ context(): Record<string, string>;
29
+ /** Package-scoped node id: `<prefix>:<packageId>:<type>:<id>`. */
30
+ urn(packageId: string, type: string, id: string): string;
31
+ /** Model-agent id: `<prefix>:model:<modelId>` (slashes in the model
32
+ * identifier collapse to dashes). */
33
+ modelUrn(model: string): string;
34
+ /** MCP source-agent id: `<prefix>:mcp-server:<sourceId>`. Known source ids
35
+ * are emitted raw (they are already URN-safe by construction); callers
36
+ * emitting an UNKNOWN id must encode it first (the capture-side builder
37
+ * uses `encodeURIComponent`, matching the reference behavior). */
38
+ sourceAgentUrn(sourceId: string): string;
39
+ /** Platform-agent id: `<prefix>:platform:<platformId>`. */
40
+ platformUrn(platformId: string): string;
41
+ }
42
+ /** Bind the emitters to one era's two literals. */
43
+ export declare function makeCivicVocabulary(ns: string, urnPrefix: string): CivicVocabulary;
44
+ /** The settlement-era vocabulary — what every new emission uses. */
45
+ export declare const CIVIC_VOCABULARY: CivicVocabulary;
46
+ /** The prior-era vocabulary, valid forever inside records signed before the
47
+ * settlement. Supply it to reproduce or verify such a record byte-for-byte;
48
+ * never to mint a new package. */
49
+ export declare const PRIOR_ERA_CIVIC_VOCABULARY: CivicVocabulary;
50
+ /** The civic record-package JSON-LD `@context`: prov / xsd / civic /
6
51
  * dcterms, in the reference emission order (legacy-chain byte discipline —
7
- * insertion order is the byte contract). */
52
+ * insertion order is the byte contract). Settlement era; for a prior-era
53
+ * graph use `PRIOR_ERA_CIVIC_VOCABULARY.context()`. */
8
54
  export declare function makeCivicProvContext(): Record<string, string>;
9
- /** Root of the `urn:civic-evidence:` id scheme. */
10
- export declare const CIVIC_URN_PREFIX = "urn:civic-evidence";
11
- /** Package-scoped node id: `urn:civic-evidence:<packageId>:<type>:<id>`. */
55
+ /** Package-scoped node id: `urn:civic-record:<packageId>:<type>:<id>`. */
12
56
  export declare function civicUrn(packageId: string, type: string, id: string): string;
13
- /** Model-agent id: `urn:civic-evidence:model:<modelId>` (slashes in the
57
+ /** Model-agent id: `urn:civic-record:model:<modelId>` (slashes in the
14
58
  * model identifier collapse to dashes). */
15
59
  export declare function civicModelUrn(model: string): string;
16
- /** MCP source-agent id: `urn:civic-evidence:mcp-server:<sourceId>`. Known
60
+ /** MCP source-agent id: `urn:civic-record:mcp-server:<sourceId>`. Known
17
61
  * source ids are emitted raw (they are already URN-safe by construction);
18
62
  * callers emitting an UNKNOWN id must encode it first (the capture-side
19
63
  * builder uses `encodeURIComponent`, matching the reference behavior). */
20
64
  export declare function civicSourceAgentUrn(sourceId: string): string;
21
- /** Platform-agent id: `urn:civic-evidence:platform:<platformId>`. */
65
+ /** Platform-agent id: `urn:civic-record:platform:<platformId>`. */
22
66
  export declare function civicPlatformUrn(platformId: string): string;
23
67
  /**
24
68
  * Platform-agent identity — WHO published, as a PROV agent. A typed config
@@ -26,7 +70,8 @@ export declare function civicPlatformUrn(platformId: string): string;
26
70
  * the civicaitools.org demo values are the exported default below.
27
71
  */
28
72
  export interface PlatformAgentConfig {
29
- /** Stable platform id — becomes `urn:civic-evidence:platform:<id>`. */
73
+ /** Stable platform id — becomes `urn:civic-record:platform:<id>` (prior-era
74
+ * graphs carry the same id under the prior-era prefix). */
30
75
  id: string;
31
76
  /** Human-readable platform title (`dcterms:title`). */
32
77
  title: string;
@@ -1,5 +1,5 @@
1
1
  // Civic PROV-O vocabulary (FORMAT-EXTENSION group) — the `civic:` JSON-LD
2
- // namespace, the `urn:civic-evidence:` id scheme, and the platform-agent
2
+ // namespace, the `urn:civic-record:` id scheme, and the platform-agent
3
3
  // identity shape. Relocated from civic-ai-tools-website
4
4
  // `src/lib/evidence/provenance.ts` (`:51` urn scheme, `:179–184` platform
5
5
  // agent, `:384–391` context) per the S2 brief §1.
@@ -7,43 +7,81 @@
7
7
  // This module defines vocabulary; it never walks a trace. The capture-side
8
8
  // provenance BUILDER (src/capture/provenance.ts) imports its terms from here —
9
9
  // that direction is the package's internal module boundary.
10
+ //
11
+ // TWO ERAS (spec Appendix J — the 2026-08-19 vocabulary settlement, migration
12
+ // class "frozen-in-signed-artifacts"). New emissions mint the SETTLEMENT-era
13
+ // terms (`CIVIC_NS` / `CIVIC_URN_PREFIX` below, the module's canonical
14
+ // values). The PRIOR-era terms are frozen inside every already-signed record —
15
+ // rewriting them would change the envelope hash and invalidate the signature —
16
+ // so they remain valid forever and are exported here too: reproducing or
17
+ // verifying a prior-era package byte-for-byte requires emitting its vocabulary
18
+ // exactly as it was signed. Era is not a trust signal (Appendix J §J.4 rule 2);
19
+ // prior-era injection exists for reproduction and verification, never for new
20
+ // emissions.
10
21
  import { makeProvContext, PROV_NS, XSD_NS, DCTERMS_NS } from '@typedstandards/produce-core';
11
22
  /** The `civic:` JSON-LD namespace URI. Part of the civic vocabulary itself
12
23
  * (it names the term set, not a deployment), so it is a constant, not a
13
- * config input. */
14
- export const CIVIC_NS = 'https://civicaitools.org/ns/evidence/';
15
- /** The civic evidence-package JSON-LD `@context`: prov / xsd / civic /
24
+ * config input. Settlement-era value (Appendix J). */
25
+ export const CIVIC_NS = 'https://civicaitools.org/ns/civic/';
26
+ /** Root of the `urn:civic-record:` id scheme. Settlement-era value
27
+ * (Appendix J). */
28
+ export const CIVIC_URN_PREFIX = 'urn:civic-record';
29
+ /** The prior-era `civic:` namespace URI, frozen inside records signed before
30
+ * the 2026-08-19 settlement. Never emitted for new packages. */
31
+ export const PRIOR_ERA_CIVIC_NS = 'https://civicaitools.org/ns/evidence/';
32
+ /** The prior-era id-scheme root, frozen inside records signed before the
33
+ * 2026-08-19 settlement. Never emitted for new packages. */
34
+ export const PRIOR_ERA_CIVIC_URN_PREFIX = 'urn:civic-evidence';
35
+ /** Bind the emitters to one era's two literals. */
36
+ export function makeCivicVocabulary(ns, urnPrefix) {
37
+ return {
38
+ ns,
39
+ urnPrefix,
40
+ context: () => makeProvContext({
41
+ prov: PROV_NS,
42
+ xsd: XSD_NS,
43
+ civic: ns,
44
+ dcterms: DCTERMS_NS,
45
+ }),
46
+ urn: (packageId, type, id) => `${urnPrefix}:${packageId}:${type}:${id}`,
47
+ modelUrn: (model) => `${urnPrefix}:model:${model.replace(/\//g, '-')}`,
48
+ sourceAgentUrn: (sourceId) => `${urnPrefix}:mcp-server:${sourceId}`,
49
+ platformUrn: (platformId) => `${urnPrefix}:platform:${platformId}`,
50
+ };
51
+ }
52
+ /** The settlement-era vocabulary — what every new emission uses. */
53
+ export const CIVIC_VOCABULARY = makeCivicVocabulary(CIVIC_NS, CIVIC_URN_PREFIX);
54
+ /** The prior-era vocabulary, valid forever inside records signed before the
55
+ * settlement. Supply it to reproduce or verify such a record byte-for-byte;
56
+ * never to mint a new package. */
57
+ export const PRIOR_ERA_CIVIC_VOCABULARY = makeCivicVocabulary(PRIOR_ERA_CIVIC_NS, PRIOR_ERA_CIVIC_URN_PREFIX);
58
+ // --- Settlement-era convenience bindings (unchanged signatures) ---
59
+ /** The civic record-package JSON-LD `@context`: prov / xsd / civic /
16
60
  * dcterms, in the reference emission order (legacy-chain byte discipline —
17
- * insertion order is the byte contract). */
61
+ * insertion order is the byte contract). Settlement era; for a prior-era
62
+ * graph use `PRIOR_ERA_CIVIC_VOCABULARY.context()`. */
18
63
  export function makeCivicProvContext() {
19
- return makeProvContext({
20
- prov: PROV_NS,
21
- xsd: XSD_NS,
22
- civic: CIVIC_NS,
23
- dcterms: DCTERMS_NS,
24
- });
64
+ return CIVIC_VOCABULARY.context();
25
65
  }
26
- /** Root of the `urn:civic-evidence:` id scheme. */
27
- export const CIVIC_URN_PREFIX = 'urn:civic-evidence';
28
- /** Package-scoped node id: `urn:civic-evidence:<packageId>:<type>:<id>`. */
66
+ /** Package-scoped node id: `urn:civic-record:<packageId>:<type>:<id>`. */
29
67
  export function civicUrn(packageId, type, id) {
30
- return `${CIVIC_URN_PREFIX}:${packageId}:${type}:${id}`;
68
+ return CIVIC_VOCABULARY.urn(packageId, type, id);
31
69
  }
32
- /** Model-agent id: `urn:civic-evidence:model:<modelId>` (slashes in the
70
+ /** Model-agent id: `urn:civic-record:model:<modelId>` (slashes in the
33
71
  * model identifier collapse to dashes). */
34
72
  export function civicModelUrn(model) {
35
- return `${CIVIC_URN_PREFIX}:model:${model.replace(/\//g, '-')}`;
73
+ return CIVIC_VOCABULARY.modelUrn(model);
36
74
  }
37
- /** MCP source-agent id: `urn:civic-evidence:mcp-server:<sourceId>`. Known
75
+ /** MCP source-agent id: `urn:civic-record:mcp-server:<sourceId>`. Known
38
76
  * source ids are emitted raw (they are already URN-safe by construction);
39
77
  * callers emitting an UNKNOWN id must encode it first (the capture-side
40
78
  * builder uses `encodeURIComponent`, matching the reference behavior). */
41
79
  export function civicSourceAgentUrn(sourceId) {
42
- return `${CIVIC_URN_PREFIX}:mcp-server:${sourceId}`;
80
+ return CIVIC_VOCABULARY.sourceAgentUrn(sourceId);
43
81
  }
44
- /** Platform-agent id: `urn:civic-evidence:platform:<platformId>`. */
82
+ /** Platform-agent id: `urn:civic-record:platform:<platformId>`. */
45
83
  export function civicPlatformUrn(platformId) {
46
- return `${CIVIC_URN_PREFIX}:platform:${platformId}`;
84
+ return CIVIC_VOCABULARY.platformUrn(platformId);
47
85
  }
48
86
  /** Demo default: the civicaitools.org reference deployment's identity. */
49
87
  export const CIVICAITOOLS_PLATFORM_AGENT = {
@@ -1,4 +1,4 @@
1
- import { type EvidencePackage, type EvaluationResults } from '@typedstandards/produce-core';
1
+ import { type RecordPackage, type EvaluationResults } from '@typedstandards/produce-core';
2
2
  /** Rubric identifier carried as the `scoringRubric` payload field and the
3
3
  * methodology `testSet`. Six criteria, 1-10 each, plus aggregate. */
4
4
  export declare const RUBRIC_ID = "civicaitools-adversarial-rubric/six-criterion-v1";
@@ -8,8 +8,12 @@ export declare const EVALUATION_RUBRIC = "You are an independent evaluator asses
8
8
  * once at module load. Any wording change to the rubric produces a new
9
9
  * version, so an attestation pins the exact prompt set that scored it. */
10
10
  export declare const RUBRIC_VERSION_SHA256: string;
11
- /** Build the user-turn evaluation content from a package's signed fields. */
12
- export declare function buildEvaluationPrompt(pkg: EvidencePackage): string;
11
+ /** Build the user-turn evaluation content from a package's signed fields.
12
+ * Takes `RecordPackage` — produce-core 0.3.0's settlement-era name for the
13
+ * same object (spec Appendix J, migration class alias-and-deprecate). The
14
+ * prior name is a deprecated alias of this exact type upstream, so existing
15
+ * callers still compile unchanged. */
16
+ export declare function buildEvaluationPrompt(pkg: RecordPackage): string;
13
17
  export type ParsedEvaluation = {
14
18
  ok: true;
15
19
  results: EvaluationResults;
@@ -59,7 +59,11 @@ Respond in this exact JSON format (no markdown fences, just raw JSON):
59
59
  * once at module load. Any wording change to the rubric produces a new
60
60
  * version, so an attestation pins the exact prompt set that scored it. */
61
61
  export const RUBRIC_VERSION_SHA256 = sha256Hex(EVALUATION_RUBRIC);
62
- /** Build the user-turn evaluation content from a package's signed fields. */
62
+ /** Build the user-turn evaluation content from a package's signed fields.
63
+ * Takes `RecordPackage` — produce-core 0.3.0's settlement-era name for the
64
+ * same object (spec Appendix J, migration class alias-and-deprecate). The
65
+ * prior name is a deprecated alias of this exact type upstream, so existing
66
+ * callers still compile unchanged. */
63
67
  export function buildEvaluationPrompt(pkg) {
64
68
  const toolCallSummary = pkg.queries
65
69
  .map((q, i) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@typedstandards/civic-typed-harness",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Civic-domain harness for Typed Standards evidence packages — the DOMAIN layer of the ADR-0021 format/domain line: civic PROV-O vocabulary and provenance builder, datHere envelope-policy derivations, and the capture machinery (trace builder, skill-metadata extraction, data-source population, adversarial rubric core). Derives; @typedstandards/produce-core assembles.",
5
5
  "license": "MIT",
6
6
  "author": "Nathan Storey",
@@ -33,10 +33,10 @@
33
33
  "prepublishOnly": "npm run clean && npm run build"
34
34
  },
35
35
  "dependencies": {
36
- "@typedstandards/produce-core": "^0.2.0"
36
+ "@typedstandards/produce-core": "^0.3.0"
37
37
  },
38
38
  "devDependencies": {
39
- "@typedstandards/verify-core": "^0.8.0",
39
+ "@typedstandards/verify-core": "^0.9.0",
40
40
  "@typescript-eslint/parser": "^8",
41
41
  "eslint": "^9",
42
42
  "typescript": "^5.9.3"