Commit f95130b
fix(ci): exclude k9 contracts from the Nickel gate (#985)
Closes #982. Cures the Nickel half of #976.
## The defect
**A suffix is not a format.** `*.k9.ncl` files are **k9 contracts**, not
Nickel
source — they open with a `K9!` sentinel on line 1, so `nickel
typecheck` dies at
`1:3` on the `!`. The gate globbed `*.ncl` and swallowed all of them.
Measured in the one pilot repo that actually ran the pipeline
(`cicd-squabbler`):
| bucket | count | can it ever pass? |
|---|---:|---|
| `*.k9.ncl` — a different format | 16 | no |
| `.ncl` importing build-time JSON (correctly uncommitted) | 2 | no,
standalone |
| genuine, standalone-checkable Nickel | 9 | yes |
| **tracked `.ncl` total** | **27** | **18 could never pass** |
That repo went red **entirely on boilerplate**, never on its own source.
`detect` counted the same inflated glob at line 132, so `has_nickel`
went true on
repos holding **no Nickel at all** — installing a toolchain in order to
fail.
## The fix
- **One shared pathspec.** `detect` and the job both use
`'*.ncl' ':!*.k9.ncl'`. *A guard that counts a different set than its
consumer
checks is how this defect arose*, so the cure is that they cannot
diverge.
- **k9 contracts are disclosed, not hidden** — counted as `N_K9`,
printed in the
detection table, and explicitly **excluded from the Total** (the same
shape as
the existing "detected but unsupported" disclosure).
- **Typecheck is skipped only where an `import` target is untracked in
git**, and
**every skip is disclosed by name with its reason** via `::notice` and
in the
step summary. A skip is never a pass. Comments are stripped before
scanning —
`_base.ncl` documents its own usage in a `#` comment containing a
literal
`import "../_base.ncl"`, and matching that would have skipped a file
that
typechecks perfectly well.
- **Format is still checked over every genuine `.ncl`, with no
exemption.** This
narrows the file set; it does not weaken the check.
- **Narrowing cannot manufacture a pass.** The job prints its
denominators and
**refuses** when zero files survive, or when every file was skipped.
Still read-only: `--check` only, no `--fix`, no `nickel format` in CI.
## Verification — the mutants, not just the greens
Run with **the workflow's own pinned nickel 1.18.0** (sha256-verified),
and the
embedded step body extracted from this YAML and diffed
**byte-identical** to the
script under test, so these results are about the artefact, not a
paraphrase.
| control | expected | got |
|---|---|---|
| unmodified tree | exit 1, 11 format errors, 2 named skips | ✅ |
| formatted tree | exit 0 — 11 checked / 9 typechecked / 2 skipped / 16
k9 excluded | ✅ |
| injected type error | exit 1 | ✅ |
| one file un-formatted | exit 1 | ✅ |
| every file skipped | **exit 1, refuses** — does not report a pass | ✅
|
| k9-only tree | `has_nickel=false`; job refuses if reached | ✅ |
Format-safety control for the follow-up commits: `nickel pprint-ast` is
**byte-identical for 11/11 files** before and after `nickel format`, so
the
reformatting is provably semantics-preserving.
## Consequence, stated plainly
Repos that were **vacuously green** because their only `.ncl` were k9
contracts
will now correctly report no Nickel. That is #976's intended cure, not a
regression.
This alone does **not** turn `cicd-squabbler` green: all 11 of its
genuine `.ncl`
genuinely fail `format --check`. That is a real defect in that repo and
is fixed
in its own PR, together with the matching fix in `rsr-template-repo` so
future
seeded repos do not inherit it.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01Ji1bq3TypfycfUPAR7hSxR
Co-authored-by: Claude Opus 5 <noreply@anthropic.com>1 parent de68103 commit f95130b
1 file changed
Lines changed: 72 additions & 7 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
129 | 129 | | |
130 | 130 | | |
131 | 131 | | |
132 | | - | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
133 | 140 | | |
134 | 141 | | |
135 | 142 | | |
| |||
224 | 231 | | |
225 | 232 | | |
226 | 233 | | |
227 | | - | |
| 234 | + | |
228 | 235 | | |
229 | 236 | | |
230 | 237 | | |
231 | 238 | | |
232 | 239 | | |
233 | 240 | | |
| 241 | + | |
| 242 | + | |
| 243 | + | |
| 244 | + | |
234 | 245 | | |
235 | 246 | | |
236 | 247 | | |
| |||
437 | 448 | | |
438 | 449 | | |
439 | 450 | | |
440 | | - | |
441 | | - | |
| 451 | + | |
| 452 | + | |
| 453 | + | |
| 454 | + | |
| 455 | + | |
| 456 | + | |
| 457 | + | |
| 458 | + | |
442 | 459 | | |
443 | | - | |
| 460 | + | |
444 | 461 | | |
445 | 462 | | |
| 463 | + | |
446 | 464 | | |
| 465 | + | |
| 466 | + | |
| 467 | + | |
447 | 468 | | |
448 | 469 | | |
| 470 | + | |
449 | 471 | | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
| 482 | + | |
| 483 | + | |
| 484 | + | |
| 485 | + | |
| 486 | + | |
| 487 | + | |
| 488 | + | |
| 489 | + | |
| 490 | + | |
| 491 | + | |
| 492 | + | |
| 493 | + | |
| 494 | + | |
450 | 495 | | |
| 496 | + | |
451 | 497 | | |
| 498 | + | |
| 499 | + | |
| 500 | + | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
452 | 505 | | |
453 | 506 | | |
454 | 507 | | |
455 | | - | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
456 | 519 | | |
457 | 520 | | |
458 | 521 | | |
| 522 | + | |
| 523 | + | |
459 | 524 | | |
460 | 525 | | |
461 | 526 | | |
| |||
833 | 898 | | |
834 | 899 | | |
835 | 900 | | |
836 | | - | |
| 901 | + | |
837 | 902 | | |
838 | 903 | | |
839 | 904 | | |
| |||
0 commit comments