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>
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>
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>
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>
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>
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>
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>
The put's flush is bounded by the result window, so a 25 ms flush and 100 ms window made a slow
ack on a loaded forge runner surface as local_send_failed instead of the window_elapsed the test
checks. Drop the flush override and give the window room.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
"server mode admits a pinned client that restarts on a new port" asserted the Accept arrived after sleep(100) and failed on the forge runner (ok(accept) undefined, run 894). It now polls for the packet or frame it asserts on, up to 2 s; no assertion changed.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
scripts/test-declared-dependency-ranges.mjs asserts the clone has release tags; actions/checkout fetches none by default.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The act-latest image has no cargo; the cross-runtime CultCache store tests build the Rust interop example and failed with "cargo: not found" once the RUDP flake and pwsh were out of the way.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Vectors admittedMessageIdDuplicates carry [invalid,valid], [valid,invalid], [valid,valid]. Decoders that see the
duplicate (C#, Rust) must refuse all three; map-collapsing decoders (Python, TS, Kotlin) judge the last key.
Python, TS, Kotlin and Rust already behave as declared; C# fails: it accepted [valid,valid].
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
The forge has two labels: docker (node 22, py 3.11) and ubuntu-latest (act-latest, node 24,
py 3.12); neither carries pwsh. Windows legs stay workflow_dispatch only.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Failing before the fix: C# 1 (duplicate messageId, second is U+200B), TypeScript 1 (a + lone surrogate),
Kotlin 1 (a document payload string with a lone surrogate no longer decodes). Python and Rust already pass
the new vectors. The range probes read the generated write-admitted-message-id-ranges.json and pass on the
correct tables; they exist to fail when any range is deleted or narrowed.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
scripts/gen-unicode-messageid-tables.py emits each runtime's Letter-or-Number and Default_Ignorable tables from the two UCD 16.0 files; --check fails on a stale or hand-edited table.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Failing before the fix: Rust 1 (16100), Python 2 (16100, 10D40), Kotlin 4 (16100, 10D40, 2EBF0, 2EE5D).
TypeScript and C# already pass: their platform data is 16.0 or later.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Raw compare. std is_numeric for N; embedded letter (Unicode 15.1) and Default_Ignorable (16.0) range tables.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Raw compare. .NET Unicode categories for L/N, an embedded Default_Ignorable range table (Unicode 16.0).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Raw compare. JDK Character categories for L/N, an embedded Default_Ignorable range table (Unicode 16.0).
The MessagePack string reader now refuses invalid UTF-8 instead of substituting U+FFFD.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Raw compare, no trim. The platform's regex property tables carry both properties; no table embedded.
D800 vector now driven from wireHex.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Pins Heimdall's rule: a write_admitted messageId needs a Unicode letter or number and no
Default_Ignorable_Code_Point, compared raw. Shared vectors plus a runner test in TS, Python,
Kotlin, C# and Rust. No decoder changed; the refuse vectors fail.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>