@joshski/dust 0.1.111 → 0.1.112
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/types.d.ts +5 -1
- package/dist/core-principles.js +608 -608
- package/dist/dust.js +695 -639
- package/dist/execution-order.d.ts +17 -0
- package/dist/execution-order.js +39 -0
- package/package.json +5 -1
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.112",
|
|
11
11
|
description: "Flow state for AI coding agents",
|
|
12
12
|
type: "module",
|
|
13
13
|
bin: {
|
|
@@ -56,6 +56,10 @@ var require_package = __commonJS((exports, module) => {
|
|
|
56
56
|
"./core-principles": {
|
|
57
57
|
import: "./dist/core-principles.js",
|
|
58
58
|
types: "./dist/core-principles.d.ts"
|
|
59
|
+
},
|
|
60
|
+
"./execution-order": {
|
|
61
|
+
import: "./dist/execution-order.js",
|
|
62
|
+
types: "./dist/execution-order.d.ts"
|
|
59
63
|
}
|
|
60
64
|
},
|
|
61
65
|
files: [
|
|
@@ -394,16 +398,22 @@ import {
|
|
|
394
398
|
// lib/git/file-sorter.ts
|
|
395
399
|
function createGitDirectoryFileSorter(gitRunner) {
|
|
396
400
|
return async (dir, files) => {
|
|
397
|
-
const
|
|
401
|
+
const results = await Promise.all(files.map(async (file) => {
|
|
398
402
|
const result = await gitRunner.run(["log", "-1", "--format=%ct", "--", file], dir);
|
|
399
|
-
const
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
timestamp: Number.isNaN(ts) ? Number.POSITIVE_INFINITY : ts
|
|
403
|
-
};
|
|
403
|
+
const epochSeconds = result.exitCode === 0 ? Number.parseInt(result.output.trim(), 10) : Number.NaN;
|
|
404
|
+
const lastCommittedAt = Number.isNaN(epochSeconds) ? null : new Date(epochSeconds * 1000).toISOString();
|
|
405
|
+
return { file, lastCommittedAt };
|
|
404
406
|
}));
|
|
405
|
-
|
|
406
|
-
|
|
407
|
+
results.sort((a, b) => {
|
|
408
|
+
if (a.lastCommittedAt === null && b.lastCommittedAt === null)
|
|
409
|
+
return 0;
|
|
410
|
+
if (a.lastCommittedAt === null)
|
|
411
|
+
return 1;
|
|
412
|
+
if (b.lastCommittedAt === null)
|
|
413
|
+
return -1;
|
|
414
|
+
return new Date(a.lastCommittedAt).getTime() - new Date(b.lastCommittedAt).getTime();
|
|
415
|
+
});
|
|
416
|
+
return results;
|
|
407
417
|
};
|
|
408
418
|
}
|
|
409
419
|
|
|
@@ -721,7 +731,7 @@ async function loadSettings(cwd, fileSystem, runtime) {
|
|
|
721
731
|
}
|
|
722
732
|
|
|
723
733
|
// lib/version.ts
|
|
724
|
-
var DUST_VERSION = "0.1.
|
|
734
|
+
var DUST_VERSION = "0.1.112";
|
|
725
735
|
|
|
726
736
|
// lib/cli/middleware.ts
|
|
727
737
|
function applyMiddleware(middlewares, execute) {
|
|
@@ -6098,6 +6108,43 @@ function extractFirstSentence2(paragraph) {
|
|
|
6098
6108
|
return match ? match[1] : null;
|
|
6099
6109
|
}
|
|
6100
6110
|
|
|
6111
|
+
// lib/execution-order.ts
|
|
6112
|
+
function computeExecutionOrder(nodes) {
|
|
6113
|
+
if (nodes.length === 0)
|
|
6114
|
+
return [];
|
|
6115
|
+
const sorted = [...nodes].toSorted((a, b) => {
|
|
6116
|
+
if (a.lastCommittedAt === null && b.lastCommittedAt === null)
|
|
6117
|
+
return 0;
|
|
6118
|
+
if (a.lastCommittedAt === null)
|
|
6119
|
+
return 1;
|
|
6120
|
+
if (b.lastCommittedAt === null)
|
|
6121
|
+
return -1;
|
|
6122
|
+
return new Date(a.lastCommittedAt).getTime() - new Date(b.lastCommittedAt).getTime();
|
|
6123
|
+
});
|
|
6124
|
+
const result = [];
|
|
6125
|
+
const completed = new Set;
|
|
6126
|
+
const nodeMap = new Map(nodes.map((n) => [n.slug, n]));
|
|
6127
|
+
while (result.length < nodes.length) {
|
|
6128
|
+
const next = sorted.find((node) => {
|
|
6129
|
+
if (completed.has(node.slug))
|
|
6130
|
+
return false;
|
|
6131
|
+
return node.blockedBy.every((slug) => completed.has(slug) || !nodeMap.has(slug));
|
|
6132
|
+
});
|
|
6133
|
+
if (!next) {
|
|
6134
|
+
for (const node of sorted) {
|
|
6135
|
+
if (!completed.has(node.slug)) {
|
|
6136
|
+
result.push({ node, executionOrder: result.length + 1 });
|
|
6137
|
+
completed.add(node.slug);
|
|
6138
|
+
}
|
|
6139
|
+
}
|
|
6140
|
+
break;
|
|
6141
|
+
}
|
|
6142
|
+
result.push({ node: next, executionOrder: result.length + 1 });
|
|
6143
|
+
completed.add(next.slug);
|
|
6144
|
+
}
|
|
6145
|
+
return result;
|
|
6146
|
+
}
|
|
6147
|
+
|
|
6101
6148
|
// lib/artifacts/workflow-tasks.ts
|
|
6102
6149
|
var CAPTURE_IDEA_PREFIX = "Add Idea: ";
|
|
6103
6150
|
var EXPEDITE_IDEA_PREFIX = "Expedite Idea: ";
|
|
@@ -6475,20 +6522,22 @@ function validateTaskType(artifact) {
|
|
|
6475
6522
|
function hasRequiredHeadings(content) {
|
|
6476
6523
|
return /^## Blocked By\s*$/m.test(content) && /^## Definition of Done\s*$/m.test(content);
|
|
6477
6524
|
}
|
|
6478
|
-
function
|
|
6525
|
+
function extractBlockedBySlugs(content) {
|
|
6479
6526
|
const blockedByMatch = content.match(/^## Blocked By\s*\n([\s\S]*?)(?=\n## |\n*$)/m);
|
|
6480
6527
|
const section = blockedByMatch[1].trim();
|
|
6481
6528
|
if (section === "(none)") {
|
|
6482
6529
|
return [];
|
|
6483
6530
|
}
|
|
6484
6531
|
const linkPattern = /\[.*?\]\(([^)]+\.md)\)/g;
|
|
6485
|
-
const
|
|
6532
|
+
const slugs = [];
|
|
6486
6533
|
let match = linkPattern.exec(section);
|
|
6487
6534
|
while (match !== null) {
|
|
6488
|
-
|
|
6535
|
+
const slugMatch = match[1].match(/([^/]+)\.md$/);
|
|
6536
|
+
if (slugMatch)
|
|
6537
|
+
slugs.push(slugMatch[1]);
|
|
6489
6538
|
match = linkPattern.exec(section);
|
|
6490
6539
|
}
|
|
6491
|
-
return
|
|
6540
|
+
return slugs;
|
|
6492
6541
|
}
|
|
6493
6542
|
async function findUnblockedTasks(cwd, fileSystem, directoryFileSorter) {
|
|
6494
6543
|
const dustPath = `${cwd}/.dust`;
|
|
@@ -6500,19 +6549,20 @@ async function findUnblockedTasks(cwd, fileSystem, directoryFileSorter) {
|
|
|
6500
6549
|
return { tasks: [], invalidTasks: [] };
|
|
6501
6550
|
}
|
|
6502
6551
|
const files = await fileSystem.readdir(tasksPath);
|
|
6503
|
-
|
|
6504
|
-
if (directoryFileSorter) {
|
|
6505
|
-
mdFiles = await directoryFileSorter(tasksPath, mdFiles);
|
|
6506
|
-
} else {
|
|
6507
|
-
mdFiles.sort((a, b) => {
|
|
6508
|
-
const aTime = fileSystem.getFileCreationTime(`${tasksPath}/${a}`);
|
|
6509
|
-
const bTime = fileSystem.getFileCreationTime(`${tasksPath}/${b}`);
|
|
6510
|
-
return aTime - bTime;
|
|
6511
|
-
});
|
|
6512
|
-
}
|
|
6552
|
+
const mdFiles = files.filter((f) => f.endsWith(".md"));
|
|
6513
6553
|
if (mdFiles.length === 0) {
|
|
6514
6554
|
return { tasks: [], invalidTasks: [] };
|
|
6515
6555
|
}
|
|
6556
|
+
let timestamps;
|
|
6557
|
+
if (directoryFileSorter) {
|
|
6558
|
+
const results = await directoryFileSorter(tasksPath, mdFiles);
|
|
6559
|
+
timestamps = new Map(results.map((r) => [r.file, r.lastCommittedAt]));
|
|
6560
|
+
} else {
|
|
6561
|
+
timestamps = new Map(mdFiles.map((f) => {
|
|
6562
|
+
const ms = fileSystem.getFileCreationTime(`${tasksPath}/${f}`);
|
|
6563
|
+
return [f, ms > 0 ? new Date(ms).toISOString() : null];
|
|
6564
|
+
}));
|
|
6565
|
+
}
|
|
6516
6566
|
const taskFiles = [];
|
|
6517
6567
|
for (const file of mdFiles) {
|
|
6518
6568
|
const filePath = `${tasksPath}/${file}`;
|
|
@@ -6533,16 +6583,22 @@ async function findUnblockedTasks(cwd, fileSystem, directoryFileSorter) {
|
|
|
6533
6583
|
});
|
|
6534
6584
|
}
|
|
6535
6585
|
}
|
|
6536
|
-
const
|
|
6586
|
+
const taskNodes = validTaskFiles.map(({ file, content }) => ({
|
|
6587
|
+
slug: file.replace(/\.md$/, ""),
|
|
6588
|
+
file,
|
|
6589
|
+
content,
|
|
6590
|
+
blockedBy: extractBlockedBySlugs(content),
|
|
6591
|
+
lastCommittedAt: timestamps.get(file) ?? null
|
|
6592
|
+
}));
|
|
6593
|
+
const ordered = computeExecutionOrder(taskNodes);
|
|
6594
|
+
const existingSlugs = new Set(taskNodes.map((t) => t.slug));
|
|
6537
6595
|
const tasks = [];
|
|
6538
|
-
for (const {
|
|
6539
|
-
const
|
|
6540
|
-
const hasIncompleteBlocker = blockers.some((blocker) => existingTasks.has(blocker));
|
|
6596
|
+
for (const { node } of ordered) {
|
|
6597
|
+
const hasIncompleteBlocker = node.blockedBy.some((slug) => existingSlugs.has(slug));
|
|
6541
6598
|
if (!hasIncompleteBlocker) {
|
|
6542
|
-
const title = extractTitle(content);
|
|
6543
|
-
const openingSentence = extractOpeningSentence(content);
|
|
6544
|
-
|
|
6545
|
-
tasks.push({ path: relativePath, title, openingSentence });
|
|
6599
|
+
const title = extractTitle(node.content);
|
|
6600
|
+
const openingSentence = extractOpeningSentence(node.content);
|
|
6601
|
+
tasks.push({ path: `.dust/tasks/${node.file}`, title, openingSentence });
|
|
6546
6602
|
}
|
|
6547
6603
|
}
|
|
6548
6604
|
return { tasks, invalidTasks };
|
|
@@ -12161,58 +12217,39 @@ async function check(dependencies, shellRunner, clock, _setInterval, _clearInter
|
|
|
12161
12217
|
// lib/bundled-core-principles.ts
|
|
12162
12218
|
var BUNDLED_PRINCIPLES = [
|
|
12163
12219
|
{
|
|
12164
|
-
slug: "
|
|
12165
|
-
content: `#
|
|
12166
|
-
|
|
12167
|
-
Dust should provide everything that is required (within reason) for an agent to be productive in an arbitrary codebase.
|
|
12220
|
+
slug: "design-for-testability",
|
|
12221
|
+
content: `# Design for Testability
|
|
12168
12222
|
|
|
12169
|
-
|
|
12223
|
+
Design code to be testable first; good structure follows naturally.
|
|
12170
12224
|
|
|
12171
|
-
|
|
12225
|
+
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.
|
|
12172
12226
|
|
|
12173
|
-
|
|
12227
|
+
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.
|
|
12174
12228
|
|
|
12175
|
-
|
|
12229
|
+
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.
|
|
12176
12230
|
|
|
12177
12231
|
## Parent Principle
|
|
12178
12232
|
|
|
12179
|
-
- [
|
|
12233
|
+
- [Decoupled Code](decoupled-code.md)
|
|
12180
12234
|
|
|
12181
12235
|
## Sub-Principles
|
|
12236
|
+
|
|
12237
|
+
- (none)
|
|
12182
12238
|
`
|
|
12183
12239
|
},
|
|
12184
12240
|
{
|
|
12185
|
-
slug: "
|
|
12186
|
-
content: `#
|
|
12187
|
-
|
|
12188
|
-
AI agents lower the cost of architectural exploration, making heavier upfront investment rational during the idea phase.
|
|
12189
|
-
|
|
12190
|
-
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.
|
|
12191
|
-
|
|
12192
|
-
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.
|
|
12193
|
-
|
|
12194
|
-
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.
|
|
12195
|
-
|
|
12196
|
-
## Convergence Criteria
|
|
12197
|
-
|
|
12198
|
-
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.
|
|
12199
|
-
|
|
12200
|
-
When exploration feels "done":
|
|
12201
|
-
|
|
12202
|
-
- Multiple approaches have been considered
|
|
12203
|
-
- Trade-offs between approaches are understood
|
|
12204
|
-
- The chosen direction has clear justification
|
|
12205
|
-
- Remaining uncertainty is about requirements, not design
|
|
12241
|
+
slug: "fast-feedback-loops",
|
|
12242
|
+
content: `# Fast Feedback Loops
|
|
12206
12243
|
|
|
12207
|
-
|
|
12244
|
+
The primary feedback loop — write code, run checks, see results — should be as fast as possible.
|
|
12208
12245
|
|
|
12209
|
-
|
|
12246
|
+
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.
|
|
12210
12247
|
|
|
12211
|
-
|
|
12248
|
+
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.
|
|
12212
12249
|
|
|
12213
12250
|
## Parent Principle
|
|
12214
12251
|
|
|
12215
|
-
- [
|
|
12252
|
+
- [Ideal Agent Developer Experience](ideal-agent-developer-experience.md)
|
|
12216
12253
|
|
|
12217
12254
|
## Sub-Principles
|
|
12218
12255
|
|
|
@@ -12220,99 +12257,77 @@ Ideas should document the alternatives considered and why they were ruled out. T
|
|
|
12220
12257
|
`
|
|
12221
12258
|
},
|
|
12222
12259
|
{
|
|
12223
|
-
slug: "
|
|
12224
|
-
content: `#
|
|
12225
|
-
|
|
12226
|
-
Design code to be testable first; good structure follows naturally.
|
|
12260
|
+
slug: "test-isolation",
|
|
12261
|
+
content: `# Test Isolation
|
|
12227
12262
|
|
|
12228
|
-
|
|
12263
|
+
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.
|
|
12229
12264
|
|
|
12230
|
-
|
|
12265
|
+
This means:
|
|
12266
|
+
- No shared mutable state between tests
|
|
12267
|
+
- No reliance on test execution order
|
|
12268
|
+
- No file system or environment pollution
|
|
12269
|
+
- Each test sets up its own dependencies
|
|
12231
12270
|
|
|
12232
|
-
|
|
12271
|
+
Test isolation enables parallel execution, makes failures easier to diagnose, and prevents cascading false failures when one test breaks.
|
|
12233
12272
|
|
|
12234
12273
|
## Parent Principle
|
|
12235
12274
|
|
|
12236
|
-
- [
|
|
12275
|
+
- [Make Changes with Confidence](make-changes-with-confidence.md)
|
|
12237
12276
|
|
|
12238
12277
|
## Sub-Principles
|
|
12239
12278
|
|
|
12240
|
-
- (
|
|
12279
|
+
- [Environment-Independent Tests](environment-independent-tests.md)
|
|
12241
12280
|
`
|
|
12242
12281
|
},
|
|
12243
12282
|
{
|
|
12244
|
-
slug: "
|
|
12245
|
-
content: `#
|
|
12283
|
+
slug: "boy-scout-rule",
|
|
12284
|
+
content: `# Boy Scout Rule
|
|
12246
12285
|
|
|
12247
|
-
|
|
12286
|
+
Always leave the code better than you found it.
|
|
12248
12287
|
|
|
12249
|
-
|
|
12288
|
+
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.
|
|
12250
12289
|
|
|
12251
|
-
|
|
12290
|
+
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.
|
|
12252
12291
|
|
|
12253
|
-
##
|
|
12292
|
+
## Parent Principle
|
|
12254
12293
|
|
|
12255
|
-
|
|
12294
|
+
- [Maintainable Codebase](maintainable-codebase.md)
|
|
12256
12295
|
|
|
12257
|
-
|
|
12258
|
-
// Avoid: flat paths that obscure hierarchy
|
|
12259
|
-
const fs = createFileSystemEmulator({
|
|
12260
|
-
files: new Map([['/project/.dust/principles/my-goal.md', '# My Goal']]),
|
|
12261
|
-
existingPaths: new Set(['/project/.dust/ideas']),
|
|
12262
|
-
})
|
|
12296
|
+
## Sub-Principles
|
|
12263
12297
|
|
|
12264
|
-
|
|
12265
|
-
|
|
12266
|
-
|
|
12267
|
-
|
|
12268
|
-
|
|
12269
|
-
|
|
12270
|
-
},
|
|
12271
|
-
ideas: {}
|
|
12272
|
-
}
|
|
12273
|
-
}
|
|
12274
|
-
})
|
|
12275
|
-
\`\`\`
|
|
12298
|
+
- (none)
|
|
12299
|
+
`
|
|
12300
|
+
},
|
|
12301
|
+
{
|
|
12302
|
+
slug: "atomic-commits",
|
|
12303
|
+
content: `# Atomic Commits
|
|
12276
12304
|
|
|
12277
|
-
|
|
12278
|
-
- Shows parent-child relationships through indentation
|
|
12279
|
-
- Makes empty directories explicit with empty objects
|
|
12280
|
-
- Requires no mental path concatenation to understand structure
|
|
12305
|
+
Each commit should tell a complete story, bundling implementation changes with their corresponding documentation updates.
|
|
12281
12306
|
|
|
12282
|
-
|
|
12307
|
+
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.
|
|
12283
12308
|
|
|
12284
|
-
|
|
12309
|
+
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.
|
|
12285
12310
|
|
|
12286
12311
|
## Parent Principle
|
|
12287
12312
|
|
|
12288
|
-
- [
|
|
12313
|
+
- [Repository Hygiene](repository-hygiene.md)
|
|
12289
12314
|
|
|
12290
12315
|
## Sub-Principles
|
|
12291
12316
|
|
|
12292
|
-
- (
|
|
12317
|
+
- [Traceable Decisions](traceable-decisions.md)
|
|
12293
12318
|
`
|
|
12294
12319
|
},
|
|
12295
12320
|
{
|
|
12296
|
-
slug: "
|
|
12297
|
-
content: `#
|
|
12298
|
-
|
|
12299
|
-
Dust should detect and enhance the experience for specific agents while remaining agnostic at its core.
|
|
12300
|
-
|
|
12301
|
-
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:
|
|
12302
|
-
|
|
12303
|
-
- **Detection** - Dust may detect which agent is running (e.g., Claude Code, Aider, Cursor) through environment variables, configuration, or other signals
|
|
12304
|
-
- **Enhancement** - Once detected, Dust can tailor its output format, prompts, or context to leverage that agent's specific strengths
|
|
12305
|
-
- **Graceful fallback** - When no specific agent is detected, Dust provides a generic experience that works with any agent
|
|
12306
|
-
|
|
12307
|
-
This principle complements Agent-Agnostic Design: the core functionality never requires a specific agent, but the experience improves when one is recognized.
|
|
12321
|
+
slug: "co-located-tests",
|
|
12322
|
+
content: `# Co-located Tests
|
|
12308
12323
|
|
|
12309
|
-
|
|
12324
|
+
Test files should live next to the code they test.
|
|
12310
12325
|
|
|
12311
|
-
|
|
12326
|
+
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.
|
|
12312
12327
|
|
|
12313
12328
|
## Parent Principle
|
|
12314
12329
|
|
|
12315
|
-
- [
|
|
12330
|
+
- [Intuitive Directory Structure](intuitive-directory-structure.md)
|
|
12316
12331
|
|
|
12317
12332
|
## Sub-Principles
|
|
12318
12333
|
|
|
@@ -12320,18 +12335,20 @@ Internal
|
|
|
12320
12335
|
`
|
|
12321
12336
|
},
|
|
12322
12337
|
{
|
|
12323
|
-
slug: "
|
|
12324
|
-
content: `#
|
|
12338
|
+
slug: "broken-windows",
|
|
12339
|
+
content: `# Broken Windows
|
|
12325
12340
|
|
|
12326
|
-
|
|
12341
|
+
Don't leave broken windows unrepaired.
|
|
12327
12342
|
|
|
12328
|
-
|
|
12343
|
+
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.
|
|
12329
12344
|
|
|
12330
|
-
|
|
12345
|
+
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.
|
|
12346
|
+
|
|
12347
|
+
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.
|
|
12331
12348
|
|
|
12332
12349
|
## Parent Principle
|
|
12333
12350
|
|
|
12334
|
-
- [
|
|
12351
|
+
- [Maintainable Codebase](maintainable-codebase.md)
|
|
12335
12352
|
|
|
12336
12353
|
## Sub-Principles
|
|
12337
12354
|
|
|
@@ -12339,57 +12356,45 @@ Dust should help projects identify files that are too large, modules that are to
|
|
|
12339
12356
|
`
|
|
12340
12357
|
},
|
|
12341
12358
|
{
|
|
12342
|
-
slug: "
|
|
12343
|
-
content: `#
|
|
12344
|
-
|
|
12345
|
-
When a big test fails, it should be self-evident how to diagnose and fix the failure.
|
|
12359
|
+
slug: "trunk-based-development",
|
|
12360
|
+
content: `# Trunk-Based Development
|
|
12346
12361
|
|
|
12347
|
-
|
|
12362
|
+
Dust is designed to support a non-branching workflow where developers commit directly to a single main branch.
|
|
12348
12363
|
|
|
12349
|
-
|
|
12364
|
+
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.
|
|
12350
12365
|
|
|
12351
|
-
|
|
12352
|
-
\`\`\`javascript
|
|
12353
|
-
// Bad: "expected true, received false" — what events arrived?
|
|
12354
|
-
expect(events.some(e => e.type === 'check-passed')).toBe(true)
|
|
12366
|
+
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.
|
|
12355
12367
|
|
|
12356
|
-
|
|
12357
|
-
expect(events.map(e => e.type)).toContain('check-passed')
|
|
12358
|
-
\`\`\`
|
|
12368
|
+
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.
|
|
12359
12369
|
|
|
12360
|
-
|
|
12361
|
-
\`\`\`javascript
|
|
12362
|
-
// Bad: "expected 2, received 0" — what requests were captured?
|
|
12363
|
-
expect(requests.length).toBe(2)
|
|
12370
|
+
See: https://trunkbaseddevelopment.com/
|
|
12364
12371
|
|
|
12365
|
-
|
|
12366
|
-
expect(requests).toHaveLength(2) // vitest shows the array
|
|
12367
|
-
\`\`\`
|
|
12372
|
+
## Parent Principle
|
|
12368
12373
|
|
|
12369
|
-
|
|
12370
|
-
\`\`\`javascript
|
|
12371
|
-
// Bad: silently passes when settings is undefined
|
|
12372
|
-
if (settings) {
|
|
12373
|
-
expect(JSON.parse(settings).key).toBeDefined()
|
|
12374
|
-
}
|
|
12374
|
+
- [Repository Hygiene](repository-hygiene.md)
|
|
12375
12375
|
|
|
12376
|
-
|
|
12377
|
-
expect(settings).toBeDefined()
|
|
12378
|
-
const parsed = JSON.parse(settings!)
|
|
12379
|
-
expect(parsed.key).toBeDefined()
|
|
12380
|
-
\`\`\`
|
|
12376
|
+
## Sub-Principles
|
|
12381
12377
|
|
|
12382
|
-
|
|
12378
|
+
(none)
|
|
12379
|
+
`
|
|
12380
|
+
},
|
|
12381
|
+
{
|
|
12382
|
+
slug: "environment-independent-tests",
|
|
12383
|
+
content: `# Environment-Independent Tests
|
|
12383
12384
|
|
|
12384
|
-
|
|
12385
|
+
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.
|
|
12385
12386
|
|
|
12386
|
-
|
|
12387
|
+
Concretely, tests should never depend on:
|
|
12388
|
+
- Ambient environment variables (e.g. \`CLAUDECODE\`, \`CI\`, \`HOME\`)
|
|
12389
|
+
- The current working directory or filesystem layout of the host machine
|
|
12390
|
+
- Network availability or external services
|
|
12391
|
+
- The identity of the user or agent running the tests
|
|
12387
12392
|
|
|
12388
|
-
|
|
12393
|
+
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.
|
|
12389
12394
|
|
|
12390
12395
|
## Parent Principle
|
|
12391
12396
|
|
|
12392
|
-
- [
|
|
12397
|
+
- [Test Isolation](test-isolation.md)
|
|
12393
12398
|
|
|
12394
12399
|
## Sub-Principles
|
|
12395
12400
|
|
|
@@ -12397,48 +12402,53 @@ Work supports this principle when every assertion in a system or integration tes
|
|
|
12397
12402
|
`
|
|
12398
12403
|
},
|
|
12399
12404
|
{
|
|
12400
|
-
slug: "
|
|
12401
|
-
content: `#
|
|
12405
|
+
slug: "comprehensive-assertions",
|
|
12406
|
+
content: `# Comprehensive Assertions
|
|
12402
12407
|
|
|
12403
|
-
|
|
12408
|
+
Assert the whole, not the parts.
|
|
12404
12409
|
|
|
12405
|
-
|
|
12410
|
+
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.
|
|
12406
12411
|
|
|
12407
|
-
|
|
12412
|
+
Small assertions are like yes/no questions to a witness. A whole-object assertion is like asking "tell me what you saw."
|
|
12408
12413
|
|
|
12409
|
-
|
|
12414
|
+
## In practice
|
|
12410
12415
|
|
|
12411
|
-
|
|
12416
|
+
Collapse multiple partial assertions into one comprehensive assertion:
|
|
12412
12417
|
|
|
12413
|
-
|
|
12418
|
+
\`\`\`javascript
|
|
12419
|
+
// Fragmented — each failure is a narrow keyhole
|
|
12420
|
+
expect(result.name).toBe("Alice");
|
|
12421
|
+
expect(result.age).toBe(30);
|
|
12422
|
+
expect(result.role).toBe("admin");
|
|
12414
12423
|
|
|
12415
|
-
|
|
12424
|
+
// Whole — a failure diff tells the full story
|
|
12425
|
+
expect(result).toEqual({
|
|
12426
|
+
name: "Alice",
|
|
12427
|
+
age: 30,
|
|
12428
|
+
role: "admin",
|
|
12429
|
+
});
|
|
12430
|
+
\`\`\`
|
|
12416
12431
|
|
|
12417
|
-
-
|
|
12418
|
-
- [Fast Feedback Loops](fast-feedback-loops.md)
|
|
12419
|
-
- [Slow Feedback Coping](slow-feedback-coping.md)
|
|
12420
|
-
- [Development Traceability](development-traceability.md)
|
|
12421
|
-
- [Context-Optimised Code](context-optimised-code.md)
|
|
12422
|
-
- [Exploratory Tooling](exploratory-tooling.md)
|
|
12423
|
-
- [Debugging Tooling](debugging-tooling.md)
|
|
12424
|
-
- [Self-Contained Repository](self-contained-repository.md)
|
|
12425
|
-
`
|
|
12426
|
-
},
|
|
12427
|
-
{
|
|
12428
|
-
slug: "broken-windows",
|
|
12429
|
-
content: `# Broken Windows
|
|
12432
|
+
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.
|
|
12430
12433
|
|
|
12431
|
-
|
|
12434
|
+
The same applies to arrays:
|
|
12432
12435
|
|
|
12433
|
-
|
|
12436
|
+
\`\`\`javascript
|
|
12437
|
+
// Avoid: partial assertions that hide the actual state
|
|
12438
|
+
expect(array).toContain('apples')
|
|
12439
|
+
expect(array).toContain('oranges')
|
|
12434
12440
|
|
|
12435
|
-
|
|
12441
|
+
// Prefer: one assertion that reveals the full picture on failure
|
|
12442
|
+
expect(array).toEqual(['apples', 'oranges'])
|
|
12443
|
+
\`\`\`
|
|
12436
12444
|
|
|
12437
|
-
|
|
12445
|
+
## How to evaluate
|
|
12446
|
+
|
|
12447
|
+
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.
|
|
12438
12448
|
|
|
12439
12449
|
## Parent Principle
|
|
12440
12450
|
|
|
12441
|
-
- [
|
|
12451
|
+
- [Make Changes with Confidence](make-changes-with-confidence.md)
|
|
12442
12452
|
|
|
12443
12453
|
## Sub-Principles
|
|
12444
12454
|
|
|
@@ -12446,95 +12456,113 @@ This principle complements the [Boy Scout Rule](boy-scout-rule.md): the Boy Scou
|
|
|
12446
12456
|
`
|
|
12447
12457
|
},
|
|
12448
12458
|
{
|
|
12449
|
-
slug: "
|
|
12450
|
-
content: `#
|
|
12451
|
-
|
|
12452
|
-
Dust should reveal details progressively as a way of achieving context window efficiency.
|
|
12459
|
+
slug: "maintainable-codebase",
|
|
12460
|
+
content: `# Maintainable Codebase
|
|
12453
12461
|
|
|
12454
|
-
|
|
12462
|
+
The dust codebase should be easy to understand, modify, and extend.
|
|
12455
12463
|
|
|
12456
|
-
This
|
|
12464
|
+
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.
|
|
12457
12465
|
|
|
12458
12466
|
## Parent Principle
|
|
12459
12467
|
|
|
12460
|
-
- [
|
|
12468
|
+
- [Agentic Flow State](agentic-flow-state.md)
|
|
12461
12469
|
|
|
12462
12470
|
## Sub-Principles
|
|
12463
12471
|
|
|
12464
|
-
- (
|
|
12472
|
+
- [Make Changes with Confidence](make-changes-with-confidence.md)
|
|
12473
|
+
- [Minimal Dependencies](minimal-dependencies.md)
|
|
12474
|
+
- [Intuitive Directory Structure](intuitive-directory-structure.md)
|
|
12475
|
+
- [Repository Hygiene](repository-hygiene.md)
|
|
12476
|
+
- [Naming Matters](naming-matters.md)
|
|
12477
|
+
- [Reasonably DRY](reasonably-dry.md)
|
|
12478
|
+
- [Make the Change Easy](make-the-change-easy.md)
|
|
12479
|
+
- [Boy Scout Rule](boy-scout-rule.md)
|
|
12480
|
+
- [Broken Windows](broken-windows.md)
|
|
12465
12481
|
`
|
|
12466
12482
|
},
|
|
12467
12483
|
{
|
|
12468
|
-
slug: "
|
|
12469
|
-
content: `#
|
|
12484
|
+
slug: "context-window-efficiency",
|
|
12485
|
+
content: `# Context Window Efficiency
|
|
12470
12486
|
|
|
12471
|
-
Dust
|
|
12487
|
+
Dust should be designed with short attention spans in mind.
|
|
12472
12488
|
|
|
12473
|
-
|
|
12489
|
+
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.
|
|
12474
12490
|
|
|
12475
|
-
|
|
12491
|
+
This means favoring brevity over completeness, using consistent structures that are fast to parse, and avoiding redundant information across files.
|
|
12476
12492
|
|
|
12477
12493
|
## Parent Principle
|
|
12478
12494
|
|
|
12479
|
-
- [
|
|
12495
|
+
- [Agent Autonomy](agent-autonomy.md)
|
|
12480
12496
|
|
|
12481
12497
|
## Sub-Principles
|
|
12482
12498
|
|
|
12483
|
-
- [
|
|
12484
|
-
- [Some Big Design Up Front](some-big-design-up-front.md)
|
|
12499
|
+
- [Progressive Disclosure](progressive-disclosure.md)
|
|
12485
12500
|
`
|
|
12486
12501
|
},
|
|
12487
12502
|
{
|
|
12488
|
-
slug: "
|
|
12489
|
-
content: `#
|
|
12503
|
+
slug: "human-ai-collaboration",
|
|
12504
|
+
content: `# Human-AI Collaboration
|
|
12490
12505
|
|
|
12491
|
-
|
|
12506
|
+
Dust exists to enable effective collaboration between humans and AI agents on complex projects.
|
|
12492
12507
|
|
|
12493
|
-
|
|
12508
|
+
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.
|
|
12494
12509
|
|
|
12495
|
-
|
|
12510
|
+
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.
|
|
12496
12511
|
|
|
12497
12512
|
## Parent Principle
|
|
12498
12513
|
|
|
12499
|
-
- [
|
|
12514
|
+
- [Agentic Flow State](agentic-flow-state.md)
|
|
12500
12515
|
|
|
12501
12516
|
## Sub-Principles
|
|
12502
12517
|
|
|
12503
|
-
- (
|
|
12518
|
+
- [Agent Autonomy](agent-autonomy.md)
|
|
12519
|
+
- [Easy Adoption](easy-adoption.md)
|
|
12520
|
+
- [Ideal Agent Developer Experience](ideal-agent-developer-experience.md)
|
|
12521
|
+
- [Lightweight Planning](lightweight-planning.md)
|
|
12504
12522
|
`
|
|
12505
12523
|
},
|
|
12506
12524
|
{
|
|
12507
|
-
slug: "
|
|
12508
|
-
content: `#
|
|
12525
|
+
slug: "functional-core-imperative-shell",
|
|
12526
|
+
content: `# Functional Core, Imperative Shell
|
|
12509
12527
|
|
|
12510
|
-
|
|
12528
|
+
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.
|
|
12511
12529
|
|
|
12512
|
-
|
|
12530
|
+
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.
|
|
12531
|
+
|
|
12532
|
+
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.
|
|
12513
12533
|
|
|
12514
12534
|
## Parent Principle
|
|
12515
12535
|
|
|
12516
|
-
- [
|
|
12536
|
+
- [Decoupled Code](decoupled-code.md)
|
|
12517
12537
|
|
|
12518
12538
|
## Sub-Principles
|
|
12519
12539
|
|
|
12520
|
-
-
|
|
12540
|
+
- (none)
|
|
12521
12541
|
`
|
|
12522
12542
|
},
|
|
12523
12543
|
{
|
|
12524
|
-
slug: "
|
|
12525
|
-
content: `#
|
|
12544
|
+
slug: "keep-unit-tests-pure",
|
|
12545
|
+
content: `# Keep Unit Tests Pure
|
|
12526
12546
|
|
|
12527
|
-
|
|
12547
|
+
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:
|
|
12528
12548
|
|
|
12529
|
-
|
|
12549
|
+
- Accesses a database
|
|
12550
|
+
- Communicates over a network
|
|
12551
|
+
- Touches the file system
|
|
12552
|
+
- Cannot run concurrently with other tests
|
|
12553
|
+
- Requires special environment setup
|
|
12530
12554
|
|
|
12531
|
-
|
|
12555
|
+
"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.
|
|
12532
12556
|
|
|
12533
|
-
|
|
12557
|
+
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.
|
|
12558
|
+
|
|
12559
|
+
## Migration Guidance
|
|
12560
|
+
|
|
12561
|
+
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.
|
|
12534
12562
|
|
|
12535
12563
|
## Parent Principle
|
|
12536
12564
|
|
|
12537
|
-
- [
|
|
12565
|
+
- [Make Changes with Confidence](make-changes-with-confidence.md)
|
|
12538
12566
|
|
|
12539
12567
|
## Sub-Principles
|
|
12540
12568
|
|
|
@@ -12542,16 +12570,16 @@ Note: This principle directly supports [Lightweight Planning](lightweight-planni
|
|
|
12542
12570
|
`
|
|
12543
12571
|
},
|
|
12544
12572
|
{
|
|
12545
|
-
slug: "
|
|
12546
|
-
content: `#
|
|
12573
|
+
slug: "runtime-agnostic-tests",
|
|
12574
|
+
content: `# Runtime Agnostic Tests
|
|
12547
12575
|
|
|
12548
|
-
Dust
|
|
12576
|
+
Dust's test suite should work across JavaScript runtimes.
|
|
12549
12577
|
|
|
12550
|
-
|
|
12578
|
+
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.
|
|
12551
12579
|
|
|
12552
12580
|
## Parent Principle
|
|
12553
12581
|
|
|
12554
|
-
- [
|
|
12582
|
+
- [Minimal Dependencies](minimal-dependencies.md)
|
|
12555
12583
|
|
|
12556
12584
|
## Sub-Principles
|
|
12557
12585
|
|
|
@@ -12559,18 +12587,18 @@ Scripts and tooling should execute quickly so developers can iterate rapidly. Sl
|
|
|
12559
12587
|
`
|
|
12560
12588
|
},
|
|
12561
12589
|
{
|
|
12562
|
-
slug: "
|
|
12563
|
-
content: `#
|
|
12590
|
+
slug: "unsurprising-ux",
|
|
12591
|
+
content: `# Unsurprising UX
|
|
12564
12592
|
|
|
12565
|
-
|
|
12593
|
+
The user interface should be as "guessable" as possible.
|
|
12566
12594
|
|
|
12567
|
-
|
|
12595
|
+
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.
|
|
12568
12596
|
|
|
12569
|
-
|
|
12597
|
+
Surprising behavior erodes trust and slows people down. Unsurprising behavior lets users stay in flow.
|
|
12570
12598
|
|
|
12571
12599
|
## Parent Principle
|
|
12572
12600
|
|
|
12573
|
-
- [
|
|
12601
|
+
- [Easy Adoption](easy-adoption.md)
|
|
12574
12602
|
|
|
12575
12603
|
## Sub-Principles
|
|
12576
12604
|
|
|
@@ -12578,12 +12606,12 @@ This approach improves testability (each test controls its own dependencies), re
|
|
|
12578
12606
|
`
|
|
12579
12607
|
},
|
|
12580
12608
|
{
|
|
12581
|
-
slug: "
|
|
12582
|
-
content: `#
|
|
12609
|
+
slug: "unit-test-coverage",
|
|
12610
|
+
content: `# Unit Test Coverage
|
|
12583
12611
|
|
|
12584
|
-
|
|
12612
|
+
Complete unit test coverage ensures low-level tests give users direct feedback as they change the code.
|
|
12585
12613
|
|
|
12586
|
-
|
|
12614
|
+
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.
|
|
12587
12615
|
|
|
12588
12616
|
## Parent Principle
|
|
12589
12617
|
|
|
@@ -12595,18 +12623,22 @@ Concretely, checks should pin their tool versions via the project's dependency m
|
|
|
12595
12623
|
`
|
|
12596
12624
|
},
|
|
12597
12625
|
{
|
|
12598
|
-
slug: "
|
|
12599
|
-
content: `#
|
|
12626
|
+
slug: "cross-platform-compatibility",
|
|
12627
|
+
content: `# Cross-Platform Compatibility
|
|
12600
12628
|
|
|
12601
|
-
|
|
12629
|
+
Dust should work consistently across operating systems: Linux, macOS, and Windows.
|
|
12602
12630
|
|
|
12603
|
-
|
|
12631
|
+
This means:
|
|
12632
|
+
- Avoiding platform-specific shell commands or syntax
|
|
12633
|
+
- Using cross-platform path handling
|
|
12634
|
+
- Testing on multiple platforms when possible
|
|
12635
|
+
- Documenting any platform-specific limitations
|
|
12604
12636
|
|
|
12605
|
-
|
|
12637
|
+
Cross-platform support broadens adoption and ensures teams with mixed environments can collaborate effectively.
|
|
12606
12638
|
|
|
12607
12639
|
## Parent Principle
|
|
12608
12640
|
|
|
12609
|
-
- [
|
|
12641
|
+
- [Easy Adoption](easy-adoption.md)
|
|
12610
12642
|
|
|
12611
12643
|
## Sub-Principles
|
|
12612
12644
|
|
|
@@ -12614,62 +12646,50 @@ Strategies include separating fast and slow test suites, running slow checks asy
|
|
|
12614
12646
|
`
|
|
12615
12647
|
},
|
|
12616
12648
|
{
|
|
12617
|
-
slug: "
|
|
12618
|
-
content: `#
|
|
12649
|
+
slug: "vcs-independence",
|
|
12650
|
+
content: `# VCS Independence
|
|
12619
12651
|
|
|
12620
|
-
|
|
12652
|
+
Dust should work independently of any specific version control system.
|
|
12621
12653
|
|
|
12622
|
-
|
|
12654
|
+
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.
|
|
12623
12655
|
|
|
12624
12656
|
## Parent Principle
|
|
12625
12657
|
|
|
12626
|
-
- [
|
|
12658
|
+
- [Easy Adoption](easy-adoption.md)
|
|
12627
12659
|
|
|
12628
12660
|
## Sub-Principles
|
|
12629
12661
|
|
|
12630
|
-
-
|
|
12631
|
-
- [Decoupled Code](decoupled-code.md)
|
|
12632
|
-
- [Fast Feedback](fast-feedback.md)
|
|
12633
|
-
- [Lint Everything](lint-everything.md)
|
|
12634
|
-
- [Readable Test Data](readable-test-data.md)
|
|
12635
|
-
- [Reproducible Checks](reproducible-checks.md)
|
|
12636
|
-
- [Stop the Line](stop-the-line.md)
|
|
12637
|
-
- [Keep Unit Tests Pure](keep-unit-tests-pure.md)
|
|
12638
|
-
- [Test Isolation](test-isolation.md)
|
|
12639
|
-
- [Self-Diagnosing Tests](self-diagnosing-tests.md)
|
|
12640
|
-
- [Unit Test Coverage](unit-test-coverage.md)
|
|
12662
|
+
- (none)
|
|
12641
12663
|
`
|
|
12642
12664
|
},
|
|
12643
12665
|
{
|
|
12644
|
-
slug: "
|
|
12645
|
-
content: `#
|
|
12666
|
+
slug: "self-contained-repository",
|
|
12667
|
+
content: `# Self-Contained Repository
|
|
12646
12668
|
|
|
12647
|
-
|
|
12669
|
+
Where possible, developers and agents should have everything they need to be productive, within the repository.
|
|
12648
12670
|
|
|
12649
|
-
This
|
|
12650
|
-
- No shared mutable state between tests
|
|
12651
|
-
- No reliance on test execution order
|
|
12652
|
-
- No file system or environment pollution
|
|
12653
|
-
- Each test sets up its own dependencies
|
|
12671
|
+
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.
|
|
12654
12672
|
|
|
12655
|
-
|
|
12673
|
+
## Applicability
|
|
12674
|
+
|
|
12675
|
+
Internal
|
|
12656
12676
|
|
|
12657
12677
|
## Parent Principle
|
|
12658
12678
|
|
|
12659
|
-
- [
|
|
12679
|
+
- [Ideal Agent Developer Experience](ideal-agent-developer-experience.md)
|
|
12660
12680
|
|
|
12661
12681
|
## Sub-Principles
|
|
12662
12682
|
|
|
12663
|
-
-
|
|
12683
|
+
- (none)
|
|
12664
12684
|
`
|
|
12665
12685
|
},
|
|
12666
12686
|
{
|
|
12667
|
-
slug: "
|
|
12668
|
-
content: `#
|
|
12687
|
+
slug: "minimal-dependencies",
|
|
12688
|
+
content: `# Minimal Dependencies
|
|
12669
12689
|
|
|
12670
|
-
Dust
|
|
12690
|
+
Dust should avoid coupling to specific tools so we can switch to better alternatives as they emerge.
|
|
12671
12691
|
|
|
12672
|
-
|
|
12692
|
+
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.
|
|
12673
12693
|
|
|
12674
12694
|
## Parent Principle
|
|
12675
12695
|
|
|
@@ -12677,72 +12697,88 @@ This includes proper gitignore configuration to exclude build artifacts, depende
|
|
|
12677
12697
|
|
|
12678
12698
|
## Sub-Principles
|
|
12679
12699
|
|
|
12680
|
-
- [
|
|
12681
|
-
- [Trunk-Based Development](trunk-based-development.md)
|
|
12700
|
+
- [Runtime Agnostic Tests](runtime-agnostic-tests.md)
|
|
12682
12701
|
`
|
|
12683
12702
|
},
|
|
12684
12703
|
{
|
|
12685
|
-
slug: "
|
|
12686
|
-
content: `#
|
|
12704
|
+
slug: "agent-specific-enhancement",
|
|
12705
|
+
content: `# Agent-Specific Enhancement
|
|
12687
12706
|
|
|
12688
|
-
|
|
12707
|
+
Dust should detect and enhance the experience for specific agents while remaining agnostic at its core.
|
|
12689
12708
|
|
|
12690
|
-
|
|
12709
|
+
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:
|
|
12691
12710
|
|
|
12692
|
-
Dust
|
|
12711
|
+
- **Detection** - Dust may detect which agent is running (e.g., Claude Code, Aider, Cursor) through environment variables, configuration, or other signals
|
|
12712
|
+
- **Enhancement** - Once detected, Dust can tailor its output format, prompts, or context to leverage that agent's specific strengths
|
|
12713
|
+
- **Graceful fallback** - When no specific agent is detected, Dust provides a generic experience that works with any agent
|
|
12693
12714
|
|
|
12694
|
-
|
|
12695
|
-
- **Immediate feedback**: Fast feedback loops let you see results quickly. Each change confirms you're on track or shows you what to adjust.
|
|
12696
|
-
- **Challenge-skill balance**: Small units of work and agent autonomy keep you in the zone - challenged enough to stay engaged, supported enough to succeed.
|
|
12697
|
-
- **Context window efficiency**: Progressive disclosure and artifact summarization ensure agents have the right context without overflow.
|
|
12698
|
-
- **Comprehensive guard rails**: Lint rules, type checks, and automated validation catch mistakes before they compound.
|
|
12715
|
+
This principle complements Agent-Agnostic Design: the core functionality never requires a specific agent, but the experience improves when one is recognized.
|
|
12699
12716
|
|
|
12700
|
-
|
|
12717
|
+
## Applicability
|
|
12718
|
+
|
|
12719
|
+
Internal
|
|
12701
12720
|
|
|
12702
12721
|
## Parent Principle
|
|
12703
12722
|
|
|
12704
|
-
- (
|
|
12723
|
+
- [Agent Autonomy](agent-autonomy.md)
|
|
12705
12724
|
|
|
12706
12725
|
## Sub-Principles
|
|
12707
12726
|
|
|
12708
|
-
-
|
|
12709
|
-
- [Maintainable Codebase](maintainable-codebase.md)
|
|
12727
|
+
- (none)
|
|
12710
12728
|
`
|
|
12711
12729
|
},
|
|
12712
12730
|
{
|
|
12713
|
-
slug: "
|
|
12714
|
-
content: `#
|
|
12731
|
+
slug: "self-diagnosing-tests",
|
|
12732
|
+
content: `# Self-Diagnosing Tests
|
|
12715
12733
|
|
|
12716
|
-
|
|
12734
|
+
When a big test fails, it should be self-evident how to diagnose and fix the failure.
|
|
12717
12735
|
|
|
12718
|
-
|
|
12736
|
+
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.
|
|
12719
12737
|
|
|
12720
|
-
##
|
|
12738
|
+
## Anti-patterns
|
|
12721
12739
|
|
|
12722
|
-
|
|
12740
|
+
**Boolean flattening** — collapsing a rich value into true/false before asserting:
|
|
12741
|
+
\`\`\`javascript
|
|
12742
|
+
// Bad: "expected true, received false" — what events arrived?
|
|
12743
|
+
expect(events.some(e => e.type === 'check-passed')).toBe(true)
|
|
12723
12744
|
|
|
12724
|
-
|
|
12745
|
+
// Good: shows the actual event types on failure
|
|
12746
|
+
expect(events.map(e => e.type)).toContain('check-passed')
|
|
12747
|
+
\`\`\`
|
|
12725
12748
|
|
|
12726
|
-
-
|
|
12727
|
-
|
|
12728
|
-
|
|
12729
|
-
|
|
12730
|
-
slug: "agent-context-inference",
|
|
12731
|
-
content: `# Agent Context Inference
|
|
12749
|
+
**Length-only assertions** — checking count without showing contents:
|
|
12750
|
+
\`\`\`javascript
|
|
12751
|
+
// Bad: "expected 2, received 0" — what requests were captured?
|
|
12752
|
+
expect(requests.length).toBe(2)
|
|
12732
12753
|
|
|
12733
|
-
|
|
12754
|
+
// Good: shows the actual requests on failure
|
|
12755
|
+
expect(requests).toHaveLength(2) // vitest shows the array
|
|
12756
|
+
\`\`\`
|
|
12734
12757
|
|
|
12735
|
-
|
|
12758
|
+
**Silent guards** — using \`if\` where an assertion belongs:
|
|
12759
|
+
\`\`\`javascript
|
|
12760
|
+
// Bad: silently passes when settings is undefined
|
|
12761
|
+
if (settings) {
|
|
12762
|
+
expect(JSON.parse(settings).key).toBeDefined()
|
|
12763
|
+
}
|
|
12736
12764
|
|
|
12737
|
-
|
|
12765
|
+
// Good: fails explicitly if settings is missing
|
|
12766
|
+
expect(settings).toBeDefined()
|
|
12767
|
+
const parsed = JSON.parse(settings!)
|
|
12768
|
+
expect(parsed.key).toBeDefined()
|
|
12769
|
+
\`\`\`
|
|
12738
12770
|
|
|
12739
|
-
##
|
|
12771
|
+
## The test
|
|
12740
12772
|
|
|
12741
|
-
|
|
12773
|
+
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.
|
|
12774
|
+
|
|
12775
|
+
## How to evaluate
|
|
12776
|
+
|
|
12777
|
+
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.
|
|
12742
12778
|
|
|
12743
12779
|
## Parent Principle
|
|
12744
12780
|
|
|
12745
|
-
- [
|
|
12781
|
+
- [Make Changes with Confidence](make-changes-with-confidence.md)
|
|
12746
12782
|
|
|
12747
12783
|
## Sub-Principles
|
|
12748
12784
|
|
|
@@ -12750,59 +12786,63 @@ Internal
|
|
|
12750
12786
|
`
|
|
12751
12787
|
},
|
|
12752
12788
|
{
|
|
12753
|
-
slug: "
|
|
12754
|
-
content: `#
|
|
12789
|
+
slug: "slow-feedback-coping",
|
|
12790
|
+
content: `# Slow Feedback Coping
|
|
12755
12791
|
|
|
12756
|
-
|
|
12792
|
+
Some feedback is unavoidably slow — dust should offer coping strategies rather than pretending it can be eliminated.
|
|
12757
12793
|
|
|
12758
|
-
|
|
12794
|
+
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.
|
|
12795
|
+
|
|
12796
|
+
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.
|
|
12759
12797
|
|
|
12760
12798
|
## Parent Principle
|
|
12761
12799
|
|
|
12762
|
-
- [
|
|
12800
|
+
- [Ideal Agent Developer Experience](ideal-agent-developer-experience.md)
|
|
12763
12801
|
|
|
12764
12802
|
## Sub-Principles
|
|
12765
12803
|
|
|
12766
|
-
-
|
|
12767
|
-
- [Clarity Over Brevity](clarity-over-brevity.md)
|
|
12804
|
+
- (none)
|
|
12768
12805
|
`
|
|
12769
12806
|
},
|
|
12770
12807
|
{
|
|
12771
|
-
slug: "
|
|
12772
|
-
content: `#
|
|
12808
|
+
slug: "agentic-flow-state",
|
|
12809
|
+
content: `# Agentic Flow State
|
|
12773
12810
|
|
|
12774
|
-
|
|
12811
|
+
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.
|
|
12775
12812
|
|
|
12776
|
-
|
|
12813
|
+
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.
|
|
12777
12814
|
|
|
12778
|
-
|
|
12815
|
+
Dust's design targets these conditions directly:
|
|
12779
12816
|
|
|
12780
|
-
|
|
12817
|
+
- **Clear goals**: Task files and lightweight planning give you a concrete target. You know exactly what you're building next.
|
|
12818
|
+
- **Immediate feedback**: Fast feedback loops let you see results quickly. Each change confirms you're on track or shows you what to adjust.
|
|
12819
|
+
- **Challenge-skill balance**: Small units of work and agent autonomy keep you in the zone - challenged enough to stay engaged, supported enough to succeed.
|
|
12820
|
+
- **Context window efficiency**: Progressive disclosure and artifact summarization ensure agents have the right context without overflow.
|
|
12821
|
+
- **Comprehensive guard rails**: Lint rules, type checks, and automated validation catch mistakes before they compound.
|
|
12781
12822
|
|
|
12782
|
-
|
|
12823
|
+
Everything dust does serves flow. When agents stay in flow, they produce better work, sustain their momentum, and complete tasks autonomously.
|
|
12783
12824
|
|
|
12784
12825
|
## Parent Principle
|
|
12785
12826
|
|
|
12786
|
-
-
|
|
12827
|
+
- (none)
|
|
12787
12828
|
|
|
12788
12829
|
## Sub-Principles
|
|
12789
12830
|
|
|
12790
|
-
- (
|
|
12831
|
+
- [Human-AI Collaboration](human-ai-collaboration.md)
|
|
12832
|
+
- [Maintainable Codebase](maintainable-codebase.md)
|
|
12791
12833
|
`
|
|
12792
12834
|
},
|
|
12793
12835
|
{
|
|
12794
|
-
slug: "
|
|
12795
|
-
content: `#
|
|
12796
|
-
|
|
12797
|
-
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.
|
|
12836
|
+
slug: "reproducible-checks",
|
|
12837
|
+
content: `# Reproducible Checks
|
|
12798
12838
|
|
|
12799
|
-
|
|
12839
|
+
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.
|
|
12800
12840
|
|
|
12801
|
-
|
|
12841
|
+
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.
|
|
12802
12842
|
|
|
12803
12843
|
## Parent Principle
|
|
12804
12844
|
|
|
12805
|
-
- [
|
|
12845
|
+
- [Make Changes with Confidence](make-changes-with-confidence.md)
|
|
12806
12846
|
|
|
12807
12847
|
## Sub-Principles
|
|
12808
12848
|
|
|
@@ -12810,22 +12850,16 @@ The functional core contains business logic as pure functions that take values a
|
|
|
12810
12850
|
`
|
|
12811
12851
|
},
|
|
12812
12852
|
{
|
|
12813
|
-
slug: "
|
|
12814
|
-
content: `#
|
|
12815
|
-
|
|
12816
|
-
Structured logging and tracing help agents understand system behaviour without resorting to ad-hoc testing cycles.
|
|
12817
|
-
|
|
12818
|
-
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.
|
|
12819
|
-
|
|
12820
|
-
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.
|
|
12853
|
+
slug: "task-first-workflow",
|
|
12854
|
+
content: `# Task-First Workflow
|
|
12821
12855
|
|
|
12822
|
-
|
|
12856
|
+
Work should be captured as a task before implementation begins, creating traceability between intent and outcome.
|
|
12823
12857
|
|
|
12824
|
-
|
|
12858
|
+
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.
|
|
12825
12859
|
|
|
12826
12860
|
## Parent Principle
|
|
12827
12861
|
|
|
12828
|
-
- [
|
|
12862
|
+
- [Lightweight Planning](lightweight-planning.md)
|
|
12829
12863
|
|
|
12830
12864
|
## Sub-Principles
|
|
12831
12865
|
|
|
@@ -12833,107 +12867,96 @@ Internal
|
|
|
12833
12867
|
`
|
|
12834
12868
|
},
|
|
12835
12869
|
{
|
|
12836
|
-
slug: "
|
|
12837
|
-
content: `#
|
|
12838
|
-
|
|
12839
|
-
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:
|
|
12840
|
-
|
|
12841
|
-
- Accesses a database
|
|
12842
|
-
- Communicates over a network
|
|
12843
|
-
- Touches the file system
|
|
12844
|
-
- Cannot run concurrently with other tests
|
|
12845
|
-
- Requires special environment setup
|
|
12870
|
+
slug: "ideal-agent-developer-experience",
|
|
12871
|
+
content: `# Ideal Agent Developer Experience
|
|
12846
12872
|
|
|
12847
|
-
|
|
12873
|
+
The agent is the developer. The human is the CEO. Dust is the PM.
|
|
12848
12874
|
|
|
12849
|
-
|
|
12875
|
+
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.
|
|
12850
12876
|
|
|
12851
|
-
|
|
12877
|
+
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.
|
|
12852
12878
|
|
|
12853
|
-
|
|
12879
|
+
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.
|
|
12854
12880
|
|
|
12855
12881
|
## Parent Principle
|
|
12856
12882
|
|
|
12857
|
-
- [
|
|
12883
|
+
- [Human-AI Collaboration](human-ai-collaboration.md)
|
|
12858
12884
|
|
|
12859
12885
|
## Sub-Principles
|
|
12860
12886
|
|
|
12861
|
-
- (
|
|
12887
|
+
- [Comprehensive Test Coverage](comprehensive-test-coverage.md)
|
|
12888
|
+
- [Fast Feedback Loops](fast-feedback-loops.md)
|
|
12889
|
+
- [Slow Feedback Coping](slow-feedback-coping.md)
|
|
12890
|
+
- [Development Traceability](development-traceability.md)
|
|
12891
|
+
- [Context-Optimised Code](context-optimised-code.md)
|
|
12892
|
+
- [Exploratory Tooling](exploratory-tooling.md)
|
|
12893
|
+
- [Debugging Tooling](debugging-tooling.md)
|
|
12894
|
+
- [Self-Contained Repository](self-contained-repository.md)
|
|
12862
12895
|
`
|
|
12863
12896
|
},
|
|
12864
12897
|
{
|
|
12865
|
-
slug: "
|
|
12866
|
-
content: `#
|
|
12867
|
-
|
|
12868
|
-
Test files should live next to the code they test.
|
|
12869
|
-
|
|
12870
|
-
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.
|
|
12871
|
-
|
|
12872
|
-
## Parent Principle
|
|
12873
|
-
|
|
12874
|
-
- [Intuitive Directory Structure](intuitive-directory-structure.md)
|
|
12898
|
+
slug: "agent-context-inference",
|
|
12899
|
+
content: `# Agent Context Inference
|
|
12875
12900
|
|
|
12876
|
-
|
|
12901
|
+
Terse human prompts should trigger the correct agent action.
|
|
12877
12902
|
|
|
12878
|
-
|
|
12879
|
-
`
|
|
12880
|
-
},
|
|
12881
|
-
{
|
|
12882
|
-
slug: "human-ai-collaboration",
|
|
12883
|
-
content: `# Human-AI Collaboration
|
|
12903
|
+
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.
|
|
12884
12904
|
|
|
12885
|
-
|
|
12905
|
+
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.
|
|
12886
12906
|
|
|
12887
|
-
|
|
12907
|
+
## Applicability
|
|
12888
12908
|
|
|
12889
|
-
|
|
12909
|
+
Internal
|
|
12890
12910
|
|
|
12891
12911
|
## Parent Principle
|
|
12892
12912
|
|
|
12893
|
-
- [
|
|
12913
|
+
- [Agent Autonomy](agent-autonomy.md)
|
|
12894
12914
|
|
|
12895
12915
|
## Sub-Principles
|
|
12896
12916
|
|
|
12897
|
-
-
|
|
12898
|
-
- [Easy Adoption](easy-adoption.md)
|
|
12899
|
-
- [Ideal Agent Developer Experience](ideal-agent-developer-experience.md)
|
|
12900
|
-
- [Lightweight Planning](lightweight-planning.md)
|
|
12917
|
+
- (none)
|
|
12901
12918
|
`
|
|
12902
12919
|
},
|
|
12903
12920
|
{
|
|
12904
|
-
slug: "
|
|
12905
|
-
content: `#
|
|
12921
|
+
slug: "agent-autonomy",
|
|
12922
|
+
content: `# Agent Autonomy
|
|
12906
12923
|
|
|
12907
|
-
Dust
|
|
12924
|
+
Dust exists to enable AI agents to produce work autonomously.
|
|
12908
12925
|
|
|
12909
|
-
|
|
12926
|
+
With sufficient planning and small enough units, this works much better in practice.
|
|
12910
12927
|
|
|
12911
12928
|
## Parent Principle
|
|
12912
12929
|
|
|
12913
|
-
- [
|
|
12930
|
+
- [Human-AI Collaboration](human-ai-collaboration.md)
|
|
12914
12931
|
|
|
12915
12932
|
## Sub-Principles
|
|
12916
12933
|
|
|
12917
|
-
- (
|
|
12934
|
+
- [Actionable Errors](actionable-errors.md)
|
|
12935
|
+
- [Batteries Included](batteries-included.md)
|
|
12936
|
+
- [Agent-Agnostic Design](agent-agnostic-design.md)
|
|
12937
|
+
- [Agent Context Inference](agent-context-inference.md)
|
|
12938
|
+
- [Agent-Specific Enhancement](agent-specific-enhancement.md)
|
|
12939
|
+
- [Context Window Efficiency](context-window-efficiency.md)
|
|
12940
|
+
- [Small Units](small-units.md)
|
|
12918
12941
|
`
|
|
12919
12942
|
},
|
|
12920
12943
|
{
|
|
12921
|
-
slug: "
|
|
12922
|
-
content: `#
|
|
12944
|
+
slug: "stubs-over-mocks",
|
|
12945
|
+
content: `# Stubs Over Mocks
|
|
12946
|
+
|
|
12947
|
+
Prefer hand-rolled stubs over mocks, in unit tests. Stubs keep tests focused on observable behavior instead of implementation details.
|
|
12948
|
+
|
|
12949
|
+
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.
|
|
12923
12950
|
|
|
12924
|
-
|
|
12951
|
+
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.
|
|
12925
12952
|
|
|
12926
|
-
|
|
12927
|
-
- Ambient environment variables (e.g. \`CLAUDECODE\`, \`CI\`, \`HOME\`)
|
|
12928
|
-
- The current working directory or filesystem layout of the host machine
|
|
12929
|
-
- Network availability or external services
|
|
12930
|
-
- The identity of the user or agent running the tests
|
|
12953
|
+
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.
|
|
12931
12954
|
|
|
12932
|
-
|
|
12955
|
+
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.
|
|
12933
12956
|
|
|
12934
12957
|
## Parent Principle
|
|
12935
12958
|
|
|
12936
|
-
- [
|
|
12959
|
+
- [Decoupled Code](decoupled-code.md)
|
|
12937
12960
|
|
|
12938
12961
|
## Sub-Principles
|
|
12939
12962
|
|
|
@@ -12964,95 +12987,116 @@ Internal
|
|
|
12964
12987
|
`
|
|
12965
12988
|
},
|
|
12966
12989
|
{
|
|
12967
|
-
slug: "
|
|
12968
|
-
content: `#
|
|
12969
|
-
|
|
12970
|
-
Each commit should tell a complete story, bundling implementation changes with their corresponding documentation updates.
|
|
12990
|
+
slug: "consistent-naming",
|
|
12991
|
+
content: `# Consistent Naming
|
|
12971
12992
|
|
|
12972
|
-
|
|
12993
|
+
Names should follow established conventions within each category to reduce cognitive load.
|
|
12973
12994
|
|
|
12974
|
-
|
|
12995
|
+
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.
|
|
12975
12996
|
|
|
12976
12997
|
## Parent Principle
|
|
12977
12998
|
|
|
12978
|
-
- [
|
|
12999
|
+
- [Naming Matters](naming-matters.md)
|
|
12979
13000
|
|
|
12980
13001
|
## Sub-Principles
|
|
12981
13002
|
|
|
12982
|
-
-
|
|
13003
|
+
- (none)
|
|
12983
13004
|
`
|
|
12984
13005
|
},
|
|
12985
13006
|
{
|
|
12986
|
-
slug: "
|
|
12987
|
-
content: `#
|
|
13007
|
+
slug: "lightweight-planning",
|
|
13008
|
+
content: `# Lightweight Planning
|
|
12988
13009
|
|
|
12989
|
-
Dust
|
|
13010
|
+
Dust aims to be a minimal, low-overhead planning system that stays relevant over time.
|
|
12990
13011
|
|
|
12991
|
-
|
|
13012
|
+
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.
|
|
12992
13013
|
|
|
12993
|
-
The
|
|
13014
|
+
The system avoids the staleness problem by deferring detail until the last responsible moment and deleting completed work rather than archiving it.
|
|
12994
13015
|
|
|
12995
|
-
|
|
13016
|
+
## Parent Principle
|
|
12996
13017
|
|
|
12997
|
-
|
|
13018
|
+
- [Human-AI Collaboration](human-ai-collaboration.md)
|
|
13019
|
+
|
|
13020
|
+
## Sub-Principles
|
|
13021
|
+
|
|
13022
|
+
- [Task-First Workflow](task-first-workflow.md)
|
|
13023
|
+
- [Some Big Design Up Front](some-big-design-up-front.md)
|
|
13024
|
+
`
|
|
13025
|
+
},
|
|
13026
|
+
{
|
|
13027
|
+
slug: "easy-adoption",
|
|
13028
|
+
content: `# Easy Adoption
|
|
13029
|
+
|
|
13030
|
+
Dust should be trivially easy to adopt in any repository.
|
|
13031
|
+
|
|
13032
|
+
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.
|
|
13033
|
+
|
|
13034
|
+
This lowers the barrier to entry and encourages experimentation.
|
|
12998
13035
|
|
|
12999
13036
|
## Parent Principle
|
|
13000
13037
|
|
|
13001
|
-
- [
|
|
13038
|
+
- [Human-AI Collaboration](human-ai-collaboration.md)
|
|
13002
13039
|
|
|
13003
13040
|
## Sub-Principles
|
|
13004
13041
|
|
|
13005
|
-
(
|
|
13042
|
+
- [Cross-Platform Compatibility](cross-platform-compatibility.md)
|
|
13043
|
+
- [Unsurprising UX](unsurprising-ux.md)
|
|
13044
|
+
- [VCS Independence](vcs-independence.md)
|
|
13006
13045
|
`
|
|
13007
13046
|
},
|
|
13008
13047
|
{
|
|
13009
|
-
slug: "
|
|
13010
|
-
content: `#
|
|
13048
|
+
slug: "intuitive-directory-structure",
|
|
13049
|
+
content: `# Intuitive Directory Structure
|
|
13011
13050
|
|
|
13012
|
-
|
|
13051
|
+
Code should be organized around related concerns in clearly named directories.
|
|
13013
13052
|
|
|
13014
|
-
When
|
|
13053
|
+
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.
|
|
13015
13054
|
|
|
13016
|
-
|
|
13055
|
+
## Parent Principle
|
|
13017
13056
|
|
|
13018
|
-
|
|
13057
|
+
- [Maintainable Codebase](maintainable-codebase.md)
|
|
13019
13058
|
|
|
13020
|
-
|
|
13059
|
+
## Sub-Principles
|
|
13021
13060
|
|
|
13022
|
-
|
|
13023
|
-
|
|
13024
|
-
|
|
13025
|
-
|
|
13026
|
-
|
|
13061
|
+
- [Co-located Tests](co-located-tests.md)
|
|
13062
|
+
`
|
|
13063
|
+
},
|
|
13064
|
+
{
|
|
13065
|
+
slug: "lint-everything",
|
|
13066
|
+
content: `# Lint Everything
|
|
13027
13067
|
|
|
13028
|
-
|
|
13029
|
-
expect(result).toEqual({
|
|
13030
|
-
name: "Alice",
|
|
13031
|
-
age: 30,
|
|
13032
|
-
role: "admin",
|
|
13033
|
-
});
|
|
13034
|
-
\`\`\`
|
|
13068
|
+
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.
|
|
13035
13069
|
|
|
13036
|
-
|
|
13070
|
+
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.
|
|
13037
13071
|
|
|
13038
|
-
|
|
13072
|
+
This project lints:
|
|
13073
|
+
- TypeScript (type checking and style)
|
|
13074
|
+
- Markdown (broken links, required sections)
|
|
13075
|
+
- Task files (structure validation)
|
|
13076
|
+
- Principle hierarchy (parent/child consistency)
|
|
13039
13077
|
|
|
13040
|
-
|
|
13041
|
-
// Avoid: partial assertions that hide the actual state
|
|
13042
|
-
expect(array).toContain('apples')
|
|
13043
|
-
expect(array).toContain('oranges')
|
|
13078
|
+
## Parent Principle
|
|
13044
13079
|
|
|
13045
|
-
|
|
13046
|
-
expect(array).toEqual(['apples', 'oranges'])
|
|
13047
|
-
\`\`\`
|
|
13080
|
+
- [Make Changes with Confidence](make-changes-with-confidence.md)
|
|
13048
13081
|
|
|
13049
|
-
##
|
|
13082
|
+
## Sub-Principles
|
|
13050
13083
|
|
|
13051
|
-
|
|
13084
|
+
(none)
|
|
13085
|
+
`
|
|
13086
|
+
},
|
|
13087
|
+
{
|
|
13088
|
+
slug: "progressive-disclosure",
|
|
13089
|
+
content: `# Progressive Disclosure
|
|
13090
|
+
|
|
13091
|
+
Dust should reveal details progressively as a way of achieving context window efficiency.
|
|
13092
|
+
|
|
13093
|
+
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.
|
|
13094
|
+
|
|
13095
|
+
This layered approach keeps initial reads lightweight while preserving access to complete information when needed.
|
|
13052
13096
|
|
|
13053
13097
|
## Parent Principle
|
|
13054
13098
|
|
|
13055
|
-
- [
|
|
13099
|
+
- [Context Window Efficiency](context-window-efficiency.md)
|
|
13056
13100
|
|
|
13057
13101
|
## Sub-Principles
|
|
13058
13102
|
|
|
@@ -13060,22 +13104,18 @@ Work supports this principle when test failures tell a rich story — showing th
|
|
|
13060
13104
|
`
|
|
13061
13105
|
},
|
|
13062
13106
|
{
|
|
13063
|
-
slug: "
|
|
13064
|
-
content: `#
|
|
13107
|
+
slug: "context-optimised-code",
|
|
13108
|
+
content: `# Context-Optimised Code
|
|
13065
13109
|
|
|
13066
|
-
|
|
13110
|
+
Code should be structured so that agents can understand and modify it within their context window constraints.
|
|
13067
13111
|
|
|
13068
|
-
|
|
13069
|
-
- Avoiding platform-specific shell commands or syntax
|
|
13070
|
-
- Using cross-platform path handling
|
|
13071
|
-
- Testing on multiple platforms when possible
|
|
13072
|
-
- Documenting any platform-specific limitations
|
|
13112
|
+
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.
|
|
13073
13113
|
|
|
13074
|
-
|
|
13114
|
+
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.
|
|
13075
13115
|
|
|
13076
13116
|
## Parent Principle
|
|
13077
13117
|
|
|
13078
|
-
- [
|
|
13118
|
+
- [Ideal Agent Developer Experience](ideal-agent-developer-experience.md)
|
|
13079
13119
|
|
|
13080
13120
|
## Sub-Principles
|
|
13081
13121
|
|
|
@@ -13083,22 +13123,37 @@ Cross-platform support broadens adoption and ensures teams with mixed environmen
|
|
|
13083
13123
|
`
|
|
13084
13124
|
},
|
|
13085
13125
|
{
|
|
13086
|
-
slug: "
|
|
13087
|
-
content: `#
|
|
13126
|
+
slug: "some-big-design-up-front",
|
|
13127
|
+
content: `# Some Big Design Up Front
|
|
13088
13128
|
|
|
13089
|
-
|
|
13129
|
+
AI agents lower the cost of architectural exploration, making heavier upfront investment rational during the idea phase.
|
|
13090
13130
|
|
|
13091
|
-
|
|
13131
|
+
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.
|
|
13092
13132
|
|
|
13093
|
-
|
|
13133
|
+
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.
|
|
13094
13134
|
|
|
13095
|
-
|
|
13135
|
+
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.
|
|
13096
13136
|
|
|
13097
|
-
|
|
13137
|
+
## Convergence Criteria
|
|
13138
|
+
|
|
13139
|
+
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.
|
|
13140
|
+
|
|
13141
|
+
When exploration feels "done":
|
|
13142
|
+
|
|
13143
|
+
- Multiple approaches have been considered
|
|
13144
|
+
- Trade-offs between approaches are understood
|
|
13145
|
+
- The chosen direction has clear justification
|
|
13146
|
+
- Remaining uncertainty is about requirements, not design
|
|
13147
|
+
|
|
13148
|
+
If a task requires significant design decisions during execution, it wasn't ready to be a task.
|
|
13149
|
+
|
|
13150
|
+
## Documenting Alternatives
|
|
13151
|
+
|
|
13152
|
+
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.
|
|
13098
13153
|
|
|
13099
13154
|
## Parent Principle
|
|
13100
13155
|
|
|
13101
|
-
- [
|
|
13156
|
+
- [Lightweight Planning](lightweight-planning.md)
|
|
13102
13157
|
|
|
13103
13158
|
## Sub-Principles
|
|
13104
13159
|
|
|
@@ -13106,16 +13161,16 @@ Internal
|
|
|
13106
13161
|
`
|
|
13107
13162
|
},
|
|
13108
13163
|
{
|
|
13109
|
-
slug: "
|
|
13110
|
-
content: `#
|
|
13164
|
+
slug: "traceable-decisions",
|
|
13165
|
+
content: `# Traceable Decisions
|
|
13111
13166
|
|
|
13112
|
-
|
|
13167
|
+
The commit history should explain why changes were made, not just what changed.
|
|
13113
13168
|
|
|
13114
|
-
|
|
13169
|
+
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.
|
|
13115
13170
|
|
|
13116
13171
|
## Parent Principle
|
|
13117
13172
|
|
|
13118
|
-
- [
|
|
13173
|
+
- [Atomic Commits](atomic-commits.md)
|
|
13119
13174
|
|
|
13120
13175
|
## Sub-Principles
|
|
13121
13176
|
|
|
@@ -13123,16 +13178,16 @@ Extracting shared code too eagerly can create tight coupling, obscure intent, an
|
|
|
13123
13178
|
`
|
|
13124
13179
|
},
|
|
13125
13180
|
{
|
|
13126
|
-
slug: "
|
|
13127
|
-
content: `#
|
|
13181
|
+
slug: "fast-feedback",
|
|
13182
|
+
content: `# Fast Feedback
|
|
13128
13183
|
|
|
13129
|
-
Dust
|
|
13184
|
+
Dust should provide fast feedback loops for developers.
|
|
13130
13185
|
|
|
13131
|
-
|
|
13186
|
+
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.
|
|
13132
13187
|
|
|
13133
13188
|
## Parent Principle
|
|
13134
13189
|
|
|
13135
|
-
- [
|
|
13190
|
+
- [Make Changes with Confidence](make-changes-with-confidence.md)
|
|
13136
13191
|
|
|
13137
13192
|
## Sub-Principles
|
|
13138
13193
|
|
|
@@ -13140,43 +13195,50 @@ Tests should use standard JavaScript testing patterns that work across Node.js,
|
|
|
13140
13195
|
`
|
|
13141
13196
|
},
|
|
13142
13197
|
{
|
|
13143
|
-
slug: "
|
|
13144
|
-
content: `#
|
|
13198
|
+
slug: "decoupled-code",
|
|
13199
|
+
content: `# Decoupled Code
|
|
13145
13200
|
|
|
13146
|
-
|
|
13201
|
+
Code should be organized into independent units with explicit dependencies.
|
|
13147
13202
|
|
|
13148
|
-
|
|
13203
|
+
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.
|
|
13149
13204
|
|
|
13150
13205
|
## Parent Principle
|
|
13151
13206
|
|
|
13152
|
-
- [
|
|
13207
|
+
- [Make Changes with Confidence](make-changes-with-confidence.md)
|
|
13153
13208
|
|
|
13154
13209
|
## Sub-Principles
|
|
13155
13210
|
|
|
13156
|
-
- (
|
|
13211
|
+
- [Dependency Injection](dependency-injection.md)
|
|
13212
|
+
- [Stubs Over Mocks](stubs-over-mocks.md)
|
|
13213
|
+
- [Functional Core, Imperative Shell](functional-core-imperative-shell.md)
|
|
13214
|
+
- [Design for Testability](design-for-testability.md)
|
|
13157
13215
|
`
|
|
13158
13216
|
},
|
|
13159
13217
|
{
|
|
13160
|
-
slug: "
|
|
13161
|
-
content: `#
|
|
13218
|
+
slug: "make-changes-with-confidence",
|
|
13219
|
+
content: `# Make Changes with Confidence
|
|
13162
13220
|
|
|
13163
|
-
|
|
13221
|
+
Developers should be able to modify code without fear of breaking existing behavior.
|
|
13164
13222
|
|
|
13165
|
-
|
|
13223
|
+
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.
|
|
13166
13224
|
|
|
13167
13225
|
## Parent Principle
|
|
13168
13226
|
|
|
13169
|
-
- [
|
|
13227
|
+
- [Maintainable Codebase](maintainable-codebase.md)
|
|
13170
13228
|
|
|
13171
13229
|
## Sub-Principles
|
|
13172
13230
|
|
|
13173
|
-
- [
|
|
13174
|
-
- [
|
|
13175
|
-
- [
|
|
13176
|
-
- [
|
|
13177
|
-
- [
|
|
13178
|
-
- [
|
|
13179
|
-
- [
|
|
13231
|
+
- [Comprehensive Assertions](comprehensive-assertions.md)
|
|
13232
|
+
- [Decoupled Code](decoupled-code.md)
|
|
13233
|
+
- [Fast Feedback](fast-feedback.md)
|
|
13234
|
+
- [Lint Everything](lint-everything.md)
|
|
13235
|
+
- [Readable Test Data](readable-test-data.md)
|
|
13236
|
+
- [Reproducible Checks](reproducible-checks.md)
|
|
13237
|
+
- [Stop the Line](stop-the-line.md)
|
|
13238
|
+
- [Keep Unit Tests Pure](keep-unit-tests-pure.md)
|
|
13239
|
+
- [Test Isolation](test-isolation.md)
|
|
13240
|
+
- [Self-Diagnosing Tests](self-diagnosing-tests.md)
|
|
13241
|
+
- [Unit Test Coverage](unit-test-coverage.md)
|
|
13180
13242
|
`
|
|
13181
13243
|
},
|
|
13182
13244
|
{
|
|
@@ -13197,18 +13259,24 @@ Abbreviated names like \`ctx\`, \`deps\`, \`fs\`, or \`args\` save a few keystro
|
|
|
13197
13259
|
`
|
|
13198
13260
|
},
|
|
13199
13261
|
{
|
|
13200
|
-
slug: "
|
|
13201
|
-
content: `#
|
|
13262
|
+
slug: "agent-agnostic-design",
|
|
13263
|
+
content: `# Agent-Agnostic Design
|
|
13202
13264
|
|
|
13203
|
-
|
|
13265
|
+
Dust should work with multiple agents without favoring one.
|
|
13204
13266
|
|
|
13205
|
-
|
|
13267
|
+
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.
|
|
13206
13268
|
|
|
13207
|
-
Dust
|
|
13269
|
+
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.
|
|
13270
|
+
|
|
13271
|
+
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.
|
|
13272
|
+
|
|
13273
|
+
## Applicability
|
|
13274
|
+
|
|
13275
|
+
Internal
|
|
13208
13276
|
|
|
13209
13277
|
## Parent Principle
|
|
13210
13278
|
|
|
13211
|
-
- [
|
|
13279
|
+
- [Agent Autonomy](agent-autonomy.md)
|
|
13212
13280
|
|
|
13213
13281
|
## Sub-Principles
|
|
13214
13282
|
|
|
@@ -13216,39 +13284,51 @@ Dust should help projects measure the speed of their feedback loops, identify bo
|
|
|
13216
13284
|
`
|
|
13217
13285
|
},
|
|
13218
13286
|
{
|
|
13219
|
-
slug: "
|
|
13220
|
-
content: `#
|
|
13221
|
-
|
|
13222
|
-
For each desired change, make the change easy, then make the easy change.
|
|
13287
|
+
slug: "readable-test-data",
|
|
13288
|
+
content: `# Readable Test Data
|
|
13223
13289
|
|
|
13224
|
-
|
|
13290
|
+
Test data setup should use natural structures that mirror what they represent.
|
|
13225
13291
|
|
|
13226
|
-
|
|
13292
|
+
## Why it matters
|
|
13227
13293
|
|
|
13228
|
-
|
|
13294
|
+
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.
|
|
13229
13295
|
|
|
13230
|
-
|
|
13296
|
+
## In practice
|
|
13231
13297
|
|
|
13232
|
-
|
|
13298
|
+
Prefer literal structures that visually match the domain:
|
|
13233
13299
|
|
|
13234
|
-
|
|
13235
|
-
|
|
13236
|
-
|
|
13237
|
-
|
|
13238
|
-
|
|
13239
|
-
|
|
13300
|
+
\`\`\`javascript
|
|
13301
|
+
// Avoid: flat paths that obscure hierarchy
|
|
13302
|
+
const fs = createFileSystemEmulator({
|
|
13303
|
+
files: new Map([['/project/.dust/principles/my-goal.md', '# My Goal']]),
|
|
13304
|
+
existingPaths: new Set(['/project/.dust/ideas']),
|
|
13305
|
+
})
|
|
13240
13306
|
|
|
13241
|
-
|
|
13307
|
+
// Prefer: nested object that mirrors file system structure
|
|
13308
|
+
const fs = createFileSystemEmulator({
|
|
13309
|
+
project: {
|
|
13310
|
+
'.dust': {
|
|
13311
|
+
principles: {
|
|
13312
|
+
'my-goal.md': '# My Goal'
|
|
13313
|
+
},
|
|
13314
|
+
ideas: {}
|
|
13315
|
+
}
|
|
13316
|
+
}
|
|
13317
|
+
})
|
|
13318
|
+
\`\`\`
|
|
13242
13319
|
|
|
13243
|
-
|
|
13320
|
+
The nested form:
|
|
13321
|
+
- Shows parent-child relationships through indentation
|
|
13322
|
+
- Makes empty directories explicit with empty objects
|
|
13323
|
+
- Requires no mental path concatenation to understand structure
|
|
13244
13324
|
|
|
13245
|
-
##
|
|
13325
|
+
## How to evaluate
|
|
13246
13326
|
|
|
13247
|
-
|
|
13327
|
+
Work supports this principle when test setup data uses structures that visually resemble what they represent, reducing cognitive load for readers.
|
|
13248
13328
|
|
|
13249
13329
|
## Parent Principle
|
|
13250
13330
|
|
|
13251
|
-
- [
|
|
13331
|
+
- [Make Changes with Confidence](make-changes-with-confidence.md)
|
|
13252
13332
|
|
|
13253
13333
|
## Sub-Principles
|
|
13254
13334
|
|
|
@@ -13256,16 +13336,16 @@ Internal
|
|
|
13256
13336
|
`
|
|
13257
13337
|
},
|
|
13258
13338
|
{
|
|
13259
|
-
slug: "
|
|
13260
|
-
content: `#
|
|
13339
|
+
slug: "reasonably-dry",
|
|
13340
|
+
content: `# Reasonably DRY
|
|
13261
13341
|
|
|
13262
|
-
|
|
13342
|
+
Don't repeat yourself is a good principle, but don't overdo it.
|
|
13263
13343
|
|
|
13264
|
-
|
|
13344
|
+
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.
|
|
13265
13345
|
|
|
13266
13346
|
## Parent Principle
|
|
13267
13347
|
|
|
13268
|
-
- [
|
|
13348
|
+
- [Maintainable Codebase](maintainable-codebase.md)
|
|
13269
13349
|
|
|
13270
13350
|
## Sub-Principles
|
|
13271
13351
|
|
|
@@ -13273,16 +13353,21 @@ Commit messages should capture intent and context that would otherwise be lost.
|
|
|
13273
13353
|
`
|
|
13274
13354
|
},
|
|
13275
13355
|
{
|
|
13276
|
-
slug: "
|
|
13277
|
-
content: `#
|
|
13356
|
+
slug: "actionable-errors",
|
|
13357
|
+
content: `# Actionable Errors
|
|
13278
13358
|
|
|
13279
|
-
|
|
13359
|
+
Error messages should tell you what to do next, not just what went wrong.
|
|
13280
13360
|
|
|
13281
|
-
|
|
13361
|
+
When something fails, the message should provide:
|
|
13362
|
+
- A clear description of the problem
|
|
13363
|
+
- Specific guidance on how to fix it
|
|
13364
|
+
- Context needed to take the next step
|
|
13365
|
+
|
|
13366
|
+
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.
|
|
13282
13367
|
|
|
13283
13368
|
## Parent Principle
|
|
13284
13369
|
|
|
13285
|
-
- [
|
|
13370
|
+
- [Agent Autonomy](agent-autonomy.md)
|
|
13286
13371
|
|
|
13287
13372
|
## Sub-Principles
|
|
13288
13373
|
|
|
@@ -13290,84 +13375,70 @@ Excluding system tests from coverage reporting focuses attention on unit tests -
|
|
|
13290
13375
|
`
|
|
13291
13376
|
},
|
|
13292
13377
|
{
|
|
13293
|
-
slug: "
|
|
13294
|
-
content: `#
|
|
13378
|
+
slug: "make-the-change-easy",
|
|
13379
|
+
content: `# Make the Change Easy
|
|
13295
13380
|
|
|
13296
|
-
|
|
13381
|
+
For each desired change, make the change easy, then make the easy change.
|
|
13297
13382
|
|
|
13298
|
-
|
|
13383
|
+
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.
|
|
13384
|
+
|
|
13385
|
+
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.
|
|
13299
13386
|
|
|
13300
13387
|
## Parent Principle
|
|
13301
13388
|
|
|
13302
|
-
- [
|
|
13389
|
+
- [Maintainable Codebase](maintainable-codebase.md)
|
|
13303
13390
|
|
|
13304
13391
|
## Sub-Principles
|
|
13305
13392
|
|
|
13306
|
-
-
|
|
13307
|
-
- [Stubs Over Mocks](stubs-over-mocks.md)
|
|
13308
|
-
- [Functional Core, Imperative Shell](functional-core-imperative-shell.md)
|
|
13309
|
-
- [Design for Testability](design-for-testability.md)
|
|
13393
|
+
- (none)
|
|
13310
13394
|
`
|
|
13311
13395
|
},
|
|
13312
13396
|
{
|
|
13313
|
-
slug: "
|
|
13314
|
-
content: `#
|
|
13397
|
+
slug: "dependency-injection",
|
|
13398
|
+
content: `# Dependency Injection
|
|
13315
13399
|
|
|
13316
|
-
|
|
13400
|
+
Avoid global mocks. Dependency injection is almost always preferable to testing code that depends directly on globals.
|
|
13317
13401
|
|
|
13318
|
-
|
|
13402
|
+
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.
|
|
13319
13403
|
|
|
13320
|
-
This
|
|
13321
|
-
- TypeScript (type checking and style)
|
|
13322
|
-
- Markdown (broken links, required sections)
|
|
13323
|
-
- Task files (structure validation)
|
|
13324
|
-
- Principle hierarchy (parent/child consistency)
|
|
13404
|
+
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.
|
|
13325
13405
|
|
|
13326
13406
|
## Parent Principle
|
|
13327
13407
|
|
|
13328
|
-
- [
|
|
13408
|
+
- [Decoupled Code](decoupled-code.md)
|
|
13329
13409
|
|
|
13330
13410
|
## Sub-Principles
|
|
13331
13411
|
|
|
13332
|
-
(none)
|
|
13412
|
+
- (none)
|
|
13333
13413
|
`
|
|
13334
13414
|
},
|
|
13335
13415
|
{
|
|
13336
|
-
slug: "
|
|
13337
|
-
content: `#
|
|
13416
|
+
slug: "repository-hygiene",
|
|
13417
|
+
content: `# Repository Hygiene
|
|
13338
13418
|
|
|
13339
|
-
|
|
13419
|
+
Dust repositories should maintain a clean, organized state with minimal noise.
|
|
13340
13420
|
|
|
13341
|
-
This
|
|
13421
|
+
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.
|
|
13342
13422
|
|
|
13343
13423
|
## Parent Principle
|
|
13344
13424
|
|
|
13345
|
-
- [
|
|
13425
|
+
- [Maintainable Codebase](maintainable-codebase.md)
|
|
13346
13426
|
|
|
13347
13427
|
## Sub-Principles
|
|
13348
13428
|
|
|
13349
|
-
- [
|
|
13350
|
-
- [
|
|
13351
|
-
- [Intuitive Directory Structure](intuitive-directory-structure.md)
|
|
13352
|
-
- [Repository Hygiene](repository-hygiene.md)
|
|
13353
|
-
- [Naming Matters](naming-matters.md)
|
|
13354
|
-
- [Reasonably DRY](reasonably-dry.md)
|
|
13355
|
-
- [Make the Change Easy](make-the-change-easy.md)
|
|
13356
|
-
- [Boy Scout Rule](boy-scout-rule.md)
|
|
13357
|
-
- [Broken Windows](broken-windows.md)
|
|
13429
|
+
- [Atomic Commits](atomic-commits.md)
|
|
13430
|
+
- [Trunk-Based Development](trunk-based-development.md)
|
|
13358
13431
|
`
|
|
13359
13432
|
},
|
|
13360
13433
|
{
|
|
13361
|
-
slug: "
|
|
13362
|
-
content: `#
|
|
13363
|
-
|
|
13364
|
-
Dust should work with multiple agents without favoring one.
|
|
13434
|
+
slug: "batteries-included",
|
|
13435
|
+
content: `# Batteries Included
|
|
13365
13436
|
|
|
13366
|
-
|
|
13437
|
+
Dust should provide everything that is required (within reason) for an agent to be productive in an arbitrary codebase.
|
|
13367
13438
|
|
|
13368
|
-
|
|
13439
|
+
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.
|
|
13369
13440
|
|
|
13370
|
-
|
|
13441
|
+
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.
|
|
13371
13442
|
|
|
13372
13443
|
## Applicability
|
|
13373
13444
|
|
|
@@ -13378,47 +13449,25 @@ Internal
|
|
|
13378
13449
|
- [Agent Autonomy](agent-autonomy.md)
|
|
13379
13450
|
|
|
13380
13451
|
## Sub-Principles
|
|
13381
|
-
|
|
13382
|
-
- (none)
|
|
13383
13452
|
`
|
|
13384
13453
|
},
|
|
13385
13454
|
{
|
|
13386
|
-
slug: "
|
|
13387
|
-
content: `#
|
|
13388
|
-
|
|
13389
|
-
Dust should be trivially easy to adopt in any repository.
|
|
13390
|
-
|
|
13391
|
-
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.
|
|
13392
|
-
|
|
13393
|
-
This lowers the barrier to entry and encourages experimentation.
|
|
13394
|
-
|
|
13395
|
-
## Parent Principle
|
|
13396
|
-
|
|
13397
|
-
- [Human-AI Collaboration](human-ai-collaboration.md)
|
|
13455
|
+
slug: "development-traceability",
|
|
13456
|
+
content: `# Development Traceability
|
|
13398
13457
|
|
|
13399
|
-
|
|
13458
|
+
Structured logging and tracing help agents understand system behaviour without resorting to ad-hoc testing cycles.
|
|
13400
13459
|
|
|
13401
|
-
|
|
13402
|
-
- [Unsurprising UX](unsurprising-ux.md)
|
|
13403
|
-
- [VCS Independence](vcs-independence.md)
|
|
13404
|
-
`
|
|
13405
|
-
},
|
|
13406
|
-
{
|
|
13407
|
-
slug: "actionable-errors",
|
|
13408
|
-
content: `# Actionable Errors
|
|
13460
|
+
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.
|
|
13409
13461
|
|
|
13410
|
-
|
|
13462
|
+
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.
|
|
13411
13463
|
|
|
13412
|
-
|
|
13413
|
-
- A clear description of the problem
|
|
13414
|
-
- Specific guidance on how to fix it
|
|
13415
|
-
- Context needed to take the next step
|
|
13464
|
+
## Applicability
|
|
13416
13465
|
|
|
13417
|
-
|
|
13466
|
+
Internal
|
|
13418
13467
|
|
|
13419
13468
|
## Parent Principle
|
|
13420
13469
|
|
|
13421
|
-
- [Agent
|
|
13470
|
+
- [Ideal Agent Developer Experience](ideal-agent-developer-experience.md)
|
|
13422
13471
|
|
|
13423
13472
|
## Sub-Principles
|
|
13424
13473
|
|
|
@@ -13426,16 +13475,22 @@ This is especially important for AI agents, who need concrete instructions to re
|
|
|
13426
13475
|
`
|
|
13427
13476
|
},
|
|
13428
13477
|
{
|
|
13429
|
-
slug: "
|
|
13430
|
-
content: `#
|
|
13478
|
+
slug: "exploratory-tooling",
|
|
13479
|
+
content: `# Exploratory Tooling
|
|
13431
13480
|
|
|
13432
|
-
|
|
13481
|
+
Agents need tools to efficiently explore and understand unfamiliar codebases.
|
|
13433
13482
|
|
|
13434
|
-
|
|
13483
|
+
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.
|
|
13484
|
+
|
|
13485
|
+
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.
|
|
13486
|
+
|
|
13487
|
+
## Applicability
|
|
13488
|
+
|
|
13489
|
+
Internal
|
|
13435
13490
|
|
|
13436
13491
|
## Parent Principle
|
|
13437
13492
|
|
|
13438
|
-
- [
|
|
13493
|
+
- [Ideal Agent Developer Experience](ideal-agent-developer-experience.md)
|
|
13439
13494
|
|
|
13440
13495
|
## Sub-Principles
|
|
13441
13496
|
|
|
@@ -13443,54 +13498,57 @@ Principles use Title Case. File names use kebab-case. Commands use lowercase wit
|
|
|
13443
13498
|
`
|
|
13444
13499
|
},
|
|
13445
13500
|
{
|
|
13446
|
-
slug: "
|
|
13447
|
-
content: `#
|
|
13501
|
+
slug: "small-units",
|
|
13502
|
+
content: `# Small Units
|
|
13448
13503
|
|
|
13449
|
-
|
|
13504
|
+
Ideas, principles, facts, and tasks should each be as discrete and fine-grained as possible.
|
|
13450
13505
|
|
|
13451
|
-
|
|
13506
|
+
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.
|
|
13507
|
+
|
|
13508
|
+
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.
|
|
13509
|
+
|
|
13510
|
+
Note: This principle directly supports [Lightweight Planning](lightweight-planning.md), which explicitly mentions that "Tasks are small and completable in single commits."
|
|
13452
13511
|
|
|
13453
13512
|
## Parent Principle
|
|
13454
13513
|
|
|
13455
|
-
- [
|
|
13514
|
+
- [Agent Autonomy](agent-autonomy.md)
|
|
13456
13515
|
|
|
13457
13516
|
## Sub-Principles
|
|
13458
13517
|
|
|
13459
|
-
-
|
|
13518
|
+
- (none)
|
|
13460
13519
|
`
|
|
13461
13520
|
},
|
|
13462
13521
|
{
|
|
13463
|
-
slug: "
|
|
13464
|
-
content: `#
|
|
13465
|
-
|
|
13466
|
-
Dust should be designed with short attention spans in mind.
|
|
13522
|
+
slug: "naming-matters",
|
|
13523
|
+
content: `# Naming Matters
|
|
13467
13524
|
|
|
13468
|
-
|
|
13525
|
+
Good naming reduces waste by eliminating confusion and making code self-documenting.
|
|
13469
13526
|
|
|
13470
|
-
|
|
13527
|
+
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.
|
|
13471
13528
|
|
|
13472
13529
|
## Parent Principle
|
|
13473
13530
|
|
|
13474
|
-
- [
|
|
13531
|
+
- [Maintainable Codebase](maintainable-codebase.md)
|
|
13475
13532
|
|
|
13476
13533
|
## Sub-Principles
|
|
13477
13534
|
|
|
13478
|
-
- [
|
|
13535
|
+
- [Consistent Naming](consistent-naming.md)
|
|
13536
|
+
- [Clarity Over Brevity](clarity-over-brevity.md)
|
|
13479
13537
|
`
|
|
13480
13538
|
},
|
|
13481
13539
|
{
|
|
13482
|
-
slug: "
|
|
13483
|
-
content: `#
|
|
13540
|
+
slug: "comprehensive-test-coverage",
|
|
13541
|
+
content: `# Comprehensive Test Coverage
|
|
13484
13542
|
|
|
13485
|
-
|
|
13543
|
+
A project's test suite is its primary safety net, and agents depend on it even more than humans do.
|
|
13486
13544
|
|
|
13487
|
-
|
|
13545
|
+
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.
|
|
13488
13546
|
|
|
13489
|
-
|
|
13547
|
+
Dust should help projects measure and improve their test coverage, flag untested areas, and encourage a culture where new code comes with new tests.
|
|
13490
13548
|
|
|
13491
13549
|
## Parent Principle
|
|
13492
13550
|
|
|
13493
|
-
- [
|
|
13551
|
+
- [Ideal Agent Developer Experience](ideal-agent-developer-experience.md)
|
|
13494
13552
|
|
|
13495
13553
|
## Sub-Principles
|
|
13496
13554
|
|
|
@@ -13498,18 +13556,16 @@ The Boy Scout Rule is not a license for large-scale refactoring during unrelated
|
|
|
13498
13556
|
`
|
|
13499
13557
|
},
|
|
13500
13558
|
{
|
|
13501
|
-
slug: "
|
|
13502
|
-
content: `#
|
|
13503
|
-
|
|
13504
|
-
The user interface should be as "guessable" as possible.
|
|
13559
|
+
slug: "stop-the-line",
|
|
13560
|
+
content: `# Stop the Line
|
|
13505
13561
|
|
|
13506
|
-
|
|
13562
|
+
Any worker — human or agent — should halt and fix a problem the moment they detect it, rather than letting defects propagate downstream.
|
|
13507
13563
|
|
|
13508
|
-
|
|
13564
|
+
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.
|
|
13509
13565
|
|
|
13510
13566
|
## Parent Principle
|
|
13511
13567
|
|
|
13512
|
-
- [
|
|
13568
|
+
- [Make Changes with Confidence](make-changes-with-confidence.md)
|
|
13513
13569
|
|
|
13514
13570
|
## Sub-Principles
|
|
13515
13571
|
|