Skip to content

fix(ci): the NOW sync gate writes nothing to the clone it runs in (Refs #5482) - #5488

Merged
dmitrii-f-t27 merged 1 commit into
masterfrom
fix/now-sync-gate-read-only
Oct 2, 2026
Merged

dmitrii-f-t27 merged 1 commit into
masterfrom
fix/now-sync-gate-read-only

Conversation

@dmitrii-f-t27

Copy link
Copy Markdown
Collaborator

Refs #5482

What was wrong

scripts/ci/now-sync-gate-diff.sh ended every passing run with setup_skill_merge_driver (added by #4728 for #3236). That function:

  • appended .claude/skills/ci-gates/SKILL.md merge=union to .gitattributes, which left M .gitattributes;
  • set merge.union.name and merge.union.driver (bash -c 'cat $BASE $LOCAL $RIGHT > $REMOTE') in the repository's shared .git/config.

Three callers run the script in a contributor's own clone: a direct run, tri gates preview (row check-now-freshness), and tri hooks pre-push (run by .githooks/pre-push, which CONTRIBUTING step 1 installs).

When a merge driver is configured under a name, it replaces git's built-in driver of that name. So this driver replaced the built-in union that .gitattributes uses for .trinity/experience/*.jsonl. git fills in %O %A %B in a driver command, but this command used $BASE $LOCAL $RIGHT $REMOTE, which are unset shell variables. Two branches appending to one *.jsonl file then failed to merge (bash: $REMOTE: ambiguous redirect, exit 1). With the built-in driver the same merge is clean. In CI the writes were lost when the runner was discarded.

Change

  • scripts/ci/now-sync-gate-diff.sh: removed the writer. A comment now states that the gate is read-only, says why, and gives the cleanup commands.
  • scripts/ci/test_now_gate_writes_nothing.py (new control): runs each caller in its own scratch repository, with an empty global config, on a range the gate passes. The writer only ran after a pass, so a refused range would prove nothing. For each caller, .git/config, .gitattributes, .git/info/attributes and git status must be byte-identical before and after the run, and a two-branch union merge of a *.jsonl log must still succeed afterwards.
    • Controls: the old writer, planted back into the script, must be caught changing both files and must break the same merge.
    • Measured against the unfixed script: all 5 callers fail on both counts (exit 1). Five more planted mutants are each caught: another config key, an unset key, .git/info/attributes, a stray untracked file, and a gate that stops passing. A no-op passes.
    • One repository per caller, because the writer is idempotent. In a first draft the callers shared one repository: the first caller wrote, and the other four rewrote the same bytes and read as clean.
  • CI: untrusted-input-gate.yml runs the script's three arms, which need no Rust. cli-tri.yml runs all five callers with --tri ./target/debug/tri, right after the build.
  • tools/census/shell.txt: re-blessed. run: steps went from 280 to 282 and "the runner does" from 259 to 261. These are the two new steps.
  • .gitattributes: the header comment pointed to .git/config "for implementations". It now says the file uses built-in drivers only. This is a comment change only.
  • docs/now/2026-10-01-the-now-gate-writes-nothing-to-the-clone.md: the NOW entry for this change.

SKILL.md conflicts are handled by the spool (ci-gates SKILL.md sections 592-593). Whether SKILL.md should get a committed merge=union line using the built-in driver is a separate decision, and this PR does not add one.

Cleaning an affected clone

git config --unset merge.union.driver
git config --unset merge.union.name
git checkout -- .gitattributes   # only if the appended line is still uncommitted

No commit on master contains the appended line. On 2026-10-01, none of the 52 open PRs touched .gitattributes.

Verified locally

  • python3 scripts/ci/test_now_gate_writes_nothing.py --tri ./target/debug/tri: 20 checks ok, exit 0. Without --tri: 14 checks ok, exit 0, and the output says the two tri callers were not run.
  • In this clone, tri gates preview --base origin/master and the gate script: the sha256 of the shared .git/config and of .gitattributes is the same before and after, and no merge.* keys exist.
  • tri census pin --gate: PASS. test_required_gates_name_their_subject.py: ok. check_now_entry_shape.py: ok.

🤖 Generated with Claude Code

#5482)

scripts/ci/now-sync-gate-diff.sh ended every pass with
setup_skill_merge_driver (#4728). It appended
`.claude/skills/ci-gates/SKILL.md merge=union` to .gitattributes and
set merge.union.name/driver in the shared .git/config. `tri gates
preview` and `tri hooks pre-push` run the script in contributors'
clones. There the configured driver replaced git's built-in `union`,
which .trinity/experience/*.jsonl relies on. Its command used unset
shell variables instead of git's %O %A %B, so every union merge of
those logs failed with `bash: $REMOTE: ambiguous redirect`. The writer
is removed.

scripts/ci/test_now_gate_writes_nothing.py runs each caller in its own
scratch repository, on a range the gate passes. The callers are the
script's three arms and, with --tri, `tri gates preview` and `tri hooks
pre-push`. For each, .git/config, .gitattributes, .git/info/attributes
and git status must be byte-identical before and after, and a union
merge must still work. A planted copy of the old writer must be caught,
and must break the merge. Against the unfixed script all five callers
fail on both counts. Wired into untrusted-input-gate.yml, and into
cli-tri.yml with the binary that job builds.

.gitattributes: its header pointed to .git/config "for
implementations"; it uses built-in drivers only.

Census: `shell` moved, run: steps 280 -> 282 and "the runner does"
259 -> 261. These are the two new steps. Re-blessed with
`tri census pin --bless`.

Clean an affected clone with
  git config --unset merge.union.driver
  git config --unset merge.union.name
  git checkout -- .gitattributes   (if the appended line is still there)

Refs #5482

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

📓 NotebookLM Notebook linked to this PR

This notebook contains session context, decisions, and artifacts for this work.

@github-actions

github-actions Bot commented Oct 1, 2026

Copy link
Copy Markdown
Contributor

PR Dashboard

Generated at: 2026-10-01 23:47:28 UTC

Summary

Status Count
Total Open PRs 50
PRs with Failing Checks 47
PRs with All Checks Green 3
READY 3
FAILING 47
PENDING 0
NO CHECKS YET 0

Seal Status

  • ⚠️ STALE -- sha256(compiler.rs)=b7d5cc5c4cf1 != manifest seal=87e5cbd3ad94.
    The committed NMSE numbers were certified against an older compiler.rs.
    Run scripts/reseal-check.sh locally for the two-step reseal command (advisory; not a merge gate).

@dmitrii-f-t27
dmitrii-f-t27 merged commit d2370ce into master Oct 2, 2026
31 of 34 checks passed
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.

1 participant