kit 6.7.0
Added
-
kit checknow reports the git-hook floor: where it is, and whether it can fail. README
calls git hooks the agent-agnostic enforcement floor, and nothing reported on them — there was
a row for memory hooks and one for the PreToolUse gates, none for these. Two measured states
were therefore invisible (#496, #497):- The floor can live outside the repo.
resolveHooksDir()honorscore.hooksPath, which
is correct (installing into.git/hookswhile git reads elsewhere is a silent no-op) — but
only the WRITER used it.check-hooks.tsandkit hooks add's own already-installed
pre-check hardcoded<gitDir>/hooks, so with an externalcore.hooksPathkit installed to
one directory and reported on another. Both readers now use the same resolver, so writer and
reader can no longer disagree. Measured consequence of the silence: a checkout copied from
another inherits its absolutecore.hooksPath, so its hooks live in the other clone's tree
— deleting that unrelated directory let a staged fake credential commit cleanly, exit 0. - A dangling
core.hooksPathmeans every hook is OFF, and now fails the gate instead of
reading like a repo that never wired hooks.
New
git hook floorrow:failfor a gone directory or an unarmed gate,warnwhen the floor
resolves outside the repo,passnaming the hooks actually wired, andskipwhen the repo has
no kit-managed hooks — kit does not claim a floor nobody asked for.kit hooks addalso prints
the resolved directory when it is external, instead of a bare✓ installed. - The floor can live outside the repo.
-
kit audit verify --all— the union view over every audit log this machine sealed.
kit audit verifyanswers for one working tree, correctly: a git worktree IS a distinct
working tree, so it gets its own chain. On a machine running several — the normal case
under a worktree-per-session agent harness — that produced N green verdicts and no
whole-machine answer. The data was already on disk and unread:~/.kit/audit-anchor.json
keys the HMAC tip per log path and is shared by every tree (15 paths on the machine that
filed #470); the only reader outsideaudit-anchor.tslooked up one path.Four outcomes, because collapsing them is what makes such a report unreadable:
verified;
stalled(the log is there but its seal no longer covers it — an unsealed tail, or a
rotated anchor key);missing(the log path is gone — a temp dir or deleted clone, which
is most of those 15 paths and never a finding);failed(chain break, truncation, tip
mismatch, or a seal that could not be checked at all). Exit 1 on anyfailed; astalled
seal warns and fails under--strict/[governance.audit].require_anchor— deliberately
the same policydecideAnchorVerdictalready applies to a single tree, since the same
evidence must not verify green in one command and red in the other.--jsonfor machines.Exit codes stay
[0, 1]:public-surface.tsdeclares that set for every stable command,
so the missing/stalled distinction is carried by the report, not by a third code. -
Every command now rejects flags it does not accept (43 of 45 modules did not, #488).
The class is not theoretical:kit check --category securityran the FULL check for six
majors because nothing rejected the flag, andkit upgrade --self.— one trailing period
— fell through to the lock-file branch, rewrote everyinstalledAt, installed nothing and
printed the success line.Sweeping 43 handlers would have fixed today and rotted tomorrow, so the shape is the one
read-only-surface.tsalready uses for the write surface: a declared table
(src/flag-surface.ts, 71 verbs) and one refusal at dispatch. The table is GENERATED from
the source (node scripts/derive-command-flags.mjs --emit) — every--flagliteral in a
command's own module, the literals its direct imports hand to an argv reader, and every
flagdocs/COMMANDS.mddocuments for that verb — andflag-surface.test.tsfails when it
drifts, so a new flag cannot land unlisted. Depth-1 is deliberate: an allowlist built from
a handler file alone rejectedkit check --attestand--no-auto-install, both documented
and both read one import away.GLOBAL_FLAGSare unioned in by the guard, so a global can
never be missing per verb. Everything after--is passed through untouched.The sets err toward accepting: over-accepting leaves a working invocation working, while
under-accepting breaks one. The guard catches what does NOTHING, not spelling. -
kit add --listlists the adapters. kit documented it in two places and read it
nowhere:argv[3]took the flag as the service NAME, so kit's own documented invocation
printedProvisioning --list…and thenUnknown service: --list.--listnow exits 0
after listing; barekit addstill exits 1, since a forgotten argument is not a completed
provision. A test pins that kit never prints an invocation its own flag floor would reject
— that is what found this one. -
self-auditnow checks documentation in both directions. The existing docs-claims
rules prove no doc names something kit lacks; they are structurally incapable of catching
the reverse, because an undocumented command has no doc reference to check. The new
undocumented commandsrule closes that half: every human-facing command must appear in
at least one non-exempt doc, in invocable form. It found 13 —kit brokerand its two
subcommands, all sevenkit profilesubcommands,kit insight,kit memory context,
andkit hooks uninstall, the off-switch for the git-hook enforcement floor, whose
installers were documented while it was not.The oracle is the union of two sources, because each alone hides a real gap: the
committed contract lists top-level verbs and no subcommands (so it cannot see
kit hooks uninstall), whileCOMMAND_HELPlists subcommands but omits some top-level
verbs (so it cannot seekit insight). Measured: contract-only reported 1 gap,
help-only reported 9, neither a superset of the other.x-kit-audience: "harness"verbs
— thegate-*commands invoked by hook wiring, never typed by a human — are excluded,
and that exclusion is read from the contract rather than hardcoded, so a new gate verb
inherits it. Brace form counts, sokit hooks {install,add,sync}documents each member.Known limit: the same inverse check is not possible for flags. No machine-readable
inventory of the flags kit accepts exists —contracts/kit.opencli.jsoncarries
x-kit-args-modeled: falseon all 71 commands, and the flag oracle used by the forward
rule is a scrape of source literals that also contains flags kit passes to subprocesses
(--severityfor trivy,--name-onlyfor git). Answering "which flags are
undocumented" needs argument modeling in the contract first.
Fixed
-
kit hooks add context-checkinstalled a gate that could not fail. With no[context]
block there is nothing to compare the live CLI state against, so the pre-push hook passed every
push while reporting✓ installed— the false green kit exists to refuse (#497). It now
refuses, points atkit context check(which prints a ready-to-paste block from the live CLI
state), and takes--forcefor someone about to declare the block. The installed hook runs
kit context check --require-declaration, so a[context]block that disappears later fails
the push instead of passing it; the bare command still exits 0, since a repo may legitimately
have no lock and a read-only report should not invent one. -
Every hook printed secret-scan's test recipe.
kit hooks add post-pull-auditand
context-checkboth told you to stage a fake credential and commit — advice that exercises
nothing for a post-merge or pre-push gate. Each built-in now prints how to exercise itself. -
triage's pip score is no longer higher than npm's for running fewer probes. The
score is a flat penalty count (100 - 45*critical - 12*warning), so it fell out of how
many probes an ecosystem HAS:pip opensandbox-serverprinted 100/100 next to
npm deepsec's 88/100, and that 88 existed only because the npm path looked for
something the pip path never did (maintainer count, newness). A reader comparing the two
numbers in the same CLI concluded the Python package was the safer one.Absence is now printed rather than scored, the same
didNotRunrule kit applies
everywhere else: the pip path declaresmaintainer countas unavailable (PyPI's JSON API
publishes no maintainer list), and the report carriesProbes declared unavailable: N
plus aCoverage: PARTIAL — …line, so a 100 with a skipped probe can no longer read as
a 100 with everything clean. A declared gap does not withholdTRIAGE PASSED— it is an
unknown, not a finding.Two probes that were absent rather than impossible now run: pip newness (PyPI has no
createdfield, but the oldest release file's upload time is the first publish — warns
under 30 days, as npm does) and npm license (the pip path had warned on a missing
license since it was written; the npm path never looked). After this the two paths run the
same probe set except maintainer count. -
author: unknownfor nearly every modern Python package.info.authorisnull
under PEP 621 (authors = [{name=…, email=…}]); the value lands ininfo.author_email.
Attribution is what settles a look-alike-repo provenance question cheaply, so it now falls
back — measured:author: unknown→author: OpenSandbox Team <…@alibaba-inc.com>. -
A comment promised a flag that never existed.
mise-path.tssaidkit setup --activate-miseappends the mise-shims PATH line; no code reads that flag, and
ensureMiseActivation— the appender — has no production caller at all (it sits in
self-audit's unwired-code advisory).kit doctorreports the gap and prints the line. The
claim is corrected rather than the feature quietly invented: writing to a user's shell
profile needs its own consent design.fix.tscarried the same shape of false claim —
git-hook installs "land inside the repo, which the repo-rooted[scope].fscovers" — which
holds only whilecore.hooksPathis unset or repo-relative; an absolute one puts the write
outside the repo. Both are now corrected. -
self-audit's flag-validation row now measures verbs, not file text. It grepped
src/commands/*.tsfor the stringunknownFlags(, which measured the shape of the fix:
a module with several handlers could contain one guard and leave its other verbs open, and
two verbs (fix,plugin) do not live undersrc/commandsat all, so no grep there could
ever see them. Coverage is now "every verb inCOMMAND_REGISTRYhas an entry in the
declared flag surface" — 71/71, and a verb without one is reported as unvalidated. -
A third test that reported a verdict its environment could not support. The
writeCheckDetailfailed-write test forced its precondition withchmod 0o500without
checking whether the mode denied this process — root mkdirs straight through it, so the
failure path was never exercised and the test reported a working write path as broken. It
now probes with a realmkdirand skips loudly when the mode does not deny, the same
guard applied to the WAL-sidecar test in 6.6.3. Verified pre-existing on a clean tree
before touching it.
Documentation
- The 13 commands above are now in
docs/COMMANDS.md, with two new sections —
exec-broker runtime posture (broker enforce-readiness/enforce) and the traveling
profile (profile show/freeze/check/sign/verify/export/import) — pluskit hooks uninstall,kit memory contextandkit insightin the tables they belong to. Text is
taken from each command's own help string rather than written from the outside.
Full changelog: https://github.com/sandstream/kit/blob/v6.7.0/CHANGELOG.md
Verify this release:
git tag -v v6.7.0
npm audit signatures