mirror of
https://github.com/garrytan/gstack.git
synced 2026-09-20 11:52:20 +02:00
fix(setup,relink): ownership proof has two strengths; weak proof never deletes a directory or discards a differing file
The first #2119 gate treated a byte-identical or banner-bearing real-file SKILL.md as full ownership, so a prefix flip could rm -rf a user's directory (their own qa skill started from a gstack SKILL.md, plus my-templates/) and the link pass could replace their customized file with a symlink. Two strengths now: - STRONG: the .gstack-owned marker (we created the directory), or a directory holding nothing but symlinks and the marker (deleting it loses no data). Only strong proof removes a directory whole. - WEAK: byte-identity with our source or the two-line gen-skill-docs banner on a real file. Weak proof covers that SKILL.md and our runtime-asset links only; a differing file is moved to ${GSTACK_HOME:-~/.gstack}/backups/skills/<ts>/<skill>/ before we link over it, and setup/relink print one summary line naming what moved. The marker is written on every platform now (path-independent proof for Windows copies and for checkouts whose path carries no gstack segment), but only for a directory gstack creates: a directory we merely link into (unclaimed, or a legacy install) never becomes deletable whole. A directory with no SKILL.md at all is unclaimed: the link pass may add our file, the cleanup pass has nothing to remove. Also from the review passes: the banner check reads 8192 bytes, not 40 lines (investigate, office-hours, plan-ceo-review and design-consultation carry the banner past line 40 and were left "foreign" on pre-marker Windows installs); a link into a checkout named without a gstack segment (git worktree add ../gstack-<branch>) is ours when that tree carries setup + VERSION + bin/; relink's fast path is gone so both files canonicalize before judging; relink's root alias (_gstack-command) is gated and stamped like every other entry; relink reports the bare entry name with setup's wording and setup dedupes when forwarding (_run_relink_quiet); the summary names the browser skills as examples. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Fable 5.1
parent
953ae675df
commit
73506b59c6
@@ -24,7 +24,7 @@ function extractFn(name: string): string {
|
||||
}
|
||||
|
||||
function cleanupBody(): string {
|
||||
return extractFn('_gstack_generated_header') + extractFn('cleanup_old_claude_symlinks');
|
||||
return [extractFn('_gstack_link_target_abs'), extractFn('_gstack_target_is_ours'), extractFn('_gstack_dir_only_links'), extractFn('_cleanup_linked_dir'), extractFn('_gstack_generated_header'), extractFn('_cleanup_weak_dir'), extractFn('cleanup_old_claude_symlinks')].join('\n');
|
||||
}
|
||||
|
||||
describe('setup: cleanup_old_claude_symlinks — static (#2204)', () => {
|
||||
@@ -68,7 +68,7 @@ describe.skipIf(process.platform === 'win32')('setup: cleanup_old_claude_symlink
|
||||
const script = [
|
||||
'set -e',
|
||||
`IS_WINDOWS=${opts.isWindows ?? '0'}`,
|
||||
extractFn('_gstack_generated_header'),
|
||||
extractFn('_gstack_link_target_abs'), extractFn('_gstack_target_is_ours'), extractFn('_gstack_dir_only_links'), extractFn('_cleanup_linked_dir'), extractFn('_gstack_generated_header'), extractFn('_cleanup_weak_dir'),
|
||||
extractFn('cleanup_old_claude_symlinks'),
|
||||
`cleanup_old_claude_symlinks "${gstackArg}" "${skills}"`,
|
||||
].join('\n');
|
||||
|
||||
Reference in New Issue
Block a user