Releases: nmiller0113/kiss
Release list
v1.8.0
A dispatched subagent must now declare, positively, the one tree it is allowed to read.
PreToolUse on the Agent tool refuses any dispatch whose prompt does not carry an
ALLOWED-SCOPE: line naming an absolute path that exists, rejects catch-alls such as the
filesystem root or the home directory, and requires the literal words IMPLICIT DENY so the
agent is actually told the rule rather than the dispatcher merely satisfying a regex.
This exists because the obvious way to fence an agent is a denylist -- do not touch that host,
that port, that service -- and a denylist fails open. A reviewer dispatched for a one-file
change followed an entirely reasonable chain of reasoning out of the repository it was given
and into unrelated infrastructure configuration, because that tree had never been named and so
was permitted by default. The wandering was not irrational. That is the point: nothing inside
the agent was ever going to stop it, and every tree the dispatcher failed to imagine was
allowed. You cannot secure a boundary by listing what sits on the far side of it.
The honest limit, stated in the hook itself: this cannot force a running subagent to honour its
fence. It guarantees the fence is stated, specific and real. An unstated boundary constrains
nothing, while a stated one at least travels into the agent's own context, which is the only
place it can do any work.
The same hook also bounds a review dispatch. The skill already says a review "asks two
questions and no others" and that a prompt naming further places to look "commissions findings
instead of checking work". That rule was prose, applied by judgement, and judgement is what
fails: whoever built the change writes the review prompt while still holding the build in their
head and enumerates everything they were worried about. Each worry is a commission, the reviewer
answers all of them, and the size of the finding list ends up set by the prompt rather than by the
code. A dispatch that reads as a review must now declare REVIEW-APERTURE: and may enumerate at
most two check directives, which is the number of questions a review gets to ask.
That second check is a heuristic backstop and says so in the source: it counts enumerated
directives, cannot read intent, and a worry list written as flowing prose walks past it. It is a
tripwire on the shape that actually recurs, not a proof. It is deliberately not a denylist of
suspicious phrasings, because a denylist fails open on everything nobody thought of, which is the
defect the scope fence exists to close.
A dispatch counts as a review when it asks for a verdict or declares an aperture, and not when
it merely contains the word. Substring matching was tried and was wrong in the obvious way:
"REWORK" sits inside "fireworks", and "rework the parser" is a perfectly good build instruction
that was being refused with a message about reviews. A false positive here is worse than a miss,
because a miss leaves a dispatch exactly as good as it was before this hook existed while a false
positive breaks a working one.
Install note: the dispatch gate needs Python 3, where the reminder hook needs only a POSIX shell.
Its command string probes for it and exits 0 when it is absent, so on a machine without Python 3
the gate is inert and silent. That is a guardrail getting out of the way rather than blocking
every agent on a machine it cannot run on, and the README now says so plainly.
Nothing changes for an existing dispatch that was already scoped, or for an agent that is not a
review. The skill text is untouched.
1.7.2
The release validator proves its leak pattern still discriminates before it trusts that
pattern's results, and the pattern is now defined once instead of inlined at the scan site.
This exists because the rewrite of that pattern in 1.7.1 was checked against a hand-made
corpus that could not have failed: it contained no host or address sitting next to a dot or a
hyphen, which is exactly what a wrong boundary class breaks. A corpus that cannot fail is not
a test. This one can: substituting the wrong class flips ten of its lines.
The corpus covers hosts and addresses adjacent to a period, hyphen, colon, comma, quote,
paren and bracket, and a tilde dotfile path. That last one matters most here, because this
validator deliberately masks ~/.claude/ before grepping, and folding that mask into the
pattern is the plausible way to break the tilde alternative without noticing.
Nothing changes for anyone installing the plugin: the skill and the hook are untouched.
1.7.1
Portability. The skill text is unchanged; this is about the machines the plugin can install
onto.
The shipped hook required bash by name. hooks.json invoked bash on the hook script,
so a machine with a POSIX shell but no bash, such as a minimal container image, got an error
at every fresh context instead of the reminder. The script was already POSIX-clean, so the
hook now runs it with sh, and it checks the file exists first and exits 0 quietly when it
does not, which is what the script's own header always promised.
The release validator resolved its interpreter as python3 only. On a box carrying only
python the hooks.json JSON-validity check skipped in silence and the run still exited 0. The
interpreter is now resolved once across both spellings, its major version is probed rather
than inferred from the name, and its absence is announced instead of passing for a clean run.
The executable-bit assertion on the hook script is gone. hooks.json hands the path to an
interpreter, which reads the file and never consults the bit, so the check tested a property
nothing here depends on and its message said the hook would not run, which was untrue.
The publish-time leak scan no longer uses \b, a GNU extension rather than standard ERE.
Where a grep lacks it the internal-hostname and IP alternatives match nothing while the scan
still prints "ok". The replacement was checked to match the original identically under GNU
grep.
README gains a Requirements section. The skill itself needs nothing and loads anywhere; the
hook needs a POSIX shell, which on Windows means Git Bash. That was true before and stated
nowhere.
New .gitattributes pinning LF. Without it a Windows clone rewrites every tracked file to
CRLF, which kills the hook on its first line and trips the validator's own CRLF check, so the
repository would reject its own checkout.
1.7.0
- 1.7.0: ship the scope reminder as a plugin hook
1.6.0
- A review finding about your own verification is a report (1.6.0)
1.5.0
- A required review's aperture is the change (1.5.0)
1.4.0
- 1.4.0: config an assistant wrote is not the requester's config
1.3.0
- 1.3.0 — close the five MEDIUM findings in the skill's own rules
1.2.3
- 1.2.3 — close the leak-mask traversal hole in check.sh
1.2.2
- 1.2.2 — drop em dashes the newly-extended validator caught