From 82452ca8f686ec3436fa94c2453856589fd6f78e Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 02:10:33 +0000 Subject: [PATCH 1/5] docs: pickup recovery protocol design, with a push-fence test The isolated design and test stage that dot's disposition (main#275 6281287486) named for the invocation-bound pickup proposal (main#275 6276723458). Docs and tests only; no rule changes, nothing is activated, no claim is released. - docs/PICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md: the five cases and their cost; the coordinator's five corrections, adopted; what the transport prevents versus what the workflow only discourages; the six scenarios (simultaneous claimants, a returning original worker, a delayed callback across expiry, a crash before release, ambiguous claim order, stale-head mutation); a protocol consistent with the corrections; a measurement plan with zero conflicting writes as the safety requirement. - tests/test_pickup_fence.py: five standard-library unittest cases on a bare repository in a temporary directory: a stale plain push and a stale --force-with-lease are rejected, a fresh lease is accepted, a bare --force overwrites, and a claim record prevents nothing. Dylan Roy - delegated AI work. Actual performer: Anthropic Claude, session session_01NMeKEismAyeqgdB4sy2NJU (Claude01N; serving model Claude Fable 5.1). Credit 0. Scientific effect NONE. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01NMeKEismAyeqgdB4sy2NJU --- ...ICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md | 194 ++++++++++++++++++ tests/test_pickup_fence.py | 108 ++++++++++ 2 files changed, 302 insertions(+) create mode 100644 docs/PICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md create mode 100644 tests/test_pickup_fence.py diff --git a/docs/PICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md b/docs/PICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md new file mode 100644 index 000000000..a9f1ddef0 --- /dev/null +++ b/docs/PICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md @@ -0,0 +1,194 @@ +# Pickup recovery: a protocol design and a fence test (20 October 2026) + +Status: a design note and one test, written as the isolated design stage that the +coordinator's disposition +[#275/6281287486](https://github.com/d6g8k5htny-coder/main/issues/275#issuecomment-6281287486) +named for proposal +[#275/6276723458](https://github.com/d6g8k5htny-coder/main/issues/275#issuecomment-6276723458). +It changes no rule, activates nothing and releases nobody's claim. The +[current workflow](../governance/OP-WORKFLOW-20260930.md) governs; the coordinator-fallback +draft [main#450](https://github.com/d6g8k5htny-coder/main/pull/450) and the usage-limit route +[main#453](https://github.com/d6g8k5htny-coder/main/pull/453) are adjacent and unchanged. +Author: Anthropic Claude, session `session_01NMeKEismAyeqgdB4sy2NJU` (Claude01N), as delegated +AI work for Dylan Roy. Scientific effect NONE. + +## 1. The problem, with its evidence + +A pickup is a comment that names a performer, an exact source and a scope. Some performers +are stateless hourly invocations under a shared label (for example the xAI workers +GROK-H01 to H04). When such an invocation ends, its pickup keeps its claim, but no execution +holds it any longer, and the next invocation under the same label states, correctly, that it +holds no prior assignment. Five cases between 15 and 19 October: + +| Pickup | Scope | Claimed | Outcome | +|---|---|---|---| +| [389/6188676525](https://github.com/d6g8k5htny-coder/main/pull/389#issuecomment-6188676525) | main#389 main text, slice M1 | 15 Oct 09:15Z | no delivery; ended by owner authority on 19 Oct 21:53Z | +| [389/6191845893](https://github.com/d6g8k5htny-coder/main/pull/389#issuecomment-6191845893) | main#389 main text, slice M2 | 15 Oct 12:17Z | same | +| [389/6182815146](https://github.com/d6g8k5htny-coder/main/pull/389#issuecomment-6182815146) | main#389 supplement draft 9 | 15 Oct 02:11Z | same | +| [389/6184901584](https://github.com/d6g8k5htny-coder/main/pull/389#issuecomment-6184901584) | main#389 supplement draft 9 | 15 Oct 05:08Z | same | +| [419/6248600576](https://github.com/d6g8k5htny-coder/main/issues/419#issuecomment-6248600576) | main#447 source review | 18 Oct 10:09Z | open as of 20 Oct 02:00Z | + +The cost: the main#389 head was frozen for four days under the practice that an open read +freezes the head, and main#447's mathematical review could not start because other lanes +deferred to the pickup. The resolution of the first four needed a direct question to the +owner ([389/6278216270](https://github.com/d6g8k5htny-coder/main/pull/389#issuecomment-6278216270)). + +## 2. What the coordinator corrected + +The proposal's four clauses are withdrawn as written. The design below keeps these +constraints from the disposition: + +1. A later invocation under a shared label is not the earlier performer. Its statement that + it holds nothing neither releases the earlier claim nor proves that the earlier execution + ended. Historical reassignment is recorded as authorized supersession or recovery, with + the predecessor's closeout preserved as UNKNOWN; it is never described as the predecessor's + own release. +2. A release-on-exit term is usable only when the exact invocation and a trustworthy + termination event are observable; otherwise it is an unfulfilled condition, not a + timeout. An unavailable executor is not known to be dead. +3. A new invocation reconciles the unresolved earlier claims it could conflict with, but does + not release them as its own. A lookback window is a convenience, not a cutoff. +4. A claim binds an exact execution identity, a canonical task and scope, and a generation. + Recovery requires explicit applicable authority, the successor's personal acceptance and a + current-state reconciliation. An expected-head guard detects stale source; it does not by + itself prevent every stale write, and any fencing claim needs technical enforcement and a + test. +5. Immutable pinned-source reviews are preferred to freezing an author's whole branch. A + review does not carry to new bytes without an exact-source or composition assessment. An + existing explicit freeze stands until its owner or an authorized disposition resolves it. + +## 3. What is technically prevented, and what is only discouraged + +`tests/test_pickup_fence.py` builds a bare repository, lets a second worker move a branch, +and records what a worker with a stale view can push. Standard library and `git` only; it +runs in about a second. + +| Write by the stale worker | Result | Enforced by | +|---|---|---| +| `git push origin work` after the branch moved | rejected (`[rejected] ... fetch first`) | the transport's fast-forward rule | +| `git push --force-with-lease=refs/heads/work:` after the branch moved | rejected (`stale info`) | the lease: the remote ref must equal the stated old value | +| `git push --force-with-lease=refs/heads/work:` with a fresh view | accepted | the lease matches | +| `git push --force origin work` | **accepted**, the other worker's commit is overwritten | nothing | +| a claim recorded beside the branch | prevents nothing | nothing | + +So a guarded push (the `ls-remote`, then `--force-with-lease=branch:old`, then `ls-remote` +pattern already used on main#389) prevents a stale worker from overwriting a moved branch, +provided every worker uses it. A bare `--force` and a claim comment prevent nothing; they are +discouraged by the workflow only. On GitHub, branch protection can forbid force pushes +altogether, which turns the last row but one from "discouraged" into "prevented" without any +worker's cooperation; and the merge endpoint accepts a `sha` parameter (the expected head) +that fails the merge when the head has moved. Neither GitHub guard is exercised by the test, +which has no remote; they are stated from the documentation. + +What no transport guard prevents: two performers reviewing the same pinned source (duplicate +work, not a conflicting write), and a successor acting on a scope whose predecessor is not +known to have stopped (a custody conflict, not a byte conflict). Those are protocol matters. + +## 4. The six scenarios + +In each scenario, A is the pickup's performer, B is a later actor, and the source is pinned by +an exact commit in the pickup. + +**S1. Simultaneous claimants.** A and B post pickups for the same scope within minutes, each +after a contender check that did not see the other. Records: two pickups, both naming the +head. Who acts: by the workflow, the earlier pickup holds the scope; the later one withdraws +in the task thread, or the two split the scope explicitly there. Prevented: nothing at the +transport level, since neither has written. Discouraged: continuing both. Design: the pickup +states its contender-check time; the later claimant, on seeing the earlier record, posts a +withdrawal or a split before any write. A reviewer's delivery that overlaps an earlier +pickup is still a delivery; the coordinator decides which review binds. + +**S2. The original worker returns after supersession.** A's pickup was superseded by an +authorized recovery record (as the four main#389 reservations were), and a later execution +under A's label returns with partial work. Records: the pickup, the supersession, the return. +Who acts: the returning execution is not known to be A (correction 1), so it may post its +partial work as data and may post a fresh pickup, but it does not resume A's claim. Prevented: +a push to a branch whose head moved, if guarded. Discouraged: treating the return as A's +continuation. Design: a returning execution states that its continuity is unverified, links +A's record, and takes a fresh pickup only if the scope is still open. + +**S3. A delayed callback across expiry.** A's delivery arrives after a recorded supersession, +for the superseded head. Records: supersession, then A's delivery. Who acts: the delivery +stands as a review of the head it names (correction 5: a review binds pinned bytes); if the +successor's work is on the same head, the coordinator records both and decides which binds; +if the head moved, A's delivery is a review of old bytes and carries forward only with an +exact-source or composition assessment. Prevented: nothing (a comment). Design: every +delivery names the exact head it checked; a delivery for a superseded head is not rejected, +and is not promoted either. + +**S4. A crash before release.** A stops without a delivery, checkpoint or release; nothing +further ever appears. Records: the pickup only. Who acts: no one may release A (corrections +1–2). The coordinator, or the owner, records a supersession after a reconciliation, which the +successor accepts personally (correction 4); until then the scope is held. Prevented: a +successor's stale push, if guarded. Design: a pickup carries a checkpoint time; a missed +checkpoint is the trigger for the coordinator's reconciliation, not for release. The +proposal's "release-on-exit" is admissible only where the platform reports termination, which +the hourly invocations do not; for them the trigger is the missed checkpoint. + +**S5. Ambiguous claim order.** Two pickups, each citing a contender check that predates the +other's post, with timestamps seconds apart or under clock skew. Records: the two posts with +server timestamps. Who acts: the server timestamp orders them; the later poster withdraws. +Design: as S1; the contender check is re-run immediately after posting, which closes the +window in which both were posted. + +**S6. Stale-head mutation.** B refreshes or amends a branch whose pickup names an earlier +head; A, with a stale view, tries to push. Records: the pickup names the head; the branch +moved. Prevented: A's push, if A uses a guarded push or if force pushes are forbidden on the +branch (section 3). Discouraged only: A's bare `--force`. Design: guarded pushes for every +shared branch, and branch protection against force pushes where the host allows it; the +pickup's head is what a review binds, so B's refresh does not invalidate A's review of the +old head, it makes it a review of old bytes (correction 5). + +## 5. A protocol consistent with the corrections + +1. **Identity.** A pickup names the execution (session or invocation identifier), not only a + label, and its generation under that label where the platform reuses labels. Two executions + under one label are two performers. +2. **Checkpoint, not lease-expiry.** A pickup states a checkpoint time. A missed checkpoint is + a trigger for the coordinator to reconcile; it is not a release, and no performer other + than the coordinator or the owner records the supersession. +3. **Reconciliation on start.** An invocation lists, before claiming, the open pickups under + its label from any time, and says for each whether it is the same execution (continuing), + has the work (returning, continuity unverified) or neither. It releases none of them. +4. **Supersession record.** Authority (the coordinator or the owner), successor acceptance, + and a current-state check, in one record that preserves the predecessor's closeout as + UNKNOWN. The successor's pickup links it. +5. **Reviews bind pinned sources.** A read names the exact commit; the author's branch is not + frozen by an open read. A later revision does not inherit the read; it gets a byte-diff + readback against the read head, or a fresh read, as today. A freeze is explicit, owned and + lifted by its owner. +6. **Guarded writes.** Every push to a shared branch uses `--force-with-lease` against the + verified old head, and force pushes are forbidden on protected branches. This is the only + part of the protocol that prevents, rather than discourages, a conflicting write. + +Point 5 is the one that would have shortened the main#389 case from four days to zero: the +author's later revisions would have gone out as new pinned heads, each with its own read, and +the open reads would have remained reads of the heads they named. + +## 6. Measurement plan + +All measures come from board and PR records, which carry server timestamps. + +- **Stalled-stage duration:** for each pickup, the time from its post to its delivery or to + the supersession record; reported as a distribution and as the count above 24 hours. The + five cases above give 4 days, 4 days, 4 days, 4 days and more than 40 hours. +- **Duplicate work:** the number of deliveries for one pinned source and scope by more than + one performer without a recorded split. +- **Owner interventions:** the number of supersessions or releases that needed the owner + rather than the coordinator. The five cases needed one owner intervention for four of them. +- **Safety requirement:** zero successful conflicting writes, counted as pushes that replaced + a commit another performer had pushed to the same branch without a recorded handoff. With + guarded pushes and branch protection this count is enforced to zero for the protected + branches; for others it is audited from reflogs, which the host keeps for protected + branches only, so the protection is also what makes the audit possible. + +A comparison runs over two windows, before and after adoption, on the same set of +coordinating lanes. No live lease-expiry experiment is part of this plan. + +## 7. Reproduce + + python3 -m unittest tests.test_pickup_fence -v + python3 -O -m unittest tests.test_pickup_fence + +Both runs pass on a checkout with `git` on the path; the test writes only inside a temporary +directory. diff --git a/tests/test_pickup_fence.py b/tests/test_pickup_fence.py new file mode 100644 index 000000000..08374d57c --- /dev/null +++ b/tests/test_pickup_fence.py @@ -0,0 +1,108 @@ +"""Which writes a stale worker can and cannot make to a shared branch. + +Standard library only. The test builds a bare repository in a temporary directory, clones it +twice (worker A and worker B), lets B move the branch, and then records which of A's pushes +succeed. It is a demonstration of what the transport actually prevents, as opposed to what a +claim comment only discourages; it is not a model of GitHub's merge API, whose +``expected_head_sha`` guard is documented separately. + +Run from the repository root: python3 -m unittest tests.test_pickup_fence +""" +import os +import subprocess +import tempfile +import unittest + + +def git(*args, cwd, check=True): + env = dict(os.environ) + env.update({ + 'GIT_AUTHOR_NAME': 'test', 'GIT_AUTHOR_EMAIL': 'test@example.invalid', + 'GIT_COMMITTER_NAME': 'test', 'GIT_COMMITTER_EMAIL': 'test@example.invalid', + 'GIT_CONFIG_NOSYSTEM': '1', 'HOME': cwd, + }) + r = subprocess.run(['git', *args], cwd=cwd, env=env, capture_output=True, text=True) + if check and r.returncode != 0: + raise AssertionError('git %s failed:\n%s%s' % (' '.join(args), r.stdout, r.stderr)) + return r + + +class PickupFenceTest(unittest.TestCase): + """Worker A holds a stale view of ``work``; worker B has pushed a newer commit.""" + + def setUp(self): + self.tmp = tempfile.TemporaryDirectory() + root = self.tmp.name + self.remote = os.path.join(root, 'remote.git') + git('init', '--bare', '-q', '-b', 'main', self.remote, cwd=root) + seed = os.path.join(root, 'seed') + git('clone', '-q', self.remote, seed, cwd=root) + with open(os.path.join(seed, 'f.txt'), 'w') as f: + f.write('base\n') + git('add', 'f.txt', cwd=seed) + git('commit', '-q', '-m', 'base', cwd=seed) + git('push', '-q', 'origin', 'HEAD:refs/heads/work', cwd=seed) + self.a = os.path.join(root, 'a') + self.b = os.path.join(root, 'b') + for d in (self.a, self.b): + git('clone', '-q', '-b', 'work', self.remote, d, cwd=root) + self.old_head = git('rev-parse', 'HEAD', cwd=self.a).stdout.strip() + # B advances the branch; A does not fetch. + with open(os.path.join(self.b, 'f.txt'), 'a') as f: + f.write('b\n') + git('commit', '-q', '-am', 'b moves work', cwd=self.b) + git('push', '-q', 'origin', 'work', cwd=self.b) + self.new_head = git('rev-parse', 'HEAD', cwd=self.b).stdout.strip() + # A commits on its stale view. + with open(os.path.join(self.a, 'f.txt'), 'a') as f: + f.write('a\n') + git('commit', '-q', '-am', 'a, stale', cwd=self.a) + + def tearDown(self): + self.tmp.cleanup() + + def remote_head(self): + return git('rev-parse', 'refs/heads/work', cwd=self.remote).stdout.strip() + + def test_plain_push_from_stale_view_is_prevented(self): + r = git('push', 'origin', 'work', cwd=self.a, check=False) + self.assertNotEqual(r.returncode, 0) + self.assertIn('[rejected]', r.stderr + r.stdout) + self.assertEqual(self.remote_head(), self.new_head) + + def test_force_with_lease_on_stale_expected_head_is_prevented(self): + r = git('push', '--force-with-lease=refs/heads/work:' + self.old_head, 'origin', 'work', cwd=self.a, check=False) + self.assertNotEqual(r.returncode, 0) + self.assertIn('stale info', r.stderr + r.stdout) + self.assertEqual(self.remote_head(), self.new_head) + + def test_force_with_lease_on_current_expected_head_is_allowed(self): + git('fetch', '-q', 'origin', cwd=self.a) + git('reset', '-q', '--hard', 'origin/work', cwd=self.a) + with open(os.path.join(self.a, 'f.txt'), 'a') as f: + f.write('a, fresh\n') + git('commit', '-q', '-am', 'a, fresh', cwd=self.a) + mine = git('rev-parse', 'HEAD', cwd=self.a).stdout.strip() + git('push', '--force-with-lease=refs/heads/work:' + self.new_head, 'origin', 'work', cwd=self.a) + self.assertEqual(self.remote_head(), mine) + + def test_plain_force_is_not_prevented(self): + """A bare ``--force`` overwrites B's commit: this write is only discouraged by the workflow.""" + git('push', '--force', '-q', 'origin', 'work', cwd=self.a) + self.assertEqual(self.remote_head(), git('rev-parse', 'HEAD', cwd=self.a).stdout.strip()) + self.assertNotEqual(self.remote_head(), self.new_head) + + def test_a_claim_record_prevents_nothing_by_itself(self): + """A claim written beside the branch (here, a note in the remote) does not stop a push.""" + git('fetch', '-q', 'origin', cwd=self.a) + git('reset', '-q', '--hard', 'origin/work', cwd=self.a) + with open(os.path.join(self.a, 'CLAIM.txt'), 'w') as f: + f.write('claimed by B until 23:59Z\n') + git('add', 'CLAIM.txt', cwd=self.a) + git('commit', '-q', '-m', 'a ignores the claim', cwd=self.a) + git('push', '-q', 'origin', 'work', cwd=self.a) + self.assertEqual(self.remote_head(), git('rev-parse', 'HEAD', cwd=self.a).stdout.strip()) + + +if __name__ == '__main__': + unittest.main() From 4b7646ca1d40884a1503288b066ff458e2a64122 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 03:08:09 +0000 Subject: [PATCH 2/5] docs: apply AMEND_SCOPED 456/6281763561 to the pickup recovery design MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit F1: the explicit lease (or the implicit lease with --force-if-includes) fences a stale writer; a deliberate rewrite from a fresh view is a conflicting write the lease does not prevent, only blocking force pushes does; section 5 point 6 lists the merge endpoint's sha and the update-branch endpoint's expected_head_sha beside the lease. F2: the safety count is audited on every branch from the repository activity record (activity_type=force_push), not from reflogs. F3: the test docstring names the merge endpoint's parameter sha. F4: S5 follows the workflow's tie rule (four checks, item 2): ambiguous or tied order stops both actors; the contender re-check detects the overlap and does not close the window. N1: point 5 would have removed the head freeze, not shortened the stalled stage. N2: the read's three lease cases are tests; test 3 is labelled a fast-forward. N3: the claim record is B's; A pushes anyway. Eight tests pass with and without -O; tests.test_workspace_landing and tools/workspace_landing_check.py are unchanged and green. Dylan Roy — delegated AI work; performer Anthropic Claude, session session_01NMeKEismAyeqgdB4sy2NJU. Scientific effect NONE. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01NMeKEismAyeqgdB4sy2NJU --- ...ICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md | 97 +++++++++++++------ tests/test_pickup_fence.py | 52 ++++++++-- 2 files changed, 110 insertions(+), 39 deletions(-) diff --git a/docs/PICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md b/docs/PICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md index a9f1ddef0..de7aca4c0 100644 --- a/docs/PICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md +++ b/docs/PICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md @@ -59,26 +59,39 @@ constraints from the disposition: ## 3. What is technically prevented, and what is only discouraged -`tests/test_pickup_fence.py` builds a bare repository, lets a second worker move a branch, -and records what a worker with a stale view can push. Standard library and `git` only; it -runs in about a second. +`tests/test_pickup_fence.py` builds a bare repository, lets a second worker (B) move a +branch, and records which pushes by the first worker (A) succeed, from a stale view and from +a fresh one. The three lease cases of the nonauthor read +[456/6281763561](https://github.com/d6g8k5htny-coder/main/pull/456#issuecomment-6281763561) +are included. Standard library and `git` only; it runs in about a second. -| Write by the stale worker | Result | Enforced by | +| Write by A | Result | Enforced by | |---|---|---| -| `git push origin work` after the branch moved | rejected (`[rejected] ... fetch first`) | the transport's fast-forward rule | -| `git push --force-with-lease=refs/heads/work:` after the branch moved | rejected (`stale info`) | the lease: the remote ref must equal the stated old value | -| `git push --force-with-lease=refs/heads/work:` with a fresh view | accepted | the lease matches | -| `git push --force origin work` | **accepted**, the other worker's commit is overwritten | nothing | -| a claim recorded beside the branch | prevents nothing | nothing | +| stale view: `git push origin work` after the branch moved | rejected (`[rejected] ... fetch first`) | the transport's fast-forward rule | +| stale view: `git push --force-with-lease=refs/heads/work:` after the branch moved | rejected (`stale info`) | the explicit lease: the remote ref must equal the stated old value | +| stale view: `git push --force-with-lease origin work`, no expected value, after a fetch that A never integrated | **accepted**, B's commit is overwritten | nothing: the implicit lease checks the remote-tracking ref, which the fetch already moved | +| the same push with `--force-if-includes` | rejected | the implicit lease with `--force-if-includes`: A's view must contain the remote ref | +| fresh view: `git push --force-with-lease=refs/heads/work:`, a fast-forward | accepted | the lease matches; the push would also pass without it, being a fast-forward | +| fresh view: the same explicit current lease, a deliberate non-fast-forward rewrite of B's commit | **accepted**, B's commit is replaced | nothing: the lease fences stale writers, not deliberate conflicting writes | +| `git push --force origin work` | **accepted**, B's commit is overwritten | nothing | +| B records a claim on the branch; A pushes anyway | prevents nothing | nothing | So a guarded push (the `ls-remote`, then `--force-with-lease=branch:old`, then `ls-remote` pattern already used on main#389) prevents a stale worker from overwriting a moved branch, -provided every worker uses it. A bare `--force` and a claim comment prevent nothing; they are -discouraged by the workflow only. On GitHub, branch protection can forbid force pushes -altogether, which turns the last row but one from "discouraged" into "prevented" without any -worker's cooperation; and the merge endpoint accepts a `sha` parameter (the expected head) -that fails the merge when the head has moved. Neither GitHub guard is exercised by the test, -which has no remote; they are stated from the documentation. +provided every worker uses it, and uses the explicit form +`--force-with-lease=:` or the implicit form together with +`--force-if-includes`. The implicit form alone fences nothing once a fetch has moved the +remote-tracking ref, which a background fetch does without the worker's knowledge. And the +lease fences stale writers only: a worker with a fresh view who deliberately rewrites the +other worker's commit, naming the current head in the lease, is not stopped. That is a +conflicting write the lease does not prevent; on an unprotected branch it remains discouraged +only, like a bare `--force`, and only blocking force pushes prevents it. A claim record +prevents nothing. On GitHub, branch protection can forbid force pushes altogether, which turns +the deliberate rewrite and the bare `--force` from "discouraged" into "prevented" without any +worker's cooperation; the merge endpoint accepts a `sha` parameter (the expected head) that +fails the merge when the head has moved; and the update-branch endpoint accepts +`expected_head_sha` in the same way. None of the GitHub guards is exercised by the test, which +has no remote; they are stated from the documentation. What no transport guard prevents: two performers reviewing the same pinned source (duplicate work, not a conflicting write), and a successor acting on a scope whose predecessor is not @@ -127,14 +140,20 @@ the hourly invocations do not; for them the trigger is the missed checkpoint. **S5. Ambiguous claim order.** Two pickups, each citing a contender check that predates the other's post, with timestamps seconds apart or under clock skew. Records: the two posts with -server timestamps. Who acts: the server timestamp orders them; the later poster withdraws. -Design: as S1; the contender check is re-run immediately after posting, which closes the -window in which both were posted. +server timestamps, which have one-second resolution, so a tie is possible. Who acts: when +the server timestamps order the posts, the later poster withdraws, as in S1; when which +record is canonical, or their order, is ambiguous or tied, both actors stop the affected +mutation and reconcile in the existing thread, and the reconciliation does not reassign +claims already released (the [current workflow](../governance/OP-WORKFLOW-20260930.md), +"Four checks bracket every claim", item 2). Design: as S1, with the tie rule; the contender check is re-run +immediately after posting, which detects the overlap. It does not close the window in which +both were posted, and nothing does; the tie rule is what handles it. **S6. Stale-head mutation.** B refreshes or amends a branch whose pickup names an earlier head; A, with a stale view, tries to push. Records: the pickup names the head; the branch -moved. Prevented: A's push, if A uses a guarded push or if force pushes are forbidden on the -branch (section 3). Discouraged only: A's bare `--force`. Design: guarded pushes for every +moved. Prevented: A's push, if A uses a guarded push in the explicit lease form or if force +pushes are forbidden on the branch (section 3). Discouraged only: A's bare `--force`, and a +deliberate rewrite of B's refresh from a fresh view. Design: guarded pushes for every shared branch, and branch protection against force pushes where the host allows it; the pickup's head is what a review binds, so B's refresh does not invalidate A's review of the old head, it makes it a review of old bytes (correction 5). @@ -157,13 +176,20 @@ old head, it makes it a review of old bytes (correction 5). frozen by an open read. A later revision does not inherit the read; it gets a byte-diff readback against the read head, or a fresh read, as today. A freeze is explicit, owned and lifted by its owner. -6. **Guarded writes.** Every push to a shared branch uses `--force-with-lease` against the - verified old head, and force pushes are forbidden on protected branches. This is the only - part of the protocol that prevents, rather than discourages, a conflicting write. - -Point 5 is the one that would have shortened the main#389 case from four days to zero: the -author's later revisions would have gone out as new pinned heads, each with its own read, and -the open reads would have remained reads of the heads they named. +6. **Guarded writes.** Every push to a shared branch uses the explicit lease + `--force-with-lease=:`, or the implicit lease together with + `--force-if-includes`, and force pushes are forbidden on protected branches. The host's own + writes carry the same guard: on GitHub, the merge endpoint's `sha` and the update-branch + endpoint's `expected_head_sha` fail the write when the head has moved. The lease and the + API guards prevent a stale write; only the prohibition of force pushes prevents a + deliberate conflicting write from a fresh view (section 3). Everything else in the protocol + discourages. + +Point 5 is the one that would have cut the head freeze of the main#389 case from four days to +zero: the author's later revisions would have gone out as new pinned heads, each with its own +read, and the open reads would have remained reads of the heads they named. It would not have +shortened the stalled stage of section 6: the four reads would still have had no delivery +until the supersession, about four days. ## 6. Measurement plan @@ -178,9 +204,16 @@ All measures come from board and PR records, which carry server timestamps. rather than the coordinator. The five cases needed one owner intervention for four of them. - **Safety requirement:** zero successful conflicting writes, counted as pushes that replaced a commit another performer had pushed to the same branch without a recorded handoff. With - guarded pushes and branch protection this count is enforced to zero for the protected - branches; for others it is audited from reflogs, which the host keeps for protected - branches only, so the protection is also what makes the audit possible. + branch protection against force pushes this count is enforced to zero on the protected + branches; with guarded pushes alone it is enforced against stale writers only (section 3). + On every branch, protected or not, and on branches since deleted, it is audited from the + host's repository activity record, + `GET /repos/{owner}/{repo}/activity?activity_type=force_push`, which records each force push + with its before and after commits; the read + [456/6281763561](https://github.com/d6g8k5htny-coder/main/pull/456#issuecomment-6281763561) + found such records in this repository for an unprotected branch and for a deleted one. How + long the host retains them was not established, so the count is taken during each + measurement window, not only at its end. A comparison runs over two windows, before and after adoption, on the same set of coordinating lanes. No live lease-expiry experiment is part of this plan. @@ -190,5 +223,5 @@ coordinating lanes. No live lease-expiry experiment is part of this plan. python3 -m unittest tests.test_pickup_fence -v python3 -O -m unittest tests.test_pickup_fence -Both runs pass on a checkout with `git` on the path; the test writes only inside a temporary -directory. +Both runs pass (eight tests) on a checkout with `git` on the path; the test writes only inside +a temporary directory. diff --git a/tests/test_pickup_fence.py b/tests/test_pickup_fence.py index 08374d57c..8cfe240ec 100644 --- a/tests/test_pickup_fence.py +++ b/tests/test_pickup_fence.py @@ -3,8 +3,13 @@ Standard library only. The test builds a bare repository in a temporary directory, clones it twice (worker A and worker B), lets B move the branch, and then records which of A's pushes succeed. It is a demonstration of what the transport actually prevents, as opposed to what a -claim comment only discourages; it is not a model of GitHub's merge API, whose -``expected_head_sha`` guard is documented separately. +claim record only discourages; it is not a model of GitHub's guards, which are documented +separately (the merge endpoint's ``sha`` parameter, the update-branch endpoint's +``expected_head_sha``, and branch protection against force pushes). + +The three lease cases of the nonauthor read of main#456 (comment 6281763561) are included: +the implicit lease after an unintegrated fetch, ``--force-if-includes``, and a deliberate +rewrite under a current lease. Run from the repository root: python3 -m unittest tests.test_pickup_fence """ @@ -77,6 +82,7 @@ def test_force_with_lease_on_stale_expected_head_is_prevented(self): self.assertEqual(self.remote_head(), self.new_head) def test_force_with_lease_on_current_expected_head_is_allowed(self): + """A fresh view with a current lease pushes; this push is also a fast-forward.""" git('fetch', '-q', 'origin', cwd=self.a) git('reset', '-q', '--hard', 'origin/work', cwd=self.a) with open(os.path.join(self.a, 'f.txt'), 'a') as f: @@ -86,6 +92,33 @@ def test_force_with_lease_on_current_expected_head_is_allowed(self): git('push', '--force-with-lease=refs/heads/work:' + self.new_head, 'origin', 'work', cwd=self.a) self.assertEqual(self.remote_head(), mine) + def test_implicit_lease_after_unintegrated_fetch_is_not_prevented(self): + """``--force-with-lease`` with no expected value trusts the remote-tracking ref, which a fetch + that A never integrated has already moved: B's commit is overwritten.""" + git('fetch', '-q', 'origin', cwd=self.a) + r = git('push', '--force-with-lease', 'origin', 'work', cwd=self.a, check=False) + self.assertEqual(r.returncode, 0) + self.assertNotEqual(self.remote_head(), self.new_head) + + def test_implicit_lease_with_force_if_includes_is_prevented(self): + """``--force-if-includes`` closes the gap of the previous case.""" + git('fetch', '-q', 'origin', cwd=self.a) + r = git('push', '--force-with-lease', '--force-if-includes', 'origin', 'work', cwd=self.a, check=False) + self.assertNotEqual(r.returncode, 0) + self.assertEqual(self.remote_head(), self.new_head) + + def test_deliberate_rewrite_under_a_current_lease_is_not_prevented(self): + """A fresh view that rewrites B's commit, with the explicit current lease, pushes: the lease + fences stale writers, not deliberate conflicting writes.""" + git('fetch', '-q', 'origin', cwd=self.a) + git('reset', '-q', '--hard', 'origin/work', cwd=self.a) + git('commit', '-q', '--amend', '-m', 'a rewrites b', cwd=self.a) + is_ff = git('merge-base', '--is-ancestor', self.new_head, 'HEAD', cwd=self.a, check=False).returncode == 0 + self.assertFalse(is_ff) + r = git('push', '--force-with-lease=refs/heads/work:' + self.new_head, 'origin', 'work', cwd=self.a, check=False) + self.assertEqual(r.returncode, 0) + self.assertNotEqual(self.remote_head(), self.new_head) + def test_plain_force_is_not_prevented(self): """A bare ``--force`` overwrites B's commit: this write is only discouraged by the workflow.""" git('push', '--force', '-q', 'origin', 'work', cwd=self.a) @@ -93,13 +126,18 @@ def test_plain_force_is_not_prevented(self): self.assertNotEqual(self.remote_head(), self.new_head) def test_a_claim_record_prevents_nothing_by_itself(self): - """A claim written beside the branch (here, a note in the remote) does not stop a push.""" + """B records a claim on the branch; A sees it and pushes anyway. Nothing stops the push.""" + with open(os.path.join(self.b, 'CLAIM.txt'), 'w') as f: + f.write('claimed by B until 23:59Z\n') + git('add', 'CLAIM.txt', cwd=self.b) + git('commit', '-q', '-m', 'b records a claim', cwd=self.b) + git('push', '-q', 'origin', 'work', cwd=self.b) git('fetch', '-q', 'origin', cwd=self.a) git('reset', '-q', '--hard', 'origin/work', cwd=self.a) - with open(os.path.join(self.a, 'CLAIM.txt'), 'w') as f: - f.write('claimed by B until 23:59Z\n') - git('add', 'CLAIM.txt', cwd=self.a) - git('commit', '-q', '-m', 'a ignores the claim', cwd=self.a) + self.assertTrue(os.path.exists(os.path.join(self.a, 'CLAIM.txt'))) + with open(os.path.join(self.a, 'f.txt'), 'a') as f: + f.write('a ignores the claim\n') + git('commit', '-q', '-am', 'a ignores the claim', cwd=self.a) git('push', '-q', 'origin', 'work', cwd=self.a) self.assertEqual(self.remote_head(), git('rev-parse', 'HEAD', cwd=self.a).stdout.strip()) From 3c31ff19b62f1fdb6fca61fc28493500e86c90b7 Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 20 Oct 2026 11:55:15 +0000 Subject: [PATCH 3/5] docs: answer the automatic review at 6f77ba62 (three P2 findings) and readback note N4 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Finding 1 (peer fallback): section 5 points 2 and 4 and scenario S4 now include the coordinator-fallback of the current workflow, landed by main#450, with its 24-hour unanswered-request trigger, eligibility, record requirements, scope limits and supersession by a later coordinator record; the status paragraph records that main#450 and main#453 have landed and that this note changes neither. Finding 2 (stalled-stage metric): terminal events are the delivery, the supersession record, or the performer's own release or withdrawal; still-open pickups are right-censored at the measurement time and reported as an open count with ages. Finding 3 (bypass actors): the zero-conflicting-writes count is enforced only where the force-push protection binds administrators and every bypass actor; exempt actors' pushes are audited; on this repository the exemption question is decided once, for the shared principal. N4 (readback 6282436478): the --force-if-includes test skips when git lacks the option and asserts the rejection in stderr. Eight tests pass with and without -O; tests.test_workspace_landing and tools/workspace_landing_check.py are green. Dylan Roy — delegated AI work; performer Anthropic Claude, session session_01NMeKEismAyeqgdB4sy2NJU. Scientific effect NONE. Co-Authored-By: Claude Fable 5.1 Claude-Session: https://claude.ai/code/session_01NMeKEismAyeqgdB4sy2NJU --- ...ICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md | 52 ++++++++++++++----- tests/test_pickup_fence.py | 6 ++- 2 files changed, 43 insertions(+), 15 deletions(-) diff --git a/docs/PICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md b/docs/PICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md index de7aca4c0..c0e0efb5c 100644 --- a/docs/PICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md +++ b/docs/PICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md @@ -6,9 +6,10 @@ coordinator's disposition named for proposal [#275/6276723458](https://github.com/d6g8k5htny-coder/main/issues/275#issuecomment-6276723458). It changes no rule, activates nothing and releases nobody's claim. The -[current workflow](../governance/OP-WORKFLOW-20260930.md) governs; the coordinator-fallback -draft [main#450](https://github.com/d6g8k5htny-coder/main/pull/450) and the usage-limit route -[main#453](https://github.com/d6g8k5htny-coder/main/pull/453) are adjacent and unchanged. +[current workflow](../governance/OP-WORKFLOW-20260930.md) governs, including the +coordinator-fallback section landed by +[main#450](https://github.com/d6g8k5htny-coder/main/pull/450) and the usage-limit route landed +by [main#453](https://github.com/d6g8k5htny-coder/main/pull/453); this note changes neither. Author: Anthropic Claude, session `session_01NMeKEismAyeqgdB4sy2NJU` (Claude01N), as delegated AI work for Dylan Roy. Scientific effect NONE. @@ -131,8 +132,9 @@ and is not promoted either. **S4. A crash before release.** A stops without a delivery, checkpoint or release; nothing further ever appears. Records: the pickup only. Who acts: no one may release A (corrections -1–2). The coordinator, or the owner, records a supersession after a reconciliation, which the -successor accepts personally (correction 4); until then the scope is held. Prevented: a +1–2). The coordinator, or the owner, or after an unanswered 24-hour request the peer fallback +of section 5 point 4, records a supersession after a reconciliation, which the successor +accepts personally (correction 4); until then the scope is held. Prevented: a successor's stale push, if guarded. Design: a pickup carries a checkpoint time; a missed checkpoint is the trigger for the coordinator's reconciliation, not for release. The proposal's "release-on-exit" is admissible only where the platform reports termination, which @@ -165,13 +167,25 @@ old head, it makes it a review of old bytes (correction 5). under one label are two performers. 2. **Checkpoint, not lease-expiry.** A pickup states a checkpoint time. A missed checkpoint is a trigger for the coordinator to reconcile; it is not a release, and no performer other - than the coordinator or the owner records the supersession. + than the coordinator, the owner or the peer fallback of point 4 records the supersession. 3. **Reconciliation on start.** An invocation lists, before claiming, the open pickups under its label from any time, and says for each whether it is the same execution (continuing), has the work (returning, continuity unverified) or neither. It releases none of them. -4. **Supersession record.** Authority (the coordinator or the owner), successor acceptance, - and a current-state check, in one record that preserves the predecessor's closeout as - UNKNOWN. The successor's pickup links it. +4. **Supersession record.** Authority, successor acceptance, and a current-state check, in + one record that preserves the predecessor's closeout as UNKNOWN. The successor's pickup + links it. The authority is the coordinator or the owner; or, exactly as the + [current workflow](../governance/OP-WORKFLOW-20260930.md) ("The confirmations above assume + a coordinator whose record reaches the board") provides, a peer, once the specific + confirmation has been requested in the task thread and has gone unanswered for 24 hours + from that request: an eligible agent of a different provider from the acting worker, who is + neither the object's author, predecessor nor receiver, has not reviewed it, reads the same + current refs, claims, findings and in-flight work, records what it read and that it concurs, + names the record a fallback confirmation, states which condition applied, and mirrors it on + the board. The fallback covers ordinary engineering, documentation, review and integration + stages only; it cannot lift an owner stop, cure a platform or permission denial, change + protections or supply scientific acceptance; a coordinator record posted later supersedes it + for anything not yet landed; and it earns no independence credit. Nothing here widens or + narrows that section. 5. **Reviews bind pinned sources.** A read names the exact commit; the author's branch is not frozen by an open read. A later revision does not inherit the read; it gets a byte-diff readback against the read head, or a fresh read, as today. A freeze is explicit, owned and @@ -195,17 +209,27 @@ until the supersession, about four days. All measures come from board and PR records, which carry server timestamps. -- **Stalled-stage duration:** for each pickup, the time from its post to its delivery or to - the supersession record; reported as a distribution and as the count above 24 hours. The - five cases above give 4 days, 4 days, 4 days, 4 days and more than 40 hours. +- **Stalled-stage duration:** for each pickup, the time from its post to its terminal event, + which is the first of: its delivery; the supersession record; the performer's own release or + withdrawal (the S1 and S5 withdrawals included), with the reason recorded. A pickup with no + terminal event at the measurement time is right-censored there: it enters the count of open + pickups with its age, and not the distribution. Reported as a distribution over terminated + pickups, the count above 24 hours, and the open count with ages. The five cases above give + 4 days, 4 days, 4 days, 4 days (terminal event: the owner-authorized end) and, for the fifth, + censored at more than 40 hours. - **Duplicate work:** the number of deliveries for one pinned source and scope by more than one performer without a recorded split. - **Owner interventions:** the number of supersessions or releases that needed the owner rather than the coordinator. The five cases needed one owner intervention for four of them. - **Safety requirement:** zero successful conflicting writes, counted as pushes that replaced a commit another performer had pushed to the same branch without a recorded handoff. With - branch protection against force pushes this count is enforced to zero on the protected - branches; with guarded pushes alone it is enforced against stale writers only (section 3). + branch protection against force pushes that applies to administrators and to every other + bypass actor (no force-push bypass list), this count is enforced to zero on the protected + branches; a protection that exempts administrators or a bypass role enforces it only for the + actors it binds, and the exempt actors' pushes are audited like any other. On this + repository every lane pushes through one shared principal, so the exemption question is + decided once, for that principal. With guarded pushes alone the count is enforced against + stale writers only (section 3). On every branch, protected or not, and on branches since deleted, it is audited from the host's repository activity record, `GET /repos/{owner}/{repo}/activity?activity_type=force_push`, which records each force push diff --git a/tests/test_pickup_fence.py b/tests/test_pickup_fence.py index 8cfe240ec..c1fee1a6b 100644 --- a/tests/test_pickup_fence.py +++ b/tests/test_pickup_fence.py @@ -101,10 +101,14 @@ def test_implicit_lease_after_unintegrated_fetch_is_not_prevented(self): self.assertNotEqual(self.remote_head(), self.new_head) def test_implicit_lease_with_force_if_includes_is_prevented(self): - """``--force-if-includes`` closes the gap of the previous case.""" + """``--force-if-includes`` closes the gap of the previous case. The option exists since git 2.30; + where it is unknown the push fails for that reason and the case is skipped, not passed.""" git('fetch', '-q', 'origin', cwd=self.a) r = git('push', '--force-with-lease', '--force-if-includes', 'origin', 'work', cwd=self.a, check=False) + if 'unknown option' in r.stderr or 'force-if-includes' in r.stderr and 'usage:' in r.stderr: + self.skipTest('git lacks --force-if-includes: ' + r.stderr.strip().splitlines()[0]) self.assertNotEqual(r.returncode, 0) + self.assertIn('[rejected]', r.stderr) self.assertEqual(self.remote_head(), self.new_head) def test_deliberate_rewrite_under_a_current_lease_is_not_prevented(self): From 43b5337aa9c16aae8446a125b7647f92c5fbe32d Mon Sep 17 00:00:00 2001 From: Dylan Roy <249851642+d6g8k5htny-coder@users.noreply.github.com> Date: Tue, 20 Oct 2026 09:46:19 -0500 Subject: [PATCH 4/5] Amend pickup design after five Ready-review findings Retain non-forcing normal updates; cover deletion/recreation and observable attribution; use fixed follow-up cohorts and exposure denominators. Add one local Git counterexample. Original Claude01N source authorship is preserved; amendment author is OpenAI/Codex /root/main456_successor_integrator_1020 under main#456 activation6293498638. Design record only: no operating-rule adoption or scientific effect. --- ...ICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md | 168 ++++++++++++------ tests/test_pickup_fence.py | 25 +++ 2 files changed, 135 insertions(+), 58 deletions(-) diff --git a/docs/PICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md b/docs/PICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md index c0e0efb5c..86262d197 100644 --- a/docs/PICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md +++ b/docs/PICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md @@ -12,6 +12,10 @@ coordinator-fallback section landed by by [main#453](https://github.com/d6g8k5htny-coder/main/pull/453); this note changes neither. Author: Anthropic Claude, session `session_01NMeKEismAyeqgdB4sy2NJU` (Claude01N), as delegated AI work for Dylan Roy. Scientific effect NONE. +Amendment author: OpenAI/Codex `/root/main456_successor_integrator_1020`, under the +separate source stage [6293498638](https://github.com/d6g8k5htny-coder/main/pull/456#issuecomment-6293498638), +addressing five Ready-review findings on head `4c5ab1f7`. Original source authorship above +is retained. This amendment is still a design record, not operating-rule adoption. ## 1. The problem, with its evidence @@ -64,7 +68,10 @@ constraints from the disposition: branch, and records which pushes by the first worker (A) succeed, from a stale view and from a fresh one. The three lease cases of the nonauthor read [456/6281763561](https://github.com/d6g8k5htny-coder/main/pull/456#issuecomment-6281763561) -are included. Standard library and `git` only; it runs in about a second. +are included, along with a ninth deletion/recreation case. That case first verifies that an +ordinary divergent push is rejected, then shows deletion plus ordinary recreation succeeds. +It exercises local Git only, not GitHub's event stream or protection settings. Standard +library and `git` only; the full focused test runs in a few seconds. | Write by A | Result | Enforced by | |---|---|---| @@ -75,24 +82,26 @@ are included. Standard library and `git` only; it runs in about a second. | fresh view: `git push --force-with-lease=refs/heads/work:`, a fast-forward | accepted | the lease matches; the push would also pass without it, being a fast-forward | | fresh view: the same explicit current lease, a deliberate non-fast-forward rewrite of B's commit | **accepted**, B's commit is replaced | nothing: the lease fences stale writers, not deliberate conflicting writes | | `git push --force origin work` | **accepted**, B's commit is overwritten | nothing | +| A deletes the branch, then recreates it at its divergent candidate with an ordinary push | **accepted**, B's head is discarded | nothing in the unprotected bare-repository setup; deletion/creation are separate operations | | B records a claim on the branch; A pushes anyway | prevents nothing | nothing | -So a guarded push (the `ls-remote`, then `--force-with-lease=branch:old`, then `ls-remote` -pattern already used on main#389) prevents a stale worker from overwriting a moved branch, -provided every worker uses it, and uses the explicit form -`--force-with-lease=:` or the implicit form together with -`--force-if-includes`. The implicit form alone fences nothing once a fetch has moved the -remote-tracking ref, which a background fetch does without the worker's knowledge. And the -lease fences stale writers only: a worker with a fresh view who deliberately rewrites the -other worker's commit, naming the current head in the lease, is not stopped. That is a -conflicting write the lease does not prevent; on an unprotected branch it remains discouraged -only, like a bare `--force`, and only blocking force pushes prevents it. A claim record -prevents nothing. On GitHub, branch protection can forbid force pushes altogether, which turns -the deliberate rewrite and the bare `--force` from "discouraged" into "prevented" without any -worker's cooperation; the merge endpoint accepts a `sha` parameter (the expected head) that -fails the merge when the head has moved; and the update-branch endpoint accepts -`expected_head_sha` in the same way. None of the GitHub guards is exercised by the test, which -has no remote; they are stated from the documentation. +The tests separate the normal fast-forward restriction from a lease's forcing behavior. +Ordinary branch updates use a non-forcing `git push`; do not add `--force-with-lease` to +normal pushes. Check the current remote head and preserve its ancestry in the candidate. +For API updates, retain a supported expected-old-head guard together with `force=false`; +the guard does not require permission to discard history. A separately authorized intentional +history rewrite on an unprotected branch is a different action: an explicit +`--force-with-lease=:` can reject a stale expectation, but a matching +lease still permits a non-fast-forward rewrite, whether intended or accidental. It is not a +custody handoff or a safe default. The implicit lease after an unintegrated fetch has the gap +shown in the table; `--force-if-includes` addresses that gap without making a rewrite ordinary. +These distinctions follow the [Git push documentation](https://git-scm.com/docs/git-push). + +A claim record prevents no push. On GitHub, protection that actually binds the acting user +can reject force pushes; branch deletion and recreation are separate replacement paths and +need the controls and audit coverage of section 6. The merge endpoint's `sha` and the +update-branch endpoint's `expected_head_sha` reject a moved expected head. None of these +GitHub guards is exercised by this local bare-repository test; they are documented separately. What no transport guard prevents: two performers reviewing the same pinned source (duplicate work, not a conflicting write), and a successor acting on a scope whose predecessor is not @@ -190,14 +199,17 @@ old head, it makes it a review of old bytes (correction 5). frozen by an open read. A later revision does not inherit the read; it gets a byte-diff readback against the read head, or a fresh read, as today. A freeze is explicit, owned and lifted by its owner. -6. **Guarded writes.** Every push to a shared branch uses the explicit lease - `--force-with-lease=:`, or the implicit lease together with - `--force-if-includes`, and force pushes are forbidden on protected branches. The host's own - writes carry the same guard: on GitHub, the merge endpoint's `sha` and the update-branch - endpoint's `expected_head_sha` fail the write when the head has moved. The lease and the - API guards prevent a stale write; only the prohibition of force pushes prevents a - deliberate conflicting write from a fresh view (section 3). Everything else in the protocol - discourages. +6. **Guarded writes without forcing normal updates.** Ordinary pushes retain Git's + fast-forward restriction, and a candidate preserves the observed remote head's ancestry. + For a supported API branch update use the expected old head and `force=false` together; + merges use `sha`, and update-branch uses `expected_head_sha`. Reserve an explicit + `--force-with-lease=:` for a separately authorized, intentional + history rewrite on an unprotected branch; a matching lease permits history loss and is + not an ordinary-update safeguard by itself. No protection is weakened and no rewrite is + authorized by this design. Protect both non-fast-forward updates and deletion where + these controls bind every relevant actor, and audit the separate replacement paths in + section 6. The guards constrain their stated transport operations, not custody or + semantic correctness. Point 5 is the one that would have cut the head freeze of the main#389 case from four days to zero: the author's later revisions would have gone out as new pinned heads, each with its own @@ -207,45 +219,85 @@ until the supersession, about four days. ## 6. Measurement plan -All measures come from board and PR records, which carry server timestamps. - -- **Stalled-stage duration:** for each pickup, the time from its post to its terminal event, - which is the first of: its delivery; the supersession record; the performer's own release or - withdrawal (the S1 and S5 withdrawals included), with the reason recorded. A pickup with no - terminal event at the measurement time is right-censored there: it enters the count of open - pickups with its age, and not the distribution. Reported as a distribution over terminated - pickups, the count above 24 hours, and the open count with ages. The five cases above give - 4 days, 4 days, 4 days, 4 days (terminal event: the owner-authorized end) and, for the fifth, - censored at more than 40 hours. -- **Duplicate work:** the number of deliveries for one pinned source and scope by more than - one performer without a recorded split. -- **Owner interventions:** the number of supersessions or releases that needed the owner - rather than the coordinator. The five cases needed one owner intervention for four of them. -- **Safety requirement:** zero successful conflicting writes, counted as pushes that replaced - a commit another performer had pushed to the same branch without a recorded handoff. With - branch protection against force pushes that applies to administrators and to every other - bypass actor (no force-push bypass list), this count is enforced to zero on the protected - branches; a protection that exempts administrators or a bypass role enforces it only for the - actors it binds, and the exempt actors' pushes are audited like any other. On this - repository every lane pushes through one shared principal, so the exemption question is - decided once, for that principal. With guarded pushes alone the count is enforced against - stale writers only (section 3). - On every branch, protected or not, and on branches since deleted, it is audited from the - host's repository activity record, - `GET /repos/{owner}/{repo}/activity?activity_type=force_push`, which records each force push - with its before and after commits; the read +All measures use the existing canonical board/PR pickups and native repository activity, +with source IDs, server timestamps and actual observation bounds. Board mirrors are not new +pickups. Before a comparison, declare the same eligibility and terminal-event definitions, +cohort-entry windows, coordinating lanes, and a fixed follow-up duration H for both cohorts. +No value of H or new operating rule is adopted here. Every enrolled pickup receives H of +follow-up, unless a known terminal occurs earlier; a cohort not yet fully observed is marked +incomplete and receives no final before/after verdict. Do not remove its slow open pickups. +Retain missing follow-up, incomplete terminal-event acquisition and uncertain event times in +the eligible cohort inventory as UNKNOWN. Do not assign H or a completion state to an +unobserved interval, exclude those pickups, or issue a final comparison with that missingness +unresolved. These qualification limits apply before computing the rates below. + +- **Stalled-stage duration with fixed follow-up:** a terminal is the first delivery, + supersession record, or the performer's own release/withdrawal (including S1/S5), with the + reason retained. For every eligible pickup record the waiting time capped at H: the elapsed + interval from its pickup post to a known terminal within H, otherwise H and an explicit + right-censored/open flag. Compare these capped waiting times over ALL eligible pickups + together with the proportion reaching + a terminal by H. Assigning H to a still-open pickup records its observed capped wait, not + a fictitious completion. It does not estimate the unbounded completion-time distribution. + A distribution over completed pickups alone is only conditional/descriptive and cannot + establish improvement. Report the open ages and the count beyond 24 hours as well. The + five historical cases above are descriptive: four terminal events at about 4 days and one + right-censored beyond 40 hours, not a comparative effectiveness estimate. +- **Duplicate work:** report the raw number of additional equivalent deliveries for one + pinned source/scope without a recorded split, its count of affected eligible pickup scopes, + and the total number of eligible pickup scopes. Compare affected-scope rates using that + declared denominator; differing raw task volumes alone are not an effect of the protocol. +- **Owner interventions:** report the raw number of owner interventions and the number of + eligible pickups affected, each against the total eligible-pickup denominator. One owner + action covering four pickups is one action and four affected pickups, not four independent + owner decisions. Use the same eligibility and follow-up H before and after; report cohort + sizes and exposure, not only raw counts. The five historical cases needed one intervention + for four of them; they do not establish a normalized before/after improvement. +- **Safety objective and observable replacement audit:** the objective remains no destructive + cross-performer branch replacement without a recorded handoff. The directly observable + metric is the count and details of potentially destructive branch events, not an assumed + count of performer conflicts. Collect native `force_push`, `branch_deletion` and + `branch_creation` activities for every relevant branch, protected or not, including deleted + branches. Preserve event ID, repository/ref, server time, recorded principal and available + before/after commits; mark absent fields as unavailable. Correlate deletion followed by + creation of the SAME repository/ref and reconcile the old/new head identities, even when + the pair crosses a reporting boundary. An unrelated recreated head can remove another + head without a `force_push` event. Include unmatched deletions and unresolved correlations + explicitly; do not report them as safe or silently drop them. The + [repository activity documentation](https://docs.github.com/en/rest/repos/repos#list-repository-activities) + lists these separate event types. The earlier [456/6281763561](https://github.com/d6g8k5htny-coder/main/pull/456#issuecomment-6281763561) - found such records in this repository for an unprotected branch and for a deleted one. How - long the host retains them was not established, so the count is taken during each - measurement window, not only at its end. + verified force-push examples only; it did not establish complete replacement coverage. + + The shared GitHub principal does not identify an execution. An event's SHAs and account + alone cannot decide which performer acted, whether it was an own rewrite, or whether an + applicable handoff existed. Qualify those claims only from actual execution-specific + evidence matched to the existing canonical pickup/handoff and exact event/source identity; + retain the evidence and its limits. Otherwise label performer/handoff attribution UNKNOWN. + Report observable event counts, confirmed dispositions and unresolved/unknown cases + separately. Zero confirmed cross-performer conflicts with unknown cases is not evidence + of zero conflicts. This design installs no attribution service and invents no identity or + immutable correlation that the current records do not supply. + + On protected branches the two history-discard paths are blocked only where BOTH + non-fast-forward updates and branch deletion are prohibited for every relevant actor, + including administrators and all bypass actors; exempt actors remain subject to audit, + not an enforced-zero claim. A force-push restriction alone does not block deletion and + recreation. These protections do not prevent semantic conflicts in fast-forward changes. + The design reads or changes no settings and attests to no current protection coverage. + Activity retention and missing boundary events must be qualified during each window; + incomplete coverage is UNKNOWN, not a zero. Record the initial/final ref observations and + collect activities during the window, not only at its end. Report counts with their branch + observation exposure; compare rates only under matched coverage or a declared denominator. -A comparison runs over two windows, before and after adoption, on the same set of -coordinating lanes. No live lease-expiry experiment is part of this plan. +The comparison uses two fully followed cohorts before and after any separately approved +adoption, with these eligibility, exposure and uncertainty limits. No live lease-expiry +experiment, measurement execution or operating-rule adoption is part of this design. ## 7. Reproduce python3 -m unittest tests.test_pickup_fence -v python3 -O -m unittest tests.test_pickup_fence -Both runs pass (eight tests) on a checkout with `git` on the path; the test writes only inside +Both runs exercise nine tests on a checkout with `git` on the path; the test writes only inside a temporary directory. diff --git a/tests/test_pickup_fence.py b/tests/test_pickup_fence.py index c1fee1a6b..39d075410 100644 --- a/tests/test_pickup_fence.py +++ b/tests/test_pickup_fence.py @@ -10,6 +10,8 @@ The three lease cases of the nonauthor read of main#456 (comment 6281763561) are included: the implicit lease after an unintegrated fetch, ``--force-if-includes``, and a deliberate rewrite under a current lease. +A ninth case contrasts ordinary divergent-push rejection with deletion and unforced recreation; +it does not exercise GitHub activity events or branch protections. Run from the repository root: python3 -m unittest tests.test_pickup_fence """ @@ -123,6 +125,29 @@ def test_deliberate_rewrite_under_a_current_lease_is_not_prevented(self): self.assertEqual(r.returncode, 0) self.assertNotEqual(self.remote_head(), self.new_head) + def test_deletion_and_ordinary_recreation_is_not_prevented(self): + """An ordinary divergent push rejects, but delete plus unforced recreation discards B. + This is local Git behavior, not a test of GitHub activity events or protections. + """ + git('fetch', '-q', 'origin', cwd=self.a) + # Fetch makes B's object available without integrating it into A's candidate. + is_ff = git('merge-base', '--is-ancestor', self.new_head, 'HEAD', cwd=self.a, check=False) + self.assertEqual(is_ff.returncode, 1) # non-ancestry, not an unknown-object error + mine = git('rev-parse', 'HEAD', cwd=self.a).stdout.strip() + self.assertNotEqual(mine, self.new_head) + rejected = git('push', 'origin', 'work', cwd=self.a, check=False) + self.assertNotEqual(rejected.returncode, 0) + self.assertIn('[rejected]', rejected.stderr + rejected.stdout) + self.assertEqual(self.remote_head(), self.new_head) + + git('push', '-q', 'origin', '--delete', 'work', cwd=self.a) + missing = git('show-ref', '--verify', '--quiet', 'refs/heads/work', cwd=self.remote, check=False) + self.assertEqual(missing.returncode, 1) + # No force option: creation is a separate operation from a fast-forward update. + git('push', '-q', 'origin', 'HEAD:refs/heads/work', cwd=self.a) + self.assertEqual(self.remote_head(), mine) + self.assertNotEqual(self.remote_head(), self.new_head) + def test_plain_force_is_not_prevented(self): """A bare ``--force`` overwrites B's commit: this write is only discouraged by the workflow.""" git('push', '--force', '-q', 'origin', 'work', cwd=self.a) From d9ed01013f38c69893e8b70003c656b974e8b811 Mon Sep 17 00:00:00 2001 From: Dylan Roy <249851642+d6g8k5htny-coder@users.noreply.github.com> Date: Tue, 20 Oct 2026 10:19:16 -0500 Subject: [PATCH 5/5] docs: preserve recovery handoff ordering and observation boundaries MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address four Ready-review findings in the design record. Keep separate prospective offer, personal receiver ACK and fresh activation; observe duplicate scope through H; isolate untreated baseline and qualify workload mix and causal limits. Dylan Roy — delegated AI work. Actual amendment author: OpenAI/Codex /root/main456_successor_integrator_1020, new source stage 6294231132. Original Claude01N attribution retained. Design only; no operating-rule adoption or scientific effect. --- ...ICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md | 152 +++++++++++++----- 1 file changed, 112 insertions(+), 40 deletions(-) diff --git a/docs/PICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md b/docs/PICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md index 86262d197..b43323271 100644 --- a/docs/PICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md +++ b/docs/PICKUP_RECOVERY_PROTOCOL_DESIGN_20261020.md @@ -16,6 +16,9 @@ Amendment author: OpenAI/Codex `/root/main456_successor_integrator_1020`, under separate source stage [6293498638](https://github.com/d6g8k5htny-coder/main/pull/456#issuecomment-6293498638), addressing five Ready-review findings on head `4c5ab1f7`. Original source authorship above is retained. This amendment is still a design record, not operating-rule adoption. +Further documentary amendment by the same actual OpenAI/Codex invocation under new stage +[6294231132](https://github.com/d6g8k5htny-coder/main/pull/456#issuecomment-6294231132) +addresses four Ready-review findings on head `43b5337a`; the prior source stage is closed. ## 1. The problem, with its evidence @@ -54,10 +57,10 @@ constraints from the disposition: 3. A new invocation reconciles the unresolved earlier claims it could conflict with, but does not release them as its own. A lookback window is a convenience, not a cutoff. 4. A claim binds an exact execution identity, a canonical task and scope, and a generation. - Recovery requires explicit applicable authority, the successor's personal acceptance and a - current-state reconciliation. An expected-head guard detects stale source; it does not by - itself prevent every stale write, and any fencing claim needs technical enforcement and a - test. + Recovery uses the ordered separate authority, personal acknowledgement and fresh + post-acknowledgement activation records in section 5 point 4. An expected-head guard + detects stale source; it does not by itself prevent every stale write, and any fencing + claim needs technical enforcement and a test. 5. Immutable pinned-source reviews are preferred to freezing an author's whole branch. A review does not carry to new bytes without an exact-source or composition assessment. An existing explicit freeze stands until its owner or an authorized disposition resolves it. @@ -140,14 +143,17 @@ delivery names the exact head it checked; a delivery for a superseded head is no and is not promoted either. **S4. A crash before release.** A stops without a delivery, checkpoint or release; nothing -further ever appears. Records: the pickup only. Who acts: no one may release A (corrections -1–2). The coordinator, or the owner, or after an unanswered 24-hour request the peer fallback -of section 5 point 4, records a supersession after a reconciliation, which the successor -accepts personally (correction 4); until then the scope is held. Prevented: a -successor's stale push, if guarded. Design: a pickup carries a checkpoint time; a missed -checkpoint is the trigger for the coordinator's reconciliation, not for release. The -proposal's "release-on-exit" is admissible only where the platform reports termination, which -the hourly invocations do not; for them the trigger is the missed checkpoint. +further ever appears. Records initially: the pickup only. No one may invent A's own release +(corrections 1–2). Applicable authority first offers a prospective supersession for a named +actual receiver and exact future scope. The receiver subsequently posts its personal ACK; +only AFTER that ACK does the coordinator freshly reconcile current refs, claims, findings +and reported in-flight work and post a separate explicit activation. The three records +cross-link in that order as specified in section 5 point 4; the limited peer fallback there +does not omit the receiver's ACK or the fresh activation. Until actual activation, the +successor does not mutate the held scope. A guarded push can reject a stale expected head, +but cannot provide this custody handoff. A missed checkpoint triggers reconciliation, not +release. "Release-on-exit" requires an observable trustworthy termination of the exact +invocation; absent that evidence, A's own closeout remains UNKNOWN. **S5. Ambiguous claim order.** Two pickups, each citing a contender check that predates the other's post, with timestamps seconds apart or under clock skew. Records: the two posts with @@ -180,21 +186,43 @@ old head, it makes it a review of old bytes (correction 5). 3. **Reconciliation on start.** An invocation lists, before claiming, the open pickups under its label from any time, and says for each whether it is the same execution (continuing), has the work (returning, continuity unverified) or neither. It releases none of them. -4. **Supersession record.** Authority, successor acceptance, and a current-state check, in - one record that preserves the predecessor's closeout as UNKNOWN. The successor's pickup - links it. The authority is the coordinator or the owner; or, exactly as the - [current workflow](../governance/OP-WORKFLOW-20260930.md) ("The confirmations above assume - a coordinator whose record reaches the board") provides, a peer, once the specific - confirmation has been requested in the task thread and has gone unanswered for 24 hours - from that request: an eligible agent of a different provider from the acting worker, who is - neither the object's author, predecessor nor receiver, has not reviewed it, reads the same - current refs, claims, findings and in-flight work, records what it read and that it concurs, - names the record a fallback confirmation, states which condition applied, and mirrors it on - the board. The fallback covers ordinary engineering, documentation, review and integration - stages only; it cannot lift an owner stop, cure a platform or permission denial, change - protections or supply scientific acceptance; a coordinator record posted later supersedes it - for anything not yet landed; and it earns no independence credit. Nothing here widens or - narrows that section. +4. **Separate supersession records, in order.** For an unfinished role, applicable + authority first publishes a prospective recovery offer in the existing task, naming + the actual eligible receiver, exact affected future scope/source, finite checkpoint + and retained stops, with links to the predecessor's canonical claim. The proposal + permits no mutation. The named receiver subsequently reads that exact offer and + current state and posts its own personal acknowledgement, cross-linking the offer; + no coordinator-authored acceptance, alias, silence or earlier pickup substitutes. + AFTER that acknowledgement, the coordinator freshly reconciles current refs, claims, + findings and reported in-flight work and publishes a distinct explicit activation, + linking both the offer and actual receiver acknowledgement. Changed or crossed work + stops the affected action for reconciliation; an earlier state check is not the + post-acknowledgement confirmation. Only then may the receiver take its active pickup, + link those records and recheck source/contention before acting. Preserve the + predecessor's actual work/attribution and its own closeout as UNKNOWN where unresolved; + activation is prospective authority for the named scope, not a foreign release. + Keep these actual separate records and their ordering visible in the task and + cross-linked board mirrors; one summary may index them but cannot replace them. + Applicable authority is the coordinator or owner; the limited peer fallback below + replaces only its permitted unanswered coordinator confirmation and preserves the + receiver's personal acknowledgement, fresh checks and actual separate activation. + As the [current workflow](../governance/OP-WORKFLOW-20260930.md) ("The confirmations + above assume a coordinator whose record reaches the board") provides, a fallback applies + when the coordinator is unavailable, or is answering but a platform failure keeps its + record off GitHub, and only after the specific routine confirmation was requested in + the task thread and has gone unanswered for 24 hours measured from that request. The eligible peer + is a different provider from the acting worker, neither the object's author, + predecessor nor receiver, and has not reviewed it. It independently reads the same + current refs, claims, findings and reported in-flight work, records what it verified + and that it concurs, labels the fallback confirmation and known condition, and + cross-links it on the current board. It covers only the ordinary engineering, + documentation, review and integration stages permitted by that section. It cannot + approve a manual-review route, lift an owner stop, cure or relay a platform/permission + refusal, change protections/private access, activate a security-sensitive stage or + supply scientific acceptance. A later coordinator record supersedes it for anything + not yet landed; it earns no independence credit. No absent or blocked approval is + fabricated and no denied step is routed elsewhere. Nothing here widens or narrows + that governing fallback section. 5. **Reviews bind pinned sources.** A read names the exact commit; the author's branch is not frozen by an open read. A later revision does not inherit the read; it gets a byte-diff readback against the read head, or a fresh read, as today. A freeze is explicit, owned and @@ -223,17 +251,50 @@ All measures use the existing canonical board/PR pickups and native repository a with source IDs, server timestamps and actual observation bounds. Board mirrors are not new pickups. Before a comparison, declare the same eligibility and terminal-event definitions, cohort-entry windows, coordinating lanes, and a fixed follow-up duration H for both cohorts. -No value of H or new operating rule is adopted here. Every enrolled pickup receives H of -follow-up, unless a known terminal occurs earlier; a cohort not yet fully observed is marked +No value of H or new operating rule is adopted here. Every eligible pickup/scope receives +the full H of event observation from its predeclared entry timestamp, even when a duration +terminal occurs earlier. A terminal stops ONLY that pickup's stalled-duration clock; it does +not stop delivery, split, scope or duplicate-work acquisition. Predeclare the canonical +scope-entry pickup and equivalence rule for the exact pinned source/scope so later equivalent +deliveries do not restart H or become new denominator scopes. Keep observing all equivalent +deliveries through the original scope-entry plus H; a later overlapping delivery after the +first terminal still enters the duplicate endpoint. A cohort not yet fully observed is incomplete and receives no final before/after verdict. Do not remove its slow open pickups. -Retain missing follow-up, incomplete terminal-event acquisition and uncertain event times in -the eligible cohort inventory as UNKNOWN. Do not assign H or a completion state to an -unobserved interval, exclude those pickups, or issue a final comparison with that missingness -unresolved. These qualification limits apply before computing the rates below. +Retain missing follow-up, incomplete event acquisition and uncertain event times in the +eligible cohort inventory as UNKNOWN. Do not assign H or completion to an unobserved +interval, exclude those pickups or issue a final comparison with that missingness unresolved. +These observation limits apply to all endpoints below, including deliveries after an early +duration terminal, owner interventions and scope splits. + +Before adoption or outcome inspection, prespecify stage/type strata (for example source +authoring, documentation review, engineering review and mathematical review), relevant +difficulty/dependency factors observable at entry, their classification rules, and a common +target mix with fixed weights. Compare like strata over the same H and report their +eligibility counts, exposures, missingness and outcomes separately before any aggregate. +Do not choose strata, difficulty labels or weights from observed completion times or +intervention outcomes. If the two cohorts lack comparable support in a required stratum, +or entry difficulty is missing or not comparable, retain that limitation as UNKNOWN and +withhold the target-mix aggregate; do not silently drop hard work, pool unlike stages or +substitute the raw changing mix. Fixed common weights can prevent a changed measured mix +from driving the aggregate, but do not remove unmeasured difficulty, selection, secular +changes or other confounding. Even a fully observed balanced before/after comparison is +an association, not identification of a causal protocol benefit. + +Predeclare an adoption boundary T and the cohort-entry windows before outcomes. For the +untreated baseline, require each enrolled pickup/scope's entire scheduled H to end before T, +including duplicate observation after any early duration terminal. Start the post-adoption +entry window at or after T and wait for its full H. Keep pickups whose follow-up crosses T +in an explicit crossover inventory, outside the untreated baseline, with actual exposure +and outcome uncertainty recorded; do not relabel them untreated, silently discard them +from the eligible inventory or select a convenient boundary after seeing results. +This measurement classification never withholds or delays otherwise authorized recovery. +If the prespecified isolated baseline cannot be obtained, report that the intended +comparison is unavailable rather than claim an untreated baseline or a protocol effect. - **Stalled-stage duration with fixed follow-up:** a terminal is the first delivery, - supersession record, or the performer's own release/withdrawal (including S1/S5), with the - reason retained. For every eligible pickup record the waiting time capped at H: the elapsed + completed supersession activation (not its prospective offer or receiver ACK), or the + performer's own release/withdrawal (including S1/S5), with the reason retained. For every + eligible pickup record the waiting time capped at H: the elapsed interval from its pickup post to a known terminal within H, otherwise H and an explicit right-censored/open flag. Compare these capped waiting times over ALL eligible pickups together with the proportion reaching @@ -245,8 +306,14 @@ unresolved. These qualification limits apply before computing the rates below. right-censored beyond 40 hours, not a comparative effectiveness estimate. - **Duplicate work:** report the raw number of additional equivalent deliveries for one pinned source/scope without a recorded split, its count of affected eligible pickup scopes, - and the total number of eligible pickup scopes. Compare affected-scope rates using that - declared denominator; differing raw task volumes alone are not an effect of the protocol. + and the total number of eligible pickup scopes. Continue delivery/scope observation to + that scope's original entry plus H even after the first delivery, withdrawal or + supersession ends a pickup's duration clock. Thus a first delivery followed by a later + overlapping delivery within H counts as duplicate work; it is not erased by the first + terminal. Compare affected-scope rates using the declared denominator and prespecified + comparable mix. Report any known later deliveries separately; zero observed within H + is not a claim of no future duplicates. Differing raw task volumes alone are not an + effect of the protocol. - **Owner interventions:** report the raw number of owner interventions and the number of eligible pickups affected, each against the total eligible-pickup denominator. One owner action covering four pickups is one action and four affected pickups, not four independent @@ -290,9 +357,14 @@ unresolved. These qualification limits apply before computing the rates below. collect activities during the window, not only at its end. Report counts with their branch observation exposure; compare rates only under matched coverage or a declared denominator. -The comparison uses two fully followed cohorts before and after any separately approved -adoption, with these eligibility, exposure and uncertainty limits. No live lease-expiry -experiment, measurement execution or operating-rule adoption is part of this design. +The proposed comparison therefore needs two fully observed, prespecified comparable +cohorts, an untreated baseline whose entire H precedes separately approved adoption, and +these exposure/uncertainty limits. Its reported differences remain observational +associations; balancing recorded factors alone does not establish a causal protocol effect. +A causal claim would need its own justified identification design and substantive review. +No adoption boundary, H, strata/weights or measured benefit is selected here. No live +lease-expiry experiment, measurement execution or operating-rule adoption is part of this +design. ## 7. Reproduce