Skip to content

fix: dashboard and CLI agree on what is deployed - #46

Merged
Robobc merged 1 commit into
mainfrom
fix/dashboard-truth
Aug 25, 2026
Merged

fix: dashboard and CLI agree on what is deployed#46
Robobc merged 1 commit into
mainfrom
fix/dashboard-truth

Conversation

@Robobc

@Robobc Robobc commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

What

Usability-review finding 9: the dashboard contradicted reality three ways.

Arithmetic that could go negative. monitor.py classified by substring, so ROLLBACK_COMPLETE counted as both deployed ("COMPLETE") and failed ("ROLLBACK"), and not_deployed was computed by subtraction. Five healthy stacks plus one rolled back reported deployed=6, failed=1, not_deployed=-1. The browser classified the same status differently, so the terminal and the UI disagreed with each other.

A fake denominator. The UI used Math.max(stackCount, 10), so a healthy greenfield deployment read 6/10 and looked permanently incomplete.

Three sets of module labels. monitor.py said 6a/S2, index.html said Module 1, deploy.sh's MODULE_MAP says E.

Fixes

  • Classification lives in monitor.classify() and only there. Each stack carries a state field and the browser renders it; the browser's own classifier remains as a fallback for older status.json files, with identical rules.
  • Scope comes from the deployment contract — expected_stacks() decides what this configuration promises, federation-aware. Anything else is not-applicable, shown dimmed as "Not In Scope", never counted as missing or failed. The denominator is the contract's count.
  • Summary counts are tallied from the emitted states rather than subtracted, so a negative count is now unrepresentable.
  • monitor.py owns the module/team labels and the browser prefers them.
  • Cleared monitor.py's pre-existing lint debt while touching it (CI lints whole changed files): sorted imports, no bare try/except/pass, no datetime.utcnow(), a logger instead of silent swallows.

Verified live

Against the running test environment:

total_stacks: 6   deployed: 6   in_progress: 0   failed: 0
not_deployed: 0   not_applicable: 4   (in-scope states sum to 6/6, no negatives)

That same deployment previously displayed 6/10 with four stacks looking absent.

Tests

tests/test_dashboard.py pins all three defects: rollback is failed and only failed, states are mutually exclusive, the denominator floor is gone, the UI prefers the emitted state, and module labels are checked against MODULE_MAP parsed out of deploy.sh — a real drift guard that fails if either side is renamed.

149 tests green, ruff clean.

Usability-review finding 9. Three ways the dashboard contradicted reality:

1. monitor.py classified by substring, so ROLLBACK_COMPLETE counted as BOTH
   deployed ("COMPLETE") and failed ("ROLLBACK"), and not_deployed was
   computed by subtraction — five healthy stacks plus one rolled back
   reported deployed=6, failed=1, not_deployed=-1. The browser classified the
   same status differently, so terminal and UI disagreed.
2. The UI's denominator was Math.max(stackCount, 10), so a healthy greenfield
   deployment read 6/10 and looked permanently incomplete.
3. Module labels differed three ways: monitor.py said "6a/S2", index.html said
   "Module 1", deploy.sh's MODULE_MAP says E.

Fixes:

- Classification lives in monitor.classify() and ONLY there. Each stack
  carries a `state`; the browser renders that field. Its local classifier
  stays as a fallback for older status.json files, with identical rules.
- Scope comes from the deployment contract: expected_stacks() decides which
  stacks this configuration promises (federation-aware — the account picks the
  role). Anything else is state "not-applicable", rendered dimmed as "Not In
  Scope" and never counted as missing or failed. The denominator is the
  contract's count.
- Summary counts come from the emitted states, never by subtraction, so a
  negative count is now unrepresentable.
- monitor.py carries the module/team labels (matching MODULE_MAP) and the
  browser prefers them; STACK_META in the HTML is only a fallback.
- Cleared monitor.py's pre-existing lint debt while touching it (CI lints
  whole changed files): sorted imports, no bare try/except/pass, no
  datetime.utcnow(), logger instead of silent swallows.

tests/test_dashboard.py pins all three: rollback is failed and only failed,
states are mutually exclusive, the fake denominator floor is gone, the UI
prefers the emitted state, and the module labels are checked against
MODULE_MAP parsed out of deploy.sh (a real drift guard — it fails if either
side is renamed).

Verified live against the test rig: the deployed footprint now reports
total_stacks=6, deployed=6, failed=0, not_deployed=0, not_applicable=4, with
the in-scope states summing exactly to the total and no negative counts. That
same deployment previously displayed 6/10 with four stacks looking absent.
149 tests green, ruff clean.
@github-actions

Copy link
Copy Markdown

Commit: 1e55f84 | Updated: 2026-08-25 15:13:31 UTC

Security Scan Results

Scanner S C H M L I Time Action Result Thresh
bandit 0 0 0 0 16 0 853ms 0 PASSED MED (g)
cdk-nag 0 0 0 0 0 0 8.2s 0 PASSED MED (g)
cfn-nag 0 0 0 0 0 0 7ms 0 PASSED MED (g)
checkov 0 0 0 0 0 0 4.9s 0 PASSED MED (g)
detect-secrets 0 0 0 0 0 0 318ms 0 PASSED MED (g)
grype 0 0 0 0 0 0 1m 2s 0 PASSED MED (g)
npm-audit 0 0 0 0 0 0 187ms 0 PASSED MED (g)
opengrep 0 0 0 0 0 0 18.2s 0 PASSED MED (g)
semgrep 0 0 0 0 0 0 <1ms 0 MISSING MED (g)
syft 0 0 0 0 0 0 2.6s 0 PASSED MED (g)

@Robobc
Robobc merged commit db8035b into main Aug 25, 2026
10 of 11 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