dflow-sdd-ddd 0.7.0 → 0.9.0
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/CHANGELOG.md +73 -0
- package/LICENSE +679 -21
- package/README.en.md +5 -4
- package/README.md +3 -3
- package/bin/dflow.js +3 -2
- package/docs/evaluating-dflow.en.md +14 -5
- package/docs/evaluating-dflow.md +14 -5
- package/docs/using-with-claude-code.en.md +17 -9
- package/docs/using-with-claude-code.md +15 -8
- package/docs/using-with-codex.en.md +12 -8
- package/docs/using-with-codex.md +8 -6
- package/lib/init.js +480 -87
- package/package.json +2 -2
- package/templates/brownfield/references/dflow-feedback-flow.md +251 -0
- package/templates/brownfield/references/drift-verification.md +183 -0
- package/templates/brownfield/references/finish-feature-flow.md +294 -0
- package/templates/brownfield/references/git-integration.md +371 -0
- package/templates/brownfield/references/init-project-flow.md +430 -0
- package/templates/brownfield/references/modify-existing-flow.md +448 -0
- package/templates/brownfield/references/new-feature-flow.md +382 -0
- package/templates/brownfield/references/new-phase-flow.md +274 -0
- package/templates/brownfield/references/pr-review-checklist.md +179 -0
- package/templates/brownfield/scaffolding/AI-AGENT-GUIDE.md +31 -4
- package/templates/brownfield/scaffolding/CLAUDE-md-snippet.md +12 -8
- package/templates/brownfield/scaffolding/Git-principles-gitflow.md +14 -13
- package/templates/brownfield/scaffolding/Git-principles-trunk.md +14 -17
- package/templates/brownfield/scaffolding/_conventions.md +1 -1
- package/templates/brownfield/scaffolding/_overview.md +3 -3
- package/templates/brownfield/templates/_index.md +20 -2
- package/templates/brownfield/templates/context-map.md +1 -1
- package/templates/brownfield/templates/glossary.md +1 -1
- package/templates/brownfield/templates/models.md +1 -1
- package/templates/brownfield/templates/rules.md +1 -1
- package/templates/brownfield/templates/tech-debt.md +1 -1
- package/templates/common/skill/SKILL.md +35 -0
- package/templates/greenfield/references/ddd-modeling-guide.md +351 -0
- package/templates/greenfield/references/dflow-feedback-flow.md +251 -0
- package/templates/greenfield/references/drift-verification.md +195 -0
- package/templates/greenfield/references/finish-feature-flow.md +314 -0
- package/templates/greenfield/references/git-integration.md +344 -0
- package/templates/greenfield/references/init-project-flow.md +464 -0
- package/templates/greenfield/references/modify-existing-flow.md +366 -0
- package/templates/greenfield/references/new-feature-flow.md +412 -0
- package/templates/greenfield/references/new-phase-flow.md +288 -0
- package/templates/greenfield/references/pr-review-checklist.md +130 -0
- package/templates/greenfield/scaffolding/AI-AGENT-GUIDE.md +31 -4
- package/templates/greenfield/scaffolding/CLAUDE-md-snippet.md +15 -13
- package/templates/greenfield/scaffolding/Git-principles-gitflow.md +14 -13
- package/templates/greenfield/scaffolding/Git-principles-trunk.md +14 -18
- package/templates/greenfield/scaffolding/_conventions.md +1 -1
- package/templates/greenfield/scaffolding/_overview.md +5 -3
- package/templates/greenfield/scaffolding/architecture-decisions-README.md +1 -1
- package/templates/greenfield/templates/_index.md +20 -2
- package/templates/greenfield/templates/context-map.md +1 -1
- package/templates/greenfield/templates/events.md +1 -1
- package/templates/greenfield/templates/glossary.md +1 -1
- package/templates/greenfield/templates/models.md +1 -1
- package/templates/greenfield/templates/rules.md +1 -1
- package/templates/greenfield/templates/tech-debt.md +1 -1
|
@@ -0,0 +1,351 @@
|
|
|
1
|
+
# DDD Modeling Guide
|
|
2
|
+
|
|
3
|
+
When a developer asks "How should I model X?" or is designing domain structures,
|
|
4
|
+
use this guide to walk them through DDD tactical patterns.
|
|
5
|
+
|
|
6
|
+
## The Modeling Conversation
|
|
7
|
+
|
|
8
|
+
Start with the business, not the code:
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
"Let's forget about databases and classes for a moment.
|
|
12
|
+
Tell me: what are the rules? What must always be true?
|
|
13
|
+
What can never happen?"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
These invariants drive the entire model design.
|
|
17
|
+
|
|
18
|
+
## Pattern Selection Flowchart
|
|
19
|
+
|
|
20
|
+
```
|
|
21
|
+
Is it identified by an ID that persists over time?
|
|
22
|
+
│
|
|
23
|
+
├─ Yes → Is it the "boss" that protects a consistency boundary?
|
|
24
|
+
│ ├─ Yes → AGGREGATE ROOT
|
|
25
|
+
│ └─ No → ENTITY (belongs inside an Aggregate)
|
|
26
|
+
│
|
|
27
|
+
└─ No → Is it defined entirely by its properties?
|
|
28
|
+
├─ Yes → VALUE OBJECT
|
|
29
|
+
└─ No → Does it represent an operation spanning multiple Aggregates?
|
|
30
|
+
├─ Yes → DOMAIN SERVICE
|
|
31
|
+
└─ No → Re-examine — it's probably one of the above
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
## Aggregate Design
|
|
35
|
+
|
|
36
|
+
Aggregates are the most important and most commonly misunderstood DDD concept.
|
|
37
|
+
|
|
38
|
+
### What is an Aggregate?
|
|
39
|
+
|
|
40
|
+
A cluster of objects treated as a single unit for data changes. The Aggregate Root
|
|
41
|
+
is the only entry point — outside code cannot reach inside and modify child entities
|
|
42
|
+
or value objects directly.
|
|
43
|
+
|
|
44
|
+
### Aggregate Design Rules
|
|
45
|
+
|
|
46
|
+
1. **Protect invariants** — The Aggregate exists to enforce business rules that span
|
|
47
|
+
multiple objects within it.
|
|
48
|
+
|
|
49
|
+
2. **One Aggregate per transaction** — A single operation should modify only ONE
|
|
50
|
+
Aggregate. If you need to modify two Aggregates, use Domain Events for eventual
|
|
51
|
+
consistency.
|
|
52
|
+
|
|
53
|
+
3. **Reference other Aggregates by ID only** — Never hold a direct object reference
|
|
54
|
+
to another Aggregate. Store its ID instead.
|
|
55
|
+
|
|
56
|
+
4. **Keep them small** — Large Aggregates cause concurrency issues. If two users can
|
|
57
|
+
independently modify different parts, those parts should probably be separate Aggregates.
|
|
58
|
+
|
|
59
|
+
### Example: Expense Report Aggregate
|
|
60
|
+
|
|
61
|
+
```csharp
|
|
62
|
+
// ExpenseReport is the Aggregate Root
|
|
63
|
+
public class ExpenseReport : AggregateRoot
|
|
64
|
+
{
|
|
65
|
+
private readonly List<ExpenseLineItem> _lineItems = new();
|
|
66
|
+
|
|
67
|
+
public EmployeeId SubmittedBy { get; private set; } // Reference by ID
|
|
68
|
+
public ReportPeriod Period { get; private set; } // Value Object
|
|
69
|
+
public Money TotalAmount => CalculateTotal(); // Derived
|
|
70
|
+
public ReportStatus Status { get; private set; } // Value Object (enum-like)
|
|
71
|
+
|
|
72
|
+
// State change through explicit methods — not property setters
|
|
73
|
+
public void AddLineItem(string description, Money amount, ExpenseCategory category)
|
|
74
|
+
{
|
|
75
|
+
// Enforce invariants
|
|
76
|
+
if (Status != ReportStatus.Draft)
|
|
77
|
+
throw new DomainException("Cannot add items to a submitted report.");
|
|
78
|
+
|
|
79
|
+
if (_lineItems.Count >= 50)
|
|
80
|
+
throw new DomainException("Maximum 50 line items per report.");
|
|
81
|
+
|
|
82
|
+
var lineItem = new ExpenseLineItem(description, amount, category);
|
|
83
|
+
_lineItems.Add(lineItem);
|
|
84
|
+
|
|
85
|
+
// Raise domain event
|
|
86
|
+
AddDomainEvent(new LineItemAddedEvent(Id, lineItem.Id, amount));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
public void Submit()
|
|
90
|
+
{
|
|
91
|
+
if (Status != ReportStatus.Draft)
|
|
92
|
+
throw new DomainException("Only draft reports can be submitted.");
|
|
93
|
+
|
|
94
|
+
if (!_lineItems.Any())
|
|
95
|
+
throw new DomainException("Cannot submit an empty report.");
|
|
96
|
+
|
|
97
|
+
Status = ReportStatus.Submitted;
|
|
98
|
+
AddDomainEvent(new ExpenseReportSubmittedEvent(Id, SubmittedBy, TotalAmount));
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
### Design Questions to Ask
|
|
104
|
+
|
|
105
|
+
When designing a new Aggregate:
|
|
106
|
+
|
|
107
|
+
1. **What are the invariants?** What business rules must ALWAYS be true?
|
|
108
|
+
2. **What's the consistency boundary?** What must be updated atomically?
|
|
109
|
+
3. **What can change independently?** Separate Aggregates for separate concerns.
|
|
110
|
+
4. **Who modifies this?** How many concurrent users? (Affects Aggregate size)
|
|
111
|
+
5. **What events does this produce?** What other parts of the system need to know?
|
|
112
|
+
|
|
113
|
+
### Set-Based / Uniqueness Invariants
|
|
114
|
+
|
|
115
|
+
Some invariants are not about one Aggregate but about a **set selected by a
|
|
116
|
+
business key or status**: "email (normalized) is unique across all Users", "each
|
|
117
|
+
seat holds at most one active booking", "each connector has at most one
|
|
118
|
+
in-progress charging session". A single Aggregate instance cannot see the rest of
|
|
119
|
+
that set, so it cannot enforce the rule on its own.
|
|
120
|
+
|
|
121
|
+
Handle them the same way **regardless of which Aggregate boundary you choose**:
|
|
122
|
+
|
|
123
|
+
1. **As separate Aggregates** (e.g. `User` and `Booking` are distinct): the
|
|
124
|
+
Application layer *orchestrates* the check — via a repository query, a
|
|
125
|
+
Specification, or a domain service (the rule stays domain-named; it is not an
|
|
126
|
+
inline `if-else` in the command handler) — and the **database enforces it with
|
|
127
|
+
a unique / partial (filtered) unique index**. The DB constraint is the real
|
|
128
|
+
guarantee under concurrency; the orchestrated check just returns a friendlier
|
|
129
|
+
error first.
|
|
130
|
+
|
|
131
|
+
2. **Folded into one Aggregate** (e.g. the active session lives *inside* a
|
|
132
|
+
`Connector` as a child entity): the in-memory check (`if (Status == InUse)
|
|
133
|
+
throw …`) is logically correct, **but is still not concurrency-safe by
|
|
134
|
+
itself**. Two concurrent commands can each load the Aggregate, both pass the
|
|
135
|
+
check, and both save. Close the race with **optimistic concurrency** (a
|
|
136
|
+
`rowversion` / version token on the Aggregate root) or a DB constraint — but
|
|
137
|
+
the version check only protects you if every save actually touches the root's
|
|
138
|
+
token: inserting a child row without bumping the root's version leaves the
|
|
139
|
+
race open. Translate the resulting concurrency exception / unique violation
|
|
140
|
+
into a meaningful business conflict (e.g. HTTP 409), not a generic 500.
|
|
141
|
+
|
|
142
|
+
**Key point:** an in-memory check — at *any* layer — is never the final guarantee
|
|
143
|
+
for a uniqueness / "only one active X" rule under concurrent requests. The durable
|
|
144
|
+
enforcement is a DB unique / filtered index, an optimistic-concurrency token, or
|
|
145
|
+
an equivalent conditional write / compare-and-swap. Pick the Aggregate boundary on
|
|
146
|
+
modeling grounds (does the inner thing have an independent lifecycle / history
|
|
147
|
+
worth querying?), then add the store-level guard either way. (Heavier
|
|
148
|
+
serialization tactics — distributed locks, per-key actors, aggregate-per-key
|
|
149
|
+
sharding — exist but are advanced; reach for a store-level constraint or version
|
|
150
|
+
check first.)
|
|
151
|
+
|
|
152
|
+
## Value Objects
|
|
153
|
+
|
|
154
|
+
### When to Use Value Objects
|
|
155
|
+
|
|
156
|
+
If the answer to ALL of these is "yes", it's a Value Object:
|
|
157
|
+
- Is it defined by its properties, not by an ID?
|
|
158
|
+
- Is it immutable once created?
|
|
159
|
+
- Can two instances with the same properties be considered equal?
|
|
160
|
+
|
|
161
|
+
### Common Value Objects
|
|
162
|
+
|
|
163
|
+
```csharp
|
|
164
|
+
// Money — the classic example
|
|
165
|
+
public record Money(decimal Amount, Currency Currency)
|
|
166
|
+
{
|
|
167
|
+
public static Money Zero(Currency currency) => new(0, currency);
|
|
168
|
+
|
|
169
|
+
public Money Add(Money other)
|
|
170
|
+
{
|
|
171
|
+
if (Currency != other.Currency)
|
|
172
|
+
throw new CurrencyMismatchException(Currency, other.Currency);
|
|
173
|
+
return new Money(Amount + other.Amount, Currency);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
public Money ConvertTo(Currency target, ExchangeRate rate)
|
|
177
|
+
{
|
|
178
|
+
return new Money(rate.Convert(Amount), target);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// DateRange
|
|
183
|
+
public record DateRange(DateOnly Start, DateOnly End)
|
|
184
|
+
{
|
|
185
|
+
public DateRange
|
|
186
|
+
{
|
|
187
|
+
if (Start > End) throw new DomainException("Start must be before End.");
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
public bool Contains(DateOnly date) => date >= Start && date <= End;
|
|
191
|
+
public int Days => End.DayNumber - Start.DayNumber + 1;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// Currency (constrained string)
|
|
195
|
+
public record Currency
|
|
196
|
+
{
|
|
197
|
+
public string Code { get; }
|
|
198
|
+
public int DecimalPlaces { get; }
|
|
199
|
+
|
|
200
|
+
public static readonly Currency TWD = new("TWD", 0);
|
|
201
|
+
public static readonly Currency USD = new("USD", 2);
|
|
202
|
+
public static readonly Currency JPY = new("JPY", 0);
|
|
203
|
+
|
|
204
|
+
private Currency(string code, int decimalPlaces)
|
|
205
|
+
{
|
|
206
|
+
Code = code;
|
|
207
|
+
DecimalPlaces = decimalPlaces;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
public decimal Round(decimal amount) =>
|
|
211
|
+
Math.Round(amount, DecimalPlaces, MidpointRounding.AwayFromZero);
|
|
212
|
+
}
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
### Value Object Design Questions
|
|
216
|
+
|
|
217
|
+
1. **Does it have behavior?** Good VOs have methods, not just properties.
|
|
218
|
+
2. **Does it enforce constraints?** Constructor should reject invalid states.
|
|
219
|
+
3. **Is it reusable?** `Money` can be used across many Aggregates.
|
|
220
|
+
|
|
221
|
+
## Domain Events
|
|
222
|
+
|
|
223
|
+
### What Are Domain Events?
|
|
224
|
+
|
|
225
|
+
Something that happened in the domain that other parts of the system care about.
|
|
226
|
+
Past tense naming: `ExpenseReportSubmitted`, `LineItemAdded`, `ReportApproved`.
|
|
227
|
+
|
|
228
|
+
### When to Use Domain Events
|
|
229
|
+
|
|
230
|
+
- When one Aggregate needs to trigger changes in another Aggregate
|
|
231
|
+
- When side effects (email, notification, audit log) should happen after a domain action
|
|
232
|
+
- When different Bounded Contexts need to communicate
|
|
233
|
+
|
|
234
|
+
### Event Design
|
|
235
|
+
|
|
236
|
+
```csharp
|
|
237
|
+
public record ExpenseReportSubmittedEvent(
|
|
238
|
+
ExpenseReportId ReportId,
|
|
239
|
+
EmployeeId SubmittedBy,
|
|
240
|
+
Money TotalAmount
|
|
241
|
+
) : IDomainEvent;
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Event Flow
|
|
245
|
+
|
|
246
|
+
```
|
|
247
|
+
1. Aggregate method called → state changes → event added to DomainEvents list
|
|
248
|
+
2. Repository saves Aggregate
|
|
249
|
+
3. After save (in same transaction or via outbox):
|
|
250
|
+
- In-process handlers: update read models, trigger other commands
|
|
251
|
+
- Cross-context: publish to message queue
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
### Event Handling Guidelines
|
|
255
|
+
|
|
256
|
+
- **Same Bounded Context**: Handle synchronously (same transaction OK for read models)
|
|
257
|
+
- **Cross Bounded Context**: Handle asynchronously (eventual consistency)
|
|
258
|
+
- Event handlers should be idempotent (safe to process multiple times)
|
|
259
|
+
- **Dispatch / clear lifecycle**: clear `DomainEvents` at the Repository / Unit
|
|
260
|
+
of Work **save boundary** — the UoW clears them *after* the save succeeds and
|
|
261
|
+
the events have been dispatched (or the outbox row persisted, or dispatch
|
|
262
|
+
explicitly decided to be dropped/deferred). Never clear inside an Aggregate
|
|
263
|
+
method, and never before the save succeeds. A simple in-process dispatcher
|
|
264
|
+
(e.g. `IPublisher` / MediatR) is enough for Phase 1; an **outbox /
|
|
265
|
+
integration-event bridge is the Phase 2+ upgrade** for reliable cross-service
|
|
266
|
+
delivery. If no dispatcher is wired yet, record it as deferred tech debt — but
|
|
267
|
+
still clear on a successful save so events cannot accumulate unbounded.
|
|
268
|
+
|
|
269
|
+
## Specifications
|
|
270
|
+
|
|
271
|
+
For complex query logic that belongs to the domain:
|
|
272
|
+
|
|
273
|
+
```csharp
|
|
274
|
+
public class PendingApprovalSpec : Specification<ExpenseReport>
|
|
275
|
+
{
|
|
276
|
+
private readonly EmployeeId _approverId;
|
|
277
|
+
|
|
278
|
+
public PendingApprovalSpec(EmployeeId approverId)
|
|
279
|
+
{
|
|
280
|
+
_approverId = approverId;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
public override Expression<Func<ExpenseReport, bool>> ToExpression()
|
|
284
|
+
{
|
|
285
|
+
return report =>
|
|
286
|
+
report.Status == ReportStatus.Submitted &&
|
|
287
|
+
report.ApproverId == _approverId;
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
## Domain Services
|
|
293
|
+
|
|
294
|
+
Use Domain Services for operations that:
|
|
295
|
+
- Involve multiple Aggregates (read-only access to the second Aggregate)
|
|
296
|
+
- Require external information (through interfaces) to make domain decisions
|
|
297
|
+
- Don't naturally belong to any single Entity
|
|
298
|
+
|
|
299
|
+
```csharp
|
|
300
|
+
// Domain Service — in Domain layer
|
|
301
|
+
public class ExpenseApprovalService
|
|
302
|
+
{
|
|
303
|
+
private readonly IApprovalPolicyRepository _policyRepo;
|
|
304
|
+
|
|
305
|
+
public ApprovalResult Evaluate(ExpenseReport report, ApprovalPolicy policy)
|
|
306
|
+
{
|
|
307
|
+
if (report.TotalAmount.Amount > policy.AutoApprovalLimit)
|
|
308
|
+
return ApprovalResult.RequiresManagerApproval;
|
|
309
|
+
|
|
310
|
+
if (policy.RestrictedCategories.Overlaps(report.Categories))
|
|
311
|
+
return ApprovalResult.RequiresComplianceReview;
|
|
312
|
+
|
|
313
|
+
return ApprovalResult.AutoApproved;
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## Bounded Context Relationships
|
|
319
|
+
|
|
320
|
+
If `dflow/specs/domain/context-map.md` does not exist yet, create it from `templates/context-map.md` before documenting relationships.
|
|
321
|
+
|
|
322
|
+
Document in `dflow/specs/domain/context-map.md`:
|
|
323
|
+
|
|
324
|
+
| Relationship | Pattern | Example |
|
|
325
|
+
|---|---|---|
|
|
326
|
+
| Context A calls Context B | Customer-Supplier | Expense → HR (get employee info) |
|
|
327
|
+
| Contexts share data | Shared Kernel | Currency, Money in SharedKernel/ |
|
|
328
|
+
| Context A translates B's language | Anti-Corruption Layer | Expense → External Accounting System |
|
|
329
|
+
| Fire and forget | Domain Events | Expense → Notification (report submitted) |
|
|
330
|
+
|
|
331
|
+
## Common Mistakes to Catch
|
|
332
|
+
|
|
333
|
+
1. **Anemic Domain Model** — Entities with only getters/setters, all logic in services
|
|
334
|
+
→ Move behavior INTO the Entity/Aggregate
|
|
335
|
+
|
|
336
|
+
2. **Too-large Aggregates** — Aggregate that loads entire object graph
|
|
337
|
+
→ Split into smaller Aggregates, reference by ID
|
|
338
|
+
|
|
339
|
+
3. **Business logic in Application layer** — If-else rules in command handlers
|
|
340
|
+
→ Move to Domain (Entity methods or Domain Services)
|
|
341
|
+
|
|
342
|
+
4. **Business logic in Infrastructure** — Rules in SQL queries or EF configurations
|
|
343
|
+
→ Domain defines WHAT, Infrastructure defines HOW
|
|
344
|
+
|
|
345
|
+
5. **Direct cross-Aggregate modification** — One command modifying two Aggregates
|
|
346
|
+
→ Use Domain Events for the second Aggregate
|
|
347
|
+
|
|
348
|
+
6. **Set-based invariant guarded only in memory** — A "unique" / "only one active"
|
|
349
|
+
rule enforced solely by an in-app check, with no DB unique constraint or
|
|
350
|
+
optimistic-concurrency token → two concurrent requests can both pass and break it
|
|
351
|
+
→ Back it with a DB constraint / `rowversion`; see "Set-Based / Uniqueness Invariants"
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# Dflow Feedback Draft Flow
|
|
2
|
+
|
|
3
|
+
`/dflow:report-dflow-feedback` helps the developer turn a Dflow problem or
|
|
4
|
+
improvement observed during real project work into a high-quality upstream
|
|
5
|
+
feedback draft. The draft is rendered **field by field to match the upstream
|
|
6
|
+
GitHub issue form**, so the developer pastes each field with no reformatting.
|
|
7
|
+
|
|
8
|
+
This flow is **not** a project feature workflow and does not change the
|
|
9
|
+
application being built. It is a standalone governance/support flow for Dflow
|
|
10
|
+
itself.
|
|
11
|
+
|
|
12
|
+
## Hard Boundaries
|
|
13
|
+
|
|
14
|
+
- Do not submit anything to GitHub automatically.
|
|
15
|
+
- Do not run `gh issue create`, `gh pr create`, `git push`, or any networked
|
|
16
|
+
submission command from this flow.
|
|
17
|
+
- Do not expose private project details, business rules, customer data,
|
|
18
|
+
secrets, tokens, internal URLs, or proprietary source snippets.
|
|
19
|
+
- Always show the draft to the developer before anything leaves the local
|
|
20
|
+
machine.
|
|
21
|
+
- If the developer later asks to submit through GitHub CLI, stop and treat that
|
|
22
|
+
as a separate explicit task with fresh permission and environment checks.
|
|
23
|
+
|
|
24
|
+
## Trigger Conditions
|
|
25
|
+
|
|
26
|
+
Enter this flow when:
|
|
27
|
+
|
|
28
|
+
- The developer explicitly runs `/dflow:report-dflow-feedback`.
|
|
29
|
+
- The developer says the Dflow process, template, generated file, or docs seem
|
|
30
|
+
wrong or improvable.
|
|
31
|
+
- The AI notices a clear contradiction or gap in Dflow guidance and asks:
|
|
32
|
+
"This looks like a possible Dflow upstream issue. Should I draft feedback for
|
|
33
|
+
you to review?"
|
|
34
|
+
|
|
35
|
+
Do not interrupt normal development for minor preference differences. If the
|
|
36
|
+
observation is speculative, ask before drafting.
|
|
37
|
+
|
|
38
|
+
## Output Location
|
|
39
|
+
|
|
40
|
+
Write the draft to:
|
|
41
|
+
|
|
42
|
+
```text
|
|
43
|
+
dflow/feedback/dflow-feedback-YYYY-MM-DD-{slug}.md
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Create `dflow/feedback/` if it does not exist. The file is local project
|
|
47
|
+
working material; the developer decides whether to copy it into a GitHub issue,
|
|
48
|
+
turn it into a PR, or discard it.
|
|
49
|
+
|
|
50
|
+
## Step 1: Classify the Feedback
|
|
51
|
+
|
|
52
|
+
Classify the feedback as one of the following. Each maps to one upstream issue
|
|
53
|
+
form (see "Upstream Issue Forms" below):
|
|
54
|
+
|
|
55
|
+
| Classification | Upstream issue form | Title prefix |
|
|
56
|
+
|---|---|---|
|
|
57
|
+
| Bug report | Bug report | `[Bug]: ` |
|
|
58
|
+
| Workflow change request | Workflow change request | `[Workflow]: ` |
|
|
59
|
+
| Documentation feedback | Documentation feedback | `[Docs]: ` |
|
|
60
|
+
| Question / unclear usage | Question | `[Question]: ` |
|
|
61
|
+
| Maintainer release/process feedback | Workflow change request (closest form; the upstream repo disables blank issues) | `[Workflow]: ` |
|
|
62
|
+
|
|
63
|
+
Capture:
|
|
64
|
+
|
|
65
|
+
- Observed during which flow or command
|
|
66
|
+
- Affected Dflow track: Greenfield, Brownfield, both, or unknown
|
|
67
|
+
- Affected area: CLI, generated template, scaffolding, skill reference,
|
|
68
|
+
tutorial, README/docs, release/governance
|
|
69
|
+
- Whether the issue blocks current project work
|
|
70
|
+
|
|
71
|
+
## Step 2: Capture Evidence Safely
|
|
72
|
+
|
|
73
|
+
Collect only the evidence needed to explain the Dflow issue.
|
|
74
|
+
|
|
75
|
+
Allowed evidence:
|
|
76
|
+
|
|
77
|
+
- Dflow command name
|
|
78
|
+
- Dflow version if known
|
|
79
|
+
- Template or reference file name
|
|
80
|
+
- Generic project type, such as "existing legacy presentation-framework app"
|
|
81
|
+
- Minimal paraphrased symptom
|
|
82
|
+
- Short sanitized snippets from Dflow-owned files
|
|
83
|
+
|
|
84
|
+
Avoid:
|
|
85
|
+
|
|
86
|
+
- Internal business rules
|
|
87
|
+
- Customer or tenant names
|
|
88
|
+
- Private repository names or URLs
|
|
89
|
+
- Secrets, tokens, credentials, or auth headers
|
|
90
|
+
- Long proprietary code snippets
|
|
91
|
+
- Full logs containing private paths or environment data
|
|
92
|
+
|
|
93
|
+
## Step 3: Redaction Pass
|
|
94
|
+
|
|
95
|
+
Before writing any field content, run a redaction check and use it as your own
|
|
96
|
+
gate. Confirm there are:
|
|
97
|
+
|
|
98
|
+
- No secrets, tokens, credentials, or auth headers
|
|
99
|
+
- No customer, tenant, or private organization names
|
|
100
|
+
- No proprietary business rules beyond a sanitized paraphrase
|
|
101
|
+
- No private repository URLs or internal hostnames
|
|
102
|
+
- No long proprietary source snippets
|
|
103
|
+
|
|
104
|
+
The draft ends with a short submitter self-check (Step 5); leave its items
|
|
105
|
+
unchecked unless the developer explicitly confirms them.
|
|
106
|
+
|
|
107
|
+
## Step 4: Resolve the Target Issue Form
|
|
108
|
+
|
|
109
|
+
Submit upstream at: **https://github.com/weilung/dflow-sdd-ddd/issues/new/choose**
|
|
110
|
+
|
|
111
|
+
Resolve the field schema for the chosen form using this priority chain (it
|
|
112
|
+
avoids any network dependency at draft time):
|
|
113
|
+
|
|
114
|
+
1. **Live upstream schema** — if you can read the target repo's
|
|
115
|
+
`.github/ISSUE_TEMPLATE/*.yml` (for example you are working inside a
|
|
116
|
+
`dflow-sdd-ddd` checkout), use that file; it is authoritative.
|
|
117
|
+
2. **Bundled field map** — otherwise use the field map in "Upstream Issue
|
|
118
|
+
Forms" below. It is a snapshot of the upstream forms shipped with Dflow.
|
|
119
|
+
3. **Generic fallback** — only if the feedback matches none of the forms, use
|
|
120
|
+
Step 6.
|
|
121
|
+
|
|
122
|
+
## Step 5: Render the Draft Field by Field
|
|
123
|
+
|
|
124
|
+
Write the draft as one block per upstream field, in the form's field order, so
|
|
125
|
+
the developer copies each block straight into the matching field.
|
|
126
|
+
|
|
127
|
+
Per field-type rules:
|
|
128
|
+
|
|
129
|
+
| Field type | How to render |
|
|
130
|
+
|---|---|
|
|
131
|
+
| `input` | One short line inside a fenced block. |
|
|
132
|
+
| `textarea` | Multi-line content inside a fenced block. If the field sets a non-empty `render:` attribute, do **not** add an extra fence (the form already code-blocks it). |
|
|
133
|
+
| `dropdown` | State the **recommended option** plus a one-line reason. If `multiple: true`, list the chosen options. |
|
|
134
|
+
| `checkboxes` | List every option as `- [x]` / `- [ ]`; mark any option whose schema sets `required: true`. |
|
|
135
|
+
| `markdown` | Display-only text in the form — produce **no** field block for it. |
|
|
136
|
+
| upload / attachment | Emit a manual step ("drag the relevant screenshot / log into the issue editor"); do not try to handle the file. |
|
|
137
|
+
|
|
138
|
+
Always start with a **Title** block: the form's title prefix plus a concise
|
|
139
|
+
one-line summary. GitHub pre-fills the prefix in the title box; the developer
|
|
140
|
+
can paste the full line over it.
|
|
141
|
+
|
|
142
|
+
Attribute handling: bring `value` / `default` in as starting content; surface
|
|
143
|
+
`placeholder` as a hint; append "(required)" to the block heading when the
|
|
144
|
+
field sets `required: true`.
|
|
145
|
+
|
|
146
|
+
**Fence escaping (dynamic).** Wrap each field's content in a backtick fence
|
|
147
|
+
whose length is *(longest backtick run in the content) + 1*, minimum 3. The
|
|
148
|
+
fence is only a local wrapper so the content survives in the draft file — when
|
|
149
|
+
pasting into the issue form, the developer copies the **inner** content, not
|
|
150
|
+
the fence. State this in the draft.
|
|
151
|
+
|
|
152
|
+
Draft skeleton:
|
|
153
|
+
|
|
154
|
+
````markdown
|
|
155
|
+
# {Issue form name} — {short title}
|
|
156
|
+
|
|
157
|
+
## Where to submit
|
|
158
|
+
|
|
159
|
+
https://github.com/weilung/dflow-sdd-ddd/issues/new/choose → choose
|
|
160
|
+
**"{Issue form name}"**. (A GitHub account is all you need; the title is
|
|
161
|
+
auto-prefixed with `{prefix}`.)
|
|
162
|
+
|
|
163
|
+
## Title
|
|
164
|
+
|
|
165
|
+
```
|
|
166
|
+
{prefix}{concise one-line summary}
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
## {Field label} (required)
|
|
170
|
+
|
|
171
|
+
```
|
|
172
|
+
{field content; copy the inner text only, not this fence}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
... one block per field, in form order ...
|
|
176
|
+
|
|
177
|
+
## Before you submit (submitter self-check)
|
|
178
|
+
|
|
179
|
+
- [ ] Any real file names / customer names / internal project code names to redact?
|
|
180
|
+
- [ ] If you attach screenshots, do they show sensitive content (internal systems, tokens, passwords)?
|
|
181
|
+
- [ ] Is opening a public issue within what your organization allows?
|
|
182
|
+
````
|
|
183
|
+
|
|
184
|
+
Keep the draft **submitter-facing only**: no maintainer tracking notes, no
|
|
185
|
+
internal references, no "for your friend / for yourself" audience switches.
|
|
186
|
+
|
|
187
|
+
## Upstream Issue Forms (bundled field map)
|
|
188
|
+
|
|
189
|
+
> Snapshot of the `weilung/dflow-sdd-ddd` issue forms. If the live `.yml` is
|
|
190
|
+
> reachable (Step 4 priority 1), prefer it. Resync this map when the upstream
|
|
191
|
+
> forms change.
|
|
192
|
+
|
|
193
|
+
### Bug report — title `[Bug]: `
|
|
194
|
+
|
|
195
|
+
| Field | Type | Required | Notes |
|
|
196
|
+
|---|---|---|---|
|
|
197
|
+
| Dflow version | input | yes | placeholder `0.2.0` |
|
|
198
|
+
| Node.js version | input | yes | from `node --version` |
|
|
199
|
+
| Project track | dropdown | yes | Greenfield / Brownfield / Not sure |
|
|
200
|
+
| Command or workflow | textarea | yes | the command or `/dflow:*` workflow used |
|
|
201
|
+
| Expected behavior | textarea | yes | |
|
|
202
|
+
| Actual behavior | textarea | yes | include relevant output |
|
|
203
|
+
| Reproduction steps | textarea | yes | smallest steps that reproduce |
|
|
204
|
+
| Additional context | textarea | no | screenshots / snippets / environment |
|
|
205
|
+
|
|
206
|
+
### Workflow change request — title `[Workflow]: `
|
|
207
|
+
|
|
208
|
+
| Field | Type | Required | Notes |
|
|
209
|
+
|---|---|---|---|
|
|
210
|
+
| Problem | textarea | yes | |
|
|
211
|
+
| Proposed change | textarea | yes | |
|
|
212
|
+
| Affected track | dropdown | yes | Greenfield / Brownfield / Both / Not sure |
|
|
213
|
+
| Affected area | checkboxes | no | CLI command / Generated template / Generated scaffolding / Skill workflow guidance / Tutorial or examples / Documentation only |
|
|
214
|
+
| Compatibility risk | textarea | yes | |
|
|
215
|
+
| Alternatives considered | textarea | no | |
|
|
216
|
+
|
|
217
|
+
### Documentation feedback — title `[Docs]: `
|
|
218
|
+
|
|
219
|
+
| Field | Type | Required | Notes |
|
|
220
|
+
|---|---|---|---|
|
|
221
|
+
| Affected page or file | input | yes | placeholder `README.md` |
|
|
222
|
+
| Reader goal | textarea | yes | what you were trying to understand or do |
|
|
223
|
+
| What was confusing? | textarea | yes | the missing, unclear, or misleading part |
|
|
224
|
+
| Suggested improvement | textarea | no | optional wording or structure |
|
|
225
|
+
|
|
226
|
+
### Question — title `[Question]: `
|
|
227
|
+
|
|
228
|
+
| Field | Type | Required | Notes |
|
|
229
|
+
|---|---|---|---|
|
|
230
|
+
| Project type | dropdown | yes | New project / Existing project / Not sure |
|
|
231
|
+
| Dflow track you are considering | dropdown | yes | Greenfield / Brownfield / Not sure |
|
|
232
|
+
| What are you trying to do? | textarea | yes | the workflow or decision you need help with |
|
|
233
|
+
| Project context | textarea | no | framework, team workflow, AI agent, constraints |
|
|
234
|
+
|
|
235
|
+
## Step 6: Generic Fallback
|
|
236
|
+
|
|
237
|
+
Use this only when the feedback matches none of the forms above. The upstream
|
|
238
|
+
repo disables blank issues, so direct the developer to pick the closest form at
|
|
239
|
+
`https://github.com/weilung/dflow-sdd-ddd/issues/new/choose` and adapt. Emit
|
|
240
|
+
two paste-ready blocks — a `Title` and a `Body` — plus the URL. Do **not** fall
|
|
241
|
+
back to a generic `## Problem` / `## Evidence` Markdown draft.
|
|
242
|
+
|
|
243
|
+
## Step 7: Present Submission Options
|
|
244
|
+
|
|
245
|
+
After writing the draft, name the draft file path and whether any submitter
|
|
246
|
+
self-check items remain unchecked, then summarize the options:
|
|
247
|
+
|
|
248
|
+
- Open the chosen issue form and paste each field block.
|
|
249
|
+
- Discard the draft if it was only a local observation.
|
|
250
|
+
|
|
251
|
+
Do not submit anything automatically.
|