dflow-sdd-ddd 0.7.0 → 0.8.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.
Files changed (51) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/docs/evaluating-dflow.en.md +14 -5
  3. package/docs/evaluating-dflow.md +14 -5
  4. package/docs/using-with-claude-code.en.md +17 -9
  5. package/docs/using-with-claude-code.md +15 -8
  6. package/lib/init.js +263 -52
  7. package/package.json +1 -1
  8. package/templates/brownfield/references/dflow-feedback-flow.md +179 -0
  9. package/templates/brownfield/references/drift-verification.md +183 -0
  10. package/templates/brownfield/references/finish-feature-flow.md +259 -0
  11. package/templates/brownfield/references/git-integration.md +312 -0
  12. package/templates/brownfield/references/init-project-flow.md +413 -0
  13. package/templates/brownfield/references/modify-existing-flow.md +444 -0
  14. package/templates/brownfield/references/new-feature-flow.md +367 -0
  15. package/templates/brownfield/references/new-phase-flow.md +259 -0
  16. package/templates/brownfield/references/pr-review-checklist.md +179 -0
  17. package/templates/brownfield/scaffolding/AI-AGENT-GUIDE.md +31 -4
  18. package/templates/brownfield/scaffolding/CLAUDE-md-snippet.md +12 -8
  19. package/templates/brownfield/scaffolding/Git-principles-gitflow.md +1 -1
  20. package/templates/brownfield/scaffolding/Git-principles-trunk.md +1 -1
  21. package/templates/brownfield/scaffolding/_conventions.md +1 -1
  22. package/templates/brownfield/scaffolding/_overview.md +3 -3
  23. package/templates/brownfield/templates/context-map.md +1 -1
  24. package/templates/brownfield/templates/glossary.md +1 -1
  25. package/templates/brownfield/templates/models.md +1 -1
  26. package/templates/brownfield/templates/rules.md +1 -1
  27. package/templates/brownfield/templates/tech-debt.md +1 -1
  28. package/templates/common/skill/SKILL.md +35 -0
  29. package/templates/greenfield/references/ddd-modeling-guide.md +351 -0
  30. package/templates/greenfield/references/dflow-feedback-flow.md +179 -0
  31. package/templates/greenfield/references/drift-verification.md +195 -0
  32. package/templates/greenfield/references/finish-feature-flow.md +280 -0
  33. package/templates/greenfield/references/git-integration.md +285 -0
  34. package/templates/greenfield/references/init-project-flow.md +447 -0
  35. package/templates/greenfield/references/modify-existing-flow.md +362 -0
  36. package/templates/greenfield/references/new-feature-flow.md +397 -0
  37. package/templates/greenfield/references/new-phase-flow.md +273 -0
  38. package/templates/greenfield/references/pr-review-checklist.md +130 -0
  39. package/templates/greenfield/scaffolding/AI-AGENT-GUIDE.md +31 -4
  40. package/templates/greenfield/scaffolding/CLAUDE-md-snippet.md +15 -13
  41. package/templates/greenfield/scaffolding/Git-principles-gitflow.md +1 -1
  42. package/templates/greenfield/scaffolding/Git-principles-trunk.md +1 -1
  43. package/templates/greenfield/scaffolding/_conventions.md +1 -1
  44. package/templates/greenfield/scaffolding/_overview.md +5 -3
  45. package/templates/greenfield/scaffolding/architecture-decisions-README.md +1 -1
  46. package/templates/greenfield/templates/context-map.md +1 -1
  47. package/templates/greenfield/templates/events.md +1 -1
  48. package/templates/greenfield/templates/glossary.md +1 -1
  49. package/templates/greenfield/templates/models.md +1 -1
  50. package/templates/greenfield/templates/rules.md +1 -1
  51. 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,179 @@
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.
6
+
7
+ This flow is **not** a project feature workflow and does not change the
8
+ application being built. It is a standalone governance/support flow for Dflow
9
+ itself.
10
+
11
+ ## Hard Boundaries
12
+
13
+ - Do not submit anything to GitHub automatically.
14
+ - Do not run `gh issue create`, `gh pr create`, `git push`, or any networked
15
+ submission command from this flow.
16
+ - Do not expose private project details, business rules, customer data,
17
+ secrets, tokens, internal URLs, or proprietary source snippets.
18
+ - Always show the draft to the developer before anything leaves the local
19
+ machine.
20
+ - If the developer later asks to submit through GitHub CLI, stop and treat that
21
+ as a separate explicit task with fresh permission and environment checks.
22
+
23
+ ## Trigger Conditions
24
+
25
+ Enter this flow when:
26
+
27
+ - The developer explicitly runs `/dflow:report-dflow-feedback`.
28
+ - The developer says the Dflow process, template, generated file, or docs seem
29
+ wrong or improvable.
30
+ - The AI notices a clear contradiction or gap in Dflow guidance and asks:
31
+ "This looks like a possible Dflow upstream issue. Should I draft feedback for
32
+ you to review?"
33
+
34
+ Do not interrupt normal development for minor preference differences. If the
35
+ observation is speculative, ask before drafting.
36
+
37
+ ## Output Location
38
+
39
+ Write the draft to:
40
+
41
+ ```text
42
+ dflow/feedback/dflow-feedback-YYYY-MM-DD-{slug}.md
43
+ ```
44
+
45
+ Create `dflow/feedback/` if it does not exist. The file is local project
46
+ working material; the developer decides whether to copy it into a GitHub issue,
47
+ turn it into a PR, or discard it.
48
+
49
+ ## Step 1: Classify the Feedback
50
+
51
+ Classify the feedback as one of:
52
+
53
+ - Bug report
54
+ - Workflow change request
55
+ - Documentation feedback
56
+ - Question / unclear usage
57
+ - Maintainer release/process feedback
58
+
59
+ Capture:
60
+
61
+ - Observed during which flow or command
62
+ - Affected Dflow track: Greenfield, Brownfield, both, or unknown
63
+ - Affected area: CLI, generated template, scaffolding, skill reference,
64
+ tutorial, README/docs, release/governance
65
+ - Whether the issue blocks current project work
66
+
67
+ ## Step 2: Capture Evidence Safely
68
+
69
+ Collect only the evidence needed to explain the Dflow issue.
70
+
71
+ Allowed evidence:
72
+
73
+ - Dflow command name
74
+ - Dflow version if known
75
+ - Template or reference file name
76
+ - Generic project type, such as "existing legacy presentation-framework app"
77
+ - Minimal paraphrased symptom
78
+ - Short sanitized snippets from Dflow-owned files
79
+
80
+ Avoid:
81
+
82
+ - Internal business rules
83
+ - Customer or tenant names
84
+ - Private repository names or URLs
85
+ - Secrets, tokens, credentials, or auth headers
86
+ - Long proprietary code snippets
87
+ - Full logs containing private paths or environment data
88
+
89
+ ## Step 3: Redaction Pass
90
+
91
+ Before writing the issue body section, perform a redaction check and include it
92
+ in the draft:
93
+
94
+ ```markdown
95
+ ## Redaction Checklist
96
+
97
+ - [ ] No secrets, tokens, credentials, or auth headers
98
+ - [ ] No customer, tenant, or private organization names
99
+ - [ ] No proprietary business rules beyond sanitized paraphrase
100
+ - [ ] No private repository URLs or internal hostnames
101
+ - [ ] No long proprietary source snippets
102
+ - [ ] Developer reviewed before submission
103
+ ```
104
+
105
+ Leave the final "Developer reviewed before submission" unchecked unless the
106
+ developer explicitly confirms it.
107
+
108
+ ## Step 4: Write the Feedback Draft
109
+
110
+ Use this structure:
111
+
112
+ ```markdown
113
+ # Dflow Feedback Draft: {short-title}
114
+
115
+ ## Summary
116
+
117
+ {One or two sentences.}
118
+
119
+ ## Type
120
+
121
+ {Bug report | Workflow change request | Documentation feedback | Question | Maintainer process feedback}
122
+
123
+ ## Observed While Using
124
+
125
+ - Dflow version: {version-or-unknown}
126
+ - Command / flow: {command-or-flow}
127
+ - Track: {Greenfield | Brownfield | both | unknown}
128
+ - Project context: {sanitized generic context}
129
+
130
+ ## Affected Dflow Area
131
+
132
+ - {CLI | template | scaffolding | skill reference | tutorial | docs | governance}
133
+ - Files or concepts: {sanitized list}
134
+
135
+ ## Problem
136
+
137
+ {What happened, why it is confusing or harmful, and who is affected.}
138
+
139
+ ## Expected Behavior or Improvement
140
+
141
+ {What Dflow should do or explain instead.}
142
+
143
+ ## Evidence
144
+
145
+ {Minimal sanitized observations.}
146
+
147
+ ## Compatibility / Breaking-Change Risk
148
+
149
+ {None | low | medium | high}, with reasoning.
150
+
151
+ ## Suggested GitHub Issue Body
152
+
153
+ {Copy-ready issue body matching the closest issue template.}
154
+
155
+ ## Optional PR Plan
156
+
157
+ {Only include if the change is small and concrete. Otherwise write "Not recommended yet; start with an issue."}
158
+
159
+ ## Redaction Checklist
160
+
161
+ - [ ] No secrets, tokens, credentials, or auth headers
162
+ - [ ] No customer, tenant, or private organization names
163
+ - [ ] No proprietary business rules beyond sanitized paraphrase
164
+ - [ ] No private repository URLs or internal hostnames
165
+ - [ ] No long proprietary source snippets
166
+ - [ ] Developer reviewed before submission
167
+ ```
168
+
169
+ ## Step 5: Present Submission Options
170
+
171
+ After writing the draft, summarize the options:
172
+
173
+ - Copy the suggested issue body manually into GitHub.
174
+ - Use the optional PR plan as implementation guidance in a Dflow source
175
+ checkout.
176
+ - Discard the draft if it was only a local observation.
177
+
178
+ Do not submit anything automatically. End by naming the draft file path and
179
+ whether any redaction checklist items remain unchecked.