feat(02-02): capture, scrub, and strictly diff stateful flows

Resolve named placeholders from a private variable store, mask
dates and ids after shape checks, and keep comparing independent steps.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Jakub Zych
2026-09-17 12:32:49 +02:00
parent bb6a5a91c9
commit aa165fe3d0
13 changed files with 1390 additions and 203 deletions

View File

@@ -33,6 +33,8 @@ func parityRecordCommand() bonfire.Command {
{Name: "spec", Description: "YAML request spec path"},
{Name: "target", Description: "Base URL of the HTTP backend"},
{Name: "output", Description: "Destination fixture path"},
{Name: "rules", Description: "Committed YAML capture rules"},
{Name: "vars", Description: "Private mode-0600 variable store outside fixtures"},
},
Run: runParityRecord,
}
@@ -45,6 +47,7 @@ func parityReplayCommand() bonfire.Command {
Flags: []bonfire.Flag{
{Name: "fixtures", Description: "Recorded YAML fixture path"},
{Name: "target", Description: "Base URL of the HTTP backend"},
{Name: "vars", Description: "Private mode-0600 variable store"},
},
Run: runParityReplay,
}
@@ -99,7 +102,22 @@ func runParityRecord(ctx context.Context, in bonfire.Input, out bonfire.Output)
if err != nil {
return err
}
flow, err := tide.RecordFlow(ctx, spec, tide.RecordConfig{Target: target})
cfg := tide.RecordConfig{Target: target}
if rulesPath, ok := in.Flag("rules"); ok && strings.TrimSpace(rulesPath) != "" {
rules, err := tide.LoadRules(rulesPath)
if err != nil {
return err
}
cfg.Rules = rules
}
if varsPath, ok := in.Flag("vars"); ok && strings.TrimSpace(varsPath) != "" {
store, err := tide.OpenStore(varsPath)
if err != nil {
return err
}
cfg.Store = store
}
flow, err := tide.RecordFlow(ctx, spec, cfg)
if err != nil {
return err
}
@@ -123,7 +141,15 @@ func runParityReplay(ctx context.Context, in bonfire.Input, out bonfire.Output)
if err != nil {
return err
}
result, err := tide.ReplayFlow(ctx, flow, tide.ReplayConfig{Target: target})
cfg := tide.ReplayConfig{Target: target}
if varsPath, ok := in.Flag("vars"); ok && strings.TrimSpace(varsPath) != "" {
store, err := tide.OpenStore(varsPath)
if err != nil {
return err
}
cfg.Store = store
}
result, err := tide.ReplayFlow(ctx, flow, cfg)
if err != nil {
return err
}