Fix open issue regressions - #551
Merged
Merged
Conversation
|
✅ Docker image built successfully
|
sandstream
pushed a commit
that referenced
this pull request
Aug 30, 2026
The decision belongs in the curated tier, but the provenance was the open question: an agent derived this from measurement and no human stated it, so `operator` -- the default when the field is absent -- would be exactly the mislabelling #550 was about. Written as `derived`. That has a useful second effect. The caveat left standing after #551 was that kit's own .kit/shared measured identically to before the fix: 46 entries, 41 without provenance, 0 aging, 0 stale, because operator and legacy entries never age. The corpus had nothing to classify. Measured now: entries=48 provenance={"(unset)":42, "operator":5, "derived":1} today : aging=0 stale=0 +200 days : aging=1 -> 0ed015 (derived) So the aging model has its first classifiable entry, and the 42 legacy ones stay exempt as designed. The mechanism was proven in isolation before; this is the first time the repo's own store exercises it.
15 tasks
sandstream
added a commit
that referenced
this pull request
Aug 30, 2026
…556) * feat(adr): accept ADR-0006 — src/utils is a leaf The first decision recovered by `kit adr derive` and then made binding. It replaces three of the nine proposed candidates with their general form: derive proposed `utils -> commands` (113 reverse edges), `utils -> adapters` (8) and `utils -> memory` (5) separately; measurement showed `utils` imports NOTHING from src/ at all, so one rule covers all three and any subsystem added later. Enforcing the general form matters because `utils` is the most fanned-in directory in the repo. A single upward import from it creates a cycle through most of the tree. Mutation-proved rather than eyeballed: green on the repo as it stands, and injecting `import "../adr.js"` into src/utils/colors.ts produces `✗ src/utils/colors.ts:1 ... (ADR-0006)` with exit 1, green again on revert. Two candidates deliberately NOT accepted, with the reason recorded in the ADR itself: - "No subsystem imports the command layer" is true of all 16 subsystems, but its general form fires on src/commands/adr-derive.test.ts, where a fixture STRING containing `import "../commands/x.js"` is read as a real import by the text-level extractor. `paths` has no negation, so "every subsystem except src/commands/**" cannot be expressed. Left underived rather than encoded wrong -- and worth noting that the tool's narrow per-directory candidates were right where the hand-written consolidation was not. - `profile` -> `exec-broker` (support 8) is a lone pair with no wider pattern. Insufficient evidence it is a decision rather than an ordering accident. Known papercut, recorded not fixed: `adr derive` still proposes the three candidates ADR-0006 now enforces. Suppressing them needs regex subsumption against accepted rules, which is not trivially decidable and not worth guessing at. * chore(memory): record the skill-gate decision as derived, not operator The decision belongs in the curated tier, but the provenance was the open question: an agent derived this from measurement and no human stated it, so `operator` -- the default when the field is absent -- would be exactly the mislabelling #550 was about. Written as `derived`. That has a useful second effect. The caveat left standing after #551 was that kit's own .kit/shared measured identically to before the fix: 46 entries, 41 without provenance, 0 aging, 0 stale, because operator and legacy entries never age. The corpus had nothing to classify. Measured now: entries=48 provenance={"(unset)":42, "operator":5, "derived":1} today : aging=0 stale=0 +200 days : aging=1 -> 0ed015 (derived) So the aging model has its first classifiable entry, and the 42 legacy ones stay exempt as designed. The mechanism was proven in isolation before; this is the first time the repo's own store exercises it. * fix(lint): clear every eslint error, and stop a test from depending on a missing tool Provisioning the standards gates turned up more than the gates themselves. **The eslint findings were not where I said they were.** I had reported 174 findings "all in examples/", based on the ten sample lines kit happened to print. Measured properly: 174 = 6 errors + 168 warnings, and by directory it is src=161, examples=10, packages=3. The six errors are now fixed: - two scanners (snyk, sentrux) rethrew a JSON parse failure without attaching `cause`, discarding the only thing that says WHERE the output went wrong. In a scanner that turns "cannot verify" into an opaque failure, so both now pass `{ cause: err }`. - a `require()` in an ESM example, now a `node:crypto` import - three unused imports/bindings Two more warnings were dead `eslint-disable` directives for `no-control-regex` that suppressed nothing -- a suppression that suppresses nothing is a lie in the code for the next reader. Removed. 174 -> 166, and 0 errors. The remaining 166 are structural: complexity=71, no-explicit-any=32, max-lines-per-function=27, max-depth=18, max-lines=11, max-params=6. That is the same debt lizard and scc report, and refactoring it is a program rather than a task. It is in the frozen baseline, which is what makes the gate usable at all. **And a test whose green depended on the machine.** `collectStandardsKeys` "returns empty slices when the general tools are absent" asserted against the real cwd with the comment "lizard/jscpd/scc are not installed in this environment". Installing lizard and jscpd turned it red with no code change. A test that passes because the box happens to be missing something proves nothing on a box that has it. It now empties PATH for the duration of the call, so the absent-tools condition is forced rather than hoped for, and it passes on both kinds of machine. Baseline: .kit-baseline.json now carries 833 frozen standards findings from the first run of complexity/duplication/size. It includes the six eslint errors fixed above -- a frozen finding that no longer occurs simply never matches -- and the 49 scc findings, so the file size gate does not report 49 "new" findings the day scc is provisioned somewhere. * feat: say what a green check covers, enforce the dependency floor, and draw the model-rig boundary Three fixes for three gaps between what kit claims and what kit enforces. **1. `kit check` now states its scope next to its verdict.** scope: 42 check(s) — 41 inspect the code, 1 executes it. A pass here does not cover runtime behaviour; `kit broker` is that tier. Counted from the checks that ran, not asserted, so it cannot go stale as categories are added. The reason it matters is measured, not stylistic: multi-tier verification research (arXiv:2607.00107, 8,918 programs across four tiers) found AI-generated code ~2x as likely as human code to trigger a confirmed runtime violation -- while under STATIC analysis the two appear equally safe, a similarity the authors call misleading. kit's surface is 41/42 static, so on a weak model its green says roughly what it would have said about human-written code. That is not a wrong verdict, it is an uninformative one wearing the same colour as an informative one. Now it says so. Sibling to the existing partial-run line, for the same reason. **2. ADR-0002's dependency floor is enforced for the first time.** Found by breaking it: `kit pkg npm:jscpd` added a fifth runtime dependency and `kit adr check` reported "4 enforced ADR(s) — no new violations". The kit-enforce block is a deny-list of twelve named packages at the import level; the ADR's title says "four runtime deps". It declared more than it enforced. It is not expressible in the grammar, for two structural reasons now recorded in the ADR: package.json is not in the file set the ADR gate walks (CODE_EXTS is source extensions only), and forbid/require_pattern match LINE BY LINE, so a `dependencies` entry cannot be told apart from a `devDependencies` one. Widening the walk and adding block-aware matching to serve one rule is a bigger change than the rule is worth, so the floor is enforced by src/dependency-floor.test.ts -- exact dependency set, exact versions, no ranges. The kit-enforce block stays as the cheaper first line. **3. ADR-0007 draws the line for the model + kit residual-risk rig.** kit owns the frozen input, the ingest schema, the deterministic adjudication and the receipts; the models run outside and the repository decides whether a finding is real. kit never produces, ranks or grades a judgement. ADR-0001 is untouched -- its forbid_import over src/** is the mechanical half, which is why 0007 carries no rule of its own rather than duplicating one that could drift. Three limits are written into the consequences so they ship with any number: lower bound only, Goodhart, and unproven transfer from C++. Everything mutation-proved rather than eyeballed: a fifth dependency and a floated version each break exactly the assertion that should notice; treating every category as executing breaks three tier-notice tests; dropping the `kit broker` pointer breaks exactly the one about naming the remedy. * feat(adr): let an ADR name where it is enforced when the grammar cannot hold it Future-proofing for a pattern that showed up three times this week rather than once. Three limits of the `kit-enforce` grammar are now measured, each found by trying to write a rule and failing: - `paths` has no negation, so "every subsystem except src/commands/**" is inexpressible - `forbid_pattern`/`require_pattern` match LINE BY LINE, so a JSON block cannot be pinned and package.json is not in the walked file set anyway - the import extractor is text-level, so a test fixture STRING that looks like an import counts as one Today, when a rule hits one of those, nothing happens. The decision quietly does not get encoded and the ADR goes on declaring more than it enforces. ADR-0002 sat in exactly that state for months: titled "four runtime deps", enforcing "not these twelve imports". The gap was invisible because a narrowly-enforced ADR and a fully-enforced one print identically. So an ADR can now name its real enforcement point: enforced_by: [src/dependency-floor.test.ts] `adr list` prints it under the ADR, so "documented, not enforced" is never confused with "not enforced anywhere". And `adr check` FAILS when a named file does not exist -- which is the half that matters. A pointer nobody verifies is worse than no pointer, because it reads as coverage. Same rule class as ci-adr-gate.test.ts (a gate must be invoked) and self-audit (a workflow's script must exist), applied to enforcement claims. Checked only for `accepted` ADRs: a proposal's pointer is an intention, not yet a claim. Mutation-proved end to end: adding a second, non-existent pointer to ADR-0002 fails `adr check` with the file cited; deleting the real dependency-floor test fails it too; turning the accepted-status guard off, or on for every status, each breaks exactly the one test that should notice. The three grammar limits are now written into CLAUDE.md and AGENTS.md alongside the mechanism, so the next person who cannot express a rule reaches for this instead of dropping it silently. * style: run prettier over the three files CI flagged `npm run format:check` failed on src/adr.ts, src/commands/usage.ts and src/commands/usage.test.ts. Formatting only — no behaviour change. Two causes, both mine: `eslint --fix` left trailing whitespace where it removed the dead `no-control-regex` disable comments, and the `list()` helper added to adr.ts had lines past the print width. The real miss is the process one. I ran build, tsc, eslint, the full suite and `review --stages adr,skill` before pushing, and never ran `format:check` — which is a step CI runs and a contributor runs locally. Reproduced the failure first, then confirmed the same command passes ("All matched files use Prettier code style!"), rather than assuming --write was sufficient. --------- Co-authored-by: Claude <noreply@anthropic.com>
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.
Fixes #529
Fixes #531
Fixes #533
Fixes #535
Fixes #536
Fixes #537
Fixes #538
Fixes #549
Fixes #550
Summary:
Verification: