Skip to content

feat: read project stack state live from CloudFormation - #2112

Merged
notgitika merged 3 commits into
refactorfrom
feat/project-stack-cfn-reader
Aug 27, 2026
Merged

feat: read project stack state live from CloudFormation#2112
notgitika merged 3 commits into
refactorfrom
feat/project-stack-cfn-reader

Conversation

@notgitika

@notgitika notgitika commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Adds the read-side of the deploy-state refactor: a small helper to describe a project's CloudFormation stack, so resource details can be read live instead of from a local snapshot that goes stale.

What's here

  • describeStack(region, credentials, stackName) — a DescribeStacks call that returns the stack, or undefined when it doesn't exist. Accepts a stack name or ARN. The AWS call is injectable (and lazy-loaded like environment.ts), so it's unit-tested without a real client.
  • Generalized the existing bootstrap isBootstrapStackNotFoundisStackNotFound and reused it.

Not here (by design)

Interpreting the stack — status classification (deployed / in-progress / failed) and which outputs to surface — is a project status decision and is left to whoever builds it, rather than baked in ahead of the consumer.

Adds a reader that describes a project's CloudFormation stack and
classifies its lifecycle into not-deployed / in-progress / failed / ready,
returning the stack outputs (resource ARNs/IDs) only when settled and
successful. This is the source-of-truth side of the deploy-state refactor:
resource details come from CloudFormation on demand rather than a local
snapshot that can go stale.

Generalizes the existing bootstrap not-found helper to isStackNotFound and
reuses it. No command is wired to this yet; project status consumes it in
a follow-up.
@github-actions github-actions Bot added the size/m PR size: M label Aug 26, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added agentcore-harness-reviewing AgentCore Harness review in progress claude-security-reviewing Claude Code /security-review in progress labels Aug 26, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Aug 26, 2026

@agentcore-devx-automation agentcore-devx-automation Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

AgentCore Harness Review

Verdict: Looks good

Small, well-scoped reader with tight tests. A few observations, none blocking:

  • classifyStack includes IMPORT_ROLLBACK_COMPLETE in READY_STATUSES alongside IMPORT_COMPLETE. That's arguably closer to ROLLBACK_COMPLETE (a failed operation with no meaningful post-state) than to UPDATE_ROLLBACK_COMPLETE, and the comment above the set only justifies UPDATE_ROLLBACK_COMPLETE. Worth double-checking against how you plan to render this in project status, but it's easy to flip later and no caller consumes it yet.
  • The readStackState "propagates non-not-found errors" test only covers the injected-reader path; the real not-found → undefined conversion lives inside describeStack and isn't exercised. Not a correctness issue (it just reuses the already-tested isStackNotFound), just noting for coverage.
  • Telemetry isn't wired here, which is fine given the PR body says the caller is a follow-up — please make sure project status (or whichever command consumes this) instruments the read.

Nothing here needs to change before merging.

@agentcore-devx-automation agentcore-devx-automation Bot removed the agentcore-harness-reviewing AgentCore Harness review in progress label Aug 26, 2026
@codecov-commenter

codecov-commenter commented Aug 26, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 66.66667% with 12 lines in your changes missing coverage. Please review.
✅ Project coverage is 97.30%. Comparing base (097e1f0) to head (c3f9896).
⚠️ Report is 5 commits behind head on refactor.

Files with missing lines Patch % Lines
src/core/project/backends/cdk/stackReader.ts 64.70% 12 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##           refactor    #2112      +/-   ##
============================================
- Coverage     97.38%   97.30%   -0.08%     
============================================
  Files           440      447       +7     
  Lines         26626    27067     +441     
============================================
+ Hits          25929    26338     +409     
- Misses          697      729      +32     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@Hweinstock Hweinstock left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I wonder if we're coming at this problem from the wrong side. I understand the end goal is to have a status handlers that reads off the resources and whether they are deployed or not, but it feels strange to build the functionality before the interface.

I feel like we're inevitably going to be reworking the functionality to match the interface or derive the interface from the functionality (which I don't think we want).

const describeStack: StackReader = async (region, credentials, stackName) => {
const { CloudFormationClient, DescribeStacksCommand } =
await import("@aws-sdk/client-cloudformation");
const client = new CloudFormationClient({ credentials, region });

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Is there a way we can inject this client from core clients?

Maybe define cloudformation client like the others here

export class CoreClient implements AwsClients {
private controlClients = new Map<string, BedrockAgentCoreControlClient>();
private dataClients = new Map<string, BedrockAgentCoreClient>();
private iamClients = new Map<string, IAMClient>();
private logsClients = new Map<string, CloudWatchLogsClient>();
private readonly createControlClient: CreateControlClient;
private readonly createDataClient: CreateDataClient;
private readonly createIamClient: CreateIamClient;
private readonly createLogsClient: CreateLogsClient;
private logger: Logger;

and inject it into the project manager?

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.

I made the call injectable so it's testable without a real client. Left it lazy-importing the SDK for now rather than going through CoreClient. figured that's cleaner to wire up when status actually consumes it. Lmk if you'd rather do it now.

};
}

describe("classifyStack", () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I feel like it would be stronger to test this through the handler (once it exists), but since it doesn't exist yet this seems reasonable

@notgitika

Copy link
Copy Markdown
Contributor Author

I wonder if we're coming at this problem from the wrong side. I understand the end goal is to have a status handlers that reads off the resources and whether they are deployed or not, but it feels strange to build the functionality before the interface.
I feel like we're inevitably going to be reworking the functionality to match the interface or derive the interface from the functionality (which I don't think we want).

I see your point and I agree. I thiknk the classification is really a status-interface decision so it can be worked on together with it. I can pull it back in this PR and only have the raw API (describeStackByName → DescribeStacks, returning the stack or undefined) implemented.

Per review, drop the stack-status classification (not-deployed / in-progress
/ failed / ready) and the StackState shape — that's a project status
interface decision and belongs with whoever builds it, not baked in ahead of
the consumer. Keep just describeStack: a DescribeStacks call that returns the
stack or undefined when it doesn't exist.

The CloudFormation call is injectable at the function seam (lazy-loaded like
environment.ts), so it's unit-tested without a real client; wiring it through
CoreClient/the project manager is left to the consumer.
@github-actions github-actions Bot added size/m PR size: M and removed size/m PR size: M labels Aug 27, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label Aug 27, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Aug 27, 2026
@notgitika

Copy link
Copy Markdown
Contributor Author

Trimmed it down to just the raw describeStack call, dropped the status classification since that's really a status decision, not this PR's.

@github-actions github-actions Bot added size/m PR size: M and removed size/m PR size: M labels Aug 27, 2026
@notgitika

Copy link
Copy Markdown
Contributor Author

patch coverage being low is expected for now. the tests deliberately bypass it by injecting a fake describe

try {
// Not-found is a thrown ValidationError, not an empty result; every other
// error (auth, throttling, malformed request) is real and propagates.
return (await describe(stackName))?.[0];

@aidandaly24 aidandaly24 Aug 27, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

nit: An empty successful DescribeStacks response is different from a missing stack. CloudFormation reports not-found through the thrown ValidationError. An empty successful response is malformed. Returning undefined here makes callers report “not deployed” instead of the actual service-response problem. This should throw MalformedServiceResponseError, like the bootstrap reader does.
Could be a followup or other PR though.

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.

good point, fixed it in this PR itself

aidandaly24
aidandaly24 previously approved these changes Aug 27, 2026
A missing stack is reported by a thrown ValidationError, so that stays the
only not-found (undefined) signal. A successful response with no stack is
malformed, not not-found; return undefined there would misreport a service
problem as 'not deployed'. Throw MalformedServiceResponseError instead,
matching the bootstrap reader.
@github-actions github-actions Bot added size/m PR size: M and removed size/m PR size: M labels Aug 27, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label Aug 27, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Aug 27, 2026
@notgitika
notgitika merged commit 794ddbf into refactor Aug 27, 2026
22 checks passed
@notgitika
notgitika deleted the feat/project-stack-cfn-reader branch August 27, 2026 16:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/m PR size: M

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants