From be1e5512044729d0da9d7d95a35892ee57ddbdae Mon Sep 17 00:00:00 2001 From: Edbert Chan Date: Fri, 11 Sep 2026 19:21:14 -0700 Subject: [PATCH 1/3] =?UTF-8?q?invoker:=20wf-1788299065196-20/prune-dangli?= =?UTF-8?q?ng-links-and-dead-hook-entries=20=E2=80=94=20Review=20claim:=20?= =?UTF-8?q?Every=20install=20run=20removes=20dangling=20symlinks=20into=20?= =?UTF-8?q?this=20repo=20from=20the=20harness=20hook=20and=20skill=20direc?= =?UTF-8?q?tories=20and=20removes=20~/.claude/settings.json=20hook=20entri?= =?UTF-8?q?es=20whose=20$HOME/.claude/hooks/=20script=20is=20missing,=20le?= =?UTF-8?q?aving=20everything=20else=20untouched.=20Review=20lane:=20polic?= =?UTF-8?q?y=20Safety=20invariant:=20A=20symlink=20is=20removed=20only=20w?= =?UTF-8?q?hen=20it=20is=20dangling=20(`[=20-L=20path=20]=20&&=20[=20!=20-?= =?UTF-8?q?e=20path=20]`)=20and=20its=20raw=20`readlink`=20target=20starts?= =?UTF-8?q?=20with=20"$REPO=5FDIR/".=20A=20settings.json=20hook=20entry=20?= =?UTF-8?q?is=20removed=20only=20when=20its=20command=20names=20a=20path?= =?UTF-8?q?=20under=20$HOME/.claude/hooks/=20whose=20file=20does=20not=20e?= =?UTF-8?q?xist.=20Real=20files,=20live=20symlinks,=20symlinks=20into=20ot?= =?UTF-8?q?her=20locations,=20and=20every=20other=20settings.json=20key,?= =?UTF-8?q?=20matcher,=20or=20command=20are=20preserved.=20Confirmed=20by?= =?UTF-8?q?=20the=20user=20on=202026-09-01.=20Effectiveness=20measurement:?= =?UTF-8?q?=20~/.claude/hooks=20goes=20from=203=20dangling=20repo=20links?= =?UTF-8?q?=20to=200=20and=20settings.json=20from=201=20dead=20hook=20entr?= =?UTF-8?q?y=20to=200=20after=20one=20install=20run,=20measured=20by=20`fi?= =?UTF-8?q?nd=20~/.claude/hooks=20-maxdepth=201=20-type=20l=20!=20-exec=20?= =?UTF-8?q?test=20-e=20{}=20\;=20-print=20|=20wc=20-l`=20and=20by=20greppi?= =?UTF-8?q?ng=20settings.json=20for=20the=20dead=20command.=20Slice=20rati?= =?UTF-8?q?onale:=20Dead-state=20cleanup=20is=20one=20claim=20that=20appli?= =?UTF-8?q?es=20to=20both=20install=20modes;=20it=20is=20kept=20apart=20fr?= =?UTF-8?q?om=20the=20engine-only=20flag=20so=20the=20reviewer=20can=20jud?= =?UTF-8?q?ge=20the=20deletion=20rules=20on=20their=20own.=20Architectural?= =?UTF-8?q?=20effect:=20install.sh=20gains=20a=20post-link=20hygiene=20pas?= =?UTF-8?q?s=20and=20one=20new=20engine-owned=20script,=20scripts/prune=5F?= =?UTF-8?q?dead=5Fhook=5Fentries.py;=20hook=20registration=20itself=20is?= =?UTF-8?q?=20unchanged.=20Goal:=20Add=20the=20dangling-symlink=20prune=20?= =?UTF-8?q?to=20install.sh,=20add=20the=20settings.json=20prune=20script?= =?UTF-8?q?=20with=20unit=20tests,=20wire=20it=20into=20install.sh,=20and?= =?UTF-8?q?=20cover=20both=20in=20the=20install=20tests.=20Motivation:=20D?= =?UTF-8?q?eleted=20worktrees=20leave=20symlinks=20and=20settings=20entrie?= =?UTF-8?q?s=20behind,=20and=20the=20dead=20entry=20prints=20an=20error=20?= =?UTF-8?q?on=20every=20Bash=20command=20in=20Claude=20Code.=20Alternative?= =?UTF-8?q?=20considerations:=20Pruning=20any=20dangling=20symlink=20regar?= =?UTF-8?q?dless=20of=20target=20was=20rejected=20because=20other=20tools?= =?UTF-8?q?=20own=20links=20in=20those=20directories;=20rewriting=20settin?= =?UTF-8?q?gs.json=20wholesale=20was=20rejected=20because=20it=20carries?= =?UTF-8?q?=20unrelated=20user=20config,=20so=20the=20script=20does=20a=20?= =?UTF-8?q?targeted=20removal=20like=20the=20existing=20marker-based=20mer?= =?UTF-8?q?ges.=20Implementation=20details:=20In=20install.sh,=20after=20t?= =?UTF-8?q?he=20hook=20link=20sections=20and=20after=20the=20engine-only?= =?UTF-8?q?=20skill=20prune,=20add=20a=20loop=20over=20~/.claude/hooks,=20?= =?UTF-8?q?~/.cursor/hooks,=20~/.codex/hooks,=20~/.claude/skills,=20~/.cur?= =?UTF-8?q?sor/skills,=20~/.codex/skills:=20for=20each=20entry=20that=20is?= =?UTF-8?q?=20a=20symlink=20whose=20target=20does=20not=20exist=20and=20wh?= =?UTF-8?q?ose=20raw=20readlink=20output=20starts=20with=20"$REPO=5FDIR/",?= =?UTF-8?q?=20rm=20it=20and=20print=20"prune=20=20=20=20(dangling=20?= =?UTF-8?q?link=20into=20this=20repo)".=20Create=20scripts/prune=5Fdead=5F?= =?UTF-8?q?hook=5Fentries.py=20(stdlib=20only):=20load=20~/.claude/setting?= =?UTF-8?q?s.json=20if=20present;=20for=20each=20event=20in=20the=20"hooks?= =?UTF-8?q?"=20object=20and=20each=20matcher=20group,=20drop=20hook=20entr?= =?UTF-8?q?ies=20whose=20"command"=20contains=20a=20token=20beginning=20wi?= =?UTF-8?q?th=20"$HOME/.claude/hooks/"=20(or=20the=20expanded=20home=20pat?= =?UTF-8?q?h)=20whose=20file=20does=20not=20exist;=20drop=20matcher=20grou?= =?UTF-8?q?ps=20left=20with=20an=20empty=20"hooks"=20array;=20write=20the?= =?UTF-8?q?=20file=20back=20only=20if=20something=20changed,=20with=202-sp?= =?UTF-8?q?ace=20indentation;=20print=20one=20line=20per=20removed=20entry?= =?UTF-8?q?=20and=20"ok=20=20=20=20=20=20no=20dead=20hook=20entries"=20oth?= =?UTF-8?q?erwise;=20leave=20commands=20that=20never=20mention=20$HOME/.cl?= =?UTF-8?q?aude/hooks/=20alone.=20Expose=20a=20pure=20function=20`prune(se?= =?UTF-8?q?ttings,=20exists)`=20returning=20`(new=5Fsettings,=20removed)`?= =?UTF-8?q?=20so=20tests=20can=20pass=20a=20fake=20exists=20predicate.=20C?= =?UTF-8?q?all=20`python3=20"$REPO=5FDIR/scripts/prune=5Fdead=5Fhook=5Fent?= =?UTF-8?q?ries.py"`=20from=20install.sh=20right=20after=20the=20existing?= =?UTF-8?q?=20settings.json=20merge=20section.=20Create=20tests/test=5Fpru?= =?UTF-8?q?ne=5Fdead=5Fhook=5Fentries.py=20with=20synthetic=20settings:=20?= =?UTF-8?q?a=20dead=20entry=20is=20removed,=20a=20live=20entry=20stays,=20?= =?UTF-8?q?an=20entry=20outside=20$HOME/.claude/hooks/=20stays=20even=20if?= =?UTF-8?q?=20its=20file=20is=20missing,=20an=20emptied=20matcher=20group?= =?UTF-8?q?=20is=20dropped,=20unrelated=20top-level=20keys=20survive.=20In?= =?UTF-8?q?=20tests/test=5Finstall.py=20add=20cases:=20a=20pre-seeded=20da?= =?UTF-8?q?ngling=20symlink=20in=20the=20fake=20home's=20hooks=20dir=20poi?= =?UTF-8?q?nting=20under=20REPO=5FROOT/.worktrees/nonexistent/=20is=20remo?= =?UTF-8?q?ved;=20a=20pre-seeded=20dangling=20symlink=20pointing=20to=20/n?= =?UTF-8?q?onexistent/elsewhere=20survives;=20a=20pre-seeded=20settings.js?= =?UTF-8?q?on=20containing=20a=20dead=20$HOME/.claude/hooks/ghost/=20entry?= =?UTF-8?q?=20plus=20an=20unrelated=20key=20ends=20with=20the=20ghost=20en?= =?UTF-8?q?try=20gone=20and=20the=20unrelated=20key=20and=20diu-stop=20ent?= =?UTF-8?q?ry=20present.=20Non-goals:=20No=20change=20to=20which=20hooks?= =?UTF-8?q?=20install,=20no=20Cursor=20hooks.json=20or=20Codex=20config.to?= =?UTF-8?q?ml=20pruning,=20no=20deletion=20of=20real=20files,=20no=20chang?= =?UTF-8?q?e=20to=20the=20engine-only=20flag,=20no=20README=20or=20docs=20?= =?UTF-8?q?edits.=20Layer:=20app=5Fregression=20Feature=20state:=20active?= =?UTF-8?q?=20Files:=20-=20install.sh=20-=20scripts/prune=5Fdead=5Fhook=5F?= =?UTF-8?q?entries.py=20-=20tests/test=5Fprune=5Fdead=5Fhook=5Fentries.py?= =?UTF-8?q?=20-=20tests/test=5Finstall.py=20Change=20types:=20-=20install.?= =?UTF-8?q?sh:=20modify=20-=20scripts/prune=5Fdead=5Fhook=5Fentries.py:=20?= =?UTF-8?q?create=20-=20tests/test=5Fprune=5Fdead=5Fhook=5Fentries.py:=20c?= =?UTF-8?q?reate=20-=20tests/test=5Finstall.py:=20modify=20Acceptance=20cr?= =?UTF-8?q?iteria:=20-=20`python3=20-m=20unittest=20tests.test=5Fprune=5Fd?= =?UTF-8?q?ead=5Fhook=5Fentries=20tests.test=5Finstall=20-v`=20exits=200?= =?UTF-8?q?=20and=20includes=20the=20new=20cases.=20-=20`python3=20scripts?= =?UTF-8?q?/check=5Fecosystem=5Fboundaries.py`=20exits=200.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Exit code: 0 From ead8cbac002d5a19d41cd43936393272fe66fe79 Mon Sep 17 00:00:00 2001 From: Edbert Chan Date: Fri, 11 Sep 2026 19:42:08 -0700 Subject: [PATCH 2/3] =?UTF-8?q?invoker:=20wf-1788299065196-20/verify-dead-?= =?UTF-8?q?hook-prune=20=E2=80=94=20Review=20claim:=20The=20prune=20unit?= =?UTF-8?q?=20tests=20and=20the=20install=20end-to-end=20tests=20pass=20ag?= =?UTF-8?q?ainst=20a=20fake=20home,=20and=20the=20boundary=20check=20still?= =?UTF-8?q?=20passes.=20Review=20lane:=20proof=20Safety=20invariant:=20Ver?= =?UTF-8?q?ification=20runs=20only=20against=20a=20temporary=20fake=20$HOM?= =?UTF-8?q?E=20and=20synthetic=20settings=20dictionaries.=20Effectiveness?= =?UTF-8?q?=20measurement:=20The=20new=20test=20cases=20assert=20the=20dan?= =?UTF-8?q?gling=20repo=20link=20and=20dead=20entry=20are=20gone=20while?= =?UTF-8?q?=20the=20outside-repo=20link=20and=20unrelated=20key=20remain.?= =?UTF-8?q?=20Slice=20rationale:=20One=20focused=20proof=20for=20the=20del?= =?UTF-8?q?etion=20rules=20before=20review.=20Architectural=20effect:=20No?= =?UTF-8?q?ne;=20verification=20only.=20Goal:=20Prove=20the=20deletion=20r?= =?UTF-8?q?ules=20delete=20exactly=20the=20dead=20state=20and=20nothing=20?= =?UTF-8?q?else.=20Motivation:=20Deletion=20code=20is=20only=20trustworthy?= =?UTF-8?q?=20when=20its=20negative=20cases=20are=20exercised.=20Alternati?= =?UTF-8?q?ve=20considerations:=20Running=20against=20the=20real=20home=20?= =?UTF-8?q?was=20rejected=20as=20non-reproducible.=20Implementation=20deta?= =?UTF-8?q?ils:=20Run=20the=20two=20unittest=20modules=20and=20the=20bound?= =?UTF-8?q?ary=20check.=20Non-goals:=20No=20mutations=20outside=20the=20fa?= =?UTF-8?q?ke=20home.=20Layer:=20app=5Fregression=20Feature=20state:=20act?= =?UTF-8?q?ive=20Acceptance=20criteria:=20-=20Exits=200=20only=20when=20al?= =?UTF-8?q?l=20checks=20pass.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Exit code: 0 From 764bab15e59de746b385dc07560a2309ef5cad5b Mon Sep 17 00:00:00 2001 From: Edbert Chan Date: Fri, 11 Sep 2026 19:42:22 -0700 Subject: [PATCH 3/3] =?UTF-8?q?invoker:=20wf-1788299065196-20/scrub-handof?= =?UTF-8?q?f-artifacts=20=E2=80=94=20Review=20claim:=20No=20ephemeral=20in?= =?UTF-8?q?ter-task=20handoff=20files=20remain=20in=20the=20worktree=20bef?= =?UTF-8?q?ore=20the=20merge=20gate.=20Review=20lane:=20cleanup=20Safety?= =?UTF-8?q?=20invariant:=20The=20scrub=20script=20only=20removes=20known?= =?UTF-8?q?=20handoff=20artifact=20names=20and=20never=20touches=20source,?= =?UTF-8?q?=20tests,=20or=20other=20repository=20files.=20Effectiveness=20?= =?UTF-8?q?measurement:=20The=20script=20exits=20non-zero=20if=20any=20han?= =?UTF-8?q?doff=20artifact=20remains.=20Slice=20rationale:=20Required=20te?= =?UTF-8?q?rminal=20scrub=20for=20every=20implementation=20workflow.=20Arc?= =?UTF-8?q?hitectural=20effect:=20None;=20hygiene=20only.=20Goal:=20Leave?= =?UTF-8?q?=20the=20branch=20free=20of=20handoff=20artifacts.=20Motivation?= =?UTF-8?q?:=20Handoff=20files=20must=20not=20reach=20the=20PR.=20Alternat?= =?UTF-8?q?ive=20considerations:=20Manual=20cleanup=20was=20rejected=20as?= =?UTF-8?q?=20non-deterministic.=20Implementation=20details:=20Run=20scrip?= =?UTF-8?q?ts/scrub-handoff-artifacts.sh.=20Non-goals:=20No=20product=20ed?= =?UTF-8?q?its.=20Layer:=20app=5Fregression=20Feature=20state:=20active=20?= =?UTF-8?q?Acceptance=20criteria:=20-=20`bash=20scripts/scrub-handoff-arti?= =?UTF-8?q?facts.sh`=20exits=200.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Exit code: 0