rvlib-shipkit 0.17.0 → 0.17.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.17.1
4
+
5
+ - **Fixed: `shipkit ci`'s fix pass ignored `defaults: { 'imports:check': false }`** and rewrote every relative import anyway. Reported from a repo that deliberately keeps relative imports: one plain `shipkit ci` rewrote 215 files with no tsconfig paths wired, so typecheck and the tests went red on the gate's own repair. A fix slot repairs the same concern its floor task checks, so one disable governs both directions now.
6
+ - **A disabled fixer says so.** The fix pass had no skipped-by-config vocabulary and printed `ok: fix pass clean` whether a slot was off or merely had nothing to do, so a cold agent read that as "imports are canonical here". Disabled slots print `skip: <id> (skipped-by-config …)`, the same line the task side already prints one row above.
7
+
3
8
  ## 0.17.0
4
9
 
5
10
  **Correction to 0.16.0.** Its notes and its `--frozen` flag description claimed some combination of flags made `shipkit ci` write nothing. None does: STATUS.md is refreshed on every run unless a caller passes `status: false`. If you wired `ci --no-fix --frozen` into CI expecting a read-only run, it leaves STATUS.md behind, and `--json` is the only output that writes nothing. 0.16.0 also flipped rule SK005 from warning to BLOCKING without saying so: a `link:` dependency that no longer resolves, or a declared dependency that is not installed, now fails the gate.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rvlib-shipkit",
3
- "version": "0.17.0",
3
+ "version": "0.17.1",
4
4
  "description": "All-in-one repo companion: ci gate (task DAG), auto-discovered health checks, release-contract fulfillness, asks queue, md-wiki tools, asset atelier, live tui",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -27,13 +27,21 @@ export type FixerResolution = {
27
27
  slots: FixerSlot[]
28
28
  /** slots nothing could fill, printed, never silent */
29
29
  unfilled: { id: string; reason: string }[]
30
+ /** slots the repo turned off, printed with the same vocabulary the task
31
+ * side uses, so a disable is never invisible in the output */
32
+ disabled: { id: string; reason: string }[]
30
33
  }
31
34
 
35
+ // a fix slot repairs the same concern a floor task checks, so ONE disable
36
+ // governs both. Turning a check off and having the fixer rewrite the files
37
+ // anyway is the config saying one thing and the tool doing another.
38
+ const GOVERNED_BY: Record<string, string> = { 'imports:fix': 'imports:check' }
39
+
32
40
  // one fixer per mechanical concern. `fmt` accepts the two names the house
33
41
  // vocabulary allows so a repo that calls it `format` is not silently unfixed.
34
42
  const FMT_SCRIPTS = ['fmt', 'format'] as const
35
43
 
36
- export function resolveFixers(root: string): FixerResolution {
44
+ export function resolveFixers(root: string, config?: RepoConfig): FixerResolution {
37
45
  const pkgPath = join(root, 'package.json')
38
46
  let scripts: Record<string, string> = {}
39
47
  if (existsSync(pkgPath)) {
@@ -42,19 +50,28 @@ export function resolveFixers(root: string): FixerResolution {
42
50
  }
43
51
  const slots: FixerSlot[] = []
44
52
  const unfilled: { id: string; reason: string }[] = []
53
+ const disabled: { id: string; reason: string }[] = []
54
+ const offByConfig = (id: string): boolean => {
55
+ const governing = GOVERNED_BY[id]
56
+ if (governing == null) return false
57
+ if (config?.defaults?.[governing] !== false) return false
58
+ disabled.push({ id, reason: `skipped-by-config (defaults: { '${governing}': false })` })
59
+ return true
60
+ }
45
61
 
46
62
  const fmtName = FMT_SCRIPTS.find((n) => scripts[n] != null)
47
63
  if (fmtName != null) slots.push({ id: 'fmt', cmd: ['bun', 'run', fmtName], source: 'package.json script' })
48
64
  else unfilled.push({ id: 'fmt', reason: `no 'fmt' or 'format' script — formatting stays a manual step` })
49
65
 
50
- if (scripts['imports:fix'] != null) slots.push({ id: 'imports:fix', cmd: ['bun', 'run', 'imports:fix'], source: 'package.json script' })
66
+ if (offByConfig('imports:fix')) { /* the repo said no, in the one place that governs both directions */ }
67
+ else if (scripts['imports:fix'] != null) slots.push({ id: 'imports:fix', cmd: ['bun', 'run', 'imports:fix'], source: 'package.json script' })
51
68
  else {
52
69
  let cli: string | null = null
53
70
  try { cli = fileURLToPath(import.meta.resolve('rvlib-ts-tools/src/cli.ts')) } catch { cli = null }
54
71
  if (cli == null) unfilled.push({ id: 'imports:fix', reason: `no 'imports:fix' script and rvlib-ts-tools is unresolvable` })
55
72
  else slots.push({ id: 'imports:fix', cmd: ['bun', cli, 'imports', '--fix'], source: 'built-in (rvlib-ts-tools)' })
56
73
  }
57
- return { slots, unfilled }
74
+ return { slots, unfilled, disabled }
58
75
  }
59
76
 
60
77
  /** worktree fingerprint: every dirty path mapped to `<status>:<content hash>`,
@@ -103,8 +120,9 @@ export async function runFixPass(root: string, config: RepoConfig, o: FixPassOpt
103
120
  // verify mode: same fixers, but rewriting anything means the COMMIT was
104
121
  // not clean, and the gate says so instead of quietly repairing it
105
122
  const verify = process.env.CI != null && process.env.CI !== ''
106
- const { slots, unfilled } = resolveFixers(root)
123
+ const { slots, unfilled, disabled } = resolveFixers(root, config)
107
124
  for (const u of unfilled) o.print(`skip: ${u.id} (${u.reason})`)
125
+ for (const d of disabled) o.print(`skip: ${d.id} (${d.reason})`)
108
126
  const before = treeState(root)
109
127
 
110
128
  for (const s of slots) {