Skip to content

feat(cli): add early diagnostics to Construct [RED-641]#1355

Open
sorccu wants to merge 3 commits into
mainfrom
simo/red-641-construct-early-diagnostics
Open

feat(cli): add early diagnostics to Construct [RED-641]#1355
sorccu wants to merge 3 commits into
mainfrom
simo/red-641-construct-early-diagnostics

Conversation

@sorccu

@sorccu sorccu commented Jun 19, 2026

Copy link
Copy Markdown
Member

Why

Constructs validate via async validate(diagnostics), but they're built through a synchronous constructor that can't do async work and, by convention, never throws for user-facing validation issues. Anything a constructor notices at construction time (e.g. an argument of the wrong type) currently has to either throw a hard error or be stashed in a single-purpose instance field solely so validate() can re-inspect it later.

This adds a first-class way for a constructor to record diagnostics at construction time, then reimplements the invalid/duplicate logicalId validation on top of it.

What

Commit 1 — infrastructure. Adds a readonly earlyDiagnostics = new Diagnostics() collector on the base Construct. A constructor records issues into it; base validate() merges them into the caller's Diagnostics via diagnostics.extend(this.earlyDiagnostics). Because every subclass validate() starts with await super.validate(diagnostics), and Project.validate() already wraps each member in ConstructDiagnostics, early diagnostics flow through the existing per-construct collection and reporting path with no changes to subclasses or commands.

Commit 2 — example. Turns two construction-time hard errors into soft validation diagnostics using the new mechanism (no holder fields):

  • Non-string logicalId is recorded as a diagnostic in the Construct constructor and the value is coerced to a string; the matching throw in Session.validateCreateConstruct is removed.
  • Duplicate logicalId is recorded on the Project in addResource instead of throwing. The duplicate resource is intentionally not stored, so the diagnostic is attached to the project to ensure it surfaces during validation, and is wrapped in a ConstructDiagnostic so the reported title names the offending construct (e.g. [CheckGroup:my-check-group]).
  • Removes the now-unused ValidationError class.

These now surface as fatal validation diagnostics (non-zero exit) under checkly validate / checkly deploy / checkly test rather than aborting with an unhandled exception.

Tests

  • New early-diagnostics.spec.ts covers the plain path, the ConstructDiagnostics attribution-prefix path, and the empty case.
  • project.spec.ts gains non-string and duplicate logicalId cases; the duplicate case asserts the construct-attributed title prefix.
  • The throw-based unit tests (alert-channel, check-group v1+v2, private-location, status-page-service) are converted to assert diagnostics.
  • The duplicate-logicalId e2e test now asserts the soft diagnostic on stdout with exit 1.

tsc --build clean; full constructs unit suite green.

Resolves RED-641.

🤖 Generated with Claude Code

Constructs validate via async validate(), but are built through a sync
constructor that cannot do async work and by convention does not throw
for user-facing validation. Add an earlyDiagnostics collector on the
base Construct that a constructor can record issues into; base validate()
merges it into the caller's Diagnostics, so the issues flow through the
existing per-construct collection and reporting path with no changes to
subclasses or commands.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QBbrKMRZw7DjdqjSgnQX41
…-641]

Turn two construction-time hard errors into soft validation diagnostics
via the new earlyDiagnostics mechanism, so they're collected and reported
alongside other validation issues instead of aborting with an exception:

- A non-string logicalId is recorded as a diagnostic in the Construct
  constructor and the value is coerced to a string; the matching throw in
  Session.validateCreateConstruct is removed.
- A duplicate logicalId is recorded as a diagnostic on the Project in
  addResource instead of throwing. The duplicate resource is intentionally
  not stored, so the diagnostic is attributed to the project to ensure it
  surfaces during validation.

Removes the now-unused ValidationError class.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QBbrKMRZw7DjdqjSgnQX41
@sorccu sorccu force-pushed the simo/red-641-construct-early-diagnostics branch from 0f4089e to 35a7fa6 Compare June 19, 2026 00:54
Temporary: trial Blacksmith Windows runners to work around the GitHub
windows-latest fleet slowdown that pushes the e2e suite past its timeouts.
Intended to be dropped before merge.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QBbrKMRZw7DjdqjSgnQX41
@sorccu sorccu force-pushed the simo/red-641-construct-early-diagnostics branch from 625ed6d to 3478e40 Compare June 19, 2026 08:06
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