chore(release): v8.1.3 — install locations, both config dirs, case, watch cwd

Version 8.1.3 in package.json, .claude-plugin/plugin.json, README badge
and changelog list, CLAUDE.md header and highlights, CHANGELOG. No tag:
release-plugin.mjs is the operator's push round.

Also in this commit: file content no longer carries order IDs or
pointers into the local-only plan (comments and test names in the
own-working-tree, av-surface and watch-cron tests; `punkt N` -> `(N)`).
README's antivirus section now says v8.1.3 touched it.

Suite after `git add`: 2344 tests, 2338 pass, 0 fail, 6 skipped.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Kjell Tore Guttormsen 2026-09-23 11:55:08 +02:00
commit 34617305c8
Signed by: ktg
SSH key fingerprint: SHA256:JakMjO6FTBBzN0Bhfj9saOoEjaFxlSdYuZQQpM/lF9Q
9 changed files with 100 additions and 25 deletions

View file

@ -40,8 +40,8 @@
// blob of ANY length (8+ characters) decoding to a command shape or a
// recursive `rm`: the whole runnable line, comments included. (b) alone
// misses it below 24 characters: a home-directory deletion encodes to 16.
// Added in v8.1.3 (2026-09-23, order 20260923T092223Z, tillegg a) after
// the README fact-check found one in a comment.
// Added in v8.1.3 (2026-09-23) after the README fact-check found one
// in a comment.
// (c) a Unicode Tag (U+E0000-U+E007F), zero-width (U+200B-U+200D, U+2060,
// U+FEFF) or bidi-control (U+202A-U+202E, U+2066-U+2069) codepoint in a
// TEXT file outside scanners/commons/conformance/**. Binary files (a NUL

View file

@ -195,17 +195,17 @@ describe('isOwnWorkingTree(): node_modules and the plugin dir are foreign (v8.1.
});
});
// v8.1.3 (order 20260923T092223Z, PM decisions):
// punkt 1 — other install locations are foreign like `node_modules`: a
// v8.1.3 (PM decisions):
// (1) other install locations are foreign like `node_modules`: a
// `site-packages` segment (Python venv) or a `vendor` segment
// (composer/bundler/Go) on the path from cwd to the target, and anything
// under a Claude Code config dir's `skills/` (third-party skills copied
// into a git-tracked ~/.claude share its git root).
// punkt 2 — BOTH `~/.claude` and `$CLAUDE_CONFIG_DIR` count as config dirs;
// (2) BOTH `~/.claude` and `$CLAUDE_CONFIG_DIR` count as config dirs;
// a leading `~` in the variable is expanded to the home dir.
// punkt 3 — case is canonicalized (realpathSync.native), so `NODE_MODULES/x`
// (3) case is canonicalized (realpathSync.native), so `NODE_MODULES/x`
// on a case-insensitive volume is the `node_modules` it names, and a
// case-mismatched path to an own subdir is own (§ v8.1.2 punkt 4).
// case-mismatched path to an own subdir is own (v8.1.2 known limit).
describe('isOwnWorkingTree(): v8.1.3 install locations, config dirs, case', () => {
let root;
let repo;
@ -241,21 +241,21 @@ describe('isOwnWorkingTree(): v8.1.3 install locations, config dirs, case', () =
rmSync(root, { recursive: true, force: true });
});
it('punkt 1: a package in a Python venv site-packages is foreign', () => {
it('(1) a package in a Python venv site-packages is foreign', () => {
assert.equal(ownFrom(repo, join(repo, '.venv', 'lib', 'python3.12', 'site-packages', 'evilpkg')), false);
});
it('punkt 1: a composer/bundler vendor/ package is foreign', () => {
it('(1) a composer/bundler vendor/ package is foreign', () => {
assert.equal(ownFrom(repo, join(repo, 'vendor', 'acme')), false);
assert.equal(ownFrom(repo, join(repo, 'vendor', 'acme', 'lib')), false);
});
it('punkt 1: the venv root and a look-alike name stay own (known-positive)', () => {
it('(1) the venv root and a look-alike name stay own (known-positive)', () => {
assert.equal(ownFrom(repo, join(repo, '.venv')), true);
assert.equal(ownFrom(repo, join(repo, 'my-vendor-notes')), true);
});
it('punkt 1: skills in a git-tracked config dir are foreign, the rest stays own', () => {
it('(1) skills in a git-tracked config dir are foreign, the rest stays own', () => {
process.env.CLAUDE_CONFIG_DIR = join(root, 'cfg');
try {
assert.equal(ownFrom(join(root, 'cfg'), join(root, 'cfg', 'skills', 'third-party')), false);
@ -266,7 +266,7 @@ describe('isOwnWorkingTree(): v8.1.3 install locations, config dirs, case', () =
}
});
it('punkt 1: skills under the default ~/.claude are foreign', () => {
it('(1) skills under the default ~/.claude are foreign', () => {
delete process.env.CLAUDE_CONFIG_DIR;
process.env.HOME = join(root, 'fakehome');
try {
@ -277,7 +277,7 @@ describe('isOwnWorkingTree(): v8.1.3 install locations, config dirs, case', () =
}
});
it('punkt 2: ~/.claude/plugins stays foreign when CLAUDE_CONFIG_DIR points at another profile', () => {
it('(2) ~/.claude/plugins stays foreign when CLAUDE_CONFIG_DIR points at another profile', () => {
process.env.HOME = join(root, 'fakehome');
process.env.CLAUDE_CONFIG_DIR = join(root, 'fakehome', 'alt');
try {
@ -292,7 +292,7 @@ describe('isOwnWorkingTree(): v8.1.3 install locations, config dirs, case', () =
}
});
it('punkt 2: a leading ~ in CLAUDE_CONFIG_DIR is expanded to the home dir', () => {
it('(2) a leading ~ in CLAUDE_CONFIG_DIR is expanded to the home dir', () => {
process.env.HOME = join(root, 'fakehome');
process.env.CLAUDE_CONFIG_DIR = '~/alt';
try {
@ -305,7 +305,7 @@ describe('isOwnWorkingTree(): v8.1.3 install locations, config dirs, case', () =
}
});
it('punkt 3: a case variant of node_modules is foreign (case-insensitive volume)', (t) => {
it('(3) a case variant of node_modules is foreign (case-insensitive volume)', (t) => {
if (!existsSync(join(repo, 'NODE_MODULES'))) {
t.skip('volume is case-sensitive: NODE_MODULES is a different directory');
return;
@ -313,7 +313,7 @@ describe('isOwnWorkingTree(): v8.1.3 install locations, config dirs, case', () =
assert.equal(ownFrom(repo, join(repo, 'NODE_MODULES', 'evil')), false);
});
it('punkt 3: a case-mismatched path to an own subdir is own (§ v8.1.2 punkt 4)', (t) => {
it('(3) a case-mismatched path to an own subdir is own (v8.1.2 known limit)', (t) => {
if (!existsSync(join(root, 'REPO'))) {
t.skip('volume is case-sensitive: REPO is not repo');
return;

View file

@ -1,5 +1,5 @@
// watch-cron-scope.test.mjs — `watch-cron.mjs` must scan each watched project
// as that project's own working tree (v8.1.3, PLAN § v8.1.3 punkt 5).
// as that project's own working tree (v8.1.3).
//
// Since v8.1.0 a project's .llm-security-ignore and .llm-security/policy.json
// are honored only when the target is the caller's own working tree (at or