diff --git a/.github/workflows/website-checks.yml b/.github/workflows/website-checks.yml
index 1bcf207b19..ad8741c14c 100644
--- a/.github/workflows/website-checks.yml
+++ b/.github/workflows/website-checks.yml
@@ -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
diff --git a/SOUL.md b/SOUL.md
index 2357902633..779478f683 100644
--- a/SOUL.md
+++ b/SOUL.md
@@ -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
diff --git a/apps/website/.trinity/seals/queen_queen_issue_proof.json b/apps/website/.trinity/seals/queen_queen_issue_proof.json
new file mode 100644
index 0000000000..b25621f7cc
--- /dev/null
+++ b/apps/website/.trinity/seals/queen_queen_issue_proof.json
@@ -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"
+}
\ No newline at end of file
diff --git a/apps/website/conformance/queen_issue_proof.json b/apps/website/conformance/queen_issue_proof.json
new file mode 100644
index 0000000000..71373f6e17
--- /dev/null
+++ b/apps/website/conformance/queen_issue_proof.json
@@ -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
+ }
+ ]
+}
diff --git a/apps/website/package.json b/apps/website/package.json
index 4f55d17b93..f3bab3f07c 100644
--- a/apps/website/package.json
+++ b/apps/website/package.json
@@ -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",
diff --git a/apps/website/qa/queen-continuous-map.mjs b/apps/website/qa/queen-continuous-map.mjs
index 46338c8fb0..196ba92ff9 100644
--- a/apps/website/qa/queen-continuous-map.mjs
+++ b/apps/website/qa/queen-continuous-map.mjs
@@ -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');
diff --git a/apps/website/qa/queen-issue-proof.mjs b/apps/website/qa/queen-issue-proof.mjs
new file mode 100644
index 0000000000..d1d838ae0e
--- /dev/null
+++ b/apps/website/qa/queen-issue-proof.mjs
@@ -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`);
diff --git a/apps/website/scripts/issue-proof-from-spec.mjs b/apps/website/scripts/issue-proof-from-spec.mjs
new file mode 100644
index 0000000000..0c8c8684d3
--- /dev/null
+++ b/apps/website/scripts/issue-proof-from-spec.mjs
@@ -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`);
diff --git a/apps/website/specs/queen/issue_proof.t27 b/apps/website/specs/queen/issue_proof.t27
new file mode 100644
index 0000000000..581cd382db
--- /dev/null
+++ b/apps/website/specs/queen/issue_proof.t27
@@ -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;
+}
diff --git a/apps/website/src/components/QueenCatalogHive.tsx b/apps/website/src/components/QueenCatalogHive.tsx
index 1178c68c01..cb43003f1a 100644
--- a/apps/website/src/components/QueenCatalogHive.tsx
+++ b/apps/website/src/components/QueenCatalogHive.tsx
@@ -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
{issue ? (issue.body || t.noBody) : issueError ? t.failed : t.loading}
diff --git a/apps/website/src/components/queenCatalogData.ts b/apps/website/src/components/queenCatalogData.ts
index a7a19f98d0..c917a301c7 100644
--- a/apps/website/src/components/queenCatalogData.ts
+++ b/apps/website/src/components/queenCatalogData.ts
@@ -52,7 +52,7 @@ export function catalogHive(input:UniverseAtlas):CatalogHive {
}
/** One coordinate space. Focus never changes membership or replaces the core. */
-export function catalogUniverse(input:UniverseAtlas,observed:WorldIssue[]=[]) {
+export function catalogUniverse(input:UniverseAtlas,observed:WorldIssue[]=[],completeWorlds:string[]=[]) {
const atlas=keyWorldAtlas(input),core=catalogHive(atlas);
const cells=[...core.cells],positions:CatalogPosition[]=cells.map((_,i)=>({...hexToWorld(spiralAxial(i)),scale:1}));
const displays:(WorldIssue|null)[]=cells.map(()=>null),regions:CatalogRegion[]=[];
@@ -61,6 +61,8 @@ export function catalogUniverse(input:UniverseAtlas,observed:WorldIssue[]=[]) {
core.cells.forEach((portal,portalIndex)=>{
if(portal?.kind!=='repo')return;
const rows=catalogIssueRows(atlas,portal.repo,observed);
+ // Partial pages and a single inspected issue cannot establish the backlog.
+ if(completeWorlds.includes(portal.repo))cells[portalIndex]={...portal,open:observed.filter(r=>r.repo===portal.repo&&r.state==='open').length};
// Reserve the snapshot's slots: a newly observed older issue appends instead of shifting existing cells.
const snapshot=catalogIssueRows(atlas,portal.repo),byKey=new Map(rows.map(r=>[r.key,r]));
const ordered=[...snapshot.map(r=>byKey.get(r.key)!),...observed.filter(r=>r.repo===portal.repo&&byKey.has(r.key)&&!snapshot.some(s=>s.key===r.key))];
@@ -89,7 +91,7 @@ export function catalogUniverse(input:UniverseAtlas,observed:WorldIssue[]=[]) {
}
regions.push(region);
});
- const map:CatalogHive={...core,cells,positions,regions,specLinks,coreCount:core.cells.length,signature:JSON.stringify([core.signature,regions,specLinks,displays.map(r=>r?.key??null)])};
+ const map:CatalogHive={...core,cells,positions,regions,specLinks,coreCount:core.cells.length,signature:JSON.stringify([core.signature,completeWorlds,cells.filter(c=>c?.kind==='repo'),regions,specLinks,displays.map(r=>r?.key??null)])};
return {map,displays};
}
diff --git a/apps/website/src/components/queenHiveDisplay.ts b/apps/website/src/components/queenHiveDisplay.ts
index 84f07f7371..8ec4aae3dd 100644
--- a/apps/website/src/components/queenHiveDisplay.ts
+++ b/apps/website/src/components/queenHiveDisplay.ts
@@ -9,8 +9,8 @@ export interface HiveDisplay {
state: string;
closedAt: string | null;
children: EpicRecord['children'];
- /** The public issue ledger supplies no issue-to-module coverage proof. */
- coverage: 'unknown';
+ /** GitHub lifecycle alone is unknown; only the separate native-proof reader may promote it. */
+ coverage: 'unknown' | 't27';
}
type Snapshot = { repo: string; closedIssues: FoundationIssue[]; epics: EpicRecord[] };
diff --git a/apps/website/src/components/queenRepositoryWorld.ts b/apps/website/src/components/queenRepositoryWorld.ts
index 06ab1b6989..60e69f4ca5 100644
--- a/apps/website/src/components/queenRepositoryWorld.ts
+++ b/apps/website/src/components/queenRepositoryWorld.ts
@@ -1,4 +1,5 @@
import type { HiveDisplay } from './queenHiveDisplay';
+import {proveWorldIssues,invalidateIssueProof,type IssueProof} from '../lib/queenIssueProof.ts';
export const PINNED_WORLDS = ['ghashtag/trios', 'ghashtag/t27'];
export const WORLD_STORAGE = 'queen.public-worlds.v1';
@@ -19,7 +20,7 @@ export function savedWorlds(raw: string | null): string[] {
catch { return [...PINNED_WORLDS]; }
}
function object(value: unknown): Record