Skip to content

test(client): the rest of the writeSkills suite - #34

Merged
XieX merged 2 commits into
xie/skills-05-write-fsfrom
xie/skills-06-write-fs-tests
Sep 18, 2026
Merged

XieX merged 2 commits into
xie/skills-05-write-fsfrom
xie/skills-06-write-fs-tests

Conversation

@XieX

@XieX XieX commented Aug 25, 2026 •

Copy link
Copy Markdown

Stacked PR 6 of 7 — merge bottom-up into xie/agent-skills-feature-ac9ac7.

  1. feat(client): Agent Skills value types #29 — value types
  2. feat(client): symlink-refusing filesystem primitives #30 — symlink-refusing filesystem primitives
  3. feat(client): Agent Skills store seam, verification, and accessors #31 — store seam, verification, accessors
  4. test(client): integrity verification and the accessor telemetry sweep #32 — integrity + telemetry tests
  5. feat(client): writeSkills materialization and the abuse matrix #33 — writeSkills + abuse matrix
  6. test(client): the rest of the writeSkills suite #34 — remaining writeSkills tests ← you are here
  7. docs(client): document Agent Skills #35 — docs

Each PR targets the one below it, so GitHub already shows only this PR's own diff.


Sixth of seven. Test-only — the functional half of the materialization suite, split out so it isn't read alongside the implementation and the abuse matrix in one sitting.

This closes the coverage gap the previous PR flagged: until now writeSkills had its defenses proven but not its ordinary behaviour.

What's covered

  • Basic writes and the returned report — what lands on disk, at what path, and what each ReconcileAction says about it.
  • Manifest — the serialization is asserted byte for byte, since it's a cross-language on-disk contract. Written literally rather than by importing the implementation's own constants: a test that imported them could not detect a change to them.
  • Reconcile semantics — written, updated, skipped_current, removed, and the prune and onUnavailable behaviours, including that a retrieval failure under keep leaves existing content alone.
  • Root handling, and the bare-string guard on the argument surface.
  • Atomicity and permissions — the temp file is created in the target's own directory so the rename is same-filesystem rather than cross-device, the mode is 0644, and a failure injected between write and rename leaves no partial file and produces an error action.
  • Resilience — an unreadable directory, a failing unlink, a manifest that cannot be rewritten. One skill failing never takes the run down, and every failure appears in the report.
  • Verify-then-write — content is re-verified immediately before the write, so a Skill a caller built by hand gets the same treatment as one an accessor produced. Directly constructed Skills carry Uint8Array content, and the pass hashes those bytes as-is; the content_bytes telemetry property is asserted to be the encoded byte count.
  • Orphaned temp files — the sweep added in feat(client): writeSkills materialization and the abuse matrix #33 removes an orphan a killed reconcile left behind, and does so ahead of the prune, so the rmdir that empties a skill directory is no longer blocked by one. Most of the block is about what the sweep must not touch: a customer file, a temp-shaped name that fails the anchored pattern at either end, a temp for another target, a symlink wearing a temp name, anything in the managed root itself, and everything at all when the manifest is corrupt.
  • The write half of the telemetry seam, with the same allowlist sweep the accessor half applies: three signal names, no content, no paths.

Verification

Client package 555 → 619 tests (+2 skipped). Workspace 1148 passing. typecheck, biome, and sherif clean.

The 2 skips are expected: the TOCTOU swap-race pair, gated off SUPPORTS_DIR_FD, which is false on Node because it exposes no *at() syscall family.

🤖 Generated with Claude Code


Note

Overview
Test-only expansion of skills-fs.test.ts: closes the gap where writeSkills had security/abuse coverage but not ordinary materialization behavior.

Adds large new Vitest blocks for basic writes (Skill vs store refs, "*", reconcile actions), manifest byte-level contract (sorted keys, no trailing newline, \uXXXX escaping, unknown field preservation), reconcile/prune semantics, root and argument validation, atomic rename/unlink (including interceptUnlink and assertAtomicRenameOf), resilience (onUnavailable, timeouts, no prune on store outage, no retries), verify-then-write, orphaned temp sweep (narrow pattern, corrupt manifest suppresses sweep), and the write-side telemetry allowlist (Materialized, Revoked, Integrity; no paths or bodies).

Shared harness additions: RecordingEmitter, _setStore / _setEmitterForTesting, telemetry signal constants, and a test hygiene check that fsOps mocks are restored between tests.

Reviewed by Cursor Bugbot for commit 1c2c602. Bugbot is set up for automated code reviews on this repo. Configure here.

XieX and others added 2 commits September 16, 2026 14:23
The functional half of the materialization suite, split out so it is not read
alongside the implementation and the abuse matrix in one sitting. No source
changes — everything here covers code that already landed, and the gap it closes
is real: until now `writeSkills` had its defenses proven but not its ordinary
behaviour.

- Basic writes and the returned report: what lands on disk, at what path, and
  what each `ReconcileAction` says about it.
- Manifest: the serialization is asserted byte for byte, since it is a
  cross-language on-disk contract. Written literally rather than by importing
  the implementation's own constants — a test that imported them could not
  detect a change to them.
- Reconcile semantics: `written`, `updated`, `skipped_current`, `removed`, and
  the `prune` and `onUnavailable` behaviours, including that a retrieval failure
  under `keep` leaves existing content alone.
- Root handling, and the bare-string guard on the argument surface.
- Atomicity and permissions: the temp file is created in the target's own
  directory so the rename is same-filesystem rather than cross-device, the mode
  is `0644`, and a failure injected between write and rename leaves no partial
  file and produces an `error` action.
- Resilience: an unreadable directory, a failing unlink, a manifest that cannot
  be rewritten. One skill failing never takes the run down, and every failure
  appears in the report.
- Verify-then-write: content is re-verified immediately before the write, so a
  `Skill` a caller built by hand gets the same treatment as one an accessor
  produced.
- Orphaned temp files: the sweep removes an orphan a killed reconcile left
  behind, and does so ahead of the prune, so the `rmdir` that empties a skill
  directory is no longer blocked by one. Most of the block is about what the
  sweep must *not* touch — a customer file, a temp-shaped name that fails the
  anchored pattern at either end, a temp for another target, a symlink wearing a
  temp name, anything in the managed root itself, and everything at all when the
  manifest is corrupt.
- The write half of the telemetry seam, with the same allowlist sweep the
  accessor half applies: three signal names, no content, no paths.

Client package: 555 -> 619 tests (+2 skipped).
typecheck, biome, and sherif clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Per review feedback. The two manifest serialization tests asserted the
right bytes but named the Python SDK in their titles and rationale. They
now describe the shared on-disk form directly — two-space indent, sorted
keys, no trailing newline, non-ASCII escaped as \uXXXX — which is what
the assertions actually check.

No assertions changed.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@XieX
XieX force-pushed the xie/skills-06-write-fs-tests branch from 1d0ebd2 to 1c2c602 Compare September 16, 2026 18:26
@XieX
XieX requested a review from andrewklatzke September 16, 2026 19:18
@XieX
XieX merged commit 2d5712d into xie/agent-skills-feature-ac9ac7 Sep 18, 2026
8 checks passed
@XieX
XieX deleted the xie/skills-06-write-fs-tests branch September 18, 2026 19:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants