Skip to content

feat(skill-tree): add agentkit.skill-tree/v1 digest, archive readers and identity rules - #57

Merged
mrgoonie merged 5 commits into
mainfrom
feat/skill-tree-digest
Oct 13, 2026
Merged

mrgoonie merged 5 commits into
mainfrom
feat/skill-tree-digest

Conversation

@mrgoonie

Copy link
Copy Markdown
Contributor

Summary

This is slice SX-1 of #54 (G1 foundation publisher). It adds @skillx/skill-tree, a TypeScript port of AgentKit's agentkit.skill-tree/v1 canonical skill-tree digest, the identity rules, and the tar and zip readers. The later slices need it to publish digest-pinned releases.

What it covers:

  • Conformance. It reproduces AgentKit's digests and errors for every tree vector and identity vector. Both vector files are copied byte-for-byte from AgentKit dev at 479483bf; fixtures/PROVENANCE.md records this, and a test pins their sha256.
  • Unicode tables. These are generated offline from AgentKit's committed CaseFolding 15.0.0 file and a dump of Go's Unicode 15.0.0 tables, without downloading the UCD. Both inputs are hash-pinned, and tests check the generated tables against them for every code point.
  • Negative tests. These cover links, case-fold collisions, invalid names, oversize trees, damaged archives and a gzip bomb, along with determinism, fuzz and drift tests. 503 tests pass.
  • Decompression fix. Gzip decompression now feeds data only as fast as it is consumed, so the size cap stops reading early.
  • CI. .github/workflows/skill-tree-ci.yml typechecks and tests this package on pull requests. It is named so it doesn't collide with feat(phase-1): agent-native discovery and compatibility #41.

Dependencies: yaml 2.9.x is new. @types/node and typescript (both already locked) are added as devDependencies.

This PR changes no runtime behavior: there is no API, no migration and no deploy.

Refs #54, #34, #31; AgentKit bestagentkits/agentkit#2277 and #2274.

Validation

  • The package's tsc --noEmit is clean, and the web typecheck is clean.
  • The package's vitest run passes: 503 tests.
  • The root suite reports one unrelated local failure in packages/cli/src/version.test.ts, because pnpm isn't on PATH in my shell. CI should confirm it.

…and identity rules

Add @skillx/skill-tree, a TypeScript implementation of the AgentKit
skill tree contract shared with the AgentKit CLI:

- canonical serialization and sha256 tree digest
- path, NFC and case-fold collision rules on Unicode 15.0.0 tables
  generated from pinned inputs, never runtime Intl data
- streaming tar/tar.gz and zip readers with exact entry, file and byte
  limits enforced while content streams (pull-driven inflate)
- package identity, alias and directory alias rules, provenance trust
  and frontmatter exposure

The AgentKit conformance vectors are vendored unedited with a provenance
note and sha256 drift tests; every tree and identity vector passes.
Negative tests cover links, special entries, sparse files, unsafe modes
and names, case collisions, damaged archives and decompression bombs.

Refs #54
…uests

The repository has no pull request CI yet, so run the package typecheck
and its conformance suite when the package, the lockfile or the test
config change.

Refs #54
- NFC/NFD follow x/text stream-safe processing: a run of more than 30
  non-starters gets a CGJ, so such a path is non-NFC; composition ports
  reorderBuffer.compose; counts and differential vectors come from Go
- header modes outside a safe, non-negative 7-digit octal value are
  rejected before the type bits are cleared
- zip entries with header faults or non-regular types are unsupported
  without inflating their data or counting toward file and byte limits
- tar reads stay within a total byte budget covering non-regular data
  and PAX/GNU extension chains
- GNU sparse files apply GNU.sparse.name, size and realsize and validate
  their maps as Go does; malformed maps fail with tree.read_failed
- numeric and PAX parsing match Go: NUL-cut octal, negative base-256,
  signed and zero-padded PAX lengths, PAX times, all header numerics
- an empty GNU long name clears an earlier one; PAX records live in a
  prototype-free object; non-safe sizes fail the limit step
- collision keys fold each path once; vector harness asserts reader
  coverage
… guard

Rewrite type NUL from the merged name before sparse handling, as Go
Reader.Next does, and use that type for the data length, the sparse
header-only check and the entry kind. Check the raw header size for
negativity before a PAX size overrides it.

The tar byte budget now counts only bytes that are not hashed file
content, at maxFiles x 64 KiB, and its detail names the ingest
overhead guard. Document the guard and the nodejs_compat requirement.
Pin that a type NUL entry whose trailing slash comes from a PAX path or a
GNU long name reads no data, and keep the vector helper from writing data
for a type NUL directory.
@mrgoonie

Copy link
Copy Markdown
Contributor Author

Review rounds 1–3 are done, and round 3 approved. Commits 95dfb49, dd28390 and 653b61c bring the TS readers in line with Go's archive/tar Reader.Next: the NUL type flag, sparse PAX/GNU handling, numeric parsing, the negative raw size, and NFC stream-safe handling with Go-generated vectors. Zip entries that are faulted or not regular files are now rejected without being inflated. A reject-only ingest overhead guard (maxFiles x 64 KiB) is documented in the package README as outside the Go parity claim. 578 tests pass and tsc is clean. Kept as a PR for the owner's merge decision. Follow-ups: whether AgentKit should adopt the overhead guard in its contract, and whether a zip header-mismatch hidden under a noise name should fail the archive.

@mrgoonie
mrgoonie marked this pull request as ready for review October 13, 2026 07:52
@mrgoonie
mrgoonie merged commit d8227f7 into main Oct 13, 2026
1 check passed
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.

1 participant