From 959129fb570ea8b5327a6760fe3bc6edc5b3dce2 Mon Sep 17 00:00:00 2001 From: gHashTag Date: Tue, 11 Aug 2026 01:56:21 +0700 Subject: [PATCH 1/3] ci(website): warn when the live site is not this build MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A51 concluded that no test in the source repo could see the output repo go stale. That was wrong by one assumption: a test that FETCHES THE LIVE SITE can. This is that test. It compares the entry bundle t27.ai serves against the one dist/index.html loads. Vite hashes by content, so equal names mean equal bundles. Three paths, each verified rather than assumed: match exit 0, 'the live site is this build' mismatch prints both hashes and points at A51 unreachable skips -- offline is not a defect in the change It never fails the job. A merge is not a deploy, and gating one on the other inverts the order of operations. It is here so the gap shows in the log instead of being found a week later in the site's . What it cannot tell you: which direction, or whether the difference matters. Whitespace moves the hash. Проверка, сравнивающая бандл на t27.ai с собранным. Не валит сборку -- слияние это не деплой; она делает зазор видимым в логе. Co-Authored-By: Claude Opus 5 --- .github/workflows/website-checks.yml | 13 ++++++ apps/website/package.json | 3 +- apps/website/scripts/deployed-check.mjs | 62 +++++++++++++++++++++++++ 3 files changed, 77 insertions(+), 1 deletion(-) create mode 100644 apps/website/scripts/deployed-check.mjs diff --git a/.github/workflows/website-checks.yml b/.github/workflows/website-checks.yml index 56c9758607..6270450e15 100644 --- a/.github/workflows/website-checks.yml +++ b/.github/workflows/website-checks.yml @@ -67,3 +67,16 @@ jobs: env: CHROME_PATH: ${{ steps.chrome.outputs.chrome-path }} run: npm run check:render -- --no-build + + # A51: four PRs merged, every check green, and the live site changed + # nothing -- this app deploys from a second repository that holds build + # output only, copied there by hand. Every check above runs against the + # source, so all of them pass on a tree whose output nobody published. + # + # This one fetches t27.ai and compares the entry bundle. It never fails + # the job: a merge is not a deploy, and blocking one on the other + # inverts the order. It is here so the gap is visible in the log rather + # than discovered a week later in the site's . + - name: Is the live site this build? + continue-on-error: true + run: npm run check:deployed diff --git a/apps/website/package.json b/apps/website/package.json index 6bf96584e7..d521c81cd9 100644 --- a/apps/website/package.json +++ b/apps/website/package.json @@ -12,7 +12,8 @@ "typecheck": "tsc -b --pretty false", "typecheck:ratchet": "node scripts/typecheck-ratchet.mjs", "check:api": "node scripts/api-contract-check.mjs", - "check:render": "node scripts/render-check.mjs" + "check:render": "node scripts/render-check.mjs", + "check:deployed": "node scripts/deployed-check.mjs" }, "dependencies": { "@monaco-editor/react": "^4.7.0", diff --git a/apps/website/scripts/deployed-check.mjs b/apps/website/scripts/deployed-check.mjs new file mode 100644 index 0000000000..fd577df3eb --- /dev/null +++ b/apps/website/scripts/deployed-check.mjs @@ -0,0 +1,62 @@ +// Is what t27.ai serves the same thing this tree builds? +// +// anomaly-register A51: four PRs merged, every check green, and the live site +// changed nothing. The SPA deploys from a second repository that holds build +// output only, and nothing copies a build into it. The site kept serving +// "First chip with native SU(3) Unitary Core" for a week after the tree that +// produces it stopped saying that. +// +// A51 concluded no test in the source repo could see this. That was wrong by +// one assumption: a test that FETCHES THE LIVE SITE can. This is that test. +// +// npm run check:deployed +// +// It compares what the deployed index.html loads against what dist/index.html +// loads. Vite hashes bundle names by content, so equal names mean equal +// bundles and a different name means the deploy is behind -- or ahead, which +// is worth knowing too. +// +// What it cannot tell you: WHICH direction, or whether the difference matters. +// A whitespace change moves the hash. So this warns; it does not fail a build. +// A gate that blocks a merge because someone has not deployed yet inverts the +// order of operations. +import { readFileSync, existsSync } from 'node:fs'; + +const SITE = process.env.DEPLOY_URL ?? 'https://t27.ai/'; +const entryOf = (html) => (html.match(/assets\/(index-[\w-]+\.js)/) ?? [])[1]; + +if (!existsSync('dist/index.html')) { + console.log(' no dist/ — run `npx vite build` first (nothing to compare)'); + process.exit(0); +} +const local = entryOf(readFileSync('dist/index.html', 'utf8')); +if (!local) { + console.error(' no entry bundle in dist/index.html. That is a build problem, not a deploy one.'); + process.exit(1); +} + +let live; +try { + const res = await fetch(SITE, { headers: { 'User-Agent': 'deployed-check' } }); + if (!res.ok) throw new Error(`HTTP ${res.status}`); + live = entryOf(await res.text()); +} catch (e) { + // Offline, or the site is down. Neither is a defect in this change, and a + // check that fails when the network does gets muted. + console.log(` could not reach ${SITE} (${e.message}) — skipping`); + process.exit(0); +} + +if (!live) { + console.error(` ${SITE} serves no index-*.js bundle. Either it is not this app, or it is broken.`); + process.exit(1); +} +if (live === local) { + console.log(` deployed: ${live} — the live site is this build`); + process.exit(0); +} +console.log(`\n the live site is NOT this build:\n`); +console.log(` ${SITE.padEnd(28)} ${live}`); +console.log(` dist/index.html ${local}\n`); +console.log(' Merging does not deploy this site. The build output lives in a second'); +console.log(' repository and is copied there by hand — see anomaly-register A51.'); From 11ef049023cb335c54937c84ad09d3777898eb65 Mon Sep 17 00:00:00 2001 From: gHashTag Date: Tue, 11 Aug 2026 02:11:50 +0700 Subject: [PATCH 2/3] fix(website): swap a duplicated unsourced card for the audit's one verified row MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A45 asked what should replace 578.8x. The obvious answer -- the audit's own "500x (FPGA projected)" -- does not survive checking: 500x tied to energy appears exactly once in three repositories, in the audit line proposing it. It is the auditor's rounding, not a measurement. Swapping 578.8x for 500x would exchange one unsourced number for another while looking like a fix, so neither figure is changed here. What did change is a duplicate. The headline cards read: 578.8x Energy Eff 100x SU(3) Speed 99.8% Cost Reduction 80.2% Code Density and 1 - 1/578.8 = 99.83%, so card 0 and card 2 were the same unsourced number twice (A46). The audit's table has exactly one row marked VERIFIED with a citation: | Memory Compression | 20x | 20x | BitNet b1.58 (arXiv:2402.17764) | VERIFIED | So the duplicate becomes "20x / Memory, verified", in all five locales. One unsourced claim removed, one sourced claim added, and the panel now carries a number a reader can check against a published paper. 578.8x still has no source. A45 stays open, and the answer to it is a measurement someone has to take, not a wording anyone can choose. Дубликат неподтверждённого числа заменён на единственную строку аудита с пометкой VERIFIED и ссылкой. Само 578.8x не менялось: предложенное аудитом 500x обеспечено не лучше. Co-Authored-By: Claude Opus 5 --- apps/website/messages/de.json | 4 ++-- apps/website/messages/en.json | 4 ++-- apps/website/messages/es.json | 4 ++-- apps/website/messages/ru.json | 4 ++-- apps/website/messages/zh.json | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/apps/website/messages/de.json b/apps/website/messages/de.json index 5b0536882c..5984bb1c6a 100644 --- a/apps/website/messages/de.json +++ b/apps/website/messages/de.json @@ -327,8 +327,8 @@ "color": "green" }, { - "value": "99,8%", - "label": "Kostenreduktion", + "value": "20×", + "label": "Speicher, verifiziert", "color": "purple" }, { diff --git a/apps/website/messages/en.json b/apps/website/messages/en.json index cfd0a224a1..181b1aa1ae 100644 --- a/apps/website/messages/en.json +++ b/apps/website/messages/en.json @@ -334,8 +334,8 @@ "color": "green" }, { - "value": "99.8%", - "label": "Cost Reduction", + "value": "20×", + "label": "Memory, verified", "color": "purple" }, { diff --git a/apps/website/messages/es.json b/apps/website/messages/es.json index b4ad7c085c..5eac2b3f19 100644 --- a/apps/website/messages/es.json +++ b/apps/website/messages/es.json @@ -327,8 +327,8 @@ "color": "green" }, { - "value": "99.8%", - "label": "Reduccion Costo", + "value": "20×", + "label": "Memoria, verificado", "color": "purple" }, { diff --git a/apps/website/messages/ru.json b/apps/website/messages/ru.json index 1da2b851e1..076fdfdef0 100644 --- a/apps/website/messages/ru.json +++ b/apps/website/messages/ru.json @@ -294,8 +294,8 @@ "color": "green" }, { - "value": "99.8%", - "label": "Снижение затрат", + "value": "20×", + "label": "Память, проверено", "color": "purple" }, { diff --git a/apps/website/messages/zh.json b/apps/website/messages/zh.json index f7b1840645..c38f04a6a4 100644 --- a/apps/website/messages/zh.json +++ b/apps/website/messages/zh.json @@ -327,8 +327,8 @@ "color": "green" }, { - "value": "99.8%", - "label": "成本降低", + "value": "20×", + "label": "内存,已验证", "color": "purple" }, { From 50bde637d413aab71719b149486f2cebddc15047 Mon Sep 17 00:00:00 2001 From: gHashTag Date: Tue, 11 Aug 2026 02:12:44 +0700 Subject: [PATCH 3/3] ci: a deploy workflow, manual on purpose MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A51: four PRs merged, every check green, and the live site changed nothing for a week, because this app deploys from a second repository that a human copies dist/ into. check:deployed now reports that gap; this closes it. Three decisions worth stating. Manual, not on-push-to-main. Publishing on every merge would mean any merge broadcasts, and this project's rule is that content is approved before it goes out. workflow_dispatch with a required 'reason' input, which lands in the commit message. It runs the checks against the tree being published, not just the PR that produced it. A deploy that skips them is how a broken build reaches the domain. It deletes bundles the new index.html cannot reach, computed by following every ./-relative import transitively. Skipping that is how 74 orphans accumulated once, keeping withdrawn claims greppable for weeks after the components carrying them were fixed. It needs a secret named GHIO_TOKEN -- a fine-grained PAT with Contents: write on gHashTag/ghashtag.github.io and nothing else. A workflow token cannot reach a second repository. Until that secret exists this does not run, and check:deployed keeps reporting the gap on every PR, which is the honest failure mode. Workflow деплоя, запускаемый вручную: публикация на каждый merge означала бы вещание без согласования. Требует секрет GHIO_TOKEN. Co-Authored-By: Claude Opus 5 --- .github/workflows/deploy-site.yml | 99 +++++++++++++++++++++++++++++++ 1 file changed, 99 insertions(+) create mode 100644 .github/workflows/deploy-site.yml diff --git a/.github/workflows/deploy-site.yml b/.github/workflows/deploy-site.yml new file mode 100644 index 0000000000..ca65980e84 --- /dev/null +++ b/.github/workflows/deploy-site.yml @@ -0,0 +1,99 @@ +# Build apps/website and publish it to the repo that serves t27.ai. +# +# anomaly-register A51: four PRs merged, every check green, and the live site +# changed nothing for a week. This app deploys from gHashTag/ghashtag.github.io, +# which holds build output only, and a human copied dist/ into it. Nothing in +# the pipeline noticed, because every other check reads the source. +# +# The deploy needs write access to a second repository, which a workflow token +# does not have. It requires a secret named GHIO_TOKEN -- a fine-grained PAT +# with Contents: write on gHashTag/ghashtag.github.io and nothing else. Until +# that secret exists this workflow does not run, and check:deployed keeps +# reporting the gap on every PR. +# +# Manual only, on purpose. Publishing to the live domain on every push to main +# would mean any merge broadcasts, and this project's rule is that content is +# approved before it goes out. +name: Deploy site to t27.ai + +on: + workflow_dispatch: + inputs: + reason: + description: 'What is being published (goes in the commit message)' + required: true + +jobs: + deploy: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: '22' + cache: npm + cache-dependency-path: apps/website/package-lock.json + + - name: Build + working-directory: apps/website + run: | + npm ci + npx vite build + + # The same three checks the PR runs, against the tree being published. + # A deploy that skips them is how a broken build reaches the domain. + - name: Checks + working-directory: apps/website + run: | + npm run typecheck:ratchet + npm run check:api + + - name: Checkout the deploy repo + uses: actions/checkout@v4 + with: + repository: gHashTag/ghashtag.github.io + token: ${{ secrets.GHIO_TOKEN }} + path: ghio + + # Copy the build, then delete bundles the new index.html cannot reach. + # Skipping that step is how 74 orphans accumulated once, keeping + # withdrawn claims greppable for weeks after the components were fixed. + - name: Copy the build and drop unreachable bundles + run: | + cp -R apps/website/dist/assets/. ghio/assets/ + cp apps/website/dist/index.html ghio/index.html + cp apps/website/dist/manifest.json ghio/manifest.json + node - <<'EOF' + const {readFileSync, readdirSync, unlinkSync} = require('node:fs'); + const {join} = require('node:path'); + const A = 'ghio/assets'; + const entry = [...readFileSync('ghio/index.html','utf8') + .matchAll(/assets\/([\w.-]+\.(?:js|css))/g)].map(m => m[1]); + const seen = new Set(); const stack = [...entry]; + while (stack.length) { + const f = stack.pop(); + if (seen.has(f)) continue; + seen.add(f); + try { + for (const m of readFileSync(join(A,f),'utf8').matchAll(/["']\.\/([\w.-]+\.(?:js|css))["']/g)) + if (!seen.has(m[1])) stack.push(m[1]); + } catch {} + } + let n = 0; + for (const f of readdirSync(A)) + if (/\.(js|css)$/.test(f) && !seen.has(f)) { unlinkSync(join(A,f)); n++; } + console.log(` ${seen.size} reachable, ${n} orphan(s) removed`); + EOF + + - name: Commit and push + working-directory: ghio + run: | + git config user.name gHashTag + git config user.email admin@t27.ai + git add -A + git diff --cached --quiet && { echo " nothing changed — the live site is already this build"; exit 0; } + git commit -m "Deploy: ${{ inputs.reason }} + + Built from gHashTag/trinity@${{ github.sha }} by the deploy workflow. + See anomaly-register A51 for why this exists." + git push