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.
“An invitation keeps the role the owner selected.”
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.
Smoke tests load Billing by URL. They don’t follow the settings navigation.
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.
The invitation still sends, but an owner can no longer choose the new member’s role. This PR was intended to update copy.
“An invitation keeps the role the owner selected.”
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 knowledgebusiness-rules / team-access.mdMarkdownNorthstar 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.
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.
| Role | Invite teammates | Manage orders |
|---|---|---|
| Owner | Yes | Edit & refund |
| Admin | Yes | Edit & refund |
| Editor | No | Edit & refund |
| Viewer | No | Read only |
src/team/invitations.tssrc/auth/permissions.ts/settings/teaminvite-memberaccept-invitationverify-roleInvite a viewer, accept as that person, then confirm that order editing is unavailable.
# 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.mdMarkdownMaya manages the Northstar store. She invites Alex to check order statuses without changing orders or customer data.
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.
| 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. |
src/team/invitations.tssrc/auth/accept-invitation.ts/joininvite-memberaccept-invitationverify-roleThe accepted membership has the same workspace, email address and role as the invitation.
# 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.mdMarkdownA customer buys two Field notebooks with a ten-percent discount. Checkout, payment and the confirmation email must agree on the amount paid.
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.
| Line item | Amount |
|---|---|
| 2 × Field notebook | $40.00 |
| WELCOME10 · 10% off merchandise | −$4.00 |
| Standard shipping | $5.00 |
| Total charged | $41.00 |
src/orders/checkout.tssrc/orders/totals.ts/checkoutseed-cartplace-orderverify-totalCheckout total = payment amount = saved order total = email receipt total.
# 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.mdMarkdownAn owner, admin or editor can fully refund a paid order before dispatch. The customer gets back the amount paid, including standard shipping.
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.
| Record | Before | After |
|---|---|---|
| Order | Paid | Refunded |
| Refunded amount | $0.00 | $41.00 |
| Available notebooks | 18 | 20 |
src/orders/refunds.tssrc/inventory/adjust-stock.ts/orders/order-1042place-orderrefund-orderverify-refundThe payment, order status and inventory agree after the refund. A second request changes none of them.
# 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.mdMarkdownUse these terms when describing business rules and writing flows. A workspace is the team’s shared store, not an individual’s account.
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.
| 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. |
src/team/membership.tssrc/inventory/stock.ts/settings/teamswitch-workspaceverify-roleSwitching workspace uses the membership for the destination, not the previous workspace.
# 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.mdGeneratedA generated route index provides starting points for exploration. A route’s presence does not establish its expected behavior or test coverage.
Read the business rules alongside the route index.
The agent records observations in authored documents after inspecting the source and exploring the application.
| 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 |
src/routes.ts/The route index itself is not a test.
# 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.
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 ┌──────────────┐
│ seed-data │
└──────┬───────┘
│
┌─────────────┴─────────────┐
│ │
┌──────▼───────┐ ┌──────▼───────┐
│ invite-member│ │ edit-profile │
└──────┬───────┘ └──────────────┘
│
┌──────▼───────┐
│ verify-role │
└──────┬───────┘
▼
┌──────────────┐
│ accept-invite│
└──────────────┘ Keep the agent and workflow you already use. Give the review what the application did before the change and what it does now.
Reads the change, chooses the checks, and uses your application’s rules to say which differences deserve a closer look.
Connect through MCPRuns the dependency graph, saves what each run showed, stored and returned, and compares the previous run with the current one.
Plan, run, compareSee the changed screen, the saved result, and the agent’s note. Open both records and decide what to merge.
Bring it to the PRDefine 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 languageimport { 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(); 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');});team.invite email="sam@example.test" role="editor"team.expect_invitation email="sam@example.test" role="editor"team is an application-specific plugin. Its implementation is maintained separately.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.
$ 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.
CI is green. Here’s what changed in the product.
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.
See before & after snapshots
Before · main
After · #1578
Removed: link “Billing” → /settings/billing
“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.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?