fix(setup): the final summary reports customized SKILL.md files moved to the backup root

The linker moved a weakly-proven, customized SKILL.md aside before linking
over it but never said so; only relink printed a "Moved N" line, and by the
time relink runs the file is already a symlink. The summary now names each
moved file and where it went, next to the foreign-entry report.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Garry Tan
2026-09-04 18:38:10 +00:00
co-authored by Claude Fable 5.1
parent 23fccb982a
commit e64bab7a00
2 changed files with 20 additions and 2 deletions
+5
View File
@@ -2982,3 +2982,8 @@ if [ ${#_FOREIGN_SKIPPED_ENTRIES[@]} -gt 0 ]; then
log "Not registered (a skill you own already uses the name; left untouched): ${_FOREIGN_SKIPPED_ENTRIES[*]}"
log " Rename or move yours, or switch modes (./setup --prefix / --no-prefix) so the names no longer collide."
fi
if [ ${#_BACKED_UP_SKILL_MDS[@]} -gt 0 ]; then
log ""
log "Moved ${#_BACKED_UP_SKILL_MDS[@]} customized SKILL.md file(s) to $_SKILL_BACKUP_ROOT before installing gstack's: ${_BACKED_UP_SKILL_MDS[*]}"
log " Those were gstack-generated files you had edited; restore anything you meant to keep under a different skill name."
fi
+15 -2
View File
@@ -429,7 +429,7 @@ describe('setup: emoji-font daemon refresh is gated on Chromium availability', (
});
/** The final summary block, executed with a recording telemetry stub. */
function runSummary(reason: string, telemetry: 'ok' | 'fail' | 'missing'): { stdout: string; stderr: string; status: number; argv: string } {
function runSummary(reason: string, telemetry: 'ok' | 'fail' | 'missing', prelude: string[] = []): { stdout: string; stderr: string; status: number; argv: string } {
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-pw-summary-'));
try {
const argvFile = path.join(tmp, 'telemetry.argv');
@@ -446,12 +446,12 @@ function runSummary(reason: string, telemetry: 'ok' | 'fail' | 'missing'): { std
'log() { [ "$QUIET" -eq 0 ] && echo "$@" || true; }',
`SOURCE_GSTACK_DIR="${tmp}"`,
`_PW_FAIL_REASON="${reason}"`,
...prelude,
tail,
'echo "REACHED_END=1"',
].join('\n');
const r = spawnSync('bash', ['-c', script], { encoding: 'utf-8', timeout: 10_000 });
if (/command not found/.test(r.stderr ?? '')) throw new Error(`harness drift (missing extracted helper):\n${r.stderr}`);
if (/command not found/.test(r.stderr ?? '')) throw new Error(`harness drift (missing extracted helper):\n${r.stderr}`);
const argv = fs.existsSync(argvFile) ? fs.readFileSync(argvFile, 'utf-8') : '';
return { stdout: r.stdout ?? '', stderr: r.stderr ?? '', status: r.status ?? -1, argv };
} finally {
@@ -501,6 +501,19 @@ describe('setup: Chromium bootstrap summary block executes', () => {
expect(failing.argv).toContain('--outcome chromium-install');
});
test('the summary names foreign entries left untouched and customized SKILL.md files moved to the backup root', () => {
const r = runSummary('', 'missing', ['_FOREIGN_SKIPPED_ENTRIES=(qa)', '_BACKED_UP_SKILL_MDS=(ship review)', '_SKILL_BACKUP_ROOT="/tmp/gstack-bk/20260904"']);
expect(r.status).toBe(0);
expect(r.stdout).toContain('Not registered (a skill you own already uses the name; left untouched): qa');
expect(r.stdout).toContain("Moved 2 customized SKILL.md file(s) to /tmp/gstack-bk/20260904 before installing gstack's: ship review");
expect(r.stdout).not.toContain('Browser unavailable');
expect(r.stdout).toContain('REACHED_END=1');
// Nothing to report → neither line.
const quiet = runSummary('', 'missing');
expect(quiet.stdout).not.toContain('Not registered');
expect(quiet.stdout).not.toContain('Moved ');
});
test('an explicit opt-out (skipped) is reported as a choice, not a failure, and sends no telemetry', () => {
const skipped = runSummary('skipped', 'ok');
expect(skipped.status).toBe(0);