Skip to content

Commit dfbcceb

Browse files
committed
✨ feat(readiness-check): add --refresh-examples flag to update sample outputs
- Move example report builders from test file to shared examples.go - Add --refresh-examples mode that writes fixtures without running checks - Validate --refresh-examples cannot combine with other readiness flags - Document refresh workflow in maintainer MCP integration guide
1 parent 8f9044f commit dfbcceb

5 files changed

Lines changed: 282 additions & 116 deletions

File tree

docs/go/maintainer/development-tracker.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# codex-mem Go Development Tracker
22

3-
Last updated: 2026-03-16
3+
Last updated: 2026-03-17
44
Status: active
55

66
## Purpose
@@ -491,6 +491,13 @@ Current blockers:
491491
- Blockers: none.
492492
- Next step: decide whether to stop here with fixed sample outputs, or extend the readiness helper with a dedicated example-generation/update workflow if fixture churn becomes noticeable.
493493

494+
### 2026-03-17 Session Update
495+
496+
- Completed: Added an explicit `go run ./scripts/readiness-check --refresh-examples` helper for checked-in readiness sample outputs. The fixed example-report builders now live in shared package code instead of only in tests, `TestReadinessExampleOutputsStayInSync` stays read-only, and the maintainer MCP integration guide now documents the helper command.
497+
- In progress: none.
498+
- Blockers: none.
499+
- Next step: decide whether the refresh helper should remain a mode on `readiness-check`, or move to a separate maintainer script if the checked-in example catalog grows further.
500+
494501
## Recommended Next Step
495502

496503
Recommended next implementation slice:

docs/go/maintainer/mcp-integration.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,14 @@ Checked-in example outputs for those readiness flows live under [../../../script
214214
- [example-slow-ci-success.txt](../../../scripts/readiness-check/testdata/example-slow-ci-success.txt)
215215
- [example-release-warning-failure.txt](../../../scripts/readiness-check/testdata/example-release-warning-failure.txt)
216216

217+
If a deliberate readiness-output change makes those fixtures drift, refresh them with:
218+
219+
```powershell
220+
go run ./scripts/readiness-check --refresh-examples
221+
```
222+
223+
Then rerun `go test ./scripts/readiness-check -run TestReadinessExampleOutputsStayInSync` plus the normal repo checks so the updated fixtures are verified in read-only mode again.
224+
217225
That combined check now covers:
218226

219227
1. `doctor --json`
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
package main
2+
3+
import (
4+
"bytes"
5+
"os"
6+
"path/filepath"
7+
"time"
8+
)
9+
10+
const (
11+
readinessExampleDirName = "testdata"
12+
readinessFollowSourceWatcherImport = "watcher_import"
13+
readinessSummaryAllPhasesPassed = "all readiness phases passed"
14+
readinessSummaryWarningPolicyFailed = "warning policy failed: WARN_FOLLOW_IMPORTS_HEALTH_STALE"
15+
)
16+
17+
type readinessExampleFixture struct {
18+
Name string
19+
RelativePath string
20+
JSON bool
21+
Report readinessReport
22+
}
23+
24+
func readinessExampleFixtures() []readinessExampleFixture {
25+
return []readinessExampleFixture{
26+
{
27+
Name: "slow-ci-text",
28+
RelativePath: "example-slow-ci-success.txt",
29+
JSON: false,
30+
Report: exampleSlowCISuccessReport(),
31+
},
32+
{
33+
Name: "ci-json",
34+
RelativePath: "example-ci-success.json",
35+
JSON: true,
36+
Report: exampleCISuccessReport(),
37+
},
38+
{
39+
Name: "release-warning-failure-text",
40+
RelativePath: "example-release-warning-failure.txt",
41+
JSON: false,
42+
Report: exampleReleaseWarningFailureReport(),
43+
},
44+
}
45+
}
46+
47+
func renderReadinessExample(report readinessReport, jsonOutput bool) ([]byte, error) {
48+
var buffer bytes.Buffer
49+
var err error
50+
if jsonOutput {
51+
err = writeReadinessJSON(&buffer, report)
52+
} else {
53+
err = writeReadinessSummary(&buffer, report)
54+
}
55+
if err != nil {
56+
return nil, err
57+
}
58+
return buffer.Bytes(), nil
59+
}
60+
61+
func writeReadinessExampleFixtures(baseDir string) ([]string, error) {
62+
fixtures := readinessExampleFixtures()
63+
writtenPaths := make([]string, 0, len(fixtures))
64+
for _, fixture := range fixtures {
65+
body, err := renderReadinessExample(fixture.Report, fixture.JSON)
66+
if err != nil {
67+
return nil, err
68+
}
69+
path := filepath.Join(baseDir, fixture.RelativePath)
70+
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {
71+
return nil, err
72+
}
73+
if err := os.WriteFile(path, body, 0o644); err != nil {
74+
return nil, err
75+
}
76+
writtenPaths = append(writtenPaths, path)
77+
}
78+
return writtenPaths, nil
79+
}
80+
81+
func exampleCISuccessReport() readinessReport {
82+
startedAt := time.Date(2026, time.March, 17, 9, 0, 0, 0, time.UTC)
83+
completedAt := startedAt.Add(3 * time.Second)
84+
doctorStartedAt := startedAt
85+
doctorCompletedAt := doctorStartedAt.Add(400 * time.Millisecond)
86+
stdioCompletedAt := doctorCompletedAt.Add(600 * time.Millisecond)
87+
httpCompletedAt := stdioCompletedAt.Add(800 * time.Millisecond)
88+
89+
report := readinessReport{
90+
Status: readinessStatusOK,
91+
Summary: readinessSummaryAllPhasesPassed,
92+
KeepGoing: false,
93+
PolicyProfile: readinessPolicyProfileCI,
94+
SlowRunThresholdMS: 8000,
95+
SlowPhaseThresholdMS: 1000,
96+
StartedAt: &startedAt,
97+
CompletedAt: &completedAt,
98+
DurationMS: 3000,
99+
Doctor: exampleHealthyDoctorReport(),
100+
Stdio: readinessSmokeTest{
101+
Status: readinessStatusOK,
102+
Summary: "mcp smoke test passed",
103+
},
104+
HTTP: readinessSmokeTest{
105+
Status: readinessStatusOK,
106+
Summary: "http mcp smoke test passed",
107+
},
108+
Phases: []readinessPhaseResult{
109+
{Name: readinessPhaseDoctor, Status: readinessStatusOK, Summary: "doctor --json passed", StartedAt: &doctorStartedAt, CompletedAt: &doctorCompletedAt, DurationMS: 400},
110+
{Name: readinessPhaseStdio, Status: readinessStatusOK, Summary: "mcp smoke test passed", StartedAt: &doctorCompletedAt, CompletedAt: &stdioCompletedAt, DurationMS: 600},
111+
{Name: readinessPhaseHTTP, Status: readinessStatusOK, Summary: "http mcp smoke test passed", StartedAt: &stdioCompletedAt, CompletedAt: &httpCompletedAt, DurationMS: 800},
112+
},
113+
}
114+
refreshWarningState(&report)
115+
return report
116+
}
117+
118+
func exampleSlowCISuccessReport() readinessReport {
119+
report := exampleCISuccessReport()
120+
report.PolicyProfile = readinessPolicyProfileSlowCI
121+
report.SlowRunThresholdMS = 20000
122+
report.SlowPhaseThresholdMS = 4000
123+
refreshWarningState(&report)
124+
return report
125+
}
126+
127+
func exampleReleaseWarningFailureReport() readinessReport {
128+
report := exampleCISuccessReport()
129+
report.Status = readinessStatusFailed
130+
report.PolicyProfile = readinessPolicyProfileRelease
131+
report.FailOnWarningCodes = []string{readinessWarnFollowHealthStale}
132+
report.Doctor = exampleHealthyDoctorReport()
133+
report.Doctor.Follow.HealthStale = true
134+
report.Doctor.Follow.Warnings = []doctorWarning{{Code: readinessWarnFollowHealthStale}}
135+
refreshWarningState(&report)
136+
report.Summary = readinessSummaryWarningPolicyFailed
137+
return report
138+
}
139+
140+
func exampleHealthyDoctorReport() *doctorReport {
141+
updatedAt := time.Date(2026, time.March, 17, 8, 59, 55, 0, time.UTC)
142+
doctor := &doctorReport{
143+
Status: "ok",
144+
}
145+
doctor.Runtime.ForeignKeys = true
146+
doctor.Runtime.RequiredSchemaOK = true
147+
doctor.Runtime.FTSReady = true
148+
doctor.Migrations.Pending = 0
149+
doctor.Audit.NoteProvenanceReady = true
150+
doctor.Audit.ExclusionAuditReady = true
151+
doctor.Audit.ImportAuditReady = true
152+
doctor.Follow.HealthPresent = true
153+
doctor.Follow.LastUpdatedAt = &updatedAt
154+
doctor.Follow.Status = "ok"
155+
doctor.Follow.Source = readinessFollowSourceWatcherImport
156+
doctor.Follow.InputCount = 1
157+
doctor.Follow.Continuous = true
158+
doctor.Follow.PollIntervalSeconds = 5
159+
doctor.Follow.SnapshotAgeSeconds = 5
160+
doctor.Follow.HealthStale = false
161+
doctor.Follow.RequestedWatchMode = "auto"
162+
doctor.Follow.ActiveWatchMode = "notify"
163+
doctor.MCP.Transport = readinessMCPTransportStdio
164+
doctor.MCP.ToolCount = 11
165+
return doctor
166+
}

scripts/readiness-check/main.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"io"
1010
"os"
1111
"os/exec"
12+
"path/filepath"
1213
"strconv"
1314
"strings"
1415
"time"
@@ -19,6 +20,7 @@ const stringNone = "none"
1920
type readinessOptions struct {
2021
JSON bool
2122
KeepGoing bool
23+
RefreshExamples bool
2224
PolicyProfile string
2325
SlowRunThresholdMS int64
2426
SlowPhaseThresholdMS int64
@@ -122,6 +124,9 @@ const (
122124
readinessStatusFailed = "failed"
123125
readinessStatusNotRun = "not_run"
124126

127+
readinessMCPTransportStdio = "stdio"
128+
readinessWarnFollowHealthStale = "WARN_FOLLOW_IMPORTS_HEALTH_STALE"
129+
125130
readinessPhaseDoctor = "doctor"
126131
readinessPhaseStdio = "stdio_mcp_smoke_test"
127132
readinessPhaseHTTP = "http_mcp_smoke_test"
@@ -144,6 +149,12 @@ func main() {
144149
if err != nil {
145150
failf("resolve working directory: %v", err)
146151
}
152+
if options.RefreshExamples {
153+
if err := refreshReadinessExamples(repoRoot, os.Stdout); err != nil {
154+
failf("refresh readiness examples: %v", err)
155+
}
156+
return
157+
}
147158

148159
report, runErr := runReadinessCheck(ctx, repoRoot, options)
149160
if err := writeReadinessOutput(os.Stdout, report, options); err != nil {
@@ -165,6 +176,8 @@ func parseOptions(args []string) (readinessOptions, error) {
165176
options.JSON = true
166177
case "--keep-going":
167178
options.KeepGoing = true
179+
case "--refresh-examples":
180+
options.RefreshExamples = true
168181
default:
169182
var err error
170183
options, i, err = parseExtendedOption(args, i, options)
@@ -177,9 +190,27 @@ func parseOptions(args []string) (readinessOptions, error) {
177190
if err != nil {
178191
return readinessOptions{}, err
179192
}
193+
if err := validateOptions(options); err != nil {
194+
return readinessOptions{}, err
195+
}
180196
return options, nil
181197
}
182198

199+
func validateOptions(options readinessOptions) error {
200+
if !options.RefreshExamples {
201+
return nil
202+
}
203+
if options.JSON ||
204+
options.KeepGoing ||
205+
options.PolicyProfile != "" ||
206+
options.SlowRunThresholdMS != 0 ||
207+
options.SlowPhaseThresholdMS != 0 ||
208+
len(options.FailOnWarningCodes) > 0 {
209+
return errors.New(`"--refresh-examples" cannot be combined with other readiness-check flags`)
210+
}
211+
return nil
212+
}
213+
183214
func parseExtendedOption(args []string, index int, options readinessOptions) (readinessOptions, int, error) {
184215
arg := strings.TrimSpace(args[index])
185216
switch {
@@ -382,6 +413,21 @@ func runReadinessCheck(ctx context.Context, repoRoot string, options readinessOp
382413
return runReadinessCheckWithRunner(ctx, repoRoot, options, runGo)
383414
}
384415

416+
func refreshReadinessExamples(repoRoot string, w io.Writer) error {
417+
baseDir := filepath.Join(repoRoot, "scripts", "readiness-check", readinessExampleDirName)
418+
writtenPaths, err := writeReadinessExampleFixtures(baseDir)
419+
if err != nil {
420+
return err
421+
}
422+
for _, path := range writtenPaths {
423+
if _, err := fmt.Fprintf(w, "refreshed_example=%s\n", path); err != nil {
424+
return err
425+
}
426+
}
427+
_, err = fmt.Fprintf(w, "refreshed_examples=%d\n", len(writtenPaths))
428+
return err
429+
}
430+
385431
func runReadinessCheckWithRunner(ctx context.Context, repoRoot string, options readinessOptions, runner goRunner) (readinessReport, error) {
386432
report := newReadinessReport(options)
387433
startedAt := time.Now().UTC()

0 commit comments

Comments
 (0)