Skip to content

Product Design · Human & Agent Interaction

Bryan V.B. Nee-Darko

I don’t hand over slides. I build the thing — deployed, testable.

Four projects. Each one answers the same question in a different domain: where does a person stay in the loop, and where has an agent earned the right to act alone? Two of them are running. Two are concepts, and they are labelled as concepts.

Accessibility · Agent system

Concept — no shipped prototype yet

The Accessibility Agent and the Agent Factory

Someone reads the product once, writes it up, and the write-up starts going stale the same week. That is what accessibility work usually looks like. An agent could run continuously instead — but only across the part of WCAG that is actually machine-decidable. Drawing that line is the whole design problem, so I drew it before building anything: all 55 criteria, classified by what an agent can be trusted to decide.

Context

WCAG 2.2 at levels A and AA is 55 success criteria. They are usually treated as one undifferentiated checklist, which is what makes accessibility feel like an unbounded chore.

They are not one kind of thing. Some — colour contrast, name-role-value, page language — are fully decidable by a machine. Some can be detected but not judged: a tool can find an image with no alt text, but it cannot tell you whether the alt text that is there is any good. And some are pure judgement: meaningful sequence, consistent identification, error suggestion.

An agent built without that distinction will mark a page as passing when it is not — and a false pass is worse than no audit at all, because a gap eventually gets found while a false pass just gets trusted.

My role

Sole author of the concept and of the artefact below. This is my own work, unshipped.

The coverage matrix is the deliverable I would take into a build. It defines the agent's permission boundary before a line of the agent exists.

Interaction model

Human

Owns judgement and intent.

  • Decides what the product is supposed to communicate — the input no checker has.
  • Rules on every criterion classified as manual.
  • Reviews and accepts or rejects each partial finding.

Handoff

The matrix is the contract.

  • full — the agent may detect and fix without asking.
  • partial — the agent may detect and draft, but must return it flagged.
  • manual — the agent may only route it to a person, with context. It may never mark it passed.

Agent

Runs continuously, inside the boundary.

  • Checks on every commit rather than once per quarter.
  • Proposes patches as reviewable diffs, never silent edits.
  • Writes the evidence trail: what was checked, what was decided, by whom.

The artefact

WCAG 2.2, levels A and AA. Filter, search and sort it.

Those three figures are out of 56 classified paths, not 55 criteria. Where the honest answer depends on the situation, a criterion is classified once per scenario and therefore appears in more than one class.

Level
Automatability

Showing 55 of 55 criteria.

55 of 55 classifications reviewed.

WCAG 2.2 levels A and AA. All 55 success criteria, classified by how much of each one an agent can decide without a person. 56 rows in total: a criterion whose answer depends on the situation is split into scenario rows that share one criterion number.
Why
1.1.1Non-text ContentAPartialDetecting a missing alt attribute is trivial. Judging whether the alt text conveys the same purpose is not.
1.2.1Audio-only and Video-only (Prerecorded)APartialThe presence of an alternative can be checked. Its equivalence cannot.
1.2.2Captions (Prerecorded)APartialA caption track's presence and timing are checkable. Accuracy needs a person.
1.2.3Audio Description or Media Alternative (Prerecorded)AManualRequires watching the content and deciding what visual information a listener loses.
1.2.4Captions (Live)AAManualLive output cannot be evaluated before it exists.
1.2.5Audio Description (Prerecorded)AAManualRequires judging what a sighted viewer receives that a listener does not.
1.3.1Info and RelationshipsAPartialStructural markup is inspectable. Whether it matches the visual grouping is a judgement.
1.3.2Meaningful SequenceAManualDOM order is readable, but meaningful is defined by the content's intent.
1.3.3Sensory CharacteristicsAManualRequires reading instructions in prose and deciding what they rely on.
1.3.4OrientationAAFullOrientation locks are detectable in CSS and in the web app manifest.
1.3.5Identify Input PurposeAAPartialAutocomplete tokens that are present can be validated. Detecting a field that should carry one but doesn't needs a person to know the field's purpose.
1.4.1Use of ColorAPartialColour-only differences can be flagged heuristically. Confirming a second cue exists needs review.
1.4.2Audio ControlAPartialAuto-playing audio over three seconds is detectable, and the common case — no autoplay — passes. But whether the pause or stop control actually works and is reachable needs verification, the same limit that puts 2.2.2 at partial.
1.4.3Contrast (Minimum)AAFullComputable from rendered colour values. The archetype of a fully automatable criterion.
1.4.4Resize TextAAFullTestable by zooming to 200% and checking for loss of content or function.
1.4.5Images of TextAAPartialText in images is detectable by OCR. The logotype exception needs a person.
1.4.10ReflowAAFullTestable at 320 CSS pixels by checking for two-dimensional scrolling.
1.4.11Non-text ContrastAAPartialComputable once you know which boundaries are meaningful. Deciding that is the hard part.
1.4.12Text SpacingAAFullApply the specified overrides, then check for clipping or overlap.
1.4.13Content on Hover or FocusAAPartialHover content is detectable. Dismissible, hoverable and persistent each need interaction.
2.1.1KeyboardAPartialFocusability is testable. Complete task coverage needs scripted flows a person wrote.
2.1.2No Keyboard TrapAFullKept full: a keyboard trap is a local property — enter a component, confirm you can leave — deterministic wherever focus reaches. The partial case is coverage: an automated pass only proves no trap was found in what it reached, not that none exists, the same limit as 2.1.1. Classified full because that coverage limit is general to all automated testing, whereas 2.1.1 additionally needs authored task flows.
2.1.4Character Key Shortcuts2 scenariosANo shortcut presentFullIf the page defines no single-character key shortcut, the criterion passes — and the absence of such handlers is statically decidable.
Shortcut presentPartialOnce a single-character shortcut exists, the pass condition is the turn-off, remap or focus-limit mechanism. Detecting the handler is easy; confirming the mitigation needs review.
2.2.1Timing AdjustableAPartialTimers are detectable. Whether one of the exceptions applies is not.
2.2.2Pause, Stop, HideAPartialMoving content is detectable. Whether the control actually works needs verification.
2.3.1Three Flashes or Below ThresholdAPartialAnalysable frame by frame for video. Not for arbitrary scripted animation.
2.4.1Bypass BlocksAFullA skip link or a landmark structure is either present and functional, or it is not.
2.4.2Page TitledAPartialPresence is trivial. Whether the title describes the page is a judgement.
2.4.3Focus OrderAPartialTab order can be recorded. Whether it preserves meaning cannot be measured.
2.4.4Link Purpose (In Context)APartialEmpty and generic link text can be flagged. Sufficiency in context cannot.
2.4.5Multiple WaysAAPartialThe presence of search or a site map is detectable. Sufficiency is not.
2.4.6Headings and LabelsAAManualDescriptiveness cannot be measured, only judged against what the content is about.
2.4.7Focus VisibleAAPartialWhether something changes on focus is detectable. Whether the indicator is sufficiently visible — adequate contrast and area — is closer to judgement, and a weak indicator is exactly what detection misses.
2.4.11Focus Not Obscured (Minimum)AAFullTestable: focus each element and check whether anything covers it.
2.5.1Pointer GesturesAManualRequires knowing which interactions are path-based or multipoint in the first place.
2.5.2Pointer CancellationAPartialDown-event handlers are detectable. The intent behind them is not.
2.5.3Label in NameAFullComparing visible text against the accessible name is mechanical.
2.5.4Motion ActuationAPartialMotion event listeners are detectable. An equivalent control needs checking.
2.5.7Dragging MovementsAAPartialDrag handlers are detectable. Whether a single-pointer alternative exists needs checking.
2.5.8Target Size (Minimum)AAFullBounding boxes and spacing are measurable from the rendered page.
3.1.1Language of PageAFullThe lang attribute is present and valid, or it is not.
3.1.2Language of PartsAAPartialLanguage changes are detectable by language identification, with varying confidence.
3.2.1On FocusAPartialA context change on focus is observable in a scripted pass, within the coverage that pass has.
3.2.2On InputAPartialSame mechanism as On Focus, with the same limit on coverage.
3.2.3Consistent NavigationAAFullComparable across pages once the navigation region is identified.
3.2.4Consistent IdentificationAAPartialComparable by function — but establishing function needs a person.
3.2.6Consistent HelpAPartialPosition is comparable across pages once the help mechanism has been identified by a person.
3.3.1Error IdentificationAPartialError output can be detected. Whether it identifies the item in error needs reading.
3.3.2Labels or InstructionsAPartialMissing labels are detectable. Sufficiency of instructions is not.
3.3.3Error SuggestionAAManualWhether a suggestion is actually useful is a judgement about the domain.
3.3.4Error Prevention (Legal, Financial, Data)AAManualRequires knowing whether the transaction is legal, financial or data-modifying.
3.3.7Redundant EntryAManualRequires understanding what information the user already supplied earlier in the process.
3.3.8Accessible Authentication (Minimum)AAPartialCognitive function tests are often detectable. The exceptions need review.
4.1.2Name, Role, ValueAPartialMissing names and invalid roles are detectable. Whether the role is the right one is a judgement.
4.1.3Status MessagesAAPartialLive regions are detectable. Whether every status is announced is not.

Agent Factory

How the classification becomes a permission boundary.

  1. Step 01

    Specification

    The coverage matrix is the input, not documentation about the input. Each of the 55 criteria carries its permission class, and that class is what the factory reads.

  2. Step 02

    Factory

    One narrow agent is generated per criterion from a shared template: a detector, a patcher, and an evidence writer. A criterion classified manual gets a detector and a router — no patcher is generated at all.

  3. Step 03

    Fleet

    The agents run per commit rather than per quarter. Each one has a single testable definition of correct, so a regression points at one agent instead of at accessibility in general.

What each class is allowed to do

Full
Opens a pull request with the fix and the evidence.
Partial
Opens a draft, flagged for review. Never merges itself.
Manual
Routes to a named person with context. Cannot record a pass.

The permission class is enforced by what gets generated, not by an instruction telling the agent to behave. An agent for a manual criterion has no code path that can mark it passed.

Decisions

Classify conservatively. When a criterion is arguable, it drops to manual.

Because
A false pass on accessibility is more damaging than a gap, because the gap gets fixed and the false pass gets trusted.
What it costs
Lower automation coverage and more human hours than an optimistic classification would claim.

An Agent Factory of narrow, single-criterion agents rather than one general accessibility agent.

Because
A narrow agent has a testable definition of correct. A general one only has an opinion.
What it costs
More surface to build and maintain, and an orchestration layer that a single agent would not need.

Result

Artefact
All 55 criteria classifiedFilterable, sortable, and on this page — not in a PDF.
Status
ConceptNo prototype has been built. Said here rather than implied.

Complex desktop product · Live

Live — running, in use, embedded below

ICS Playlist Studio, and the discipline of a read-only mode

Ten minutes before a class, an instructor is still assembling the set. Ninety seconds into it, they are riding it live, with a room watching. Generating a good playlist for indoor cycling turned out to be straightforward. Serving the same person in both of those states — authoring and operating — did not.

Context

Indoor Cycling Stuttgart runs strength rides: music mapped to a ride structure, with cadence, intensity blocks and leadership cues. Building one by hand is an hour of work.

The operating conditions are the real constraint. Instructors finish a set minutes before a class, in a studio, on a phone, with a room filling up. Then the class starts and the context inverts: they are no longer editing, they are executing, in front of twenty people, with no attention to spare.

A single interface that serves both states serves neither.

My role

Product design and build. Sole designer and engineer.

Responsible for the generation model, the studio interface, the Ride Mode split, and the deployment you can open below.

Interaction model

Human

Stays the author.

  • Sets the ride intent: type, structure, duration, the cues that make it theirs.
  • Edits every generated block. The draft is a starting point, not a verdict.
  • Decides when the set is finished and the ride begins.

Handoff

Two seams, not one.

  • Generation → authoring: the system returns a complete but fully editable draft. It never publishes on its own.
  • Authoring → Ride Mode: crossing this seam is a deliberate act, and it removes every editing capability.
  • Ride Mode cannot write. The write operations are not present in that mode — not merely disabled.

Agent

Proposes structure, never commits it.

  • Maps ride structure to track selection and block timing.
  • Produces a coherent whole rather than a list of suggestions.
  • Has no authority over the live class. In Ride Mode it is simply absent.

Live demo

The read-only Ride Mode deployment, embedded.

The demo is a full application. It is loaded on request rather than on page load, so embedding it does not cost this page the performance it claims. It opens in read-only Ride Mode.

This is the real deployment, not a recording. Ride Mode has no write capability, which is what makes it safe to hand to a stranger. The accessibility of the embedded application is outside this page’s control — if the frame does not load, use the direct link.

Decisions

Ride Mode is strictly read-only. No edit, no save, no delete — the operations do not exist in that mode.

Because
During a class, an accidental touch isn't a minor annoyance — it's a visible failure in front of a room of people who paid to be there, at the one moment the instructor can't look down and recover.
What it costs
An instructor who notices a mistake mid-ride genuinely cannot fix it in place. They have to leave Ride Mode to do it.

Accept that cost rather than adding a confirmation dialog.

Because
A confirm step turns a hard guarantee into a soft one, and under pressure people dismiss dialogs without reading them. Leaving a mode is a conscious act; a mis-swipe is not.
What it costs
The recovery path is slower than it could be, and that is the point.

Result

In use
Prep collapsed to secondsPlaylist, movements and live delivery in one — each track cued about five seconds before it plays, at the same quality every ride.
Status
LiveThe embed below is the real deployment, in read-only Ride Mode.

Luxury hospitality · Governance

Concept — governance model, unbuilt

Concierge OS and the CodeKeeper

A luxury property trades on its register — a particular way of saying things no competitor quite matches. A language model erodes exactly that, drifting toward generic politeness until every property sounds like every other one. Making the model more capable does not help. Holding it in character over months, without stationing a person in front of every guest, is what this system is built around.

Context

What a luxury property sells is specificity — a particular register, a particular set of things it would and would not say. That register is the asset.

An assistant trained toward helpfulness drifts toward the mean of all hospitality writing. The drift is slow, individually invisible, and nobody notices until the property no longer sounds like itself.

Reviewing every response would catch it, and would also destroy the latency that makes an assistant worth having. Reviewing nothing lets it compound.

My role

Sole author. This is a governance model, not a shipped product.

The contribution is the third option between per-request review and no review at all.

Interaction model

Human · staff

Keeps the moments that carry the reputation.

  • Handles arrival, recovery, and anything with emotional weight.
  • Is never the bottleneck for a room-service timing question.

CodeKeeper

Outside the request path, inside the calibration path.

  • Holds the house DNA — the written definition of how this property speaks.
  • Samples output on a cadence and compares it against that definition.
  • Adjusts prompts, examples and boundaries. Does not answer guests.

Agent

Absorbs volume.

  • Logistics, timing, coordination, recall of stated preference.
  • Operates within the register the CodeKeeper maintains.
  • Escalates on emotional weight rather than on confidence score.

Governance

Where the CodeKeeper sits, and where it deliberately does not.

Request path — milliseconds, no human

  1. Guest

    Asks for something. Expects an answer, not a queue.

  2. Agent

    Answers within the register it has been given.

  3. Response

    Goes out immediately. No human sits in this path.

Calibration path — the CodeKeeper

  1. Sample

    The CodeKeeper reads a slice of what actually went out, on a cadence.

  2. Compare

    Against the house DNA — the written definition of how this property speaks.

  3. Adjust

    Prompts, examples and boundaries move. The guest never waited for this.

The loop closes back onto the agent, never onto the guest. A CodeKeeper who starts answering guests has stopped doing the job.

Decisions

The CodeKeeper sits outside the request loop and calibrates in batch, not per response.

Because
Per-request human review does not scale and removes the speed that justifies the system. No review at all lets drift accumulate silently. Calibration is the only version that survives contact with volume.
What it costs
Drift is caught late — a batch of responses can go out slightly off-register before the next calibration pass catches it.

Name it as a role a person holds, not as an automated eval.

Because
The thing being protected is taste. An automated check can only measure consistency with yesterday, which is exactly how a system drifts steadily in one direction.
What it costs
It is a standing headcount commitment, and the model depends on that person's judgement.

Result

Artefact
Governance model and role definitionThe feedback loop is diagrammed below.
Status
ConceptMine alone. Nothing has been built.

Generative image production · Working

Working system — pipeline in use

Northlight, or why the shoot became optional

Every variant of a product image used to cost another location, another crew and another day. Northlight replaces that with a Node.js pipeline on fal.ai, Blender scenes as geometric reference, and a structured split between cheap probing and expensive production. For a large class of product imagery, the shoot stops being necessary.

Context

Brand and product imagery is produced through shoots: location, crew, set, schedule. Every variant — another angle, another colourway, another season — costs another setup.

Generative models can produce the image. What they cannot do unsupervised is produce the same product, at the same angle, under the same light, two hundred times. Consistency is the entire job, and it is the thing prompting is worst at.

The design work, then, is subtractive: deciding how much of the model's freedom to remove.

My role

Built the system. Pipeline architecture, the Blender reference method, and the probe/production structure.

Working and in use — the twelve frames below are real output from it, not mock-ups.

Interaction model

Human

Art direction, upstream.

  • Decides the frame, the light and what correct looks like.
  • Builds or selects the Blender reference that fixes camera and geometry.
  • Judges the probe pass and chooses what earns a production render.

Handoff

The Blender scene is the contract.

  • Fixed by the reference: camera, framing, proportion, light direction.
  • Free for the model: surface, material response, atmosphere, grain.
  • The boundary is geometric, so it can be checked rather than argued about.

Agent

Generates at two tiers.

  • Probe: cheap, wide, many variants — used to find the direction.
  • Production: narrow, expensive, final — run only on approved probes.
  • Both are reproducible from the scene file, not from a remembered prompt.

The series

Twelve production frames. Pick one to see it large.

A man works standing at the desk in a timber-clad study, laptop open, tall windows onto a garden casting hard afternoon light across the floor.
Timber studyEditorial, single occupant

One product, one geometry, twelve rooms. Every frame here would have been a separate shoot — a separate location, set, crew and day. None of them was photographed.

Decisions

Constrain every generation with an explicit 3D reference instead of prompting for composition.

Because
A brand needs the same product to look like the same product across the whole set. Reproducibility is worth more than a good accident.
What it costs
Setup time per scene, and the genuinely surprising results that unconstrained prompting sometimes produces are ruled out.

Split every job into a probe pass and a production pass.

Because
Judgement should be spent on cheap output. Money should be spent only on output that has already been judged.
What it costs
An extra step in every job, and a discipline the operator has to hold to when they are in a hurry.

Result

Turnaround
Seconds per shot, not a shootOnce a scene is set, a new situation renders in seconds — no location, crew or reshoot.
Status
WorkingPipeline runs. Twelve production frames below.

In closing

This page is the argument

A deck can describe an interaction model. It cannot be operated. Everything on this page that claims to work can be used right now — filter the matrix, load the live product, open any frame in the series. Nothing is a screenshot of something that exists elsewhere.

The same standard applies to what isn't finished. Two of these four are concepts. They say so at the top, not in a footnote.