CultNet governance contracts: sealed-vote API and fork-desk card (schemas, specs, vectors, C#/TS suites) #72

Merged
libby merged 15 commits from libby/vote-fork-contracts into main 2026-11-10 17:01:44 +00:00

2026-11-10

soul
Fork card: depends are the stored question's prose lines, verbatim
Some checks failed
CultMesh QUIC native / Build win32-x64 (pull_request) Has been skipped
CultMesh QUIC native / Build linux-x64 (pull_request) Has been skipped
CultMesh QUIC native / Release native binaries (pull_request) Has been skipped
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Failing after 14m40s
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (pull_request) Successful in 15s
`huginn schema question` types depends as up to 8 Lines, not references;
Huginn PR #29 (desk.rs:87) hashes them as stored. The page said each was a
campaign:kind:label rendering of a reference. Hash vectors are unchanged
(their depends strings are ids written as text).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 16:35:27 +00:00
soul
Fork card: Soul fixes (decode coverage, byte-built sameResponse, claim boundary, digest covers retracted)
Some checks failed
CultMesh QUIC native / Build win32-x64 (pull_request) Has been skipped
CultMesh QUIC native / Build linux-x64 (pull_request) Has been skipped
CultMesh QUIC native / Release native binaries (pull_request) Has been skipped
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (pull_request) Has been cancelled
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Has been cancelled
- mindRoutes: a percent-encoded configured Mind resolves once (%6eibu), a
  double-encoded one does not; `.`, `..` are unknown; the C# judge decodes
  strictly so invalid UTF-8 is a miss by rule, not by luck.
- sameResponse pairs are compared as bytes built from the spec's refusal rule,
  not from the vector against itself.
- claim_in_future: exactly five minutes is not flagged, five minutes and half
  a second is, from the stored received_at.
- the digest that decides "same body" must cover `retracted` too.
- Dropped the judge rule that the new ruled_from-needs-a-ruling rule subsumes.
- Mapping table says what closes a card and when `ruling` is nil.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 16:33:38 +00:00
soul
Fork card: the mind's record is projected (answer id n<seq>, snake_case codes), same body is Huginn's alone, claim_in_future follows the stored received_at
Some checks failed
CultMesh QUIC native / Build win32-x64 (pull_request) Has been skipped
CultMesh QUIC native / Build linux-x64 (pull_request) Has been skipped
CultMesh QUIC native / Release native binaries (pull_request) Has been skipped
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (pull_request) Has been cancelled
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Has been cancelled
From the read of Huginn PR #29 at 3a16772: its record names answers by the
full document id and spells refusal codes in PascalCase, keeps body_sha256
beside the fields, and decides claim_in_future against an unrounded clock.
The page now says what the second door projects, that no digest is a field
of the document, and what a digest must cover to be "same body".

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 16:21:16 +00:00
soul
Fork card transport: the Mind id rides in the path; V14 asserted on every scenario
Routes are GET /api/desk/{mind}/forks, GET /api/desk/{mind}/forks/{fork_id}
and POST /api/desk/{mind}/answers with fork_id still in the body (Heimdall's
file 4.2 and 4.3a). Spec: the {mind} segment rules M1 to M5 (fixed configured
list, never joined into a path, unknown Mind is the unknown-fork refusal with
the same bytes, the route's Mind must hold the fork_id, scopes not per Mind,
the decider list is Bifrost-to-Huginn only, marked Bifrost/Huginn work).
Schema: read_request carries `mind`. Vectors: transport.mindRoutes (47 cases,
sameResponse pairs, the socket a judge would open), scenarios for a body that
names `mind` or `deciders` (body_unknown_field), read_request with the Mind.
Both judges replay them; both also assert on every scenario that a request id
bound by another account never returns that account's answer id or document
(V14), with `of` allowed to name the counted answer. Open choice 11: an
unreachable known Mind answers unavailable, an unknown one fork_unknown.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 16:21:01 +00:00
soul
Fork card: a closed card may name no ruling (withdrawn or superseded question)
Step 6 reads status, and the mind closes a question on any resolution, not
only a ruling (Heimdall's file, step 6: "resolved or withdrawn: fork_closed";
Huginn PR #29 desk.rs:161). The rule "a closed card names its ruling" made a
withdrawn question an invalid card. Now only an open card may not name a
ruling, and ruled_from needs a ruling. Vectors: a valid withdrawn card, and
negatives for an open card with a ruling and ruled_from without one.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 16:18:14 +00:00
soul
Fork card: replayed stale_fork returns the current card; identical body from another account; same body includes answered_at
Some checks failed
CultMesh QUIC native / Build win32-x64 (pull_request) Has been skipped
CultMesh QUIC native / Build linux-x64 (pull_request) Has been skipped
CultMesh QUIC native / Release native binaries (pull_request) Has been skipped
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Failing after 15m55s
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (pull_request) Successful in 26s
Heimdall 1.3e Q1-Q6, V12-V18: vectors for an identical body from another
account (own Counted, never the first account's Refused), only answered_at
changed, a forbidden or door-refused or unknown-fork request binding no id, an
uppercase id. Both judges replay a stale_fork with the current card.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 15:43:31 +00:00
soul
Fork card: Differences names request_id as this page's addition
Some checks failed
CultMesh QUIC native / Build win32-x64 (pull_request) Has been skipped
CultMesh QUIC native / Build linux-x64 (pull_request) Has been skipped
CultMesh QUIC native / Release native binaries (pull_request) Has been skipped
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (pull_request) Has been cancelled
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Has been cancelled
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 15:39:01 +00:00
soul
Fork card: unavailable is not confirmed, request_id makes a retry safe, forbidden precedes a bad body
Some checks failed
CultMesh QUIC native / Build win32-x64 (pull_request) Has been skipped
CultMesh QUIC native / Build linux-x64 (pull_request) Has been skipped
CultMesh QUIC native / Release native binaries (pull_request) Has been skipped
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (pull_request) Has been cancelled
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Has been cancelled
unavailable now says the outcome is NOT CONFIRMED (a timeout after Huginn had
the request may have stored it). The answer request and the stored desk_answer
carry a client-set request_id: a retry with the same id and body returns the
stored outcome and never adds a Revision; the same id with another body is
request_id_conflict (409). A non-Active member with a bad body gets forbidden
first. Scenarios: unavailable-then-retry (stored and not stored), replayed
Refused, conflict, same id from another account, forbidden before the body.
Both judges follow; vectors regenerated.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 15:38:15 +00:00
soul
Fork card transport: forbidden (403) for a non-Active member; unavailable covers list and read
Some checks failed
CultMesh QUIC native / Build win32-x64 (pull_request) Has been skipped
CultMesh QUIC native / Build linux-x64 (pull_request) Has been skipped
CultMesh QUIC native / Release native binaries (pull_request) Has been skipped
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (pull_request) Has been cancelled
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Has been cancelled
Door refusals gain `forbidden`, judged after scope and before Huginn is asked.
The Transport section states unavailable (503) also answers list and read when
Huginn is down; auth and membership are judged first so it never hides a
refusal. Nine new auth cases pin both; both judges follow.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 15:31:11 +00:00
soul
Fork card: transport (list, read, answer; desk.read and desk.answer), note limit in UTF-8 bytes, unavailable
Some checks failed
CultMesh QUIC native / Build win32-x64 (pull_request) Has been skipped
CultMesh QUIC native / Build linux-x64 (pull_request) Has been skipped
CultMesh QUIC native / Release native binaries (pull_request) Has been skipped
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (pull_request) Has been cancelled
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Has been cancelled
Adds the Transport section: three routes, request and response documents
(list_request, list, read_request, entry), the stamp and cursor, auth in order
(bearer, audience bifrost-desk, scope), the MAS token and votes.* tokens
refused, Huginn's outcome returned unchanged, a stale refusal carrying the
current card; every part marked as owed by Bifrost or Huginn. Note limit is
4000 UTF-8 bytes (a rule beside the looser maxLength) with ASCII, multibyte,
astral and combining vectors. unavailable (503) is a document of its own,
outside the refusal set, stores nothing, and is raised after body shape and
before the door. Vectors regenerated; both judges replay lists, auth, notes and
the unavailable scenarios.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 15:26:50 +00:00
soul
Governance suites: judges follow the contracts (no ruled, ruled_from on open card, UTF-16 reason rule); validator counts code points
Some checks failed
CultMesh QUIC native / Build win32-x64 (pull_request) Has been skipped
CultMesh QUIC native / Build linux-x64 (pull_request) Has been skipped
CultMesh QUIC native / Release native binaries (pull_request) Has been skipped
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (pull_request) Has been cancelled
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Has been cancelled
Generator, C# judge and TS judge drop `ruled`, add the open-card ruled_from
rule and the reason UTF-16 rule. MiniJsonSchemaValidator counted UTF-16 units
for minLength/maxLength where JSON Schema and Ajv count code points, so the
two judges refused different strings; it now counts runes.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 15:20:04 +00:00
soul
Vote API: reauthentication_required, held, highStakes; Standard scope, trimming, auth per route, unbuilt limits
Closed refusal set gains reauthentication_required (401, sealed commit on a
highStakes motion, Bifrost #37 D2) and held (503, the voting brake, D5);
motion and list item carry highStakes, fixed at open. Spec text: the 413 cap
and per-account 429 are not on Bifrost's member API yet; commitment mismatch
(plain 400) and contended reveal (WrongPhase 409) must be split first; Bifrost
trims candidate and reason today and must stop; Standard motions are not
covered here; auth is stated per route now and after #37 with an opaque bearer
on the wire. A reason is counted in UTF-16 units: the schema alone cannot say
it, so a rule and two astral vectors do.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 15:20:04 +00:00
soul
Fork card: drop ruled, add V10a, attribution is not authority, unknown fork and malformed body are not stored
A ruling that answers a fork closes it (Eureka and Heimdall, 2026-11-10), so a
retraction after a ruling is fork_closed and there is no `ruled` code (R3 and
V10 go; V10a replaces them). An open card now cannot carry ruled_from. The
stamped `authority` on a stored answer is an attribution; authority exists only
where a ruling cites a counted answer. The page says an unknown fork and a
malformed body are refused and not stored (Huginn answers fork_unknown; it does
not key a record). Note length is counted in code points.

Vectors are regenerated; the generator and suites follow in the next commits.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 15:20:04 +00:00
soul
GovernanceVectorTests: Clone keeps a nil node nil (null choice and note in stored answers)
Some checks failed
CultMesh QUIC native / Build win32-x64 (pull_request) Has been skipped
CultMesh QUIC native / Build linux-x64 (pull_request) Has been skipped
CultMesh QUIC native / Release native binaries (pull_request) Has been skipped
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (pull_request) Has been cancelled
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Has been cancelled
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 15:08:37 +00:00
soul
CultNet governance contracts: vote API (sealed motions) and fork-desk card, with schemas, specs, vectors and C#/TS conformance
Some checks failed
CultMesh QUIC native / Build win32-x64 (pull_request) Has been skipped
CultMesh QUIC native / Build linux-x64 (pull_request) Has been skipped
CultMesh QUIC native / Release native binaries (pull_request) Has been skipped
CultMesh QUIC native / Verify committed binaries match SHA256SUMS (pull_request) Has been cancelled
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Has been cancelled
contracts/cultnet/governance: vote-api.{md,schema.json}, fork-card.{md,schema.json}, vectors for both (positive, negative per refusal, byte-exact MessagePack), generator scripts/gen-governance-vectors.py. Fork rules follow /opt/commons/handoff/fork-desk-answer-binding-20261110.md (options_hash canonical JSON with NFC, desk_answer tagged outcome, retraction, second door).

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-10 15:07:37 +00:00