hachure 0.1.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/README.md ADDED
@@ -0,0 +1,267 @@
1
+ # Hachure — an open trust format
2
+
3
+ **Namespace:** `hachure.org/v1`
4
+ **Reference implementation:** `@kontourai/surface`
5
+ **Status:** pre-1.0, hard versioning, no compatibility promises yet
6
+ **Originally developed by:** [Kontour AI](https://kontour.ai)
7
+
8
+ ---
9
+
10
+ ## Install
11
+
12
+ ```sh
13
+ npm i -D hachure
14
+ ```
15
+
16
+ The package ships the normative JSON schemas, conformance test vectors, and the
17
+ `STATUS_FUNCTION_VERSION` constant that ties implementations to a specific
18
+ algorithm revision.
19
+
20
+ **Claiming conformance:** run the test vectors from `testVectors` against your
21
+ implementation. For each vector, call your status-derivation function with
22
+ `vector.input` and `vector.now`, then assert that the derived status for every
23
+ claim ID matches `vector.expect.statusByClaimId`. Passing all vectors for a given
24
+ `STATUS_FUNCTION_VERSION` is the bar for a conforming implementation.
25
+
26
+ ```js
27
+ import { testVectors, STATUS_FUNCTION_VERSION } from 'hachure';
28
+
29
+ for (const { name, vector } of testVectors) {
30
+ const results = deriveStatuses(vector.input, new Date(vector.now));
31
+ for (const [claimId, expected] of Object.entries(vector.expect.statusByClaimId)) {
32
+ assert.equal(results[claimId], expected, `${name} / ${claimId}`);
33
+ }
34
+ }
35
+ ```
36
+
37
+ ---
38
+
39
+
40
+ ## What this is
41
+
42
+ Hachure is an open format for portable trust state. It defines how claims about
43
+ real-world subjects — and the evidence, policies, verification events, authority
44
+ records, and derivation rules behind them — are represented so they can cross
45
+ product and vendor boundaries without the receiver needing access to the
46
+ producer's internals.
47
+
48
+ Hachures are the short strokes on hand-drawn maps that show the shape and
49
+ steepness of terrain. This format does the same for trust: it shows the contours
50
+ of what is supported, what is stale, what is disputed, and what is simply
51
+ asserted.
52
+
53
+ The format is deliberately not named after any company or product.
54
+ `@kontourai/surface` is its reference implementation. Producers outside the
55
+ Kontour suite can emit and consume these records without adopting a vendor name
56
+ into their wire format.
57
+
58
+ **Governance intent:** Hachure is currently developed by Kontour AI, which holds
59
+ the name to protect it. We intend to move the specification to neutral
60
+ governance as adoption warrants.
61
+
62
+ The Kontour products build on top: Survey emits Trust Bundles, Veritas authors claims
63
+ through it, Flow consumes Surface-shaped evidence at gates, and Console operates across
64
+ all of them. Each product stands alone; the format requires none of them.
65
+
66
+ ---
67
+
68
+ ## Namespace and versioning
69
+
70
+ All core trust-format records use `apiVersion: hachure.org/v1` in the Kontour
71
+ Resource Shape envelope. Product-specific records use product-scoped namespaces
72
+ (`surface.kontour.ai/v1alpha1`, `survey.kontour.ai/v1alpha1`, etc.).
73
+
74
+ Pre-1.0: the format uses hard breaking changes rather than compatibility aliases.
75
+ No forward or backward compatibility guarantees are made across versions. Version
76
+ bumps are reflected in `schemaVersion` (an integer field in TrustBundle, currently
77
+ `3`) and in `STATUS_FUNCTION_VERSION` (a string exported by the reference
78
+ implementation, currently `"1"`).
79
+
80
+ ---
81
+
82
+ ## Scope: core record shapes
83
+
84
+ This specification covers the following record types. Each is a first-class concept
85
+ in the format; none requires a specific producer or product to instantiate.
86
+
87
+ ### TrustBundle
88
+
89
+ The central wire record. A portable, point-in-time package of trust state from a
90
+ single producer: claims, evidence, policies, verification events, and optional identity
91
+ links, claim groups, and authority traces.
92
+
93
+ Plain-language definition (ADR 0002):
94
+
95
+ > A Trust Bundle is a portable, point-in-time package of trust state from a single
96
+ > producer — claims, the evidence and verification events behind them, and the policies
97
+ > the producer played by — packed so it can cross a product boundary without the
98
+ > receiver needing access to the producer's internals.
99
+
100
+ The `source` field identifies the producer. Bundles from multiple producers can be
101
+ merged; conflicts surface as `disputed` status (never last-write-wins).
102
+
103
+ An optional `identityLinks` array declares co-referent subjects — real-world entities
104
+ known under more than one identifier. Each link carries a stable optional `id`, a
105
+ `subjects` array (two or more `{ subjectType, subjectId }` refs), and an optional
106
+ `relation` field: `"equivalent"` (default — the subjects denote the same entity),
107
+ `"subsumes"` (the first subject is a superset of the others), or `"converts"` (the
108
+ subjects are related by a unit or scale transformation, parameterised by an optional
109
+ `conversion: { factor, offset, note }` object). A link may additionally carry a
110
+ `mappingClaimId` pointing to the Claim that evidences the mapping assertion itself;
111
+ when set, inquiry resolution through that link is subject to a weakest-link status cap —
112
+ a disputed mapping claim cannot yield a verified answer.
113
+
114
+ ### Claim
115
+
116
+ An assertion about a real-world subject. A claim has a stable `id`, a `subjectType`
117
+ and `subjectId` pair identifying what is being asserted, a `claimType`, a
118
+ `fieldOrBehavior`, and a `value`. Claims carry optional `impactLevel`, integrity
119
+ anchors, policy references, derivation edges, and confidence basis metadata.
120
+
121
+ Derived trust status is never stored on the claim itself as source of truth; it is
122
+ computed from the surrounding bundle at evaluation time.
123
+
124
+ ### Evidence
125
+
126
+ An item of support for a claim. Evidence is linked to a claim via `claimId`. Each
127
+ item carries `evidenceType`, `method`, `sourceRef`, an excerpt or summary, and
128
+ `observedAt`. Evidence can declare a `passing` boolean and a `blocking` flag; a
129
+ non-passing, non-blocked evidence item is a soft signal; a non-passing blocking
130
+ item can cause a `disputed` status outcome.
131
+
132
+ `supportStrength` (default `"entails"`) distinguishes full entailment from citation:
133
+ only `"entails"` evidence feeds policy requirement checks. `"cited"` evidence is
134
+ contextual but does not satisfy required-evidence policies.
135
+
136
+ ### VerificationPolicy
137
+
138
+ A policy declares what evidence and methods are required to reach `verified` status
139
+ for a given `claimType`, and how long verification remains valid. Core fields:
140
+ `requiredEvidence` (array of evidence types), `requiredMethods`, `requiresCorroboration`,
141
+ `validityRule` (one of `duration`, `commit`, `historical`, `manual`), and
142
+ `acceptanceCriteria`.
143
+
144
+ Policies are resolved against claims by `verificationPolicyId` first, then by
145
+ `claimType` exact match, then by walking the `parentType` chain from most-specific
146
+ to most-general. See [Status Derivation](status-function.md) for how the resolved
147
+ policy feeds the derivation.
148
+
149
+ ### VerificationEvent
150
+
151
+ An append-only event representing a status decision for a claim. Events carry
152
+ `claimId`, `status`, `actor`, `method`, `evidenceIds`, and timestamps. Events are
153
+ never updated; they accumulate as a ledger. The most recent event of a given kind
154
+ shapes the derived status via the fold described in [Status Derivation](status-function.md).
155
+
156
+ A verification event may carry `resolvesDispute: true` and an `authorityRef` to
157
+ indicate it is an authority-gated dispute-resolution decision (ADR 0003 §8).
158
+
159
+ ### AuthorityTrace
160
+
161
+ A record establishing that a named actor held a named authority over a subject during
162
+ a time window. Authority traces are the credential that makes a dispute-resolution
163
+ event binding: the fold checks that the resolution event's actor has an active trace
164
+ at the decision timestamp. Fields: `actorRef`, `authorityType`, `authorityRef`,
165
+ `validFrom`, `validUntil`, `revokedAt`, and optional integrity anchors.
166
+
167
+ ### InquiryRecord
168
+
169
+ An append-only record capturing the resolution of a consumer-side question (Inquiry)
170
+ against the ledger (ADR 0003 §6). An InquiryRecord carries the original question, the
171
+ resolution path (matched claim or named derivation rule plus input claims), the answer
172
+ with its status at evaluation time, a frozen snapshot of input claim statuses, the
173
+ `statusFunctionVersion` used, and the `resolvedAt` timestamp.
174
+
175
+ Records never go stale because they never assert present-tense truth; they assert what
176
+ was knowable at a specific moment. The `statusFunctionVersion` field enables
177
+ re-evaluation if the derivation algorithm changes.
178
+
179
+ ### DerivationRule
180
+
181
+ A named, versioned rule that derives a boolean answer from existing claims (ADR 0003 §5).
182
+ Rules compose claims using value predicates (`eq`, `gt`, `gte`, `lte`, `in`, `exists`)
183
+ and status predicates (`acceptedStatuses`), combined with `"all"` or `"any"`. Rules
184
+ are promoted from Flow's gate-expectation language. The weakest-link confidence ceiling
185
+ propagates through rule evaluation unchanged.
186
+
187
+ ---
188
+
189
+ ## Status semantics
190
+
191
+ Status is a pure, versioned function of the bundle data and a `now` timestamp. The
192
+ full specification of the derivation algorithm is in [status-function.md](status-function.md).
193
+
194
+ The eight possible statuses:
195
+
196
+ | Status | Meaning |
197
+ |---|---|
198
+ | `unknown` | No supporting evidence or events; the claim cannot be evaluated. |
199
+ | `proposed` | Evidence exists or a verification event indicates proposed, but policy requirements are not fully met. |
200
+ | `assumed` | The claim is treated as true for operational purposes without full verification evidence. |
201
+ | `verified` | A verification event asserts verified, required policy evidence is present, and the verification is still fresh. |
202
+ | `stale` | The most recent verified event has expired under the policy's validity rule. |
203
+ | `disputed` | A verified claim has blocking contradicting evidence, or a terminal dispute event exists. |
204
+ | `superseded` | A terminal event marks the claim as superseded. |
205
+ | `rejected` | A terminal event marks the claim as rejected. |
206
+
207
+ ---
208
+
209
+ ## Normative schemas
210
+
211
+ The JSON schemas at [`schemas/`](schemas/) are the normative wire contracts for
212
+ all core record shapes. The following schema files are part of this format:
213
+
214
+ | Schema file | Record type(s) |
215
+ |---|---|
216
+ | `trust-bundle.schema.json` | TrustBundle (top-level container) |
217
+ | `claim.schema.json` | Claim |
218
+ | `evidence.schema.json` | Evidence |
219
+ | `verification-policy.schema.json` | VerificationPolicy |
220
+ | `verification-event.schema.json` | VerificationEvent |
221
+ | `trust-report.schema.json` | TrustReport (derived, not emitted by producers) |
222
+ | `derivation-rule.schema.json` | DerivationRule |
223
+ | `inquiry-record.schema.json` | InquiryRecord |
224
+
225
+ Schemas are not duplicated in this directory. The reference implementation
226
+ validates TrustBundle input against these schemas via `validateTrustBundle()`.
227
+
228
+ ---
229
+
230
+ ## Out of scope: future extension profiles
231
+
232
+ The following producer domains are explicitly out of scope for this core specification.
233
+ Each is a candidate for a future extension profile that imports the core record shapes
234
+ and adds domain-specific vocabulary:
235
+
236
+ - **Survey chains** — the source → extraction → candidate → review → claim provenance
237
+ path that Survey uses to build trust bundles. Survey emits bundles conforming to
238
+ this spec; the review-trail records above it are Survey-scoped.
239
+ - **Veritas standards** — repo-area claims, per-run evidence collection, merge-gate
240
+ integration, and the `ClaimReviewRecord` pattern from the Kontour Resource Shape.
241
+ - **Flow gates** — gate-expectation language, run-scoped views, gate results, and
242
+ the `GateRun` resource shape.
243
+
244
+ Extension profiles reference this spec as their foundation and declare any additional
245
+ fields or constraints. They do not modify the core record shapes.
246
+
247
+ ---
248
+
249
+ ## Executable conformance
250
+
251
+ `spec/conformance/` contains test vector bundles and expected per-claim statuses at a
252
+ fixed `now`. The test at `tests/spec-conformance.test.ts` loads every test vector and
253
+ asserts that the reference implementation derives the expected statuses, making this
254
+ specification executable.
255
+
256
+ See [conformance/README.md](conformance/README.md) for the test vector inventory.
257
+
258
+ ---
259
+
260
+ ## Canonical home
261
+
262
+ This repository (`hachure-org/spec`) is the canonical home of the Hachure
263
+ specification: prose, normative JSON Schemas, and conformance test vectors.
264
+ The reference implementation lives at
265
+ [`kontourai/surface`](https://github.com/kontourai/surface), which runs these
266
+ test vectors in its suite. Until the extraction settles, changes land here and
267
+ in the reference implementation together; this repo wins on conflict.
@@ -0,0 +1,31 @@
1
+ # Spec Conformance Test vectors
2
+
3
+ This directory contains input bundles and expected per-claim statuses that make
4
+ the [Status Derivation specification](../status-function.md) executable.
5
+
6
+ Each test vector is a JSON file with an `input` (a valid TrustBundle) and an `expect`
7
+ object listing expected per-claim statuses at a fixed `now` timestamp. The test at
8
+ `tests/spec-conformance.test.ts` loads every test vector and asserts that the reference
9
+ implementation derives the expected statuses.
10
+
11
+ ## Test vector inventory
12
+
13
+ | File | Scenario | Now |
14
+ |---|---|---|
15
+ | `sf-verified-commit.json` | Commit-scoped policy — verified when integrity ref matches | 2026-06-10T00:00:00.000Z |
16
+ | `sf-stale-duration.json` | Duration policy — stale when window expired | 2026-06-10T00:00:00.000Z |
17
+ | `sf-disputed-blocking.json` | Verified event + blocking contradicting evidence → disputed | 2026-06-10T00:00:00.000Z |
18
+ | `sf-authority-resolved.json` | Disputed claim resolved by authority-gated event | 2026-06-10T00:00:00.000Z |
19
+ | `sf-reference-bundle-snapshot.json` | Full the reference bundle at fixed now — four claims | 2026-06-10T00:00:00.000Z |
20
+
21
+ ## Test vector format
22
+
23
+ ```json
24
+ {
25
+ "now": "<ISO 8601 string>",
26
+ "input": { /* TrustBundle */ },
27
+ "expect": {
28
+ "statusByClaimId": { "<claimId>": "<TrustStatus>" }
29
+ }
30
+ }
31
+ ```
@@ -0,0 +1,106 @@
1
+ {
2
+ "now": "2026-06-10T00:00:00.000Z",
3
+ "input": {
4
+ "schemaVersion": 3,
5
+ "source": "spec-conformance:authority-resolved",
6
+ "claims": [
7
+ {
8
+ "id": "claim.status.current",
9
+ "subjectType": "data-field",
10
+ "subjectId": "dataset:program-status",
11
+ "surface": "public-data.program",
12
+ "claimType": "public-data-field",
13
+ "fieldOrBehavior": "currentStatus",
14
+ "value": "active",
15
+ "createdAt": "2026-05-01T00:00:00.000Z",
16
+ "updatedAt": "2026-05-01T00:00:00.000Z",
17
+ "impactLevel": "high",
18
+ "verificationPolicyId": "policy.public-data-field.basic"
19
+ }
20
+ ],
21
+ "evidence": [
22
+ {
23
+ "id": "evidence.status.supporting",
24
+ "claimId": "claim.status.current",
25
+ "evidenceType": "source_excerpt",
26
+ "method": "observation",
27
+ "sourceRef": "primary source",
28
+ "excerptOrSummary": "Program is currently active.",
29
+ "observedAt": "2026-05-01T00:00:00.000Z",
30
+ "collectedBy": "crawler"
31
+ },
32
+ {
33
+ "id": "evidence.status.conflicting",
34
+ "claimId": "claim.status.current",
35
+ "evidenceType": "source_excerpt",
36
+ "method": "observation",
37
+ "sourceRef": "secondary source",
38
+ "excerptOrSummary": "Program is inactive — conflicts with primary source.",
39
+ "observedAt": "2026-05-02T00:00:00.000Z",
40
+ "collectedBy": "crawler",
41
+ "passing": false,
42
+ "blocking": true
43
+ }
44
+ ],
45
+ "policies": [
46
+ {
47
+ "id": "policy.public-data-field.basic",
48
+ "claimType": "public-data-field",
49
+ "requiredEvidence": ["source_excerpt"],
50
+ "requiredMethods": ["observation"],
51
+ "requiresCorroboration": false,
52
+ "acceptanceCriteria": ["field verified from source"],
53
+ "reviewAuthority": "domain-expert",
54
+ "validityRule": { "kind": "duration", "durationDays": 60 },
55
+ "stalenessTriggers": ["source page changes"],
56
+ "conflictRules": [],
57
+ "impactLevel": "high"
58
+ }
59
+ ],
60
+ "events": [
61
+ {
62
+ "id": "event.status.initial-verified",
63
+ "claimId": "claim.status.current",
64
+ "status": "verified",
65
+ "actor": "operator",
66
+ "method": "attestation",
67
+ "evidenceIds": ["evidence.status.supporting"],
68
+ "createdAt": "2026-05-01T00:00:00.000Z",
69
+ "verifiedAt": "2026-05-01T00:00:00.000Z"
70
+ },
71
+ {
72
+ "id": "event.status.authority-resolution",
73
+ "claimId": "claim.status.current",
74
+ "status": "verified",
75
+ "actor": "reviewer:alice",
76
+ "method": "authority-review",
77
+ "evidenceIds": ["evidence.status.supporting"],
78
+ "createdAt": "2026-05-10T00:00:00.000Z",
79
+ "verifiedAt": "2026-05-10T00:00:00.000Z",
80
+ "resolvesDispute": true,
81
+ "authorityRef": "authority.domain-expert"
82
+ }
83
+ ],
84
+ "authorityTrace": [
85
+ {
86
+ "id": "trace.alice.domain-expert",
87
+ "subject": {
88
+ "subjectType": "data-field",
89
+ "subjectId": "dataset:program-status"
90
+ },
91
+ "actorRef": "reviewer:alice",
92
+ "authorityType": "role",
93
+ "authorityRef": "authority.domain-expert",
94
+ "sourceRef": "org-policy-2026",
95
+ "observedAt": "2026-01-01T00:00:00.000Z",
96
+ "validFrom": "2026-01-01T00:00:00.000Z",
97
+ "validUntil": "2027-01-01T00:00:00.000Z"
98
+ }
99
+ ]
100
+ },
101
+ "expect": {
102
+ "statusByClaimId": {
103
+ "claim.status.current": "verified"
104
+ }
105
+ }
106
+ }
@@ -0,0 +1,78 @@
1
+ {
2
+ "now": "2026-06-10T00:00:00.000Z",
3
+ "input": {
4
+ "schemaVersion": 3,
5
+ "source": "spec-conformance:disputed-blocking",
6
+ "claims": [
7
+ {
8
+ "id": "claim.fee.value",
9
+ "subjectType": "data-field",
10
+ "subjectId": "dataset:registration-fee",
11
+ "surface": "public-data.fees",
12
+ "claimType": "public-data-field",
13
+ "fieldOrBehavior": "registrationFee",
14
+ "value": 150,
15
+ "createdAt": "2026-06-01T00:00:00.000Z",
16
+ "updatedAt": "2026-06-01T00:00:00.000Z",
17
+ "impactLevel": "medium",
18
+ "verificationPolicyId": "policy.public-data-field.basic"
19
+ }
20
+ ],
21
+ "evidence": [
22
+ {
23
+ "id": "evidence.fee.passing",
24
+ "claimId": "claim.fee.value",
25
+ "evidenceType": "source_excerpt",
26
+ "method": "observation",
27
+ "sourceRef": "source A: example.org/fees",
28
+ "excerptOrSummary": "Registration fee is $150.",
29
+ "observedAt": "2026-06-01T00:00:00.000Z",
30
+ "collectedBy": "crawler"
31
+ },
32
+ {
33
+ "id": "evidence.fee.conflicting",
34
+ "claimId": "claim.fee.value",
35
+ "evidenceType": "source_excerpt",
36
+ "method": "observation",
37
+ "sourceRef": "source B: fees.example.org",
38
+ "excerptOrSummary": "Registration fee is $200 — conflicts with $150 in source A.",
39
+ "observedAt": "2026-06-05T00:00:00.000Z",
40
+ "collectedBy": "crawler",
41
+ "passing": false,
42
+ "blocking": true
43
+ }
44
+ ],
45
+ "policies": [
46
+ {
47
+ "id": "policy.public-data-field.basic",
48
+ "claimType": "public-data-field",
49
+ "requiredEvidence": ["source_excerpt"],
50
+ "requiredMethods": ["observation"],
51
+ "requiresCorroboration": false,
52
+ "acceptanceCriteria": ["field verified from source"],
53
+ "reviewAuthority": "operator",
54
+ "validityRule": { "kind": "duration", "durationDays": 30 },
55
+ "stalenessTriggers": ["source page changes"],
56
+ "conflictRules": [],
57
+ "impactLevel": "medium"
58
+ }
59
+ ],
60
+ "events": [
61
+ {
62
+ "id": "event.fee.verified",
63
+ "claimId": "claim.fee.value",
64
+ "status": "verified",
65
+ "actor": "operator",
66
+ "method": "attestation",
67
+ "evidenceIds": ["evidence.fee.passing"],
68
+ "createdAt": "2026-06-01T00:00:00.000Z",
69
+ "verifiedAt": "2026-06-01T00:00:00.000Z"
70
+ }
71
+ ]
72
+ },
73
+ "expect": {
74
+ "statusByClaimId": {
75
+ "claim.fee.value": "disputed"
76
+ }
77
+ }
78
+ }