Skip to content

kit 6.7.0

Choose a tag to compare

@github-actions github-actions released this 20 Aug 11:34
· 42 commits to main since this release
d8ffb2d

Added

  • kit check now 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() honors core.hooksPath, which
      is correct (installing into .git/hooks while git reads elsewhere is a silent no-op) — but
      only the WRITER used it. check-hooks.ts and kit hooks add's own already-installed
      pre-check hardcoded <gitDir>/hooks, so with an external core.hooksPath kit 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 absolute core.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.hooksPath means every hook is OFF, and now fails the gate instead of
      reading like a repo that never wired hooks.

    New git hook floor row: fail for a gone directory or an unarmed gate, warn when the floor
    resolves outside the repo, pass naming the hooks actually wired, and skip when the repo has
    no kit-managed hooks — kit does not claim a floor nobody asked for. kit hooks add also prints
    the resolved directory when it is external, instead of a bare ✓ installed.

  • kit audit verify --all — the union view over every audit log this machine sealed.
    kit audit verify answers 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 outside audit-anchor.ts looked 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 any failed; a stalled
    seal warns and fails under --strict / [governance.audit].require_anchor — deliberately
    the same policy decideAnchorVerdict already applies to a single tree, since the same
    evidence must not verify green in one command and red in the other. --json for machines.

    Exit codes stay [0, 1]: public-surface.ts declares 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 security ran the FULL check for six
    majors because nothing rejected the flag, and kit upgrade --self. — one trailing period
    — fell through to the lock-file branch, rewrote every installedAt, 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.ts already 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 --flag literal in a
    command's own module, the literals its direct imports hand to an argv reader, and every
    flag docs/COMMANDS.md documents for that verb — and flag-surface.test.ts fails when it
    drifts, so a new flag cannot land unlisted. Depth-1 is deliberate: an allowlist built from
    a handler file alone rejected kit check --attest and --no-auto-install, both documented
    and both read one import away. GLOBAL_FLAGS are 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 --list lists 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
    printed Provisioning --list… and then Unknown service: --list. --list now exits 0
    after listing; bare kit add still 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-audit now 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 commands rule closes that half: every human-facing command must appear in
    at least one non-exempt doc, in invocable form. It found 13 — kit broker and its two
    subcommands, all seven kit profile subcommands, kit insight, kit memory context,
    and kit 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), while COMMAND_HELP lists subcommands but omits some top-level
    verbs (so it cannot see kit insight). Measured: contract-only reported 1 gap,
    help-only reported 9, neither a superset of the other. x-kit-audience: "harness" verbs
    — the gate-* 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, so kit 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.json carries
    x-kit-args-modeled: false on 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
    (--severity for trivy, --name-only for git). Answering "which flags are
    undocumented" needs argument modeling in the contract first.

Fixed

  • kit hooks add context-check installed 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 at kit context check (which prints a ready-to-paste block from the live CLI
    state), and takes --force for 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-audit and
    context-check both 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-server printed 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 didNotRun rule kit applies
    everywhere else: the pip path declares maintainer count as unavailable (PyPI's JSON API
    publishes no maintainer list), and the report carries Probes declared unavailable: N
    plus a Coverage: PARTIAL — … line, so a 100 with a skipped probe can no longer read as
    a 100 with everything clean. A declared gap does not withhold TRIAGE PASSED — it is an
    unknown, not a finding.

    Two probes that were absent rather than impossible now run: pip newness (PyPI has no
    created field, 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: unknown for nearly every modern Python package. info.author is null
    under PEP 621 (authors = [{name=…, email=…}]); the value lands in info.author_email.
    Attribution is what settles a look-alike-repo provenance question cheaply, so it now falls
    back — measured: author: unknownauthor: OpenSandbox Team <…@alibaba-inc.com>.

  • A comment promised a flag that never existed. mise-path.ts said kit setup --activate-mise appends 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 doctor reports 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.ts carried the same shape of false claim —
    git-hook installs "land inside the repo, which the repo-rooted [scope].fs covers" — which
    holds only while core.hooksPath is 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/*.ts for the string unknownFlags(, 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 under src/commands at all, so no grep there could
    ever see them. Coverage is now "every verb in COMMAND_REGISTRY has 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
    writeCheckDetail failed-write test forced its precondition with chmod 0o500 without
    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 real mkdir and 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) — plus kit hooks uninstall, kit memory context and kit insight in 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