fix(consistency): preserve immutable historical binding rows
All checks were successful
CI Smoke / host-smoke (push) Successful in 0s
CI Smoke / container-smoke (push) Successful in 1s
Build and Publish Multi-Context Image / build-and-push (push) Successful in 28s

Assistant: codex
Assistant-Model: gpt-6.1-sol
Assistant-Session: 01a1468b-63fa-7710-85dc-8f2e16b3ebfa
This commit is contained in:
tegwick 2026-10-17 02:30:15 +02:00
parent a25b62f75a
commit c103cbf3c8
3 changed files with 78 additions and 2 deletions

View file

@ -2767,11 +2767,12 @@ def _git_commit_writeback(
sync_env = {**_os.environ, "GIT_CUSTODIAN_SYNC": "1"} sync_env = {**_os.environ, "GIT_CUSTODIAN_SYNC": "1"}
try: try:
subprocess.run( subprocess.run(
["git", "-C", repo_path, "add", str(file_path)], ["git", "-C", repo_path, "add", "--", str(file_path)],
check=True, capture_output=True, env=sync_env, check=True, capture_output=True, env=sync_env,
) )
subprocess.run( subprocess.run(
["git", "-C", repo_path, *_git_identity_config(), "commit", "-m", msg], ["git", "-C", repo_path, *_git_identity_config(),
"commit", "--only", "-m", msg, "--", str(file_path)],
check=True, capture_output=True, env=sync_env, check=True, capture_output=True, env=sync_env,
) )
return True return True

View file

@ -0,0 +1,60 @@
"""Real disposable Git proof; does not import owner app or test conftest."""
import ast
from pathlib import Path
import subprocess
import sys
import pytest
SOURCE=Path(__file__).resolve().parents[1]/'scripts/consistency_check.py'
def actual_writeback():
tree=ast.parse(SOURCE.read_text());nodes=[]
for node in tree.body:
if isinstance(node,ast.FunctionDef) and node.name in {'_git_identity_config','_git_commit_writeback'}:
node.returns=None
for arg in node.args.args+node.args.kwonlyargs:arg.annotation=None
nodes.append(node)
env=dict(subprocess=subprocess,sys=sys,Path=Path)
exec(compile(ast.Module(body=nodes,type_ignores=[]),str(SOURCE),'exec'),env)
return env['_git_commit_writeback']
def git(root,*args):return subprocess.check_output(['git','-C',str(root),*args]).decode().strip()
@pytest.fixture
def repo(tmp_path):
root=tmp_path/'owner';root.mkdir();git(root,'init','-b','main');git(root,'config','user.name','Synthetic Test');git(root,'config','user.email','synthetic@example.invalid');git(root,'config','core.hooksPath','.git/hooks')
for name in ['writeback.md','.custodian-brief.md','evidence.json']:(root/name).write_text('original\n')
git(root,'add','.');git(root,'commit','-m','baseline')
(root/'.custodian-brief.md').write_text('staged unrelated\n');git(root,'add','.custodian-brief.md');(root/'.custodian-brief.md').write_text('working unrelated\n')
(root/'evidence.json').write_text('dirty evidence\n')
return root
def test_writeback_commits_only_target_preserves_unrelated_stage_and_worktree(repo):
before_index=git(repo,'ls-files','--stage','.custodian-brief.md','evidence.json');before_head=git(repo,'rev-parse','HEAD')
target=repo/'writeback.md';target.write_text('new status\n')
assert actual_writeback()(str(repo),target,['synthetic status'])
assert git(repo,'rev-parse','HEAD')!=before_head
assert git(repo,'diff-tree','--no-commit-id','--name-only','-r','HEAD')=='writeback.md'
assert git(repo,'show','HEAD:.custodian-brief.md')=='original'
assert git(repo,'show',':.custodian-brief.md')=='staged unrelated'
assert git(repo,'ls-files','--stage','.custodian-brief.md','evidence.json')==before_index
assert (repo/'.custodian-brief.md').read_text()=='working unrelated\n'
assert (repo/'evidence.json').read_text()=='dirty evidence\n'
def test_noop_never_commits_unrelated_staged_change(repo):
head=git(repo,'rev-parse','HEAD');index=git(repo,'ls-files','--stage')
assert not actual_writeback()(str(repo),repo/'writeback.md',['no change'])
assert git(repo,'rev-parse','HEAD')==head and git(repo,'ls-files','--stage')==index
@pytest.mark.parametrize('name',['new writeback.md','-leading-dash.md'])
def test_new_target_and_argument_boundary(repo,name):
target=repo/name;target.write_text('new file\n')
assert actual_writeback()(str(repo),target,['new synthetic target'])
assert git(repo,'diff-tree','--no-commit-id','--name-only','-r','HEAD')==name
assert git(repo,'show',':.custodian-brief.md')=='staged unrelated'
def test_normal_hook_refusal_preserves_unrelated_state(repo):
hook=repo/'.git/hooks/pre-commit';hook.write_text('#!/bin/sh\nexit 1\n');hook.chmod(0o755)
head=git(repo,'rev-parse','HEAD');index=git(repo,'ls-files','--stage','.custodian-brief.md','evidence.json')
target=repo/'writeback.md';target.write_text('new status\n')
assert not actual_writeback()(str(repo),target,['refused synthetic change'])
assert git(repo,'rev-parse','HEAD')==head
assert git(repo,'ls-files','--stage','.custodian-brief.md','evidence.json')==index
assert git(repo,'show',':.custodian-brief.md')=='staged unrelated'

View file

@ -342,3 +342,18 @@ new full-row baseline preservation and actual rollback-only refusal probes,
and only then return to T07 ordinary-pipeline acceptance. This prevents future and only then return to T07 ordinary-pipeline acceptance. This prevents future
binding mutation; it does not reconstruct or restore original parent hashes. binding mutation; it does not reconstruct or restore original parent hashes.
No production changes, new autonomy or credential authority are granted here. No production changes, new autonomy or credential authority are granted here.
T09 source repair refinement, 2026-10-17: the attempted current-client regression
actually loaded an older packaged consistency checker from the owner virtualenv,
not the reviewed canonical source. That trial again failed parent-row hash
preservation; it is retained separately and does not count as R5 client proof.
Normal C-23 also legitimately normalized WP18 ready to active and created a
source commit, but the generic writeback commit included an unrelated staged
brief. A narrow successor scopes git add and git commit --only to the actual
writeback file, preserving unrelated index/worktree state. Real disposable Git
regression tests cover pre-existing staged changes, hooks, no-op and path names.
Verify the actually loaded CLI checker hash after ordinary owner package refresh
before any retry; capture legitimate source HEAD transitions rather than assume
a static revision. T07 and T10 remain wait. No original parent preimage recovery,
new runtime delivery or broader authority follows from this source repair.