Skip to content
OpenRelationshipPublic

About

🥒 brine: model the behavior before you build it. Interview the people who do the work, get Gherkin specs and a typed fact ledger. For planning apps and internal tools. Ships an agent skill.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

25 Commits

Folders and files

Repository files navigation

brine: model the behavior before you build it

brine

skills.sh License

Model the behavior before you build it.

Most software gets planned from what people say they want. brine plans it from how the work actually happens. You send a question tree to the people who do the work: the operations team a new internal tool has to fit, the customers or clients an app is for, the specialist who knows how a job is really done. They answer one question at a time, typed or spoken. A model reads each answer to choose the next question, so the interview follows what they actually do. Their answers come back as a behavior model you can build against:

  • Gherkin scenarios for how the work flows, each one traced to the answers it came from.
  • A fact ledger for everything else they said: numbers, timers, rules, their exact wording, real cases, red flags, terms, wishes, risks and what they measure. Each fact is typed and cited, and can be emitted as constants for code or rendered as a backlog, risk register or playbook.

A respondent's answer in the brine interview page, with lines from highlighted phrases to what they become: the decision model opening a follow-up, two traced Gherkin scenarios, typed ledger facts, and an open fact

One typed or spoken answer becomes a follow-up question, traced scenarios, typed facts, and an open question where the respondent was vague, instead of a guess.

Use it to:

  • Plan an internal tool and its stack. Interview the team that runs a process, such as an agency moving a client request to a delivered video: where the request lands (a monday board, a Slack thread, a shared inbox), who approves it, what gets checked before it counts as done, and where it waits. The ledger separates fixed rules that belong in code, judgment calls that need a person (or a model, with evals drawn from real cases), and the systems and records each step reads or writes. That tells you what to build and what to integrate, not a vendor's demo.
  • Plan an app around a customer, client or user. Model their process before you design screens. For a clinic's front desk: the states an appointment moves through, the reminder that goes out 48 hours before and who sends it, the no-show rule, and the insurance exceptions staff sort out by phone today.
  • Write down how a specialist does the job. A senior estimator pricing a renovation, or a payroll lead closing the month: the inputs they trust, the checks they run and the thresholds they use, in their own words, so the team and its software work to the same spec.

How it works

  • The tree (src/spec.ts, src/walk.ts). Chapters of questions, walked in order. when guards skip what does not apply, next jumps go forward, and decide asks Jev typed questions about an answer ("does this name an exception?", "who caused the delay?") so later guards branch on what was said. brine check proves every address resolves, every jump goes forward and every guard looks back, and it samples walks to tell you how long a sitting is.
  • The page (src/ui). One question at a time with a chapter list to jump around, and a waveform button in the answer box that records. The respondent never sees transcripts, reasons, branches or Gherkin.
  • The Worker (src/worker.ts). A dependency-free Cloudflare Worker: invite links and passcodes, each respondent's walk in its own Durable Object, recordings and an append-only answer log in R2, transcription through OpenRouter's speech-to-text endpoint (openai/gpt-4o-mini-transcribe by default), decisions through OpenRouter's Decisions API, and an admin export.
  • The loop (src/loop.ts). What happens after the first round: brine trace ties every as-is scenario back to the answers it came from (@q: tags) and lists answers nothing cites; brine followup drafts the second round from the features' Rules (read-backs) and # TODOs.
  • The ledger (src/ledger.ts). Everything the respondent said that is not behavior (numbers, timers, rules, their wording, stories, signals, terms, ideas, risks, metrics) as typed facts cited to answers; brine ledger check|render|emit|records projects it into code, documents and retrieval records.
  • The skill (skills/brine). An agent skill: how to design the questions from a vocabulary or pipeline (references/method.md), how to write Gherkin from the answers (references/gherkin.md), and what else the answers become (references/derivations.md).

Use it

bun install
bun test                                    # the walk, the checker, the Worker
bun bin/brine.ts check example/src/interview.ts
bun bin/brine.ts outline example/src/interview.ts

Install the skill for your agent (Claude Code, Cursor, Codex and others) from skills.sh:

npx skills add OpenRelationship/brine

Then ask the agent to "design a brine interview for about ". The skill tells it to clone this repo for the code.

Host an interview

Write questions.ts exporting interview: Interview, then:

// worker.ts
import { brine } from "brine/worker"
import { interview } from "./questions"
const app = brine(interview)
export default app
export const BrineSession = app.BrineSession   // one Durable Object per respondent
// main.tsx
import { Brine } from "brine/ui"
import "brine/ui/brine.css"
createRoot(document.getElementById("root")!).render(<Brine interview={interview} brand={{ name: "Acme" }} />)

example/ is a complete app. It needs a KV namespace (BRINE), an R2 bucket (BRINE_AUDIO), and two secrets: OPENROUTER_API_KEY and BRINE_ADMIN_TOKEN. The Durable Object (BRINE_SESSIONS) and the passcode rate limit (BRINE_ENTER_LIMIT) are declared in example/wrangler.jsonc; both are optional.

wrangler kv namespace create BRINE           # put the id in wrangler.jsonc
wrangler r2 bucket create brine-example-audio
wrangler secret put OPENROUTER_API_KEY
wrangler secret put BRINE_ADMIN_TOKEN
bun run example:build && wrangler deploy --config example/wrangler.jsonc

Share it

bun bin/brine.ts card example/src/interview.ts --company "Sam's Bakery" --mark logo.svg --out public

writes public/card.png, the link preview for the invite: who the interview is for, a heading, how long a sitting takes (estimated from the kinds of questions a walk asks) and how many questions. Serve it next to the page and point og:image at its absolute URL; the command prints the tags. It needs the optional @resvg/resvg-js.

Run it

export BRINE_ADMIN_TOKEN=…
bun bin/brine.ts invite https://your.worker.dev "Sam Baker"   # prints the link to send
bun bin/brine.ts status https://your.worker.dev
bun bin/brine.ts export https://your.worker.dev ./answers      # answers.json + answers.md

answers.md lists every asked question with its why and yields, the answer, verbatim transcripts and Jev's reads. Hand it to Claude with the skill loaded and ask for the features.

The spec in one screen

{
  id: "wholesale", kind: "long",
  ask: "Walk me through the last wholesale order, from the call to the van leaving.",
  when: { q: "sources", is: "wholesale" },            // only if they take wholesale
  decide: { exception },                              // Jev: does the answer name an exception?
  why: "The wholesale path end to end, from a real instance.",   // designer only
  yields: "Scenario: a wholesale order — Given/When/Then",      // designer only
},
{
  id: "wholesale_exception", kind: "long",
  ask: "You mentioned it sometimes goes differently. Tell me about the last time it did.",
  when: { q: "wholesale", decision: "exception", is: "yes" },
  why: "…", yields: "Scenario: wholesale exception",
}

Kinds: long, text, choice, multi, number, scale. Guards: is, not, answered, atLeast, decision, and all / any / none. A decision's fallback is used when there is no model, so pick the result that asks more.

Close the loop

The first round is not the end. Write the as-is features from answers.md with @q: tags and # TODO / # IDEA / # CONFLICT markers, rule on the conflicts (# RULED:), write the product specs, fit them onto a shared step set, then send a short second round that reads the rules back:

bun bin/brine.ts trace questions.ts answers/answers.json features/**/*.feature   # every @q: resolves, every answer cited
bun bin/brine.ts followup round-2 "Did we get it right?" features/**/*.feature > round2.ts   # draft; rewrite in their words

Scenarios hold behavior. Everything else the respondent said (numbers, timers, rules, their wording, real cases, red flags, terms, wishes, risks, what they watch) goes into one fact ledger, each fact cited to its answers, and every other artifact is projected from it:

bun bin/brine.ts ledger check questions.ts ledger.json answers/answers.json   # well formed, every citation a question
bun bin/brine.ts ledger emit ledger.json number timer rule template > src/facts.gen.ts   # code reads constants by id
bun bin/brine.ts ledger render ledger.json idea          # the backlog (risk, term, ... likewise)
bun bin/brine.ts ledger records answers/answers.json ledger.json features/**/*.feature > answers.jsonl   # retrieval

The steps are in skills/brine/SKILL.md; the markers and the step pass in skills/brine/references/gherkin.md; the ledger's kinds, projections and checks, ranked, in skills/brine/references/derivations.md.

License

Apache License 2.0. See LICENSE.

About

🥒 brine: model the behavior before you build it. Interview the people who do the work, get Gherkin specs and a typed fact ledger. For planning apps and internal tools. Ships an agent skill.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages