Skip to content

A team on one feature (shared feature lanes)

Most day-to-day work in TOVIO is trunk-based: you land stacked changes straight onto main, and most of what Git would call "feature branches" simply disappear. But sometimes a team needs to build one large feature together, off to the side — kept out of main until it's cohesive enough to ship into a release. And several teams may be doing exactly that, each on a different feature, at the same time.

That is a shared feature lane, and TOVIO makes it a first-class, low-friction pattern — because a lane is just a shared landing target, not a workspace anyone has to "be on." This guide shows how two developers (or a whole team) collaborate on one feature in isolation, how several such teams run in parallel, and how a finished feature is promoted into a release.

Built and tested; release evidence remains open

The offline single-developer pieces (commit, land, lane, stacks) run on the core today. Sharing a lane across a team — sync, the Forge, lane protection, proposals — is built and passes its hermetic --features tls test suite. Production binaries passed the distinct-Docker-host exercise under a real operator CA; public-release, broader HA/load, hosted-parity, and external evidence remain Phase 5 gates.

When to reach for a feature lane

Prefer trunk + feature flags + short stacks when you can — it carries the least integration debt. Reach for a shared feature lane when a feature is (a) genuinely multi-developer, (b) not safe to expose on main incrementally even behind a flag, and (c) meant to land into a release as a unit.

The shape

A feature lane (feature/payments) is a shared landing target the whole feature-team lands onto — the feature's own integration line, isolated from main:

flowchart LR
    subgraph TEAM[" Payments team "]
      A["Alice<br/>chg:a1 → chg:a2"]
      B["Bob<br/>chg:b1"]
    end
    A -. "land --into feature/payments" .-> FB(["feature/payments<br/>(shared integration line)"])
    B -. "land --into feature/payments" .-> FB
    FB == "promote when ready" ==> M(["main → release/2.5"])
    classDef ptr fill:#eef,stroke:#557;
    class FB,M ptr;
  • Each developer still works in their own changes (stacks welcome). They just land onto feature/payments instead of main.
  • The feature stays off main until the team promotes it — main never sees the half-finished feature.
  • Other teams do the same on their own lane (feature/search, feature/inventory), in parallel, without stepping on each other.

1. Create the feature lane

tovio lane <name> creates a lane at the current tip, so switch to trunk first:

$ tovio switch main
$ tovio lane feature/payments             # a shared landing target, cut from trunk
$ tovio sync                              # publish it so the team can land onto it

Keep the gate light on a feature lane so the team moves fast, while main and release/* stay strictly protected. Protection lives in the policy manifest and is declared with tovio policy protect (lane protection):

$ tovio policy protect main --require-review --min-approvals 2 --required-checks build-check,semantic-check
$ tovio policy protect "feature/*"        # conflict-free only: stay buildable, no review or CI ceremony

Read back, the manifest carries one protected_branch block per pattern (the on-disk vocabulary keeps the word branch):

# strict on main; lighter on the feature line
[[protected_branch]]
pattern               = "main"
require_conflict_free = true
required_checks       = ["build-check", "semantic-check"]
# (require_review: min_approvals = 2 — main becomes Forge-advanced only; cleared-reviewer coverage also applies)

[[protected_branch]]
pattern               = "feature/*"
require_conflict_free = true                # stay buildable, but no review/CI ceremony to land here

An unprotected feature lane is also fine — you can even land changes that still carry conflicts onto it, since only a protected lane requires conflict-free (land).

2. Each developer's loop — land onto the feature, not main

Everyone keeps their normal everyday loop; the only difference is the land target:

# ...edit... (auto-tracked; a change opens automatically — no `add`)
$ tovio commit -m "add Stripe webhook verifier"    # finalize your change → chg:xxx
$ tovio land my-lane --into feature/payments        # land onto the FEATURE line, not main
$ tovio sync                                        # share; teammates pull it

Because lane refs are CRDTs, two developers landing onto feature/payments at the same moment converge — never "rejected" or "diverged history." A genuine content overlap becomes a stored conflict object that someone resolves once, not a blocked push. And landing the base of a stack cascades the rebase to the changes above it, Change IDs preserved.

Build directly on a teammate's in-flight change

Bob's UI depends on Alice's not-yet-landed API change? He stacks on it and syncs; TOVIO records the dependency. If a teammate's change upstream of yours still has an unresolved conflict, tovio sync --isolate builds you against the last-known-clean version and re-materializes when their resolution syncs — so nobody on the team is ever blocked by anyone else's conflict. See Team workflows §10 and clone & sync.

3. Keep the feature current with trunk (manage the drift)

A feature lane that sits still while main moves accrues integration debt. Periodically fold trunk's progress into the feature. There is no separate merge verb — tovio land integrates one lane into another, storing (never throwing) any conflict:

$ tovio land main --into feature/payments    # bring trunk's advances into the feature line
# resolve any conflict ONCE — the resolution travels with the change and won't come back

Your own lane, cut from feature/payments, tracks it as its upstream: auto-sync folds the feature lane's advances into your lane automatically whenever that merge is conflict-free (clone & sync). The longer a feature diverges from trunk, the more the fold costs — which is why the idiom is to keep features short-lived and land trunk in often.

4. Several teams, several features, in parallel

Running many at once needs nothing special:

flowchart LR
    M(["main (trunk)"]) --> FP(["feature/payments"])
    M --> FS(["feature/search"])
    M --> FI(["feature/inventory"])
    FP == promote ==> R(["release/2.5"])
    FS == promote ==> R
    classDef ptr fill:#eef,stroke:#557;
    class M,FP,FS,FI,R ptr;
  • Each feature has its own lane (its own CRDT ref), so a land on one never collides with another.
  • The path-scoped land cascade means a land only rebases descendants whose file-set overlaps — payments and search, touching different areas of a monorepo, don't thrash each other. (See Branching workflows.)
  • Optionally scope each team's code or secrets by path with a read policy, so only that team can read them — the same repo, safely partitioned (Permissions).
  • An AI agent on a feature gets a capability token scoped to that feature's paths and lane, so it can't wander into another team's work.

5. Promote the finished feature into a release

When the feature is done, you integrate it forward — onto main, then through your environment / release flow. You don't lose the history: every change keeps its stable chg: id, so the promotion is fully traceable.

A review-required main is advanced only by the Forge — no client, not even a local tovio land --into main, can publish it. So the promotion goes through a proposal: push the feature lane, propose its tip change onto main, collect the approvals the manifest requires (including a cleared reviewer for any protected path), and the Forge lands it.

$ tovio sync                                                        # publish the feature lane's tip
$ tovio change propose chg:a2 --target main --remote forge.example.dev:7743
# ...CI posts build-check + semantic-check; two eligible reviewers approve
#    (`tovio review chg:a2 --approve --proposal prop:… --remote forge.example.dev:7743`)...
# ...the Forge lands the approved proposal (its dashboard and GitHub-compatible merge both drive that step)...
$ tovio sync                                                        # bring the advanced main down
$ tovio land main --into release/2.5           # promote forward into the release line
$ tovio tag v2.5.0                             # immutable release marker
  • The strict main / release gate refuses a conflicted or non-buildable set, so a half-finished feature cannot slip into a release (lane protection).
  • Any conflict you already resolved once on the feature lane re-applies on promotion — you don't re-resolve it (the conflict algebra).
  • After promotion the feature lane has done its job: delete it (tovio lane --delete feature/payments, fully undoable), or keep it to backport a fix (the same chg: id lands on release/2.5 and main, traceably).

Caveats & the idiomatic recommendation

  • Long-lived feature lanes accrue integration debt. TOVIO lowers it (auto-rebase cascade, resolve-once conflicts, stable IDs, conflict-free auto-integration) but doesn't repeal it.
  • Prefer trunk + feature flags + short-lived lanes. Reach for a shared feature lane when a feature genuinely must stay off trunk until it's a releasable unit — then land trunk in often and promote as soon as it's cohesive.
  • main and release/* stay strictly protected; a feature lane can be lightly gated (or unprotected) so the team moves fast.

Where to go next

Last reviewed September 9, 2026

Suggest an improvement to this page Not for security reports — see disclosure