compound-workflow 0.1.1
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/.claude-plugin/marketplace.json +11 -0
- package/.claude-plugin/plugin.json +12 -0
- package/.cursor-plugin/plugin.json +12 -0
- package/README.md +155 -0
- package/package.json +22 -0
- package/scripts/install-cli.mjs +313 -0
- package/scripts/sync-into-repo.sh +103 -0
- package/src/.agents/agents/research/best-practices-researcher.md +132 -0
- package/src/.agents/agents/research/framework-docs-researcher.md +134 -0
- package/src/.agents/agents/research/git-history-analyzer.md +62 -0
- package/src/.agents/agents/research/learnings-researcher.md +288 -0
- package/src/.agents/agents/research/repo-research-analyst.md +146 -0
- package/src/.agents/agents/review/agent-native-reviewer.md +299 -0
- package/src/.agents/agents/workflow/bug-reproduction-validator.md +87 -0
- package/src/.agents/agents/workflow/lint.md +20 -0
- package/src/.agents/agents/workflow/spec-flow-analyzer.md +149 -0
- package/src/.agents/commands/assess.md +60 -0
- package/src/.agents/commands/install.md +53 -0
- package/src/.agents/commands/metrics.md +59 -0
- package/src/.agents/commands/setup.md +9 -0
- package/src/.agents/commands/sync.md +9 -0
- package/src/.agents/commands/test-browser.md +393 -0
- package/src/.agents/commands/workflow/brainstorm.md +252 -0
- package/src/.agents/commands/workflow/compound.md +142 -0
- package/src/.agents/commands/workflow/plan.md +737 -0
- package/src/.agents/commands/workflow/review-v2.md +148 -0
- package/src/.agents/commands/workflow/review.md +110 -0
- package/src/.agents/commands/workflow/triage.md +54 -0
- package/src/.agents/commands/workflow/work.md +439 -0
- package/src/.agents/references/README.md +12 -0
- package/src/.agents/references/standards/README.md +9 -0
- package/src/.agents/scripts/self-check.mjs +227 -0
- package/src/.agents/scripts/sync-opencode.mjs +355 -0
- package/src/.agents/skills/agent-browser/SKILL.md +223 -0
- package/src/.agents/skills/audit-traceability/SKILL.md +260 -0
- package/src/.agents/skills/brainstorming/SKILL.md +250 -0
- package/src/.agents/skills/compound-docs/SKILL.md +533 -0
- package/src/.agents/skills/compound-docs/assets/critical-pattern-template.md +34 -0
- package/src/.agents/skills/compound-docs/assets/resolution-template.md +97 -0
- package/src/.agents/skills/compound-docs/references/yaml-schema.md +87 -0
- package/src/.agents/skills/compound-docs/schema.project.yaml +18 -0
- package/src/.agents/skills/compound-docs/schema.yaml +119 -0
- package/src/.agents/skills/data-foundations/SKILL.md +185 -0
- package/src/.agents/skills/document-review/SKILL.md +108 -0
- package/src/.agents/skills/file-todos/SKILL.md +177 -0
- package/src/.agents/skills/file-todos/assets/todo-template.md +106 -0
- package/src/.agents/skills/financial-workflow-integrity/SKILL.md +423 -0
- package/src/.agents/skills/git-worktree/SKILL.md +268 -0
- package/src/.agents/skills/pii-protection-prisma/SKILL.md +629 -0
- package/src/.agents/skills/process-metrics/SKILL.md +46 -0
- package/src/.agents/skills/process-metrics/assets/daily-template.md +37 -0
- package/src/.agents/skills/process-metrics/assets/monthly-template.md +21 -0
- package/src/.agents/skills/process-metrics/assets/weekly-template.md +25 -0
- package/src/.agents/skills/technical-review/SKILL.md +83 -0
- package/src/AGENTS.md +213 -0
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
---
|
|
2
|
+
status: pending
|
|
3
|
+
priority: p2
|
|
4
|
+
issue_id: "XXX"
|
|
5
|
+
tags: [] # e.g. spike, discussion, backend, testing
|
|
6
|
+
dependencies: []
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Brief Task Title
|
|
10
|
+
|
|
11
|
+
Replace with a concise title describing what needs to be done.
|
|
12
|
+
|
|
13
|
+
## Problem Statement
|
|
14
|
+
|
|
15
|
+
What is broken, missing, or needs improvement? Provide clear context about why this matters.
|
|
16
|
+
|
|
17
|
+
## Findings
|
|
18
|
+
|
|
19
|
+
Investigation results, root cause analysis, and key discoveries.
|
|
20
|
+
|
|
21
|
+
- Finding 1 (include file references when possible)
|
|
22
|
+
- Finding 2
|
|
23
|
+
|
|
24
|
+
## Proposed Solutions
|
|
25
|
+
|
|
26
|
+
Present multiple options with pros, cons, effort estimates, and risk assessment.
|
|
27
|
+
|
|
28
|
+
### Option 1: [Solution Name]
|
|
29
|
+
|
|
30
|
+
Approach: Describe the solution clearly.
|
|
31
|
+
|
|
32
|
+
Pros:
|
|
33
|
+
- Benefit 1
|
|
34
|
+
- Benefit 2
|
|
35
|
+
|
|
36
|
+
Cons:
|
|
37
|
+
- Drawback 1
|
|
38
|
+
- Drawback 2
|
|
39
|
+
|
|
40
|
+
Effort: 2-3 hours
|
|
41
|
+
|
|
42
|
+
Risk: Low / Medium / High
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
### Option 2: [Solution Name]
|
|
47
|
+
|
|
48
|
+
Approach: Describe the solution clearly.
|
|
49
|
+
|
|
50
|
+
Pros:
|
|
51
|
+
- Benefit 1
|
|
52
|
+
|
|
53
|
+
Cons:
|
|
54
|
+
- Drawback 1
|
|
55
|
+
|
|
56
|
+
Effort: 4-6 hours
|
|
57
|
+
|
|
58
|
+
Risk: Low / Medium / High
|
|
59
|
+
|
|
60
|
+
## Recommended Action
|
|
61
|
+
|
|
62
|
+
To be filled during triage. Clear, actionable plan for resolving this todo.
|
|
63
|
+
|
|
64
|
+
## Technical Details
|
|
65
|
+
|
|
66
|
+
Affected files, related components, data changes, or architectural considerations.
|
|
67
|
+
|
|
68
|
+
## Resources
|
|
69
|
+
|
|
70
|
+
Links to logs, tests, PRs, documentation, similar issues.
|
|
71
|
+
|
|
72
|
+
- Plan: <path-to-plan-file>
|
|
73
|
+
- Plan checkbox: <exact checkbox text, if applicable>
|
|
74
|
+
- Plan section: <heading reference, if no checkbox>
|
|
75
|
+
|
|
76
|
+
## Acceptance Criteria
|
|
77
|
+
|
|
78
|
+
Testable checklist items for verifying completion.
|
|
79
|
+
|
|
80
|
+
- [ ] Acceptance criteria item 1
|
|
81
|
+
- [ ] Acceptance criteria item 2
|
|
82
|
+
|
|
83
|
+
## Work Log
|
|
84
|
+
|
|
85
|
+
Chronological record of work sessions, actions taken, and learnings.
|
|
86
|
+
|
|
87
|
+
### YYYY-MM-DD - Session Title
|
|
88
|
+
|
|
89
|
+
By: <name>
|
|
90
|
+
|
|
91
|
+
Actions:
|
|
92
|
+
- Changes made (include file references)
|
|
93
|
+
- Commands executed
|
|
94
|
+
- Tests run
|
|
95
|
+
|
|
96
|
+
Learnings:
|
|
97
|
+
- What worked / what didn't
|
|
98
|
+
- Key insights
|
|
99
|
+
|
|
100
|
+
---
|
|
101
|
+
|
|
102
|
+
(Add more entries as work progresses)
|
|
103
|
+
|
|
104
|
+
## Notes
|
|
105
|
+
|
|
106
|
+
Additional context, decisions, or reminders.
|
|
@@ -0,0 +1,423 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: financial-workflow-integrity
|
|
3
|
+
description: Enforce correct financial workflows using durable state, idempotency, immutability, and concurrency guards (Prisma + Postgres).
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Financial Workflow Integrity Standard (Prisma + Postgres)
|
|
7
|
+
|
|
8
|
+
## Purpose
|
|
9
|
+
|
|
10
|
+
Provide a practical standard for building workflows that have financial outcomes or regulatory sensitivity. This skill focuses on correctness under:
|
|
11
|
+
|
|
12
|
+
- retries and duplicates
|
|
13
|
+
- concurrent executions
|
|
14
|
+
- long-running orchestration (queues / workflow engines)
|
|
15
|
+
- human approvals and external callbacks
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## Non-Negotiable Rules (MUST / MUST NOT)
|
|
20
|
+
|
|
21
|
+
### State transitions
|
|
22
|
+
|
|
23
|
+
- MUST represent workflow progress with explicit states/statuses.
|
|
24
|
+
- MUST guard state transitions using optimistic concurrency or a lock with TTL.
|
|
25
|
+
- MUST treat the DB as the source of truth for business state.
|
|
26
|
+
- MUST NOT rely on workflow engine "runs once" as correctness.
|
|
27
|
+
- MUST NOT run side effects unless you won the transition.
|
|
28
|
+
|
|
29
|
+
### Idempotency
|
|
30
|
+
|
|
31
|
+
- MUST apply request-level idempotency to all externally triggered money-adjacent actions (submit, approve, payout, charge).
|
|
32
|
+
- MUST apply step-level idempotency to any side effect (email, webhook, CRM record, ledger entry).
|
|
33
|
+
- MUST enforce idempotency with DB unique constraints (not just checks in code).
|
|
34
|
+
- MUST scope idempotency to actor + operation + entity to prevent cross-user/cross-operation collisions.
|
|
35
|
+
|
|
36
|
+
### Immutability
|
|
37
|
+
|
|
38
|
+
- MUST snapshot submissions and financial decisions.
|
|
39
|
+
- MUST NOT mutate the canonical data that a decision was made on.
|
|
40
|
+
- MUST model edits as a new submission/version.
|
|
41
|
+
|
|
42
|
+
### Failure behavior
|
|
43
|
+
|
|
44
|
+
- MUST fail closed for financial actions when uncertain.
|
|
45
|
+
- MUST produce a deterministic outcome for retries (replay stored response where appropriate).
|
|
46
|
+
|
|
47
|
+
### Money representation
|
|
48
|
+
|
|
49
|
+
- MUST represent money as integer minor units + currency (e.g. cents + ISO currency code).
|
|
50
|
+
- MUST NOT use floating point for monetary values.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
54
|
+
## Core Data Model (Baseline)
|
|
55
|
+
|
|
56
|
+
### applications (or equivalent)
|
|
57
|
+
|
|
58
|
+
Required columns:
|
|
59
|
+
|
|
60
|
+
- `status` (enum)
|
|
61
|
+
- `current_step`
|
|
62
|
+
- `version` (int)
|
|
63
|
+
- `created_at`, `updated_at`
|
|
64
|
+
|
|
65
|
+
### Immutable submissions
|
|
66
|
+
|
|
67
|
+
Store what was submitted/approved against:
|
|
68
|
+
|
|
69
|
+
```sql
|
|
70
|
+
CREATE TABLE application_submissions (
|
|
71
|
+
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
72
|
+
application_id uuid NOT NULL REFERENCES applications(id) ON DELETE CASCADE,
|
|
73
|
+
submission_no int NOT NULL,
|
|
74
|
+
submitted_data jsonb NOT NULL,
|
|
75
|
+
submitted_at timestamptz NOT NULL DEFAULT now(),
|
|
76
|
+
UNIQUE (application_id, submission_no)
|
|
77
|
+
);
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Idempotency keys (request-level)
|
|
81
|
+
|
|
82
|
+
Rules:
|
|
83
|
+
|
|
84
|
+
- `request_hash` MUST be computed from a canonical, stable representation.
|
|
85
|
+
- Hash inputs MUST include: operation, entity id, actor/principal id, and any money fields (amount_minor, currency).
|
|
86
|
+
- If a key is re-used with a different hash, reject as misuse.
|
|
87
|
+
|
|
88
|
+
Recommended schema:
|
|
89
|
+
|
|
90
|
+
```sql
|
|
91
|
+
CREATE TYPE idempotency_status AS ENUM ('STARTED','SUCCEEDED','FAILED');
|
|
92
|
+
|
|
93
|
+
CREATE TABLE idempotency_keys (
|
|
94
|
+
scope text NOT NULL, -- e.g. "application.submit" / "payout.create" / "webhook.stripe"
|
|
95
|
+
key text NOT NULL, -- client/provider idempotency key
|
|
96
|
+
operation text NOT NULL, -- explicit operation name
|
|
97
|
+
entity_id uuid NULL, -- entity being acted on (nullable for provider webhooks)
|
|
98
|
+
principal_id text NULL, -- user/admin/service principal identifier
|
|
99
|
+
|
|
100
|
+
request_hash text NOT NULL,
|
|
101
|
+
status idempotency_status NOT NULL DEFAULT 'STARTED',
|
|
102
|
+
response jsonb NULL, -- safe response only; MUST NOT contain secrets/PII
|
|
103
|
+
|
|
104
|
+
expires_at timestamptz NOT NULL, -- lease/ttl for STARTED recovery
|
|
105
|
+
locked_at timestamptz NULL,
|
|
106
|
+
locked_by text NULL,
|
|
107
|
+
|
|
108
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
109
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
110
|
+
PRIMARY KEY (scope, key)
|
|
111
|
+
);
|
|
112
|
+
|
|
113
|
+
CREATE INDEX idempotency_keys_status_idx ON idempotency_keys(status);
|
|
114
|
+
CREATE INDEX idempotency_keys_expires_at_idx ON idempotency_keys(expires_at);
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### Step runs (side-effect idempotency)
|
|
118
|
+
|
|
119
|
+
Each step that causes an external effect MUST create a durable row first.
|
|
120
|
+
|
|
121
|
+
```sql
|
|
122
|
+
CREATE TYPE step_run_status AS ENUM ('STARTED','SUCCEEDED','FAILED');
|
|
123
|
+
|
|
124
|
+
CREATE TABLE workflow_step_runs (
|
|
125
|
+
entity_id uuid NOT NULL,
|
|
126
|
+
step_key text NOT NULL,
|
|
127
|
+
|
|
128
|
+
status step_run_status NOT NULL DEFAULT 'STARTED',
|
|
129
|
+
metadata jsonb NOT NULL DEFAULT '{}'::jsonb,
|
|
130
|
+
attempt_count int NOT NULL DEFAULT 1,
|
|
131
|
+
last_error text NULL,
|
|
132
|
+
|
|
133
|
+
created_at timestamptz NOT NULL DEFAULT now(),
|
|
134
|
+
updated_at timestamptz NOT NULL DEFAULT now(),
|
|
135
|
+
|
|
136
|
+
PRIMARY KEY (entity_id, step_key)
|
|
137
|
+
);
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Provider event dedupe (webhooks)
|
|
141
|
+
|
|
142
|
+
Always dedupe by provider event id.
|
|
143
|
+
|
|
144
|
+
```sql
|
|
145
|
+
CREATE TABLE provider_events (
|
|
146
|
+
provider text NOT NULL,
|
|
147
|
+
event_id text NOT NULL,
|
|
148
|
+
received_at timestamptz NOT NULL DEFAULT now(),
|
|
149
|
+
payload jsonb NULL,
|
|
150
|
+
PRIMARY KEY (provider, event_id)
|
|
151
|
+
);
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## Optimistic Concurrency Standard
|
|
157
|
+
|
|
158
|
+
### Rule
|
|
159
|
+
|
|
160
|
+
Every transition MUST be:
|
|
161
|
+
|
|
162
|
+
- conditional on current status
|
|
163
|
+
- conditional on current version
|
|
164
|
+
- increments version
|
|
165
|
+
|
|
166
|
+
Example:
|
|
167
|
+
|
|
168
|
+
```sql
|
|
169
|
+
UPDATE applications
|
|
170
|
+
SET status = 'PROCESSING',
|
|
171
|
+
version = version + 1,
|
|
172
|
+
updated_at = now()
|
|
173
|
+
WHERE id = $1
|
|
174
|
+
AND status = 'SUBMITTED'
|
|
175
|
+
AND version = $2;
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
If 0 rows updated: treat as conflict.
|
|
179
|
+
|
|
180
|
+
- reload and no-op if already advanced
|
|
181
|
+
- never run side effects unless you won the transition
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Request-Level Idempotency Standard
|
|
186
|
+
|
|
187
|
+
### When required
|
|
188
|
+
|
|
189
|
+
Any operation initiated by:
|
|
190
|
+
|
|
191
|
+
- HTTP request from client
|
|
192
|
+
- webhook from provider
|
|
193
|
+
- job replays
|
|
194
|
+
- admin actions
|
|
195
|
+
|
|
196
|
+
...that could result in a financial or user-impacting outcome.
|
|
197
|
+
|
|
198
|
+
### Pattern
|
|
199
|
+
|
|
200
|
+
1. Compute `request_hash` from canonical payload.
|
|
201
|
+
2. Claim key by inserting `idempotency_keys` row (or transitioning an expired STARTED row under a lease).
|
|
202
|
+
3. If insert conflicts:
|
|
203
|
+
- load existing row
|
|
204
|
+
- if hash mismatch => reject (key reuse attempt)
|
|
205
|
+
- if SUCCEEDED => return stored response
|
|
206
|
+
- if STARTED (not expired) => return "in progress" / retry-after
|
|
207
|
+
- if FAILED => return deterministic failure (optionally allow explicit retry with a new key)
|
|
208
|
+
4. After claim succeeds:
|
|
209
|
+
- perform action
|
|
210
|
+
- set status SUCCEEDED and store safe response
|
|
211
|
+
|
|
212
|
+
Never perform the action before claiming the key.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## Step-Level Idempotency Standard
|
|
217
|
+
|
|
218
|
+
### When required
|
|
219
|
+
|
|
220
|
+
Any step that causes a side effect:
|
|
221
|
+
|
|
222
|
+
- send email
|
|
223
|
+
- create external record
|
|
224
|
+
- write ledger entry
|
|
225
|
+
- publish event
|
|
226
|
+
- call webhook
|
|
227
|
+
|
|
228
|
+
### Pattern
|
|
229
|
+
|
|
230
|
+
```sql
|
|
231
|
+
INSERT INTO workflow_step_runs (entity_id, step_key, metadata)
|
|
232
|
+
VALUES ($1, 'send_confirmation_email', '{}'::jsonb)
|
|
233
|
+
ON CONFLICT DO NOTHING;
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
Only proceed if insert succeeded.
|
|
237
|
+
|
|
238
|
+
Update the row to `SUCCEEDED` with provider ids in `metadata`.
|
|
239
|
+
|
|
240
|
+
---
|
|
241
|
+
|
|
242
|
+
## External Systems & Provider Idempotency
|
|
243
|
+
|
|
244
|
+
If your provider supports idempotency (e.g. payments):
|
|
245
|
+
|
|
246
|
+
- MUST pass an idempotency key to the provider
|
|
247
|
+
- MUST also enforce idempotency internally (do not fully trust providers)
|
|
248
|
+
|
|
249
|
+
Store provider identifiers (payment id, message id, etc.) in `workflow_step_runs.metadata` for reconciliation.
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## Human-in-the-loop Approvals
|
|
254
|
+
|
|
255
|
+
### Standard pattern
|
|
256
|
+
|
|
257
|
+
- `AWAITING_REVIEW` state represents a durable wait
|
|
258
|
+
- approval action itself is idempotent + concurrency guarded
|
|
259
|
+
- decision is immutable and audited
|
|
260
|
+
|
|
261
|
+
Model:
|
|
262
|
+
|
|
263
|
+
- `review_decisions` table with unique `(application_id, decision_no)`
|
|
264
|
+
- store approver id, timestamp, reason codes
|
|
265
|
+
- store the submission id/version being decided on
|
|
266
|
+
|
|
267
|
+
---
|
|
268
|
+
|
|
269
|
+
## External Callbacks / Webhooks
|
|
270
|
+
|
|
271
|
+
### Rules
|
|
272
|
+
|
|
273
|
+
- Always treat callbacks as duplicates.
|
|
274
|
+
- Validate signature, timestamp, and replay protection if available.
|
|
275
|
+
- Insert into `provider_events` first; if conflict, no-op.
|
|
276
|
+
- Apply request-level idempotency on callback processing scope if you execute multiple sub-actions.
|
|
277
|
+
- Update business state via version-guarded transitions.
|
|
278
|
+
|
|
279
|
+
---
|
|
280
|
+
|
|
281
|
+
## Failure Modes
|
|
282
|
+
|
|
283
|
+
### Workflow step partially succeeds
|
|
284
|
+
|
|
285
|
+
Example: email sent but DB update failed.
|
|
286
|
+
|
|
287
|
+
Mitigation:
|
|
288
|
+
|
|
289
|
+
- `workflow_step_runs` is the source of truth for "was it done?"
|
|
290
|
+
- store provider message id
|
|
291
|
+
- retries become no-op (or resume from durable state)
|
|
292
|
+
|
|
293
|
+
### Conflicting transitions
|
|
294
|
+
|
|
295
|
+
Mitigation:
|
|
296
|
+
|
|
297
|
+
- only the winner of version-guarded transition proceeds
|
|
298
|
+
- others reload and exit
|
|
299
|
+
|
|
300
|
+
### Stuck STARTED idempotency key
|
|
301
|
+
|
|
302
|
+
Mitigation:
|
|
303
|
+
|
|
304
|
+
- enforce `expires_at` and define a recovery path
|
|
305
|
+
- recovery MUST be lease-based (update-if-expired) to avoid multiple recoveries
|
|
306
|
+
- include manual override with audit trail
|
|
307
|
+
|
|
308
|
+
---
|
|
309
|
+
|
|
310
|
+
## Safe-by-default Outcome Policy
|
|
311
|
+
|
|
312
|
+
For money-adjacent actions:
|
|
313
|
+
|
|
314
|
+
- prefer returning "in progress" over executing twice
|
|
315
|
+
- prefer failing closed over guessing
|
|
316
|
+
- require manual intervention when state is ambiguous
|
|
317
|
+
|
|
318
|
+
---
|
|
319
|
+
|
|
320
|
+
## Prisma Models (Baseline)
|
|
321
|
+
|
|
322
|
+
```prisma
|
|
323
|
+
model Application {
|
|
324
|
+
id String @id @default(uuid()) @db.Uuid
|
|
325
|
+
status String
|
|
326
|
+
currentStep String
|
|
327
|
+
version Int @default(1)
|
|
328
|
+
|
|
329
|
+
createdAt DateTime @default(now())
|
|
330
|
+
updatedAt DateTime @updatedAt
|
|
331
|
+
|
|
332
|
+
submissions ApplicationSubmission[]
|
|
333
|
+
stepRuns WorkflowStepRun[]
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
model ApplicationSubmission {
|
|
337
|
+
id String @id @default(uuid()) @db.Uuid
|
|
338
|
+
applicationId String @db.Uuid
|
|
339
|
+
application Application @relation(fields: [applicationId], references: [id], onDelete: Cascade)
|
|
340
|
+
|
|
341
|
+
submissionNo Int
|
|
342
|
+
submittedData Json
|
|
343
|
+
submittedAt DateTime @default(now())
|
|
344
|
+
|
|
345
|
+
@@unique([applicationId, submissionNo])
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
enum IdempotencyStatus {
|
|
349
|
+
STARTED
|
|
350
|
+
SUCCEEDED
|
|
351
|
+
FAILED
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
model IdempotencyKey {
|
|
355
|
+
scope String
|
|
356
|
+
key String
|
|
357
|
+
operation String
|
|
358
|
+
entityId String? @db.Uuid
|
|
359
|
+
principalId String?
|
|
360
|
+
|
|
361
|
+
requestHash String
|
|
362
|
+
status IdempotencyStatus @default(STARTED)
|
|
363
|
+
response Json?
|
|
364
|
+
|
|
365
|
+
expiresAt DateTime
|
|
366
|
+
lockedAt DateTime?
|
|
367
|
+
lockedBy String?
|
|
368
|
+
|
|
369
|
+
createdAt DateTime @default(now())
|
|
370
|
+
updatedAt DateTime @updatedAt
|
|
371
|
+
|
|
372
|
+
@@id([scope, key])
|
|
373
|
+
@@index([status])
|
|
374
|
+
@@index([expiresAt])
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
enum StepRunStatus {
|
|
378
|
+
STARTED
|
|
379
|
+
SUCCEEDED
|
|
380
|
+
FAILED
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
model WorkflowStepRun {
|
|
384
|
+
entityId String @db.Uuid
|
|
385
|
+
stepKey String
|
|
386
|
+
|
|
387
|
+
status StepRunStatus @default(STARTED)
|
|
388
|
+
metadata Json @default("{}")
|
|
389
|
+
attemptCount Int @default(1)
|
|
390
|
+
lastError String?
|
|
391
|
+
|
|
392
|
+
createdAt DateTime @default(now())
|
|
393
|
+
updatedAt DateTime @updatedAt
|
|
394
|
+
|
|
395
|
+
@@id([entityId, stepKey])
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
model ProviderEvent {
|
|
399
|
+
provider String
|
|
400
|
+
eventId String
|
|
401
|
+
receivedAt DateTime @default(now())
|
|
402
|
+
payload Json?
|
|
403
|
+
|
|
404
|
+
@@id([provider, eventId])
|
|
405
|
+
}
|
|
406
|
+
```
|
|
407
|
+
|
|
408
|
+
---
|
|
409
|
+
|
|
410
|
+
## PR Review Checklist
|
|
411
|
+
|
|
412
|
+
- [ ] All state transitions are guarded by status + version.
|
|
413
|
+
- [ ] Any externally triggered financial action uses request-level idempotency.
|
|
414
|
+
- [ ] Idempotency scope includes actor/principal + operation + entity (prevents collisions).
|
|
415
|
+
- [ ] Every side effect has step-level idempotency with durable status/metadata.
|
|
416
|
+
- [ ] Submission/decision snapshots are immutable (edits create a new submission/version).
|
|
417
|
+
- [ ] Provider idempotency keys are used where supported.
|
|
418
|
+
- [ ] Webhooks dedupe by provider event id and are treated as duplicates.
|
|
419
|
+
- [ ] Audit events emitted for transitions and decisions (no sensitive payloads).
|
|
420
|
+
|
|
421
|
+
---
|
|
422
|
+
|
|
423
|
+
End of Skill.
|