Skip to content

Commit 08b1c92

Browse files
committed
fix: keep serve polling after late artifact path
1 parent b05019e commit 08b1c92

7 files changed

Lines changed: 134 additions & 15 deletions

File tree

CHANGELOG.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,27 @@
11
# Changelog
22

3+
## v0.2.3 - 2026-05-01
4+
5+
Technical-preview patch release focused on Kubernetes handoff reliability for the `v0.2.x` line.
6+
7+
Included in this release:
8+
9+
- fixed `serve` / `watch` startup when the configured artifact file appears after the service starts
10+
- kept polling active when filesystem watcher setup cannot attach to a not-yet-created artifact directory and `watch_polling` is enabled
11+
- added regression coverage for late Bering artifact handoff in service mode
12+
13+
Stable within the `v0.2.3` preview:
14+
15+
- the same strict `1.0.0` and `1.1.0` Bering contract acceptance introduced in `v0.2.0`
16+
- deterministic batch analysis, baseline comparison, and CI gate behavior from the `v0.2.0` line
17+
- service mode can recover when a downstream chart starts Sheaft before the first Bering artifact exists
18+
19+
Still experimental in `v0.2.3`:
20+
21+
- long-running `serve` / `watch` service mode remains technical-preview surface
22+
- local `discover` helper
23+
- broader operator-facing packaging and operational conventions around image/chart deployment
24+
325
## v0.2.2 - 2026-05-01
426

527
Technical-preview patch release focused on post-audit hardening for the `v0.2.x` line.

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
[![ci-template-smoke](https://img.shields.io/github/actions/workflow/status/MB3R-Lab/Sheaft/ci-template-smoke.yml?branch=main&label=ci-template-smoke)](https://github.com/MB3R-Lab/Sheaft/actions/workflows/ci-template-smoke.yml)
66
[![schema-contract](https://img.shields.io/github/actions/workflow/status/MB3R-Lab/Sheaft/schema-contract.yml?branch=main&label=schema-contract)](https://github.com/MB3R-Lab/Sheaft/actions/workflows/schema-contract.yml)
77
[![Go version](https://img.shields.io/github/go-mod/go-version/MB3R-Lab/Sheaft)](https://github.com/MB3R-Lab/Sheaft/blob/main/go.mod)
8-
[![Technical preview](https://img.shields.io/badge/preview-v0.2.2-orange)](https://github.com/MB3R-Lab/Sheaft/releases/tag/v0.2.2)
8+
[![Technical preview](https://img.shields.io/badge/preview-v0.2.3-orange)](https://github.com/MB3R-Lab/Sheaft/releases/tag/v0.2.3)
99
[![Bering support](https://img.shields.io/badge/Bering-1.0%20%7C%201.1-blue)](https://github.com/MB3R-Lab/Sheaft/blob/main/docs/compatibility-matrix.md)
1010

1111
Sheaft is a downstream resilience posture engine and CI/CD gate for model artifacts produced by Bering or another compatible upstream producer.
@@ -21,9 +21,9 @@ It stays downstream of topology discovery. The public surface in this repository
2121

2222
## Stability / Release Status
2323

24-
The current public release is `v0.2.2`. The `v0.2.x` line is an experimental public release and should be treated as a technical preview, not a stable GA release.
24+
The current public release is `v0.2.3`. The `v0.2.x` line is an experimental public release and should be treated as a technical preview, not a stable GA release.
2525

26-
Stable within the `v0.2.2` technical preview:
26+
Stable within the `v0.2.3` technical preview:
2727

2828
- strict acceptance of the baseline Bering contract line: `io.mb3r.bering.model@1.0.0` and `io.mb3r.bering.snapshot@1.0.0`
2929
- strict acceptance of the advanced Bering contract line: `io.mb3r.bering.model@1.1.0` and `io.mb3r.bering.snapshot@1.1.0`

docs/install.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# Install
22

3-
The current public technical preview is `v0.2.2`. Prefer release assets for evaluation and automation; use `go install` or `go build` as fallback paths.
3+
The current public technical preview is `v0.2.3`. Prefer release assets for evaluation and automation; use `go install` or `go build` as fallback paths.
44

55
The machine-readable entrypoint for release consumers is the `release-manifest.json` asset attached to each GitHub Release. It records exact archive names, checksums, image references, chart version, and the default config pack asset for that release.
66

docs/roadmap.md

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,12 @@ This file captures the repository-side audit refreshed on 2026-05-01: current Gi
1818

1919
## Release Tracking State
2020

21-
- Latest public release shipped: [Sheaft v0.2.2 technical preview](https://github.com/MB3R-Lab/Sheaft/releases/tag/v0.2.2)
22-
- Historical shipped milestones: `v0.1.0 technical preview`, `v0.1.1 technical preview`, `v0.2.0 technical preview`, `v0.2.1 technical preview`, `v0.2.2 technical preview`
23-
- Active backlog milestone: `Post-v0.2.2 technical preview`
21+
- Latest public release shipped: [Sheaft v0.2.3 technical preview](https://github.com/MB3R-Lab/Sheaft/releases/tag/v0.2.3)
22+
- Historical shipped milestones: `v0.1.0 technical preview`, `v0.1.1 technical preview`, `v0.2.0 technical preview`, `v0.2.1 technical preview`, `v0.2.2 technical preview`, `v0.2.3 technical preview`
23+
- Active backlog milestone: `Post-v0.2.3 technical preview`
2424
- Previous release-tracking issue: [#81](https://github.com/MB3R-Lab/Sheaft/issues/81)
25-
- Current release-tracking issue: TBD after the `v0.2.2` release is published.
26-
- GitHub issue and milestone sync was refreshed after the `v0.2.2` patch release so the tracker now reflects the shipped release state.
25+
- Current release-tracking issue: TBD after the `v0.2.3` release is published.
26+
- GitHub issue and milestone sync was refreshed after the `v0.2.3` patch release so the tracker now reflects the shipped release state.
2727

2828
## Audit Summary
2929

@@ -182,6 +182,6 @@ Goal: make the Bering -> Sheaft workflow repeatable beyond a technical preview.
182182

183183
## Current Execution Note
184184

185-
- Repository-side audit refreshed on 2026-05-01 for the `v0.2.2` technical-preview patch release and the product-capability backlog review.
186-
- GitHub issue [#71](https://github.com/MB3R-Lab/Sheaft/issues/71) should stay aligned with this file: latest release `v0.2.2`, active milestone `Post-v0.2.2 technical preview`, and the same trust-first priority order.
185+
- Repository-side audit refreshed on 2026-05-01 for the `v0.2.3` technical-preview patch release and the product-capability backlog review.
186+
- GitHub issue [#71](https://github.com/MB3R-Lab/Sheaft/issues/71) should stay aligned with this file: latest release `v0.2.3`, active milestone `Post-v0.2.3 technical preview`, and the same trust-first priority order.
187187
- The next highest-priority repo task is **R4.3: expand applicability boundaries into concrete do-not-trust signals and detector heuristics**.

internal/service/service.go

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -198,16 +198,22 @@ func (s *Service) watchLoop(ctx context.Context) {
198198
if s.watchFS() {
199199
w, err := fsnotify.NewWatcher()
200200
if err == nil {
201+
watchReady := true
201202
for _, path := range s.locator.WatchPaths() {
202203
if err := w.Add(path); err != nil {
203204
_ = w.Close()
204205
s.setError(fmt.Errorf("watch artifact path %q: %w", path, err))
205-
return
206+
watchReady = false
207+
break
206208
}
207209
}
208-
watcher = w
209-
events = w.Events
210-
watchErrors = w.Errors
210+
if watchReady {
211+
watcher = w
212+
events = w.Events
213+
watchErrors = w.Errors
214+
} else if !s.watchPolling() {
215+
return
216+
}
211217
} else {
212218
s.setError(fmt.Errorf("create filesystem watcher: %w", err))
213219
if !s.watchPolling() {

internal/service/service_test.go

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"context"
55
"net/http"
66
"net/http/httptest"
7+
"os"
78
"path/filepath"
89
"strings"
910
"testing"
@@ -220,6 +221,66 @@ func TestWatchLoop_ReportsFilesystemWatchAddError(t *testing.T) {
220221
}
221222
}
222223

224+
func TestWatchLoop_FallsBackToPollingWhenWatchPathAppearsLate(t *testing.T) {
225+
t.Parallel()
226+
227+
dir := t.TempDir()
228+
artifactPath := filepath.Join(dir, "missing", "latest.json")
229+
watchFS := true
230+
watchPolling := true
231+
serveCfg := config.ServeConfig{
232+
SchemaVersion: config.ServeSchemaVersion,
233+
Artifact: config.ArtifactSource{
234+
Path: artifactPath,
235+
Mode: "file",
236+
},
237+
PollInterval: "50ms",
238+
WatchFS: &watchFS,
239+
WatchPolling: &watchPolling,
240+
History: config.HistoryConfig{
241+
MaxItems: 1,
242+
},
243+
}.Normalized()
244+
svc := newWithDeps(
245+
serveCfg,
246+
config.Policy{
247+
Mode: config.ModeWarn,
248+
DefaultAction: config.ModeWarn,
249+
GlobalThreshold: 0.9,
250+
FailureProbability: 0.1,
251+
Trials: 100,
252+
}.ToAnalysisConfig().Normalized(),
253+
realClock{},
254+
newLocator(serveCfg.Artifact),
255+
nil,
256+
analyzer.AnalyzeLoaded,
257+
prometheus.NewRegistry(),
258+
)
259+
260+
ctx, cancel := context.WithCancel(context.Background())
261+
defer cancel()
262+
go svc.watchLoop(ctx)
263+
264+
if err := os.MkdirAll(filepath.Dir(artifactPath), 0o755); err != nil {
265+
t.Fatalf("create artifact dir: %v", err)
266+
}
267+
if err := model.WriteToFile(artifactPath, testModel(2, "topology-late")); err != nil {
268+
t.Fatalf("write late model: %v", err)
269+
}
270+
271+
deadline := time.Now().Add(3 * time.Second)
272+
for time.Now().Before(deadline) {
273+
current := svc.CurrentReport()
274+
if current != nil && current.InputArtifact != nil && current.InputArtifact.TopologyVersion == "topology-late" {
275+
return
276+
}
277+
time.Sleep(50 * time.Millisecond)
278+
}
279+
280+
status := svc.CurrentStatus()
281+
t.Fatalf("expected current report after late artifact appears, status=%+v", status)
282+
}
283+
223284
func TestHTTPServerAppliesTimeouts(t *testing.T) {
224285
t.Parallel()
225286

release/v0.2.3.md

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
## Sheaft v0.2.3
2+
3+
`v0.2.3` is a technical-preview patch release for the `v0.2.x` line.
4+
5+
### What changed
6+
7+
- `serve` / `watch` now keeps polling active when filesystem watcher setup cannot attach to a not-yet-created artifact directory and `watch_polling` is enabled
8+
- service mode now handles the common Kubernetes handoff order where Sheaft starts before Bering writes the first `latest.json`
9+
- regression coverage now verifies that a late artifact file produces a current report instead of leaving `/current-report` at 503
10+
11+
### Stable within 0.2.3
12+
13+
- strict support for Bering `io.mb3r.bering.model@1.0.0` / `@1.1.0`
14+
- strict support for Bering `io.mb3r.bering.snapshot@1.0.0` / `@1.1.0`
15+
- deterministic batch analysis, baseline comparison, and CI gate behavior from the `v0.2.0` line
16+
17+
### Experimental within 0.2.3
18+
19+
- long-running `serve` / `watch` service mode remains technical-preview surface
20+
- local `discover` helper remains experimental
21+
- image/chart operational conventions are hardened but not promoted to a stable GA contract
22+
23+
### Compatibility
24+
25+
Sheaft `v0.2.3` remains compatible with these upstream contracts:
26+
27+
- `io.mb3r.bering.model@1.0.0`
28+
- `io.mb3r.bering.snapshot@1.0.0`
29+
- `io.mb3r.bering.model@1.1.0`
30+
- `io.mb3r.bering.snapshot@1.1.0`

0 commit comments

Comments
 (0)