WordloopWordloop
Work

How We Work

A streamlined process for outcome-driven delivery, mapping strategic problems straight through to test-driven execution.

How We Work

This section describes how we move from an observed problem to shipped customer value. The process is lean by design, enforcing that technical execution is strictly bound to clear intent and verified by automated tests from the very beginning. It answers the fundamental question: How do you move fast without skipping the discovery that stops you building the wrong thing?

Work flows through four stages, each more concrete than the last:

Rendering architecture map...

Inside each stage

1. Problem Statement

Most wasted work is caused by excellent execution of the wrong thing. Skipping from idea to solution — without pausing to understand the problem — leads to building with false confidence.

A Problem Statement captures observed pain — real, evidenced, specific — alongside an appetite: a judgment about how much time this problem is worth solving.

  • Appetite is not an estimate of how long a solution will take. It is an opportunity cost judgment made before the solution is defined. You are betting that the problem is worth that much time.
  • Problem statements do not accumulate indefinitely. They are a curated list, updated as understanding evolves and retired when no longer relevant.
  • Platform and infrastructure problems are valid problem statements. The "who experiences it" can be internal — the engineering team, the system's reliability, the business's compliance posture. Feature bets routinely surface infrastructure gaps (e.g., a missing event backplane, no deletion cascade). The right response is to extract the gap as its own problem statement — not to expand the feature bet. The feature bet declares the constraint explicitly; the platform bet solves it.

Reviewing a Problem Statement

CheckWhat to verify
Pain is realDescribes observed friction with evidence (user feedback, metrics, incidents), not a hypothetical.
No solution leakingThe problem section describes pain, not a technical gap. "We need a WebSocket endpoint" is a solution. "Users can't see updates until they refresh" is a problem.
Appetite is a judgmentExplains the reasoning behind the time budget, not just a number. Reads as an opportunity cost decision.
Why-now is compellingSomething concrete has changed — not "it would be nice" but a real trigger.
Scope is boundedSpecific enough to shape into a pitch. Vague problems ("improve the user experience") can't be pitched.

Common pitfalls:

  • Solution masquerading as a problem: "We need to add real-time transcription" is a solution. The problem is "Users cannot follow meeting discussions in real-time because transcripts are only available after the meeting ends."
  • Missing evidence: "Users probably want X" is not evidence. Reference specific feedback, usage data, incidents, or engineering friction.
  • Appetite without reasoning: "4 weeks" is not an appetite judgment. "This affects ~60% of meetings. Four weeks is worth it to eliminate the manual workaround that takes 10 minutes per meeting" is.

2. Pitch

Unformed ideas become backlogs. Backlogs create the illusion that everything is captured and considered, when really they are lists of things nobody explicitly said no to.

Before a problem reaches the build phase, it is shaped into a Pitch. A pitch links a validated problem to a rough solution proposal. It is concrete enough to execute against but stays away from micro-detail.

A pitch must contain:

  • The problem — what was observed, who experiences it, why it matters now.
  • The appetite — how much time to spend.
  • A rough solution sketch — the general approach to the solution.
  • Rabbit holes — approaches already considered and ruled out. Include plausible-looking approaches that would blow the appetite or the scope, and infrastructure assumptions the bet makes (e.g., "we assume sticky sessions, not a backplane").
  • Explicit no-gos — what is completely out of scope. Include both obvious exclusions and natural extensions that users would reasonably expect but that don't belong in this version (e.g., pause/resume, mobile support, export/download). Vague no-gos invite scope creep — be specific about what's excluded and why.

A funded pitch becomes a Bet — a commitment bounded by the appetite.

Reviewing a Pitch

CheckWhat to verify
Problem traces to a statementThe pitch's problem section restates the validated problem statement, not a different problem.
Solution stays fat-markerThe proposed solution describes direction, not implementation. No endpoint paths, field names, or service-internal details.
Rabbit holes are genuineEach ruled-out approach is plausible and the rejection reason names a real cost (appetite, complexity, maintenance burden).
No-gos include natural extensionsThe list names capabilities users would reasonably expect but that are excluded — not just obvious unrelated features.
No-gos are specificEach exclusion explains what and why. "We won't do everything" is not a no-go. "Pause/resume recording is out because it requires server-side state management that exceeds the appetite" is.
Appetite is preservedThe proposed solution fits within the appetite. If it doesn't, either reshape the solution or increase the appetite with justification.

Common pitfalls:

  • Over-designed solution: If the pitch includes database schemas, API paths, or component hierarchies, it has gone too far. Those belong in the TDD.
  • Missing rabbit holes: Every problem has at least one plausible approach that should be avoided. An empty section suggests insufficient exploration.
  • Weak no-gos: No-gos that only list obviously unrelated features. The valuable no-gos are the natural extensions users expect — "download/export of recordings is out of v1 because it requires a transcoding pipeline."
  • Problem drift: The pitch describes a different or broader problem than the problem statement. The pitch should refine the same problem, not expand it.

3. TDD: Foundations

Technical Design bridges the intent of the pitch to parallel execution. We start by laying the technical foundation so that progress isn't blocked later by misaligned interfaces.

UI Design

The UI Design doc translates the pitch's rough solution sketch into concrete, screen-level detail. It answers: what exactly will the user see and do?

Organise it by screen — not by feature, not by user story. Each screen the bet touches gets its own section with:

  • A wireframe — even a rough sketch. This is the anchor; the text describes it.
  • Layout — the regions on screen and what content lives in each one.
  • States — what the user sees during loading, active use, empty states, errors, and degraded conditions.
  • Key interactions — what the user can do and what happens in response.

After the screens, map the user journeys between them (how the user moves from entry point to final outcome), and list edge cases (anything unusual the system needs to handle visibly).

Be specific about data objects. If a screen shows tasks, define what a task is: which fields it has, which are required, whether they nest, what states they can be in. If a screen has a text editor, say whether it's rich text or plain, whether it auto-saves or has a save button. These details directly determine the API contracts and database schema that come next.

Stay at the user level. If you're specifying which service owns the logic, how the frontend integrates, or where data persists — you've gone too far. The UI Design doc describes what the user experiences, not how the system delivers it. System concerns belong in the Data Flow doc.

The output feeds directly into: Data Flow diagrams (which service calls which), API contracts (what fields and endpoints exist), and database schemas (what gets stored). If someone can't design those artefacts from the UI Design doc alone, the doc isn't detailed enough.

Reviewing a UI Design

CheckWhat to verify
Every screen traces to the pitchNo screen appears that wasn't implied by the pitch's proposed solution.
States are completeEvery screen has loading, active, empty, and error states at minimum. Features with live connections have per-failure-mode states (Connecting, Degraded, Connectivity Lost, Reconnected).
Data objects are specifiedFields, required status, nesting, states, and editing behaviour are defined for every data type shown on screen.
Interactions stay user-levelNo service names, endpoint paths, or persistence details. "The task appears in the list" not "Core returns 201."
User journeys cover branchesHappy path AND error/recovery branches are mapped.
Downstream derivabilityA data flow designer can derive every service boundary from this document alone.

Common pitfalls:

  • Feature-oriented organisation: Grouping by feature ("Recording feature") instead of by screen. Screens are the unit of user experience; features span screens.
  • Missing empty and error states: Only the happy path is described.
  • Vague data objects: "The screen shows tasks" without defining fields, states, or editing behaviour. This ambiguity cascades into vague contracts and schemas.
  • Implementation leaking in: "Core sends a WebSocket event" or "The SWR cache is invalidated." UI Design describes what the user sees, not how the system delivers it.

Data Flows

The Data Flow doc maps every user interaction from the UI Design through service boundaries. It answers: what calls what, what data crosses each boundary, and what happens when something fails. It is the primary input for API contracts and database schemas — if someone can't design those artefacts from this doc alone, the doc isn't complete.

Start with a system context graph. Before drawing any sequences, draw the topology: which services exist, which protocols connect them, which data stores each service owns. This is a static map — it orients readers and makes the scope of the bet explicit.

Name flows after what triggers them. Group related flows into logical Parts (e.g., "Session Lifecycle", "Streaming Processing", "Failure Modes"). A flow name describes what the user does or what system condition fires — not the implementation.

Use descriptive operation labels — never endpoint paths. Diagram labels should read like Create task (idempotent, echo-suppressed) not POST /meetings/{id}/tasks. Header names, field names, and HTTP methods all belong in the Contracts doc. Each arrow in a flow is a contract boundary (what shape the data takes) and a sequencing constraint (downstream cannot build until upstream is agreed). Naming the operation is enough — the Contracts doc defines the shape precisely.

Failure modes are required, not optional. For every significant service boundary in the bet, there must be at least one flow describing what happens when that boundary fails. If the UI Design doc models a "Degraded" or "Connectivity Lost" state, the Data Flow doc must show the recovery sequence. Resilience is a first-class design concern — not an afterthought.

Close with two required sections:

  • Design Decisions — tradeoffs made, alternatives ruled out, constraints that drove choices. Captures reasoning that isn't visible in the diagrams.
  • Boundary Inventory — a table of every service-to-service boundary in the doc. Five columns: Boundary | Flows | From → To | Protocol | Data shape. Each row here becomes a contract entry in the Contracts doc.

Reviewing a Data Flow

CheckWhat to verify
System context matches scopeEvery node in the topology appears in at least one flow. No phantom services.
Every flow traces to a UI interactionNo flow exists without a triggering user action or system event from the UI Design.
Labels are descriptive, not implementationNo endpoint paths, HTTP methods, header names, or field names in diagram labels.
Failure modes are presentEvery significant service boundary has at least one failure flow. UI "Degraded" states have matching recovery flows here.
Design decisions explain tradeoffsEach decision names what was considered and why the chosen option won — not just "we chose X."
Boundary inventory is completeEvery service-to-service call in every flow appears in the boundary inventory.

Common pitfalls:

  • Implementation in diagram labels: POST /meetings/{id}/tasks or Authorization: Bearer appearing in sequence diagrams. Use descriptive labels; push implementation to Contracts.
  • Missing failure modes: Only happy-path flows are drawn. This is the most common defect.
  • Incomplete boundary inventory: Flows show 8 service calls but the inventory lists 5. The gap means 3 contract boundaries are invisible.
  • Design decisions without alternatives: "We chose WebSocket" without explaining what was considered (HTTP polling, SSE, HTTP streaming) and why WebSocket won.

Contracts & Schemas

The agreed API contracts (REST, WebSocket, Pub/Sub) and database schemas (PostgreSQL, object storage). Downstream UI can mock against the contract; upstream Core can build against it.

Choose the protocol by the interaction pattern:

ProtocolUse when
RESTCaller needs a clear success/failure response and the receiver owns resulting state.
WebSocketInteraction is bidirectional, low-latency, or session-oriented.
Pub/SubFire-and-forget event notification. Producer doesn't wait for consumer.

All Wordloop contracts share these semantics:

ConcernContract
AuthbearerAuth for user-facing; service auth / mTLS for service-to-service
Trace contextForward traceparent / tracestate
IdempotencyAll creating or side-effecting retryable POST operations require Idempotency-Key
Echo suppressionUser mutations accept Client-Session-Id when WebSocket echoes exist
ErrorsRFC 9457 application/problem+json

Schema design conventions:

  • TEXT + CHECK over PG enums — allows adding/removing values without a migration.
  • FK indexes are mandatory — PostgreSQL does not auto-index foreign key columns. Every FK carries an index.
  • JSONB for write-once-read-few — genuinely dynamic structure only. If individual keys are queried, filtered, or joined on, normalize into columns.
  • Append-only audit tables — status transitions tracked in a separate history table with (record_id, created_at) index.

Reviewing Contracts

CheckWhat to verify
Every contract traces to the boundary inventoryNo contract without a matching row. No boundary inventory row without a contract.
Shared semantics are appliedAuth, trace context, idempotency, echo suppression, and error format specified per operation.
Idempotency is explicitEvery side-effecting operation states its deduplication key.
Error responses use RFC 9457REST errors return application/problem+json with type, title, status, detail.
WebSocket reconnection is specifiedReplay cursor, reconnect strategy, duplicate handling documented.
Example payloads are realisticField values are realistic, not placeholders like "string" or "example."

Reviewing Schemas

CheckWhat to verify
Every table traces to a data flowNo table without a justifying persistent store in the data flow.
State machines are diagrammedTables with lifecycle states have a state diagram showing transitions and terminal states.
Every FK has an indexForeign key columns have corresponding indexes.
JSONB is justifiedEvery JSONB column explains why normalisation was rejected.
Index rationale is documentedThe index design table explains which query each index serves.
Naming is consistentTable and column names match the terminology in contracts and data flow.

4. TDD: Execution

Once the technical foundation is set, the bet is decomposed into deliverable units.

  • Integration Milestones: Points of user-visible value. This is the integration of multiple pieces that results in a cohesive feature or state change for the user.
  • Domain Slices: The smallest independently buildable and testable units of work. We never slice horizontally (e.g. building all databases, then all APIs, then all UI). We always slice vertically. A vertical slice could be a full feature connecting App -> Core -> ML, or a complete vertical slice completely within a single domain (e.g., being able to do CRUD on a Meeting in Core via the API). Slices must be independently deployable and verifiable.

Designing milestones

Order milestones by integration value. The first milestone delivers the simplest end-to-end flow that proves the architecture works; later milestones add richness. Each milestone must be independently shippable — users get value from milestone 1 even if milestone 2 never ships. If milestone 1 only produces database tables and milestone 3 adds the UI, that is horizontal slicing disguised as milestones.

Dependencies flow forward. Milestone 2 may depend on milestone 1, but never the reverse.

Reviewing a Milestone

CheckWhat to verify
Goal describes user-visible valueReads as an observable outcome, not a list of technical tasks.
Ordering has a rationale"Why This Comes Now" explains dependencies and what this milestone unblocks.
Scope is preciseA reader can tell whether a piece of work belongs in this milestone or another.
Not Included names natural extensionsExclusions include capabilities a reader would expect here but that belong later.
Slices are ordered by dependencyPrerequisites are explicit. Slice 1 must be mergeable before Slice 2 starts (unless independent).
Acceptance criteria are observableEach criterion can be verified by running the system — no source code reading.

Anatomy of a well-written slice

A slice that guides an AI agent or engineer to build the right thing has six parts:

  1. Owner / Domain / Complexity / Prerequisite callout — one line each. Complexity (S / M / L) signals the build window. Prerequisite names the exact prior merge gate — "Slice 1 merged + ./dev gen all" is specific enough to act on; "after Core" is not.
  2. One-paragraph intro — what does this vertical capability achieve, and why is it the right unit of work? Links to the milestone.
  3. Required Capabilities — an unchecked task list, grouped by logical area when the domain has distinct concerns (e.g. Schema / API / Events for a Core slice). These become the implementation checklist. Each item is a falsifiable statement of behaviour, not a vague description.
  4. Dependencies — concrete merge or configuration gates. If a generated client must be up to date, say "./dev gen all run after Slice 1 merged".
  5. Domain-specific section — one of:
    • Core: Schema Notes table (new/modified tables, key columns, index rationale)
    • ML: Pipeline Sequencing diagram (stage order, transition triggers, failure handling)
    • App: UI States table (state name, trigger condition, what the user sees)
  6. Test Cases — a table with Test | Location | Assertion columns. Location is one of test_core, test_ml, test_app, test_system. Assertions are specific and falsifiable ("returns 201 with Location header", not "works correctly"). These rows map directly to the generated test stub in tests/bets/<slug>/test_slice_<domain>_<slice>.py.

What a slice is not: A slice is not a summary of the contracts doc, not a user story, not a list of files to change. It is a vertical capability specification that tells an agent exactly what to build, in what order, and how to verify it is done.

The vertical slicing test: Can this slice be deployed to production and verified without any future slice existing? If yes, it is vertical. If it requires a downstream slice to be useful, it is either too thin or horizontally sliced.

  • Vertical (correct): "Upload audio file" slice — includes storage schema, upload endpoint, GCS integration, and API response. Deployable and verifiable on its own.
  • Horizontal (wrong): "Database schema" slice → "API endpoints" slice → "Frontend upload" slice. Nothing works until the last slice ships.

Falsifiable capabilities: Each item in Required Capabilities must be testable as true or false.

  • Good: "POST /meetings/{id}/audio returns 202 with a Location header pointing to the recording resource."
  • Bad: "Audio handling is implemented."

Specific test assertions: Each row in the Test Cases table must name the exact expected outcome.

  • Good: "Returns RFC 9457 problem+json with status 409 for duplicate Idempotency-Key."
  • Bad: "Handles duplicates."

Reviewing a Slice

CheckWhat to verify
Header is completeOwner, domain, complexity, and prerequisite specified. Prerequisite is an exact merge gate.
Intro describes target stateWhat the system does when complete — not the changes from current state.
Capabilities are falsifiableEach item can be verified as true or false. No vague descriptions.
Capabilities trace to contractsEvery API-facing capability references a contract endpoint. Every schema capability references a schema definition.
Dependencies are concreteMerge gates name the exact slice and post-merge steps (e.g., ./dev gen all).
Domain section matches domainCore has Schema Notes, ML has Pipeline Sequencing, App has UI States.
Test cases have specific assertionsEvery row has a falsifiable assertion.
Slice is verticalDeployable and verifiable independently. Does not require a downstream slice.

Bet Test Suite

Every milestone and slice has a corresponding test in the bet progress suite — a set of system-level tests that prove delivered capability. These tests are the single source of truth for bet progress. Red means work to do; green means proven.

How the bet test suite works:

  1. When a milestone or slice is scaffolded via ./dev new milestone or ./dev new slice, empty test stubs are generated in tests/bets/<slug>/.
  2. Test stubs are filled in as part of slice execution, verifying observable end-to-end outcomes.
  3. Run the full suite on demand with ./dev test bet <slug> to see an at-a-glance progress report: which milestones are complete, which slices within the current milestone are done, and which are still red.
  4. These tests are system-level only — they exercise the running system end-to-end, not individual units. They are manually triggered because they are slow (they start services, hit real databases, call real APIs).
  5. Because they test integration outcomes, they can be written before the production code exists. The test describes the target behaviour; the implementation makes it green.

Lifecycle:

  • During the bet — the suite tracks progress. A passing suite means the milestone or slice is provably done.
  • After a slice is complete — full test coverage is implemented according to the project's testing strategy: service perimeter tests, unit tests for complex logic, and system tests as appropriate. These permanent tests live in the service repos, not the bet suite.
  • After the bet is delivered — the bet test suite is archived via ./dev archive bet <slug>. It moves to tests/bets/_archive/<slug>/. These tests are not maintained going forward — the features are absorbed into the system and covered by permanent service tests.

Two layers of testing:

Bet progress suiteService and system tests
Locationtests/bets/<slug>/Service repo or tests/system/
LifecycleTemporary — archived with betPermanent — stays in codebase
ScopeEnd-to-end integration outcomesPerimeter, unit, contract, system
Happy pathYesYes
Error handlingMinimalThorough
Edge casesRarelyYes
ObservabilityNoYes (trace validation)
Contract complianceNoYes (consumer-driven)

Test design principles:

  • Test the target state. Tests describe what the system does when the bet is done. Write the test as if the feature already exists.
  • System-level only. Bet tests make HTTP requests and verify end-to-end flows. They do not import application code or mock dependencies.
  • Specific and falsifiable. Every assertion maps to a row in the slice's Test Cases table.
  • Written before the code exists. The test describes target behaviour; the implementation makes it green.

Completing a Slice

A slice is not done when the code works. A merged slice means the capability is shipped, tested, reviewed, and documented. The following checklist applies to every completed slice:

  1. Code merged and deployed — the slice is running in the system, not sitting in a branch.
  2. Bet test green — the corresponding bet progress tests pass via ./dev test bet <slug>.
  3. Permanent tests implemented — full service-level test coverage per the testing strategy. Service perimeter tests (default), unit tests (complex logic), system tests (cross-service paths).
  4. Code review — peer review of implementation quality, adherence to architectural principles, and consistency with the slice spec.
  5. API review — any new or modified API surfaces reviewed for consistency with contracts, error handling, and naming conventions.
  6. Testing review — test coverage reviewed for completeness: happy paths, error paths, edge cases, and observability assertions.
  7. Documentation updated — system documentation that is affected by this slice is brought up to date:
    • Architecture and service descriptions (if the slice changes system topology or responsibilities)
    • Data flow documentation (if new boundaries or protocols are introduced)
    • API reference (auto-generated from specs where possible, manually updated where not)
    • Database reference (if schema changes are included)
    • Runbooks and operational guides (if new failure modes or operational concerns are introduced)

Writing Guidelines

Bet documentation is read by engineers and AI agents under time pressure. Every sentence should earn its place.

Describe the Target State

All bet documentation — pitches, TDD docs, milestones, slices — describes the target state of the system. There are no diffs, no gap analyses, no references to what already exists or what needs to change. Just a clear description of how the system works when the work is done.

This simplifies consumption. A reader should be able to pick up any document and understand the end goal without needing to know what the system looks like today. When designing the target state, build on top of what already exists to avoid excessive drift, but the documentation itself does not express the current state, the target state, and the delta between them. That analysis happens during slice execution as part of the implementation plan — it does not live in these docs.

Do:

  • "The meeting detail page shows a live transcript panel that updates as segments arrive."
  • "POST /meetings/{id}/audio stores the file to GCS and publishes a transcription.requested event."

Don't:

  • "Currently, meetings don't support audio upload. We need to add a new endpoint..."
  • "The existing meeting list page will be extended to include..."
  • "Unlike the current implementation, the new version will..."

Tone and Style

  • Direct and declarative. State what the system does, not what it should or could do. "The upload endpoint returns 202" not "The upload endpoint should return 202."
  • Concrete over abstract. Name the endpoint, the field, the state, the event. Specificity prevents ambiguity.
  • Short sentences. One idea per sentence. Break complex statements into separate bullets or rows.
  • No filler. Cut words like "basically", "essentially", "in order to", "it is important to note that". If a sentence works without the word, remove it.
  • Active voice. "The service publishes an event" not "An event is published by the service."
  • Consistent terminology. Use the same term for the same concept everywhere. If it's a "segment" in the contract, it's a "segment" in the slice, the milestone, and the test case. Don't alternate between "segment", "chunk", and "piece".

Structure for Progressive Exposure

Documents should reward scanning. A reader should get the key message in 10 seconds, the full picture in 2 minutes, and the details on demand.

  1. Frontmatter — title, description, status, and ownership. Enough to decide whether to read further.
  2. Opening line or paragraph — the goal or purpose in one or two sentences. No preamble.
  3. Primary sections — the main content, ordered by importance. The most critical information comes first.
  4. Detail sections — tables, test cases, schema notes, domain-specific content. Readers arrive here when they need to implement or verify.

Use tables for structured, repeating information (test cases, schema columns, states). Use bullet lists for sequential or grouped items. Use prose only when relationships between ideas matter more than the individual items.


Document Chain Integrity

Each document in the bet lifecycle consumes its upstream and produces for its downstream. When this chain breaks — a data flow that invents boundaries not in the UI design, or a slice with test cases that don't trace to milestone acceptance criteria — the bet has a structural defect. Review is the practice of verifying the chain holds.

DocumentUpstream checkDownstream check
Problem StatementIs the pain real, evidenced, specific?Does the pitch restate this problem (not a different one)?
PitchDoes it solve the stated problem within the appetite?Do TDD success criteria map to the pitch's solution?
UI DesignDoes every screen trace to a capability in the pitch?Can the data flow be derived from these screens alone?
Data FlowDoes every flow trace to a UI interaction?Does the boundary inventory account for every service call?
ContractsDoes every contract trace to the boundary inventory?Do slice capabilities reference these contracts?
SchemasDoes every table trace to a persistent store in the data flow?Do slice schema notes reference these tables?
MilestonesDo acceptance criteria trace to TDD success criteria?Does every slice belong to exactly one milestone?
SlicesDo capabilities trace to contracts and schemas?Do test cases trace to milestone acceptance criteria?

Bet Operations

By utilizing the Golden Path CLI tools, documentation is kept exactly in sync with the integration testing layout.

Start a new bet

./dev new bet <slug>

Promotes a pitch into an active bet at work/<slug>/ and creates the baseline test boundary suite in tests/bets/<slug>/. The slug must be lowercase kebab-case (e.g. speaker-navigation). A pitch must exist first — run ./dev new pitch <slug>.

Scaffolding Architecture (TDD)

# Scaffolds architectural boundaries
./dev new contract <bet-slug> <service> <protocol>
./dev new schema <bet-slug> <service> <database-tech>

# Scaffolds milestones and domains
./dev new milestone <bet-slug> <milestone-slug>
./dev new slice <bet-slug> <milestone-slug> <domain> <slice-slug>

Generating a slice or a milestone will drop corresponding placeholder testing boundaries in tests/bets/.

Run bet progress tests

./dev test bet <slug>

Runs the bet progress suite on demand. Watch the test output to verify that your delivery is progressing as intended.

Archive a delivered bet

./dev archive bet <slug>

Moves the bet directory to _archive/ and the associated test suite to tests/bets/_archive/<slug>/. URL routing is preserved.

On this page