Anthony: "this needs a cookie crumb navbar, all pages need this with the
new IA" and "broad and drill down, I'm not seeing that in the sidebar".
lib/crumbs.ts derives the trail from the path and the spec registry, so
no page declares it: /openthreat is Home > Specs > Catalogs a site serves
about itself > OpenThreat, /opencpu adds OpenServer before OpenCPU,
/docs/openthreat ends in Specification, /docs/cli is Home > Docs > CLI,
a blog post passes its title as the leaf. components/breadcrumbs.tsx
renders it (server-rendered, with a BreadcrumbList JSON-LD) at the top
of every SiteShell page; the SPA routes rendered by page-markup.ts get
the same trail as a string.
components/side-nav.tsx replaces the flat sidebar: the four groups stay,
and under Specs the family the current page belongs to unfolds to its
specs, and the spec to its blocks, marked active. The home page string
marks the active entry for the SPA routes too.
Claude-Session: https://claude.ai/code/session_014cmNRtR2vL1p89dbVQ7FZJ
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Anthony: "that site needs better information architecture, it's impossible
to find anything", "start broad in sidebar and drill down with dedicated
pages, not all one page", and "I see none of our specs" on the home page.
One registry, lib/specs.ts, now lists every specification in four
families (people and agents; access and credentials; catalogs a site
serves about itself; agents and process), with a landing path, a
specification path and, for OpenServer's blocks, a parent. Everything
that lists specs reads it: the sidebar (lib/nav.ts, four groups: Start,
Specs, Tools, Company, rendered by SiteShell and by the home page string
from the same array), /specs and /specs/<family>, the home page's
Standards Surface grid (families with their specs, replacing the five
abstract primitives), /docs (grouped by family, then guides), the sitemap
and llms.txt. DOC_SLUGS is derived from the registry. Adding a spec is
one entry plus its files; the four hand-kept lists are gone.
Claude-Session: https://claude.ai/code/session_014cmNRtR2vL1p89dbVQ7FZJ
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
The CLI is the product, and the way you get it was nowhere on the site. You
had to already know the URL of a script served out of public/.
Two placements, one command:
- The homepage hero gets the loud version, directly under the lede and above
the fold -- a bordered dark panel, the command at full size, Copy alongside.
- Every page carries a compact version in the rail, between the brand and the
nav. Present on arrival, never competing with navigation.
Both come from renderInstallCommand() in one module. The homepage builds its
HTML as a string and the rest of the site is JSX, which is precisely the shape
that lets one copy of a command drift while the other stays right -- so there
is one definition and SiteShell renders it rather than restating it.
The command keeps its flags: `curl -fsSL`. Without -f, curl prints an HTTP
error body and still exits 0, so a 404 gets piped into sh; without -L the
install breaks the first time the URL redirects. This is the form install.sh
already documents in its own header.
Copy is one delegated listener on document for any [data-copy] button, mounted
site-wide in the layout. Delegation because the two placements arrive by
different rendering paths and a document listener does not care which; it also
means the next copy button needs the attribute and no wiring. It falls back to
a throwaway textarea + execCommand outside a secure context, where
navigator.clipboard is simply undefined, so the button never no-ops silently.
The contract tests pin the command, both placements, and that the clipboard
payload equals the visible text -- a Copy button that hands over something
other than what is on screen is worse than no button. They also read
public/install.sh and assert it is #!/bin/sh and documents this exact command,
so `| sh` cannot quietly become a lie.
apps/logicsrc-web: 13 new tests pass, 46 total. The ontology-api contract file
fails to resolve @logicsrc/validators, which it also does on a pristine
origin/master -- unbuilt workspace package, unrelated to this change.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
* feat(web): move Hire Us pricing to $400/hour metered billing (PRD 0002)
Replaces the $250/week retainer with a $400/hour rate billed against actual
hours, invoiced through CoinPay after the client approves them. A 10-hour
minimum engagement replaces the week as the unit of commitment.
The weekly price lived in 12 places, not the 3 the PRD listed: the front-page
Hire Us section, the Top-Level Pages list, /hire-us metadata, /pricing
(metadata, two FAQ answers, rate bullet), /about, llms.txt, skill.md, and the
Hire Us form success message.
Metered billing rather than a committed weekly block, because the old
"recurring CoinPay invoice" copy documented a mechanic that never existed:
/api/payments/create makes a single one-shot payment, not a subscription.
- coinpay-checkout derives amount_usd from hours x 400 instead of a hardcoded
250, validates hours as quarter-hour increments at or above the minimum, and
returns 422 before calling CoinPay on bad input. Payment metadata carries
billing/hours/rate_usd_per_hour in place of interval.
- project-request returns a rate, billing mode, and minimum; no amount exists
until hours are approved.
- CoinPay config block documents COINPAY_RATE_USD_PER_HOUR / COINPAY_BILLING /
COINPAY_MINIMUM_HOURS instead of a weekly amount and interval.
- New real /terms route replacing the SPA stub: what is billable, the
approve-then-invoice flow, the minimum, cancellation on one week's notice,
and an explicit clause that existing engagements keep their terms until both
sides agree in writing.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
* fix(mcp): advance prd_next_id expectation to 0003 for PRD 0002
The standards test asserts prd_next_id against the live prd/ directory, so
adding prd/0002-hourly-hire-us-rate.md moves the next free id to 0003. This
assertion advances with every PRD added to the repo.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Only the .rail sidebar is dark; the .workspace content area is on the
light (#f6f7f4) page background. The previous blog styling assumed a dark
workspace, so text was light-grey on white (unreadable) and the link
green was too light.
- Darken the global link color to #0a7d59 (readable on white); content
links only — rail nav stays inherited.
- Repaint .blog-content (post HTML) for a light surface: dark body text,
light code/pre, light borders.
- Blog index/post: dark titles, readable grey meta, light row borders;
light-themed footer.
- Add post thumbnails to the /blog index from featured_image.url.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Add SiteShell (rail nav + workspace + footer) and wrap /blog and
/blog/[slug] in it so they share the site's dark chrome instead of
rendering as bare standalone pages.
- Style rendered post HTML (.blog-content) for the dark workspace.
- Links were `color: inherit` everywhere, so content-area links matched
body text and were invisible. Give links a distinct accent (#5ac8a6);
keep the rail nav and buttons on their own colors.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>