SpokeToWork was squash-forked from ScriptHammer in Dec 2025. There is no shared git history and no upstream remote, so drift can only be measured by comparing files — git log will never show it.
As of 2026-08-01, upstream has landed 461 commits since this fork's last commit (3cca5af, 2026-04-20).
Most of that is divergence we should never import. This epic exists so nobody re-derives that judgement, and — more importantly — so nobody tries to "catch up to upstream" wholesale.
🚫 DO NOT IMPORT
Upstream has grown in directions that have no SpokeToWork surface. Importing any of this would be pure cost.
| Upstream subsystem |
Why not |
src/twin/, src/world/, src/stage/, src/packs/, src/post/ |
Cesium + three.js digital-twin renderer. No consumer here. |
scripts/bake/*, sites/*.json, public/twins/, sites/_warehouse/ |
LiDAR/OSM/3DEP bake pipeline for the twin world. |
src/lib/manifest.ts (20 upstream commits) |
Despite the name this is not the PWA manifest — it's the baked-twin runtime contract. Do not "sync" it against ours. |
Stripe / PayPal subscription work, supabase/functions/{cancel,create,resume,retry}-subscription, checkout/capture functions |
We have payment-demo only, no live subscription surface. |
Calendly / @calcom/embed-react |
Not a SpokeToWork surface. |
src/services/messaging/providers/ incl. dotnet-provider.ts, .github/workflows/conformance.yml, the ASP.NET/EF Core backend |
A second messaging backend behind an abstraction seam. We have one Supabase backend and no reason for the seam. |
Upstream src/services/messaging/message-service.ts as a file |
Diverged the opposite way from ours (provider seam + caches) vs. ours (RLS/fetch error discrimination, ensureKeys, query timeouts). Cherry-pick at most. |
The "Machine Shop" / depth-token design system (.sh-btn, sh-doc, Archivo/Archivo Black, depth-tokens.spec.ts) |
This is ScriptHammer's brand identity, not a fix. We have spoketowork-dark / spoketowork-light. Read it as reference for #67; import nothing verbatim. |
Blog/docs content, graphify hooks |
Content, not code. (The markdown-processor.ts fixes are separate and are worth taking — see below.) |
Turnstile/CAPTCHA arc, Mailpit signup mailer, email-health.yml, auth-config-drift.yml, smoke.yml |
Real work, but tied to upstream's Supabase project config and mail identity. New features, not fixes to shared code. |
✅ WE ARE AHEAD — do not "restore" these from upstream
Importing upstream here would be a regression:
eslint-plugin-no-secrets config, the gitleaks CI job, the pnpm audit + SQL-injection CI steps
deploy.yml gated on workflow_run: [CI] success
- Storybook config (no
staticDirs EEXIST bug, array-form alias ordering, mocked Auth/ActiveRoute contexts, guarded MSW init)
- Vitest 4 with the 3-environment project split; Playwright 1.57; Node 22;
actions/upload-pages-artifact@v4
tests/setup.ts Supabase mock harness
tests/e2e/utils/shard-users.ts + prebaked-keys.ts (upstream has no equivalent)
supabase/functions/delete-user-account/ (GDPR deletion — upstream lacks it)
CookieConsent buttons already at min-h-11 min-w-11
.gitleaks.toml — ours is stricter; cherry-pick only upstream's github_pat_ / sk_test_ rules
✔️ ALREADY DONE / NOTHING TO INHERIT
📥 Import candidates, ranked
Effort: XS = one line · S = under an hour · M = half a day · L = more
Tier 1 — take these
| # |
What it fixes |
Upstream ref |
Our target |
Effort |
| 1 |
Deploy serves HTML referencing assets that no longer exist → site renders with no CSS for ~10 min after each deploy |
scripts/retain-previous-assets.mjs + ASSET_MANIFEST step (#476) |
.github/workflows/deploy.yml |
M |
| 2 |
.dockerignore — build context is ~1.19 GB |
upstream 101 lines vs our 48 |
.dockerignore |
S |
| 3 |
Dead builder/runner Dockerfile stages that cannot build under output: 'export' |
ebbac6b |
docker/Dockerfile:69-99 |
XS |
| 4 |
validate:breakpoints and test:coverage are defined but never invoked in CI |
— |
.github/workflows/ci.yml |
XS |
| 5 |
Component validator accepts empty test stubs (content.includes('test') vs 'test(') |
— |
scripts/audit-components.js:277,301 |
XS |
| 6 |
8 node:test files for the generator/validators already sit in our tree and are never executed |
test:scripts script |
package.json + ci.yml |
S |
| 7 |
NEXT_PUBLIC_* stored as secrets. — masked in logs, unavailable to fork PRs |
— |
all workflows |
S |
| 8 |
Coverage exclude list (~50 entries) we dropped, which inflates the denominator with untestable route files |
— |
vitest.config.ts |
S |
| 9 |
generateId leaves a leading hyphen → every anchor on an emoji heading is broken |
2b807ce |
src/lib/blog/markdown-processor.ts:270-278 |
XS |
| 10 |
SpecKit: our .specify/ has no scripts/bash/, so the speckit.* slash commands have no scripts to call; constitution is v1.0.0 vs upstream v1.0.2 |
.specify/scripts/bash/* |
.specify/ |
S |
Tier 2 — a11y / layout batch (feeds #70 and #67)
Upstream ran a large accessibility + typography sweep (their #373, #425, #428, #457, #473, #476, #479, #483, #495). Ours has the same defects because it has the same ancestor code.
| # |
What it fixes |
Our target |
Effort |
| 11 |
container capped at the previous breakpoint — up to 43% dead gutter on most pages |
src/app/globals.css @utility container |
XS + gate |
| 12 |
html, body { overflow-x: hidden } breaks post-rotation width and makes the overflow gate unfailable |
src/app/globals.css:6-11, src/app/layout.tsx |
S |
| 13 |
Overflow gate sweeps 2 of 39 routes and short-circuits to false |
tests/e2e/tests/mobile-horizontal-scroll.spec.ts, src/config/test-viewports.ts:204 |
S |
| 14 |
sm: breakpoint fires at exactly 428px (iPhone 14/15 Pro Max) |
src/app/globals.css:16, src/config/breakpoints.ts:33,109 |
S |
| 15 |
Sub-44px touch targets: CreateGroupModal.tsx:216 (btn-xs min-h-0 min-w-0 p-0), PWAInstall.tsx:256,263, GlobalNav.tsx:199,212,220, RouteCompanyList.tsx:142, TableHead.tsx:29, ModerationCard.tsx:56,69,91,98 |
various |
S |
| 16 |
Touch-target gate measures ~1 element and reports green |
tests/e2e/tests/mobile-touch-targets.spec.ts |
S |
| 17 |
AAA contrast sweep across all routes × both themes |
new tests/e2e/color-contrast.spec.ts |
M |
| 18 |
Nav and footer inset differently at every width ≥ sm |
GlobalNav.tsx:169 |
XS |
⚠️ Tier 2 will find real violations on first run. Budget for the fixes it surfaces, and land 12 and 13 together — removing the clip reveals overflow that the gate must be able to see.
Tier 3 — needs review before importing
| # |
What |
Why careful |
| 19 |
Messaging sequence-number / RLS integrity work, enforce_message_update_columns, is_conversation_{member,creator,owner} |
Upstream has 13 more RLS policies than us across 10 shared tables, concentrated on messages (-3) and conversations (-3). Per-policy audit, not a bulk copy. Overlaps #75. |
| 20 |
custom_access_token_hook + is_admin() as the admin pattern |
We use set_own_role, a weaker pattern. Directly relevant to #75. Do not copy policies — port the pattern. |
| 21 |
edge_idempotency_keys table |
We have webhook Edge Functions with no idempotency backing. |
| 22 |
e2e browser de-serialization (needs: chain → max-parallel) |
Blocked. E2E_SHARD_INDEX is cut -d/ -f1 of matrix.shard, so all three browsers resolve to the same e2e-s1..s4-* users. The per-browser offset shard-users.ts:82 documents (chromium 1-4, firefox 5-8, webkit 9-12) was never implemented. Implement that first or the browsers collide. |
| 23 |
pa11y adoption |
See #76 item 5 — decide adopt-or-delete first. |
Reverse-import candidates (ours → upstream)
Worth offering back: the tests/setup.ts Supabase mock, shard-users.ts/prebaked-keys.ts, the delete-user-account Edge Function, our .gitleaks.toml + secret-scanning CI, storybook-audit.spec.ts + storybook-contrast-audit.spec.ts, and the workflow_run-gated deploy.
Method note
Because there is no shared history, the only reliable comparison is file-level. git -C ../ScriptHammer log --oneline --since=<date> gives the narrative; actual drift needs a diff of the specific file. Do not assume a same-named file is the same file — src/lib/manifest.ts is the cautionary example.
SpokeToWork was squash-forked from ScriptHammer in Dec 2025. There is no shared git history and no upstream remote, so drift can only be measured by comparing files —
git logwill never show it.As of 2026-08-01, upstream has landed 461 commits since this fork's last commit (
3cca5af, 2026-04-20).Most of that is divergence we should never import. This epic exists so nobody re-derives that judgement, and — more importantly — so nobody tries to "catch up to upstream" wholesale.
🚫 DO NOT IMPORT
Upstream has grown in directions that have no SpokeToWork surface. Importing any of this would be pure cost.
src/twin/,src/world/,src/stage/,src/packs/,src/post/scripts/bake/*,sites/*.json,public/twins/,sites/_warehouse/src/lib/manifest.ts(20 upstream commits)supabase/functions/{cancel,create,resume,retry}-subscription, checkout/capture functionspayment-demoonly, no live subscription surface.@calcom/embed-reactsrc/services/messaging/providers/incl.dotnet-provider.ts,.github/workflows/conformance.yml, the ASP.NET/EF Core backendsrc/services/messaging/message-service.tsas a fileensureKeys, query timeouts). Cherry-pick at most..sh-btn,sh-doc, Archivo/Archivo Black,depth-tokens.spec.ts)spoketowork-dark/spoketowork-light. Read it as reference for #67; import nothing verbatim.graphifyhooksmarkdown-processor.tsfixes are separate and are worth taking — see below.)email-health.yml,auth-config-drift.yml,smoke.yml✅ WE ARE AHEAD — do not "restore" these from upstream
Importing upstream here would be a regression:
eslint-plugin-no-secretsconfig, thegitleaksCI job, thepnpm audit+ SQL-injection CI stepsdeploy.ymlgated onworkflow_run: [CI] successstaticDirsEEXIST bug, array-form alias ordering, mocked Auth/ActiveRoute contexts, guarded MSW init)actions/upload-pages-artifact@v4tests/setup.tsSupabase mock harnesstests/e2e/utils/shard-users.ts+prebaked-keys.ts(upstream has no equivalent)supabase/functions/delete-user-account/(GDPR deletion — upstream lacks it)CookieConsentbuttons already atmin-h-11 min-w-11.gitleaks.toml— ours is stricter; cherry-pick only upstream'sgithub_pat_/sk_test_rules✔️ ALREADY DONE / NOTHING TO INHERIT
storybook ^10.2.10is installed here.complete-user-workflow.spec.ts:751istest.setTimeout(60000).lighthouseas a devDep but no measured scores and no harness. Original work either way.📥 Import candidates, ranked
Effort: XS = one line · S = under an hour · M = half a day · L = more
Tier 1 — take these
scripts/retain-previous-assets.mjs+ ASSET_MANIFEST step (#476).github/workflows/deploy.yml.dockerignore— build context is ~1.19 GB.dockerignorebuilder/runnerDockerfile stages that cannot build underoutput: 'export'ebbac6bdocker/Dockerfile:69-99validate:breakpointsandtest:coverageare defined but never invoked in CI.github/workflows/ci.ymlcontent.includes('test')vs'test(')scripts/audit-components.js:277,301node:testfiles for the generator/validators already sit in our tree and are never executedtest:scriptsscriptpackage.json+ci.ymlNEXT_PUBLIC_*stored assecrets.— masked in logs, unavailable to fork PRsexcludelist (~50 entries) we dropped, which inflates the denominator with untestable route filesvitest.config.tsgenerateIdleaves a leading hyphen → every anchor on an emoji heading is broken2b807cesrc/lib/blog/markdown-processor.ts:270-278.specify/has noscripts/bash/, so thespeckit.*slash commands have no scripts to call; constitution is v1.0.0 vs upstream v1.0.2.specify/scripts/bash/*.specify/Tier 2 — a11y / layout batch (feeds #70 and #67)
Upstream ran a large accessibility + typography sweep (their #373, #425, #428, #457, #473, #476, #479, #483, #495). Ours has the same defects because it has the same ancestor code.
containercapped at the previous breakpoint — up to 43% dead gutter on most pagessrc/app/globals.css@utility containerhtml, body { overflow-x: hidden }breaks post-rotation width and makes the overflow gate unfailablesrc/app/globals.css:6-11,src/app/layout.tsxfalsetests/e2e/tests/mobile-horizontal-scroll.spec.ts,src/config/test-viewports.ts:204sm:breakpoint fires at exactly 428px (iPhone 14/15 Pro Max)src/app/globals.css:16,src/config/breakpoints.ts:33,109CreateGroupModal.tsx:216(btn-xs min-h-0 min-w-0 p-0),PWAInstall.tsx:256,263,GlobalNav.tsx:199,212,220,RouteCompanyList.tsx:142,TableHead.tsx:29,ModerationCard.tsx:56,69,91,98tests/e2e/tests/mobile-touch-targets.spec.tstests/e2e/color-contrast.spec.tsGlobalNav.tsx:169Tier 3 — needs review before importing
enforce_message_update_columns,is_conversation_{member,creator,owner}messages(-3) andconversations(-3). Per-policy audit, not a bulk copy. Overlaps #75.custom_access_token_hook+is_admin()as the admin patternset_own_role, a weaker pattern. Directly relevant to #75. Do not copy policies — port the pattern.edge_idempotency_keystableneeds:chain →max-parallel)E2E_SHARD_INDEXiscut -d/ -f1ofmatrix.shard, so all three browsers resolve to the samee2e-s1..s4-*users. The per-browser offsetshard-users.ts:82documents (chromium 1-4, firefox 5-8, webkit 9-12) was never implemented. Implement that first or the browsers collide.Reverse-import candidates (ours → upstream)
Worth offering back: the
tests/setup.tsSupabase mock,shard-users.ts/prebaked-keys.ts, thedelete-user-accountEdge Function, our.gitleaks.toml+ secret-scanning CI,storybook-audit.spec.ts+storybook-contrast-audit.spec.ts, and theworkflow_run-gated deploy.Method note
Because there is no shared history, the only reliable comparison is file-level.
git -C ../ScriptHammer log --oneline --since=<date>gives the narrative; actual drift needs a diff of the specific file. Do not assume a same-named file is the same file —src/lib/manifest.tsis the cautionary example.