Skip to content
kc@kumarChandrachooda.com:~$ cd /design-system/workshop && read --section="01 workshop-editor" 0%
KC Design System — Sketch / Sketch

Rough it out.
Workshop.

The sketch style redraws the system freehand in design-system-sketch.css: handwriting type, wobble borders, sticky-note labels and marker highlights, everything a degree or two off square. Same components, same markup — the notebook the classic skin was ruled up from.

Reading in Sketchbook — sketch / sketch Browse all 8 →

01

The editor

The two-pane arrangement: a form column beside a monospace Markdown column, with the counts under it. Made of Core's form fields and one Workshop-only layout rule. Proves the forms layer at density — nine fields (tags since the agent council of 7 Nov 2026), two fieldsets and a validation summary in a 340px column is the hardest thing the layer has to do, and this is the specimen that will catch a padding change nobody meant.
The hardest thing the forms layer has to do. Eight fields, a grouped choice and a validation summary in a 340px column beside a monospace body pane. If a padding or a label size changes in Core › Form fields, this is the specimen that shows it first — without opening the Workshop, and without a copy of its markup to be wrong.

Front matter

slug — derived ⚠ outbox-without-a-scheduler

tags

Chosen from what exists — a new one is a request, not a text box. 0 chosen. Most posts carry five.

It is the card, the meta description and the OG tag. 56/200

authorKey — from your session ⚠ jo-tan

⚠ is a field the system owns. A request that carries authorKey or state is rejected rather than ignored — there is no property here to bind them to.

Body — Markdown

0 words 0 min read 0 code blocks 0 headings

when would you like this out?

A suggestion — the owner still sets the date. Nothing you can press here writes scheduled or published; those two words appear on this control and nowhere else in your half of the app, and they appear as a request.

Not ready to submit:

  • The body is under the floor of 200 words.

Greying the button out is a courtesy. The endpoint re-checks every one of these before it does anything.

The same form in the states a draft moves through. Ready, with nothing blocking and a date wished for, the submit button armed; submitted, every field disabled, the slug frozen because somebody else is about to read it at that address, and withdraw the one way back; and sent back, with the reviewer's words quoted above it and the form editable again.

Front matter

slug — derived ⚠ outbox-without-a-scheduler

tags

Chosen from what exists — a new one is a request, not a text box. 0 chosen. Most posts carry five.

It is the card, the meta description and the OG tag. 56/200

authorKey — from your session ⚠ jo-tan

⚠ is a field the system owns. A request that carries authorKey or state is rejected rather than ignored — there is no property here to bind them to.

Body — Markdown

0 words 0 min read 0 code blocks 0 headings

when would you like this out?

A suggestion — the owner still sets the date. Nothing you can press here writes scheduled or published; those two words appear on this control and nowhere else in your half of the app, and they appear as a request.

Front matter

slug — derived ⚠ outbox-without-a-scheduler frozen at first submission — a published URL is a promise

tags

Chosen from what exists — a new one is a request, not a text box. 0 chosen. Most posts carry five.

It is the card, the meta description and the OG tag. 56/200

authorKey — from your session ⚠ jo-tan

⚠ is a field the system owns. A request that carries authorKey or state is rejected rather than ignored — there is no property here to bind them to.

Body — Markdown

0 words 0 min read 0 code blocks 0 headings

when would you like this out?

A suggestion — the owner still sets the date. Nothing you can press here writes scheduled or published; those two words appear on this control and nowhere else in your half of the app, and they appear as a request.

Kumar asked for changes 19 Aug 19:02

The second section repeats the first; cut one and the argument is sharper.

It is yours again — edit below and submit when you are ready. Nothing was rejected and nothing was lost.

The head of a one-draft screen, in all five tones, and the timeline under it. The state pill is .ds-state, the record row's own; the author's head carries the preview link and the owner's does not.

Workshop › outbox-without-a-scheduler

Draft preview ↗

Workshop › outbox-without-a-scheduler

In review

Workshop › outbox-without-a-scheduler

Changes requested

Workshop › outbox-without-a-scheduler

Scheduled

Workshop › outbox-without-a-scheduler

Published

Timeline

  1. 19 Aug 14:02

    a blank draft

  2. 19 Aug 15:02

    You submitted this for review.

  3. 19 Aug 19:02

    Kumar asked for changes.

02

The queue and the decision

The record row with a state pill on the right, and the three outcomes beneath it as one form of three radio rows, each with its own field and button. Made of Post cards, Badges & pills, Buttons and the form fields. Proves the five-state scale reads when the five pills are beside each other rather than one per screen, which is the only place their relative weight is visible.
Five states, side by side. On a real screen you see one or two; this is the only place their relative weight is visible, which is what makes it the specimen that catches a colour that has stopped meaning what it meant. The rows are .ds-record from Post cards and the pills are .ds-state from Badges & pills.
The three outcomes are one form of three radio rows (.kc-pub, .kc-pubopt), each with its own field and its own button underneath, because the three outcomes are equals — a primary button and two links would be the screen arguing for one of them. Inside them: the forms layer and .ds-btn.

The post is written at approval and dated ahead, so it appears on its own when the date arrives. Nothing has to be running for that to happen. Written to a local journal only.

The post is written at approval and dated ahead, so it appears on its own when the date arrives. Nothing has to be running for that to happen.

What the decision is made over: the front matter as a dense info list and the body as submitted, both read-only — not by permission, because a correction is the author's to make. And a row the viewer may see but not open, which renders the same row with no link.

Front matter

title
Outbox without a scheduler
slug
outbox-without-a-scheduler
summary
Claiming a batch without two instances fighting over it.
category
Distributed systems
author
Jo Tan jo-tan

Read-only, and not because of a permission. A correction is the author’s to make — asking for it is what the first outcome below is for, and editing somebody’s words on their behalf leaves no trail that it happened.

Body — as submitted

A body long enough to have cleared the gate.
  • A guest post about queues Jo Tan Distributed systems waiting 2 days In review
  • Five modules, one database each Architecture 3 Aug 2026 Published
03

The overview

The section head with a count, the chip filter row and the series arc. Made of Page hero, Buttons & chips and Series map. Proves the series map survives its new modifier — "nobody has taken this part" was added for this app and is rendered on the blog, so the one specimen showing both is here.
The arc with a gap in it. .ds-series-map__item--none is the one state the blog’s map does not have — the blog builds its arc from the posts that exist, and an arc built from a series’ intended shape has gaps in it. The modifier was added for this app and lives in 80-taxonomy.css with the rest of the map, so this specimen and the blog’s are the same component.
Series 1/4 out Aspire in Production
  1. The dashboard you get for free out
  2. Service discovery, and when to stop scheduled
  3. not started open
  4. Outbox without a scheduler draft

Nobody has taken part 3.

Series 1/5 out Microservices with Convey
  1. Why a framework at all out
  2. not started open
  3. Messaging without a broker in review
  4. Sagas, honestly with the author
  5. not started open

Nobody has taken parts 2, 5.

Series 1/4 out Aspire in Production
  1. The dashboard you get for free out
  2. Service discovery, and when to stop scheduled
  3. not started open
  4. Outbox without a scheduler draft

Nobody has taken part 3.

The list that opens the overview: a section head whose count is a consequence of the filter rather than a metric, the state chips that set it, the records, and the two empty states — nothing yet, and nothing in the state a chip picked. Then the topics an author has written in, as tag chips and no text box.

Everything you have touched

0 of 0

Nothing yet.

The blank door is one click away.

Topics you have written in

existing terms only — a new one is a request, not a text box
  • Distributed systems
  • Architecture
  • .NET
04

The gate and the banner

The preview strip — the top strip family's --preview member — as the app that needed it first renders it, for the author and for the owner. The section that documented something built to be deleted, and kept the record: its second half was the interim sign-in screen, which went when real sign-in landed, as this summary said it would.
The preview strip, in the app that needed it first. It is the top strip family's --preview member, documented in Chrome › Top strip family; what is here is the real one, rendered from the preview route’s own partial, so a change to it shows up on the page that uses it rather than only on the page that describes it.
Draft You are previewing an unpublished postoutbox-without-a-scheduler · proposed 19 Sep 2026 copy preview link back to editor
In review You are previewing Jo Tan’s unpublished postoutbox-without-a-scheduler · proposed 19 Sep 2026 copy preview link back to editor
And the screen that was meant to go, went. The second half of this section rendered _SetupForm in two states — the interim sign-in that identified somebody without authenticating them, and its refusal. The note beside it read: “When sign-in lands, this half of the section goes. That is the intended outcome rather than a regression, and it is written here so that whoever deletes it knows it was expected.”

Sign-in landed and BY4 deleted the screen, the form, its cookie and its switch. This paragraph is what is left of it, kept rather than removed, because a section that quietly shrinks is a section whose reason for shrinking is lost — and because it is the one place in the showcase where a prediction about a deletion can be read beside the deletion.
05

The work bar & the screens' furniture

The app's own chrome and what every screen is built from: the work bar's light member for an author on their own work and its dark member for the owner on somebody else's; the screen head with its breadcrumb kicker; the note on a callout, neutral or a refusal; and the foot note. The ground says whose work it is and the badge says who you are — two facts on two channels.
The ground says whose work it is; the badge says who you are. An author on their own drafts gets the light strip — it is their work — and the owner on somebody else's gets the dark member, with the Owner badge either way. Two facts, two channels.
Jo Tan Author overview new blog ↗
Jo Tan Owner reviewingoutbox-without-a-scheduler · submitted 3 days ago overview review blog ↗
Every screen opens the same way: the page hero, a breadcrumb kicker below the overview, a title and one lead. A note on a callout says a fact about the screen — neutral — or a refusal, in the danger tone; the foot note closes a screen with one bold question and its answer.

Your writing

Only your own work is counted here — a draft is the author’s and only the author’s until it is submitted.

Jo Tan

Workshop › review

2 waiting on you

Everything anyone has submitted.

This does not survive a restart yet. Keep anything you would be sorry to lose somewhere else too.

Written somewhere else? Start blank and paste it into the body pane.

06

The ways in

Three doors into one kind of draft — blank, the next part of a series, shaped like an existing post — as three cards of equal weight in one form. A door with nothing behind it is shut and says why rather than disappearing.
Three doors, one room. Blank, the next part of a series, and shaped like an existing post: three cards of equal weight, because they differ only in what they fill in for you. A door with nothing behind it is shut and says why, rather than disappearing.

A blank draft

A title and nothing else. The default, and the one that should be one keystroke away.

The next part of a series

Fills in the series, the part number after the last one, and the category it has been filed under.

Shaped like an existing post

Copies the structure — the taxonomy and the heading skeleton. Never the prose.

A blank draft

A title and nothing else. The default, and the one that should be one keystroke away.

The next part of a series

Fills in the series, the part number after the last one, and the category it has been filed under.

No series yet. This door opens once one exists.

Shaped like an existing post

Copies the structure — the taxonomy and the heading skeleton. Never the prose.

Nothing published to copy the shape of yet.

07

Plan a series

The series planner on the author's own model (_AiSeries, _SeriesPart, and the chain's _ChainStep): the author's model plans the parts, then one worker per part searches the archive. A shared word is not enough: covered and near are overlaps the author judges, clear is clear, and where meaning cannot be asked a part is unjudged. Behind WORKSHOP_ENABLED and AI_ENABLED.
The author's model plans, the site's workers judge. One worker per part searches the archive: covered and near are overlaps the author judges (a link, or posts named), clear is clear, and where meaning cannot be asked a part is unjudged, never flagged on shared words alone. Nothing is kept.
orchestrator · your model · typed SeriesPlan, 5 parts · passed its schema

Event-driven .NET, from outbox to saga

part 1 · worker · unjudged

Why events get lost between the database and the broker

focus: outbox transaction reliabilitykeywords pass (BM25 16.8); meaning could not be asked, so nothing is flagged, named or linkedtags: dotnet, messaging, outbox, reliabilityunjudged
part 2 · worker · unjudged

An outbox table and a relay, in EF Core

focus: outbox relay ef corekeywords pass (BM25 26.7); meaning could not be asked, so nothing is flagged, named or linkedtags: ef-core, dotnet, multi-tenancy, migrationsunjudged
Meaning could not be asked (no model of the vectors’ stamp), so no part is flagged on shared words alone. · 1 call to your model · nothing kept here: copy what you want
orchestrator · your model · typed SeriesPlan, 5 parts · repaired once, passed its schema

Event-driven .NET, from outbox to saga

part 1 · worker · covered: an overlap, offered as a link

Why events get lost between the database and the broker

both searches pick first: The Outbox That Keeps Your Events Honest (BM25 16.8, meaning 0.538); keep the link or drop ittags: dotnet, messaging, outbox, reliabilitycovered · the author judges
part 2 · worker · near: named, not linked

An outbox table and a relay, in EF Core

keywords pick Making EF Core Migrations Schema-Aware, meaning picks The Outbox That Keeps Your Events Honestnear: The Outbox That Commits Somewhere Elsetags: ef-core, dotnet, multi-tenancy, migrationsnear · the author judges
part 3 · worker · clear

EF Core compiled models and startup time

keywords pick Making EF Core Migrations Schema-Aware (BM25 25.5), meaning picks A DB-First Model in Code-First Clothing; no post is in both first fourstags: ef-core, dotnet, multi-tenancy, migrations
1 of 5 parts overlaps an existing post: both searches pick it first, and it is offered as a link for the author to keep or drop. 1 of 5 is near existing posts: they are named, not linked, for the author to judge. · 2 calls to your model
planner · refused before any model is asked

The planner refused the topic or the count, so no model was asked.

no topic, so nothing was asked
orchestrator · your model · refused after one repair

Your model’s plan broke its schema twice, so no worker ran. Ask again, or try another model.

parts: 4 items, expected 5
connection · none granted to the planner

No connection in this tab is granted to the planner. Connect one at /ai/connect.

Markup

As rendered on the page behind this — captured, not transcribed.


    

Which CV would you like?

Pick the one closest to the role. Each is two pages and carries the same facts.

Applied AI

Agentic AI & Cloud Engineer · multi-agent systems, RAG and MCP on AWS

Cloud & .NET

C#, .NET 10 and ASP.NET Core microservices on AWS and Azure · Terraform & AWS CDK

Data Engineering

AWS lakehouse · Kinesis, Glue (PySpark), Apache Iceberg, Lake Formation

Everything

All three domains, with the standout project from each