@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/dist/dust.js
CHANGED
|
@@ -7,7 +7,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
7
7
|
var require_package = __commonJS((exports, module) => {
|
|
8
8
|
module.exports = {
|
|
9
9
|
name: "@joshski/dust",
|
|
10
|
-
version: "0.1.
|
|
10
|
+
version: "0.1.107",
|
|
11
11
|
description: "Flow state for AI coding agents",
|
|
12
12
|
type: "module",
|
|
13
13
|
bin: {
|
|
@@ -63,8 +63,7 @@ var require_package = __commonJS((exports, module) => {
|
|
|
63
63
|
"bin",
|
|
64
64
|
"lib/istanbul/minimal-reporter.cjs",
|
|
65
65
|
"lib/docker/default.Dockerfile",
|
|
66
|
-
"biome"
|
|
67
|
-
".dust/principles"
|
|
66
|
+
"biome"
|
|
68
67
|
],
|
|
69
68
|
repository: {
|
|
70
69
|
type: "git",
|
|
@@ -382,12 +381,12 @@ function buildUnattendedEnv(options) {
|
|
|
382
381
|
}
|
|
383
382
|
|
|
384
383
|
// lib/cli/wire.ts
|
|
385
|
-
import { existsSync as
|
|
384
|
+
import { existsSync as existsSync2, statSync as statSync2 } from "node:fs";
|
|
386
385
|
import {
|
|
387
386
|
chmod as chmod2,
|
|
388
387
|
mkdir as mkdir2,
|
|
389
388
|
readdir as readdir2,
|
|
390
|
-
readFile as
|
|
389
|
+
readFile as readFile3,
|
|
391
390
|
rename,
|
|
392
391
|
writeFile as writeFile2
|
|
393
392
|
} from "node:fs/promises";
|
|
@@ -712,7 +711,7 @@ async function loadSettings(cwd, fileSystem, runtime) {
|
|
|
712
711
|
}
|
|
713
712
|
|
|
714
713
|
// lib/version.ts
|
|
715
|
-
var DUST_VERSION = "0.1.
|
|
714
|
+
var DUST_VERSION = "0.1.107";
|
|
716
715
|
|
|
717
716
|
// lib/cli/middleware.ts
|
|
718
717
|
function applyMiddleware(middlewares, execute) {
|
|
@@ -1808,6 +1807,301 @@ function feedbackLoopSpeed() {
|
|
|
1808
1807
|
- No changes to files outside \`.dust/\`
|
|
1809
1808
|
`;
|
|
1810
1809
|
}
|
|
1810
|
+
function flakyTests() {
|
|
1811
|
+
return dedent`
|
|
1812
|
+
# Flaky Tests
|
|
1813
|
+
|
|
1814
|
+
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.
|
|
1815
|
+
|
|
1816
|
+
${ideasHint}
|
|
1817
|
+
|
|
1818
|
+
## Context
|
|
1819
|
+
|
|
1820
|
+
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.
|
|
1821
|
+
|
|
1822
|
+
## Scope
|
|
1823
|
+
|
|
1824
|
+
Focus on detecting these patterns in test files:
|
|
1825
|
+
|
|
1826
|
+
1. **Fixed sleep calls** - \`setTimeout()\`, \`sleep()\`, \`delay()\` with hardcoded millisecond values
|
|
1827
|
+
2. **Wait/delay utilities** - Custom wait functions that use fixed timeouts
|
|
1828
|
+
3. **Retry with fixed delays** - Retry loops that sleep a constant amount between attempts
|
|
1829
|
+
4. **Timing comments** - Comments indicating timing assumptions (e.g., "wait 100ms", "give it time to settle")
|
|
1830
|
+
5. **Event ordering assumptions** - Tests assuming synchronous event propagation
|
|
1831
|
+
6. **Race conditions** - Multiple concurrent async operations without proper synchronization
|
|
1832
|
+
7. **Missing synchronization** - Assertions on eventually-consistent state without waiting
|
|
1833
|
+
8. **Subprocess timing issues** - Child process tests with improper async handling
|
|
1834
|
+
|
|
1835
|
+
## Analysis Steps
|
|
1836
|
+
|
|
1837
|
+
### 1. Identify Test Files
|
|
1838
|
+
|
|
1839
|
+
Search for test files using common patterns:
|
|
1840
|
+
- \`**/*.test.ts\`
|
|
1841
|
+
- \`**/*.test.js\`
|
|
1842
|
+
- \`**/*.spec.ts\`
|
|
1843
|
+
- \`**/*.spec.js\`
|
|
1844
|
+
- Framework-specific patterns (e.g., \`__tests__/**\`)
|
|
1845
|
+
|
|
1846
|
+
### 2. Search for Fixed Sleep Patterns
|
|
1847
|
+
|
|
1848
|
+
Search test files for these patterns:
|
|
1849
|
+
|
|
1850
|
+
**Direct delay calls:**
|
|
1851
|
+
- \`setTimeout\`
|
|
1852
|
+
- \`sleep\`
|
|
1853
|
+
- \`delay\`
|
|
1854
|
+
- \`wait\`
|
|
1855
|
+
- \`Thread.sleep\`
|
|
1856
|
+
- \`time.sleep\`
|
|
1857
|
+
|
|
1858
|
+
**Timing comments:**
|
|
1859
|
+
- "wait for"
|
|
1860
|
+
- "sleep"
|
|
1861
|
+
- "give it time"
|
|
1862
|
+
- "let it settle"
|
|
1863
|
+
- References to specific millisecond values in comments near test assertions
|
|
1864
|
+
|
|
1865
|
+
### 3. Detect Event Ordering Assumptions
|
|
1866
|
+
|
|
1867
|
+
Search for tests that assume synchronous event propagation:
|
|
1868
|
+
|
|
1869
|
+
**Patterns to detect:**
|
|
1870
|
+
- \`emitter.emit('event')\` or similar followed immediately by assertions (without await/wait)
|
|
1871
|
+
- Event handler registration followed by immediate state checks
|
|
1872
|
+
- Tests asserting on event-driven state changes without synchronization
|
|
1873
|
+
- Missing promise wrappers around event-driven flows
|
|
1874
|
+
|
|
1875
|
+
**Example problematic pattern:**
|
|
1876
|
+
\`\`\`typescript
|
|
1877
|
+
eventEmitter.emit('data-updated')
|
|
1878
|
+
expect(component.state).toBe('updated') // Assumes synchronous propagation
|
|
1879
|
+
\`\`\`
|
|
1880
|
+
|
|
1881
|
+
**Suggested alternatives:**
|
|
1882
|
+
- Promise-based event waiting: Wrap events in promises
|
|
1883
|
+
- Polling utilities: Wait for state to match expected value
|
|
1884
|
+
- Framework-specific event handling patterns
|
|
1885
|
+
|
|
1886
|
+
### 4. Detect Race Conditions
|
|
1887
|
+
|
|
1888
|
+
Search for tests with multiple concurrent async operations without proper synchronization:
|
|
1889
|
+
|
|
1890
|
+
**Patterns to detect:**
|
|
1891
|
+
- Multiple promise calls without \`Promise.all()\` or sequential awaits
|
|
1892
|
+
- State mutations from different async contexts without locks/ordering
|
|
1893
|
+
- \`afterEach()\`/\`afterAll()\` cleanup that may run before async operations complete
|
|
1894
|
+
- Shared state between tests without proper reset in setup/teardown
|
|
1895
|
+
- Tests where the outcome depends on which operation finishes first
|
|
1896
|
+
|
|
1897
|
+
**Example problematic patterns:**
|
|
1898
|
+
\`\`\`typescript
|
|
1899
|
+
// Missing Promise.all()
|
|
1900
|
+
doAsync1() // Not awaited
|
|
1901
|
+
doAsync2() // Not awaited
|
|
1902
|
+
expect(result).toBe(expected) // Which result?
|
|
1903
|
+
|
|
1904
|
+
// Cleanup race
|
|
1905
|
+
afterEach(() => {
|
|
1906
|
+
cleanupState() // May run before async operations finish
|
|
1907
|
+
})
|
|
1908
|
+
\`\`\`
|
|
1909
|
+
|
|
1910
|
+
**Severity:** Mark as Critical when operations clearly race, Warning for potential races
|
|
1911
|
+
|
|
1912
|
+
### 5. Detect Missing Synchronization
|
|
1913
|
+
|
|
1914
|
+
Search for assertions on eventually-consistent state:
|
|
1915
|
+
|
|
1916
|
+
**Patterns to detect:**
|
|
1917
|
+
- Assertions on state modified by async operations without awaiting
|
|
1918
|
+
- Manual polling with \`while\` loops and fixed delays
|
|
1919
|
+
- Comments mentioning "eventually", "should become", "will be"
|
|
1920
|
+
- Integration test retries or manual delay logic around assertions
|
|
1921
|
+
- Database/cache operations followed by immediate reads without guarantees
|
|
1922
|
+
|
|
1923
|
+
**Example problematic pattern:**
|
|
1924
|
+
\`\`\`typescript
|
|
1925
|
+
saveToDatabase(data) // Async operation
|
|
1926
|
+
const result = readFromDatabase() // Immediate read
|
|
1927
|
+
expect(result).toBe(data) // May fail if write not complete
|
|
1928
|
+
\`\`\`
|
|
1929
|
+
|
|
1930
|
+
**Suggest:** Condition-based waiting utilities or framework-provided eventually helpers
|
|
1931
|
+
|
|
1932
|
+
### 6. Detect Subprocess/Child Process Timing Issues
|
|
1933
|
+
|
|
1934
|
+
Search for tests using child processes with improper async handling:
|
|
1935
|
+
|
|
1936
|
+
**Patterns to detect:**
|
|
1937
|
+
- \`spawn()\`, \`exec()\`, \`fork()\` without waiting for completion
|
|
1938
|
+
- Assertions on subprocess output without waiting for exit/close events
|
|
1939
|
+
- Race conditions between stdout/stderr events and exit events
|
|
1940
|
+
- Cleanup that doesn't account for async process termination
|
|
1941
|
+
- Reading process output before process completes
|
|
1942
|
+
|
|
1943
|
+
**Example problematic patterns:**
|
|
1944
|
+
\`\`\`typescript
|
|
1945
|
+
const proc = spawn('command')
|
|
1946
|
+
expect(proc.stdout).toContain('expected') // May not have output yet
|
|
1947
|
+
|
|
1948
|
+
// Cleanup race
|
|
1949
|
+
afterEach(() => {
|
|
1950
|
+
proc.kill() // Doesn't wait for process to actually exit
|
|
1951
|
+
})
|
|
1952
|
+
\`\`\`
|
|
1953
|
+
|
|
1954
|
+
**Suggest:** Promise-based subprocess wrappers or event-to-promise utilities
|
|
1955
|
+
|
|
1956
|
+
### 7. Detect Framework-Agnostic Async Patterns
|
|
1957
|
+
|
|
1958
|
+
Search for common cross-framework async issues:
|
|
1959
|
+
|
|
1960
|
+
**Patterns to detect:**
|
|
1961
|
+
- Missing awaits after state/DOM updates
|
|
1962
|
+
- Hardcoded waits instead of selector/condition-based waiting
|
|
1963
|
+
- Improper async wrapper usage (forgetting await, not handling promises)
|
|
1964
|
+
- Async test functions without proper await chains
|
|
1965
|
+
- \`.then()\` chains without error handling in tests
|
|
1966
|
+
|
|
1967
|
+
**Focus on patterns that appear across frameworks rather than framework-specific APIs.**
|
|
1968
|
+
|
|
1969
|
+
### 8. Identify Available Utilities
|
|
1970
|
+
|
|
1971
|
+
Before proposing solutions, search the codebase for existing condition-based waiting utilities:
|
|
1972
|
+
|
|
1973
|
+
**Common utility names:**
|
|
1974
|
+
- \`waitFor\`
|
|
1975
|
+
- \`waitUntil\`
|
|
1976
|
+
- \`waitForCondition\`
|
|
1977
|
+
- \`poll\`
|
|
1978
|
+
- \`eventually\`
|
|
1979
|
+
- \`retryUntil\`
|
|
1980
|
+
|
|
1981
|
+
**Framework-specific helpers:**
|
|
1982
|
+
- Testing Library: \`waitFor\`, \`waitForElementToBeRemoved\`
|
|
1983
|
+
- Playwright/Puppeteer: \`waitForSelector\`, \`waitForFunction\`
|
|
1984
|
+
- Cypress: \`cy.wait\` with aliases (not fixed delays)
|
|
1985
|
+
- WebdriverIO: \`waitUntil\`
|
|
1986
|
+
|
|
1987
|
+
If utilities exist, adapt recommendations to use them. If not, suggest implementing simple polling helpers.
|
|
1988
|
+
|
|
1989
|
+
## Output Format
|
|
1990
|
+
|
|
1991
|
+
### Per-File Ideas
|
|
1992
|
+
|
|
1993
|
+
Create one idea file per test file containing fixed sleep patterns. Each idea should include:
|
|
1994
|
+
|
|
1995
|
+
**Title format:** "Flaky Test: Fixed Delays in [Component/Feature] Tests"
|
|
1996
|
+
|
|
1997
|
+
**Structure:**
|
|
1998
|
+
\`\`\`markdown
|
|
1999
|
+
# Flaky Test: Fixed Delays in [Component/Feature] Tests
|
|
2000
|
+
|
|
2001
|
+
## Context
|
|
2002
|
+
|
|
2003
|
+
[Test file path] contains hardcoded delays that make tests timing-dependent and prone to flakiness.
|
|
2004
|
+
|
|
2005
|
+
## Findings
|
|
2006
|
+
|
|
2007
|
+
### [Test Suite/Describe Block Name]
|
|
2008
|
+
|
|
2009
|
+
**Location:** [file:line]
|
|
2010
|
+
|
|
2011
|
+
**Pattern:**
|
|
2012
|
+
\`\`\`[language]
|
|
2013
|
+
[code excerpt showing the fixed delay]
|
|
2014
|
+
\`\`\`
|
|
2015
|
+
|
|
2016
|
+
**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).
|
|
2017
|
+
|
|
2018
|
+
[Repeat for each finding in the file]
|
|
2019
|
+
|
|
2020
|
+
## Proposed Solution
|
|
2021
|
+
|
|
2022
|
+
Replace fixed delays with condition-based waiting:
|
|
2023
|
+
|
|
2024
|
+
### Before
|
|
2025
|
+
\`\`\`[language]
|
|
2026
|
+
[current code with setTimeout/sleep]
|
|
2027
|
+
\`\`\`
|
|
2028
|
+
|
|
2029
|
+
### After
|
|
2030
|
+
\`\`\`[language]
|
|
2031
|
+
[refactored code using waitFor/polling utility]
|
|
2032
|
+
\`\`\`
|
|
2033
|
+
|
|
2034
|
+
[If no utility exists:]
|
|
2035
|
+
Consider implementing a simple polling utility:
|
|
2036
|
+
\`\`\`[language]
|
|
2037
|
+
[example polling helper implementation]
|
|
2038
|
+
\`\`\`
|
|
2039
|
+
|
|
2040
|
+
## Benefits
|
|
2041
|
+
|
|
2042
|
+
- Tests become more reliable (no race conditions)
|
|
2043
|
+
- Tests run faster (don't wait longer than necessary)
|
|
2044
|
+
- Tests are more explicit about what they're waiting for
|
|
2045
|
+
\`\`\`
|
|
2046
|
+
|
|
2047
|
+
### Severity Levels
|
|
2048
|
+
|
|
2049
|
+
Use these severity indicators in idea titles:
|
|
2050
|
+
|
|
2051
|
+
- **Critical:** Obvious issues likely to cause flakiness (e.g., "Flaky Test [CRITICAL]: ...")
|
|
2052
|
+
- **Warning:** Suspicious patterns or edge cases (e.g., "Flaky Test [WARNING]: ...")
|
|
2053
|
+
- **Info:** Timing dependencies that may be intentional (e.g., "Flaky Test [INFO]: ...")
|
|
2054
|
+
|
|
2055
|
+
Mark patterns as Critical when:
|
|
2056
|
+
- Direct \`setTimeout\`/\`sleep\` calls with hardcoded durations
|
|
2057
|
+
- Retry logic with fixed delays
|
|
2058
|
+
- Comments explicitly mentioning waiting for time to pass
|
|
2059
|
+
- Obvious race conditions between async operations
|
|
2060
|
+
- Missing awaits on async operations before assertions
|
|
2061
|
+
- Event emission followed immediately by assertions
|
|
2062
|
+
|
|
2063
|
+
Mark patterns as Warning when:
|
|
2064
|
+
- Unclear if the delay is test-related or production code
|
|
2065
|
+
- Complex timing logic that may be intentional
|
|
2066
|
+
- Potential race conditions requiring analysis
|
|
2067
|
+
- Subprocess tests without clear synchronization
|
|
2068
|
+
- Shared state between tests without visible reset
|
|
2069
|
+
|
|
2070
|
+
Mark patterns as Info when:
|
|
2071
|
+
- Borderline cases requiring human judgment
|
|
2072
|
+
- Patterns that might be intentional performance tests
|
|
2073
|
+
- Timing dependencies with unclear context
|
|
2074
|
+
|
|
2075
|
+
## Applicability
|
|
2076
|
+
|
|
2077
|
+
This audit applies to codebases with:
|
|
2078
|
+
- Automated test suites (unit, integration, or end-to-end tests)
|
|
2079
|
+
- Async operations being tested (API calls, UI updates, event handling)
|
|
2080
|
+
|
|
2081
|
+
If the project has no tests or only synchronous tests, document that finding and skip the detailed analysis.
|
|
2082
|
+
|
|
2083
|
+
## Blocked By
|
|
2084
|
+
|
|
2085
|
+
(none)
|
|
2086
|
+
|
|
2087
|
+
## Definition of Done
|
|
2088
|
+
|
|
2089
|
+
- Identified all test files in the codebase using common patterns
|
|
2090
|
+
- Searched test files for fixed sleep patterns (\`setTimeout\`, \`sleep\`, etc.)
|
|
2091
|
+
- Searched for timing-related comments in test files
|
|
2092
|
+
- Detected event ordering assumptions (emit without wait)
|
|
2093
|
+
- Detected race conditions (multiple async operations without synchronization)
|
|
2094
|
+
- Detected missing synchronization (assertions on eventually-consistent state)
|
|
2095
|
+
- Detected subprocess/child process timing issues
|
|
2096
|
+
- Detected framework-agnostic async patterns (missing awaits, hardcoded waits)
|
|
2097
|
+
- Identified available condition-based waiting utilities (existing or framework-provided)
|
|
2098
|
+
- Created idea files for each test file containing async patterns (one idea per test file)
|
|
2099
|
+
- Each idea includes context, findings with line numbers, and actionable solutions
|
|
2100
|
+
- Solutions are adapted to utilities available in the target codebase
|
|
2101
|
+
- Severity levels assigned appropriately (Critical, Warning, or Info)
|
|
2102
|
+
- No changes to files outside \`.dust/\`
|
|
2103
|
+
`;
|
|
2104
|
+
}
|
|
1811
2105
|
function ideasFromPrinciples() {
|
|
1812
2106
|
return dedent`
|
|
1813
2107
|
# Ideas from Principles
|
|
@@ -3486,6 +3780,7 @@ var stockAuditFunctions = {
|
|
|
3486
3780
|
"error-handling": errorHandling,
|
|
3487
3781
|
"facts-verification": factsVerification,
|
|
3488
3782
|
"feedback-loop-speed": feedbackLoopSpeed,
|
|
3783
|
+
"flaky-tests": flakyTests,
|
|
3489
3784
|
"global-state": globalState,
|
|
3490
3785
|
"commit-review": commitReview,
|
|
3491
3786
|
"ideas-from-principles": ideasFromPrinciples,
|
|
@@ -9718,57 +10013,6 @@ Run \`dust bucket tool ${toolName}\` to see available operations.`);
|
|
|
9718
10013
|
// lib/cli/commands/lint-markdown.ts
|
|
9719
10014
|
import { isAbsolute, join as join10, relative, sep } from "node:path";
|
|
9720
10015
|
|
|
9721
|
-
// lib/artifacts/principles.ts
|
|
9722
|
-
function extractLinksFromSection(content, sectionHeading) {
|
|
9723
|
-
const lines = content.split(`
|
|
9724
|
-
`);
|
|
9725
|
-
const links = [];
|
|
9726
|
-
let inSection = false;
|
|
9727
|
-
for (const line of lines) {
|
|
9728
|
-
if (line.startsWith("## ")) {
|
|
9729
|
-
inSection = line.trimEnd() === `## ${sectionHeading}`;
|
|
9730
|
-
continue;
|
|
9731
|
-
}
|
|
9732
|
-
if (!inSection)
|
|
9733
|
-
continue;
|
|
9734
|
-
if (line.startsWith("# "))
|
|
9735
|
-
break;
|
|
9736
|
-
const linkMatch = line.match(MARKDOWN_LINK_PATTERN);
|
|
9737
|
-
if (linkMatch) {
|
|
9738
|
-
const target = linkMatch[2];
|
|
9739
|
-
const slugMatch = target.match(/([^/]+)\.md$/);
|
|
9740
|
-
if (slugMatch) {
|
|
9741
|
-
links.push(slugMatch[1]);
|
|
9742
|
-
}
|
|
9743
|
-
}
|
|
9744
|
-
}
|
|
9745
|
-
return links;
|
|
9746
|
-
}
|
|
9747
|
-
function extractSingleLinkFromSection(content, sectionHeading) {
|
|
9748
|
-
const links = extractLinksFromSection(content, sectionHeading);
|
|
9749
|
-
return links.length === 1 ? links[0] : null;
|
|
9750
|
-
}
|
|
9751
|
-
async function parsePrinciple(fileSystem, dustPath, slug) {
|
|
9752
|
-
const principlePath = `${dustPath}/principles/${slug}.md`;
|
|
9753
|
-
if (!fileSystem.exists(principlePath)) {
|
|
9754
|
-
throw new Error(`Principle not found: "${slug}" (expected file at ${principlePath})`);
|
|
9755
|
-
}
|
|
9756
|
-
const content = await fileSystem.readFile(principlePath);
|
|
9757
|
-
const title = extractTitle(content);
|
|
9758
|
-
if (!title) {
|
|
9759
|
-
throw new Error(`Principle file has no title: ${principlePath}`);
|
|
9760
|
-
}
|
|
9761
|
-
const parentPrinciple = extractSingleLinkFromSection(content, "Parent Principle");
|
|
9762
|
-
const subPrinciples = extractLinksFromSection(content, "Sub-Principles");
|
|
9763
|
-
return {
|
|
9764
|
-
slug,
|
|
9765
|
-
title,
|
|
9766
|
-
content,
|
|
9767
|
-
parentPrinciple,
|
|
9768
|
-
subPrinciples
|
|
9769
|
-
};
|
|
9770
|
-
}
|
|
9771
|
-
|
|
9772
10016
|
// lib/artifacts/index.ts
|
|
9773
10017
|
var ARTIFACT_TYPES = [
|
|
9774
10018
|
"facts",
|
|
@@ -10757,179 +11001,1365 @@ async function check(dependencies, shellRunner, clock, _setInterval, _clearInter
|
|
|
10757
11001
|
return { exitCode };
|
|
10758
11002
|
}
|
|
10759
11003
|
|
|
10760
|
-
// lib/
|
|
10761
|
-
|
|
10762
|
-
|
|
10763
|
-
|
|
10764
|
-
|
|
11004
|
+
// lib/bundled-core-principles.ts
|
|
11005
|
+
var BUNDLED_PRINCIPLES = [
|
|
11006
|
+
{
|
|
11007
|
+
slug: "batteries-included",
|
|
11008
|
+
content: `# Batteries Included
|
|
10765
11009
|
|
|
10766
|
-
|
|
11010
|
+
Dust should provide everything that is required (within reason) for an agent to be productive in an arbitrary codebase.
|
|
10767
11011
|
|
|
10768
|
-
|
|
10769
|
-
init Initialize a new Dust repository
|
|
10770
|
-
lint Run lint checks on .dust/ files
|
|
10771
|
-
list List all items (tasks, ideas, principles, facts)
|
|
10772
|
-
tasks List tasks (actionable work with definitions of done)
|
|
10773
|
-
ideas List ideas (vague proposals, convert to tasks when ready)
|
|
10774
|
-
principles List principles (guiding values, stable, rarely change)
|
|
10775
|
-
facts List facts (documentation of current system state)
|
|
10776
|
-
next Show tasks ready to work on (not blocked)
|
|
10777
|
-
check Run project-defined quality gate hook
|
|
10778
|
-
agent Agent greeting and routing instructions
|
|
10779
|
-
audit Create tasks from audit templates
|
|
10780
|
-
focus Declare current objective (for remote session tracking)
|
|
10781
|
-
pick task Pick the next task to work on
|
|
10782
|
-
implement task Implement a task
|
|
10783
|
-
new task Create a new task
|
|
10784
|
-
new principle Create a new principle
|
|
10785
|
-
new idea Create a new idea
|
|
10786
|
-
loop claude Run continuous Claude iteration on tasks
|
|
10787
|
-
pre push Git pre-push hook validation
|
|
10788
|
-
help Show this help message
|
|
11012
|
+
An agent working autonomously should not be blocked because a tool or configuration is missing. For example, dust should ship custom lint rules for different linters, even though those linters are not dependencies of dust itself. If an agent needs a capability to do its job well in a typical codebase, dust should provide it out of the box.
|
|
10789
11013
|
|
|
10790
|
-
|
|
11014
|
+
This means accepting some breadth of scope — bundling configs, rules, and utilities that target external tools — in exchange for agents that can start producing useful work immediately without manual setup.
|
|
10791
11015
|
|
|
10792
|
-
|
|
10793
|
-
- principles/ - Guiding values (stable, rarely change)
|
|
10794
|
-
- ideas/ - Proposals (convert to tasks when ready)
|
|
10795
|
-
- tasks/ - Actionable work with definitions of done
|
|
10796
|
-
- facts/ - Documentation of current system state
|
|
11016
|
+
## Applicability
|
|
10797
11017
|
|
|
10798
|
-
|
|
11018
|
+
Internal
|
|
10799
11019
|
|
|
10800
|
-
|
|
10801
|
-
`;
|
|
10802
|
-
}
|
|
10803
|
-
async function help(dependencies) {
|
|
10804
|
-
dependencies.context.stdout(generateHelpText(dependencies.settings));
|
|
10805
|
-
return { exitCode: 0 };
|
|
10806
|
-
}
|
|
11020
|
+
## Parent Principle
|
|
10807
11021
|
|
|
10808
|
-
|
|
10809
|
-
async function implementTask(dependencies) {
|
|
10810
|
-
const { context, settings } = dependencies;
|
|
10811
|
-
const hooksInstalled = await manageGitHooks(dependencies);
|
|
10812
|
-
const vars = templateVariables(settings, hooksInstalled, process.env);
|
|
10813
|
-
context.stdout(`Run \`${vars.bin} focus "<task name>"\` to set your focus and see implementation instructions.`);
|
|
10814
|
-
return { exitCode: 0 };
|
|
10815
|
-
}
|
|
11022
|
+
- [Agent Autonomy](agent-autonomy.md)
|
|
10816
11023
|
|
|
10817
|
-
|
|
10818
|
-
|
|
10819
|
-
|
|
10820
|
-
|
|
10821
|
-
|
|
10822
|
-
|
|
10823
|
-
if (testCommand) {
|
|
10824
|
-
checks.push({ name: "test", command: testCommand });
|
|
10825
|
-
}
|
|
10826
|
-
return { dustCommand, checks };
|
|
10827
|
-
}
|
|
10828
|
-
var USE_DUST_FACT = `# Use dust for planning
|
|
11024
|
+
## Sub-Principles
|
|
11025
|
+
`
|
|
11026
|
+
},
|
|
11027
|
+
{
|
|
11028
|
+
slug: "some-big-design-up-front",
|
|
11029
|
+
content: `# Some Big Design Up Front
|
|
10829
11030
|
|
|
10830
|
-
|
|
10831
|
-
`;
|
|
10832
|
-
function claudeMdContent(dustCommand) {
|
|
10833
|
-
return dedent`
|
|
10834
|
-
# Claude Code Instructions
|
|
11031
|
+
AI agents lower the cost of architectural exploration, making heavier upfront investment rational during the idea phase.
|
|
10835
11032
|
|
|
10836
|
-
|
|
11033
|
+
Agile's rejection of "big design up front" (BDUF) was largely economic: detailed architecture was expensive to produce and often wrong. AI agents change that equation — they can explore multiple variants, prototype them, and measure trade-offs cheaply. When evaluating alternatives costs less, the expected value of avoiding large structural mistakes increases.
|
|
10837
11034
|
|
|
10838
|
-
|
|
10839
|
-
`;
|
|
10840
|
-
}
|
|
10841
|
-
function agentsMdContent(dustCommand) {
|
|
10842
|
-
return dedent`
|
|
10843
|
-
# Agent Instructions
|
|
11035
|
+
This doesn't mean returning to traditional BDUF. Uncertainty about future requirements still limits what prediction can achieve. The insight is that the optimal amount of upfront work has shifted, not that prediction became reliable.
|
|
10844
11036
|
|
|
10845
|
-
|
|
11037
|
+
The model is hybrid: thorough AI-assisted exploration during ideas, followed by straightforward execution during tasks. "Lightweight" refers to task-level planning, not idea-level exploration. Invest heavily in understanding alternatives during the idea phase, then decompose into atomic tasks once the direction is clear.
|
|
10846
11038
|
|
|
10847
|
-
|
|
10848
|
-
|
|
10849
|
-
|
|
10850
|
-
|
|
10851
|
-
|
|
10852
|
-
|
|
10853
|
-
|
|
10854
|
-
|
|
10855
|
-
|
|
10856
|
-
|
|
10857
|
-
|
|
10858
|
-
|
|
10859
|
-
|
|
10860
|
-
|
|
10861
|
-
|
|
10862
|
-
|
|
10863
|
-
|
|
10864
|
-
|
|
10865
|
-
|
|
10866
|
-
|
|
10867
|
-
|
|
10868
|
-
|
|
10869
|
-
|
|
10870
|
-
|
|
10871
|
-
|
|
10872
|
-
|
|
10873
|
-
|
|
10874
|
-
|
|
10875
|
-
|
|
10876
|
-
|
|
10877
|
-
|
|
10878
|
-
|
|
10879
|
-
|
|
10880
|
-
|
|
10881
|
-
|
|
10882
|
-
|
|
10883
|
-
|
|
10884
|
-
|
|
10885
|
-
|
|
10886
|
-
|
|
10887
|
-
|
|
10888
|
-
|
|
10889
|
-
|
|
10890
|
-
|
|
10891
|
-
|
|
10892
|
-
|
|
10893
|
-
|
|
10894
|
-
|
|
10895
|
-
|
|
10896
|
-
|
|
10897
|
-
|
|
10898
|
-
|
|
10899
|
-
|
|
10900
|
-
|
|
10901
|
-
|
|
10902
|
-
|
|
10903
|
-
|
|
10904
|
-
|
|
10905
|
-
|
|
10906
|
-
|
|
10907
|
-
|
|
10908
|
-
|
|
10909
|
-
|
|
11039
|
+
## Convergence Criteria
|
|
11040
|
+
|
|
11041
|
+
Exploration should continue until clear trade-offs are identified and the chosen approach can be articulated against alternatives. This is convergence-based, not time-boxed — simple ideas converge quickly, complex architectural decisions require more exploration.
|
|
11042
|
+
|
|
11043
|
+
When exploration feels "done":
|
|
11044
|
+
|
|
11045
|
+
- Multiple approaches have been considered
|
|
11046
|
+
- Trade-offs between approaches are understood
|
|
11047
|
+
- The chosen direction has clear justification
|
|
11048
|
+
- Remaining uncertainty is about requirements, not design
|
|
11049
|
+
|
|
11050
|
+
If a task requires significant design decisions during execution, it wasn't ready to be a task.
|
|
11051
|
+
|
|
11052
|
+
## Documenting Alternatives
|
|
11053
|
+
|
|
11054
|
+
Ideas should document the alternatives considered and why they were ruled out. This creates a decision log that helps future agents and humans understand context. Include alternatives in the idea body or Open Questions sections.
|
|
11055
|
+
|
|
11056
|
+
## Parent Principle
|
|
11057
|
+
|
|
11058
|
+
- [Lightweight Planning](lightweight-planning.md)
|
|
11059
|
+
|
|
11060
|
+
## Sub-Principles
|
|
11061
|
+
|
|
11062
|
+
- (none)
|
|
11063
|
+
`
|
|
11064
|
+
},
|
|
11065
|
+
{
|
|
11066
|
+
slug: "design-for-testability",
|
|
11067
|
+
content: `# Design for Testability
|
|
11068
|
+
|
|
11069
|
+
Design code to be testable first; good structure follows naturally.
|
|
11070
|
+
|
|
11071
|
+
Testability should be a primary design driver, not a quality to be retrofitted. When code is designed to be testable from the start, it naturally becomes decoupled, explicit in its dependencies, and clear in its interfaces.
|
|
11072
|
+
|
|
11073
|
+
The discipline of testability forces good design: functions become pure, dependencies become explicit, side effects become isolated. Rather than viewing testability as a tax on production code, recognize it as a compass that points toward better architecture.
|
|
11074
|
+
|
|
11075
|
+
This is particularly important in agent-driven development. Agents cannot manually verify their changes—they rely entirely on tests. Code that resists testing resists autonomous modification.
|
|
11076
|
+
|
|
11077
|
+
## Parent Principle
|
|
11078
|
+
|
|
11079
|
+
- [Decoupled Code](decoupled-code.md)
|
|
11080
|
+
|
|
11081
|
+
## Sub-Principles
|
|
11082
|
+
|
|
11083
|
+
- (none)
|
|
11084
|
+
`
|
|
11085
|
+
},
|
|
11086
|
+
{
|
|
11087
|
+
slug: "readable-test-data",
|
|
11088
|
+
content: `# Readable Test Data
|
|
11089
|
+
|
|
11090
|
+
Test data setup should use natural structures that mirror what they represent.
|
|
11091
|
+
|
|
11092
|
+
## Why it matters
|
|
11093
|
+
|
|
11094
|
+
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.
|
|
11095
|
+
|
|
11096
|
+
## In practice
|
|
11097
|
+
|
|
11098
|
+
Prefer literal structures that visually match the domain:
|
|
11099
|
+
|
|
11100
|
+
\`\`\`javascript
|
|
11101
|
+
// Avoid: flat paths that obscure hierarchy
|
|
11102
|
+
const fs = createFileSystemEmulator({
|
|
11103
|
+
files: new Map([['/project/.dust/principles/my-goal.md', '# My Goal']]),
|
|
11104
|
+
existingPaths: new Set(['/project/.dust/ideas']),
|
|
11105
|
+
})
|
|
11106
|
+
|
|
11107
|
+
// Prefer: nested object that mirrors file system structure
|
|
11108
|
+
const fs = createFileSystemEmulator({
|
|
11109
|
+
project: {
|
|
11110
|
+
'.dust': {
|
|
11111
|
+
principles: {
|
|
11112
|
+
'my-goal.md': '# My Goal'
|
|
11113
|
+
},
|
|
11114
|
+
ideas: {}
|
|
10910
11115
|
}
|
|
10911
11116
|
}
|
|
10912
|
-
|
|
10913
|
-
|
|
10914
|
-
|
|
10915
|
-
|
|
10916
|
-
|
|
10917
|
-
|
|
10918
|
-
|
|
10919
|
-
|
|
10920
|
-
|
|
10921
|
-
|
|
10922
|
-
|
|
11117
|
+
})
|
|
11118
|
+
\`\`\`
|
|
11119
|
+
|
|
11120
|
+
The nested form:
|
|
11121
|
+
- Shows parent-child relationships through indentation
|
|
11122
|
+
- Makes empty directories explicit with empty objects
|
|
11123
|
+
- Requires no mental path concatenation to understand structure
|
|
11124
|
+
|
|
11125
|
+
## How to evaluate
|
|
11126
|
+
|
|
11127
|
+
Work supports this principle when test setup data uses structures that visually resemble what they represent, reducing cognitive load for readers.
|
|
11128
|
+
|
|
11129
|
+
## Parent Principle
|
|
11130
|
+
|
|
11131
|
+
- [Make Changes with Confidence](make-changes-with-confidence.md)
|
|
11132
|
+
|
|
11133
|
+
## Sub-Principles
|
|
11134
|
+
|
|
11135
|
+
- (none)
|
|
11136
|
+
`
|
|
11137
|
+
},
|
|
11138
|
+
{
|
|
11139
|
+
slug: "agent-specific-enhancement",
|
|
11140
|
+
content: `# Agent-Specific Enhancement
|
|
11141
|
+
|
|
11142
|
+
Dust should detect and enhance the experience for specific agents while remaining agnostic at its core.
|
|
11143
|
+
|
|
11144
|
+
While Dust has [Agent-Agnostic Design](agent-agnostic-design.md) and works with any capable agent, it can still optimize the "agent DX" (developer experience) when it detects a specific agent is being used. This means:
|
|
11145
|
+
|
|
11146
|
+
- **Detection** - Dust may detect which agent is running (e.g., Claude Code, Aider, Cursor) through environment variables, configuration, or other signals
|
|
11147
|
+
- **Enhancement** - Once detected, Dust can tailor its output format, prompts, or context to leverage that agent's specific strengths
|
|
11148
|
+
- **Graceful fallback** - When no specific agent is detected, Dust provides a generic experience that works with any agent
|
|
11149
|
+
|
|
11150
|
+
This principle complements Agent-Agnostic Design: the core functionality never requires a specific agent, but the experience improves when one is recognized.
|
|
11151
|
+
|
|
11152
|
+
## Applicability
|
|
11153
|
+
|
|
11154
|
+
Internal
|
|
11155
|
+
|
|
11156
|
+
## Parent Principle
|
|
11157
|
+
|
|
11158
|
+
- [Agent Autonomy](agent-autonomy.md)
|
|
11159
|
+
|
|
11160
|
+
## Sub-Principles
|
|
11161
|
+
|
|
11162
|
+
- (none)
|
|
11163
|
+
`
|
|
11164
|
+
},
|
|
11165
|
+
{
|
|
11166
|
+
slug: "context-optimised-code",
|
|
11167
|
+
content: `# Context-Optimised Code
|
|
11168
|
+
|
|
11169
|
+
Code should be structured so that agents can understand and modify it within their context window constraints.
|
|
11170
|
+
|
|
11171
|
+
Large files, deeply nested abstractions, and sprawling dependency chains all work against agents. A 3,000-line file cannot be fully loaded into context. A function that requires understanding six levels of indirection demands more context than one that is self-contained. Context-optimised code favours small files, shallow abstractions, explicit dependencies, and co-located related logic.
|
|
11172
|
+
|
|
11173
|
+
Dust should help projects identify files that are too large, modules that are too tangled, and patterns that make agent comprehension harder than it needs to be. This is not just about file size — it is about ensuring that the unit of code an agent needs to understand fits comfortably within the window available.
|
|
11174
|
+
|
|
11175
|
+
## Parent Principle
|
|
11176
|
+
|
|
11177
|
+
- [Ideal Agent Developer Experience](ideal-agent-developer-experience.md)
|
|
11178
|
+
|
|
11179
|
+
## Sub-Principles
|
|
11180
|
+
|
|
11181
|
+
- (none)
|
|
11182
|
+
`
|
|
11183
|
+
},
|
|
11184
|
+
{
|
|
11185
|
+
slug: "self-diagnosing-tests",
|
|
11186
|
+
content: `# Self-Diagnosing Tests
|
|
11187
|
+
|
|
11188
|
+
When a big test fails, it should be self-evident how to diagnose and fix the failure.
|
|
11189
|
+
|
|
11190
|
+
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.
|
|
11191
|
+
|
|
11192
|
+
## Anti-patterns
|
|
11193
|
+
|
|
11194
|
+
**Boolean flattening** — collapsing a rich value into true/false before asserting:
|
|
11195
|
+
\`\`\`javascript
|
|
11196
|
+
// Bad: "expected true, received false" — what events arrived?
|
|
11197
|
+
expect(events.some(e => e.type === 'check-passed')).toBe(true)
|
|
11198
|
+
|
|
11199
|
+
// Good: shows the actual event types on failure
|
|
11200
|
+
expect(events.map(e => e.type)).toContain('check-passed')
|
|
11201
|
+
\`\`\`
|
|
11202
|
+
|
|
11203
|
+
**Length-only assertions** — checking count without showing contents:
|
|
11204
|
+
\`\`\`javascript
|
|
11205
|
+
// Bad: "expected 2, received 0" — what requests were captured?
|
|
11206
|
+
expect(requests.length).toBe(2)
|
|
11207
|
+
|
|
11208
|
+
// Good: shows the actual requests on failure
|
|
11209
|
+
expect(requests).toHaveLength(2) // vitest shows the array
|
|
11210
|
+
\`\`\`
|
|
11211
|
+
|
|
11212
|
+
**Silent guards** — using \`if\` where an assertion belongs:
|
|
11213
|
+
\`\`\`javascript
|
|
11214
|
+
// Bad: silently passes when settings is undefined
|
|
11215
|
+
if (settings) {
|
|
11216
|
+
expect(JSON.parse(settings).key).toBeDefined()
|
|
10923
11217
|
}
|
|
10924
11218
|
|
|
10925
|
-
//
|
|
10926
|
-
|
|
11219
|
+
// Good: fails explicitly if settings is missing
|
|
11220
|
+
expect(settings).toBeDefined()
|
|
11221
|
+
const parsed = JSON.parse(settings!)
|
|
11222
|
+
expect(parsed.key).toBeDefined()
|
|
11223
|
+
\`\`\`
|
|
11224
|
+
|
|
11225
|
+
## The test
|
|
11226
|
+
|
|
11227
|
+
If a test fails, can a developer who has never seen the code identify the problem from the failure output alone — without re-running, adding console.logs, or reading the test source? The closer to "yes", the better.
|
|
11228
|
+
|
|
11229
|
+
## How to evaluate
|
|
11230
|
+
|
|
11231
|
+
Work supports this principle when every assertion in a system or integration test would, on failure, reveal the actual state richly enough to guide a fix. Bare boolean checks, length-only assertions, and silent conditional guards are violations.
|
|
11232
|
+
|
|
11233
|
+
## Parent Principle
|
|
11234
|
+
|
|
11235
|
+
- [Make Changes with Confidence](make-changes-with-confidence.md)
|
|
11236
|
+
|
|
11237
|
+
## Sub-Principles
|
|
11238
|
+
|
|
11239
|
+
- (none)
|
|
11240
|
+
`
|
|
11241
|
+
},
|
|
11242
|
+
{
|
|
11243
|
+
slug: "ideal-agent-developer-experience",
|
|
11244
|
+
content: `# Ideal Agent Developer Experience
|
|
11245
|
+
|
|
11246
|
+
The agent is the developer. The human is the CEO. Dust is the PM.
|
|
11247
|
+
|
|
11248
|
+
With today's AI coding assistants, the human is stuck in a tight loop with agents — constantly directing, reviewing, and course-correcting. Dust is designed to relieve humans from this tight loop. Like an assistant to a CEO, dust predominantly brings fully-researched questions and well-prepared work to the human, rather than expecting the human to drive every decision. The human checks in less frequently, and when they do, they make high-leverage strategic calls rather than micromanaging implementation.
|
|
11249
|
+
|
|
11250
|
+
For this to work, the agent's development environment must be excellent. The agent reads the code, writes changes, runs the checks, and iterates until the task is done. Everything about the codebase and its tooling either helps or hinders that process. Comprehensive tests are the agent's only way to verify correctness. Fast feedback loops are the agent's iteration speed. Structured logs are the agent's eyes into runtime behaviour. Small, well-organised files are what fit in the agent's context window. Exploratory and debugging tools are how the agent navigates and diagnoses without trial and error.
|
|
11251
|
+
|
|
11252
|
+
Each sub-principle represents a different aspect of the ideal agent developer setup. The better these are, the less the human needs to be in the loop.
|
|
11253
|
+
|
|
11254
|
+
## Parent Principle
|
|
11255
|
+
|
|
11256
|
+
- [Human-AI Collaboration](human-ai-collaboration.md)
|
|
11257
|
+
|
|
11258
|
+
## Sub-Principles
|
|
11259
|
+
|
|
11260
|
+
- [Comprehensive Test Coverage](comprehensive-test-coverage.md)
|
|
11261
|
+
- [Fast Feedback Loops](fast-feedback-loops.md)
|
|
11262
|
+
- [Slow Feedback Coping](slow-feedback-coping.md)
|
|
11263
|
+
- [Development Traceability](development-traceability.md)
|
|
11264
|
+
- [Context-Optimised Code](context-optimised-code.md)
|
|
11265
|
+
- [Exploratory Tooling](exploratory-tooling.md)
|
|
11266
|
+
- [Debugging Tooling](debugging-tooling.md)
|
|
11267
|
+
- [Self-Contained Repository](self-contained-repository.md)
|
|
11268
|
+
`
|
|
11269
|
+
},
|
|
11270
|
+
{
|
|
11271
|
+
slug: "broken-windows",
|
|
11272
|
+
content: `# Broken Windows
|
|
11273
|
+
|
|
11274
|
+
Don't leave broken windows unrepaired.
|
|
11275
|
+
|
|
11276
|
+
A broken window — a bad name, a hack, a TODO that lingers, a test that's been skipped — signals that nobody cares. That signal invites more neglect. One shortcut becomes two, then ten, and the codebase quietly rots from the inside.
|
|
11277
|
+
|
|
11278
|
+
When you spot a broken window, fix it immediately if the fix is small. If it's too large, capture it as a task so it doesn't get forgotten. The key is to never normalise the damage. Even a comment acknowledging the problem ("this needs fixing because...") is better than silent acceptance.
|
|
11279
|
+
|
|
11280
|
+
This principle complements the [Boy Scout Rule](boy-scout-rule.md): the Boy Scout Rule encourages proactive improvement, while Broken Windows warns against tolerating known problems. Together they keep entropy at bay.
|
|
11281
|
+
|
|
11282
|
+
## Parent Principle
|
|
11283
|
+
|
|
11284
|
+
- [Maintainable Codebase](maintainable-codebase.md)
|
|
11285
|
+
|
|
11286
|
+
## Sub-Principles
|
|
11287
|
+
|
|
11288
|
+
- (none)
|
|
11289
|
+
`
|
|
11290
|
+
},
|
|
11291
|
+
{
|
|
11292
|
+
slug: "progressive-disclosure",
|
|
11293
|
+
content: `# Progressive Disclosure
|
|
11294
|
+
|
|
11295
|
+
Dust should reveal details progressively as a way of achieving context window efficiency.
|
|
11296
|
+
|
|
11297
|
+
Not all information is needed at once. A task list showing just titles is sufficient for choosing what to work on. Full task details are only needed when actively implementing. Linked principles and facts can be followed when deeper context is required.
|
|
11298
|
+
|
|
11299
|
+
This layered approach keeps initial reads lightweight while preserving access to complete information when needed.
|
|
11300
|
+
|
|
11301
|
+
## Parent Principle
|
|
11302
|
+
|
|
11303
|
+
- [Context Window Efficiency](context-window-efficiency.md)
|
|
11304
|
+
|
|
11305
|
+
## Sub-Principles
|
|
11306
|
+
|
|
11307
|
+
- (none)
|
|
11308
|
+
`
|
|
11309
|
+
},
|
|
11310
|
+
{
|
|
11311
|
+
slug: "lightweight-planning",
|
|
11312
|
+
content: `# Lightweight Planning
|
|
11313
|
+
|
|
11314
|
+
Dust aims to be a minimal, low-overhead planning system that stays relevant over time.
|
|
11315
|
+
|
|
11316
|
+
Planning artifacts are simple markdown files that live alongside code. Ideas are intentionally vague until implementation is imminent. Tasks are small and completable in single commits. Facts document current reality rather than aspirational states.
|
|
11317
|
+
|
|
11318
|
+
The system avoids the staleness problem by deferring detail until the last responsible moment and deleting completed work rather than archiving it.
|
|
11319
|
+
|
|
11320
|
+
## Parent Principle
|
|
11321
|
+
|
|
11322
|
+
- [Human-AI Collaboration](human-ai-collaboration.md)
|
|
11323
|
+
|
|
11324
|
+
## Sub-Principles
|
|
11325
|
+
|
|
11326
|
+
- [Task-First Workflow](task-first-workflow.md)
|
|
11327
|
+
- [Some Big Design Up Front](some-big-design-up-front.md)
|
|
11328
|
+
`
|
|
11329
|
+
},
|
|
11330
|
+
{
|
|
11331
|
+
slug: "comprehensive-test-coverage",
|
|
11332
|
+
content: `# Comprehensive Test Coverage
|
|
11333
|
+
|
|
11334
|
+
A project's test suite is its primary safety net, and agents depend on it even more than humans do.
|
|
11335
|
+
|
|
11336
|
+
Agents cannot manually verify that their changes work. They rely entirely on automated tests to confirm correctness. Gaps in test coverage become gaps in agent capability — areas where changes are risky and feedback is absent. Comprehensive coverage means every meaningful behaviour is tested, so agents can make changes anywhere in the codebase with confidence.
|
|
11337
|
+
|
|
11338
|
+
Dust should help projects measure and improve their test coverage, flag untested areas, and encourage a culture where new code comes with new tests.
|
|
11339
|
+
|
|
11340
|
+
## Parent Principle
|
|
11341
|
+
|
|
11342
|
+
- [Ideal Agent Developer Experience](ideal-agent-developer-experience.md)
|
|
11343
|
+
|
|
11344
|
+
## Sub-Principles
|
|
11345
|
+
|
|
11346
|
+
- (none)
|
|
11347
|
+
`
|
|
11348
|
+
},
|
|
11349
|
+
{
|
|
11350
|
+
slug: "intuitive-directory-structure",
|
|
11351
|
+
content: `# Intuitive Directory Structure
|
|
11352
|
+
|
|
11353
|
+
Code should be organized around related concerns in clearly named directories.
|
|
11354
|
+
|
|
11355
|
+
When files that serve similar purposes are grouped together, the codebase becomes easier to navigate and understand. A developer looking for "commands" should find them in a \`commands\` directory. Utilities should live with utilities. This organization reduces cognitive load and makes the project structure self-documenting.
|
|
11356
|
+
|
|
11357
|
+
## Parent Principle
|
|
11358
|
+
|
|
11359
|
+
- [Maintainable Codebase](maintainable-codebase.md)
|
|
11360
|
+
|
|
11361
|
+
## Sub-Principles
|
|
11362
|
+
|
|
11363
|
+
- [Co-located Tests](co-located-tests.md)
|
|
11364
|
+
`
|
|
11365
|
+
},
|
|
11366
|
+
{
|
|
11367
|
+
slug: "small-units",
|
|
11368
|
+
content: `# Small Units
|
|
11369
|
+
|
|
11370
|
+
Ideas, principles, facts, and tasks should each be as discrete and fine-grained as possible.
|
|
11371
|
+
|
|
11372
|
+
Small, focused documents enable precise relationships between them. A task can link to exactly the principles it serves. A fact can describe one specific aspect of the system. This granularity reduces ambiguity.
|
|
11373
|
+
|
|
11374
|
+
Tasks especially benefit from being small. A narrowly scoped task gives agents or humans the best chance of delivering exactly what was intended, in a single atomic commit.
|
|
11375
|
+
|
|
11376
|
+
Note: This principle directly supports [Lightweight Planning](lightweight-planning.md), which explicitly mentions that "Tasks are small and completable in single commits."
|
|
11377
|
+
|
|
11378
|
+
## Parent Principle
|
|
11379
|
+
|
|
11380
|
+
- [Agent Autonomy](agent-autonomy.md)
|
|
11381
|
+
|
|
11382
|
+
## Sub-Principles
|
|
11383
|
+
|
|
11384
|
+
- (none)
|
|
11385
|
+
`
|
|
11386
|
+
},
|
|
11387
|
+
{
|
|
11388
|
+
slug: "fast-feedback",
|
|
11389
|
+
content: `# Fast Feedback
|
|
11390
|
+
|
|
11391
|
+
Dust should provide fast feedback loops for developers.
|
|
11392
|
+
|
|
11393
|
+
Scripts and tooling should execute quickly so developers can iterate rapidly. Slow feedback discourages frequent validation and leads to larger, riskier changes. Fast feedback enables small, confident steps.
|
|
11394
|
+
|
|
11395
|
+
## Parent Principle
|
|
11396
|
+
|
|
11397
|
+
- [Make Changes with Confidence](make-changes-with-confidence.md)
|
|
11398
|
+
|
|
11399
|
+
## Sub-Principles
|
|
11400
|
+
|
|
11401
|
+
- (none)
|
|
11402
|
+
`
|
|
11403
|
+
},
|
|
11404
|
+
{
|
|
11405
|
+
slug: "dependency-injection",
|
|
11406
|
+
content: `# Dependency Injection
|
|
11407
|
+
|
|
11408
|
+
Avoid global mocks. Dependency injection is almost always preferable to testing code that depends directly on globals.
|
|
11409
|
+
|
|
11410
|
+
When code depends on global state or singletons, testing requires mocking those globals—which introduces hidden coupling, complicates test setup, and risks interference between tests. Dependency injection makes dependencies explicit: they're passed in as arguments, making the code's requirements visible and enabling tests to supply controlled implementations.
|
|
11411
|
+
|
|
11412
|
+
This approach improves testability (each test controls its own dependencies), readability (dependencies are declared upfront), and flexibility (swapping implementations doesn't require changing the consuming code). It also makes refactoring safer since dependencies are explicit rather than implicit.
|
|
11413
|
+
|
|
11414
|
+
## Parent Principle
|
|
11415
|
+
|
|
11416
|
+
- [Decoupled Code](decoupled-code.md)
|
|
11417
|
+
|
|
11418
|
+
## Sub-Principles
|
|
11419
|
+
|
|
11420
|
+
- (none)
|
|
11421
|
+
`
|
|
11422
|
+
},
|
|
11423
|
+
{
|
|
11424
|
+
slug: "reproducible-checks",
|
|
11425
|
+
content: `# Reproducible Checks
|
|
11426
|
+
|
|
11427
|
+
Every check must produce the same result regardless of who runs it, when, or on what machine. If a check passes for one developer but fails for another, the check is broken.
|
|
11428
|
+
|
|
11429
|
+
Concretely, checks should pin their tool versions via the project's dependency manager (e.g. \`devDependencies\`) rather than relying on \`npx\`/\`bunx\` to fetch the latest version at runtime. Unpinned versions introduce non-determinism — a check that passed yesterday may fail today due to a tool upgrade that nobody chose to adopt.
|
|
11430
|
+
|
|
11431
|
+
## Parent Principle
|
|
11432
|
+
|
|
11433
|
+
- [Make Changes with Confidence](make-changes-with-confidence.md)
|
|
11434
|
+
|
|
11435
|
+
## Sub-Principles
|
|
11436
|
+
|
|
11437
|
+
- (none)
|
|
11438
|
+
`
|
|
11439
|
+
},
|
|
11440
|
+
{
|
|
11441
|
+
slug: "slow-feedback-coping",
|
|
11442
|
+
content: `# Slow Feedback Coping
|
|
11443
|
+
|
|
11444
|
+
Some feedback is unavoidably slow — dust should offer coping strategies rather than pretending it can be eliminated.
|
|
11445
|
+
|
|
11446
|
+
Integration tests, end-to-end tests, deployment pipelines, and external API calls all take time. Pretending they can be made instant is unrealistic. Instead, dust should help developers and agents cope with slow feedback effectively: by structuring work so that fast checks catch most problems early, by batching slow checks intelligently, by providing clear progress indicators, and by ensuring that when slow feedback does arrive, it is actionable and specific.
|
|
11447
|
+
|
|
11448
|
+
Strategies include separating fast and slow test suites, running slow checks asynchronously or in CI, caching expensive operations, and designing workflows that minimise how often slow feedback is needed.
|
|
11449
|
+
|
|
11450
|
+
## Parent Principle
|
|
11451
|
+
|
|
11452
|
+
- [Ideal Agent Developer Experience](ideal-agent-developer-experience.md)
|
|
11453
|
+
|
|
11454
|
+
## Sub-Principles
|
|
11455
|
+
|
|
11456
|
+
- (none)
|
|
11457
|
+
`
|
|
11458
|
+
},
|
|
11459
|
+
{
|
|
11460
|
+
slug: "make-changes-with-confidence",
|
|
11461
|
+
content: `# Make Changes with Confidence
|
|
11462
|
+
|
|
11463
|
+
Developers should be able to modify code without fear of breaking existing behavior.
|
|
11464
|
+
|
|
11465
|
+
Tests, type checking, and other automated verification enable safe refactoring and evolution of the codebase. When changes break something, fast feedback identifies the problem before it spreads. This confidence encourages continuous improvement rather than fragile, stagnant code.
|
|
11466
|
+
|
|
11467
|
+
## Parent Principle
|
|
11468
|
+
|
|
11469
|
+
- [Maintainable Codebase](maintainable-codebase.md)
|
|
11470
|
+
|
|
11471
|
+
## Sub-Principles
|
|
11472
|
+
|
|
11473
|
+
- [Comprehensive Assertions](comprehensive-assertions.md)
|
|
11474
|
+
- [Decoupled Code](decoupled-code.md)
|
|
11475
|
+
- [Fast Feedback](fast-feedback.md)
|
|
11476
|
+
- [Lint Everything](lint-everything.md)
|
|
11477
|
+
- [Readable Test Data](readable-test-data.md)
|
|
11478
|
+
- [Reproducible Checks](reproducible-checks.md)
|
|
11479
|
+
- [Stop the Line](stop-the-line.md)
|
|
11480
|
+
- [Keep Unit Tests Pure](keep-unit-tests-pure.md)
|
|
11481
|
+
- [Test Isolation](test-isolation.md)
|
|
11482
|
+
- [Self-Diagnosing Tests](self-diagnosing-tests.md)
|
|
11483
|
+
- [Unit Test Coverage](unit-test-coverage.md)
|
|
11484
|
+
`
|
|
11485
|
+
},
|
|
11486
|
+
{
|
|
11487
|
+
slug: "test-isolation",
|
|
11488
|
+
content: `# Test Isolation
|
|
11489
|
+
|
|
11490
|
+
Tests should not interfere with one another. Each test must be independently runnable and produce the same result regardless of execution order or which other tests run alongside it.
|
|
11491
|
+
|
|
11492
|
+
This means:
|
|
11493
|
+
- No shared mutable state between tests
|
|
11494
|
+
- No reliance on test execution order
|
|
11495
|
+
- No file system or environment pollution
|
|
11496
|
+
- Each test sets up its own dependencies
|
|
11497
|
+
|
|
11498
|
+
Test isolation enables parallel execution, makes failures easier to diagnose, and prevents cascading false failures when one test breaks.
|
|
11499
|
+
|
|
11500
|
+
## Parent Principle
|
|
11501
|
+
|
|
11502
|
+
- [Make Changes with Confidence](make-changes-with-confidence.md)
|
|
11503
|
+
|
|
11504
|
+
## Sub-Principles
|
|
11505
|
+
|
|
11506
|
+
- [Environment-Independent Tests](environment-independent-tests.md)
|
|
11507
|
+
`
|
|
11508
|
+
},
|
|
11509
|
+
{
|
|
11510
|
+
slug: "repository-hygiene",
|
|
11511
|
+
content: `# Repository Hygiene
|
|
11512
|
+
|
|
11513
|
+
Dust repositories should maintain a clean, organized state with minimal noise.
|
|
11514
|
+
|
|
11515
|
+
This includes proper gitignore configuration to exclude build artifacts, dependencies, editor files, and other generated content from version control. A well-maintained repository makes it easier for both humans and AI to navigate and understand the codebase.
|
|
11516
|
+
|
|
11517
|
+
## Parent Principle
|
|
11518
|
+
|
|
11519
|
+
- [Maintainable Codebase](maintainable-codebase.md)
|
|
11520
|
+
|
|
11521
|
+
## Sub-Principles
|
|
11522
|
+
|
|
11523
|
+
- [Atomic Commits](atomic-commits.md)
|
|
11524
|
+
- [Trunk-Based Development](trunk-based-development.md)
|
|
11525
|
+
`
|
|
11526
|
+
},
|
|
11527
|
+
{
|
|
11528
|
+
slug: "agentic-flow-state",
|
|
11529
|
+
content: `# Agentic Flow State
|
|
11530
|
+
|
|
11531
|
+
Flow is the mental state where work becomes effortless - where you're fully immersed, losing track of time, operating at peak performance. Psychologist Mihaly Csikszentmihalyi identified three conditions that create flow: clear goals, immediate feedback, and challenge-skill balance.
|
|
11532
|
+
|
|
11533
|
+
For AI agents, achieving flow state means staying engaged and productive without interruption. Agents enter flow when they have optimal context, comprehensive guard rails, and minimal friction. Context window optimization ensures agents have exactly what they need without cognitive overload. In-session guard rails prevent agents from straying off course or making mistakes that break their momentum.
|
|
11534
|
+
|
|
11535
|
+
Dust's design targets these conditions directly:
|
|
11536
|
+
|
|
11537
|
+
- **Clear goals**: Task files and lightweight planning give you a concrete target. You know exactly what you're building next.
|
|
11538
|
+
- **Immediate feedback**: Fast feedback loops let you see results quickly. Each change confirms you're on track or shows you what to adjust.
|
|
11539
|
+
- **Challenge-skill balance**: Small units of work and agent autonomy keep you in the zone - challenged enough to stay engaged, supported enough to succeed.
|
|
11540
|
+
- **Context window efficiency**: Progressive disclosure and artifact summarization ensure agents have the right context without overflow.
|
|
11541
|
+
- **Comprehensive guard rails**: Lint rules, type checks, and automated validation catch mistakes before they compound.
|
|
11542
|
+
|
|
11543
|
+
Everything dust does serves flow. When agents stay in flow, they produce better work, sustain their momentum, and complete tasks autonomously.
|
|
11544
|
+
|
|
11545
|
+
## Parent Principle
|
|
11546
|
+
|
|
11547
|
+
- (none)
|
|
11548
|
+
|
|
11549
|
+
## Sub-Principles
|
|
11550
|
+
|
|
11551
|
+
- [Human-AI Collaboration](human-ai-collaboration.md)
|
|
11552
|
+
- [Maintainable Codebase](maintainable-codebase.md)
|
|
11553
|
+
`
|
|
11554
|
+
},
|
|
11555
|
+
{
|
|
11556
|
+
slug: "stop-the-line",
|
|
11557
|
+
content: `# Stop the Line
|
|
11558
|
+
|
|
11559
|
+
Any worker — human or agent — should halt and fix a problem the moment they detect it, rather than letting defects propagate downstream.
|
|
11560
|
+
|
|
11561
|
+
Originating from the Toyota production system, "Stop the Line" empowers every participant to pause work immediately upon identifying a defect, failing check, or safety hazard. Problems are cheaper to fix at their source than after they've compounded through later stages. In the context of dust, this means agents and humans alike should treat broken checks, test failures, and lint errors as blockers that demand immediate attention — not warnings to be deferred.
|
|
11562
|
+
|
|
11563
|
+
## Parent Principle
|
|
11564
|
+
|
|
11565
|
+
- [Make Changes with Confidence](make-changes-with-confidence.md)
|
|
11566
|
+
|
|
11567
|
+
## Sub-Principles
|
|
11568
|
+
|
|
11569
|
+
- (none)
|
|
11570
|
+
`
|
|
11571
|
+
},
|
|
11572
|
+
{
|
|
11573
|
+
slug: "agent-context-inference",
|
|
11574
|
+
content: `# Agent Context Inference
|
|
11575
|
+
|
|
11576
|
+
Terse human prompts should trigger the correct agent action.
|
|
11577
|
+
|
|
11578
|
+
When a human gives a brief instruction like "the button should be green", the agent should be able to infer what to do. The agent shouldn't require the human to specify file paths, component names, or implementation details that can be discovered from the repository.
|
|
11579
|
+
|
|
11580
|
+
This reduces friction for humans and makes agent interactions feel more natural. The burden of context discovery shifts to the agent, which can use dust's CLI and repository structure to find what it needs.
|
|
11581
|
+
|
|
11582
|
+
## Applicability
|
|
11583
|
+
|
|
11584
|
+
Internal
|
|
11585
|
+
|
|
11586
|
+
## Parent Principle
|
|
11587
|
+
|
|
11588
|
+
- [Agent Autonomy](agent-autonomy.md)
|
|
11589
|
+
|
|
11590
|
+
## Sub-Principles
|
|
11591
|
+
|
|
11592
|
+
- (none)
|
|
11593
|
+
`
|
|
11594
|
+
},
|
|
11595
|
+
{
|
|
11596
|
+
slug: "naming-matters",
|
|
11597
|
+
content: `# Naming Matters
|
|
11598
|
+
|
|
11599
|
+
Good naming reduces waste by eliminating confusion and making code self-documenting.
|
|
11600
|
+
|
|
11601
|
+
Poor names cause rework, bugs, and communication overhead. When names don't clearly convey meaning, developers waste time deciphering code, misunderstand intentions, and introduce defects. Well-chosen names serve as documentation that never goes stale, reducing the need for explanatory comments and enabling both humans and AI agents to navigate the codebase efficiently.
|
|
11602
|
+
|
|
11603
|
+
## Parent Principle
|
|
11604
|
+
|
|
11605
|
+
- [Maintainable Codebase](maintainable-codebase.md)
|
|
11606
|
+
|
|
11607
|
+
## Sub-Principles
|
|
11608
|
+
|
|
11609
|
+
- [Consistent Naming](consistent-naming.md)
|
|
11610
|
+
- [Clarity Over Brevity](clarity-over-brevity.md)
|
|
11611
|
+
`
|
|
11612
|
+
},
|
|
11613
|
+
{
|
|
11614
|
+
slug: "stubs-over-mocks",
|
|
11615
|
+
content: `# Stubs Over Mocks
|
|
11616
|
+
|
|
11617
|
+
Prefer hand-rolled stubs over mocks, in unit tests. Stubs keep tests focused on observable behavior instead of implementation details.
|
|
11618
|
+
|
|
11619
|
+
Mocks tend to encode a script of “expected calls” (what was invoked, in what order, with what arguments). That makes tests brittle: harmless refactors (changing internal decomposition, adding caching, batching calls, reordering operations) can break tests even when the externally visible behavior is unchanged. You end up maintaining tests that police how the code works rather than what it does.
|
|
11620
|
+
|
|
11621
|
+
Stubs (and especially in-memory emulators) push tests toward the contract: provide inputs, run the code, assert outputs and side effects. When a test fails, it’s usually because a behavior changed, not because the internal call choreography shifted. That improves signal-to-noise, reduces rewrites during refactors, and makes it easier to evolve the implementation.
|
|
11622
|
+
|
|
11623
|
+
For external dependencies (databases, queues, object stores, HTTP services), the default choice should be an in-memory emulator: a drop-in replacement that is faithful enough to the real interface/semantics but runs entirely in-process. It gives most of the benefits of integration testing—realistic state transitions, error modes, concurrency behavior where relevant—without the cost, flakiness, and setup burden of booting real infrastructure. It also keeps the test environment hermetic (no network, no shared state), which improves determinism and makes tests fast.
|
|
11624
|
+
|
|
11625
|
+
Still use mocks selectively—mainly to assert something is called (e.g., telemetry emission, "at most once" notifications, payment capture guarded by a feature flag) or when a dependency is impossible to emulate. But for most cases, stubs and in-memory emulators produce tests that are clearer, more resilient to refactoring, and better aligned with the system's actual contracts.
|
|
11626
|
+
|
|
11627
|
+
## Parent Principle
|
|
11628
|
+
|
|
11629
|
+
- [Decoupled Code](decoupled-code.md)
|
|
11630
|
+
|
|
11631
|
+
## Sub-Principles
|
|
11632
|
+
|
|
11633
|
+
- (none)
|
|
11634
|
+
`
|
|
11635
|
+
},
|
|
11636
|
+
{
|
|
11637
|
+
slug: "functional-core-imperative-shell",
|
|
11638
|
+
content: `# Functional Core, Imperative Shell
|
|
11639
|
+
|
|
11640
|
+
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.
|
|
11641
|
+
|
|
11642
|
+
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.
|
|
11643
|
+
|
|
11644
|
+
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.
|
|
11645
|
+
|
|
11646
|
+
## Parent Principle
|
|
11647
|
+
|
|
11648
|
+
- [Decoupled Code](decoupled-code.md)
|
|
11649
|
+
|
|
11650
|
+
## Sub-Principles
|
|
11651
|
+
|
|
11652
|
+
- (none)
|
|
11653
|
+
`
|
|
11654
|
+
},
|
|
11655
|
+
{
|
|
11656
|
+
slug: "development-traceability",
|
|
11657
|
+
content: `# Development Traceability
|
|
11658
|
+
|
|
11659
|
+
Structured logging and tracing help agents understand system behaviour without resorting to ad-hoc testing cycles.
|
|
11660
|
+
|
|
11661
|
+
When something goes wrong, agents often resort to adding temporary log statements, running the code, reading the output, and repeating — a slow and wasteful debugging loop. Good traceability means the system already records what happened and why, through structured logs, trace IDs, and observable state. This lets agents diagnose issues by reading existing output rather than generating new experiments.
|
|
11662
|
+
|
|
11663
|
+
Dust should encourage projects to adopt structured logging, promote traceability as a first-class concern, and provide tools that surface relevant trace information when agents need it.
|
|
11664
|
+
|
|
11665
|
+
## Applicability
|
|
11666
|
+
|
|
11667
|
+
Internal
|
|
11668
|
+
|
|
11669
|
+
## Parent Principle
|
|
11670
|
+
|
|
11671
|
+
- [Ideal Agent Developer Experience](ideal-agent-developer-experience.md)
|
|
11672
|
+
|
|
11673
|
+
## Sub-Principles
|
|
11674
|
+
|
|
11675
|
+
- (none)
|
|
11676
|
+
`
|
|
11677
|
+
},
|
|
11678
|
+
{
|
|
11679
|
+
slug: "keep-unit-tests-pure",
|
|
11680
|
+
content: `# Keep Unit Tests Pure
|
|
11681
|
+
|
|
11682
|
+
Unit tests (those run very frequently as part of a tight feedback loop) should be pure and side-effect free. A test is **not** a unit test if it:
|
|
11683
|
+
|
|
11684
|
+
- Accesses a database
|
|
11685
|
+
- Communicates over a network
|
|
11686
|
+
- Touches the file system
|
|
11687
|
+
- Cannot run concurrently with other tests
|
|
11688
|
+
- Requires special environment setup
|
|
11689
|
+
|
|
11690
|
+
"Unit tests" here means tests run frequently during development — not system tests, which intentionally exercise the full stack including I/O. Pure unit tests exercise only business logic, not infrastructure.
|
|
11691
|
+
|
|
11692
|
+
The value of pure unit tests is that they are fast, deterministic, and isolate business logic from infrastructure concerns. When unit tests pass but integration or system tests fail, developers can immediately narrow the problem to the boundary layer — a diagnostic "binary chop" that accelerates debugging.
|
|
11693
|
+
|
|
11694
|
+
## Migration Guidance
|
|
11695
|
+
|
|
11696
|
+
Where existing tests are impure (e.g. they spawn processes, write temporary files, or make network calls), prefer converting them to use in-memory alternatives — stubs, fakes, or dependency-injected doubles — rather than leaving them as-is. Opportunistic migration is fine; a big-bang rewrite is not required.
|
|
11697
|
+
|
|
11698
|
+
## Parent Principle
|
|
11699
|
+
|
|
11700
|
+
- [Make Changes with Confidence](make-changes-with-confidence.md)
|
|
11701
|
+
|
|
11702
|
+
## Sub-Principles
|
|
11703
|
+
|
|
11704
|
+
- (none)
|
|
11705
|
+
`
|
|
11706
|
+
},
|
|
11707
|
+
{
|
|
11708
|
+
slug: "co-located-tests",
|
|
11709
|
+
content: `# Co-located Tests
|
|
11710
|
+
|
|
11711
|
+
Test files should live next to the code they test.
|
|
11712
|
+
|
|
11713
|
+
When tests are co-located with their source files, developers can immediately see what's tested and what isn't. Finding the test for a module becomes trivial—it's right there in the same directory. This proximity encourages writing tests as part of the development flow rather than as an afterthought, and makes it natural to update tests when modifying code.
|
|
11714
|
+
|
|
11715
|
+
## Parent Principle
|
|
11716
|
+
|
|
11717
|
+
- [Intuitive Directory Structure](intuitive-directory-structure.md)
|
|
11718
|
+
|
|
11719
|
+
## Sub-Principles
|
|
11720
|
+
|
|
11721
|
+
- (none)
|
|
11722
|
+
`
|
|
11723
|
+
},
|
|
11724
|
+
{
|
|
11725
|
+
slug: "human-ai-collaboration",
|
|
11726
|
+
content: `# Human-AI Collaboration
|
|
11727
|
+
|
|
11728
|
+
Dust exists to enable effective collaboration between humans and AI agents on complex projects.
|
|
11729
|
+
|
|
11730
|
+
The human is the CEO — they set direction, make strategic decisions, and check in when it matters. Dust is the PM — it manages the work, prepares context, and brings fully-researched questions to the human rather than expecting them to drive every detail. Agents are the developers — they read code, write changes, and iterate autonomously.
|
|
11731
|
+
|
|
11732
|
+
Today's AI coding tools keep humans in a tight loop with agents. Dust is designed to loosen that loop, so humans spend less time directing and more time deciding.
|
|
11733
|
+
|
|
11734
|
+
## Parent Principle
|
|
11735
|
+
|
|
11736
|
+
- [Agentic Flow State](agentic-flow-state.md)
|
|
11737
|
+
|
|
11738
|
+
## Sub-Principles
|
|
11739
|
+
|
|
11740
|
+
- [Agent Autonomy](agent-autonomy.md)
|
|
11741
|
+
- [Easy Adoption](easy-adoption.md)
|
|
11742
|
+
- [Ideal Agent Developer Experience](ideal-agent-developer-experience.md)
|
|
11743
|
+
- [Lightweight Planning](lightweight-planning.md)
|
|
11744
|
+
`
|
|
11745
|
+
},
|
|
11746
|
+
{
|
|
11747
|
+
slug: "vcs-independence",
|
|
11748
|
+
content: `# VCS Independence
|
|
11749
|
+
|
|
11750
|
+
Dust should work independently of any specific version control system.
|
|
11751
|
+
|
|
11752
|
+
While git is common, dust's core functionality should not require git. This enables use in repositories using other VCS (Mercurial, SVN, Perforce) or in non-VCS workflows.
|
|
11753
|
+
|
|
11754
|
+
## Parent Principle
|
|
11755
|
+
|
|
11756
|
+
- [Easy Adoption](easy-adoption.md)
|
|
11757
|
+
|
|
11758
|
+
## Sub-Principles
|
|
11759
|
+
|
|
11760
|
+
- (none)
|
|
11761
|
+
`
|
|
11762
|
+
},
|
|
11763
|
+
{
|
|
11764
|
+
slug: "environment-independent-tests",
|
|
11765
|
+
content: `# Environment-Independent Tests
|
|
11766
|
+
|
|
11767
|
+
Tests must produce the same result regardless of where they run. A test that passes locally but fails in CI (or vice versa) is a broken test.
|
|
11768
|
+
|
|
11769
|
+
Concretely, tests should never depend on:
|
|
11770
|
+
- Ambient environment variables (e.g. \`CLAUDECODE\`, \`CI\`, \`HOME\`)
|
|
11771
|
+
- The current working directory or filesystem layout of the host machine
|
|
11772
|
+
- Network availability or external services
|
|
11773
|
+
- The identity of the user or agent running the tests
|
|
11774
|
+
|
|
11775
|
+
When a function's behavior depends on environment variables, the test must explicitly control those variables (via \`stubEnv\`, dependency injection, or passing an \`env\` parameter) rather than relying on whatever happens to be set in the current shell.
|
|
11776
|
+
|
|
11777
|
+
## Parent Principle
|
|
11778
|
+
|
|
11779
|
+
- [Test Isolation](test-isolation.md)
|
|
11780
|
+
|
|
11781
|
+
## Sub-Principles
|
|
11782
|
+
|
|
11783
|
+
- (none)
|
|
11784
|
+
`
|
|
11785
|
+
},
|
|
11786
|
+
{
|
|
11787
|
+
slug: "debugging-tooling",
|
|
11788
|
+
content: `# Debugging Tooling
|
|
11789
|
+
|
|
11790
|
+
Agents need effective tools for diagnosing and fixing issues without manual intervention.
|
|
11791
|
+
|
|
11792
|
+
Traditional debugging relies on breakpoints, stepping through code, and interactive inspection — techniques that work well for humans but poorly for agents. Agents need debugging tools that produce readable, structured output: stack traces with context, diff-friendly error messages, reproducible test cases, and diagnostic commands that can be run non-interactively.
|
|
11793
|
+
|
|
11794
|
+
Dust should help projects adopt agent-friendly debugging practices: structured error output, diagnostic scripts, snapshot testing for complex state, and tools that turn vague symptoms into specific, actionable information.
|
|
11795
|
+
|
|
11796
|
+
## Applicability
|
|
11797
|
+
|
|
11798
|
+
Internal
|
|
11799
|
+
|
|
11800
|
+
## Parent Principle
|
|
11801
|
+
|
|
11802
|
+
- [Ideal Agent Developer Experience](ideal-agent-developer-experience.md)
|
|
11803
|
+
|
|
11804
|
+
## Sub-Principles
|
|
11805
|
+
|
|
11806
|
+
- (none)
|
|
11807
|
+
`
|
|
11808
|
+
},
|
|
11809
|
+
{
|
|
11810
|
+
slug: "atomic-commits",
|
|
11811
|
+
content: `# Atomic Commits
|
|
11812
|
+
|
|
11813
|
+
Each commit should tell a complete story, bundling implementation changes with their corresponding documentation updates.
|
|
11814
|
+
|
|
11815
|
+
When a task is completed, the commit deletes the task file, updates relevant facts to reflect the new reality, and removes any ideas that have been realized. This discipline ensures that any point in the commit history represents a coherent, self-documenting state of the project.
|
|
11816
|
+
|
|
11817
|
+
Clean commit history is essential because archaeology depends on it. Future humans and AI agents will traverse history to understand why decisions were made and how the system evolved.
|
|
11818
|
+
|
|
11819
|
+
## Parent Principle
|
|
11820
|
+
|
|
11821
|
+
- [Repository Hygiene](repository-hygiene.md)
|
|
11822
|
+
|
|
11823
|
+
## Sub-Principles
|
|
11824
|
+
|
|
11825
|
+
- [Traceable Decisions](traceable-decisions.md)
|
|
11826
|
+
`
|
|
11827
|
+
},
|
|
11828
|
+
{
|
|
11829
|
+
slug: "trunk-based-development",
|
|
11830
|
+
content: `# Trunk-Based Development
|
|
11831
|
+
|
|
11832
|
+
Dust is designed to support a non-branching workflow where developers commit directly to a single main branch.
|
|
11833
|
+
|
|
11834
|
+
In trunk-based development, teams collaborate on code in one primary branch rather than maintaining multiple long-lived feature branches. This eliminates merge conflicts, enables continuous integration, and keeps the codebase continuously releasable.
|
|
11835
|
+
|
|
11836
|
+
The \`dust loop claude\` command embodies this philosophy: agents pull from main, implement a task, and push directly back to main. There are no feature branches, no pull requests, no merge queues. Each commit is atomic and complete.
|
|
11837
|
+
|
|
11838
|
+
This approach scales through discipline rather than isolation. Feature flags and incremental changes replace long-running branches. The repository history becomes a linear sequence of working states.
|
|
11839
|
+
|
|
11840
|
+
See: https://trunkbaseddevelopment.com/
|
|
11841
|
+
|
|
11842
|
+
## Parent Principle
|
|
11843
|
+
|
|
11844
|
+
- [Repository Hygiene](repository-hygiene.md)
|
|
11845
|
+
|
|
11846
|
+
## Sub-Principles
|
|
11847
|
+
|
|
11848
|
+
(none)
|
|
11849
|
+
`
|
|
11850
|
+
},
|
|
11851
|
+
{
|
|
11852
|
+
slug: "comprehensive-assertions",
|
|
11853
|
+
content: `# Comprehensive Assertions
|
|
11854
|
+
|
|
11855
|
+
Assert the whole, not the parts.
|
|
11856
|
+
|
|
11857
|
+
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.
|
|
11858
|
+
|
|
11859
|
+
Small assertions are like yes/no questions to a witness. A whole-object assertion is like asking "tell me what you saw."
|
|
11860
|
+
|
|
11861
|
+
## In practice
|
|
11862
|
+
|
|
11863
|
+
Collapse multiple partial assertions into one comprehensive assertion:
|
|
11864
|
+
|
|
11865
|
+
\`\`\`javascript
|
|
11866
|
+
// Fragmented — each failure is a narrow keyhole
|
|
11867
|
+
expect(result.name).toBe("Alice");
|
|
11868
|
+
expect(result.age).toBe(30);
|
|
11869
|
+
expect(result.role).toBe("admin");
|
|
11870
|
+
|
|
11871
|
+
// Whole — a failure diff tells the full story
|
|
11872
|
+
expect(result).toEqual({
|
|
11873
|
+
name: "Alice",
|
|
11874
|
+
age: 30,
|
|
11875
|
+
role: "admin",
|
|
11876
|
+
});
|
|
11877
|
+
\`\`\`
|
|
11878
|
+
|
|
11879
|
+
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.
|
|
11880
|
+
|
|
11881
|
+
The same applies to arrays:
|
|
11882
|
+
|
|
11883
|
+
\`\`\`javascript
|
|
11884
|
+
// Avoid: partial assertions that hide the actual state
|
|
11885
|
+
expect(array).toContain('apples')
|
|
11886
|
+
expect(array).toContain('oranges')
|
|
11887
|
+
|
|
11888
|
+
// Prefer: one assertion that reveals the full picture on failure
|
|
11889
|
+
expect(array).toEqual(['apples', 'oranges'])
|
|
11890
|
+
\`\`\`
|
|
11891
|
+
|
|
11892
|
+
## How to evaluate
|
|
11893
|
+
|
|
11894
|
+
Work supports this principle when test failures tell a rich story — showing the complete actual value alongside the complete expected value, so the reader can understand what happened without re-running anything.
|
|
11895
|
+
|
|
11896
|
+
## Parent Principle
|
|
11897
|
+
|
|
11898
|
+
- [Make Changes with Confidence](make-changes-with-confidence.md)
|
|
11899
|
+
|
|
11900
|
+
## Sub-Principles
|
|
11901
|
+
|
|
11902
|
+
- (none)
|
|
11903
|
+
`
|
|
11904
|
+
},
|
|
11905
|
+
{
|
|
11906
|
+
slug: "cross-platform-compatibility",
|
|
11907
|
+
content: `# Cross-Platform Compatibility
|
|
11908
|
+
|
|
11909
|
+
Dust should work consistently across operating systems: Linux, macOS, and Windows.
|
|
11910
|
+
|
|
11911
|
+
This means:
|
|
11912
|
+
- Avoiding platform-specific shell commands or syntax
|
|
11913
|
+
- Using cross-platform path handling
|
|
11914
|
+
- Testing on multiple platforms when possible
|
|
11915
|
+
- Documenting any platform-specific limitations
|
|
11916
|
+
|
|
11917
|
+
Cross-platform support broadens adoption and ensures teams with mixed environments can collaborate effectively.
|
|
11918
|
+
|
|
11919
|
+
## Parent Principle
|
|
11920
|
+
|
|
11921
|
+
- [Easy Adoption](easy-adoption.md)
|
|
11922
|
+
|
|
11923
|
+
## Sub-Principles
|
|
11924
|
+
|
|
11925
|
+
- (none)
|
|
11926
|
+
`
|
|
11927
|
+
},
|
|
11928
|
+
{
|
|
11929
|
+
slug: "exploratory-tooling",
|
|
11930
|
+
content: `# Exploratory Tooling
|
|
11931
|
+
|
|
11932
|
+
Agents need tools to efficiently explore and understand unfamiliar codebases.
|
|
11933
|
+
|
|
11934
|
+
When an agent encounters a new codebase — or an unfamiliar corner of a familiar one — it needs to quickly build a mental model: what exists, how it fits together, and where to make changes. Without good exploratory tools, agents waste context on trial-and-error searches, reading irrelevant files, and forming incorrect assumptions.
|
|
11935
|
+
|
|
11936
|
+
Dust should promote and integrate tools that help agents explore: dependency graphs, module overviews, search utilities tuned for code navigation, and summaries of project structure. The goal is to make the "orientation" phase of any task as short and reliable as possible.
|
|
11937
|
+
|
|
11938
|
+
## Applicability
|
|
11939
|
+
|
|
11940
|
+
Internal
|
|
11941
|
+
|
|
11942
|
+
## Parent Principle
|
|
11943
|
+
|
|
11944
|
+
- [Ideal Agent Developer Experience](ideal-agent-developer-experience.md)
|
|
11945
|
+
|
|
11946
|
+
## Sub-Principles
|
|
11947
|
+
|
|
11948
|
+
- (none)
|
|
11949
|
+
`
|
|
11950
|
+
},
|
|
11951
|
+
{
|
|
11952
|
+
slug: "reasonably-dry",
|
|
11953
|
+
content: `# Reasonably DRY
|
|
11954
|
+
|
|
11955
|
+
Don't repeat yourself is a good principle, but don't overdo it.
|
|
11956
|
+
|
|
11957
|
+
Extracting shared code too eagerly can create tight coupling, obscure intent, and make changes harder. When two pieces of code look similar but serve different purposes or are likely to evolve independently, duplication is the better choice. The cost of a wrong abstraction is higher than the cost of a little repetition. Extract shared code when the duplication is truly about the same concept and has proven stable, not just because two things happen to look alike right now.
|
|
11958
|
+
|
|
11959
|
+
## Parent Principle
|
|
11960
|
+
|
|
11961
|
+
- [Maintainable Codebase](maintainable-codebase.md)
|
|
11962
|
+
|
|
11963
|
+
## Sub-Principles
|
|
11964
|
+
|
|
11965
|
+
- (none)
|
|
11966
|
+
`
|
|
11967
|
+
},
|
|
11968
|
+
{
|
|
11969
|
+
slug: "runtime-agnostic-tests",
|
|
11970
|
+
content: `# Runtime Agnostic Tests
|
|
11971
|
+
|
|
11972
|
+
Dust's test suite should work across JavaScript runtimes.
|
|
11973
|
+
|
|
11974
|
+
Tests should use standard JavaScript testing patterns that work across Node.js, Bun, and other runtimes. Avoiding runtime-specific test APIs ensures the project can leverage different runtimes' advantages while maintaining broad compatibility.
|
|
11975
|
+
|
|
11976
|
+
## Parent Principle
|
|
11977
|
+
|
|
11978
|
+
- [Minimal Dependencies](minimal-dependencies.md)
|
|
11979
|
+
|
|
11980
|
+
## Sub-Principles
|
|
11981
|
+
|
|
11982
|
+
- (none)
|
|
11983
|
+
`
|
|
11984
|
+
},
|
|
11985
|
+
{
|
|
11986
|
+
slug: "task-first-workflow",
|
|
11987
|
+
content: `# Task-First Workflow
|
|
11988
|
+
|
|
11989
|
+
Work should be captured as a task before implementation begins, creating traceability between intent and outcome.
|
|
11990
|
+
|
|
11991
|
+
This discipline ensures that every change has a documented purpose. The commit history shows pairs of "Add task" followed by implementation, making it easy to understand why each change was made. It also prevents scope creep by defining boundaries before work starts.
|
|
11992
|
+
|
|
11993
|
+
## Parent Principle
|
|
11994
|
+
|
|
11995
|
+
- [Lightweight Planning](lightweight-planning.md)
|
|
11996
|
+
|
|
11997
|
+
## Sub-Principles
|
|
11998
|
+
|
|
11999
|
+
- (none)
|
|
12000
|
+
`
|
|
12001
|
+
},
|
|
12002
|
+
{
|
|
12003
|
+
slug: "agent-autonomy",
|
|
12004
|
+
content: `# Agent Autonomy
|
|
12005
|
+
|
|
12006
|
+
Dust exists to enable AI agents to produce work autonomously.
|
|
12007
|
+
|
|
12008
|
+
With sufficient planning and small enough units, this works much better in practice.
|
|
12009
|
+
|
|
12010
|
+
## Parent Principle
|
|
12011
|
+
|
|
12012
|
+
- [Human-AI Collaboration](human-ai-collaboration.md)
|
|
12013
|
+
|
|
12014
|
+
## Sub-Principles
|
|
12015
|
+
|
|
12016
|
+
- [Actionable Errors](actionable-errors.md)
|
|
12017
|
+
- [Batteries Included](batteries-included.md)
|
|
12018
|
+
- [Agent-Agnostic Design](agent-agnostic-design.md)
|
|
12019
|
+
- [Agent Context Inference](agent-context-inference.md)
|
|
12020
|
+
- [Agent-Specific Enhancement](agent-specific-enhancement.md)
|
|
12021
|
+
- [Context Window Efficiency](context-window-efficiency.md)
|
|
12022
|
+
- [Small Units](small-units.md)
|
|
12023
|
+
`
|
|
12024
|
+
},
|
|
12025
|
+
{
|
|
12026
|
+
slug: "clarity-over-brevity",
|
|
12027
|
+
content: `# Clarity Over Brevity
|
|
12028
|
+
|
|
12029
|
+
Names should be descriptive and self-documenting, even if longer.
|
|
12030
|
+
|
|
12031
|
+
Abbreviated names like \`ctx\`, \`deps\`, \`fs\`, or \`args\` save a few keystrokes but obscure meaning. Full names like \`context\`, \`dependencies\`, \`fileSystem\`, and \`arguments\` make code immediately understandable without requiring readers to decode conventions. This is especially valuable when AI agents or new contributors read the codebase for the first time.
|
|
12032
|
+
|
|
12033
|
+
## Parent Principle
|
|
12034
|
+
|
|
12035
|
+
- [Naming Matters](naming-matters.md)
|
|
12036
|
+
|
|
12037
|
+
## Sub-Principles
|
|
12038
|
+
|
|
12039
|
+
- (none)
|
|
12040
|
+
`
|
|
12041
|
+
},
|
|
12042
|
+
{
|
|
12043
|
+
slug: "fast-feedback-loops",
|
|
12044
|
+
content: `# Fast Feedback Loops
|
|
12045
|
+
|
|
12046
|
+
The primary feedback loop — write code, run checks, see results — should be as fast as possible.
|
|
12047
|
+
|
|
12048
|
+
Fast feedback is the foundation of productive development, for both humans and agents. When tests, linters, and type checks run in seconds rather than minutes, developers iterate more frequently and catch problems earlier. 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.
|
|
12049
|
+
|
|
12050
|
+
Dust should help projects measure the speed of their feedback loops, identify bottlenecks, and keep them fast as the codebase grows. This includes promoting practices like unit tests over integration tests for speed, incremental compilation, and check parallelisation.
|
|
12051
|
+
|
|
12052
|
+
## Parent Principle
|
|
12053
|
+
|
|
12054
|
+
- [Ideal Agent Developer Experience](ideal-agent-developer-experience.md)
|
|
12055
|
+
|
|
12056
|
+
## Sub-Principles
|
|
12057
|
+
|
|
12058
|
+
- (none)
|
|
12059
|
+
`
|
|
12060
|
+
},
|
|
12061
|
+
{
|
|
12062
|
+
slug: "make-the-change-easy",
|
|
12063
|
+
content: `# Make the Change Easy
|
|
12064
|
+
|
|
12065
|
+
For each desired change, make the change easy, then make the easy change.
|
|
12066
|
+
|
|
12067
|
+
This principle, articulated by Kent Beck, recognizes that the hardest part of a change is often not the change itself but the state of the code receiving it. When code resists a change, the right response is to first refactor until the change becomes straightforward, and only then make it. The warning - "this may be hard" - acknowledges that preparing the ground takes real effort, but the result is a change that fits naturally rather than one forced in against the grain.
|
|
12068
|
+
|
|
12069
|
+
Work that supports this principle includes refactoring before feature work, improving abstractions that make a category of changes simpler, and resisting the urge to bolt changes onto code that isn't ready for them.
|
|
12070
|
+
|
|
12071
|
+
## Parent Principle
|
|
12072
|
+
|
|
12073
|
+
- [Maintainable Codebase](maintainable-codebase.md)
|
|
12074
|
+
|
|
12075
|
+
## Sub-Principles
|
|
12076
|
+
|
|
12077
|
+
- (none)
|
|
12078
|
+
`
|
|
12079
|
+
},
|
|
12080
|
+
{
|
|
12081
|
+
slug: "self-contained-repository",
|
|
12082
|
+
content: `# Self-Contained Repository
|
|
12083
|
+
|
|
12084
|
+
Where possible, developers and agents should have everything they need to be productive, within the repository.
|
|
12085
|
+
|
|
12086
|
+
No third-party tools should be required beyond those that can be installed with a single command defined in the repository. Setup instructions, scripts, configuration, and dependencies should all live in version control so that cloning the repo and running a single install command is sufficient to start working. This eliminates onboarding friction, reduces "works on my machine" issues, and is especially important for agents — who cannot browse the web to find missing tools or ask colleagues how to set things up.
|
|
12087
|
+
|
|
12088
|
+
## Applicability
|
|
12089
|
+
|
|
12090
|
+
Internal
|
|
12091
|
+
|
|
12092
|
+
## Parent Principle
|
|
12093
|
+
|
|
12094
|
+
- [Ideal Agent Developer Experience](ideal-agent-developer-experience.md)
|
|
12095
|
+
|
|
12096
|
+
## Sub-Principles
|
|
12097
|
+
|
|
12098
|
+
- (none)
|
|
12099
|
+
`
|
|
12100
|
+
},
|
|
12101
|
+
{
|
|
12102
|
+
slug: "traceable-decisions",
|
|
12103
|
+
content: `# Traceable Decisions
|
|
12104
|
+
|
|
12105
|
+
The commit history should explain why changes were made, not just what changed.
|
|
12106
|
+
|
|
12107
|
+
Commit messages should capture intent and context that would otherwise be lost. Future maintainers (human or AI) will traverse history to understand the reasoning behind decisions. A commit that says "Fix bug" is less valuable than one that explains what was broken and why the fix is correct.
|
|
12108
|
+
|
|
12109
|
+
## Parent Principle
|
|
12110
|
+
|
|
12111
|
+
- [Atomic Commits](atomic-commits.md)
|
|
12112
|
+
|
|
12113
|
+
## Sub-Principles
|
|
12114
|
+
|
|
12115
|
+
- (none)
|
|
12116
|
+
`
|
|
12117
|
+
},
|
|
12118
|
+
{
|
|
12119
|
+
slug: "unit-test-coverage",
|
|
12120
|
+
content: `# Unit Test Coverage
|
|
12121
|
+
|
|
12122
|
+
Complete unit test coverage ensures low-level tests give users direct feedback as they change the code.
|
|
12123
|
+
|
|
12124
|
+
Excluding system tests from coverage reporting focuses attention on unit tests - the tests that provide the fastest, most specific feedback. When coverage tools only measure unit tests, developers can quickly identify which parts of the codebase lack fine-grained test protection.
|
|
12125
|
+
|
|
12126
|
+
## Parent Principle
|
|
12127
|
+
|
|
12128
|
+
- [Make Changes with Confidence](make-changes-with-confidence.md)
|
|
12129
|
+
|
|
12130
|
+
## Sub-Principles
|
|
12131
|
+
|
|
12132
|
+
- (none)
|
|
12133
|
+
`
|
|
12134
|
+
},
|
|
12135
|
+
{
|
|
12136
|
+
slug: "decoupled-code",
|
|
12137
|
+
content: `# Decoupled Code
|
|
12138
|
+
|
|
12139
|
+
Code should be organized into independent units with explicit dependencies.
|
|
12140
|
+
|
|
12141
|
+
Decoupled code is easier to test, understand, and modify. Dependencies are passed in rather than hard-coded, enabling units to be tested in isolation and composed flexibly. This reduces the blast radius of changes and makes the system more maintainable.
|
|
12142
|
+
|
|
12143
|
+
## Parent Principle
|
|
12144
|
+
|
|
12145
|
+
- [Make Changes with Confidence](make-changes-with-confidence.md)
|
|
12146
|
+
|
|
12147
|
+
## Sub-Principles
|
|
12148
|
+
|
|
12149
|
+
- [Dependency Injection](dependency-injection.md)
|
|
12150
|
+
- [Stubs Over Mocks](stubs-over-mocks.md)
|
|
12151
|
+
- [Functional Core, Imperative Shell](functional-core-imperative-shell.md)
|
|
12152
|
+
- [Design for Testability](design-for-testability.md)
|
|
12153
|
+
`
|
|
12154
|
+
},
|
|
12155
|
+
{
|
|
12156
|
+
slug: "lint-everything",
|
|
12157
|
+
content: `# Lint Everything
|
|
12158
|
+
|
|
12159
|
+
Prefer static analysis over runtime checks. Every error caught by a linter is an error that never reaches tests, and every error caught by tests is an error that never reaches production.
|
|
12160
|
+
|
|
12161
|
+
Lint markdown, lint types, lint formatting. If it can be checked statically, check it. Linters are fast, deterministic, and catch entire categories of bugs before code even runs.
|
|
12162
|
+
|
|
12163
|
+
This project lints:
|
|
12164
|
+
- TypeScript (type checking and style)
|
|
12165
|
+
- Markdown (broken links, required sections)
|
|
12166
|
+
- Task files (structure validation)
|
|
12167
|
+
- Principle hierarchy (parent/child consistency)
|
|
12168
|
+
|
|
12169
|
+
## Parent Principle
|
|
12170
|
+
|
|
12171
|
+
- [Make Changes with Confidence](make-changes-with-confidence.md)
|
|
12172
|
+
|
|
12173
|
+
## Sub-Principles
|
|
12174
|
+
|
|
12175
|
+
(none)
|
|
12176
|
+
`
|
|
12177
|
+
},
|
|
12178
|
+
{
|
|
12179
|
+
slug: "maintainable-codebase",
|
|
12180
|
+
content: `# Maintainable Codebase
|
|
12181
|
+
|
|
12182
|
+
The dust codebase should be easy to understand, modify, and extend.
|
|
12183
|
+
|
|
12184
|
+
This principle governs how we develop and maintain dust itself, separate from the principles that describe what dust offers its users. A well-maintained codebase enables rapid iteration, reduces bugs, and makes contributions easier.
|
|
12185
|
+
|
|
12186
|
+
## Parent Principle
|
|
12187
|
+
|
|
12188
|
+
- [Agentic Flow State](agentic-flow-state.md)
|
|
12189
|
+
|
|
12190
|
+
## Sub-Principles
|
|
12191
|
+
|
|
12192
|
+
- [Make Changes with Confidence](make-changes-with-confidence.md)
|
|
12193
|
+
- [Minimal Dependencies](minimal-dependencies.md)
|
|
12194
|
+
- [Intuitive Directory Structure](intuitive-directory-structure.md)
|
|
12195
|
+
- [Repository Hygiene](repository-hygiene.md)
|
|
12196
|
+
- [Naming Matters](naming-matters.md)
|
|
12197
|
+
- [Reasonably DRY](reasonably-dry.md)
|
|
12198
|
+
- [Make the Change Easy](make-the-change-easy.md)
|
|
12199
|
+
- [Boy Scout Rule](boy-scout-rule.md)
|
|
12200
|
+
- [Broken Windows](broken-windows.md)
|
|
12201
|
+
`
|
|
12202
|
+
},
|
|
12203
|
+
{
|
|
12204
|
+
slug: "agent-agnostic-design",
|
|
12205
|
+
content: `# Agent-Agnostic Design
|
|
12206
|
+
|
|
12207
|
+
Dust should work with multiple agents without favoring one.
|
|
12208
|
+
|
|
12209
|
+
Rather than implementing agents, Dust generates prompts and context that can be passed to any capable agent. This keeps Dust lightweight and allows teams to use whatever agent tooling they prefer.
|
|
12210
|
+
|
|
12211
|
+
Dust may have built-in support for invoking popular agents (Claude, Aider, Codex, etc.), but the choice of agent should always be made by the user at runtime - never hard-coded into repository configuration.
|
|
12212
|
+
|
|
12213
|
+
Note: Supporting multiple agents directly contributes to [Easy Adoption](easy-adoption.md), since teams can use their preferred agent tools without being locked into a specific platform.
|
|
12214
|
+
|
|
12215
|
+
## Applicability
|
|
12216
|
+
|
|
12217
|
+
Internal
|
|
12218
|
+
|
|
12219
|
+
## Parent Principle
|
|
12220
|
+
|
|
12221
|
+
- [Agent Autonomy](agent-autonomy.md)
|
|
12222
|
+
|
|
12223
|
+
## Sub-Principles
|
|
12224
|
+
|
|
12225
|
+
- (none)
|
|
12226
|
+
`
|
|
12227
|
+
},
|
|
12228
|
+
{
|
|
12229
|
+
slug: "easy-adoption",
|
|
12230
|
+
content: `# Easy Adoption
|
|
12231
|
+
|
|
12232
|
+
Dust should be trivially easy to adopt in any repository.
|
|
12233
|
+
|
|
12234
|
+
Getting started with Dust should require minimal friction. A developer should be able to bootstrap Dust in their repository with a single command, without needing to install dependencies, configure build tools, or understand the internals.
|
|
12235
|
+
|
|
12236
|
+
This lowers the barrier to entry and encourages experimentation.
|
|
12237
|
+
|
|
12238
|
+
## Parent Principle
|
|
12239
|
+
|
|
12240
|
+
- [Human-AI Collaboration](human-ai-collaboration.md)
|
|
12241
|
+
|
|
12242
|
+
## Sub-Principles
|
|
12243
|
+
|
|
12244
|
+
- [Cross-Platform Compatibility](cross-platform-compatibility.md)
|
|
12245
|
+
- [Unsurprising UX](unsurprising-ux.md)
|
|
12246
|
+
- [VCS Independence](vcs-independence.md)
|
|
12247
|
+
`
|
|
12248
|
+
},
|
|
12249
|
+
{
|
|
12250
|
+
slug: "actionable-errors",
|
|
12251
|
+
content: `# Actionable Errors
|
|
12252
|
+
|
|
12253
|
+
Error messages should tell you what to do next, not just what went wrong.
|
|
12254
|
+
|
|
12255
|
+
When something fails, the message should provide:
|
|
12256
|
+
- A clear description of the problem
|
|
12257
|
+
- Specific guidance on how to fix it
|
|
12258
|
+
- Context needed to take the next step
|
|
12259
|
+
|
|
12260
|
+
This is especially important for AI agents, who need concrete instructions to recover autonomously. A good error message turns a dead end into a signpost.
|
|
12261
|
+
|
|
12262
|
+
## Parent Principle
|
|
12263
|
+
|
|
12264
|
+
- [Agent Autonomy](agent-autonomy.md)
|
|
12265
|
+
|
|
12266
|
+
## Sub-Principles
|
|
12267
|
+
|
|
12268
|
+
- (none)
|
|
12269
|
+
`
|
|
12270
|
+
},
|
|
12271
|
+
{
|
|
12272
|
+
slug: "consistent-naming",
|
|
12273
|
+
content: `# Consistent Naming
|
|
12274
|
+
|
|
12275
|
+
Names should follow established conventions within each category to reduce cognitive load.
|
|
12276
|
+
|
|
12277
|
+
Principles use Title Case. File names use kebab-case. Commands use lowercase with hyphens. When naming conventions exist, follow them. When they don't, establish one and apply it consistently. Inconsistent naming creates friction for both humans and AI agents trying to predict or recall identifiers.
|
|
12278
|
+
|
|
12279
|
+
## Parent Principle
|
|
12280
|
+
|
|
12281
|
+
- [Naming Matters](naming-matters.md)
|
|
12282
|
+
|
|
12283
|
+
## Sub-Principles
|
|
12284
|
+
|
|
12285
|
+
- (none)
|
|
12286
|
+
`
|
|
12287
|
+
},
|
|
12288
|
+
{
|
|
12289
|
+
slug: "minimal-dependencies",
|
|
12290
|
+
content: `# Minimal Dependencies
|
|
12291
|
+
|
|
12292
|
+
Dust should avoid coupling to specific tools so we can switch to better alternatives as they emerge.
|
|
10927
12293
|
|
|
10928
|
-
|
|
10929
|
-
|
|
10930
|
-
|
|
10931
|
-
|
|
10932
|
-
|
|
12294
|
+
By keeping dependencies minimal and using standard APIs where possible, we maintain the freedom to adopt new tools without major rewrites. This applies to runtimes, test frameworks, build tools, and other infrastructure choices.
|
|
12295
|
+
|
|
12296
|
+
## Parent Principle
|
|
12297
|
+
|
|
12298
|
+
- [Maintainable Codebase](maintainable-codebase.md)
|
|
12299
|
+
|
|
12300
|
+
## Sub-Principles
|
|
12301
|
+
|
|
12302
|
+
- [Runtime Agnostic Tests](runtime-agnostic-tests.md)
|
|
12303
|
+
`
|
|
12304
|
+
},
|
|
12305
|
+
{
|
|
12306
|
+
slug: "context-window-efficiency",
|
|
12307
|
+
content: `# Context Window Efficiency
|
|
12308
|
+
|
|
12309
|
+
Dust should be designed with short attention spans in mind.
|
|
12310
|
+
|
|
12311
|
+
AI agents operate within limited context windows. Every token consumed by planning artifacts is a token unavailable for reasoning about code. Dust keeps artifacts concise and scannable so agents can quickly understand what needs to be done without wading through verbose documentation.
|
|
12312
|
+
|
|
12313
|
+
This means favoring brevity over completeness, using consistent structures that are fast to parse, and avoiding redundant information across files.
|
|
12314
|
+
|
|
12315
|
+
## Parent Principle
|
|
12316
|
+
|
|
12317
|
+
- [Agent Autonomy](agent-autonomy.md)
|
|
12318
|
+
|
|
12319
|
+
## Sub-Principles
|
|
12320
|
+
|
|
12321
|
+
- [Progressive Disclosure](progressive-disclosure.md)
|
|
12322
|
+
`
|
|
12323
|
+
},
|
|
12324
|
+
{
|
|
12325
|
+
slug: "boy-scout-rule",
|
|
12326
|
+
content: `# Boy Scout Rule
|
|
12327
|
+
|
|
12328
|
+
Always leave the code better than you found it.
|
|
12329
|
+
|
|
12330
|
+
When working in any area of the codebase, take the opportunity to make small improvements — clearer names, removed dead code, better structure — even if they're not directly related to the task at hand. These incremental improvements compound over time, preventing gradual decay and keeping the codebase healthy without requiring dedicated cleanup efforts.
|
|
12331
|
+
|
|
12332
|
+
The Boy Scout Rule is not a license for large-scale refactoring during unrelated work. Improvements should be small, obvious, and low-risk. If a cleanup is too large to include alongside the current task, capture it as a separate task instead.
|
|
12333
|
+
|
|
12334
|
+
## Parent Principle
|
|
12335
|
+
|
|
12336
|
+
- [Maintainable Codebase](maintainable-codebase.md)
|
|
12337
|
+
|
|
12338
|
+
## Sub-Principles
|
|
12339
|
+
|
|
12340
|
+
- (none)
|
|
12341
|
+
`
|
|
12342
|
+
},
|
|
12343
|
+
{
|
|
12344
|
+
slug: "unsurprising-ux",
|
|
12345
|
+
content: `# Unsurprising UX
|
|
12346
|
+
|
|
12347
|
+
The user interface should be as "guessable" as possible.
|
|
12348
|
+
|
|
12349
|
+
Following the [Principle of Least Astonishment](https://en.wikipedia.org/wiki/Principle_of_least_astonishment), users form expectations about how a tool will behave based on conventions, prior experience, and intuition. Dust's interface (including the CLI) should match those expectations wherever possible. If users are observed trying to use the interface in ways we didn't anticipate, the interface should be adjusted to meet their expectations — even if that means supporting many ways of achieving the same result.
|
|
12350
|
+
|
|
12351
|
+
Surprising behavior erodes trust and slows people down. Unsurprising behavior lets users stay in flow.
|
|
12352
|
+
|
|
12353
|
+
## Parent Principle
|
|
12354
|
+
|
|
12355
|
+
- [Easy Adoption](easy-adoption.md)
|
|
12356
|
+
|
|
12357
|
+
## Sub-Principles
|
|
12358
|
+
|
|
12359
|
+
- (none)
|
|
12360
|
+
`
|
|
12361
|
+
}
|
|
12362
|
+
];
|
|
10933
12363
|
|
|
10934
12364
|
// lib/artifacts/core-principles.ts
|
|
10935
12365
|
function sortNodes(nodes) {
|
|
@@ -10984,47 +12414,251 @@ function getCorePrincipleTree(allPrinciples, config) {
|
|
|
10984
12414
|
}
|
|
10985
12415
|
|
|
10986
12416
|
// lib/core-principles.ts
|
|
10987
|
-
function
|
|
10988
|
-
|
|
10989
|
-
|
|
10990
|
-
|
|
10991
|
-
|
|
12417
|
+
function extractLinksFromSection(content, sectionHeading) {
|
|
12418
|
+
const lines = content.split(`
|
|
12419
|
+
`);
|
|
12420
|
+
const links = [];
|
|
12421
|
+
let inSection = false;
|
|
12422
|
+
for (const line of lines) {
|
|
12423
|
+
if (line.startsWith("## ")) {
|
|
12424
|
+
inSection = line.trimEnd() === `## ${sectionHeading}`;
|
|
12425
|
+
continue;
|
|
12426
|
+
}
|
|
12427
|
+
if (!inSection)
|
|
12428
|
+
continue;
|
|
12429
|
+
if (line.startsWith("# "))
|
|
12430
|
+
break;
|
|
12431
|
+
const linkMatch = line.match(MARKDOWN_LINK_PATTERN);
|
|
12432
|
+
if (linkMatch) {
|
|
12433
|
+
const target = linkMatch[2];
|
|
12434
|
+
const slugMatch = target.match(/([^/]+)\.md$/);
|
|
12435
|
+
if (slugMatch) {
|
|
12436
|
+
links.push(slugMatch[1]);
|
|
12437
|
+
}
|
|
12438
|
+
}
|
|
12439
|
+
}
|
|
12440
|
+
return links;
|
|
12441
|
+
}
|
|
12442
|
+
function extractSingleLinkFromSection(content, sectionHeading) {
|
|
12443
|
+
const links = extractLinksFromSection(content, sectionHeading);
|
|
12444
|
+
return links.length === 1 ? links[0] : null;
|
|
10992
12445
|
}
|
|
10993
|
-
function
|
|
10994
|
-
const
|
|
10995
|
-
|
|
10996
|
-
|
|
10997
|
-
const principlesDir = join11(packageRoot, ".dust", "principles");
|
|
10998
|
-
if (!existsSync(principlesDir)) {
|
|
10999
|
-
throw new Error(`Core principles directory not found at ${principlesDir}. ` + "Ensure the @joshski/dust package is properly installed.");
|
|
12446
|
+
function parsePrincipleContent(slug, content) {
|
|
12447
|
+
const title = extractTitle(content);
|
|
12448
|
+
if (!title) {
|
|
12449
|
+
throw new Error(`Principle has no title: ${slug}`);
|
|
11000
12450
|
}
|
|
11001
|
-
|
|
12451
|
+
const parentPrinciple = extractSingleLinkFromSection(content, "Parent Principle");
|
|
12452
|
+
const subPrinciples = extractLinksFromSection(content, "Sub-Principles");
|
|
12453
|
+
return {
|
|
12454
|
+
slug,
|
|
12455
|
+
title,
|
|
12456
|
+
content,
|
|
12457
|
+
parentPrinciple,
|
|
12458
|
+
subPrinciples
|
|
12459
|
+
};
|
|
11002
12460
|
}
|
|
11003
12461
|
async function readAllCorePrinciples() {
|
|
11004
|
-
|
|
11005
|
-
const packageRoot = dirname5(dirname5(principlesDir));
|
|
11006
|
-
const dustPath = join11(packageRoot, ".dust");
|
|
11007
|
-
const fileSystem = createFileReader();
|
|
11008
|
-
const files = readdirSync(principlesDir);
|
|
11009
|
-
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
11010
|
-
const principles = [];
|
|
11011
|
-
for (const file of mdFiles) {
|
|
11012
|
-
const slug = file.replace(/\.md$/, "");
|
|
11013
|
-
const principle = await parsePrinciple(fileSystem, dustPath, slug);
|
|
11014
|
-
principles.push(principle);
|
|
11015
|
-
}
|
|
11016
|
-
return principles;
|
|
12462
|
+
return BUNDLED_PRINCIPLES.map(({ slug, content }) => parsePrincipleContent(slug, content));
|
|
11017
12463
|
}
|
|
11018
12464
|
async function getCorePrincipleHierarchy(config = {}) {
|
|
11019
12465
|
const allPrinciples = await readAllCorePrinciples();
|
|
11020
12466
|
return getCorePrincipleTree(allPrinciples, config);
|
|
11021
12467
|
}
|
|
11022
|
-
|
|
11023
|
-
|
|
11024
|
-
|
|
12468
|
+
|
|
12469
|
+
// lib/cli/commands/core-principle.ts
|
|
12470
|
+
async function corePrinciple(dependencies) {
|
|
12471
|
+
const { arguments: commandArguments, context } = dependencies;
|
|
12472
|
+
if (commandArguments.length === 0) {
|
|
12473
|
+
context.stderr("Error: Missing principle name");
|
|
12474
|
+
context.stderr("Usage: dust core principle <name>");
|
|
12475
|
+
context.stderr("");
|
|
12476
|
+
context.stderr("Available principles can be found by running:");
|
|
12477
|
+
context.stderr(" dust principles");
|
|
12478
|
+
return { exitCode: 1 };
|
|
12479
|
+
}
|
|
12480
|
+
const slug = commandArguments[0];
|
|
12481
|
+
const allPrinciples = await readAllCorePrinciples();
|
|
12482
|
+
const principle = allPrinciples.find((p) => p.slug === slug);
|
|
12483
|
+
if (!principle) {
|
|
12484
|
+
context.stderr(`Error: Core principle "${slug}" not found`);
|
|
12485
|
+
context.stderr("");
|
|
12486
|
+
context.stderr("Available principles can be found by running:");
|
|
12487
|
+
context.stderr(" dust principles");
|
|
12488
|
+
return { exitCode: 1 };
|
|
12489
|
+
}
|
|
12490
|
+
context.stdout(principle.content);
|
|
12491
|
+
return { exitCode: 0 };
|
|
12492
|
+
}
|
|
12493
|
+
|
|
12494
|
+
// lib/cli/commands/help.ts
|
|
12495
|
+
function generateHelpText(settings) {
|
|
12496
|
+
const bin = settings.dustCommand;
|
|
12497
|
+
return dedent`
|
|
12498
|
+
✨ dust - Flow state for AI coding agents.
|
|
12499
|
+
|
|
12500
|
+
Usage: ${bin} <command> [options]
|
|
12501
|
+
|
|
12502
|
+
Commands:
|
|
12503
|
+
init Initialize a new Dust repository
|
|
12504
|
+
lint Run lint checks on .dust/ files
|
|
12505
|
+
list List all items (tasks, ideas, principles, facts)
|
|
12506
|
+
tasks List tasks (actionable work with definitions of done)
|
|
12507
|
+
ideas List ideas (vague proposals, convert to tasks when ready)
|
|
12508
|
+
principles List principles (guiding values, stable, rarely change)
|
|
12509
|
+
facts List facts (documentation of current system state)
|
|
12510
|
+
next Show tasks ready to work on (not blocked)
|
|
12511
|
+
check Run project-defined quality gate hook
|
|
12512
|
+
agent Agent greeting and routing instructions
|
|
12513
|
+
audit Create tasks from audit templates
|
|
12514
|
+
core principle Display a specific core principle
|
|
12515
|
+
focus Declare current objective (for remote session tracking)
|
|
12516
|
+
pick task Pick the next task to work on
|
|
12517
|
+
implement task Implement a task
|
|
12518
|
+
new task Create a new task
|
|
12519
|
+
new principle Create a new principle
|
|
12520
|
+
new idea Create a new idea
|
|
12521
|
+
loop claude Run continuous Claude iteration on tasks
|
|
12522
|
+
pre push Git pre-push hook validation
|
|
12523
|
+
help Show this help message
|
|
12524
|
+
|
|
12525
|
+
🤖 Agent Guide
|
|
12526
|
+
|
|
12527
|
+
Dust is a lightweight planning system. The .dust/ directory contains:
|
|
12528
|
+
- principles/ - Guiding values (stable, rarely change)
|
|
12529
|
+
- ideas/ - Proposals (convert to tasks when ready)
|
|
12530
|
+
- tasks/ - Actionable work with definitions of done
|
|
12531
|
+
- facts/ - Documentation of current system state
|
|
12532
|
+
|
|
12533
|
+
Workflow: Pick a task → implement it → delete the task file → commit atomically.
|
|
12534
|
+
|
|
12535
|
+
Run \`${bin} agent\` to get started!
|
|
12536
|
+
`;
|
|
12537
|
+
}
|
|
12538
|
+
async function help(dependencies) {
|
|
12539
|
+
dependencies.context.stdout(generateHelpText(dependencies.settings));
|
|
12540
|
+
return { exitCode: 0 };
|
|
12541
|
+
}
|
|
12542
|
+
|
|
12543
|
+
// lib/cli/commands/implement-task.ts
|
|
12544
|
+
async function implementTask(dependencies) {
|
|
12545
|
+
const { context, settings } = dependencies;
|
|
12546
|
+
const hooksInstalled = await manageGitHooks(dependencies);
|
|
12547
|
+
const vars = templateVariables(settings, hooksInstalled, process.env);
|
|
12548
|
+
context.stdout(`Run \`${vars.bin} focus "<task name>"\` to set your focus and see implementation instructions.`);
|
|
12549
|
+
return { exitCode: 0 };
|
|
12550
|
+
}
|
|
12551
|
+
|
|
12552
|
+
// lib/cli/commands/init.ts
|
|
12553
|
+
var DUST_DIRECTORIES = [...ARTIFACT_TYPES, "config"];
|
|
12554
|
+
function generateSettings(cwd, fileSystem, runtime) {
|
|
12555
|
+
const dustCommand = detectDustCommand(cwd, fileSystem, runtime);
|
|
12556
|
+
const testCommand = detectTestCommand(cwd, fileSystem, runtime);
|
|
12557
|
+
const checks = [];
|
|
12558
|
+
if (testCommand) {
|
|
12559
|
+
checks.push({ name: "test", command: testCommand });
|
|
12560
|
+
}
|
|
12561
|
+
return { dustCommand, checks };
|
|
12562
|
+
}
|
|
12563
|
+
var USE_DUST_FACT = `# Use dust for planning
|
|
12564
|
+
|
|
12565
|
+
This project uses [dust](https://github.com/joshski/dust) for planning and documentation.
|
|
12566
|
+
`;
|
|
12567
|
+
function claudeMdContent(dustCommand) {
|
|
12568
|
+
return dedent`
|
|
12569
|
+
# Claude Code Instructions
|
|
12570
|
+
|
|
12571
|
+
This project uses [dust](https://github.com/joshski/dust) for planning and documentation.
|
|
12572
|
+
|
|
12573
|
+
Always immediately run \`${dustCommand} agent\` when you start working in this repository.
|
|
12574
|
+
`;
|
|
12575
|
+
}
|
|
12576
|
+
function agentsMdContent(dustCommand) {
|
|
12577
|
+
return dedent`
|
|
12578
|
+
# Agent Instructions
|
|
12579
|
+
|
|
12580
|
+
This project uses [dust](https://github.com/joshski/dust) for planning and documentation.
|
|
12581
|
+
|
|
12582
|
+
Always immediately run \`${dustCommand} agent\` when you start working in this repository.
|
|
12583
|
+
`;
|
|
12584
|
+
}
|
|
12585
|
+
async function init(dependencies) {
|
|
12586
|
+
const { context, fileSystem, runtime } = dependencies;
|
|
12587
|
+
const colors = getColors();
|
|
12588
|
+
const dustPath = `${context.cwd}/.dust`;
|
|
12589
|
+
const dustCommand = detectDustCommand(context.cwd, fileSystem, runtime);
|
|
12590
|
+
const agentInstruction = `Always immediately run \`${dustCommand} agent\` when you start working in this repository.`;
|
|
12591
|
+
await fileSystem.mkdir(dustPath, { recursive: true });
|
|
12592
|
+
for (const dir of DUST_DIRECTORIES) {
|
|
12593
|
+
await fileSystem.mkdir(`${dustPath}/${dir}`, { recursive: true });
|
|
12594
|
+
}
|
|
12595
|
+
let dustDirCreated = false;
|
|
12596
|
+
try {
|
|
12597
|
+
await fileSystem.writeFile(`${dustPath}/facts/use-dust-for-planning.md`, USE_DUST_FACT, { flag: "wx" });
|
|
12598
|
+
dustDirCreated = true;
|
|
12599
|
+
} catch (error) {
|
|
12600
|
+
if (error.code !== "EEXIST") {
|
|
12601
|
+
throw error;
|
|
12602
|
+
}
|
|
12603
|
+
}
|
|
12604
|
+
try {
|
|
12605
|
+
const settings = generateSettings(context.cwd, fileSystem, runtime);
|
|
12606
|
+
await fileSystem.writeFile(`${dustPath}/config/settings.json`, `${JSON.stringify(settings, null, 2)}
|
|
12607
|
+
`, { flag: "wx" });
|
|
12608
|
+
} catch (error) {
|
|
12609
|
+
if (error.code !== "EEXIST") {
|
|
12610
|
+
throw error;
|
|
12611
|
+
}
|
|
12612
|
+
}
|
|
12613
|
+
if (dustDirCreated) {
|
|
12614
|
+
context.stdout(`${colors.green}✨ Initialized${colors.reset} Dust repository in ${colors.cyan}.dust/${colors.reset}`);
|
|
12615
|
+
context.stdout(`${colors.green}\uD83D\uDCC1 Created directories:${colors.reset} ${colors.dim}${DUST_DIRECTORIES.join(", ")}${colors.reset}`);
|
|
12616
|
+
context.stdout(`${colors.green}\uD83D\uDCC4 Created initial fact:${colors.reset} ${colors.cyan}.dust/facts/use-dust-for-planning.md${colors.reset}`);
|
|
12617
|
+
context.stdout(`${colors.green}⚙️ Created settings:${colors.reset} ${colors.cyan}.dust/config/settings.json${colors.reset}`);
|
|
12618
|
+
} else {
|
|
12619
|
+
context.stdout(`${colors.yellow}\uD83D\uDCE6 Note:${colors.reset} ${colors.cyan}.dust${colors.reset} directory already exists, skipping creation`);
|
|
12620
|
+
}
|
|
12621
|
+
const claudeMdPath = `${context.cwd}/CLAUDE.md`;
|
|
12622
|
+
try {
|
|
12623
|
+
await fileSystem.writeFile(claudeMdPath, claudeMdContent(dustCommand), {
|
|
12624
|
+
flag: "wx"
|
|
12625
|
+
});
|
|
12626
|
+
context.stdout(`${colors.green}\uD83D\uDCC4 Created${colors.reset} ${colors.cyan}CLAUDE.md${colors.reset} with agent instructions`);
|
|
12627
|
+
} catch (error) {
|
|
12628
|
+
if (error.code === "EEXIST") {
|
|
12629
|
+
context.stdout(`${colors.yellow}⚠️ Warning:${colors.reset} ${colors.cyan}CLAUDE.md${colors.reset} already exists. Consider adding: ${colors.dim}"${agentInstruction}"${colors.reset}`);
|
|
12630
|
+
} else {
|
|
12631
|
+
throw error;
|
|
12632
|
+
}
|
|
12633
|
+
}
|
|
12634
|
+
const agentsMdPath = `${context.cwd}/AGENTS.md`;
|
|
12635
|
+
try {
|
|
12636
|
+
await fileSystem.writeFile(agentsMdPath, agentsMdContent(dustCommand), {
|
|
12637
|
+
flag: "wx"
|
|
12638
|
+
});
|
|
12639
|
+
context.stdout(`${colors.green}\uD83D\uDCC4 Created${colors.reset} ${colors.cyan}AGENTS.md${colors.reset} with agent instructions`);
|
|
12640
|
+
} catch (error) {
|
|
12641
|
+
if (error.code === "EEXIST") {
|
|
12642
|
+
context.stdout(`${colors.yellow}⚠️ Warning:${colors.reset} ${colors.cyan}AGENTS.md${colors.reset} already exists. Consider adding: ${colors.dim}"${agentInstruction}"${colors.reset}`);
|
|
12643
|
+
} else {
|
|
12644
|
+
throw error;
|
|
12645
|
+
}
|
|
12646
|
+
}
|
|
12647
|
+
const runner = dustCommand.split(" ")[0];
|
|
12648
|
+
context.stdout("");
|
|
12649
|
+
context.stdout(`${colors.bold}\uD83D\uDE80 Next steps:${colors.reset} Commit the changes if you are happy, then get planning!`);
|
|
12650
|
+
context.stdout("");
|
|
12651
|
+
context.stdout(`${colors.dim}If this is a new repository, you can start adding ideas or tasks right away:${colors.reset}`);
|
|
12652
|
+
context.stdout(` ${colors.cyan}>${colors.reset} ${runner} claude "Idea: friendly UI for non-technical users"`);
|
|
12653
|
+
context.stdout(` ${colors.cyan}>${colors.reset} ${runner} codex "Task: set up code coverage"`);
|
|
12654
|
+
context.stdout("");
|
|
12655
|
+
context.stdout(`${colors.dim}If this is an existing codebase, you might want to backfill principles and facts:${colors.reset}`);
|
|
12656
|
+
context.stdout(` ${colors.cyan}>${colors.reset} ${runner} claude "Add principles and facts based on the code in this repository"`);
|
|
12657
|
+
return { exitCode: 0 };
|
|
11025
12658
|
}
|
|
11026
12659
|
|
|
11027
12660
|
// lib/cli/commands/list.ts
|
|
12661
|
+
import { basename as basename3 } from "node:path";
|
|
11028
12662
|
function workflowTypeToStatus(type) {
|
|
11029
12663
|
switch (type) {
|
|
11030
12664
|
case "refine":
|
|
@@ -11214,11 +12848,7 @@ async function processListType(context) {
|
|
|
11214
12848
|
emitListEvent(emitEvent, type, collectedItems);
|
|
11215
12849
|
}
|
|
11216
12850
|
}
|
|
11217
|
-
async function loadCorePrinciples(
|
|
11218
|
-
const corePath = getCorePrinciplesPath().replace(/\/$/, "");
|
|
11219
|
-
if (resolve4(localDirPath) === resolve4(corePath)) {
|
|
11220
|
-
return [];
|
|
11221
|
-
}
|
|
12851
|
+
async function loadCorePrinciples(excludeSet) {
|
|
11222
12852
|
const allCorePrinciples = await readAllCorePrinciples();
|
|
11223
12853
|
return allCorePrinciples.filter((p) => !isInternalPrinciple(p.content) && !excludeSet.has(p.slug));
|
|
11224
12854
|
}
|
|
@@ -11227,7 +12857,7 @@ async function processPrinciplesList(context) {
|
|
|
11227
12857
|
const { excludeCorePrinciples, tree } = context;
|
|
11228
12858
|
const excludeSet = new Set(excludeCorePrinciples ?? []);
|
|
11229
12859
|
const localDirPath = `${dustPath}/principles`;
|
|
11230
|
-
const corePrinciples = await loadCorePrinciples(
|
|
12860
|
+
const corePrinciples = await loadCorePrinciples(excludeSet);
|
|
11231
12861
|
const hasCorePrinciples = corePrinciples.length > 0;
|
|
11232
12862
|
const localDirExists = fileSystem.exists(localDirPath);
|
|
11233
12863
|
const localFiles = localDirExists ? await fileSystem.readdir(localDirPath) : [];
|
|
@@ -11272,12 +12902,11 @@ async function processPrinciplesList(context) {
|
|
|
11272
12902
|
}
|
|
11273
12903
|
} else {
|
|
11274
12904
|
if (hasCorePrinciples) {
|
|
11275
|
-
const corePath = getCorePrinciplesPath();
|
|
11276
12905
|
const coreEntries = corePrinciples.toSorted((a, b) => a.slug.localeCompare(b.slug)).map((p) => ({
|
|
11277
12906
|
slug: p.slug,
|
|
11278
12907
|
openingSentence: extractOpeningSentence(p.content)
|
|
11279
12908
|
}));
|
|
11280
|
-
const coreLines = formatPrinciplesSection(
|
|
12909
|
+
const coreLines = formatPrinciplesSection("\uD83C\uDFAF Core Principles (use: dust core principle <name>)", coreEntries);
|
|
11281
12910
|
for (const line of coreLines) {
|
|
11282
12911
|
stdout(line);
|
|
11283
12912
|
}
|
|
@@ -11400,9 +13029,9 @@ async function list(dependencies) {
|
|
|
11400
13029
|
}
|
|
11401
13030
|
|
|
11402
13031
|
// lib/loop/loop.ts
|
|
11403
|
-
import { existsSync
|
|
13032
|
+
import { existsSync, writeFileSync as writeFileSync2, unlinkSync as unlinkSync2 } from "node:fs";
|
|
11404
13033
|
import os3 from "node:os";
|
|
11405
|
-
import { join as
|
|
13034
|
+
import { join as join11 } from "node:path";
|
|
11406
13035
|
|
|
11407
13036
|
// lib/loop/parse-args.ts
|
|
11408
13037
|
var DEFAULT_MAX_ITERATIONS = 10;
|
|
@@ -11465,7 +13094,7 @@ async function setupDockerProxies(dockerResult, loopDependencies, sessionId, inc
|
|
|
11465
13094
|
const apiProxy = await createClaudeApiProxyServer();
|
|
11466
13095
|
stopApiProxy = apiProxy.stop;
|
|
11467
13096
|
const claudeApiProxyUrl = `http://${hostAddress}:${apiProxy.port}`;
|
|
11468
|
-
settingsFilePath =
|
|
13097
|
+
settingsFilePath = join11(os3.tmpdir(), `dust-claude-settings-${sessionId}.json`);
|
|
11469
13098
|
const settingsContent = generateApiKeyHelperSettings(claudeApiProxyUrl);
|
|
11470
13099
|
writeFileSync2(settingsFilePath, settingsContent, "utf-8");
|
|
11471
13100
|
log14(`created settings file at ${settingsFilePath}`);
|
|
@@ -11483,7 +13112,7 @@ function resolveDockerDependencies(loopDependencies) {
|
|
|
11483
13112
|
return {
|
|
11484
13113
|
spawn: loopDependencies.dockerDeps?.spawn ?? loopDependencies.spawn,
|
|
11485
13114
|
homedir: loopDependencies.dockerDeps?.homedir ?? os3.homedir,
|
|
11486
|
-
existsSync: loopDependencies.dockerDeps?.existsSync ??
|
|
13115
|
+
existsSync: loopDependencies.dockerDeps?.existsSync ?? existsSync
|
|
11487
13116
|
};
|
|
11488
13117
|
}
|
|
11489
13118
|
async function setupContainerProxies(dockerResult, containerRuntime, loopDependencies, sessionId) {
|
|
@@ -12067,6 +13696,7 @@ var commandRegistry = {
|
|
|
12067
13696
|
audit,
|
|
12068
13697
|
"bucket worker": bucketWorker,
|
|
12069
13698
|
"bucket tool": bucketTool,
|
|
13699
|
+
"core principle": corePrinciple,
|
|
12070
13700
|
focus,
|
|
12071
13701
|
"new task": newTask,
|
|
12072
13702
|
"new principle": newPrinciple,
|
|
@@ -12176,9 +13806,9 @@ function createGlobScanner(readdirFn) {
|
|
|
12176
13806
|
};
|
|
12177
13807
|
}
|
|
12178
13808
|
var defaultFileSystemPrimitives = {
|
|
12179
|
-
existsSync:
|
|
13809
|
+
existsSync: existsSync2,
|
|
12180
13810
|
statSync: statSync2,
|
|
12181
|
-
readFile:
|
|
13811
|
+
readFile: readFile3,
|
|
12182
13812
|
writeFile: writeFile2,
|
|
12183
13813
|
mkdir: mkdir2,
|
|
12184
13814
|
readdir: readdir2,
|