@skitterbyte/skitterspec 16.5.2 → 16.7.0

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.
@@ -0,0 +1,69 @@
1
+ # Checks That Accuse
2
+
3
+ A check **accuses** when being wrong costs something: it deletes, it exits
4
+ non-zero, or it tells the user their code is broken. Those checks earn the four
5
+ rules below. An ordinary conditional does not — this is about the ones that act
6
+ on what they conclude.
7
+
8
+ Almost every accusation begins as an **absence**: a name not in a list, a
9
+ directory not on disk, a version not in a response. An absence is evidence only
10
+ once you have established that the lookup could have seen the thing. Three
11
+ times in one day, in unrelated code, we established nothing and acted anyway:
12
+
13
+ | Absence observed | Concluded | What had blinded the lookup |
14
+ |------------------|-----------|-----------------------------|
15
+ | ref not in the issue list | "it does not exist" | the query excluded archived issues, and capped at 250 |
16
+ | version not in the registry's list | "the publish failed" | the registry is eventually consistent |
17
+ | lifecycle folder not on disk | "half-installed" | git does not store an empty directory |
18
+
19
+ The bills: 146 healthy refs accused, a valid release tag deleted, a non-zero
20
+ exit on a healthy repo. Each blind spot was knowable in advance.
21
+
22
+ ## 1. Prefer a positive signal to an absence
23
+
24
+ Assert something that must be **present**, not something that must not be
25
+ missing. A positive signal fails loudly when you are wrong about it; an absence
26
+ fails silently whenever the lookup was narrower than you assumed.
27
+
28
+ The scaffold check above stopped asking "is the lifecycle folder there?" — a
29
+ folder git drops as soon as it empties — and started asking whether the config
30
+ folder the installer always writes into is there. Same intent, and the new
31
+ question has an answer.
32
+
33
+ Where no positive signal exists, widen the lookup until absence means
34
+ something (include the archived rows, ask the API for the one id rather than
35
+ scanning a page) — or do not conclude.
36
+
37
+ ## 2. Name the blind spot beside the check
38
+
39
+ A comment naming what could make this lookup lie is what makes the next reader
40
+ check it. Not what the code does — what would fool it:
41
+
42
+ ```js
43
+ // A LIFECYCLE FOLDER IS NOT CHECKED, deliberately. git does not track empty
44
+ // directories, so it disappears whenever the bucket empties and returns the
45
+ // moment something lands in it.
46
+ ```
47
+
48
+ Write it when you write the check, while you still know why it is safe.
49
+
50
+ ## 3. Pair every accusation with a stays-silent test
51
+
52
+ For each accusing check, a test that feeds it a **healthy but unusual** input
53
+ and asserts it says nothing: the empty bucket, the archived record, the
54
+ just-published version, the file the user edited on purpose. The positive test
55
+ proves the check can fire; only this one proves it does not fire at everyone
56
+ else. All three incidents would have been caught by it, and prose alone had
57
+ already failed to prevent them.
58
+
59
+ ## 4. Bias the unknown case toward inaction
60
+
61
+ Three states, not two: yes, no, and *cannot tell*. Route the third to the
62
+ harmless branch — skip, warn, keep, retry — never to the destructive one.
63
+
64
+ The install manifest classifies a file whose hash it does not recognise as
65
+ `customized` rather than stale, so a resync **keeps** it (`managedState`,
66
+ `packages/common/src/init.js`). An unrecognised hash could mean a user's edit or
67
+ a lost manifest; only one of those readings is safe to act on, so it takes that
68
+ one. Being wrong there costs a redundant file on disk. The opposite default
69
+ costs the user their work.
@@ -173,6 +173,13 @@ test, split the fix into phase files (`01-<slug>.md` …) with a phase index in
173
173
  `00-overview.md`, and leave the spec in `in-progress` for `/spec-go` to continue.
174
174
  Say so explicitly — don't fake green.
175
175
 
176
+ **Then refresh the mirror (only if a provider is installed).** The Fix tasks are
177
+ ticked, so the repo is now the truth about this fix — and this skill can take a
178
+ bug all the way to green without `/spec-go` ever running. Without a provider this
179
+ is a no-op.
180
+
181
+
182
+
176
183
  ## 6. Report
177
184
 
178
185
  Summarise: root cause, the failing→passing test, the fix, and the full test
@@ -139,20 +139,20 @@ Before writing any code for this phase, get the workspace clean:
139
139
  next phase on top of an uncommitted one. (Skip if this is the first phase —
140
140
  there's nothing prior to commit.)
141
141
 
142
- ## 3b. Sync with the tracker (only if a provider is installed)
143
-
144
- **Only when a ticketing provider is installed** and the spec is linked to the
145
- tracker. Otherwise skip this step — no provider means zero change to the flow
146
- below. Follow the provider's steps below (nothing to do here without one).
147
-
148
-
149
-
150
142
  ## 4. Implement the phase
151
143
 
152
144
  Identify the **first unfinished phase** from the `00-overview.md` phase index,
153
145
  then open its phase file (`0N-<slug>.md`) — that file holds the tasks. Mark it
154
146
  started: set the phase-file heading to `🔄` and its `> **Status:**` to
155
147
  `In progress`, and flip the matching row in the overview phase index to `🔄`.
148
+
149
+ **Then sync with the tracker (only if a provider is installed).** The phase has
150
+ just changed state, so refresh the mirror before the build starts — that is what
151
+ makes the phase show as in progress *while* it is being built rather than only
152
+ once it is over. Without a provider this is a no-op and nothing below changes.
153
+
154
+
155
+
156
156
  Then build it, following the project rules in `.claude/rules/*.md` and `CLAUDE.md`:
157
157
 
158
158
  - Work task by task through the phase file. Make focused edits that match
@@ -175,6 +175,12 @@ Then build it, following the project rules in `.claude/rules/*.md` and `CLAUDE.m
175
175
  - If new work surfaced, add it as tasks to the appropriate phase file (or add a
176
176
  new phase file + index row) rather than doing it silently.
177
177
 
178
+ **Then refresh the mirror (only if a provider is installed).** The phase is done
179
+ in the repo now; leaving the tracker to catch up at `/spec-complete` is what makes
180
+ a mirror lag a whole spec behind. Without a provider this is a no-op.
181
+
182
+
183
+
178
184
  ## 6. Report
179
185
 
180
186
  Summarise what was implemented, the test result (quote failures if any), and
@@ -180,6 +180,13 @@ narrative and decisions).
180
180
  - Commit the fix to the `hotfix/<slug>` branch (this commit is what gets tagged
181
181
  and cherry-picked). Tick the Fix tasks; add a Changelog line.
182
182
 
183
+ **Then refresh the mirror (only if a provider is installed).** The Fix tasks are
184
+ ticked, so the repo is now the truth about this fix — and this skill can take a
185
+ bug all the way to green without `/spec-go` ever running. Without a provider this
186
+ is a no-op.
187
+
188
+
189
+
183
190
  ## 7. Report
184
191
 
185
192
  Summarise: the base tag, root cause, the failing→passing test, the fix, and the
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@skitterbyte/skitterspec",
3
- "version": "16.5.2",
3
+ "version": "16.7.0",
4
4
  "description": "Spec-driven development for Claude Code — a tracker-free filesystem workflow: lifecycle skills and per-spec isolation. For Linear sync, install @skitterbyte/skitterspec-linear instead.",
5
5
  "keywords": [
6
6
  "claude",
package/src/init.js CHANGED
@@ -406,6 +406,11 @@ function installClaudeMd(dir, { mode }) {
406
406
 
407
407
  // True when the repo looks already set up: any managed file present, any spec
408
408
  // lifecycle folder, or the CLAUDE.md spec marker (Decision 1 — detect eagerly).
409
+ // Is skitterspec already installed here? Matched on what we ACTUALLY install —
410
+ // our managed files, our lifecycle folders, our CLAUDE.md marker — never on
411
+ // `.claude/` merely existing: someone else's skills are not evidence of ours,
412
+ // and reading them as ours would treat every Claude Code project as a
413
+ // half-finished install.
409
414
  function isExistingSetup(dir) {
410
415
  if (managedTargets(dir).some((t) => fs.existsSync(t.abs))) return true
411
416
  if (SPEC_FOLDERS.some((f) => fs.existsSync(path.join(dir, 'specs', f)))) return true