@joshski/dust 0.1.106 → 0.1.107
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/README.md +2 -3
- package/dist/artifacts.js +1 -1
- package/dist/audits.js +296 -0
- package/dist/core-principles.js +1401 -87
- package/dist/dust.js +1885 -255
- package/package.json +2 -3
- package/.dust/principles/actionable-errors.md +0 -18
- package/.dust/principles/agent-agnostic-design.md +0 -21
- package/.dust/principles/agent-autonomy.md +0 -19
- package/.dust/principles/agent-context-inference.md +0 -19
- package/.dust/principles/agent-specific-enhancement.md +0 -23
- package/.dust/principles/agentic-flow-state.md +0 -24
- package/.dust/principles/atomic-commits.md +0 -15
- package/.dust/principles/batteries-included.md +0 -17
- package/.dust/principles/boy-scout-rule.md +0 -15
- package/.dust/principles/broken-windows.md +0 -17
- package/.dust/principles/clarity-over-brevity.md +0 -13
- package/.dust/principles/co-located-tests.md +0 -13
- package/.dust/principles/comprehensive-assertions.md +0 -50
- package/.dust/principles/comprehensive-test-coverage.md +0 -15
- package/.dust/principles/consistent-naming.md +0 -13
- package/.dust/principles/context-optimised-code.md +0 -15
- package/.dust/principles/context-window-efficiency.md +0 -15
- package/.dust/principles/cross-platform-compatibility.md +0 -19
- package/.dust/principles/debugging-tooling.md +0 -19
- package/.dust/principles/decoupled-code.md +0 -16
- package/.dust/principles/dependency-injection.md +0 -15
- package/.dust/principles/design-for-testability.md +0 -17
- package/.dust/principles/development-traceability.md +0 -19
- package/.dust/principles/easy-adoption.md +0 -17
- package/.dust/principles/environment-independent-tests.md +0 -19
- package/.dust/principles/exploratory-tooling.md +0 -19
- package/.dust/principles/fast-feedback-loops.md +0 -15
- package/.dust/principles/fast-feedback.md +0 -13
- package/.dust/principles/functional-core-imperative-shell.md +0 -15
- package/.dust/principles/human-ai-collaboration.md +0 -18
- package/.dust/principles/ideal-agent-developer-experience.md +0 -24
- package/.dust/principles/intuitive-directory-structure.md +0 -13
- package/.dust/principles/keep-unit-tests-pure.md +0 -25
- package/.dust/principles/lightweight-planning.md +0 -16
- package/.dust/principles/lint-everything.md +0 -19
- package/.dust/principles/maintainable-codebase.md +0 -21
- package/.dust/principles/make-changes-with-confidence.md +0 -23
- package/.dust/principles/make-the-change-easy.md +0 -15
- package/.dust/principles/minimal-dependencies.md +0 -13
- package/.dust/principles/naming-matters.md +0 -14
- package/.dust/principles/progressive-disclosure.md +0 -15
- package/.dust/principles/readable-test-data.md +0 -48
- package/.dust/principles/reasonably-dry.md +0 -13
- package/.dust/principles/repository-hygiene.md +0 -14
- package/.dust/principles/reproducible-checks.md +0 -13
- package/.dust/principles/runtime-agnostic-tests.md +0 -13
- package/.dust/principles/self-contained-repository.md +0 -17
- package/.dust/principles/self-diagnosing-tests.md +0 -54
- package/.dust/principles/slow-feedback-coping.md +0 -15
- package/.dust/principles/small-units.md +0 -17
- package/.dust/principles/some-big-design-up-front.md +0 -34
- package/.dust/principles/stop-the-line.md +0 -13
- package/.dust/principles/stubs-over-mocks.md +0 -19
- package/.dust/principles/task-first-workflow.md +0 -13
- package/.dust/principles/test-isolation.md +0 -19
- package/.dust/principles/traceable-decisions.md +0 -13
- package/.dust/principles/trunk-based-development.md +0 -19
- package/.dust/principles/unit-test-coverage.md +0 -13
- package/.dust/principles/unsurprising-ux.md +0 -15
- package/.dust/principles/vcs-independence.md +0 -13
package/README.md
CHANGED
|
@@ -9,11 +9,10 @@ Dust provides a CLI that agents use to systematically blaze through your backlog
|
|
|
9
9
|
## Quick Start
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
|
|
13
|
-
npx dust init
|
|
12
|
+
claude "install dust as per https://github.com/joshski/dust"
|
|
14
13
|
```
|
|
15
14
|
|
|
16
|
-
This
|
|
15
|
+
This works with other agents (codex, cursor, opencode, etc.) too. The agent will install dust and set up the [.dust](./.dust/facts/dust-directory-structure.md) directory with an [instruction](./.dust/facts/agents-md-instruction.md) in your `AGENTS.md` file.
|
|
17
16
|
|
|
18
17
|
## Adding Tasks
|
|
19
18
|
|
package/dist/artifacts.js
CHANGED
|
@@ -869,7 +869,7 @@ async function decomposeIdea(fileSystem, dustPath, options, dustCommand) {
|
|
|
869
869
|
return createIdeaTransitionTask(fileSystem, dustPath, "decompose", "Decompose Idea: ", options.ideaSlug, (ideaTitle) => `Create one or more well-defined tasks from this idea. Prefer smaller, narrowly scoped tasks that each deliver a thin but complete vertical slice of working software -- a path through the system that can be tested end-to-end -- rather than component-oriented tasks (like "add schema" or "build endpoint") that only work once all tasks are done. Split the idea into multiple tasks if it covers more than one logical change. Run \`${cmd} principles\` to link relevant principles and \`${cmd} facts\` for design decisions that should inform the task. See [${ideaTitle}](../ideas/${options.ideaSlug}.md).`, [
|
|
870
870
|
"One or more new tasks are created in .dust/tasks/",
|
|
871
871
|
"Task's Principles section links to relevant principles from .dust/principles/",
|
|
872
|
-
|
|
872
|
+
`The original idea (.dust/ideas/${options.ideaSlug}.md) is deleted or updated to reflect remaining scope`
|
|
873
873
|
], "Decomposes Idea", {
|
|
874
874
|
description: options.description,
|
|
875
875
|
resolvedQuestions: options.openQuestionResponses
|
package/dist/audits.js
CHANGED
|
@@ -787,6 +787,301 @@ function feedbackLoopSpeed() {
|
|
|
787
787
|
- No changes to files outside \`.dust/\`
|
|
788
788
|
`;
|
|
789
789
|
}
|
|
790
|
+
function flakyTests() {
|
|
791
|
+
return dedent`
|
|
792
|
+
# Flaky Tests
|
|
793
|
+
|
|
794
|
+
Detect timing-dependent patterns that cause test flakiness. This includes fixed sleep/delay usage, event ordering assumptions, race conditions, missing synchronization, and subprocess timing issues.
|
|
795
|
+
|
|
796
|
+
${ideasHint}
|
|
797
|
+
|
|
798
|
+
## Context
|
|
799
|
+
|
|
800
|
+
Test flakiness undermines confidence in CI and slows development. While fixed delays account for nearly 50% of async-related test flakiness, the remaining issues stem from race conditions, event ordering assumptions, and missing synchronization. This audit provides comprehensive detection of async patterns that cause flakiness through semantic analysis of code structure and control flow.
|
|
801
|
+
|
|
802
|
+
## Scope
|
|
803
|
+
|
|
804
|
+
Focus on detecting these patterns in test files:
|
|
805
|
+
|
|
806
|
+
1. **Fixed sleep calls** - \`setTimeout()\`, \`sleep()\`, \`delay()\` with hardcoded millisecond values
|
|
807
|
+
2. **Wait/delay utilities** - Custom wait functions that use fixed timeouts
|
|
808
|
+
3. **Retry with fixed delays** - Retry loops that sleep a constant amount between attempts
|
|
809
|
+
4. **Timing comments** - Comments indicating timing assumptions (e.g., "wait 100ms", "give it time to settle")
|
|
810
|
+
5. **Event ordering assumptions** - Tests assuming synchronous event propagation
|
|
811
|
+
6. **Race conditions** - Multiple concurrent async operations without proper synchronization
|
|
812
|
+
7. **Missing synchronization** - Assertions on eventually-consistent state without waiting
|
|
813
|
+
8. **Subprocess timing issues** - Child process tests with improper async handling
|
|
814
|
+
|
|
815
|
+
## Analysis Steps
|
|
816
|
+
|
|
817
|
+
### 1. Identify Test Files
|
|
818
|
+
|
|
819
|
+
Search for test files using common patterns:
|
|
820
|
+
- \`**/*.test.ts\`
|
|
821
|
+
- \`**/*.test.js\`
|
|
822
|
+
- \`**/*.spec.ts\`
|
|
823
|
+
- \`**/*.spec.js\`
|
|
824
|
+
- Framework-specific patterns (e.g., \`__tests__/**\`)
|
|
825
|
+
|
|
826
|
+
### 2. Search for Fixed Sleep Patterns
|
|
827
|
+
|
|
828
|
+
Search test files for these patterns:
|
|
829
|
+
|
|
830
|
+
**Direct delay calls:**
|
|
831
|
+
- \`setTimeout\`
|
|
832
|
+
- \`sleep\`
|
|
833
|
+
- \`delay\`
|
|
834
|
+
- \`wait\`
|
|
835
|
+
- \`Thread.sleep\`
|
|
836
|
+
- \`time.sleep\`
|
|
837
|
+
|
|
838
|
+
**Timing comments:**
|
|
839
|
+
- "wait for"
|
|
840
|
+
- "sleep"
|
|
841
|
+
- "give it time"
|
|
842
|
+
- "let it settle"
|
|
843
|
+
- References to specific millisecond values in comments near test assertions
|
|
844
|
+
|
|
845
|
+
### 3. Detect Event Ordering Assumptions
|
|
846
|
+
|
|
847
|
+
Search for tests that assume synchronous event propagation:
|
|
848
|
+
|
|
849
|
+
**Patterns to detect:**
|
|
850
|
+
- \`emitter.emit('event')\` or similar followed immediately by assertions (without await/wait)
|
|
851
|
+
- Event handler registration followed by immediate state checks
|
|
852
|
+
- Tests asserting on event-driven state changes without synchronization
|
|
853
|
+
- Missing promise wrappers around event-driven flows
|
|
854
|
+
|
|
855
|
+
**Example problematic pattern:**
|
|
856
|
+
\`\`\`typescript
|
|
857
|
+
eventEmitter.emit('data-updated')
|
|
858
|
+
expect(component.state).toBe('updated') // Assumes synchronous propagation
|
|
859
|
+
\`\`\`
|
|
860
|
+
|
|
861
|
+
**Suggested alternatives:**
|
|
862
|
+
- Promise-based event waiting: Wrap events in promises
|
|
863
|
+
- Polling utilities: Wait for state to match expected value
|
|
864
|
+
- Framework-specific event handling patterns
|
|
865
|
+
|
|
866
|
+
### 4. Detect Race Conditions
|
|
867
|
+
|
|
868
|
+
Search for tests with multiple concurrent async operations without proper synchronization:
|
|
869
|
+
|
|
870
|
+
**Patterns to detect:**
|
|
871
|
+
- Multiple promise calls without \`Promise.all()\` or sequential awaits
|
|
872
|
+
- State mutations from different async contexts without locks/ordering
|
|
873
|
+
- \`afterEach()\`/\`afterAll()\` cleanup that may run before async operations complete
|
|
874
|
+
- Shared state between tests without proper reset in setup/teardown
|
|
875
|
+
- Tests where the outcome depends on which operation finishes first
|
|
876
|
+
|
|
877
|
+
**Example problematic patterns:**
|
|
878
|
+
\`\`\`typescript
|
|
879
|
+
// Missing Promise.all()
|
|
880
|
+
doAsync1() // Not awaited
|
|
881
|
+
doAsync2() // Not awaited
|
|
882
|
+
expect(result).toBe(expected) // Which result?
|
|
883
|
+
|
|
884
|
+
// Cleanup race
|
|
885
|
+
afterEach(() => {
|
|
886
|
+
cleanupState() // May run before async operations finish
|
|
887
|
+
})
|
|
888
|
+
\`\`\`
|
|
889
|
+
|
|
890
|
+
**Severity:** Mark as Critical when operations clearly race, Warning for potential races
|
|
891
|
+
|
|
892
|
+
### 5. Detect Missing Synchronization
|
|
893
|
+
|
|
894
|
+
Search for assertions on eventually-consistent state:
|
|
895
|
+
|
|
896
|
+
**Patterns to detect:**
|
|
897
|
+
- Assertions on state modified by async operations without awaiting
|
|
898
|
+
- Manual polling with \`while\` loops and fixed delays
|
|
899
|
+
- Comments mentioning "eventually", "should become", "will be"
|
|
900
|
+
- Integration test retries or manual delay logic around assertions
|
|
901
|
+
- Database/cache operations followed by immediate reads without guarantees
|
|
902
|
+
|
|
903
|
+
**Example problematic pattern:**
|
|
904
|
+
\`\`\`typescript
|
|
905
|
+
saveToDatabase(data) // Async operation
|
|
906
|
+
const result = readFromDatabase() // Immediate read
|
|
907
|
+
expect(result).toBe(data) // May fail if write not complete
|
|
908
|
+
\`\`\`
|
|
909
|
+
|
|
910
|
+
**Suggest:** Condition-based waiting utilities or framework-provided eventually helpers
|
|
911
|
+
|
|
912
|
+
### 6. Detect Subprocess/Child Process Timing Issues
|
|
913
|
+
|
|
914
|
+
Search for tests using child processes with improper async handling:
|
|
915
|
+
|
|
916
|
+
**Patterns to detect:**
|
|
917
|
+
- \`spawn()\`, \`exec()\`, \`fork()\` without waiting for completion
|
|
918
|
+
- Assertions on subprocess output without waiting for exit/close events
|
|
919
|
+
- Race conditions between stdout/stderr events and exit events
|
|
920
|
+
- Cleanup that doesn't account for async process termination
|
|
921
|
+
- Reading process output before process completes
|
|
922
|
+
|
|
923
|
+
**Example problematic patterns:**
|
|
924
|
+
\`\`\`typescript
|
|
925
|
+
const proc = spawn('command')
|
|
926
|
+
expect(proc.stdout).toContain('expected') // May not have output yet
|
|
927
|
+
|
|
928
|
+
// Cleanup race
|
|
929
|
+
afterEach(() => {
|
|
930
|
+
proc.kill() // Doesn't wait for process to actually exit
|
|
931
|
+
})
|
|
932
|
+
\`\`\`
|
|
933
|
+
|
|
934
|
+
**Suggest:** Promise-based subprocess wrappers or event-to-promise utilities
|
|
935
|
+
|
|
936
|
+
### 7. Detect Framework-Agnostic Async Patterns
|
|
937
|
+
|
|
938
|
+
Search for common cross-framework async issues:
|
|
939
|
+
|
|
940
|
+
**Patterns to detect:**
|
|
941
|
+
- Missing awaits after state/DOM updates
|
|
942
|
+
- Hardcoded waits instead of selector/condition-based waiting
|
|
943
|
+
- Improper async wrapper usage (forgetting await, not handling promises)
|
|
944
|
+
- Async test functions without proper await chains
|
|
945
|
+
- \`.then()\` chains without error handling in tests
|
|
946
|
+
|
|
947
|
+
**Focus on patterns that appear across frameworks rather than framework-specific APIs.**
|
|
948
|
+
|
|
949
|
+
### 8. Identify Available Utilities
|
|
950
|
+
|
|
951
|
+
Before proposing solutions, search the codebase for existing condition-based waiting utilities:
|
|
952
|
+
|
|
953
|
+
**Common utility names:**
|
|
954
|
+
- \`waitFor\`
|
|
955
|
+
- \`waitUntil\`
|
|
956
|
+
- \`waitForCondition\`
|
|
957
|
+
- \`poll\`
|
|
958
|
+
- \`eventually\`
|
|
959
|
+
- \`retryUntil\`
|
|
960
|
+
|
|
961
|
+
**Framework-specific helpers:**
|
|
962
|
+
- Testing Library: \`waitFor\`, \`waitForElementToBeRemoved\`
|
|
963
|
+
- Playwright/Puppeteer: \`waitForSelector\`, \`waitForFunction\`
|
|
964
|
+
- Cypress: \`cy.wait\` with aliases (not fixed delays)
|
|
965
|
+
- WebdriverIO: \`waitUntil\`
|
|
966
|
+
|
|
967
|
+
If utilities exist, adapt recommendations to use them. If not, suggest implementing simple polling helpers.
|
|
968
|
+
|
|
969
|
+
## Output Format
|
|
970
|
+
|
|
971
|
+
### Per-File Ideas
|
|
972
|
+
|
|
973
|
+
Create one idea file per test file containing fixed sleep patterns. Each idea should include:
|
|
974
|
+
|
|
975
|
+
**Title format:** "Flaky Test: Fixed Delays in [Component/Feature] Tests"
|
|
976
|
+
|
|
977
|
+
**Structure:**
|
|
978
|
+
\`\`\`markdown
|
|
979
|
+
# Flaky Test: Fixed Delays in [Component/Feature] Tests
|
|
980
|
+
|
|
981
|
+
## Context
|
|
982
|
+
|
|
983
|
+
[Test file path] contains hardcoded delays that make tests timing-dependent and prone to flakiness.
|
|
984
|
+
|
|
985
|
+
## Findings
|
|
986
|
+
|
|
987
|
+
### [Test Suite/Describe Block Name]
|
|
988
|
+
|
|
989
|
+
**Location:** [file:line]
|
|
990
|
+
|
|
991
|
+
**Pattern:**
|
|
992
|
+
\`\`\`[language]
|
|
993
|
+
[code excerpt showing the fixed delay]
|
|
994
|
+
\`\`\`
|
|
995
|
+
|
|
996
|
+
**Issue:** This test waits a fixed duration instead of waiting for a specific condition, making it either unreliable (if too short) or unnecessarily slow (if too long).
|
|
997
|
+
|
|
998
|
+
[Repeat for each finding in the file]
|
|
999
|
+
|
|
1000
|
+
## Proposed Solution
|
|
1001
|
+
|
|
1002
|
+
Replace fixed delays with condition-based waiting:
|
|
1003
|
+
|
|
1004
|
+
### Before
|
|
1005
|
+
\`\`\`[language]
|
|
1006
|
+
[current code with setTimeout/sleep]
|
|
1007
|
+
\`\`\`
|
|
1008
|
+
|
|
1009
|
+
### After
|
|
1010
|
+
\`\`\`[language]
|
|
1011
|
+
[refactored code using waitFor/polling utility]
|
|
1012
|
+
\`\`\`
|
|
1013
|
+
|
|
1014
|
+
[If no utility exists:]
|
|
1015
|
+
Consider implementing a simple polling utility:
|
|
1016
|
+
\`\`\`[language]
|
|
1017
|
+
[example polling helper implementation]
|
|
1018
|
+
\`\`\`
|
|
1019
|
+
|
|
1020
|
+
## Benefits
|
|
1021
|
+
|
|
1022
|
+
- Tests become more reliable (no race conditions)
|
|
1023
|
+
- Tests run faster (don't wait longer than necessary)
|
|
1024
|
+
- Tests are more explicit about what they're waiting for
|
|
1025
|
+
\`\`\`
|
|
1026
|
+
|
|
1027
|
+
### Severity Levels
|
|
1028
|
+
|
|
1029
|
+
Use these severity indicators in idea titles:
|
|
1030
|
+
|
|
1031
|
+
- **Critical:** Obvious issues likely to cause flakiness (e.g., "Flaky Test [CRITICAL]: ...")
|
|
1032
|
+
- **Warning:** Suspicious patterns or edge cases (e.g., "Flaky Test [WARNING]: ...")
|
|
1033
|
+
- **Info:** Timing dependencies that may be intentional (e.g., "Flaky Test [INFO]: ...")
|
|
1034
|
+
|
|
1035
|
+
Mark patterns as Critical when:
|
|
1036
|
+
- Direct \`setTimeout\`/\`sleep\` calls with hardcoded durations
|
|
1037
|
+
- Retry logic with fixed delays
|
|
1038
|
+
- Comments explicitly mentioning waiting for time to pass
|
|
1039
|
+
- Obvious race conditions between async operations
|
|
1040
|
+
- Missing awaits on async operations before assertions
|
|
1041
|
+
- Event emission followed immediately by assertions
|
|
1042
|
+
|
|
1043
|
+
Mark patterns as Warning when:
|
|
1044
|
+
- Unclear if the delay is test-related or production code
|
|
1045
|
+
- Complex timing logic that may be intentional
|
|
1046
|
+
- Potential race conditions requiring analysis
|
|
1047
|
+
- Subprocess tests without clear synchronization
|
|
1048
|
+
- Shared state between tests without visible reset
|
|
1049
|
+
|
|
1050
|
+
Mark patterns as Info when:
|
|
1051
|
+
- Borderline cases requiring human judgment
|
|
1052
|
+
- Patterns that might be intentional performance tests
|
|
1053
|
+
- Timing dependencies with unclear context
|
|
1054
|
+
|
|
1055
|
+
## Applicability
|
|
1056
|
+
|
|
1057
|
+
This audit applies to codebases with:
|
|
1058
|
+
- Automated test suites (unit, integration, or end-to-end tests)
|
|
1059
|
+
- Async operations being tested (API calls, UI updates, event handling)
|
|
1060
|
+
|
|
1061
|
+
If the project has no tests or only synchronous tests, document that finding and skip the detailed analysis.
|
|
1062
|
+
|
|
1063
|
+
## Blocked By
|
|
1064
|
+
|
|
1065
|
+
(none)
|
|
1066
|
+
|
|
1067
|
+
## Definition of Done
|
|
1068
|
+
|
|
1069
|
+
- Identified all test files in the codebase using common patterns
|
|
1070
|
+
- Searched test files for fixed sleep patterns (\`setTimeout\`, \`sleep\`, etc.)
|
|
1071
|
+
- Searched for timing-related comments in test files
|
|
1072
|
+
- Detected event ordering assumptions (emit without wait)
|
|
1073
|
+
- Detected race conditions (multiple async operations without synchronization)
|
|
1074
|
+
- Detected missing synchronization (assertions on eventually-consistent state)
|
|
1075
|
+
- Detected subprocess/child process timing issues
|
|
1076
|
+
- Detected framework-agnostic async patterns (missing awaits, hardcoded waits)
|
|
1077
|
+
- Identified available condition-based waiting utilities (existing or framework-provided)
|
|
1078
|
+
- Created idea files for each test file containing async patterns (one idea per test file)
|
|
1079
|
+
- Each idea includes context, findings with line numbers, and actionable solutions
|
|
1080
|
+
- Solutions are adapted to utilities available in the target codebase
|
|
1081
|
+
- Severity levels assigned appropriately (Critical, Warning, or Info)
|
|
1082
|
+
- No changes to files outside \`.dust/\`
|
|
1083
|
+
`;
|
|
1084
|
+
}
|
|
790
1085
|
function ideasFromPrinciples() {
|
|
791
1086
|
return dedent`
|
|
792
1087
|
# Ideas from Principles
|
|
@@ -2465,6 +2760,7 @@ var stockAuditFunctions = {
|
|
|
2465
2760
|
"error-handling": errorHandling,
|
|
2466
2761
|
"facts-verification": factsVerification,
|
|
2467
2762
|
"feedback-loop-speed": feedbackLoopSpeed,
|
|
2763
|
+
"flaky-tests": flakyTests,
|
|
2468
2764
|
"global-state": globalState,
|
|
2469
2765
|
"commit-review": commitReview,
|
|
2470
2766
|
"ideas-from-principles": ideasFromPrinciples,
|