release: v0.47.0 — shell noFollowSymlinks, resolveToolPath, shared primitives generator core - #54
Merged
Merged
Conversation
- createShellTools({ noFollowSymlinks: true }): shell_read/write/edit/grep
refuse a symlinked FINAL path component at open time (O_NOFOLLOW, atomic;
Windows lstat fallback, non-atomic). Throws err.code 'ELOOP'. Dangling-link
writes refused, target never created. One shared open helper; grep root
opened atomically; dev/ino recheck narrows the dir-listing race.
- resolveToolPath(p): the one canonicalizer (~ via os.homedir(), then
path.resolve) the tools open with, exported from bare-agent/tools so the
gate checks the same string the tool opens (bareguard 0.19.0 split).
- Fixed: empty HOME turned ~/x into /x; now throws. Non-string/empty paths
throw a type-only error instead of defaulting to cwd.
- Parent-chain containment stays bareguard's fs.resolveSymlinks.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RNVyxYsxAMaxUW1wZk1H4b
- shell_read/shell_edit read through the handle to EOF instead of trusting stat.size, so procfs/sysfs files (size 0, real content) read correctly with noFollowSymlinks on or off. Regression from 136a00c; /proc tests added. - Docs no longer cite unpublished bareguard option names; state the resolved-path containment check is upcoming and the gate is lexical today. - Document that shell_run/shell_exec cwd is not covered by noFollowSymlinks or resolveToolPath. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RNVyxYsxAMaxUW1wZk1H4b
- New readBounded(fh, limit): chunked read that stops at EOF or limit+1
bytes, whatever stat.size claims. shell_read on an unknown-size file
truncates honestly ("file size unknown"); shell_edit refuses a file over
its maxBytes cap before editing (no change made).
- /dev/zero no longer OOM-crashes the process (it did in 0.46.6 via
fs.readFile, and in 1d5b6f6 via fh.readFile); /proc/kallsyms no longer
loaded whole. Child-process tests, mutation-proved.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RNVyxYsxAMaxUW1wZk1H4b
…runcated) The multi-line @when/@fails tags were cut to their first line in primitives.json, and the @when wrongly said bareguard canonicalizes the path (the executor does). Rewritten as single accurate lines, unpublished bareguard version number dropped, manifest regenerated. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RNVyxYsxAMaxUW1wZk1H4b
parseBlock read only the tag's first line, so a wrapped @when/@fails was silently truncated in primitives.json while check:primitives still passed (it compares against its own truncated output). Now a continuation line is a loud problem (non-zero exit). Fixture tests, mutation-proved. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RNVyxYsxAMaxUW1wZk1H4b
…try doc - A wrapped @when/@fails line that itself starts with `@word` was parsed as an unknown tag and silently dropped, so the truncation slipped past 89b693d's continuation check. Unknown tags inside a @when block are now a loud problem, against a KNOWN_TAGS set scanned from the repo's own @when blocks. Fixture test, mutation-proved. - CHANGELOG: drop the false claim that resolveToolPath's truncation shipped in 0.46.6 (it was caught on this branch before release). - Retry @when rewritten as a complete one-line sentence; CLAUDE.md primitives count 51 -> 52. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RNVyxYsxAMaxUW1wZk1H4b
A tag body is every line up to the next tag. Rule A: multi-line @when/@fails/@category/@name/@Signature fails. Rule B: unknown @tag in a @when block fails, with alias hints. @example keeps tag-shaped code lines. primitives.json unchanged. Mirrors bareguard 73530ae. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…SHA pin One generator core (scripts/primitives-core.mjs), vendored byte-identically into bareguard and litectx, with a per-repo primitives.config.mjs and a CORE_SHA256 pin test (test/primitives-core.test.mjs) that fails on drift. Rules: JSDoc tag bodies, single-line when/fails/category/name/signature, unknown-tag rejection, and @example must be the last tag (a @param/@type line inside an example no longer truncates it). Class methods, recursive scan, Windows-safe paths, CRLF-safe hashing. primitives.json byte-identical in all three repos. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RNVyxYsxAMaxUW1wZk1H4b
…est attribution A known tag after @example is now a hard error (move @example last) instead of silently truncating the example or swallowing a real tag; any other @word line stays example content. Duplicate catalog names fail loudly (suggests @name Class#method). Docs corrected: the unknown-@word bug was litectx-only; the known-tag truncation hole was in all three. createShellTools' two @example tags merged into one (primitives.json byte-identical). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RNVyxYsxAMaxUW1wZk1H4b
…rt with @ @example is the last tag; any line inside it that starts with "@" (any spacing, case, alias, or word) is a hard error. Replaces the per-case known-tag guessing that kept leaking (indented @PARAM, @return, @PARAM slipped through silently). Tags elsewhere are matched on the trimmed line. Net -27 lines. primitives.json byte-identical in all three repos (bareguard after moving @example last in 4 gate.js blocks). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RNVyxYsxAMaxUW1wZk1H4b
A line starting with "@" is either a tag at the normal position (right after " * ") or a hard error; inside @example it is always an error. Undoes 2152464's trimmed tag match, which let an indented "@category" under @when silently overwrite the field. primitives.json byte-identical in all three repos. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RNVyxYsxAMaxUW1wZk1H4b
Decisions Log index (docs/index.md) had gone stale: two follow-on commits in this range (2152464, da2ecd8) each appended new entries to docs/wiki/decisions-log.md after the last index rebuild (fa13186), without re-running it. The line count (212) and every heading's L-range below the insertion point were off by up to 8 lines, and the new "unreleased / shared primitives.json generator core" top-level entry was missing from the index entirely. Recomputed all ranges from the file's current headings.
…zer, bounded reads, shared primitives generator core
- createShellTools({ noFollowSymlinks: true }) opt-in symlink refusal for the four
file tools; resolveToolPath(p) exported as the shared canonicalizer.
- shell_read/shell_edit read bounded (readBounded, 64KB chunks, cap+1 ceiling) —
fixes empty reads from procfs/sysfs and an OOM crash on EOF-less device files.
- Shared vendored primitives.json generator core (scripts/primitives-core.mjs)
across bare-agent/bareguard/litectx, with strict @example-last, duplicate-name,
wrapped-tag, and unknown-@when-tag hard errors.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RNVyxYsxAMaxUW1wZk1H4b
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
v0.47.0
createShellTools({ noFollowSymlinks: true })(opt-in): read/write/edit/grep refuse a symlinked final path component at open time (ELOOP). Requested by fwdloop.resolveToolPath(p)— new export frombare-agent/tools: the canonicalizer every shell file tool applies (~viaos.homedir(), thenpath.resolve), so a gate judges the same string the tool opens./dev/zeroor huge size-0 (procfs) files.scripts/primitives-core.mjs, vendored byte-identically into bareguard and litectx, per-repoprimitives.config.mjs, SHA pin test. One rule for@-lines: a tag at the normal position, or a hard error (inside@example, always an error). Duplicate names error.primitives.jsonbyte-identical in all three repos.Reviewed (
/branch-review, ready, no blockers) and/shipgreen: 1297 pass / 0 fail, typecheck, build, check:primitives.🤖 Generated with Claude Code
https://claude.ai/code/session_01RNVyxYsxAMaxUW1wZk1H4b