Skip to content

Commit fa2abf1

Browse files
committed
Add product flow explorer
1 parent d46ead6 commit fa2abf1

6 files changed

Lines changed: 1391 additions & 11 deletions

File tree

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,7 @@ Check configured live app product targets:
135135

136136
```bash
137137
npx codedecay product --format markdown
138+
npx codedecay product --target web --explore --max-pages 5 --format markdown
138139
```
139140

140141
## Commands
@@ -152,7 +153,7 @@ npx codedecay product --format markdown
152153
| `codedecay memory-learn` | Learn local memory from CI, PR, and CodeDecay report signals. |
153154
| `codedecay execute` | Run explicitly configured local commands and OSS tool adapters. |
154155
| `codedecay differential` | Run configured probes on base and head and compare behavior. |
155-
| `codedecay product` | Check configured live app product targets with optional safe startup and teardown. |
156+
| `codedecay product` | Check configured live app product targets and optionally crawl product flows with project Playwright. |
156157
| `codedecay mcp` | Start a local MCP server for agent clients. |
157158
| `codedecay help` | Show root or per-command help. |
158159
| `codedecay man` | Show a longer manual page for a command. |
@@ -197,6 +198,7 @@ codedecay uninstall --purge-local
197198
| --- | --- | --- |
198199
| `codedecay analyze`, `redteam`, `agent`, `snapshot` | Yes | Runs deterministic local analysis with no model calls. |
199200
| `codedecay execute`, `differential` | No | Runs only repo-allowlisted local commands after explicit opt-in. |
201+
| `codedecay product --explore` | No | Uses a project-provided Playwright install to crawl configured live app targets after explicit opt-in. |
200202
| `codedecay llm-review` | No | Calls a user-owned provider only when the user invokes it directly. |
201203
| Optional LLM providers | No | Disabled by default. User-owned providers are configured explicitly and only commands that opt in may call them. |
202204

docs/product-testing.md

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,40 @@ safety:
8282
allowCommands: true
8383
```
8484

85+
## Playwright Flow Explorer
86+
87+
Use `codedecay product --explore` to crawl configured product targets and write a
88+
stable flow map artifact.
89+
90+
```bash
91+
npx codedecay product --target web --explore --max-pages 5 --format markdown
92+
```
93+
94+
The explorer is intentionally conservative:
95+
96+
- it runs only when `safety.allowCommands: true`,
97+
- it loads `playwright` from the target project,
98+
- it does not install Playwright packages or browser binaries,
99+
- it crawls same-origin links from the product target URL,
100+
- it records page URLs, titles, links, forms, buttons, inputs, selectors, and
101+
accessible names,
102+
- it records screenshots when the project Playwright driver can provide them,
103+
- it blocks potentially destructive forms and actions unless
104+
`--allow-destructive-actions` is passed,
105+
- it obeys `--max-pages` and `--max-actions`.
106+
107+
Flow maps are written under:
108+
109+
```text
110+
.codedecay/local/product-flow-maps/<target-id>/flow-map.json
111+
```
112+
113+
The JSON schema lives at
114+
[`schemas/product-flow-map.schema.json`](schemas/product-flow-map.schema.json).
115+
116+
Markdown and JSON product reports link to the flow-map artifact so agents and
117+
humans can reuse the discovered product surface as test-generation input.
118+
85119
## Failure Bundle Schema
86120

87121
Product verification failures are represented as versioned bundles on
@@ -124,12 +158,12 @@ of guessing from a dashboard screenshot.
124158

125159
## Current Limits
126160

127-
This release defines the target model, live health-check runner, and failure
128-
evidence contract. It does not yet generate UI/API tests automatically.
161+
This release defines the target model, live health-check runner, Playwright flow
162+
map explorer, and failure evidence contract. It does not yet generate UI/API
163+
tests automatically.
129164

130165
The next implementation pieces are:
131166

132-
- Playwright product explorer and flow map,
133167
- generated UI regression tests,
134168
- OpenAPI/API scenario generation,
135169
- MCP run-fix-rerun tools,
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2020-12/schema",
3+
"$id": "https://submuxhq.github.io/CodeDecay/schemas/product-flow-map.schema.json",
4+
"title": "CodeDecay Product Flow Map",
5+
"type": "object",
6+
"additionalProperties": false,
7+
"required": ["schemaVersion", "generatedAt", "target", "driver", "limits", "summary", "pages", "blockedActions"],
8+
"properties": {
9+
"schemaVersion": {
10+
"const": 1
11+
},
12+
"generatedAt": {
13+
"type": "string",
14+
"format": "date-time"
15+
},
16+
"target": {
17+
"type": "object",
18+
"additionalProperties": false,
19+
"required": ["id", "baseUrl", "origin"],
20+
"properties": {
21+
"id": { "type": "string", "minLength": 1 },
22+
"baseUrl": { "type": "string", "format": "uri" },
23+
"origin": { "type": "string", "format": "uri" }
24+
}
25+
},
26+
"driver": {
27+
"const": "playwright"
28+
},
29+
"limits": {
30+
"type": "object",
31+
"additionalProperties": false,
32+
"required": ["sameOrigin", "maxPages", "maxActions", "allowDestructiveActions"],
33+
"properties": {
34+
"sameOrigin": { "const": true },
35+
"maxPages": { "type": "integer", "minimum": 1 },
36+
"maxActions": { "type": "integer", "minimum": 1 },
37+
"allowDestructiveActions": { "type": "boolean" }
38+
}
39+
},
40+
"summary": {
41+
"type": "object",
42+
"additionalProperties": false,
43+
"required": ["pages", "interactiveElements", "blockedActions", "skippedActions"],
44+
"properties": {
45+
"pages": { "type": "integer", "minimum": 0 },
46+
"interactiveElements": { "type": "integer", "minimum": 0 },
47+
"blockedActions": { "type": "integer", "minimum": 0 },
48+
"skippedActions": { "type": "integer", "minimum": 0 }
49+
}
50+
},
51+
"pages": {
52+
"type": "array",
53+
"items": {
54+
"type": "object",
55+
"additionalProperties": false,
56+
"required": ["url", "title", "path", "depth", "links", "interactiveElements"],
57+
"properties": {
58+
"url": { "type": "string", "format": "uri" },
59+
"title": { "type": "string" },
60+
"path": { "type": "string" },
61+
"depth": { "type": "integer", "minimum": 0 },
62+
"screenshotPath": { "type": "string" },
63+
"links": {
64+
"type": "array",
65+
"items": { "$ref": "#/$defs/link" }
66+
},
67+
"interactiveElements": {
68+
"type": "array",
69+
"items": { "$ref": "#/$defs/interactiveElement" }
70+
}
71+
}
72+
}
73+
},
74+
"blockedActions": {
75+
"type": "array",
76+
"items": { "$ref": "#/$defs/blockedAction" }
77+
}
78+
},
79+
"$defs": {
80+
"link": {
81+
"type": "object",
82+
"additionalProperties": false,
83+
"required": ["href", "text", "selector", "sameOrigin", "discovered"],
84+
"properties": {
85+
"href": { "type": "string", "format": "uri" },
86+
"text": { "type": "string" },
87+
"selector": { "type": "string" },
88+
"sameOrigin": { "type": "boolean" },
89+
"discovered": { "type": "boolean" }
90+
}
91+
},
92+
"interactiveElement": {
93+
"type": "object",
94+
"additionalProperties": false,
95+
"required": ["kind", "selector", "name", "destructive", "blocked"],
96+
"properties": {
97+
"kind": {
98+
"enum": ["link", "form", "button", "input"]
99+
},
100+
"selector": { "type": "string" },
101+
"name": { "type": "string" },
102+
"action": { "type": "string" },
103+
"method": { "type": "string" },
104+
"inputType": { "type": "string" },
105+
"destructive": { "type": "boolean" },
106+
"blocked": { "type": "boolean" },
107+
"blockReason": { "type": "string" }
108+
}
109+
},
110+
"blockedAction": {
111+
"type": "object",
112+
"additionalProperties": false,
113+
"required": ["pageUrl", "selector", "name", "reason"],
114+
"properties": {
115+
"pageUrl": { "type": "string", "format": "uri" },
116+
"selector": { "type": "string" },
117+
"name": { "type": "string" },
118+
"reason": { "type": "string" }
119+
}
120+
}
121+
}
122+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
{
2+
"$schema": "https://json-schema.org/draft/2020-12/schema",
3+
"$id": "https://submuxhq.github.io/CodeDecay/schemas/product-flow-map.schema.json",
4+
"title": "CodeDecay Product Flow Map",
5+
"type": "object",
6+
"additionalProperties": false,
7+
"required": ["schemaVersion", "generatedAt", "target", "driver", "limits", "summary", "pages", "blockedActions"],
8+
"properties": {
9+
"schemaVersion": {
10+
"const": 1
11+
},
12+
"generatedAt": {
13+
"type": "string",
14+
"format": "date-time"
15+
},
16+
"target": {
17+
"type": "object",
18+
"additionalProperties": false,
19+
"required": ["id", "baseUrl", "origin"],
20+
"properties": {
21+
"id": { "type": "string", "minLength": 1 },
22+
"baseUrl": { "type": "string", "format": "uri" },
23+
"origin": { "type": "string", "format": "uri" }
24+
}
25+
},
26+
"driver": {
27+
"const": "playwright"
28+
},
29+
"limits": {
30+
"type": "object",
31+
"additionalProperties": false,
32+
"required": ["sameOrigin", "maxPages", "maxActions", "allowDestructiveActions"],
33+
"properties": {
34+
"sameOrigin": { "const": true },
35+
"maxPages": { "type": "integer", "minimum": 1 },
36+
"maxActions": { "type": "integer", "minimum": 1 },
37+
"allowDestructiveActions": { "type": "boolean" }
38+
}
39+
},
40+
"summary": {
41+
"type": "object",
42+
"additionalProperties": false,
43+
"required": ["pages", "interactiveElements", "blockedActions", "skippedActions"],
44+
"properties": {
45+
"pages": { "type": "integer", "minimum": 0 },
46+
"interactiveElements": { "type": "integer", "minimum": 0 },
47+
"blockedActions": { "type": "integer", "minimum": 0 },
48+
"skippedActions": { "type": "integer", "minimum": 0 }
49+
}
50+
},
51+
"pages": {
52+
"type": "array",
53+
"items": {
54+
"type": "object",
55+
"additionalProperties": false,
56+
"required": ["url", "title", "path", "depth", "links", "interactiveElements"],
57+
"properties": {
58+
"url": { "type": "string", "format": "uri" },
59+
"title": { "type": "string" },
60+
"path": { "type": "string" },
61+
"depth": { "type": "integer", "minimum": 0 },
62+
"screenshotPath": { "type": "string" },
63+
"links": {
64+
"type": "array",
65+
"items": { "$ref": "#/$defs/link" }
66+
},
67+
"interactiveElements": {
68+
"type": "array",
69+
"items": { "$ref": "#/$defs/interactiveElement" }
70+
}
71+
}
72+
}
73+
},
74+
"blockedActions": {
75+
"type": "array",
76+
"items": { "$ref": "#/$defs/blockedAction" }
77+
}
78+
},
79+
"$defs": {
80+
"link": {
81+
"type": "object",
82+
"additionalProperties": false,
83+
"required": ["href", "text", "selector", "sameOrigin", "discovered"],
84+
"properties": {
85+
"href": { "type": "string", "format": "uri" },
86+
"text": { "type": "string" },
87+
"selector": { "type": "string" },
88+
"sameOrigin": { "type": "boolean" },
89+
"discovered": { "type": "boolean" }
90+
}
91+
},
92+
"interactiveElement": {
93+
"type": "object",
94+
"additionalProperties": false,
95+
"required": ["kind", "selector", "name", "destructive", "blocked"],
96+
"properties": {
97+
"kind": {
98+
"enum": ["link", "form", "button", "input"]
99+
},
100+
"selector": { "type": "string" },
101+
"name": { "type": "string" },
102+
"action": { "type": "string" },
103+
"method": { "type": "string" },
104+
"inputType": { "type": "string" },
105+
"destructive": { "type": "boolean" },
106+
"blocked": { "type": "boolean" },
107+
"blockReason": { "type": "string" }
108+
}
109+
},
110+
"blockedAction": {
111+
"type": "object",
112+
"additionalProperties": false,
113+
"required": ["pageUrl", "selector", "name", "reason"],
114+
"properties": {
115+
"pageUrl": { "type": "string", "format": "uri" },
116+
"selector": { "type": "string" },
117+
"name": { "type": "string" },
118+
"reason": { "type": "string" }
119+
}
120+
}
121+
}
122+
}

0 commit comments

Comments
 (0)