The Merge Gate Charter

Project19 min · Slice → Gate → Run → Wrap → Prove · Updated 2026-10-28

You end this lesson with: factory/merge-gate-charter.md — review gate, holdout set, protected paths and risk lanes merged into one charter for one repo, with a red control for each gate

Chapters

  1. Brief
    01
  2. Steps
    02
  3. Acceptance
    03
  4. Rubric
    04
  5. Key Takeaways
    05
  6. What You Have Now
    06

Project 01. The Merge Gate Charter

Illustrative composite. Parcelwise is an invented six-person shipping-quote service. In its first month with agents, four gates were written by four people on four afternoons: a review gate, a hidden holdout set, a protected-path list and a risk-lane file. Each file was sensible alone. On a Tuesday an agent opened a 12-line pull request that deleted one assertion from the tenant-isolation test. The review gate read "tests only, under 40 lines" and called it tier 0, no human. The risk-lane file listed that same folder as a high-lane trigger. Nobody had ever laid the two files side by side, so the gate that ran first won, the change merged unread, and the test stayed missing for nine days. Modeled cost: one customer's rate card showed up in another customer's quote.

You already hold the four parts from Lessons 04 to 07. This project joins them. You will write the four module files for the Parcelwise repo, place twelve real-shaped pull requests through each module on its own, watch the files disagree, and settle every disagreement in one charter. The charter also needs a red control for each gate: a deliberately bad change that must fail, run before you trust the gate. The deliverable is factory/merge-gate-charter.md.

Brief

You are the platform lead's second pair of hands. Everything you need is in this section. Nothing external is required.

The repo and the people

The service is parcelwise-api: a quote engine that takes a parcel and a destination zone and returns a price. The agent identity that opens pull requests is parcelwise-agent[bot]. The factory/ directory you write is the team's private governance checkout, parcelwise-factory. The agent identity has no grant on it, and the service repo only runs the gate scripts that read from it.

text
parcelwise-api/  AGENTS.md                    rules the agent reads each run  REVIEW.md                    instructions for the AI reviewer  package.json                 dependencies (package-lock.json beside it)  .claude/settings.json        agent permissions  .claude/hooks/               pre-tool hooks  .github/workflows/ci.yml     the required check  .github/CODEOWNERS           ownership routing  scripts/gates/               check-protected.sh, lane.sh, tier-rules.txt  src/pricing/                 weight, zone and surcharge rules (the money path)  src/carriers/                carrier adapters  src/api/                     HTTP handlers  src/auth/                    API keys and tenant checks  migrations/                  schema changes  tests/                       unit and contract tests  tests/invariants/            tenant-isolation and no-negative-price tests  docs/                        public documentation
PersonRoleOwns
Mara QuillPlatform leadplatform-owners: .github/, .claude/, AGENTS.md, REVIEW.md, scripts/gates/
Tomas ReyesPricing ownerpricing-owners: src/pricing/
Ines KowalQAqa-owners: writes and computes the holdout scenarios
Jae ParkAPI ownerapi-owners: src/api/, src/carriers/, src/auth/

Illustrative composite. Agents open about 14 pull requests a week. The owners can give about 5 hours (300 minutes) a week to reading. One careful read of a change that needs a single owner takes 20 minutes. When two humans must read, each takes 30 minutes. A sampled audit read takes 15 minutes. All figures are modeled, not measured.

The issue the builder sees (pricing)

"Quote total is base plus fuel. Chargeable weight is the larger of actual kilograms and volumetric kilograms, where volumetric kilograms are length times width times height in centimetres divided by 5000. Round chargeable weight up to the next 0.5 kg. Base is chargeable kilograms times the zone rate per kilogram: Z1 is 3.20, Z2 is 4.10. The minimum base is 6.00. Fuel is eight percent of base, rounded half up to the cent. All amounts are in euros."

Module settings, as each author wrote them

Review gate (written by Jae). Fresh-context reviewer, advisory, never approves. It reads REVIEW.md and scripts/gates/tier-rules.txt from the base branch. Round cap 3. Rounds 2 and 3 review only new lines. Findings are Important (blocks), Nit or Pre-existing (never block). Scoreboard thresholds: resolution rate below 0.30 for two weeks, noise ratio above 0.40 for two weeks, cap hits above 0.10 of PRs. Tiers are set by the classifier from the diff:

TierDiff touchesSpecialist reviewersMerge needs
0only docs/** or tests/**, under 40 changed lines2CI green and the merge rule. No human
1other app code (src/api/**, src/carriers/**, package.json), up to 400 changed lines4CI green, the merge rule, one named code owner who is not the person who prompted the agent
2src/pricing/**, src/auth/**, migrations/**, CI workflow files, the reviewer config files6CI green, the merge rule, two humans (one a code owner), approvals pinned to the latest commit

Holdout set (written by Ines). Three scenarios with parcels A, B and C below, stored outside the service repo. Validator sees the issue text and the running build, never the plan. Verdict is ids and clauses only. Two validator runs per PR, then a human.

ParcelActual kgSize cmZoneRed control it must fail
A3.250 × 40 × 30Z1R1: ignores volumetric weight
B1.220 × 20 × 20Z2R2: rounds to the nearest 0.5 kg instead of up
C0.410 × 10 × 10Z1R3: applies the minimum to the total after fuel instead of to the base

Protected paths (written by Mara). Agent PRs touching these are refused; the owner makes the change: AGENTS.md, .claude/**, .github/workflows/**, .github/CODEOWNERS, scripts/gates/**. Server rule: ruleset on main, code-owner review required, no bypass actors. Client deny rules mirror the list.

Risk lanes (written by Tomas). Size is added plus deleted lines, lockfiles excluded; refusal cap 800. High lane, any one trigger: H1 protected path; H2 touches src/auth/**, migrations/** or tests/invariants/**; H3 size 401 to 800; H4 reviewer and validator disagree; H5 retry cap exhausted; H6 no reviewer verdict. Low lane, every test: L1 size 150 or fewer and 8 files or fewer; L2 no floor path; L3 required checks green including the holdout; L4 reviewer finished with no open or dismissed findings; L5 dependency manifests unchanged. Floors: src/pricing/** and package.json are medium at least. Medium is everything else. Audit sample: low lane ten percent, medium lane twenty-five percent, new paths in shadow mode for 30 PRs. Tripwire: rolling four weeks per lane, trip on a revert rate above the human baseline, two or more reverts in the last 40 lane PRs, or any incident traced to a low-lane merge.

Rules the platform lead has already decided

  1. Where two modules disagree about how much human reading a change needs, the stricter one wins, and the module that lost is edited so no file contradicts the charter.
  2. Gate order is fixed: protected paths, then required checks including the holdout, then the review gate's merge rule, then approvers.
  3. The author never sets a tier or a lane, and no bot approval fills a required human slot.
  4. The charter, the four module files and scripts/gates/ are themselves protected.
  5. Crosswalk: tier 0 equals low lane equals final level 0; tier 1 equals medium equals final level 1; tier 2 equals high equals final level 2. A change's final level is the higher of its tier and its lane.

Final level 0 needs CI, the holdout and the merge rule only, plus the weekly audit draw. Level 1 adds one named code owner and a merge queue with a canary. Level 2 adds two humans and a named reader of the full diff.

The pull request log

All 12 were opened this month. "Checks" means the required CI checks. Lines exclude lockfiles.

PRAuthorFilesLinesChecksHoldoutReviewer
PR-01parcelwise-agent[bot]docs/faq.md8greenpassclean
PR-02parcelwise-agent[bot]src/carriers/dhl-adapter.ts, tests/carriers/dhl-adapter.test.ts62greenpassclean
PR-03parcelwise-agent[bot]src/pricing/zones.ts5greenpassclean
PR-04parcelwise-agent[bot]package.json (lockfile adds 2,900 more)12greenpassclean
PR-05parcelwise-agent[bot]tests/invariants/tenant-isolation.test.ts12greenpassclean
PR-06parcelwise-agent[bot].github/workflows/ci.yml3greenpassclean
PR-07parcelwise-agent[bot]REVIEW.md6greenpassclean
PR-08parcelwise-agent[bot]four files in src/api/310greenpassclean
PR-09parcelwise-agent[bot]nine files in src/api/, mechanical rename520greenpassclean
PR-10parcelwise-agent[bot]src/api/quote-handler.ts, src/pricing/surcharge.ts40greenC failsclean
PR-11Mara Quill.github/workflows/ci.yml14greenpassclean
PR-12parcelwise-agent[bot]src/auth/api-keys.ts28greenpassclean

Shape of each file

The audit script reads these headings and columns, so use them exactly.

FileRequired headingsRequired table
factory/review-gate.mdReviewer config, Round cap, Finding states, Approvers per tier, Reviewer scoreboardTier / Set by / Diff touches / Specialist reviewers / Merge needs
factory/holdout-set.mdPlacement, Where the scenarios live, How a figure gets in, H1, H2, H3, Controls procedurenone; each scenario carries a fixture, expected figures, Red control Rn and Author-side result on Rn
factory/protected-paths.mdServer-side rule, Client-side mirror, Red controlClass / Patterns / May change / Enforced by
factory/risk-lanes.mdHigh lane, Low lane, Floors, Human action per lane, Audit sample, Revert-rate tripwireLane / Human action before merge / After merge
factory/merge-gate-charter.mdScope and owner, Gate order, Gates and red controls, Precedence rules, Reconciled conflicts, PR placement, Reading budget, Red control logsee Step 6

Steps

Steps 1 to 4 are plain writing. Step 5 is worked for three rows and open for nine. Steps 6 to 9 are the real project.

  1. Write factory/protected-paths.md. Turn Mara's settings into a class table, the ruleset, a CODEOWNERS block and a red control. Write it as given. Do not fix anything yet. Produces factory/protected-paths.md.
  2. Write factory/review-gate.md. Reviewer config from the base branch, round cap, finding states, the tier table above and the scoreboard. Same rule: write what Jae decided. Produces factory/review-gate.md.
  3. Write factory/holdout-set.md. Compute the three expected quotes by hand with a calculator, from the issue text and the parcel only. Show the arithmetic the way Lesson 05 does. Then work out what each red control returns and name one property it still satisfies. Never run the service to get a figure. Produces factory/holdout-set.md. Compare your figures with the check below only after you have written them.
  1. Write factory/risk-lanes.md. Counting rules, H1 to H6, L1 to L5, floors, the human-action table, audit sample and tripwire, from Tomas's settings. Do not edit them to match the other files. Produces factory/risk-lanes.md.
  2. Place the 12 PRs through each module alone. For each PR write the module's own answer: refused by protected paths or not, review tier, risk lane. Keep this as scratch notes; it is what step 6 compares. Three rows are done for you.
PRProtected paths saysReview gate saysRisk lanes says
PR-01not touchedtier 0 (docs, 8 lines)low (L1 to L5 all pass)
PR-02not touchedtier 1 (app code, 62 lines)low (62 lines, 2 files, no floor)
PR-03not touchedtier 2 (src/pricing/**)medium (floor on src/pricing/**, no high trigger)

Your task is PR-04 to PR-12. Before you open the next check, list every PR where the three columns would send the change to different amounts of human reading, and every PR that one module cannot place at all.

  1. Write the charter skeleton and the gate table. Create factory/merge-gate-charter.md with the headings in the shape table. Under Gates and red controls, add one row per gate with exactly these columns: Gate / Source file / Stops / Owner / Red control / Expected result. The four gates are named Protected paths, Holdout set, Review gate and Risk lanes. Owners are people from the table, never teams, never bots. Fill the Protected paths row from this worked example and write the other three yourself. Under Precedence rules write the five rules in your own words, with the crosswalk. Produces factory/merge-gate-charter.md.
GateSource fileStopsOwnerRed controlExpected result
Protected pathsfactory/protected-paths.mdAn agent PR rewriting the files that instruct, constrain or judge itMara QuillAs the agent identity, append a blank line to AGENTS.md on branch agent/probe and open a PRCheck protected-paths fails and the merge button is blocked
  1. Reconcile. Under Reconciled conflicts add one row for every seam you found, with columns Conflict / Module A says / Module B says / Charter decision. Then edit the losing module file so it says what the charter says. Under PR placement add all 12 PRs with columns PR / Author / Paths / Lines / Protected path / Review tier / Risk lane / Final approval / Deciding rule. The deciding rule names a file and a rule id, such as risk-lanes H2. A protected-path hit by the agent identity is "refused", not a level. A red required check is "no lane". Under Reading budget show the weekly arithmetic for the modeled mix of 6 level-0, 5 level-1 and 3 level-2 pull requests, using the audit draw for level 0 only. Produces edits to the four module files and the filled charter. Check your hardest rows against the key only after you have written them.
  1. Run each red control and fill the log. Under Red control log add one row per gate with columns Gate / Control / Expected / Observed / Ran. Write the Expected cell before you run anything. The Ran cell says ran if a script or a real branch produced the Observed cell and paper if you walked it by hand. Run at least the protected-paths and holdout controls for real: the first with a local branch and the check-protected.sh script from Lesson 06, the second with a tiny quote function and your three mutants. Produces the Red control log in factory/merge-gate-charter.md. A filled example:
GateControlExpectedObservedRan
Holdout setRun H3 on R3Fails: expected 6.48, R3 returns 6.00H3 failed on R3 with 6.00; passed on the good buildran
  1. Run the consistency checks and fix what they print. From the directory that holds factory/:
bash
# governing files named by the review gate must be protectedgrep -q "REVIEW.md" factory/protected-paths.md || echo "NOT PROTECTED: REVIEW.md"grep -q 'scripts/gates/\*\*' factory/protected-paths.md || echo "NOT PROTECTED: tier-rules.txt"# every gate row needs a red control and an expected resultawk -F'|' '/^\| *(Protected paths|Holdout set|Review gate|Risk lanes) *\|/ && ($6 ~ /^ *$/ || $7 ~ /^ *$/) {print "NO RED CONTROL: " $2}' factory/merge-gate-charter.md# twelve PRs placed (expect 12)grep -c '^| PR-[0-9][0-9] ' factory/merge-gate-charter.md

A clean run prints only 12. Produces no new file; fix the ones above.

Acceptance

  • Five files exist under factory/: the four modules and merge-gate-charter.md.
  • Each module has the headings and table columns listed in the shape table.
  • factory/holdout-set.md carries 41.47, 8.86 and 6.48 with their arithmetic, and a red control R1, R2 and R3 each with an author-side result.
  • factory/protected-paths.md lists REVIEW.md, and factory/risk-lanes.md keeps tests/invariants/** as a high trigger.
  • tests/invariants/** is excluded from tier 0 in factory/review-gate.md.
  • The charter gate table has a row for each of four gates, each with a red control and an expected result, and each owner is a named person.
  • The charter lists at least three reconciled conflicts, and the losing module file was edited to match.
  • All 12 PRs are placed, each with a deciding rule that names a file and a rule id.
  • The reading budget shows its arithmetic and states what breaks it.
  • The red control log has an Expected cell written before the Observed cell, and two controls marked ran.
  • The step 9 commands print nothing except 12.
  • A reviewer can check that no module lets the agent identity or a bot approval fill a human slot.

Rubric

CriterionWeakStrong
ReconciliationThe charter pastes the four files together, or picks a winner without naming the conflictFinds the seams by placing every PR through every module, applies stricter-wins, and edits the losing file so nothing contradicts the charter
Red controlsAbsurd failures that any check catches, never run, or the Observed cell is filled before the Expected onePlausible wrong builds, Expected written first, at least two run for real, and paper is honestly labelled
PlacementPRs placed by what the author says or by a single module; no rule namedEvery row names a file and rule id, final level is the higher of tier and lane, refusals and red checks are handled apart
EnforceabilityRules read as intentions a person must rememberEvery rule is a path pattern, a threshold, a script or a ruleset setting, owned by a named person
Budget honestyNo arithmetic, or a total that quietly exceeds the hoursArithmetic shown, total against the available hours, and the one change that would break it

Key Takeaways

  • Place every pull request through every gate separately; the disagreements show you where the charter is needed.
  • Resolve conflicts by the stricter human requirement, then edit the loser so no file contradicts the charter.
  • Write the expected result of a red control before you run it.
  • Compute holdout figures by hand from the fixture, and name the property each mutant still satisfies.
  • Count the reading minutes your charter demands, and write down what would break the budget.

What You Have Now

factory/merge-gate-charter.md - review gate, holdout set, protected paths and risk lanes merged into one charter for one repo, with a red control for each gate. It sits in factory/ beside the four module files it reconciles and the earlier files from the Slice module, and from now on every gate change is an edit to the charter and the module together, never to one alone. Re-run the red control log whenever you change a ruleset, a threshold or a protected pattern. This is the Gate module's finished artifact: a change reaches the main branch through one ordered set of rules, one named human per level and one proof that each gate can fail.

text
factory/  line-map.md  release-slice.md  autonomy-map.md  review-gate.md  holdout-set.md  protected-paths.md  risk-lanes.md  merge-gate-charter.md

NO-QUIZ

Quiz coming to this lesson

This lesson doesn't have a graded quiz yet — completion is still tracked, just not verified against questions.

Reading is free — your progress isn't being saved

Nothing you did on this page is recorded. Paid students get saved progress and verified completions, recorded quiz answers, the spaced-repetition review queue, labs, and a certificate for each course they finish.

30-day money-back guarantee. One-time payment — lifetime access and updates.