@joshski/dust 0.1.108 → 0.1.109
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 +503 -28
- package/dist/dust.js +545 -56
- package/dist/filesystem/error-codes.d.ts +2 -0
- package/dist/loop/iteration.d.ts +2 -2
- package/dist/patch.js +18 -10
- package/dist/validation.js +15 -7
- package/package.json +1 -1
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
|
|
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
|
-
|
|
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
|
|
@@ -1743,7 +2136,7 @@ function testAssertions() {
|
|
|
1743
2136
|
|
|
1744
2137
|
## Background
|
|
1745
2138
|
|
|
1746
|
-
|
|
2139
|
+
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
2140
|
|
|
1748
2141
|
## Scope
|
|
1749
2142
|
|
|
@@ -1802,7 +2195,7 @@ function testAssertions() {
|
|
|
1802
2195
|
- Require test updates for unrelated changes
|
|
1803
2196
|
- Obscure what the test is actually verifying
|
|
1804
2197
|
|
|
1805
|
-
This works in tension with
|
|
2198
|
+
This works in tension with comprehensive assertions (asserting the whole, not the parts). Let context determine the balance:
|
|
1806
2199
|
- Public API contracts → comprehensive assertions
|
|
1807
2200
|
- Internal implementation tests → precise assertions
|
|
1808
2201
|
- Snapshot tests → consider \`toMatchSnapshot()\` with care
|
|
@@ -1829,7 +2222,7 @@ function testAssertions() {
|
|
|
1829
2222
|
|
|
1830
2223
|
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
2224
|
|
|
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).
|
|
2225
|
+
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
2226
|
|
|
1834
2227
|
The anti-pattern to avoid:
|
|
1835
2228
|
\`\`\`javascript
|
|
@@ -1992,6 +2385,95 @@ function loggingAndTraceability() {
|
|
|
1992
2385
|
- No changes to files outside \`.dust/\`
|
|
1993
2386
|
`;
|
|
1994
2387
|
}
|
|
2388
|
+
function testDeterminism() {
|
|
2389
|
+
return dedent`
|
|
2390
|
+
# Test Determinism
|
|
2391
|
+
|
|
2392
|
+
Audit unit tests for non-deterministic patterns that cause tests to produce inconsistent results across different environments or executions.
|
|
2393
|
+
|
|
2394
|
+
${ideasHint}
|
|
2395
|
+
|
|
2396
|
+
## Context
|
|
2397
|
+
|
|
2398
|
+
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.
|
|
2399
|
+
|
|
2400
|
+
## Scope
|
|
2401
|
+
|
|
2402
|
+
Search for unit test files and analyze them for determinism issues:
|
|
2403
|
+
|
|
2404
|
+
1. **Unit test files** - Files matching \`*.test.ts\`, \`*.test.js\`, \`*.spec.ts\`, \`*.spec.js\`
|
|
2405
|
+
- Exclude system test files (files containing 'system-test' or in 'system-tests/' directories)
|
|
2406
|
+
- Exclude exploratory test files
|
|
2407
|
+
|
|
2408
|
+
2. **Issue categories to detect**:
|
|
2409
|
+
- Time dependencies (\`Date.now()\`, \`new Date()\`) — should use dependency injection or stubbed time
|
|
2410
|
+
- Randomness (\`Math.random()\`, \`crypto.randomBytes()\`, \`randomUUID()\`) — should use seeded random or injection
|
|
2411
|
+
- Environment variables (\`process.env.VARIABLE\` without \`stubEnv\`) — should use \`stubEnv()\` or pass env as a parameter
|
|
2412
|
+
- Filesystem operations (file reads/writes in unit tests) — should use in-memory filesystem or ensure cleanup
|
|
2413
|
+
- Real timers (\`setTimeout\`, \`setInterval\` without fake timers) — should use \`vi.useFakeTimers()\`
|
|
2414
|
+
- Platform-specific code (\`process.platform\`, \`__dirname\`, \`os.EOL\`) — should use dependency injection or normalize paths
|
|
2415
|
+
|
|
2416
|
+
## Analysis Steps
|
|
2417
|
+
|
|
2418
|
+
1. **Find unit test files**
|
|
2419
|
+
- Search for \`**/*.test.ts\`, \`**/*.test.js\`, \`**/*.spec.ts\`, \`**/*.spec.js\`
|
|
2420
|
+
- Filter out system test files and exploratory tests
|
|
2421
|
+
|
|
2422
|
+
2. **Analyze each test file**
|
|
2423
|
+
- Read the file content
|
|
2424
|
+
- Look for the patterns listed above
|
|
2425
|
+
- 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
|
|
2426
|
+
|
|
2427
|
+
3. **Create ideas for issues found**
|
|
2428
|
+
- Group issues by test file
|
|
2429
|
+
- For each file with issues, create an idea file documenting:
|
|
2430
|
+
- Test file path
|
|
2431
|
+
- List of issues with line numbers
|
|
2432
|
+
- Issue categories
|
|
2433
|
+
- Current problematic patterns
|
|
2434
|
+
- Recommended refactoring approaches
|
|
2435
|
+
|
|
2436
|
+
## Output Format
|
|
2437
|
+
|
|
2438
|
+
For each test file with determinism issues, create an idea file with:
|
|
2439
|
+
|
|
2440
|
+
### Title
|
|
2441
|
+
"Refactor [filename] for test determinism"
|
|
2442
|
+
|
|
2443
|
+
### Content Structure
|
|
2444
|
+
\`\`\`markdown
|
|
2445
|
+
# Refactor [filename] for test determinism
|
|
2446
|
+
|
|
2447
|
+
The test file \`[path]\` contains non-deterministic patterns that should be refactored.
|
|
2448
|
+
|
|
2449
|
+
## Issues Found
|
|
2450
|
+
|
|
2451
|
+
### [Category Name] (line X)
|
|
2452
|
+
- **Pattern**: \`[code snippet]\`
|
|
2453
|
+
- **Issue**: [explanation of why this is non-deterministic]
|
|
2454
|
+
- **Recommendation**: [specific refactoring guidance]
|
|
2455
|
+
|
|
2456
|
+
[Repeat for each issue]
|
|
2457
|
+
\`\`\`
|
|
2458
|
+
|
|
2459
|
+
## Applicability
|
|
2460
|
+
|
|
2461
|
+
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.
|
|
2462
|
+
|
|
2463
|
+
## Blocked By
|
|
2464
|
+
|
|
2465
|
+
(none)
|
|
2466
|
+
|
|
2467
|
+
## Definition of Done
|
|
2468
|
+
|
|
2469
|
+
- Searched for unit test files (\`*.test.ts\`, \`*.test.js\`, \`*.spec.ts\`, \`*.spec.js\`)
|
|
2470
|
+
- Excluded system test and exploratory test files
|
|
2471
|
+
- Analyzed each unit test file for determinism issues
|
|
2472
|
+
- Created idea files for test files containing determinism issues
|
|
2473
|
+
- Each idea includes specific line numbers, patterns, and refactoring guidance
|
|
2474
|
+
- No changes to files outside \`.dust/\`
|
|
2475
|
+
`;
|
|
2476
|
+
}
|
|
1995
2477
|
function testPyramid() {
|
|
1996
2478
|
return dedent`
|
|
1997
2479
|
# Test Pyramid
|
|
@@ -2360,7 +2842,7 @@ function ciDevelopmentParity() {
|
|
|
2360
2842
|
2. **Wasted cycles** - Developers push code that passes locally only to have CI fail
|
|
2361
2843
|
3. **Agent confusion** - AI agents rely on consistent feedback; discrepancies trigger incorrect debugging paths
|
|
2362
2844
|
|
|
2363
|
-
|
|
2845
|
+
Every check must produce the same result regardless of who runs it, when, or on what machine.
|
|
2364
2846
|
|
|
2365
2847
|
## Scope
|
|
2366
2848
|
|
|
@@ -2450,7 +2932,7 @@ function ciDevelopmentParity() {
|
|
|
2450
2932
|
|
|
2451
2933
|
- Developers may push code that passes locally but fails CI on other checks
|
|
2452
2934
|
- CI provides no coverage for [check category]
|
|
2453
|
-
-
|
|
2935
|
+
- Problems aren't caught before merge—any worker should halt and fix a problem the moment they detect it
|
|
2454
2936
|
|
|
2455
2937
|
## Suggested Fix
|
|
2456
2938
|
|
|
@@ -2474,7 +2956,7 @@ function ciDevelopmentParity() {
|
|
|
2474
2956
|
## Impact
|
|
2475
2957
|
|
|
2476
2958
|
- Developers don't get [check category] feedback until CI runs
|
|
2477
|
-
-
|
|
2959
|
+
- Fast feedback loops are broken—local checks give incomplete picture
|
|
2478
2960
|
- Agents may make changes that pass local checks but fail CI
|
|
2479
2961
|
|
|
2480
2962
|
## Suggested Fix
|
|
@@ -2512,7 +2994,7 @@ function commitMessageQuality() {
|
|
|
2512
2994
|
|
|
2513
2995
|
## Context
|
|
2514
2996
|
|
|
2515
|
-
|
|
2997
|
+
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
2998
|
|
|
2517
2999
|
## Scope
|
|
2518
3000
|
|
|
@@ -2653,20 +3135,14 @@ function commitMessageQuality() {
|
|
|
2653
3135
|
`;
|
|
2654
3136
|
}
|
|
2655
3137
|
function suggestAudits() {
|
|
2656
|
-
|
|
2657
|
-
const template = render();
|
|
2658
|
-
const description = extractOpeningSentence(template);
|
|
2659
|
-
return `- **${name}**: ${description}`;
|
|
2660
|
-
}).join(`
|
|
2661
|
-
`);
|
|
2662
|
-
let content = dedent`
|
|
3138
|
+
return dedent`
|
|
2663
3139
|
# Suggest Audits
|
|
2664
3140
|
|
|
2665
3141
|
Analyze recent commits and create tasks for relevant audits to run.
|
|
2666
3142
|
|
|
2667
3143
|
## Context
|
|
2668
3144
|
|
|
2669
|
-
This audit examines recent commit history and suggests which
|
|
3145
|
+
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
3146
|
|
|
2671
3147
|
## Commit Range
|
|
2672
3148
|
|
|
@@ -2678,21 +3154,17 @@ function suggestAudits() {
|
|
|
2678
3154
|
|
|
2679
3155
|
## Available Audits
|
|
2680
3156
|
|
|
2681
|
-
|
|
2682
|
-
content += `
|
|
2683
|
-
|
|
2684
|
-
` + auditList + `
|
|
2685
|
-
`;
|
|
2686
|
-
content += dedent`
|
|
3157
|
+
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
3158
|
|
|
2688
3159
|
## Analysis Steps
|
|
2689
3160
|
|
|
2690
|
-
1. **
|
|
2691
|
-
2. **
|
|
2692
|
-
3. **
|
|
3161
|
+
1. **List audits** - Run \`dust audit\` to get the complete list of available audits with descriptions
|
|
3162
|
+
2. **Gather commits** - Get the list of commits in the determined range with their messages and changed files
|
|
3163
|
+
3. **Categorize changes** - Group commits by the type of work (features, fixes, refactoring, tests, docs, config)
|
|
3164
|
+
4. **Match to audits** - For each relevant audit, explain why recent changes make it valuable:
|
|
2693
3165
|
- What specific commits or file changes triggered the suggestion?
|
|
2694
3166
|
- What might the audit uncover given this context?
|
|
2695
|
-
|
|
3167
|
+
5. **Create tasks** - For each suggested audit, create a task file in \`.dust/tasks/\`
|
|
2696
3168
|
|
|
2697
3169
|
## Output
|
|
2698
3170
|
|
|
@@ -2741,7 +3213,6 @@ function suggestAudits() {
|
|
|
2741
3213
|
- Each task explains why the audit is valuable given recent changes
|
|
2742
3214
|
- No changes to files outside \`.dust/\`
|
|
2743
3215
|
`;
|
|
2744
|
-
return content;
|
|
2745
3216
|
}
|
|
2746
3217
|
var stockAuditFunctions = {
|
|
2747
3218
|
"agent-developer-experience": agentDeveloperExperience,
|
|
@@ -2757,7 +3228,9 @@ var stockAuditFunctions = {
|
|
|
2757
3228
|
"data-access-review": dataAccessReview,
|
|
2758
3229
|
"dead-code": deadCode,
|
|
2759
3230
|
"design-patterns": designPatterns,
|
|
3231
|
+
"directory-hierarchy": directoryHierarchy,
|
|
2760
3232
|
"error-handling": errorHandling,
|
|
3233
|
+
"facts-expansion": factsExpansion,
|
|
2761
3234
|
"facts-verification": factsVerification,
|
|
2762
3235
|
"feedback-loop-speed": feedbackLoopSpeed,
|
|
2763
3236
|
"flaky-tests": flakyTests,
|
|
@@ -2765,6 +3238,7 @@ var stockAuditFunctions = {
|
|
|
2765
3238
|
"commit-review": commitReview,
|
|
2766
3239
|
"ideas-from-principles": ideasFromPrinciples,
|
|
2767
3240
|
"idiomatic-style": idiomaticStyle,
|
|
3241
|
+
"incidental-test-details": incidentalTestDetails,
|
|
2768
3242
|
"logging-and-traceability": loggingAndTraceability,
|
|
2769
3243
|
"primitive-obsession": primitiveObsession,
|
|
2770
3244
|
"repository-context": repositoryContext,
|
|
@@ -2774,6 +3248,7 @@ var stockAuditFunctions = {
|
|
|
2774
3248
|
"stale-ideas": staleIdeas,
|
|
2775
3249
|
"suggest-audits": suggestAudits,
|
|
2776
3250
|
"test-assertions": testAssertions,
|
|
3251
|
+
"test-determinism": testDeterminism,
|
|
2777
3252
|
"test-pyramid": testPyramid,
|
|
2778
3253
|
"ubiquitous-language": ubiquitousLanguage,
|
|
2779
3254
|
"ux-audit": uxAudit
|