Skip to content

install_skills: read back a global rollback like #429's project path; M4 host removal receipt - #467

Merged
seathatflowsinourveins merged 3 commits into
mainfrom
claude/m4-host-removal-global-rollback-20260928
Sep 28, 2026
Merged

seathatflowsinourveins merged 3 commits into
mainfrom
claude/m4-host-removal-global-rollback-20260928

Conversation

@seathatflowsinourveins

@seathatflowsinourveins seathatflowsinourveins commented Sep 28, 2026 •

Copy link
Copy Markdown
Owner

What

Follow-up to #464 (M4) on #429's convention. On this host, the M4 removal of the trial skill verification-before-completion exposed a gap. The scoped native remove, skills remove <name> -g -y -a claude-code codex, exits 0, yet it leaves the canonical ~/.agents/skills/<name> folder and its lock entry in place whenever another detected universal agent resolves to that folder. Codex keeps reading the folder, so the removal is only partial.

#429 already reads back the project rollback. The global rollback still reported rolled-back without checking.

  • tools/adoption/install_skills.py:
  • tests/test_install_skills.py: the new cases run on main's fake-CLI retain_after_remove fixtures. Failing-first against bdf25d2: 6 of 52 fail.
  • Receipt evidence/receipts/skills-m4-host-removal-20260928.json, with artifacts: the scoped-remove reproduction, the host read-back and the redacted override-drop script. It records:
    • the scoped remove kept the canonical folder and the lock entry;
    • the set was asserted as the only copy before the unscoped remove, which completed;
    • the profile install read-back;
    • skills_status over 28 skills;
    • the dropped skillOverrides key.
  • Records:

Review repair round (bb3c88f)

The cross-family review at 6744c2e (codex exec, gpt-6-astra, effort max) returned MERGE-AFTER-FIXES. The independent stack-verifier confirmed all 8 claims. The repair covers every finding:

  • Major: the global read-back looked for the Claude link only under ~/.claude. It now resolves it as skills 1.7.0 does: $CLAUDE_CONFIG_DIR/skills when that is set and not blank (dist/cli.mjs L1398, L1511).
  • Major: a malformed lock that still held the entry confirmed the rollback. lock_retains now reads the lock strictly.
  • Minor: read-back I/O errors raised. Any OSError or ValueError during the read-back is now error, "rollback could not be verified", in both modes.
  • Nit: the addendum said "settings". It now says skillOverrides.
  • Verifier gap: no test covered a missing binary. Now one does.
  • Native checks:
    • m4_claude_config_dir_probe.py: the pinned CLI links only under CLAUDE_CONFIG_DIR. With an unrelated ~/.claude copy present, the pre-repair installer reported a false error; the repaired one reports rolled-back.
    • The unchanged reproduction, re-run with the repaired installer, produced output identical to its first run.

Final round (after the re-check at bb3c88f)

The GPT-6 re-check found the lock, error and nit findings fixed and the CLAUDE_CONFIG_DIR finding partial. A second stack-verifier confirmed 9 of 10 claims and found the same gaps:

  • Python str.strip() differs from JS trim(): it keeps U+FEFF and strips U+001C-U+001F and U+0085.
  • pathlib keeps .., which Node path.join collapses.
  • One line reference was stale.

The fix:

  • JS_TRIM_CHARS holds the 25 code points node's trim() removed over every code point. A test compares it with node when node is on PATH.
  • The global path is normalized as path.join does.
  • The fake CLI mirrors both rules.
  • 59 tests pass. Against bb3c88f's installer, 3 subtests fail and 1 test errors.
  • The native probe has two new arms. A …/missing/../x value links under x and never creates missing. A U+FEFF-only value falls back to ~/.claude.

SOTA sources

  • vercel-labs/skills@7407f389 src/remove.ts: isStillUsed keeps the canonical folder and the lock entry while another detected agent's path is canonical. Installed 1.7.0 dist/cli.mjs L6853-6907; getAgentBaseDir L2214; isUniversalAgent L2180.
  • skills@1.7.0 npm dist/cli.mjs L1398 (CLAUDE_CONFIG_DIR?.trim()) and L1511 (join(claudeHome, "skills")); ECMA-262 String.prototype.trim (WhiteSpace and LineTerminator), measured with node v24.21.0.
  • openai/codex rust-v0.157.1 codex-rs/ext/skills/src/host_roots.rs L103-108: Codex reads ~/.agents/skills.
  • Runtime-worker skills program: project-scoped installs and a 137-skill manifest #429 (bdf25d2): the project read-back convention and the reason strings that are reused here.

Evidence classes

Class What
our-integration A1 tests/test_install_skills.py: 59 OK (final-round tests fail against bb3c88f's installer; 5 repair-round tests fail against 6744c2e's; 6 of the original 52 failed against bdf25d2)
our-integration A2 36 affected modules: 2,049 OK, skipped=43
our-integration A3 python3 scripts/validate.py: passed, 165 receipts
our-integration A4 registry tests: 3 OK
native-measurement the host removal and read-back on this host only; historical for any other host
native-measurement, synthetic-fixture the scratch-HOME reproduction (both runs) and the CLAUDE_CONFIG_DIR probe

The project-mode native reproduction was not run. The malformed-lock, unreadable-lock and missing-binary cases are fake-CLI fault injections only; the receipt records both limits.

🤖 Generated with Claude Code

… M4 host removal receipt

The skills 1.7.0 CLI's scoped `remove <name> -g -y -a claude-code codex` exits 0 yet keeps the
canonical ~/.agents/skills/<name> folder and its lock entry whenever another detected universal
agent resolves to that folder (vercel-labs/skills src/remove.ts at 7407f389; installed
dist/cli.mjs L6883-6907). Codex reads that folder (openai/codex codex-rs/ext/skills/src/host_roots.rs
L103-108, rust-v0.157.1). The global rollback still reported 'rolled-back' without a read-back.

- install_skills.py: the global rollback reads back canonical folder, lock entry and Claude link
  and reports #429's 'error' with its reason strings; a remove that cannot run returns 'error'
  instead of raising. Tests on main's fake-CLI fixtures; failing-first 6 of 52 against bdf25d2.
- Receipt for the M4 host removal on this host (scoped remove kept canonical + lock; set asserted;
  unscoped remove completed; profile install read-back; skills_status 28; override drop).
- Correction addendum to the M4 host step; one anti-pattern row cross-linked with #429's.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@seathatflowsinourveins seathatflowsinourveins added the lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers label Sep 28, 2026
Scout and others added 2 commits September 28, 2026 17:04
…nverifiable lock (review repair)

Repair round for the cross-family review of #467 at 6744c2e (codex exec, gpt-6-astra, effort max:
MERGE-AFTER-FIXES) and the independent verifier's coverage note.

- claude_skills_dir: the global Claude link is read back where skills 1.7.0 puts it,
  $CLAUDE_CONFIG_DIR/skills when set and not blank (npm dist/cli.mjs L1398, L1511), else ~/.claude/skills.
- lock_retains: only a missing lock or a parsed lock without the entry confirms removal; a malformed
  or unreadable lock, or any OSError/ValueError during the read-back, is 'error' with
  "rollback could not be verified" in both modes.
- Tests: fake CLI mirrors CLAUDE_CONFIG_DIR, adds lock_after_remove faults and a deleted binary; the
  module strips a host CLAUDE_CONFIG_DIR. 57 OK; 5 fail against 6744c2e's installer.
- Native: m4_claude_config_dir_probe.py (pinned CLI links only under CLAUDE_CONFIG_DIR; a blank value
  falls back; pre-repair installer false 'error' on an unrelated ~/.claude copy, repaired 'rolled-back');
  the unchanged reproduction rerun with the repaired installer matches its first output.
- Receipt data.review_repair_round, a limitation, addendum and anti-pattern row updated; addendum nit
  (skillOverrides, not settings).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ath.join rules

Final round after the re-check of bb3c88f (codex exec, gpt-6-astra, effort max: F1 partial) and a
second stack-verifier (9/10 confirmed; the same gaps):
- JS_TRIM_CHARS: the 25 code points node's String.prototype.trim removed over every code point
  (str.strip keeps U+FEFF and strips U+001C-U+001F, U+0085); test compares it with node on PATH.
- The global Claude folder is normalized as path.join does (os.path.normpath; a leading // collapses).
- Fake CLI mirrors both; 59 OK; against bb3c88f's installer 3 subtests fail and 1 errors.
- Probe arms for a '..' value and U+FEFF alone, and node's trim set and version; probe and the
  unchanged reproduction rerun with the final installer; receipt block, limitation and addendum
  (line references recomputed: L17-33, L125-136, L449-451, L452-478) updated.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@seathatflowsinourveins
seathatflowsinourveins merged commit 83229e2 into main Sep 28, 2026
26 checks passed
@seathatflowsinourveins
seathatflowsinourveins deleted the claude/m4-host-removal-global-rollback-20260928 branch September 28, 2026 21:45
seathatflowsinourveins added a commit that referenced this pull request Sep 29, 2026
…n global paths as skills 1.7.0 does (#482)

* install_skills, skills_status: join global skills paths as the CLI's path.join does

The pinned skills 1.7.0 CLI builds the global lock path and the canonical
skill folder with Node's path.join, which collapses "..": getSkillLockPath
(vercel-labs/skills@7407f389 src/skill-lock.ts:67-72, npm dist/cli.mjs
L3746-3750) and getCanonicalSkillsDir (src/installer.ts:128-131,
dist/cli.mjs L2208-2210). os.homedir() returns HOME verbatim, so only that
join removes a ".." from --home. pathlib keeps it. With a ".." through a
missing folder in --home or XDG_STATE_HOME, a canonical folder or lock
entry that the rollback's remove kept read as removed ('rolled-back'),
and a matching add read as unverified and was rolled back.

- tools/adoption/install_skills.py: one node_path_join helper, the #467
  claude_skills_dir rule (normpath of the non-empty parts joined with /,
  a leading // collapsed to /), now builds both lock_file_path branches,
  canonical_skill_dir's global branch and claude_skills_dir. Project
  paths are unchanged. Lines 1-481 keep the numbers the #467 addendum
  cites.
- scripts/skills_status.py: resolve_lock_path applies the same join. Its
  copy of the helper sits after main(), so cited lines keep their numbers,
  and stays local because tree_drift_check.py executes this file from its
  own bytes and records their sha256.
- tests: the fake CLI joins its global paths the same way. New tests cover
  ".." in --home and XDG_STATE_HOME (rollback read-back 'error', a
  'rolled-back' control, a verified install), the unchanged project
  branches, and the helper against node's own path.join. Against
  f021926's scripts they fail: 6 failures and 1 error in
  test_install_skills, 1 failure in test_skills_status; the control and
  every earlier test pass there.
- evidence/artifacts/skills-listing-restore-20260928/README.md: dated
  erratum naming resolve_lock_path and load_lock (tree_drift_check.py
  L284-285) and what a re-run now matches. The script and its outputs
  are unchanged.
- catalogs/foundation/manifest.json: the sandbox gate cites
  docs/secret-storage.md line 30 and "Follow-ups not in this change"
  lines 1221-1225 (were line 29 and 1171-1175).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Register the path-join change's files in manifests/evidence.json

Hot-file protocol (docs/lanes.md): re-registers the six files changed in
the previous commit with scripts/host_receipts.py register_file. No new
receipt. component_matrix.py --check and new_host_grand_list.py --check
exit 0, so neither output is rewritten; scripts/validate.py exits 0.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Read each global skills path as the program that writes or reads it builds it (repair round)

- install_skills.py: compare --project-dir with --home joined as the CLI joins HOME
  (path.join), then resolved; a '..' after a symlink no longer passes containment.
- skills_status.py: the canonical and claude-code link folders are joined as the
  skills 1.7.0 CLI joins them. A set CLAUDE_CONFIG_DIR moves the link folder (JS trim,
  path.join) and settings.json (Claude Code 2.1.284's own rule: untrimmed, NFC,
  path.resolve). A set CODEX_HOME moves config.toml (Codex rust-v0.157.1 canonicalizes
  it; the default ~/.codex is normalized lexically).
- skill_usage.py: any non-empty XDG_STATE_HOME, as the CLI takes it, joined with
  install_skills.py's node_path_join (one copy).
- Tests: fail-first cases for each change; the paths skills_status builds from its
  copies of the join and trim agree with install_skills.py's.
- skills-listing-restore README erratum: the lock read differs from the recorded
  code's only when a '..' follows a missing folder or a symlink.
- Nits: fake-CLI fidelity helper, test-module docstring, "final part" wording.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Register the repair round's files in manifests/evidence.json

Re-registers the eight changed files with host_receipts.register_file.
component_matrix.py --check and new_host_grand_list.py --check passed without
--write; scripts/validate.py passed.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* Erratum: 'non-empty' XDG_STATE_HOME, not 'set' (an empty value falls back to HOME; cross-family re-check F4)

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Scout <scout@local>
Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lane:foundation Foundation lane: Claude/Codex setup, hosts, memory, RAG, research, workers

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant