From a0980d15a9bfbc8254892e26fedfb36125f963d6 Mon Sep 17 00:00:00 2001 From: Charles Green Date: Sun, 25 Oct 2026 14:28:27 +0900 Subject: [PATCH 1/4] Fix product-card link styles and define compare-and-swap. Bare inherited the browser's visited colour and underline, so every line on /products/ looked like a link. Treat card-as-link as a flex card with body colour and the status pinned to the bottom. Spell out CAS on /sync/. --- content/sync.md | 8 ++++++++ data/sync.yaml | 4 ++-- static/css/main.css | 25 +++++++++++++++++++++++++ static/css/sections.css | 7 ++++++- 4 files changed, 41 insertions(+), 3 deletions(-) diff --git a/content/sync.md b/content/sync.md index 8b33b93..4103694 100644 --- a/content/sync.md +++ b/content/sync.md @@ -14,6 +14,14 @@ The default store is in memory and is lost on restart. Set `SYNC_S3_BUCKET` (and Sync does not run agents, merge pull requests, or execute jobs. Workers that claim a job are what run the payload. +## Compare-and-swap + +CAS is compare-and-swap. Sync keeps JSON documents, not rows in a SQL database. Each document has an ETag, a version stamp from the last write. + +An update sends that stamp as `If-Match`. The write succeeds only if the document is still the version you read. If another worker wrote first, Sync returns `412` and you read again. + +That is how two agents can share a session or claim a job without silently overwriting each other. + ## MCP companion MCP clients should not call that HTTP API themselves. [sync-mcp](https://github.com/aixgo-dev/sync-mcp) is a stdio MCP server that wraps Sync and exposes sessions, messages and jobs as tools. diff --git a/data/sync.yaml b/data/sync.yaml index 323a000..ff33d92 100644 --- a/data/sync.yaml +++ b/data/sync.yaml @@ -16,7 +16,7 @@ hero: stats: - value: "CAS" - label: "JSON objects with If-Match / ETag" + label: "Compare-and-swap: a write is rejected if the document changed since you read it" accent: true - value: "org/project" label: "Tenant path and API-key scope" @@ -51,7 +51,7 @@ cannot: roadmap: - version: "v0.1.0" state: "done" - item: "CAS store, tenant keys, HTTP sessions, messages and jobs" + item: "Compare-and-swap store, tenant keys, HTTP sessions, messages and jobs" - version: "next" state: "progress" item: "Desired-state GET/PUT, session attach, device register" diff --git a/static/css/main.css b/static/css/main.css index 919e17b..43adb45 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -459,6 +459,31 @@ color: var(--ax-text-muted); } +/* Card-as-link. Guides already opted in via .ax-listcard. Product and + homepage sibling cards were bare , so the browser + painted every heading, paragraph and status line as a visited link. */ +a.ax-card { + display: flex; + flex-direction: column; + color: inherit; + text-decoration: none; +} + +a.ax-card:visited { + color: inherit; +} + +a.ax-card:focus-visible { + outline: 2px solid var(--ax-accent); + outline-offset: 3px; +} + +a.ax-card .ax-microlabel:last-child { + margin-top: auto; + padding-top: var(--ax-s-5); + color: var(--ax-text-dim); +} + /* Buttons. */ .ax-btn { display: inline-flex; diff --git a/static/css/sections.css b/static/css/sections.css index 7b74e93..b1e6529 100644 --- a/static/css/sections.css +++ b/static/css/sections.css @@ -953,10 +953,15 @@ .ax-guidegroup .ax-grid { margin-top: var(--ax-s-5); } .ax-listcard { - display: block; + display: flex; + flex-direction: column; text-decoration: none; } +.sibling-card { + min-height: 11.5rem; +} + .ax-listcard__meta { margin-bottom: var(--ax-s-3); } /* Tags are metadata, not the argument, so they take the border treatment From 64f78d024690f1e6ba8cc06f44b043beab827ff9 Mon Sep 17 00:00:00 2001 From: Charles Green Date: Sun, 25 Oct 2026 14:34:55 +0900 Subject: [PATCH 2/4] Use the existing list-card link pattern on product cards. The earlier pass added flex, min-height, and a focus ring. Those were new design. Guides already use ax-listcard for text-decoration: none; the product and homepage sibling cards now do the same. --- layouts/_default/products.html | 4 ++-- layouts/index.html | 2 +- static/css/main.css | 25 ------------------------- static/css/sections.css | 7 +------ 4 files changed, 4 insertions(+), 34 deletions(-) diff --git a/layouts/_default/products.html b/layouts/_default/products.html index d67d46a..9e8270b 100644 --- a/layouts/_default/products.html +++ b/layouts/_default/products.html @@ -25,13 +25,13 @@

{{ .Title }}

- +

Framework

The Go framework for building agents. One module, one binary.

MIT

{{- range hugo.Data.products }} - +

{{ .name }}

{{ .description }}

{{ .status }}

diff --git a/layouts/index.html b/layouts/index.html index e02e99d..1f04f44 100644 --- a/layouts/index.html +++ b/layouts/index.html @@ -206,7 +206,7 @@

Aixgo builds agents. Code, Aixgate and Sync handle the rest.

{{- range hugo.Data.products }} - +

{{ .name }}

{{ .description }}

{{ .status }}

diff --git a/static/css/main.css b/static/css/main.css index 43adb45..919e17b 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -459,31 +459,6 @@ color: var(--ax-text-muted); } -/* Card-as-link. Guides already opted in via .ax-listcard. Product and - homepage sibling cards were bare
, so the browser - painted every heading, paragraph and status line as a visited link. */ -a.ax-card { - display: flex; - flex-direction: column; - color: inherit; - text-decoration: none; -} - -a.ax-card:visited { - color: inherit; -} - -a.ax-card:focus-visible { - outline: 2px solid var(--ax-accent); - outline-offset: 3px; -} - -a.ax-card .ax-microlabel:last-child { - margin-top: auto; - padding-top: var(--ax-s-5); - color: var(--ax-text-dim); -} - /* Buttons. */ .ax-btn { display: inline-flex; diff --git a/static/css/sections.css b/static/css/sections.css index b1e6529..7b74e93 100644 --- a/static/css/sections.css +++ b/static/css/sections.css @@ -953,15 +953,10 @@ .ax-guidegroup .ax-grid { margin-top: var(--ax-s-5); } .ax-listcard { - display: flex; - flex-direction: column; + display: block; text-decoration: none; } -.sibling-card { - min-height: 11.5rem; -} - .ax-listcard__meta { margin-bottom: var(--ax-s-3); } /* Tags are metadata, not the argument, so they take the border treatment From a81a687d0e655abf7973d3681935847fad3ead0f Mon Sep 17 00:00:00 2001 From: Charles Green Date: Sun, 25 Oct 2026 14:37:51 +0900 Subject: [PATCH 3/4] Retrigger Cloudflare Pages preview. The previous Pages check failed instantly after a follow-up push. Lint was green. Empty commit to get a new preview build. From 16c55bcee5815bc04166805002206b0ee7dd9dc2 Mon Sep 17 00:00:00 2001 From: Charles Green Date: Sun, 25 Oct 2026 14:48:48 +0900 Subject: [PATCH 4/4] Wrap hero titles and inherit list-card colour. text-wrap: balance plus overflow:hidden on the hero can clip a short title on a narrow viewport. List cards inherit colour so a visited link cannot leak through an unstyled child. --- static/css/main.css | 1 + static/css/sections.css | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/static/css/main.css b/static/css/main.css index 919e17b..1c13127 100644 --- a/static/css/main.css +++ b/static/css/main.css @@ -431,6 +431,7 @@ border-radius: var(--ax-r-lg); padding: var(--ax-s-5); box-shadow: var(--ax-e-1); + overflow-wrap: break-word; transition: border-color var(--ax-dur-1) var(--ax-ease), box-shadow var(--ax-dur-2) var(--ax-ease), diff --git a/static/css/sections.css b/static/css/sections.css index 7b74e93..07643c7 100644 --- a/static/css/sections.css +++ b/static/css/sections.css @@ -152,6 +152,10 @@ letter-spacing: -0.028em; line-height: 1.04; color: var(--ax-text); + /* Global h1 uses text-wrap: balance, which will keep a short title on + one line even when it overflows. The hero clips overflow, so wrap. */ + text-wrap: wrap; + overflow-wrap: break-word; } .ax-hero .ax-rule { margin-top: 1.25rem; } @@ -955,6 +959,7 @@ .ax-listcard { display: block; text-decoration: none; + color: inherit; } .ax-listcard__meta { margin-bottom: var(--ax-s-3); }