@joshski/dust 0.1.108 → 0.1.110

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/dist/audits.js CHANGED
@@ -510,6 +510,72 @@ function deadCode() {
510
510
  - No changes to files outside \`.dust/\`
511
511
  `;
512
512
  }
513
+ function directoryHierarchy() {
514
+ return dedent`
515
+ # Directory Hierarchy
516
+
517
+ Review directory structure and create improvement ideas.
518
+
519
+ ${ideasHint}
520
+
521
+ ## Scope
522
+
523
+ Analyze the project's directory organization for these issues:
524
+
525
+ 1. **Concern mixing** - Directories containing files that serve different purposes
526
+ 2. **Missing grouping** - Related files scattered across multiple locations
527
+ 3. **Depth inconsistency** - Similar directories at inconsistent depths
528
+ 4. **Naming inconsistency** - Directory names that don't follow established patterns
529
+ 5. **Singleton directories** - Directories with only a single file or subdirectory
530
+ 6. **Orphaned files** - Files at inappropriate directory levels
531
+
532
+ ## Analysis Steps
533
+
534
+ 1. **Explore the directory tree** - Walk the project's file system recursively, excluding \`node_modules\`, \`.git\`, \`dist\`, \`build\`, \`coverage\`, and other common build artifact directories
535
+ 2. **Identify issues** - For each of the issue types listed above, look for concrete examples in the directory structure
536
+ 3. **Create ideas** - For each issue found, create an idea file in \`.dust/ideas/\` with:
537
+ - Descriptive filename based on the issue type and affected paths
538
+ - The specific paths affected
539
+ - Why the current structure is problematic
540
+ - A proposed reorganization
541
+ - Migration complexity estimate (low/medium/high)
542
+
543
+ ## Output Format
544
+
545
+ Each idea file should follow this structure:
546
+
547
+ \`\`\`markdown
548
+ # [Issue Type]: [Brief Description]
549
+
550
+ ## Current Structure
551
+
552
+ [List specific paths from affectedPaths]
553
+
554
+ ## Problem
555
+
556
+ [Description of why this is problematic]
557
+
558
+ ## Proposed Solution
559
+
560
+ [Suggested reorganization]
561
+
562
+ ## Migration Complexity
563
+
564
+ [Low/Medium/High with brief rationale]
565
+ \`\`\`
566
+
567
+ ## Blocked By
568
+
569
+ (none)
570
+
571
+ ## Definition of Done
572
+
573
+ - Explored the directory tree excluding standard build/tool directories
574
+ - Created idea files for all findings in \`.dust/ideas/\`
575
+ - Each idea includes specific paths, problem description, proposed solution, and complexity
576
+ - No changes to files outside \`.dust/\`
577
+ `;
578
+ }
513
579
  function documentationDrift() {
514
580
  return dedent`
515
581
  # Documentation Drift
@@ -660,6 +726,110 @@ function factsVerification() {
660
726
  - No changes to files outside \`.dust/\`
661
727
  `;
662
728
  }
729
+ function factsExpansion() {
730
+ return dedent`
731
+ # Facts Expansion
732
+
733
+ Review the codebase for significant facts that should be documented in \`.dust/facts/\`.
734
+
735
+ ${ideasHint}
736
+
737
+ ## Context
738
+
739
+ Facts capture how things work today, providing context for agents and contributors. However, not all significant aspects of the codebase are currently documented as facts. This creates gaps where agents working in specific areas may lack important context that isn't obvious from scanning code or having prior framework knowledge.
740
+
741
+ ## Applicability
742
+
743
+ This audit applies to all codebases. If \`.dust/facts/\` does not exist, the audit will identify initial facts to document.
744
+
745
+ ## Scope
746
+
747
+ Analyze the codebase for undocumented facts across these areas:
748
+
749
+ ### Architectural Decisions
750
+ - Separation of concerns patterns not enforced by directory structure
751
+ - Dependency flow rules (e.g., what can depend on what)
752
+ - Layer boundaries and their purposes
753
+ - Module initialization order requirements
754
+ - Plugin or extension mechanisms
755
+
756
+ ### Implementation Conventions
757
+ - Naming patterns for specific types of code (factories, builders, validators)
758
+ - Error handling conventions (when to throw vs return errors)
759
+ - Async/await patterns and Promise handling
760
+ - Resource cleanup patterns
761
+ - State management approaches
762
+
763
+ ### External Integration Points
764
+ - CLI command structure and parsing approach
765
+ - Event emission patterns
766
+ - File system conventions
767
+ - Process spawning patterns
768
+ - Network communication protocols
769
+
770
+ ### Performance Characteristics
771
+ - Known performance bottlenecks
772
+ - Caching strategies
773
+ - Lazy loading patterns
774
+ - Resource pooling approaches
775
+ - Optimization trade-offs
776
+
777
+ ### Historical Context
778
+ - Migration paths from previous approaches
779
+ - Deprecated patterns still present in legacy code
780
+ - Trade-offs made in past decisions
781
+ - Features that were removed and why
782
+
783
+ ## Analysis Approach
784
+
785
+ 1. **Scan for patterns** - Look for repeated implementation patterns across multiple files
786
+ 2. **Identify conventions** - Find coding conventions that aren't enforced by linters
787
+ 3. **Review configuration** - Document configuration systems and their purposes
788
+ 4. **Trace data flows** - Identify how data moves through the system
789
+ 5. **Check existing facts** - Compare findings against what's already documented in \`.dust/facts/\`
790
+ 6. **Filter for significance** - Only suggest facts that would genuinely help future agents (facts that aren't obvious from code inspection or general framework knowledge)
791
+
792
+ ## Significance Criteria
793
+
794
+ A fact is worth documenting if:
795
+ - It's not obvious from reading the code in isolation
796
+ - It represents a project-specific decision or convention
797
+ - Future agents would benefit from knowing it before making changes
798
+ - It documents framework patterns actually used in this project
799
+
800
+ ## Output Format
801
+
802
+ For each suggested fact, create an idea file in \`.dust/ideas/\` that includes:
803
+
804
+ ### Fact Title
805
+ A clear, concise title for the proposed fact.
806
+
807
+ ### Why This Matters
808
+ Explanation of why this fact would be valuable to document (what gaps it fills, what problems it prevents).
809
+
810
+ ### What to Document
811
+ Specific aspects to cover in the fact file.
812
+
813
+ ### Where to Look
814
+ File paths or code locations that demonstrate this fact.
815
+
816
+ ### Example Content
817
+ A sketch of what the fact file might contain (2-3 sentences showing the style and key points).
818
+
819
+ ## Blocked By
820
+
821
+ (none)
822
+
823
+ ## Definition of Done
824
+
825
+ - Analyzed codebase for undocumented patterns across all specified areas
826
+ - Compared findings against existing facts in \`.dust/facts/\`
827
+ - Applied significance criteria to filter suggestions
828
+ - Created idea files for each suggested fact with complete metadata
829
+ - Each idea includes: fact title, why it matters, what to document, where to look, example content
830
+ - No changes to files outside \`.dust/\`
831
+ `;
832
+ }
663
833
  function feedbackLoopSpeed() {
664
834
  return dedent`
665
835
  # Feedback Loop Speed
@@ -670,7 +840,7 @@ function feedbackLoopSpeed() {
670
840
 
671
841
  ## Context
672
842
 
673
- The [Fast Feedback Loops](../principles/fast-feedback-loops.md) principle emphasizes that the primary feedback loop—write code, run checks, see results—should be as fast as possible. Agents especially benefit because they operate in tight loops of change-and-verify; slow feedback wastes tokens and context window space on waiting rather than working.
843
+ The primary feedback loop—write code, run checks, see results—should be as fast as possible. Agents especially benefit because they operate in tight loops of change-and-verify; slow feedback wastes tokens and context window space on waiting rather than working.
674
844
 
675
845
  This audit focuses specifically on measuring the development feedback loop speed to help identify which checks consume the most time.
676
846
 
@@ -1116,6 +1286,223 @@ function ideasFromPrinciples() {
1116
1286
  - No changes to files outside \`.dust/\`
1117
1287
  `;
1118
1288
  }
1289
+ function incidentalTestDetails() {
1290
+ return dedent`
1291
+ # Incidental Test Details
1292
+
1293
+ Identify tests with overly specific data and other incidental details that obscure test intent.
1294
+
1295
+ ${ideasHint}
1296
+
1297
+ ## Context
1298
+
1299
+ Test clarity suffers when tests include incidental complexity — details that aren't relevant to what's being tested. Overly specific data, unused properties, magic numbers, excessive mocking, and complex nested structures all make tests harder to understand and maintain. This audit identifies these patterns as candidates for simplification.
1300
+
1301
+ The audit flags patterns for review without making judgments about whether they're necessary in specific cases. Some tests legitimately need complex setup to verify specific behaviors; the goal is to surface candidates so agents can evaluate each case and simplify where appropriate.
1302
+
1303
+ ## Guidance
1304
+
1305
+ ### Readable Test Data
1306
+
1307
+ Test data setup should use natural structures that mirror what they represent.
1308
+
1309
+ When test data is easy to read, tests become self-documenting. A file system hierarchy expressed as a nested object immediately conveys structure, while a flat Map with path strings requires mental parsing to understand the relationships.
1310
+
1311
+ Prefer literal structures that visually match the domain:
1312
+
1313
+ \`\`\`javascript
1314
+ // Avoid: flat paths that obscure hierarchy
1315
+ const fs = createFileSystemEmulator({
1316
+ files: new Map([['/project/.dust/principles/my-goal.md', '# My Goal']]),
1317
+ existingPaths: new Set(['/project/.dust/ideas']),
1318
+ })
1319
+
1320
+ // Prefer: nested object that mirrors file system structure
1321
+ const fs = createFileSystemEmulator({
1322
+ project: {
1323
+ '.dust': {
1324
+ principles: {
1325
+ 'my-goal.md': '# My Goal'
1326
+ },
1327
+ ideas: {}
1328
+ }
1329
+ }
1330
+ })
1331
+ \`\`\`
1332
+
1333
+ The nested form:
1334
+ - Shows parent-child relationships through indentation
1335
+ - Makes empty directories explicit with empty objects
1336
+ - Requires no mental path concatenation to understand structure
1337
+
1338
+ ### Comprehensive Assertions
1339
+
1340
+ Assert the whole, not the parts.
1341
+
1342
+ When you break a complex object into many small assertions, a failure tells you *one thing that's wrong*. When you assert against the whole expected value, the diff tells you *what actually happened versus what you expected* — the full picture, in one glance.
1343
+
1344
+ Small assertions are like yes/no questions to a witness. A whole-object assertion is like asking "tell me what you saw."
1345
+
1346
+ Collapse multiple partial assertions into one comprehensive assertion:
1347
+
1348
+ \`\`\`javascript
1349
+ // Fragmented — each failure is a narrow keyhole
1350
+ expect(result.name).toBe("Alice");
1351
+ expect(result.age).toBe(30);
1352
+ expect(result.role).toBe("admin");
1353
+
1354
+ // Whole — a failure diff tells the full story
1355
+ expect(result).toEqual({
1356
+ name: "Alice",
1357
+ age: 30,
1358
+ role: "admin",
1359
+ });
1360
+ \`\`\`
1361
+
1362
+ If \`role\` is \`"user"\` and \`age\` is \`29\`, the fragmented version stops at the first failure. The whole-object assertion shows both discrepancies at once, in context.
1363
+
1364
+ ### Self-Diagnosing Tests
1365
+
1366
+ When a big test fails, it should be self-evident how to diagnose and fix the failure.
1367
+
1368
+ The more moving parts a test has — end-to-end, system, integration — the more critical this becomes. A test that fails with \`expected true, received false\` forces the developer (or agent) to re-run, add logging, and guess. A test that fails with a rich diff showing the actual state versus the expected state turns diagnosis into reading.
1369
+
1370
+ Anti-patterns:
1371
+
1372
+ **Boolean flattening** — collapsing a rich value into true/false before asserting:
1373
+ \`\`\`javascript
1374
+ // Bad: "expected true, received false" — what events arrived?
1375
+ expect(events.some(e => e.type === 'check-passed')).toBe(true)
1376
+
1377
+ // Good: shows the actual event types on failure
1378
+ expect(events.map(e => e.type)).toContain('check-passed')
1379
+ \`\`\`
1380
+
1381
+ **Length-only assertions** — checking count without showing contents:
1382
+ \`\`\`javascript
1383
+ // Bad: "expected 2, received 0" — what requests were captured?
1384
+ expect(requests.length).toBe(2)
1385
+
1386
+ // Good: shows the actual requests on failure
1387
+ expect(requests).toHaveLength(2) // vitest shows the array
1388
+ \`\`\`
1389
+
1390
+ **Silent guards** — using \`if\` where an assertion belongs:
1391
+ \`\`\`javascript
1392
+ // Bad: silently passes when settings is undefined
1393
+ if (settings) {
1394
+ expect(JSON.parse(settings).key).toBeDefined()
1395
+ }
1396
+
1397
+ // Good: fails explicitly if settings is missing
1398
+ expect(settings).toBeDefined()
1399
+ const parsed = JSON.parse(settings!)
1400
+ expect(parsed.key).toBeDefined()
1401
+ \`\`\`
1402
+
1403
+ ### Functional Core, Imperative Shell
1404
+
1405
+ Separate code into a pure "functional core" and a thin "imperative shell." The core takes values in and returns values out, with no side effects. The shell handles I/O and wires things together.
1406
+
1407
+ Purely functional code makes some things easier to understand: because values don't change, you can call functions and know that only their return value matters—they don't change anything outside themselves.
1408
+
1409
+ The functional core contains business logic as pure functions that take values and return values. The imperative shell sits at the boundary, reading input, calling into the core, and performing side effects with the results. This keeps the majority of code easy to test (no mocks or stubs needed for pure functions) and makes the I/O surface area small and explicit.
1410
+
1411
+ ## Scope
1412
+
1413
+ Search for test files and analyze them for clarity issues:
1414
+
1415
+ 1. **Test files** - Files matching \`*.test.ts\`, \`*.test.js\`, \`*.spec.ts\`, \`*.spec.js\`
1416
+ - Include unit, integration, and system tests
1417
+ - Exclude exploratory test files
1418
+
1419
+ 2. **Patterns to identify**:
1420
+ - Object literals with unused properties in test setup
1421
+ - Magic numbers without semantic meaning
1422
+ - Excessive mock/stub setup
1423
+ - Complex nested structures where simpler ones would suffice
1424
+ - Brittle string assertions coupled to formatting
1425
+ - Boolean flattening (testing \`.toBe(true)\` instead of showing actual values)
1426
+ - Length-only assertions (testing \`.length\` instead of \`.toHaveLength()\`)
1427
+ - Silent guards (using \`if\` where assertions belong)
1428
+
1429
+ ## Analysis Steps
1430
+
1431
+ 1. **Find test files**
1432
+ - Search for \`**/*.test.ts\`, \`**/*.test.js\`, \`**/*.spec.ts\`, \`**/*.spec.js\`
1433
+ - Filter out exploratory tests
1434
+
1435
+ 2. **Analyze each test file**
1436
+ - Look for object literals in test setup with properties that aren't used in assertions
1437
+ - Identify numeric literals that lack semantic meaning (e.g., \`42\`, \`123\` without explaining what they represent)
1438
+ - Count mock/stub setup lines relative to actual test logic
1439
+ - Check for deeply nested test data structures (3+ levels)
1440
+ - Find string assertions that compare exact formatting (spaces, newlines, etc.) rather than semantic content
1441
+ - Detect boolean flattening patterns (\`.some()\`, \`.every()\`, \`.includes()\` followed by \`.toBe(true/false)\`)
1442
+ - Find length checks using \`.length\` property instead of \`.toHaveLength()\`
1443
+ - Locate conditional logic in tests (\`if\` statements) that should be assertions
1444
+
1445
+ 3. **Create ideas for issues found**
1446
+ - Group issues by test file
1447
+ - For each file with issues, create an idea file documenting:
1448
+ - Test file path
1449
+ - List of patterns found with line numbers
1450
+ - Pattern categories
1451
+ - Current problematic patterns
1452
+ - Recommended refactoring approaches
1453
+
1454
+ ## Output Format
1455
+
1456
+ For each test file with clarity issues, create an idea file with:
1457
+
1458
+ ### Title
1459
+ "Simplify test data in [filename]"
1460
+
1461
+ ### Content Structure
1462
+ \`\`\`markdown
1463
+ # Simplify test data in [filename]
1464
+
1465
+ The test file \`[path]\` contains incidental details that obscure test intent.
1466
+
1467
+ ## Issues Found
1468
+
1469
+ ### [Pattern Name] (line X)
1470
+ - **Current**: \`[code snippet]\`
1471
+ - **Issue**: [explanation of how this obscures intent]
1472
+ - **Recommendation**: [specific simplification guidance]
1473
+
1474
+ [Repeat for each issue]
1475
+ \`\`\`
1476
+
1477
+ ## Applicability
1478
+
1479
+ This audit applies to codebases with test files. If the codebase has no test files (\`*.test.ts\`, \`*.spec.js\`, etc.), document that finding and skip the detailed analysis.
1480
+
1481
+ ## Focus
1482
+
1483
+ This audit focuses purely on test clarity — whether tests clearly communicate intent. It does not evaluate test performance or execution speed.
1484
+
1485
+ ## Blocked By
1486
+
1487
+ (none)
1488
+
1489
+ ## Definition of Done
1490
+
1491
+ - Searched for all test files in the codebase
1492
+ - Analyzed test files for incidental complexity patterns
1493
+ - Identified tests with unused properties in setup data
1494
+ - Found magic numbers lacking semantic meaning
1495
+ - Flagged excessive mock/stub setup
1496
+ - Located complex nested structures
1497
+ - Detected brittle string assertions
1498
+ - Found boolean flattening patterns
1499
+ - Located length-only assertions
1500
+ - Identified silent guards (if statements in tests)
1501
+ - Created idea files for each test file with findings
1502
+ - Each idea includes: file path, issues with line numbers, pattern categories, current patterns, recommendations
1503
+ - No changes to files outside \`.dust/\`
1504
+ `;
1505
+ }
1119
1506
  function commitReview() {
1120
1507
  return dedent`
1121
1508
  # Commit Review
@@ -1126,7 +1513,13 @@ function commitReview() {
1126
1513
 
1127
1514
  ## Scope
1128
1515
 
1129
- Analyze commits since the last commit-review audit (check \`.dust/done/\` for previous runs). Focus on these signals:
1516
+ Determine which commits to analyze:
1517
+
1518
+ 1. Check VCS history for a prior commit-review run: \`git log --grep="Audit: Commit Review" -1 --format=%H\`
1519
+ 2. If found, analyze commits since that commit
1520
+ 3. If not found, analyze the last 20 commits as a fallback
1521
+
1522
+ Focus on these signals:
1130
1523
 
1131
1524
  1. **File churn** - Files modified frequently across multiple commits may have unclear responsibilities or be accumulating technical debt
1132
1525
  2. **Size growth** - Files that have grown significantly may benefit from decomposition
@@ -1472,6 +1865,168 @@ function slowTests() {
1472
1865
  - No changes to files outside \`.dust/\`
1473
1866
  `;
1474
1867
  }
1868
+ function overAbstraction() {
1869
+ return dedent`
1870
+ # Over-Abstraction
1871
+
1872
+ Identify violations of the "reasonably-dry" principle where code has been over-engineered with excessive abstraction.
1873
+
1874
+ ${ideasHint}
1875
+
1876
+ ## Scope
1877
+
1878
+ Detect these over-abstraction patterns:
1879
+
1880
+ 1. **Single-use abstractions** - Interfaces, base classes, or utility functions used in only one place
1881
+ 2. **Deep inheritance hierarchies** - Classes extending more than 2 levels deep
1882
+ 3. **Premature generalization** - Parameters always used with the same value, unused options/flags
1883
+ 4. **Excessive indirection** - Multiple layers of wrappers adding no value
1884
+
1885
+ ## Analysis Steps
1886
+
1887
+ ### 1. Find Single-Use Abstractions
1888
+
1889
+ Search for abstractions that are only used once:
1890
+
1891
+ 1. **Interfaces with one implementation**
1892
+ - Search for \`interface\` declarations
1893
+ - Check if each interface has only one implementing class
1894
+ - Flag interfaces that exist solely for testing (can be replaced with the concrete type)
1895
+
1896
+ 2. **Base classes with one subclass**
1897
+ - Search for \`abstract class\` or classes used as base classes
1898
+ - Count implementations extending each base class
1899
+ - Flag base classes with only one subclass
1900
+
1901
+ 3. **Utility functions called once**
1902
+ - Search for exported utility functions
1903
+ - Check call sites - if only called from one location, it's over-abstraction
1904
+ - Consider inlining single-use utilities
1905
+
1906
+ 4. **Generic types with one concrete usage**
1907
+ - Find generic type parameters: \`<T>\`, \`<TData>\`, etc.
1908
+ - Check if T is always the same type at all call sites
1909
+ - Flag generics that could be concrete types
1910
+
1911
+ ### 2. Detect Deep Inheritance Hierarchies
1912
+
1913
+ Find inheritance chains longer than 2 levels:
1914
+
1915
+ 1. Search for \`extends\` keywords in class declarations
1916
+ 2. Build inheritance tree for each class
1917
+ 3. Flag chains deeper than 2 (A extends B extends C extends D...)
1918
+ 4. Respect framework conventions (don't flag React.Component, etc.)
1919
+
1920
+ ### 3. Identify Premature Generalization
1921
+
1922
+ Look for flexibility that's never used:
1923
+
1924
+ 1. **Always-same parameter values**
1925
+ - Find function parameters
1926
+ - Check all call sites - if always the same value, it's not needed
1927
+ - Flag parameters that could be constants or removed
1928
+
1929
+ 2. **Unused configuration options**
1930
+ - Search for configuration objects/interfaces
1931
+ - Check which options are actually used
1932
+ - Flag options that are never set or always default
1933
+
1934
+ 3. **Unused function parameters**
1935
+ - Find parameters that aren't referenced in function bodies
1936
+ - Flag as candidates for removal
1937
+
1938
+ ### 4. Find Excessive Indirection
1939
+
1940
+ Detect wrapper chains that add no value:
1941
+
1942
+ 1. **Delegation chains**
1943
+ - Search for functions that only call another function
1944
+ - Flag wrappers that don't add logic, just forward calls
1945
+ - Example: \`function foo(x) { return bar(x) }\`
1946
+
1947
+ 2. **Proxy patterns without behavior**
1948
+ - Find classes that wrap another class
1949
+ - Check if wrapper adds any logic beyond forwarding
1950
+ - Flag pure proxies
1951
+
1952
+ 3. **Middleware without transformation**
1953
+ - Look for middleware/interceptor patterns
1954
+ - Check if they modify data or just pass through
1955
+ - Flag pass-through middleware
1956
+
1957
+ ## Output Format
1958
+
1959
+ For each over-abstraction found, create an idea file in \`.dust/ideas/\` with:
1960
+
1961
+ \`\`\`markdown
1962
+ # Over-Abstraction: [Type] in [Location]
1963
+
1964
+ ## Type
1965
+
1966
+ [Single-use | Deep hierarchy | Premature generalization | Excessive indirection]
1967
+
1968
+ ## Location
1969
+
1970
+ \`\`\`
1971
+ [file path]:[line number]
1972
+ \`\`\`
1973
+
1974
+ ## Description
1975
+
1976
+ [What the abstraction is]
1977
+
1978
+ ## Problem
1979
+
1980
+ [Why this is over-abstraction - complexity without benefit]
1981
+
1982
+ ## Usage Analysis
1983
+
1984
+ - **Times used**: [count]
1985
+ - **Variation in usage**: [how different are the use cases]
1986
+ - **Complexity cost**: [lines of code, indirection levels, etc.]
1987
+
1988
+ ## Suggested Simplification
1989
+
1990
+ [How to remove or reduce this abstraction]
1991
+
1992
+ ## Impact
1993
+
1994
+ [Lines of code saved, reduced complexity, improved clarity]
1995
+ \`\`\`
1996
+
1997
+ ## Special Considerations
1998
+
1999
+ 1. **Framework conventions** - Don't flag patterns mandated by frameworks:
2000
+ - React: Component base classes, hooks patterns
2001
+ - Express: Middleware signatures
2002
+ - Testing: Test base classes, fixture patterns
2003
+
2004
+ 2. **Library boundaries** - Public API abstractions may be justified even if internal usage is simple
2005
+
2006
+ 3. **Test code** - Apply the same standards to test code as production code
2007
+
2008
+ 4. **Context depth thresholds**:
2009
+ - Deep hierarchies (>2 levels) make understanding difficult
2010
+ - Wrapper chains (>2 levels) obscure actual behavior
2011
+ - Generic parameters should have multiple concrete usages
2012
+
2013
+ ## Blocked By
2014
+
2015
+ (none)
2016
+
2017
+ ## Definition of Done
2018
+
2019
+ - Searched for single-use interfaces, base classes, and utility functions
2020
+ - Identified deep inheritance hierarchies (>2 levels)
2021
+ - Found parameters always used with the same value
2022
+ - Detected unused configuration options
2023
+ - Located excessive wrapper chains and delegation
2024
+ - Respected framework conventions (didn't flag framework-mandated patterns)
2025
+ - Created idea files for each over-abstraction found
2026
+ - Each idea includes usage analysis and simplification suggestions
2027
+ - No changes to files outside \`.dust/\`
2028
+ `;
2029
+ }
1475
2030
  function primitiveObsession() {
1476
2031
  return dedent`
1477
2032
  # Primitive Obsession
@@ -1743,7 +2298,7 @@ function testAssertions() {
1743
2298
 
1744
2299
  ## Background
1745
2300
 
1746
- The [Comprehensive Assertions](../principles/comprehensive-assertions.md) principle covers asserting whole objects rather than fragments. The [Self-Diagnosing Tests](../principles/self-diagnosing-tests.md) principle covers making failure messages informative. This audit addresses complementary assertion quality signals not covered by existing principles.
2301
+ Comprehensive assertions (asserting the whole, not the parts) provide richer failure diagnostics. Self-diagnosing tests ensure that failures reveal enough context to guide a fix without re-running. This audit addresses complementary assertion quality signals not covered by those principles.
1747
2302
 
1748
2303
  ## Scope
1749
2304
 
@@ -1802,7 +2357,7 @@ function testAssertions() {
1802
2357
  - Require test updates for unrelated changes
1803
2358
  - Obscure what the test is actually verifying
1804
2359
 
1805
- This works in tension with [Comprehensive Assertions](../principles/comprehensive-assertions.md). Let context determine the balance:
2360
+ This works in tension with comprehensive assertions (asserting the whole, not the parts). Let context determine the balance:
1806
2361
  - Public API contracts → comprehensive assertions
1807
2362
  - Internal implementation tests → precise assertions
1808
2363
  - Snapshot tests → consider \`toMatchSnapshot()\` with care
@@ -1829,7 +2384,7 @@ function testAssertions() {
1829
2384
 
1830
2385
  Tests should ideally verify one behavior or scenario. When a test has multiple unrelated assertions, a failure in the first masks all subsequent ones.
1831
2386
 
1832
- This does not mean "one \`expect\` call per test". A single logical assertion may require multiple \`expect\` calls to express (especially for complex state). The [Comprehensive Assertions](../principles/comprehensive-assertions.md) principle often allows collapsing multiple calls into one whole-object assertion.
2387
+ This does not mean "one \`expect\` call per test". A single logical assertion may require multiple \`expect\` calls to express (especially for complex state). Comprehensive assertions (asserting the whole, not the parts) often allow collapsing multiple calls into one whole-object assertion.
1833
2388
 
1834
2389
  The anti-pattern to avoid:
1835
2390
  \`\`\`javascript
@@ -1992,6 +2547,95 @@ function loggingAndTraceability() {
1992
2547
  - No changes to files outside \`.dust/\`
1993
2548
  `;
1994
2549
  }
2550
+ function testDeterminism() {
2551
+ return dedent`
2552
+ # Test Determinism
2553
+
2554
+ Audit unit tests for non-deterministic patterns that cause tests to produce inconsistent results across different environments or executions.
2555
+
2556
+ ${ideasHint}
2557
+
2558
+ ## Context
2559
+
2560
+ Tests must produce the same result regardless of where they run. Non-deterministic tests undermine confidence in CI, make debugging harder, and waste developer time chasing phantom failures. This audit identifies patterns that introduce non-determinism: time dependencies, randomness, environment variable access, filesystem operations, real timers, and platform-specific behavior.
2561
+
2562
+ ## Scope
2563
+
2564
+ Search for unit test files and analyze them for determinism issues:
2565
+
2566
+ 1. **Unit test files** - Files matching \`*.test.ts\`, \`*.test.js\`, \`*.spec.ts\`, \`*.spec.js\`
2567
+ - Exclude system test files (files containing 'system-test' or in 'system-tests/' directories)
2568
+ - Exclude exploratory test files
2569
+
2570
+ 2. **Issue categories to detect**:
2571
+ - Time dependencies (\`Date.now()\`, \`new Date()\`) — should use dependency injection or stubbed time
2572
+ - Randomness (\`Math.random()\`, \`crypto.randomBytes()\`, \`randomUUID()\`) — should use seeded random or injection
2573
+ - Environment variables (\`process.env.VARIABLE\` without \`stubEnv\`) — should use \`stubEnv()\` or pass env as a parameter
2574
+ - Filesystem operations (file reads/writes in unit tests) — should use in-memory filesystem or ensure cleanup
2575
+ - Real timers (\`setTimeout\`, \`setInterval\` without fake timers) — should use \`vi.useFakeTimers()\`
2576
+ - Platform-specific code (\`process.platform\`, \`__dirname\`, \`os.EOL\`) — should use dependency injection or normalize paths
2577
+
2578
+ ## Analysis Steps
2579
+
2580
+ 1. **Find unit test files**
2581
+ - Search for \`**/*.test.ts\`, \`**/*.test.js\`, \`**/*.spec.ts\`, \`**/*.spec.js\`
2582
+ - Filter out system test files and exploratory tests
2583
+
2584
+ 2. **Analyze each test file**
2585
+ - Read the file content
2586
+ - Look for the patterns listed above
2587
+ - Note: patterns used inside stub/mock setups (\`vi.fn()\`, \`vi.mock()\`, \`vi.spyOn()\`), function parameter type annotations, or \`stubEnv()\` calls are not issues — they represent proper test practices
2588
+
2589
+ 3. **Create ideas for issues found**
2590
+ - Group issues by test file
2591
+ - For each file with issues, create an idea file documenting:
2592
+ - Test file path
2593
+ - List of issues with line numbers
2594
+ - Issue categories
2595
+ - Current problematic patterns
2596
+ - Recommended refactoring approaches
2597
+
2598
+ ## Output Format
2599
+
2600
+ For each test file with determinism issues, create an idea file with:
2601
+
2602
+ ### Title
2603
+ "Refactor [filename] for test determinism"
2604
+
2605
+ ### Content Structure
2606
+ \`\`\`markdown
2607
+ # Refactor [filename] for test determinism
2608
+
2609
+ The test file \`[path]\` contains non-deterministic patterns that should be refactored.
2610
+
2611
+ ## Issues Found
2612
+
2613
+ ### [Category Name] (line X)
2614
+ - **Pattern**: \`[code snippet]\`
2615
+ - **Issue**: [explanation of why this is non-deterministic]
2616
+ - **Recommendation**: [specific refactoring guidance]
2617
+
2618
+ [Repeat for each issue]
2619
+ \`\`\`
2620
+
2621
+ ## Applicability
2622
+
2623
+ This audit applies to codebases with unit tests. If the codebase has no unit test files (\`*.test.ts\`, \`*.spec.js\`, etc.), document that finding and skip the detailed analysis.
2624
+
2625
+ ## Blocked By
2626
+
2627
+ (none)
2628
+
2629
+ ## Definition of Done
2630
+
2631
+ - Searched for unit test files (\`*.test.ts\`, \`*.test.js\`, \`*.spec.ts\`, \`*.spec.js\`)
2632
+ - Excluded system test and exploratory test files
2633
+ - Analyzed each unit test file for determinism issues
2634
+ - Created idea files for test files containing determinism issues
2635
+ - Each idea includes specific line numbers, patterns, and refactoring guidance
2636
+ - No changes to files outside \`.dust/\`
2637
+ `;
2638
+ }
1995
2639
  function testPyramid() {
1996
2640
  return dedent`
1997
2641
  # Test Pyramid
@@ -2360,7 +3004,7 @@ function ciDevelopmentParity() {
2360
3004
  2. **Wasted cycles** - Developers push code that passes locally only to have CI fail
2361
3005
  3. **Agent confusion** - AI agents rely on consistent feedback; discrepancies trigger incorrect debugging paths
2362
3006
 
2363
- The [Reproducible Checks](../principles/reproducible-checks.md) principle ensures the same checks run everywhere.
3007
+ Every check must produce the same result regardless of who runs it, when, or on what machine.
2364
3008
 
2365
3009
  ## Scope
2366
3010
 
@@ -2450,7 +3094,7 @@ function ciDevelopmentParity() {
2450
3094
 
2451
3095
  - Developers may push code that passes locally but fails CI on other checks
2452
3096
  - CI provides no coverage for [check category]
2453
- - The [Stop the Line](../principles/stop-the-line.md) principle is violated - problems aren't caught before merge
3097
+ - Problems aren't caught before merge—any worker should halt and fix a problem the moment they detect it
2454
3098
 
2455
3099
  ## Suggested Fix
2456
3100
 
@@ -2474,7 +3118,7 @@ function ciDevelopmentParity() {
2474
3118
  ## Impact
2475
3119
 
2476
3120
  - Developers don't get [check category] feedback until CI runs
2477
- - [Fast Feedback Loops](../principles/fast-feedback-loops.md) are broken - local checks give incomplete picture
3121
+ - Fast feedback loops are brokenlocal checks give incomplete picture
2478
3122
  - Agents may make changes that pass local checks but fail CI
2479
3123
 
2480
3124
  ## Suggested Fix
@@ -2512,7 +3156,7 @@ function commitMessageQuality() {
2512
3156
 
2513
3157
  ## Context
2514
3158
 
2515
- The [Traceable Decisions](../principles/traceable-decisions.md) principle emphasizes that commit history should explain why changes were made. Good commit messages help agents understand project history and make better decisions. This audit evaluates commit message quality itself, not the code changes.
3159
+ Commit history should explain why changes were made, not just what changed. Good commit messages help agents understand project history and make better decisions. This audit evaluates commit message quality itself, not the code changes.
2516
3160
 
2517
3161
  ## Scope
2518
3162
 
@@ -2653,20 +3297,14 @@ function commitMessageQuality() {
2653
3297
  `;
2654
3298
  }
2655
3299
  function suggestAudits() {
2656
- const auditList = Object.entries(stockAuditFunctions).filter(([name]) => name !== "suggest-audits").toSorted(([a], [b]) => a.localeCompare(b)).map(([name, render]) => {
2657
- const template = render();
2658
- const description = extractOpeningSentence(template);
2659
- return `- **${name}**: ${description}`;
2660
- }).join(`
2661
- `);
2662
- let content = dedent`
3300
+ return dedent`
2663
3301
  # Suggest Audits
2664
3302
 
2665
3303
  Analyze recent commits and create tasks for relevant audits to run.
2666
3304
 
2667
3305
  ## Context
2668
3306
 
2669
- This audit examines recent commit history and suggests which stock audits would be valuable based on what changed. Rather than manually selecting audits, this provides an automated way to maintain codebase health by matching recent work to appropriate audits.
3307
+ This audit examines recent commit history and suggests which audits would be valuable based on what changed. Rather than manually selecting audits, this provides an automated way to maintain codebase health by matching recent work to appropriate audits.
2670
3308
 
2671
3309
  ## Commit Range
2672
3310
 
@@ -2678,21 +3316,17 @@ function suggestAudits() {
2678
3316
 
2679
3317
  ## Available Audits
2680
3318
 
2681
- `;
2682
- content += `
2683
-
2684
- ` + auditList + `
2685
- `;
2686
- content += dedent`
3319
+ Run \`dust audit\` to list all available audits (including both stock audits and any repository-specific audits configured in \`.dust/config/audits/\`). This will show the audit name and description for each available audit.
2687
3320
 
2688
3321
  ## Analysis Steps
2689
3322
 
2690
- 1. **Gather commits** - Get the list of commits in the determined range with their messages and changed files
2691
- 2. **Categorize changes** - Group commits by the type of work (features, fixes, refactoring, tests, docs, config)
2692
- 3. **Match to audits** - For each relevant audit, explain why recent changes make it valuable:
3323
+ 1. **List audits** - Run \`dust audit\` to get the complete list of available audits with descriptions
3324
+ 2. **Gather commits** - Get the list of commits in the determined range with their messages and changed files
3325
+ 3. **Categorize changes** - Group commits by the type of work (features, fixes, refactoring, tests, docs, config)
3326
+ 4. **Match to audits** - For each relevant audit, explain why recent changes make it valuable:
2693
3327
  - What specific commits or file changes triggered the suggestion?
2694
3328
  - What might the audit uncover given this context?
2695
- 4. **Create tasks** - For each suggested audit, create a task file in \`.dust/tasks/\`
3329
+ 5. **Create tasks** - For each suggested audit, create a task file in \`.dust/tasks/\`
2696
3330
 
2697
3331
  ## Output
2698
3332
 
@@ -2741,7 +3375,6 @@ function suggestAudits() {
2741
3375
  - Each task explains why the audit is valuable given recent changes
2742
3376
  - No changes to files outside \`.dust/\`
2743
3377
  `;
2744
- return content;
2745
3378
  }
2746
3379
  var stockAuditFunctions = {
2747
3380
  "agent-developer-experience": agentDeveloperExperience,
@@ -2757,7 +3390,9 @@ var stockAuditFunctions = {
2757
3390
  "data-access-review": dataAccessReview,
2758
3391
  "dead-code": deadCode,
2759
3392
  "design-patterns": designPatterns,
3393
+ "directory-hierarchy": directoryHierarchy,
2760
3394
  "error-handling": errorHandling,
3395
+ "facts-expansion": factsExpansion,
2761
3396
  "facts-verification": factsVerification,
2762
3397
  "feedback-loop-speed": feedbackLoopSpeed,
2763
3398
  "flaky-tests": flakyTests,
@@ -2765,7 +3400,9 @@ var stockAuditFunctions = {
2765
3400
  "commit-review": commitReview,
2766
3401
  "ideas-from-principles": ideasFromPrinciples,
2767
3402
  "idiomatic-style": idiomaticStyle,
3403
+ "incidental-test-details": incidentalTestDetails,
2768
3404
  "logging-and-traceability": loggingAndTraceability,
3405
+ "over-abstraction": overAbstraction,
2769
3406
  "primitive-obsession": primitiveObsession,
2770
3407
  "repository-context": repositoryContext,
2771
3408
  "security-review": securityReview,
@@ -2774,6 +3411,7 @@ var stockAuditFunctions = {
2774
3411
  "stale-ideas": staleIdeas,
2775
3412
  "suggest-audits": suggestAudits,
2776
3413
  "test-assertions": testAssertions,
3414
+ "test-determinism": testDeterminism,
2777
3415
  "test-pyramid": testPyramid,
2778
3416
  "ubiquitous-language": ubiquitousLanguage,
2779
3417
  "ux-audit": uxAudit