Capture
The four seconds between having a thought and losing it is the only scarce resource here. Every acceptance-criteria tool that died, died at authoring time: a form appeared and the person went back to Slack. So capturing has to be a reflex, with no prompts, no wizard, no required fields and no init.
The one thing I want it to be strict about is refusing to quietly clobber something that already exists.
- CAPTURE-1 I can write a thought down in one command with no setup.
- CAPTURE-1.a I never have to run an init step before hi is useful.
- CAPTURE-1.b I am never asked a question in the middle of capturing.
- CAPTURE-1.c If I type something hi cannot handle, it says so plainly instead of crashing with a stack trace.
- CAPTURE-2 I can use a brand new id and it just works.
- CAPTURE-2.a If the family is new, hi starts the file itself rather than asking me where to put it.
- CAPTURE-2.b If I hang a case off something that is not there yet, hi says so and names what is missing.
- CAPTURE-3 If I reuse an id that already exists it refuses, and tells me the next free one.
- CAPTURE-4 I find a case directly under its parent, not at the bottom of the file.
- CAPTURE-4.a I find a case in the file where its parent actually lives, not wherever the family happens to be declared.
- CAPTURE-5 If hi refuses for any reason, nothing is written to disk.
- CAPTURE-6 hi finds my workspace by what is inside it, not by a directory name, so a folder called hi for something else is left alone.
- CAPTURE-7 If a file has no criteria section yet, hi makes one instead of appending wherever the file happens to end.
- CAPTURE-8 I put hi's own options before the id, so nothing in my sentence is mistaken for one.
- CAPTURE-9 The sentence I type is the sentence that lands in the file, word for word.
- CAPTURE-10 hi works from any directory inside my repository, and a repository is where it stops looking.
- CAPTURE-11 Every capture tells me which file it landed in, so I never have to go looking.
- CAPTURE-12 If a file in my hi directory is empty, hi tells me what is wrong with it instead of crashing.
- CAPTURE-13 When an id is taken, the next free one I am offered is one I can actually use.
- CAPTURE-14 An id written somewhere hi cannot read it is still taken, and is never handed out twice.
- CAPTURE-15 If hi cannot read one of my files, it says so and refuses, instead of carrying on as though the file were not there.
- CAPTURE-16 If two files both claim a family, a new criterion I write in that family is refused rather than landing in whichever file happens to sort first.
Check
hi must be installable on a Friday afternoon without turning anyone's build red. It has no opinion about whether a criterion is any good, whether it is finished, or whether anything downstream implements it. Incomplete intent is the normal state of intent.
The only thing it will fail on is a file that is structurally wrong, because that is the one case where being quiet would let ids rot.
- CHECK-1 I can add hi to an existing repo and it will not turn the build red.
- CHECK-1.a A criterion with nothing implementing it is never an error.
- CHECK-1.b An unfinished file is never an error.
- CHECK-2 hi fails only when a file is structurally wrong.
- CHECK-2.a Two criteria sharing one id is an error.
- CHECK-2.b A case whose parent is not in the same file is an error, because a case belongs with the criterion it is a case of.
- CHECK-2.c Reusing a retired id is an error.
- CHECK-2.d A line that is shaped like an id but is not a valid one is an error, because otherwise it would read as prose and vanish.
- CHECK-2.e A criterion outside the criteria and retired sections is an error, because nothing would read it there.
- CHECK-2.f Using a family the file never declared is an error.
- CHECK-2.g I am told when two files both claim the same family, instead of captures quietly landing in whichever one sorts first.
- CHECK-3 Every problem names the file and the line, so I can go straight to it.
- CHECK-4 Checking works offline and reads nothing but my own files.
- CHECK-5 One run tells me every problem in every file, so I fix them all in one pass.
- CHECK-6 I can read each of hi's notes on its own in a script, under a name that stays the same when the wording changes.
The format
A hi file has to be something a person would write anyway. If someone deletes the binary tomorrow, the files should still read perfectly as a document. That is the whole test. Nothing below the frontmatter exists to serve the machine, and nothing the tool writes should look different from what a careful person would have typed by hand.
Ids are the one thing I am strict about, because an id that moves is worse than no id at all. You should be able to say "SEND-1.a is wrong" out loud in a standup a year from now and have it still mean the same line.
- FILE-1 A hi file reads as an ordinary markdown document with no tool installed.
- FILE-1.a If the binary disappears tomorrow, nothing is lost, because hi keeps nothing of its own anywhere else.
- FILE-1.b On GitHub or in any preview, the criteria are a list, one per line with cases nested, not a paragraph of run-together sentences.
- FILE-1.c The id is bold, so it reads as a label rather than as the first words of the sentence.
- FILE-2 The machine-facing part of a file is its frontmatter, and nothing below it exists to serve the tool.
- FILE-3 When hi writes a line, it looks like something I would have typed by hand.
- FILE-4 hi never rewrites, reflows, or reformats prose that I wrote.
- FILE-4.a After hi adds a criterion, the rest of the file comes back byte for byte identical, apart from the frontmatter line that names the families.
- FILE-5 One file covers one feature, and it can hold several id families.
- FILE-6 One criterion is one line, however long the sentence runs, so I can grep it and diff it.
- FILE-7 hi understands my frontmatter whether I list the families inline or one per line, and when it adds a family it keeps the style I chose.
- FILE-8 If a write fails partway through, my file is left exactly as it was.
- FILE-9 A fenced code block in my prose is prose, so I can show an example of the format without it becoming real criteria.
- FILE-10 If my file uses Windows line endings, hi writes them back the same way.
- FILE-11 A byte-order mark from my editor does not make a valid file look broken.
- FILE-12 The paths hi prints or exports use forward slashes on every platform, so a link works wherever it is read.
- FILE-13 A file starts with my own words about why this feature exists, so the first thing anyone reads is the why and not the list.
- FILE-14 I can type a criterion into the file by hand, bullet or no bullet, bold or plain, and hi still reads it as one.
- FILE-15 A criterion I changed my mind about stays in the file under Retired, with my reason beside it, so the file remembers what we dropped.
- FILE-18 I understand a criterion as what this should be, not as a report of what it currently does.
- FILE-19 Two captures running at the same time both land, instead of one quietly overwriting the other.
- FILE-20 A criterion hi cannot see is never silently invisible; it is reported rather than ignored.
- FILE-21 One paragraph of my prose is one line, however long it runs, so a break only ever appears where I left a blank line.
- FILE-21.a The files hi writes for me start out that way, so the first thing I read is the convention rather than an exception to it.
- FILE-22 When hi tells me it wrote something down, I can find it again with hi, or hi refuses instead of telling me it worked.
- FILE-22.a If my file is half written, with a fence I never closed, hi refuses to save into it and leaves my unfinished words alone.
- FILE-22.b An example of the format inside my prose is never mistaken for the section it names, by the verbs that write as much as by the ones that read.
- FILE-22.c A command that writes one criterion leaves every other criterion in my file exactly as it was, or it refuses.
- FILE-23 If hi is killed while it is holding the write lock, the next capture recovers by itself instead of leaving me a file to delete.
- FILE-24 While another hi is still running, nothing takes the write lock away from it, however long it has been holding it.
- FILE-25 A file that says it was written for a version of the format this hi does not understand is refused, rather than read as though it were the version I have.
- FILE-25.a That refusal leaves every file exactly as it was, because hi stopped before it wrote anything.
- ID-1 An id never moves once written, so I can say it out loud a year later and still mean the same line.
- ID-1.a Inserting a criterion never renumbers anything around it.
- ID-1.b A retired id stays reserved forever and is never handed out again.
- ID-1.c A zero-padded number is refused, because SEND-007 and SEND-7 must never be two names for one line.
- ID-2 I choose the id myself, because I am the one who has to say it.
- ID-2.a A family name is in capitals, like SEND or BILLING or SEND_2FA, so an id stands out as a name in the middle of a sentence.
- ID-3 An id tells me whether it is a case or a step.
- ID-3.a A letter is another case of its parent, like SEND-1.a and SEND-1.b under SEND-1.
- ID-3.b A number is a step or a detail inside its parent, like SEND-1.a.1 inside SEND-1.a.
- ID-4 If I write an id that breaks the alternation, hi tells me instead of quietly accepting it.
- ID-5 Whatever order I capture, retire and edit in, alone or alongside another hi, an id I have seen is never given to a second sentence.
- ID-5.a Every criterion keeps its id, its section and its sentence across those steps.
- FILE-16 I can tell who each criterion speaks for, because every one names the role it is written in. roles were removed from the format; the plain sentence is the criterion
- FILE-17 If I cannot put a role in front of a sentence, I learn while typing that I wrote a fact rather than a want. roles were removed from the format; the test survives as advice in DECISIONS, not as a rule
Generate
Intent is written once, by a human, and everything downstream is generated from it: tickets to work from, a payload an agent can turn into a spec, and the feature list at the front of the product so nobody keeps it by hand. That is what makes writing it down first pay for itself instead of being one more document to maintain.
Generation must work with no auth, no network, and no integration, because the moment it needs setup it stops being used.
- ISSUE-1 I can turn a criterion into a ticket without leaving the terminal.
- ISSUE-1.a I get a ticket printed by default, so it works with whatever tracker I actually use.
- ISSUE-1.a.1 I can print a ticket with no login, no network, and nothing set up first.
- ISSUE-1.b I can add one flag and open a real GitHub issue instead.
- ISSUE-2 I find the criterion id on the ticket, so a closed ticket traces back to the intent it served.
- ISSUE-3 I get the criterion's cases in the body, so the ticket is the whole picture.
- ISSUE-3.a I read a case in the ticket as nested under what it is a case of, not flattened into a list of peers.
- ISSUE-4 I cannot turn a retired criterion into work.
- ISSUE-5 I get the feature's intent prose on the ticket, so I know why the work exists and not just what to build.
- ISSUE-6 A ticket carries my intent prose, and nothing hi generated into the file around it.
- ISSUE-7 I read the intent on a ticket as whole paragraphs, not as a narrow column broken wherever the lines happened to be wrapped in the file.
- ISSUE-7.a A blank line I left between two thoughts is still a break on the ticket, because that is where I meant one.
- ISSUE-7.b A list or an example in my intent arrives on the ticket with its own lines intact.
- EXPORT-1 I can hand an agent everything it needs to write the spec in one command.
- EXPORT-1.a I get the intent prose and not just the criteria.
- EXPORT-2 I can export one family, one file, or the whole product.
- EXPORT-3 I read a smaller export as the same payload with less in it, so I never need a special case.
- EXPORT-4 I get the retired criteria in the export kept apart from the live ones, so the agent never writes a spec for something we dropped.
- EXPORT-5 The starter prompts hi wrote into a file never reach an agent as if I had written them.
- EXPORT-6 I can tell the shape of the export payload apart from the version of the files it was built from, so a change to one never reads as a change to the other.
- EXPORT-7 I can export one criterion, and I get it with its cases and the intent of its file, so an agent working on one piece reads only that piece.
- EXPORT-7.a I never get a case without the criteria it sits under.
- INDEX-1 I get a root file that shows what features exist without keeping a list by hand.
- INDEX-1.a I can run hi with no root file yet, or no list in it, and it starts one with a place for my own prose rather than refusing until I set it up.
- INDEX-2 I keep the rest of the root file mine, because hi only ever rewrites the list it generated.
- INDEX-2.a I can quote the comments hi marks its list with, even on a line of their own or inside a code block, and hi does not mistake my example for the real list.
- INDEX-2.b I get a refusal when those comments are broken or unpaired, rather than hi guessing where its list ends.
- INDEX-2.c If hi cannot read my root file at all, it leaves every byte of it alone rather than starting a fresh one over the top.
- INDEX-3 The product-level file exists from my first capture, so I never have to discover it.
- INDEX-3.a hi keeps reminding me while the product-level why is still unwritten.
- INDEX-4 The list at the front of my product is true after every command that changes it, without me remembering to refresh it.
- INDEX-4.a A capture that stored my criterion is never reported as a failure because the list could not be refreshed.
- INDEX-4.b If I write a criterion into the file by hand, hi tells me the list is behind rather than leaving it wrong.
- INDEX-4.c If I delete the generated list from my root file, it stays deleted until I ask for it back, because refreshing a list is not the same as installing one.
- INDEX-5 Refreshing the list at the front of my product cannot undo a criterion another hi is capturing at the same moment.
Habit
hi dies in the gap between someone agreeing with it and someone having it. They read the README, install the binary, and never run a second command. Nothing goes stale, because there was never a hi/ at all.
So hi has to arrive attached to work somebody already wanted, rather than as a thing you set up first. An agent asked to build a feature writes the criteria for it, shows them to the person, and captures the ones they confirm. That happens before every feature and not only the first, because a habit with a judgment call in it is a habit that erodes.
The question belongs to the agent and never to hi. Capture still asks nothing and requires nothing; the confirmation happens in the conversation, which is the one place where a question is already the medium.
- HABIT-1 A coding agent working in my repository reaches for hi on its own, without me explaining it first.
- HABIT-2 Intent gets written before the work, every feature and not only the first one.
- HABIT-3 Nothing lands in my files that I did not agree to, however fast the typist was.
- HABIT-4 An agent starts hi in a repository that has none, from the feature I just asked for, instead of waiting for me to set it up.
- HABIT-4.a Whatever hi says at the start of my work, it never stops me from working.
- HABIT-4.b Once something is written down here, hi stops mentioning it.
- HABIT-5 An agent that merged a branch touching my intent files checks the ids afterwards, because two branches can each hand out the same id and git will not say so.
- HABIT-6 I can bring the instruction file hi wrote for agents up to date without capturing a dummy criterion.
- HABIT-6.a If that file is still a template hi has shipped, hi replaces it with the current one.
- HABIT-6.b If I have edited that file, hi leaves every byte of it alone and tells me so.
Changing your mind
Changing your mind is normal, and the format has always had a place for it. For a while the tool did not: ## Retired existed in the file and no command put anything there, so the only way to retire something was to hand-edit the markdown, in a tool whose whole pitch is that you do not hand-edit.
Someone using hi cold on a real product cut seven criteria by deleting lines and never found the section at all. Their sentences are gone and the ids they used are not written down anywhere.
Retiring should cost one command, keep the id spoken for forever, and keep the reason next to the thing it explains.
- RETIRE-1 I can retire a criterion with a command instead of hand-editing the file.
- RETIRE-1.a Its cases go with it, so nothing is left orphaned behind it.
- RETIRE-1.b I can say why I changed my mind, and the reason stays next to what I retired.
- RETIRE-1.c I can come back later and say why, without editing the file by hand.
- RETIRE-1.d I am told which cases went with a criterion I retired, in case one belonged to something else.
- RETIRE-2 A retired id is still spoken for, so it is never handed out to something else.
- RETIRE-3 I am told when a retired criterion never says why it was retired.
- RETIRE-4 I can learn the standard a team retires things by, because every retirement says why.
- RETIRE-5 What I retire lands in the retired section, wherever that section sits in the file.
- RETIRE-6 A reason I type is a reason, even with line breaks in it, and can never become a criterion.
- RETIRE-7 What I retire stays retired, even when something else is writing to the same file at the same time.
The view
The people who decide what we build mostly do not want to read a markdown file full of identifiers. If the only way to see what we agreed to is to open hi/chat.md in an editor, then the intent is written for engineers and the product side goes back to arguing from memory.
So there has to be a view that is just the sentences. The why first, the criteria as a readable list, and the ids present but quiet. One file I can send to somebody.
- VIEW-1 I can see what we agreed to without reading markdown or being handed a file full of ids.
- VIEW-1.a I get the intent prose first, and the ids stay small and out of the way.
- VIEW-1.b I see cases visually nested under what they are cases of.
- VIEW-1.c I can leave comments in my intent prose and they stay out of the page.
- VIEW-1.d I can copy text off the page and the id and the sentence stay apart, with each criterion on its own line.
- VIEW-2 I have one self-contained file I can send to anyone.
- VIEW-2.a I know the page reads properly on a phone, because that is where people open what I send them.
-
VIEW-3
I can use bold, italic,
code, and links in a criterion, and they render properly. - VIEW-3.a Anything that looks like markup in a sentence is escaped, never executed.
- VIEW-4 Retired criteria are on the page but folded away, so the history is there without being noise.
- VIEW-5 Nothing on the page says whether anything is done, so nobody can read it as a progress report.
- VIEW-6 I can search the whole page by id or wording and see only what matches.
- VIEW-7 I can narrow the page to one feature by clicking it.
- VIEW-8 I can sort everything by id or by family instead of reading it grouped.
- VIEW-9 I can click any id to get a link straight to that criterion, and that link works when I send it to someone.
- VIEW-9.a A link to a criterion lands on it even when a filter would have hidden it.
- VIEW-10 I still see every criterion with scripting turned off, and I am not shown a search box that cannot search.
- VIEW-11 The page is named after my product, taking the name from the heading I wrote in INTENT.md.
- VIEW-12 I can reach any feature from a list that stays on screen while I scroll, instead of scrolling to look for it.
- VIEW-12.a The list tells me how many criteria are in each feature before I go there.
- VIEW-12.b The list shows me which feature I am currently reading.
- VIEW-13 When I search, the words that matched are highlighted where they sit, so I can see why a line came back.
- VIEW-14 I can move through criteria from the keyboard, without reaching for the mouse.
- VIEW-15 Clicking an id copies a link I can paste to someone, and tells me it did.
- VIEW-16 Nothing generic sits above my own words; the page opens with what I wrote, not with boilerplate about the tool.
- VIEW-17 The page wears the CorvidLabs brand, using the kit's own tokens rather than colours invented here.
- VIEW-17.a The two brand faces are named first and the page falls back to the system's own, because it still has to open with no network.
- VIEW-18 I can switch the page between light and dark myself, and it remembers which I chose.
- VIEW-19 The page I would generate is published somewhere I can send a link to, so someone can see what hi looks like before installing it.
- VIEW-19.a What is published is hi's own real criteria, not a mock-up, so a page that is wrong is wrong for everybody at once.
- VIEW-20 The page is checked by something that actually opens it, not only by reading the html it was built from.