Skip to content

Debt-exception / Ratchet-exception trailers never survive the squash merge — 0 of 8 in the Debtfile's whole history #958

Description

@hyperpolymath

Summary

The Debt-exception: and Ratchet-exception: commit trailers are the only sanctioned escape hatch for the debt and exemption ratchets. On this repository they have never once survived into main's history. Both ratchets fail when re-run against main today.

The mechanism works perfectly pre-merge and is erased by the merge.

Measured

#954 raised two debt ceilings and declared both correctly. The PR-branch commits carried:

Debt-exception: gate-scripts-without-tests — re-baselining fossilized 31/30 to measured 40/40 (#953); falls as tests land.
Debt-exception: todo-fixme-markers — re-baselining fossilized 77/76 to measured 80/80 (#953); advisory markers only.

governance / Debt ratchet passed on the PR — correctly, because check-debt-ratchet.sh:133 scans git log --format=%B "${BASE_REF}..HEAD" and the trailers were there.

The squash merge then collapsed those two commits into 9c256b67, whose message carries neither trailer at column 0:

$ git log -1 --format=%B 9c256b67 | grep -nE '^(Debt-exception|Ratchet-exception)'
(no output)

The only surviving trace is indented, backticked prose inside the PR description, which the gate's grep -iE '^Debt-exception' cannot match by design.

Both ratchets now fail against main's own history:

$ bash scripts/check-debt-ratchet.sh be6c9580        # be6c9580 = #954's actual CI base
rc=1   "A debt ceiling rose, or an entry was deleted, without saying so."

$ bash scripts/check-exemption-ratchet.sh be6c9580
rc=1   same shape

It has never worked, not once

Across every commit that has ever touched .machine_readable/Debtfile.a2ml:

commit surviving Debt-exception trailers subject
9c256b67 0 main-red-round2 (#954)
08f77c14 0 validate-sha-pins could not fail (#882)
0afcfd4c 0 re-measure Debtfile probes (#820)
092dedad 0 retire Deno from check-ts-allowlist (#730)
5db75ff7 0 re-measure after the paydown
e083dfe7 0 re-measure Debtfile probes (#724)
5fcf81f2 0 track Deno removal as a probed integer (#601)
43ab303c 0 a Debtfile and a debt runner (#591)

Zero out of eight.

Root cause — a repository setting, not a script bug

$ gh api repos/hyperpolymath/standards --jq '{squash_merge_commit_title, squash_merge_commit_message}'
{"squash_merge_commit_title":"PR_TITLE","squash_merge_commit_message":"PR_BODY"}

PR_BODY means the squash commit's message is the pull request description, not the concatenated branch commit messages. So a trailer written into a commit is discarded at merge by design. Only text in the PR body survives — and only at column 0 would the gate ever match it.

This is not specific to standards. Both comparison repos share the setting:

repo title / message
hyperpolymath/standards PR_TITLE / PR_BODY
hyperpolymath/rsr-template-repo PR_TITLE / PR_BODY
metadatastician/stapeln PR_TITLE / PR_BODY

Any repo adopting these ratchets inherits the same hole.

Why it matters

  1. The audit trail the mechanism exists to create does not exist. Every ceiling raise in this repo's history looks unjustified. The reasons were written; they were thrown away.
  2. The gate is self-defeating on re-run. Any process that re-checks a merged range — a release audit, a bisect, a consumer pinning an older base — sees an undeclared raise it cannot explain.
  3. The escape hatch cannot be audited for abuse. You cannot grep main for "who raised a ceiling and why", which is the one question the trailer was invented to answer.

Proposed cures — three options, owner's call

  1. Change the setting to COMMIT_MESSAGES. Branch commit messages are concatenated into the squash commit, so trailers survive verbatim. Cost: the curated PR body no longer becomes the commit message, and squash commits get noisier.
  2. Document that the trailer goes in the PR body at column 0. No settings change; costs nothing; relies entirely on author discipline, which is what already failed here — the author of fix(main-red-round2): asana restore+ack, fail-closed validator, self-lint baseline, debt re-baseline #954 did follow the documented rule and still lost the trailer.
  3. Make the gate check the PR body as well as the commit range. github.event.pull_request.body is available to the workflow. This makes the gate consistent with what actually persists under PR_BODY, and can be paired with (2).

My recommendation is (1) plus (3): (1) makes the record durable regardless of author behaviour, and (3) makes the gate measure the thing that actually survives. (2) alone is the weakest arm — it has already been tried implicitly and failed.

Acceptance criteria

  • A ceiling raise merged to main leaves a column-0 Debt-exception: line greppable in git log on main.
  • bash scripts/check-debt-ratchet.sh <base of the merge> exits 0 when re-run against main after such a merge.
  • Same for check-exemption-ratchet.sh and Ratchet-exception:.
  • A regression test merges (or simulates) a squash and asserts the trailer survives — this defect is invisible to any test that only exercises a PR branch.
  • The chosen cure is applied to rsr-template-repo and the template, so adopting repos do not inherit the hole.

Related: #955 (the debt breach that surfaced this), #787 (decision surface).

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    tech-debtKnown shortcut, drift, or hygiene owed - includes cleanup

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions