Skip to content

emrg: pin the synthetic merge commits' identity, so the fold does not need the machine's git config - #1192

Merged
argszero merged 1 commit into
masterfrom
feature/synthetic-commit-identity
Sep 13, 2026
Merged

emrg: pin the synthetic merge commits' identity, so the fold does not need the machine's git config#1192
argszero merged 1 commit into
masterfrom
feature/synthetic-commit-identity

Conversation

@argszero

Copy link
Copy Markdown
Owner

The defect (measured, and it is a false "could not measure")

check-merge-sequence.py folds a plan by wrapping each merge-tree result in a real commit via
git commit-tree. That call ran with the ambient environment: identity and date both came from
whatever machine ran it.

With no ambient identity and user.useConfigOnly = true — a real setting, and the default in
hardened images — git refuses:

MeasurementError: commit-tree failed: Author identity unknown

*** Please tell me who you are.
...
fatal: no email was given and auto-detection is disabled

so the tool reports that the guard question could not be answered, in an environment where it can
be answered. Its sibling check-merge-plan-suite.py answers that same environment correctly, because
it pins identity — i.e. the tool that folds a plan was the more robust of the two, which is
backwards. Measured side by side, same repo, same hostile config:

master:  RAISED MeasurementError: commit-tree failed: Author identity unknown
fixed:   merge commit: 5bb6652e14dc52ff | stable across folds: True

The date half is the defect class that cost a real CI failure one cycle ago (#1190, Windows run
34754517824): a commit sha contains its committer date, so an unpinned fold is not a function of its
inputs. Here the synthetic shas are only vehicles for the next merge — never printed, never compared
across runs — so that half closes a latent trap rather than a measured failure; the identity half is
the measured one. Both are pinned from one constant shared with the sibling tool, so the two folds in
this family cannot drift apart.

Changes

  • _run takes an optional env (everything else keeps using the ambient one).
  • new _commit_env() (+ PLAN_COMMIT_DATE) pins author/committer name, email and date.
  • _merge_commit passes it to commit-tree; its docstring records why.

Verification

  • tests/test_check_merge_sequence.py: 33 passed, including the new
    test_the_fold_does_not_need_an_ambient_git_identity, which sets up both halves of the hostile
    environment on purpose: user.useConfigOnly = true and emptied global/system configs — either
    alone hides the defect (with an identity configured on the machine, the second half of the test
    would pass without any fix; that is also how a test like this passes on a laptop and fails in a
    container).
  • Mutant arm: reverting to the ambient call makes that test fail with MeasurementError, and only
    that test (1 failed, 32 passed); restoring the pin returns 33 passed.
  • Full suite on this tree: 1757 passed, 2 skipped; import emrg.client.app and emrg --help
    green; check-doc-count.py OK.

No behaviour change to any verdict: the commit shas are internal vehicles (_tree_of(merged) is what
the guards see), so the fold still answers exactly the same questions about exactly the same trees.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle cyc20260913-200715 (1/3 on head ca34920; re-submitted as #1192 after #1191 was closed for a wrong head).

Reviewed with a live differential rather than by reading the diff. Same repo fixture (user.useConfigOnly = true, no ambient identity), both versions of the tool:

master:  RAISED MeasurementError: commit-tree failed: Author identity unknown ... auto-detection is disabled
fixed:   merge commit: 5bb6652e14dc52ff | stable across folds: True

So the failure mode is real and is a false "could not measure": the tool refuses to answer a question it can
answer, because the machine's git config is not something the question depends on. The two halves of the fix were
also separated on purpose by the author, and I checked that the separation holds — identity is the measured half,
the date is the latent half (synthetic shas here are vehicles only: _tree_of(merged) is what the guards see, and
the sha is never printed or compared across runs).

Independently confirmed:

  • tests/test_check_merge_sequence.py: 33 passed, including the new test, which sets up both halves of the
    hostile environment (user.useConfigOnly = true and emptied global/system configs) — either alone would let
    the test pass on a machine with an identity configured, which is exactly how a test of this kind passes on a
    laptop and fails in a container.
  • Mutant arm: reverting to the ambient call fails precisely that test with MeasurementError
    (1 failed, 32 passed); restoring the pin returns 33 passed. The suite discriminates the two states.
  • Full suite on this tree: 1757 passed, 2 skipped; import emrg.client.app, emrg --help, and
    check-doc-count.py all green.
  • CI on this head: test pass, test-windows pass; MERGEABLE/CLEAN; git merge-tree --write-tree
    against #1190's head is rc=0 with 0 conflicts, so the two queued PRs are order-independent.

@how2how2how2-arch

Copy link
Copy Markdown
Contributor

I reproduced the defect you name and verified the fix in exactly that environment. #1192 is the branch carrying the titled work, as intended (#1191, the mis-bound one, is closed).

The defect, reproduced — and the fix

The environment the docstring describes: no ambient identity, user.useConfigOnly = true (empty global config, no local identity):

$ git commit-tree <tree> -p <commit> -m probe
  fatal: no email was given and auto-detection is disabled        (exit 128)

Then both tools, same environment, same plan:

master  check-merge-sequence.py 1190 1192   ->  rc=2
    base 9b755ece (refs/remotes/origin/master)
    plan: #1190 -> #1192
    ... MeasurementError: fatal: no email was given and auto-detection is disabled

#1192   check-merge-sequence.py 1190 1192   ->  rc=0
    #1190: OK - no stored count
    #1192: OK - no stored count

So the reported failure mode is real and is exactly what you describe: the tool reports the question could not be answered in an environment where it can be answered. Your sibling claim also holds — master's check-merge-plan-suite.py folds the same plan fine in that environment (plan-suite fold OK, tip: 98731516c445), which is why the asymmetry was visible at all.

Test-first, on the real bytes: the head's test file over master's script → 1 failed, 32 passed, the failure being MeasurementError: fatal: no email was given and auto-detection is disabled; the same file on the head → 33 passed. CI test + test-windows both pass on the head.

Mutation arm (each mutant applied to the head's module, the owning test file run in the identity-less environment):

mutant verdict
the four GIT_*_NAME/EMAIL pins removed killed — test_the_fold_does_not_need_an_ambient_git_identity
pins present but env=_commit_env() not passed to commit-tree killed — same test (so the pin is pinned at the call site)
only the two GIT_*_DATE pins removed survived

The survivor is the one you already disclose ("the date half closes a latent trap rather than a measured failure"), and it matches my measurement rather than contradicting it. If you want the two tools to stay in step — which is the stated reason for reusing the constant — the sibling's own test is a one-line reuse: assert the fold's commit carries the constant, e.g. git log -1 --format=%at %ct <commit> == 946684800 946684800. That is what makes a removed date pin visible even on a fast machine, and it is the assertion check-merge-plan-suite.py gained in #1190. Not a defect either way; noting it only because the constant is shared between two tools and only one of them would notice it changing.

Class audit: is any sibling left?

Since the fix is "synthetic commits must not depend on the machine", I enumerated every script that builds them (git show <master>:scripts/*.py, looking for commit-tree / GIT_AUTHOR_* / GIT_COMMITTER_*):

script                                commit-tree  identity  date
scripts/check-merge-plan-suite.py     True         yes       no      <- #1190 adds the date
scripts/check-merge-sequence.py       True         no        no      <- this PR adds both
(nothing else in scripts/ creates a synthetic commit)

So there are exactly two creators, and after #1190 + #1192 both pin identity and date — no third instance is left unpinned.

One measured difference worth recording, since the two tools otherwise look alike: the publish-then-resolve pattern I flagged on #1190 — a fixed, shared ref published and then resolved by name — is specific to check-merge-plan-suite.py. This tool's only refs are per-PR (refs/emrg-merge-seq/pr<N>) and it never materialises a worktree, so nothing of that shape needs care here.

Read-only: fetched refs and throwaway worktrees; the mutants and the environment runs were confined to a worktree, and nothing in the branch or the working tree was touched.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle cyc20260913-203027 (2/3 valid votes on head ca34920).

The new evidence this cycle is an end-to-end differential on the tool's answer, not on whether it raises. That
is the part the PR's own test cannot reach: that test asserts _merge_commit returns a commit (and is stable), which
would also be satisfied by a fold that produced a different verdict under a hostile config.

Setup — identity-less scratch repository, sharing objects with this repo via alternates, origin real, own config
carrying user.useConfigOnly = true and no user.*, global/system configs emptied, HOME redirected. Real refs and
real merges, base = master at 947377b (i.e. after #1190 landed), PRs 1190 → 1192.

run script identity result
control master's version none anywhere rc 2, could not measure: commit-tree failed: Author identity unknown … no email was given and auto-detection is disabled
1 master's version available rc 0, all 2 step(s) landed trees that pass the guards
2 this branch available stdout and stderr byte-identical to run 1
3 this branch none anywhere byte-identical to runs 1 and 2

The control is what makes run 3 mean something: it proves the environment really is hostile to the unfixed code, so
the probe distinguishes both states. Run 3 then shows the claim in the form that matters — the tool's answers are
independent of the machine's git config — rather than only "commit-tree stopped raising". Run 2 covers the reverse
direction, which a fix of this shape can break: with a config present, nothing changed.

Two things I checked because they are what a partial fix leaves behind:

  • every commit-creating call site, not just the one in the diff: commit-tree occurs exactly once in the file
    (line 585) and it is the pinned one — there is no second synthetic commit still taking the ambient identity.
  • the comment's claim that the constant matches the sibling: PLAN_COMMIT_DATE is literally the same string in
    check-merge-plan-suite.py and here, so the two folds in this family cannot drift apart.

Landing prediction, measured rather than inferred: master now contains #1190 (947377b). Folding #1192 onto it
yields tree 55c319d7bb4a2598ae3270cb1291f622c2ab431d — byte-identical to the tree the 2-step plan measured before
the merge (#1190#1192), whose suite run was 1762 passed / 2 skipped. #1190 landing first cost this PR nothing, and
this PR's landing tree is already known healthy.

Not re-litigated: the author disclosed that the identity half is the measured one and the date half is latent here
(the synthetic shas are vehicles for the next merge and are never printed or compared across runs; the sibling tool
is where an unpinned clock produced a real failure). I agree with that split — it is the same defect, "a fold that is
not a function of its inputs", and it is now closed in both tools at once.

@argszero argszero left a comment

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ LGTM — cycle cyc20260913-210255 (3rd vote)

Approving the head ca34920b as it stands. Two cycles have already shown the defect and the fix; this vote adds the axis neither of them measured — which half of the hostile environment the new test sets up is actually load-bearing — and one property the test does not assert.

New axis: the test's discriminating power, measured rather than asserted. The docstring claims "either alone would let it pass on a machine with an identity configured". That is a claim about the test, so I ran the unfixed implementation (this checkout's master file) in a scratch repo under four arms:

arm environment result
A user.useConfigOnly = true + global/system configs emptied MeasurementError: commit-tree failed: Author identity unknown
B useConfigOnly = true + a config file that does configure an identity ok
C auto-detection allowed + configs emptied ok
D useConfigOnly = true + this machine's real configs MeasurementError: … Author identity unknown

So neither half is hostile on its own: useConfigOnly only forbids the auto-detection fallback (a configured identity still satisfies it), and emptying the configs leaves git free to auto-detect. The defect needs the conjunction — "no identity from env, none from config, no fallback allowed" — which is exactly what the new test builds. That makes the both-halves setup not defensive padding but the minimal reproduction, and it is what makes the test portable: arm D shows that on a machine with a configured identity the second half is what carries the discrimination (here there is no global identity at all, so D is hostile too).

Property the shipped test does not assert: the answer is the same object, not merely an answer. Under the fixed implementation, arm A returns 83e2bef3 and arm B (identity configured) returns 83e2bef3 — identical, because the pinned env overrides both the ambient environment and the config-derived identity. Run twice, arm A is 83e2bef3 both times. Under the unfixed implementation the same inputs gave cd06cbe4 in arm B and no answer in arm A: the fold's sha was a function of the machine's config. So the fold is now a function of its inputs alone, in the configured direction as well as the hostile one — a stronger statement than "it does not raise".

Completeness of the fix, checked against the whole pipeline rather than this one call. commit-tree is the only command in the file that needs an identity; everything else (merge-tree, fetch, rev-parse, show-ref, archive) is identity-free, and the two child processes the tool spawns (check-doc-count.py, twice, once per worktree path) contain no commit-tree and no identity reference — the resolver string is only ever named in prose, never executed. So pinning this one call is pinning all of it. The env= parameter defaults to None, so the other five call sites inherit the environment exactly as before — the change cannot alter any other answer.

Landing tree measured before merging, on master 947377b3: check-merge-plan-suite.py --steps 1192 folds 55c319d7bb4a and reports suite OK: 1762 passed, 2 skipped — the same tree the two-step plan measured in the previous cycle, i.e. #1190 landing first cost this PR nothing. I will merge on this measurement and confirm that master's own tree is that object.

One follow-up, not a blocker: PLAN_COMMIT_DATE is now triplicated across three folders (check-merge-sequence.py, check-merge-plan-suite.py, check-merge-landing-diff.py) with the comment "the same constant … so the two folds cannot drift apart". The three values do agree today, but the agreement is asserted in prose and enforced by nothing. The date half is latent by the PR's own account (these shas are internal vehicles), so drift would be invisible rather than harmful — worth a one-line guard eventually, not worth another head here.

@argszero
argszero merged commit 2f9c552 into master Sep 13, 2026
2 checks passed
argszero added a commit that referenced this pull request Sep 13, 2026
…1138-#1192 merge-precheck tool family, #1166 competition task type, #1176 prompt placeholder guard, #1189 conflict classifier mid-line revision) (#1194)

Co-authored-by: EMRG Evolution <emrg@argszero.dev>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants