Your agents test your code, not your product.

Green CI can still hide a broken journey.

Give your agent a living map of your product: its journeys, APIs and rules, maintained in application knowledge. Reflow runs the flows you define and keeps the snapshots. Your agent can check what changed against what should happen, right where coding agents are at their worst.

Bring your own agent. Claude Code, Codex or your preferred MCP client.

Closed beta. Sign in to register interest.
App access is limited to approved teams.

Refactor settings navigation #1578
agent/settings-nav main38 files changed
unit tests 1,214passed
typecheck + lintpassed
e2e smoke 42passed

Smoke tests load Billing by URL. They don’t follow the settings navigation.

Your agentReflow evidence3 product findings

CI is green. Here’s what changed in the product.

Unreachable screen

Billing still exists. The way in doesn’t.

The Billing flow can no longer follow Settings → Billing. The direct URL still loads, but the navigation link captured in the previous run is gone.

DashboardSettingsBilling
/settings/billing saved journey broken
See before & after snapshots

Before · main

- navigation "Settings":
  - link "Profile":
    - /url: /settings/profile
  - link "Team":
    - /url: /settings/team
  - link "Billing":
    - /url: /settings/billing

After · #1578

- navigation "Settings":
  - link "Profile":
    - /url: /settings/profile
  - link "Team":
    - /url: /settings/team

Removed: link “Billing” → /settings/billing

Dead API

“Resend invite” calls a route that doesn’t exist.

Click “Resend invite” → POST /invites/resend → 404. The flow receives no successful response, and its mailbox check finds no invitation.

web/InviteRow.tsx:48 404 Not Found
Flip-flop For review

The plan banner moved. Again.

Your agent compares four retained runs: top, bottom, sidebar, then top again. Is this move part of the intended change?

#1482 bottom → top#1522 bottom#1570 sidebar#1578 top
2 broken journeys. 1 change worth a second look.
Illustrative agent review · example flows and retained runs

Put the application change
in the pull request.

Show your reviewer the missing control, the changed result and the rule they affect. Your agent can explain which changes belong in scope and which need a closer look.

Keep the screenshots and flow results one click from the review, with a change summary that updates as the run progresses.

reflow / Pull request reviewIllustrative example
Pull request #184

Update invitation copy

copy/team-invite → main · head 9c1e2f7
invite-memberPassed
verify-roleFailed
accept-inviteBlocked
team-invitation.pngChanged
Application reviewPrepared note

The role selector disappeared.

The invitation still sends, but an owner can no longer choose the new member’s role. This PR was intended to update copy.

Before
AcmeWorkspace Invite a teammate Add someone to your workspace. Email address sam@acme.test RoleEditor Send invitation
After
AcmeWorkspace Invite a teammate Add someone to your workspace. Email address sam@acme.test Role selector removed Send invitation
Application rule · team-access.md

“An invitation keeps the role the owner selected.”

Before / after capturesFlow results

A living map of
your product.

Who can invite a teammate? What should a refund restore? Your agent records those rules in Reflow’s application knowledge repository and updates them as the app changes. It reads and writes that knowledge through MCP.

Connect those pages to the flows that check them. Keep the knowledge current as your product changes, so a fresh agent session can pick up the journeys and rules that matter.

Explore application knowledge
reflow / NorthstarIllustrative example

Application knowledge

Business rules, journeys and the source behind them.
business-rules / team-access.mdMarkdown

Team access & permissions

Northstar is a shared workspace for a commerce team. A person’s role determines what they can change, from inviting a teammate to issuing a refund.

Expected behavior

An invitation preserves the role selected by its sender.

Owners and admins can invite editors and viewers. Accepting an invitation grants exactly that role; it never promotes someone to owner.

Who can do what

RoleInvite teammatesManage orders
OwnerYesEdit & refund
AdminYesEdit & refund
EditorNoEdit & refund
ViewerNoRead only

References

src/team/invitations.tssrc/auth/permissions.ts/settings/team

Flows that check this rule

  • invite-member
  • accept-invitation
  • verify-role

Invite a viewer, accept as that person, then confirm that order editing is unavailable.

Read Markdown original
# Team access & permissions

Northstar is a shared workspace for a commerce team. A person’s role determines what they can change, from inviting a teammate to issuing a refund.

## Expected behavior

An invitation preserves the role selected by its sender.

Owners and admins can invite editors and viewers. Accepting an invitation grants exactly that role; it never promotes someone to owner.

## Who can do what

| Role | Invite teammates | Manage orders |
| --- | --- | --- |
| Owner | Yes | Edit & refund |
| Admin | Yes | Edit & refund |
| Editor | No | Edit & refund |
| Viewer | No | Read only |

## References

- src/team/invitations.ts
- src/auth/permissions.ts
- /settings/team

## Flows

- invite-member
- accept-invitation
- verify-role

Invite a viewer, accept as that person, then confirm that order editing is unavailable.

## Still to explore

An invitation sent before its sender changes role has not been explored.
journeys / invite-a-teammate.mdMarkdown

Invite a teammate

Maya manages the Northstar store. She invites Alex to check order statuses without changing orders or customer data.

Expected behavior

Alex joins the same workspace with the Viewer role.

The invitation is tied to Alex’s email address and selected role. Signing in with a different address must not accept it.

At each step

StepExpected behavior
Send invitationEmail and selected role appear in pending invitations.
Accept invitationThe invited address is verified before joining.
Open the workspaceOrders are readable; editing controls are unavailable.

References

src/team/invitations.tssrc/auth/accept-invitation.ts/join

Flows that check this rule

  • invite-member
  • accept-invitation
  • verify-role

The accepted membership has the same workspace, email address and role as the invitation.

Read Markdown original
# Invite a teammate

Maya manages the Northstar store. She invites Alex to check order statuses without changing orders or customer data.

## Expected behavior

Alex joins the same workspace with the Viewer role.

The invitation is tied to Alex’s email address and selected role. Signing in with a different address must not accept it.

## At each step

| Step | Expected behavior |
| --- | --- |
| Send invitation | Email and selected role appear in pending invitations. |
| Accept invitation | The invited address is verified before joining. |
| Open the workspace | Orders are readable; editing controls are unavailable. |

## References

- src/team/invitations.ts
- src/auth/accept-invitation.ts
- /join

## Flows

- invite-member
- accept-invitation
- verify-role

The accepted membership has the same workspace, email address and role as the invitation.

## Still to explore

Expired invitations still need a separate journey.
business-rules / checkout.mdMarkdown

Checkout totals

A customer buys two Field notebooks with a ten-percent discount. Checkout, payment and the confirmation email must agree on the amount paid.

Expected behavior

Charge the total the customer approved at checkout.

Apply the discount to merchandise before adding shipping. Store monetary amounts in cents and round the discount once, for the whole order.

Worked example · USD

Line itemAmount
2 × Field notebook$40.00
WELCOME10 · 10% off merchandise−$4.00
Standard shipping$5.00
Total charged$41.00

References

src/orders/checkout.tssrc/orders/totals.ts/checkout

Flows that check this rule

  • seed-cart
  • place-order
  • verify-total

Checkout total = payment amount = saved order total = email receipt total.

Read Markdown original
# Checkout totals

A customer buys two Field notebooks with a ten-percent discount. Checkout, payment and the confirmation email must agree on the amount paid.

## Expected behavior

Charge the total the customer approved at checkout.

Apply the discount to merchandise before adding shipping. Store monetary amounts in cents and round the discount once, for the whole order.

## Worked example · USD

| Line item | Amount |
| --- | --- |
| 2 × Field notebook | $40.00 |
| WELCOME10 · 10% off merchandise | −$4.00 |
| Standard shipping | $5.00 |
| Total charged | $41.00 |

## References

- src/orders/checkout.ts
- src/orders/totals.ts
- /checkout

## Flows

- seed-cart
- place-order
- verify-total

Checkout total = payment amount = saved order total = email receipt total.

## Still to explore

Combining discount codes is not documented.
business-rules / refunds.mdMarkdown

Refunds & returned stock

An owner, admin or editor can fully refund a paid order before dispatch. The customer gets back the amount paid, including standard shipping.

Expected behavior

A full refund reverses the payment and returns the items to stock.

For the notebook order, return $41.00 and restore two notebooks. Repeating the request must not refund or restock the order twice.

Expected changes

RecordBeforeAfter
OrderPaidRefunded
Refunded amount$0.00$41.00
Available notebooks1820

References

src/orders/refunds.tssrc/inventory/adjust-stock.ts/orders/order-1042

Flows that check this rule

  • place-order
  • refund-order
  • verify-refund

The payment, order status and inventory agree after the refund. A second request changes none of them.

Read Markdown original
# Refunds & returned stock

An owner, admin or editor can fully refund a paid order before dispatch. The customer gets back the amount paid, including standard shipping.

## Expected behavior

A full refund reverses the payment and returns the items to stock.

For the notebook order, return $41.00 and restore two notebooks. Repeating the request must not refund or restock the order twice.

## Expected changes

| Record | Before | After |
| --- | --- | --- |
| Order | Paid | Refunded |
| Refunded amount | $0.00 | $41.00 |
| Available notebooks | 18 | 20 |

## References

- src/orders/refunds.ts
- src/inventory/adjust-stock.ts
- /orders/order-1042

## Flows

- place-order
- refund-order
- verify-refund

The payment, order status and inventory agree after the refund. A second request changes none of them.

## Still to explore

Partial refunds and returns after dispatch are not covered by this rule.
application / glossary.mdMarkdown

The language of Northstar

Use these terms when describing business rules and writing flows. A workspace is the team’s shared store, not an individual’s account.

Expected behavior

A membership connects one person to one workspace and one role.

The same person may belong to several workspaces. Permissions are evaluated within the workspace they are using.

Terms used in this application

TermMeaning
WorkspaceA store, its orders and its team.
MembershipA person’s role in one workspace.
InvitationA pending offer of membership to an email address.
Available stockUnits that can be sold; excludes units in paid orders.

References

src/team/membership.tssrc/inventory/stock.ts/settings/team

Flows that check this rule

  • switch-workspace
  • verify-role

Switching workspace uses the membership for the destination, not the previous workspace.

Read Markdown original
# The language of Northstar

Use these terms when describing business rules and writing flows. A workspace is the team’s shared store, not an individual’s account.

## Expected behavior

A membership connects one person to one workspace and one role.

The same person may belong to several workspaces. Permissions are evaluated within the workspace they are using.

## Terms used in this application

| Term | Meaning |
| --- | --- |
| Workspace | A store, its orders and its team. |
| Membership | A person’s role in one workspace. |
| Invitation | A pending offer of membership to an email address. |
| Available stock | Units that can be sold; excludes units in paid orders. |

## References

- src/team/membership.ts
- src/inventory/stock.ts
- /settings/team

## Flows

- switch-workspace
- verify-role

Switching workspace uses the membership for the destination, not the previous workspace.

## Still to explore

Guest access is not defined in the current application model.
generated / routes.mdGenerated

Application routes

A generated route index provides starting points for exploration. A route’s presence does not establish its expected behavior or test coverage.

Expected behavior

Read the business rules alongside the route index.

The agent records observations in authored documents after inspecting the source and exploring the application.

Discovered routes

PathPurpose
/settings/teamMembers and pending invitations
/joinAccept an invitation
/checkoutReview and pay for a cart
/orders/:idOrder details and refunds

References

src/routes.ts/

Coverage

    The route index itself is not a test.

    Read Markdown original
    # Application routes
    
    A generated route index provides starting points for exploration. A route’s presence does not establish its expected behavior or test coverage.
    
    ## Expected behavior
    
    Read the business rules alongside the route index.
    
    The agent records observations in authored documents after inspecting the source and exploring the application.
    
    ## Discovered routes
    
    | Path | Purpose |
    | --- | --- |
    | /settings/team | Members and pending invitations |
    | /join | Accept an invitation |
    | /checkout | Review and pay for a cart |
    | /orders/:id | Order details and refunds |
    
    ## References
    
    - src/routes.ts
    - /
    
    ## Coverage
    
    
    
    The route index itself is not a test.
    
    ## Still to explore
    
    Unlinked and permission-restricted screens may require further exploration.
    Fictional application · sample content

    Plan tests around
    their dependencies.

    The invitation flow depends on seeded data, and the role check depends on an invitation. Reflow runs them in that order.

    Your agent declares prerequisites with needs. Before execution, Reflow explains which work will run, reuse completed results, wait for dependencies, or stay blocked. A little like Terraform for testing.

    Understand the execution model
    Test dependency graphProduct example
    The invitation and profile flows both depend on seed-data.

    Code review. Bring your own agent.

    Keep the agent and workflow you already use. Give the review what the application did before the change and what it does now.

    Your agent

    Reads the change, chooses the checks, and uses your application’s rules to say which differences deserve a closer look.

    Connect through MCP

    Reflow

    Runs the dependency graph, saves what each run showed, stored and returned, and compares the previous run with the current one.

    Plan, run, compare

    Your review

    See the changed screen, the saved result, and the agent’s note. Open both records and decide what to merge.

    Bring it to the PR

    A testing DSL
    for your application.

    Define reusable plugin commands for your application’s tests. Your agent writes tests in the language of your application.

    Compact flows keep routine browser details out of your agent’s context, leaving more room for product rules, edge cases and the change under review.

    Build your testing language
    Use Team commands in the invite testReuse the Team plugin’s invitation commands
    #1582
    Files changed 2−29+2
    tests/invite-editor.spec.tsPlaywright
    import { test, expect } from '@playwright/test';
    test('invite an editor', async ({ page }) => {
    await page.goto('/settings/team');
    await expect(page.getByRole('heading', {
    name: 'Team members', exact: true,
    })).toBeVisible();
    Show 22 more removed lines
    await page.getByRole('button', {
    name: 'Invite member', exact: true,
    }).click();
    const dialog = page.getByRole('dialog', {
    name: 'Invite a teammate',
    });
    await expect(dialog).toBeVisible();
    await dialog.getByLabel('Email').fill('sam@example.test');
    await dialog.getByLabel('Role').selectOption('editor');
    const sent = page.waitForResponse(response =>
    response.url().endsWith('/api/invitations') &&
    response.request().method() === 'POST'
    );
    await dialog.getByRole('button', { name: 'Send invite' }).click();
    expect((await sent).status()).toBe(201);
    await expect(dialog).not.toBeVisible();
    const member = page.getByRole('row').filter({
    hasText: 'sam@example.test',
    });
    await expect(member).toContainText('Editor');
    await expect(member).toContainText('Invited');
    });
    .reflow/flows/invite-editor.mdFlow steps · excerpt
    @@ invitation steps · excerpt @@
    team.invite email="sam@example.test" role="editor"
    team.expect_invitation email="sam@example.test" role="editor"
    Two commands in the flow.The selectors, waits and assertions live in the plugin. Your agent can inspect them when it needs to.
    Illustrative excerpts · team is an application-specific plugin. Its implementation is maintained separately.

    Scale the testing.
    Keep the context focused.

    Your agent can run independent flows in parallel and read selected knowledge and test results through tool calls.

    Start with a run summary, then inspect the relevant knowledge and snapshots. The agent can work across the application without loading the whole repository or every test result into its context window.

    A refund, in your application’s language.Custom plugin example
    $ reflow apply
      customer → order → refund → verify
    
     ✓ Prepare the customer
       shop.customer id="alex" balance=100
       CUSTOMER       BALANCE       INVENTORY
       Alex           $100.00       10 available
    
     ✓ Place an order
       shop.place_order customer="alex" total=25
       ORDER          BALANCE       INVENTORY
       paid           $75.00        9 available
    
     ✓ Refund the order
       shop.refund_order customer="alex"
       ORDER          paid → refunded
       BALANCE        $75.00 → $100.00
       INVENTORY      9 → 10
    
     ✓ Assert the business outcome
       shop.expect_balance customer="alex" amount=100
       shop.expect_stock count=10
       shop.expect_order status="refunded"
       shop.snapshot name="after-refund"
       ✓ Balance restored  ✓ Stock restored
       ✓ Order refunded    snapshot: after-refund
    
      4 flows passed. Snapshot ready for review.

    Your agent.
    Evidence for your next review.

    Connect your agent