feature-flow-cli 0.1.0__py3-none-any.whl
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.
- feature_flow/__init__.py +3 -0
- feature_flow/__main__.py +7 -0
- feature_flow/_bundle/adapters/codex/feature-flow/SKILL.md +62 -0
- feature_flow/_bundle/adapters/codex/feature-flow/agents/openai.yaml +6 -0
- feature_flow/_bundle/agents/ticket-builder.md +24 -0
- feature_flow/_bundle/agents/ticket-reviewer.md +16 -0
- feature_flow/_bundle/guides/build.md +139 -0
- feature_flow/_bundle/guides/plan.md +93 -0
- feature_flow/_bundle/guides/review.md +87 -0
- feature_flow/_bundle/guides/show.md +30 -0
- feature_flow/_bundle/guides/templates/commands.md +15 -0
- feature_flow/_bundle/guides/templates/learnings.md +7 -0
- feature_flow/_bundle/guides/templates/map.md +26 -0
- feature_flow/_bundle/guides/templates/spec.md +62 -0
- feature_flow/_bundle/guides/templates/ticket.md +29 -0
- feature_flow/_bundle/guides/templates/ui-mockup.md +43 -0
- feature_flow/_bundle/scripts/floor-guard.py +14 -0
- feature_flow/_bundle/scripts/flow-status.py +14 -0
- feature_flow/_bundle/scripts/flow-view.html +536 -0
- feature_flow/_bundle/scripts/flow-view.py +14 -0
- feature_flow/_bundle/scripts/flow.py +14 -0
- feature_flow/_bundle/scripts/gate.py +14 -0
- feature_flow/_bundle/skills/architect-review/SKILL.md +143 -0
- feature_flow/_bundle/skills/automation-design/SKILL.md +274 -0
- feature_flow/_bundle/skills/feature-flow/SKILL.md +62 -0
- feature_flow/checks.py +71 -0
- feature_flow/cli.py +47 -0
- feature_flow/command.py +39 -0
- feature_flow/conductor.py +299 -0
- feature_flow/floorguard.py +340 -0
- feature_flow/gate.py +100 -0
- feature_flow/git.py +47 -0
- feature_flow/install.py +365 -0
- feature_flow/prompts.py +52 -0
- feature_flow/state.py +75 -0
- feature_flow/status.py +231 -0
- feature_flow/tickets.py +168 -0
- feature_flow/view.py +312 -0
- feature_flow_cli-0.1.0.dist-info/METADATA +293 -0
- feature_flow_cli-0.1.0.dist-info/RECORD +44 -0
- feature_flow_cli-0.1.0.dist-info/WHEEL +5 -0
- feature_flow_cli-0.1.0.dist-info/entry_points.txt +3 -0
- feature_flow_cli-0.1.0.dist-info/licenses/LICENSE +21 -0
- feature_flow_cli-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: architect-review
|
|
3
|
+
description: Enterprise architecture review — evaluate a module, feature, or PR diff against production-grade standards. Covers resilience, observability, data contracts, scalability, and security. Returns a prioritized findings report with severity ratings.
|
|
4
|
+
argument-hint: "[file-path | feature-name | 'staged' | 'last-commit']"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Perform an enterprise-grade architecture review of `$ARGUMENTS`.
|
|
8
|
+
|
|
9
|
+
## Step 0: Gather the target
|
|
10
|
+
|
|
11
|
+
- If `$ARGUMENTS` is `staged` → run `git diff --staged`
|
|
12
|
+
- If `$ARGUMENTS` is `last-commit` → run `git diff HEAD~1 HEAD`
|
|
13
|
+
- If `$ARGUMENTS` is a file path → read the file
|
|
14
|
+
- If `$ARGUMENTS` is a feature name → search `plans/$ARGUMENTS/` (or the project's ticket folder) and the relevant source directories
|
|
15
|
+
- If `$ARGUMENTS` is empty → review `git diff --staged`
|
|
16
|
+
|
|
17
|
+
Read all relevant files before forming any judgements. For backend code, also read the module file and any dependent service interfaces.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Step 1: Structural Architecture Review
|
|
22
|
+
|
|
23
|
+
### Module Boundaries
|
|
24
|
+
- [ ] **Single Responsibility**: Does each class/module own exactly one domain concern?
|
|
25
|
+
- [ ] **Interface Segregation**: Are interfaces narrow (consumers depend only on what they use)?
|
|
26
|
+
- [ ] **Dependency Direction**: Do dependencies flow inward (domain ← application ← infrastructure)?
|
|
27
|
+
- [ ] **Circular Dependencies**: Any imports that cycle back to the same module?
|
|
28
|
+
- [ ] **Module Encapsulation**: Is the public surface (exports, registered providers) deliberate, with internals kept private?
|
|
29
|
+
|
|
30
|
+
### Data Contracts
|
|
31
|
+
- [ ] **Typed boundaries**: All external inputs (WS messages, HTTP bodies, API responses) validated with typed guards?
|
|
32
|
+
- [ ] **Discriminated unions**: Event/message types use union types, not string comparisons?
|
|
33
|
+
- [ ] **No `any`**: Every `any` is a contract hole — flag each one with its severity
|
|
34
|
+
- [ ] **Shared types**: Cross-boundary types live in one shared definition, not duplicated per side?
|
|
35
|
+
- [ ] **Immutability**: Config objects and props marked `readonly`?
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## Step 2: Resilience & Fault Tolerance Review
|
|
40
|
+
|
|
41
|
+
For each async operation or external call found in the code:
|
|
42
|
+
|
|
43
|
+
- [ ] **Timeout**: Is there a timeout guard? Unbounded awaits are production incidents.
|
|
44
|
+
- [ ] **Retry**: Is retry implemented with exponential backoff + jitter for transient failures?
|
|
45
|
+
- [ ] **Circuit Breaker**: Is there a circuit breaker to stop cascading failure under sustained errors?
|
|
46
|
+
- [ ] **Idempotency**: Can this operation safely run twice without side effects?
|
|
47
|
+
- [ ] **Dead Letter / Fallback**: What happens when the operation permanently fails?
|
|
48
|
+
- [ ] **Graceful Degradation**: Does the system continue operating (degraded) when this service is unavailable?
|
|
49
|
+
- [ ] **Bulkhead Isolation**: Can failure in this path impact unrelated request paths?
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
## Step 3: Observability Review
|
|
54
|
+
|
|
55
|
+
- [ ] **Structured logging**: All log calls use the project's structured logger with metadata, no bare `console.log` / `print`
|
|
56
|
+
- [ ] **Correlation IDs**: Are request/session IDs propagated through async boundaries?
|
|
57
|
+
- [ ] **Error context**: Caught errors logged with `{ error, stack, context }` — not just `err.message`
|
|
58
|
+
- [ ] **Operation timing**: Are slow paths (AI calls, DB queries, external APIs) measured?
|
|
59
|
+
- [ ] **Failure visibility**: Are error conditions logged at `error` level, warnings at `warn`?
|
|
60
|
+
- [ ] **OpenTelemetry**: Are spans created for significant operations? Is trace context propagated?
|
|
61
|
+
|
|
62
|
+
---
|
|
63
|
+
|
|
64
|
+
## Step 4: Performance & Scalability Review
|
|
65
|
+
|
|
66
|
+
- [ ] **Blocking operations**: Any sync-heavy work on the event loop that should be offloaded?
|
|
67
|
+
- [ ] **N+1 queries**: Any DB or cache access inside a loop?
|
|
68
|
+
- [ ] **Caching opportunity**: Is expensive, repeated computation cached (Redis or in-memory with TTL)?
|
|
69
|
+
- [ ] **Queue offload**: Should long-running work be pushed to a background job queue instead of awaited inline?
|
|
70
|
+
- [ ] **Memory leaks**: Event listeners, intervals, and subscriptions cleaned up properly?
|
|
71
|
+
- [ ] **Pagination**: Are unbounded list queries guarded with limits?
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Step 5: Security Review
|
|
76
|
+
|
|
77
|
+
- [ ] **Input validation**: All external inputs sanitized before use?
|
|
78
|
+
- [ ] **Injection risk**: Any user data concatenated into queries, shell commands, or log messages?
|
|
79
|
+
- [ ] **Auth guards**: Are protected routes/WS handlers gated with auth checks?
|
|
80
|
+
- [ ] **Secret exposure**: No secrets, API keys, or PII in logs or error responses?
|
|
81
|
+
- [ ] **Human-in-the-loop**: Destructive or irreversible operations require explicit user approval?
|
|
82
|
+
- [ ] **Rate limiting**: Are high-frequency endpoints or WS message types rate-limited?
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## Step 6: Testability Review
|
|
87
|
+
|
|
88
|
+
- [ ] **Dependency injection**: Are dependencies injected (not instantiated inline) so they can be mocked?
|
|
89
|
+
- [ ] **Pure functions**: Are complex transformations extracted as pure, easily-testable functions?
|
|
90
|
+
- [ ] **Test surface**: Is there a test in the project's test directory covering the primary paths?
|
|
91
|
+
- [ ] **Edge cases in tests**: Are error paths (timeout, null input, unavailable service) tested?
|
|
92
|
+
|
|
93
|
+
---
|
|
94
|
+
|
|
95
|
+
## Step 7: Project Convention Violations
|
|
96
|
+
|
|
97
|
+
Read `CLAUDE.md`, `AGENTS.md`, and any contributing or style docs in the repo. For each explicit rule or "forbidden" item they list, check whether the target violates it, and add each violation to the findings table with the rule it breaks. If the repo documents no conventions, mark this category ✅ PASS.
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Step 8: Produce the Report
|
|
102
|
+
|
|
103
|
+
Output a prioritized findings report:
|
|
104
|
+
|
|
105
|
+
```
|
|
106
|
+
## Architecture Review: [target]
|
|
107
|
+
|
|
108
|
+
### Summary
|
|
109
|
+
[1-3 sentence overall assessment: is this production-ready, needs hardening, or has blockers?]
|
|
110
|
+
|
|
111
|
+
### Severity Legend
|
|
112
|
+
🔴 BLOCKER — Must fix before merge. Production incident risk or contract breakage.
|
|
113
|
+
🟠 HIGH — Fix in this PR or immediately after. Degrades reliability or observability.
|
|
114
|
+
🟡 MEDIUM — Fix in next iteration. Code smell or missing best practice.
|
|
115
|
+
🟢 LOW — Nice to have. Minor improvement.
|
|
116
|
+
✅ PASS — No issues found in this category.
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
### Findings
|
|
121
|
+
|
|
122
|
+
| # | Severity | Category | Location | Issue | Recommendation |
|
|
123
|
+
|---|----------|----------|----------|-------|----------------|
|
|
124
|
+
| 1 | 🔴 | Resilience | `ServiceName.method()` | No timeout on upstream API call | Wrap with `Promise.race([call, timeout(10_000)])` |
|
|
125
|
+
| 2 | 🟠 | Observability | `handler.ts:42` | Error caught but not logged with context | `logger.error('Failed to process', { error: e.message, sessionId })` |
|
|
126
|
+
...
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
### Categories with No Issues
|
|
131
|
+
✅ Data Contracts — all types narrow and properly guarded
|
|
132
|
+
✅ Security — inputs validated, auth guards present
|
|
133
|
+
...
|
|
134
|
+
|
|
135
|
+
---
|
|
136
|
+
|
|
137
|
+
### Recommended Next Actions
|
|
138
|
+
1. [Highest priority fix — one sentence]
|
|
139
|
+
2. [Second priority fix]
|
|
140
|
+
3. [Optional improvement]
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
If there are zero findings in a category, mark it ✅ PASS — do not pad the report with empty sections.
|
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: automation-design
|
|
3
|
+
description: Design an enterprise-grade automation pipeline or workflow system. Takes a plain-language description of what needs to be automated and produces a full technical blueprint: pipeline stages, fault-tolerance model, state machine, data contracts, observability plan, and a phased implementation plan that the feature-flow skill can turn into tickets.
|
|
4
|
+
argument-hint: "[plain-language description of the automation, e.g., 'auto-retry failed jobs with backoff', 'nightly batch import pipeline', 'resume interrupted uploads']"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
Design an enterprise automation system for: **$ARGUMENTS**
|
|
8
|
+
|
|
9
|
+
You are a world-class automation architect. You design systems that are **reliable at scale**, **observable in production**, and **maintainable by a team**. Every automation you design has a clear failure model, not just a happy path.
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Step 1: Clarify the Automation Scope
|
|
14
|
+
|
|
15
|
+
Before designing, establish:
|
|
16
|
+
|
|
17
|
+
1. **Trigger**: What starts the automation? (user action, schedule, event, webhook, threshold breach)
|
|
18
|
+
2. **Input**: What data does it receive? What is the expected shape and validation requirements?
|
|
19
|
+
3. **Output / Side Effect**: What does it produce or change? (DB write, API call, UI update, message sent)
|
|
20
|
+
4. **Frequency & Volume**: How often? How many concurrent instances? What's the peak load?
|
|
21
|
+
5. **Latency Requirement**: Is this real-time (<100ms), near-real-time (<2s), or batch (minutes)?
|
|
22
|
+
6. **Failure Tolerance**: What's acceptable? (retry silently, alert user, block, compensate?)
|
|
23
|
+
7. **Statefulness**: Does it need to resume after interruption, or restart from scratch?
|
|
24
|
+
|
|
25
|
+
If the user already provided enough context, skip the interview and proceed to Step 2.
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Step 2: Research Existing Patterns
|
|
30
|
+
|
|
31
|
+
Before proposing anything new, read the codebase:
|
|
32
|
+
|
|
33
|
+
1. **Existing pipeline code** — workers, job processors, schedulers, event handlers
|
|
34
|
+
2. **Existing workflow patterns** — similar automations already in the repo, and existing `plans/` folders
|
|
35
|
+
3. **Shared types** — existing message/event/contract types the automation should reuse
|
|
36
|
+
4. **Job queue usage** — how existing jobs are defined, retried, and monitored
|
|
37
|
+
5. **Trigger and message flow** — existing gateways, controllers, or event buses the automation could plug into
|
|
38
|
+
|
|
39
|
+
Identify: what can be reused, what needs extending, what must be built from scratch.
|
|
40
|
+
|
|
41
|
+
---
|
|
42
|
+
|
|
43
|
+
## Step 3: Design the Automation Blueprint
|
|
44
|
+
|
|
45
|
+
### 3a. Pipeline Stage Diagram
|
|
46
|
+
|
|
47
|
+
Model the automation as a sequence of named, isolated stages:
|
|
48
|
+
|
|
49
|
+
```
|
|
50
|
+
[Trigger / Input]
|
|
51
|
+
│
|
|
52
|
+
▼
|
|
53
|
+
┌─────────────────────────────────────────────┐
|
|
54
|
+
│ Stage 1: INPUT VALIDATION │
|
|
55
|
+
│ • Validate schema (throw on invalid) │
|
|
56
|
+
│ • Normalize / enrich input │
|
|
57
|
+
│ • Emit: validation.passed / failed metric │
|
|
58
|
+
└─────────────────────────────────────────────┘
|
|
59
|
+
│ valid input
|
|
60
|
+
▼
|
|
61
|
+
┌─────────────────────────────────────────────┐
|
|
62
|
+
│ Stage 2: [DOMAIN PROCESSING STAGE NAME] │
|
|
63
|
+
│ • [Core business logic] │
|
|
64
|
+
│ • Side effect: [DB write / API call / etc.] │
|
|
65
|
+
│ • Retry: [yes/no — strategy] │
|
|
66
|
+
└─────────────────────────────────────────────┘
|
|
67
|
+
│
|
|
68
|
+
├─── success ──► [Stage 3 or Output]
|
|
69
|
+
│
|
|
70
|
+
└─── failure ──► [Error Handler / DLQ]
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
Rules for pipeline design:
|
|
74
|
+
- Each stage is a **single responsibility** — one input, one output, one error path
|
|
75
|
+
- Stages communicate via **typed events or return values**, never shared mutable state
|
|
76
|
+
- Each stage logs entry, exit, and duration
|
|
77
|
+
- Each stage that calls an external system has a **timeout + fallback**
|
|
78
|
+
|
|
79
|
+
### 3b. State Machine (if stateful)
|
|
80
|
+
|
|
81
|
+
If the automation has multiple states (e.g., a multi-step workflow), define the state machine:
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
States: PENDING → RUNNING → [COMPLETED | FAILED | RETRYING | CANCELLED]
|
|
85
|
+
|
|
86
|
+
Transitions:
|
|
87
|
+
PENDING + trigger → RUNNING (persist state, start processing)
|
|
88
|
+
RUNNING + success → COMPLETED (persist result, emit event)
|
|
89
|
+
RUNNING + retryable → RETRYING (increment attempt, schedule next)
|
|
90
|
+
RUNNING + fatal-error → FAILED (persist error, alert, DLQ)
|
|
91
|
+
RETRYING + max-attempts → FAILED
|
|
92
|
+
* + cancel → CANCELLED (compensate if needed)
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 3c. Fault Tolerance Model
|
|
96
|
+
|
|
97
|
+
For each external call or async operation in the pipeline:
|
|
98
|
+
|
|
99
|
+
| Operation | Timeout | Retry Strategy | Max Attempts | Circuit Breaker | Fallback |
|
|
100
|
+
|-----------|---------|----------------|--------------|-----------------|---------|
|
|
101
|
+
| [op name] | [ms] | [exp backoff + jitter] | [N] | [yes/no] | [what happens] |
|
|
102
|
+
|
|
103
|
+
**Retry formula**: `delay = min(baseDelay * 2^attempt + jitter(0..500ms), maxDelay)`
|
|
104
|
+
|
|
105
|
+
### 3d. Data Contracts
|
|
106
|
+
|
|
107
|
+
Define all types at the pipeline boundary:
|
|
108
|
+
|
|
109
|
+
```typescript
|
|
110
|
+
// Input contract
|
|
111
|
+
interface AutomationInput {
|
|
112
|
+
readonly id: string; // correlation ID for tracing
|
|
113
|
+
readonly triggeredBy: string; // user ID or system
|
|
114
|
+
readonly payload: [specific type];
|
|
115
|
+
readonly metadata: {
|
|
116
|
+
readonly sessionId: string;
|
|
117
|
+
readonly timestamp: number;
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Output / result contract
|
|
122
|
+
type AutomationResult =
|
|
123
|
+
| { readonly status: 'completed'; readonly output: [type]; readonly duration: number }
|
|
124
|
+
| { readonly status: 'failed'; readonly error: string; readonly attempt: number }
|
|
125
|
+
| { readonly status: 'partial'; readonly completed: [type][]; readonly failed: [type][] };
|
|
126
|
+
|
|
127
|
+
// Event emitted to consumers
|
|
128
|
+
interface AutomationEvent {
|
|
129
|
+
readonly type: 'automation.completed' | 'automation.failed' | 'automation.step';
|
|
130
|
+
readonly automationId: string;
|
|
131
|
+
readonly payload: AutomationResult;
|
|
132
|
+
}
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
### 3e. Observability Plan
|
|
136
|
+
|
|
137
|
+
Every automation must be observable. Define:
|
|
138
|
+
|
|
139
|
+
```
|
|
140
|
+
Logs (structured):
|
|
141
|
+
INFO — stage start/end with duration
|
|
142
|
+
WARN — retryable failure (attempt N of M)
|
|
143
|
+
ERROR — fatal failure with full context { error, stack, input.id, stage }
|
|
144
|
+
|
|
145
|
+
Metrics (OpenTelemetry or the project's metrics library):
|
|
146
|
+
automation.[name].duration — histogram
|
|
147
|
+
automation.[name].success — counter
|
|
148
|
+
automation.[name].failure — counter (tagged by stage + error type)
|
|
149
|
+
automation.[name].retry — counter
|
|
150
|
+
automation.[name].queue.depth — gauge (if queue-based)
|
|
151
|
+
|
|
152
|
+
Traces:
|
|
153
|
+
Root span: automation.[name]
|
|
154
|
+
Child spans: one per stage
|
|
155
|
+
Attributes: correlation ID, input size, output size, attempt count
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### 3f. Job Queue Configuration (if async/background)
|
|
159
|
+
|
|
160
|
+
Express these settings in whatever queue library the project already uses:
|
|
161
|
+
|
|
162
|
+
```typescript
|
|
163
|
+
// Job definition
|
|
164
|
+
interface [AutomationName]Job {
|
|
165
|
+
data: AutomationInput;
|
|
166
|
+
opts: {
|
|
167
|
+
attempts: 3,
|
|
168
|
+
backoff: { type: 'exponential', delay: 1000 },
|
|
169
|
+
removeOnComplete: 100, // keep last 100 completed
|
|
170
|
+
removeOnFail: 500, // keep last 500 failed for debugging
|
|
171
|
+
timeout: 30_000, // kill job after 30s
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Step 4: Integration Points
|
|
179
|
+
|
|
180
|
+
Map how this automation fits into the project's existing architecture:
|
|
181
|
+
|
|
182
|
+
```
|
|
183
|
+
[Where it plugs in]
|
|
184
|
+
├── Message trigger → existing gateway/handler → new message type: [name]
|
|
185
|
+
├── OR HTTP trigger → new endpoint on an existing service
|
|
186
|
+
├── OR Scheduled → queue or scheduler cron job
|
|
187
|
+
└── OR Event-driven → subscribes to existing [EventName] event
|
|
188
|
+
|
|
189
|
+
[Output goes to]
|
|
190
|
+
├── Stream back to the client (real-time progress)
|
|
191
|
+
├── OR DB persistence (entity/table: [name])
|
|
192
|
+
├── OR Cache (TTL: [value])
|
|
193
|
+
└── OR triggers downstream automation: [name]
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
---
|
|
197
|
+
|
|
198
|
+
## Step 5: Implementation Plan
|
|
199
|
+
|
|
200
|
+
Produce a phased plan. Each phase should be small enough to become one ticket:
|
|
201
|
+
|
|
202
|
+
### Phase 1: Types & Contracts
|
|
203
|
+
- Define all input/output/event types where the project keeps shared types
|
|
204
|
+
- No implementation — types only
|
|
205
|
+
- Verify: `[the project's build or typecheck command]`
|
|
206
|
+
|
|
207
|
+
### Phase 2: Core Pipeline Service
|
|
208
|
+
- Create the service in the project's usual location for the domain, named after the automation
|
|
209
|
+
- Implement pipeline stages as private methods
|
|
210
|
+
- Inject dependencies (optional services gracefully degrade)
|
|
211
|
+
- Include full fault tolerance (retry, timeout, circuit breaker)
|
|
212
|
+
- Verify: `[the project's build command]`
|
|
213
|
+
|
|
214
|
+
### Phase 3: Queue / Trigger Integration
|
|
215
|
+
- Register the queue processor if async
|
|
216
|
+
- Wire into the chosen trigger: gateway, endpoint, scheduler or event subscription
|
|
217
|
+
- Add routing for the new message type or route
|
|
218
|
+
- Verify: `[the project's build command]`
|
|
219
|
+
|
|
220
|
+
### Phase 4: Tests
|
|
221
|
+
- Unit tests for each pipeline stage in isolation
|
|
222
|
+
- Integration test for the happy path
|
|
223
|
+
- Error path tests (timeout, retry exhaustion, invalid input)
|
|
224
|
+
- Verify: `[the project's test command]`
|
|
225
|
+
|
|
226
|
+
### Phase 5: Client / UI Integration (only if the automation has a user-facing surface)
|
|
227
|
+
- Add client state for tracking automation status
|
|
228
|
+
- Wire event listeners for progress streaming
|
|
229
|
+
- UI: progress indicator + error state
|
|
230
|
+
- Verify: `[the client build and test command]`
|
|
231
|
+
|
|
232
|
+
---
|
|
233
|
+
|
|
234
|
+
## Step 6: Create Strategy Folder
|
|
235
|
+
|
|
236
|
+
Ask the user: "Should I turn this design into a plan at `plans/[automation-name]/` (a spec, a map and a graph of tickets)?"
|
|
237
|
+
|
|
238
|
+
If yes → plan it with the feature-flow skill (`/feature-flow [automation-name]` in Claude Code, `$feature-flow [automation-name]` in Codex), using this blueprint as the specification.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## Step 7: Output the Blueprint
|
|
243
|
+
|
|
244
|
+
```
|
|
245
|
+
## Automation Blueprint: [Name]
|
|
246
|
+
|
|
247
|
+
### What it does
|
|
248
|
+
[2-3 sentences: trigger → processing → output]
|
|
249
|
+
|
|
250
|
+
### Architecture Pattern
|
|
251
|
+
[Pipeline / State Machine / Event-Driven / Scheduled Batch]
|
|
252
|
+
|
|
253
|
+
### Pipeline Stages
|
|
254
|
+
[diagram from Step 3a]
|
|
255
|
+
|
|
256
|
+
### Fault Tolerance Summary
|
|
257
|
+
[table from Step 3c]
|
|
258
|
+
|
|
259
|
+
### Key Data Types
|
|
260
|
+
[contracts from Step 3d]
|
|
261
|
+
|
|
262
|
+
### Integration Point
|
|
263
|
+
[where it plugs in to the existing architecture]
|
|
264
|
+
|
|
265
|
+
### Implementation Phases
|
|
266
|
+
[phases from Step 5 — with file paths and verify commands]
|
|
267
|
+
|
|
268
|
+
### Risks & Mitigations
|
|
269
|
+
- [Risk 1]: [Mitigation]
|
|
270
|
+
- [Risk 2]: [Mitigation]
|
|
271
|
+
|
|
272
|
+
### Open Questions
|
|
273
|
+
- [Any decisions that need user input before implementation]
|
|
274
|
+
```
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: feature-flow
|
|
3
|
+
description: Use to plan a feature or build its tickets, one fresh builder and one fresh reviewer subagent per ticket, with scripts/flow.py deciding every step. Also draws the ticket graph with show.
|
|
4
|
+
argument-hint: "[feature] [show] [auto]"
|
|
5
|
+
disable-model-invocation: true
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
Plan or build the feature in `$ARGUMENTS`.
|
|
9
|
+
|
|
10
|
+
The first word is the **feature**. `show` means draw the graph. `auto` means ask nothing and go. If no feature was given, list `plans/*/` and ask which one.
|
|
11
|
+
|
|
12
|
+
The conductor is `python3 scripts/flow.py <feature> <command>`. It decides the order, runs the gate and the floor guard, and keeps its state in `.feature-flow/state/`, a folder git ignores. You ask it, and you do what it says. The guides it uses are in `guides/` or `.feature-flow/guides/`.
|
|
13
|
+
|
|
14
|
+
## Show
|
|
15
|
+
|
|
16
|
+
With `show`, follow `guides/show.md` for the feature and stop.
|
|
17
|
+
|
|
18
|
+
## Plan
|
|
19
|
+
|
|
20
|
+
If `plans/<feature>/` does not exist, run `FLOW_INVOKE=/feature-flow python3 scripts/flow.py <feature> start` and check that it prints `PLAN`. Then:
|
|
21
|
+
|
|
22
|
+
1. Follow `guides/plan.md` with the user.
|
|
23
|
+
2. Run `python3 scripts/flow-status.py <feature> --check` and fix every problem.
|
|
24
|
+
3. Stop. List the files you created and suggest the commit command (`git add plans/<feature> && git commit -m "docs(<feature>): plan"`). Say to commit the plan and run `/feature-flow <feature>` again. While the plan is uncommitted, do not say the feature is ready to build.
|
|
25
|
+
|
|
26
|
+
## Before building
|
|
27
|
+
|
|
28
|
+
With a plan present, check these before `start`, and stop at the first that fails:
|
|
29
|
+
|
|
30
|
+
- `git status --porcelain` is empty.
|
|
31
|
+
- `python3 scripts/flow-status.py <feature> --check` prints `OK`.
|
|
32
|
+
- `ticket-builder.md` and `ticket-reviewer.md` are in `.claude/agents/`, `~/.claude/agents/`, or `$CLAUDE_HOME/agents/` when `CLAUDE_HOME` is set. If not, say to run `bash install.sh` from feature-flow.
|
|
33
|
+
|
|
34
|
+
Then run `FLOW_INVOKE=/feature-flow python3 scripts/flow.py <feature> start`. It prints `OK <token>`. Keep the token and put `FLOW_SESSION=<token>` in front of **every** later conductor command, with `FLOW_INVOKE=/feature-flow`.
|
|
35
|
+
|
|
36
|
+
If `start` prints `STOP` naming another owner, another session may still be working this feature. Ask the user whether that session is closed. Only on a clear yes, run `start` once more with `FLOW_TAKEOVER=1`. In `auto` mode, never take over: report and stop.
|
|
37
|
+
|
|
38
|
+
Say what will happen: for each ticket, a builder subagent and then a reviewer subagent. After every few tickets (`FLOW_TICKETS_PER_SESSION`, default 4) you hand off to a new session. Ask for a yes, unless `auto` was given.
|
|
39
|
+
|
|
40
|
+
## The loop
|
|
41
|
+
|
|
42
|
+
Run `next`, act on its one line, and repeat:
|
|
43
|
+
|
|
44
|
+
- `BUILD <ticket> <NN> <sha>`: run `prompt`. Spawn a `ticket-builder` subagent with that output as its whole prompt, and ask for a short summary back. Wait for its final reply: an Agent call can return before the subagent finishes, and the reply then arrives as a notification. Then run `next`.
|
|
45
|
+
- `REVIEW <ticket> <NN> <sha>`: run `prompt` and spawn a `ticket-reviewer` subagent with it. Wait for its final reply. Save the whole reply with Bash, not the Write tool, into the conductor's git-ignored state folder: `cat > .feature-flow/state/flow-review-<feature>.txt <<'EOF'` ... `EOF`. Never under `.git`: an unattended session is refused there as a sensitive file. Then, as separate commands, run `verdict .feature-flow/state/flow-review-<feature>.txt` and `next`. If `verdict` prints `RETRY`, just run `next`.
|
|
46
|
+
- `DONE <summary>`: report it and suggest `/feature-flow <feature> show`.
|
|
47
|
+
- `STOP <reason>`: report the reason, run `python3 scripts/flow-status.py <feature>`, show the table, and stop.
|
|
48
|
+
- `HANDOFF <line>`: stop here. Tell the user to open a new session and type exactly `<line>`. The new session resumes where this one stopped.
|
|
49
|
+
|
|
50
|
+
A conductor command looks like this:
|
|
51
|
+
|
|
52
|
+
FLOW_SESSION=<token> FLOW_INVOKE=/feature-flow python3 scripts/flow.py <feature> next
|
|
53
|
+
|
|
54
|
+
## Rules while building
|
|
55
|
+
|
|
56
|
+
These apply from `start` on, once a plan exists. Planning (above) writes the plan files itself.
|
|
57
|
+
|
|
58
|
+
- Never run the gate, the floor guard or a review yourself. The conductor runs the checks, and the reviewer subagent reviews.
|
|
59
|
+
- Never edit a ticket, the plan or the code, and never commit. The builder does that.
|
|
60
|
+
- Never skip a step, reorder steps, or decide the next step yourself. Only `next` decides.
|
|
61
|
+
- Never read a ticket's `## Answer` into the reviewer's prompt. `prompt` already holds everything it needs.
|
|
62
|
+
- Tell the user one short line per phase (`01 built`, `01 review: PASS`), not the subagents' reports.
|
feature_flow/checks.py
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
"""The checks the conductor runs: the bash scripts it still calls, and the ported ones in process.
|
|
2
|
+
|
|
3
|
+
Each call lives in one function so plans/interactive-flow-python can swap it for an
|
|
4
|
+
in-process call without touching the conductor.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import subprocess
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
from feature_flow import status
|
|
11
|
+
from feature_flow import gate as gate_module
|
|
12
|
+
from feature_flow import floorguard
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class Result:
|
|
16
|
+
def __init__(self, code, out):
|
|
17
|
+
self.code = code
|
|
18
|
+
self.out = out
|
|
19
|
+
|
|
20
|
+
@property
|
|
21
|
+
def ok(self):
|
|
22
|
+
return self.code == 0
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class _Collect:
|
|
26
|
+
"""stdout and stderr of an in-process port, folded together in the order they were written."""
|
|
27
|
+
|
|
28
|
+
def __init__(self):
|
|
29
|
+
self.parts = []
|
|
30
|
+
|
|
31
|
+
def write(self, text):
|
|
32
|
+
self.parts.append(text)
|
|
33
|
+
|
|
34
|
+
def text(self):
|
|
35
|
+
return "".join(self.parts)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _bash(script, *args):
|
|
39
|
+
result = subprocess.run(["bash", str(script)] + [str(a) for a in args],
|
|
40
|
+
stdout=subprocess.PIPE, stderr=subprocess.STDOUT, universal_newlines=True)
|
|
41
|
+
return Result(result.returncode, result.stdout)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def flow_status(scripts, feature, mode):
|
|
45
|
+
"""The ticket graph reader with --next, --counts or --check, in process. stderr is folded into the output."""
|
|
46
|
+
out = _Collect()
|
|
47
|
+
code = status.run([feature, mode], out, out)
|
|
48
|
+
return Result(code, out.text())
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def gate(scripts, feature):
|
|
52
|
+
"""The gate, run in process. stderr is folded into the output as the subprocess call did."""
|
|
53
|
+
chunks = []
|
|
54
|
+
code = gate_module.run(str(feature), chunks.append, chunks.append)
|
|
55
|
+
out = b"".join(chunks).decode("utf-8", "replace").replace("\r\n", "\n").replace("\r", "\n")
|
|
56
|
+
return Result(code, out)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def floor_guard(scripts, feature, num, base):
|
|
60
|
+
"""feature_flow.floorguard in-process; stdout and stderr are folded together in order."""
|
|
61
|
+
chunks = []
|
|
62
|
+
code = floorguard.run([str(feature), str(num), str(base)], chunks.append, chunks.append)
|
|
63
|
+
out = "".join(chunks).encode("utf-8", "surrogateescape").decode("utf-8", "replace")
|
|
64
|
+
return Result(code, out)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def smoke(command):
|
|
68
|
+
args, use_shell = gate_module.shell(command)
|
|
69
|
+
result = subprocess.run(args, shell=use_shell, stdout=subprocess.PIPE, stderr=subprocess.STDOUT,
|
|
70
|
+
universal_newlines=True)
|
|
71
|
+
return Result(result.returncode, result.stdout)
|
feature_flow/cli.py
ADDED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""python3 scripts/flow.py <feature> <command>: the conductor's command line."""
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
from feature_flow.conductor import Conductor, NoPhase, Stop
|
|
7
|
+
|
|
8
|
+
USAGE = "usage: python3 scripts/flow.py <feature> start | next | prompt | verdict <file>"
|
|
9
|
+
COMMANDS = ("start", "next", "prompt", "verdict")
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def main(argv=None, scripts=None):
|
|
13
|
+
args = list(sys.argv[1:] if argv is None else argv)
|
|
14
|
+
if len(args) < 2 or args[1] not in COMMANDS or not args[0] or args[0].startswith("-"):
|
|
15
|
+
print(USAGE, file=sys.stderr)
|
|
16
|
+
return 2
|
|
17
|
+
feature, command = args[0], args[1]
|
|
18
|
+
if (command == "verdict") != (len(args) == 3) or len(args) > 3:
|
|
19
|
+
print(USAGE, file=sys.stderr)
|
|
20
|
+
return 2
|
|
21
|
+
scripts = Path(scripts) if scripts else Path.cwd() / "scripts"
|
|
22
|
+
conductor = None
|
|
23
|
+
try:
|
|
24
|
+
conductor = Conductor(feature, scripts)
|
|
25
|
+
if command == "verdict":
|
|
26
|
+
line = conductor.verdict(args[2])
|
|
27
|
+
elif command == "prompt":
|
|
28
|
+
line = conductor.prompt()
|
|
29
|
+
elif command == "start":
|
|
30
|
+
line = conductor.start()
|
|
31
|
+
else:
|
|
32
|
+
line = conductor.next()
|
|
33
|
+
except NoPhase as err:
|
|
34
|
+
print(err, file=sys.stderr)
|
|
35
|
+
return 2
|
|
36
|
+
except Stop as stop:
|
|
37
|
+
line = "STOP %s" % stop
|
|
38
|
+
if conductor is not None:
|
|
39
|
+
conductor.log("STOP")
|
|
40
|
+
print(line)
|
|
41
|
+
return 1
|
|
42
|
+
print(line, end="" if line.endswith("\n") else "\n")
|
|
43
|
+
return 0
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
if __name__ == "__main__":
|
|
47
|
+
sys.exit(main())
|
feature_flow/command.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"""feature-flow: the command an installed package puts on PATH (`uvx feature-flow-cli install .`).
|
|
2
|
+
|
|
3
|
+
It installs the flow into a repo and reads a plan. The flow itself runs from the repo, as
|
|
4
|
+
python3 scripts/flow.py, with the copy the installer put there.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import os
|
|
8
|
+
import sys
|
|
9
|
+
|
|
10
|
+
from feature_flow import __version__, install, status, view
|
|
11
|
+
|
|
12
|
+
USAGE = """\
|
|
13
|
+
usage: feature-flow install [target-repo] [--agent claude|codex|all] [--user] [--force] [--dry-run]
|
|
14
|
+
feature-flow status <feature> [--next | --counts | --check | --mermaid [plain] | --json]
|
|
15
|
+
feature-flow view <feature> [--watch] [--no-open] [--out FILE]
|
|
16
|
+
feature-flow --version
|
|
17
|
+
install copies the skill, its roles, scripts and guides into a repo; feature-flow install --help says more.
|
|
18
|
+
status and view read plans/<feature>/ in the current directory, like scripts/flow-status.py and scripts/flow-view.py.
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def main(argv=None):
|
|
23
|
+
args = list(sys.argv[1:] if argv is None else argv)
|
|
24
|
+
if not args or args[0] in ("-h", "--help", "help"):
|
|
25
|
+
out = sys.stdout if args else sys.stderr
|
|
26
|
+
out.write(USAGE)
|
|
27
|
+
return 0 if args else 2
|
|
28
|
+
command, rest = args[0], args[1:]
|
|
29
|
+
if command in ("-V", "--version"):
|
|
30
|
+
print("feature-flow " + __version__)
|
|
31
|
+
return 0
|
|
32
|
+
if command == "install":
|
|
33
|
+
return install.main(install.source_root(), rest)
|
|
34
|
+
if command == "status":
|
|
35
|
+
return status.main(rest)
|
|
36
|
+
if command == "view":
|
|
37
|
+
return view.main(rest, here=os.path.join(install.source_root(), "scripts"))
|
|
38
|
+
sys.stderr.write("unknown command: %s\n%s" % (command, USAGE))
|
|
39
|
+
return 2
|