Skip to content

Publish rolled-up status.summary verdict on StackResource - #63

Merged
ashishmax31 merged 4 commits into
mainfrom
summary-state
Aug 4, 2026
Merged

Publish rolled-up status.summary verdict on StackResource#63
ashishmax31 merged 4 commits into
mainfrom
summary-state

Conversation

@ashishmax31

Copy link
Copy Markdown
Contributor

What

The agent publishes its conclusion, not just its evidence: status.summary (state: Waiting|Building|Deploying|Ready|Failed + reason + message + observedGeneration), written by the status derivation every pass. The hub will map it 1:1 to release timeline events instead of re-deriving state from condition order/values/absence + portCheck + failure details (its current second-interpreter ladder).

How

  • summarize() in status_derive.go — single writer, runs after the full sub-reconciler chain, stamped immediately before StatusHash() so the hash covers it (hub change-detection is free).
  • Precedence contract: Failed verdict > Building > Waiting > runtime crash > Ready/Deploying. Build/deps outrank the crash detail because when those gates fail the workload reconciler never ran this pass — a crash entry is a previous revision's leftover.
  • Deploying picks the most specific diagnosis: port dial > kubelet readiness detail > verdict message, with a "(previous revision still serving traffic)" note while the old revision holds traffic.
  • Exported ReasonBuildFailed / ReasonPortNotListening so the hub never mirrors literals; converted all literal sites.
  • Bug found along the way: BuildLastFailureDetail never set Type on crash entries — readers were compensating with empty-Type normalization. Writer now stamps runtime_crash; normalization removed.

Rollout note

CRD schema is structural (pruning): clusters must get the release crds.yaml applied before the chart upgrade, or status.summary is silently dropped. Helm skips crds/ on upgrade.

Tests

  • 14 new tests: full precedence matrix incl. the two contract pins (build-outranks-stale-crash, stale-generation conditions ignored), deploying-detail fallback order, serving suffix, StatusHash sensitivity to summary changes.
  • make test-unit green, go vet clean, CRD copies verified identical.
  • Integration suite not run (no test cluster in this environment).

https://claude.ai/code/session_01TpRLszVTJudSd86RreufSC

The agent now publishes its conclusion, not just its evidence: a summary
block (state Waiting|Building|Deploying|Ready|Failed + reason + message +
observedGeneration) written by the status derivation every pass, so the hub
maps it 1:1 to release timeline events instead of re-deriving state from
condition order, values, absence, PortCheck and failure details.

- summarize() in status derivation, single writer, stamped before StatusHash
  so the hash covers it; precedence: Failed verdict > Building > Waiting >
  runtime crash > Ready/Deploying
- Deploying detail picks port dial > readiness detail > verdict message,
  with a "previous revision still serving traffic" note while the old
  revision holds traffic
- Exported ReasonBuildFailed / ReasonPortNotListening; literals replaced
- BuildLastFailureDetail now stamps Type=runtime_crash (was unset; readers
  no longer normalize empty Type)
- CRDs regenerated in config/deploy/crds and chart crds (identical)

Claude-Session: https://claude.ai/code/session_01TpRLszVTJudSd86RreufSC
if pc := resource.Status.PortCheck; pc != nil && pc.Status == v1alpha1.PortCheckStatusTypeFailure {
return v1alpha1.ReasonPortNotListening, portDialMessage(pc.FailingPortNumbers)
}
if resource.Status.PortCheck == nil {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

resource.Status.PortCheck cant be nil when failureDetail is populated due to port check

…deployingDetail

A readiness_failure entry has one writer, recordPortFailure, which only runs
past the port-check grace window and files the PortNotListening Failed
verdict in the same pass — so the Deploying path can never see one. Inside
the grace window the details are cleared and only PortCheck carries the
diagnosis, which the port-dial branch above already handles.

Claude-Session: https://claude.ai/code/session_01TpRLszVTJudSd86RreufSC
// portDialMessage names the declared ports the last dial proved closed.
func portDialMessage(ports []int32) string {
if len(ports) == 0 {
return "declared ports not accepting connections"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If no failing part numbers then we are good right?

Failure is only written when at least one dialed port is closed, so a
Failure PortCheck always carries FailingPortNumbers. The guard was a
hub-transplant defensive leftover.

Claude-Session: https://claude.ai/code/session_01TpRLszVTJudSd86RreufSC
Precedence now reads as a single switch — one case per state, first case
wins — with the inputs named up front. The serving-traffic note moves to
appendServingNote, applied only to Deploying.

Claude-Session: https://claude.ai/code/session_01TpRLszVTJudSd86RreufSC
@ashishmax31
ashishmax31 merged commit 8e653d5 into main Aug 4, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant