@techninja/clearstack 0.3.10 → 0.3.12

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.
@@ -267,6 +267,25 @@ This prevents premature extraction while keeping the eventual split obvious.
267
267
  function moveObj(o, dx, dy) { ... }
268
268
  ```
269
269
 
270
+ ### When a File Exceeds 150 Lines
271
+
272
+ The **only correct response** is to split the file into two or more files.
273
+ Never do any of the following to reduce line count:
274
+
275
+ - Remove or shorten JSDoc comments
276
+ - Collapse multi-line expressions onto one line
277
+ - Remove blank lines between logical sections
278
+ - Combine unrelated functions into one
279
+ - Delete code that is still needed
280
+
281
+ These make the code harder to read, which defeats the purpose of the limit.
282
+ The limit exists to force decomposition, not compression.
283
+
284
+ The spec checker runs formatters (Prettier, ESLint `--fix`) **before**
285
+ counting lines, so the line count always reflects the formatted result.
286
+ Write code in its natural readable form, run `npm run spec all`, and if
287
+ a file is over the limit, split it.
288
+
270
289
  ---
271
290
 
272
291
  ## npm Scripts: One Entry Point Per Domain
@@ -328,6 +347,53 @@ multiple keys.
328
347
  Spec is the inner dev loop — run it constantly. Tests are the commit gate —
329
348
  run them before pushing. CI runs both, in parallel.
330
349
 
350
+ ### Spec Output Contract
351
+
352
+ The spec checker is designed for **minimal, complete output**. Every check
353
+ prints exactly one line: ✅ on pass, ❌ on fail with the violating file and
354
+ reason. The final summary is 2 lines (`N/N checks passed`).
355
+
356
+ Total output for a passing run is ~12 lines. A failing run adds one line
357
+ per violation with the exact file path and what to fix.
358
+
359
+ **Do not pipe, grep, tail, or filter spec output.** It is already the
360
+ minimal actionable result. Filtering it discards the violation details
361
+ that tell you which file to fix, forcing redundant re-runs.
362
+
363
+ To narrow scope, run a targeted check instead of filtering `all`:
364
+
365
+ ```bash
366
+ # Run everything
367
+ npm run spec all
368
+
369
+ # Run one check by key
370
+ npm run spec code # line counts (code files ≤150)
371
+ npm run spec docs # line counts (doc files ≤500)
372
+ npm run spec imports # import map aliases (no ../)
373
+ npm run spec types # JSDoc types (tsc --checkJs)
374
+ npm run spec audit # security audit
375
+
376
+ # Parent keys run all children
377
+ npm run spec lint # ESLint + Stylelint + Markdown lint
378
+ npm run spec format # Prettier (all formatters)
379
+
380
+ # Child keys run one
381
+ npm run spec lint es # ESLint only
382
+ npm run spec lint css # Stylelint only
383
+ npm run spec lint md # Markdown lint only
384
+ npm run spec format prettier
385
+ ```
386
+
387
+ ```bash
388
+ # ❌ Wrong — discards violation file paths
389
+ npm run spec all 2>&1 | tail -3
390
+ npm run spec all 2>&1 | grep -E "pass|fail"
391
+ ```
392
+
393
+ This matters especially for LLM-assisted development: the spec output is
394
+ structured so an LLM can read it in one pass, identify the failing file,
395
+ and fix it without re-running the check. Filtering breaks that loop.
396
+
331
397
  ---
332
398
 
333
399
  ## Session Retrospective
@@ -478,25 +478,11 @@ re-fetches automatically.
478
478
  ### Debouncing Batch Operations
479
479
 
480
480
  Operations like drag-to-reorder send multiple PUTs, each triggering an SSE
481
- event. Without debouncing, each event clears the store and triggers a
482
- re-render while the previous render is still pending — causing cascading
483
- errors.
481
+ event. Without debouncing, each event clears the store mid-render.
484
482
 
485
483
  The `connectRealtime()` utility debounces by entity type: multiple SSE
486
- events within 300ms trigger only one `store.clear()`. This means a reorder
487
- of 5 tasks sends 5 PUTs → 5 SSE events → 1 store clear after 300ms.
484
+ events within 300ms trigger only one `store.clear()`. A reorder of 5 tasks
485
+ sends 5 PUTs → 5 SSE events → 1 store clear after the batch settles.
488
486
 
489
- ```javascript
490
- // Inside connectRealtime debounce per entity type
491
- const timers = {};
492
- source.addEventListener('update', (event) => {
493
- const { type } = JSON.parse(event.data);
494
- clearTimeout(timers[type]);
495
- timers[type] = setTimeout(() => {
496
- store.clear([Model]); // one clear after the batch settles
497
- }, 300);
498
- });
499
- ```
500
-
501
- For the local user, the UI should not call `store.clear()` explicitly
502
- after batch operations — let the debounced SSE handler do it once.
487
+ For the local user, don't call `store.clear()` explicitly after batch
488
+ operations let the debounced SSE handler do it once.
package/lib/check.js CHANGED
@@ -69,12 +69,6 @@ export function buildChecks(dir, cfg, cmds) {
69
69
  const css = () => countFiles(dir, ['.css'], cfg.ignore);
70
70
  const md = () => countFiles(dir, ['.md'], cfg.ignore);
71
71
  return [
72
- { key: 'code', name: `Code (max ${cfg.codeMax} lines)`,
73
- run: () => checkFileLines(dir, cfg.codeExt, cfg.codeMax, cfg.ignore, `Code (max ${cfg.codeMax} lines)`) },
74
- { key: 'docs', name: `Docs (max ${cfg.docsMax} lines)`,
75
- run: () => checkFileLines(dir, cfg.docsExt, cfg.docsMax, cfg.ignore, `Docs (max ${cfg.docsMax} lines)`) },
76
- { key: 'imports', name: 'Import map aliases (no ../ imports)',
77
- run: () => checkImports(dir, cfg.ignore, 'Import map aliases (no ../ imports)') },
78
72
  { key: 'es', name: 'ESLint', parent: 'lint',
79
73
  run: () => runCmd('ESLint', cmds.lint, dir, `${js()} files`) },
80
74
  { key: 'css', name: 'Stylelint', parent: 'lint',
@@ -83,6 +77,12 @@ export function buildChecks(dir, cfg, cmds) {
83
77
  run: () => runCmd('Markdown', cmds.mdlint, dir, `${md()} files`) },
84
78
  { key: 'prettier', name: 'Prettier', parent: 'format',
85
79
  run: () => runCmd('Prettier', cmds.prettier, dir, `${js()} files`) },
80
+ { key: 'code', name: `Code (max ${cfg.codeMax} lines)`,
81
+ run: () => checkFileLines(dir, cfg.codeExt, cfg.codeMax, cfg.ignore, `Code (max ${cfg.codeMax} lines)`) },
82
+ { key: 'docs', name: `Docs (max ${cfg.docsMax} lines)`,
83
+ run: () => checkFileLines(dir, cfg.docsExt, cfg.docsMax, cfg.ignore, `Docs (max ${cfg.docsMax} lines)`) },
84
+ { key: 'imports', name: 'Import map aliases (no ../ imports)',
85
+ run: () => checkImports(dir, cfg.ignore, 'Import map aliases (no ../ imports)') },
86
86
  { key: 'types', name: 'JSDoc types (tsc --checkJs)',
87
87
  run: () => runCmd('JSDoc types', cmds.types, dir, `${js()} files`) },
88
88
  { key: 'audit', name: 'Security audit',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@techninja/clearstack",
3
- "version": "0.3.10",
3
+ "version": "0.3.12",
4
4
  "type": "module",
5
5
  "description": "A no-build web component framework specification — scaffold, validate, and evolve spec-compliant projects",
6
6
  "bin": {
@@ -267,6 +267,25 @@ This prevents premature extraction while keeping the eventual split obvious.
267
267
  function moveObj(o, dx, dy) { ... }
268
268
  ```
269
269
 
270
+ ### When a File Exceeds 150 Lines
271
+
272
+ The **only correct response** is to split the file into two or more files.
273
+ Never do any of the following to reduce line count:
274
+
275
+ - Remove or shorten JSDoc comments
276
+ - Collapse multi-line expressions onto one line
277
+ - Remove blank lines between logical sections
278
+ - Combine unrelated functions into one
279
+ - Delete code that is still needed
280
+
281
+ These make the code harder to read, which defeats the purpose of the limit.
282
+ The limit exists to force decomposition, not compression.
283
+
284
+ The spec checker runs formatters (Prettier, ESLint `--fix`) **before**
285
+ counting lines, so the line count always reflects the formatted result.
286
+ Write code in its natural readable form, run `npm run spec all`, and if
287
+ a file is over the limit, split it.
288
+
270
289
  ---
271
290
 
272
291
  ## npm Scripts: One Entry Point Per Domain
@@ -328,6 +347,53 @@ multiple keys.
328
347
  Spec is the inner dev loop — run it constantly. Tests are the commit gate —
329
348
  run them before pushing. CI runs both, in parallel.
330
349
 
350
+ ### Spec Output Contract
351
+
352
+ The spec checker is designed for **minimal, complete output**. Every check
353
+ prints exactly one line: ✅ on pass, ❌ on fail with the violating file and
354
+ reason. The final summary is 2 lines (`N/N checks passed`).
355
+
356
+ Total output for a passing run is ~12 lines. A failing run adds one line
357
+ per violation with the exact file path and what to fix.
358
+
359
+ **Do not pipe, grep, tail, or filter spec output.** It is already the
360
+ minimal actionable result. Filtering it discards the violation details
361
+ that tell you which file to fix, forcing redundant re-runs.
362
+
363
+ To narrow scope, run a targeted check instead of filtering `all`:
364
+
365
+ ```bash
366
+ # Run everything
367
+ npm run spec all
368
+
369
+ # Run one check by key
370
+ npm run spec code # line counts (code files ≤150)
371
+ npm run spec docs # line counts (doc files ≤500)
372
+ npm run spec imports # import map aliases (no ../)
373
+ npm run spec types # JSDoc types (tsc --checkJs)
374
+ npm run spec audit # security audit
375
+
376
+ # Parent keys run all children
377
+ npm run spec lint # ESLint + Stylelint + Markdown lint
378
+ npm run spec format # Prettier (all formatters)
379
+
380
+ # Child keys run one
381
+ npm run spec lint es # ESLint only
382
+ npm run spec lint css # Stylelint only
383
+ npm run spec lint md # Markdown lint only
384
+ npm run spec format prettier
385
+ ```
386
+
387
+ ```bash
388
+ # ❌ Wrong — discards violation file paths
389
+ npm run spec all 2>&1 | tail -3
390
+ npm run spec all 2>&1 | grep -E "pass|fail"
391
+ ```
392
+
393
+ This matters especially for LLM-assisted development: the spec output is
394
+ structured so an LLM can read it in one pass, identify the failing file,
395
+ and fix it without re-running the check. Filtering breaks that loop.
396
+
331
397
  ---
332
398
 
333
399
  ## Session Retrospective
@@ -478,25 +478,11 @@ re-fetches automatically.
478
478
  ### Debouncing Batch Operations
479
479
 
480
480
  Operations like drag-to-reorder send multiple PUTs, each triggering an SSE
481
- event. Without debouncing, each event clears the store and triggers a
482
- re-render while the previous render is still pending — causing cascading
483
- errors.
481
+ event. Without debouncing, each event clears the store mid-render.
484
482
 
485
483
  The `connectRealtime()` utility debounces by entity type: multiple SSE
486
- events within 300ms trigger only one `store.clear()`. This means a reorder
487
- of 5 tasks sends 5 PUTs → 5 SSE events → 1 store clear after 300ms.
484
+ events within 300ms trigger only one `store.clear()`. A reorder of 5 tasks
485
+ sends 5 PUTs → 5 SSE events → 1 store clear after the batch settles.
488
486
 
489
- ```javascript
490
- // Inside connectRealtime debounce per entity type
491
- const timers = {};
492
- source.addEventListener('update', (event) => {
493
- const { type } = JSON.parse(event.data);
494
- clearTimeout(timers[type]);
495
- timers[type] = setTimeout(() => {
496
- store.clear([Model]); // one clear after the batch settles
497
- }, 300);
498
- });
499
- ```
500
-
501
- For the local user, the UI should not call `store.clear()` explicitly
502
- after batch operations — let the debounced SSE handler do it once.
487
+ For the local user, don't call `store.clear()` explicitly after batch
488
+ operations let the debounced SSE handler do it once.