This repository has no description

centralise project agent configuration, with a check that it is still read master

Project configuration moves into the ~/.claude repository, one directory per project, reached from the project through a relative symlink. Relative is what makes a single link text work on both machines: projects sit two levels below home on each, so ../../.claude/project-config/<name> resolves whether the level above is ~/misc or ~/workspace, with no rewrite step. laptop/adopt-project-config.sh performs the move on the laptop, where the ~/.claude git repository is; an adoption done on the box would be overwritten by the next delivery and could never be committed. It asserts the two-levels-below- home depth rather than trusting it, refuses to merge into an existing directory, and restores the directory in place if the link it creates does not resolve. server/lib/22-claude-config.sh delivers project-config and creates the same links on the box from the delivered payload, so a rebuild reproduces them instead of needing every project re-pushed. Its subject list is the payload itself, never a written-down list, so adopting a project needs no second edit here or in verify.sh. A real .claude holding anything the payload does not is deferred rather than replaced; a box-local settings.local.json is moved into the directory the link points at, since that is where it will be read from. settings.local.json and *.lock are excluded from the delivery. With --delete that does two things at once: it declines to send files the payload should never carry, and it protects the box's own from deletion, since rsync does not delete an excluded file on the receiving side. Two checks, because one cannot do this job. verify.sh asserts the links resolve — structural, cheap, and blind to Claude Code withdrawing support for a symlinked .claude, which would leave every link perfect and every rule unread. server/bin/check-project-config.sh runs a real session against a throwaway project in the same shape and fails loudly if a SessionStart hook does not fire. It is not called from verify.sh: a drift check costing a model call would report an API outage as drift. The behavioural check was wrong first and said so loudly, which is the argument for having built it. It started the session from the repository root rather than the throwaway project, so it reported on a different project and read as the symlink not working. Four control shapes — real directory, absolute link, relative link outside ~/.claude, and the shape actually used — all fire.