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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/website-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ jobs:
# and the nightly t27 world scan -- the only place these ran -- then
# failed for three nights. Pure, no browser.
- name: Queen map close-ups stay readable
run: npm run check:queen-displays && npm run check:queen-continuous
run: npm run check:queen-displays && npm run check:queen-continuous && npm run check:queen-issue-proof && npm run check:queen-worlds
# The PASSPORT argues a result is worthless without the record of how it
# was taken, so its own figures must not drift from its own prose: every
# plotted quantity carries the literal the text prints for it, in both
Expand Down
8 changes: 5 additions & 3 deletions SOUL.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
# QUEEN hive status-fill agent
# Corona issue proof reader

- Type: Custom / UI
- Bound GitHub issue: gHashTag/trinity#890
- Mission: implement truthful, overview-readable issue-status hex fills.
- Bound GitHub issue: gHashTag/trinity#1360
- Evidence dependency: gHashTag/tt-trinity-corona#12.
- Mission: show honey only for a closed allowlisted issue whose exact native
seal, conformance vectors, verifier and CI match the current canonical source.
- Allowed: scoped local source/tests/docs/skill edits, read-only repository and
public browser inspection, local test/build/preview, issue-bound journaling.
- Stop: unrelated changes, unavailable provenance, external publication/secrets
Expand Down
12 changes: 12 additions & 0 deletions apps/website/.trinity/seals/queen_queen_issue_proof.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
"gen_hash_c": "sha256:2dd94ece3724a73e039e6f6051fce5c6b66a62d7fd65eaeec4a994f9c35e5a68",
"gen_hash_rust": "sha256:17be9c555632c81e3f30e42d37abc7cedbf5e1c79f50f6f07c44fc0729c74e1e",
"gen_hash_verilog": "sha256:37ed904ae570e8d46b1a99a4e79d4b3608b3dbe51274bb2f6d085ba80dea9fd0",
"gen_hash_zig": "sha256:0f93bc44dc9f89ef77a0854963488e1b1d16e454d717285802f64c6d89a54240",
"module": "queen_issue_proof",
"ring": 12,
"sealed_at": "2026-10-04T12:58:19Z",
"sealed_by": "t27c-bootstrap@0.4.0",
"spec_hash": "sha256:76c068c85d3af34aaa6f4a571742f6ad00de727a37f0ffe419c83bbdb1b84c71",
"spec_path": "specs/queen/issue_proof.t27"
}
70 changes: 70 additions & 0 deletions apps/website/conformance/queen_issue_proof.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
{
"spec_path": "specs/queen/issue_proof.t27",
"spec_hash": "76c068c85d3af34aaa6f4a571742f6ad00de727a37f0ffe419c83bbdb1b84c71",
"vectors": [
{
"mask": 0,
"expected": 0
},
{
"mask": 1,
"expected": 0
},
{
"mask": 2,
"expected": 0
},
{
"mask": 3,
"expected": 0
},
{
"mask": 4,
"expected": 0
},
{
"mask": 5,
"expected": 0
},
{
"mask": 6,
"expected": 0
},
{
"mask": 7,
"expected": 0
},
{
"mask": 8,
"expected": 0
},
{
"mask": 9,
"expected": 0
},
{
"mask": 10,
"expected": 0
},
{
"mask": 11,
"expected": 0
},
{
"mask": 12,
"expected": 0
},
{
"mask": 13,
"expected": 0
},
{
"mask": 14,
"expected": 0
},
{
"mask": 15,
"expected": 1
}
]
}
1 change: 1 addition & 0 deletions apps/website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@
"check:queen-coverage": "node --experimental-strip-types qa/queen-coverage-contract.mjs",
"check:queen-displays": "node --experimental-strip-types qa/queen-hive-display-contract.mjs",
"check:queen-worlds": "node --experimental-strip-types qa/queen-repository-worlds.mjs",
"check:queen-issue-proof": "node scripts/issue-proof-from-spec.mjs --check && node --experimental-strip-types qa/queen-issue-proof.mjs",
"check:queen-dead-api": "node qa/queen-viewport-contract.mjs --dead-api",
"check:queen-touch": "node qa/queen-touch-contract.mjs",
"check:queen-round": "node qa/queen-round-contract.mjs",
Expand Down
3 changes: 3 additions & 0 deletions apps/website/qa/queen-continuous-map.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ assert.equal(hiveEpicProgress(epic,[{...issue,number:7,state:'open'},{...issue,r
const before=new Map(world.displays.flatMap((row,i)=>row?[[row.key,world.map.positions[i]]]:[]));
updated.displays.forEach((row,i)=>{if(row)assert.deepEqual(updated.map.positions[i],before.get(row.key),'Observation must not teleport other cells');});
assert.equal(updated.displays.find(i=>i?.key===issue.key).state,'closed');
const complete=data.catalogUniverse(atlas,[{...issue,state:'closed'}],[issue.repo]);
assert.equal(complete.map.cells.find(c=>c?.kind==='repo'&&c.repo===issue.repo).open,0,'complete observed backlog replaces dated open count');
assert.equal(updated.map.cells.find(c=>c?.kind==='repo'&&c.repo===issue.repo).open,core.cells.find(c=>c?.kind==='repo'&&c.repo===issue.repo).open,'partial observation keeps snapshot backlog count');
const extra={...issue,key:`${issue.repo}#999999`,number:999999,state:'closed'};
const extended=data.catalogUniverse(atlas,[extra]);
for(const [i,row] of extended.displays.entries())if(row&&before.has(row.key))assert.deepEqual(extended.map.positions[i],before.get(row.key),'An observed old issue appends without moving snapshot cells');
Expand Down
72 changes: 72 additions & 0 deletions apps/website/qa/queen-issue-proof.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import assert from 'node:assert/strict';
import {createHash} from 'node:crypto';
import {readFileSync} from 'node:fs';
import {createIssueProofReader} from '../src/lib/queenIssueProof.ts';
import {issueProofPolicy as production} from '../src/lib/queenIssueProof.generated.ts';
import {hiveTaskPaint} from '../src/components/queenHiveDisplay.ts';

// Public HTTP boundary fixtures with real SHA-256, no network and no fake digest.
const hash=text=>createHash('sha256').update(text).digest('hex');
const sha='a'.repeat(40),signal=new AbortController().signal,repo=production.REPO;
const row=(overrides={})=>({key:`${repo}#1`,repo,number:1,title:'Phase A',kind:'issue',state:'closed',closedAt:'2026-10-03T13:05:56Z',children:[],coverage:'unknown',...overrides});
function fixture({source,seal:editSeal,vectors:editVectors}={}) {
const spec=source??production.ISSUES.map(n=>`; https://github.com/${repo}/issues/${n}`).join('\n');
const seal={spec_path:production.SPEC,spec_hash:`sha256:${hash(spec)}`,...Object.fromEntries(['c','rust','verilog','zig'].map(b=>[`gen_hash_${b}`,`sha256:${'b'.repeat(64)}`]))};
const vectors={spec_path:production.SPEC,spec_hash:seal.spec_hash,vectors:[{expected:false},{expected:true}]};
editSeal?.(seal);editVectors?.(vectors);
const files=new Map([[production.SPEC,spec],[production.SEAL,JSON.stringify(seal)],[production.VECTORS,JSON.stringify(vectors)],[production.VERIFIER,'verifier fixture'],[production.WORKFLOW,'workflow fixture'],[production.MAKEFILE,'make fixture']]);
const policy={...production,VECTOR_COUNT:2,...Object.fromEntries(['SPEC','SEAL','VECTORS','VERIFIER','WORKFLOW','MAKEFILE'].map(k=>[`${k}_HASH`,hash(files.get(production[k]))]))};
const runs=[production.WORKFLOW,production.GDS_WORKFLOW].map((path,i)=>({id:i+1,head_sha:sha,event:'push',path,status:'completed',conclusion:'success',html_url:`https://github.com/${repo}/actions/runs/${i+1}`,repository:{full_name:repo}}));
const reads=[];
const fetcher=async(url,opts)=>{
reads.push({url,opts});assert.equal(opts.credentials,'omit');assert.equal(opts.cache,'no-store');assert.equal(opts.headers.Authorization,undefined);
if(url.endsWith('/commits/HEAD'))return Response.json({sha});
if(url.includes('/actions/runs?'))return Response.json({workflow_runs:runs});
const prefix=`https://raw.githubusercontent.com/${repo}/${sha}/`;
assert.ok(url.startsWith(prefix),'all evidence is pinned to the resolved SHA');
const bytes=files.get(url.slice(prefix.length));
return new Response(bytes??'missing',{status:bytes===undefined?404:200});
};
return {files,runs,reads,fetcher,reader:createIssueProofReader(policy)};
}
let cases=0;
async function check(label,mutate,expected='unknown',options){
const f=fixture(options);mutate?.(f);
const result=(await f.reader.proveWorldIssues([row()],repo,signal,f.fetcher))[0];
assert.equal(result.coverage,expected,label);
assert.equal(Boolean(result.proof),expected==='t27',label);
if(expected==='t27'){assert.equal(result.proof.commit,sha);assert.equal(hiveTaskPaint(result).tone,'honey');}
else assert.notEqual(hiveTaskPaint(result).tone,'honey');
cases++;
}
await check('complete canonical evidence',null,'t27');
for(const path of [production.SPEC,production.SEAL,production.VECTORS,production.VERIFIER,production.WORKFLOW,production.MAKEFILE]){
await check(`tampered ${path}`,f=>f.files.set(path,f.files.get(path)+' changed'));
await check(`missing ${path}`,f=>f.files.delete(path));
}
for(const edit of [r=>r.head_sha='c'.repeat(40),r=>r.event='pull_request',r=>r.repository.full_name='someone/fork',r=>r.status='in_progress',r=>r.conclusion='failure',r=>r.html_url='https://evil.test/fake',r=>r.path='some-other.yml'])await check('wrong CI identity/result',f=>edit(f.runs[0]));
await check('GDS failure',f=>f.runs[1].conclusion='failure');
await check('newest failed run supersedes older successful run',f=>f.runs.push({...f.runs[0],id:3,html_url:`https://github.com/${repo}/actions/runs/3`,conclusion:'failure'}));
await check('missing issue binding',null,'unknown',{source:`; https://github.com/${repo}/issues/123`});
await check('seal has stale source',null,'unknown',{seal:s=>s.spec_hash=`sha256:${'c'.repeat(64)}`});
await check('seal blocked tests',null,'unknown',{seal:s=>s.tests={blocked:'missing compiler'}});
await check('seal missing backend',null,'unknown',{seal:s=>delete s.gen_hash_rust});
await check('vectors missing cases',null,'unknown',{vectors:v=>v.vectors.pop()});
await check('vectors bound to another source',null,'unknown',{vectors:v=>v.spec_hash=`sha256:${'c'.repeat(64)}`});
await check('anonymous rate limit',f=>f.fetcher=async()=>new Response('rate limited',{status:403}));
await check('invalid head',f=>f.fetcher=async()=>Response.json({sha:'main'}));
const f=fixture();
const accepted=(await f.reader.proveWorldIssues([row()],repo,signal,f.fetcher))[0];
const before=f.reads.length;
await f.reader.proveWorldIssues([row()],repo,signal,f.fetcher);assert.equal(f.reads.length,before,'minute cache avoids repeated anonymous reads');
f.reader.invalidateIssueProof(repo);
const unavailable=async()=>new Response('limited',{status:429});
assert.equal((await f.reader.proveWorldIssues([accepted],repo,signal,unavailable))[0].coverage,'unknown','failed refresh removes a cached positive');
assert.equal((await f.reader.proveWorldIssues([{...accepted,state:'open'}],repo,signal,f.fetcher))[0].coverage,'unknown','reopening removes proof');
assert.equal((await f.reader.proveWorldIssues([row({number:99})],repo,signal,f.fetcher))[0].coverage,'unknown','other issues never inherit proof');
const unbound=fixture();await unbound.reader.proveWorldIssues([row({repo:'another/repo'})],'another/repo',signal,unbound.fetcher);assert.equal(unbound.reads.length,0);
cases+=5;
const matrix=JSON.parse(readFileSync(new URL('../conformance/queen_issue_proof.json',import.meta.url)));
assert.equal(matrix.vectors.length,16);
for(const v of matrix.vectors)assert.equal(production.ACCEPT[v.mask],v.expected,`conformance mask ${v.mask}`);
console.log(`Issue proof reader: ${cases} cases and ${matrix.vectors.length} conformance vectors PASS`);
30 changes: 30 additions & 0 deletions apps/website/scripts/issue-proof-from-spec.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import {readFileSync,writeFileSync,mkdirSync} from 'node:fs';
import {join} from 'node:path';
import {SITE,loadCompiler,constsOf,checkSchema,compilerErrors,verdictOf,sha256} from './agents-from-specs.mjs';
import {runSpecTests} from './viewport-from-spec.mjs';
const source='specs/queen/issue_proof.t27';
const text=readFileSync(join(SITE,source),'utf8');
const analyze=await loadCompiler(readFileSync(join(SITE,'public/t27/t27_compiler.wasm')));
const analysis=analyze(text),v=verdictOf(analysis),constants=constsOf(analysis);
if(compilerErrors(analysis).length||!v.typecheckOk||!v.hirOk||v.discarded||analysis.ast?.name!=='queen_issue_proof')throw new Error('Issue proof spec rejected');
const schema={REPO:'str',ISSUES:'arr-u32',SPEC:'str',SPEC_HASH:'str',SEAL:'str',SEAL_HASH:'str',VECTORS:'str',VECTORS_HASH:'str',VERIFIER:'str',VERIFIER_HASH:'str',MAKEFILE:'str',MAKEFILE_HASH:'str',WORKFLOW:'str',WORKFLOW_HASH:'str',GDS_WORKFLOW:'str',VECTOR_COUNT:'u32',CACHE_MS:'u32',ACCEPT:'arr-u8'};
const problems=checkSchema(constants,schema,{},source);
if(problems.length)throw new Error(problems.join('; '));
const fields=Object.fromEntries(Object.entries(constants).map(([k,v])=>[k,v.value]));
const result=runSpecTests(analysis,fields);
if(result.tests!==2||result.asserts!==19||result.failures.length)throw new Error(JSON.stringify(result));
if(fields.ACCEPT.length!==16||fields.ACCEPT.some((v,i)=>v!==Number(i===15)))throw new Error('Incomplete acceptance truth table');
if(process.argv.includes('--check')){
const seal=JSON.parse(readFileSync(join(SITE,'.trinity/seals/queen_queen_issue_proof.json'),'utf8'));
if(seal.spec_path!==source||seal.spec_hash!==`sha256:${sha256(Buffer.from(text))}`||seal.tests?.blocked)throw new Error('Missing or stale native issue proof seal');
}
for(const [k,v] of Object.entries(fields))if(k.endsWith('_HASH')&&!/^[a-f0-9]{64}$/.test(v))throw new Error(`Invalid ${k}`);
const outputs={
'src/lib/queenIssueProof.generated.ts':`// GENERATED from ${source}; sha256 ${sha256(Buffer.from(text))}\nexport const issueProofPolicy = ${JSON.stringify(fields,null,2)} as const;\n`,
'conformance/queen_issue_proof.json':JSON.stringify({spec_path:source,spec_hash:sha256(Buffer.from(text)),vectors:fields.ACCEPT.map((expected,mask)=>({mask,expected}))},null,2)+'\n',
};
for(const [path,bytes] of Object.entries(outputs)){
if(process.argv.includes('--check')){if(readFileSync(join(SITE,path),'utf8')!==bytes)throw new Error(`Stale ${path}`);}
else {mkdirSync(join(SITE,path,'..'),{recursive:true});writeFileSync(join(SITE,path),bytes);}
}
console.log(`Issue proof spec: ${result.tests} tests, ${result.asserts} assertions; 16 conformance rows`);
52 changes: 52 additions & 0 deletions apps/website/specs/queen/issue_proof.t27
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// SPDX-License-Identifier: Apache-2.0
; Issue: https://github.com/gHashTag/trinity/issues/1360
; A closed issue becomes honey only after exact, canonical executable evidence.
; These pins describe Corona PR13; unmerged/failing/pending evidence stays unknown.
module queen_issue_proof;

pub const REPO : str = "ghashtag/tt-trinity-corona";
pub const ISSUES : [2]u32 = [1, 12];
pub const SPEC : str = "specs/corona/phase_a.t27";
pub const SPEC_HASH : str = "7d4f7bbd507fc35cc05d88cab97582f42d619f4c5ced93584894ffd2aeeb567a";
pub const SEAL : str = ".trinity/seals/corona_corona_phase_a.json";
pub const SEAL_HASH : str = "78babf1f37f9e36c45caa169e4868067ea17da61f86fa3a9bef249b2b940e673";
pub const VECTORS : str = "conformance/corona_phase_a.json";
pub const VECTORS_HASH : str = "48e7ae489c76a00a3745ce96f82a3f1a6ae423be4dbbaa8df62e822b2737117c";
pub const VERIFIER : str = "tools/phase_a_conformance.py";
pub const VERIFIER_HASH : str = "2adcd80666cf08882707ca2bfc1a5b9ed7ed06fbf78838c9549fe986b11dd369";
pub const MAKEFILE : str = "Makefile";
pub const MAKEFILE_HASH : str = "8e7ca522a8f3dfbff6a8e9ec47f329f36b8682a173d08ec95da863dc4a1ba4cd";
pub const WORKFLOW : str = ".github/workflows/ci.yml";
pub const WORKFLOW_HASH : str = "e1193c3add7fd5cf7af94b1447dbca89547c83da4b2e33e4c399ab21baace75c";
pub const GDS_WORKFLOW : str = ".github/workflows/gds.yml";
pub const VECTOR_COUNT : u32 = 1920;
pub const CACHE_MS : u32 = 60000;
; bit 0 = closed; bit 1 = exact source/issue; bit 2 = native seal/vectors;
; bit 3 = canonical CI and GDS succeeded at the current default-branch SHA.
; Observed backlog counts may replace snapshot counts only for a complete page.
; The inspector links to the spec and both runs at the observed source revision.
pub const ACCEPT : [16]u8 = [0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1];

test missing_evidence_is_never_honey {
assert ACCEPT[0] == 0;
assert ACCEPT[1] == 0;
assert ACCEPT[2] == 0;
assert ACCEPT[3] == 0;
assert ACCEPT[4] == 0;
assert ACCEPT[5] == 0;
assert ACCEPT[6] == 0;
assert ACCEPT[7] == 0;
assert ACCEPT[8] == 0;
assert ACCEPT[9] == 0;
assert ACCEPT[10] == 0;
assert ACCEPT[11] == 0;
assert ACCEPT[12] == 0;
assert ACCEPT[13] == 0;
assert ACCEPT[14] == 0;
}
test complete_proof_is_honey {
assert ACCEPT[15] == 1;
assert VECTOR_COUNT == 1920;
assert ISSUES[0] == 1;
assert ISSUES[1] == 12;
}
18 changes: 16 additions & 2 deletions apps/website/src/components/QueenCatalogHive.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,28 @@ import {SELECTION_KEY,validSelection} from '../lib/queenEmbed';
import {atlasAgentPacket,type UniverseAtlas,type AtlasIssue} from '../lib/queenUniverseAtlas';
import type {CombHandle} from './queenHud';
import type {HiveDisplayProjection,HiveDisplay} from './queenHiveDisplay';
import type {WorldIssue} from './queenRepositoryWorld';
import {loadWorldIssues,type WorldIssue} from './queenRepositoryWorld';
import {supportsIssueProof} from '../lib/queenIssueProof';
import './QueenCatalogHive.css';

export function QueenCatalogHive({atlas,lang,handleRef,foundationVisible=true,fitInset=0,onInspect}:{atlas:UniverseAtlas;lang:'ru'|'en';handleRef:Ref<CombHandle>;foundationVisible?:boolean;fitInset?:number;onInspect?:()=>void}) {
const ru=lang==='ru';
const [params,setParams]=useSearchParams(),focus=catalogFocus(`#/queen?${params}`,atlas),repo=focus?.repo??null;
const [observed,setObserved]=useState<Record<string,WorldIssue>>({});
const world=useMemo(()=>catalogUniverse(atlas,Object.values(observed)),[atlas,observed]),map=world.map;
const [completeWorlds,setCompleteWorlds]=useState<string[]>([]);
useEffect(()=>{
if(!repo||!supportsIssueProof(repo))return;
const request=new AbortController();
// Refresh this small supported world when selected; a static atlas predating
// closure must not override the exact GitHub/native-proof observation.
loadWorldIssues(repo,1,request.signal).then(({rows,hasMore})=>{
if(request.signal.aborted)return;
setObserved(prev=>({...Object.fromEntries(Object.entries(prev).filter(([,row])=>row.repo!==repo)),...Object.fromEntries(rows.map(row=>[row.key,row]))}));
setCompleteWorlds(prev=>[...prev.filter(r=>r!==repo),...(!hasMore?[repo]:[])]);
}).catch(()=>{/* The dated atlas remains explicitly a snapshot on failure. */});
return()=>request.abort();
},[repo]);
const world=useMemo(()=>catalogUniverse(atlas,Object.values(observed),completeWorlds),[atlas,observed,completeWorlds]),map=world.map;
const issueRows=world.displays.filter(row=>row?.repo===repo);
const cards=useMemo(()=>world.displays.map(row=>row?{number:row.number,title:row.title,column:row.state}:null),[world]);
const issueKey=focus?.number?`${repo}#${focus.number}`:null;
Expand Down
Loading
Loading