Skip to content

feat(queen): record what accepted .t27 work has earned, append-only - #516

Merged
gHashTag merged 15 commits into
fix/queen-worker-provider-and-prompt-sizefrom
feat/queen-tri-earnings
Oct 2, 2026
Merged

gHashTag merged 15 commits into
fix/queen-worker-provider-and-prompt-sizefrom
feat/queen-tri-earnings

Conversation

@gHashTag

@gHashTag gHashTag commented Oct 1, 2026 •

Copy link
Copy Markdown
Owner

Why

Owner's goal (2026-10-01): people who write .t27 specs mine TRI for the specs the Queen accepts, and later sell it as a TON jetton. Nothing can be minted from a number nobody wrote down, so this PR adds the record. It is phase 1 of the plan in gHashTag/trinity-fpga#801 (docs/docs/depin/decisions.md). No token is involved, and nothing here is withdrawable.

What

  • queen_tri_earnings (in MIGRATION_SQL) is append-only:
    • One row per (repository, issue, judged commit) the Queen accepted, where the turn's boundary declared a .t27 file.
    • work_id = sha256('t27-accept:v1|<repo>|<issue>|<commit>'). Anyone can recompute it from public data, which is what work_id in the mint protocol needs.
    • There is no amount column. TRI per spec is an owner decision (O2) and is not decided.
  • recordEarnings(pool, repo) runs every round, after the CI take-back:
    • It is idempotent.
    • A later sendBack/escalate of the same commit revokes the earning, and the revocation is final. A fresh accept of the same commit does not bring it back. A new commit accepted after a send-back is a new earning.
  • GET /queen/public-earnings is public-read on the same terms as the leaderboard:
    • It returns earners (grouped through TRIOS_KEY_OWNERS), totals, and the last 100 earnings.
    • It never includes an issue title, worker text, review note or credential.
    • Its body says "recorded, not withdrawable: no token is deployed" and triPerSpec: null, and lists what is not yet true:
      • an accept does not require a merge (O4);
      • spec paths are declared, not yet checked against the diff;
      • TRI per spec is undecided.

Why a table rather than derive-on-read like the leaderboard

A CI take-back edits queen_dispatch in place (queen-ci-verdict.ts). If earnings were derived on read, an acceptance that was later taken back would disappear. Here it stays, with revoked_at beside it.

Tests

  • tests/api/queen-tri-earnings.test.ts (unit):
    • grouping and ranking;
    • query parameters, ON CONFLICT DO NOTHING, and the "only a later verdict revokes" guard;
    • the "no amount, not withdrawable" wording;
    • a 503 without a database.
  • tests/pglive/queen-tri-earnings-live.test.ts runs against a real PostgreSQL (scratch DB, same rules as the migration gate: fails rather than skips unless TRIOS_PG_MIGRATE_GATE=offline). It checks:
    • the same commit accepted twice is recorded once, dated by the first acceptance;
    • a non-.t27 accept is excluded;
    • work_id equals a sha256 computed in Node;
    • a send-back before the acceptance does not revoke it;
    • a take-back revokes, a re-accept does not resurrect, and a new commit earns.
  • Route-guard census re-measured: 46 mounts, 9 public-read, 23 /queen mounts. Prefix and wrapper counts are unchanged.

Run locally:

  • bun test on the four touched suites: 44 pass.
  • bun run test:pglive: 4 pass, against local PG 17.
  • tsc --noEmit: 0 errors.
  • biome check: clean. The one existing complexity warning in queen-tick.ts was already there.

Not in this PR

  • The amount (O2), the merge requirement (O4), and diff-checking the spec paths.
  • The attestor service and any minting. Those stay blocked on owner decisions O1–O6.
  • The website panel that reads this route will come in a separate PR in gHashTag/trinity.

Base and CI

🤖 Generated with Claude Code

The first half of spec authors mining TRI: nothing can be minted from a
number nobody wrote down. Every Queen round now records each accepted turn
whose boundary names a .t27 file as one earning per (repository, issue,
judged commit), with work_id = sha256('t27-accept:v1|repo|issue|commit') so
anyone can recompute it from public data.

Why a table, when the leaderboard derives its score on read: a CI take-back
edits queen_dispatch in place, so an acceptance derived on read would vanish
instead of showing as taken back. Rows here are inserted and revoked, never
deleted. A later sendBack/escalate of the same commit revokes an earning, and
the revocation is final.

GET /queen/public-earnings serves the record (public-read, no titles, no
worker text, no notes) and says in its own body that nothing is withdrawable:
no token is deployed, TRI per spec is undecided, and an accept does not yet
require a merge.

Tests: unit (grouping, query parameters, 503 without a database) and a live
PostgreSQL test in tests/pglive covering idempotency, the work_id hash, the
non-.t27 exclusion, take-back revocation and a new commit after a send-back.
Route-guard census re-measured: 46 mounts, 9 public-read.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

✅ Tests passed — 2450/2510

Suite Passed Failed Skipped
✅ agent 87/87 0 0
✅ build 9/9 0 0
✅ cdp-protocol 5/5 0 0
✅ eval 93/93 0 0
✅ server-agent 272/272 0 0
✅ server-api 1319/1378 0 59
✅ server-browser 6/6 0 0
✅ server-integration 10/11 0 1
✅ server-lib 279/279 0 0
✅ server-pglive 13/13 0 0
✅ server-root 68/68 0 0
✅ server-skills 31/31 0 0
✅ server-tools 244/244 0 0
✅ shared 14/14 0 0

View workflow run

claude and others added 11 commits October 1, 2026 16:52
…dules symlink

Every `Tests / *` job on #517 and #518 was cancelled at the 20-minute
budget while still inside `bun ci`, before any test ran.

Two things combined:
- trios/agent-server/apps/server/node_modules was committed as a symlink
  to a local macOS path. `.gitignore` said `node_modules/`, which only
  matches directories, so the symlink slipped through.
- setup-bun ran without a version. The `packageManager: bun@1.3.6` pin
  lives in trios/agent-server/package.json, not at the repo root, so CI
  got the latest release (1.4.2), which hangs on that dangling symlink.
  1.3.x installs past it.

Untrack the symlink, make the ignore rule match files too, and read the
Bun version from the workspace package.json in test.yml.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SJ8KjRoGNBoHBoDR92fAo2
GET /queen/public-earnings/:workId returns one earning with its earner's
GitHub login, the scheme and TRI_PER_SPEC = 27 (owner decision O2,
2026-10-01). This is what each TRI signer reads before it signs; the merge
rule (O4) is checked by the signers on GitHub, not here. Status text says
what is true: mintable on TON testnet only, V1, signer quorum, NOT trustless.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…example.com

With installs no longer hanging, server-tools ran for the first time
since 2026-09-23 and failed one test: get_page_content read
https://example.com 57 ms after opening it and found no "Example
Domain". The test is about extracting text, so it now writes that text
into about:blank with evaluate_script, as get_page_links already does.

Locally (BrowserOS AppImage, headless, --no-sandbox): the old test
fails the same way; the new one passes 3/3.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SJ8KjRoGNBoHBoDR92fAo2
GET /queen/public-earnings/by/:github lists the earnings of the keys lent
under that login, newest first: what the TRI wallet shows its owner as
claimable. The ledger's 'recent' is capped at 100 and cannot serve this.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… process

server-tools still exited 1 after every test in observation.test.ts
passed: before navigation-newtab-guard.test.ts the helper ran
`lsof -ti :<cdp port>`, which also lists clients still connected to the
port. One of them was the bun test process itself (its CDP socket to the
previous file's browser), so the SIGTERM ended the whole run and no
junit report was written ("workflow > server-tools setup").

Use `lsof -ti tcp:<port> -sTCP:LISTEN` and drop process.pid.

Locally, input.test.ts + navigation-newtab-guard.test.ts in one process:
before, exit 143 right after "Terminating process(es) <own pid>, ...";
after, 18 pass / 0 fail. The whole test:tools group now runs to the end
(242 pass; the 2 local failures load https://example.com, which this
sandbox's browser cannot reach and CI can).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SJ8KjRoGNBoHBoDR92fAo2
…example.com

With the run no longer killing itself, server-tools finished in CI with
243 pass / 1 fail: `wait_for finds text on page` waited its full 10 s
for "Example Domain" on https://example.com and never saw it - the same
page get_page_content could not read either.

The page now adds that text itself 500 ms after load, so the test still
proves wait_for waits, with nothing outside the runner involved.

Locally: 2/2 wait_for tests pass on repeat; the whole test:tools group
is 243 pass, the one local failure being take_screenshot (a 60 s hang
in this sandbox only - it passes in CI).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SJ8KjRoGNBoHBoDR92fAo2
… too

server-tools on 3d57649 ran clean except one test that had passed on
both earlier runs: `search_dom > finds multiple elements with CSS class
selector` (123 ms, fewer than 3 matches). It searches once, straight
after new_page - the race this file already names and fixes with
searchUntil for two sibling tests. Use the same helper here.

Locally: search_dom 13/13, three runs in a row.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SJ8KjRoGNBoHBoDR92fAo2
…udget

`the salvage commit > never splits a rename across the path cap` runs
real git over 205 files and salvageWorktree. It takes ~2 s for the whole
file locally and passed on the two CI runs before, then hit bun's 5 s
default once on a loaded runner (job 110500921083) with nothing in the
change touching salvage. A git-heavy fixture test should not share the
budget of a pure unit test.

Locally: queen-salvage-guards.test.ts 13 pass / 0 fail.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SJ8KjRoGNBoHBoDR92fAo2
#522 mounted /queen/contributor-keys and left the route-guard audit
unchanged, so feat/queen-supervisor fails four route-guard tests: 46
mounts against a pin of 45, 23 /queen mounts against 22, and an
unguarded mount nobody allowlisted.

The route is a server-to-server door for the app render proxy and has
its own guard: a bearer equal to QUEEN_CONTRIBUTOR_PROXY_TOKEN (32+
bytes, timingSafeEqual) plus a verified contributor header, and it is
off while that token is unset. The trusted-origin check would refuse
its only caller, so it is allowlisted with that reason and the pins are
re-measured. No other number moved.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01SJ8KjRoGNBoHBoDR92fAo2
Bring the PR up to date with its base branch (contributor keys and XP
account management, #522; public board verdict and judged head, #517).

Conflict in src/api/server.ts: both sides added a mount right after
/queen/public-leaderboard. Keep both - /queen/public-earnings from this
branch and /queen/contributor-keys from the base. Every other file merged
cleanly; the queen_tri_earnings DDL in pg-migrate.ts and the
recordEarnings call in queen-tick.ts are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The base object literal widened EARNINGS_STATUS to string, so the
function no longer matched EarningsOfLogin. Typing base as
Omit<EarningsOfLogin, 'earnings'> keeps the literal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gHashTag
gHashTag changed the base branch from feat/queen-supervisor to fix/queen-worker-provider-and-prompt-size October 2, 2026 07:10
…into feat/queen-tri-earnings

Route-guard pins now add both new mounts: 47 total, 24 under /queen
(9 public-read, 8 wrapper-guarded, 7 allowlisted).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gHashTag

gHashTag commented Oct 2, 2026

Copy link
Copy Markdown
Owner Author

Reviewer bee: I reviewed the code and found no blocking issues. Tests / * and PR test summary are green, and the pglive earnings test ran against a real Postgres. I merged #520 first as a squash (29ed593). After that, GitHub marks this PR CONFLICTING/DIRTY against fix/queen-worker-provider-and-prompt-size. The likely cause is that this branch carries #520's original commits while the base now has the squashed copy, so the shared edits collide, most likely in tests/api/routes/route-guard.test.ts, where #520 pins 46/23 and this PR pins 47/24. I have not resolved it and have not merged. The author needs to merge the base in, keep this PR's numbers (47 total, 24 /queen: 9 public-read, 8 wrapper, 7 allowlisted), and push. Once CI is green again I will merge.

Non-blocking notes from the review:

  • earningsLedger reads the whole queen_tri_earnings table on every public GET. Only recent is capped at 100, and /by/:github is uncapped. The table grows only with accepted spec turns, so this is fine for now. If it grows, consider SQL aggregates for the totals and earners plus a LIMIT.
  • Each public request opens and closes its own pg pool (max 1). That is better than the leaderboard, which never closes its pool, but every uncached hit still costs a fresh DB connection.

Route-guard pins keep this branch's sums: 47 mounts, 24 under /queen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gHashTag
gHashTag merged commit cca450d into fix/queen-worker-provider-and-prompt-size Oct 2, 2026
15 of 16 checks passed
@gHashTag
gHashTag deleted the feat/queen-tri-earnings branch October 2, 2026 08:35
gHashTag added a commit to gHashTag/trinity that referenced this pull request Oct 4, 2026
…e will never be a token (#1201)

* feat(game): show recorded .t27 spec earnings, and stop promising there will never be a token

The leaderboard tab now reads /queen/public-earnings (gHashTag/BrowserOS#516)
and shows, per lender, how many accepted .t27 commits were written down as
earned and how many a later verdict on the same commit took back. The server's
own status sentence ("recorded, not withdrawable: no token is deployed") is
printed above the counts, translated only when it matches word for word, and
the server's list of what is not yet true is printed under them. Until the
swarm serving the page has the route, the panel says the record is not
published rather than that something broke.

The game and FAQ copy promised that there would never be a token. That is no
longer what the project intends, so the copy now says what is true: a unit of
proof stays non-transferable, an accepted spec is recorded as an earning, and
whether a spec earning may become a movable token is an open decision, not a
promise. Everything stays watch-only.

Typecheck ratchet: 179 errors across 26 files, equal to baseline; none in the
changed files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(website): drop the "never a token" wording from the game copy

Move 03, the cost FAQ and the motto note argued that a unit of proof would
never be a token and that a spec earning becoming one was an open decision.
That is not the project's position: TRI is designed as a token minted from
accepted work. The copy now states only what is true today: accepted turns
and .t27 specs are recorded, TRI is the token those earnings are designed to
mint, it is not deployed, and nothing is withdrawable yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Dmitrii Vasilev <samfold01@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants