Repository navigation
fix: keep the data branch present through promotion - #4032
Conversation
… on changed files
fro-bot
left a comment
There was a problem hiding this comment.
Verdict: CONDITIONAL
Blocking issues
- P1 — Moving retry baseline permits lost writes.
packages/wiki-write-core/src/wiki-ingest.ts:368–370resolvesbaseCommitSha: nullagainstmainon every attempt. Reproduced: a concurrent writer changes a write-set path, that change is squash-promoted, and the retry adopts the newermaintree as its baseline. It succeeds on attempt 2 and overwrites the concurrent content with stale output. Passing the original derivation SHA correctly rejects the same sequence. Require the immutable derivation commit and retain its baseline across retries; regeneratedistafter fixing this.
Non-blocking concerns
- P2 — Legitimate concurrent merge-back produces a false ownership alert.
scripts/merge-back-data.ts:239–255can readmainat M0, then readdataafter another run has merged M1. It mistakes inherited M1 changes for unauthorized writes and blocks before reaching the ref-race retry. Independently reproduced; a fresh invocation returnsnoop. Before alerting, confirmmainstill matches the captured SHA; retry with fresh snapshots if it moved. - P2 — Alert failure hides the ownership block.
scripts/merge-back-data.ts:252–255awaits alert delivery before returningblocked. If delivery fails, the CLI reports onlyAPI_ERROR, losing the ownership classification and path count when no issue was delivered. Preserve a privacy-safeBLOCKEDdiagnostic alongside the alert-delivery failure and retain the nonzero exit.
Missing tests
- Add a null-base regression with a concurrent write and promotion between attempts; assert rejection and preservation of the concurrent content.
- Add the overlapping M0/M1 merge-back scenario and an alert-delivery rejection case.
Risk assessment (HIGH) + rationale
The new public null-base mode can silently overwrite concurrent wiki content. The ownership snapshot race also interrupts valid promotions. 700 targeted tests passed across 9 files, and check:wiki-write-core-dist passed; those checks do not cover the reproduced races. Live ruleset enforcement remains an after-merge verification.
Run Summary
| Field | Value |
|---|---|
| Event | pull_request |
| Repository | fro-bot/.github |
| Run ID | 39546002242 |
| Cache | hit |
| Session | ses_ea335a09bffemFedqSA9NC2tNr |
| Verification | 700 tests passed; committed dist current; two races reproduced in memory |
| Coverage limitation | TypeScript specialist return rejected for invalid schema; reported defects independently validated |
…lock visible when the alert fails
fro-bot
left a comment
There was a problem hiding this comment.
Verdict: PASS
The follow-up commits resolve all three prior findings: the derivation baseline stays immutable, overlapping merge-backs retry against fresh snapshots, and alert-delivery failures preserve the ownership-block diagnostic.
Blocking issues
None
Non-blocking concerns
None
Missing tests
None
Risk assessment (MED) + rationale
This changes control-plane branch writes, but the reviewed fixes preserve fail-closed behavior. 705 targeted tests passed across 9 files, and the committed dist check passed. An independent promotion-between-attempts probe rejected the stale write and preserved concurrent content. Live deletion-ruleset enforcement remains the documented after-merge check.
Run Summary
| Field | Value |
|---|---|
| Event | pull_request |
| Repository | fro-bot/.github |
| Run ID | 39548328359 |
| Cache | hit |
| Session | ses_ea335a09bffemFedqSA9NC2tNr |
| Scope | Follow-up commits efa8d7a and f4160b6; all prior findings resolved |
| Verification | 705 tests passed; dist current; stale-write regression independently verified |
Closes #4018
Every
data→mainpromotion squash-merges a PR whose head isdata, anddelete_branch_on_mergedeletesdataat the merge. Nothing recreates it until the next writer bootstraps it. The gateway and dashboard readmetadata/repos.yamlfromdata, and during a 54-minute gap on 10-19 the gateway denied every repo for about 40 minutes.Changes
.github/settings.ymladds a second ruleset onrefs/heads/datathat has only thedeletionrule and no bypass actors. The App-only write ruleset is unchanged, so the App'salwaysbypass does not cover deletion.delete_branch_on_mergestays on for every other branch.scripts/merge-back-data.ts, first step ofmerge-data.yaml). Whendatadoes not already containmain, the App writes one commit with parents[main, data]. The commit's tree ismain's tree, with every data-owned path taken fromdata, including paths thatdatadeleted. Data-owned paths are the authority guard's guarded set, shared throughisDataOwnedPath.datano longer conflicts the next promotion.datadiffers from both the merge base andmain. Landing the same content onmainclears it.updateRef409/422 (3 attempts, re-checking each time) and never forces.datais missing, the bootstrap recreates it.merge-data-pr.ts):updateBranchmerge conflict goes to the conflict alert, which no longer asks for a push todata.@fro.bot/wiki-write-core, patch release):commitWikiChangestakes an optionalbaseCommitSha, thedatacommit its files were derived from. On every attempt it refuses to write if any write-set path changed since that base.updateRef422) is replayed only when the write set is unchanged. The race with the merge-back is therefore retried, while concurrent wiki writes still fail loudly.sync-wikioutputs thedatacommit it restored, and the ingest steps and wiki-repair pass it as the base.metadata/README.mddescribes the persistent lifecycle (deletingdataon purpose means removing the deletion ruleset first, and owned-path reverts go throughdata). The bootstrap solution doc and the autonomous-commit rules are updated to match.The remaining overwrite cases between wiki writers predate this change. They are tracked in #4031.
Verification
contents: writecan move a branch through Git DataupdateRefto a[main, branch]commit that changes 24 workflow files relative to the old tip.check-types,lint,test(5790 passed),check:wiki-write-core-dist,check:mutation-guards(clean;merge-back-data.ts275/275 killed) and actionlint all pass.After merge
data branch: no deletionhas an empty bypass list.datakeeps the same tip SHA across the squash merge. Then dispatch merge-data and confirm it writes one merge-back commit and that the next promotion diff contains only new writes.