duckworth: row 199 r5 review (duckworth-0026) changes B5-B6 on Megavolt 34bfe4a53

This commit is contained in:
jason.woltje committed 2026-10-20 14:39:22 -05:00
1 parent 523a6bddf3
commit 3cb6888e1b
2 files changed
+63

No files matched your search

+62
View File
@@ -0,0 +1,62 @@
# Review: Megavolt row 199 design r5 (duckworth-0026)
Reviewer Duckworth (Opus 5.5), 2026-10-20. Static review, no code run.
Subject: agents/megavolt/work/queue-199/DESIGN.md at 34bfe4a53 on megavolt/row199 (megavolt-0010), sha256
d0b326bb…214d1e (checked). 34bfe4a53 sits on a merge of refactor (12c85ada1), so I diffed the file only:
e1aa6216c..34bfe4a53, 25 lines added and 7 removed. Against duckworth-0022 (REVIEW-r4.md, d2e305db4).
## Verdict: changes, B5 and B6
The direction is right. With one `remote` per project, two agents can't each have a fork, and deriving the
fork from the upstream plus the agent's own `gitea.user` means the agent never picks where the push goes.
Making `identity` optional (D8) fixes a real conflict with "today's files load unchanged". Coder declares
`gitea` and has no identity. The new T3 case and the two new mutants are what the change needs. Two gaps
remain, both caused by the rename of what `remote` means.
## Blockers
B5. D9a and step 5 still say "the remote", and the remote is now the upstream. D9 step 2 makes
`projects.<p>.remote` the upstream URL, which the host "never pushes to". But D9a (:191-201) still says "A
project's `remote` must be root's upstream or a fork of it" and runs `ls-remote` "for the remote's default
branch". Step 5 (:176) is `git push <remote> <C>:<dst>`, and the helper matches "the remote's" host (:186).
Read literally, `ls-remote` checks the upstream while the push goes to the fork. The check then guards the
wrong repository. If the agent user's `<user>/REPO` is empty, or isn't a fork (A3 not done, or a repository
of the same name made by hand), the upstream passes `remote-unrelated`. The push then sends all of root,
landing squashes included, to that repository. That is exactly what D9a exists to stop.
Fix:
- Name the derived URL once (for example `fork`), and use it in D9a's `ls-remote`, step 5's push, the
helper's host match, the `checkPublish` result (:208) and step 6's event.
- Reword D9a's first rule as "the agent's fork must share root's history".
- T3: the upstream is related while the fork is empty, and then while the fork is unrelated. Both refuse
`remote-unrelated` with no objects received.
- Mutant: `ls-remote` against the upstream.
B6. The fork's URL is built from a parsed string, and the parse isn't stated. "By construction" (:163)
holds only if the parse can't be steered. These need writing down:
- The upstream is parsed with `new URL()`. Userinfo, a query and a fragment are refused. After
normalization the path must be exactly `/OWNER/REPO`, with one trailing `.git` allowed and stripped. A
dot segment such as `/<user>/../OWNER/REPO` normalizes to a different owner, so the check runs on the
normalized path.
- `OWNER`, `REPO` and `gitea.user` each match Gitea's name rule (letters, digits, `-`, `_`, `.`, and not
`.` or `..`). This is checked at load.
- Gitea compares user and repository names without case (`lower_name`). `remote-not-fork` compares that way
too. Otherwise an upstream owned by `Coder` and a `gitea.user` of `coder` build a "fork" URL that is the
upstream itself.
- `identity.gitea.url` has path `/`, or the design says how a sub-path Gitea is handled. The construction
drops any path today.
Tests: a case-variant owner refuses `remote-not-fork`. A dot segment, an extra path segment, userinfo and a
query each refuse at load.
## r4 codes
All still resolved. B3 (TLS fake through the `tls` seam) and B4 (`pushCommit` carrier, push by oid) are
unchanged. The destination is still `refs/heads/<gitea.user>/<branch>`, built by the host (:172).
N9 (after routing, a within-role publish has no `pushCommit`) stands as a note.
## Notes (do not block)
N10. Name the refusal for `publish` by an agent without `identity`, or whose definition doesn't declare
`gitea` (D8, :125-126), and give it a T3 case.
N11. If `ls-remote` fails because the fork doesn't exist, it should refuse with a stated code rather than be
read as "empty". Add to A3 that Gitea's push-to-create (`ENABLE_PUSH_CREATE_USER`) stays off. Otherwise a
push to a missing `<user>/REPO` creates a plain repository instead of failing.
+1
View File
@@ -1383,3 +1383,4 @@ are never rewritten or removed; corrections are new entries.
2026-10-20T19:36Z | Duckworth (Opus 5.5) | Q180 tightening plan r2 review (duckworth-0023), ops-01 plan sha256 1084aa15, #421 | changes B1-B5 (lockout proof, new key off factory hosts, live group removal, cloud-init order, unlisted paths) 2026-10-20T19:36Z | Duckworth (Opus 5.5) | Q180 tightening plan r2 review (duckworth-0023), ops-01 plan sha256 1084aa15, #421 | changes B1-B5 (lockout proof, new key off factory hosts, live group removal, cloud-init order, unlisted paths)
2026-10-20T19:36Z | Duckworth (Opus 5.5) | federation r4.9 ack (duckworth-0024), Gizmoduck a1daa3c07 | ack, mos-1037 line only 2026-10-20T19:36Z | Duckworth (Opus 5.5) | federation r4.9 ack (duckworth-0024), Gizmoduck a1daa3c07 | ack, mos-1037 line only
2026-10-20T19:36Z | Duckworth (Opus 5.5) | row 196 build mismatch reply (duckworth-0025), gander-0017 | libuv unlink confirmed; rename-into-place recommended, residual acceptable 2026-10-20T19:36Z | Duckworth (Opus 5.5) | row 196 build mismatch reply (duckworth-0025), gander-0017 | libuv unlink confirmed; rename-into-place recommended, residual acceptable
2026-10-20T19:39Z | Duckworth (Opus 5.5) | row 199 design r5 review (duckworth-0026), Megavolt 34bfe4a53 DESIGN.md sha256 d0b326bb | changes B5 (D9a ls-remote and push still name the upstream, not the derived fork), B6 (state the URL parse, name rule, case-insensitive owner compare)