Stage 13 · Handoff
Take it from here
One link instead of a conversation. Below is what the product is, where every part of it lives, how it behaves, what it is assembled from, what accessibility it already carries, and the prompt you paste to add the next feature.
- Screens in colour
- 62 pages across 13 screens
- The system
- 75 components, 4 patterns, 2 token levels
- Flows specified
- 4 of 4, every step with a source
- Questions this page closes
- 24 from readers who had never seen it
What this is
One sentence, and the rest of the page assumes it
Harrier is an adjudication console for analysts at managed detection and response providers. One analyst carries forty or more client tenants. An AI agent named Clerk collects the signals, correlates them into a case, runs the first pass of the investigation and files a written verdict with its evidence. The human job is to rule on it: accept, amend or reject, in seconds, with the evidence in view and the override always one key away.
The one thing that distinguishes it: the fleet of earned trust, made readable where the analyst works. Every client's current latitude and accuracy trend legible at a glance in the operator console, at the resting state of the right hand pane, rather than on a configuration page.
The person it is built for is Rasha Idrissi, a Tier 2 analyst four years into operations, who arrives distrusting AI tooling. Every trust mechanism in the product argues with that rather than assuming it away. She is described in personas, and where two decisions conflict, hers wins.
Three links
The repository, the product, the system
github.com/sergiodesign4u-dot/harrier
The product, liveThe console in colour. Every screen, every state, clickable.design/index.html
The design system, liveEvery component and every foundation, painted by the same stylesheet the product links.design/kit/overview.html
The edge of the package
Three things to know before you open anything, because the opposite is the natural assumption
This is a fully assembled clickable product, not an application. There is no data layer, no API, no router and no state. Every screen is a static document showing canonical fixture data: seven tenants and one canonical case, held identical across every surface that draws them.
States live in separate files, not behind a switch. A screen with eight states is eight files, named <screen>-<state>.html. That is why there are 66 files for 14 screens, and it is deliberate: a state you can link to is a state that can be reviewed.
What was planned differently is in the backlog, with a reason and an owner on every row. See what is deliberately not done.
The route through the repository
Which folder, what is in it, who reads it
| Folder | What is in it | Who opens it |
|---|---|---|
design/ | The product in colour. 66 pages, every state of every screen. Not one of them contains a line of CSS: no style block, no @media, no transition, no colour value, and no class the system does not declare | Everyone. This is the product |
design/system/ | The code of the design system, and it lifts out whole: two token levels, 75 component files, 4 patterns, one entry point at index.css | Anyone changing how anything looks |
design/kit/ | The stand that shows the system. A page per component with five blocks each, the foundations, the rules of use and the thirteen prohibitions | Anyone assembling a screen |
design/kit/responsive.html | How it behaves at any width. One breakpoint, in rem, and the ladder that decides when a breakpoint is even allowed | The first question a developer asks |
design/kit/motion.html | How it moves, with the actual numbers rather than "about two hundred milliseconds". One duration for response, one period for status, and what happens under reduced motion | The second question |
design/kit/checks/ | Twenty instruments. Every prohibition in this project is also a function that measures it in a browser, because a rule that lives only in prose is a rule nobody runs | Anyone before claiming their work is finished |
handoff/docs/ | The four documents this page leads to: behaviour, the map, accessibility, and the prompt for adding a feature | You, today |
voice/ | The text of the product and the rules for writing a sentence that does not exist yet | Anyone adding a screen |
ia/ | The structure. A specification per node with its state matrix, keyboard model, addressing and permissions. This is what a screen has to satisfy | Before drawing anything new |
research/, design/concept/ | Where it came from. The market, the people, the jobs, and why the visual language is this one | When a decision looks arbitrary |
docs/decisions.md | Why, for everything. Half of every "why did they do that" is already answered here | You in a year |
wireframes/ | History of the process, and not a source of truth. 62 pages in grey, the originals of every coloured page except the four of node 7.1, which never had a wireframe. They exist so the colour could be proved a remap rather than a repaint, and they are the witness that proof stands on. Never read a behaviour out of them: the coloured screen is the product. They stay grey, and since 2026-08-26 they are kept in step rather than frozen: a defect found later is carried back into them, a stage boundary is not | Nobody, day to day |
Two rename maps, and they are in one file under two headings, which is worth knowing before you go looking: design/kit/docs/inventory.md section 10 carries the stage 08 map, and the later renames, nf to miss among them, are in section 8.
Which theme is the real one
Dark by default, and the light one is now a control rather than a proof
The console ground is dark and that is what ships. :root carries it, and with nothing stored that is what a person gets. The light theme exists as [data-theme="light"], it is complete, every role is paired into it, and since 2026-08-27 the analyst can reach it: the second control in the top bar, beside the name, and the choice is remembered.
The default is a decision and not an accident, so do not make it follow the machine. prefers-color-scheme is deliberately never read. The dark ground was argued against the reading research and settled on the rota that 79 per cent of these teams run; an operating system that happens to be light is not evidence about that rota. If you are asked to make the console follow the system, that is a product decision with an owner, not a default anyone should quietly flip.
Two consequences for you, and the second is the one that bites. A colour role is written twice, in the same edit, and the two values are not the same primitive: contrast is computed against the opposite ground, so the light side takes a different step of the ramp. A role written once loses its focus ring in the other theme, and nothing but reading the two lines side by side finds it. And a new screen must link design/system/theme.js from its head, above the stylesheet. Omit it and nothing looks broken: the screen renders dark and the bar quietly comes back with one control instead of two.
The consequence for you is one line: a colour role is written twice, in the same edit, and the two values are not the same primitive. Contrast is computed against the opposite ground, so the light side takes a different step of the ramp. A role written once loses its focus ring in the other theme, and nothing but reading the two lines side by side finds it.
How it behaves
The part you cannot see by looking
The screens show you what things look like. They cannot show you when the error appears, what counts as valid, what stands in the pane when nothing is selected, or where success goes. That is handoff/docs/behaviour.md, and it covers all four flows: rule on the case, pick up and hand off a shift, answer for a decision months later, and get in and arrive where the link pointed.
Every row in it names its source, and the source is one of three: the file of the screen, the flow diagram, or the specification of the IA node. A behaviour with no source is not written there at all. It goes into a list called NOT SETTLED and waits for a person, because behaviour is exactly where a plausible invention is indistinguishable from a decision.
Two rows are in that list today and neither blocks anything built. They are named on the page rather than resolved by guess.
What every screen is assembled from
And what moves if you touch a token
handoff/docs/map.md goes both ways. Downward: screen, zone, component, the tokens that component reads, where its text is addressed. Upward, and this is the half you will actually open: for every token, every screen it stands on.
The upward list is an inversion of the downward table rather than a second pass over the code, because two editions of the same data drift apart and the one that drifts first is the one nobody opens. It resolves in two knees, component to semantic role to primitive, because a component reads colour only through a role and a direct search would put the entire primitive level in the dead list.
It was taken by opening all 62 screens in a browser and walking the rendered DOM, not by matching patterns in the files. Two of the components on every screen are rendered by javascript and appear in no markup at all, and the zone of a component is its ancestry rather than its spelling.
Accessibility
Already built, and here is how to check it yourself
handoff/docs/a11y.md lists every point, where it lives in the code, a command or an action that verifies it in under a minute, and a status with exactly two values. A point with no way to test it is not on the list: it would look like finished work and would not be.
Confirmed means run at this stage, not remembered from an earlier one. Contrast, a real keyboard walk in both themes, browser zoom at 200% and the reader's own font size at 200%, and computed style under reduced motion, all across the whole product.
It ends with one real failure, found by an instrument written for this checklist on its first full run: the focus ring on the record rail measures below the non text threshold in the shipped dark theme. It is not fixed here. The handoff documents the product, and changing a state token after the product was accepted would void every measurement that acceptance stood on. It is a row in the backlog with a named owner.
How to add a feature
The prompt, ready to paste
handoff/docs/one-shot.md is not advice about prompts, it is the prompt. Copy it, put your feature where it says, paste it. It was written for the exam this stage ran and it has been through that exam twice, on two different features, with two agents that had never seen this repository: node 7.1 Tenant detail, which is in the product, and node 6.1 Client summary draft, which was a probe and was deleted.
This page carries its opening rather than all of it, and the reason is a rule of the package. Documentation here references code and never restates it, so a second copy of a 180 line prompt on this page would be the one that goes stale. What is worth having in front of you is the shape: the prompt is a ROUTE before it is an instruction, and the first thing it does is send the reader to the three files this stage produced.
The part nobody thinks to ask for is the last block, and without it the screen exists only on disk. A screen goes flat in design/ with no subfolder, one file per state named <screen>-<state>.html, it links exactly two stylesheets, it carries the design only panel the other screens carry, and it is registered in design/_nav.js. Without that last line the coverage map does not know it exists and the hub will not render it. Its strings go into voice/docs/microcopy.md section 8 with a page and a zone.
And it forbids inventing in the file of a screen without forbidding growth, which are two different things and the difference is the whole of it. No @media, no transition, no animation, no @keyframes, no <style> and no style= in a screen: those words are copied out of design/system/CLAUDE.md rather than paraphrased. What is missing appears in the SYSTEM first, as a complete component with its five parts, and only then stands on the screen.
Who decides
Because two open lists in this package are addressed to a person
Both open lists in this package, the NOT SETTLED rows in the behaviour specification and the debts in the accessibility checklist, are addressed to the owner of the product, which in this repository is its author. A row on either list is a question, not a defect, and answering it by reasonable inference is exactly the failure both lists exist to prevent.
If there is no owner to ask, the rule is the one this whole project runs on: write the question down where the work is, ship the rest, and say out loud what you assumed. An assumption that is stated can be corrected. An assumption that is implemented cannot be found.
What is deliberately not done
So that a decision does not read as a hole
design/kit/docs/backlog.md holds every one, with what is missing, where it is visible, why it was deferred and who closes it. An entry with no owner is a wish rather than a backlog item, so there are none.
What is in it: neither webfont is loaded, which is the one item that costs money and a request; a field has no error state, which is a voice decision before it is a visual one; twenty three collapsible blocks that need twenty three summary sentences nobody has written; and the two debts this stage added.
And six nodes have no screen at all, deliberately: clusters 6 and 7, which are the client summary and the autonomy grants, plus permission denied. They carry a record in the registry with a count of zero, so the coverage map renders them as work not done rather than hiding them. Only one of the six has a written specification, and that is what made it the exam of this stage.
Was unclear, became
What readers with no context could not find, and where the answer is now
Every question on this page came from somebody who had never seen this product. The full log, with each question, where the reader looked for it, the verdict on whether the answer really was missing, and what closed it, is handoff/docs/onboarding-gaps.md.
It has two halves and they are counted separately on purpose. Twenty agents built the last ten screens of the product from a written contract, each reading the design documentation for the first time, and what they had to ask is the first half. One reader was given the repository and this stage's own question, which is different: not one screen from a briefing but the whole product from the README down.
The numbers, and the two halves stay apart. The reader of this stage returned 13 questions of its own: 10 it could not answer and 3 it answered wrongly, which is the more expensive list. Of the 10, three were really absent, four were decided long ago and written down nowhere, and two were present and not where it looked, which is a defect of the route rather than of the documentation. The rollout's twenty readers contributed 15 more, 12 of them really absent. The exam then ran twice, on two features, with two agents that had never seen this repository: 7 gaps on node 7.1 and 10 on node 6.1.
The second list is longer than the first and that is said out loud rather than smoothed. None of the first run's gaps recurred, and the agent cites the paragraph that closed each one. What grew is not the package: of the second run's nine, about three are this handoff and five are the IA and the voice layer being genuinely empty for a cluster the track deferred. A feature with no node specification costs the reader more than a feature with one, and that is a fact about the map rather than about the guide.
| Was unclear | Became |
|---|---|
| Where does a new interface string get written down? The inventory says of itself that its sections 3 and 4 are the wording as it stood before stage 05, so a lookup there returns the text that was replaced | The most expensive row in the table, because it is the first thing every feature needs. Answered in the text section of one-shot.md: a new string is written against voice.md, lands in microcopy.md section 8 as a ruled row with its page and its zone, and the screen carries it verbatim |
| Which wins when a node specification and the built screen disagree? The precedence was stated three different ways in three files and never as one rule | Written once, at the head of behaviour.md: the IA node owns behaviour and states, the built coloured screen owns markup and applied text, and the microcopy ruling table owns why a string reads the way it does |
| Where is the grey to colour rename map? The grey original of a screen carries one set of class names and the coloured one carries another | Present, and not where it looked. A route defect: the section called The rename map holds the stage 08 map, and the stage 12 renames sit in a different section under a different heading. Both are now named in the route above, with what each covers |
| Are the coloured screens generated, and what happens if a grey generator is re-run? | Decided long ago and written nowhere. The grey was produced once by its generators and they have not run since stage 05; the colour is authored and it is the product. Re-running a generator would rebuild the witness, not the product |
| Should a new screen carry the design annotations the others do? | Present, and the reader did not see it, because the answer is a count rather than a sentence: 40 of the coloured pages carry one and 22 do not, and it happened to open two of the 22. Stated in one-shot.md: an annotation is where a node number, a zone label or an argument for the design goes, and a screen with nothing of that kind to say carries none |
And the critique of this stage, by class. Four instruments: an independent read only pass, this session's own pass over the code and in the browser, the two reader sets above, and one that returned nothing because it could not run.
| Class | Found | By | Outcome |
|---|---|---|---|
| A section of this page never filled | 2 | Claude | Fixed. How to add a feature carried the words filled by step 7, and the stylesheet rule written for its prompt block had zero users |
| A token named that does not exist | 1 | Codex | Fixed. The accessibility checklist credited a role that was never added; the real answer is that the boundary of a control is a different role and already carries its threshold |
| A status the file's own rule forbids | 5 | Codex, then counted again | Fixed. The checklist declared two values and used four. The rows that no instrument can run are now called what they are, and the rule declares three |
| A table row missing a cell | 3 | Codex | Fixed. Three validation rules had lost the column naming where they render |
| A rule quoted after it was replaced | 2 | Codex | Fixed in both places. This page and the README still said the grey folder was frozen and must not be edited; the rule changed on 2026-08-26 and now says it is kept in step |
| Form | 1 | Claude | Fixed. The README had no closing line break, which drops its last status row out of any parser |
| Duplicate instead of a reference | 5 | Codex | Cleared, with the reason on each. Two are measurements from the exam rather than restated values, one is the evidence a row exists to give, and two are interface strings that still match the inventory exactly. This product addresses a string by its text because it has no keys |
| A markdown link that survived into a page | 1 | Claude | Cleared. The two characters sit inside a code element on the checks page, where they are the name of the check itself |
| A path naming no file | 5 | Claude | Cleared. All five are named in the record of their own deletion |
| The product changed after it was accepted | 147 files | Claude | Carried to the decision record with the baseline named. Every change is owner accepted work that landed after stage 12 closed, and none of it is this stage inventing |
| An instrument that returned nothing because it cannot run | 1 | Claude | Said out loud, for the second time this stage. Its detector was given a file carrying a failing contrast pair, a forbidden transition, an empty button, two headings and a marketing sentence, and returned an empty list |