fix(consistency): preserve immutable historical binding rows
Assistant: codex Assistant-Model: gpt-6.1-sol Assistant-Session: 01a1468b-63fa-7710-85dc-8f2e16b3ebfa
This commit is contained in:
parent
a25b62f75a
commit
c103cbf3c8
3 changed files with 78 additions and 2 deletions
|
|
@ -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
|
||||||
|
|
|
||||||
60
tests/test_writeback_git_isolated.py
Normal file
60
tests/test_writeback_git_isolated.py
Normal 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'
|
||||||
|
|
@ -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.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue