Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// Generated by `t27c gen-ts` from claim.t27. Do not edit.
//
// Every name, value and type here comes from that spec. Change the spec and
// regenerate; an edit made here is lost the next time anyone builds.

export const CL_RUNNING = 0 satisfies number;
export const CL_ACCEPTED = 1 satisfies number;
export const CL_REJECTED = 2 satisfies number;
export const CL_AWAITING_REVIEW = 3 satisfies number;
export const CL_FAILED = 4 satisfies number;
export const CL_CANCELLED = 5 satisfies number;
export const CLAIM_STATES = 6 satisfies number;
export const V_NONE = 0 satisfies number;
export const V_ACCEPT = 1 satisfies number;
export const V_SEND_BACK = 2 satisfies number;
export const V_ESCALATE = 3 satisfies number;
export const V_WAIT = 4 satisfies number;
export const V_FAILED = 5 satisfies number;
export const V_CANCELLED = 6 satisfies number;
export const V_EMPTY = 7 satisfies number;
export const V_STALE_CONTRACT = 8 satisfies number;
export const V_OBSOLETE = 9 satisfies number;
export const V_OTHER = 10 satisfies number;
export const VERDICT_CODES = 11 satisfies number;
export const SEND_BACK_FLOOR_MINUTES = 60 satisfies number;
export const WAIT_FROZEN_FLOOR_MINUTES = 360 satisfies number;
export const DEFAULT_SEND_BACK_CEILING = 2 satisfies number;
export const FREE_ATTEMPT_CEILING = 3 satisfies number;
export const REVIEWER_MISS_CEILING = 3 satisfies number;
export const UNREAD_CRITERIA = 1 satisfies number;
// t27c gen-ts: fn claim_of_step was not emitted -- this backend lowers declarations, not bodies.
// t27c gen-ts: fn criteria_read_as was not emitted -- this backend lowers declarations, not bodies.
// t27c gen-ts: fn escalation_kind_of was not emitted -- this backend lowers declarations, not bodies.
// t27c gen-ts: fn claim_of was not emitted -- this backend lowers declarations, not bodies.
// t27c gen-ts: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
// t27c gen-ts: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
// t27c gen-ts: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
// t27c gen-ts: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
// t27c gen-ts: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
// t27c gen-ts: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
// t27c gen-ts: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
// t27c gen-ts: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
// t27c gen-ts: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
// t27c gen-ts: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
// t27c gen-ts: a TestBlock was not emitted -- it is checked by the compiler, not by the artifact.
// t27c gen-ts: a InvariantBlock was not emitted -- it is checked by the compiler, not by the artifact.
// t27c gen-ts: a InvariantBlock was not emitted -- it is checked by the compiler, not by the artifact.
export const BACKFILL_WINDOW_MINUTES = 120 satisfies number;
export const CEILING_FLOOR_MINUTES = 60 satisfies number;
export const KIND_BASE_TRUTH_ALONE = 7 satisfies number;
export const KIND_BEYOND_THE_PATCH = 6 satisfies number;
export const KIND_DEAD_LETTER = 3 satisfies number;
export const KIND_NO_CRITERIA = 1 satisfies number;
export const KIND_REVIEWER_GAVE_UP = 4 satisfies number;
export const KIND_SEND_BACK_CEILING = 2 satisfies number;
export const KIND_UNRECORDED = 0 satisfies number;
export const MAX_RELEASES = 1 satisfies number;
export const OBSOLETE_STATE = "obsolete" satisfies string;
export const RETRY_FLOOR_MINUTES = 30 satisfies number;
export const STEP_BACKFILL = 2 satisfies number;
export const STEP_CLOSE = 3 satisfies number;
export const STEP_HOLD = 0 satisfies number;
export const STEP_RELEASE = 1 satisfies number;
// t27c gen-ts: fn escalation_kind was not emitted -- this backend lowers declarations, not bodies.
// t27c gen-ts: fn next_step was not emitted -- this backend lowers declarations, not bodies.
// t27c gen-ts: fn wants_backfill was not emitted -- this backend lowers declarations, not bodies.

// Declaration order, which the spec's own laws depend on.
export const __STRUCT_ORDER__ = [] as const;
export const __DECL_ORDER__ = ["CL_RUNNING", "CL_ACCEPTED", "CL_REJECTED", "CL_AWAITING_REVIEW", "CL_FAILED", "CL_CANCELLED", "CLAIM_STATES", "V_NONE", "V_ACCEPT", "V_SEND_BACK", "V_ESCALATE", "V_WAIT", "V_FAILED", "V_CANCELLED", "V_EMPTY", "V_STALE_CONTRACT", "V_OBSOLETE", "V_OTHER", "VERDICT_CODES", "SEND_BACK_FLOOR_MINUTES", "WAIT_FROZEN_FLOOR_MINUTES", "DEFAULT_SEND_BACK_CEILING", "FREE_ATTEMPT_CEILING", "REVIEWER_MISS_CEILING", "UNREAD_CRITERIA", "BACKFILL_WINDOW_MINUTES", "CEILING_FLOOR_MINUTES", "KIND_BASE_TRUTH_ALONE", "KIND_BEYOND_THE_PATCH", "KIND_DEAD_LETTER", "KIND_NO_CRITERIA", "KIND_REVIEWER_GAVE_UP", "KIND_SEND_BACK_CEILING", "KIND_UNRECORDED", "MAX_RELEASES", "OBSOLETE_STATE", "RETRY_FLOOR_MINUTES", "STEP_BACKFILL", "STEP_CLOSE", "STEP_HOLD", "STEP_RELEASE"] as const;

// What this spec holds and this backend did not print. Empty is the whole
// story most of the time; an entry here is a promise the artifact does not
// keep, and reading it is how a tool tells a partial module from a complete
// one without parsing comments.
//
// A LIST, not a map keyed by name. `specs/numeric/formats.t27` declares four
// separate consts called `result`, one per test block, and under an object
// literal three of the four omissions vanished into the fourth -- a record of
// what went missing that itself went missing. A spec is free to reuse a
// name; this file is not free to lose the second one.
export const __NOT_EMITTED__ = Object.freeze([] as const);
164 changes: 164 additions & 0 deletions trios/agent-server/apps/server/src/api/services/queen-claim.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,164 @@
/**
* @license
* Copyright 2025 BrowserOS
* SPDX-License-Identifier: AGPL-3.0-or-later
*
* WHAT A DISPATCH ROW STILL CLAIMS (gHashTag/t27 specs/queen/claim.t27,
* t27#8765; gHashTag/trios#1767).
*
* Every round asks it of every queen_dispatch row, and the board asks it of
* every card: running, accepted, rejected, awaitingReview, failed or
* cancelled. The card's claim_of decides, as generated wasm, and it imports
* the review valve's own escalation_kind and next_step instead of restating
* them. This file only reads the row into the card's codes and the card's
* answer back into the word the round, the board and queend use.
*
* The rule was hand-written TypeScript in queen-tick.ts until this card. That
* body is kept verbatim in tests/api/queen-claim-card.test.ts, where the card
* is proven to answer it word for word over every input the round can pass.
*/

import { logger } from '../../lib/logger'
import { flag, loadCardWasm, u32 } from './queen-card-wasm'
import {
CL_ACCEPTED,
CL_AWAITING_REVIEW,
CL_CANCELLED,
CL_FAILED,
CL_REJECTED,
CL_RUNNING,
DEFAULT_SEND_BACK_CEILING,
OBSOLETE_STATE,
V_ACCEPT,
V_CANCELLED,
V_EMPTY,
V_ESCALATE,
V_FAILED,
V_NONE,
V_OBSOLETE,
V_OTHER,
V_SEND_BACK,
V_STALE_CONTRACT,
V_WAIT,
} from './queen-claim-card.gen'

export const CLAIM_CARD = 'queen/claim.wasm'

export type DispatchClaim =
| 'running'
| 'accepted'
| 'rejected'
| 'awaitingReview'
| 'failed'
| 'cancelled'

/** What a row carries besides its verdict, as stateOfDispatch has always taken it. */
export interface ClaimLease {
/** Milliseconds since finished_at, the one clock nothing rewrites. */
idleMs?: number
sendBacks?: number
/** The send-back ceiling; the card's DEFAULT_SEND_BACK_CEILING when absent. */
ceiling?: number
/** How many times this issue's ceiling has already been handed back. */
releases?: number
/** The escalation's kind is read from these (specs/queen/review_valve.t27). */
criteria?: number
freeAttempts?: number
reviewerMisses?: number
/**
* TRIOS_QUEEN_RELEASE_DEAD_CLAIMS (holds.t27 send_back_claim, t27#9111). It
* is read by stateOfDispatch before the card is asked, never by the card;
* the environment's reading is used when it is not given.
*/
releaseDeadClaims?: boolean
}

/**
* The review_state words the card has a rule for. Recognising a word is text
* work and stays here; what the word means is the card's. A word this table
* does not name is V_OTHER, which the card holds with no clock.
*/
const VERDICTS: ReadonlyMap<string, number> = new Map([
['', V_NONE],
['accept', V_ACCEPT],
['sendBack', V_SEND_BACK],
['escalate', V_ESCALATE],
['wait', V_WAIT],
['failed', V_FAILED],
['cancelled', V_CANCELLED],
['empty', V_EMPTY],
['stale-contract', V_STALE_CONTRACT],
[OBSOLETE_STATE, V_OBSOLETE],
])

/** The card's code for a review_state value; null and undefined are no verdict. */
export function verdictCode(reviewState: unknown): number {
return VERDICTS.get(String(reviewState ?? '')) ?? V_OTHER
}

const CLAIMS: ReadonlyMap<number, DispatchClaim> = new Map([
[CL_RUNNING, 'running'],
[CL_ACCEPTED, 'accepted'],
[CL_REJECTED, 'rejected'],
[CL_AWAITING_REVIEW, 'awaitingReview'],
[CL_FAILED, 'failed'],
[CL_CANCELLED, 'cancelled'],
])

/** Codes already reported, so a drifted card logs once, not once per row per round. */
const reported = new Set<number>()

/**
* The word for a claim code. claim.t27 answers only CL_ codes, so any other
* code means the vendored card drifted from its constants. That is logged as
* an error and the row is held as awaitingReview, the claim the card gives a
* verdict it does not know: the issue and its files stay held, so nothing is
* dispatched twice, and one row cannot fail every round and the board (the
* round maps every in-flight row through this, with no try per row). A card
* that cannot load still throws, from loadCardWasm.
*/
export function claimWord(code: number): DispatchClaim {
const word = CLAIMS.get(code)
if (word !== undefined) return word
if (!reported.has(code)) {
reported.add(code)
logger.error('The claim card answered a code that is no claim', {
card: CLAIM_CARD,
code,
held: 'awaitingReview',
})
}
return 'awaitingReview'
}

/**
* Whole minutes since finished_at. Every floor in the card is a whole number
* of minutes, so `idleMs >= floorMs` holds exactly when this is at least the
* floor in minutes. An unreadable (NaN) or negative clock reads 0: it keeps
* the hold, as every comparison against NaN did.
*/
export const idleMinutes = (idleMs: number | undefined): number =>
u32((idleMs ?? 0) / 60_000)

/** What the row claims, decided by the card's claim_of. */
export function claimOf(
finished: boolean,
reviewState: unknown,
lease: ClaimLease = {},
): DispatchClaim {
return claimWord(
loadCardWasm(CLAIM_CARD).call(
'claim_of',
flag(finished),
verdictCode(reviewState),
idleMinutes(lease.idleMs),
u32(lease.sendBacks ?? 0),
u32(lease.ceiling ?? DEFAULT_SEND_BACK_CEILING),
u32(lease.releases ?? 0),
flag(lease.criteria != null),
u32(lease.criteria ?? 0),
u32(lease.freeAttempts ?? 0),
u32(lease.reviewerMisses ?? 0),
),
)
}
Loading
Loading