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 +267 -0
- package/conformance/README.md +31 -0
- package/conformance/sf-authority-resolved.json +106 -0
- package/conformance/sf-disputed-blocking.json +78 -0
- package/conformance/sf-reference-bundle-snapshot.json +332 -0
- package/conformance/sf-stale-duration.json +76 -0
- package/conformance/sf-verified-commit.json +68 -0
- package/index.mjs +61 -0
- package/interop-in-toto.md +133 -0
- package/package.json +34 -0
- package/schemas/claim.schema.json +82 -0
- package/schemas/derivation-rule.schema.json +57 -0
- package/schemas/evidence.schema.json +49 -0
- package/schemas/inquiry-record.schema.json +76 -0
- package/schemas/trust-bundle.schema.json +180 -0
- package/schemas/verification-event.schema.json +28 -0
- package/schemas/verification-policy.schema.json +65 -0
- package/status-function.md +225 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# Status Derivation — Specification
|
|
2
|
+
|
|
3
|
+
**Function:** `status = f(claim, evidence, events, policy, authorityTrace, now)`
|
|
4
|
+
**Version constant:** `STATUS_FUNCTION_VERSION` (currently `"1"`)
|
|
5
|
+
**Source of truth:** `src/status.ts` in `@kontourai/surface`
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Principle
|
|
10
|
+
|
|
11
|
+
Claim status is a pure, versioned, deterministic function (ADR 0003 §7). Given the
|
|
12
|
+
same inputs and the same `STATUS_FUNCTION_VERSION`, any conforming implementation
|
|
13
|
+
must derive the same status. There is no stored status field that overrides
|
|
14
|
+
computation; the derived status is always recomputed from the input bundle at
|
|
15
|
+
evaluation time.
|
|
16
|
+
|
|
17
|
+
`now` is an explicit input so that time-based staleness checks are reproducible.
|
|
18
|
+
A caller that wants a point-in-time view fixes `now` before evaluating; there are
|
|
19
|
+
no clock-tick events and no background expiry.
|
|
20
|
+
|
|
21
|
+
Reproducibility guarantee: if two independent implementations receive the same
|
|
22
|
+
`(claim, evidence, events, policies, authorityTrace, now)` and the same
|
|
23
|
+
`STATUS_FUNCTION_VERSION`, they must return the same `TrustStatus`.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## Inputs
|
|
28
|
+
|
|
29
|
+
| Input | Type | Required | Description |
|
|
30
|
+
|---|---|---|---|
|
|
31
|
+
| `claim` | `Claim` | yes | The claim being evaluated. |
|
|
32
|
+
| `evidence` | `Evidence[]` | yes | All evidence items whose `claimId` matches the claim. |
|
|
33
|
+
| `events` | `VerificationEvent[]` | yes | All verification events for the entire bundle; the function filters by `claimId`. |
|
|
34
|
+
| `policies` | `VerificationPolicy[]` | yes | All policies in the bundle; the function resolves the applicable policy internally. |
|
|
35
|
+
| `now` | `Date` | no (defaults to wall clock) | The evaluation timestamp for freshness checks. |
|
|
36
|
+
| `authorityTrace` | `AuthorityTrace[]` | no (defaults to `[]`) | Active authority records enabling dispute resolution. |
|
|
37
|
+
|
|
38
|
+
`deriveClaimStatus` (the public versioned entry point) resolves the policy and
|
|
39
|
+
partitions evidence before calling the internal `deriveTrustStatus` function.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Evidence partitioning
|
|
44
|
+
|
|
45
|
+
Before the fold, evidence is partitioned by `supportStrength`:
|
|
46
|
+
|
|
47
|
+
- **`"entails"`** (default when `supportStrength` is absent) — fully entails the
|
|
48
|
+
claim; satisfies policy requirement checks and corroboration counts.
|
|
49
|
+
- **`"cited"`** — contextual support only; does not satisfy required-evidence
|
|
50
|
+
policy checks and does not count toward corroboration.
|
|
51
|
+
|
|
52
|
+
Only entailing evidence is passed to `deriveTrustStatus`. Cited evidence is
|
|
53
|
+
available to callers but does not influence status derivation.
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
## Policy resolution
|
|
58
|
+
|
|
59
|
+
Before the fold, the applicable `VerificationPolicy` is resolved from the policies
|
|
60
|
+
array using this priority order:
|
|
61
|
+
|
|
62
|
+
1. If `claim.verificationPolicyId` is set and a policy with that `id` exists, it wins.
|
|
63
|
+
2. Otherwise, find a policy whose `claimType` exactly matches `claim.claimType`.
|
|
64
|
+
3. Otherwise, walk the `parentType` chain declared by policies (most-specific first)
|
|
65
|
+
and pick the first match.
|
|
66
|
+
4. If no policy is found, `policy` is `undefined` and the fold proceeds without one.
|
|
67
|
+
|
|
68
|
+
The first policy declared for a given `claimType` wins; later declarations do not
|
|
69
|
+
silently override.
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## The fold
|
|
74
|
+
|
|
75
|
+
The fold is an ordered sequence of checks. The first matching branch terminates
|
|
76
|
+
the evaluation and returns its status. No subsequent checks are applied.
|
|
77
|
+
|
|
78
|
+
### Step 1: Authority-gated dispute resolution
|
|
79
|
+
|
|
80
|
+
Check for the most recent verification event (sorted most-recent-first by `createdAt`)
|
|
81
|
+
that satisfies both of these conditions:
|
|
82
|
+
|
|
83
|
+
- `event.resolvesDispute === true`
|
|
84
|
+
- The event's `actor` has an active `AuthorityTrace` at the time of the decision
|
|
85
|
+
|
|
86
|
+
An `AuthorityTrace` is active at a given `eventCreatedAt` if all of the following hold:
|
|
87
|
+
|
|
88
|
+
- `trace.actorRef === event.actor`
|
|
89
|
+
- `trace.revokedAt` is absent, or `trace.revokedAt > eventCreatedAt`
|
|
90
|
+
- `trace.validFrom` is absent, or `trace.validFrom <= eventCreatedAt`
|
|
91
|
+
- `trace.validUntil` is absent, or `trace.validUntil >= eventCreatedAt`
|
|
92
|
+
- If `event.authorityRef` is set, `trace.authorityRef === event.authorityRef`
|
|
93
|
+
|
|
94
|
+
If such a resolution event is found:
|
|
95
|
+
|
|
96
|
+
- Check whether any evidence item satisfies **all** of:
|
|
97
|
+
- `evidence.passing === false`
|
|
98
|
+
- `evidence.blocking !== false`
|
|
99
|
+
- `Date.parse(evidence.observedAt) > Date.parse(resolutionEvent.createdAt)`
|
|
100
|
+
|
|
101
|
+
If such a "newer blocking failure" exists, return **`disputed`** (the resolution
|
|
102
|
+
is overridden by fresh contradicting evidence).
|
|
103
|
+
|
|
104
|
+
- Otherwise, return `resolutionEvent.status` directly. This is the authority-gated
|
|
105
|
+
resolution outcome.
|
|
106
|
+
|
|
107
|
+
### Step 2: Terminal event statuses
|
|
108
|
+
|
|
109
|
+
Filter all events to those matching `claim.id`, sort most-recent-first by `createdAt`.
|
|
110
|
+
Let `latestEvent` be the first (most recent) event.
|
|
111
|
+
|
|
112
|
+
If `latestEvent` exists and its `status` is one of `"rejected"`, `"disputed"`,
|
|
113
|
+
`"superseded"`, or `"stale"` — return that status. These are terminal: they are
|
|
114
|
+
not overridden by evidence inspection.
|
|
115
|
+
|
|
116
|
+
### Step 3: Assumed from event
|
|
117
|
+
|
|
118
|
+
If `latestEvent` exists and `latestEvent.status === "assumed"` — return **`assumed`**.
|
|
119
|
+
|
|
120
|
+
### Step 4: Verified event path
|
|
121
|
+
|
|
122
|
+
If `latestEvent` exists and `latestEvent.status === "verified"`:
|
|
123
|
+
|
|
124
|
+
#### 4a. Staleness check
|
|
125
|
+
|
|
126
|
+
If a policy is present, check whether the verification is stale based on
|
|
127
|
+
`policy.validityRule.kind`:
|
|
128
|
+
|
|
129
|
+
- **`"commit"`** — stale if `claim.currentIntegrityRef` is set AND none of the
|
|
130
|
+
evidence items linked by `latestEvent.evidenceIds` carry an `integrityRef` equal
|
|
131
|
+
to `claim.currentIntegrityRef`. (If `claim.currentIntegrityRef` is absent, not stale.)
|
|
132
|
+
- **`"duration"`** — stale if `now > verifiedTime + (policy.validityRule.durationDays × 86400000 ms)`,
|
|
133
|
+
where `verifiedTime = Date.parse(latestEvent.verifiedAt ?? latestEvent.createdAt)`.
|
|
134
|
+
- **`"historical"` or `"manual"`** — never stale by time or commit change.
|
|
135
|
+
- If `policy` is absent — not stale.
|
|
136
|
+
|
|
137
|
+
If stale: return **`stale`**.
|
|
138
|
+
|
|
139
|
+
#### 4b. Policy evidence gap check
|
|
140
|
+
|
|
141
|
+
If a policy is present, check whether all required evidence types and methods are
|
|
142
|
+
present among entailing evidence items:
|
|
143
|
+
|
|
144
|
+
- Build the set of `evidenceType` values from entailing evidence.
|
|
145
|
+
- Build the set of `method` values from entailing evidence.
|
|
146
|
+
- Compute `missingTypes = policy.requiredEvidence` items not in the type set.
|
|
147
|
+
- Compute `missingMethods = (policy.requiredMethods ?? [])` items not in the method set.
|
|
148
|
+
- Corroboration gap: `policy.requiresCorroboration === true` and `entailingEvidence.length < 2`.
|
|
149
|
+
|
|
150
|
+
If any gap exists: return **`proposed`** (the event says verified but policy
|
|
151
|
+
requirements are not met — the claim is effectively a proposed state).
|
|
152
|
+
|
|
153
|
+
#### 4c. Blocking failure check
|
|
154
|
+
|
|
155
|
+
Check whether any evidence item satisfies both:
|
|
156
|
+
- `evidence.passing === false`
|
|
157
|
+
- `evidence.blocking !== false` (i.e., `blocking` is `true` or absent/`undefined`)
|
|
158
|
+
|
|
159
|
+
If such evidence exists: return **`disputed`**.
|
|
160
|
+
|
|
161
|
+
#### 4d. Verified
|
|
162
|
+
|
|
163
|
+
Return **`verified`**.
|
|
164
|
+
|
|
165
|
+
### Step 5: Claim-level status baseline
|
|
166
|
+
|
|
167
|
+
If there is no verification event (or `latestEvent.status` was not one of the above):
|
|
168
|
+
|
|
169
|
+
- If `claim.status === "proposed"` — return **`proposed`**.
|
|
170
|
+
- If `claim.status === "assumed"` — return **`assumed`**.
|
|
171
|
+
|
|
172
|
+
### Step 6: No policy
|
|
173
|
+
|
|
174
|
+
If no policy is resolved (`policy` is `undefined`):
|
|
175
|
+
|
|
176
|
+
- Return **`proposed`** if `evidence.length > 0` (there is evidence but no policy to
|
|
177
|
+
evaluate it against).
|
|
178
|
+
- Return **`unknown`** if `evidence.length === 0`.
|
|
179
|
+
|
|
180
|
+
### Step 7: Policy evidence presence
|
|
181
|
+
|
|
182
|
+
If a policy is present but no verification event exists:
|
|
183
|
+
|
|
184
|
+
- Build the set of `evidenceType` values from all evidence (not just entailing, at
|
|
185
|
+
this step — but in practice the partitioning above was already applied upstream).
|
|
186
|
+
- If `policy.requiredEvidence` is a subset of the evidence type set: return **`proposed`**.
|
|
187
|
+
- Otherwise: return **`unknown`**.
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Derivation ceiling
|
|
192
|
+
|
|
193
|
+
For derived claims (claims built from other claims via `derivationEdges` or
|
|
194
|
+
`derivedFrom`), the status of the derived claim cannot exceed the weakest input
|
|
195
|
+
claim status. This ceiling is applied by the derivation layer before the fold
|
|
196
|
+
receives the claim; it is reflected in `DerivationChangeRecord` entries in the
|
|
197
|
+
report.
|
|
198
|
+
|
|
199
|
+
---
|
|
200
|
+
|
|
201
|
+
## Status ordering (for ceiling purposes)
|
|
202
|
+
|
|
203
|
+
From weakest to strongest: `unknown` < `rejected` < `superseded` < `disputed` <
|
|
204
|
+
`stale` < `assumed` < `proposed` < `verified`.
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Output
|
|
209
|
+
|
|
210
|
+
`deriveClaimStatus` returns `{ status: TrustStatus; policyId: string | undefined }`.
|
|
211
|
+
`policyId` is the `id` of the resolved policy, or `undefined` if none was found.
|
|
212
|
+
|
|
213
|
+
---
|
|
214
|
+
|
|
215
|
+
## Versioning
|
|
216
|
+
|
|
217
|
+
`STATUS_FUNCTION_VERSION` is a string exported by `@kontourai/surface`. It is
|
|
218
|
+
incremented when the algorithm changes in a way that could produce different outputs
|
|
219
|
+
for the same inputs. `InquiryRecord.statusFunctionVersion` captures which version
|
|
220
|
+
was active at resolution time, enabling re-evaluation when the algorithm version
|
|
221
|
+
changes.
|
|
222
|
+
|
|
223
|
+
Conforming implementations must declare which `STATUS_FUNCTION_VERSION` value they
|
|
224
|
+
implement. Implementations claiming version `"1"` must satisfy all conformance
|
|
225
|
+
cases in `spec/conformance/`.
|