workspace: the root CLAUDE.md becomes a symlink; check 5 learns two shapes (R-230(b))
gates / gates (push) Successful in 7s
gates / gates (push) Successful in 7s
Two files kept identical by hand and one check is a divergence class policed forever; one file reachable by two paths cannot diverge at all. install_workspace.py now links by default, MIGRATES an existing regular file (backing it up first and SAYING SO if it differed -- that difference is the last chance to notice an unsynced edit), and keeps --copy for a clone that wants the old shape. Check 5 asserts a different thing per shape: for a link, that it points at the versioned copy and resolves to a real file; for two files, byte-identity as before. A dangling link is worse than a diverged copy -- the instructions load NOTHING and there is no content left to notice is wrong -- so that case is red-proofed. NOT yet proven to LOAD: that needs a fresh session and a hook line, which is Phase 7. If it does not load, this reverts to the copy.
This commit is contained in:
@@ -454,6 +454,68 @@ def test_memory_version_literal_and_address_warn_but_do_not_fail():
|
||||
check("loopback is not reported", "127.0.0.1" not in out)
|
||||
|
||||
|
||||
# --- check 5: the workspace file has TWO legal shapes --------------------------------------------
|
||||
|
||||
|
||||
def make_workspace_pair(tmp, live_content=None, link=False, link_target=None):
|
||||
"""Build <tmp>/CLAUDE.md + the versioned copy, as either a copy or a symlink."""
|
||||
cp = os.path.join(tmp, "felhom.eu", "documentation", "runbooks")
|
||||
os.makedirs(cp, exist_ok=True)
|
||||
versioned = os.path.join(cp, "workspace-CLAUDE.md")
|
||||
with open(versioned, "w", encoding="utf-8") as fh:
|
||||
fh.write("# Workspace\n")
|
||||
live = os.path.join(tmp, "CLAUDE.md")
|
||||
if link:
|
||||
os.symlink(link_target or os.path.relpath(versioned, tmp), live)
|
||||
else:
|
||||
with open(live, "w", encoding="utf-8") as fh:
|
||||
fh.write(live_content if live_content is not None else "# Workspace\n")
|
||||
return live, versioned
|
||||
|
||||
|
||||
def test_workspace_symlink_shape_passes():
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
make_workspace_pair(tmp, link=True)
|
||||
root = make_repo(tmp, "# Repo\n")
|
||||
rc, out = run_gate(root)
|
||||
check("symlink shape exits 0", rc == 0, "rc=%d" % rc)
|
||||
check("tally reports the symlink", "SYMLINK" in out)
|
||||
|
||||
|
||||
def test_dangling_workspace_symlink_fails():
|
||||
"""A dangling link is worse than a diverged copy: the instructions load NOTHING and there is no
|
||||
content left to notice is wrong."""
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
_, versioned = make_workspace_pair(tmp, link=True)
|
||||
os.unlink(versioned)
|
||||
root = make_repo(tmp, "# Repo\n")
|
||||
rc, out = run_gate(root)
|
||||
check("dangling workspace symlink exits non-zero", rc != 0, "rc=%d" % rc)
|
||||
check("message says the instructions load nothing", "load NOTHING" in out)
|
||||
|
||||
|
||||
def test_workspace_symlink_to_the_wrong_file_fails():
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
other = os.path.join(tmp, "somewhere-else.md")
|
||||
with open(other, "w", encoding="utf-8") as fh:
|
||||
fh.write("# Not the versioned copy\n")
|
||||
make_workspace_pair(tmp, link=True, link_target="somewhere-else.md")
|
||||
root = make_repo(tmp, "# Repo\n")
|
||||
rc, out = run_gate(root)
|
||||
check("symlink to the wrong target exits non-zero", rc != 0, "rc=%d" % rc)
|
||||
check("message says it reintroduces divergence", "reintroduces the divergence" in out)
|
||||
|
||||
|
||||
def test_two_file_shape_still_checked_byte_identical():
|
||||
"""A clone elsewhere may legitimately have two files; the link shape is not forced on anyone."""
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
make_workspace_pair(tmp, live_content="# Workspace DIVERGED\n")
|
||||
root = make_repo(tmp, "# Repo\n")
|
||||
rc, out = run_gate(root)
|
||||
check("two-file shape still catches divergence", rc != 0, "rc=%d" % rc)
|
||||
check("tally names the two-file shape", "two-file shape" in out)
|
||||
|
||||
|
||||
def main():
|
||||
print("test_instructions_gate")
|
||||
for fn in sorted(
|
||||
|
||||
Reference in New Issue
Block a user