workspace: the root CLAUDE.md becomes a symlink; check 5 learns two shapes (R-230(b))
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:
2026-08-06 11:41:36 +02:00
parent 15fa5273ba
commit f49b1f390b
3 changed files with 185 additions and 7 deletions
+62
View File
@@ -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(