Skip to content

sync: adding any new file costs two PRs, a hand-edited sha256 in library code, and an error message naming the wrong file #2354

Description

@gltanaka

Adding a single new file to this repository currently requires three separate, undocumented steps. Measured while landing #2328/#2329: the ceremony took longer than writing the change it gated, and cost four CI cycles.

Since 2026-07-12 there have been 26 PRs titled preauthorize, plus five dedicated worktrees named for preauthorization work. This is the tax behind them.

1. The ownership rule must already be in the protected base

ci_detect_changed_modules._protected_human_owned_paths() resolves the auto-heal exemption from the base, not the PR head:

result = subprocess.run(["git", "show", f"{base_ref}:{_SYNC_OWNERSHIP_PATH}"], ...)

so a PR structurally cannot exempt its own new files. Every new file needs a prior merged PR adding a dormant row to .pdd/sync-ownership.json, then a second PR adding the file. This is enforced but documented nowhere.

2. The error names the wrong file

module ownership failed: unmapped auto-heal module candidate for
tests/test_check_cloud_green.py: 'check_cloud_green';
add or correct its architecture.json ownership

architecture.json is the wrong place: it maps prompts to generated pdd/*.py modules, and a plain script or test is not prompt-backed. Adding an entry there would be incorrect. The correct location is .pdd/sync-ownership.json, which is what scripts/sops_release_env.py and its test already use. Anyone following the message verbatim does the wrong thing.

3. Editing the ownership file silently detaches ownership from eight other files

pdd/sync_core/manifest.py:285 hardcodes a sha256 of .pdd/sync-ownership.json:

_SYNC_ROLLOUT_REPAIR_OWNERSHIP_BYTES = ("<base digest>", "<head digest>")

On any mismatch _sync_rollout_repair_rules returns base_rules with no diagnostic, and the eight repaired .pdd/meta/* paths lose their ownership, surfacing as unrelated-looking failures about code_generator_python.json and friends. So a bookkeeping edit to a JSON policy file requires hand-editing a digest in shipped library code.

#2332 added a guard test that now names the required digest, turning that cascade into one clear message. The underlying coupling remains.

Also: pinned-base guards rejected every new file

test_sync_rollout_repair_executes_the_actual_protected_transition and test_release_video_opt_out_uses_only_actual_base_owned_paths asserted the whole tree was clean against frozen 2026-07-25 base commits. Since ownership loads only from the base, any file added afterwards could never satisfy them. Fixed in #2328 by scoping those assertions to paths that existed at the pinned base; recording it here because the same shape may exist elsewhere.

Suggested fixes, cheapest first

  1. Fix the error message to name .pdd/sync-ownership.json and show the exact row to add. Minutes of work, removes the worst misdirection.
  2. Replace the hardcoded digest with a computed comparison, or fail loudly instead of silently returning base_rules. The guard test from fix(release): opt v0.0.310 out of video and repair gate-caught test bugs #2332 makes this safe to change.
  3. Allow additive, human-owned rules from the PR head. The base-only rule exists to stop a candidate granting itself authority over existing files; a purely additive row for a path that does not yet exist does not do that. This is the change that removes the two-PR requirement.
  4. Consider auto-deriving ownership for conventional paths (tests/test_*.py, scripts/*.py, .github/workflows/*.yml) instead of requiring 2,499 hand-maintained exact rows.

Items 1–3 are small and are paid on every single new file.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    Status
    Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions