chore(storefront): page-surface naming cleanup + template convention - #753

Merged
bussyjd merged 3 commits into
mainfrom
chore/page-surface-naming
Jul 17, 2026
Merged

chore(storefront): page-surface naming cleanup + template convention#753
bussyjd merged 3 commits into
mainfrom
chore/page-surface-naming

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Three independent cleanups around the public page surfaces (catalog storefront / per-offer landing / 402 checkout), no behaviour change:

  1. skillCatalog*staticSite* — the busybox httpd named after the first file it ever held now serves skill.md, services.json (the storefront's own backend), openapi.json, the API docs, and every per-offer landing bundle. Go identifiers renamed for what it is; the skill.md builders split out as buildSkillMarkdown/skillMarkdown*. Every k8s wire name is untouched (obol-skill-md, obol-skill-md-route, obol-catalog-headers, namespace x402) — verified by diffing string literals removed vs re-added (net zero); a comment at the const block documents the deliberate identifier/wire-name mismatch and why renaming the live objects is a migration, not a rename.
  2. Landing page template extracted to templates/offer_landing.html + go:embed, adopting the existing x402 convention (payment_required.html et al.) instead of a backtick literal mid-package. Content byte-identical.
  3. internal/x402/templates/DESIGN.md corrected — it documented a hand-mirrored token palette and a drift-check command that had been dead since theming centralized into internal/storefront/theme.go (the template contains zero hex values; the old check compared an empty set forever). Now points at the real single owner, documents the one surviving hand-mirror (the storefront's TS fallback) with a drift check verified to both pass today and fail on injected drift, and adds a table of the five data-obol="page-*" surfaces.

Tests green (go test ./...); commits signed.

https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk

skillCatalog* named a busybox httpd after the first file it ever held.
It now serves skill.md, services.json (the storefront's own backend via
SERVICES_URL), openapi.json, the API docs, and one bundle per
hostname-bound offer -- including every offer landing page. "Skill
catalog" described one of those.
Two concepts were sharing the name, so this splits them:
skillCatalog* -> staticSite* (the httpd + its ConfigMap)
buildSkillCatalogMarkdown -> buildSkillMarkdown (builds skill.md)
skillCatalog{HowToPay,TryIt,RouteLines} -> skillMarkdown* (its sections)
catalogMu -> staticSiteMu (guards the static-site reconcile)
Identifiers only. Every k8s wire name is untouched -- obol-skill-md,
obol-skill-md-route, obol-catalog-headers and namespace x402 are all
byte-identical, verified by diffing the string literals removed against
those re-added (net zero). Renaming the objects would be a live-cluster
migration and they are referenced from internal/tunnel, cmd/obol,
internal/stackbackup, the embedded x402.yaml and next.config.ts; a
comment at the const block now explains the deliberate mismatch.
ServiceCatalog* (the /api/services.json wire types) keeps its name -- that
one is genuinely a catalog.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
…emplate
The landing page was a backtick literal buried mid-offerbundle.go while all
three x402 pages (payment_required, siwx_challenge, error_page) follow one
convention: templates/<name>.html + //go:embed + <name>HTMLSrc + <name>Tmpl.
It is the surface users actually hit and it was the only one you could not
open as HTML.
Adopts the existing x402 convention rather than inventing a second one:
internal/serviceoffercontroller/templates/offer_landing.html
//go:embed templates/offer_landing.html
var offerLandingHTMLSrc string
Content is byte-identical to the previous literal (verified by extracting
the literal from origin/main and diffing against the new file), so the
rendered page is unchanged.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
… gone
DESIGN.md told the next engineer to hand-mirror the token palette across
three files and gave a drift check to enforce it. Both were stale:
payment_required.html contains zero hex values -- theming moved to
storefront.ResolveTheme(...).CSSVars() via {{.Branding.ThemeCSS}}. The
documented check diffed hex out of that template against globals.css, so
its left side was empty and it compared an empty set forever. Following
the doc would have re-introduced exactly the drift it existed to prevent.
- SS 2: describes the real source (theme.go is the single owner) and says
not to paste hex back in.
- SS 5: points at the one hand-mirror that does survive -- theme.ts's
LIGHT_THEME_VARS, a fallback that rots silently because a healthy page
takes tokens from the feed. The new check compares theme.go's ThemeLight
against it; verified it passes today (13 pairs each side) and verified it
FAILS on injected drift, which the old one could not do.
- SS 0: adds the five public surfaces and their data-obol="page-*" markers.
Nothing else in the repo lists them. Records the two things that are easy
to get backwards: page-402 renders only on a paid path with Accept:
text/html (so a root-priced offer's browser visitors get page-landing,
never page-402), and data-obol="checkout" is a mount div on two pages
rather than a name for the 402 page.
- Corrects "four surfaces" -> five.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
@bussyjd
bussyjd requested review from HananINouman and OisinKyne and removed request for OisinKyneJuly 15, 2026 13:25
@bussyjdbussyjd mentioned this pull request Jul 15, 2026
6 tasks
@bussyjd
bussyjd merged commit a0a8c6e into mainJul 17, 2026
10 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

chore(storefront): page-surface naming cleanup + template convention - #753

Merged
bussyjd merged 3 commits into
mainfrom
chore/page-surface-naming
Jul 17, 2026
Merged

chore(storefront): page-surface naming cleanup + template convention#753
bussyjd merged 3 commits into
mainfrom
chore/page-surface-naming

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Three independent cleanups around the public page surfaces (catalog storefront / per-offer landing / 402 checkout), no behaviour change:

  1. skillCatalog*staticSite* — the busybox httpd named after the first file it ever held now serves skill.md, services.json (the storefront's own backend), openapi.json, the API docs, and every per-offer landing bundle. Go identifiers renamed for what it is; the skill.md builders split out as buildSkillMarkdown/skillMarkdown*. Every k8s wire name is untouched (obol-skill-md, obol-skill-md-route, obol-catalog-headers, namespace x402) — verified by diffing string literals removed vs re-added (net zero); a comment at the const block documents the deliberate identifier/wire-name mismatch and why renaming the live objects is a migration, not a rename.
  2. Landing page template extracted to templates/offer_landing.html + go:embed, adopting the existing x402 convention (payment_required.html et al.) instead of a backtick literal mid-package. Content byte-identical.
  3. internal/x402/templates/DESIGN.md corrected — it documented a hand-mirrored token palette and a drift-check command that had been dead since theming centralized into internal/storefront/theme.go (the template contains zero hex values; the old check compared an empty set forever). Now points at the real single owner, documents the one surviving hand-mirror (the storefront's TS fallback) with a drift check verified to both pass today and fail on injected drift, and adds a table of the five data-obol="page-*" surfaces.

Tests green (go test ./...); commits signed.

https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk

skillCatalog* named a busybox httpd after the first file it ever held.
It now serves skill.md, services.json (the storefront's own backend via
SERVICES_URL), openapi.json, the API docs, and one bundle per
hostname-bound offer -- including every offer landing page. "Skill
catalog" described one of those.
Two concepts were sharing the name, so this splits them:
skillCatalog* -> staticSite* (the httpd + its ConfigMap)
buildSkillCatalogMarkdown -> buildSkillMarkdown (builds skill.md)
skillCatalog{HowToPay,TryIt,RouteLines} -> skillMarkdown* (its sections)
catalogMu -> staticSiteMu (guards the static-site reconcile)
Identifiers only. Every k8s wire name is untouched -- obol-skill-md,
obol-skill-md-route, obol-catalog-headers and namespace x402 are all
byte-identical, verified by diffing the string literals removed against
those re-added (net zero). Renaming the objects would be a live-cluster
migration and they are referenced from internal/tunnel, cmd/obol,
internal/stackbackup, the embedded x402.yaml and next.config.ts; a
comment at the const block now explains the deliberate mismatch.
ServiceCatalog* (the /api/services.json wire types) keeps its name -- that
one is genuinely a catalog.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
…emplate
The landing page was a backtick literal buried mid-offerbundle.go while all
three x402 pages (payment_required, siwx_challenge, error_page) follow one
convention: templates/<name>.html + //go:embed + <name>HTMLSrc + <name>Tmpl.
It is the surface users actually hit and it was the only one you could not
open as HTML.
Adopts the existing x402 convention rather than inventing a second one:
internal/serviceoffercontroller/templates/offer_landing.html
//go:embed templates/offer_landing.html
var offerLandingHTMLSrc string
Content is byte-identical to the previous literal (verified by extracting
the literal from origin/main and diffing against the new file), so the
rendered page is unchanged.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
… gone
DESIGN.md told the next engineer to hand-mirror the token palette across
three files and gave a drift check to enforce it. Both were stale:
payment_required.html contains zero hex values -- theming moved to
storefront.ResolveTheme(...).CSSVars() via {{.Branding.ThemeCSS}}. The
documented check diffed hex out of that template against globals.css, so
its left side was empty and it compared an empty set forever. Following
the doc would have re-introduced exactly the drift it existed to prevent.
- SS 2: describes the real source (theme.go is the single owner) and says
not to paste hex back in.
- SS 5: points at the one hand-mirror that does survive -- theme.ts's
LIGHT_THEME_VARS, a fallback that rots silently because a healthy page
takes tokens from the feed. The new check compares theme.go's ThemeLight
against it; verified it passes today (13 pairs each side) and verified it
FAILS on injected drift, which the old one could not do.
- SS 0: adds the five public surfaces and their data-obol="page-*" markers.
Nothing else in the repo lists them. Records the two things that are easy
to get backwards: page-402 renders only on a paid path with Accept:
text/html (so a root-priced offer's browser visitors get page-landing,
never page-402), and data-obol="checkout" is a mount div on two pages
rather than a name for the 402 page.
- Corrects "four surfaces" -> five.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
@bussyjd
bussyjd requested review from HananINouman and OisinKyne and removed request for OisinKyneJuly 15, 2026 13:25
@bussyjdbussyjd mentioned this pull request Jul 15, 2026
6 tasks
@bussyjd
bussyjd merged commit a0a8c6e into mainJul 17, 2026
10 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(storefront): page-surface naming cleanup + template convention - #753

Merged
bussyjd merged 3 commits into
mainfrom
chore/page-surface-naming
Jul 17, 2026
Merged

chore(storefront): page-surface naming cleanup + template convention#753
bussyjd merged 3 commits into
mainfrom
chore/page-surface-naming

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Three independent cleanups around the public page surfaces (catalog storefront / per-offer landing / 402 checkout), no behaviour change:

  1. skillCatalog*staticSite* — the busybox httpd named after the first file it ever held now serves skill.md, services.json (the storefront's own backend), openapi.json, the API docs, and every per-offer landing bundle. Go identifiers renamed for what it is; the skill.md builders split out as buildSkillMarkdown/skillMarkdown*. Every k8s wire name is untouched (obol-skill-md, obol-skill-md-route, obol-catalog-headers, namespace x402) — verified by diffing string literals removed vs re-added (net zero); a comment at the const block documents the deliberate identifier/wire-name mismatch and why renaming the live objects is a migration, not a rename.
  2. Landing page template extracted to templates/offer_landing.html + go:embed, adopting the existing x402 convention (payment_required.html et al.) instead of a backtick literal mid-package. Content byte-identical.
  3. internal/x402/templates/DESIGN.md corrected — it documented a hand-mirrored token palette and a drift-check command that had been dead since theming centralized into internal/storefront/theme.go (the template contains zero hex values; the old check compared an empty set forever). Now points at the real single owner, documents the one surviving hand-mirror (the storefront's TS fallback) with a drift check verified to both pass today and fail on injected drift, and adds a table of the five data-obol="page-*" surfaces.

Tests green (go test ./...); commits signed.

https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk

skillCatalog* named a busybox httpd after the first file it ever held.
It now serves skill.md, services.json (the storefront's own backend via
SERVICES_URL), openapi.json, the API docs, and one bundle per
hostname-bound offer -- including every offer landing page. "Skill
catalog" described one of those.
Two concepts were sharing the name, so this splits them:
skillCatalog* -> staticSite* (the httpd + its ConfigMap)
buildSkillCatalogMarkdown -> buildSkillMarkdown (builds skill.md)
skillCatalog{HowToPay,TryIt,RouteLines} -> skillMarkdown* (its sections)
catalogMu -> staticSiteMu (guards the static-site reconcile)
Identifiers only. Every k8s wire name is untouched -- obol-skill-md,
obol-skill-md-route, obol-catalog-headers and namespace x402 are all
byte-identical, verified by diffing the string literals removed against
those re-added (net zero). Renaming the objects would be a live-cluster
migration and they are referenced from internal/tunnel, cmd/obol,
internal/stackbackup, the embedded x402.yaml and next.config.ts; a
comment at the const block now explains the deliberate mismatch.
ServiceCatalog* (the /api/services.json wire types) keeps its name -- that
one is genuinely a catalog.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
…emplate
The landing page was a backtick literal buried mid-offerbundle.go while all
three x402 pages (payment_required, siwx_challenge, error_page) follow one
convention: templates/<name>.html + //go:embed + <name>HTMLSrc + <name>Tmpl.
It is the surface users actually hit and it was the only one you could not
open as HTML.
Adopts the existing x402 convention rather than inventing a second one:
internal/serviceoffercontroller/templates/offer_landing.html
//go:embed templates/offer_landing.html
var offerLandingHTMLSrc string
Content is byte-identical to the previous literal (verified by extracting
the literal from origin/main and diffing against the new file), so the
rendered page is unchanged.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
… gone
DESIGN.md told the next engineer to hand-mirror the token palette across
three files and gave a drift check to enforce it. Both were stale:
payment_required.html contains zero hex values -- theming moved to
storefront.ResolveTheme(...).CSSVars() via {{.Branding.ThemeCSS}}. The
documented check diffed hex out of that template against globals.css, so
its left side was empty and it compared an empty set forever. Following
the doc would have re-introduced exactly the drift it existed to prevent.
- SS 2: describes the real source (theme.go is the single owner) and says
not to paste hex back in.
- SS 5: points at the one hand-mirror that does survive -- theme.ts's
LIGHT_THEME_VARS, a fallback that rots silently because a healthy page
takes tokens from the feed. The new check compares theme.go's ThemeLight
against it; verified it passes today (13 pairs each side) and verified it
FAILS on injected drift, which the old one could not do.
- SS 0: adds the five public surfaces and their data-obol="page-*" markers.
Nothing else in the repo lists them. Records the two things that are easy
to get backwards: page-402 renders only on a paid path with Accept:
text/html (so a root-priced offer's browser visitors get page-landing,
never page-402), and data-obol="checkout" is a mount div on two pages
rather than a name for the 402 page.
- Corrects "four surfaces" -> five.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
@bussyjd
bussyjd requested review from HananINouman and OisinKyne and removed request for OisinKyneJuly 15, 2026 13:25
@bussyjdbussyjd mentioned this pull request Jul 15, 2026
6 tasks
@bussyjd
bussyjd merged commit a0a8c6e into mainJul 17, 2026
10 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(storefront): page-surface naming cleanup + template convention - #753

Merged
bussyjd merged 3 commits into
mainfrom
chore/page-surface-naming
Jul 17, 2026
Merged

chore(storefront): page-surface naming cleanup + template convention#753
bussyjd merged 3 commits into
mainfrom
chore/page-surface-naming

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Three independent cleanups around the public page surfaces (catalog storefront / per-offer landing / 402 checkout), no behaviour change:

  1. skillCatalog*staticSite* — the busybox httpd named after the first file it ever held now serves skill.md, services.json (the storefront's own backend), openapi.json, the API docs, and every per-offer landing bundle. Go identifiers renamed for what it is; the skill.md builders split out as buildSkillMarkdown/skillMarkdown*. Every k8s wire name is untouched (obol-skill-md, obol-skill-md-route, obol-catalog-headers, namespace x402) — verified by diffing string literals removed vs re-added (net zero); a comment at the const block documents the deliberate identifier/wire-name mismatch and why renaming the live objects is a migration, not a rename.
  2. Landing page template extracted to templates/offer_landing.html + go:embed, adopting the existing x402 convention (payment_required.html et al.) instead of a backtick literal mid-package. Content byte-identical.
  3. internal/x402/templates/DESIGN.md corrected — it documented a hand-mirrored token palette and a drift-check command that had been dead since theming centralized into internal/storefront/theme.go (the template contains zero hex values; the old check compared an empty set forever). Now points at the real single owner, documents the one surviving hand-mirror (the storefront's TS fallback) with a drift check verified to both pass today and fail on injected drift, and adds a table of the five data-obol="page-*" surfaces.

Tests green (go test ./...); commits signed.

https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk

skillCatalog* named a busybox httpd after the first file it ever held.
It now serves skill.md, services.json (the storefront's own backend via
SERVICES_URL), openapi.json, the API docs, and one bundle per
hostname-bound offer -- including every offer landing page. "Skill
catalog" described one of those.
Two concepts were sharing the name, so this splits them:
skillCatalog* -> staticSite* (the httpd + its ConfigMap)
buildSkillCatalogMarkdown -> buildSkillMarkdown (builds skill.md)
skillCatalog{HowToPay,TryIt,RouteLines} -> skillMarkdown* (its sections)
catalogMu -> staticSiteMu (guards the static-site reconcile)
Identifiers only. Every k8s wire name is untouched -- obol-skill-md,
obol-skill-md-route, obol-catalog-headers and namespace x402 are all
byte-identical, verified by diffing the string literals removed against
those re-added (net zero). Renaming the objects would be a live-cluster
migration and they are referenced from internal/tunnel, cmd/obol,
internal/stackbackup, the embedded x402.yaml and next.config.ts; a
comment at the const block now explains the deliberate mismatch.
ServiceCatalog* (the /api/services.json wire types) keeps its name -- that
one is genuinely a catalog.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
…emplate
The landing page was a backtick literal buried mid-offerbundle.go while all
three x402 pages (payment_required, siwx_challenge, error_page) follow one
convention: templates/<name>.html + //go:embed + <name>HTMLSrc + <name>Tmpl.
It is the surface users actually hit and it was the only one you could not
open as HTML.
Adopts the existing x402 convention rather than inventing a second one:
internal/serviceoffercontroller/templates/offer_landing.html
//go:embed templates/offer_landing.html
var offerLandingHTMLSrc string
Content is byte-identical to the previous literal (verified by extracting
the literal from origin/main and diffing against the new file), so the
rendered page is unchanged.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
… gone
DESIGN.md told the next engineer to hand-mirror the token palette across
three files and gave a drift check to enforce it. Both were stale:
payment_required.html contains zero hex values -- theming moved to
storefront.ResolveTheme(...).CSSVars() via {{.Branding.ThemeCSS}}. The
documented check diffed hex out of that template against globals.css, so
its left side was empty and it compared an empty set forever. Following
the doc would have re-introduced exactly the drift it existed to prevent.
- SS 2: describes the real source (theme.go is the single owner) and says
not to paste hex back in.
- SS 5: points at the one hand-mirror that does survive -- theme.ts's
LIGHT_THEME_VARS, a fallback that rots silently because a healthy page
takes tokens from the feed. The new check compares theme.go's ThemeLight
against it; verified it passes today (13 pairs each side) and verified it
FAILS on injected drift, which the old one could not do.
- SS 0: adds the five public surfaces and their data-obol="page-*" markers.
Nothing else in the repo lists them. Records the two things that are easy
to get backwards: page-402 renders only on a paid path with Accept:
text/html (so a root-priced offer's browser visitors get page-landing,
never page-402), and data-obol="checkout" is a mount div on two pages
rather than a name for the 402 page.
- Corrects "four surfaces" -> five.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
@bussyjd
bussyjd requested review from HananINouman and OisinKyne and removed request for OisinKyneJuly 15, 2026 13:25
@bussyjdbussyjd mentioned this pull request Jul 15, 2026
6 tasks
@bussyjd
bussyjd merged commit a0a8c6e into mainJul 17, 2026
10 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

chore(storefront): page-surface naming cleanup + template convention - #753

Merged
bussyjd merged 3 commits into
mainfrom
chore/page-surface-naming
Jul 17, 2026
Merged

chore(storefront): page-surface naming cleanup + template convention#753
bussyjd merged 3 commits into
mainfrom
chore/page-surface-naming

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Three independent cleanups around the public page surfaces (catalog storefront / per-offer landing / 402 checkout), no behaviour change:

  1. skillCatalog*staticSite* — the busybox httpd named after the first file it ever held now serves skill.md, services.json (the storefront's own backend), openapi.json, the API docs, and every per-offer landing bundle. Go identifiers renamed for what it is; the skill.md builders split out as buildSkillMarkdown/skillMarkdown*. Every k8s wire name is untouched (obol-skill-md, obol-skill-md-route, obol-catalog-headers, namespace x402) — verified by diffing string literals removed vs re-added (net zero); a comment at the const block documents the deliberate identifier/wire-name mismatch and why renaming the live objects is a migration, not a rename.
  2. Landing page template extracted to templates/offer_landing.html + go:embed, adopting the existing x402 convention (payment_required.html et al.) instead of a backtick literal mid-package. Content byte-identical.
  3. internal/x402/templates/DESIGN.md corrected — it documented a hand-mirrored token palette and a drift-check command that had been dead since theming centralized into internal/storefront/theme.go (the template contains zero hex values; the old check compared an empty set forever). Now points at the real single owner, documents the one surviving hand-mirror (the storefront's TS fallback) with a drift check verified to both pass today and fail on injected drift, and adds a table of the five data-obol="page-*" surfaces.

Tests green (go test ./...); commits signed.

https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk

skillCatalog* named a busybox httpd after the first file it ever held.
It now serves skill.md, services.json (the storefront's own backend via
SERVICES_URL), openapi.json, the API docs, and one bundle per
hostname-bound offer -- including every offer landing page. "Skill
catalog" described one of those.
Two concepts were sharing the name, so this splits them:
skillCatalog* -> staticSite* (the httpd + its ConfigMap)
buildSkillCatalogMarkdown -> buildSkillMarkdown (builds skill.md)
skillCatalog{HowToPay,TryIt,RouteLines} -> skillMarkdown* (its sections)
catalogMu -> staticSiteMu (guards the static-site reconcile)
Identifiers only. Every k8s wire name is untouched -- obol-skill-md,
obol-skill-md-route, obol-catalog-headers and namespace x402 are all
byte-identical, verified by diffing the string literals removed against
those re-added (net zero). Renaming the objects would be a live-cluster
migration and they are referenced from internal/tunnel, cmd/obol,
internal/stackbackup, the embedded x402.yaml and next.config.ts; a
comment at the const block now explains the deliberate mismatch.
ServiceCatalog* (the /api/services.json wire types) keeps its name -- that
one is genuinely a catalog.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
…emplate
The landing page was a backtick literal buried mid-offerbundle.go while all
three x402 pages (payment_required, siwx_challenge, error_page) follow one
convention: templates/<name>.html + //go:embed + <name>HTMLSrc + <name>Tmpl.
It is the surface users actually hit and it was the only one you could not
open as HTML.
Adopts the existing x402 convention rather than inventing a second one:
internal/serviceoffercontroller/templates/offer_landing.html
//go:embed templates/offer_landing.html
var offerLandingHTMLSrc string
Content is byte-identical to the previous literal (verified by extracting
the literal from origin/main and diffing against the new file), so the
rendered page is unchanged.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
… gone
DESIGN.md told the next engineer to hand-mirror the token palette across
three files and gave a drift check to enforce it. Both were stale:
payment_required.html contains zero hex values -- theming moved to
storefront.ResolveTheme(...).CSSVars() via {{.Branding.ThemeCSS}}. The
documented check diffed hex out of that template against globals.css, so
its left side was empty and it compared an empty set forever. Following
the doc would have re-introduced exactly the drift it existed to prevent.
- SS 2: describes the real source (theme.go is the single owner) and says
not to paste hex back in.
- SS 5: points at the one hand-mirror that does survive -- theme.ts's
LIGHT_THEME_VARS, a fallback that rots silently because a healthy page
takes tokens from the feed. The new check compares theme.go's ThemeLight
against it; verified it passes today (13 pairs each side) and verified it
FAILS on injected drift, which the old one could not do.
- SS 0: adds the five public surfaces and their data-obol="page-*" markers.
Nothing else in the repo lists them. Records the two things that are easy
to get backwards: page-402 renders only on a paid path with Accept:
text/html (so a root-priced offer's browser visitors get page-landing,
never page-402), and data-obol="checkout" is a mount div on two pages
rather than a name for the 402 page.
- Corrects "four surfaces" -> five.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
@bussyjd
bussyjd requested review from HananINouman and OisinKyne and removed request for OisinKyneJuly 15, 2026 13:25
@bussyjdbussyjd mentioned this pull request Jul 15, 2026
6 tasks
@bussyjd
bussyjd merged commit a0a8c6e into mainJul 17, 2026
10 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(storefront): page-surface naming cleanup + template convention - #753

Merged
bussyjd merged 3 commits into
mainfrom
chore/page-surface-naming
Jul 17, 2026
Merged

chore(storefront): page-surface naming cleanup + template convention#753
bussyjd merged 3 commits into
mainfrom
chore/page-surface-naming

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Three independent cleanups around the public page surfaces (catalog storefront / per-offer landing / 402 checkout), no behaviour change:

  1. skillCatalog*staticSite* — the busybox httpd named after the first file it ever held now serves skill.md, services.json (the storefront's own backend), openapi.json, the API docs, and every per-offer landing bundle. Go identifiers renamed for what it is; the skill.md builders split out as buildSkillMarkdown/skillMarkdown*. Every k8s wire name is untouched (obol-skill-md, obol-skill-md-route, obol-catalog-headers, namespace x402) — verified by diffing string literals removed vs re-added (net zero); a comment at the const block documents the deliberate identifier/wire-name mismatch and why renaming the live objects is a migration, not a rename.
  2. Landing page template extracted to templates/offer_landing.html + go:embed, adopting the existing x402 convention (payment_required.html et al.) instead of a backtick literal mid-package. Content byte-identical.
  3. internal/x402/templates/DESIGN.md corrected — it documented a hand-mirrored token palette and a drift-check command that had been dead since theming centralized into internal/storefront/theme.go (the template contains zero hex values; the old check compared an empty set forever). Now points at the real single owner, documents the one surviving hand-mirror (the storefront's TS fallback) with a drift check verified to both pass today and fail on injected drift, and adds a table of the five data-obol="page-*" surfaces.

Tests green (go test ./...); commits signed.

https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk

skillCatalog* named a busybox httpd after the first file it ever held.
It now serves skill.md, services.json (the storefront's own backend via
SERVICES_URL), openapi.json, the API docs, and one bundle per
hostname-bound offer -- including every offer landing page. "Skill
catalog" described one of those.
Two concepts were sharing the name, so this splits them:
skillCatalog* -> staticSite* (the httpd + its ConfigMap)
buildSkillCatalogMarkdown -> buildSkillMarkdown (builds skill.md)
skillCatalog{HowToPay,TryIt,RouteLines} -> skillMarkdown* (its sections)
catalogMu -> staticSiteMu (guards the static-site reconcile)
Identifiers only. Every k8s wire name is untouched -- obol-skill-md,
obol-skill-md-route, obol-catalog-headers and namespace x402 are all
byte-identical, verified by diffing the string literals removed against
those re-added (net zero). Renaming the objects would be a live-cluster
migration and they are referenced from internal/tunnel, cmd/obol,
internal/stackbackup, the embedded x402.yaml and next.config.ts; a
comment at the const block now explains the deliberate mismatch.
ServiceCatalog* (the /api/services.json wire types) keeps its name -- that
one is genuinely a catalog.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
…emplate
The landing page was a backtick literal buried mid-offerbundle.go while all
three x402 pages (payment_required, siwx_challenge, error_page) follow one
convention: templates/<name>.html + //go:embed + <name>HTMLSrc + <name>Tmpl.
It is the surface users actually hit and it was the only one you could not
open as HTML.
Adopts the existing x402 convention rather than inventing a second one:
internal/serviceoffercontroller/templates/offer_landing.html
//go:embed templates/offer_landing.html
var offerLandingHTMLSrc string
Content is byte-identical to the previous literal (verified by extracting
the literal from origin/main and diffing against the new file), so the
rendered page is unchanged.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
… gone
DESIGN.md told the next engineer to hand-mirror the token palette across
three files and gave a drift check to enforce it. Both were stale:
payment_required.html contains zero hex values -- theming moved to
storefront.ResolveTheme(...).CSSVars() via {{.Branding.ThemeCSS}}. The
documented check diffed hex out of that template against globals.css, so
its left side was empty and it compared an empty set forever. Following
the doc would have re-introduced exactly the drift it existed to prevent.
- SS 2: describes the real source (theme.go is the single owner) and says
not to paste hex back in.
- SS 5: points at the one hand-mirror that does survive -- theme.ts's
LIGHT_THEME_VARS, a fallback that rots silently because a healthy page
takes tokens from the feed. The new check compares theme.go's ThemeLight
against it; verified it passes today (13 pairs each side) and verified it
FAILS on injected drift, which the old one could not do.
- SS 0: adds the five public surfaces and their data-obol="page-*" markers.
Nothing else in the repo lists them. Records the two things that are easy
to get backwards: page-402 renders only on a paid path with Accept:
text/html (so a root-priced offer's browser visitors get page-landing,
never page-402), and data-obol="checkout" is a mount div on two pages
rather than a name for the 402 page.
- Corrects "four surfaces" -> five.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
@bussyjd
bussyjd requested review from HananINouman and OisinKyne and removed request for OisinKyneJuly 15, 2026 13:25
@bussyjdbussyjd mentioned this pull request Jul 15, 2026
6 tasks
@bussyjd
bussyjd merged commit a0a8c6e into mainJul 17, 2026
10 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

chore(storefront): page-surface naming cleanup + template convention - #753

Merged
bussyjd merged 3 commits into
mainfrom
chore/page-surface-naming
Jul 17, 2026
Merged

chore(storefront): page-surface naming cleanup + template convention#753
bussyjd merged 3 commits into
mainfrom
chore/page-surface-naming

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Three independent cleanups around the public page surfaces (catalog storefront / per-offer landing / 402 checkout), no behaviour change:

  1. skillCatalog*staticSite* — the busybox httpd named after the first file it ever held now serves skill.md, services.json (the storefront's own backend), openapi.json, the API docs, and every per-offer landing bundle. Go identifiers renamed for what it is; the skill.md builders split out as buildSkillMarkdown/skillMarkdown*. Every k8s wire name is untouched (obol-skill-md, obol-skill-md-route, obol-catalog-headers, namespace x402) — verified by diffing string literals removed vs re-added (net zero); a comment at the const block documents the deliberate identifier/wire-name mismatch and why renaming the live objects is a migration, not a rename.
  2. Landing page template extracted to templates/offer_landing.html + go:embed, adopting the existing x402 convention (payment_required.html et al.) instead of a backtick literal mid-package. Content byte-identical.
  3. internal/x402/templates/DESIGN.md corrected — it documented a hand-mirrored token palette and a drift-check command that had been dead since theming centralized into internal/storefront/theme.go (the template contains zero hex values; the old check compared an empty set forever). Now points at the real single owner, documents the one surviving hand-mirror (the storefront's TS fallback) with a drift check verified to both pass today and fail on injected drift, and adds a table of the five data-obol="page-*" surfaces.

Tests green (go test ./...); commits signed.

https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk

skillCatalog* named a busybox httpd after the first file it ever held.
It now serves skill.md, services.json (the storefront's own backend via
SERVICES_URL), openapi.json, the API docs, and one bundle per
hostname-bound offer -- including every offer landing page. "Skill
catalog" described one of those.
Two concepts were sharing the name, so this splits them:
skillCatalog* -> staticSite* (the httpd + its ConfigMap)
buildSkillCatalogMarkdown -> buildSkillMarkdown (builds skill.md)
skillCatalog{HowToPay,TryIt,RouteLines} -> skillMarkdown* (its sections)
catalogMu -> staticSiteMu (guards the static-site reconcile)
Identifiers only. Every k8s wire name is untouched -- obol-skill-md,
obol-skill-md-route, obol-catalog-headers and namespace x402 are all
byte-identical, verified by diffing the string literals removed against
those re-added (net zero). Renaming the objects would be a live-cluster
migration and they are referenced from internal/tunnel, cmd/obol,
internal/stackbackup, the embedded x402.yaml and next.config.ts; a
comment at the const block now explains the deliberate mismatch.
ServiceCatalog* (the /api/services.json wire types) keeps its name -- that
one is genuinely a catalog.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
…emplate
The landing page was a backtick literal buried mid-offerbundle.go while all
three x402 pages (payment_required, siwx_challenge, error_page) follow one
convention: templates/<name>.html + //go:embed + <name>HTMLSrc + <name>Tmpl.
It is the surface users actually hit and it was the only one you could not
open as HTML.
Adopts the existing x402 convention rather than inventing a second one:
internal/serviceoffercontroller/templates/offer_landing.html
//go:embed templates/offer_landing.html
var offerLandingHTMLSrc string
Content is byte-identical to the previous literal (verified by extracting
the literal from origin/main and diffing against the new file), so the
rendered page is unchanged.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
… gone
DESIGN.md told the next engineer to hand-mirror the token palette across
three files and gave a drift check to enforce it. Both were stale:
payment_required.html contains zero hex values -- theming moved to
storefront.ResolveTheme(...).CSSVars() via {{.Branding.ThemeCSS}}. The
documented check diffed hex out of that template against globals.css, so
its left side was empty and it compared an empty set forever. Following
the doc would have re-introduced exactly the drift it existed to prevent.
- SS 2: describes the real source (theme.go is the single owner) and says
not to paste hex back in.
- SS 5: points at the one hand-mirror that does survive -- theme.ts's
LIGHT_THEME_VARS, a fallback that rots silently because a healthy page
takes tokens from the feed. The new check compares theme.go's ThemeLight
against it; verified it passes today (13 pairs each side) and verified it
FAILS on injected drift, which the old one could not do.
- SS 0: adds the five public surfaces and their data-obol="page-*" markers.
Nothing else in the repo lists them. Records the two things that are easy
to get backwards: page-402 renders only on a paid path with Accept:
text/html (so a root-priced offer's browser visitors get page-landing,
never page-402), and data-obol="checkout" is a mount div on two pages
rather than a name for the 402 page.
- Corrects "four surfaces" -> five.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
@bussyjd
bussyjd requested review from HananINouman and OisinKyne and removed request for OisinKyneJuly 15, 2026 13:25
@bussyjdbussyjd mentioned this pull request Jul 15, 2026
6 tasks
@bussyjd
bussyjd merged commit a0a8c6e into mainJul 17, 2026
10 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

chore(storefront): page-surface naming cleanup + template convention - #753

Merged
bussyjd merged 3 commits into
mainfrom
chore/page-surface-naming
Jul 17, 2026
Merged

chore(storefront): page-surface naming cleanup + template convention#753
bussyjd merged 3 commits into
mainfrom
chore/page-surface-naming

Conversation

@bussyjd

Copy link
Copy Markdown
Contributor

Three independent cleanups around the public page surfaces (catalog storefront / per-offer landing / 402 checkout), no behaviour change:

  1. skillCatalog*staticSite* — the busybox httpd named after the first file it ever held now serves skill.md, services.json (the storefront's own backend), openapi.json, the API docs, and every per-offer landing bundle. Go identifiers renamed for what it is; the skill.md builders split out as buildSkillMarkdown/skillMarkdown*. Every k8s wire name is untouched (obol-skill-md, obol-skill-md-route, obol-catalog-headers, namespace x402) — verified by diffing string literals removed vs re-added (net zero); a comment at the const block documents the deliberate identifier/wire-name mismatch and why renaming the live objects is a migration, not a rename.
  2. Landing page template extracted to templates/offer_landing.html + go:embed, adopting the existing x402 convention (payment_required.html et al.) instead of a backtick literal mid-package. Content byte-identical.
  3. internal/x402/templates/DESIGN.md corrected — it documented a hand-mirrored token palette and a drift-check command that had been dead since theming centralized into internal/storefront/theme.go (the template contains zero hex values; the old check compared an empty set forever). Now points at the real single owner, documents the one surviving hand-mirror (the storefront's TS fallback) with a drift check verified to both pass today and fail on injected drift, and adds a table of the five data-obol="page-*" surfaces.

Tests green (go test ./...); commits signed.

https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk

skillCatalog* named a busybox httpd after the first file it ever held.
It now serves skill.md, services.json (the storefront's own backend via
SERVICES_URL), openapi.json, the API docs, and one bundle per
hostname-bound offer -- including every offer landing page. "Skill
catalog" described one of those.
Two concepts were sharing the name, so this splits them:
skillCatalog* -> staticSite* (the httpd + its ConfigMap)
buildSkillCatalogMarkdown -> buildSkillMarkdown (builds skill.md)
skillCatalog{HowToPay,TryIt,RouteLines} -> skillMarkdown* (its sections)
catalogMu -> staticSiteMu (guards the static-site reconcile)
Identifiers only. Every k8s wire name is untouched -- obol-skill-md,
obol-skill-md-route, obol-catalog-headers and namespace x402 are all
byte-identical, verified by diffing the string literals removed against
those re-added (net zero). Renaming the objects would be a live-cluster
migration and they are referenced from internal/tunnel, cmd/obol,
internal/stackbackup, the embedded x402.yaml and next.config.ts; a
comment at the const block now explains the deliberate mismatch.
ServiceCatalog* (the /api/services.json wire types) keeps its name -- that
one is genuinely a catalog.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
…emplate
The landing page was a backtick literal buried mid-offerbundle.go while all
three x402 pages (payment_required, siwx_challenge, error_page) follow one
convention: templates/<name>.html + //go:embed + <name>HTMLSrc + <name>Tmpl.
It is the surface users actually hit and it was the only one you could not
open as HTML.
Adopts the existing x402 convention rather than inventing a second one:
internal/serviceoffercontroller/templates/offer_landing.html
//go:embed templates/offer_landing.html
var offerLandingHTMLSrc string
Content is byte-identical to the previous literal (verified by extracting
the literal from origin/main and diffing against the new file), so the
rendered page is unchanged.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
… gone
DESIGN.md told the next engineer to hand-mirror the token palette across
three files and gave a drift check to enforce it. Both were stale:
payment_required.html contains zero hex values -- theming moved to
storefront.ResolveTheme(...).CSSVars() via {{.Branding.ThemeCSS}}. The
documented check diffed hex out of that template against globals.css, so
its left side was empty and it compared an empty set forever. Following
the doc would have re-introduced exactly the drift it existed to prevent.
- SS 2: describes the real source (theme.go is the single owner) and says
not to paste hex back in.
- SS 5: points at the one hand-mirror that does survive -- theme.ts's
LIGHT_THEME_VARS, a fallback that rots silently because a healthy page
takes tokens from the feed. The new check compares theme.go's ThemeLight
against it; verified it passes today (13 pairs each side) and verified it
FAILS on injected drift, which the old one could not do.
- SS 0: adds the five public surfaces and their data-obol="page-*" markers.
Nothing else in the repo lists them. Records the two things that are easy
to get backwards: page-402 renders only on a paid path with Accept:
text/html (so a root-priced offer's browser visitors get page-landing,
never page-402), and data-obol="checkout" is a mount div on two pages
rather than a name for the 402 page.
- Corrects "four surfaces" -> five.
Claude-Session: https://claude.ai/code/session_014YjPMViNrZ7zBVgUQzwEKk
@bussyjd
bussyjd requested review from HananINouman and OisinKyne and removed request for OisinKyneJuly 15, 2026 13:25
@bussyjdbussyjd mentioned this pull request Jul 15, 2026
6 tasks
@bussyjd
bussyjd merged commit a0a8c6e into mainJul 17, 2026
10 checks passed
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@bussyjd@OisinKyne