Wolf Ventures
How the demo works
Everything dark on the screen is the demo. Everything light is the product. This page is about the dark part: what it reads, what it is allowed to claim, and why it cannot claim progress the product has not made.
One shell, two products, one adapter between them
The bar, the flow list, the ring and the handover strip are written once. They know nothing about escrow or about compliance.
The shell wraps the product on two sides, top and left, and never reaches inside the frame. What it renders in between is the real application, with its own sidebar and its own top bar, because those are chrome a reviewer is meant to see and use.
The shell is five files, and not one of them imports anything from either product. What they share instead is lib/demo/types.ts, which describes the live state of a guided run and nothing else: which flow, how far in, whose turn, the one sentence to show, the screen it happens on, and whether the step has actually happened. Neither engine is rewritten, and neither is wrapped in new logic.
Each product supplies an adapter that produces that shape from its own engine. lib/demo/adapters/escrow.ts reads the escrow flow player and the live transaction; lib/demo/adapters/aml.ts reads the compliance flow player, the AML world and the escrow records together, because some compliance work lands on a transaction rather than on a customer file. Both adapters translate. Neither decides anything.
That boundary is what makes the differences between the two products invisible to the shell, and it is worth naming the three real ones.
- Seats
- Escrow has personas, so a step can belong to a seat the reviewer is not currently holding and the bar offers to hand it over. The AML workspace has one operator: there is no seat to take, so the adapter reports that condition as permanently false rather than inventing a handover the product does not have.
- Colours
- An escrow is two companies with Wolf between them, so the colours name the sides rather than nine job titles. Compliance work does not move between companies, it moves between lines of defence, so there the colours name the first line, the special unit, management and the second reviewer. One colour vocabulary, two different meanings, each defined in its own adapter.
- Named rows
- Escrow names a row with a fixed identifier, because a condition exists in the seed. A compliance step may name a part of the screen that only exists once the flow has created it, so there the name can be a function of live state. The adapter resolves it before the shell ever sees it.
The flow list is built the same way. Every flow carries a number, continuous from one, in the order the rail presents it. That number is derived from the flow data rather than written down beside it, so it cannot drift when a flow is added or reordered, and it gives a room of people one shared vocabulary: show me thirty-four has exactly one answer. The internal codes stay in the data, where the self-check and acceptance pages depend on them, and never appear in the list or the bar.
Every flow is reachable from that list, and exactly once. A single step normally sits folded under the transaction it works, but one of them works a transaction no full scenario uses, so the list prints the remaining group underneath as well, minus whatever is already above it. Without that, the flow would be reachable by search and by nothing else.
The bar, and why it never disappears
One line, one height, always present. It names the step and points at the control, and it never explains the product.
A bar that vanishes between flows leaves the reviewer on a screen with no instruction, which is how the previous build lost people. So the bar is rendered unconditionally and has an answer for every position, including the two where there is no step to point at.
- Nothing running
- One sentence and one way forward: pick a flow on the left, or start the recommended one from the button on the right.
- A step is open
- Flow number and name, how far in, the seats this flow needs as coloured dots, the instruction, the seat acting now, and one button. The instruction is truncated rather than wrapped, because the bar has to keep its height, and the full sentence is on the element itself for a reader who hovers.
- The flow is finished
- The outcome, and the flow that follows. Where a flow names its successor the pair is the point; otherwise the bar simply offers the next number, on the reasoning that whoever has just finished a flow wants another flow more than they want the list.
Under the bar sits a strip a few pixels tall, one segment per step, coloured by the seat that acts. Segments behind the current one are dimmed less than those ahead of it, and any of them can be clicked. Clicking one ahead does nothing useful on purpose: the jump is clamped to the step you are on, because a later step’s prerequisites are real and cannot be skipped into existence.
A step passes only when the record changes
This is the claim the whole demo rests on, so it is worth being exact about where it is enforced.
Every step in both libraries carries a predicate: a function from the live record to true or false. The escrow predicates read the transaction; the compliance predicates read the AML workspace together with the escrow records. The shell calls the adapter, the adapter calls the predicate, and the result is the only thing that turns Waiting into Continue.
The player itself stores two values: which flow is running, and which step it is on. It holds no notion of progress beyond that, and it never performs an action on the reviewer’s behalf. Everything else is read back out of the product, which is also why leaving a flow costs nothing: the run is forgotten and the record keeps whatever the reviewer actually did to it.
So a step that does nothing cannot pass. Opening the screen does not complete it. Neither does scrolling to the control, hovering it, or pressing Continue early: until the predicate is true the button is inert and says so.
Predicates are written against the operative record, not against any record.
The other half of the same problem is the starting position. Beginning a flow reseeds the record it works on, so a second run does not open on a position the first run left behind, with its early steps reading as already satisfied. The escrow player reseeds the transaction the flow names; the compliance player clears the whole workspace, because there the unit of work is a desk rather than a single case, and it reseeds any escrow transactions the flow touches as well.
The role track: the process written as its handovers
An escrow is not one desk doing nine things, and a compliance case is not one officer doing five. The order of hands is the product.
Below the bar, the flow is drawn as the sequence of seats that hold it. The seat acting now is ringed in its own colour and scrolls itself into view as the case moves; earlier seats stay legible and can be clicked to go back; later seats are dimmed.
Consecutive steps taken by the same seat collapse into one link. Four operations steps in a row are one stretch of the process, not four handovers, and drawing them as four would say that something changed hands when nothing did. The reviewer cares about who holds the case, not about how many clicks that seat needs while holding it. Clicking a collapsed link returns to the first step of that stretch, and its tooltip names the range it covers.
How the ring finds its control
The bar names the action; the ring marks the button that performs it. Nothing on any screen is annotated by hand for this.
The match works because the step’s sentence contains the control’s own label. Escrow’s authority-gated controls already carry their label as data, and a step’s instruction is written to quote it, so the comparison is exact. Compliance work is built from ordinary buttons instead, so there the button’s own visible words are the label: the same fact, read off the screen rather than off an attribute nobody would remember to keep in step.
Around that sit four rules for choosing between candidates, each of which exists because the naive version pointed somewhere wrong.
The earlier mention wins
A sentence can name two real controls in order. File the report with MROS, then show the goAML XML is a step in the reporting flow, and both buttons are on the screen. Only the first is what to do now, so the label that appears earliest in the sentence takes the ring.
Where two labels start at the same position, one is a prefix of the other and the longer one is the real control: Approve must never beat Approve the release.
A named row beats a matching one
A transaction with three outstanding conditions carries three identical upload buttons, and a sentence naming the document cannot pick between them. So the step names its row or its section by identifier, and the screen carries the same identifier as a scope. The search then runs inside that scope only.
23 escrow steps name a row this way, by condition id. Compliance steps name a section of a form or an involved person, and two name a case that did not exist until this run created it.
A named scope that is absent means nothing, not something else
If the named row is not on the screen yet, the search returns no match rather than falling back to the whole page. Pointing at the invoice when the sentence says bill of lading is worse than pointing at nothing.
The one exception is the last resort. Where a scope is named and nothing inside it is called what the sentence says, the scope itself takes the ring. Tick at least one means is not the name of a button, and the form is the honest answer.
Only what is enabled, and only what is painted
A control the product has blocked is skipped: it carries that fact as data, and ringing it would be an instruction to press something that does nothing.
Visibility is tested by asking the browser for the element’s rectangles. That rejects the second, hidden copy of the page that streaming leaves in the document while it settles, and it still accepts a control inside the fixed drawer, where the cheaper test would wrongly report it as unrendered.
The search never stops while the step is open.
The ring clears the moment the step completes, because at that point the next instruction is in the bar and not on the screen. It is drawn as a coloured outline the product never uses for state, it changes neither the size nor the position of what it marks, and it stops pulsing for anyone who has asked for reduced motion.
Behind the label match sits a second pass over what the controls actually say, which is what carries the compliance screens. It looks only inside the product frame, never in the flow list or the bar, and it ignores labels shorter than eight characters: Edit and Open appear on every screen, and matching one of them would point somewhere arbitrary.
Outside events live outside the product
Some steps wait on something no reviewer can click into being: a bank credits an account, a partner rail settles, a custody clock runs out.
Those events have to be fireable or the flows that depend on them cannot be shown. In the previous build the console that fired them sat on the transaction page, and it was the single largest piece of scaffolding on any product screen: a permanent reminder, in the middle of the thing being judged, that the thing being judged was a prototype.
It now sits in the demo layer, as a drawer that belongs to the dark chrome rather than to the light product. The transaction page is exactly what the finished application would show. The drawer is visibly not part of it, it exists only while a step genuinely needs an outside event, and it opens itself when that step arrives, because a reviewer who has just been told to wait for a partner event should not also have to go looking for the panel. Closing it leaves a single button in the corner.
Inside, the console is unchanged: the same partner and rail events, the same exceptions, the reset to the seeded position, and the list of commands applied so far, so the demo can always account for how it got here. Only events that make sense at the current position are offered. Its buttons carry their labels the same way product controls do, so the ring lands on the right one inside the drawer.
29 steps across the escrow library are marked as waiting on an outside event, and their instructions all open with the same two words so the reviewer knows before reading further that this one is not theirs to perform. The compliance demo has no such steps. The drawer is the sixth file in the shell folder and the only one that reaches into a product, so it is the escrow layout that mounts it rather than the shell, and the shell stays ignorant.
What “Go to this step” is for
The product keeps its own sidebar, and a reviewer is meant to use it. This is what happens when they do.
Every step knows the screen it happens on, and on a transaction that includes the tab. The open tab lives in the address rather than in component state, so switching tabs is a navigation: a link can name one, the browser’s back button returns to the last one, and anything watching the address can tell which screen you are looking at. The bar compares the step’s path and its tab against where the browser actually is. When the two disagree, the control the sentence names is simply not on the screen and the ring has nothing to mark, which is the moment a reviewer decides the demo is broken rather than that they took a turn.
So the bar says so, and offers one press back. It is not a rail: nothing stops the reviewer wandering, and wandering is often the point, because a product you are only allowed to walk through in one line is not a product you can judge. The button only appears while the step is unfinished, since a completed step has no control left to return to.
One thing outranks it. If the step belongs to a seat the reviewer is not holding, the bar asks about the seat first and offers to hand it over on the spot, keeping the current step. Who acts is part of what is being demonstrated, so being on the right screen as the wrong person is the less useful of the two problems to solve first.
What that adds up to
61 escrow flows and 6 compliance flows, 264 step definitions between them, each with a seat, a sentence, a screen and a predicate over live state. One shell reading one shape. No step that passes without the record moving.