Skip to content

Product development workflow

This is OGUR's durable product-development process for work performed by founders, Claude Code, and Codex. The active scope and release dates live in mvp-scope.md; this document defines how an idea becomes a shipped change without quietly becoming a new milestone.

The workflow

flowchart TD
    Idea["Feature idea or user request"] --> PM["Product manager gate<br/>read-only; emits a structured verdict"]
    PM --> Verdict["Structured verdict<br/>verdict + disposition + scope citation"]
    Verdict --> Intake["/linear-feature-intake<br/>the only Linear writer;<br/>records the decision, never re-decides it"]
    Intake -->|"STOP"| Stop["Decision record only; no execution ticket"]
    Intake -->|"EVIDENCE"| Research["One blocked customer-research issue"]
    Research --> PM
    Intake -->|"BACKLOG"| Backlog["One Backlog issue in the future milestone"]
    Intake -->|"SPEC"| Design["Designer"]
    Intake -->|"SPEC"| Tech["Technical lead"]
    Design --> Spec["One implementation spec"]
    Tech --> Spec
    Spec --> Founder["Founder approves scope, estimate, and cut"]
    Founder --> Issues["Meta issue + implementation children in Linear"]
    Issues --> Build["Implementation agent"]
    Issues -.->|"not built"| Dispatch["/dispatch-implementation<br/>selects harness + model; owns routing only"]
    Dispatch -.-> Plan["Executor plan-only session<br/>read-only; returns a technical plan"]
    Plan -.-> PlanOK["Plan approval<br/>checked against the approved spec"]
    PlanOK -.-> Build
    Build --> EngineReview{"Diff touches<br/>ogur/engine/?"}
    EngineReview -->|"yes"| ProofReview["review-engine on the resolved PR head<br/>(+ coverage-eval for discovery_modality)"]
    EngineReview -->|"no"| QA
    ProofReview --> QA["QA executes acceptance gate"]
    QA -->|"FAIL"| Build
    QA -->|"PASS"| Audit["/linear-release-audit<br/>gates vs Linear + GitHub evidence"]
    Audit -->|"gate not met"| Build
    Audit -->|"all gates met"| Release["Release"]
    Release --> Learn["User evidence and release evidence"]
    Learn --> Decision["Decision log or ADR"]
    Decision --> PM
    Steward["/linear-steward<br/>keeps the board's claims true"] -.->|"audits, does not decide"| Issues

The expensive path starts only after the product-manager gate. Rejected ideas do not receive designs, estimates, implementation plans, or speculative branches.

The product-manager → Linear handoff

The gate decides; Linear records. Those are two jobs and they are held by two things, because a component that both decides and creates will find a reason to create.

product-manager emits, alongside its prose verdict, one machine-readable block:

proposal:            # one sentence, the PM's words
verdict:             # IN-MVP-1 | IN-MVP-2 | OUT | NEEDS-EVIDENCE
milestone:           # the milestone the verdict places it in; null for OUT
scope_citation:      # the quoted line of mvp-scope.md the verdict rests on
user_evidence:       # named evidence, or null
reversal_condition:  # the concrete observable that would change the verdict
disposition:         # STOP | EVIDENCE | BACKLOG | SPEC

disposition is what /linear-feature-intake switches on. The valid pairings are OUTSTOP, NEEDS-EVIDENCEEVIDENCE, a non-active milestone→BACKLOG, and the active milestone→SPEC; any other pairing is a malformed gate output and the intake skill stops rather than picking a half.

Three properties are load-bearing:

  • product-manager remains the sole scope authority. /linear-feature-intake performs no assessment of its own and may not override, soften, or re-interpret a verdict.
  • product-manager remains read-only. It has no Edit or Write, and it does not touch Linear. The handoff is either the PM workflow invoking the intake skill, or an orchestrator passing the verdict block to it.
  • OUT never becomes a backlog feature. It yields a decision record — status Done, carrying the reversal condition — and no execution ticket in any form.
  • One execution record per accepted idea. An idea that already has a Linear issue is updated, never duplicated. The intake skill's §2 duplicate search runs before any write, and finding an equivalent issue stops the run.
  • No parallel backlog file. Linear is the only execution record; /spec does not maintain a Markdown backlog beside it. OUT verdicts append one line to docs/product/spec-log.md for gate calibration — that log schedules nothing and records nothing accepted, so it is an audit trail, not a second board.

Status. The structured block is the contract the intake skill is written against, and product-manager emits it as of PR #223 — so --verdict is live rather than blocked. --verdict --spec is the persistence-only path /spec uses after it has already run the gate and written the spec; supplying --spec means the intake skill must not invoke /spec, which is what keeps the two orchestrators from calling each other.

Linear skills

Three harness-neutral skills implement the Linear half of this workflow. Their canonical definitions are in .agents/skills/; the files under .claude/skills/ are adapters that point at them, so Claude Code and Codex cannot drift. All three read linear-workspace-contract.md first — the statuses, the one-primary-project rule, what Done requires per work type, and the mutation-safety protocol.

Skill Answers Default
/linear-steward Are the board's claims true? Includes focused GitHub→Linear finished-work reconciliation read-only audit; explicit apply writes
/linear-feature-intake What records does this verdict imply? writes only what the disposition implies
/linear-release-audit Would the release gate pass today? read-only; --apply writes

The rule they share, and the reason they are worth having: a merged implementation PR is not evidence that a release gate passed. The gate needs the gate's own acceptance artifact. Everything else in the contract follows from taking that seriously.

Downstream dependency — dispatch-implementation (named, not built)

The chain above stops at "implementation agent", which is a hand-wave: today a human picks a harness, opens a session, and hopes the session does not re-open scope. The seam that closes it is a separate harness-neutral skill, dispatch-implementation. It does not exist yet. It is named here so the skills that feed it are built against a known consumer rather than retro-fitted to one.

product-manager (#223)
  → linear-feature-intake
  → founder approval
  → dispatch-implementation
  → executor plan-only session
  → plan approval
  → implementation session
  → draft PR
  → independent review / eval gates
  → Linear read-back

It is a dispatcher, not a fourth gate. It consumes an approved, implementation-ready Linear issue and may not reconsider scope, rewrite acceptance criteria, or create a second ticket — the same three refusals linear-feature-intake carries, for the same reason: a component that both routes and decides will decide in favour of routing.

Specification is not planning

The originating harness may produce the product specification — user problem, scope decision, constraints, acceptance criteria, reversal condition. It may not silently produce and approve the executor's technical plan. Those are two artifacts, authored by two parties, and they are preserved separately in the handoff.

Every dispatched executor therefore starts in a hard plan-only stage:

Executor Plan-only mechanism
Claude Launch with --permission-mode plan. It may inspect the repository and return a technical plan; it may not edit
Codex The installed CLI has no literal --plan switch. Launch codex exec --sandbox read-only with a plan-only structured-output schema — the filesystem boundary, not a prompt, is what prevents implementation

Only after the returned plan has been checked against the approved spec is the same scoped task resumed with write access. This is what stops the dispatcher — or the agent that wrote the spec — from smuggling unreviewed architectural choices into implementation under cover of "it was already approved".

It runs in both directions

The dispatcher selects an executor. It is not a one-way Codex-to-Claude bridge.

Codex triage/spec  → dispatch → Claude plan → Claude implementation
Claude triage/spec → dispatch → Codex plan  → Codex implementation

Both directions use the same Linear issue, the same spec schema, the same approval checkpoint, worktree isolation, structured completion report, and independent review gates. A Claude-authored implementation is normally reviewed by Codex; a Codex-authored implementation by Claude or another independent review task. The implementing session never certifies its own gate.

Dispatcher responsibilities

  1. Validate the PM verdict and spec, the acceptance criteria, the exact Linear identifier, and explicit approval to begin planning.
  2. Select both harness and model by risk, ambiguity, relevant strengths, and expected horizon — not by task size alone.
  3. Create an isolated worktree and branch, and run the chosen executor plan-only.
  4. Stop for plan approval before granting edit permissions, then resume the same scoped task for implementation.
  5. Return branch, draft PR, tests, acceptance evidence, blockers, costs, and the originating Linear issue.
  6. Trigger independent review gates — review-claude-pr, /review-engine, domain evals such as /coverage-eval, or the symmetric review path — before any issue or release gate advances.

Current CLI surface, for whoever builds it: Claude supports --worktree, --permission-mode plan, --model fable|opus|sonnet, --effort, --max-budget-usd, background execution, session resumption, and structured output. Codex supports non-interactive execution, read-only and workspace-write sandbox stages, session resumption, model selection, and structured output.

Invariants it inherits

  • Linear is shared state between harnesses, not one harness's scratchpad.
  • product-manager owns scope. The dispatcher owns routing. Neither owns the other's.
  • The receiving executor owns the technical plan.
  • Plan approval precedes edits.
  • Neither harness may merge automatically.

Prerequisite: tool access is present, repo-local skill discovery is unverified. Both harnesses reach Linear — Codex through the linear plugin (~/.codex/plugins/cache/openai-curated-remote/linear/…/.mcp.jsonhttps://mcp.linear.app/mcp, OAuth), Claude through its own MCP configuration — so the Linear tool surface the three skills call is available on both sides.

What has not been demonstrated is that a project-local .agents/skills/ directory is discovered from a repository checkout. The layout matches what Codex's own plugin skills use (SKILL.md + agents/openai.yaml), but those are distributed through a marketplace, not read out of a working tree. Until one skill has been invoked end to end from a real Codex session, treat .agents/ as the agreed home for the canonical definition rather than as proven wiring. The repository's AGENTS.md also points Codex at a .Codex/skills/ path that does not exist — it is a find-replaced mirror of CLAUDE.md, and it is untracked. Fixing it is the cheapest way to close the gap.

Roles and refusal conditions

Role Decides Produces Refuses to do
Product manager Whether the proposal is required by the active milestone One scope verdict with contract citation Turn founder enthusiasm, competitor parity, or sunk cost into scope
Designer How the approved user task fits the existing product surface Surface, states, data, and provenance contract Invent a component when an existing one fits; silently contradict the UX spec
Technical lead What exists, what changes, and where the architectural seam is File-grounded delta, risks, collisions, half-day estimate Estimate work without naming real files and symbols
QA What observable evidence proves the feature and release Runnable acceptance criteria and false-positive definition Sign off on a criterion with no execution path
Founder Scope amendments, trade-offs, and release judgement Approval, explicit cut, or contract amendment Delegate product judgement to the agents
Implementation agent The approved code and tests One reviewable branch or PR Reopen scope inside a coding session

The role agents are read-only. They decide and specify; they do not implement. This prevents a gate from approving its own work after it has already built it.

How that is enforced, precisely. product-manager, designer, and qa are granted file-reading and search tools only, so the refusal holds at the tool boundary and no prompt can talk them past it. tech-lead additionally needs a shell — its collision check reads open PRs and worktrees — and a shell can write, push, and call APIs whatever the other tool grants say. Its restriction is prompt-level and its contract lists the inspect-only commands it may run. The distinction is recorded rather than smoothed over: three enforced, one asserted.

One feature, one record

The four roles populate one feature spec under docs/product/specs/; they do not produce four independent plans. A feature record contains:

  1. User problem and evidence
  2. Scope verdict and milestone citation
  3. Surface and data contract
  4. Reuse/build delta and estimate
  5. Runnable acceptance and release checks

Temporary agent plans are disposable. Permanent decisions belong in an ADR or the decision log. Release evidence belongs with the feature or release record.

Milestone behavior

IN means in the currently active milestone, not permanently IN-MVP-1. After a release, the founder advances the active milestone in the scope contract; the same workflow then gates that milestone's work. Future-milestone ideas receive a backlog entry but no implementation spec until their milestone becomes active.

Release transition and harness reset

After MVP-1, reserve a maximum of two working days for a harness reset before MVP-2 execution resumes:

  1. Archive superseded plans and demo-only workflows
  2. Confirm every role and shared skill still has exactly one canonical file under .agents/, with harness adapters that point rather than duplicate
  3. Merge or retire overlapping skills; do not rewrite proven skills for tidiness
  4. Update the active milestone and its release gate
  5. Resume product work when those four outcomes exist, even if broader cleanup remains

The reset is not a repository-wide cleanup milestone. Unbounded cleanup has no natural stopping condition and may not consume the MVP-2 delivery window.

The scope contract is amended only when all four conditions hold:

  1. Named user evidence requires the change
  2. The active release gate fails without it
  3. The technical lead has produced a file-grounded estimate
  4. Existing scope is cut to pay for it, or the deadline moves explicitly

Session and release discipline

Before implementation

  1. Read the active scope contract
  2. Run the product-manager gate
  3. Run designer and technical-lead assessment in parallel
  4. Define QA acceptance before code starts
  5. Obtain founder approval for the spec and its scope trade-off

During implementation

  • One approved feature per branch or worktree
  • No new product decisions inside the coding session
  • Tests and proof artifacts land with the implementation
  • New architectural seams require an ADR

After implementation

  1. QA executes the acceptance artifact
  2. A reviewer inspects the merge-base diff
  3. Proof-based engine review, when the merge-base diff touches ogur/engine/ or tests/unit/engine/ — see below. Required, not advisory
  4. The founder checks that the user need—not merely the specification—was answered
  5. Merge or reject the change
  6. Record a decision only when a decision was actually ratified

Proof-based engine review — a conditional, required gate

"A reviewer inspects the diff" is too weak for the engine. PR #211 carried a green submitted suite while exact-head reproductions exposed adapter-routing, record-evidence, cache-fingerprint, and classification defects. Reading the diff did not find those; running against the head did.

So when the merge-base diff touches ogur/engine/ or tests/unit/engine/:

  1. Run review-engine against the resolved PR head, not whichever branch happens to be checked out — pass the PR number, or base_ref + head_sha. Its output records the reviewed SHA; that SHA is the evidence, and a review of the wrong ref produces valid-looking proof about code the PR does not contain.
  2. If the diff touches ogur/engine/discovery_modality/, also run coverage-eval over the cross-deck surface and report the delta against the prior comparable run. If the shared matcher changed, the number is not comparable — say so and re-baseline rather than reporting a delta.
  3. Attach the proven findings and the clearly-marked questions to the review record.
  4. Every accepted defect gains a permanent regression test in the repository. Review-only reproductions used to prove a finding may stay outside it.

Canonical artifacts

Keep the permanent process surface small:

Artifact Purpose
docs/product/mvp-scope.md Active release gates, explicit exclusions, cuts, and founder decisions
docs/product/product-development-workflow.md Durable path from idea to release
docs/product/specs/ Approved, implementation-ready feature records
docs/adr/ Ratified architectural decisions and consequences
Release evidence Runnable QA results and user evidence

Skills and agent prompts implement this process; they are not the source of truth.

One role contract, two harnesses. The canonical role contracts are tracked under .agents/roles/product-manager.md, tech-lead.md, designer.md, qa.md. Codex and any other harness read them directly; .claude/agents/<role>.md is a thin Claude Code adapter carrying frontmatter, harness-specific notes, and a pointer to the canonical file. Harness-neutral skills follow the same shape: canonical under .agents/skills/<name>/SKILL.md, adapter under .claude/skills/<name>/SKILL.md.

The rule this enforces: a role's behaviour is never restated in two files. A second copy of a prompt cannot be kept in sync by discipline, and its divergence is invisible until the two harnesses return different verdicts on the same proposal. Adding a role or a shared skill means adding the canonical file first; an adapter that duplicates rather than points is the defect.