100 lines
13 KiB
Markdown
100 lines
13 KiB
Markdown
# PR merge squash message field
|
||
|
||
- **Charter:** `/home/hermes/agent-work/CHARTER-PRMERGE-MESSAGE-FIELD.md`
|
||
- **Owner:** `be-coder-08`
|
||
- **Branch:** `fix/pr-merge-message-field`
|
||
- **Base:** remote `main` / local `origin/main` at `85d2108e4ed15c744ad3b87a5b629e7b2d39405a`
|
||
- **Estate:** HOMELAB tooling shared by HOMELAB and USC
|
||
|
||
## Objective
|
||
|
||
Add an optional, identity-checked Gitea squash message to `pr-merge.sh` so genuine multi-author PRs retain non-poster branch authors without weakening hardcoded squash behavior.
|
||
|
||
## Binding requirements
|
||
|
||
1. `Do` remains hardcoded to `squash`; no provider/repository default may select merge style.
|
||
2. A verified trailer uses a PR commit's linked `author.login` and that same commit's author email. No `/users/{login}` primary-email lookup occurs. Recorded rationale: this asks only what the provider can answer.
|
||
3. A commit with `author.login` null blocks before merge, prints both the null provider fact and commit email fact, and names the escalation principal.
|
||
4. The BLOCK arm must be observed firing; a normal canonical single-author API payload remains explicit squash plus its reviewed `head_commit_id`.
|
||
5. Every provider mutation is read back from the provider; no real PR is merged during tests.
|
||
|
||
## Derived interface decisions
|
||
|
||
- Add `--co-author-trailers` rather than accepting arbitrary message text. The wrapper enumerates PR commits and constructs trailers, making an unchecked `Co-authored-by` line unexpressible.
|
||
- Require `--escalate-to PRINCIPAL` with `--co-author-trailers`, so the BLOCK diagnostic always names a principal rather than a generic role.
|
||
- Do not expose `MergeTitleField` separately. When trailers exist, set it from the provider PR title and set `MergeMessageField` only to construction-generated trailers. This preserves one provider source for the title and avoids an unrelated caller-controlled degree of freedom.
|
||
- Preserve first-commit order and emit one trailer per distinct non-poster `author.login`, using that first linked commit's own email.
|
||
|
||
## Canonical delivery plan
|
||
|
||
1. Port the capability into the installed source of truth, `packages/mosaic/framework/tools/git/pr-merge.sh`; do not retain `infra/fleet/tools/git` as a second copy.
|
||
2. Preserve canonical `--expect-head`, exact head branch/repository/SHA queue inspection, Gitea atomic head pinning, GitHub `--match-head-commit`, and delete-after-merge semantics.
|
||
3. Do not port the deployed-only `--skip-queue-guard` bypass. Add the focused harness to the canonical framework-shell suite and re-establish RED/GREEN on the packaged baseline.
|
||
4. Deliver through a reviewed package release followed by `mosaic update` with its default framework reseed. The installer snapshots, manifest-syncs framework-owned `tools/**`, and rolls back on failure.
|
||
5. Before either estate relies on the change, require installed/package hash equality, `MergeMessageField` presence, and a green focused harness. Release/reseed ownership is currently unassigned and blocks activation after source merge.
|
||
|
||
## Evidence
|
||
|
||
- RED against the byte-identical deployed baseline (`sha256 08a65e8584c5…`): rc 1 with eight named failures. The wrapper rejected `--co-author-trailers`; the null-login path emitted none of the required BLOCK facts/principal; and both verified/ordinary API paths failed the stdin-config credential assertion (ordinary path exposed the fixture token through curl argv). Log: `/home/hermes/agent-work/be-coder-08/evidence/prmerge-message-field-red.log`.
|
||
- GREEN on the deployed-baseline candidate: verified linked multi-author payload, null-login BLOCK, required named principal, explicit squash, stdin-config token transport, and absence of `/users` lookup all passed. Log: `/home/hermes/agent-work/be-coder-08/evidence/prmerge-message-field-green.log`.
|
||
- RED against canonical packaged baseline `c581ef48…`: rc 1 with 32 assertions. It rejects the new option, and the first harness version did not satisfy canonical head branch/repository/SHA metadata. Log: `/home/hermes/agent-work/be-coder-08/evidence/prmerge-packaged-baseline-red.log`. The port adapts the fixture rather than weakening canonical head controls.
|
||
- Provider capability probe against `git.mosaicstack.dev`: authenticated `be-coder-08` POST to deliberately nonexistent PR `2147483647` with both message fields returned JSON HTTP 404; the unauthenticated same request returned JSON HTTP 401 (not the charter's predicted 403). The authenticated-vs-unauthenticated differential proves write authorization resolved while no mergeable subject existed. `tl-mosaic` ruled the literal non-load-bearing: preserve the observed 404/401 pair and do not manufacture a 403 case. No cause was inferred and no real PR was targeted.
|
||
- Provider-generated trailer behavior is not treated as exclusive or absent. The wrapper's VERIFIED/BLOCK decision binds each requested non-poster trailer to commit `author.login` plus that commit's email; it does not assume `MergeMessageField` is the squash's only trailer source. The poster is omitted from the constructed list because the resulting squash author already records the poster; any additional provider-generated trailer is outside this change's unmeasured mechanism.
|
||
- An early candidate SHA-256 `5de32876990e4f26920448cb3220cc7f1146d558b4dd2bc1ee1a2abee2f2cbe6` passed the initial harness, then author-side review found credential-fallback and argv-exposure defects. The live deployed wrapper was atomically restored to baseline SHA-256 `08a65e8584c52c6d41ea1c686f8b95585c21e4b37320a2447eba09359a0e02c1`; the remediated candidate remains only in the worktree.
|
||
|
||
## Remediation and current review state
|
||
|
||
1. Token and Basic Auth now use stdin curl configuration, not argv. PR title, contributor email, and the JSON payload also remain out of child argv.
|
||
2. Each credential attempt binds commit inspection and merge. A token failure during either inspection or mutation causes Basic fallback to repeat inspection before mutation; the payload pins the inspected `head_commit_id`.
|
||
3. Focused tests cover token-resolution fail-closed behavior, both HTTP-401 fallback seams, metadata/credential argv absence, null-login BLOCK, explicit squash, canonical reviewed-head binding, unchanged ordinary payload, and retained log-safe provider diagnostics. Token-resolution RED: `/home/hermes/agent-work/be-coder-08/evidence/prmerge-token-resolution-red.log`.
|
||
4. Codex review rounds 3–5 requested retained provider error text, log-safe provider diagnostics, fail-closed credential fallback, stable value-option parsing, and PR-title trailer-injection prevention. These are remediated with regression assertions. A post-remediation independent review is still required.
|
||
5. **Accepted linkage limitation:** `author.login` resolution proves that the commit address maps to a registered provider account. It does not prove that the named principal authored the commit because Git author metadata is self-asserted. This gate checks attribution linkage, not authorship; commit signing is out of scope and currently unadopted. Coordinators explicitly ruled that this does not add a third state.
|
||
6. Codex's sandbox could not execute the harness because its checkout was read-only; that environmental limitation is recorded separately from host-side test results.
|
||
|
||
## Disposable provider fixture acceptance
|
||
|
||
- Use a retained scratch repository only, with two branch authors and `author != committer` on at least one commit.
|
||
- Arm A supplies a message-field trailer for one non-poster; record whether that value lands without forcing the partial-pair result into under-specified `APPENDS`/`REPLACES` labels. Demonstrate an absence control.
|
||
- Arm B includes a registered trailer for a different non-poster on a branch commit; record whether it survives or drops. Verify identity through an existing commit whose `author.login` resolves and demonstrate an absence control.
|
||
- Parse landed trailers key-agnostically with `^[A-Za-z-]+-[Bb]y:` and record generated poster pair presence/absence plus resulting poster attribution.
|
||
- Record `/users/<login>` status and raw email only as non-gating estate telemetry. Never read `active`, `visibility`, or any profile field as an identity gate.
|
||
- Use distinct principals: poster `be-coder-08`, merger `Mos`, Arm A `be-coder-07`, and Arm B `be-coder-06`. Capture every trailer-shaped line verbatim and in order. Zero trailer lines means the generator did not fire and the run is `VOID`, not evidence that either arm dropped.
|
||
- Report the same read-back evidence to `mos-claude` on socket `default` and `tl-mosaic` on socket `mosaic-fleet`. Report values rather than mechanism inferences and stop on any poster-attribution regression.
|
||
|
||
## Fixture preflight
|
||
|
||
- Retained public repository: `mosaicstack/prmerge-trailer-fixture`; PR `#1`, posted by `be-coder-08` and reserved for merge by `Mos`.
|
||
- Existing `mosaicstack/stack` commits resolve `be-coder-07` and `be-coder-06` through `author.login`; exact addresses are `[email protected]` and `[email protected]`.
|
||
- Non-gating HOMELAB telemetry for authenticated reader `be-coder-08`: `/api/v1/users/be-coder-06` returned HTTP 200 with raw `email` value `[email protected]`.
|
||
- Provider preflight showed PR commit enumeration is newest-first. A new RED test proved that deriving `head_commit_id` from the final array element selected the wrong commit. The candidate now reads `.head.sha` from the authenticated PR endpoint before enumeration, verifies it appears in the commit set, and atomically pins that SHA in the explicit squash payload. RED: `/home/hermes/agent-work/be-coder-08/evidence/prmerge-head-order-red.log`.
|
||
- Fixture PR head `f6ba6e5105031fa21f5ff7bd8e4379d99c16e1de` has `author.login=be-coder-07`, `committer.login=be-coder-08`, and branch-message trailer `Co-authored-by: be-coder-06 <[email protected]>`.
|
||
|
||
## Fixture result
|
||
|
||
- `Mos` merged retained fixture PR `#1` through staged candidate SHA-256 `60e779a85fd13b729d859ea7c986d1e9b1641b97991611329226c1b3113ffb6e`; resulting squash commit: `3f550715d9bc716426fd355a65fe997b3a90fa7d` with one parent.
|
||
- Provider read-back: poster/commit author `be-coder-08`, committer/merger `Mos`. The run is non-void.
|
||
- Trailer-shaped lines, verbatim and in order:
|
||
1. `Co-authored-by: be-coder-07 <[email protected]>`
|
||
2. `Co-authored-by: be-coder-08 <[email protected]>`
|
||
- Arm A supplied field value (`be-coder-07`) landed. Arm B branch trailer (`be-coder-06`) dropped. Both fabricated absence controls remained absent. No `Co-committed-by:` line landed.
|
||
- The candidate payload construction explicitly excludes the poster and supplied only the Arm A `be-coder-07` line. Therefore the landed poster line was provider-generated, not candidate-composed. The raw result supports `FIELD LANDS`, `BRANCH DROPS`, and `POSTER GENERATED`; it does not support a claim that candidate code supplied the poster. Evidence: `/home/hermes/agent-work/be-coder-08/evidence/prmerge-fixture-readback.log` and the retained provider object.
|
||
- Retained fixture PR `#2` measured the N=2 shape needed by `#1030`: supplied `be-coder-07` then `be-coder-06`; both landed in that order, followed by the provider-generated poster line. No truncation or dedup occurred at N=2. Resulting squash: `39db9d13aed0…`.
|
||
|
||
## Current hold point
|
||
|
||
PR `mosaicstack/stack#1066` is open. Its first frozen head `f4b162fa…` was terminal-green in Woodpecker `mosaic` pipeline `#2225`, but that evidence becomes stale when the canonical port moves the head. The deployed wrapper remains baseline `08a65e85…`; no manual copy will occur. Canonical port tests, commit amendment, rebase, one guarded force-with-lease, exact-head CI, and new independent review remain. Even after source merge, activation remains blocked on an assigned package-release/reseed owner and installed-byte read-back.
|
||
|
||
## Security review 96 remediation
|
||
|
||
Exact reviewed predecessor head: `1ceb11058f64dd7f4a817ceb2124f980a1c4dd23`.
|
||
|
||
RED-first focused harness produced 10 named failures: all curl calls lacked size/time/connect bounds; raw ESC email reached mutation; oversized and stalled curl failures were discarded and reached mutation; nonempty Basic output with resolver rc 91 authorized mutation.
|
||
|
||
Security remediation:
|
||
|
||
- Removed the cross-principal HTTP-401 Basic fallback. Both inspection-401 and merge-401 paths now refuse without Basic resolution or mutation; `get_gitea_basic_auth` references in the merge subject are 0.
|
||
- Applied `--max-filesize`, `--max-time`, and `--connect-timeout` to all 3/3 provider curl sites and fail closed on curl transport rc at all 3/3 sites.
|
||
- Required linked email bytes to be ASCII and printable before constructing `MergeMessageField`; guarded construction sites 1/1.
|
||
|
||
GREEN: message-field, exact-head, empty-UID/API, queue branch/repository/SHA, bash syntax, ShellCheck, and diff check pass. R7 total-removal mutants went RED: email guard 3 rows; bound switches 1 row; transport-rc guards 4 rows; HTTP-401 refusal 3 rows. R7 bound: mutants prove total removal only; explicit denominators above prove site coverage.
|