From e1b4984b4c5225fc93f2df359be53066cac7ded7 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 19 Oct 2026 01:35:55 +0000 Subject: [PATCH 1/5] widgets: 27 hive widgets, logic compiled from gHashTag/t27 specs/queen and specs/automation Twenty-seven tool widgets for course 11, the Queen and the hive. Each one opens with the question its lesson asks and decides nothing itself: its logic.js is the fn bodies of one t27 master c42f8fb40 spec, compiled to wasm by 999 scripts/t27-logic.mjs and imported with widget-logic.mjs --import. The 15 specs: queen/shaping, tasks, priority, review_first, dispatch, hive, drain, reviewer, merger_gate, key_failover; automation/review-jam, queen-contributor-keys, token-wallet, agent-hive-group, card-chat-night-watch. review-jam needed two fixes in the door generator, both in gHashTag/999-multibots-telegraf#4020. jam_action(case: u8) made a door tsc and esbuild refuse. jam_is_queens_turn compiled to (case > 8) | (250 >> case), so a door that read all 32 bits answered true for JAM_NONE. The logic here is built with that PR. hive-kit.js and hive-kit.css build on dsp-kit.js: a kanban board whose cards sit in the column a spec fn names, a review gate, meters, a ladder, a clock dial, comb cells, a grid of answers and tap rows. A share card (?card=1) hides the sliders, so it shows the answer. No K_ constant holds a result of a spec fn: a K_ is an input, a preset, a range, or a constant of the logic spec that widget-logic --check holds equal. The gallery grows from 279 to 306 widgets. The hive shelf (game) sits before the wars pin at 271..297, and the pins after it move by 27. The 15 lesson specs are vendored under public/t27/files/specs byte for byte as t27 master. That replaces older copies of review-jam, agent-hive-group and dispatch, which no course or widget read before. Refs #1698 Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01ScHVrGSr6zZUdwkdC8DR9k --- .../specs/automation/agent-hive-group.t27 | 88 ++- .../t27/files/specs/automation/review-jam.t27 | 201 ++++-- .../public/t27/files/specs/queen/dispatch.t27 | 211 ++++++- .../public/t27/files/specs/queen/drain.t27 | 321 ++++++++++ .../public/t27/files/specs/queen/hive.t27 | 184 ++++++ .../t27/files/specs/queen/key_failover.t27 | 383 +++++++++++ .../t27/files/specs/queen/review_first.t27 | 175 ++++++ .../public/t27/files/specs/queen/reviewer.t27 | 315 ++++++++++ apps/website/public/widgets/gallery.t27 | 43 +- .../public/widgets/hive-aging/card.png | Bin 0 -> 28474 bytes .../public/widgets/hive-aging/hive-aging.t27 | 80 +++ .../public/widgets/hive-aging/index.html | 47 ++ .../public/widgets/hive-aging/logic.js | 40 ++ .../public/widgets/hive-aging/logic.t27 | 163 +++++ .../website/public/widgets/hive-aging/tool.js | 37 ++ .../public/widgets/hive-boundary/card.png | Bin 0 -> 69085 bytes .../widgets/hive-boundary/hive-boundary.t27 | 79 +++ .../public/widgets/hive-boundary/index.html | 47 ++ .../public/widgets/hive-boundary/logic.js | 36 ++ .../public/widgets/hive-boundary/logic.t27 | 130 ++++ .../public/widgets/hive-boundary/tool.js | 31 + .../website/public/widgets/hive-burn/card.png | Bin 0 -> 43514 bytes .../public/widgets/hive-burn/hive-burn.t27 | 71 +++ .../public/widgets/hive-burn/index.html | 47 ++ .../website/public/widgets/hive-burn/logic.js | 40 ++ .../public/widgets/hive-burn/logic.t27 | 139 ++++ apps/website/public/widgets/hive-burn/tool.js | 31 + .../public/widgets/hive-called/card.png | Bin 0 -> 44470 bytes .../widgets/hive-called/hive-called.t27 | 73 +++ .../public/widgets/hive-called/index.html | 47 ++ .../public/widgets/hive-called/logic.js | 116 ++++ .../public/widgets/hive-called/logic.t27 | 361 +++++++++++ .../public/widgets/hive-called/tool.js | 26 + .../website/public/widgets/hive-cell/card.png | Bin 0 -> 59722 bytes .../public/widgets/hive-cell/hive-cell.t27 | 90 +++ .../public/widgets/hive-cell/index.html | 47 ++ .../website/public/widgets/hive-cell/logic.js | 44 ++ .../public/widgets/hive-cell/logic.t27 | 184 ++++++ apps/website/public/widgets/hive-cell/tool.js | 32 + .../public/widgets/hive-chain/card.png | Bin 0 -> 44858 bytes .../public/widgets/hive-chain/hive-chain.t27 | 77 +++ .../public/widgets/hive-chain/index.html | 47 ++ .../public/widgets/hive-chain/logic.js | 116 ++++ .../public/widgets/hive-chain/logic.t27 | 361 +++++++++++ .../website/public/widgets/hive-chain/tool.js | 30 + .../public/widgets/hive-column/card.png | Bin 0 -> 42390 bytes .../widgets/hive-column/hive-column.t27 | 69 ++ .../public/widgets/hive-column/index.html | 47 ++ .../public/widgets/hive-column/logic.js | 123 ++++ .../public/widgets/hive-column/logic.t27 | 594 ++++++++++++++++++ .../public/widgets/hive-column/tool.js | 25 + .../public/widgets/hive-cooldown/card.png | Bin 0 -> 48420 bytes .../widgets/hive-cooldown/hive-cooldown.t27 | 84 +++ .../public/widgets/hive-cooldown/index.html | 47 ++ .../public/widgets/hive-cooldown/logic.js | 116 ++++ .../public/widgets/hive-cooldown/logic.t27 | 361 +++++++++++ .../public/widgets/hive-cooldown/tool.js | 32 + .../public/widgets/hive-credit/card.png | Bin 0 -> 46486 bytes .../widgets/hive-credit/hive-credit.t27 | 82 +++ .../public/widgets/hive-credit/index.html | 47 ++ .../public/widgets/hive-credit/logic.js | 68 ++ .../public/widgets/hive-credit/logic.t27 | 183 ++++++ .../public/widgets/hive-credit/tool.js | 36 ++ .../public/widgets/hive-drain/card.png | Bin 0 -> 59269 bytes .../public/widgets/hive-drain/hive-drain.t27 | 76 +++ .../public/widgets/hive-drain/index.html | 47 ++ .../public/widgets/hive-drain/logic.js | 76 +++ .../public/widgets/hive-drain/logic.t27 | 321 ++++++++++ .../website/public/widgets/hive-drain/tool.js | 26 + .../website/public/widgets/hive-gate/card.png | Bin 0 -> 43753 bytes .../public/widgets/hive-gate/hive-gate.t27 | 79 +++ .../public/widgets/hive-gate/index.html | 47 ++ .../website/public/widgets/hive-gate/logic.js | 52 ++ .../public/widgets/hive-gate/logic.t27 | 302 +++++++++ apps/website/public/widgets/hive-gate/tool.js | 36 ++ .../public/widgets/hive-jam-action/card.png | Bin 0 -> 65980 bytes .../hive-jam-action/hive-jam-action.t27 | 68 ++ .../public/widgets/hive-jam-action/index.html | 47 ++ .../public/widgets/hive-jam-action/logic.js | 32 + .../public/widgets/hive-jam-action/logic.t27 | 274 ++++++++ .../public/widgets/hive-jam-action/tool.js | 22 + apps/website/public/widgets/hive-jam/card.png | Bin 0 -> 55371 bytes .../public/widgets/hive-jam/hive-jam.t27 | 68 ++ .../public/widgets/hive-jam/index.html | 47 ++ apps/website/public/widgets/hive-jam/logic.js | 32 + .../website/public/widgets/hive-jam/logic.t27 | 274 ++++++++ apps/website/public/widgets/hive-jam/tool.js | 24 + .../public/widgets/hive-key-error/card.png | Bin 0 -> 50544 bytes .../widgets/hive-key-error/hive-key-error.t27 | 75 +++ .../public/widgets/hive-key-error/index.html | 47 ++ .../public/widgets/hive-key-error/logic.js | 60 ++ .../public/widgets/hive-key-error/logic.t27 | 383 +++++++++++ .../public/widgets/hive-key-error/tool.js | 32 + .../public/widgets/hive-key-rest/card.png | Bin 0 -> 47240 bytes .../widgets/hive-key-rest/hive-key-rest.t27 | 75 +++ .../public/widgets/hive-key-rest/index.html | 47 ++ .../public/widgets/hive-key-rest/logic.js | 60 ++ .../public/widgets/hive-key-rest/logic.t27 | 383 +++++++++++ .../public/widgets/hive-key-rest/tool.js | 25 + apps/website/public/widgets/hive-kit.css | 88 +++ apps/website/public/widgets/hive-kit.js | 153 +++++ .../public/widgets/hive-lifecycle/card.png | Bin 0 -> 65566 bytes .../widgets/hive-lifecycle/hive-lifecycle.t27 | 72 +++ .../public/widgets/hive-lifecycle/index.html | 47 ++ .../public/widgets/hive-lifecycle/logic.js | 123 ++++ .../public/widgets/hive-lifecycle/logic.t27 | 594 ++++++++++++++++++ .../public/widgets/hive-lifecycle/tool.js | 29 + .../public/widgets/hive-master-red/card.png | Bin 0 -> 63629 bytes .../hive-master-red/hive-master-red.t27 | 82 +++ .../public/widgets/hive-master-red/index.html | 47 ++ .../public/widgets/hive-master-red/logic.js | 52 ++ .../public/widgets/hive-master-red/logic.t27 | 302 +++++++++ .../public/widgets/hive-master-red/tool.js | 37 ++ .../public/widgets/hive-next-key/card.png | Bin 0 -> 50136 bytes .../widgets/hive-next-key/hive-next-key.t27 | 87 +++ .../public/widgets/hive-next-key/index.html | 47 ++ .../public/widgets/hive-next-key/logic.js | 60 ++ .../public/widgets/hive-next-key/logic.t27 | 383 +++++++++++ .../public/widgets/hive-next-key/tool.js | 49 ++ .../public/widgets/hive-night/card.png | Bin 0 -> 52503 bytes .../public/widgets/hive-night/hive-night.t27 | 75 +++ .../public/widgets/hive-night/index.html | 47 ++ .../public/widgets/hive-night/logic.js | 32 + .../public/widgets/hive-night/logic.t27 | 146 +++++ .../website/public/widgets/hive-night/tool.js | 32 + .../public/widgets/hive-price/card.png | Bin 0 -> 27811 bytes .../public/widgets/hive-price/hive-price.t27 | 78 +++ .../public/widgets/hive-price/index.html | 47 ++ .../public/widgets/hive-price/logic.js | 40 ++ .../public/widgets/hive-price/logic.t27 | 139 ++++ .../website/public/widgets/hive-price/tool.js | 29 + .../website/public/widgets/hive-rank/card.png | Bin 0 -> 68473 bytes .../public/widgets/hive-rank/hive-rank.t27 | 84 +++ .../public/widgets/hive-rank/index.html | 47 ++ .../website/public/widgets/hive-rank/logic.js | 40 ++ .../public/widgets/hive-rank/logic.t27 | 163 +++++ apps/website/public/widgets/hive-rank/tool.js | 43 ++ .../public/widgets/hive-review-first/card.png | Bin 0 -> 48607 bytes .../hive-review-first/hive-review-first.t27 | 71 +++ .../widgets/hive-review-first/index.html | 47 ++ .../public/widgets/hive-review-first/logic.js | 44 ++ .../widgets/hive-review-first/logic.t27 | 175 ++++++ .../public/widgets/hive-review-first/tool.js | 30 + .../public/widgets/hive-reviewers/card.png | Bin 0 -> 27615 bytes .../widgets/hive-reviewers/hive-reviewers.t27 | 91 +++ .../public/widgets/hive-reviewers/index.html | 47 ++ .../public/widgets/hive-reviewers/logic.js | 60 ++ .../public/widgets/hive-reviewers/logic.t27 | 315 ++++++++++ .../public/widgets/hive-reviewers/tool.js | 33 + .../public/widgets/hive-revisit/card.png | Bin 0 -> 61825 bytes .../widgets/hive-revisit/hive-revisit.t27 | 74 +++ .../public/widgets/hive-revisit/index.html | 47 ++ .../public/widgets/hive-revisit/logic.js | 60 ++ .../public/widgets/hive-revisit/logic.t27 | 315 ++++++++++ .../public/widgets/hive-revisit/tool.js | 28 + .../public/widgets/hive-shape/card.png | Bin 0 -> 42576 bytes .../public/widgets/hive-shape/hive-shape.t27 | 88 +++ .../public/widgets/hive-shape/index.html | 47 ++ .../public/widgets/hive-shape/logic.js | 36 ++ .../public/widgets/hive-shape/logic.t27 | 130 ++++ .../website/public/widgets/hive-shape/tool.js | 37 ++ .../website/public/widgets/hive-ship/card.png | Bin 0 -> 46829 bytes .../public/widgets/hive-ship/hive-ship.t27 | 80 +++ .../public/widgets/hive-ship/index.html | 47 ++ .../website/public/widgets/hive-ship/logic.js | 44 ++ .../public/widgets/hive-ship/logic.t27 | 184 ++++++ apps/website/public/widgets/hive-ship/tool.js | 50 ++ .../public/widgets/hive-task-filter/card.png | Bin 0 -> 52118 bytes .../hive-task-filter/hive-task-filter.t27 | 79 +++ .../widgets/hive-task-filter/index.html | 47 ++ .../public/widgets/hive-task-filter/logic.js | 40 ++ .../public/widgets/hive-task-filter/logic.t27 | 149 +++++ .../public/widgets/hive-task-filter/tool.js | 31 + apps/website/specs/widgets/gallery.t27 | 43 +- apps/website/specs/widgets/hive-aging.t27 | 80 +++ apps/website/specs/widgets/hive-boundary.t27 | 79 +++ apps/website/specs/widgets/hive-burn.t27 | 71 +++ apps/website/specs/widgets/hive-called.t27 | 73 +++ apps/website/specs/widgets/hive-cell.t27 | 90 +++ apps/website/specs/widgets/hive-chain.t27 | 77 +++ apps/website/specs/widgets/hive-column.t27 | 69 ++ apps/website/specs/widgets/hive-cooldown.t27 | 84 +++ apps/website/specs/widgets/hive-credit.t27 | 82 +++ apps/website/specs/widgets/hive-drain.t27 | 76 +++ apps/website/specs/widgets/hive-gate.t27 | 79 +++ .../website/specs/widgets/hive-jam-action.t27 | 68 ++ apps/website/specs/widgets/hive-jam.t27 | 68 ++ apps/website/specs/widgets/hive-key-error.t27 | 75 +++ apps/website/specs/widgets/hive-key-rest.t27 | 75 +++ apps/website/specs/widgets/hive-lifecycle.t27 | 72 +++ .../website/specs/widgets/hive-master-red.t27 | 82 +++ apps/website/specs/widgets/hive-next-key.t27 | 87 +++ apps/website/specs/widgets/hive-night.t27 | 75 +++ apps/website/specs/widgets/hive-price.t27 | 78 +++ apps/website/specs/widgets/hive-rank.t27 | 84 +++ .../specs/widgets/hive-review-first.t27 | 71 +++ apps/website/specs/widgets/hive-reviewers.t27 | 91 +++ apps/website/specs/widgets/hive-revisit.t27 | 74 +++ apps/website/specs/widgets/hive-shape.t27 | 88 +++ apps/website/specs/widgets/hive-ship.t27 | 80 +++ .../specs/widgets/hive-task-filter.t27 | 79 +++ .../website/src/lib/queenWidgets.generated.ts | 463 +++++++++++++- 202 files changed, 17973 insertions(+), 101 deletions(-) create mode 100644 apps/website/public/t27/files/specs/queen/drain.t27 create mode 100644 apps/website/public/t27/files/specs/queen/hive.t27 create mode 100644 apps/website/public/t27/files/specs/queen/key_failover.t27 create mode 100644 apps/website/public/t27/files/specs/queen/review_first.t27 create mode 100644 apps/website/public/t27/files/specs/queen/reviewer.t27 create mode 100644 apps/website/public/widgets/hive-aging/card.png create mode 100644 apps/website/public/widgets/hive-aging/hive-aging.t27 create mode 100644 apps/website/public/widgets/hive-aging/index.html create mode 100644 apps/website/public/widgets/hive-aging/logic.js create mode 100644 apps/website/public/widgets/hive-aging/logic.t27 create mode 100644 apps/website/public/widgets/hive-aging/tool.js create mode 100644 apps/website/public/widgets/hive-boundary/card.png create mode 100644 apps/website/public/widgets/hive-boundary/hive-boundary.t27 create mode 100644 apps/website/public/widgets/hive-boundary/index.html create mode 100644 apps/website/public/widgets/hive-boundary/logic.js create mode 100644 apps/website/public/widgets/hive-boundary/logic.t27 create mode 100644 apps/website/public/widgets/hive-boundary/tool.js create mode 100644 apps/website/public/widgets/hive-burn/card.png create mode 100644 apps/website/public/widgets/hive-burn/hive-burn.t27 create mode 100644 apps/website/public/widgets/hive-burn/index.html create mode 100644 apps/website/public/widgets/hive-burn/logic.js create mode 100644 apps/website/public/widgets/hive-burn/logic.t27 create mode 100644 apps/website/public/widgets/hive-burn/tool.js create mode 100644 apps/website/public/widgets/hive-called/card.png create mode 100644 apps/website/public/widgets/hive-called/hive-called.t27 create mode 100644 apps/website/public/widgets/hive-called/index.html create mode 100644 apps/website/public/widgets/hive-called/logic.js create mode 100644 apps/website/public/widgets/hive-called/logic.t27 create mode 100644 apps/website/public/widgets/hive-called/tool.js create mode 100644 apps/website/public/widgets/hive-cell/card.png create mode 100644 apps/website/public/widgets/hive-cell/hive-cell.t27 create mode 100644 apps/website/public/widgets/hive-cell/index.html create mode 100644 apps/website/public/widgets/hive-cell/logic.js create mode 100644 apps/website/public/widgets/hive-cell/logic.t27 create mode 100644 apps/website/public/widgets/hive-cell/tool.js create mode 100644 apps/website/public/widgets/hive-chain/card.png create mode 100644 apps/website/public/widgets/hive-chain/hive-chain.t27 create mode 100644 apps/website/public/widgets/hive-chain/index.html create mode 100644 apps/website/public/widgets/hive-chain/logic.js create mode 100644 apps/website/public/widgets/hive-chain/logic.t27 create mode 100644 apps/website/public/widgets/hive-chain/tool.js create mode 100644 apps/website/public/widgets/hive-column/card.png create mode 100644 apps/website/public/widgets/hive-column/hive-column.t27 create mode 100644 apps/website/public/widgets/hive-column/index.html create mode 100644 apps/website/public/widgets/hive-column/logic.js create mode 100644 apps/website/public/widgets/hive-column/logic.t27 create mode 100644 apps/website/public/widgets/hive-column/tool.js create mode 100644 apps/website/public/widgets/hive-cooldown/card.png create mode 100644 apps/website/public/widgets/hive-cooldown/hive-cooldown.t27 create mode 100644 apps/website/public/widgets/hive-cooldown/index.html create mode 100644 apps/website/public/widgets/hive-cooldown/logic.js create mode 100644 apps/website/public/widgets/hive-cooldown/logic.t27 create mode 100644 apps/website/public/widgets/hive-cooldown/tool.js create mode 100644 apps/website/public/widgets/hive-credit/card.png create mode 100644 apps/website/public/widgets/hive-credit/hive-credit.t27 create mode 100644 apps/website/public/widgets/hive-credit/index.html create mode 100644 apps/website/public/widgets/hive-credit/logic.js create mode 100644 apps/website/public/widgets/hive-credit/logic.t27 create mode 100644 apps/website/public/widgets/hive-credit/tool.js create mode 100644 apps/website/public/widgets/hive-drain/card.png create mode 100644 apps/website/public/widgets/hive-drain/hive-drain.t27 create mode 100644 apps/website/public/widgets/hive-drain/index.html create mode 100644 apps/website/public/widgets/hive-drain/logic.js create mode 100644 apps/website/public/widgets/hive-drain/logic.t27 create mode 100644 apps/website/public/widgets/hive-drain/tool.js create mode 100644 apps/website/public/widgets/hive-gate/card.png create mode 100644 apps/website/public/widgets/hive-gate/hive-gate.t27 create mode 100644 apps/website/public/widgets/hive-gate/index.html create mode 100644 apps/website/public/widgets/hive-gate/logic.js create mode 100644 apps/website/public/widgets/hive-gate/logic.t27 create mode 100644 apps/website/public/widgets/hive-gate/tool.js create mode 100644 apps/website/public/widgets/hive-jam-action/card.png create mode 100644 apps/website/public/widgets/hive-jam-action/hive-jam-action.t27 create mode 100644 apps/website/public/widgets/hive-jam-action/index.html create mode 100644 apps/website/public/widgets/hive-jam-action/logic.js create mode 100644 apps/website/public/widgets/hive-jam-action/logic.t27 create mode 100644 apps/website/public/widgets/hive-jam-action/tool.js create mode 100644 apps/website/public/widgets/hive-jam/card.png create mode 100644 apps/website/public/widgets/hive-jam/hive-jam.t27 create mode 100644 apps/website/public/widgets/hive-jam/index.html create mode 100644 apps/website/public/widgets/hive-jam/logic.js create mode 100644 apps/website/public/widgets/hive-jam/logic.t27 create mode 100644 apps/website/public/widgets/hive-jam/tool.js create mode 100644 apps/website/public/widgets/hive-key-error/card.png create mode 100644 apps/website/public/widgets/hive-key-error/hive-key-error.t27 create mode 100644 apps/website/public/widgets/hive-key-error/index.html create mode 100644 apps/website/public/widgets/hive-key-error/logic.js create mode 100644 apps/website/public/widgets/hive-key-error/logic.t27 create mode 100644 apps/website/public/widgets/hive-key-error/tool.js create mode 100644 apps/website/public/widgets/hive-key-rest/card.png create mode 100644 apps/website/public/widgets/hive-key-rest/hive-key-rest.t27 create mode 100644 apps/website/public/widgets/hive-key-rest/index.html create mode 100644 apps/website/public/widgets/hive-key-rest/logic.js create mode 100644 apps/website/public/widgets/hive-key-rest/logic.t27 create mode 100644 apps/website/public/widgets/hive-key-rest/tool.js create mode 100644 apps/website/public/widgets/hive-kit.css create mode 100644 apps/website/public/widgets/hive-kit.js create mode 100644 apps/website/public/widgets/hive-lifecycle/card.png create mode 100644 apps/website/public/widgets/hive-lifecycle/hive-lifecycle.t27 create mode 100644 apps/website/public/widgets/hive-lifecycle/index.html create mode 100644 apps/website/public/widgets/hive-lifecycle/logic.js create mode 100644 apps/website/public/widgets/hive-lifecycle/logic.t27 create mode 100644 apps/website/public/widgets/hive-lifecycle/tool.js create mode 100644 apps/website/public/widgets/hive-master-red/card.png create mode 100644 apps/website/public/widgets/hive-master-red/hive-master-red.t27 create mode 100644 apps/website/public/widgets/hive-master-red/index.html create mode 100644 apps/website/public/widgets/hive-master-red/logic.js create mode 100644 apps/website/public/widgets/hive-master-red/logic.t27 create mode 100644 apps/website/public/widgets/hive-master-red/tool.js create mode 100644 apps/website/public/widgets/hive-next-key/card.png create mode 100644 apps/website/public/widgets/hive-next-key/hive-next-key.t27 create mode 100644 apps/website/public/widgets/hive-next-key/index.html create mode 100644 apps/website/public/widgets/hive-next-key/logic.js create mode 100644 apps/website/public/widgets/hive-next-key/logic.t27 create mode 100644 apps/website/public/widgets/hive-next-key/tool.js create mode 100644 apps/website/public/widgets/hive-night/card.png create mode 100644 apps/website/public/widgets/hive-night/hive-night.t27 create mode 100644 apps/website/public/widgets/hive-night/index.html create mode 100644 apps/website/public/widgets/hive-night/logic.js create mode 100644 apps/website/public/widgets/hive-night/logic.t27 create mode 100644 apps/website/public/widgets/hive-night/tool.js create mode 100644 apps/website/public/widgets/hive-price/card.png create mode 100644 apps/website/public/widgets/hive-price/hive-price.t27 create mode 100644 apps/website/public/widgets/hive-price/index.html create mode 100644 apps/website/public/widgets/hive-price/logic.js create mode 100644 apps/website/public/widgets/hive-price/logic.t27 create mode 100644 apps/website/public/widgets/hive-price/tool.js create mode 100644 apps/website/public/widgets/hive-rank/card.png create mode 100644 apps/website/public/widgets/hive-rank/hive-rank.t27 create mode 100644 apps/website/public/widgets/hive-rank/index.html create mode 100644 apps/website/public/widgets/hive-rank/logic.js create mode 100644 apps/website/public/widgets/hive-rank/logic.t27 create mode 100644 apps/website/public/widgets/hive-rank/tool.js create mode 100644 apps/website/public/widgets/hive-review-first/card.png create mode 100644 apps/website/public/widgets/hive-review-first/hive-review-first.t27 create mode 100644 apps/website/public/widgets/hive-review-first/index.html create mode 100644 apps/website/public/widgets/hive-review-first/logic.js create mode 100644 apps/website/public/widgets/hive-review-first/logic.t27 create mode 100644 apps/website/public/widgets/hive-review-first/tool.js create mode 100644 apps/website/public/widgets/hive-reviewers/card.png create mode 100644 apps/website/public/widgets/hive-reviewers/hive-reviewers.t27 create mode 100644 apps/website/public/widgets/hive-reviewers/index.html create mode 100644 apps/website/public/widgets/hive-reviewers/logic.js create mode 100644 apps/website/public/widgets/hive-reviewers/logic.t27 create mode 100644 apps/website/public/widgets/hive-reviewers/tool.js create mode 100644 apps/website/public/widgets/hive-revisit/card.png create mode 100644 apps/website/public/widgets/hive-revisit/hive-revisit.t27 create mode 100644 apps/website/public/widgets/hive-revisit/index.html create mode 100644 apps/website/public/widgets/hive-revisit/logic.js create mode 100644 apps/website/public/widgets/hive-revisit/logic.t27 create mode 100644 apps/website/public/widgets/hive-revisit/tool.js create mode 100644 apps/website/public/widgets/hive-shape/card.png create mode 100644 apps/website/public/widgets/hive-shape/hive-shape.t27 create mode 100644 apps/website/public/widgets/hive-shape/index.html create mode 100644 apps/website/public/widgets/hive-shape/logic.js create mode 100644 apps/website/public/widgets/hive-shape/logic.t27 create mode 100644 apps/website/public/widgets/hive-shape/tool.js create mode 100644 apps/website/public/widgets/hive-ship/card.png create mode 100644 apps/website/public/widgets/hive-ship/hive-ship.t27 create mode 100644 apps/website/public/widgets/hive-ship/index.html create mode 100644 apps/website/public/widgets/hive-ship/logic.js create mode 100644 apps/website/public/widgets/hive-ship/logic.t27 create mode 100644 apps/website/public/widgets/hive-ship/tool.js create mode 100644 apps/website/public/widgets/hive-task-filter/card.png create mode 100644 apps/website/public/widgets/hive-task-filter/hive-task-filter.t27 create mode 100644 apps/website/public/widgets/hive-task-filter/index.html create mode 100644 apps/website/public/widgets/hive-task-filter/logic.js create mode 100644 apps/website/public/widgets/hive-task-filter/logic.t27 create mode 100644 apps/website/public/widgets/hive-task-filter/tool.js create mode 100644 apps/website/specs/widgets/hive-aging.t27 create mode 100644 apps/website/specs/widgets/hive-boundary.t27 create mode 100644 apps/website/specs/widgets/hive-burn.t27 create mode 100644 apps/website/specs/widgets/hive-called.t27 create mode 100644 apps/website/specs/widgets/hive-cell.t27 create mode 100644 apps/website/specs/widgets/hive-chain.t27 create mode 100644 apps/website/specs/widgets/hive-column.t27 create mode 100644 apps/website/specs/widgets/hive-cooldown.t27 create mode 100644 apps/website/specs/widgets/hive-credit.t27 create mode 100644 apps/website/specs/widgets/hive-drain.t27 create mode 100644 apps/website/specs/widgets/hive-gate.t27 create mode 100644 apps/website/specs/widgets/hive-jam-action.t27 create mode 100644 apps/website/specs/widgets/hive-jam.t27 create mode 100644 apps/website/specs/widgets/hive-key-error.t27 create mode 100644 apps/website/specs/widgets/hive-key-rest.t27 create mode 100644 apps/website/specs/widgets/hive-lifecycle.t27 create mode 100644 apps/website/specs/widgets/hive-master-red.t27 create mode 100644 apps/website/specs/widgets/hive-next-key.t27 create mode 100644 apps/website/specs/widgets/hive-night.t27 create mode 100644 apps/website/specs/widgets/hive-price.t27 create mode 100644 apps/website/specs/widgets/hive-rank.t27 create mode 100644 apps/website/specs/widgets/hive-review-first.t27 create mode 100644 apps/website/specs/widgets/hive-reviewers.t27 create mode 100644 apps/website/specs/widgets/hive-revisit.t27 create mode 100644 apps/website/specs/widgets/hive-shape.t27 create mode 100644 apps/website/specs/widgets/hive-ship.t27 create mode 100644 apps/website/specs/widgets/hive-task-filter.t27 diff --git a/apps/website/public/t27/files/specs/automation/agent-hive-group.t27 b/apps/website/public/t27/files/specs/automation/agent-hive-group.t27 index ab1bd5732a..2f801b0db7 100644 --- a/apps/website/public/t27/files/specs/automation/agent-hive-group.t27 +++ b/apps/website/public/t27/files/specs/automation/agent-hive-group.t27 @@ -12,6 +12,11 @@ // alive, one first speaker on the all tag, follow-ups without a tag, /stop and /mute. // v3 (2026-10-06): the rest of that round -- the seen/done reaction, standing briefs, // a topic a person deleted, and /summary. +// v4 (2026-10-09): agents hand off in private, bot to bot. Telegram lets a bot send a private +// message to another bot by @username when Bot-to-Bot Communication Mode is on for both +// (owner: "I switched it on exactly for A2A"). A colleague is heard in private only with the +// hand-off tag, an ask is one hop, and the answer is carried to the person, never a new turn, +// and only while its ask is fresh. // Earlier laws are unchanged; each version only adds, so a host on an older one stays true. // phi^2 + 1/phi^2 = 3 | TRINITY @@ -19,7 +24,7 @@ module automation::agent_hive_group { pub const KIND : str = "automation"; pub const ID : str = "agent-hive-group"; pub const REPO : str = "999-multibots-telegraf"; - pub const VERSION : u8 = 3; + pub const VERSION : u8 = 4; // Bot messages in a row after one human message: human -> A -> B -> C, then silence. pub const MAX_BOT_DEPTH : u32 = 3; @@ -155,6 +160,47 @@ module automation::agent_hive_group { return asked; } + // ---- v4: hand-off in private, bot to bot ---- + + // Every private hand-off between two agents starts with this tag, then its kind and the + // person it is for. Only a hive agent of the same owner can send it, so the person named in + // it is trusted: the turn runs in that person's name, never the owner's. + pub const DM_TAG : str = "#a2a"; + pub const DM_ASK : u8 = 1; + pub const DM_ANSWER : u8 = 2; + // An answer to a person hands off to at most this many colleagues. + pub const MAX_DM_HANDOFFS : u32 = 2; + + // In private a person is always heard; a bot only when it is a colleague (an agent bot of + // the same owner) and its message carries the hand-off tag with a known kind. + fn dm_heard(from_bot: bool, colleague: bool, tag_ok: bool) -> bool { + return !from_bot || (colleague && tag_ok); + } + + // An ask runs the colleague's turn; an answer is only carried to the person. So a private + // chain is two messages long and cannot loop, whatever the agents write. + fn dm_runs_turn(kind: u8) -> bool { + return kind == DM_ASK; + } + fn dm_carries(kind: u8) -> bool { + return kind == DM_ANSWER; + } + + // An ask is always a known tag; an answer only while its ask is fresh -- a colleague never + // writes to a person nobody asked it about. Half an hour covers the longest turn. + pub const DM_ANSWER_WINDOW_MS : u32 = 1800000; + fn dm_tag_ok(kind: u8, since_ask_ms: u32) -> bool { + if kind == DM_ASK { return true; } + if kind == DM_ANSWER { return since_ask_ms < DM_ANSWER_WINDOW_MS; } + return false; + } + + // How many of the hand-offs an answer asked for are sent. + fn dm_handoffs_sent(asked: u32) -> u32 { + if asked > MAX_DM_HANDOFFS { return MAX_DM_HANDOFFS; } + return asked; + } + test "a person calls an agent by tag, all tag, reply or its own topic" { assert(addressed(true, false, false, false, false)); assert(addressed(false, true, false, false, false)); @@ -269,8 +315,46 @@ module automation::agent_hive_group { assert(summary_count(201) == 200); } + test "in private a colleague is heard only with the hand-off tag" { + assert(dm_heard(false, false, false)); + assert(dm_heard(true, true, true)); + assert(!dm_heard(true, false, true)); + assert(!dm_heard(true, true, false)); + } + + test "an ask runs a turn, an answer is only carried" { + assert(DM_ASK == 1); + assert(DM_ANSWER == 2); + assert(dm_runs_turn(DM_ASK)); + assert(!dm_runs_turn(DM_ANSWER)); + assert(!dm_runs_turn(0)); + assert(dm_carries(DM_ANSWER)); + assert(!dm_carries(DM_ASK)); + assert(!dm_carries(0)); + } + + test "an answer is carried only while its ask is fresh" { + assert(DM_ANSWER_WINDOW_MS == 1800000); + assert(dm_tag_ok(DM_ASK, 4000000000)); + assert(dm_tag_ok(DM_ANSWER, 0)); + assert(dm_tag_ok(DM_ANSWER, 1799999)); + assert(!dm_tag_ok(DM_ANSWER, 1800000)); + assert(!dm_tag_ok(DM_ANSWER, 4000000000)); + assert(!dm_tag_ok(0, 0)); + assert(!dm_tag_ok(3, 0)); + } + + test "an answer hands off to at most two colleagues" { + assert(MAX_DM_HANDOFFS == 2); + assert(dm_handoffs_sent(0) == 0); + assert(dm_handoffs_sent(1) == 1); + assert(dm_handoffs_sent(2) == 2); + assert(dm_handoffs_sent(3) == 2); + assert(dm_handoffs_sent(100) == 2); + } + test "VERSION says what the header's last note says" { - assert(VERSION == 3); + assert(VERSION == 4); } } diff --git a/apps/website/public/t27/files/specs/automation/review-jam.t27 b/apps/website/public/t27/files/specs/automation/review-jam.t27 index 9eeba322c6..80cd798a66 100644 --- a/apps/website/public/t27/files/specs/automation/review-jam.t27 +++ b/apps/website/public/t27/files/specs/automation/review-jam.t27 @@ -1,9 +1,11 @@ -// Review jam case detection and Queen action mapping -// Part of EPIC #5388 - gHashTag/t27#5700 +// Review jam: which case a stuck pull request is in, and the Queen's action. +// Part of EPIC #5388. Contract: gHashTag/t27#9032 (re-filed from #5700). +// `tri jam` (gHashTag/999-multibots-telegraf) imports these numbers instead +// of hand-copying them. module review_jam; -// Jam case constants (u8, position = value, starting at 0) +// Jam case constants (u8, value = position, starting at 0) pub const JAM_NONE: u8 = 0; pub const JAM_NO_REVIEWER: u8 = 1; pub const JAM_STACKED_ON_OPEN: u8 = 2; @@ -15,7 +17,7 @@ pub const JAM_NOISE_RED: u8 = 7; pub const JAM_DRAFT_OR_FRESH: u8 = 8; pub const JAM_UNMEASURED: u8 = 9; -// Queen action constants (u8, position = value, starting at 0) +// Queen action constants (u8, value = position, starting at 0) pub const ACT_NOTHING: u8 = 0; pub const ACT_DISPATCH_REVIEWER: u8 = 1; pub const ACT_WAIT_FOR_BASE: u8 = 2; @@ -27,18 +29,17 @@ pub const ACT_NAME_NOISE_AND_REVIEW: u8 = 7; pub const ACT_WAIT: u8 = 8; pub const ACT_MEASURE_AGAIN: u8 = 9; -// Determine the jam case based on PR state -// First matching rule wins, in this order: -// 1. not measured -> UNMEASURED -// 2. draft or fresh -> DRAFT_OR_FRESH -// 3. stacked on an open PR -> STACKED_ON_OPEN -// 4. conflicting -> CONFLICTING -// 5. labeled reviewed (and still open) -> LABELED_NOT_MERGED -// 6. refused and pushed after -> REFUSED_THEN_PUSHED -// 7. refused -> REFUSED_NOT_FIXED -// 8. only noise checks red -> NOISE_RED -// 9. no reviewer -> NO_REVIEWER -// 10. otherwise NONE +// The jam case of a pull request. The FIRST matching rule wins: +// 1. not measured -> UNMEASURED +// 2. draft or fresh -> DRAFT_OR_FRESH +// 3. stacked on an open PR -> STACKED_ON_OPEN +// 4. conflicting -> CONFLICTING +// 5. labeled reviewed (and still open) -> LABELED_NOT_MERGED +// 6. refused and pushed after -> REFUSED_THEN_PUSHED +// 7. refused -> REFUSED_NOT_FIXED +// 8. only noise checks red -> NOISE_RED +// 9. no reviewer -> NO_REVIEWER +// 10. otherwise -> NONE pub fn jam_case(measured: bool, draft_or_fresh: bool, stacked_on_open: bool, conflicting: bool, labeled_reviewed: bool, refused: bool, pushed_after_refusal: bool, has_reviewer: bool, only_noise_red: bool) -> u8 { if !measured { return JAM_UNMEASURED; @@ -70,9 +71,9 @@ pub fn jam_case(measured: bool, draft_or_fresh: bool, stacked_on_open: bool, con JAM_NONE } -// Map jam case to Queen action -// Case maps to action with same position (NONE -> NOTHING, NO_REVIEWER -> DISPATCH_REVIEWER, etc.) -// Any value above 9 -> ACT_MEASURE_AGAIN (unknown case is never "nothing to do") +// The Queen's action for a case: the action with the SAME position. +// Any value above 9 is an unknown case, and an unknown case is never +// "nothing to do": it maps to ACT_MEASURE_AGAIN. pub fn jam_action(case: u8) -> u8 { if case > 9 { return ACT_MEASURE_AGAIN; @@ -80,44 +81,48 @@ pub fn jam_action(case: u8) -> u8 { case } -// Determine if it's the Queen's turn to act -// True for every case except NONE, DRAFT_OR_FRESH and STACKED_ON_OPEN +// Whether the case is the Queen's turn to act: true for every case except +// NONE, DRAFT_OR_FRESH and STACKED_ON_OPEN. pub fn jam_is_queens_turn(case: u8) -> bool { case != JAM_NONE && case != JAM_DRAFT_OR_FRESH && case != JAM_STACKED_ON_OPEN } -// Tests - one per case, plus edge cases +// ---- jam_case: one test per case ---- test "unmeasured beats every other flag" { assert jam_case(false, true, true, true, true, true, true, true, true) == JAM_UNMEASURED; } +test "unmeasured with every other flag clear" { + assert jam_case(false, false, false, false, false, false, false, false, false) == JAM_UNMEASURED; +} + test "draft or fresh" { assert jam_case(true, true, false, false, false, false, false, false, false) == JAM_DRAFT_OR_FRESH; } test "stacked on open PR" { - assert jam_case(true, false, true, false, false, false, false, false, false) == JAM_STACKED_ON_OPEN; + assert jam_case(true, false, true, false, false, false, false, true, false) == JAM_STACKED_ON_OPEN; } test "conflicting" { - assert jam_case(true, false, false, true, false, false, false, false, false) == JAM_CONFLICTING; + assert jam_case(true, false, false, true, false, false, false, true, false) == JAM_CONFLICTING; } test "labeled not merged" { - assert jam_case(true, false, false, false, true, false, false, false, false) == JAM_LABELED_NOT_MERGED; + assert jam_case(true, false, false, false, true, false, false, true, false) == JAM_LABELED_NOT_MERGED; } test "refused then pushed" { - assert jam_case(true, false, false, false, false, true, true, false, false) == JAM_REFUSED_THEN_PUSHED; + assert jam_case(true, false, false, false, false, true, true, true, false) == JAM_REFUSED_THEN_PUSHED; } test "refused not fixed" { - assert jam_case(true, false, false, false, false, true, false, false, false) == JAM_REFUSED_NOT_FIXED; + assert jam_case(true, false, false, false, false, true, false, true, false) == JAM_REFUSED_NOT_FIXED; } test "noise red" { - assert jam_case(true, false, false, false, false, false, false, false, true) == JAM_NOISE_RED; + assert jam_case(true, false, false, false, false, false, false, true, true) == JAM_NOISE_RED; } test "no reviewer" { @@ -128,70 +133,142 @@ test "none" { assert jam_case(true, false, false, false, false, false, false, true, false) == JAM_NONE; } -test "unknown case maps to measure again" { - assert jam_action(200) == ACT_MEASURE_AGAIN; -} +// ---- jam_case: first-match order, each rule against the next one ---- -test "queen's turn for labeled not merged" { - assert jam_is_queens_turn(JAM_LABELED_NOT_MERGED) == true; +test "draft beats stacked" { + assert jam_case(true, true, true, false, false, false, false, true, false) == JAM_DRAFT_OR_FRESH; } -test "queen's turn for no reviewer" { - assert jam_is_queens_turn(JAM_NO_REVIEWER) == true; +test "stacked and unreviewed is stacked, not no reviewer" { + assert jam_case(true, false, true, false, false, false, false, false, false) == JAM_STACKED_ON_OPEN; } -test "queen's turn for conflicting" { - assert jam_is_queens_turn(JAM_CONFLICTING) == true; +test "stacked beats conflicting" { + assert jam_case(true, false, true, true, false, false, false, true, false) == JAM_STACKED_ON_OPEN; } -test "queen's turn for refused then pushed" { - assert jam_is_queens_turn(JAM_REFUSED_THEN_PUSHED) == true; +test "conflicting beats labeled" { + assert jam_case(true, false, false, true, true, false, false, true, false) == JAM_CONFLICTING; } -test "queen's turn for refused not fixed" { - assert jam_is_queens_turn(JAM_REFUSED_NOT_FIXED) == true; +test "labeled beats refused then pushed" { + assert jam_case(true, false, false, false, true, true, true, true, false) == JAM_LABELED_NOT_MERGED; } -test "queen's turn for noise red" { - assert jam_is_queens_turn(JAM_NOISE_RED) == true; +test "refused then pushed beats noise red" { + assert jam_case(true, false, false, false, false, true, true, true, true) == JAM_REFUSED_THEN_PUSHED; } -test "queen's turn for unmeasured" { - assert jam_is_queens_turn(JAM_UNMEASURED) == true; +test "refused not fixed beats noise red" { + assert jam_case(true, false, false, false, false, true, false, true, true) == JAM_REFUSED_NOT_FIXED; } -test "not queen's turn for none" { - assert jam_is_queens_turn(JAM_NONE) == false; +test "noise red beats no reviewer" { + assert jam_case(true, false, false, false, false, false, false, false, true) == JAM_NOISE_RED; } -test "not queen's turn for draft or fresh" { - assert jam_is_queens_turn(JAM_DRAFT_OR_FRESH) == false; +test "pushed without a refusal is not refused then pushed" { + assert jam_case(true, false, false, false, false, false, true, true, false) == JAM_NONE; } -test "not queen's turn for stacked on open" { - assert jam_is_queens_turn(JAM_STACKED_ON_OPEN) == false; +// ---- jam_action: same position, unknown -> measure again ---- + +test "action none to nothing" { + assert jam_action(JAM_NONE) == ACT_NOTHING; } -test "specific scenario: labeled reviewed but still open" { - assert jam_case(true, false, false, false, true, false, false, true, false) == JAM_LABELED_NOT_MERGED; +test "action no reviewer to dispatch reviewer" { + assert jam_action(JAM_NO_REVIEWER) == ACT_DISPATCH_REVIEWER; } -test "specific scenario: unmeasured with any other flags" { - assert jam_case(false, true, true, true, true, true, true, true, true) == JAM_UNMEASURED; +test "action stacked to wait for base" { + assert jam_action(JAM_STACKED_ON_OPEN) == ACT_WAIT_FOR_BASE; } -test "action mapping: none to nothing" { - assert jam_action(JAM_NONE) == ACT_NOTHING; +test "action refused not fixed to dispatch coder" { + assert jam_action(JAM_REFUSED_NOT_FIXED) == ACT_DISPATCH_CODER; } -test "action mapping: no reviewer to dispatch reviewer" { - assert jam_action(JAM_NO_REVIEWER) == ACT_DISPATCH_REVIEWER; +test "action refused then pushed to dispatch re-review" { + assert jam_action(JAM_REFUSED_THEN_PUSHED) == ACT_DISPATCH_RE_REVIEW; } -test "action mapping: labeled not merged to finish merge" { +test "action labeled not merged to finish merge" { assert jam_action(JAM_LABELED_NOT_MERGED) == ACT_FINISH_MERGE; } -test "action mapping: unmeasured to measure again" { +test "action conflicting to rebase by coder" { + assert jam_action(JAM_CONFLICTING) == ACT_REBASE_BY_CODER; +} + +test "action noise red to name noise and review" { + assert jam_action(JAM_NOISE_RED) == ACT_NAME_NOISE_AND_REVIEW; +} + +test "action draft or fresh to wait" { + assert jam_action(JAM_DRAFT_OR_FRESH) == ACT_WAIT; +} + +test "action unmeasured to measure again" { assert jam_action(JAM_UNMEASURED) == ACT_MEASURE_AGAIN; -} \ No newline at end of file +} + +test "action first unknown case measures again" { + assert jam_action(10) == ACT_MEASURE_AGAIN; +} + +test "action unknown case 200 measures again" { + assert jam_action(200) == ACT_MEASURE_AGAIN; +} + +// ---- jam_is_queens_turn ---- + +test "queens turn for no reviewer" { + assert jam_is_queens_turn(JAM_NO_REVIEWER) == true; +} + +test "queens turn for refused not fixed" { + assert jam_is_queens_turn(JAM_REFUSED_NOT_FIXED) == true; +} + +test "queens turn for refused then pushed" { + assert jam_is_queens_turn(JAM_REFUSED_THEN_PUSHED) == true; +} + +test "queens turn for labeled not merged" { + assert jam_is_queens_turn(JAM_LABELED_NOT_MERGED) == true; +} + +test "queens turn for conflicting" { + assert jam_is_queens_turn(JAM_CONFLICTING) == true; +} + +test "queens turn for noise red" { + assert jam_is_queens_turn(JAM_NOISE_RED) == true; +} + +test "queens turn for unmeasured" { + assert jam_is_queens_turn(JAM_UNMEASURED) == true; +} + +test "not queens turn for none" { + assert jam_is_queens_turn(JAM_NONE) == false; +} + +test "not queens turn for draft or fresh" { + assert jam_is_queens_turn(JAM_DRAFT_OR_FRESH) == false; +} + +test "not queens turn for stacked on open" { + assert jam_is_queens_turn(JAM_STACKED_ON_OPEN) == false; +} + +// ---- the two scenarios of #9032, end to end ---- + +test "scenario bee-reviewed but still open finishes the merge" { + assert jam_action(jam_case(true, false, false, false, true, false, false, true, false)) == ACT_FINISH_MERGE; +} + +test "scenario failed read is never reported as free" { + assert jam_action(jam_case(false, false, false, false, false, false, false, true, false)) == ACT_MEASURE_AGAIN; +} diff --git a/apps/website/public/t27/files/specs/queen/dispatch.t27 b/apps/website/public/t27/files/specs/queen/dispatch.t27 index e49055757e..42911bfbb6 100644 --- a/apps/website/public/t27/files/specs/queen/dispatch.t27 +++ b/apps/website/public/t27/files/specs/queen/dispatch.t27 @@ -137,8 +137,35 @@ pub const CANCEL_RULE : str = "none: no route aborts a running turn or drops a d ; --- Events, persistence, idempotency, projections -------------------------------------------------- pub const EVENT_RULE : str = "no event IDs and no event log: GitHub is polled each round, a bee's result is read from its stream, and the only history is queen_dispatch_history (whole-row archives before an overwrite) and queen_transcript (keyed by conversation and sequence)"; pub const IDEMPOTENCY_RULE : str = "a second ending matches no row; an ending of an older turn matches no row; a second criteria release and a second CI take-back find nothing; requests during a round coalesce into one more round"; -pub const PUBLIC_FIELDS : [6]str = ["/queen/status swarmState", "/queen/status queue.state", "/queen/status workers capacity, active, idle", "/queen/public-board columns", "/queen/public-board cards: number, title, column, criteria, needs, and for a finished row verdict and judgedHead", "/queen/public-board pulse: rounds, bees, verdicts, lastRoundAt, roundSeconds"]; +pub const PUBLIC_FIELDS : [6]str = ["/queen/status swarmState", "/queen/status queue.state", "/queen/status workers capacity, active, idle", "/queen/public-board columns", "/queen/public-board cards: number, title, column, criteria, needs, verdict, judgedHead, and for empty cards end_reason", "/queen/public-board pulse: rounds, bees, verdicts, lastRoundAt, roundSeconds"]; pub const PRIVATE_FIELDS : str = "owned paths, branches, conversations, transcripts, workers, holders, the refusal text, providers, keys and token counts stay on the authenticated /queen/board"; + +; --- End reasons for runs that produce no commits ----------------------------------------------------------- +; Every finished dispatch has exactly one end reason from this closed list. "unknown" is allowed but counted. +; The runtime records these reasons; the public board shows them on "empty" cards and counts them over 7 days. +pub const R_MODEL_ENDED_TURN : u32 = 0; // Model ended its turn without editing any files +pub const R_TIME_TOKEN_LIMIT : u32 = 1; // Hit time or token limit during execution +pub const R_CRASHED : u32 = 2; // Container or tool crashed (with exit code) +pub const R_BOUNDARY_CORRECT : u32 = 3; // Boundary file was already correct, no changes needed +pub const R_REFUSAL_START : u32 = 4; // Refusal before the run started (invalid task, missing resources) +pub const R_PUSH_FAILED : u32 = 5; // Push to remote failed after successful run +pub const R_UNKNOWN : u32 = 6; // Cause could not be determined by the runner + +pub const END_REASON_COUNT : u32 = 7; +pub const END_REASON_NAMES : [7]str = [ + "model_ended_turn", + "time_token_limit", + "crashed", + "boundary_correct", + "refusal_start", + "push_failed", + "unknown" +]; + +; --- Public board extension: show end reason on empty cards ----------------------------------------------- +; The public board's "empty" cards now include the end reason when available, enabling breakdown of +; the 56.0% empty-branch share by reason from public endpoints alone. +pub const PUBLIC_BOARD_EMPTY_REASON : str = "for empty cards, include end_reason field when available"; pub const LOCAL_VERSUS_EXTERNAL : str = "local: gHashTag/trinity's Zig lotus-policy and its .trinity/queen files on one machine; external: the trios supervisor in a container on Railway, documented in trios/docs/public-swarm-state.md and reached over its public endpoints"; ; --- Agents and workers --------------------------------------------------------------------------------- pub const AGENT_LETTERS : u32 = 27; @@ -317,6 +344,42 @@ invariant illegal_is_outside_every_state { assert ILLEGAL > S_RELEASED; } invariant the_ceiling_is_two { assert SENDBACK_CEILING == 2 and MAX_CEILING_RELEASES == 1; } invariant the_floors_rise { assert EMPTY_FLOOR_MS < SENDBACK_FLOOR_MS and SENDBACK_FLOOR_MS < WAIT_FLOOR_MS; } invariant the_heartbeat_beats_inside_the_ttl { assert LEASE_HEARTBEAT_SECONDS * 3 == LEASE_TTL_SECONDS; } +invariant end_reason_count_matches_names { assert END_REASON_COUNT == END_REASON_NAMES.len(); } +invariant all_end_reasons_valid { + assert is_valid_end_reason(R_MODEL_ENDED_TURN); + assert is_valid_end_reason(R_TIME_TOKEN_LIMIT); + assert is_valid_end_reason(R_CRASHED); + assert is_valid_end_reason(R_BOUNDARY_CORRECT); + assert is_valid_end_reason(R_REFUSAL_START); + assert is_valid_end_reason(R_PUSH_FAILED); + assert is_valid_end_reason(R_UNKNOWN); +} + +; --- End reason handling functions -------------------------------------------------------------------------- + +pub fn is_valid_end_reason(reason: u32) -> bool { + return reason < END_REASON_COUNT; +} + +pub fn end_reason_to_name(reason: u32) -> str { + if (is_valid_end_reason(reason)) { + return END_REASON_NAMES[reason]; + } + return "invalid_end_reason"; +} + +pub fn is_no_diff_end_reason(reason: u32) -> bool { + return reason == R_MODEL_ENDED_TURN + or reason == R_TIME_TOKEN_LIMIT + or reason == R_BOUNDARY_CORRECT + or reason == R_REFUSAL_START + or reason == R_PUSH_FAILED + or reason == R_UNKNOWN; +} + +pub fn is_crash_end_reason(reason: u32) -> bool { + return reason == R_CRASHED; +} test the_table_moves_only_along_its_guards { assert next_state(S_NONE, E_START) == S_RUNNING; @@ -329,6 +392,28 @@ test the_table_moves_only_along_its_guards { assert next_state(S_REAPED, E_CRITERIA_CHANGED) == ILLEGAL; assert next_state(S_STALE, E_REDISPATCH) == S_RUNNING; assert next_state(S_RUNNING, E_END_OTHER_TURN) == ILLEGAL; + // Every legal transition, once each: a guard no assert reaches can be dropped unnoticed (#9886). + assert next_state(S_NONE, E_REFUSE) == S_REFUSED; + assert next_state(S_NONE, E_END) == ILLEGAL; + assert next_state(S_RUNNING, E_REAP_STALL) == S_REAPED; + assert next_state(S_RUNNING, E_REAP_BOOT) == S_REAPED; + assert next_state(S_ENDED, E_VERDICT_ACCEPT) == S_ACCEPT; + assert next_state(S_ENDED, E_VERDICT_SENDBACK) == S_SENDBACK; + assert next_state(S_ENDED, E_VERDICT_ESCALATE) == S_ESCALATE; + assert next_state(S_ENDED, E_VERDICT_WAIT) == S_WAIT; + assert next_state(S_ENDED, E_VERDICT_EMPTY) == S_EMPTY; + assert next_state(S_ENDED, E_CRITERIA_CHANGED) == S_STALE; + assert next_state(S_ENDED, E_FLOOR_PASSED) == S_RELEASED; + assert next_state(S_WAIT, E_VERDICT_ACCEPT) == S_ACCEPT; + assert next_state(S_ACCEPT, E_CI_RED_UNDER_CEILING) == S_SENDBACK; + assert next_state(S_SENDBACK, E_CRITERIA_CHANGED) == S_STALE; + assert next_state(S_SENDBACK, E_FLOOR_PASSED) == S_RELEASED; + assert next_state(S_EMPTY, E_CRITERIA_CHANGED) == S_STALE; + assert next_state(S_SENDBACK, E_START) == ILLEGAL; + assert next_state(S_ESCALATE, E_CRITERIA_CHANGED) == S_STALE; + assert next_state(S_REAPED, E_REDISPATCH) == S_RUNNING; + assert next_state(S_REFUSED, E_REDISPATCH) == S_RUNNING; + assert next_state(S_RELEASED, E_REDISPATCH) == S_RUNNING; } test a_claim_follows_the_verdict_and_three_clocks { @@ -341,6 +426,16 @@ test a_claim_follows_the_verdict_and_three_clocks { assert claim_of(true, V_SENDBACK, 3600000, 2, releases_read_by_round(1)) == C_FAILED; assert claim_of(true, V_WAIT, 21600000, 0, 0) == C_FAILED; assert claim_of(true, V_ESCALATE, 999999999, 0, 0) == C_AWAITING_REVIEW; + // #9886: each verdict that frees the issue, the send-back floor at its edge with a release + // already spent, and the wait floor's two edges. + assert claim_of(true, V_FAILED, 0, 0, 0) == C_FAILED; + assert claim_of(true, V_CANCELLED, 0, 0, 0) == C_FAILED; + assert claim_of(true, V_STALE, 0, 0, 0) == C_FAILED; + assert claim_of(true, V_SENDBACK, 3600000, 1, 1) == C_FAILED; + assert claim_of(true, V_NONE, 21600000, 0, 0) == C_FAILED; + assert claim_of(true, V_BLANK, 21600000, 0, 0) == C_FAILED; + assert claim_of(true, V_WAIT, 21599999, 0, 0) == C_AWAITING_REVIEW; + assert claim_of(true, V_WAIT, 21600000, 2, 0) == C_AWAITING_REVIEW; } test a_closed_issue_is_drawn_done_whatever_its_verdict { @@ -348,12 +443,16 @@ test a_closed_issue_is_drawn_done_whatever_its_verdict { assert column_of(C_AWAITING_REVIEW, true, true) == COL_REVIEW; assert column_of(C_RUNNING, false, true) == COL_RUNNING; assert column_of(C_FAILED, true, true) == COL_DROPPED; + assert column_of(C_ACCEPTED, true, true) == COL_DONE; } test the_first_eligible_wins_in_any_order { assert chosen_of(false, false, true, true, false) == 2; assert chosen_of(false, true, false, false, true) == 1; assert chosen_of(false, false, false, false, false) == NONE_CHOSEN; + assert chosen_of(true, true, false, false, false) == 0; + assert chosen_of(false, false, false, true, true) == 3; + assert chosen_of(false, false, false, false, true) == 4; assert can_start(3, DEFAULT_WORKER_LIMIT) == true; assert can_start(4, DEFAULT_WORKER_LIMIT) == false; } @@ -369,6 +468,7 @@ test review_escalates_without_work_and_after_two_send_backs { assert retry_allowed(1) == true; assert retry_allowed(2) == false; assert counts_against_issue(true) == false; + assert counts_against_issue(false) == true; } test one_lease_one_owner_and_age_alone_reaps { @@ -379,7 +479,116 @@ test one_lease_one_owner_and_age_alone_reaps { assert owners_per_term() == 1; assert stall_reapable(121) == true; assert stall_reapable(119) == false; + // The comparison is strict: 120 is not reapable. f58 measured 119 and 121, not 120 itself. + assert stall_reapable(120) == false; assert followup_rounds(5) == 1; + assert followup_rounds(0) == 0; assert accept_requires_pull_request() == false; assert writer_enforces_table() == false; } + +; --- End reason tests -------------------------------------------------------------------------------------- + +test end_reason_constants_are_correct { + assert R_MODEL_ENDED_TURN == 0; + assert R_TIME_TOKEN_LIMIT == 1; + assert R_CRASHED == 2; + assert R_BOUNDARY_CORRECT == 3; + assert R_REFUSAL_START == 4; + assert R_PUSH_FAILED == 5; + assert R_UNKNOWN == 6; + assert END_REASON_COUNT == 7; + // The lines above fold at comptime, so this test ran 0 runtime asserts (#9886); these two + // call the functions that read the constants: the last reason is named, one past it is not. + assert end_reason_to_name(END_REASON_COUNT - 1) == "unknown"; + assert is_valid_end_reason(END_REASON_COUNT) == false; +} + +test end_reason_names_array_matches_constants { + assert END_REASON_NAMES[0] == "model_ended_turn"; + assert END_REASON_NAMES[1] == "time_token_limit"; + assert END_REASON_NAMES[2] == "crashed"; + assert END_REASON_NAMES[3] == "boundary_correct"; + assert END_REASON_NAMES[4] == "refusal_start"; + assert END_REASON_NAMES[5] == "push_failed"; + assert END_REASON_NAMES[6] == "unknown"; +} + +test end_reason_validation_works { + assert is_valid_end_reason(R_MODEL_ENDED_TURN) == true; + assert is_valid_end_reason(R_TIME_TOKEN_LIMIT) == true; + assert is_valid_end_reason(R_CRASHED) == true; + assert is_valid_end_reason(R_BOUNDARY_CORRECT) == true; + assert is_valid_end_reason(R_REFUSAL_START) == true; + assert is_valid_end_reason(R_PUSH_FAILED) == true; + assert is_valid_end_reason(R_UNKNOWN) == true; + assert is_valid_end_reason(7) == false; // One past the last valid reason + assert is_valid_end_reason(99) == false; // Way past the last valid reason +} + +test end_reason_to_name_conversion { + assert end_reason_to_name(R_MODEL_ENDED_TURN) == "model_ended_turn"; + assert end_reason_to_name(R_TIME_TOKEN_LIMIT) == "time_token_limit"; + assert end_reason_to_name(R_CRASHED) == "crashed"; + assert end_reason_to_name(R_BOUNDARY_CORRECT) == "boundary_correct"; + assert end_reason_to_name(R_REFUSAL_START) == "refusal_start"; + assert end_reason_to_name(R_PUSH_FAILED) == "push_failed"; + assert end_reason_to_name(R_UNKNOWN) == "unknown"; + assert end_reason_to_name(99) == "invalid_end_reason"; // Invalid reason returns special string +} + +test no_diff_end_reason_classification { + assert is_no_diff_end_reason(R_MODEL_ENDED_TURN) == true; + assert is_no_diff_end_reason(R_TIME_TOKEN_LIMIT) == true; + assert is_no_diff_end_reason(R_BOUNDARY_CORRECT) == true; + assert is_no_diff_end_reason(R_REFUSAL_START) == true; + assert is_no_diff_end_reason(R_PUSH_FAILED) == true; + assert is_no_diff_end_reason(R_UNKNOWN) == true; + assert is_no_diff_end_reason(R_CRASHED) == false; // Crashes are separate category +} + +test crash_end_reason_classification { + assert is_crash_end_reason(R_CRASHED) == true; + assert is_crash_end_reason(R_MODEL_ENDED_TURN) == false; + assert is_crash_end_reason(R_TIME_TOKEN_LIMIT) == false; + assert is_crash_end_reason(R_BOUNDARY_CORRECT) == false; + assert is_crash_end_reason(R_REFUSAL_START) == false; + assert is_crash_end_reason(R_PUSH_FAILED) == false; + assert is_crash_end_reason(R_UNKNOWN) == false; +} + +test end_reason_enables_breakdown_of_empty_branches { + // This test validates that the end reason vocabulary enables breaking down + // the 56.0% empty-branch share by reason from public endpoints alone + + // Simulate counts for empty branches by reason + var model_ended_count : u32 = 100; + var token_limit_count : u32 = 80; + var boundary_correct_count : u32 = 60; + var refusal_start_count : u32 = 40; + var push_failed_count : u32 = 30; + var unknown_count : u32 = 7; // Should be < 10% of total + var crash_count : u32 = 0; + + var total_empty : u32 = model_ended_count + token_limit_count + boundary_correct_count + + refusal_start_count + push_failed_count + unknown_count + crash_count; + + // Verify unknown is less than 10% of total (success criterion from issue) + assert unknown_count < total_empty / 10; + + // Verify we can calculate breakdown percentages + var model_percentage : u32 = (model_ended_count * 100) / total_empty; + var token_percentage : u32 = (token_limit_count * 100) / total_empty; + var unknown_percentage : u32 = (unknown_count * 100) / total_empty; + + assert model_percentage > 0; + assert token_percentage > 0; + assert unknown_percentage < 10; // Unknown should be < 10% + + // Verify no-diff vs crash categorization works + var no_diff_total : u32 = model_ended_count + token_limit_count + boundary_correct_count + + refusal_start_count + push_failed_count + unknown_count; + var crash_total : u32 = crash_count; + + assert no_diff_total == total_empty - crash_total; +} diff --git a/apps/website/public/t27/files/specs/queen/drain.t27 b/apps/website/public/t27/files/specs/queen/drain.t27 new file mode 100644 index 0000000000..8a5ceee5bd --- /dev/null +++ b/apps/website/public/t27/files/specs/queen/drain.t27 @@ -0,0 +1,321 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/queen/drain.t27 -- when a draining Queen process may exit: what it waits for, what it stops, what it leaves +; Owner's night loop, 2026-10-10 (gHashTag/trios#1729 item 2): "every deploy still freezes the swarm for +; 1800 s; with durable jobs and waits and stoppable turns, a spec can bound the drain by in-flight turns". +; Measured before this card (gHashTag/trios trios/agent-server, branch queen): +; - the drain is not a fixed wait. On SIGTERM the server drains its own bee runner (queen-runner.ts +; drainBeeRunner): it takes no new order and waits while any bee it carries still runs, up to +; TRIOS_BEE_RUNNER_DRAIN_SECONDS = 1800, then salvages the branch of each bee still running and exits +; about 2 s later. Rounds, the reviewer and the wait scheduler run on through that wait +; (roundsThroughDrain, BrowserOS#546) and are cut by the exit. Railway's +; RAILWAY_DEPLOYMENT_DRAINING_SECONDS = 1830 is only the SIGKILL that follows; the service has a +; volume, so the next container starts when this one ends. +; - so the Queen's own bees are what the drain waits for, and they usually outlive it. GitHub's +; deployment statuses for the Queen's service, 2026-10-08 23:14Z to 2026-10-10 12:01Z: 7 of 8 deploys +; took 30.4 to 33.7 min from start to live, and one took 2.8 min (00:44Z, a quiet night). +; - the 1800 s protects a bee that ends within 30 min of SIGTERM. A bee still running then is cut +; anyway (salvaged, its conversation lost), and its row stays claimed until its task lease lapses. +; Owner rules this card keeps (2026-10-08): a deploy must not cut a bee short, and must not idle the +; swarm; never shorten the drain without asking (a 120 s drain was tried and reverted in 5 min). +; How others stop for a deploy: OTP stops children in reverse order with a shutdown time each, then +; kills; Akka Cluster Sharding hands a shard off within 60 s and warns that Kubernetes' 30 s grace can +; be shorter; Kubernetes sends SIGTERM, waits terminationGracePeriodSeconds, then SIGKILL; DBOS, +; Temporal and Restate keep a workflow's progress in the store, so a stopped process loses only the +; step it was on. +; What is reused, not restated: +; - actors.t27 turn_signal (a turn's own bound, 0 meaning TURN_MAX_SECONDS) and stop_signal +; (X_SHUTDOWN, then X_KILL past a timeout); +; - turn_stop.t27: a stopped turn gets its abort, TURN_STOP_GRACE_MS, then its escalation, and its lane +; is free within LANE_FREE_MS; +; - waits.t27 section 7: a deploy writes nothing to a wait; the next process takes it up +; (pickup_after_seconds); +; - jobs.t27: a job holds no lane, and an effect runs through the effects journal, so a process that +; stops mid-step never repeats it; +; - control.t27 section 2: a runner's task lease lapses TASK_LEASE_TTL_SECONDS after its last renewal, +; and only then is its task reclaimable. +; Not here: the store, the signals, the salvage (the runtime, gHashTag/trios). +; ASCII only (L3). +; phi^2 + 1/phi^2 = 3 | TRINITY + +module QueenDrain; + +use queen::actors::turn_signal; +use queen::actors::stop_signal; +use queen::control::reclaimable; +use queen::waits::pickup_after_seconds; + +; --- 1. What a draining process holds --------------------------------------------------------------- + +/// The kinds of work a Queen process can hold when SIGTERM arrives. +/// WK_BEE: a bee this process runs for its runner (queen-runner.ts), a turn of up to an hour. +/// WK_REVIEW: one review (reviewer.t27), a turn bounded by REVIEW_ROW_SECONDS. +/// WK_ROUND: a Queen round, a reconcile: it re-reads the store and decides again. +/// WK_JOB: a step of a job (jobs.t27). +/// WK_WAIT: a wait row this process claimed (waits.t27). +pub const WK_BEE : u8 = 0; +pub const WK_REVIEW : u8 = 1; +pub const WK_ROUND : u8 = 2; +pub const WK_JOB : u8 = 3; +pub const WK_WAIT : u8 = 4; +pub const WORK_KINDS : u8 = 5; + +/// actors.t27 X_KILL and X_NONE, written as literals here (stop_signal and turn_signal answer them). +pub const X_KILL_LIT : u8 = 2; +pub const X_NONE_LIT : u8 = 99; + +/// Whether the store keeps this work as it stands, so another process continues it from there and +/// a stop throws nothing away. A wait is a row a deploy never writes (waits.t27 section 7). A job is +/// a row and its effects are journaled (jobs.t27). A round is a reconcile: everything it decided is +/// decided again from the store by the next round, and every effect it made is journaled. A review +/// and a bee are turns: their row stays, but what the turn has done so far is in its process. +pub fn row_keeps(kind: u8) -> bool { + if (kind == WK_WAIT) { return true; } + if (kind == WK_JOB) { return true; } + if (kind == WK_ROUND) { return true; } + return false; +} + +/// A turn's own bound in seconds, as actors.t27 turn_signal reads it (0 is TURN_MAX_SECONDS, 3600). +/// A review is reviewer.t27 REVIEW_ROW_SECONDS (300, a literal here). A bee names none. +pub const REVIEW_BOUND_SECONDS : u32 = 300; + +pub fn bound_of(kind: u8) -> u32 { + if (kind == WK_REVIEW) { return REVIEW_BOUND_SECONDS; } + return 0; +} + +/// The most work a drain may throw away to end sooner: one review. A turn whose own timer kills it +/// within this many seconds (turn_signal says X_KILL at this age) loses at most that much to a stop, +/// and the next process redoes it from its row. A bee's turn does not qualify: its bound is an hour, +/// and what it has done lives in its conversation, which no row keeps. Chosen, not measured. +pub const STOP_LOSS_SECONDS : u32 = 300; + +pub fn redo_is_cheap(bound_seconds: u32) -> bool { + return turn_signal(STOP_LOSS_SECONDS, bound_seconds) == X_KILL_LIT; +} + +; --- 2. What the drain owes each piece of work -------------------------------------------------------- + +/// DV_DONE: its work ended; nothing is owed. +/// DV_LEAVE: the store keeps it (row_keeps); the process exits without it and the next one takes it up. +/// DV_STOP: the drain does not wait for it. It runs on while something else holds the process, and is +/// stopped when the process exits, through turn_stop.t27 (its abort, the grace, the escalation). +/// DV_HOLD: the drain waits for it to end on its own. +/// DV_HAND_BACK: held to the cap and still running: it is stopped as DV_STOP is, what it wrote is +/// salvaged into its branch and stored, and its row is ended now, so its issue is free at once. +pub const DV_DONE : u8 = 0; +pub const DV_LEAVE : u8 = 1; +pub const DV_STOP : u8 = 2; +pub const DV_HOLD : u8 = 3; +pub const DV_HAND_BACK : u8 = 4; + +/// A bee is held until it ends or the cap passes: the owner's rule that a deploy does not cut a bee +/// short, kept to the same cap as before this card. A review is not held: a stop loses at most one +/// review, which the next process redoes. Rows are left. +pub fn drain_verdict(kind: u8, ended: bool, waited_seconds: u32, cap_seconds: u32) -> u8 { + if (ended) { return DV_DONE; } + if (row_keeps(kind)) { return DV_LEAVE; } + if (redo_is_cheap(bound_of(kind))) { return DV_STOP; } + if (waited_seconds >= cap_seconds) { return DV_HAND_BACK; } + return DV_HOLD; +} + +; --- 3. What keeps working while something holds ------------------------------------------------------ + +/// Whether a source of work starts anything new during the drain. Nothing that would start a bee in +/// this process: that bee would hold the drain for up to its hour. Everything else keeps going while +/// a held bee keeps the process alive, because the next process cannot start before this one ends +/// (one instance, one volume), and stopping the rounds then idled the swarm for the whole drain +/// (measured 2026-10-08, BrowserOS#546). Once nothing holds, the process is leaving: nothing starts. +pub fn takes_new_work(starts_a_bee_here: bool, holding: u32) -> bool { + if (starts_a_bee_here) { return false; } + return holding > 0; +} + +; --- 4. The hard cap ---------------------------------------------------------------------------------- + +/// What must still fit after the cap, before the platform's SIGKILL: the hand-back of every held bee +/// (its abort and grace, the salvage into its branch, the bundle, the row), run side by side under one +/// deadline, then the server's own close (main.ts gives it 2 s). Chosen, not measured. +pub const HAND_BACK_SECONDS : u32 = 20; +pub const EXIT_SECONDS : u32 = 5; + +/// The cap: the runner's drain (TRIOS_BEE_RUNNER_DRAIN_SECONDS), never so late that the hand-back and +/// the exit cannot finish before the platform kills the process (RAILWAY_DEPLOYMENT_DRAINING_SECONDS). +/// A platform drain of 0 is one this process cannot read: the runner's drain stands, as before. +/// With today's 1800 and 1830 the cap is 1800: this card cuts no bee sooner than the drain it replaces. +pub fn drain_cap_seconds(runner_drain_seconds: u32, platform_seconds: u32) -> u32 { + if (platform_seconds == 0) { return runner_drain_seconds; } + if (platform_seconds <= HAND_BACK_SECONDS + EXIT_SECONDS) { return 0; } + let latest : u32 = platform_seconds - HAND_BACK_SECONDS - EXIT_SECONDS; + if (runner_drain_seconds < latest) { return runner_drain_seconds; } + return latest; +} + +; --- 5. The exit -------------------------------------------------------------------------------------- + +/// turn_stop.t27's promises, written as literals here: the grace after the abort, and the time by +/// which a stopped turn's lane is free. +pub const TURN_STOP_GRACE_MS : u32 = 3000; +pub const LANE_FREE_MS : u32 = 5000; + +pub const EXIT_NOW : u8 = 0; +pub const EXIT_WAIT : u8 = 1; + +/// The process exits once nothing holds it and every turn it stopped has had its stop land: its abort +/// at once, the grace, the escalation (stop_signal with the grace says X_KILL by LANE_FREE_MS). A +/// process that stopped nothing exits at once. +pub fn exit_step(holding: u32, stopping: u32, since_stop_ms: u32) -> u8 { + if (holding > 0) { return EXIT_WAIT; } + if (stopping == 0) { return EXIT_NOW; } + if (since_stop_ms >= LANE_FREE_MS) { return EXIT_NOW; } + return EXIT_WAIT; +} + +; --- 6. When the work is free for another process ----------------------------------------------------- + +/// control.t27 TASK_LEASE_TTL_SECONDS, written as a literal here (pinned by the invariant below). +pub const TASK_LEASE_TTL_LIT : u32 = 180; + +/// Seconds after the exit before another process may take the issue of a bee the drain stopped. +/// Handed back, its row is ended at the cap: free at once. Left claimed, as before this card, it waits +/// until its task lease lapses (control.t27 reclaimable), counted from the last renewal, which stopped +/// with the process. +pub fn bee_free_after_seconds(handed_back: bool, since_renewal: u32) -> u32 { + if (handed_back) { return 0; } + if (reclaimable(false, since_renewal)) { return 0; } + return TASK_LEASE_TTL_LIT - since_renewal; +} + +/// Seconds after the exit before another process may take a wait row this process had claimed: the +/// rest of its claim (waits.t27 pickup_after_seconds). The drain writes nothing to it. +pub fn wait_free_after_seconds(claimed: bool, since_claim: u32) -> u32 { + return pickup_after_seconds(claimed, since_claim); +} + +; --- tests -------------------------------------------------------------------------------------------- + +test rows_are_left_to_the_next_process { + assert row_keeps(WK_WAIT) == true; + assert row_keeps(WK_JOB) == true; + assert row_keeps(WK_ROUND) == true; + assert row_keeps(WK_REVIEW) == false; + assert row_keeps(WK_BEE) == false; + assert drain_verdict(WK_WAIT, false, 0, 1800) == DV_LEAVE; + assert drain_verdict(WK_JOB, false, 0, 1800) == DV_LEAVE; + assert drain_verdict(WK_ROUND, false, 0, 1800) == DV_LEAVE; + // past the cap too: a row is never handed back, it is simply there + assert drain_verdict(WK_WAIT, false, 1800, 1800) == DV_LEAVE; +} + +test a_review_is_stopped_at_the_exit_not_waited_for { + assert bound_of(WK_REVIEW) == 300; + assert redo_is_cheap(300) == true; + assert redo_is_cheap(301) == false; + assert drain_verdict(WK_REVIEW, false, 0, 1800) == DV_STOP; + assert drain_verdict(WK_REVIEW, false, 1800, 1800) == DV_STOP; +} + +test a_bee_is_held_until_it_ends_or_the_cap { + assert bound_of(WK_BEE) == 0; + // its bound is TURN_MAX_SECONDS: a stop could throw away an hour + assert redo_is_cheap(0) == false; + assert redo_is_cheap(3600) == false; + assert drain_verdict(WK_BEE, false, 0, 1800) == DV_HOLD; + assert drain_verdict(WK_BEE, false, 1799, 1800) == DV_HOLD; + assert drain_verdict(WK_BEE, false, 1800, 1800) == DV_HAND_BACK; + assert drain_verdict(WK_BEE, false, 4000, 1800) == DV_HAND_BACK; + // a bee that ended is owed nothing, at any time + assert drain_verdict(WK_BEE, true, 0, 1800) == DV_DONE; + assert drain_verdict(WK_BEE, true, 1800, 1800) == DV_DONE; + assert drain_verdict(WK_REVIEW, true, 0, 1800) == DV_DONE; + assert drain_verdict(WK_WAIT, true, 0, 1800) == DV_DONE; +} + +test a_cap_of_zero_hands_every_bee_back_at_once { + assert drain_verdict(WK_BEE, false, 0, 0) == DV_HAND_BACK; +} + +test nothing_starts_a_bee_here_and_the_rest_runs_only_while_held { + assert takes_new_work(true, 3) == false; + assert takes_new_work(true, 0) == false; + assert takes_new_work(false, 1) == true; + assert takes_new_work(false, 0) == false; +} + +test the_cap_is_todays_drain_and_fits_the_platform { + // today: runner 1800, Railway 1830 -> 1800, no bee cut sooner than before + assert drain_cap_seconds(1800, 1830) == 1800; + // a platform drain too short for the hand-back moves the cap earlier + assert drain_cap_seconds(1800, 1800) == 1775; + assert drain_cap_seconds(1800, 26) == 1; + assert drain_cap_seconds(1800, 25) == 0; + assert drain_cap_seconds(1800, 10) == 0; + // a shorter runner drain is the owner's to choose, and it stands + assert drain_cap_seconds(600, 1830) == 600; + assert drain_cap_seconds(0, 1830) == 0; + // a platform drain this process cannot read leaves the runner's drain alone + assert drain_cap_seconds(1800, 0) == 1800; +} + +test the_exit_waits_for_held_work_and_for_stops_to_land { + assert exit_step(1, 0, 0) == EXIT_WAIT; + assert exit_step(2, 3, 9000) == EXIT_WAIT; + assert exit_step(0, 0, 0) == EXIT_NOW; + assert exit_step(0, 2, 0) == EXIT_WAIT; + assert exit_step(0, 2, 4999) == EXIT_WAIT; + assert exit_step(0, 2, 5000) == EXIT_NOW; +} + +test a_handed_back_bee_is_free_at_once_and_a_left_one_waits_out_its_lease { + assert bee_free_after_seconds(true, 0) == 0; + assert bee_free_after_seconds(true, 100) == 0; + assert bee_free_after_seconds(false, 0) == 180; + assert bee_free_after_seconds(false, 45) == 135; + assert bee_free_after_seconds(false, 179) == 1; + assert bee_free_after_seconds(false, 180) == 0; + assert bee_free_after_seconds(false, 4294967295) == 0; +} + +test a_wait_row_is_free_after_the_rest_of_its_claim { + assert wait_free_after_seconds(false, 0) == 0; + assert wait_free_after_seconds(true, 0) == 60; + assert wait_free_after_seconds(true, 45) == 15; + assert wait_free_after_seconds(true, 60) == 0; +} + +invariant the_verdicts_and_kinds_are_pinned { + assert DV_DONE == 0; + assert DV_LEAVE == 1; + assert DV_STOP == 2; + assert DV_HOLD == 3; + assert DV_HAND_BACK == 4; + assert WK_BEE == 0; + assert WK_WAIT == 4; + assert WORK_KINDS == 5; + assert EXIT_NOW == 0; + assert EXIT_WAIT == 1; +} + +invariant the_literals_match_the_cards_they_come_from { + // actors.t27: turn_signal answers X_KILL at the bound and X_NONE before it + assert turn_signal(300, 300) == X_KILL_LIT; + assert turn_signal(299, 300) == X_NONE_LIT; + // control.t27: the task lease lapses at TASK_LEASE_TTL_SECONDS + assert reclaimable(false, TASK_LEASE_TTL_LIT) == true; + assert reclaimable(false, TASK_LEASE_TTL_LIT - 1) == false; + // turn_stop.t27: by LANE_FREE_MS a stopped turn's grace has passed (stop_signal says X_KILL) + assert stop_signal(LANE_FREE_MS, TURN_STOP_GRACE_MS) == X_KILL_LIT; + assert stop_signal(TURN_STOP_GRACE_MS - 1, TURN_STOP_GRACE_MS) == X_NONE_LIT; +} + +invariant a_review_is_cheap_to_redo_and_a_bee_is_not { + assert STOP_LOSS_SECONDS == REVIEW_BOUND_SECONDS; + assert redo_is_cheap(REVIEW_BOUND_SECONDS) == true; + assert redo_is_cheap(0) == false; +} + +invariant the_hand_back_fits_between_todays_drain_and_the_platform_kill { + assert HAND_BACK_SECONDS + EXIT_SECONDS <= 30; + assert drain_cap_seconds(1800, 1830) == 1800; +} diff --git a/apps/website/public/t27/files/specs/queen/hive.t27 b/apps/website/public/t27/files/specs/queen/hive.t27 new file mode 100644 index 0000000000..ec636503bf --- /dev/null +++ b/apps/website/public/t27/files/specs/queen/hive.t27 @@ -0,0 +1,184 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/queen/hive.t27 -- how the game draws the swarm: where each ship flies, what an event does to +; a cell, how a ship leaves, and how often the map may grow +; Owner, 2026-10-08 (translated): the bees fly over the GitHub issue each is working on, in the game +; (https://app.t27.ai/game); the ships come from the same 3D kit as the comb. Epic #7718, slice 4. +; What the game did before this card (gHashTag/trinity QueenCombBabylon, measured that day): +; - a bee that left the feed vanished in the frame it left: a finished task had no ending on screen. +; - a bee on an issue with no cell yet waited at the hive's entrance, in the middle of the map, far +; from the repository it works for. +; - every newly running issue the atlas did not hold added one cell, and a new cell rebuilt the whole +; scene: one rebuild for most bees that started. +; - nothing on the map answered an event: an accepted task, a lost lease and a cancel looked the same +; as no news. +; The event kinds are control.t27's EV_* (index of a per-kind table here). Bee states arrive as two +; flags rather than tasks.t27's numbers, so no number is restated. The game runs this card as wasm. +; ASCII only (L3). +; phi^2 + 1/phi^2 = 3 | TRINITY + +module QueenHive; + +pub const HIVE_KINDS : u8 = 10; + +; --- 1. Where a ship flies --------------------------------------------------------------------------- + +/// Where its issue's cell stands: on the map, waiting for the next batch (section 4), or nowhere this +/// map can show (a repository with no region, or a task that is not an issue). +pub const CELL_PLACED : u8 = 0; +pub const CELL_PENDING : u8 = 1; +pub const CELL_NONE : u8 = 2; + +/// PARK: in a row at the hive's entrance. ORBIT: circling its issue's rim. HOVER: still at the rim, +/// heard from too long ago to be drawn working. PORTAL: at its repository's portal, until its cell is +/// placed. HOME: flying back to the entrance, its task over. +pub const SM_PARK : u8 = 0; +pub const SM_ORBIT : u8 = 1; +pub const SM_HOVER : u8 = 2; +pub const SM_PORTAL : u8 = 3; +pub const SM_HOME : u8 = 4; + +/// `queued`: not claimed yet. `working`: heard from lately (tasks.t27 bee_state). `departing`: no +/// longer in the feed. +pub fn ship_mode(queued: bool, working: bool, cell: u8, departing: bool) -> u8 { + if (departing) { return SM_HOME; } + if (queued) { return SM_PARK; } + if (cell == CELL_PENDING) { return SM_PORTAL; } + if (cell != CELL_PLACED) { return SM_PARK; } + if (working) { return SM_ORBIT; } + return SM_HOVER; +} + +; --- 2. How a ship leaves ---------------------------------------------------------------------------- + +/// A ship whose bee left the feed flies home and fades out over DEPART_MS, instead of vanishing. +pub const DEPART_MS : u32 = 2400; + +pub fn ship_kept(ms_since_left: u32) -> bool { + return ms_since_left < DEPART_MS; +} + +/// Opacity in thousandths: whole while its bee is in the feed, then falling to nothing. +pub fn ship_alpha(departing: bool, ms_since_left: u32) -> u32 { + if (departing == false) { return 1000; } + if (ms_since_left >= DEPART_MS) { return 0; } + return 1000 - (ms_since_left * 1000) / DEPART_MS; +} + +; --- 3. What an event does to a cell ----------------------------------------------------------------- + +/// FX_ACCEPT gold, FX_SEND_BACK amber, FX_LOST red (the lease expired: the bee went silent and the +/// task was taken back), FX_CANCEL grey (a person stopped it), FX_START white at the cell the bee is +/// headed for. task.ended draws nothing on the cell: its ship flies home, which says it. +pub const FX_NONE : u8 = 0; +pub const FX_START : u8 = 1; +pub const FX_ACCEPT : u8 = 2; +pub const FX_SEND_BACK : u8 = 3; +pub const FX_LOST : u8 = 4; +pub const FX_CANCEL : u8 = 5; + +/// Per kind (control.t27 EV_*): created START, lease.expired LOST, reviewed ACCEPT or SEND_BACK by +/// its verdict, cancel CANCEL; everything else nothing. +pub const FX_OF_KIND : [10]u8 = [1, 0, 0, 4, 0, 2, 0, 5, 0, 0]; + +/// `accepted`: a review's verdict was an acceptance. Only a review reads it. +pub fn cell_effect(kind: u8, accepted: bool) -> u8 { + if (kind >= HIVE_KINDS) { return FX_NONE; } + let fx : u8 = FX_OF_KIND[kind]; + if (fx == FX_ACCEPT and accepted == false) { return FX_SEND_BACK; } + return fx; +} + +/// A flash rises at once and fades over FLASH_MS; an event older than that is not drawn. +pub const FLASH_MS : u32 = 1800; + +pub fn flash_alpha(ms_since: u32) -> u32 { + if (ms_since >= FLASH_MS) { return 0; } + return 1000 - (ms_since * 1000) / FLASH_MS; +} + +; --- 4. How often the map may grow ------------------------------------------------------------------- + +/// A running issue the atlas does not hold needs a cell, and a cell is part of the built scene: adding +/// one rebuilds it. So new cells are placed in batches, at most one batch every CELL_BATCH_SECONDS -- +/// the same period a board takes a whole snapshot (events.t27 SNAPSHOT_EVERY_SECONDS) -- and all of +/// them at once when the page first opens. Until its cell is placed, a bee waits at its repository's +/// portal (SM_PORTAL), so where it is going is already true. +pub const CELL_BATCH_SECONDS : u32 = 300; + +pub fn cells_due(pending: u32, placed_before: bool, seconds_since_batch: u32) -> bool { + if (pending == 0) { return false; } + if (placed_before == false) { return true; } + return seconds_since_batch >= CELL_BATCH_SECONDS; +} + +test a_ship_flies_where_its_bee_is { + // queued: at the entrance, wherever its cell stands + assert ship_mode(true, false, CELL_PLACED, false) == SM_PARK; + assert ship_mode(true, true, CELL_PENDING, false) == SM_PARK; + // claimed, its cell placed: orbit while heard, hover when quiet + assert ship_mode(false, true, CELL_PLACED, false) == SM_ORBIT; + assert ship_mode(false, false, CELL_PLACED, false) == SM_HOVER; + // its cell not placed yet: at its repository's portal, working or not + assert ship_mode(false, true, CELL_PENDING, false) == SM_PORTAL; + assert ship_mode(false, false, CELL_PENDING, false) == SM_PORTAL; + // no cell this map can show: back at the entrance + assert ship_mode(false, true, CELL_NONE, false) == SM_PARK; + // gone from the feed: home, whatever it was + assert ship_mode(false, true, CELL_PLACED, true) == SM_HOME; + assert ship_mode(true, false, CELL_NONE, true) == SM_HOME; +} + +test a_ship_leaves_by_flying_home { + assert ship_kept(0) == true; + assert ship_kept(DEPART_MS - 1) == true; + assert ship_kept(DEPART_MS) == false; + assert ship_alpha(false, 0) == 1000; + assert ship_alpha(false, 99999) == 1000; + assert ship_alpha(true, 0) == 1000; + assert ship_alpha(true, 1200) == 500; + assert ship_alpha(true, DEPART_MS - 1) == 1; + assert ship_alpha(true, DEPART_MS) == 0; + assert ship_alpha(true, 4000000000) == 0; +} + +test an_event_lights_its_cell { + assert cell_effect(0, false) == FX_START; + assert cell_effect(1, true) == FX_NONE; + assert cell_effect(2, false) == FX_NONE; + assert cell_effect(3, false) == FX_LOST; + assert cell_effect(4, true) == FX_NONE; + assert cell_effect(5, true) == FX_ACCEPT; + assert cell_effect(5, false) == FX_SEND_BACK; + assert cell_effect(6, true) == FX_NONE; + assert cell_effect(7, false) == FX_CANCEL; + assert cell_effect(8, false) == FX_NONE; + assert cell_effect(9, true) == FX_NONE; + // only a review reads the verdict + assert cell_effect(3, true) == FX_LOST; + assert cell_effect(HIVE_KINDS, true) == FX_NONE; +} + +test a_flash_fades { + assert flash_alpha(0) == 1000; + assert flash_alpha(900) == 500; + assert flash_alpha(FLASH_MS - 1) == 1; + assert flash_alpha(FLASH_MS) == 0; + assert flash_alpha(4000000000) == 0; +} + +test the_map_grows_in_batches { + assert cells_due(0, false, 0) == false; + assert cells_due(0, true, 99999) == false; + // the first answer places every cell at once + assert cells_due(3, false, 0) == true; + // after that, one batch a period + assert cells_due(1, true, CELL_BATCH_SECONDS - 1) == false; + assert cells_due(1, true, CELL_BATCH_SECONDS) == true; +} + +invariant one_row_per_kind { + assert HIVE_KINDS == 10; + assert FX_OF_KIND[5] == FX_ACCEPT; + assert FLASH_MS < DEPART_MS * 2; + assert CELL_BATCH_SECONDS == 300; +} diff --git a/apps/website/public/t27/files/specs/queen/key_failover.t27 b/apps/website/public/t27/files/specs/queen/key_failover.t27 new file mode 100644 index 0000000000..3ce96bef2b --- /dev/null +++ b/apps/website/public/t27/files/specs/queen/key_failover.t27 @@ -0,0 +1,383 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/queen/key_failover.t27 -- a bee whose key is refused moves to another key, then another pool (gHashTag/t27#9993). +; Measured 2026-10-17 13:00-15:18Z in the Railway logs of trios-bee-runner-2: 93 bee turns, 17 of them +; ended after 0 steps; at least 22 AI_RetryError on z.ai "429 code 1305" (temporary overload), in bursts +; at 13:53, 13:59, 14:08 (16 in one minute), 14:19, 14:47 and 14:50. trios-bee-runner-1: about 6 of 48 +; turns ended after 0 steps. The bee for #9967 started at 14:50:47 on pool-2 key 9/10 and died at 14:50:54. +; The cause: a bee gets ONE key when it is dispatched (trios queen-dispatch.ts resolveWorkerProvider, +; round-robin) and keeps it. On 1305 the SDK retried the same key three times, the turn closed with no +; completion frame, and the dispatch ended "cause undetermined" while 33 other keys in two pools (24 and +; 10) stood by. The reviewer already does the right thing (trios queen-tick.ts, "a transient refusal +; tries the next lane too", after it lost 16 of 16 calls to 1302/1305 on 2026-09-23); bees did not. +; The rule here, after Envoy's retry_host_predicate previous_hosts plus outlier ejection, and the LiteLLM +; router's cooldown-then-fallback: +; 1. classify the refusal: transient, quota, auth or a bad request (error_kind); +; 2. a transient refusal rests the key for seconds (cooldown_seconds), a quota or auth refusal closes +; it for the long break -- 1302 and 1305 never close a key and never retire work; +; 3. the next attempt goes to the next free key of the pool, never the key that just failed +; (next_free); 1305 is an overload of the pool's MODEL, not of one key, so once POOL_OVERLOAD_KEYS +; distinct keys of one pool answered 1305 inside OVERLOAD_WINDOW_SECONDS the bee moves to the next +; pool instead of burning the rest (pool_overloaded); +; 4. at most MAX_ATTEMPTS_PER_TURN attempts, then the turn ends as provider_overloaded (trios#1708), +; its claim is released (trios#1886), and the end is not charged to the issue (charges_issue). +; Masks: bit i of a u32 mask is key i of the pool (or pool i of the pool list); a pool holds at most +; POOL_MAX keys. NO_KEY means "none left". The host keeps the clock and the masks; this file decides. +; Not here: the wiring into trios. It lands as generated code, not hand-written TypeScript. ASCII only (L3). +; phi^2 + 1/phi^2 = 3 | TRINITY + +module QueenKeyFailover; + +; --- 1. What the provider said ----------------------------------------------------------------------- + +/// KE_OK: the request was answered. KE_TRANSIENT: refused for now, another key or a moment later may +/// pass. KE_QUOTA: the key's quota, plan or balance is spent. KE_AUTH: the key itself is refused. +/// KE_REQUEST: the request is wrong; every key would answer the same. +pub const KE_OK : u8 = 0; +pub const KE_TRANSIENT : u8 = 1; +pub const KE_QUOTA : u8 = 2; +pub const KE_AUTH : u8 = 3; +pub const KE_REQUEST : u8 = 4; + +/// The z.ai rate limit and temporary overload codes. Both arrive as HTTP 429 and both clear on their own. +pub const ZAI_RATE_LIMIT : u32 = 1302; +pub const ZAI_OVERLOAD : u32 = 1305; + +/// The z.ai codes whose documented meaning is a spent quota, plan or balance: 1113 and 1308-1321 +/// except 1312, the closed list ZAI_QUOTA_EXHAUSTED_CODES in trios queen-dispatch.ts (#1301). +pub fn is_quota_code(code: u32) -> bool { + if (code == 1113) { return true; } + if (code == 1312) { return false; } + if (code < 1308) { return false; } + return code <= 1321; +} + +/// `status` is the HTTP status, 0 when no response came at all (timeout, reset, abort). `zai_code` is +/// the business code in the body, 0 when there is none. The code wins over the status, because z.ai +/// sends a spent quota and a passing overload with the same 429. +pub fn error_kind(status: u32, zai_code: u32) -> u8 { + if (is_quota_code(zai_code)) { return KE_QUOTA; } + if (zai_code == ZAI_RATE_LIMIT) { return KE_TRANSIENT; } + if (zai_code == ZAI_OVERLOAD) { return KE_TRANSIENT; } + if (status == 0) { return KE_TRANSIENT; } + if (status == 402) { return KE_QUOTA; } + if (status == 401) { return KE_AUTH; } + if (status == 403) { return KE_AUTH; } + if (status == 408) { return KE_TRANSIENT; } + if (status == 429) { return KE_TRANSIENT; } + if (status >= 600) { return KE_REQUEST; } + if (status >= 500) { return KE_TRANSIENT; } + if (status >= 400) { return KE_REQUEST; } + return KE_OK; +} + +; --- 2. How long a refused key rests ------------------------------------------------------------------ + +/// A transient refusal rests the key COOL_BASE_SECONDS, doubled per strike in a row, never more than +/// COOL_CAP_SECONDS: long enough that the next bee does not hit the same overloaded key at once, short +/// enough that 34 keys are never all resting through a one-minute burst like 14:08. +pub const COOL_BASE_SECONDS : u32 = 15; +pub const COOL_CAP_SECONDS : u32 = 240; +/// A spent quota or a refused key closes for the long break the runtime already uses (30 minutes). +pub const KEY_BREAK_SECONDS : u32 = 1800; + +/// `strikes` is the key's transient refusals in a row before this one. The cap is the base doubled +/// exactly four times (an invariant pins it), so the doubling meets the cap and never steps past it. +pub fn cooldown_seconds(kind: u8, strikes: u32) -> u32 { + if (kind == KE_QUOTA) { return KEY_BREAK_SECONDS; } + if (kind == KE_AUTH) { return KEY_BREAK_SECONDS; } + if (kind != KE_TRANSIENT) { return 0; } + var wait : u32 = COOL_BASE_SECONDS; + var n : u32 = 0; + while (n < strikes) { + if (wait >= COOL_CAP_SECONDS) { return COOL_CAP_SECONDS; } + wait = wait * 2; + n = n + 1; + } + return wait; +} + +/// Whether the refusal closes the key for the long break. A transient refusal never does. +pub fn key_closes(kind: u8) -> bool { + if (kind == KE_QUOTA) { return true; } + return kind == KE_AUTH; +} + +; --- 3. The next key, the next pool ------------------------------------------------------------------ + +pub const POOL_MAX : u32 = 32; +pub const NO_KEY : u32 = 255; + +pub fn bit(i: u32) -> u32 { + if (i >= POOL_MAX) { return 0; } + return 1 << i; +} + +pub fn is_set(mask: u32, i: u32) -> bool { + return (mask & bit(i)) != 0; +} + +/// The next index after `last`, wrapping, that is neither tried in this turn nor blocked (resting or +/// closed), and is never `last` itself: the scan visits the size - 1 other indices only. `last` out of +/// range (NO_KEY) visits all of them from index 0. NO_KEY when none is free. A list longer than +/// POOL_MAX does not fit a mask and is refused (NO_KEY), never read as its first 32. The same rule +/// picks a key inside a pool and a pool inside the pool list. +pub fn next_free(tried: u32, blocked: u32, size: u32, last: u32) -> u32 { + if (size > POOL_MAX) { return NO_KEY; } + var start : u32 = 0; + var count : u32 = size; + if (last < size) { + start = last + 1; + count = size - 1; + } + var k : u32 = 0; + while (k < count) { + var i : u32 = (start + k) % size; + if (is_set(tried | blocked, i) == false) { return i; } + k = k + 1; + } + return NO_KEY; +} + +/// 1305 from POOL_OVERLOAD_KEYS distinct keys of one pool inside OVERLOAD_WINDOW_SECONDS is the +/// pool's model overloaded. A pool smaller than that is overloaded when every key said it. +pub const POOL_OVERLOAD_KEYS : u32 = 3; +pub const OVERLOAD_WINDOW_SECONDS : u32 = 60; + +pub fn pool_overloaded(distinct_keys_1305: u32, pool_size: u32) -> bool { + if (distinct_keys_1305 >= POOL_OVERLOAD_KEYS) { return true; } + if (pool_size == 0) { return false; } + return distinct_keys_1305 >= pool_size; +} + +; --- 4. The step after a refusal --------------------------------------------------------------------- + +/// FA_DONE: answered, go on. FA_NEXT_KEY: retry on next_free's key. FA_NEXT_POOL: retry on the next +/// pool. FA_GIVE_UP: no key left; the turn ends as provider_overloaded. FA_FAIL: the request is wrong. +pub const FA_DONE : u8 = 0; +pub const FA_NEXT_KEY : u8 = 1; +pub const FA_NEXT_POOL : u8 = 2; +pub const FA_GIVE_UP : u8 = 3; +pub const FA_FAIL : u8 = 4; + +/// The bound on attempts in one turn, the failed one included: three keys of the first pool to see an +/// overload, three of the second, and two to spare. It keeps a turn from spinning through 34 keys. +pub const MAX_ATTEMPTS_PER_TURN : u32 = 8; + +/// `attempts` counts the attempts of this turn so far, this one included. `next_key` and `next_pool` +/// are next_free's answers for the current pool and for the pool list (NO_KEY when none). +pub fn step(kind: u8, attempts: u32, next_key: u32, distinct_keys_1305: u32, pool_size: u32, next_pool: u32) -> u8 { + if (kind == KE_OK) { return FA_DONE; } + if (kind >= KE_REQUEST) { return FA_FAIL; } + if (attempts >= MAX_ATTEMPTS_PER_TURN) { return FA_GIVE_UP; } + var pool_out : bool = false; + if (kind == KE_TRANSIENT) { pool_out = pool_overloaded(distinct_keys_1305, pool_size); } + if (pool_out == false) { + if (next_key != NO_KEY) { return FA_NEXT_KEY; } + } + if (next_pool != NO_KEY) { return FA_NEXT_POOL; } + return FA_GIVE_UP; +} + +/// Whether a turn that ended on this kind counts against the issue. A provider that is overloaded, +/// spent or refusing a key says nothing about the work, so it never retires the issue; a wrong request +/// does. Only KE_REQUEST charges. This narrows dispatch_exit.t27 counts_against_issue, which charges +/// every DX_PROVIDER_ERROR. +pub fn charges_issue(kind: u8) -> bool { + return kind == KE_REQUEST; +} + +; --- 5. Tests ---------------------------------------------------------------------------------------- + +test the_quota_list_is_the_closed_list_of_the_runtime { + assert is_quota_code(1113) == true; + assert is_quota_code(1308) == true; + assert is_quota_code(1311) == true; + assert is_quota_code(1312) == false; + assert is_quota_code(1313) == true; + assert is_quota_code(1321) == true; + assert is_quota_code(1322) == false; + assert is_quota_code(1307) == false; + assert is_quota_code(1112) == false; + assert is_quota_code(1114) == false; + assert is_quota_code(ZAI_RATE_LIMIT) == false; + assert is_quota_code(ZAI_OVERLOAD) == false; + assert is_quota_code(0) == false; +} + +test the_code_wins_over_the_status { + assert error_kind(429, 1305) == KE_TRANSIENT; + assert error_kind(429, 1302) == KE_TRANSIENT; + assert error_kind(429, 1113) == KE_QUOTA; + assert error_kind(429, 1316) == KE_QUOTA; + assert error_kind(200, 1308) == KE_QUOTA; + assert error_kind(400, 1305) == KE_TRANSIENT; + assert error_kind(400, 1302) == KE_TRANSIENT; + assert error_kind(429, 1312) == KE_TRANSIENT; +} + +test a_status_without_a_code_falls_in_its_kind { + assert error_kind(0, 0) == KE_TRANSIENT; + assert error_kind(200, 0) == KE_OK; + assert error_kind(399, 0) == KE_OK; + assert error_kind(400, 0) == KE_REQUEST; + assert error_kind(401, 0) == KE_AUTH; + assert error_kind(402, 0) == KE_QUOTA; + assert error_kind(403, 0) == KE_AUTH; + assert error_kind(404, 0) == KE_REQUEST; + assert error_kind(407, 0) == KE_REQUEST; + assert error_kind(408, 0) == KE_TRANSIENT; + assert error_kind(409, 0) == KE_REQUEST; + assert error_kind(428, 0) == KE_REQUEST; + assert error_kind(429, 0) == KE_TRANSIENT; + assert error_kind(430, 0) == KE_REQUEST; + assert error_kind(499, 0) == KE_REQUEST; + assert error_kind(500, 0) == KE_TRANSIENT; + assert error_kind(503, 0) == KE_TRANSIENT; + assert error_kind(529, 0) == KE_TRANSIENT; + assert error_kind(599, 0) == KE_TRANSIENT; + assert error_kind(600, 0) == KE_REQUEST; +} + +test a_transient_refusal_rests_seconds_and_a_spent_key_closes { + assert cooldown_seconds(KE_TRANSIENT, 0) == 15; + assert cooldown_seconds(KE_TRANSIENT, 1) == 30; + assert cooldown_seconds(KE_TRANSIENT, 2) == 60; + assert cooldown_seconds(KE_TRANSIENT, 3) == 120; + assert cooldown_seconds(KE_TRANSIENT, 4) == COOL_CAP_SECONDS; + assert cooldown_seconds(KE_TRANSIENT, 5) == COOL_CAP_SECONDS; + assert cooldown_seconds(KE_TRANSIENT, 4000000000) == COOL_CAP_SECONDS; + assert cooldown_seconds(KE_QUOTA, 0) == KEY_BREAK_SECONDS; + assert cooldown_seconds(KE_AUTH, 7) == KEY_BREAK_SECONDS; + assert cooldown_seconds(KE_OK, 3) == 0; + assert cooldown_seconds(KE_REQUEST, 3) == 0; + assert key_closes(KE_QUOTA) == true; + assert key_closes(KE_AUTH) == true; + assert key_closes(KE_TRANSIENT) == false; + assert key_closes(KE_OK) == false; + assert key_closes(KE_REQUEST) == false; +} + +test bits_name_keys { + assert bit(0) == 1; + assert bit(3) == 8; + assert bit(31) == 2147483648; + assert bit(32) == 0; + assert is_set(8, 3) == true; + assert is_set(8, 2) == false; + assert is_set(4294967295, 31) == true; + assert is_set(4294967295, 32) == false; +} + +test the_next_key_is_never_the_one_that_failed { + // the #9967 case: pool 2 has 10 keys, key 9 answered 1305; the next try wraps to key 0 + assert next_free(bit(9), 0, 10, 9) == 0; + // even when the failed key is not marked tried, it is skipped + assert next_free(0, 0, 10, 9) == 0; + assert next_free(0, 0, 10, 4) == 5; + // a pool of one key has no other key + assert next_free(0, 0, 1, 0) == NO_KEY; + // tried and resting keys are both skipped + assert next_free(bit(5), bit(6), 10, 4) == 7; + assert next_free(bit(0) | bit(1), 0, 3, 2) == NO_KEY; + // a fresh turn starts at key 0 + assert next_free(0, 0, 24, NO_KEY) == 0; + assert next_free(bit(0), 0, 24, NO_KEY) == 1; + // every key but the failed one is taken + assert next_free(4294967295 - bit(9), 0, 10, 9) == NO_KEY; + // an empty pool has nothing + assert next_free(0, 0, 0, NO_KEY) == NO_KEY; + // `last` just past the end is out of range: the scan starts at 0 + assert next_free(0, 0, 10, 10) == 0; + // a full mask of POOL_MAX keys wraps from the top index + assert next_free(0, 0, 32, 31) == 0; + assert next_free(0, 0, 32, NO_KEY) == 0; + assert next_free(0, 4294967295 - bit(31), 32, 30) == 31; + // a list longer than POOL_MAX does not fit a mask and is refused + assert next_free(0, 0, 33, NO_KEY) == NO_KEY; + assert next_free(0, 0, 40, 31) == NO_KEY; +} + +test an_overload_seen_on_three_keys_is_the_pools_model { + assert pool_overloaded(0, 24) == false; + assert pool_overloaded(2, 24) == false; + assert pool_overloaded(3, 24) == true; + assert pool_overloaded(16, 24) == true; + assert pool_overloaded(2, 2) == true; + assert pool_overloaded(1, 2) == false; + assert pool_overloaded(1, 1) == true; + assert pool_overloaded(0, 0) == false; +} + +/// The conformance table: one assert per row. Columns: kind, attempts, next key, distinct keys that +/// answered 1305 in the window, pool size, next pool. +test conformance_rows { + // r01 answered: go on + assert step(KE_OK, 1, 5, 0, 24, 1) == FA_DONE; + // r02 a wrong request fails, whatever keys are left + assert step(KE_REQUEST, 1, 5, 0, 24, 1) == FA_FAIL; + // r03 an unknown kind is read as a wrong request + assert step(9, 1, 5, 0, 24, 1) == FA_FAIL; + // r04 the #9967 case: first 1305 on pool 2, keys left: next key, not the same key + assert step(KE_TRANSIENT, 1, 0, 1, 10, 0) == FA_NEXT_KEY; + // r05 a rate limit (1302) on a fresh pool: next key + assert step(KE_TRANSIENT, 1, 3, 0, 24, 1) == FA_NEXT_KEY; + // r06 the third distinct key said 1305: the model is overloaded, next pool + assert step(KE_TRANSIENT, 3, 4, 3, 24, 1) == FA_NEXT_POOL; + // r07 the 14:08 burst: overloaded and no other pool: give up, do not burn the rest + assert step(KE_TRANSIENT, 3, 4, 3, 24, NO_KEY) == FA_GIVE_UP; + // r08 a spent quota on one key does not mean an overloaded pool: next key + assert step(KE_QUOTA, 1, 2, 5, 24, 1) == FA_NEXT_KEY; + // r09 a refused key: next key + assert step(KE_AUTH, 1, 2, 0, 24, 1) == FA_NEXT_KEY; + // r10 no key left in the pool: next pool + assert step(KE_TRANSIENT, 2, NO_KEY, 1, 24, 1) == FA_NEXT_POOL; + // r11 no key and no pool left: give up + assert step(KE_QUOTA, 2, NO_KEY, 0, 24, NO_KEY) == FA_GIVE_UP; + // r12 the attempt bound stops a turn with keys still left + assert step(KE_TRANSIENT, MAX_ATTEMPTS_PER_TURN, 4, 0, 24, 1) == FA_GIVE_UP; + // r13 one attempt below the bound still moves on + assert step(KE_TRANSIENT, MAX_ATTEMPTS_PER_TURN - 1, 4, 0, 24, 1) == FA_NEXT_KEY; + // r14 a two-key pool where both said 1305 is overloaded + assert step(KE_TRANSIENT, 2, NO_KEY, 2, 2, 0) == FA_NEXT_POOL; + // r15 a 503 with no 1305 seen keeps to the pool + assert step(KE_TRANSIENT, 2, 7, 0, 24, 1) == FA_NEXT_KEY; + // r16 the bound beats a wrong request? no: a wrong request fails first + assert step(KE_REQUEST, MAX_ATTEMPTS_PER_TURN, 4, 0, 24, 1) == FA_FAIL; + // r17 an answer after many attempts is still an answer + assert step(KE_OK, MAX_ATTEMPTS_PER_TURN, NO_KEY, 9, 24, NO_KEY) == FA_DONE; +} + +test an_overloaded_provider_never_retires_the_issue { + assert charges_issue(KE_TRANSIENT) == false; + assert charges_issue(KE_QUOTA) == false; + assert charges_issue(KE_AUTH) == false; + assert charges_issue(KE_REQUEST) == true; + assert charges_issue(KE_OK) == false; + assert charges_issue(9) == false; +} + +; --- 6. Invariants ----------------------------------------------------------------------------------- + +invariant the_kinds_and_steps_are_distinct { + assert KE_OK == 0 and KE_TRANSIENT == 1 and KE_QUOTA == 2 and KE_AUTH == 3 and KE_REQUEST == 4; + assert FA_DONE == 0 and FA_NEXT_KEY == 1 and FA_NEXT_POOL == 2 and FA_GIVE_UP == 3 and FA_FAIL == 4; +} + +/// A transient rest is always shorter than the long break, so a 1305 can never close a key. +invariant a_rest_is_shorter_than_a_break { + assert COOL_BASE_SECONDS > 0 and COOL_CAP_SECONDS == COOL_BASE_SECONDS * 16; + assert COOL_CAP_SECONDS < KEY_BREAK_SECONDS and KEY_BREAK_SECONDS == 1800; +} + +/// The bound leaves room to see an overload in one pool and still try the next: the overload is +/// detected before the bound stops the turn. +invariant the_bound_leaves_room_for_a_second_pool { + assert MAX_ATTEMPTS_PER_TURN > POOL_OVERLOAD_KEYS * 2; + assert POOL_OVERLOAD_KEYS >= 2 and OVERLOAD_WINDOW_SECONDS > 0; +} + +/// NO_KEY is no index a mask can hold. +invariant no_key_is_out_of_range { + assert POOL_MAX == 32 and NO_KEY >= POOL_MAX; + assert ZAI_RATE_LIMIT == 1302 and ZAI_OVERLOAD == 1305; +} diff --git a/apps/website/public/t27/files/specs/queen/review_first.t27 b/apps/website/public/t27/files/specs/queen/review_first.t27 new file mode 100644 index 0000000000..dfce350f1d --- /dev/null +++ b/apps/website/public/t27/files/specs/queen/review_first.t27 @@ -0,0 +1,175 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/queen/review_first.t27 -- no new work from the backlog while finished work waits in review (gHashTag/t27#10555). +; The owner's rule, 2026-10-18: the Queen takes nothing from the backlog while there is a queue in review. +; Measured 2026-10-18 17:09Z on api.t27.ai/queen/status: 69 of 70 workers busy; in the last hour 102 +; dispatched, 56 finished, 11 reviewed; reviewable (the review sweep's own queue) 29, unreviewed 362. +; The swarm starts about nine bees for every verdict, so the queue in review only grows, and each new bee +; holds files (claim.t27) that the bees already in review need when they are sent back. +; The rule: +; 1. the review queue is exactly the review sweep's own filter, the one the status page counts as +; `reviewable`: the dispatch started, it finished, its review_state is NULL or 'wait', it was not +; reaped, and its issue is still open (in_review_queue); +; 2. while that queue is longer than REVIEW_QUEUE_MAX (0: not while anything waits) a candidate from +; the backlog is refused, and the round says why (admission, R_REVIEW_FIRST); +; 3. work the review itself asked for is never backlog: an issue the review sent back is dispatched +; again at any queue length (KIND_REWORK), and so is an issue the owner assigned by hand +; (KIND_ASSIGNED, the supervisor's pendingAssigns); +; 4. the queue empties on its own: every outcome of the review that is not an accept has a clock +; (review_valve.t27), so a closed backlog always reopens without a person. +; The host counts the queue and names each candidate's kind; this file decides. Not here: the wiring into +; trios (queen-tick.ts candidate filter, vendored wasm and t27c gen-ts constants), how keys are shared +; between bees and the reviewer, the review verdicts themselves. ASCII only (L3). +; phi^2 + 1/phi^2 = 3 | TRINITY + +module QueenReviewFirst; + +; --- 1. What a candidate is --------------------------------------------------------------------------- + +/// KIND_BACKLOG: an open issue with no dispatch the review is working on. KIND_REWORK: an issue whose +/// last dispatch the review sent back (review_state 'sendBack'). KIND_ASSIGNED: an issue the owner +/// assigned by hand. The codes are mirrored by number: a new kind takes the next code, none moves. +pub const KIND_BACKLOG : u8 = 0; +pub const KIND_REWORK : u8 = 1; +pub const KIND_ASSIGNED : u8 = 2; + +/// The owner's hand wins over the review's send-back, and both win over the backlog. +pub fn kind_of(assigned: bool, sent_back: bool) -> u8 { + if (assigned) { return KIND_ASSIGNED; } + if (sent_back) { return KIND_REWORK; } + return KIND_BACKLOG; +} + +; --- 2. What the review queue is ---------------------------------------------------------------------- + +/// A dispatch row's review_state as the host reads it: RS_NONE is NULL (no verdict yet), RS_WAIT is +/// 'wait' (fewer verdicts than criteria, dispatch.t27 REVIEW_RULE), RS_OTHER is any written verdict. +pub const RS_NONE : u8 = 0; +pub const RS_WAIT : u8 = 1; +pub const RS_OTHER : u8 = 2; + +/// Whether one dispatch row is in the review queue: the sweep's own filter (trios queen-tick.ts +/// reviewFinishedDispatches, counted as `reviewable` by queen-public-status.ts). A row whose issue was +/// closed is never reviewed, so it never holds the backlog shut. +pub fn in_review_queue(started: bool, finished: bool, state: u8, reaped: bool, issue_open: bool) -> bool { + if (started == false) { return false; } + if (finished == false) { return false; } + if (reaped) { return false; } + if (issue_open == false) { return false; } + if (state == RS_NONE) { return true; } + return state == RS_WAIT; +} + +; --- 3. Admission ------------------------------------------------------------------------------------- + +/// How many dispatches may wait in review while the backlog stays open. Zero, the owner's words: not +/// while anything is in review. +pub const REVIEW_QUEUE_MAX : u32 = 0; + +/// R_ADMIT: the candidate may be chosen. R_REVIEW_FIRST: refused, the backlog is closed while the review +/// queue is longer than REVIEW_QUEUE_MAX. +pub const R_ADMIT : u8 = 0; +pub const R_REVIEW_FIRST : u8 = 1; + +/// The refusal as the round reports it, after the queue length: "29 dispatches wait in review; ...". +pub const REFUSAL_TAIL : str = " dispatches wait in review; the backlog is closed until the review queue is empty"; + +pub fn backlog_open(review_queue: u32) -> bool { + return review_queue <= REVIEW_QUEUE_MAX; +} + +pub fn admission(kind: u8, review_queue: u32) -> u8 { + if (kind == KIND_REWORK) { return R_ADMIT; } + if (kind == KIND_ASSIGNED) { return R_ADMIT; } + if (backlog_open(review_queue)) { return R_ADMIT; } + return R_REVIEW_FIRST; +} + +pub fn admits(kind: u8, review_queue: u32) -> bool { + return admission(kind, review_queue) == R_ADMIT; +} + +/// Whether a round that admitted no candidate owes the review-first sentence: some candidate was +/// refused by this rule and none was admitted. +pub fn round_refused(admitted: u32, refused: u32) -> bool { + if (admitted > 0) { return false; } + return refused > 0; +} + +; --- 4. Tests ----------------------------------------------------------------------------------------- + +test the_owner_hand_wins_then_the_review_then_the_backlog { + assert kind_of(false, false) == 0; + assert kind_of(false, true) == 1; + assert kind_of(true, false) == 2; + assert kind_of(true, true) == 2; +} + +test the_queue_is_the_sweeps_own_filter { + assert in_review_queue(true, true, RS_NONE, false, true) == true; + assert in_review_queue(true, true, RS_WAIT, false, true) == true; + assert in_review_queue(true, true, RS_OTHER, false, true) == false; + assert in_review_queue(true, false, RS_NONE, false, true) == false; + assert in_review_queue(false, true, RS_NONE, false, true) == false; + assert in_review_queue(true, true, RS_NONE, true, true) == false; + assert in_review_queue(true, true, RS_NONE, false, false) == false; + assert in_review_queue(true, true, RS_WAIT, true, true) == false; + assert in_review_queue(true, true, 7, false, true) == false; +} + +test the_backlog_is_closed_while_anything_waits_in_review { + assert admission(KIND_BACKLOG, 0) == 0; + assert admission(KIND_BACKLOG, 1) == 1; + assert admission(KIND_BACKLOG, 29) == 1; + assert admission(KIND_BACKLOG, 4294967295) == 1; + assert backlog_open(0) == true; + assert backlog_open(1) == false; + assert admits(KIND_BACKLOG, 0) == true; + assert admits(KIND_BACKLOG, 1) == false; +} + +test work_the_review_asked_for_is_never_backlog { + assert admission(KIND_REWORK, 0) == 0; + assert admission(KIND_REWORK, 1) == 0; + assert admission(KIND_REWORK, 29) == 0; + assert admission(KIND_REWORK, 4294967295) == 0; + assert admits(KIND_REWORK, 362) == true; +} + +test the_owners_assignment_is_never_held { + assert admission(KIND_ASSIGNED, 0) == 0; + assert admission(KIND_ASSIGNED, 29) == 0; + assert admits(KIND_ASSIGNED, 4294967295) == true; +} + +test an_unknown_kind_is_backlog_and_is_held { + assert admission(3, 0) == 0; + assert admission(3, 1) == 1; + assert admission(255, 29) == 1; +} + +test a_round_reports_the_rule_only_when_it_stopped_everything { + assert round_refused(0, 0) == false; + assert round_refused(0, 5) == true; + assert round_refused(1, 5) == false; + assert round_refused(3, 0) == false; +} + +test the_measured_round_of_2026_10_18 { + assert admits(kind_of(false, false), 29) == false; + assert admits(kind_of(false, true), 29) == true; + assert admits(kind_of(true, false), 29) == true; + assert admits(kind_of(false, false), 0) == true; +} + +; --- 5. Invariants ------------------------------------------------------------------------------------ + +invariant the_codes_never_move { + assert KIND_BACKLOG == 0 and KIND_REWORK == 1 and KIND_ASSIGNED == 2; + assert RS_NONE == 0 and RS_WAIT == 1 and RS_OTHER == 2; + assert R_ADMIT == 0 and R_REVIEW_FIRST == 1; +} + +/// The owner's bound: nothing may wait in review while the backlog is open. +invariant the_bound_is_the_owners { + assert REVIEW_QUEUE_MAX == 0; +} diff --git a/apps/website/public/t27/files/specs/queen/reviewer.t27 b/apps/website/public/t27/files/specs/queen/reviewer.t27 new file mode 100644 index 0000000000..dcc617112b --- /dev/null +++ b/apps/website/public/t27/files/specs/queen/reviewer.t27 @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/queen/reviewer.t27 -- the Queen's reviewer runs on its own clock, several reviews at a time +; Owner, 2026-10-08 (translated): "do point 3 -- a separate reviewer". Measured that afternoon on the +; live Queen (gHashTag/BrowserOS trios/agent-server): +; - bees finished 47 tasks in an hour and the Queen reviewed 23, so finished work queued up (91); +; - the review sweep ran INSIDE the round, before it handed out work: at most 3 reviews, one after +; another, and at most 45 s of wall clock (0.75 of a 60 s tick), each review a model call of up to +; 2 minutes -- in practice about one review a round; +; - every finished task waiting for its verdict held its files (holds.t27 now lets go after 30 min). +; The reviewer moves out of the round: its own clock, up to REVIEWER_CONCURRENCY rows at once, each +; on its own lane, one row never twice at once, and every waiting row visited in turn. The round +; then reviews nothing: two sweeps over one row would race on its verdict. +; ASCII only (L3). +; phi^2 + 1/phi^2 = 3 | TRINITY + +module QueenReviewer; + +/// How often the reviewer looks for rows to take: often, because a pass that finds every slot busy +/// costs one query. +pub const REVIEWER_EVERY_SECONDS : u32 = 10; + +/// Rows reviewed at once by a host that has not measured its lanes and memory: the fixed bound the +/// reviewer started with. Each row takes its own lane (a key no bee and no other review holds). +/// A host that knows its keys, lanes and free memory uses `reviewer_concurrency` instead; this stays +/// as the bound behind `review_slots` so callers that pass no measurement keep their behaviour. +pub const REVIEWER_CONCURRENCY : u32 = 4; + +/// The most rows reviewed at once, whatever the lanes and memory allow: four times the fixed 4, and +/// three times the 5 the measured load of 2026-10-09 needs (see `slots_needed`). +pub const REVIEWER_CEILING : u32 = 16; + +/// Seconds in an hour; finish rates are measured per hour. +pub const SECONDS_PER_HOUR : u32 = 3600; + +/// One row's wall clock: the witness, the criteria measurement and the model call. It no longer +/// shares a 45 s budget with the round, because it no longer holds the round up. +pub const REVIEW_ROW_SECONDS : u32 = 300; + +/// Measured 2026-10-09 on the live Queen (`/queen/status`, `/queen/public-activity`): about 58 +/// dispatches finished an hour against about 11 reviewed, with 111 rows waiting. At REVIEW_ROW_SECONDS +/// a slot reviews at most 3600 / 300 = 12 rows an hour, so the fixed 4 tops out at 48 an hour: below +/// the finish rate even if every review used its whole budget, and the backlog only grows. +/// How many rows the reviewer may take at once, from what the host measures: +/// - `keys` model keys, each with `lanes_per_key` lanes free for review; +/// - `free_mb` megabytes free, each review costing `mb_per_review` (0 = not measured: one review); +/// - never above REVIEWER_CEILING, never above the rows `waiting`; +/// - 0 only when nothing waits, and at least 1 while anything waits, so the backlog always drains. +pub fn reviewer_concurrency(keys: u32, lanes_per_key: u32, free_mb: u32, mb_per_review: u32, waiting: u32) -> u32 { + if (waiting == 0) { return 0; } + var k : u32 = keys; + if (k > REVIEWER_CEILING) { k = REVIEWER_CEILING; } + var l : u32 = lanes_per_key; + if (l > REVIEWER_CEILING) { l = REVIEWER_CEILING; } + var bound : u32 = k * l; + if (bound > REVIEWER_CEILING) { bound = REVIEWER_CEILING; } + var by_memory : u32 = 1; + if (mb_per_review > 0) { by_memory = free_mb / mb_per_review; } + if (by_memory < bound) { bound = by_memory; } + if (waiting < bound) { bound = waiting; } + if (bound < 1) { return 1; } + return bound; +} + +/// Rows at once needed so the reviewer finishes at least `finished_per_hour` rows an hour when every +/// row uses its whole REVIEW_ROW_SECONDS: ceil(finished_per_hour * REVIEW_ROW_SECONDS / 3600). +/// Split by the hour so no u32 product overflows. +pub fn slots_needed(finished_per_hour: u32) -> u32 { + let whole : u32 = finished_per_hour / SECONDS_PER_HOUR; + let part : u32 = finished_per_hour % SECONDS_PER_HOUR; + let part_seconds : u32 = part * REVIEW_ROW_SECONDS; + var extra : u32 = part_seconds / SECONDS_PER_HOUR; + if (part_seconds % SECONDS_PER_HOUR > 0) { extra = extra + 1; } + return whole * REVIEW_ROW_SECONDS + extra; +} + +/// Whether `slots` reviews at once keep up with `finished_per_hour` finished rows. +pub fn keeps_up(slots: u32, finished_per_hour: u32) -> bool { + return slots >= slots_needed(finished_per_hour); +} + +/// How many rows a pass starts against a bound (normally `reviewer_concurrency`): the free slots, but +/// no more than the rows waiting. +pub fn review_slots_within(bound: u32, in_flight: u32, waiting: u32) -> u32 { + if (in_flight >= bound) { return 0; } + let room : u32 = bound - in_flight; + if (waiting < room) { return waiting; } + return room; +} + +/// How many rows a pass starts on a host that measured nothing: the same rule against the fixed +/// REVIEWER_CONCURRENCY, unchanged for its existing callers. +pub fn review_slots(in_flight: u32, waiting: u32) -> u32 { + return review_slots_within(REVIEWER_CONCURRENCY, in_flight, waiting); +} + +/// The order rows are taken in: the one this reviewer visited longest ago first, a row it never +/// visited before any it has. Without it, a row that is re-read and waits again (no review spent) +/// keeps its place at the head and the reviewer circles the same few rows. +pub fn visit_first(never_a: bool, seconds_since_a: u32, never_b: bool, seconds_since_b: u32) -> bool { + if (never_a and never_b == false) { return true; } + if (never_b and never_a == false) { return false; } + return seconds_since_a > seconds_since_b; +} + +/// While the reviewer runs, the round reviews nothing. +pub fn round_reviews(reviewer_running: bool) -> bool { + return reviewer_running == false; +} + +/// A row that answered `wait` is not visited again at once. Measured 2026-10-09 on the live Queen, +/// over 44 minutes: 941 row visits, 888 of them `wait`, from just 22 rows whose branch the Queen could +/// not read. One row was visited 54 times. Only 53 visits decided anything. Nothing about such a row +/// changes in 50 s, and the frozen-wait valve releases it only after six hours, so each one burned +/// about 430 visits of the reviewer's slots. +/// After n waits in a row, the next visit waits WAIT_REVISIT_BASE_SECONDS doubled n - 1 times, never +/// more than WAIT_REVISIT_CAP_SECONDS. Any answer other than `wait` resets the count, so a row that +/// has started to read is visited at the normal pace again. +pub const WAIT_REVISIT_BASE_SECONDS : u32 = 30; +pub const WAIT_REVISIT_CAP_SECONDS : u32 = 900; + +pub fn revisit_after_seconds(waits: u32) -> u32 { + if (waits == 0) { return 0; } + var wait : u32 = WAIT_REVISIT_BASE_SECONDS; + var n : u32 = 1; + while (n < waits) { + if (wait >= WAIT_REVISIT_CAP_SECONDS) { return WAIT_REVISIT_CAP_SECONDS; } + wait = wait * 2; + n = n + 1; + } + if (wait > WAIT_REVISIT_CAP_SECONDS) { return WAIT_REVISIT_CAP_SECONDS; } + return wait; +} + +/// Whether a row may be taken now: one that never waited always may, and one that waited only after +/// its revisit time has passed since the last visit. +pub fn due_again(waits: u32, seconds_since_visit: u32) -> bool { + return seconds_since_visit >= revisit_after_seconds(waits); +} + +/// A RUNNER BEE THAT COMMITTED NOTHING LEAVES NOTHING TO READ (gHashTag/t27#9069). Measured on the +/// live Queen, 07:10-09:43Z on 2026-10-11: 425 of the reviewer's 480 row visits answered `wait` +/// because the diff of queen-N could not be read. A runner stores its bee's branch as a bundle only +/// when the bee committed something, so a bee that committed nothing leaves no branch here, the +/// diff fails, and the row waits for the six-hour frozen-wait valve while its issue stays claimed. +/// A container bee that commits nothing is judged an empty attempt at once, and its issue is free +/// again after the retry floor. +/// A runner stores the bundle after the turn has ended, so a row may be visited before its bundle +/// is there. Over 07:10-10:31Z, 15 runner rows whose diff failed got their bundle on a later visit, +/// the latest 58 s after the first. So a finished runner row with no stored bundle is an empty +/// attempt only once NO_BUNDLE_GRACE_MINUTES have passed since it finished; before that it waits, +/// and a bundle that arrives meanwhile is judged on its branch. A container row (`on_runner` +/// false) and a row whose bundle is stored are never decided here: their diff failed for some +/// other reason, and that still decides nothing. +pub const NO_BUNDLE_GRACE_MINUTES : u32 = 10; + +pub fn no_bundle_is_empty(on_runner: bool, bundle_stored: bool, minutes_since_finished: u32) -> bool { + if (on_runner == false) { return false; } + if (bundle_stored) { return false; } + return minutes_since_finished >= NO_BUNDLE_GRACE_MINUTES; +} + +test a_pass_fills_the_free_slots_with_waiting_rows { + assert review_slots(0, 0) == 0; + assert review_slots(0, 2) == 2; + assert review_slots(0, 100) == REVIEWER_CONCURRENCY; + assert review_slots(1, 100) == REVIEWER_CONCURRENCY - 1; + assert review_slots(REVIEWER_CONCURRENCY - 1, 1) == 1; + assert review_slots(REVIEWER_CONCURRENCY, 100) == 0; + assert review_slots(REVIEWER_CONCURRENCY + 1, 100) == 0; +} + +test the_bound_follows_lanes_memory_and_backlog { + // nothing waits: no slot, whatever the host has + assert reviewer_concurrency(10, 10, 100000, 100, 0) == 0; + // lanes bound: 2 keys x 3 lanes + assert reviewer_concurrency(2, 3, 100000, 100, 111) == 6; + // memory bound: 1000 MB at 300 MB a review + assert reviewer_concurrency(10, 10, 1000, 300, 111) == 3; + // the hard ceiling: 16, though 10 x 10 lanes and 1000 reviews' worth of memory are free + assert reviewer_concurrency(10, 10, 100000, 100, 111) == 16; + assert reviewer_concurrency(4294967295, 4294967295, 4294967295, 1, 4294967295) == REVIEWER_CEILING; + // never above the backlog + assert reviewer_concurrency(10, 10, 100000, 100, 2) == 2; + // at least one while anything waits, even with no lane or memory measured + assert reviewer_concurrency(0, 0, 0, 0, 1) == 1; + assert reviewer_concurrency(10, 10, 100, 300, 111) == 1; + assert reviewer_concurrency(10, 10, 100000, 0, 111) == 1; + assert reviewer_concurrency(10, 0, 100000, 100, 111) == 1; +} + +test the_measured_load_needs_five_and_the_bound_admits_them { + // 58 finished an hour at 300 s a row: ceil(58 * 300 / 3600) = ceil(4.83) = 5 + assert slots_needed(58) == 5; + assert slots_needed(0) == 0; + assert slots_needed(12) == 1; + assert slots_needed(13) == 2; + assert slots_needed(3600) == REVIEW_ROW_SECONDS; + assert slots_needed(3601) == REVIEW_ROW_SECONDS + 1; + assert slots_needed(4294967295) == 357913942; + // the fixed 4 cannot keep up; 70 lanes (measured 2026-10-09) and 8 GB at 512 MB a review can + assert keeps_up(REVIEWER_CONCURRENCY, 58) == false; + assert keeps_up(reviewer_concurrency(7, 10, 8192, 512, 111), 58) == true; + assert reviewer_concurrency(7, 10, 8192, 512, 111) == 16; + assert keeps_up(4, 48) == true; + assert keeps_up(4, 49) == false; +} + +test a_pass_fills_the_free_slots_against_the_computed_bound { + let bound : u32 = reviewer_concurrency(7, 10, 8192, 512, 111); + assert review_slots_within(bound, 0, 111) == 16; + assert review_slots_within(bound, 11, 111) == 5; + assert review_slots_within(bound, 15, 3) == 1; + assert review_slots_within(bound, 16, 111) == 0; + assert review_slots_within(bound, 17, 111) == 0; + assert review_slots_within(bound, 0, 3) == 3; + assert review_slots_within(0, 0, 111) == 0; +} + +test every_row_is_visited_in_turn { + // never visited goes first + assert visit_first(true, 0, false, 9999) == true; + assert visit_first(false, 9999, true, 0) == false; + // otherwise the one visited longest ago + assert visit_first(false, 300, false, 20) == true; + assert visit_first(false, 20, false, 300) == false; + assert visit_first(false, 20, false, 20) == false; + assert visit_first(true, 0, true, 0) == false; +} + +test the_round_stands_aside_for_the_reviewer { + assert round_reviews(true) == false; + assert round_reviews(false) == true; +} + +test a_row_that_keeps_waiting_is_visited_less_and_less { + assert revisit_after_seconds(0) == 0; + assert revisit_after_seconds(1) == 30; + assert revisit_after_seconds(2) == 60; + assert revisit_after_seconds(3) == 120; + assert revisit_after_seconds(5) == 480; + assert revisit_after_seconds(6) == WAIT_REVISIT_CAP_SECONDS; + assert revisit_after_seconds(50) == WAIT_REVISIT_CAP_SECONDS; + assert revisit_after_seconds(4294967295) == WAIT_REVISIT_CAP_SECONDS; + // a row that never waited is always due; one that waited is due only once its time has passed + assert due_again(0, 0) == true; + assert due_again(1, 29) == false; + assert due_again(1, 30) == true; + assert due_again(6, 899) == false; + assert due_again(6, 900) == true; +} + +test no_bundle_is_empty_for_a_runner_row_past_the_grace { + // a runner row with no stored bundle, the grace spent: an empty attempt, not a wait + assert no_bundle_is_empty(true, false, NO_BUNDLE_GRACE_MINUTES) == true; + assert no_bundle_is_empty(true, false, NO_BUNDLE_GRACE_MINUTES + 1) == true; + // the six hours the frozen-wait valve would otherwise have held it + assert no_bundle_is_empty(true, false, 360) == true; + assert no_bundle_is_empty(true, false, 4294967295) == true; +} + +test no_bundle_is_empty_waits_inside_the_grace { + // just finished: the runner may not have stored its bundle yet + assert no_bundle_is_empty(true, false, 0) == false; + // a bundle 40 s late is still awaited, and judged on its branch when it arrives + assert no_bundle_is_empty(true, false, 1) == false; + assert no_bundle_is_empty(true, false, NO_BUNDLE_GRACE_MINUTES - 1) == false; +} + +test no_bundle_is_empty_leaves_a_container_row_alone { + // a bee that ran here has no bundle by design; its diff failing decides nothing + assert no_bundle_is_empty(false, false, 0) == false; + assert no_bundle_is_empty(false, false, NO_BUNDLE_GRACE_MINUTES) == false; + assert no_bundle_is_empty(false, false, 4294967295) == false; + assert no_bundle_is_empty(false, true, 4294967295) == false; +} + +test no_bundle_is_empty_leaves_a_stored_bundle_alone { + // the bundle is there, so the diff failed for another reason: still a wait + assert no_bundle_is_empty(true, true, 0) == false; + assert no_bundle_is_empty(true, true, NO_BUNDLE_GRACE_MINUTES) == false; + assert no_bundle_is_empty(true, true, 4294967295) == false; +} + +invariant a_runner_row_with_no_bundle_is_judged_inside_the_hour { + // the latest late bundle measured on 2026-10-11 came 58 s after the row's first visit + assert NO_BUNDLE_GRACE_MINUTES > 1; + assert no_bundle_is_empty(true, false, 1) == false; + // a row that keeps waiting is visited at least every WAIT_REVISIT_CAP_SECONDS, so the visit + // that judges it comes within the hour of its finish, not after the six-hour valve + assert NO_BUNDLE_GRACE_MINUTES * 60 + WAIT_REVISIT_CAP_SECONDS <= SECONDS_PER_HOUR; +} + +invariant the_reviewer_is_quicker_than_the_round { + assert REVIEWER_EVERY_SECONDS < 60; + assert REVIEWER_CONCURRENCY > 1; + assert REVIEW_ROW_SECONDS >= 120; + // a stuck row is still seen several times an hour, so one that starts to read is not lost for long + assert WAIT_REVISIT_CAP_SECONDS <= 3600; + assert WAIT_REVISIT_BASE_SECONDS >= REVIEWER_EVERY_SECONDS; +} + +invariant the_reviewer_can_outrun_the_measured_finish_rate { + // FR-002: at 58 finished an hour and REVIEW_ROW_SECONDS, a host with the lanes and memory gets + // at least the slots that load needs, and the ceiling is not what stops it + assert keeps_up(reviewer_concurrency(REVIEWER_CEILING, REVIEWER_CEILING, 4294967295, 1, 111), 58); + assert REVIEWER_CEILING >= slots_needed(58); + assert REVIEWER_CEILING >= REVIEWER_CONCURRENCY; + // the fixed bound alone could not, which is why it is only the fallback + assert keeps_up(REVIEWER_CONCURRENCY, 58) == false; + // one hour holds a whole number of review budgets, so slots_needed is exact at whole hours + assert SECONDS_PER_HOUR % REVIEW_ROW_SECONDS == 0; + // FR-001: 0 only when nothing waits + assert reviewer_concurrency(0, 0, 0, 0, 0) == 0; + assert reviewer_concurrency(0, 0, 0, 0, 1) >= 1; +} diff --git a/apps/website/public/widgets/gallery.t27 b/apps/website/public/widgets/gallery.t27 index 17117ac2a9..fb07268ccf 100644 --- a/apps/website/public/widgets/gallery.t27 +++ b/apps/website/public/widgets/gallery.t27 @@ -78,15 +78,15 @@ pub const TOOL_NOSCRIPT : str = "This widget runs in your browser and needs Java ; Parallel arrays, WIDGET_COUNT long. WIDGET_TITLES is empty wherever the page carries an ; og:title, which is every kind but tab: the page is the one place the words live. A tool's ; WIDGET_HOOKS entry is empty too: its hook is the page's t27:hook, written from its own spec. -pub const WIDGET_COUNT : u16 = 279; -pub const WIDGET_IDS : [279]str = ["x7-board", "tri-x7-fasm", "tri-x7-mutate", "perf2-loop", "tri-fpga-rgmii", "tri-fpga-jtag", "tri-fpga-pins", "devkit-flow", "play-verilog", "bit-diff", "build-receipt", "cost-curves", "gf16-calc", "idcode", "lut-explain", "pin-map", "placement-map", "resource-bars", "slack-waterfall", "test-waves", "synth-smells", "bit-autopsy", "fasm-skyline", "vcd-wrapped", "fits-on", "stat-card", "lut-depth", "fsm-sketch", "lut-treemap", "t27-cell", "t27-vs-nvfp4", "uart-bucket", "tri-fpga-specs", "tri-fpga-tools", "tri-fpga-wire", "tri-fpga-batch", "tri-fpga-status", "tri-fpga-tmpcheck", "tri-fpga-keycheck", "tri-fpga-selftest", "tri-x7-refs", "tri-x7-audit", "t27c-clock-domain", "t27c-clocks", "t27c-wukong-v1", "t27c-cts", "t27c-mmcm", "t27c-mmcm-mutate", "t27c-timing", "t27c-reset-sync", "t27c-reset-sync-gen", "t27c-mtbf", "t27c-clock-domain-gen", "t27c-fifo", "t27c-fifo-mutate", "t27c-fifo-flags", "t27c-emitter-xdc", "t27c-fpga-rxcap", "t27-tri-competitors", "t27-tri-discard", "tri-fpga-txhold", "tri-fpga-rxcap", "tri-fpga-rxer", "tri-fpga-steps", "tri-fpga-ioclients", "tri-fpga-claim", "tri-fpga-release", "tri-fpga-next", "t27c-memory", "t27c-spi-tb", "t27c-check-uart", "t27c-check-spi", "t27c-check-apb", "t27c-check-axi4", "t27c-check-bridge", "t27c-check-memory", "t27c-verilog-uart", "t27c-verilog-spi", "t27c-verilog-apb", "t27c-verilog-axi4", "t27c-verilog-bridge", "t27c-verilog-top", "t27c-verilog-uart-tb", "t27c-verilog-apb-tb", "t27c-verilog-axi-tb", "t27c-verilog-mem-tb", "t27c-verilog-integration-tb", "t27c-verilog-top-tb", "t27c-verilog-spi-tb", "t27c-hir-uart", "t27c-hir-apb", "t27c-hir-axi4", "t27c-eth-crc", "t27c-rgmii", "jtag-verdict", "line-bounce", "eye-isi", "ecc-secded", "carry-chain", "prefix-adder", "csa-tree", "shift-add", "booth-radix4", "dsp-fit", "q-format", "round-sat", "sqnr", "cordic", "cordic-vector", "sine-table", "alias-fold", "nco", "dft-bin", "fir-response", "fir-design", "fir-cost", "decimator", "cic-growth", "polyphase", "fft-butterfly", "bit-reverse", "fft-scaling", "barker-corr", "fir-fit", "fir-on-board", "play-hello-world", "play-rust", "play-js", "t27c-hello-world", "t27-tri-misread", "test-touch", "t27c-stages", "t27c-e8m0", "play-e8m0", "t27c-ocp-mx", "play-gfternary", "t27c-gfternary", "play-golden-sieve", "t27c-golden-sieve", "play-tnf17", "t27c-tnf17", "t27c-activation-quantizer", "t27c-bitnet-majority", "t27c-bitnet-neuron-nchunk", "t27c-comb-ternary-dot", "t27c-stream-ternary-mac", "t27c-bitnet-mlp", "t27c-gft-smul", "t27c-gft-sadd", "t27c-gft-signed-mac", "t27c-gft-relu", "t27c-gft-exp2", "t27c-gft-argmax4", "t27c-gft-nll", "t27c-gft-sgd-step", "t27c-gft-xornet", "basics-what-a-spec-is", "basics-the-module-line", "basics-comments-and-prose", "basics-constants", "basics-whole-numbers", "basics-true-false-and-text", "basics-arrays", "basics-trits", "basics-expressions", "basics-a-test-block", "basics-many-tests", "basics-functions", "basics-local-names", "basics-if-and-else", "basics-loops", "basics-pub-or-private", "basics-structs", "basics-enums", "basics-use-other-modules", "basics-invariants", "basics-gen-ts", "basics-seven-backends", "basics-stages-of-t27c", "basics-tri-commands", "basics-reading-errors", "basics-a-small-program", "basics-where-next", "golden-split", "golden-phi-ratio", "golden-lucas", "golden-split-optimality", "golden-radix-economy", "golden-phi-in-gf16", "golden-gf4", "golden-gf6", "golden-gf8", "golden-gf10", "golden-gf12", "golden-gf14", "golden-gf16", "golden-dot-two", "golden-gf24", "golden-gf32", "golden-gf48", "golden-gf64", "golden-gf96", "golden-gf128", "golden-gf256", "golden-gf512", "golden-gf1024", "golden-gft8", "golden-gft16", "golden-gft32", "golden-decode", "t27-tri-vectors", "t27-tri-census", "t27-tri-seals", "t27-tri-prose", "t27-tri-lean", "tri-selftest", "play-terminal-gates", "tc-why-three", "tc-balanced-count", "tc-flip-and-cut", "tc-kleene-gates", "tc-consensus-any", "tc-sum-and-carry", "tc-half-adder", "tc-full-adder", "tc-ripple-carry", "tc-times-a-trit", "tc-long-multiply", "tc-ternary-mac", "tc-two-bits", "tc-five-in-a-byte", "tc-trit-word", "tc-instruction-word", "tc-opcode-map", "tc-immediate", "tc-ternary-alu", "tc-three-way-jump", "tc-run-a-program", "tc-compare", "tc-thirds", "tc-sort-three", "tc-one-neuron", "tc-detector", "tc-tiny-layer", "nn-relu", "nn-sigmoid", "nn-tanh", "nn-gelu", "nn-silu", "nn-elu", "nn-softmax", "nn-embedding", "nn-dropout", "nn-conv", "nn-maxpool", "nn-batchnorm", "nn-mse", "nn-bce", "nn-huber", "nn-sgd", "nn-rmsprop", "nn-adam", "nn-backprop", "nn-xor", "nn-lamb", "nn-rnn", "nn-lstm", "nn-attention", "nn-positions", "nn-encoder", "nn-ppo", "wars", "tri-game-tick", "tri-game-vault", "perf2-knockouts", "race-the-bee", "gatle", "tri-blog-list", "tri-game-selftest"]; -pub const WIDGET_KINDS : [279]str = ["cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "player", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "player", "player", "player", "cast", "cast", "tool", "tool", "cast", "player", "cast", "player", "cast", "player", "cast", "player", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tab", "cast", "cast", "cast", "tool", "tool", "cast", "cast"]; -pub const WIDGET_CATEGORIES : [279]str = ["fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "game", "game", "game", "game", "game", "game", "game", "game"]; -pub const WIDGET_PAGES : [279]str = ["term/x7-board/", "term/tri-x7-fasm/", "term/tri-x7-mutate/", "term/perf2-loop/", "term/tri-fpga-rgmii/", "term/tri-fpga-jtag/", "term/tri-fpga-pins/", "term/devkit-flow/", "play/hello-world/verilog/", "widgets/bit-diff/", "widgets/build-receipt/", "widgets/cost-curves/", "widgets/gf16-calc/", "widgets/idcode/", "widgets/lut-explain/", "widgets/pin-map/", "widgets/placement-map/", "widgets/resource-bars/", "widgets/slack-waterfall/", "widgets/test-waves/", "widgets/synth-smells/", "widgets/bit-autopsy/", "widgets/fasm-skyline/", "widgets/vcd-wrapped/", "widgets/fits-on/", "widgets/stat-card/", "widgets/lut-depth/", "widgets/fsm-sketch/", "widgets/lut-treemap/", "widgets/t27-cell/", "widgets/t27-vs-nvfp4/", "widgets/uart-bucket/", "term/tri-fpga-specs/", "term/tri-fpga-tools/", "term/tri-fpga-wire/", "term/tri-fpga-batch/", "term/tri-fpga-status/", "term/tri-fpga-tmpcheck/", "term/tri-fpga-keycheck/", "term/tri-fpga-selftest/", "term/tri-x7-refs/", "term/tri-x7-audit/", "term/t27c-clock-domain/", "term/t27c-clocks/", "term/t27c-wukong-v1/", "term/t27c-cts/", "term/t27c-mmcm/", "term/t27c-mmcm-mutate/", "term/t27c-timing/", "term/t27c-reset-sync/", "term/t27c-reset-sync-gen/", "term/t27c-mtbf/", "term/t27c-clock-domain-gen/", "term/t27c-fifo/", "term/t27c-fifo-mutate/", "term/t27c-fifo-flags/", "term/t27c-emitter-xdc/", "term/t27c-fpga-rxcap/", "term/t27-tri-competitors/", "term/t27-tri-discard/", "term/tri-fpga-txhold/", "term/tri-fpga-rxcap/", "term/tri-fpga-rxer/", "term/tri-fpga-steps/", "term/tri-fpga-ioclients/", "term/tri-fpga-claim/", "term/tri-fpga-release/", "term/tri-fpga-next/", "term/t27c-memory/", "term/t27c-spi-tb/", "term/t27c-check-uart/", "term/t27c-check-spi/", "term/t27c-check-apb/", "term/t27c-check-axi4/", "term/t27c-check-bridge/", "term/t27c-check-memory/", "term/t27c-verilog-uart/", "term/t27c-verilog-spi/", "term/t27c-verilog-apb/", "term/t27c-verilog-axi4/", "term/t27c-verilog-bridge/", "term/t27c-verilog-top/", "term/t27c-verilog-uart-tb/", "term/t27c-verilog-apb-tb/", "term/t27c-verilog-axi-tb/", "term/t27c-verilog-mem-tb/", "term/t27c-verilog-integration-tb/", "term/t27c-verilog-top-tb/", "term/t27c-verilog-spi-tb/", "term/t27c-hir-uart/", "term/t27c-hir-apb/", "term/t27c-hir-axi4/", "term/t27c-eth-crc/", "term/t27c-rgmii/", "widgets/jtag-verdict/", "widgets/line-bounce/", "widgets/eye-isi/", "widgets/ecc-secded/", "widgets/carry-chain/", "widgets/prefix-adder/", "widgets/csa-tree/", "widgets/shift-add/", "widgets/booth-radix4/", "widgets/dsp-fit/", "widgets/q-format/", "widgets/round-sat/", "widgets/sqnr/", "widgets/cordic/", "widgets/cordic-vector/", "widgets/sine-table/", "widgets/alias-fold/", "widgets/nco/", "widgets/dft-bin/", "widgets/fir-response/", "widgets/fir-design/", "widgets/fir-cost/", "widgets/decimator/", "widgets/cic-growth/", "widgets/polyphase/", "widgets/fft-butterfly/", "widgets/bit-reverse/", "widgets/fft-scaling/", "widgets/barker-corr/", "widgets/fir-fit/", "widgets/fir-on-board/", "play/hello-world/", "play/hello-world/rust/", "play/hello-world/js/", "term/t27c-hello-world/", "term/t27-tri-misread/", "widgets/test-touch/", "widgets/t27c-stages/", "term/t27c-e8m0/", "play/e8m0/", "term/t27c-ocp-mx/", "play/gfternary/", "term/t27c-gfternary/", "play/golden-sieve/", "term/t27c-golden-sieve/", "play/tnf17/", "term/t27c-tnf17/", "term/t27c-activation-quantizer/", "term/t27c-bitnet-majority/", "term/t27c-bitnet-neuron-nchunk/", "term/t27c-comb-ternary-dot/", "term/t27c-stream-ternary-mac/", "term/t27c-bitnet-mlp/", "term/t27c-gft-smul/", "term/t27c-gft-sadd/", "term/t27c-gft-signed-mac/", "term/t27c-gft-relu/", "term/t27c-gft-exp2/", "term/t27c-gft-argmax4/", "term/t27c-gft-nll/", "term/t27c-gft-sgd-step/", "term/t27c-gft-xornet/", "widgets/basics-what-a-spec-is/", "widgets/basics-the-module-line/", "widgets/basics-comments-and-prose/", "widgets/basics-constants/", "widgets/basics-whole-numbers/", "widgets/basics-true-false-and-text/", "widgets/basics-arrays/", "widgets/basics-trits/", "widgets/basics-expressions/", "widgets/basics-a-test-block/", "widgets/basics-many-tests/", "widgets/basics-functions/", "widgets/basics-local-names/", "widgets/basics-if-and-else/", "widgets/basics-loops/", "widgets/basics-pub-or-private/", "widgets/basics-structs/", "widgets/basics-enums/", "widgets/basics-use-other-modules/", "widgets/basics-invariants/", "widgets/basics-gen-ts/", "widgets/basics-seven-backends/", "widgets/basics-stages-of-t27c/", "widgets/basics-tri-commands/", "widgets/basics-reading-errors/", "widgets/basics-a-small-program/", "widgets/basics-where-next/", "widgets/golden-split/", "widgets/golden-phi-ratio/", "widgets/golden-lucas/", "widgets/golden-split-optimality/", "widgets/golden-radix-economy/", "widgets/golden-phi-in-gf16/", "widgets/golden-gf4/", "widgets/golden-gf6/", "widgets/golden-gf8/", "widgets/golden-gf10/", "widgets/golden-gf12/", "widgets/golden-gf14/", "widgets/golden-gf16/", "widgets/golden-dot-two/", "widgets/golden-gf24/", "widgets/golden-gf32/", "widgets/golden-gf48/", "widgets/golden-gf64/", "widgets/golden-gf96/", "widgets/golden-gf128/", "widgets/golden-gf256/", "widgets/golden-gf512/", "widgets/golden-gf1024/", "widgets/golden-gft8/", "widgets/golden-gft16/", "widgets/golden-gft32/", "widgets/golden-decode/", "term/t27-tri-vectors/", "term/t27-tri-census/", "term/t27-tri-seals/", "term/t27-tri-prose/", "term/t27-tri-lean/", "term/tri-selftest/", "term/play-terminal-gates/", "widgets/tc-why-three/", "widgets/tc-balanced-count/", "widgets/tc-flip-and-cut/", "widgets/tc-kleene-gates/", "widgets/tc-consensus-any/", "widgets/tc-sum-and-carry/", "widgets/tc-half-adder/", "widgets/tc-full-adder/", "widgets/tc-ripple-carry/", "widgets/tc-times-a-trit/", "widgets/tc-long-multiply/", "widgets/tc-ternary-mac/", "widgets/tc-two-bits/", "widgets/tc-five-in-a-byte/", "widgets/tc-trit-word/", "widgets/tc-instruction-word/", "widgets/tc-opcode-map/", "widgets/tc-immediate/", "widgets/tc-ternary-alu/", "widgets/tc-three-way-jump/", "widgets/tc-run-a-program/", "widgets/tc-compare/", "widgets/tc-thirds/", "widgets/tc-sort-three/", "widgets/tc-one-neuron/", "widgets/tc-detector/", "widgets/tc-tiny-layer/", "widgets/nn-relu/", "widgets/nn-sigmoid/", "widgets/nn-tanh/", "widgets/nn-gelu/", "widgets/nn-silu/", "widgets/nn-elu/", "widgets/nn-softmax/", "widgets/nn-embedding/", "widgets/nn-dropout/", "widgets/nn-conv/", "widgets/nn-maxpool/", "widgets/nn-batchnorm/", "widgets/nn-mse/", "widgets/nn-bce/", "widgets/nn-huber/", "widgets/nn-sgd/", "widgets/nn-rmsprop/", "widgets/nn-adam/", "widgets/nn-backprop/", "widgets/nn-xor/", "widgets/nn-lamb/", "widgets/nn-rnn/", "widgets/nn-lstm/", "widgets/nn-attention/", "widgets/nn-positions/", "widgets/nn-encoder/", "widgets/nn-ppo/", "wars", "term/tri-game-tick/", "term/tri-game-vault/", "term/perf2-knockouts/", "widgets/race-the-bee/", "widgets/gatle/", "term/tri-blog-list/", "term/tri-game-selftest/"]; -pub const WIDGET_TITLES : [279]str = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "WARS: agent configurations compared on real GitHub issues", "", "", "", "", "", "", ""]; -pub const WIDGET_LINES : [279]str = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "One issue, one pinned commit, the same tools and gates; only the decision layer changes, and a missing run stays unknown.", "", "", "", "", "", "", ""]; +pub const WIDGET_COUNT : u16 = 306; +pub const WIDGET_IDS : [306]str = ["x7-board", "tri-x7-fasm", "tri-x7-mutate", "perf2-loop", "tri-fpga-rgmii", "tri-fpga-jtag", "tri-fpga-pins", "devkit-flow", "play-verilog", "bit-diff", "build-receipt", "cost-curves", "gf16-calc", "idcode", "lut-explain", "pin-map", "placement-map", "resource-bars", "slack-waterfall", "test-waves", "synth-smells", "bit-autopsy", "fasm-skyline", "vcd-wrapped", "fits-on", "stat-card", "lut-depth", "fsm-sketch", "lut-treemap", "t27-cell", "t27-vs-nvfp4", "uart-bucket", "tri-fpga-specs", "tri-fpga-tools", "tri-fpga-wire", "tri-fpga-batch", "tri-fpga-status", "tri-fpga-tmpcheck", "tri-fpga-keycheck", "tri-fpga-selftest", "tri-x7-refs", "tri-x7-audit", "t27c-clock-domain", "t27c-clocks", "t27c-wukong-v1", "t27c-cts", "t27c-mmcm", "t27c-mmcm-mutate", "t27c-timing", "t27c-reset-sync", "t27c-reset-sync-gen", "t27c-mtbf", "t27c-clock-domain-gen", "t27c-fifo", "t27c-fifo-mutate", "t27c-fifo-flags", "t27c-emitter-xdc", "t27c-fpga-rxcap", "t27-tri-competitors", "t27-tri-discard", "tri-fpga-txhold", "tri-fpga-rxcap", "tri-fpga-rxer", "tri-fpga-steps", "tri-fpga-ioclients", "tri-fpga-claim", "tri-fpga-release", "tri-fpga-next", "t27c-memory", "t27c-spi-tb", "t27c-check-uart", "t27c-check-spi", "t27c-check-apb", "t27c-check-axi4", "t27c-check-bridge", "t27c-check-memory", "t27c-verilog-uart", "t27c-verilog-spi", "t27c-verilog-apb", "t27c-verilog-axi4", "t27c-verilog-bridge", "t27c-verilog-top", "t27c-verilog-uart-tb", "t27c-verilog-apb-tb", "t27c-verilog-axi-tb", "t27c-verilog-mem-tb", "t27c-verilog-integration-tb", "t27c-verilog-top-tb", "t27c-verilog-spi-tb", "t27c-hir-uart", "t27c-hir-apb", "t27c-hir-axi4", "t27c-eth-crc", "t27c-rgmii", "jtag-verdict", "line-bounce", "eye-isi", "ecc-secded", "carry-chain", "prefix-adder", "csa-tree", "shift-add", "booth-radix4", "dsp-fit", "q-format", "round-sat", "sqnr", "cordic", "cordic-vector", "sine-table", "alias-fold", "nco", "dft-bin", "fir-response", "fir-design", "fir-cost", "decimator", "cic-growth", "polyphase", "fft-butterfly", "bit-reverse", "fft-scaling", "barker-corr", "fir-fit", "fir-on-board", "play-hello-world", "play-rust", "play-js", "t27c-hello-world", "t27-tri-misread", "test-touch", "t27c-stages", "t27c-e8m0", "play-e8m0", "t27c-ocp-mx", "play-gfternary", "t27c-gfternary", "play-golden-sieve", "t27c-golden-sieve", "play-tnf17", "t27c-tnf17", "t27c-activation-quantizer", "t27c-bitnet-majority", "t27c-bitnet-neuron-nchunk", "t27c-comb-ternary-dot", "t27c-stream-ternary-mac", "t27c-bitnet-mlp", "t27c-gft-smul", "t27c-gft-sadd", "t27c-gft-signed-mac", "t27c-gft-relu", "t27c-gft-exp2", "t27c-gft-argmax4", "t27c-gft-nll", "t27c-gft-sgd-step", "t27c-gft-xornet", "basics-what-a-spec-is", "basics-the-module-line", "basics-comments-and-prose", "basics-constants", "basics-whole-numbers", "basics-true-false-and-text", "basics-arrays", "basics-trits", "basics-expressions", "basics-a-test-block", "basics-many-tests", "basics-functions", "basics-local-names", "basics-if-and-else", "basics-loops", "basics-pub-or-private", "basics-structs", "basics-enums", "basics-use-other-modules", "basics-invariants", "basics-gen-ts", "basics-seven-backends", "basics-stages-of-t27c", "basics-tri-commands", "basics-reading-errors", "basics-a-small-program", "basics-where-next", "golden-split", "golden-phi-ratio", "golden-lucas", "golden-split-optimality", "golden-radix-economy", "golden-phi-in-gf16", "golden-gf4", "golden-gf6", "golden-gf8", "golden-gf10", "golden-gf12", "golden-gf14", "golden-gf16", "golden-dot-two", "golden-gf24", "golden-gf32", "golden-gf48", "golden-gf64", "golden-gf96", "golden-gf128", "golden-gf256", "golden-gf512", "golden-gf1024", "golden-gft8", "golden-gft16", "golden-gft32", "golden-decode", "t27-tri-vectors", "t27-tri-census", "t27-tri-seals", "t27-tri-prose", "t27-tri-lean", "tri-selftest", "play-terminal-gates", "tc-why-three", "tc-balanced-count", "tc-flip-and-cut", "tc-kleene-gates", "tc-consensus-any", "tc-sum-and-carry", "tc-half-adder", "tc-full-adder", "tc-ripple-carry", "tc-times-a-trit", "tc-long-multiply", "tc-ternary-mac", "tc-two-bits", "tc-five-in-a-byte", "tc-trit-word", "tc-instruction-word", "tc-opcode-map", "tc-immediate", "tc-ternary-alu", "tc-three-way-jump", "tc-run-a-program", "tc-compare", "tc-thirds", "tc-sort-three", "tc-one-neuron", "tc-detector", "tc-tiny-layer", "nn-relu", "nn-sigmoid", "nn-tanh", "nn-gelu", "nn-silu", "nn-elu", "nn-softmax", "nn-embedding", "nn-dropout", "nn-conv", "nn-maxpool", "nn-batchnorm", "nn-mse", "nn-bce", "nn-huber", "nn-sgd", "nn-rmsprop", "nn-adam", "nn-backprop", "nn-xor", "nn-lamb", "nn-rnn", "nn-lstm", "nn-attention", "nn-positions", "nn-encoder", "nn-ppo", "hive-shape", "hive-boundary", "hive-task-filter", "hive-aging", "hive-rank", "hive-review-first", "hive-lifecycle", "hive-ship", "hive-drain", "hive-reviewers", "hive-revisit", "hive-gate", "hive-jam", "hive-jam-action", "hive-master-red", "hive-key-error", "hive-key-rest", "hive-next-key", "hive-credit", "hive-price", "hive-burn", "hive-called", "hive-chain", "hive-cooldown", "hive-night", "hive-column", "hive-cell", "wars", "tri-game-tick", "tri-game-vault", "perf2-knockouts", "race-the-bee", "gatle", "tri-blog-list", "tri-game-selftest"]; +pub const WIDGET_KINDS : [306]str = ["cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "player", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "player", "player", "player", "cast", "cast", "tool", "tool", "cast", "player", "cast", "player", "cast", "player", "cast", "player", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "cast", "cast", "cast", "cast", "cast", "cast", "cast", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tool", "tab", "cast", "cast", "cast", "tool", "tool", "cast", "cast"]; +pub const WIDGET_CATEGORIES : [306]str = ["fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "fpga", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "compiler", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game", "game"]; +pub const WIDGET_PAGES : [306]str = ["term/x7-board/", "term/tri-x7-fasm/", "term/tri-x7-mutate/", "term/perf2-loop/", "term/tri-fpga-rgmii/", "term/tri-fpga-jtag/", "term/tri-fpga-pins/", "term/devkit-flow/", "play/hello-world/verilog/", "widgets/bit-diff/", "widgets/build-receipt/", "widgets/cost-curves/", "widgets/gf16-calc/", "widgets/idcode/", "widgets/lut-explain/", "widgets/pin-map/", "widgets/placement-map/", "widgets/resource-bars/", "widgets/slack-waterfall/", "widgets/test-waves/", "widgets/synth-smells/", "widgets/bit-autopsy/", "widgets/fasm-skyline/", "widgets/vcd-wrapped/", "widgets/fits-on/", "widgets/stat-card/", "widgets/lut-depth/", "widgets/fsm-sketch/", "widgets/lut-treemap/", "widgets/t27-cell/", "widgets/t27-vs-nvfp4/", "widgets/uart-bucket/", "term/tri-fpga-specs/", "term/tri-fpga-tools/", "term/tri-fpga-wire/", "term/tri-fpga-batch/", "term/tri-fpga-status/", "term/tri-fpga-tmpcheck/", "term/tri-fpga-keycheck/", "term/tri-fpga-selftest/", "term/tri-x7-refs/", "term/tri-x7-audit/", "term/t27c-clock-domain/", "term/t27c-clocks/", "term/t27c-wukong-v1/", "term/t27c-cts/", "term/t27c-mmcm/", "term/t27c-mmcm-mutate/", "term/t27c-timing/", "term/t27c-reset-sync/", "term/t27c-reset-sync-gen/", "term/t27c-mtbf/", "term/t27c-clock-domain-gen/", "term/t27c-fifo/", "term/t27c-fifo-mutate/", "term/t27c-fifo-flags/", "term/t27c-emitter-xdc/", "term/t27c-fpga-rxcap/", "term/t27-tri-competitors/", "term/t27-tri-discard/", "term/tri-fpga-txhold/", "term/tri-fpga-rxcap/", "term/tri-fpga-rxer/", "term/tri-fpga-steps/", "term/tri-fpga-ioclients/", "term/tri-fpga-claim/", "term/tri-fpga-release/", "term/tri-fpga-next/", "term/t27c-memory/", "term/t27c-spi-tb/", "term/t27c-check-uart/", "term/t27c-check-spi/", "term/t27c-check-apb/", "term/t27c-check-axi4/", "term/t27c-check-bridge/", "term/t27c-check-memory/", "term/t27c-verilog-uart/", "term/t27c-verilog-spi/", "term/t27c-verilog-apb/", "term/t27c-verilog-axi4/", "term/t27c-verilog-bridge/", "term/t27c-verilog-top/", "term/t27c-verilog-uart-tb/", "term/t27c-verilog-apb-tb/", "term/t27c-verilog-axi-tb/", "term/t27c-verilog-mem-tb/", "term/t27c-verilog-integration-tb/", "term/t27c-verilog-top-tb/", "term/t27c-verilog-spi-tb/", "term/t27c-hir-uart/", "term/t27c-hir-apb/", "term/t27c-hir-axi4/", "term/t27c-eth-crc/", "term/t27c-rgmii/", "widgets/jtag-verdict/", "widgets/line-bounce/", "widgets/eye-isi/", "widgets/ecc-secded/", "widgets/carry-chain/", "widgets/prefix-adder/", "widgets/csa-tree/", "widgets/shift-add/", "widgets/booth-radix4/", "widgets/dsp-fit/", "widgets/q-format/", "widgets/round-sat/", "widgets/sqnr/", "widgets/cordic/", "widgets/cordic-vector/", "widgets/sine-table/", "widgets/alias-fold/", "widgets/nco/", "widgets/dft-bin/", "widgets/fir-response/", "widgets/fir-design/", "widgets/fir-cost/", "widgets/decimator/", "widgets/cic-growth/", "widgets/polyphase/", "widgets/fft-butterfly/", "widgets/bit-reverse/", "widgets/fft-scaling/", "widgets/barker-corr/", "widgets/fir-fit/", "widgets/fir-on-board/", "play/hello-world/", "play/hello-world/rust/", "play/hello-world/js/", "term/t27c-hello-world/", "term/t27-tri-misread/", "widgets/test-touch/", "widgets/t27c-stages/", "term/t27c-e8m0/", "play/e8m0/", "term/t27c-ocp-mx/", "play/gfternary/", "term/t27c-gfternary/", "play/golden-sieve/", "term/t27c-golden-sieve/", "play/tnf17/", "term/t27c-tnf17/", "term/t27c-activation-quantizer/", "term/t27c-bitnet-majority/", "term/t27c-bitnet-neuron-nchunk/", "term/t27c-comb-ternary-dot/", "term/t27c-stream-ternary-mac/", "term/t27c-bitnet-mlp/", "term/t27c-gft-smul/", "term/t27c-gft-sadd/", "term/t27c-gft-signed-mac/", "term/t27c-gft-relu/", "term/t27c-gft-exp2/", "term/t27c-gft-argmax4/", "term/t27c-gft-nll/", "term/t27c-gft-sgd-step/", "term/t27c-gft-xornet/", "widgets/basics-what-a-spec-is/", "widgets/basics-the-module-line/", "widgets/basics-comments-and-prose/", "widgets/basics-constants/", "widgets/basics-whole-numbers/", "widgets/basics-true-false-and-text/", "widgets/basics-arrays/", "widgets/basics-trits/", "widgets/basics-expressions/", "widgets/basics-a-test-block/", "widgets/basics-many-tests/", "widgets/basics-functions/", "widgets/basics-local-names/", "widgets/basics-if-and-else/", "widgets/basics-loops/", "widgets/basics-pub-or-private/", "widgets/basics-structs/", "widgets/basics-enums/", "widgets/basics-use-other-modules/", "widgets/basics-invariants/", "widgets/basics-gen-ts/", "widgets/basics-seven-backends/", "widgets/basics-stages-of-t27c/", "widgets/basics-tri-commands/", "widgets/basics-reading-errors/", "widgets/basics-a-small-program/", "widgets/basics-where-next/", "widgets/golden-split/", "widgets/golden-phi-ratio/", "widgets/golden-lucas/", "widgets/golden-split-optimality/", "widgets/golden-radix-economy/", "widgets/golden-phi-in-gf16/", "widgets/golden-gf4/", "widgets/golden-gf6/", "widgets/golden-gf8/", "widgets/golden-gf10/", "widgets/golden-gf12/", "widgets/golden-gf14/", "widgets/golden-gf16/", "widgets/golden-dot-two/", "widgets/golden-gf24/", "widgets/golden-gf32/", "widgets/golden-gf48/", "widgets/golden-gf64/", "widgets/golden-gf96/", "widgets/golden-gf128/", "widgets/golden-gf256/", "widgets/golden-gf512/", "widgets/golden-gf1024/", "widgets/golden-gft8/", "widgets/golden-gft16/", "widgets/golden-gft32/", "widgets/golden-decode/", "term/t27-tri-vectors/", "term/t27-tri-census/", "term/t27-tri-seals/", "term/t27-tri-prose/", "term/t27-tri-lean/", "term/tri-selftest/", "term/play-terminal-gates/", "widgets/tc-why-three/", "widgets/tc-balanced-count/", "widgets/tc-flip-and-cut/", "widgets/tc-kleene-gates/", "widgets/tc-consensus-any/", "widgets/tc-sum-and-carry/", "widgets/tc-half-adder/", "widgets/tc-full-adder/", "widgets/tc-ripple-carry/", "widgets/tc-times-a-trit/", "widgets/tc-long-multiply/", "widgets/tc-ternary-mac/", "widgets/tc-two-bits/", "widgets/tc-five-in-a-byte/", "widgets/tc-trit-word/", "widgets/tc-instruction-word/", "widgets/tc-opcode-map/", "widgets/tc-immediate/", "widgets/tc-ternary-alu/", "widgets/tc-three-way-jump/", "widgets/tc-run-a-program/", "widgets/tc-compare/", "widgets/tc-thirds/", "widgets/tc-sort-three/", "widgets/tc-one-neuron/", "widgets/tc-detector/", "widgets/tc-tiny-layer/", "widgets/nn-relu/", "widgets/nn-sigmoid/", "widgets/nn-tanh/", "widgets/nn-gelu/", "widgets/nn-silu/", "widgets/nn-elu/", "widgets/nn-softmax/", "widgets/nn-embedding/", "widgets/nn-dropout/", "widgets/nn-conv/", "widgets/nn-maxpool/", "widgets/nn-batchnorm/", "widgets/nn-mse/", "widgets/nn-bce/", "widgets/nn-huber/", "widgets/nn-sgd/", "widgets/nn-rmsprop/", "widgets/nn-adam/", "widgets/nn-backprop/", "widgets/nn-xor/", "widgets/nn-lamb/", "widgets/nn-rnn/", "widgets/nn-lstm/", "widgets/nn-attention/", "widgets/nn-positions/", "widgets/nn-encoder/", "widgets/nn-ppo/", "widgets/hive-shape/", "widgets/hive-boundary/", "widgets/hive-task-filter/", "widgets/hive-aging/", "widgets/hive-rank/", "widgets/hive-review-first/", "widgets/hive-lifecycle/", "widgets/hive-ship/", "widgets/hive-drain/", "widgets/hive-reviewers/", "widgets/hive-revisit/", "widgets/hive-gate/", "widgets/hive-jam/", "widgets/hive-jam-action/", "widgets/hive-master-red/", "widgets/hive-key-error/", "widgets/hive-key-rest/", "widgets/hive-next-key/", "widgets/hive-credit/", "widgets/hive-price/", "widgets/hive-burn/", "widgets/hive-called/", "widgets/hive-chain/", "widgets/hive-cooldown/", "widgets/hive-night/", "widgets/hive-column/", "widgets/hive-cell/", "wars", "term/tri-game-tick/", "term/tri-game-vault/", "term/perf2-knockouts/", "widgets/race-the-bee/", "widgets/gatle/", "term/tri-blog-list/", "term/tri-game-selftest/"]; +pub const WIDGET_TITLES : [306]str = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "WARS: agent configurations compared on real GitHub issues", "", "", "", "", "", "", ""]; +pub const WIDGET_LINES : [306]str = ["", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "One issue, one pinned commit, the same tools and gates; only the decision layer changes, and a missing run stays unknown.", "", "", "", "", "", "", ""]; ; Why a person would pass this one on: the reason the widget is in the gallery at all. -pub const WIDGET_HOOKS : [279]str = ["The bitstream is byte-identical to openXC7's and runs on a real board: the claim most FPGA people doubt, shown.", "A Xilinx 7-series bitstream assembled with no Vivado anywhere in the path.", "Plant a bug in the bitstream path and watch the tests catch it: mutation testing for hardware.", "Place and route to a loaded bitstream, with the clock on screen.", "RGMII hold margins read from nextpnr's own SDF, not from a datasheet.", "The cable asks which chip is there and the chip answers with its IDCODE.", "Before an edit to a Verilog file: every spec and generator it would touch.", "The whole FPGA flow, one layer per line, from spec to board.", "A spec turned into Verilog inside a post, with the RTL read the way a hardware engineer reads it.", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "17 of 17 conformance specs compile, their test blocks pass, and no generated file has drifted.", "14 of 14 bench tools pass their own self-tests in one sweep: decode 18/18, repin 17/17, firelog 10/10, tmpcheck 9/9, stamps 9/9, keep 9/9.", "Requested 115,200 puts 115,385 on the wire (+0.16 %), 921,600 puts 923,077, and 3,000,000 is exact.", "26 checks, no board, no RTL: every receipt returns, 11 loss and fault cases recover, and a lie stops the run.", "One command reads the bench: cables, locks, and the last board runs: three x7-board runs PASS 51840/51840.", "112 files under /tmp pinned by sha256 in specs: every one has a kept copy, 0 lost, 0 unlocated.", "84 board and model logs scanned for the TRI-NET key: 0 hits, 0 logs of 1 MB or more.", "16 spec-named scripts run their own --self-test, 0 bad; 8 without the flag are listed, not skipped silently.", "The part table in far.t27 matches prjxray-db part.json, all 6 reference bitstreams present, bitwalk reads all 6 in 84.4 ms.", "One command re-runs every sweep: CRC 12/12, ECC 32520/32520 frames, 82/82 pins, 4224/4224 segbits, gold 5/5. audit: CLEAN.", "The CDC spec through the real compiler: 12 tests pass natively and the header says what the model does and does not claim.", "The tri clocks card as a spec: constants a tool page reads, and a native test-report that says so plainly -- 0 tests, because it is not a test suite.", "The board itself as a spec: 15 tests pass natively and the part line names the xc7a200tfgg676 the Wukong carries.", "Clock-tree synthesis in numbers: 15 tests pass natively, and the header pins the Artix-7 teaching delays the tree arithmetic uses.", "The MMCM model through the real compiler: VCO window, M/D/O ranges, fractional multiply in eighths, the 1/56 phase step, 16 tests natively.", "Widen the VCO window with one sed and exactly one test objects -- the rejection test that names the 500 MHz config it calls out.", "Static timing as arithmetic: 25 tests pass natively on arcs, slack and Fmax, with the Artix-7 teaching delays in the header.", "The three reset kinds through the real compiler: async assert with sync release through 2 to 4 flops, and the racy async release, 16 tests natively.", "What the compiler emits for the reset pipe: t27c gen prints the Zig, one struct copy per clock, and wc -l counts it.", "Metastability MTBF without floats: the WP323 form in Q10 log2, one flop negative slack, two flops astronomical, 15 tests natively.", "The CDC model as Zig: t27c gen prints what it compiled and wc -l says how many lines of code the model is.", "The FIFO stdlib through the real compiler: 18 tests pass natively and 6 invariants are proved comptime.", "Move a pointer by 3 instead of 1 and every test still passes: the suite asserts fill and flags, never pointer values.", "Flip one flag assignment and a single test objects: the guard that keeps the FIFO honest.", "The pins-to-XDC emitter spec parsed by the real compiler, and the create_clock lines it exists to write.", "The fpga-rxcap tool card as a spec: constants for a tool page, and a native run that says 0 tests and why.", "168 records: 14 papers entered twice, 141 cite no score, 144 state zero at pass@1.", "67 specs lose 23,578 tokens to parser recovery, each held at its pinned bound.", "Smallest hold 3445 ps on TXD1 against a 1000 ps bench; each TX pin setup and hold from the routed SDF.", "Per-bit setup and hold of an async RX nibble against its RXC edge, from the routed SDF: one nibble changes 2387 to 2486 ps apart, so a binary counter tears where a gray one cannot.", "RX_ER pulses injected at exact nibble positions of a ping -- preamble, mid-frame, IP header -- and the frame state each pulse leaves behind, RTL vs model.", "The Ethernet bring-up plan on the bench: pre-registered steps, logged exits, what ran and what did not.", "The registered IO clients on the bench, read live: names, claims and what each declares it owns.", "The bench claim in action: one client takes the hardware lock and the tool reports who holds it.", "The bench lock given back: the client gives up its claim and the bench reports itself free.", "The next queued step on the bench, read live from the run state.", "The memory map spec through the real compiler: 15 tests pass natively and 6 invariants are proved comptime.", "The SPI testbench through the real compiler: 7 tests pass natively, driving the modes and checking the captures.", "t27c check on the UART spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "t27c check on the SPI spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "t27c check on the APB bridge spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "t27c check on the AXI4 spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "t27c check on the bus bridge spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "t27c check on the memory spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "The UART spec lowered to synthesizable Verilog: the TRINITY banner, the module port list and the datapath as wires.", "The SPI spec lowered to synthesizable Verilog: the TRINITY banner, the module port list and the datapath as wires.", "The APB bridge spec lowered to synthesizable Verilog: port list, PSEL and PENABLE wiring, the address decode.", "The AXI4 spec lowered to synthesizable Verilog: the five channels' wiring and handshakes as generated wires.", "The bus bridge spec lowered to synthesizable Verilog: both sides of the crossing as generated wires.", "The whole design in one spec lowered to synthesizable Verilog: clocks, resets and bus wiring as the compiler emits them.", "The UART testbench spec lowered to Verilog: what a bench looks like when the compiler emits it.", "The APB bridge testbench spec lowered to Verilog: what a bench looks like when the compiler emits it.", "The AXI4 testbench spec lowered to Verilog: what a bench looks like when the compiler emits it.", "The memory testbench spec lowered to Verilog: what a bench looks like when the compiler emits it.", "The integration testbench spec lowered to Verilog: every bus of the design in one bench.", "The top-level testbench spec lowered to Verilog: the whole design under one bench.", "The SPI testbench spec lowered to Verilog: what a bench looks like when the compiler emits it.", "The UART spec lowered to hardware IR: the compiler's own view of the wires and state before Verilog exists.", "The APB bridge spec lowered to hardware IR: PSEL, PENABLE and the decode as the compiler structures them.", "The AXI4 spec lowered to hardware IR: the five channels and their handshakes as the compiler structures them.", "The Ethernet FCS spec through the real compiler: 8 tests pass natively -- the table derived from one bit step, the 0xCBF43926 check value, the 0xDEBB20E3 receiver residue.", "The RGMII spec through the real compiler: 7 tests pass natively and 1 invariant is proved comptime -- the 8 ns gigabit period, a nibble on both edges, the 78 ps tap ladder.", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "The t27 compiler running as WebAssembly in a post: compile, test, seven backends.", "The same spec as Rust, printed by a terminal in the page.", "The only backend that runs in the browser: generate the module, then run it.", "t27c on hello_world.t27, on a real machine, every byte as printed.", "Code that compiles and is wrong: the tool that finds it.", "", "", "The OCP MX scale byte as a spec: 2^(e-127), code 255 is NaN, 18 tests and 9 comptime invariants, compiled on a real machine.", "The same scale byte compiled in your browser: seven backends, the spec's tests run in the page, and every skip says why.", "One outlier sets the scale for 32 weights: MXFP4 flushes 23 of them to zero, MXFP8 E4M3 none. 49 tests pass; a planted bug fails one.", "Ternary weights scaled by phi, compiled in your browser: codes 00, 01, 10, and the fourth code folded to zero.", "13 of 13 tests pass natively, and the report says 8 of them executed no assert; a planted bug fails exactly one.", "Five measured rules for a ternary weight alphabet, and the one formula that passes all five, run in your browser.", "One more trit and the spec does not compile at all: BLOCKED, not failed, because no test ever ran.", "A 17-bit float whose exponent is four balanced trits, 3^4 = 81 values, compiled in your browser.", "34 tests pass natively, none vacuous; swap one XOR for OR and only the test that negates twice fails.", "A neuron's sum squeezed back to one trit: 7 tests pass natively, and turning one > into >= fails only the boundary test.", "One ternary neuron, two functions picked by its weights: 12 tests pass natively, and one weight moved from P to Z fails exactly one.", "A neuron wider than 27 inputs, summed chunk by chunk: 11 tests pass natively, and a loop that stops after one chunk fails exactly one.", "A 27-lane ternary dot product as pure wires: 4 tests pass natively, and one wrong sign in tmul fails exactly one.", "The same dot product added into a register on every clock: 4 tests pass natively, and a lane mask short one bit fails exactly one.", "A whole small ternary network, 3 neurons then 2: 4 tests pass natively, and a trit packed one bit low fails exactly one.", "A signed multiply: 3 tests pass natively, none vacuous, and a sign stuck at 0 fails exactly one, m2.", "A signed add: 3 tests pass natively, and adding where opposite signs must subtract fails exactly one, a2.", "Two signed products summed: 4 tests pass natively, and OR in place of the sign XOR fails exactly one, pp.", "A ReLU: 4 tests pass natively, and letting negative inputs through fails only negz, the one test with a negative input.", "2^x for a softmax: 4 tests pass natively, and dropping the minus sign of k fails exactly one, em1.", "argmax by comparing bits alone: 4 tests pass natively, and >= in place of > fails exactly one, tie_low.", "A loss in bits: 3 tests pass natively, and a zero with the sign bit set fails exactly one, perfect.", "One SGD step: 3 tests pass natively, and g times g in place of eta times g fails exactly one, ascend.", "A 2-layer ReLU net for XOR: 4 tests pass natively, and one relu removed fails exactly one, x00.", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "34 vector files, 512 cases: 2 files executed, 8 debt, 24 prose-only; 19% carry data.", "Four census populations recounted another way all agree; three are named as uncheckable.", "31 of 1,428 seals record no output (16 specs); 3 of those are missing from the debt ledger.", "11 specs refused at parse, 0 blocked only by prose, 23 broken on purpose as fixtures.", "12 Lean files, 15,553 lines and 4 sorry, are reached by no build root: nothing compiles them.", "tri, railway, python3 and the loop directory: each present or not, in one call.", "The terminal for seven t27 backends: every command run on hello_world. 21 pass, 0 fail.", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "Pick a lane and see whether the TRI decision layer beats the baseline on the same issue.", "One tick of the Queen's loop: an anomaly scan that reports only what changed.", "The loop's skills checked against snapshots before an agent may use them.", "Three performance bits, and one knockout bitstream per bit to prove each one matters.", "", "", "73 published posts with date, language and revision, newest first.", "6 of 6 self-tests hold: anomalies, blog_drift, lessons, site_gate, tick, vault."]; +pub const WIDGET_HOOKS : [306]str = ["The bitstream is byte-identical to openXC7's and runs on a real board: the claim most FPGA people doubt, shown.", "A Xilinx 7-series bitstream assembled with no Vivado anywhere in the path.", "Plant a bug in the bitstream path and watch the tests catch it: mutation testing for hardware.", "Place and route to a loaded bitstream, with the clock on screen.", "RGMII hold margins read from nextpnr's own SDF, not from a datasheet.", "The cable asks which chip is there and the chip answers with its IDCODE.", "Before an edit to a Verilog file: every spec and generator it would touch.", "The whole FPGA flow, one layer per line, from spec to board.", "A spec turned into Verilog inside a post, with the RTL read the way a hardware engineer reads it.", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "17 of 17 conformance specs compile, their test blocks pass, and no generated file has drifted.", "14 of 14 bench tools pass their own self-tests in one sweep: decode 18/18, repin 17/17, firelog 10/10, tmpcheck 9/9, stamps 9/9, keep 9/9.", "Requested 115,200 puts 115,385 on the wire (+0.16 %), 921,600 puts 923,077, and 3,000,000 is exact.", "26 checks, no board, no RTL: every receipt returns, 11 loss and fault cases recover, and a lie stops the run.", "One command reads the bench: cables, locks, and the last board runs: three x7-board runs PASS 51840/51840.", "112 files under /tmp pinned by sha256 in specs: every one has a kept copy, 0 lost, 0 unlocated.", "84 board and model logs scanned for the TRI-NET key: 0 hits, 0 logs of 1 MB or more.", "16 spec-named scripts run their own --self-test, 0 bad; 8 without the flag are listed, not skipped silently.", "The part table in far.t27 matches prjxray-db part.json, all 6 reference bitstreams present, bitwalk reads all 6 in 84.4 ms.", "One command re-runs every sweep: CRC 12/12, ECC 32520/32520 frames, 82/82 pins, 4224/4224 segbits, gold 5/5. audit: CLEAN.", "The CDC spec through the real compiler: 12 tests pass natively and the header says what the model does and does not claim.", "The tri clocks card as a spec: constants a tool page reads, and a native test-report that says so plainly -- 0 tests, because it is not a test suite.", "The board itself as a spec: 15 tests pass natively and the part line names the xc7a200tfgg676 the Wukong carries.", "Clock-tree synthesis in numbers: 15 tests pass natively, and the header pins the Artix-7 teaching delays the tree arithmetic uses.", "The MMCM model through the real compiler: VCO window, M/D/O ranges, fractional multiply in eighths, the 1/56 phase step, 16 tests natively.", "Widen the VCO window with one sed and exactly one test objects -- the rejection test that names the 500 MHz config it calls out.", "Static timing as arithmetic: 25 tests pass natively on arcs, slack and Fmax, with the Artix-7 teaching delays in the header.", "The three reset kinds through the real compiler: async assert with sync release through 2 to 4 flops, and the racy async release, 16 tests natively.", "What the compiler emits for the reset pipe: t27c gen prints the Zig, one struct copy per clock, and wc -l counts it.", "Metastability MTBF without floats: the WP323 form in Q10 log2, one flop negative slack, two flops astronomical, 15 tests natively.", "The CDC model as Zig: t27c gen prints what it compiled and wc -l says how many lines of code the model is.", "The FIFO stdlib through the real compiler: 18 tests pass natively and 6 invariants are proved comptime.", "Move a pointer by 3 instead of 1 and every test still passes: the suite asserts fill and flags, never pointer values.", "Flip one flag assignment and a single test objects: the guard that keeps the FIFO honest.", "The pins-to-XDC emitter spec parsed by the real compiler, and the create_clock lines it exists to write.", "The fpga-rxcap tool card as a spec: constants for a tool page, and a native run that says 0 tests and why.", "168 records: 14 papers entered twice, 141 cite no score, 144 state zero at pass@1.", "67 specs lose 23,578 tokens to parser recovery, each held at its pinned bound.", "Smallest hold 3445 ps on TXD1 against a 1000 ps bench; each TX pin setup and hold from the routed SDF.", "Per-bit setup and hold of an async RX nibble against its RXC edge, from the routed SDF: one nibble changes 2387 to 2486 ps apart, so a binary counter tears where a gray one cannot.", "RX_ER pulses injected at exact nibble positions of a ping -- preamble, mid-frame, IP header -- and the frame state each pulse leaves behind, RTL vs model.", "The Ethernet bring-up plan on the bench: pre-registered steps, logged exits, what ran and what did not.", "The registered IO clients on the bench, read live: names, claims and what each declares it owns.", "The bench claim in action: one client takes the hardware lock and the tool reports who holds it.", "The bench lock given back: the client gives up its claim and the bench reports itself free.", "The next queued step on the bench, read live from the run state.", "The memory map spec through the real compiler: 15 tests pass natively and 6 invariants are proved comptime.", "The SPI testbench through the real compiler: 7 tests pass natively, driving the modes and checking the captures.", "t27c check on the UART spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "t27c check on the SPI spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "t27c check on the APB bridge spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "t27c check on the AXI4 spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "t27c check on the bus bridge spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "t27c check on the memory spec: 0 errors, 0 warnings -- the first bar every lesson's spec clears.", "The UART spec lowered to synthesizable Verilog: the TRINITY banner, the module port list and the datapath as wires.", "The SPI spec lowered to synthesizable Verilog: the TRINITY banner, the module port list and the datapath as wires.", "The APB bridge spec lowered to synthesizable Verilog: port list, PSEL and PENABLE wiring, the address decode.", "The AXI4 spec lowered to synthesizable Verilog: the five channels' wiring and handshakes as generated wires.", "The bus bridge spec lowered to synthesizable Verilog: both sides of the crossing as generated wires.", "The whole design in one spec lowered to synthesizable Verilog: clocks, resets and bus wiring as the compiler emits them.", "The UART testbench spec lowered to Verilog: what a bench looks like when the compiler emits it.", "The APB bridge testbench spec lowered to Verilog: what a bench looks like when the compiler emits it.", "The AXI4 testbench spec lowered to Verilog: what a bench looks like when the compiler emits it.", "The memory testbench spec lowered to Verilog: what a bench looks like when the compiler emits it.", "The integration testbench spec lowered to Verilog: every bus of the design in one bench.", "The top-level testbench spec lowered to Verilog: the whole design under one bench.", "The SPI testbench spec lowered to Verilog: what a bench looks like when the compiler emits it.", "The UART spec lowered to hardware IR: the compiler's own view of the wires and state before Verilog exists.", "The APB bridge spec lowered to hardware IR: PSEL, PENABLE and the decode as the compiler structures them.", "The AXI4 spec lowered to hardware IR: the five channels and their handshakes as the compiler structures them.", "The Ethernet FCS spec through the real compiler: 8 tests pass natively -- the table derived from one bit step, the 0xCBF43926 check value, the 0xDEBB20E3 receiver residue.", "The RGMII spec through the real compiler: 7 tests pass natively and 1 invariant is proved comptime -- the 8 ns gigabit period, a nibble on both edges, the 78 ps tap ladder.", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "The t27 compiler running as WebAssembly in a post: compile, test, seven backends.", "The same spec as Rust, printed by a terminal in the page.", "The only backend that runs in the browser: generate the module, then run it.", "t27c on hello_world.t27, on a real machine, every byte as printed.", "Code that compiles and is wrong: the tool that finds it.", "", "", "The OCP MX scale byte as a spec: 2^(e-127), code 255 is NaN, 18 tests and 9 comptime invariants, compiled on a real machine.", "The same scale byte compiled in your browser: seven backends, the spec's tests run in the page, and every skip says why.", "One outlier sets the scale for 32 weights: MXFP4 flushes 23 of them to zero, MXFP8 E4M3 none. 49 tests pass; a planted bug fails one.", "Ternary weights scaled by phi, compiled in your browser: codes 00, 01, 10, and the fourth code folded to zero.", "13 of 13 tests pass natively, and the report says 8 of them executed no assert; a planted bug fails exactly one.", "Five measured rules for a ternary weight alphabet, and the one formula that passes all five, run in your browser.", "One more trit and the spec does not compile at all: BLOCKED, not failed, because no test ever ran.", "A 17-bit float whose exponent is four balanced trits, 3^4 = 81 values, compiled in your browser.", "34 tests pass natively, none vacuous; swap one XOR for OR and only the test that negates twice fails.", "A neuron's sum squeezed back to one trit: 7 tests pass natively, and turning one > into >= fails only the boundary test.", "One ternary neuron, two functions picked by its weights: 12 tests pass natively, and one weight moved from P to Z fails exactly one.", "A neuron wider than 27 inputs, summed chunk by chunk: 11 tests pass natively, and a loop that stops after one chunk fails exactly one.", "A 27-lane ternary dot product as pure wires: 4 tests pass natively, and one wrong sign in tmul fails exactly one.", "The same dot product added into a register on every clock: 4 tests pass natively, and a lane mask short one bit fails exactly one.", "A whole small ternary network, 3 neurons then 2: 4 tests pass natively, and a trit packed one bit low fails exactly one.", "A signed multiply: 3 tests pass natively, none vacuous, and a sign stuck at 0 fails exactly one, m2.", "A signed add: 3 tests pass natively, and adding where opposite signs must subtract fails exactly one, a2.", "Two signed products summed: 4 tests pass natively, and OR in place of the sign XOR fails exactly one, pp.", "A ReLU: 4 tests pass natively, and letting negative inputs through fails only negz, the one test with a negative input.", "2^x for a softmax: 4 tests pass natively, and dropping the minus sign of k fails exactly one, em1.", "argmax by comparing bits alone: 4 tests pass natively, and >= in place of > fails exactly one, tie_low.", "A loss in bits: 3 tests pass natively, and a zero with the sign bit set fails exactly one, perfect.", "One SGD step: 3 tests pass natively, and g times g in place of eta times g fails exactly one, ascend.", "A 2-layer ReLU net for XOR: 4 tests pass natively, and one relu removed fails exactly one, x00.", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "34 vector files, 512 cases: 2 files executed, 8 debt, 24 prose-only; 19% carry data.", "Four census populations recounted another way all agree; three are named as uncheckable.", "31 of 1,428 seals record no output (16 specs); 3 of those are missing from the debt ledger.", "11 specs refused at parse, 0 blocked only by prose, 23 broken on purpose as fixtures.", "12 Lean files, 15,553 lines and 4 sorry, are reached by no build root: nothing compiles them.", "tri, railway, python3 and the loop directory: each present or not, in one call.", "The terminal for seven t27 backends: every command run on hello_world. 21 pass, 0 fail.", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "", "Pick a lane and see whether the TRI decision layer beats the baseline on the same issue.", "One tick of the Queen's loop: an anomaly scan that reports only what changed.", "The loop's skills checked against snapshots before an agent may use them.", "Three performance bits, and one knockout bitstream per bit to prove each one matters.", "", "", "73 published posts with date, language and revision, newest first.", "6 of 6 self-tests hold: anomalies, blog_drift, lessons, site_gate, tick, vault."]; ; --- Ideas: asked for, not built --------------------------------------------------------------- ; From what engineers share today (gh-skyline, Tiny Tapeout renders, Wrapped cards, Gatle-style @@ -119,9 +119,9 @@ test the_fpga_shelf_comes_first_and_opens_on_the_board { } test the_arena_is_a_tab_and_says_its_own_words { - assert WIDGET_IDS[271] == "wars"; - assert WIDGET_KINDS[271] == "tab"; - assert WIDGET_PAGES[271] == "wars"; + assert WIDGET_IDS[298] == "wars"; + assert WIDGET_KINDS[298] == "tab"; + assert WIDGET_PAGES[298] == "wars"; } test the_gallery_posts_nothing { @@ -157,8 +157,8 @@ test the_verification_shelf_is_rehearsals_and_audits { test ideas_are_counted_apart_from_widgets { assert IDEA_COUNT == 6; - assert WIDGET_IDS[276] == "gatle"; - assert WIDGET_COUNT == 279; + assert WIDGET_IDS[303] == "gatle"; + assert WIDGET_COUNT == 306; } test the_ternary_machine_shelf_runs_before_the_arena { @@ -172,9 +172,9 @@ test every_tool_lives_under_widgets_and_keeps_its_words_on_its_page { assert WIDGET_KINDS[9] == "tool"; assert WIDGET_PAGES[9] == "widgets/bit-diff/"; assert WIDGET_HOOKS[9] == ""; - assert WIDGET_KINDS[275] == "tool"; - assert WIDGET_CATEGORIES[275] == "game"; - assert WIDGET_PAGES[275] == "widgets/race-the-bee/"; + assert WIDGET_KINDS[302] == "tool"; + assert WIDGET_CATEGORIES[302] == "game"; + assert WIDGET_PAGES[302] == "widgets/race-the-bee/"; } test the_arithmetic_shelf_closes_the_fpga_bench { @@ -192,5 +192,14 @@ test the_neural_network_shelf_runs_before_the_arena { assert WIDGET_PAGES[244] == "widgets/nn-relu/"; assert WIDGET_IDS[270] == "nn-ppo"; assert WIDGET_CATEGORIES[270] == "compiler"; - assert WIDGET_IDS[271] == "wars"; + assert WIDGET_IDS[298] == "wars"; +} + +test the_hive_shelf_runs_before_the_arena { + assert WIDGET_IDS[271] == "hive-shape"; + assert WIDGET_KINDS[271] == "tool"; + assert WIDGET_PAGES[271] == "widgets/hive-shape/"; + assert WIDGET_IDS[297] == "hive-cell"; + assert WIDGET_CATEGORIES[297] == "game"; + assert WIDGET_IDS[298] == "wars"; } diff --git a/apps/website/public/widgets/hive-aging/card.png b/apps/website/public/widgets/hive-aging/card.png new file mode 100644 index 0000000000000000000000000000000000000000..88b3ab5c262804ffb9aed1aeaaf9d1d532e04771 GIT binary patch literal 28474 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 + + + + + +Aging: how long a labelled low issue waits · Trinity SยณAI + + + + + + + + + + + + + + + + + + + + + + + + +
+Trinity SยณAI +

Aging: how long a labelled low issue waits

+

Pick a level, label it or not, and drag its age: a labelled issue gains one level after 14 days, at most one, and never reaches critical.

+
+
+ +
+

This page sets no cookie and sends nothing anywhere.

+
Where the numbers come from
  • gHashTag/t27 specs/queen/priority.t27 (t27 master c42f8fb40): every value on the page is effective_level and why, its fn bodies compiled to wasm (logic.js)
  • Course 11 of t27.ai, queen-and-hive (gHashTag/trinity#1698): lesson 4 opens this widget
+

This widget's .t27 source · All widgets

+
+ + + + diff --git a/apps/website/public/widgets/hive-aging/logic.js b/apps/website/public/widgets/hive-aging/logic.js new file mode 100644 index 0000000000..50d377cb05 --- /dev/null +++ b/apps/website/public/widgets/hive-aging/logic.js @@ -0,0 +1,40 @@ +// Generated by scripts/t27-logic.mjs from priority.t27. Do not edit. +// +// The spec's own fn bodies, compiled: t27c gen (Zig) -> zig 0.16.0 (wasm32). +// Change the spec in gHashTag/t27, vendor it, regenerate. A rule written +// by hand beside this file is the thing this file exists to end. +// spec-sha256: d5d5e42e10e89055810a8e8115285f6af4683f8c6e6c36501fa2b4419f4ed070 +// Imported by scripts/widget-logic.mjs for public/widgets/hive-aging/: types stripped, nothing else changed. +const WASM = "AGFzbQEAAAABJgVgBX9/f39/AX9gBn9/f39/fwF/YAF/AX9gA39/fwF/YAJ/fwF/AwYFAAECAwQEBQFwAQEBBQMBABAGCQF/AUGAgMAACwdbBgZtZW1vcnkCAAd0Mjdfd2h5AAAMdDI3X291dHJhbmtzAAEMdDI3X2VsaWdpYmxlAAITdDI3X2VmZmVjdGl2ZV9sZXZlbAADEHQyN19jYXBwZWRfbGV2ZWwABAqyAQUnAAJAIARFDQBBBA8LAkAgASAARg0AQQMPCyADQQFxQQIgAiABRhsLNwEBf0EBIQYCQCAAIANJDQBBACEGIAAgA0sNAEEBIQYgASAESQ0AIAEgBE0gAiAFSXEhBgsgBgsFACAARQs9AAJAAkAgAUEBcUUNACAAQQJJDQBBASEBIABBf2ogAkENSyICTQ0BIAAgAmshAQwBCyAAIQELIAFB/wFxCwwAIAAgAUECSyAAGws="; +const W = new WebAssembly.Instance( + new WebAssembly.Module(Uint8Array.from(atob(WASM), (c) => c.charCodeAt(0))) +).exports; +function int(v, lo, hi, at) { + if (!Number.isInteger(v) || v < lo || v > hi) + throw new RangeError(`${at}: ${v} is outside ${lo}..${hi}`); + return v; +} +function cappedLevel(base, criticalsBefore) { + return W.t27_capped_level(int(base, 0, 255, "capped_level.base"), int(criticalsBefore, 0, 4294967295, "capped_level.criticals_before")) & 255; +} +function effectiveLevel(level, labelled, ageDays) { + return W.t27_effective_level(int(level, 0, 255, "effective_level.level"), labelled ? 1 : 0, int(ageDays, 0, 4294967295, "effective_level.age_days")) & 255; +} +function eligible(openBlockers) { + return (W.t27_eligible(int(openBlockers, 0, 4294967295, "eligible.open_blockers")) & 1) !== 0; +} +function outranks(effA, levelA, indexA, effB, levelB, indexB) { + return (W.t27_outranks(int(effA, 0, 255, "outranks.eff_a"), int(levelA, 0, 255, "outranks.level_a"), int(indexA, 0, 4294967295, "outranks.index_a"), int(effB, 0, 255, "outranks.eff_b"), int(levelB, 0, 255, "outranks.level_b"), int(indexB, 0, 4294967295, "outranks.index_b")) & 1) !== 0; +} +function why(base, level, eff, labelled, openBlockers) { + return W.t27_why(int(base, 0, 255, "why.base"), int(level, 0, 255, "why.level"), int(eff, 0, 255, "why.eff"), labelled ? 1 : 0, int(openBlockers, 0, 4294967295, "why.open_blockers")) & 255; +} +const __NOT_LOWERED__ = Object.freeze([]); +export { + __NOT_LOWERED__, + cappedLevel, + effectiveLevel, + eligible, + outranks, + why +}; diff --git a/apps/website/public/widgets/hive-aging/logic.t27 b/apps/website/public/widgets/hive-aging/logic.t27 new file mode 100644 index 0000000000..e0a7399a68 --- /dev/null +++ b/apps/website/public/widgets/hive-aging/logic.t27 @@ -0,0 +1,163 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/queen/priority.t27 -- the order the Queen should take open issues in (gHashTag/t27#6366) +; Today the Queen takes the first eligible issue in GitHub's listing order, newest first, and reads no +; label (specs/queen/dispatch.t27, PRIORITY_RULE). This file is the one home of the rule that replaces it. +; Runtimes read it, nobody restates it: the supervisor imports the label vocabulary from `t27c gen-js` +; of this file, `tri priority` calls the functions below through `t27c gen-c` and ctypes. +; Design notes from the survey of agent schedulers (Copilot agent, Devin, OpenHands, Cursor, Jira rank, +; Kubernetes priority queue, Sidekiq weights, stokowski's Linear sort): +; - "no priority" must never sort ahead of "urgent" (stokowski sorted Linear's 0 = none first); +; - an issue with an open blocker is skipped, not waited on (head-of-line blocking); +; - a cap on critical stops priority inflation; aging stops starvation of labelled low work; +; - an issue nobody labelled keeps today's place, so a repository with no labels sees no change. +; Not here: the fair share of bees per level, cooldown after failed attempts, GitHub's typed Priority +; field (read it first once the org enables it; its four values map onto the same levels). +; phi^2 + 1/phi^2 = 3 | TRINITY + +module QueenPriority; + +/// Levels, lower runs first. An unlabelled issue is NORMAL and never ages. +pub const LEVEL_CRITICAL : u8 = 0; +pub const LEVEL_HIGH : u8 = 1; +pub const LEVEL_NORMAL : u8 = 2; +pub const LEVEL_LOW : u8 = 3; + +/// The label vocabulary, comma separated, one string per level. A label in none of them means nothing. +/// When an issue carries labels of two levels, the more urgent one counts. +pub const CRITICAL_LABELS : str = "P0,priority/critical,critical"; +pub const HIGH_LABELS : str = "P1,priority/high,high-priority"; +pub const NORMAL_LABELS : str = "P2,priority/medium"; +pub const LOW_LABELS : str = "P3,P4,priority/low"; + +/// A labelled issue gains one level after this many days open, and at most MAX_AGING_STEPS levels, +/// never CRITICAL. One step, not more: a LOW label that aged past unlabelled work would make "low" +/// the fastest way to the front of a long queue. +pub const AGING_DAYS : u32 = 14; +pub const MAX_AGING_STEPS : u32 = 1; + +/// At most this many issues of one listing run as CRITICAL, taken in listing order; the rest run as HIGH. +pub const CRITICAL_CAP : u32 = 3; + +/// Why an issue sits where it does: the code `tri priority` prints and a supervisor logs. +pub const WHY_LISTING : u8 = 0; +pub const WHY_LABEL : u8 = 1; +pub const WHY_AGED : u8 = 2; +pub const WHY_CAPPED : u8 = 3; +pub const WHY_BLOCKED : u8 = 4; + +/// The level after the critical cap: criticals_before counts CRITICAL issues listed before this one. +pub fn capped_level(base: u8, criticals_before: u32) -> u8 { + if (base == LEVEL_CRITICAL) { + if (criticals_before >= CRITICAL_CAP) { + return LEVEL_HIGH; + } + } + return base; +} + +/// The level an issue runs at: its capped level, raised one step per AGING_DAYS (at most MAX_AGING_STEPS) +/// when it is labelled. +pub fn effective_level(level: u8, labelled: bool, age_days: u32) -> u8 { + if (labelled == false) { + return level; + } + if (level <= LEVEL_HIGH) { + return level; + } + var steps : u32 = age_days / AGING_DAYS; + if (steps > MAX_AGING_STEPS) { + steps = MAX_AGING_STEPS; + } + var gap : u32 = (level as u32) - (LEVEL_HIGH as u32); + if (steps >= gap) { + return LEVEL_HIGH; + } + return ((level as u32) - steps) as u8; +} + +/// An issue with an open blocker is not a candidate; the Queen moves past it. +pub fn eligible(open_blockers: u32) -> bool { + return open_blockers == 0; +} + +/// Whether issue a is taken before issue b: lower effective level, then lower capped level (an aged +/// issue never beats a real one of the same level), then earlier in GitHub's listing. +pub fn outranks(eff_a: u8, level_a: u8, index_a: u32, eff_b: u8, level_b: u8, index_b: u32) -> bool { + if (eff_a < eff_b) { return true; } + if (eff_a > eff_b) { return false; } + if (level_a < level_b) { return true; } + if (level_a > level_b) { return false; } + return index_a < index_b; +} + +/// The reason printed for one issue. +pub fn why(base: u8, level: u8, eff: u8, labelled: bool, open_blockers: u32) -> u8 { + if (open_blockers > 0) { return WHY_BLOCKED; } + if (level != base) { return WHY_CAPPED; } + if (eff != level) { return WHY_AGED; } + if (labelled) { return WHY_LABEL; } + return WHY_LISTING; +} + +test no_label_keeps_the_listing_order { + assert effective_level(LEVEL_NORMAL, false, 400) == LEVEL_NORMAL; + assert outranks(LEVEL_NORMAL, LEVEL_NORMAL, 0, LEVEL_NORMAL, LEVEL_NORMAL, 1); + assert outranks(LEVEL_NORMAL, LEVEL_NORMAL, 1, LEVEL_NORMAL, LEVEL_NORMAL, 0) == false; + // #7077: a strict order, so a sort can use it: no issue outranks itself + assert outranks(LEVEL_NORMAL, LEVEL_NORMAL, 4, LEVEL_NORMAL, LEVEL_NORMAL, 4) == false; +} + +test critical_runs_before_a_newer_unlabelled_issue { + assert outranks(LEVEL_CRITICAL, LEVEL_CRITICAL, 9, LEVEL_NORMAL, LEVEL_NORMAL, 0); +} + +test unlabelled_never_beats_urgent { + assert outranks(LEVEL_NORMAL, LEVEL_NORMAL, 0, LEVEL_CRITICAL, LEVEL_CRITICAL, 5) == false; + assert outranks(LEVEL_NORMAL, LEVEL_NORMAL, 0, LEVEL_HIGH, LEVEL_HIGH, 5) == false; +} + +test labelled_work_ages_one_level_and_never_to_critical { + assert effective_level(LEVEL_LOW, true, 13) == LEVEL_LOW; + assert effective_level(LEVEL_LOW, true, 14) == LEVEL_NORMAL; + assert effective_level(LEVEL_LOW, true, 9000) == LEVEL_NORMAL; + assert effective_level(LEVEL_NORMAL, true, 14) == LEVEL_HIGH; + assert effective_level(LEVEL_HIGH, true, 9000) == LEVEL_HIGH; +} + +test an_aged_issue_loses_the_tie_to_a_real_one { + var aged : u8 = effective_level(LEVEL_LOW, true, 28); + assert outranks(aged, LEVEL_LOW, 0, LEVEL_NORMAL, LEVEL_NORMAL, 7) == false; + assert outranks(LEVEL_NORMAL, LEVEL_NORMAL, 7, aged, LEVEL_LOW, 0); + // #7077: of two LOW labels the aged one goes first, whatever the listing says + assert outranks(aged, LEVEL_LOW, 5, LEVEL_LOW, LEVEL_LOW, 0); + assert outranks(LEVEL_LOW, LEVEL_LOW, 0, aged, LEVEL_LOW, 5) == false; +} + +test the_fourth_critical_runs_as_high { + assert capped_level(LEVEL_CRITICAL, 0) == LEVEL_CRITICAL; + assert capped_level(LEVEL_CRITICAL, 2) == LEVEL_CRITICAL; + assert capped_level(LEVEL_CRITICAL, 3) == LEVEL_HIGH; + assert capped_level(LEVEL_LOW, 9) == LEVEL_LOW; +} + +test a_blocked_issue_is_skipped { + assert eligible(0); + assert eligible(1) == false; + assert why(LEVEL_CRITICAL, LEVEL_CRITICAL, LEVEL_CRITICAL, true, 2) == WHY_BLOCKED; +} + +test every_reason_is_reachable { + assert why(LEVEL_NORMAL, LEVEL_NORMAL, LEVEL_NORMAL, false, 0) == WHY_LISTING; + assert why(LEVEL_HIGH, LEVEL_HIGH, LEVEL_HIGH, true, 0) == WHY_LABEL; + assert why(LEVEL_LOW, LEVEL_LOW, LEVEL_NORMAL, true, 0) == WHY_AGED; + assert why(LEVEL_CRITICAL, LEVEL_HIGH, LEVEL_HIGH, true, 0) == WHY_CAPPED; +} + +invariant levels_are_ordered { + assert LEVEL_CRITICAL < LEVEL_HIGH; + assert LEVEL_HIGH < LEVEL_NORMAL; + assert LEVEL_NORMAL < LEVEL_LOW; + assert CRITICAL_CAP > 0; + assert AGING_DAYS > 0; + assert MAX_AGING_STEPS == 1; +} diff --git a/apps/website/public/widgets/hive-aging/tool.js b/apps/website/public/widgets/hive-aging/tool.js new file mode 100644 index 0000000000..9fa9557a2a --- /dev/null +++ b/apps/website/public/widgets/hive-aging/tool.js @@ -0,0 +1,37 @@ +// hive-aging/tool.js -- lesson 4 of queen-and-hive: how long a labelled low issue waits. Every point +// of both lines and every tile is effective_level (and why) of gHashTag/t27 specs/queen/priority.t27, +// compiled to wasm (./logic.js). Words are SAY_ and numbers K_ constants of +// specs/widgets/hive-aging.t27. +import { effectiveLevel, why } from './logic.js' +import { COLORS, K, W, ask, canvas, controls, el, plot, root, say, source, svgEl, tiles } from '../hive-kit.js' + +const AMAX = K('K_AGE_MAX', 60) +const ctl = controls([ + { key: 'level', label: say('SAY_LEVEL'), kind: 'choice', options: W.SAY_LEVEL_NAMES }, + { key: 'age', label: say('SAY_AGE'), min: 0, max: AMAX, step: 1 }, + { key: 'labelled', label: say('SAY_LABELLED'), kind: 'toggle' }, +], { names: W.SAY_PRESET_NAMES, values: { level: K('K_PRESET_LEVEL', []), labelled: K('K_PRESET_LABELLED', []), age: K('K_PRESET_AGE', []) } }, draw) + +const svg = canvas('hk-plot', say('SAY_PLOT_LABEL')) +const stat = el('div') +root.replaceChildren(ask(say('SAY_ASK')), ctl.node, stat, svg, el('p', { class: 'dk-legend' }, el('span', {}, el('i', { style: `background:${COLORS[0]}` }), say('SAY_LINE')), el('span', {}, el('i', { style: `background:${COLORS[2]};opacity:0.6` }), say('SAY_FLAT'))), el('p', { class: 'dk-note' }, say('SAY_NOTE')), source()) + +function draw(s) { + svg.replaceChildren() + const p = plot(svg, { w: 640, h: 300, x0: 0, x1: AMAX, y0: 3.4, y1: -0.4, xticks: K('K_AGE_TICKS', []), yticks: [0, 1, 2, 3], yfmt: (t) => say('SAY_LEVEL_NAMES', t), xlabel: say('SAY_AGE'), left: 78 }) + const days = [...Array(AMAX + 1)].map((_, d) => d) + const step = (f) => days.flatMap((d) => [[d, f(d)], [d + 1, f(d)]]).filter(([x]) => x <= AMAX) + p.line(step((d) => effectiveLevel(s.level, false, d)), COLORS[2], 2, { 'stroke-dasharray': '6 5' }) + p.line(step((d) => effectiveLevel(s.level, true, d)), COLORS[0], 4) + const eff = effectiveLevel(s.level, !!s.labelled, s.age) + svg.append(svgEl('circle', { cx: p.x(s.age), cy: p.y(eff), r: 8, fill: COLORS[1], stroke: '#020403', 'stroke-width': 3 })) + const w = why(s.level, s.level, eff, !!s.labelled, 0) + stat.replaceChildren(tiles([ + { label: say('SAY_PICKED'), value: say('SAY_LEVEL_NAMES', s.level), tone: 2 }, + { label: say('SAY_EFFECTIVE'), value: say('SAY_LEVEL_NAMES', eff), tone: 1 }, + { label: say('SAY_WHY'), value: say('SAY_WHY_NAMES', w), tone: 5 }, + { label: say('SAY_STEP'), value: String(K('K_AGING_DAYS', 14)), tone: 0 }, + ])) +} + +ctl.start() diff --git a/apps/website/public/widgets/hive-boundary/card.png b/apps/website/public/widgets/hive-boundary/card.png new file mode 100644 index 0000000000000000000000000000000000000000..bd0f4d06ff956575361fc73fed22726a56085932 GIT binary patch literal 69085 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
`eUv$B#JO*Y>Ssq=M{f z(PVqxqFhaeYXKf_gX(QlU}G~o*FUsn9B30SL_ESyFXbt`K{*~`d)G%YvT*E1Gj*Y7Xo*&OtZ|JHr>y%ka&f*=tA_Ha< zgMudB1?IjWkcFu#B_cOErtv;Vj5cG=P&&gF9dC}D3bM1a4ubnO_vKiV?V5B+FKG$| zyKhduTfL_7sEz?_^YiTS!>nsX=J7#RMMZ<(wA?+fFW{#8^2M@UBwU5VR7&1V3waz) zQg}AKf3w{00x%-Uf3lqG>y!svZ8=Rz6b5G1i`N&o`L*Ajji5Y28O4U3G&|VnVGIZ0 z^=jme5w6L@MVEk8Fo2B8AhB(EACFvjzV)^}IAH6y)UE34civk4PIrjTkH~cq$>GtN&U72J*M2{8oOIQXIayQj+#i>#Omp=n zHxYDTUDUUB&&?UL^dNMzU);b2XERO_TkiF-J7GV^wtHdUSyv&Sl9H5`2Z9-Zef*@ zYpfvJy1bRY*>`wki_1pL(-3?O3KD<$f|~iYs&bmzE9A0iZ>(V^yYJ(?y?tkL zLzjo>E`FPZ_8JKsKWE89RcZWpP5Ip74-qdn1$Y0muoypsj2SYMV!JqxiylcK$e-Hl zyUjL&b&B8{i>6rZUCLu`irzaQFu8LSR#K*o4+1Bol)o~s?l*@Nk-I+?eI3r!8MW{{ zJ4wRcPX858rLY1kZQ-9*%IlYb>%QPfmZ53w7E=yhJrWmw;S@ia%m*QZQV?$2aRy#A zd7KHW7>Qm`)w)C!QH~KuKCO*Xf~+%NGRdoq-?LS%?zanpanK@j8ahj>!-nM(Frd|6YO5xz@r$oi2CdTwNWdypUP(4RmO z9B_~+r?+rp+Y14ouK-`xX!j1M{FWQ19wbaH~Pk6>|( zoT?)t74<+LQ2wN?R;d&FLcZ(3m{m^!h>Yy*#*bMF>5R;@Je3n`kHUU?L49EX6@Z~+ z1B;Z6Ev1T%zmjf~s1Y>7)a}N#(berT*O&3S}?#h z!Rh^)3Ska^<#3F{cbd6ZRbW{x$*OZOwN|ScDr#ce{F;5GW6$rURO6yGau{F4xePIn z&-Ct#l~s8L8ED#`hcq*SR<7CLV|kTR-7!s?Ye<0ndz;OzV^ba_*8-1;g`K8t5FUPt zzlUG(pruV{bSr!FbPprw6_$DES2ZDCEC05ls(yK~sKU|{iUcR5!r<^vR@Kt`FslHrJ!zsjnjKI!OA6qcMcv3b8ao(!B>8{{f_I> z?&aO;l_Vri8uj5jnNDCeqSBp<7m4uMo%#y7WA=#mn1!Cx(SpI`U48xc=kdocqnQx# zLfe2)-J(R3O%jY?+nBf#ZXNVS%*Ihz31j^N339i0+rE%nD2tPAWZW)$=#bir}er@pe|k)NnZP5x-^b)0JK#%$zM2gTZnG{SdIjGY-;#g1cp@(e5k z`Mbi-|I(%>jfWJxqOMTdU*sHggG-7b4{s@mX(zt5}Xi~6Y?Wg$7nk@T^1hmw(G}N{Hm}Jx-ar{tB+xb5%KsBwG#ob3+Mu@4{Xx5!Xq)f$Ir08NEN_>t1GG!m2Em?I3cS7Pa=&`3 zX~Mb2$;!&0A%G2gLbG>4yHzzw&nj)~^P+#1EQ{wXg=%mRReJZlZJJG4k)V{FX+#Jj zxusd&Rv&K*V-m z&JCO3BbZIYXUup74K4xdncy+fLRCNre(y3Ldgi%laFBSndb*LZ3sI>hJt1`DU54xo zYa5Rs$%OuN@_b#6PVwxMaBxx4D2)lg%Aby_U!+TXt7(#W&6!V>7$m?O-g>$#tpraU zlMQzdip`uJ2G7`y5sm9;q;xn<*u7@*abD>iY7Cbd&|yso-A7AFoJEiSP3Bde6Rif% zYn|KOAsMk0BJvt9J8f{c?ykswVg8Zw4p#g;uv8V*p|sjH2|R7}{^A40pq}{?kF^|V zdP%ehGMBnVg1lgF_DlhdtmC;gZ5$&OOpp>zgtuj)$Mzy;s;-^(&UOi`dn4R1#kfYt zdcQ8QpF#T-_`cVoQW*l2>Mlba-dD-k$b2fYP+BEBnR3lIn6v+oJ+tj}YRHeqnc#?j zx-q9{3KKJEO8pgnyvW`@RJ6v++t2y^%y@h|JJ{T_%}xzJk0NjZia4YfaImm`WW!47 z*ReS@wmushEe)oP;7fJlQx4^4*8N|sopo3oTes%Vi9v7)!2$$N&>+DrxJz&k?(W*b zf_s4A?iSqX;O-V^+})w^o&s|2{qDWr%ro;m(|>k%RqefNm+bYf-@CA5*Ts{8S1cLD zvZRr5_ZI#WXPvA~MZuzrX(6 z1ZcfDVc}X-!#^bU|skgQH36C zfAY_El(!7DK|)5YL+4-+Xj%H4e&3HzhQTEYk8VQW;NdO`hyMJf{0QFU#)j6MzpIP3Kw5 z={X$+rkM9&l4$cxle+|~7b~+B{ zBwK4gQUr?@wYRcvarEVT9G{9KB;P0MxvYjSUb1N6>4rCalfkxkvCiQ{$+%tc!YUD%X&eP{){(e1&uK{c@ z{5V-HxSr~4CZ;-DiIKH*x|xw!Tpn%_$3~N>@-L1$N0WQEQToe>Kwn^+i@-mQne6?< zxt57wkDO1d4;jYC7BUou=Q%#P&BRpi)Z6OlEB83_LMX`4KI=@yAm5z%>lff%?4Y)m&^BCL3Z(qzyx2KFt7WS! zH4fLjx!Ii0e+fYsZg@EEaC{!$yPpnzH~Px3RIo@+${i&b+ZHO}9i55uH~FT$zXFIY z!tSXT;?__T!>Uk48pQ6%>JwD@6Cf4+r-Z>zbqeBrSqDvJ_K3{S0yA^3cG3H@R7c5S zykRHLyZZ^_N!kt5|C;jYF8tD4*$~BlKlw8-ANn6BcHB!OUxiJPJ>ut9r(k!eLT$=0 zr<*X0_uyq6tV*^|jFE!)e1G_g^eO7RFJkt$lfe+2ceU)R+OZK~4D;?!<~Xqcg(&;r z;rbK&kBsOWY3zZ(E&KXzAwd3;><(V{x=KY#*?)O?QGWZIDbsE#1~JytF;WA+83?P{Vmb1m!uxOhY$0-+vXyM)=cLzQg*l-a)0D-)23V zOBpVb8e5-s3TNkmP(ZQ+F>_;$;H(R z{Cyd{`N9q#)v3r&R>qfZ-pzm8_6#@0-B0@Khifw03du_r4U6>nnjdQ7zp(Z23?U%b zTb4>~-t&7X%vH|h`rm+o^o z$d)R7JWC_Vq?=-r(~*Xkk?f)Ci6B>|Ifv6h7I2FZ`~C1>80`<&{yj6h49(`Nv-ADviVnk*iFEg6#?hsh@k;I!dC%`HVF zUkl9hN>hw}X^hZd8fqX5J3W1wAPqlgZQm3SOiIT}8x>Ld+)D6WprI(U1dNl-?S)t@ z8jGZLo{zgVm%jH-(^$G2C2}<>peA?IOxB$G&PB<%l`55VTwv70lIFV3l+u1 zFyYmbephuMX`0O|=+KW3oAae`<|4pd{OkMY;DLv<-G6ma8qXt=WPB;Sfwhbm{wt1@Khv4G0KG(+Le3#d(sRNEi6{?XU0XgK+$;xY=dT zTJ!Q$wU)^rzyI(YNcPT)XQCuUJGXa0ML8Ei6#A2PpYzv}`N$$4FS42bcVPl}lha)` zom6%+Lt~tM*?V)Uzy7k0A2Am0^I^^z`6(=HLIXwIJ8^zPDKiiliY!3!xR+Y@*4DQ5 z&d$-V!g_ePP5)@xb?Ec!>$B~gIGETH&-9UM%KtzU63k4lvBu}SOu5ny?3$!QxbL8$ zP3==#_GgaJ{+Iw#a=ABC5_;*0E1j~Lj|cb~{0ajDqvZp-m(oXrE?c}M#+(ASU& z^3coOgM%7^YZ@A^q+|x4P<=lV;#%%MrA7Dx8D6KmBGy3LwgeHvy-oyXE=NYZlXi|$?Ox9WDVz<~aXfop5 zl9AkHB_qH&hn1o8TGX_=Lip23KDy!l!9jRX5E%+3ZfF@2?%u%;>4)YYS-W_N8zDTb zfy57c`@qmh?A_83i*&Qi=?!A_AAj@OeVpz`fk{M$1*W?(>g5LUjM~h4i=y(#Ib1?~ zXC(s?E`h^aB;#78`Eh|h?^gPa=yVaS(k{dzDio{8L(NNz`B<&{ScNm_CyD! zwe-w6+gzko&cBz+2blQy7FfKNp@(*)-s_O)Z1;2X3*NP1{})r;O{Rn6s+Pr1>iAdK zD~r8kjJzDfTw0}0NUV4SeVW3#7Zq!F?;?cX%-uM@X7AtV`lEUQpCyIZ()}rMbYpd;4yZ7=RAd?tLB69> zyegwMKh6xK4Bi0L&d$%udCO+XzUZahh4WUKcGX0aaapPO@816(AdEL1mzr(ejFkxG zBDot+aY?1<=TGu|V!QE7LrT)0B(OPL>kQh}D$u^B*x1x;s;+mqDF>9&3_K=SJZ1j` zzI=9b$94QWg;z}y_M@5L?O|Afjf1SdbE9wSRo(asqPucEmn*nE{IfisfFIt&XQaro?P5RJup?sGR;(@ z{3^RFwo?tWP}U-EczTR6Xuw?9=FCp*y%EzSYh8AaX?ErKqUfkORqtmuGx6h7lz27b znBm1}UEY6U0s3}y6Yy9wMf$lseZSxs%UtbjNCK=)Y3S zR4FUSi*hJXR<5|CxLP;LZZ*{OlW)*c|CufgE*bPZw`(sbLdqA4Gdq<+qc9qa5l3f7 z!9P_JPRNLQeekoFRgou-mh!b8X=Jwkjw2c*8L|Zzq|z`?GoJO%T#>>^xIhUD=4JJ% zyi&hnWLdViNO5tQdg&Abl7ahTa z*x$gPByH?-axTPGi(j|>{R~o>kh10S)A$c!*%27(7M8BDT1oQP!jX)!oPOo@p-EK} zmRiY*Cefx&AGbdAM9!uW zj?GsLV$=K7X`wDvd4^=sBpYA4&U>1IWKpHw)C2^7iZ3U52Yl^RPA968)aZ%|3*_^$ z_|ZeHZ~RsIri{$Zqb!Q2N@bKNgQ;mP+Az`~^Od=Sbc6P2*%i}irA*1GM?duU&F}G8|<5h{(ab)8651hF*GuQa3jdP3JkyS0bL*o;9b}F1&Zm zjfzgblTS&L8eoTldJE;+tGA0ew;dl#x#hkzlOMz#!8}i`)(JgzmdaRXli3dz$DWfb zEuHO5E+mg0XI&OF$dfw7y<(11KPHq8hOb2MX5#i?rFpwz#Pu}&KCYK7u2eKFTQDPQ z%CvnvwWf@!2v6V$x8aDHJNHQ?-C9W_>Ib>Gcv*hM#jcx$E<;~X2T5zZRIz|p-`;6Q z+WVGc2lCIE&LD_ z68%6j0jKtKPEIkJWH2AId&ZBW=lo%I7-8;fn10704hSX04b?91oXbcULLD3>PGr&S z#*{uB*^Bu~S8&WRlc}4(i40t61&?psNmqa?JBOH|F{`!nAgfm0xK1)77wQ33d)$%rlNp zS8`L=*;Y_7QG(xcN+o8_YpE}}eG;r7Y(vvtuHfL*bmTcw`FCW-O!I~8bxPD}2ZySu zp`})fYYdCB;$*GQH@b8#%|d(DA~QDpZZ>}(L#oK{bfTh}vl}|SaVr;~vgL{?*hsq& zB}Yg$ehprd(SjMfU}YO+f+mT>S4Ax9N;O}V3Jg8#KsV-fkN0w}^)kD~p1RjA0!O|ZT~&NTJHUtiVu(^=J3KY<)5V@a>JZPU z{9U7+5rVXR)Dcq(YmCnFSf8nB%|>23^Qp~l-FPczK4P`9f+c-F8oF8LT{j3xF_c9q zC8bzQxlo>|lD$q-@zDkC)Nwo7rLX(11&QgLT_v2)RU! zel)BmP-k3-4Ng8C{QX_MBqR%jJ(x&|MClPmI@wHZrAe0^-RgHzf_9!=V|p}}kq`O)obFx4N7lp5FQt~C@nbkWXdr%zP;Ryet-P;%&~jR3eymFZiuX*+6E z>TBr_9kKYo^VA~K=FWm4vFU%;V2~JoeTdM;fCEI`q#OLAE#8kB9Kp&b;$A7M&I&GF z`?vh+`S_m1%;S?~SIia4toQ~7t_3Wdo- zv+YVqAY-Y%A`6VYxXPu2nR%42+8Z<@xOFL$_;Ux*E;xIw?)}#B6^9S0{F<4(ouXX6 zT3ljGoCyo0TejUUq1wP8VtHAkDi$>?DVt(pkUjB6lI)I@7*-jG)&2vPoh!vARqvo` zE@m@dAW5sa(&>#6#)hn3$*b2_ZTt~Wmub8F05ba}uJ_6LTci9MQ6@(Hq&nM`^GpB`6b>6oc?|z+09FXzCoQM{!f1R084Dh*j|yjf zx_=yeT_oPzM;vKlb%w-{adYrq%h275ib-{sV$Kk=PGeTdW7?4W z{<8V&>swN`VZ!6}uBXsBobsKoz!Hz~F3C{SD?Ws&)O>#tHSSyEJPq4#>COpXRNM#1 z1b`A65q*7l)rF;@@H{+HGY!x8=JdsngN&kT-&yG*1%|-q&(gCwdhINAe$Z!TqxQW& z&|iP)2}LVz9xR+vNHhlPHBAlTOC|~AkbUZV-x4WLY~?`(2zP;Af849b(4|x{u`9iw z{~v8K{tdw4!2UGd$@4VLyo8Ujk29FKf&~87s$B)LvPGF}xonGA&$TL;n|7JC$b=oQ z@$X=cpB;c+AFoq--%@t3Ufsvu;t?P=Z^B)1o;vYv9eXH_(o5Fk zYlM}G1HHQ^HGH<axfykNEIhqJPHz&JW*fQ>0z2#7N@)0DEEqm}0z-f3& z@B;yO6o-i0E&(+HAaj5Ir}ZIW9qTQXZFwSdrEk&}R?;?kuG>YI#`$@4>GOk-4}dVS zE_m!A0dw0SxpKM>+!0(0NOpA#CZGVU1l@*X($`GF&R_WG%hxITCV0?JLDy)?gTng& z07VN9p(Jm<7ZCSfYtmdN*J@cpX=y>ZquhSE5^A_PoeM2cY5A&r=vtAMnHa))u>9ML zH?QfbkN49(;)BYU($FXqpU%J*d~Da!Sojxk0z~F%4lfwza{y{3q33s4wDWiyj%2Iy zeXvV1;XMI7oRYTGYXku;vz!e-eGHelc4QzDKgpC%PxN*r7e-$QAX>y6@&Puw64df< zLNR@&5k@b>E<7CcZ_?Z-r=MAL^*P+{arkkMIq&h{oA@0Mr~00M^yjCz_0Aqb)^hWM z4t5aB>^7Y~=~%o`eR^&CTpjAWfA{^X3`}a_E%;7)(;=I^l@{HX6;5*~`9@|G?M*A>; z6oD|(F9T*jD&HF{L0%QStJ4PkUEd)7%)%N6xB4F;g~Pe?1mo^~oMx|N%2tR{&UZEM zR(ku*%83ei3aqxwW^2sW+yQ0-UD;w(Mw6|QR_ysZVo>47@lW@evkxvlAWVk~ug5FQQ8QlgwTT}{#S z)N*e}h&4hjJo-lbn<)uSM5Or&g+IjZv7w0ElIuh<_HEOaXbsxs09jeYabaS;#ZJ!B zPXDe*; zL=?gTC@>nQyICM@=}j5!^Xn+*9@KsCnOb%6l3xP0nYzZ^e%tWK#nlP@E;~*V>Sa(w z@r{|tiWjSrw!_gJ{(q1lA_M7Pb^eJ3Y|jUOl&`DG$UXoO%6CP)*D0tnk`!(rpghWh zO1zs|H^^@dW2x%-^)%xQY_f(bTTSsV?E0%`|Jts1PRh+$4^CrRT6GrC+qD3xi@ zBj2}Mx^DJ<_wRm&Ry*sRyRZl1Al;3t!cM9yy`jUXb-T5N+*Mt{!AQnJH=;+i>1oNM zjT9g@NnuwKBp5*|g9Y?GeUKHhmDV|p1PZ)d9w+r}aa+mtJ*qQZ3})f8hxyoId_(OE zpkC#&uNJjVLYP06T7n10c8nLaV;cDT(bBhVG7G25j57HPx*>Mo?3&bzUSm?c&s_?! zGM{h}5RShXzljU6Xx`du*fK{#&$Z&+y^+m*jn0z&dwja@BXA0Qoeekt0~IC-f-Y%5!<6Dt>C1O17^ppQJTwbr)fRAA2_jGdD06SVn zVAyt`!p??Xd&-hO03Uzo8m}v^h=95Txb2Jh)Nc8%{_pe0{Ue>RP!cn1DylS!&+@DwY(r2yLj53P5j~L0svP&o)za=o~p;aBce|Ts)#5r+KB_ z(&u}Yt@;)&q#&BqGG_C>?+ndHOQ}i*Dfsd46;_*^GOApQPf2!0C2a>&n9h|evVCJL zh9N`9xVOuQxfV6joR!AH1;y#|G{|So^wCV68Cd+S0g3>h;<0^rI+I|EFwtx%s;pdv z8hUsG@HM2;wB~HPhexAge8|b?$4xN=6MIh_7biYW0GO?>Uj-=hF%Q zgA*BO(tt?BkNTY=Mw~RN=b<4e-Z(2rs8WI5s3v14n;_fUYZ|F|g^2PV$?p_a6+R{} z<}x>$txP2lRdU=QPKO8I6aYfX*S8=4_BwP_?KT_m)YgrvGQbit(KYiZZlN0dttOX* zK7?Dn9+w!%m}@Md6BeaH#S*u<*S^QBxJ6~wzY$*ks<$GT#YnU`<~ejNZabNTfjf1H z=8Qu#rbSr08n=+jKD(Y4y7X(x%R9m?3{Sb9Ih)rsw$%oXMNnEx_&)9mtDm+>_fh!Fxy*!GQ zjLBQ;Sg@p}ECy!Gm+Vvw=!J;XsbjtD70oGx*VFZT&&vrbCR&0p z$w;Py6co!9$Z~MIO{zHP&Q+bya}YEQn*8=hQ0(@mi8Cdms*4)mNo_OZ(H4Ea))0!k z3YKur7B8#PwVPjwp=d~q%)vf*Hz}H&gV!XKHMk&`AhnY95B^f~DxIt51FKLkK{Z8te)wMXG{H)kSG)U8-IL+r^$9Jj z)1`a!ORIV56uxb3+d^=NWVRSOyJ`7HGpFAelM{RI70q5nZEJp89^i0LED;t$*|9}L zZk7N6E1lcdb~?5RH^EG7u{xJyII$J@L7qzMr^)Yu1`L>$OH5Ej8YuU87)ad+Qp696 zUC0yWmeJ&n6CQs5{4eYV=<76qU-zW3t|n*REgW1*g3W^0PQ4YUI@RXgC+}~(v5^Hy zE0#339Z*MytQ*HEuu?vd>3EhCt?gme{^aswunH6oOk10qG<#c`i}-m{a6}QWgGATk z&IoG7AowoTRd>U+!=fA;n)q$rc6&k!L{(7k@}xNI!x4)E$=KD*I?>$`vSm+`6<1_w z^BPsUiOlUnkaq2Q4^gqoFM^9cANqZ#6tlXzP%$|=j#4rA(n$E$X*@z2=gBEj%0ev~ z##A6R)8e!aE6cFVXu}=?wYr`z`5In|;sTjTJq~TL8bZAsGV-cd{=g3vrjcF9cvZ*i ze0Z1odJGMpK|+*cOTP~?bXUFhd(iBARKZ*cJqF*is%8~*a6 zQ%=9_#LW^mv~fN%;^+o~ub5!vQRwNM8os(>5k7koAXRm;MX5|HM^OvYa{H+l!yk_U z?oO`_+T&8Ffc*eFKp}*T_89{nvW-{hnOEtWAaRX|4{lnmwBj!UFO(FOO0`oH2%$M0(yPb%Ra`Q0 zOYiPfs|4FePHoGkAa#2$=57rOMPn$|w)>6|E&vpjhFjo0pc*h8&{X7_Gc_g@Pm%?n zeM@&zp*prh#B*O@0q7Pv@}&^`ictyLC`rwg!PAge`6ZS4j^ zRnP36(lk{L9}1V82ESH0^Ly?Rmb6-_5hzJTB8BbtGILC$o};*33QCqPmIve~$cqcb zDDft*D^O>(7pT`z4HdnE$Y&coJ@>0(QPvy)k>pp2#l+y+UdAZfnzHwOT+dlh9k9$} zFprG>s@-idxkTjB@~{ws*ILmeZahcuA|G}krVhfc5Udn8+S+ySR4K^dm6yok{|2+s zJ_zWWc7 zP>@F3BKmF>1e+Lz4khnwO|SR=rtf93ucjMOrSHO1m*$BEAqxmHxO;Vb zxPqJg7T)=z?lU+&cW>~3EwD5>Ml_vM%3$w=-wq8HFC-JCfjC1Og8EQvV`{J)Z)V|W zWq8joox7L#QGS~cy>N{InfUI921p0!bZ-sOu<*cP$mH5(Rl$PNk>t* zb)vsxQq!=L72`P5ZycGHd9-(nVkkC4yPzI}vuErw@daPV=Np@}lQki$$ZT`}Yub=N zivB8)>;4xYd2mo&?wHE>lr(@w7xqqNxwkR+f(-v<{z@}nZ+M(XciMm#AwPiOKy>g5# zv18quvnVz)ijh>hXXI!_*{zN=2JfYIW5{b{2fw*3$C(F~=7|UK^~dAK6-=p33BW`I zA-b%r{6%jY#^Q=c`Y~?(^#Lvi>5{o&VIsUDz&GB=?0p`{NkR~4CjMnLykA|Plz8eF z6fzt79G{VKDmPBa!s6Jeb+ek)Tv}5jDk-H&M^8)1uv%Ez1i?D`?0*YGCS13ck&A&#f5p1Bn`gl;K&yn4ci9^V7T(N*^<_;5Ss_>x4RlwiAoO3#MnCFYyEc-K{YLE<3M!Zl_t{J?-MuJ-H#$2Qjm7Ttyk zwoQZ9JBX6o%oHd^TA~kNYq~6{>h@r6N_TS&cEF?)skxgO*=jQ3gyiCQ`;Y>iMZ3DQFEwI;%Os3>XDnbUe^8d+EGoY|7!Pd)k!UY~GqDN8D@ zn~bEBE+#p+uzB)0wIgV-I94{YB3qQS2DcY6s9$$)QiC04+1zFDxhw*`yxe)lniGb# z3PYR*M}nzJa@65ZeYQgy>4eKRQLX ztbANDRMEWZ2a-}*btO%koVtPSVJ`a{R=Z!pO#j3JT2-^ZdMe1I0{WddrZzV^RLku- z`vg<3Kckx(bgVy3yx}@BS%Sl0K~u1ZUa9J$GuV+JQ%5Gh7J7&DdBHaCXp!OME}T94 zSfmgn+Ku|z@Uk^FQ;>v(U-w$)&e!pjLpvP+C@&y6oR;svHy&qtGmWWY^J zO?Tg8fB$zUEAsi6JO3e8`Nc1_3by743*mp0STTGm+Oe)zP&`I zFVW8k=Ca22Zevvr(>(xWhdl>0wppumHvCkk7sc@>6YfdLKnEP|<;N*ey^?f4dB=5D ztc4~ib54D(i}w+0qUR>6~Z((F>l=Bg`E%*!P%d(qO4!~)=uk*vzL{@~;ba%A5u2ybm zt?;~_2-KLObVOBcTpdJq5;5DPr@!$FAOzi3Ea;zrSx7Cq5Z|2LRkA8>(W*{wo6{PV za|EhTIBfU3P*lWV_GF_7lxb;H3JnmJm{ni$C?=Jxh=?kgl@1jUN;S>TndW_*88^X~ zaC(13i0n%TZBr7VPaX zvl}&jlS@TiW4eYSU}95jut_#&pKjU>M{`U5D9 zYU=fBAL@+?`TNSrfDDt8A*K0U01d*XVU-09AEVvbksNUCIy%<0G= zujCd&czL;UJ$I}Qs`Z>eouUc+nM&BmLuz#!)4wzYE8jaaLWJ6je#d74IeE@T5gp7P z$8GG(iNOQWoefyCtC--F5lT;T_}zZb#(opilJC-y=C^dJ?Eir|jV_OFp_DCe$nf5* zrdnz*Uo2jBEfBuPdpUpMyf|rvlIn>*w^P>W2}4b_Fb{b~u^cmv_YyvW$;s;WXR?$00^+4nFONFZyK;smE{W{)9`T7unf_nfGV z>UTAh@2eI#PlR;AeSF80HV=(3((Y~)09?Ll<_AKG8V+DNtmKSbrAZiZ-EgG&%RffwI4>*|=1 zZ7N}sDNx5FBDHI@u*ho-BC4z3#%z6> zX<9zuIX{o(+{-SsLO%W7Q<~2Qu~`mxrC`t9-_tdT5w0q++~zX(%v0<+Q!`knqmI|$ z7h1wUyKL<}-C<=8xgw+wVXnxq$*sTSF{m$bv2Uz1%Q(tt)Bh}`Aj@|1W$PBGiP#7!cH9bIZnIgWxR>-{br_?CJ>~1MF<@B z#Ih@pXB#)rT?oU724eUF!9=!*j%FZ+qc}-6r9hWGGk^LM#Y<96&Cjv#Dd1V3T)3F7xXQH+DiZ-;zi&Y11h}xclV8^6G}dOUeLkSXbbn%T-H{OYfjsFC|LMGds`*=JA09^!}rnlVNI!GBl zCp@Eor1T-f#^Xa%v7NQ!Ve+z@N#fp}I1%i!A3qplGW&QiG>dy{!w+yI3)n{{{O3@A zKfYnz9}1}~iL3_$!yNFvZdr2+{0(i4;nTw6qEps0VJq1fzWQtadDNtu zk*1c#RtQ607|?n|&wBxm?-1_*0jPOAl?bOy5&4A=BBsuH%#{uvLoK;x@fyJg=%(}e z?i0YOtvPr)?2|p9lWO!ouCMZ6!ut};wYwS~&ba8C;%l&^y1R>87Z|NhKO3NY{!IqX zQe6;Wn*I}YLZ0L7IMZHrM2Z6Xzew_U^6t^7EPidC#U}anP$WwE7$-wtr?r@L7?(4^ zHa#Fur2Co9M~xLm&Ie6AT+oT&c-%QogQKD3hRr>;v-=Yv=kd^$4+#-`-JpF;H#pEO z0i)tb^~h_jo0~t-(=)okU)mrvM~mr>q453W1V9PPy|~v+XCBW32PPK$^W^=AZB#g0 z%eV3}p#4x{`h@3mDZ!xNUz1qJ(VDlQJ3@eT0v)>%JAujA*n|Ey+?;gDARi zz_7PY5UN}4EE1Dm{oA`371dAKo;GhEW{D1CC>_<1_5HjI$7$WiW~7CKY0es8wl z#{D(t<`O#Pm_!2HI9i+^PRuU@^m{A9OWt6V@7EMuXrFH|Ly?PpSm{pZi80yO?@-R7 zyWOgsKkRTYZX(lyWQJM!=;G5LnDu9SEiiGyn*k?Lk+?6eyhSe7LalsqRC7Rn&Jd#O zRX4c+RO|H$n1Fug{zUFS^y|ZACmQ_oe@b{Bd}_L$fr8kTB`|D`*w)0Xl`720Mv{F^r*VTba>q;2%mKe-uyS(7kF}T% zj2_7YnojHu91L{f&dxEJA)3o*i8zSa0aAJuJVal+ZuK_S`EXKberMgKG=M_6KVwH| zsJdrLpvM1qeRrUAi%s0(?GXR`jo@Kq@}sY4LjF&Jt|Q&x$f zJt$IvOyBsN*=0JOowlEv*|)uH@jo9%rMy0Mz2bvx~TU#+W+tX4xLGP^n^{TxdA$i$4~^zJb^skYykIdkn3-k7lpDNq3Z+V9+jD=a#9y}k1rG3Mi5 zGOdd+1Xc@O7O&%b6G|j?W+1Xhx6NCf?lRshnrat}H%3tIOxZD!VZs+mx zx%EjUksClBR;SkTj(xprgWI}zdt}tRu$}C(;Q<@HJh{gM{1QTV`7-d!4|oc110$qU zD3KK>$1pEn=XO-5`#BT}3EQON4E-k-;PLu3;o#tp7U6RK$cVe-KL>5!JlI)rH4!VB za@YnuyaB(_6>Idj7P8p4E4Jv5|A8m0rGB6ufC>qIfAXe2Nd0FKp?{Md^%2GEgRS9s zNM7Dyz~Ny)!0@o_pMk~H`|>4iXl3i<^vux-YVQy&VJGJGfriVxMDPs;-uk+0Lw!A$ zWw?`d#6Jy0J4DS1|AJ$9K3g|ZAw+3$e%I{DsPrFMHKbrWLwoyOE-@LW@Rd^&)(yhV z14=FW$9eKKaOG_DP>58;NmLdJgaW&sh$<%xsy@95?mS{!kccI*fG|31VY(iY$tPO5Zl3%wl2APgDddRwl7qO#NG#8 z9xJRzJur^xb8(R!e@&wtQM@c;iG+G-SY~ESb!hcr$?s<~Gki>AKUlsIXJ(@KV%It$ zM<>Bb+UzpK@p)%Hv_JTXR;`aXHvcJo6BD6<62g2!E}`xreJp4M4%8n0`KC`)-mZEI z2Qp}#f*H%bw)0ANU82ro^P%JoeDi&S(28CQ2^Z?E^Lm_;s^OE#V_86$L{MjWjFz6T zQ2i@fMfnY)rW_TmJ!woZ(O_)cqlB2)TNs*I2$Ufej&0WZgjCNFoG6(mrapYx-eZbu zHlnONq3$PcF&)NV^<&BI8enxaatrfVW;P|IbLZFtEgmKc4zHx>=GCQg)9C#6n_6GF zh>AE*s1xYo>^!Qdq2$eq-TV+`^ZT8x1)AjBiJq{(W#+`FEi8WU@pTTdz2K(+s?XT| zsP0xJ>2)4lK0Qs4P#Uf$Ak~VLZ(0y86x+KlJeKchwvAc{ynFely3JYMdIA7eW%CKg zrYlBpb8x3d9DWh#SL8E;gyD+Y8CGa5Cwf zenPNEIp=17=C&nBV3EgGRe2d=;C59vQX~1gk28>5aI?=4Z!yK0#0AEOTIn2Op zI%PrE*GNliHg{Dtp?Z0S5|t(^rpi;ZVbMJ>9id$0G+Omo7AN?Dpw%cZ%rt~>&-v38 zv#{BfsSzGcB7(aMo!7d7XLnQnj{Ov0s{8sIpg0b3OUg?1`0JwT_vJz2%(^Spvlu>e zCMLFwVl(AIQHmIe(_dZ{GFB`646E!%kY;kvDCyBo4;1_F{M9R4I$+_^d29P&{Rby{5oAZ}qB}Zd$G&Mg{q!D~Ym6YT;rt zOrj#D@4I-%GFiMvuPP0H{11m8MdwR%yVYAY#>%Yv=5&Lx>eL6+1yzt@VK|$Hp~2pu zXycl(7t_4j)WQX$RDwx$&e)Ii=Ci^MxeBSlXfG`TaMRreZ}P$x`ap`<74O{?BKg(4 z(86`g&_PJ5)Kz%1rn=gMJBOvJ*5E+TOB<%RyIoudpbU5OMX%9OU7{7w)h=Rbh0<|{)Rt#7p3CRk z63jIgEE0C7R@jBi1y)TfI6QJnBde_F7_kIFU!^4+QY?2|Dn2Z~viSBpgPQe-UF=8J zXVJi77FKQv`oVmUOi|ptWPa6p;lUYG*3!X(O0lX2Jcsx31y%$YsHs0>&6XDyhp_=8 zuW`muaiJ3|fo~*Oq!I&4OgXI_+9Vj{xjp3VYCzf0ForsP84JhO%V7IW*5aX&kEv#j zAFrzkntv)&jIMM#d-niYFTCrTKt9vGR@8c48qF+@)(Y(Zei+%^PP4;%fLzwZ1E*1mcW?%#X_s ziP^EYjz}p(v%T^q%g^6yHYYJz&ZwDjvnEIWml8UB!{Q+!mU!IW_H1B|8Zt{P-rY}gQ;gF-DuDsI#GL7K}wBcPLq*K2t0-_*TvA72#-FwA8H zTzOt=lj#VNpi5~fHMH%KDP6-ub9CAq$}Orp0u(Zk}f>VRnSjquC}!zrJZc z28z{may*}k(A)`j36Ujxg`xJXf_*k=LJ~XG`mSHP@GKZ{4=2AqO+S-Dhz<*Rf?+NL zmrj7Xi{hOE32|5CD)aL`j-vJSCrfeI_9z?H)&ilrTd)9GlGo@;wxG=0y9y?@cDX&P z8FslSDnA2u=^};q?d@ANd~8qD7ClPDb0gIX%PVT)leCN6AB*PfP@W_R-#-Sw9yvV6 z1V8{E!oj=9HCa^HRCDuBKd~dk72V3`;K}27OC{)y>QdHD>lER+gXb}B-3eeGo(#JK z&vP8U^zbpBuK}aGO|UY{%nLGYgiJ5#EY}&m7D}FWOUrEV-~C7ce*b>h$l(ksHXW~j zBgyyn#@5<)bQC+1yt|`Slf@=VK~O+Gv0{$FJ);fE69x*)knR4^zi~9&oJ;0WCLNm) zpQ!v(D|uH(-^cY3k!y6#V4P z|Ha;0M#Z&teZDzy0)*fJ0t9z=PjDx=LvVKsQji1-9-PA6B~W+)72LIO3JP}#S~y)K z=REg$yWiX6z1`#X7xF!2AIp#-Rn6qi3+t4QvF_x;3-Q-128&j;7E*g>_T)C3)W=`0o5Zwel@ z?|P(TZ0+9Ny4~5eobV@OG`)UY?JhUO-d_^0mReSkQ)Soi^?fqCR zAW~Su?UPePmO(Ad-JXe0yy|ltASYKx>l~}(qs8Pm$QJU~po`-b2mp#sE z)HvGJHx+MRQRu@QmH%8UeyN?pm-gs&)^_p+Q1&s2P|JaqXtIYS&Z2K_f;rBcCZXEbrA<$&>%Cz7k7Ij;`e9 z;nCkdZRd#L;I909k^^R!A|u(<41^tM~cCM&ju9#%@jpXRl#3^ zGPY`H9rQ$0GFe&g%G$2eyc2&k-dsng(kzyp6SzgdU6>dTtEQv@ zZ0-vfOXTY)+M>tz`2T)294~Fwua^E4&4HNHjf&0V$))_IUQ>8+7-n)4KXvH^7AmEr zXB1>-z90gf3re?TllKCLg{=y$r;*Hf0 zqhp3-B(BRM`(ST!mgxd_tc}sGSFtQdl3}>iWp+;-s98my;@4cmT~XiK_beIr8cHoY z>^B>rZN>r{JWcUVc?=uT5^7rbB%YtS#vSM0s*csQBx zGO)8IXsGN7kdVo6yqZ9+EOpMPa(0v1jE##cgh?+5W^P5ibSvX7CQ(C1q2vZplqiia znUDyt$nWbL*Jg*K;i>3qTeyYA1GhUnWG()dWMxivx%o;_&cNOJyxCklNI&hc-1VJb zI2^T*a#7?TtZa~YA3Apb0ABLDr_=}g9q$l<1@-{VByCbH<9CV2VY#7m?CN{nHo1-m zVL`%FRX18sa>$JNRA=&;F8fupjKxd!4VxOOowCJdbD4g_RX^%%lWh8(O}CCG1-6Yi zve%5_Ef9hhS-M|7mGw8_Sn?o9d*vQ$4+_y^wl8V11*OY3{c9+0nO6gM>3qJ1*Aw8~ z3~s|&>}{H%W$952D)^_H)k^<)e1KR=nA+pZ$$w%2*s1@pg1d%AskbeTEBNQT0kEBF zZ?8F_H^7G-n5Der?8rT%n%1Yz!}Xl~{pbzS|G>c^O|H4UioNTx7}9=2)#$Fc8y_vo z{-pYZH^n>EA&t*ypI_o%Ae;wL>*XO~2CCTq3nHr}A8Ko-IB6_QJ_be~4XuvS5l3pu zxbP5f$4r;Z3J@GR1GhLnd9&~C9tuGLA_yJkZUQoxlS0w>4 zaS0VaayCA@+}&Tm-Y03qgnPI*IbBZQ4)wN#OirD%qP%D^nOWK1$+@d(@MaVm>J>>q zZs$MBHlShTD}A)k#}`5r7tWObr4+k!0`%n(;>wa~pEx-D^sF~Y{JCiMRxg!MCtAce z6pI*(!7Q5OXW(I<#cj*AMx=1++XT>>*#xohHXLdY9fz4Xvb02~NSrtC-M1n<-<`I- z;`Qv=^YE_l?<0ImyQ$9;>^qWJx!v|3UJlQmP4;0AN?Hg2rJ+m6A)aXEKXZ1a_2a#Y zTx*DRXGCs&ep0?h3c_f(UWHPa!-RPj>V#1F~`I-j!eI>#&fo z*zuE6zc<2)G(-BQ$Zf~wEIL!jTk9gxV&TQ~|ZmmF>54PpGRu-C%Dab|C z=Z`C1?!?-WE^p_{AUn6-$a>qzzdkG< zfgCap6!1(`J9ks+*tE0kDoTGc);pyDA!N~Ak=M(uG`uDE+J{#{!=jj=Vqd*`prUy{ z(p%Uk$MazqbK4U@pWE$D?kT*##lk%;*N?^|u$}T##8l;dv@Egn^+TiQK45$81YWU+_f)b)E6~xN?m3WRuzUbaGn!VRJOh^=)grTJeB=zG*IN`t&$U*Sc zPMuzn79kbk9L*vtihH8({c^v11^oGftPBfAzyR3Z{EIz4NpDXit?pbN;$1D`t2Br_ zUKgy{iVc-SsqwIG&>KbmuZj%*%K`ObC2xKyU78M~qZ+pL-?PA+L#IZD6C`o{;d-zj z(pS7ov5=Ot(Mz|kAgs5ZUg&a^us2&f$1`+Zx`g8Y7MRljiN`Fk7a8z8!)FLUvWr#a zQoI}TyVX~~(`>E>KGd=vrwaHPPbu*7MP|p(x*)#MJ>S_v6)xtz?Yz$fn5@9&Dh> zD=dt}#)8l#wmT;=DHrAyydhr3eLE(iP`F@Da7mJM?GLc+a{N7K_lcQvU8|#vQge2K z*E6%TX9iMYL$rzy?#5@ubw7=QqN@3g1Gz<1dv&ZCTpMSpgCX#-y&&Q|+?w^prom-X z*9uS1^(YaqSiR6qB4Bt)v&P_%)YMUKjTLNj59 ze$bu0({b{tzGvR8ArJ$7bcC8p!1oF*7IUI_dJ8@CWe(h5#Pfzczt$gwQ z^)XImIs*Lv$kO?JxBq78;7AQ<^$cBS9>+oOku++9dEbdYndrodq-KT5 zuwG@zwToT&gQ%wI{(wf-tA)Ntm1MTp5)5+0bWEiG8{CGmgmh zL}#;%x(`XEObGOMLMB$z37+o!kmS^j{|k;Y@t5Q%yUZ3<3$i=AQpDU8T|hP4R-^f> zKG#mnih6ZTI!DBXYt@It3))GRpm25>VAK>hV0*E^uXxyy3~jvGm%Bp?j3}lZ^)Q!P z;JwlQd>YbBEmfCOS!Bbobe4}=;A)+#Yw7dDYXcE4kI*`T`L74YOMt`haxB#q&U#clVa z&}r=$^+ruwJ3qi`5HMh%DH53xKfpgY z#jG&waQRW_Pm}jsV|RPHJIBG{U3v&;@ZG6j%hsSkx}d;oxAP8QKk4-7tj4LSto%ug z_k!gDRLy;-?ZFL){LPk0-2l+fZ1st6-*cZNH1(v0b&rTanoVWHXdzc|A2$(11jZeb zpQ9K20@w2(&GJ;_=^QCFpT#i2RmqG>TOar4-bPUPrNC=~qS_$EO3v}Cvn7r$^2QPl zV|B$&@9QCld!Wx%GMT>qgfM5@@jYefzk}+a0;DEUj?-_-$~nr=1B;eN(OA}^@?QXs zQG5qHU2Z;5+;|XTXBgC`Ft<%sgQH2t?O4oY@h@8l7MRqY2h#fr7Uwal_joMKj%6LZ zxD@)JpR~Oro<&|zy!BWlZ%LmBGI^ajv1HietGD{wN&iOc?76HM3e2n{sHnAdHI(D1 zYbU1&OP9mTa61VOQ;lqV8-TPfp|v>cv>|!ME+GqwFh$k)@~U;Y9LB(--RwaE6&tfw zz(t-{fwcUv1+QGQqNdr8(L}qh;ure&7#fop&cG*x4Pu7hL6y0)eW8JW4VfD3Sb|OL z1qpVxUk#PR4Rnltr0en{u@(Q1C_2?jRc>>rL*Q}g?6{d7?lt_ltAx$q50wo~&iYHxEPW-_q2m|2p{O9!)JjGOY&Dl@L#lF{X#>LmyTrL}wOB%-F{0tg z8nq&$@r_3X9Q}RQbaNEV{IA_g|Gx}Om#1t2)o&I0-due=cpvkry~ZcpY0hBou#e#& zmDiyKdR1&=P%I*<8DzuOQoRx%s$&BdFy7j3f?O*ScnqA+f@|xm(fvF>b+&2CRgttH z&N{wD`WLL&YY|E@f7bU>$Oi8fUWKkfVBbB`ybW(!-LW9o`RA6BRlE7(lb*j$J?w5n z+kEFZPR2@09KKoI)yvhNlsDDLZElqMoI_AKD2Fi4nl26pbbT-2QS;cF#XaQ-m&y3n z7}XhYp)h-<^Fcc~{jO}d9YYz2t&#)y0Q=@}O61l8aJEU-2<-VL$uIANCGIZf9~f_S zzr{`i($8|#=xQ}= zhrFA{Og*4*g-{P1n_NZJhy_uM1me6IPDTFYojx~p$vICs%sv-X7u8&tQJ7e93}DM! zg3>&NkZ><1F7+$p3@sKJLv+b9-_2Q*0)Lov-n>rI#RV8LW@Yt+Y9}5mxKq@$F91_G z8dFBS*A^-JxnA}vuvIsZDHoN7c-P^WGnymAVbXL3kYDHKC&z)ikTral-w(gk!4 z42S)AVZY2-Ndy(0tFdjBBojr#m|MvadTDW~0YsQfU&ELky}47PhK>i>{d#fxc`OvV zYHMS*zkTYKN`4S=N$zG|^&=~*r7%-e9T-ox7T%d6D?jAI#G|W)s}IA!5g2a{08QHK zs4f#ZTOgp#;+qa);d$N;I9;C2tHe^D!iT4dFw zEXJD@(6wae%mu6fvCnw3xdPi6D>oG>r1u&bZ-wwVd3JG$ZloKU-67j?pL4)W-w)EDlo zQ-vQdrZ;yB`Yx>137Hn&t}k45KRWg67WQXX5BtcfiM8yJ@dS_##s1izb;X{B`bflhN0nczR-CN*>~CNVt~vPQ1Tv_-WL(65jYVV#3*|m|1}ymt{5RV8h8lXIGqANu&SR zl^}9CLtRe%jRFh4cXQgD{m8sSZK#@+N_h)o#b;9XzMiog;_!d;NkK2=bvR&!F(dwL z49(-@z2jW_^25^6+p;P2s7DbERcLDsHk$$L>dE0X6XpdLTX})VfOVc>_0M7!#vYed z%7QgnQx#0B#%a0NY3pMXR#f(lSpn2pqJ9v0I$U*~8`osjvhP?W_VdMppln;EciH^ zZL~7Darp74Xxz>)ZE~GFkn0)02&4LFMiG??S^Hgm1)}pE5+O|I=##Tmo(b!rtDypi zpHYC_W^*5iw1b&&b!Cb6*cFu5=E^1!^v;s?S@0$MZZ6PzOl4`LAyOzaHHw7soH}Zy zTzDma7`U{dWylVcduI61=@@hzORj~2)FiF77_A7FU7m85hET=~4_|HC4n=QMl}d{y zre&51&-S}<;`Le?hEb6Rr8A^#5BdPxjjdUqwN&@n)6T0|nX5M_O>h$nm9ofSaOxan z^K=9@^4O%87VW5IYFa&v>!`|INjfb&V53R(XSnz$7GSSAMZ51iJfQcw|J70`4dS+9 zuV<*+AypzN=?R-?Be)?l&9OmfuUpYoZ<8aHxYoU{d&cRjdort6e&&k*l*p!OBJDw+ zmQ&4-<9B<{q@(}>x8-LqqvrO=>-W%8RPhrj3oIO_lM>^BWuxlJ4`HTX(|Z8zPMqFd%{D=TY`wlM@ zi@H%WQ2ug+8TG~todP!xX2H7M&|-IPU(FM30d~2RhbI-FBIo}R+p`Bp!fx<47X(c5 z5G1ygU+VR1Uq+=~qJI$67L*yoomF+tVoBZXCZY&IhyL2oqa=PB8|UK&b< zrDG2wfhYl^oaqwvCKZ{w1!-N%h%LkpMr{Gz6l?{LA0xr{BY3QP zE#G3@V(;8ebt3bv{r*zjO$+~Qdk3;*)Bl9yBUI47p9#{uCj~LZxaeFHQ~#f!`q0pN zd}cAdlN7#>JN=dqr4EZXJQYlP1o+FqLl@b0?WMZTI)7PVuVR1-oz1ju)wk-DDY_8t zy3yR}n6zdy{nIpE1xlH`xDYd$6UyLt`f-AeS!@0Q^8`B$Eo+;UU7uQV*bE1LUAv4@ zrIxmJeiwbMj{MUSEnS_2rNT0HzH{gs``p)Sn~0N_Cx)%!>5hnSXQZ8({O@3VN)^cy zWYYLSMO);Fd)4FWK$kjNPHiAonE$t26_vy(OXW3uYkUE;D2Gbx(3*=tpx5A;hH{k( zSv-C2bqM7i+%;?7_{X>7+6@`B39l4D;T$X#zHdhz`X&v6&epi!>^=#L1;C0h(>?cu z&3)G9ht_y*R(q??0mPW2@?ra}X$Kr3{T1;Y`uObyDIIwX6Kql#BruQC{yqM^<(@Vh zhh_$$?6v9EX42}tviXB1WzVTufq-X9(VGl-?4&q1`~J=tNFW|o}wm+9>;^-nX5{RfV!79Q5n(h>5&))o`{Y;0){H~ zo-5*N!A}yqmBggYI|nL)lFsw!A*lG2T}Oy4ZD$wRjnT*ig#HwsaATYH9pnIfjj#)@oWx(9Z<=D*pDBRRWi4CvqlaMznu^&SD+`d%blH!jHirpe7yQPu4T;1 z>W-{oSurg6y^@nSooA!IIxeE(W7>ZQzVWX0G2^kTVCALUzTPO_?wjllJYfuxtboO& z#hVdXj+HVNaYndec)4s8bw+2il}6sV*_IDz8~%j%lc`d%Yu+-JygLvGcB|V$<#>_nhZIdR4>QkYJL(RlKbbvE;W(htQpELt@aYp4^vo-(-{_?$Poh&1NC=b^ z*KPDrq-qp!x`dSvXHY%1+#M;%dju?n0NbcfL7*FUEtj;7L=)kbIP~GfT_FOJ5$F}uxCAJBI+F;9G zCA?G=K&_ztUd}#^h7=o~{|P0?EXm%+?)^S@j!C5sg-ep|uwu2E9i5#k?bJqL$jxp_ z8nr%~KCXK2oMc*%lk*0(JxtGAD_1WMOM7!_a3o5o9rB%+qD(RKcYyY5@>}|@yF32_ z;k`Q;B^lizi2Kehz?)KY>|vX@R@o#{~iM zC$fWtLm>5eLoZMd-aK!KHP!Tkm$Hg@K%b8xL(&i34shjHi3*rc$(v+wn?Lp*#m%`u zD;9;3&47{MpwS{>1g`w}uPtofBv!18P|6{#+Cf&_u}ma0IGMpshc8P|z0+Az7343E zCE3`R%cyEu2M!E%<=ZLb(Uww9!=XQtLtRacH!&+lL#b5LmGo;*fA^2?Ko7u7yUA%> zwwHB0_0Dc@3s%DTGb9JSj&LX-(RKK{3|=KGpxw~7F0Oia=BRt>%N0F&QLy=ZE;$9{cQ4V8zM-!=JL-CvbsqGQo2+o+!YJwACQ^?4iC@{JlK#y= zc+hGrc)&i(H-b+bpqVtn4ZhZYjGO!C3}CQ_BCp(A>4A`scDLy~pW96@pS%9v`5<{c z@1_rcxX7RUvf#UA{`%t2)Km;Y#W%Mo1k!s5APfKRTVP#^Vaf{9E09{Pa+y3@$kUNi2V2S48K@HQSx9-UxspL{PM^r z`ZKfzMCzVP`v)>KLYH>n9q(MQQnwL1;4%O2SD-?qzNgw%Al71#td-hxU2pbi3F`0a zmY@7?{t8kc0=C?dfGM&n+J)u=ZN`leI-}c>Ifeh z!6p&GmwK{-%o$O*9^4M8vMn+0GhpK^lba8qp1S6m7D+F(N3D3DvT5I@K2cLkaHmwhiU;1iNlCcKn4YXw2WF7#fdf#y@vR=%B z7k}GCt4-=7veVh#oVwu%n%0kNuurqI(Hkh)_lc%_Z&FKN9`9DHhpMmV-(hePNu@sv z?wy+J{By~P2o{?9cI7JkZbmt$8B>_9I(f`Iau`BP}F*>T}Us9a`5^ z{=MH28D!J8@$xgJF%xWZv99AH#0ru_a^9!4B{D10oCGYmMIJb3d%+un&^fPn)Tmxz zEsjGVyn)Ae3lI!%Ndh`0`7lLn|32(TPO>lGFJo)Ac*accr9U?_)-C531?Tri*fY3T zD2C_*P`}WvJHQ*YTk0;CH@c0EPB~`(tkaPAleQtGZtbeO0kwi!b+}@=!uPNv8VTSC z^XpeLKxUHB=53BoZVR^tY0ThctY&e{xBJOA=rmXLVScDDV=I7ceyEH(IBnlW zw_zw_NY~c6CYEkR^APTJDBfQ=3TJk(E=O9;`u;i; z&}D1>N*MusmZET1PmMl>Z6FD1PfE7YTqpM3aEyu#M|m0h(PQh68;nLNef2U6*B?WV z<``>L{{}!KD74CcaaKAVb+$e9Pb{G3IOf)tYmQ})`J1XK@d6K`)k&3Dv$8Vp~BM;|tqE zgmK=G@G#}w_I*}aB$ zA5*8xx^^Zo#Gkb+=-#PLaqzyJ3P&cu`B#1?L4U3iiM_=U@BDV%G6ILpFT4X@H`oRoEoLLjhAv`34XkSU&~#04nQ(k zQpVr$iuVmQnNR9@y%-Z1ao87h%ZKl6AGn-)Z;}pc`6WcY3J?#LIKK_(4q)pAtq(m% z7n*i6H{9sW!PYbWr!>HTlo)&#%D}PQ^8O06v^omC*;fVZ#{!xQ1m6s@K9)3*l-^8z z?rwjMMmV+}z*g7EoJ2rqFt^aS(h+@E&wt;XHCX0DX7^F)4@V3%hvSbmasu@XSK29Qe|58G zv|s5x+Q-&Ya*4vm_e7S;`=o`Fy)qb@dyxJsEhVNRtYQW^@1*W&w~k{_)Np|})JTJW zp!#AT^`1M1EC={ET}h5F>&-hq$XiomQj0(TeUKw+Lih(@v?pk<^y#tv-)bS&!2e(O z^Y6N!|HE1zM@cox_9{$9vA`fQqd!6#`MBF#UyO~j`g!}voxOds#PygXU9C^kdLg`& zoCAZWXD27TL%dD?{>XNP_aEw@&wi|c$7Nle1EIv3&s3nY6O;fKDyKAN{4%+}^<(v=mYh4TZevN3k`73gk&*U$X=x7& z{J%v&beUjKY!@ftWDjk`C821LStGx|pW*(Tl2)^v-g8;TuTsXMR>8uQ$$yYi(7nFT z*XIUtFD~<5d^oi;noW#uV;@P8!?yjdM{;OW{0}taL5pMzc_2jV#|hLx(Ul`KN8EjeJ{MZ@5k@3f|lTn@8 z@^h^oL#k^Ra8ust5?PuPLXnNPUB|z$a@uxelmFLsKI~QZKt*IJiw+J!StRi8WHids z?z_BtwcYR=A;+uKK-B5NcgIMS85_*fl@VF`_S)V?Uq;baJI77!z&N*P#@a~Lv&P9a ze$=V0XKae$+j~t5C+y!V`<(gzEn&|3*4ivWbFCm0P{RqqzQLxSw5zDAzLJdPo?4|H zVtK+|g;w@;cXaFGU6(X84B;cY6Ppl3m{XGIUGaeo=XZ(B%f#YN(H75p=8=r6w!Dv* zas=>h0DFBqqG&9O8$E*-2|eof97;ko&296WcvtA9T<3}A{)>8a(Me;Luy;17kxF*k zq$V)_8M~`-zD1okYgo~|Bh<}|tE-mdBvv=YI+P=387EM3s-R{z7c~E~!$QMv+tLeW zI1?+=SDtlaZdJ%rm$5VppRlrWQz`6$?=6a?-aWo=mb(E<{(b+xd#fomSnJ)0p|4}- zxNck{xLk(s8?AZR7mWh=;El9|@-M;{1AL~};bLEfy6uXXwCq$(apNsJy6hULo`Ruc ztEMs4X1G)(l-!_JS`9PXqL0K@K-^UGb8-Dx2t843?u=Sop5tWP>J~A3jVOJV_KqIc zhr$taDuGk&r^4SuCQ`y&;<37oaukYHA~`>4AWN{7Jt1R8$lQEqsJcXascLg$@+r?K zj|c-U_YQsP?CI=Rbhk@4w=+-%g@&1#G*hEqPMT6qa@+E$Y@@8CM^$prG7CBYw>Lhw ziWkl(ALIOnLROdQ5gG$s#WF6_tE2Bu(yJ=q7FfEL;6F*3+}KPW=2}Pw z%)R8fi`u@+XcqVqR;+XV!1wX`(1Z2?R^IlvXqJrh-%)XB8xnQbj_4CLnJZNK+q{H& z2db;(z4J~zrbh4*4YGl{`=Jak=x8544ez7p@JHilM;mx}4#6K`H2?nogQG7TBjYPL z-Fu%{1i`Zbb8!)nv?ZEj`g#YE42d8ToDc^LB;5FA1po-}gYHI|DSCek!{*PNIW+lSL*eVPBps?sa+8nSM(1VsqMpto1c$8ISo5mVk$v z{BB_qE+36aPQj2+hqyw1@aA~yZigqB!-f!z@8OrviUtiYYLvyCanZvS+Rx&C@rkpu zbL$f8^7KN7=cGRPWz&GEUlF1aHntu(=&pK@@=>)pe%jtoPU>DqJ@+y08Hg*12!hF- z>)z)nBgbuI?6mp0zBe9!v_-ER%_`0l7qvzLwg_XfoUF3afLs z%sm5L@Z08lr_k`V+zZYt+8jbY;_%1wQjr9F&W?~F9}~r~T5M-U1RcYq1FV{iI;LH4 z&Yf?rts~iqA3apQvH1hL=_IVkL=tU(y_b47GXzdDTkB)|7+%Od&S+dH6Pk`0yZNrx z4jM9tabi`aNWC_u;Db8V-NhWya)Xw|&l78JaUA}dNXTO$X}<=WtH_(1itur?Nxs5s zKGR=4(b(n#ua_*UI7herzu7{3v4fwh@Yu;W-tgw|AAgKVb|v96>Fcl>H39RS-(n~Z z9)WodhSogx^-_oCZq(rUjkMZOBDMoB(?;Vj;YC=cmLO+F^?Zx@Mf55{QjeK?}6A(PA+#8 z%!ciF%?bP41y_~DbEu!r9{cQwKxweJ!OJArclxlz zLuVc?&QzhR5?k#v))VEWS7k%({yEreG<7oIQ=O^ z#Cj>TsHK4q{X|PWRj-ATS&G$kB4qs3gm-)PE8#?gwre$)DYbrMT%3$jtOfVUV=^{9 z{`iw1DBYHl9y(3r6eiep-*{CIL;z>O`=l-70?syz-3Rqk<)33M`qz3Z3A@B@( z-?RR;W8azc1CMm)f`QK9y7YqdlqRo3hT#x~!Gx=oRHXX5FMO!8ygbM^4yz{dnzyQI_iQt(yL8&*n4}_RV-A_h4$B0QR4aPNvh?X`YmvL(?8nqrQ{S z&h5enxN~FubAvcnen-$-G5F?Rc7z3X?tim_EQM8ScXk%^)y>-{UPQF1WpNTnGc3wD z3DPA#ut+b#wpocN^~q^jT1y!*tG26s^=FsLNy=|C2yt%_n92z&$t(z_9SKy@`AfTj zxM?Q-(UWmCi?cz z9`c7O2FEKekUqHn1!Y`V3U>csh_j1nCGi`X*w-Z`eZ>Q2@%dG1uz~)k3J+IK zaJv0o2A)^&v4nvw{$-}%%usnGQJoFx&Q<{oTRcDYLkTmAme2Yo-%O{l~_h3zAfIm^Y zoNRC~z2yG10drHIpWR%Q4uF8TgMR49v;BMoEq=80TFH;D+wE?!iNTA@^Yd5z`7NAe zn-P0WJ(|IdxkX=$%Oqyl$cY%6aNeVd3Iua?LuD7@`Uk(XNMEE%S)SS4Psi+IsXm~IQ&22gK;}c@^eFJ<5N@ex~Wx1&*jK+mdFmqnKr0yI_u-CZ{?)a66 zHp=5tfe$f|d>hCPuGvYH0>ew(OP`Q!JFe{^6Hj27oj7k)R5-Qa@40?elZ_|Zf_<93 zVN!Agm4+Tt2+NIYtd^24$s|vNUROI|`K6@OoS1{Vg3vwFG0F}K9L=3P;I_&R+DY9LF_)Sw8i+tXF&0|k%4E=~_0>H+ z0nxElh1z#Tgg;Ze?U}quQiyR`jn^YNq&A0jhKAV429@OowKtl+-K7`Zcr3R1O{e6e zOx>?}H6Ii1^ShO)y&ag`>idr#=|6O}vxMKwmmSvG8gAr{QQ4j>ud0kE`Q;IVkRdGL z_4rYvxZTAT(z;YxXfh+CN@|r`d&|w1-LZ{(?LmFS)v9)X!Bf=;gW&R#@xsaogQl!) zJ4S;Ff`TK(n-)${ah30!11H>ac$>3jrw(JWei{L6HA7{5@(i* zy;;PdWVj*R@l?mO56)3AM`G*{u36XosyafDmkEHJ$i!|Fq8C`2%v#YO+ka}JTMhB^ ztP^X2PDHzcl^Lj9J{C!1+yCh$j~`zI&^4y2GX8 zQiL4IBf;&waGu)tYmaD@pc5!hB;q)R?4b1{yfv#Z{=+Z@1N}3*Q7PQayQ(jZE zRdqjxqF5C5_><6UY3J^?lz8kF2aWR!>;Y7v znjrWtQ~y(#@Xy9TI;%U)7Pui)j@tq5kRkq!as1%Ddh1(^gLnACR?esF5?H6-BF#eM zTyDV!76>D+Z%5<_6Q^MCyjdLatL9^A4cPQM(Oi=RgCP_Cb2g4!LCh<~&d3lf<;iUr zVeRIY*%dI*V<7TXgN$V7e%uB?Q}q#yHUExAzoZ3^+qg`yhQ@Lr6{;=8ju?KkrZhpa zI35pYbMy5)RsvC!BII%rEfuwOOZxNpfkqX8qd8#mmUc?wgqviUNHh_bw~L|ytm;q%vDO@tpXy=ir$^nK6Q5%(g6Rht@@ysD6Nw?bI@uG;u{j41 z94B0T)D;bEs^zA!+cKqFdK^9(FX<8$D|1hM>Ka9T}4* zAr&$EF^?C1BF|lI?CpnxYu|v5f_1|n3Xs2N6N$XvI0whGqoh9Ky23N~i%a+y3DVPw z2G&olswO-Z?5YW*4Fhl7URQWl{C2LtPGf(f--nGZpwAbSu=Rhp($2O!> zB-&qF=TdJLK5_*_23s%LYd~cI zJnp}8l@?x;yu-C9>}VG4sKJ)NBrQwjV4jZi6=X|$tYpAXlfRJ>5xUH3xR&L1jCW8v zlk=o0y@FYnk2{n}z@1fW&DW&T zd(M@YJp-YioW7NVHF|Dz%{RzW;2x@Ztpx>Da-3>+x%0DTt~*GDmN&f$jX?cQ_xzTJhkx#)YZxLOWt5uv+VLV($hr1g ze=YkcmfWd{=jfGi6dH@KlkBxRz2hna)C+}4Z{s$o`XR9{CFY?|&L!gc;Zo?A@HT&yL#xT5zOTsLZG5tBJ+Ylx}Qp}zB~*ZPhFeyg+v z<&ll6b8%BhRIWY9&1y9k>uV}vtLzlI1-opm-YNx$#w>^Js0S&1j1!sr-gIra;vTJ; zaLo;&5vm0Ig8Dy{J`0k#({qfTw^@&@AAJ;jSuR}!9@W@4K(v0%xzNA}CgSI04^XY< z@)`mM%Ns!x56)SgU0Hc<#m8b;&Lqmb-0R0b8J*7J(lyReCHCT-=6WD3qNnAr9t0;H zVU&5hDJVqXkm@(Q1y9qkhk9H+uo4U%G`A<>>xv0*!0Xm&HWMjhCVYDmx)>5u-KkJy zP^+}MX<#3;T633L9r&`+mLv^y<|Xk@Ea25r$2Df0#+kP7oN0 zJ>sKtLfxQb^EP=V9&D)h@)R?jTX=&2_}*PW%C?CX z=1Po@i%|Po9(kXl{hC5YGq+(^WIL=E4qGFuv1929U|DZ+Vs@S%@(}fD5??UL@%9{9 z^K=>GCYUE|aJl`FC6uRY%8pu~mDmE9@1`?(7#t9At^FRyg=CY|?y}CM7I&(&vzB#K zMarzN&i5uDByt^p-0;MAn4L!1`+KzIhU5#W8rdJ~Apzq_s$UC6W+WERc7C zV!%Vmnj1SV?H6by4dkLTr|}-Rb8qQ=qwHdE|F%5h&gFt?^jgVP!qD1`T555pm+Y%@ zCD05vxu4+@<+;>ycY%XGZc(tc@kjfyCAFQy2t^MUhytg$qF$Adax_5D-S~Ta#Zm;2 zNW{=dbkuXVO`*WW*$@7j*r>SzO6ui?QFk`EuDCtIr>-e#`*U4Z0*8*dkA)kvOi$w@ zr63PmN>x22U~~2>dD&<#BW-a*zq4_e;HQAcEiL2>2jgq?qhI7f?HTjKo1L8K^Q5YR z(ci_Iy2{{G!*h^>_IEFrLX)17$Fft=MOdx(fF1SBZ$|0W+k)J5p+$vLCb#&IV?-H8 zkoHbl!Qi)>ulAy71U!C+qt*5B1nv=%7>)eo=`2u91ESD?TZw%6`|?-Vb+RZ?4L^|q zPFw18?j?kNf}>h`^IHFceE`!5=Ftuk0Lj_uoHv?F$2>Md^R?SfGu8QRUO4s}J}*=F z2Vd>W{Wzbw1wBzHU6_JoPRMH;cA_YTbxg%+0V$Bu8ub7v6ZM;DaoA=T)yl+;T#Fpt z%TMO}#cqkiesyPD=8Ljn*i1jvL{!pi!kr8^DuZO?NUNez=xl2{cVG$(u|%E*OU*Z* z0+?RzWA2M_YKFyePqAL?u<6{e5{8;xcnG6K{I~YLDlCq!SvLtGKoUX>2u?z94K9NO zg1hTLaMxjQ7zhr*-F<+;-622-u7kU~1a~+S$oKEP&-pL*zB(7@%vC?rYpw3CT3ubM z-gi}Tzj~|Hwek|^R?=sr(E0R+QzCLykJz@5>Gjkvl9ealWB0G!pxs8Jg=lH`wQkew zVErrStbKo3N_pd;T9-JON0@Q!ZjpH0P2NGh`IeB=K;6r_5k+q~(;=)e*VOoXAHOm$ zP8}5yA0VQmEM|nK_Q}8Vo6EvvYUEU$@-hn5>SB5A*0f#|5vz@L^~>#T!~XImIZN~y z+v<)#q1AesUl@}?pR3_=tTBnwPg=6!$oy@AbTYuJ24i9d~1_qMJzgl>byWmCOK0*}E(?iRSOen7Jm zX2GvhnEre>Qf3L9(#BvVN73VRPw!})cbTGycdczYd9+7dxx zNG;&qcjnAtkc>3r)O|Klah**&BJJc+@oV;yGvrvMvCp!YNV(Mc%kj_(k+k;Z>2vJ3 zAwAe7Oxu*`*M&zx33*HA;O6r$6j@D%+Ru<&ru}xl3lV@uMy|MiT+L-$_G}M&I!tgW z(@PIL?mzpDLv39;ltY_Q24&~#(9O$y*ivip8{FF>QCm0jDH-qPA^{})gsOj6<19!5 zVDPK_YwbMQ#e)sl%R>sngd4uWpxeKn@2QbQkAZ|8SOH^{Vq8HFn?dq-uC!e?+nz}O zZwkl@m*>_}ed1kjB@)?4!!-hu;@<9`59g*xPn=v0eSAGkUs`6kM*Ps%>gt2f_m4Fk zq7bWJ`H9$JHO}@h{?=(ZoJ|X?Ad{d;KK3M(5S)aqxP&-c#xc-hKorSH-BS3l#4htY z-0B@=<#;L>Rl_));ID(G-p!5C8|Z|L&eY{r(7|I)rssZ%B}Q)5qxLEfI%i#&3!s!@ z>ZwEFp&phEjQ$^?h8p!Up-EOF?0!;O^sgA9C~r7WYvQbrVz(zcuZ4q{pzyiH!0U$c z1Z-f3(Q+fW)KeI)<;-OE^Y>u72Ks`SYljCiwtj==`8p|Y??kWo)8reo9%`Z>5f^Y^ zA~rt$?h?js$24NO9S`5wV#2=-%19{2Q+2^4LO|XzV5)??F_qBjLk-nocjcIZBS|`_rJjBX zs8ZuA%9rCvzg91aS~OG~)-SOsVPP7nOeTtINMR=~PDiL>N}*w);W5RR=}`OSb+rBi zCNb20@h&OVGi|YJPD_!F9O)Rb@@ChzkM_5!HPJjnAYnLjnCF>U>x4X-GM-a?fu30trq#N?_4)&jrPA}#%_)| zo=hBFRS99Fa2xKtvrzca&?sB1lCuc7Fb&RmU(_oyhnW)QT2oEdhnc-Mozw?$ecM29 zs)cG)L_s*1#cC zO1!&qv=%!Z)6p(Yne7eV5J^2N7wgo0ghCC3C}y`vB|>YuuGt)0Yuq&+%kDil;4hyo ziI3Mg*3q4%%Nw{s-!apm*I6Zw^=rlEgYIhZ4%se`e%oxdq_{959m%(wPbE`JtbLo^ zgB_UR&3i?Y<;EL7cQ+%b8z+-go!`Jj)SC7))fktr(XE}8q&E7 zv{zn1IW?PD%hCLf!Kr7xQ#TpSFfVchNZw0`+0{8y>ohewGPkGf^xSE0M4M-Kp{V~> z`#KQ^34pr)?&kgBNvL*@6#a{iq)gFS4D2R*rw*Mp+!-#k9PyQ-zLkdc@l@=!si-?A7x9w0B^dW zQwo&Uw`^mD+0+bP!5|M^ozViVBmLg>_+NS1tb|mHj{)M!dy}UhvdrW!P6Uhkc+-#k z-?3>QRfjbLIMbq3VW2JM4eY39*NjY3G`#2{K)c4ouf-!ErR`SUr}M7?`OR-zr7UGgr0H+A!1^FiS_OtKx6m{4#po=~wZzLC&k_Ig^yF4g(V4Pj*64sn z1}rygs)QNPBtH;XVP3@C--yq~{o|HHCpSReIGK*L<)=x%* z>CWi{@mQTqAnEyasbFkY%Lyt_V0_c=`cU=+m|QWXUjKXz1>~shn&IF$5kL8H3PE8< z{K}1YG`t@LG96I`BDIDFe}3rg7~Le`Ciq$e;%K%cV2`1N!4^_jxO*GXeZ%T%TGdRc z-?k6Kd%TO}ih|RH+30eJuV}u9NCaYR2P$O<|AsxaBGP%yL;8f&AOm}< z;^wGQxdD(epL5e{`29CyrkQDV(|X2wT9R-j1w+@It#)gr*J$VyM2LeHmwE&X$I%1H zxeUE?gHb)j!kn>EZrB@zrI};@sy;L;{t9w23hD$2Q&9B9hwV|P_R(rTv47A;dEZ5 zOPpIbA6!?uQa4API6+r8noGAmO?g$4w4Iv^?%<9AD2pkvt=`~5Ksk#9v2FPP|9Yn# z-Dl!@OHwjVuN^svQ$n7m{6#z)SK<4U%`lFT(`@z)&(E&B#2B8kjnqy@g+@cnlab(l zSc2!w*)F$+E;inwc-@PE%Nk!o~jx5TVs> zs$Pp=^Ahh+MFT|hi#n_Kfs*q#<%ZT4?~aeNGyb>sv|fT_uIlwHncW(C8+2>+&}#0B zmYJ%Pw+V%v7jVcJ#S~YX)71A9nhOBcmUO#gInn4n-Ug3ndTFr9GBXVjQHKXjCDS=x zZy<2Geg_e>X`c2s1k$g&LGu+A3GPuSgJ>%Rn^$6E9LjAR-cq?F8t@#aCDIC5rLAFjQ^t^R}W zq)M9h@riT~#5@6cXYQb`)*7yh74*)iCtgPWh~SgwCJo)D?5kkK9+^^_2k^JvOB2|O zkBY$ifWR5islm1pZdG+N^W6~dxS5&BH%72s3qs!6j&fFPxkBlG5rwu5z=TBkFw~?_$KerN}!{;S58Kt0I2b}(b1Yfwe^b| zSL7I8*>5}?a>Fz55=TYWpga+sJ^2Y<<9o|n>)KDxGMv! ztJ<*90JPs=g$CXm-Az!!dT?8p{s$(TEyam5#bszfXG{p~*1=m3XVt;1{xfl3%Xsu4E$`gfVcl*{#MNJ2 z0D|lM4vog*t?n%c{Hp)uY#JjT2wlGjydIvZ7_PJgoNI?rg=4A^sSd}RV(PCA2UIj) zpU(A=d~Jgjx{_(~I5qaH4x$#}nB-N>ar>ToproDvG=Dgj>q<7>L5S~7X#b4LpomY= znR2P3V(O3_y44YXs&Y2FIK2pUTS$V^Cq*PxXSB-zC=^rb-Z(4wYk!I}yrmcmCDVqn zQKOAqT8$*N^I8$^j9uShd;>n^U!Q5y)+R9sIx|6a?SWY}9STx};pEd_-f%v4jc^vV zhUlhf7Ew&eMY^8(zoLp*F!4q|{%wRu7U_n2rdQ=nN5dB8Ui*DCHyS$qsE-#roZH|I7OB`GN63e`NEPf^Zn(t!xA zzv;7_^Vkt1!gbXs3=Zi?#l4^^n^w`(qoHTs>^BVvfjx!WQXPEdZn?j7<4`6u&eP|$ zk35ra8n@Fl?jm3|lPZ6C7w)S=UVLZ2=bqd%Gag_d|BISYdS3BHF)C^YpYUfl)gLzn zVLQXtV}*QKbVw31k~)oaWZyl$=ZBi`sDo27Z_bd4^5HumS`#35;80SntQPN~s30dDJ-@i~37tm^+A?))Z|>y6EGY zdpFiM{DccGnlLB~lnfX#FsB9m-Ohm9+1%`*Q%1?!w!p5es5INrT?EVjAPLrT<)Nmg zUP1PjqQPpPh+Aq|cy@NQ_m3=uvL?)OQWKV`%1N^f%HpO|3>;8H=|Y2V22K8M&L}5% zhxx~PAXpeu|5S|`r~IGNX#S}(WB1hKULOD5!6JJ-LNVD78t5}fxMVUVlEHSTtCntJ z%BsL|QoUuovg~8D#m%x{^P8hBfeB9jYVOs7l+*guc8U#@tF3?<6kz zSYLw_fs50kTFIgHrP{~Omb2rEAbGpe^aCV8$qp)lUfjvfmXn7Ko^4(n;{|zDS~QHR zH16v)^f7Mu&1R!HR3hRR#|csGW#If!`F9 zkX52n!CM2c+f8~K_Opp=WeUVu`$)_Aa>Y*uO`KeO;u0Qx-2PNDlC6vnWAo&qtD(%m zEqc&+_Ch*Njb+2KBb>GHYIp3r&HZE^csYeAndH^QUFoRnLuor+y_VgeX1m`yf)l-j z&vr&sb_*vLi2{EndrEY)*?zDEZz>2h{#EZe=aTT#i#mc+e3lRQW8~hC(;Lh#s3%@c z_BR1)h~yywCwLjc!%CT5l<%L?$jy()ws1OtYPoA%5u8JR^?S}91J$VspPPqxZLO#( zISSiQb&7dp|9l-yz)O`&$)fkQ&k4o}mfYwgZFKp`eyw)A%kK$*?TRXTXg_DFL2x96 zbpF*%6~KuFq^kM;x@q#G{nB`?TOcL*^0Y0RQX6pchm%_uppEcBN7>o-*fLd{a*f&yJu5E3E#`5`AroXU%!eW_8s**bjocCDxrMN?yO-_%0U_v1^ON zl53M+`R1v!#JRG_;E8ur=g+jDan^*VPR`fRs*O2`G01EZR7cMAtECof*;Z^)a4bCL zgA>X#w_a)sVl_mb&>1$A;RSl(kkf)BJ~m$j&}}@Q8I$5{Fcj3!i4aD;y|i*9^x5O=fCJVFm{+S`dF_IuS>wzh)MgEVyL)2O% z3CcTkX2i?G0D)K=DLZnfn#Z3oUTV(OG42+NF*r=-^`o4kE|Mjg{#<7US9>g_``UGg z*M$o6d$yUS)5x(f-=Gr=(cz!D`Sv-tLXzVF+hZVJ$~gbXJY}nX&FRU{`lUm(A*u3( z;h6C`9s9TbC-VN6C^FDY{3w5!Mx?3?-(qX%Wh^h{c0Wrnr#m%WrOeX9Au-8k%m&d>&EmE=O>X-Y z4kzV20*{VV+Z%5ev$ua4e)17ba^2Tqy$%e;HT$3mYw>9(DNt6}EB@*s>*my6l4(nj zN8`{jAM>rnMRko4x8aq1O|ZkM6Qxf5KvW+PyVCOJe0D1y6k>j}lSTil=_o`KWi)Ew zeUP8;7YIY9f#!}WIZdIG?e?X#Yi^bBi6f6nN<_Kt$e08~txG}bBlpeV+W8AaN}BCp z`bx4nW1W=~m&>JOmZp(LyYy9M?SWA3%r98C$76HaezJGEt&XuP_+JY9V9huB4I{rFqeErKW+E=Kat6{NI)aY|69HgXYD)-|O_ zmg3Bc=`}tx>}@iaUU~-sZD{R~#QhV@zo#rK})tQi`G$f6L+-Axl)j;72fq#@O+WRlad?ikkHH;`IBz1*in@^?Tp>2xJ|B*Zq%c zi7P0?O)%;gl;{_vvV(uD_)g>PU5`}EL1m>Oge%=~!nZyzgCH{e`K4lhicq&zAz>}$ z_)7r)k@4_f^NQP#PlSUjFl%&pu}biF$Ev6LGXBjtmRjkiNtH(xkE3`OzXJ=p`blxTD>Ons+7xFn{~K}YUDlzO5? zvD{xdVT=h7>NAIv?=FlcZw+6yQ7|AXEMZ~B#fGj5)*OLO4;HG!SD|_N^(uBuvUwlx zhcxePeeQMZd=+=o@*));x)JyE@O9;NTPzaO)mSJ?C_nW+FPjtC)!2F{IcVWy&+FNaQiy@ou00W&LM}TvR+N`e~ zmdi6*IBiotS}AZ+E{Yuosb#6f6e?O%h%L9Q+-*sAzx-D6>EYz-j8C@>;?9>zAtCU) zDer#ih1D|Zw>Ap09AA0}hR7m$YSs5$$ypPF*G#h@Cj?Ok-q$_l4< zC)Zw*IoW(}mwHe4HhVI%hV&hLqklx6=eldEg1Mj}U4Epx%uc2Id1>116U ziQ05Vh`Nj|ig6_0+aGBnV8KSI#w|qQ1C}Qw)~NgV)!Bh;?F!YhqEE!?v!ux44u()v zmHS4qDw4Kxf*TCPK0b^jNjuHyQ-{!U6uz3_aqJ^#T0dB3sC@_Pi4tpX>cy%*9DC3Gg|t;^0?MUKK7~_s(pTPkn*f zn#T@e87j#?yGQhPTC;?}FsEOh=$yfsIhNV*S)XfY5k+b?mz~P>Po@kGu=*?UJAa=ztt0g&& zihI+?<&M>&lq*LqpQpEi=@Z|yPW&F8tEU!~e^988|0SZ<3w?d2wKl!w{rxCF+I1`o^fuxNb>vSDg?9`}AC|+M_hfqx zQ$Fs$m}fV6j){w6Xb_Sn9i%@8HkO@@aH$vclJe*YFnyHC)1xp}0`w`|KQ;)L)5o-4 z^r^(SX|jL-2R{h+bEI7hjbUY6FWruj@;Qx3B%6-yIRRMWMLm0hv%dA+qT`)af_7N( zCNnw;F5@kvgRvNlWmH9lwdSmOhTMN4QUW$E{p5#+C!NWL90#nHm}Cb(bvn6hkL6~B z$Xt1ZpkI>~3-m4|Jhg2rwMl~79h(4oTm!&u8!KcsS~(1&YlHk@!)ap13?ukUOLLD= zd@JgNWgQEfP5C`rIf8$B;H?}E-=3r1S(WjJk6T7X)UA&z#GO&J0QS+M?$EMQ5>NW= zofsQi*|_uFwgbyV7arp|F&6kV*$SGVSFvh1W#g=i9C%@}aW~s-H{OatNxXjrV~cpd zrkyAEWtyL~pBxU6wSjsFlX~sP?Q?t0{keG9=3W?dP~X*RKZcIH(pGYM^!TW`z%~FJ z{{1!k9Omadx{i}0_BW?C$pJS4(>zARQ=gYd!O&Roq6QY0!mT*knf?^<9KF)6FYl`s zwl_&oaaUUu#9JynJ^NFpjjQWu1DSf*m=i5dMmu>#ZuG4V;*wS*w4ruKC)yvhkeqE_ zf=DH2O<;;8oz~p1O2cBamW-tIK zSnl2prI6xk0fh;DlGF`;NRWIXC_#=c%<9`I?h;&A4U|G8u#~hl#eC0S;xMn-cZ|eV zsuLC9sed|4Qlsdqs7_%{RGbRBe1J_i=l^=-E#D@8`}cs_W2zNvHt~%_kdhlqukgFg zkJ~#f;nt)qw9b995Bu>AE|}y#WXEIHXb=u!sncc)xbsm!Z?)W9SI`BWFjeE1cp1j) z>>Myz#o77hCD!`AzKL7wJCW2h!ibGjLPdHaeAux}kkNN!bq9?6{g#H#>&U&@E%nSJ zwRj7Srzz+wo*$gql$F$`$672bvD;7t^uDct(hl=sA3p&OAJvzq>2w+e7qf+QkD96* zL=uBa)mX%)ukjvgby=@+u`>~b5G1;;lZm-TjigtS$O52GquA&RXhyI?%N4uaSza67 zi86%qaSLl+typKd*7Zu#12b}1rDIV4!mU247>Y1dOHW`ABS)%_>y%?HH%_N9cFg$o z^_$emc<@2QVjXTif;CjW)Ie~tEzG~UHwCKBTPU2&EHF38EBxwaZMDoULzpyeY&nkRx3?WRO={vxDn!FY(HmTfjkhWaeSf$;;RtiQ6JME&H>~N<#c|e1j0$ zSEv))!E@fncxR_&pOveZfaHa4vIM4FnJAAN+yr z5@+4fr8eq&vW)SN0-Tz)x?SDJ^ISM&ZvJ=`O<)p5a)%i0bmus%(!t3;t+TV&h!H22 zy(@w(kgyOj0>tDlKyTl&7VHj>00COBJKmW!gAOM}}>12DmR(lw&0 zs$g;~#zcmkKSg-lfj^kM3SvrX#aaOZBK{PcngT7ugSc-uqI%Zpmp*=wonc0SR0YdV z1NS&`zK5v$ruETelpK8(9VJ)o+&@C3MX$(6W3JXTCxn0Oc-D`LE<~>FC*Gi+Sf4?( zH9S>w{j;6llu!Ylh1YX#5*41jvd9%?@BAadE^(IdCLFp4#s^{6E!NU zgFJ>r!5|XTSshvZ&8tV8sW$cPPaOe^FNvRTN`C6!NE+GPT%4wrOR$`XWa@{S&oNR; zN;G0x3qG_*f7e=wZUbEe!V)%CO_Bo*`E(l(YZMi~e!Vyqa}0m!gtT*pgsgFIRvyrL z6})?s2onO~3n2Lr?xr#+DdC4ET}@RpLUft23VXtQkwqT4_$I$K4WLE~Y0Xy8(sR6A z+}&SAI@$xN{rQ(ANiCQ?pFP zsN6PVrfMV|54#=;fIZ%uXUQGp#&Xq81Drl9;H@-i3{ z=7P)W1oPM2+D#7fL2@+4_LBAi5#3V4%6@skj0tJJ_fqQ39;G5ewE&ATdVa3`JnDnE zW{&frsm>iYABVwBeetNzrXQz0fI?c&a~6j)D_qXAf-bmFRAevL$+XUfXUPQu^CV00 z3~y#~to5mez?>i6+B}xt-FXwQr5RQvc5`}+cdo{HbkG*Lm*}3%qh_Mg{@|1Ga{-sn zO7ZM8k=T_hXtz2oQ8okof85@$*aQ6I0W(7u7fX8El2N7Mj@z{E!qy`C ze|D|3y_kML7X!={3ned#G~JE*1baH{jix5)#)A1{B`^t9VePjy#y-)q5`58&6xLF9tr#?`ToB=)ttiUv zFA;ICn%6r5N2`Nd)m(o$1pHYAxDyzyQN2}3OjFLKm706YW2<<)=Lwd)lvpH#?x5&P|~1K*a{-x$n)-sG#v zwZE?8U)NA))ofX(baS%u0D|RMt+Lp0o;OP{w&CH5v@osza~NG+iEy^UNGg-bScs+{ zLZeZv=e5>aco*<+zw||Tqn+I4{h8R8U%W3+B@gB>Zc4Wy%5Pbl%9}f@`sc}W;6ze9 ziLl1gmK%~;O~$sv(;_T-PM}b{hgFB~9#2Y*)9Q=1R>vtX;@{rd$;x9cSF6UDn)Y`O z^%YqSRh=|oB~yI%v~!B6sRj^*d$f5eX&jw}TY4g5J4ieQ_p39v zyTG(V#D5;!9c=X8Eb1*~J6q=P6%f(1aAS`oEOcL-Q#sOr@WMp0^cc!nMc#gz7QRF;cv7PI zfs^?}_4mrx(?_({*=0)2!TJf1UKlZr_Asw-&>p-Zk6au~tK06=s}84{c5XZy%q)~6 z!q8qq zWeuC4)ob0lsEvIy8{!y%kHpnBav2P6xWJZpp&>*>8?7T+AzDq26UN?gr1%W_i`B`= z50mQ)Q+nJqi`dHz4oA1@Auf!^ow|9v98kt*wZB|=#F`8`s6kQ^Lpq$4+mZh<&!V3Va=qJ zSRqtb90s3R?FaG>vw5AtyY#taSXp(Ay61(|S;F@s7$ z7T0(zKUj3PJn$Q$kTV+|%{-Id6QNPike(^2bAP%+1}0^-+F{poNp-QmBE4?lQjYHl zILs~;?~OXA#6AEbr5xCOFlgH+Xb;KGee11!a%1y(ck{h*>V8kA$T;Zzu~XkZlCnZ! za&pB=W+H_Xtp`)Lh8>G|R+I26szO3;7a>u@dNIMYu4O{@RC&Rce&08Rd4~+;A^huSX$~5AAHpKa`{y67!o^=J8YuTW z(_1i(k+Qma_iZn4cBQM&CZDYZMqE6$JGRw^ZqdmqAek!XnCOzvyl#il>bFJRnqNB( zmA&EBy5dnhxV*6Bos%DzA!ANZ+^qYSN8LXIU+e5|a#}cfToW%G_6tK-*LW{7@XP$5 zYgfIiyao29+LM@-mMlLp)jqR{%&3_7>$~OL50VES#T7O?%=6cjJh`Tgz=)wA#VH0! zjo%Io_`l}H$?O8U6jIBy@iC(>b$K3Y&x~0RX|XL_OOr0r=f6M~EAgt}=o^XahV_gk z00o2_XMMpgy+hJNZ=q5oF+Z>3sPH)WgH)5Qw+{51EPj8-HXK4M~WR=j&f%<<}%J_N@+YNY`F#$K7ME#ALu0JHxt zgog;=0fp2_qwSaL_KxJPE#ViDK3b1t;~;c(3iyO8OJ9oKuyn}B*pc39&d}}2h1IS` zG-ss8rF7=Kpb}ebx;P`)q7O(_NtOl>toPZ3pFEG*EQ286y?CS%WaZMgSq6r;n$6+7 z2`JRICS_Z?$t})b$jSRt`5+GocEk>t6glyHIap#0h??l|4|Lcl%dp8$r96N?l^Pcq zVi3`RLu;a1z;4pF>zRVRnSAz?oC6|`k1zvy8tZn8&lWUgo2L>5<(9?t4Niu&l?lgf zK6jG{107+#Nyu-rQ7~L2_^ZayT~p4dB5SUDLxR`TpPF3cyLP(~5*>Cb%TM6gN>6+<1()@g%5a5cDWPujCbipUgqSrMlDdy@ZKJ)__Q~?=6L0gFx zeSOp2m=n9-t&HC21k8i86`ywHIkVF02BqAr+|s6kGDHcR$fBj{b9rw|Pm}f!byKq7 z8HI2E(N$Vfp3GNkA5H!9Yj8q~Z(kRWFm5|sn$yt&QGQNv?9jrs>9RX@c3Jil^ntytYbrSjp{GS3DT3Cs}*mawL-M&H^;Vuwo>jxn;63}9t~ug>_&emoW5B6ck=tM zxjE9Td%EBtXwrRL{`Wc`@qO65SpMJO0o>)-@a&jek#2En>dv7&(QffEZok&QaabV? z+LzFEF{vDjP^?O3Ht_!O+o6^h(>)m&zW3KHr^G1hUgxDJ@?Z9MuC7?)WPyBo`urjy zOHdGMb8-5+Z0fSc`P@oF6a-gP(xj=2%Tc^1U$|GUzULJX5za*kg|4eqD_Te05BLLk zzmP$El#@&*3{eB0S*$ZpUcf=O9Unpx6RG2l$u}$XyRP`}Z0du7i*hsK{6ZM#9%1p5O4gQ$=OvyU64N_tEUOy6@1RDPr8<(XEo;9g~~< zfep)9iVq>_ax1+-DfGs3&E0X`-!(yjq3Na^2-eKK=u#D(UiQBnO3NV@7us&kz`b`m z<_A4L>)+q^fAXnH9c6GpV3xpglKWEwCNewtU3on}g;-JegC;t_djB8*EbFn*PYHfO zW-q*IF!eR@mG2+wL4glj`?1ew%Plolloo1z7+s;uKWBtOpYbY!BK{x-4yw*&b?VMi zT*CjHbT9MN{gc!r{}mnrj@VG@*k(ND(?cXuTeLIS|raQ5(iJ0Ib`pp@PP2O#~V`{-_*UsAw>hEk|dIq zo)Y#}jlqFc8|p6h7r{&Qj|$gER!06x?ExW;bo8OE@ozW5-LSrt{9s!b`@)%m1)f_Sg_J~iC~V^TFuneqFn>ebt(h70(-4(Km65> z%X7sj+rxw*6*tMEP73+!G-~s!T3Won8E#TXN~13aBJacZ|Jf)07rW{|65srrx9k4` z{pY`l1OERz`+rZHkt2NTRB!LGyax`pY2NpTKcudC5ii5-t;&NDYQ({lKdT$02XxPw XlDzqTAabQYcpxe$^`(&i>$m>}56+dD literal 0 HcmV?d00001 diff --git a/apps/website/public/widgets/hive-boundary/hive-boundary.t27 b/apps/website/public/widgets/hive-boundary/hive-boundary.t27 new file mode 100644 index 0000000000..06b989b0a1 --- /dev/null +++ b/apps/website/public/widgets/hive-boundary/hive-boundary.t27 @@ -0,0 +1,79 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/widgets/hive-boundary.t27 -- A boundary: which files a task may reserve +; Source of truth for public/widgets/hive-boundary/ (gHashTag/trinity): scripts/widget-pages-from-spec.mjs +; compiles this file, checks its constants, evaluates the tests below and writes the page; the page +; hands every constant to tool.js as window.T27_WIDGET. tool.js holds no English and NO RULE of its +; own: every column, verdict and number it shows comes from public/widgets/hive-boundary/logic.js -- the fn +; bodies of gHashTag/t27 specs/queen/shaping.t27 compiled to wasm, beside its copy logic.t27. +; Lesson 2 of the course queen-and-hive. ASCII only (L3), English only (LANG-EN). +; A K_ constant is an input, a preset, a range or a constant of the logic spec (widget-logic.mjs holds +; a K_ whose name the logic spec also has equal to it); never the result of a spec fn. + +; WHAT IT DOES: each of four paths carries four facts the reader taps: the file exists, its folder +; exists, the issue cites it, it would be a new file in a language the only-t27 rule denies. Each +; path is path_accepted of its facts; the gate is shape_lands(missing after, 4, refused). + +; WHAT IT DOES NOT CLAIM: it does not read a repository: the four paths and their facts are what +; the reader sets. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module widget_hive_boundary; + +pub const KIND : str = "widget-tool"; +pub const ID : str = "hive-boundary"; +pub const TITLE : str = "A boundary: which files a task may reserve"; +pub const DESCRIPTION : str = "Four paths a model proposed for an issue. Tap what is true of each: the spec accepts or refuses it, and a shape lands only when every path holds."; +pub const IMAGE_ALT : str = "Boundary card: four proposed paths. An existing spec and a new cited t27 file in an existing folder are green; a cited file in a folder that does not exist and a new cited .py file are red. The gate is shut: 2 of 4 paths refused, the shape does not land."; +pub const HOOK : str = "A wrong boundary is worse than a missing one: it reserves files the work does not own. An invented path is refused, and one refusal stops the shape."; +pub const CATEGORY : str = "game"; +pub const DATA_SOURCES : [2]str = ["gHashTag/t27 specs/queen/shaping.t27 (t27 master c42f8fb40): every value on the page is path_accepted and shape_lands, its fn bodies compiled to wasm (logic.js)", "Course 11 of t27.ai, queen-and-hive (gHashTag/trinity#1698): lesson 2 opens this widget"]; +pub const READS_LOCAL_FILES : bool = false; +pub const SENDS_NOTHING : bool = true; + +pub const K_CARD_WIDTH : u16 = 1200; +pub const K_CARD_HEIGHT : u16 = 630; + +; --- Numbers: ranges, presets, and constants the logic spec holds ---------------------------- +pub const K_PATHS : u8 = 4; +pub const K_MAX_BOUNDARY_PATHS : u32 = 12; +pub const K_FACT_BITS : [4]u8 = [1, 2, 4, 8]; +pub const K_PRESET_P0 : [4]u8 = [1, 1, 1, 1]; +pub const K_PRESET_P1 : [4]u8 = [6, 6, 6, 0]; +pub const K_PRESET_P2 : [4]u8 = [4, 6, 4, 4]; +pub const K_PRESET_P3 : [4]u8 = [14, 6, 14, 14]; +pub const K_PRESET_LEFT : [4]u8 = [0, 0, 1, 0]; +pub const K_START_PRESET : u8 = 0; + +; --- Words on screen ------------------------------------------------------------------------ +pub const SAY_PRESETS_LABEL : str = "Start from"; +pub const SAY_PRESET_NAMES : [4]str = ["Two invented paths", "Every path checked", "Still a section missing", "Only real files"]; +pub const SAY_ASK : str = "Which files may a task reserve before a bee starts on it?"; +pub const SAY_PATHS : [4]str = ["specs/queen/hive.t27", "specs/queen/comb.t27", "tools/queen/draw/new.t27", "tools/queen/draw.py"]; +pub const SAY_FACTS : [4]str = ["exists", "folder exists", "cited by the issue", "new, foreign language"]; +pub const SAY_LEFT : str = "a section still missing after the shape"; +pub const SAY_OK : str = "accepted"; +pub const SAY_BAD : str = "refused"; +pub const SAY_LANDS : str = "the shape lands"; +pub const SAY_STOPS : str = "the shape does not land"; +pub const SAY_COUNT : str = "{0} of {1} paths refused"; +pub const SAY_NOTE : str = "A file that exists is always fine: finding the file a title describes is the point. A new file needs a folder that exists, a citation in the issue, and a language the only-t27 rule allows. More than 12 paths is not a task but a project."; +pub const SAY_SOURCE : str = "Logic: t27 spec queen/shaping.t27, compiled"; + +test the_card_is_the_share_size { + assert K_CARD_WIDTH == 1200; + assert K_CARD_HEIGHT == 630; + assert K_CARD_WIDTH * 21 == K_CARD_HEIGHT * 40; +} + +test four_paths_four_facts { + assert K_PATHS == 4; + assert K_FACT_BITS[0] == 1; + assert K_FACT_BITS[3] == 8; +} + +test the_start_has_one_path_of_each_kind { + assert K_PRESET_P0[0] == 1; + assert K_PRESET_P1[0] == 6; + assert K_PRESET_P3[0] == 14; + assert K_PATHS <= K_MAX_BOUNDARY_PATHS; +} diff --git a/apps/website/public/widgets/hive-boundary/index.html b/apps/website/public/widgets/hive-boundary/index.html new file mode 100644 index 0000000000..796ccefbdc --- /dev/null +++ b/apps/website/public/widgets/hive-boundary/index.html @@ -0,0 +1,47 @@ + + + + + + +A boundary: which files a task may reserve · Trinity SยณAI + + + + + + + + + + + + + + + + + + + + + + + + +
+Trinity SยณAI +

A boundary: which files a task may reserve

+

Four paths a model proposed for an issue. Tap what is true of each: the spec accepts or refuses it, and a shape lands only when every path holds.

+
+
+ +
+

This page sets no cookie and sends nothing anywhere.

+
Where the numbers come from
  • gHashTag/t27 specs/queen/shaping.t27 (t27 master c42f8fb40): every value on the page is path_accepted and shape_lands, its fn bodies compiled to wasm (logic.js)
  • Course 11 of t27.ai, queen-and-hive (gHashTag/trinity#1698): lesson 2 opens this widget
+

This widget's .t27 source · All widgets

+
+ + + + diff --git a/apps/website/public/widgets/hive-boundary/logic.js b/apps/website/public/widgets/hive-boundary/logic.js new file mode 100644 index 0000000000..7be9bc1a59 --- /dev/null +++ b/apps/website/public/widgets/hive-boundary/logic.js @@ -0,0 +1,36 @@ +// Generated by scripts/t27-logic.mjs from shaping.t27. Do not edit. +// +// The spec's own fn bodies, compiled: t27c gen (Zig) -> zig 0.16.0 (wasm32). +// Change the spec in gHashTag/t27, vendor it, regenerate. A rule written +// by hand beside this file is the thing this file exists to end. +// spec-sha256: e887aacb9648b6efe25fd8fdd334e671e099692647f1d6836d121fe082205b05 +// Imported by scripts/widget-logic.mjs for public/widgets/hive-boundary/: types stripped, nothing else changed. +const WASM = "AGFzbQEAAAABFgNgAn9/AX9gA39/fwF/YAR/f39/AX8DBQQAAQICBAUBcAEBAQUDAQAQBgkBfwFBgIDAAAsHVQUGbWVtb3J5AgAPdDI3X2F0dGVtcHRfZHVlAAAPdDI3X3NoYXBlX2xhbmRzAAERdDI3X3BhdGhfYWNjZXB0ZWQAAhB0Mjdfc2hhcGVfYWN0aW9uAAMKZAQMACAARSABQecCS3ILEQAgACACckUgAUF/akEMSXELIQEBf0EBIQQCQCAAQQFxDQAgASADQQFzcSACcSEECyAECyEAQQBBAUEAQQIgA0EBcRsgAkECSRsgAUEBcRtBACAAGws="; +const W = new WebAssembly.Instance( + new WebAssembly.Module(Uint8Array.from(atob(WASM), (c) => c.charCodeAt(0))) +).exports; +function int(v, lo, hi, at) { + if (!Number.isInteger(v) || v < lo || v > hi) + throw new RangeError(`${at}: ${v} is outside ${lo}..${hi}`); + return v; +} +function shapeAction(missing, container, attempts, told) { + return W.t27_shape_action(int(missing, 0, 255, "shape_action.missing"), container ? 1 : 0, int(attempts, 0, 4294967295, "shape_action.attempts"), told ? 1 : 0) & 255; +} +function pathAccepted(exists, dirExists, cited, foreignNew) { + return (W.t27_path_accepted(exists ? 1 : 0, dirExists ? 1 : 0, cited ? 1 : 0, foreignNew ? 1 : 0) & 1) !== 0; +} +function shapeLands(missingAfter, paths, rejected) { + return (W.t27_shape_lands(int(missingAfter, 0, 255, "shape_lands.missing_after"), int(paths, 0, 4294967295, "shape_lands.paths"), int(rejected, 0, 4294967295, "shape_lands.rejected")) & 1) !== 0; +} +function attemptDue(attempts, minutesSinceLast) { + return (W.t27_attempt_due(int(attempts, 0, 4294967295, "attempt_due.attempts"), int(minutesSinceLast, 0, 4294967295, "attempt_due.minutes_since_last")) & 1) !== 0; +} +const __NOT_LOWERED__ = Object.freeze([]); +export { + __NOT_LOWERED__, + attemptDue, + pathAccepted, + shapeAction, + shapeLands +}; diff --git a/apps/website/public/widgets/hive-boundary/logic.t27 b/apps/website/public/widgets/hive-boundary/logic.t27 new file mode 100644 index 0000000000..e2bf702451 --- /dev/null +++ b/apps/website/public/widgets/hive-boundary/logic.t27 @@ -0,0 +1,130 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/queen/shaping.t27 -- the Queen shapes an issue that is not ready, or says once what it lacks +; Owner, 2026-10-08 (translated): forbid issues that break the rules; find every fragile place and fix +; it; 233 + 115 issues are not ready for work because their spec is missing or incomplete. +; Measured that morning on /queen/status: of 704 candidates, the round said "nothing to choose" in most +; rounds while 63 of 70 lanes stood idle. 233 issues were skipped as missingBoundary and 115 as +; incompleteSpec. Some of them have a `## Boundary` heading with prose under it and no path (#7703, +; #7642), which the rule counts as missing. +; -- +; The rule an issue must meet is specs/policy/issue_shape.t27 (judge: one bit per missing section). +; A workflow cannot hold that line here: .yml is owner-only, and `gh issue create` never sees a +; template. So the Queen holds it in her own round. Every open issue that judge() refuses is either +; SHAPED by her (the missing sections are written by a model, every boundary path is checked against +; the repository, and the body is written back only if judge() then returns 0), or, when shaping +; failed SHAPE_ATTEMPT_LIMIT times, its author is told ONCE what is missing. +; -- +; A wrong boundary is worse than a missing one (tools/queen/propose_boundary.py): it reserves files the +; work does not own. So path_accepted refuses a path the model invented: a path that does not exist is +; accepted only when the issue itself cites it and its directory exists, and never when it would be a +; new file in a language the only-t27 rule denies. +; Containers (labels epic, roadmap) are not tasks, as issue_shape says; they are never shaped. +; ASCII only (L3). +; phi^2 + 1/phi^2 = 3 | TRINITY + +module QueenShaping; + +/// What the Queen does with one open issue. +/// LEAVE nothing: it is ready, a container, or already told +/// SHAPE ask for the missing sections, judge them, write them back if they pass +/// TELL comment once, naming what is missing +pub const SH_LEAVE : u8 = 0; +pub const SH_SHAPE : u8 = 1; +pub const SH_TELL : u8 = 2; + +/// Attempts at shaping one issue before its author is told instead. +pub const SHAPE_ATTEMPT_LIMIT : u32 = 2; +/// Issues shaped per round. A shape is one model call; the round has other work. +pub const SHAPES_PER_ROUND : u32 = 3; +/// Minutes between two attempts on one issue, so a refused shape is not retried every round. +pub const SHAPE_RETRY_MINUTES : u32 = 360; +/// A boundary is a reservation; one naming more files than this is not a task, it is a project. +pub const MAX_BOUNDARY_PATHS : u32 = 12; + +/// `missing`: judge()'s bits for the body (0 = ready); `container`: labelled epic or roadmap; +/// `attempts`: shapes tried so far; `told`: the author was already told. +pub fn shape_action(missing: u8, container: bool, attempts: u32, told: bool) -> u8 { + if (missing == 0) { return SH_LEAVE; } + if (container) { return SH_LEAVE; } + if (attempts < SHAPE_ATTEMPT_LIMIT) { return SH_SHAPE; } + if (told) { return SH_LEAVE; } + return SH_TELL; +} + +/// Is one proposed boundary path acceptable? +/// exists the file is in the repository at the base ref +/// dir_exists its directory is (a new file beside its siblings) +/// cited the issue's own title or body names this path +/// foreign_new it would be a NEW file whose extension the only-t27 rule denies to new work +/// A file that exists is always acceptable: finding the file a title describes is the point. A file +/// that does not exist must be cited by the issue, in a directory that exists, and not foreign. +pub fn path_accepted(exists: bool, dir_exists: bool, cited: bool, foreign_new: bool) -> bool { + if (exists) { return true; } + if (dir_exists == false) { return false; } + if (foreign_new) { return false; } + return cited; +} + +/// Does a shape land? Only when the body it produces is ready (judge() = 0), it names between 1 and +/// MAX_BOUNDARY_PATHS paths, and every one of them was accepted. +pub fn shape_lands(missing_after: u8, paths: u32, rejected: u32) -> bool { + if (missing_after != 0) { return false; } + if (paths == 0) { return false; } + if (paths > MAX_BOUNDARY_PATHS) { return false; } + return rejected == 0; +} + +/// Is an attempt due? Never twice inside SHAPE_RETRY_MINUTES on one issue. +pub fn attempt_due(attempts: u32, minutes_since_last: u32) -> bool { + if (attempts == 0) { return true; } + return minutes_since_last >= SHAPE_RETRY_MINUTES; +} + +test a_ready_issue_and_a_container_are_left_alone { + assert shape_action(0, false, 0, false) == SH_LEAVE; + assert shape_action(15, true, 0, false) == SH_LEAVE; + assert shape_action(1, true, 9, false) == SH_LEAVE; +} + +test an_unready_issue_is_shaped_then_its_author_is_told_once { + assert shape_action(1, false, 0, false) == SH_SHAPE; + assert shape_action(15, false, SHAPE_ATTEMPT_LIMIT - 1, false) == SH_SHAPE; + assert shape_action(15, false, SHAPE_ATTEMPT_LIMIT, false) == SH_TELL; + assert shape_action(15, false, SHAPE_ATTEMPT_LIMIT, true) == SH_LEAVE; + assert shape_action(8, false, SHAPE_ATTEMPT_LIMIT + 3, true) == SH_LEAVE; +} + +test a_boundary_path_must_be_true_of_the_repository { + // an existing file, cited or found + assert path_accepted(true, true, false, false) == true; + assert path_accepted(true, true, true, false) == true; + // a new spec the issue asks for, beside its siblings + assert path_accepted(false, true, true, false) == true; + // a new file the issue never mentioned: invented + assert path_accepted(false, true, false, false) == false; + // a new file in a directory that does not exist + assert path_accepted(false, false, true, false) == false; + // a new .py or .ts the only-t27 rule denies, even when cited + assert path_accepted(false, true, true, true) == false; +} + +test a_shape_lands_only_when_the_body_is_ready_and_every_path_holds { + assert shape_lands(0, 1, 0) == true; + assert shape_lands(0, MAX_BOUNDARY_PATHS, 0) == true; + assert shape_lands(1, 3, 0) == false; + assert shape_lands(0, 0, 0) == false; + assert shape_lands(0, MAX_BOUNDARY_PATHS + 1, 0) == false; + assert shape_lands(0, 3, 1) == false; +} + +test a_refused_shape_is_not_retried_every_round { + assert attempt_due(0, 0) == true; + assert attempt_due(1, SHAPE_RETRY_MINUTES - 1) == false; + assert attempt_due(1, SHAPE_RETRY_MINUTES) == true; +} + +invariant the_codes_and_limits_never_move { + assert SH_LEAVE == 0 and SH_SHAPE == 1 and SH_TELL == 2; + assert SHAPE_ATTEMPT_LIMIT > 0 and SHAPES_PER_ROUND > 0; + assert MAX_BOUNDARY_PATHS > 0; +} diff --git a/apps/website/public/widgets/hive-boundary/tool.js b/apps/website/public/widgets/hive-boundary/tool.js new file mode 100644 index 0000000000..17f8e48508 --- /dev/null +++ b/apps/website/public/widgets/hive-boundary/tool.js @@ -0,0 +1,31 @@ +// hive-boundary/tool.js -- lesson 2 of queen-and-hive: which files a task may reserve. Each path is +// path_accepted of the four facts the reader taps, and the gate is shape_lands of the result, both +// from gHashTag/t27 specs/queen/shaping.t27 compiled to wasm (./logic.js). The host only counts the +// refused paths it hands to shape_lands. Words are SAY_ and numbers K_ constants of +// specs/widgets/hive-boundary.t27. +import { pathAccepted, shapeLands } from './logic.js' +import { K, W, ask, controls, el, fill, gate, head, pill, root, say, source, taps } from '../hive-kit.js' + +const N = K('K_PATHS', 4), BITS = K('K_FACT_BITS', [1, 2, 4, 8]) +const keys = [...Array(N)].map((_, i) => `p${i}`) +const ctl = controls([ + ...keys.map((key) => ({ key, kind: 'hidden', min: 0, max: 15 })), + { key: 'left', label: say('SAY_LEFT'), kind: 'toggle' }, +], { names: W.SAY_PRESET_NAMES, values: { ...Object.fromEntries(keys.map((k, i) => [k, K(`K_PRESET_P${i}`, [])])), left: K('K_PRESET_LEFT', []) } }, draw) + +const out = el('div') +root.replaceChildren(ask(say('SAY_ASK')), ctl.node, out, el('p', { class: 'dk-note' }, say('SAY_NOTE')), source()) + +function draw(s) { + const ok = keys.map((k) => { const m = s[k]; return pathAccepted(!!(m & BITS[0]), !!(m & BITS[1]), !!(m & BITS[2]), !!(m & BITS[3])) }) + const refused = ok.filter((v) => !v).length + const lands = shapeLands(s.left ? 1 : 0, N, refused) + out.replaceChildren( + ...keys.map((k, i) => el('div', { class: 'hk-path' }, + el('p', { class: 'hk-path-head' }, el('code', {}, say('SAY_PATHS', i)), pill(ok[i] ? say('SAY_OK') : say('SAY_BAD'), ok[i] ? 0 : 6)), + taps(BITS.map((b, j) => ({ label: say('SAY_FACTS', j), pressed: !!(s[k] & b), tone: ok[i] ? 0 : 6 })), (j) => ctl.set(k, s[k] ^ BITS[j]), say('SAY_PATHS', i)))), + head(fill(say('SAY_COUNT'), refused, N)), + gate(lands, lands ? say('SAY_LANDS') : say('SAY_STOPS'), fill(say('SAY_COUNT'), refused, N))) +} + +ctl.start() diff --git a/apps/website/public/widgets/hive-burn/card.png b/apps/website/public/widgets/hive-burn/card.png new file mode 100644 index 0000000000000000000000000000000000000000..7b57f49774920a317a74964019782cc6fcc9c7ec GIT binary patch literal 43514 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 literal 0 HcmV?d00001 diff --git a/apps/website/public/widgets/hive-burn/hive-burn.t27 b/apps/website/public/widgets/hive-burn/hive-burn.t27 new file mode 100644 index 0000000000..6fbb21cc7e --- /dev/null +++ b/apps/website/public/widgets/hive-burn/hive-burn.t27 @@ -0,0 +1,71 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/widgets/hive-burn.t27 -- Tokens that burn: a month to spend them +; Source of truth for public/widgets/hive-burn/ (gHashTag/trinity): scripts/widget-pages-from-spec.mjs +; compiles this file, checks its constants, evaluates the tests below and writes the page; the page +; hands every constant to tool.js as window.T27_WIDGET. tool.js holds no English and NO RULE of its +; own: every column, verdict and number it shows comes from public/widgets/hive-burn/logic.js -- the fn +; bodies of gHashTag/t27 specs/automation/token-wallet.t27 compiled to wasm, beside its copy logic.t27. +; Lesson 21 of the course queen-and-hive. ASCII only (L3), English only (LANG-EN). +; A K_ constant is an input, a preset, a range or a constant of the logic spec (widget-logic.mjs holds +; a K_ whose name the logic spec also has equal to it); never the result of a spec fn. + +; WHAT IT DOES: the reader drags today from day 0 to day 60. Each batch is days_left(the day it +; landed, today, 30). The list is ordered by those answers, which is the spend order the spec names, +; soonest burn first. + +; WHAT IT DOES NOT CLAIM: the spec marks the burn as decided but not yet in the ledger; the page +; shows the rule, not a balance. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module widget_hive_burn; + +pub const KIND : str = "widget-tool"; +pub const ID : str = "hive-burn"; +pub const TITLE : str = "Tokens that burn: a month to spend them"; +pub const DESCRIPTION : str = "Three batches of tokens, each burning 30 days after it lands. Drag today across two months: days_left counts down every batch, and the one that burns soonest is spent first."; +pub const IMAGE_ALT : str = "Burn card on day 25: three bars on a 60-day strip. The package month from day 0 has 5 days left, gold; the start gift from day 3 has 8 days; the top-up from day 12 has 17 days, green. A white line marks today."; +pub const HOOK : str = "All money goes into tokens, and the tokens burn within the month: a person who paid makes content instead of saving tokens."; +pub const CATEGORY : str = "game"; +pub const DATA_SOURCES : [2]str = ["gHashTag/t27 specs/automation/token-wallet.t27 (t27 master c42f8fb40): every value on the page is days_left, its fn bodies compiled to wasm (logic.js)", "Course 11 of t27.ai, queen-and-hive (gHashTag/trinity#1698): lesson 21 opens this widget"]; +pub const READS_LOCAL_FILES : bool = false; +pub const SENDS_NOTHING : bool = true; + +pub const K_CARD_WIDTH : u16 = 1200; +pub const K_CARD_HEIGHT : u16 = 630; + +; --- Numbers: ranges, presets, and constants the logic spec holds ---------------------------- +pub const K_TODAY_MAX : u8 = 60; +pub const K_MONTH_DAYS : u16 = 30; +pub const K_BATCH_DAYS : [3]u8 = [0, 3, 12]; +pub const K_PRESET_TODAY : [4]u8 = [25, 2, 31, 45]; +pub const K_START_PRESET : u8 = 0; + +; --- Words on screen ------------------------------------------------------------------------ +pub const SAY_PRESETS_LABEL : str = "Start from"; +pub const SAY_PRESET_NAMES : [4]str = ["Day 25", "Day 2", "Day 31, the month burned", "Day 45"]; +pub const SAY_ASK : str = "How long do tokens last, and which ones are spent first?"; +pub const SAY_TODAY : str = "today, day"; +pub const SAY_BATCHES : [3]str = ["the package month", "the start gift", "a top-up"]; +pub const SAY_LANDED : str = "landed on day {0}"; +pub const SAY_LEFT : str = "{0} days left"; +pub const SAY_GONE : str = "burned"; +pub const SAY_STRIP : str = "days 0 to 60, one bar a batch"; +pub const SAY_ORDER : str = "spent first: the soonest to burn"; +pub const SAY_NOTE : str = "The spec warns at 7, 3 and 1 days. Tokens held before the burn existed never burn. Whether bought top-ups should live longer is still the owner's call: one line, TOPUP_DAYS."; +pub const SAY_SOURCE : str = "Logic: t27 spec automation/token-wallet.t27, compiled"; + +test the_card_is_the_share_size { + assert K_CARD_WIDTH == 1200; + assert K_CARD_HEIGHT == 630; + assert K_CARD_WIDTH * 21 == K_CARD_HEIGHT * 40; +} + +test a_month_is_thirty_days { + assert K_MONTH_DAYS == 30; + assert K_TODAY_MAX == K_MONTH_DAYS * 2; +} + +test the_batches_land_in_the_first_month { + assert K_BATCH_DAYS[2] < K_MONTH_DAYS; + assert K_PRESET_TODAY[0] == 25; +} diff --git a/apps/website/public/widgets/hive-burn/index.html b/apps/website/public/widgets/hive-burn/index.html new file mode 100644 index 0000000000..bbe2c20562 --- /dev/null +++ b/apps/website/public/widgets/hive-burn/index.html @@ -0,0 +1,47 @@ + + + + + + +Tokens that burn: a month to spend them · Trinity SยณAI + + + + + + + + + + + + + + + + + + + + + + + + +
+Trinity SยณAI +

Tokens that burn: a month to spend them

+

Three batches of tokens, each burning 30 days after it lands. Drag today across two months: days_left counts down every batch, and the one that burns soonest is spent first.

+
+
+ +
+

This page sets no cookie and sends nothing anywhere.

+
Where the numbers come from
  • gHashTag/t27 specs/automation/token-wallet.t27 (t27 master c42f8fb40): every value on the page is days_left, its fn bodies compiled to wasm (logic.js)
  • Course 11 of t27.ai, queen-and-hive (gHashTag/trinity#1698): lesson 21 opens this widget
+

This widget's .t27 source · All widgets

+
+ + + + diff --git a/apps/website/public/widgets/hive-burn/logic.js b/apps/website/public/widgets/hive-burn/logic.js new file mode 100644 index 0000000000..7e64e84997 --- /dev/null +++ b/apps/website/public/widgets/hive-burn/logic.js @@ -0,0 +1,40 @@ +// Generated by scripts/t27-logic.mjs from token-wallet.t27. Do not edit. +// +// The spec's own fn bodies, compiled: t27c gen (Zig) -> zig 0.16.0 (wasm32). +// Change the spec in gHashTag/t27, vendor it, regenerate. A rule written +// by hand beside this file is the thing this file exists to end. +// spec-sha256: 425a06b53a9250178536c29d0dac0081f36be8f05199bf622f747f96e040fc11 +// Imported by scripts/widget-logic.mjs for public/widgets/hive-burn/: types stripped, nothing else changed. +const WASM = "AGFzbQEAAAABDQJgA39/fwF/YAF/AX8DBgUAAQEBAQQFAXABAQEFAwEAEAYJAX8BQYCAwAALB2IGBm1lbW9yeQIADXQyN19kYXlzX2xlZnQAABB0MjdfcG9ydHJhaXRzX2luAAEMdDI3X3JlZWxzX2luAAIQdDI3X3ByaWNlX3Rva2VucwADEHQyN19tb250aF90b2tlbnMABApHBRsAQQAgAiAAakH//wNxIgAgAWsiAiACIABLGwsHACAAQQxuCwgAIABBjgJuCw8AIABBAXRBhydqQYgnbgsIACAAQcgBbAs="; +const W = new WebAssembly.Instance( + new WebAssembly.Module(Uint8Array.from(atob(WASM), (c) => c.charCodeAt(0))) +).exports; +function int(v, lo, hi, at) { + if (!Number.isInteger(v) || v < lo || v > hi) + throw new RangeError(`${at}: ${v} is outside ${lo}..${hi}`); + return v; +} +function monthTokens(usd) { + return W.t27_month_tokens(int(usd, 0, 4294967295, "month_tokens.usd")) >>> 0; +} +function priceTokens(costMicroUsd) { + return W.t27_price_tokens(int(costMicroUsd, 0, 4294967295, "price_tokens.cost_micro_usd")) >>> 0; +} +function reelsIn(tokens) { + return W.t27_reels_in(int(tokens, 0, 4294967295, "reels_in.tokens")) >>> 0; +} +function portraitsIn(tokens) { + return W.t27_portraits_in(int(tokens, 0, 4294967295, "portraits_in.tokens")) >>> 0; +} +function daysLeft(grantedDay, today, life) { + return W.t27_days_left(int(grantedDay, 0, 65535, "days_left.granted_day"), int(today, 0, 65535, "days_left.today"), int(life, 0, 65535, "days_left.life")) & 65535; +} +const __NOT_LOWERED__ = Object.freeze([]); +export { + __NOT_LOWERED__, + daysLeft, + monthTokens, + portraitsIn, + priceTokens, + reelsIn +}; diff --git a/apps/website/public/widgets/hive-burn/logic.t27 b/apps/website/public/widgets/hive-burn/logic.t27 new file mode 100644 index 0000000000..9b8ec63538 --- /dev/null +++ b/apps/website/public/widgets/hive-burn/logic.t27 @@ -0,0 +1,139 @@ +// SPDX-License-Identifier: Apache-2.0 +// specs/automation/token-wallet.t27 -- the token wallet of the agent market: every dollar a +// person pays becomes tokens at ONE rate, every operation is sold at twice what it costs, and +// tokens that are not spent within a month burn. +// Host: 999-multibots-telegraf apps/vibee-editor/packages/vibee-atoms/src/token-wallet.ts +// (the month a package buys, what it is worth in reels and portraits, when it burns), +// read by the mini app's welcome screen and package cards (player/src/pages/Welcome.tsx, +// player/src/components/Agents/PackageCards.tsx) and by render's billing +// (render/src/agent/billing-shared.ts: the rate and the markup every price is made with). +// Generated: `t27c gen-ts` of this file is vendored into the host as +// apps/vibee-editor/packages/vibee-atoms/src/spec/token-wallet.gen.ts. The host imports +// the numbers from there and never restates them; a host test runs this file's vectors +// against the host. +// Why a burn: the owner, 2026-10-01: all money goes into tokens, and the tokens must be spent +// within the month or they burn -- so a person who paid makes content instead of saving +// tokens. Kling and Runway burn a month of plan credits the same way; Kling spends the +// credits that burn soonest first, and so does this. +// Why one rate: every price is made at $0.005 a token, but the Stars packs (render-server.ts +// PACKS) sell a token for 1.1-1.5 Stars, about three times that (tools.ts SALE_USD_PER_TOKEN). +// A package month is sold at THIS rate; moving the Stars packs onto it is the owner's call. +// Claim status: v1 (2026-10-01). The rate, the markup and the start gift are what the host +// already charges. The month, the burn and the spend order are DECIDED but not yet in the +// ledger: render/src/token-ledger.ts keeps one balance with no dates; the batches with an +// expiry are the next change, and the burn must not be sold before it lands. +// Owner decision still open: whether tokens bought on top of a package burn in 30 days like +// the package month (the owner's rule, kept here) or live longer, as Kling's and Runway's +// bought credits do (TOPUP_DAYS is the one line to change). +// phi^2 + 1/phi^2 = 3 | TRINITY + +module automation::token_wallet { + + pub const KIND : str = "automation"; + pub const ID : str = "token-wallet"; + pub const REPO : str = "999-multibots-telegraf"; + pub const VERSION : u8 = 1; + + // ---- One rate, one markup ----------------------------------------------------------- + pub const TOKEN_MICRO_USD : u32 = 5000; // one token is half a cent, everywhere + pub const MARKUP_PCT : u32 = 200; // an operation is sold at twice what it costs us + pub const MICRO_PER_USD : u32 = 1000000; + + // ---- What a month buys ---------------------------------------------------------------- + // A package's monthly price, in whole dollars, becomes tokens at the one rate: nothing is + // kept back, the margin is the markup on what is spent and whatever burns. + pub const SETUP_INCLUDES_MONTH : bool = true; // the launch price carries the first month + + // ---- The burn ----------------------------------------------------------------------- + pub const MONTH_DAYS : u16 = 30; // a package month burns 30 days after it lands + pub const TOPUP_DAYS : u16 = 30; // owner's rule: bought tokens burn the same way + pub const START_DAYS : u16 = 30; // the start gift burns too + pub const SPEND_ORDER : str = "soonest-burn-first"; + pub const BURNS_BACKDATED : bool = false; // tokens held before v1 never burn + pub const WARN_DAYS_FIRST : u16 = 7; + pub const WARN_DAYS_SECOND : u16 = 3; + pub const WARN_DAYS_LAST : u16 = 1; + + // ---- The start gift ------------------------------------------------------------------- + pub const START_TOKENS : u32 = 20; // granted once, on the first paid call + + // ---- What tokens are worth, in things a person can picture ---------------------------- + // The reference reel is the story reel's own estimate (crm-story-reel.t27 + // PRICE_ESTIMATE_TOKENS: three stills, three lines, 13 s of lip-sync, one cut); the + // portrait is the lead magnet's price (crm-lead-magnet.t27 OP_TOKENS). A host test holds + // both to those specs, so the welcome screen cannot promise a reel at an old price. + pub const REEL_TOKENS : u32 = 270; + pub const PORTRAIT_TOKENS : u32 = 12; + + // Dollars a month to tokens a month. + fn month_tokens(usd: u32) -> u32 { + return usd * MICRO_PER_USD / TOKEN_MICRO_USD; + } + + // An operation's price in tokens from what it costs us, in micro-dollars: rounded up, so + // nothing is sold below twice its cost. + fn price_tokens(cost_micro_usd: u32) -> u32 { + return (cost_micro_usd * MARKUP_PCT / 100 + TOKEN_MICRO_USD - 1) / TOKEN_MICRO_USD; + } + + // Whole reels a number of tokens buys. + fn reels_in(tokens: u32) -> u32 { + return tokens / REEL_TOKENS; + } + + // Whole portraits a number of tokens buys. + fn portraits_in(tokens: u32) -> u32 { + return tokens / PORTRAIT_TOKENS; + } + + // Days left before a batch granted on `granted_day` burns, on `today` (0 when gone). + fn days_left(granted_day: u16, today: u16, life: u16) -> u16 { + if (today >= granted_day + life) { + return 0; + } + return granted_day + life - today; + } + + test "every dollar of a package month becomes tokens at the one rate" { + assert(month_tokens(15) == 3000); + assert(month_tokens(35) == 7000); + assert(month_tokens(50) == 10000); + assert(SETUP_INCLUDES_MONTH); + } + + test "an operation is sold at twice its cost, rounded up" { + assert(MARKUP_PCT == 200); + assert(price_tokens(30000) == 12); + assert(price_tokens(20000) == 8); + assert(price_tokens(1) == 1); + } + + test "a month burns on day thirty and nothing held before it burns" { + assert(days_left(0, 0, MONTH_DAYS) == 30); + assert(days_left(0, 23, MONTH_DAYS) == 7); + assert(days_left(0, 30, MONTH_DAYS) == 0); + assert(TOPUP_DAYS == MONTH_DAYS); + assert(SPEND_ORDER == "soonest-burn-first"); + assert(!BURNS_BACKDATED); + } + + test "the warnings come at seven, three and one day left" { + assert(WARN_DAYS_FIRST > WARN_DAYS_SECOND); + assert(WARN_DAYS_SECOND > WARN_DAYS_LAST); + assert(WARN_DAYS_FIRST < MONTH_DAYS); + } + + test "the start gift buys one portrait and no reel" { + assert(portraits_in(START_TOKENS) == 1); + assert(reels_in(START_TOKENS) == 0); + } + + test "a Blogger month is twenty-five reels" { + assert(reels_in(month_tokens(35)) == 25); + assert(reels_in(month_tokens(15)) == 11); + assert(reels_in(month_tokens(50)) == 37); + assert(portraits_in(month_tokens(35)) == 583); + } +} + +// phi^2 + 1/phi^2 = 3 | TRINITY diff --git a/apps/website/public/widgets/hive-burn/tool.js b/apps/website/public/widgets/hive-burn/tool.js new file mode 100644 index 0000000000..8bd54ca014 --- /dev/null +++ b/apps/website/public/widgets/hive-burn/tool.js @@ -0,0 +1,31 @@ +// hive-burn/tool.js -- lesson 21 of queen-and-hive: tokens that burn. Each batch's countdown is +// days_left(landed, today, MONTH_DAYS), the fn body of gHashTag/t27 specs/automation/token-wallet.t27 +// compiled to wasm (./logic.js); the list is ordered by those answers. Words are SAY_ and numbers K_ +// constants of specs/widgets/hive-burn.t27. +import { daysLeft } from './logic.js' +import { COLORS, K, W, ask, canvas, controls, el, fill, head, ladder, root, say, source, svgEl } from '../hive-kit.js' + +const TMAX = K('K_TODAY_MAX', 60), LIFE = K('K_MONTH_DAYS', 30), LANDED = K('K_BATCH_DAYS', []) +const ctl = controls([{ key: 'today', label: say('SAY_TODAY'), min: 0, max: TMAX, step: 1 }], { names: W.SAY_PRESET_NAMES, values: { today: K('K_PRESET_TODAY', []) } }, draw) +const svg = canvas('hk-strip', say('SAY_STRIP')) +const out = el('div') +root.replaceChildren(ask(say('SAY_ASK')), ctl.node, svg, out, el('p', { class: 'dk-note' }, say('SAY_NOTE')), source()) +const TONES = [1, 3, 0] + +function draw(s) { + const left = LANDED.map((d) => daysLeft(d, s.today, LIFE)) + const w = 420, x = (d) => 20 + (d / TMAX) * (w - 40) + svg.replaceChildren() + svg.setAttribute('viewBox', `0 0 ${w} 190`) + LANDED.forEach((d, i) => { + const y = 20 + i * 50 + svg.append(svgEl('rect', { x: x(d), y, width: x(d + LIFE) - x(d), height: 30, rx: 8, fill: COLORS[TONES[i]], opacity: left[i] ? 0.85 : 0.18 })) + svg.append(svgEl('text', { x: x(d) + 6, y: y + 20, class: 'dk-txt', style: 'fill:#020403;font-weight:700;font-size:13px' }, say('SAY_BATCHES', i))) + }) + svg.append(svgEl('line', { x1: x(s.today), x2: x(s.today), y1: 10, y2: 170, stroke: '#fff', 'stroke-width': 3 })) + for (const t of [0, 15, 30, 45, 60].filter((t) => t <= TMAX)) svg.append(svgEl('text', { x: x(t), y: 186, class: 'dk-tick', 'text-anchor': 'middle' }, t)) + const order = LANDED.map((_, i) => i).sort((a, b) => (left[a] || Infinity) - (left[b] || Infinity)) + out.replaceChildren(head(say('SAY_ORDER')), ladder(order.map((i) => ({ label: say('SAY_BATCHES', i), sub: `${fill(say('SAY_LANDED'), LANDED[i])} ยท ${left[i] ? fill(say('SAY_LEFT'), left[i]) : say('SAY_GONE')}`, tone: TONES[i], on: left[i] > 0 })))) +} + +ctl.start() diff --git a/apps/website/public/widgets/hive-called/card.png b/apps/website/public/widgets/hive-called/card.png new file mode 100644 index 0000000000000000000000000000000000000000..ae5d8d82ea917efd52b4ac0d6ac3cbbc6cc1c5f8 GIT binary patch literal 44470 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 literal 0 HcmV?d00001 diff --git a/apps/website/public/widgets/hive-called/hive-called.t27 b/apps/website/public/widgets/hive-called/hive-called.t27 new file mode 100644 index 0000000000..65f982237e --- /dev/null +++ b/apps/website/public/widgets/hive-called/hive-called.t27 @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: Apache-2.0 +; specs/widgets/hive-called.t27 -- Who is called: an agent bot in a group of agents +; Source of truth for public/widgets/hive-called/ (gHashTag/trinity): scripts/widget-pages-from-spec.mjs +; compiles this file, checks its constants, evaluates the tests below and writes the page; the page +; hands every constant to tool.js as window.T27_WIDGET. tool.js holds no English and NO RULE of its +; own: every column, verdict and number it shows comes from public/widgets/hive-called/logic.js -- the fn +; bodies of gHashTag/t27 specs/automation/agent-hive-group.t27 compiled to wasm, beside its copy logic.t27. +; Lesson 22 of the course queen-and-hive. ASCII only (L3), English only (LANG-EN). +; A K_ constant is an input, a preset, a range or a constant of the logic spec (widget-logic.mjs holds +; a K_ whose name the logic spec also has equal to it); never the result of a spec fn. + +; WHAT IT DOES: the reader toggles tagged, the all tag, a reply to this agent, and the agent's own +; topic. The two cards are addressed(flags, from a person) and addressed(flags, from a bot); every +; table cell is addressed of its row's single flag and its column's sender. + +; WHAT IT DOES NOT CLAIM: it sends nothing to Telegram; a live group also needs Bot-to-Bot +; Communication Mode on in @BotFather. +; phi^2 + 1/phi^2 = 3 | TRINITY + +module widget_hive_called; + +pub const KIND : str = "widget-tool"; +pub const ID : str = "hive-called"; +pub const TITLE : str = "Who is called: an agent bot in a group of agents"; +pub const DESCRIPTION : str = "Each agent has its own bot and topic in one group. Toggle how a message reaches an agent: addressed answers for a person and for another agent's bot."; +pub const IMAGE_ALT : str = "Called card: a message written in the agent's own topic, with no tag and no reply. The person's message wakes the agent, green; the same message from another agent's bot does not, grey. A table of five ways to call reads tag, reply, all tag, own topic, nothing for both senders."; +pub const HOOK : str = "A bot never triggers the whole hive and never wakes a topic's owner by merely writing there. Only a tag or a reply calls an agent from another bot."; +pub const CATEGORY : str = "game"; +pub const DATA_SOURCES : [2]str = ["gHashTag/t27 specs/automation/agent-hive-group.t27 (t27 master c42f8fb40): every value on the page is addressed, its fn bodies compiled to wasm (logic.js)", "Course 11 of t27.ai, queen-and-hive (gHashTag/trinity#1698): lesson 22 opens this widget"]; +pub const READS_LOCAL_FILES : bool = false; +pub const SENDS_NOTHING : bool = true; + +pub const K_CARD_WIDTH : u16 = 1200; +pub const K_CARD_HEIGHT : u16 = 630; + +; --- Numbers: ranges, presets, and constants the logic spec holds ---------------------------- +pub const K_FLAGS : u8 = 4; +pub const K_PRESET_TAGGED : [4]u8 = [0, 1, 0, 0]; +pub const K_PRESET_ALL : [4]u8 = [0, 0, 1, 0]; +pub const K_PRESET_REPLY : [4]u8 = [0, 0, 0, 1]; +pub const K_PRESET_TOPIC : [4]u8 = [1, 0, 0, 0]; +pub const K_START_PRESET : u8 = 0; + +; --- Words on screen ------------------------------------------------------------------------ +pub const SAY_PRESETS_LABEL : str = "Start from"; +pub const SAY_PRESET_NAMES : [4]str = ["In the agent's topic", "Tagged by name", "The all tag", "A reply to the agent"]; +pub const SAY_ASK : str = "A message lands in the hive group. Which agent must answer it?"; +pub const SAY_FLAGS : [4]str = ["tagged by name", "the @all tag", "a reply to this agent", "in this agent's topic"]; +pub const SAY_SENDERS : [2]str = ["a person writes", "another agent's bot writes"]; +pub const SAY_CALLED : str = "called: answers"; +pub const SAY_QUIET : str = "not called: stays quiet"; +pub const SAY_TABLE : str = "one way at a time"; +pub const SAY_NOTHING : str = "none of them"; +pub const SAY_YES : str = "calls"; +pub const SAY_NO : str = "quiet"; +pub const SAY_NOTE : str = "Telegram gives no loop protection between bots. So the rule is narrow for bots: a tag or a reply, nothing else, and the depth and the cooldown of the next two lessons stop the rest."; +pub const SAY_SOURCE : str = "Logic: t27 spec automation/agent-hive-group.t27, compiled"; + +test the_card_is_the_share_size { + assert K_CARD_WIDTH == 1200; + assert K_CARD_HEIGHT == 630; + assert K_CARD_WIDTH * 21 == K_CARD_HEIGHT * 40; +} + +test four_ways_to_call { + assert K_FLAGS == 4; + assert K_PRESET_TOPIC[0] == 1; +} + +test each_preset_sets_one_flag { + assert K_PRESET_TAGGED[1] + K_PRESET_ALL[1] + K_PRESET_REPLY[1] + K_PRESET_TOPIC[1] == 1; + assert K_PRESET_TAGGED[0] + K_PRESET_ALL[0] + K_PRESET_REPLY[0] + K_PRESET_TOPIC[0] == 1; +} diff --git a/apps/website/public/widgets/hive-called/index.html b/apps/website/public/widgets/hive-called/index.html new file mode 100644 index 0000000000..85af67e4f2 --- /dev/null +++ b/apps/website/public/widgets/hive-called/index.html @@ -0,0 +1,47 @@ + + + + + + +Who is called: an agent bot in a group of agents · Trinity SยณAI + + + + + + + + + + + + + + + + + + + + + + + + +
+Trinity SยณAI +

Who is called: an agent bot in a group of agents

+

Each agent has its own bot and topic in one group. Toggle how a message reaches an agent: addressed answers for a person and for another agent's bot.

+
+
+ +
+

This page sets no cookie and sends nothing anywhere.

+
Where the numbers come from
  • gHashTag/t27 specs/automation/agent-hive-group.t27 (t27 master c42f8fb40): every value on the page is addressed, its fn bodies compiled to wasm (logic.js)
  • Course 11 of t27.ai, queen-and-hive (gHashTag/trinity#1698): lesson 22 opens this widget
+

This widget's .t27 source · All widgets

+
+ + + + diff --git a/apps/website/public/widgets/hive-called/logic.js b/apps/website/public/widgets/hive-called/logic.js new file mode 100644 index 0000000000..76652519b2 --- /dev/null +++ b/apps/website/public/widgets/hive-called/logic.js @@ -0,0 +1,116 @@ +// Generated by scripts/t27-logic.mjs from agent-hive-group.t27. Do not edit. +// +// The spec's own fn bodies, compiled: t27c gen (Zig) -> zig 0.16.0 (wasm32). +// Change the spec in gHashTag/t27, vendor it, regenerate. A rule written +// by hand beside this file is the thing this file exists to end. +// spec-sha256: 9cdd5ed3872b60e900a364d843f908920334408f965bde194d233b64534076f9 +// Imported by scripts/widget-logic.mjs for public/widgets/hive-called/: types stripped, nothing else changed. +const WASM = "AGFzbQEAAAABJAVgAX8Bf2ACf38Bf2ADf39/AX9gBH9/f38Bf2AFf39/f38BfwMZGAABAAACAAIBAAEAAQEAAQADAAAAAgAABAQFAXABAQEFAwEAEAYJAX8BQYCAwAALB74DGQZtZW1vcnkCABR0MjdfZG1faGFuZG9mZnNfc2VudAAADXQyN19kbV90YWdfb2sAAQ50MjdfZG1fY2FycmllcwACEHQyN19kbV9ydW5zX3R1cm4AAwx0MjdfZG1faGVhcmQABBF0Mjdfc3VtbWFyeV9jb3VudAAFEnQyN19yZWNyZWF0ZV90b3BpYwAGEXQyN19tYXlfc2V0X2JyaWVmAAcOdDI3X2JyaWVmX2ZpdHMACBB0MjdfYnJpZWZfc291cmNlAAkTdDI3X2tlZXBzX3NlZW5fbWFyawAKC3QyN190dXJuX29rAAsVdDI3X2hlYXJkX3doaWxlX211dGVkAAwJdDI3X211dGVkAA0ZdDI3X21heV9hbnN3ZXJfYWZ0ZXJfc3RvcAAOC3QyN19zdG9wcGVkAA8OdDI3X2ZvbGxvd3NfdXAAEBF0Mjdfc3BlYWtzX29uX2FsbAARDnQyN190eXBpbmdfZHVlABIPdDI3X2NsYWltX2ZyZXNoABMOdDI3X21heV9hbnN3ZXIAFAp0MjdfY29vbGVkABUOdDI3X2NoYWluX29wZW4AFg10MjdfYWRkcmVzc2VkABcK7AIYDAAgAEECIABBAkkbCyoBAX9BASECAkACQAJAIABBf2oOAgIAAQsgAUHA7u0ASQ8LQQAhAgsgAgsHACAAQQJGCwcAIABBAUYLDQAgAEEBcyABIAJxcgsTACAAQcgBIABByAFJG0EyIAAbCxIAIAAgAXFBf3MgAkG/zyRLcQsKACAAIAFBAXNxCwgAIABB6QdJCw8AQQIgAUEBcSAAQQFxGwsEACAACwQAIAALDQAgACABQf/c2wFLcgsKACAAQYDd2wFJCw8AIABBAXMgAUG/zyRLcgsJACAAQcDPJEkLFQAgAEF/cyABcSACcSADQcCpB0lxCwUAIABFCwgAIABBnx9LCwkAIABBoPc2SQsjAQF/QQEhAwJAIABBAXFFDQAgAUEDSSACQYcnS3EhAwsgAwsIACAAQYcnSwsHACAAQQNJCygBAX9BASEFAkAgAEEBcQ0AIAJBAXENACAEQQFzIAEgA3JxIQULIAUL"; +const W = new WebAssembly.Instance( + new WebAssembly.Module(Uint8Array.from(atob(WASM), (c) => c.charCodeAt(0))) +).exports; +function int(v, lo, hi, at) { + if (!Number.isInteger(v) || v < lo || v > hi) + throw new RangeError(`${at}: ${v} is outside ${lo}..${hi}`); + return v; +} +function addressed(tagged, allTag, replyToMe, ownTopic, fromBot) { + return (W.t27_addressed(tagged ? 1 : 0, allTag ? 1 : 0, replyToMe ? 1 : 0, ownTopic ? 1 : 0, fromBot ? 1 : 0) & 1) !== 0; +} +function chainOpen(depth) { + return (W.t27_chain_open(int(depth, 0, 4294967295, "chain_open.depth")) & 1) !== 0; +} +function cooled(sinceLastMs) { + return (W.t27_cooled(int(sinceLastMs, 0, 4294967295, "cooled.since_last_ms")) & 1) !== 0; +} +function mayAnswer(fromBot, depth, sinceLastMs) { + return (W.t27_may_answer(fromBot ? 1 : 0, int(depth, 0, 4294967295, "may_answer.depth"), int(sinceLastMs, 0, 4294967295, "may_answer.since_last_ms")) & 1) !== 0; +} +function claimFresh(ageMs) { + return (W.t27_claim_fresh(int(ageMs, 0, 4294967295, "claim_fresh.age_ms")) & 1) !== 0; +} +function typingDue(sinceLastMs) { + return (W.t27_typing_due(int(sinceLastMs, 0, 4294967295, "typing_due.since_last_ms")) & 1) !== 0; +} +function speaksOnAll(rank) { + return (W.t27_speaks_on_all(int(rank, 0, 4294967295, "speaks_on_all.rank")) & 1) !== 0; +} +function followsUp(fromBot, samePerson, sameThread, ageMs) { + return (W.t27_follows_up(fromBot ? 1 : 0, samePerson ? 1 : 0, sameThread ? 1 : 0, int(ageMs, 0, 4294967295, "follows_up.age_ms")) & 1) !== 0; +} +function stopped(sinceStopMs) { + return (W.t27_stopped(int(sinceStopMs, 0, 4294967295, "stopped.since_stop_ms")) & 1) !== 0; +} +function mayAnswerAfterStop(fromBot, sinceStopMs) { + return (W.t27_may_answer_after_stop(fromBot ? 1 : 0, int(sinceStopMs, 0, 4294967295, "may_answer_after_stop.since_stop_ms")) & 1) !== 0; +} +function muted(sinceMuteMs) { + return (W.t27_muted(int(sinceMuteMs, 0, 4294967295, "muted.since_mute_ms")) & 1) !== 0; +} +function heardWhileMuted(tagged, sinceMuteMs) { + return (W.t27_heard_while_muted(tagged ? 1 : 0, int(sinceMuteMs, 0, 4294967295, "heard_while_muted.since_mute_ms")) & 1) !== 0; +} +function turnOk(answerSent, reactionOk) { + return (W.t27_turn_ok(answerSent ? 1 : 0, reactionOk ? 1 : 0) & 1) !== 0; +} +function keepsSeenMark(answerSent) { + return (W.t27_keeps_seen_mark(answerSent ? 1 : 0) & 1) !== 0; +} +function briefSource(hasTopicBrief, hasGroupBrief) { + return W.t27_brief_source(hasTopicBrief ? 1 : 0, hasGroupBrief ? 1 : 0) & 255; +} +function briefFits(chars) { + return (W.t27_brief_fits(int(chars, 0, 4294967295, "brief_fits.chars")) & 1) !== 0; +} +function maySetBrief(isAdmin, fromBot) { + return (W.t27_may_set_brief(isAdmin ? 1 : 0, fromBot ? 1 : 0) & 1) !== 0; +} +function recreateTopic(stored, threadFound, sinceLastTryMs) { + return (W.t27_recreate_topic(stored ? 1 : 0, threadFound ? 1 : 0, int(sinceLastTryMs, 0, 4294967295, "recreate_topic.since_last_try_ms")) & 1) !== 0; +} +function summaryCount(asked) { + return W.t27_summary_count(int(asked, 0, 4294967295, "summary_count.asked")) >>> 0; +} +function dmHeard(fromBot, colleague, tagOk) { + return (W.t27_dm_heard(fromBot ? 1 : 0, colleague ? 1 : 0, tagOk ? 1 : 0) & 1) !== 0; +} +function dmRunsTurn(kind) { + return (W.t27_dm_runs_turn(int(kind, 0, 255, "dm_runs_turn.kind")) & 1) !== 0; +} +function dmCarries(kind) { + return (W.t27_dm_carries(int(kind, 0, 255, "dm_carries.kind")) & 1) !== 0; +} +function dmTagOk(kind, sinceAskMs) { + return (W.t27_dm_tag_ok(int(kind, 0, 255, "dm_tag_ok.kind"), int(sinceAskMs, 0, 4294967295, "dm_tag_ok.since_ask_ms")) & 1) !== 0; +} +function dmHandoffsSent(asked) { + return W.t27_dm_handoffs_sent(int(asked, 0, 4294967295, "dm_handoffs_sent.asked")) >>> 0; +} +const __NOT_LOWERED__ = Object.freeze([]); +export { + __NOT_LOWERED__, + addressed, + briefFits, + briefSource, + chainOpen, + claimFresh, + cooled, + dmCarries, + dmHandoffsSent, + dmHeard, + dmRunsTurn, + dmTagOk, + followsUp, + heardWhileMuted, + keepsSeenMark, + mayAnswer, + mayAnswerAfterStop, + maySetBrief, + muted, + recreateTopic, + speaksOnAll, + stopped, + summaryCount, + turnOk, + typingDue +}; diff --git a/apps/website/public/widgets/hive-called/logic.t27 b/apps/website/public/widgets/hive-called/logic.t27 new file mode 100644 index 0000000000..2f801b0db7 --- /dev/null +++ b/apps/website/public/widgets/hive-called/logic.t27 @@ -0,0 +1,361 @@ +// SPDX-License-Identifier: Apache-2.0 +// specs/automation/agent-hive-group.t27 -- the agents' hive: one bot per agent, one forum group. +// Host: 999-multibots-telegraf src/core/bot/agentHive/ (addressing, chain, catalogue of bots). +// Trace: owner 2026-10-05, "every agent its own bot and topic, all together in one group". +// Generated: t27c gen-ts; vendored spec and generated constants in src/spec/. +// Claim status: routing contract; a live group needs Bot-to-Bot Communication Mode +// switched on in @BotFather for every bot, which no API call can do. +// Telegram gives no loop protection between bots, so the depth and the cooldown +// below are the only thing that stops two agents from answering each other forever. +// v1 (2026-10-05): who answers, the bot chain limit, the cooldown, the claim window. +// v2 (2026-10-05): the competitor round (999-multibots-telegraf epic #3641) -- typing kept +// alive, one first speaker on the all tag, follow-ups without a tag, /stop and /mute. +// v3 (2026-10-06): the rest of that round -- the seen/done reaction, standing briefs, +// a topic a person deleted, and /summary. +// v4 (2026-10-09): agents hand off in private, bot to bot. Telegram lets a bot send a private +// message to another bot by @username when Bot-to-Bot Communication Mode is on for both +// (owner: "I switched it on exactly for A2A"). A colleague is heard in private only with the +// hand-off tag, an ask is one hop, and the answer is carried to the person, never a new turn, +// and only while its ask is fresh. +// Earlier laws are unchanged; each version only adds, so a host on an older one stays true. +// phi^2 + 1/phi^2 = 3 | TRINITY + +module automation::agent_hive_group { + pub const KIND : str = "automation"; + pub const ID : str = "agent-hive-group"; + pub const REPO : str = "999-multibots-telegraf"; + pub const VERSION : u8 = 4; + + // Bot messages in a row after one human message: human -> A -> B -> C, then silence. + pub const MAX_BOT_DEPTH : u32 = 3; + // One agent answers another agent at most this often. + pub const BOT_REPLY_COOLDOWN_MS : u32 = 5000; + // How long "create the bot for agent X" waits for Telegram's creation message. + pub const CLAIM_TTL_MS : u32 = 900000; + // The tag that calls every agent of the hive at once. + pub const ALL_TAG : str = "@all"; + // Suggested username of an agent's bot: t27__bot. + pub const BOT_USERNAME_PREFIX : str = "t27_"; + pub const BOT_USERNAME_SUFFIX : str = "_bot"; + + // A person may call an agent by its tag, by the all tag, by replying to it, + // or by writing in the agent's own topic. Another bot only by tag or reply: + // a bot never triggers the whole hive, and never wakes a topic's owner by + // merely writing there. + fn addressed(tagged: bool, all_tag: bool, reply_to_me: bool, own_topic: bool, from_bot: bool) -> bool { + return tagged || reply_to_me || (!from_bot && (all_tag || own_topic)); + } + + // depth = how many bot messages already stand between this one and the person. + fn chain_open(depth: u32) -> bool { + return depth < MAX_BOT_DEPTH; + } + + fn cooled(since_last_ms: u32) -> bool { + return since_last_ms >= BOT_REPLY_COOLDOWN_MS; + } + + // A person is always answered; a bot only inside the chain and after the cooldown. + fn may_answer(from_bot: bool, depth: u32, since_last_ms: u32) -> bool { + return !from_bot || (chain_open(depth) && cooled(since_last_ms)); + } + + fn claim_fresh(age_ms: u32) -> bool { + return age_ms < CLAIM_TTL_MS; + } + + // ---- v2 ---- + + // Telegram shows "typing" for about five seconds; an agent turn takes up to a minute. + // The action is sent again this long after the last one, until the answer goes out. + pub const TYPING_REFRESH_MS : u32 = 4000; + // On the all tag only this many agents answer at once, best fit first; the rest + // answer only when tagged. Eight answers to one "@all hi" is a flood, not a team. + pub const ALL_FIRST_SPEAKERS : u32 = 1; + // A person's next message in the same topic goes to the agent that answered + // last, without a new tag, for this long. + pub const FOLLOW_UP_WINDOW_MS : u32 = 120000; + // /stop: no bot answers a bot in that chat for this long. People are still answered. + pub const STOP_WINDOW_MS : u32 = 600000; + // /mute: the agent answers in that topic only when tagged by name, for this long. + pub const MUTE_MS : u32 = 3600000; + + fn typing_due(since_last_ms: u32) -> bool { + return since_last_ms >= TYPING_REFRESH_MS; + } + + // rank = the agent's place in the best-fit order for this message, from 0. + fn speaks_on_all(rank: u32) -> bool { + return rank < ALL_FIRST_SPEAKERS; + } + + // A bot never follows up: it must tag or reply, as v1 says. + fn follows_up(from_bot: bool, same_person: bool, same_thread: bool, age_ms: u32) -> bool { + return !from_bot && same_person && same_thread && age_ms < FOLLOW_UP_WINDOW_MS; + } + + fn stopped(since_stop_ms: u32) -> bool { + return since_stop_ms < STOP_WINDOW_MS; + } + + // A stop cuts bot-to-bot turns only; a person is always heard. + fn may_answer_after_stop(from_bot: bool, since_stop_ms: u32) -> bool { + return !from_bot || !stopped(since_stop_ms); + } + + fn muted(since_mute_ms: u32) -> bool { + return since_mute_ms < MUTE_MS; + } + + // A muted agent still answers its own tag: muting a topic must not lose a direct call. + fn heard_while_muted(tagged: bool, since_mute_ms: u32) -> bool { + return tagged || !muted(since_mute_ms); + } + + // ---- v3 ---- + + // A reaction is a courtesy: chats may switch reactions off, so the answer counts + // whether or not the reaction went through. The "seen" mark stays only when the + // turn ended in an answer; a failed turn takes it away so nobody waits for nothing. + fn turn_ok(answer_sent: bool, reaction_ok: bool) -> bool { + return answer_sent; + } + + fn keeps_seen_mark(answer_sent: bool) -> bool { + return answer_sent; + } + + // /brief: standing instructions for a group or one topic, set by an admin only. + pub const MAX_BRIEF_CHARS : u32 = 1000; + // Which brief an agent turn carries: 0 none, 1 the group's, 2 the topic's. + // The narrower one wins; the two are never glued together. + fn brief_source(has_topic_brief: bool, has_group_brief: bool) -> u8 { + if has_topic_brief { return 2; } + if has_group_brief { return 1; } + return 0; + } + + // An empty /brief is accepted: it clears the brief, it is not refused. + fn brief_fits(chars: u32) -> bool { + return chars <= MAX_BRIEF_CHARS; + } + + fn may_set_brief(is_admin: bool, from_bot: bool) -> bool { + return is_admin && !from_bot; + } + + // A person deleted an agent's topic: the stored id is stale, so it is recreated. + // Without rights the recreate fails every time, so it is tried at most this often. + pub const TOPIC_RECREATE_COOLDOWN_MS : u32 = 600000; + fn recreate_topic(stored: bool, thread_found: bool, since_last_try_ms: u32) -> bool { + return (!stored || !thread_found) && since_last_try_ms >= TOPIC_RECREATE_COOLDOWN_MS; + } + + // /summary: the topic keeps a bounded log; a bare /summary reads the default count. + pub const SUMMARY_LOG_SIZE : u32 = 200; + pub const SUMMARY_DEFAULT : u32 = 50; + fn summary_count(asked: u32) -> u32 { + if asked == 0 { return SUMMARY_DEFAULT; } + if asked > SUMMARY_LOG_SIZE { return SUMMARY_LOG_SIZE; } + return asked; + } + + // ---- v4: hand-off in private, bot to bot ---- + + // Every private hand-off between two agents starts with this tag, then its kind and the + // person it is for. Only a hive agent of the same owner can send it, so the person named in + // it is trusted: the turn runs in that person's name, never the owner's. + pub const DM_TAG : str = "#a2a"; + pub const DM_ASK : u8 = 1; + pub const DM_ANSWER : u8 = 2; + // An answer to a person hands off to at most this many colleagues. + pub const MAX_DM_HANDOFFS : u32 = 2; + + // In private a person is always heard; a bot only when it is a colleague (an agent bot of + // the same owner) and its message carries the hand-off tag with a known kind. + fn dm_heard(from_bot: bool, colleague: bool, tag_ok: bool) -> bool { + return !from_bot || (colleague && tag_ok); + } + + // An ask runs the colleague's turn; an answer is only carried to the person. So a private + // chain is two messages long and cannot loop, whatever the agents write. + fn dm_runs_turn(kind: u8) -> bool { + return kind == DM_ASK; + } + fn dm_carries(kind: u8) -> bool { + return kind == DM_ANSWER; + } + + // An ask is always a known tag; an answer only while its ask is fresh -- a colleague never + // writes to a person nobody asked it about. Half an hour covers the longest turn. + pub const DM_ANSWER_WINDOW_MS : u32 = 1800000; + fn dm_tag_ok(kind: u8, since_ask_ms: u32) -> bool { + if kind == DM_ASK { return true; } + if kind == DM_ANSWER { return since_ask_ms < DM_ANSWER_WINDOW_MS; } + return false; + } + + // How many of the hand-offs an answer asked for are sent. + fn dm_handoffs_sent(asked: u32) -> u32 { + if asked > MAX_DM_HANDOFFS { return MAX_DM_HANDOFFS; } + return asked; + } + + test "a person calls an agent by tag, all tag, reply or its own topic" { + assert(addressed(true, false, false, false, false)); + assert(addressed(false, true, false, false, false)); + assert(addressed(false, false, true, false, false)); + assert(addressed(false, false, false, true, false)); + assert(!addressed(false, false, false, false, false)); + } + + test "a bot calls an agent only by tag or reply" { + assert(addressed(true, false, false, false, true)); + assert(addressed(false, false, true, false, true)); + assert(!addressed(false, true, false, false, true)); + assert(!addressed(false, false, false, true, true)); + assert(!addressed(false, true, false, true, true)); + } + + test "the bot chain closes after three bot messages" { + assert(chain_open(0)); + assert(chain_open(2)); + assert(!chain_open(3)); + assert(!chain_open(4)); + } + + test "a bot waits out the cooldown before answering a bot" { + assert(!cooled(0)); + assert(!cooled(4999)); + assert(cooled(5000)); + } + + test "a person is always answered and a bot only inside the limits" { + assert(may_answer(false, 9, 0)); + assert(may_answer(true, 2, 5000)); + assert(!may_answer(true, 3, 5000)); + assert(!may_answer(true, 2, 4999)); + } + + test "a creation claim lives fifteen minutes" { + assert(claim_fresh(0)); + assert(claim_fresh(899999)); + assert(!claim_fresh(900000)); + } + + test "typing is sent again four seconds after the last one" { + assert(!typing_due(0)); + assert(!typing_due(3999)); + assert(typing_due(4000)); + } + + test "on the all tag only the best fit answers at once" { + assert(speaks_on_all(0)); + assert(!speaks_on_all(1)); + assert(!speaks_on_all(7)); + } + + test "a person follows up in the same topic for two minutes without a tag" { + assert(follows_up(false, true, true, 0)); + assert(follows_up(false, true, true, 119999)); + assert(!follows_up(false, true, true, 120000)); + assert(!follows_up(false, false, true, 0)); + assert(!follows_up(false, true, false, 0)); + assert(!follows_up(true, true, true, 0)); + } + + test "a stop silences bots for ten minutes and never a person" { + assert(!may_answer_after_stop(true, 0)); + assert(!may_answer_after_stop(true, 599999)); + assert(may_answer_after_stop(true, 600000)); + assert(may_answer_after_stop(false, 0)); + } + + test "a muted agent still answers its own tag for the hour" { + assert(heard_while_muted(true, 0)); + assert(!heard_while_muted(false, 0)); + assert(!heard_while_muted(false, 3599999)); + assert(heard_while_muted(false, 3600000)); + } + + test "the answer counts even when the chat refuses reactions" { + assert(turn_ok(true, false)); + assert(turn_ok(true, true)); + assert(!turn_ok(false, true)); + assert(keeps_seen_mark(true)); + assert(!keeps_seen_mark(false)); + } + + test "the topic brief wins over the group brief, and only an admin sets one" { + assert(brief_source(true, true) == 2); + assert(brief_source(true, false) == 2); + assert(brief_source(false, true) == 1); + assert(brief_source(false, false) == 0); + assert(brief_fits(0)); + assert(brief_fits(1000)); + assert(!brief_fits(1001)); + assert(may_set_brief(true, false)); + assert(!may_set_brief(false, false)); + assert(!may_set_brief(true, true)); + } + + test "a deleted topic is recreated, at most once in ten minutes" { + assert(recreate_topic(false, false, 600000)); + assert(recreate_topic(false, true, 600000)); + assert(recreate_topic(true, false, 600000)); + assert(!recreate_topic(true, true, 600000)); + assert(!recreate_topic(true, false, 599999)); + } + + test "a summary reads fifty by default and never past the log" { + assert(summary_count(0) == 50); + assert(summary_count(1) == 1); + assert(summary_count(10) == 10); + assert(summary_count(200) == 200); + assert(summary_count(201) == 200); + } + + test "in private a colleague is heard only with the hand-off tag" { + assert(dm_heard(false, false, false)); + assert(dm_heard(true, true, true)); + assert(!dm_heard(true, false, true)); + assert(!dm_heard(true, true, false)); + } + + test "an ask runs a turn, an answer is only carried" { + assert(DM_ASK == 1); + assert(DM_ANSWER == 2); + assert(dm_runs_turn(DM_ASK)); + assert(!dm_runs_turn(DM_ANSWER)); + assert(!dm_runs_turn(0)); + assert(dm_carries(DM_ANSWER)); + assert(!dm_carries(DM_ASK)); + assert(!dm_carries(0)); + } + + test "an answer is carried only while its ask is fresh" { + assert(DM_ANSWER_WINDOW_MS == 1800000); + assert(dm_tag_ok(DM_ASK, 4000000000)); + assert(dm_tag_ok(DM_ANSWER, 0)); + assert(dm_tag_ok(DM_ANSWER, 1799999)); + assert(!dm_tag_ok(DM_ANSWER, 1800000)); + assert(!dm_tag_ok(DM_ANSWER, 4000000000)); + assert(!dm_tag_ok(0, 0)); + assert(!dm_tag_ok(3, 0)); + } + + test "an answer hands off to at most two colleagues" { + assert(MAX_DM_HANDOFFS == 2); + assert(dm_handoffs_sent(0) == 0); + assert(dm_handoffs_sent(1) == 1); + assert(dm_handoffs_sent(2) == 2); + assert(dm_handoffs_sent(3) == 2); + assert(dm_handoffs_sent(100) == 2); + } + + test "VERSION says what the header's last note says" { + assert(VERSION == 4); + } +} + +// phi^2 + 1/phi^2 = 3 | TRINITY diff --git a/apps/website/public/widgets/hive-called/tool.js b/apps/website/public/widgets/hive-called/tool.js new file mode 100644 index 0000000000..3049b52e41 --- /dev/null +++ b/apps/website/public/widgets/hive-called/tool.js @@ -0,0 +1,26 @@ +// hive-called/tool.js -- lesson 22 of queen-and-hive: who is called in a group of agent bots. Both +// cards and every table cell are addressed(tagged, all tag, reply, own topic, from a bot), the fn body +// of gHashTag/t27 specs/automation/agent-hive-group.t27 compiled to wasm (./logic.js). Words are SAY_ +// and numbers K_ constants of specs/widgets/hive-called.t27. +import { addressed } from './logic.js' +import { K, W, ask, controls, el, grid, head, root, say, source, two, verdict } from '../hive-kit.js' + +const NF = K('K_FLAGS', 4) +const keys = ['tagged', 'all', 'reply', 'topic'] +const ctl = controls(keys.map((key, i) => ({ key, label: say('SAY_FLAGS', i), kind: 'toggle' })), + { names: W.SAY_PRESET_NAMES, values: { tagged: K('K_PRESET_TAGGED', []), all: K('K_PRESET_ALL', []), reply: K('K_PRESET_REPLY', []), topic: K('K_PRESET_TOPIC', []) } }, draw) + +const out = el('div') +root.replaceChildren(ask(say('SAY_ASK')), ctl.node, out, el('p', { class: 'dk-note' }, say('SAY_NOTE')), source()) + +function draw(s) { + const f = keys.map((k) => !!s[k]) + const card = (bot) => { const on = addressed(f[0], f[1], f[2], f[3], bot); return verdict(on ? say('SAY_CALLED') : say('SAY_QUIET'), on ? 0 : 8, say('SAY_SENDERS', bot ? 1 : 0)) } + const one = (row) => [...Array(NF)].map((_, i) => i === row) + out.replaceChildren( + two(card(false), card(true)), + head(say('SAY_TABLE')), + grid({ rows: NF + 1, cols: 2, rowLabels: [...W.SAY_FLAGS, say('SAY_NOTHING')], colLabels: W.SAY_SENDERS, cell: (r, c) => { const v = addressed(...one(r), c === 1); return { text: v ? say('SAY_YES') : say('SAY_NO'), tone: v ? 0 : 8, on: v } } })) +} + +ctl.start() diff --git a/apps/website/public/widgets/hive-cell/card.png b/apps/website/public/widgets/hive-cell/card.png new file mode 100644 index 0000000000000000000000000000000000000000..71e6bef96daa82cc0e32b6be63fc7b349f993b8e GIT binary patch literal 59722 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