From 2fd3de1324cf5dffc98ad951d1d708f7e24027c6 Mon Sep 17 00:00:00 2001 From: Will Tsai <28876888+willtsai@users.noreply.github.com> Date: Fri, 30 Jan 2026 10:57:35 -0800 Subject: [PATCH 01/15] modify constitution Signed-off-by: Will Tsai <28876888+willtsai@users.noreply.github.com> --- .specify/memory/constitution.md | 37 ++++++++++++++++++++++++++++++--- 1 file changed, 34 insertions(+), 3 deletions(-) diff --git a/.specify/memory/constitution.md b/.specify/memory/constitution.md index be23bbe5..e77d24f3 100644 --- a/.specify/memory/constitution.md +++ b/.specify/memory/constitution.md @@ -1,8 +1,11 @@ backend + backend --> cache +\``` +``` + +## 4. Commit the Graph + +The app graph is designed to be committed alongside your Bicep files: + +```bash +git add app.bicep .radius/app-graph.json +git commit -m "Add Redis cache to application" +``` + +## 5. Set Up GitHub Action for PR Diffs + +Add `.github/workflows/app-graph-diff.yml`: + +```yaml +name: App Graph Diff + +on: + pull_request: + paths: + - '**/.radius/app-graph.json' + +permissions: + pull-requests: write + +jobs: + diff: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: radius-project/app-graph-diff-action@v1 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} +``` + +When a PR changes the app graph, the Action posts a comment showing: +- Added/removed/modified resources +- New/removed connections +- Before/after Mermaid diagrams + +## 6. Validate Graph Freshness in CI + +Add validation to catch stale graphs: + +```yaml +name: Validate App Graph + +on: + pull_request: + paths: + - '**/*.bicep' + - '**/.radius/app-graph.json' + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + + - name: Install Radius CLI + run: | + curl -fsSL https://get.radapp.io/install.sh | bash + rad bicep download + + - name: Validate graph is current + run: | + rad app graph app.bicep --stdout > /tmp/expected.json + diff .radius/app-graph.json /tmp/expected.json || { + echo "::error::App graph is stale. Run 'rad app graph app.bicep' and commit." + exit 1 + } +``` + +## Common Options + +| Option | Description | +|--------|-------------| +| `--stdout` | Write JSON to stdout instead of file | +| `-o ` | Write to custom output path | +| `--format markdown` | Also generate Markdown preview | +| `--no-git` | Skip git metadata (faster) | +| `--parameters ` | Use parameter file for Bicep | +| `--at ` | Generate graph at specific commit | + +## Troubleshooting + +### "Bicep CLI not found" + +```bash +rad bicep download +``` + +### "Not a git repository" + +Git metadata is optional. The graph generates successfully, but `gitInfo` fields show "not available". + +### "Stale graph detected in CI" + +Regenerate and commit: + +```bash +rad app graph app.bicep +git add .radius/app-graph.json +git commit --amend --no-edit +git push --force-with-lease +``` + +## Next Steps + +- [View graph history](./history.md) - Track architecture evolution over time +- [Compare environments](./environments.md) - See how portable types resolve differently +- [Customize the GitHub Action](./github-action.md) - Advanced configuration options diff --git a/specs/001-git-app-graph-preview/research.md b/specs/001-git-app-graph-preview/research.md new file mode 100644 index 00000000..ff1cee64 --- /dev/null +++ b/specs/001-git-app-graph-preview/research.md @@ -0,0 +1,239 @@ +# Research: Git App Graph Preview + +**Feature Branch**: `001-git-app-graph-preview` +**Date**: February 4, 2026 + +## Research Tasks + +### 1. Radius CLI Architecture + +**Decision**: Extend existing `rad app graph` command in `pkg/cli/cmd/app/graph/` + +**Rationale**: +- The existing `rad app graph ` command already follows established CLI patterns +- Command structure uses Cobra framework with Runner pattern (`NewCommand()` + `Runner.Run()`) +- Output formatting is handled through `pkg/cli/output/` utilities +- Adding file-based input preserves conceptual consistency ("both are app graphs") + +**Alternatives Considered**: +- New `rad graph` top-level command: Rejected (breaks CLI hierarchy, less discoverable) +- New `rad bicep graph` command: Rejected (conceptually these are both "app graphs", just from different sources) + +### 2. Existing App Graph Data Structures + +**Decision**: Extend `ApplicationGraphResponse` and related types in `pkg/corerp/api/` + +**Rationale**: +- Existing types (`ApplicationGraphResource`, `ApplicationGraphConnection`) capture most needed fields +- Adding git metadata fields is additive and backward-compatible +- Deterministic output requires adding `sourceHash`, `sourceFile`, `sourceLine` metadata +- Types are already JSON-serializable via auto-generated marshalling code + +**Existing Structure** (from `zz_generated_models.go`): +```go +type ApplicationGraphResponse struct { + Resources []*ApplicationGraphResource +} + +type ApplicationGraphResource struct { + ID *string + Name *string + Type *string + ProvisioningState *string + Connections []*ApplicationGraphConnection + OutputResources []*ApplicationGraphOutputResource +} +``` + +**Additions Needed**: +- `GitInfo` struct: commit SHA, author, date, message +- `SourceFile`, `SourceLine` for Bicep source tracking +- `Metadata` struct: generatedAt, sourceFiles, sourceHash, radiusCliVersion + +### 3. Bicep Parsing Approach + +**Decision**: Use official Bicep CLI for compilation, then parse ARM JSON + +**Rationale** (per Constitution Principle VII - Simplicity Over Cleverness): +- Bicep CLI provides `bicep build --stdout` to compile to ARM JSON +- Radius already has Bicep CLI integration in `pkg/cli/bicep/` +- ARM JSON is stable and well-documented; parsing it avoids Bicep grammar complexity +- External modules are resolved by Bicep CLI, not our code + +**Alternatives Considered**: +- Custom Bicep parser: Rejected (complex, maintenance burden, grammar changes) +- ANTLR-based parser: Rejected (over-engineering for this use case) +- Use Bicep language server: Rejected (heavyweight, overkill for static analysis) + +**Implementation Pattern**: +```go +// Existing pattern in pkg/cli/bicep/types.go +func (impl *Impl) PrepareTemplate(filePath string) (map[string]any, error) { + args := []string{"build", "--stdout", filePath} + // Execute bicep CLI and parse JSON output +} +``` + +### 4. Graph Extraction from ARM JSON + +**Decision**: Extract resources and connections from compiled ARM JSON template + +**Rationale**: +- ARM JSON has stable schema with `resources` array +- Radius resources use `connections` and `routes` properties for relationships +- Existing graph logic in `pkg/corerp/frontend/controller/applications/graph_util.go` shows patterns + +**Key Extraction Points**: +1. `resources[].type` - Resource type identification +2. `resources[].name` - Resource name (may contain expressions) +3. `resources[].properties.connections` - Direct connections +4. `resources[].properties.routes` - Gateway routes +5. `resources[].dependsOn` - Explicit dependencies + +### 5. Git Integration + +**Decision**: Use `git log` and `git blame` via exec, not library + +**Rationale**: +- Git is universally available in development environments +- Shell commands are simpler than CGo bindings to libgit2 +- Radius already uses exec patterns for Bicep CLI +- Graceful degradation when not in git repo or shallow clone + +**Commands Needed**: +- `git blame -l -e ` - Get commit SHA per line +- `git log -1 --format='%H|%ae|%aI|%s' ` - Get commit metadata +- `git rev-parse --show-toplevel` - Detect git repo root + +### 6. GitHub Action Architecture + +**Decision**: Lightweight Action that reads committed JSON from git history; no graph generation + +**Rationale** (per spec Committed Artifact Model): +- Action only needs git and jq, not Bicep/Radius tooling +- Works in forks without special secrets +- Fast execution (JSON comparison vs. full compilation) +- Reproducible (graph captured at commit time) + +**Implementation**: +1. Checkout base and head commits +2. Read `.radius/app-graph.json` from each +3. Compute diff using JSON comparison +4. Render diff as Markdown with Mermaid diagrams +5. Post/update PR comment using `peter-evans/create-or-update-comment` + +**Alternatives Considered**: +- GitHub App: Rejected for MVP (more complex setup, centralized management) +- Generate graph in CI: Rejected (requires Bicep tooling, slower, less reproducible) + +### 7. Diff Computation Strategy + +**Decision**: JSON-based diffing with semantic resource comparison + +**Rationale**: +- JSON is deterministic when keys are sorted +- Resource ID provides stable identity across commits +- Diff should show added/removed/modified at resource level, not line level + +**Diff Algorithm**: +1. Parse base and head JSON +2. Create resource map keyed by ID +3. Compare: + - Added: ID in head but not base + - Removed: ID in base but not head + - Modified: ID in both but properties differ +4. For connections: compare by (sourceID, targetID) tuples + +### 8. Output Formats + +**Decision**: JSON canonical, Markdown additive with embedded Mermaid + +**Rationale** (per spec): +- JSON is machine-readable, deterministic, diffable +- Markdown renders in GitHub UI without additional tooling +- Mermaid diagrams supported natively by GitHub +- Separation allows different consumers (CI vs. humans) + +**JSON Schema**: +```json +{ + "metadata": { + "generatedAt": "2026-01-30T10:15:00Z", + "sourceFiles": ["app.bicep", "modules/database.bicep"], + "sourceHash": "sha256:abc123...", + "radiusCliVersion": "0.35.0" + }, + "resources": [...], + "connections": [...] +} +``` + +**Mermaid Shapes** (per spec): +- Containers: rectangles (`[name]`) +- Gateways: diamonds (`{name}`) +- Databases: cylinders (`[(name)]`) + +### 9. Platform Abstraction for Future Integrations + +**Decision**: Separate diff computation from rendering; platform-specific rendering layer + +**Rationale** (per user input: "design choices today won't limit us in the future"): +- Core diff logic (JSON comparison) is platform-agnostic +- GitHub-specific code isolated to: + - GitHub Action (workflow YAML) + - Markdown/Mermaid rendering + - PR comment posting +- Future GitLab integration would only need new rendering layer + +**Architecture**: +``` +┌─────────────────────────────────────────────────────────────────┐ +│ Platform-Agnostic │ +├─────────────────────────────────────────────────────────────────┤ +│ CLI (rad app graph) │ JSON Schema │ Diff Computation │ +│ Git Integration │ Data Model │ Core Rendering (MD) │ +└─────────────────────────────────────────────────────────────────┘ + │ + ┌─────────┴─────────┐ + ▼ ▼ + ┌───────────────┐ ┌───────────────┐ + │ GitHub Action │ │ GitLab CI │ + │ PR Comments │ │ MR Notes │ + │ Mermaid │ │ (Future) │ + └───────────────┘ └───────────────┘ +``` + +### 10. Radius Bicep Extension Compatibility + +**Decision**: Support Radius Bicep extension type definitions + +**Rationale**: +- Radius extends Bicep with custom types (`Applications.Core/containers`, etc.) +- These types must be recognized in ARM JSON output +- Type registry already exists in `pkg/corerp/api/` + +**Implementation**: +- Resource type detection checks for `Applications.*` prefix +- Portable types (`Radius.Data/store`) shown as-is in static graph +- Environment-resolved graph (P3) requires live Radius environment connection + +## Technology Decisions Summary + +| Area | Decision | Rationale | +|------|----------|-----------| +| CLI Framework | Cobra (existing) | Consistency with `rad` CLI | +| Bicep Parsing | Bicep CLI → ARM JSON | Simplicity, official support | +| Git Operations | Shell exec | Universal, no CGo | +| Data Structures | Extend existing types | Backward compatible | +| GitHub Integration | Action, not App | Simpler setup, fork-friendly | +| Diff Algorithm | JSON semantic diff | Deterministic, meaningful | +| Output | JSON + optional Markdown | Machine + human readable | +| Platform Abstraction | Rendering layer separation | Future GitLab support | + +## Open Questions Resolution + +1. **Bicep Compiler Integration**: ✅ Use official Bicep CLI (per Constitution Principle VII) +2. **Graph Storage**: ✅ Committed to `.radius/app-graph.json` (per spec) +3. **GitHub App vs Action**: ✅ GitHub Action for MVP (simpler, fork-friendly) +4. **Diff Visualization**: Table + Mermaid in PR comments +5. **Parameter Handling**: Use defaults if no params file; mark unknown values as `` From 05a431b695369c63866b99ebee688b629d48b289 Mon Sep 17 00:00:00 2001 From: Will Tsai <28876888+willtsai@users.noreply.github.com> Date: Wed, 4 Feb 2026 10:05:57 -0800 Subject: [PATCH 11/15] updated spec with speckit clarify Signed-off-by: Will Tsai <28876888+willtsai@users.noreply.github.com> --- specs/001-git-app-graph-preview/spec.md | 28 +++++++++++++++++++++---- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/specs/001-git-app-graph-preview/spec.md b/specs/001-git-app-graph-preview/spec.md index ae16c2e0..28197dd9 100644 --- a/specs/001-git-app-graph-preview/spec.md +++ b/specs/001-git-app-graph-preview/spec.md @@ -5,6 +5,16 @@ **Status**: Draft **Input**: User description: "Radius currently stores the state of application deployments as an app graph within its data store. Today, the app graph does not get generated until the application is deployed. Help me build an app graph representation for applications that are defined (e.g. in an app.bicep file) but not yet deployed. Additionally, enrich the app graph representation with git changelog info (i.e. git commit data) so that I may use this data to visualize how the app graph changes over time (i.e. across commits). The ultimate goal is to be able to visualize the app graph and do diffs of the app graph in GitHub on PRs, commit comparisons, etc." +## Clarifications + +### Session 2026-02-04 + +- Q: GitHub App vs Action for PR integration? → A: GitHub Action (fork-friendly, no installation approval required, aligns with existing Radius workflow patterns) +- Q: Diff visualization format in PR comments? → A: Table + Mermaid diagrams (change table for details, before/after diagrams for visual topology) +- Q: How to handle Bicep parameters without params file? → A: Require params file (fail with error if Bicep has required parameters but no `--parameters` provided) +- Q: GitHub Action trigger events? → A: `pull_request` + `push` (PR for review comments, push to main for baseline tracking) +- Q: Monorepo support with multiple app graphs? → A: Auto-detect all `**/.radius/app-graph.json` files; each diffed independently + ## Problem Statement Radius currently generates application graphs only after deployment, which means: @@ -37,7 +47,9 @@ As a **platform engineer**, I want to review app graph changes in PRs, so I can 4. **Given** a Bicep file with parameterized values, **When** I run `rad app graph app.bicep --parameters params.json`, **Then** the graph reflects the resolved parameter values. -5. **Given** a Bicep file using the Radius Bicep extension types, **When** I run `rad app graph app.bicep`, **Then** the graph correctly identifies Radius-specific resource types and their relationships. +5. **Given** a Bicep file with required parameters (no defaults) and no `--parameters` flag, **When** I run `rad app graph app.bicep`, **Then** I receive a clear error listing the missing required parameters. + +6. **Given** a Bicep file using the Radius Bicep extension types, **When** I run `rad app graph app.bicep`, **Then** the graph correctly identifies Radius-specific resource types and their relationships. --- @@ -101,6 +113,12 @@ As a PR reviewer, I want to see a visual diff of the app graph in PR comments, s **Operational Model**: The GitHub Action reads committed `.radius/app-graph.json` files from git history — it does NOT generate graphs on-demand. This keeps the Action lightweight (no Bicep/Radius tooling required) and fast. +**Trigger Events**: The Action supports two trigger modes: +- **`pull_request`**: Posts diff comments on PRs when `.radius/app-graph.json` changes +- **`push` to main/default branch**: Updates baseline tracking for historical comparison + +**Monorepo Support**: The Action auto-detects all `**/.radius/app-graph.json` files in the repository. Each graph is diffed independently, with separate PR comment sections per application. + **Independent Test**: Create a PR with Bicep changes and updated graph JSON, verify the action posts a comment showing before/after graph comparison. **Acceptance Scenarios**: @@ -115,6 +133,8 @@ As a PR reviewer, I want to see a visual diff of the app graph in PR comments, s 5. **Given** a PR where Bicep files changed but `.radius/app-graph.json` was not updated, **When** the CI validation job runs, **Then** it fails with a message instructing the developer to run `rad app graph app.bicep` and commit the updated graph. +6. **Given** a monorepo with multiple Radius applications (e.g., `apps/frontend/.radius/app-graph.json` and `apps/backend/.radius/app-graph.json`), **When** the GitHub Action runs on a PR, **Then** it detects all graph files and posts a unified comment with separate diff sections per application. + --- ### User Story 5 - Historical Graph Timeline (Priority: P3) @@ -389,11 +409,11 @@ This feature MUST include comprehensive testing across the testing pyramid: 2. ~~**Graph Storage**: Should generated graphs be committed to the repo (e.g., `app-graph.json`)? Trade-off: visibility vs. repo noise.~~ **RESOLVED (Initial Implementation)**: Graphs are committed to `.radius/app-graph.json`. This enables lightweight GitHub Action (reads from git history, no tooling required) and provides auditable graph evolution. **Future Evolution**: External storage backends (e.g., SQLite, cloud databases) could be supported for scenarios requiring graph queries across repositories, historical analytics, or enterprise-scale graph management. -3. **GitHub App vs Action**: Should the PR integration be a GitHub Action (user-managed) or a GitHub App (centrally managed)? Trade-off: flexibility vs. ease of setup. +3. ~~**GitHub App vs Action**: Should the PR integration be a GitHub Action (user-managed) or a GitHub App (centrally managed)? Trade-off: flexibility vs. ease of setup.~~ **RESOLVED**: GitHub Action. Fork-friendly, no installation approval required, aligns with existing Radius workflow patterns, supports incremental adoption. -4. **Diff Visualization**: What's the preferred format for showing diffs in PR comments—table-based, Mermaid side-by-side, or unified text diff? +4. ~~**Diff Visualization**: What's the preferred format for showing diffs in PR comments—table-based, Mermaid side-by-side, or unified text diff?~~ **RESOLVED**: Table + Mermaid diagrams. Change table shows added/removed/modified resources with details; before/after Mermaid diagrams provide visual topology comparison. Both render natively in GitHub. -5. **Parameter Handling**: How should we handle Bicep parameters without a params file—use defaults, require params, or mark as "unknown"? +5. ~~**Parameter Handling**: How should we handle Bicep parameters without a params file—use defaults, require params, or mark as "unknown"?~~ **RESOLVED**: Require params file. If Bicep has required parameters (no defaults) but no `--parameters` flag is provided, fail with a clear error message listing the missing parameters. --- From 901b104f2d9b28bd537a548643a9f9cdaa3f7936 Mon Sep 17 00:00:00 2001 From: Will Tsai <28876888+willtsai@users.noreply.github.com> Date: Wed, 4 Feb 2026 10:34:04 -0800 Subject: [PATCH 12/15] add tasks and clarify plan details Signed-off-by: Will Tsai <28876888+willtsai@users.noreply.github.com> --- specs/001-git-app-graph-preview/quickstart.md | 5 + specs/001-git-app-graph-preview/research.md | 2 +- specs/001-git-app-graph-preview/tasks.md | 323 ++++++++++++++++++ 3 files changed, 329 insertions(+), 1 deletion(-) create mode 100644 specs/001-git-app-graph-preview/tasks.md diff --git a/specs/001-git-app-graph-preview/quickstart.md b/specs/001-git-app-graph-preview/quickstart.md index 9a6dd4ed..90bb3f65 100644 --- a/specs/001-git-app-graph-preview/quickstart.md +++ b/specs/001-git-app-graph-preview/quickstart.md @@ -167,6 +167,11 @@ on: pull_request: paths: - '**/.radius/app-graph.json' + push: + branches: + - main + paths: + - '**/.radius/app-graph.json' permissions: pull-requests: write diff --git a/specs/001-git-app-graph-preview/research.md b/specs/001-git-app-graph-preview/research.md index ff1cee64..b25cdc60 100644 --- a/specs/001-git-app-graph-preview/research.md +++ b/specs/001-git-app-graph-preview/research.md @@ -236,4 +236,4 @@ func (impl *Impl) PrepareTemplate(filePath string) (map[string]any, error) { 2. **Graph Storage**: ✅ Committed to `.radius/app-graph.json` (per spec) 3. **GitHub App vs Action**: ✅ GitHub Action for MVP (simpler, fork-friendly) 4. **Diff Visualization**: Table + Mermaid in PR comments -5. **Parameter Handling**: Use defaults if no params file; mark unknown values as `` +5. **Parameter Handling**: ✅ Require params file; fail with clear error listing missing required parameters if Bicep has required parameters (no defaults) but no `--parameters` flag provided diff --git a/specs/001-git-app-graph-preview/tasks.md b/specs/001-git-app-graph-preview/tasks.md new file mode 100644 index 00000000..0e946ed2 --- /dev/null +++ b/specs/001-git-app-graph-preview/tasks.md @@ -0,0 +1,323 @@ +# Tasks: Git App Graph Preview + +**Input**: Design documents from `/specs/001-git-app-graph-preview/` +**Prerequisites**: plan.md ✓, spec.md ✓, research.md ✓, data-model.md ✓, contracts/ ✓, quickstart.md ✓ + +**Tests**: Included per spec.md "Testing Requirements" section - comprehensive testing across the testing pyramid. + +**Organization**: Tasks grouped by user story for independent implementation and testing. + +## Format: `[ID] [P?] [Story] Description` + +- **[P]**: Can run in parallel (different files, no dependencies) +- **[Story]**: Which user story this task belongs to (US1, US2, US3, US4, US5, US6) +- Exact file paths for radius repository + +--- + +## Phase 1: Setup (Shared Infrastructure) + +**Purpose**: Project initialization and scaffolding for the static graph feature + +- [ ] T001 Create package structure `pkg/cli/bicep/` with package doc in pkg/cli/bicep/doc.go +- [ ] T002 Create package structure `pkg/cli/git/` with package doc in pkg/cli/git/doc.go +- [ ] T003 [P] Add new test directories: test/unit/cli/graph/, test/integration/cli/graph/, test/functional/cli/graph/ +- [ ] T004 [P] Define AppGraph types in pkg/corerp/api/v20231001preview/appgraph_static_types.go +- [ ] T005 [P] Define GraphDiff types in pkg/corerp/api/v20231001preview/appgraph_diff_types.go +- [ ] T006 Add JSON schema validation helpers in pkg/cli/output/json.go (deterministic key ordering) + +--- + +## Phase 2: Foundational (Blocking Prerequisites) + +**Purpose**: Core infrastructure required by ALL user stories + +**⚠️ CRITICAL**: No user story work can begin until this phase is complete + +- [ ] T007 Implement Bicep CLI executor in pkg/cli/bicep/executor.go (wraps `bicep build --stdout`) +- [ ] T008 Implement ARM JSON parser interface in pkg/cli/bicep/parser.go (extract resources array) +- [ ] T009 Implement resource extraction from ARM JSON in pkg/cli/bicep/extractor.go +- [ ] T010 Implement connection detection from resource properties in pkg/cli/bicep/connections.go +- [ ] T011 [P] Implement source hash computation (SHA256 of input files) in pkg/cli/bicep/hash.go +- [ ] T012 [P] Add Bicep file detection logic (`.bicep` extension) in pkg/cli/cmd/app/graph/detect.go +- [ ] T013 Unit tests for Bicep executor in test/unit/cli/graph/executor_test.go +- [ ] T014 Unit tests for ARM JSON parser in test/unit/cli/graph/parser_test.go + +**Checkpoint**: Foundation ready - user story implementation can begin + +--- + +## Phase 3: User Story 1 - Generate App Graph from Bicep Files (Priority: P1) 🎯 MVP + +**Goal**: Enable `rad app graph app.bicep` to generate a JSON app graph from Bicep files without deployment + +**Independent Test**: Run CLI against sample Bicep file, verify JSON output contains expected resources and connections + +### Tests for User Story 1 + +- [ ] T015 [P] [US1] Unit test for graph generation with valid Bicep in test/unit/cli/graph/static_test.go +- [ ] T016 [P] [US1] Unit test for error handling with invalid Bicep in test/unit/cli/graph/static_errors_test.go +- [ ] T017 [P] [US1] Unit test for module resolution in test/unit/cli/graph/modules_test.go +- [ ] T018 [US1] Integration test with real Bicep CLI in test/integration/cli/graph/bicep_integration_test.go + +### Implementation for User Story 1 + +- [ ] T019 [US1] Implement static graph generator in pkg/cli/cmd/app/graph/static.go +- [ ] T020 [US1] Add file input detection to existing graph.go entry point in pkg/cli/cmd/app/graph/graph.go +- [ ] T021 [US1] Implement module resolution (transitive Bicep imports) in pkg/cli/bicep/modules.go +- [ ] T022 [US1] Add parameter file support (`--parameters`) in pkg/cli/cmd/app/graph/params.go +- [ ] T023 [US1] Implement required parameter validation (fail if missing) in pkg/cli/bicep/params.go +- [ ] T024 [US1] Add Radius Bicep extension type detection in pkg/cli/bicep/radius_types.go +- [ ] T025 [US1] Implement error handling with line/column info in pkg/cli/bicep/errors.go +- [ ] T026 [US1] Add logging for graph generation operations in pkg/cli/cmd/app/graph/static.go + +**Checkpoint**: `rad app graph app.bicep` generates valid JSON graph from Bicep files + +--- + +## Phase 4: User Story 2 - Export Graph as Diff-Friendly Format (Priority: P1) + +**Goal**: Output deterministic JSON to `.radius/app-graph.json` and optional Markdown with Mermaid diagrams + +**Independent Test**: Generate graph twice from identical input, verify byte-identical output; verify Markdown renders in GitHub + +### Tests for User Story 2 + +- [ ] T027 [P] [US2] Unit test for deterministic JSON output in test/unit/cli/graph/json_determinism_test.go +- [ ] T028 [P] [US2] Unit test for Mermaid diagram generation in test/unit/cli/graph/mermaid_test.go +- [ ] T029 [P] [US2] Unit test for Markdown table formatting in test/unit/cli/graph/markdown_test.go +- [ ] T030 [US2] Integration test for file output paths in test/integration/cli/graph/output_test.go + +### Implementation for User Story 2 + +- [ ] T031 [US2] Implement deterministic JSON serializer in pkg/cli/output/deterministic_json.go +- [ ] T032 [US2] Implement default output path (`.radius/app-graph.json`) in pkg/cli/cmd/app/graph/output.go +- [ ] T033 [US2] Add `--stdout` flag for stdout output in pkg/cli/cmd/app/graph/flags.go +- [ ] T034 [US2] Add `-o` flag for custom output path in pkg/cli/cmd/app/graph/flags.go +- [ ] T035 [US2] Implement Mermaid diagram generator in pkg/cli/output/mermaid.go +- [ ] T036 [US2] Implement Markdown table formatter in pkg/cli/output/markdown.go +- [ ] T037 [US2] Add `--format markdown` flag (generates both JSON + Markdown) in pkg/cli/cmd/app/graph/flags.go +- [ ] T038 [US2] Integrate display.go with new output formatters in pkg/cli/cmd/app/graph/display.go +- [ ] T039 [US2] Add Mermaid shape mapping (containers→rectangles, gateways→diamonds, databases→cylinders) in pkg/cli/output/mermaid.go + +**Checkpoint**: Deterministic JSON output + optional Markdown with Mermaid diagrams working + +--- + +## Phase 5: User Story 3 - Git Metadata Enrichment (Priority: P2) + +**Goal**: Automatically enrich each resource with git commit information (SHA, author, date, message) + +**Independent Test**: Generate graph in git repo, verify each resource has git metadata; verify `--no-git` skips enrichment + +### Tests for User Story 3 + +- [ ] T040 [P] [US3] Unit test for git blame parsing in test/unit/cli/graph/git_blame_test.go +- [ ] T041 [P] [US3] Unit test for git log parsing in test/unit/cli/graph/git_log_test.go +- [ ] T042 [P] [US3] Unit test for uncommitted file detection in test/unit/cli/graph/git_uncommitted_test.go +- [ ] T043 [US3] Integration test with real git repository in test/integration/cli/graph/git_integration_test.go + +### Implementation for User Story 3 + +- [ ] T044 [US3] Implement git repository detection in pkg/cli/git/repo.go +- [ ] T045 [US3] Implement git blame executor in pkg/cli/git/blame.go +- [ ] T046 [US3] Implement git log metadata extraction in pkg/cli/git/log.go +- [ ] T047 [US3] Implement per-resource git info enrichment in pkg/cli/git/metadata.go +- [ ] T048 [US3] Add uncommitted changes detection in pkg/cli/git/status.go +- [ ] T049 [US3] Add `--no-git` flag for faster execution in pkg/cli/cmd/app/graph/flags.go +- [ ] T050 [US3] Handle non-git directories gracefully in pkg/cli/git/metadata.go +- [ ] T051 [US3] Handle shallow clones (mark as "history unavailable") in pkg/cli/git/blame.go +- [ ] T052 [US3] Add linked commit SHA in Markdown output in pkg/cli/output/markdown.go + +**Checkpoint**: Graph resources include git metadata by default; `--no-git` works + +--- + +## Phase 6: User Story 4 - GitHub Action for PR Graph Diff (Priority: P2) + +**Goal**: GitHub Action reads committed JSON from git history and posts diff comments on PRs + +**Independent Test**: Create PR with graph changes, verify Action posts comment with change table and Mermaid diagrams + +### Tests for User Story 4 + +- [ ] T053 [P] [US4] Unit test for JSON-to-JSON diff computation in test/unit/cli/graph/diff_test.go +- [ ] T054 [P] [US4] Unit test for diff summary generation in test/unit/cli/graph/diff_summary_test.go +- [ ] T055 [US4] Unit test for diff Markdown rendering in test/unit/cli/graph/diff_render_test.go + +### Implementation for User Story 4 + +- [ ] T056 [US4] Implement graph diff computation in pkg/cli/cmd/app/graph/diff.go +- [ ] T057 [US4] Implement resource comparison (by ID) in pkg/cli/cmd/app/graph/diff.go +- [ ] T058 [US4] Implement connection comparison (by source+target tuple) in pkg/cli/cmd/app/graph/diff.go +- [ ] T059 [US4] Implement diff summary statistics in pkg/cli/cmd/app/graph/diff.go +- [ ] T060 [US4] Implement diff Markdown renderer (change table + before/after Mermaid) in pkg/cli/output/diff_markdown.go +- [ ] T061 [US4] Create GitHub Action definition in actions/app-graph-diff/action.yml +- [ ] T062 [US4] Implement diff computation shell script in actions/app-graph-diff/diff.sh +- [ ] T063 [US4] Implement Mermaid rendering for Action in actions/app-graph-diff/render.js +- [ ] T064 [US4] Add PR comment create-or-update logic in actions/app-graph-diff/comment.sh +- [ ] T065 [US4] Add monorepo support (glob `**/.radius/app-graph.json`) in actions/app-graph-diff/detect.sh +- [ ] T066 [US4] Add staleness validation (compare committed vs regenerated) in actions/app-graph-diff/validate.sh +- [ ] T067 [US4] Add `push` trigger support for baseline tracking in actions/app-graph-diff/action.yml + +**Checkpoint**: GitHub Action posts diff comments on PRs with graph changes + +--- + +## Phase 7: User Story 5 - Historical Graph Timeline (Priority: P3) + +**Goal**: Enable `rad app graph history` to show graph evolution across commits + +**Independent Test**: Run history command on repo with multiple commits, verify timeline shows graph changes + +### Tests for User Story 5 + +- [ ] T068 [P] [US5] Unit test for timeline generation in test/unit/cli/graph/history_test.go +- [ ] T069 [US5] Integration test with multi-commit git history in test/integration/cli/graph/history_integration_test.go + +### Implementation for User Story 5 + +- [ ] T070 [US5] Implement `rad app graph history` subcommand in pkg/cli/cmd/app/graph/history.go +- [ ] T071 [US5] Add `--commits N` flag in pkg/cli/cmd/app/graph/history.go +- [ ] T072 [US5] Implement `--at ` flag for graph at specific commit in pkg/cli/cmd/app/graph/flags.go +- [ ] T073 [US5] Implement `rad app graph diff --from X --to Y` subcommand in pkg/cli/cmd/app/graph/diff.go +- [ ] T074 [US5] Implement commit range iteration in pkg/cli/git/history.go +- [ ] T075 [US5] Implement change summary per commit in pkg/cli/cmd/app/graph/history.go + +**Checkpoint**: Historical timeline and commit-specific graph generation working + +--- + +## Phase 8: User Story 6 - Environment-Resolved Graph (Priority: P3) + +**Goal**: Enable `rad app graph --environment` to show resolved recipe types instead of abstract Radius types + +**Independent Test**: Generate graph with environment flag, verify abstract types resolve to concrete infrastructure types + +### Tests for User Story 6 + +- [ ] T076 [P] [US6] Unit test for recipe type resolution in test/unit/cli/graph/resolve_test.go +- [ ] T077 [US6] Integration test with Radius environment in test/integration/cli/graph/environment_test.go + +### Implementation for User Story 6 + +- [ ] T078 [US6] Add `--environment` flag in pkg/cli/cmd/app/graph/flags.go +- [ ] T079 [US6] Implement environment connection in pkg/cli/cmd/app/graph/environment.go +- [ ] T080 [US6] Implement recipe lookup from environment in pkg/cli/cmd/app/graph/recipes.go +- [ ] T081 [US6] Implement portable type → concrete type resolution in pkg/cli/cmd/app/graph/resolve.go +- [ ] T082 [US6] Add "no recipe bound" annotation for unbound types in pkg/cli/cmd/app/graph/resolve.go +- [ ] T083 [US6] Integrate resolved types into graph output in pkg/cli/cmd/app/graph/static.go + +**Checkpoint**: Environment-resolved graphs show concrete infrastructure types + +--- + +## Phase 9: Polish & Cross-Cutting Concerns + +**Purpose**: Documentation, validation, and refinements + +- [ ] T084 [P] Update pkg/cli/cmd/app/graph/README.md with new command documentation +- [ ] T085 [P] Add godoc comments to all exported types in pkg/cli/bicep/ +- [ ] T086 [P] Add godoc comments to all exported types in pkg/cli/git/ +- [ ] T087 Run golangci-lint and fix any issues in new code +- [ ] T088 [P] Create functional E2E test in test/functional/cli/graph/e2e_test.go +- [ ] T089 Validate quickstart.md scenarios work end-to-end +- [ ] T090 Update CHANGELOG.md with new feature description +- [ ] T091 [P] Create GitHub Action README in actions/app-graph-diff/README.md + +--- + +## Dependencies & Execution Order + +### Phase Dependencies + +- **Setup (Phase 1)**: No dependencies - can start immediately +- **Foundational (Phase 2)**: Depends on Setup - BLOCKS all user stories +- **US1 (Phase 3)**: Depends on Foundational - First MVP +- **US2 (Phase 4)**: Depends on Foundational - Second MVP capability +- **US3 (Phase 5)**: Depends on US1/US2 working - Enrichment layer +- **US4 (Phase 6)**: Depends on US2 (diff-friendly output) + US3 (git metadata) +- **US5 (Phase 7)**: Depends on US3 (git integration) +- **US6 (Phase 8)**: Can start after US1 - independent branch +- **Polish (Phase 9)**: After all desired user stories complete + +### User Story Dependencies + +``` +Foundational (Phase 2) + │ + ├──────────────┬──────────────┐ + ▼ ▼ ▼ + US1 (P1) US2 (P1) US6 (P3) + │ │ (independent) + └──────┬───────┘ + ▼ + US3 (P2) + │ + ┌──────┴──────┐ + ▼ ▼ + US4 (P2) US5 (P3) +``` + +### Parallel Opportunities + +**Phase 1 (Setup)**: T001, T002 sequential; T003-T006 all parallel +**Phase 2 (Foundational)**: T007-T010 sequential; T011-T012 parallel; T013-T014 parallel +**Each User Story**: All tests marked [P] can run in parallel; implementation tasks mostly sequential within story + +--- + +## Parallel Example: User Story 1 Tests + +```bash +# Launch all US1 tests in parallel: +Task: "T015 [P] [US1] Unit test for graph generation with valid Bicep" +Task: "T016 [P] [US1] Unit test for error handling with invalid Bicep" +Task: "T017 [P] [US1] Unit test for module resolution" + +# Then run integration test: +Task: "T018 [US1] Integration test with real Bicep CLI" +``` + +--- + +## Implementation Strategy + +### MVP First (US1 + US2 = P1 Stories) + +1. Complete Phase 1: Setup +2. Complete Phase 2: Foundational (CRITICAL) +3. Complete Phase 3: User Story 1 (core graph generation) +4. Complete Phase 4: User Story 2 (diff-friendly output) +5. **STOP and VALIDATE**: Run quickstart.md scenarios locally +6. Deploy CLI changes for developer feedback + +### Git Integration (P2 Stories) + +1. Add US3: Git metadata enrichment +2. Add US4: GitHub Action for PR diffs +3. **VALIDATE**: Test in real PR workflow + +### Advanced Features (P3 Stories) + +1. Add US5: Historical timeline (optional) +2. Add US6: Environment resolution (optional) +3. Final polish phase + +### Parallel Team Strategy + +With multiple developers: +- **Developer A**: Phase 1 → Phase 2 → US1 → US3 → US5 +- **Developer B**: Phase 1 (parallel) → US2 → US4 (GitHub Action) +- **Developer C**: US6 (can start after Phase 2) + +--- + +## Notes + +- Radius repository is at `../radius` relative to design-notes +- All Go code must pass `golangci-lint` (Constitution Principle II) +- All exported types need godoc comments (NFR-002) +- JSON output must be deterministic (FR-004, SC-002) +- Existing `rad app graph ` behavior must not change (FR-003) +- GitHub Action is lightweight (reads JSON only, no Bicep tooling) From e654efe4382e585922dea1b226243011b1c17ab7 Mon Sep 17 00:00:00 2001 From: Will Tsai <28876888+willtsai@users.noreply.github.com> Date: Mon, 23 Feb 2026 13:40:52 -0800 Subject: [PATCH 13/15] modify spec: auto-gen app graph data in GH Action, specify UI requirements Signed-off-by: Will Tsai <28876888+willtsai@users.noreply.github.com> --- specs/001-git-app-graph-preview/spec.md | 140 +++++++++++++++++------- 1 file changed, 103 insertions(+), 37 deletions(-) diff --git a/specs/001-git-app-graph-preview/spec.md b/specs/001-git-app-graph-preview/spec.md index 28197dd9..ab92c72e 100644 --- a/specs/001-git-app-graph-preview/spec.md +++ b/specs/001-git-app-graph-preview/spec.md @@ -13,7 +13,7 @@ - Q: Diff visualization format in PR comments? → A: Table + Mermaid diagrams (change table for details, before/after diagrams for visual topology) - Q: How to handle Bicep parameters without params file? → A: Require params file (fail with error if Bicep has required parameters but no `--parameters` provided) - Q: GitHub Action trigger events? → A: `pull_request` + `push` (PR for review comments, push to main for baseline tracking) -- Q: Monorepo support with multiple app graphs? → A: Auto-detect all `**/.radius/app-graph.json` files; each diffed independently +- Q: Monorepo support with multiple app graphs? → A: Auto-detect all Bicep entry points (e.g., `**/app.bicep`); each diffed independently ## Problem Statement @@ -39,7 +39,7 @@ As a **platform engineer**, I want to review app graph changes in PRs, so I can **Acceptance Scenarios**: -1. **Given** a valid `app.bicep` file with container, gateway, and database resources, **When** I run `rad app graph app.bicep`, **Then** I receive a JSON graph representation showing all resources and their connections. +1. **Given** a valid `app.bicep` file with container, gateway, and database resources, **When** I run `rad app graph app.bicep`, **Then** I receive a JSON graph representation showing all resources, their connections, and key properties for each resource (e.g., container image, ports, database name). 2. **Given** a Bicep file with syntax errors, **When** I run `rad app graph app.bicep`, **Then** I receive a clear error message indicating the parsing failure with line/column information. @@ -75,7 +75,7 @@ As a developer, I want the app graph exported in a deterministic, diff-friendly - A resource table with name, type, source file, and git metadata - An embedded Mermaid diagram showing the topology that GitHub renders automatically -5. **Given** a graph exported to markdown, **When** viewed in GitHub, **Then** the Mermaid diagram renders as a visual flowchart with distinct shapes for resource types (containers as rectangles, gateways as diamonds, databases as cylinders). +5. **Given** a graph exported to markdown, **When** viewed in GitHub, **Then** the Mermaid diagram renders as a visual flowchart following the Visual Design Guidelines (rounded rectangle nodes, GitHub-consistent color palette for diff states, distinct shapes for databases) and node labels include key properties (e.g., container image tag, port numbers). 6. **Given** two app graphs from different commits, **When** I diff them, **Then** the diff is computed from JSON (not Markdown), and added/removed/modified resources are clearly identified. @@ -111,29 +111,29 @@ As a PR reviewer, I want to see a visual diff of the app graph in PR comments, s **Why this priority**: High-value GitHub integration, but depends on P1 capabilities being stable. -**Operational Model**: The GitHub Action reads committed `.radius/app-graph.json` files from git history — it does NOT generate graphs on-demand. This keeps the Action lightweight (no Bicep/Radius tooling required) and fast. +**Operational Model**: The GitHub Action **generates the app graph automatically** as part of the workflow — developers do NOT need to run `rad app graph app.bicep` manually or commit graph artifacts. The Action checks out the PR head and base branches, runs `rad app graph app.bicep` on each, computes the diff, and posts the result as a PR comment. This keeps the developer experience friction-free at the cost of requiring Bicep/Radius tooling in CI. **Trigger Events**: The Action supports two trigger modes: - **`pull_request`**: Posts diff comments on PRs when `.radius/app-graph.json` changes - **`push` to main/default branch**: Updates baseline tracking for historical comparison -**Monorepo Support**: The Action auto-detects all `**/.radius/app-graph.json` files in the repository. Each graph is diffed independently, with separate PR comment sections per application. +**Monorepo Support**: The Action auto-detects all Bicep entry points (e.g., `**/app.bicep`) in the repository. Each application graph is generated and diffed independently, with separate PR comment sections per application. **Independent Test**: Create a PR with Bicep changes and updated graph JSON, verify the action posts a comment showing before/after graph comparison. **Acceptance Scenarios**: -1. **Given** a PR that includes changes to `.radius/app-graph.json`, **When** the GitHub Action runs, **Then** it reads the JSON from base and head commits and posts a comment showing the graph diff with added/removed/modified resources highlighted. +1. **Given** a PR that includes changes to Bicep files, **When** the GitHub Action runs, **Then** it generates the app graph from both the base and head commits, computes the diff, and posts a comment showing added (green), removed (red), and modified (yellow) resources using the Visual Design Guidelines color conventions. -2. **Given** a PR with no changes to `.radius/app-graph.json`, **When** the GitHub Action runs, **Then** it posts a comment indicating "No app graph changes detected." +2. **Given** a PR with no changes to Bicep files (or where Bicep changes produce an identical graph), **When** the GitHub Action runs, **Then** it posts a comment indicating "No app graph changes detected." 3. **Given** a PR that adds a new connection between resources, **When** the GitHub Action runs, **Then** the diff clearly shows the new connection with source and target resources. 4. **Given** a PR comment already exists from a previous run, **When** the PR is updated and the action runs again, **Then** the existing comment is updated rather than creating a duplicate. -5. **Given** a PR where Bicep files changed but `.radius/app-graph.json` was not updated, **When** the CI validation job runs, **Then** it fails with a message instructing the developer to run `rad app graph app.bicep` and commit the updated graph. +5. **Given** a developer who has not run `rad app graph` locally, **When** they push Bicep changes and open a PR, **Then** the GitHub Action generates the graph and posts the diff automatically — no manual graph generation or commit is required. -6. **Given** a monorepo with multiple Radius applications (e.g., `apps/frontend/.radius/app-graph.json` and `apps/backend/.radius/app-graph.json`), **When** the GitHub Action runs on a PR, **Then** it detects all graph files and posts a unified comment with separate diff sections per application. +6. **Given** a monorepo with multiple Radius applications (e.g., `apps/frontend/app.bicep` and `apps/backend/app.bicep`), **When** the GitHub Action runs on a PR, **Then** it detects all Bicep entry points and posts a unified comment with separate diff sections per application. --- @@ -193,10 +193,9 @@ As a platform engineer, I want to see how abstract Radius types resolve to concr - Mark affected values as "dynamic" in the graph, use placeholder notation - What happens when Bicep files use cloud-specific resources (Azure, AWS)? - Graph generation MUST work regardless of cloud provider; cloud-specific resources are represented with their provider prefix (e.g., `Microsoft.Storage/storageAccounts`, `AWS::S3::Bucket`) -- What happens when the committed graph is stale (Bicep changed but graph not regenerated)? - - CI validation job compares committed graph to freshly generated graph; fails PR if they differ - - Graph JSON includes `sourceHash` to detect staleness without full regeneration - - Clear error message instructs developer to run `rad app graph app.bicep` +- What happens if a developer commits `.radius/app-graph.json` alongside Bicep changes? + - The GitHub Action ignores committed graph artifacts and always generates fresh graphs from Bicep source to ensure accuracy + - Committed graph files are treated as optional local convenience, not as CI inputs - What does the graph show for portable Radius types like `Radius.Data/store` that resolve differently per environment? - **Static graph shows abstract types**: The declared `Radius.Data/store` is shown, not the resolved infrastructure (PostgreSQL, CosmosDB, etc.) - This is intentional—the static graph represents the **portable application architecture** independent of environment-specific recipe resolution @@ -235,13 +234,61 @@ This feature extends the existing `rad app graph` command with file-based input --- -## Committed Artifact Model +## Visual Design Guidelines + +All graph visualizations — Mermaid diagrams, PR comment diff tables, and Markdown output — MUST follow a visual style consistent with the **native GitHub UI** to feel integrated rather than bolted-on. + +### Color Palette (Diff States) + +| State | Color | Usage | GitHub Reference | +|-------|-------|-------|------------------| +| **Added** | Green (`#2da44e` / `--color-success-fg`) | New resources, new connections | Same as GitHub diff additions | +| **Removed** | Red (`#cf222e` / `--color-danger-fg`) | Deleted resources, deleted connections | Same as GitHub diff deletions | +| **Modified** | Yellow (`#bf8700` / `--color-attention-fg`) | Changed properties or connections | Same as GitHub warning/attention | +| **Unchanged** | Gray (`#656d76` / `--color-fg-muted`) | Context resources shown for topology reference | Same as GitHub muted text | + +### Mermaid Diagram Styling + +- **Node shapes**: Rounded rectangles (`([...])`) for all resource nodes to match GitHub's card/box aesthetic + - Containers: rounded rectangle with container icon annotation + - Gateways: rounded rectangle with gateway/diamond icon annotation + - Databases: cylinder shape (Mermaid built-in `[(...)]`) +- **Node labels**: Resource name as primary label, key properties (e.g., image tag, port) as secondary text +- **Edges**: Solid lines for direct connections, dashed lines for implicit/inferred dependencies +- **Diff coloring**: Use Mermaid `classDef` styles to apply the color palette above: + ```mermaid + classDef added fill:#dafbe1,stroke:#2da44e,color:#1a7f37 + classDef removed fill:#ffebe9,stroke:#cf222e,color:#82071e + classDef modified fill:#fff8c5,stroke:#bf8700,color:#6a5600 + classDef unchanged fill:#f6f8fa,stroke:#d0d7de,color:#656d76 + ``` +- **Layout**: Top-to-bottom (`TB`) flow direction for application topology + +### PR Comment Styling + +- **Change summary table**: Use GitHub Markdown table with status emoji prefix for scanability: + - `🟢 Added` — green circle for new resources + - `🔴 Removed` — red circle for deleted resources + - `🟡 Modified` — yellow circle for changed resources +- **Before/After diagrams**: Labeled with `### Before` and `### After` headings inside a collapsible `
` block to avoid overwhelming long PRs +- **Comment header**: Include a brief summary line (e.g., "App Graph: +2 resources, -1 resource, ~1 modified") so reviewers can assess impact at a glance without expanding details +- **Borders and containers**: Use GitHub's native `> [!NOTE]`, `> [!WARNING]`, and `> [!IMPORTANT]` callout syntax where appropriate for status messages + +### General Principles + +- Visual output MUST render correctly in both GitHub light and dark themes +- Avoid hardcoded colors that become illegible in dark mode — use GitHub's CSS variable equivalents as reference and test both themes +- Prefer semantic color usage (green = added, red = removed) over decorative color +- Keep visualizations compact — prioritize information density over whitespace +- All visual elements should feel like a native part of the GitHub PR review experience, not an external tool's output -The app graph JSON is designed to be **committed to version control** as a tracked artifact. This enables lightweight GitHub integration without requiring the Action to have Bicep/Radius tooling. +--- + +The app graph JSON can be generated locally as a **developer preview** but is NOT required to be committed. The GitHub Action generates graphs automatically from Bicep source during CI. ### Default Output Location -By default, `rad app graph app.bicep` writes to `.radius/app-graph.json` relative to the Bicep file's directory: +Locally, `rad app graph app.bicep` writes to `.radius/app-graph.json` relative to the Bicep file's directory: ``` myapp/ @@ -259,34 +306,33 @@ myapp/ # 1. Make changes to Bicep files vim app.bicep -# 2. Regenerate the graph (writes to .radius/app-graph.json by default) -rad app graph app.bicep +# 2. (Optional) Preview the graph locally +rad app graph app.bicep # writes to .radius/app-graph.json +rad app graph app.bicep --stdout # or view in terminal -# 3. Commit both the Bicep changes and updated graph -git add app.bicep .radius/app-graph.json +# 3. Commit Bicep changes (no need to commit graph artifacts) +git add app.bicep git commit -m "Add redis cache to application" -# 4. Push and create PR — GitHub Action reads committed JSON to render diff +# 4. Push and create PR — GitHub Action generates graphs and posts diff automatically git push ``` -### Why Committed Artifacts? +**Key Point**: Developers do NOT need to run `rad app graph` or commit `.radius/app-graph.json` for the PR workflow to function. The GitHub Action handles graph generation and diffing entirely. Local graph generation is available as an optional preview tool. + +### Why This Model? | Benefit | Explanation | |---------|-------------| -| **Simple GitHub Action** | Action is a lightweight viewer that reads JSON from git history — no Bicep CLI, no Radius environment needed | -| **Fast CI** | No graph generation in CI; diff is just JSON comparison | -| **Reproducible** | Graph captured at commit time, not regenerated with potentially different tooling | -| **Auditable** | Graph evolution visible in git history alongside code changes | -| **Fork-friendly** | Works in forks without special tooling or secrets | - -### Staleness Detection +| **Zero developer friction** | Developers only commit Bicep files — no extra `rad app graph` step or graph artifacts to manage | +| **Always up-to-date** | Graph is generated fresh from Bicep source on every PR, eliminating stale graph drift | +| **Reproducible** | Graph generated from committed Bicep source using pinned tooling in CI | +| **Fork-friendly** | Works in forks — the GitHub Action installs its own tooling | +| **Optional local preview** | Developers can still run `rad app graph app.bicep` locally to preview changes before pushing | -To prevent committed graphs from drifting out of sync with Bicep files: +### Graph Metadata -1. **CI Validation Job** (recommended): Regenerate graph in CI, compare to committed version, fail if different -2. **Pre-commit Hook** (optional): Validate graph matches Bicep before allowing commit -3. **Graph Metadata**: JSON includes `sourceHash` field — hash of input Bicep file(s) for staleness detection +Generated graphs include metadata for traceability and debugging: ```json { @@ -294,13 +340,16 @@ To prevent committed graphs from drifting out of sync with Bicep files: "generatedAt": "2026-01-30T10:15:00Z", "sourceFiles": ["app.bicep", "modules/database.bicep"], "sourceHash": "sha256:abc123...", - "radiusCliVersion": "0.35.0" + "radiusCliVersion": "0.35.0", + "generatedBy": "github-action" }, "resources": [...], "connections": [...] } ``` +Since the GitHub Action generates graphs on-the-fly from Bicep source, there is no staleness concern — the graph always reflects the current state of the code at each commit. + --- ## Requirements *(mandatory)* @@ -317,8 +366,8 @@ To prevent committed graphs from drifting out of sync with Bicep files: - **FR-008**: System MUST enrich graph nodes with git metadata (commit SHA, author, date, message) by default when in a git repository, with `--no-git` flag to disable - **FR-009**: System MUST track which Bicep file(s) define each resource for git blame integration - **FR-010**: System MUST write graph output to `.radius/app-graph.json` by default (relative to Bicep file location), with `--stdout` flag for stdout output and `-o` flag for custom path -- **FR-011**: System MUST include `sourceHash` metadata in JSON output to enable staleness detection -- **FR-012**: System MUST provide a GitHub Action that reads committed graph JSON from git history and posts graph diffs on PRs (no graph generation in CI) +- **FR-011**: System MUST include `sourceHash` metadata in JSON output for traceability +- **FR-012**: System MUST provide a GitHub Action that generates app graphs from Bicep source for both base and head commits, computes the diff, and posts the result as a PR comment (developers are NOT required to generate or commit graph artifacts manually) - **FR-013**: System MUST update existing PR comments rather than creating duplicates - **FR-014**: System MUST support generating graphs at specific git commits/refs - **FR-015**: System MUST handle Bicep parameter files to resolve parameterized values @@ -326,6 +375,7 @@ To prevent committed graphs from drifting out of sync with Bicep files: - **FR-017**: System MUST handle unresolvable references gracefully with placeholder annotations - **FR-018**: System MUST work with Bicep files targeting any cloud provider (multi-cloud neutrality per Constitution Principle III) - **FR-019**: System MUST be compatible with the Radius Bicep extension type definitions +- **FR-020**: System MUST extract and include resource-specific properties (e.g., container image, ports, hostnames, database names) in each graph node so that visualizations can display them as node labels or detail annotations. Secret values MUST NOT be captured. ### Non-Functional Requirements @@ -334,6 +384,7 @@ To prevent committed graphs from drifting out of sync with Bicep files: - **NFR-003**: Feature MUST NOT require changes to existing deployment workflows (Constitution Principle IX - Incremental Adoption) - **NFR-004**: CLI commands MUST follow existing `rad` CLI patterns and conventions - **NFR-005**: Error messages MUST be actionable with clear guidance for resolution (Constitution Principle VI) +- **NFR-006**: All visual output (Mermaid diagrams, PR comment tables, Markdown previews) MUST follow a GitHub-native visual style — rounded corners, GitHub's diff color palette (green/red/yellow), and correct rendering in both light and dark themes ### Key Entities @@ -345,11 +396,26 @@ To prevent committed graphs from drifting out of sync with Bicep files: - ID: Unique resource identifier (matches Radius resource ID format) - Name: Human-readable resource name - Type: Resource type (e.g., `Applications.Core/containers`) + - Properties: Key resource-specific properties useful for visualization (see below) - SourceFile: Bicep file path where resource is defined - SourceLine: Line number in source file - Connections: Outbound connections to other resources - GitInfo: Last commit SHA, author, date, message affecting this resource + **Resource Properties for Visualization**: Each resource node MUST include salient properties extracted from the Bicep definition so that visualizations can display them as node labels or detail panels. The properties captured are resource-type-specific: + + | Resource Type | Properties Captured | + |---------------|--------------------| + | `Applications.Core/containers` | `image` (container image reference), `ports` (port mappings), `env` (environment variable names, not values) | + | `Applications.Core/gateways` | `hostname`, `routes` (path prefixes) | + | `Applications.Core/httpRoutes` | `port`, `scheme` | + | `Applications.Datastores/*` | `resourceProvisioning`, `database` (name), `server` (host) | + | `Applications.Messaging/*` | `resourceProvisioning`, `queue`/`topic` (name) | + | Cloud-specific resources | `sku`, `location`, provider-specific key properties | + | All resources | `application` (parent app ID), `environment` (environment ID) | + + Properties with values that cannot be statically resolved (e.g., runtime expressions, secrets) are included with a `"dynamic"` placeholder value. Secret values (e.g., passwords, connection strings) are NEVER captured — only non-sensitive configuration is included. + - **AppGraphConnection**: Edge between two resources - SourceID: Origin resource - TargetID: Destination resource @@ -373,7 +439,7 @@ To prevent committed graphs from drifting out of sync with Bicep files: - **SC-002**: Generated graphs are 100% deterministic (identical input produces byte-identical output) - **SC-003**: Graph diff correctly identifies all added, removed, and modified resources with zero false positives - **SC-004**: GitHub Action posts PR comments within 60 seconds of workflow trigger -- **SC-005**: Markdown output (including embedded Mermaid diagram) renders correctly in GitHub without manual formatting +- **SC-005**: Markdown output (including embedded Mermaid diagram) renders correctly in GitHub in both light and dark themes, following the Visual Design Guidelines for color and layout - **SC-006**: Git enrichment adds < 2 seconds overhead for repositories with up to 1000 commits - **SC-007**: System handles Bicep files up to 5000 lines without performance degradation - **SC-008**: Error messages include actionable guidance in 100% of failure cases From f5971e99d784f5c1863db675218c933688d099a0 Mon Sep 17 00:00:00 2001 From: Will Tsai <28876888+willtsai@users.noreply.github.com> Date: Mon, 23 Feb 2026 13:53:39 -0800 Subject: [PATCH 14/15] modify spec: generate images instead of mermaid diagrams Signed-off-by: Will Tsai <28876888+willtsai@users.noreply.github.com> --- specs/001-git-app-graph-preview/spec.md | 102 ++++++++++++++++-------- 1 file changed, 68 insertions(+), 34 deletions(-) diff --git a/specs/001-git-app-graph-preview/spec.md b/specs/001-git-app-graph-preview/spec.md index ab92c72e..73961f7c 100644 --- a/specs/001-git-app-graph-preview/spec.md +++ b/specs/001-git-app-graph-preview/spec.md @@ -10,7 +10,7 @@ ### Session 2026-02-04 - Q: GitHub App vs Action for PR integration? → A: GitHub Action (fork-friendly, no installation approval required, aligns with existing Radius workflow patterns) -- Q: Diff visualization format in PR comments? → A: Table + Mermaid diagrams (change table for details, before/after diagrams for visual topology) +- Q: Diff visualization format in PR comments? → A: Table + AI-generated graph images (change table for details, before/after topology images for visual comparison) - Q: How to handle Bicep parameters without params file? → A: Require params file (fail with error if Bicep has required parameters but no `--parameters` provided) - Q: GitHub Action trigger events? → A: `pull_request` + `push` (PR for review comments, push to main for baseline tracking) - Q: Monorepo support with multiple app graphs? → A: Auto-detect all Bicep entry points (e.g., `**/app.bicep`); each diffed independently @@ -73,9 +73,9 @@ As a developer, I want the app graph exported in a deterministic, diff-friendly 4. **Given** an app graph, **When** I run `rad app graph app.bicep --format markdown`, **Then** I receive **both** `.radius/app-graph.json` and `.radius/app-graph.md` containing: - A resource table with name, type, source file, and git metadata - - An embedded Mermaid diagram showing the topology that GitHub renders automatically + - An embedded topology image (PNG/SVG) generated by AI from the graph data -5. **Given** a graph exported to markdown, **When** viewed in GitHub, **Then** the Mermaid diagram renders as a visual flowchart following the Visual Design Guidelines (rounded rectangle nodes, GitHub-consistent color palette for diff states, distinct shapes for databases) and node labels include key properties (e.g., container image tag, port numbers). +5. **Given** a graph exported to markdown, **When** viewed in GitHub, **Then** the topology image renders as a visual flowchart following the Visual Design Guidelines (rounded rectangle nodes, GitHub-consistent color palette for diff states, distinct shapes for databases) and node labels include key properties (e.g., container image tag, port numbers). 6. **Given** two app graphs from different commits, **When** I diff them, **Then** the diff is computed from JSON (not Markdown), and added/removed/modified resources are clearly identified. @@ -214,7 +214,7 @@ This feature extends the existing `rad app graph` command with file-based input | `rad app graph app.bicep` | Bicep file (`.bicep` extension) | JSON to `.radius/app-graph.json` (default) | | `rad app graph app.bicep --stdout` | Bicep file + stdout flag | JSON to stdout (no file written) | | `rad app graph app.bicep -o custom.json` | Bicep file + custom output | JSON to specified file | -| `rad app graph app.bicep --format markdown` | Bicep file + markdown | JSON + Markdown to `.radius/` | +| `rad app graph app.bicep --format markdown` | Bicep file + markdown | JSON + Markdown + topology image to `.radius/` | | `rad app graph app.bicep --no-git` | Bicep file + no-git | JSON without git metadata (faster) | | `rad app graph app.bicep --at abc123` | Bicep file + commit | JSON at specific commit | | `rad app graph diff app.bicep --from abc123 --to def456` | Bicep file + commits | Diff computed from JSON, output as JSON or Markdown | @@ -223,8 +223,9 @@ This feature extends the existing `rad app graph` command with file-based input **Output Model**: - **JSON is canonical**: Always generated, serves as the single source of truth for all automation and diff operations -- **Markdown is additive**: When `--format markdown` is specified, Markdown is generated *in addition to* JSON as a human-readable preview -- **GitHub Action uses JSON**: Diff computation is always JSON-to-JSON; Markdown is purely a rendering/presentation layer for PR comments +- **Markdown is additive**: When `--format markdown` is specified, Markdown is generated *in addition to* JSON as a human-readable preview, with an embedded topology image +- **Images are visual output**: AI-generated PNG/SVG topology images are produced for Markdown previews and PR comments; images are a rendering layer, not a data format +- **GitHub Action uses JSON**: Diff computation is always JSON-to-JSON; images and Markdown are purely rendering/presentation layers for PR comments **Design Rationale**: Unifying under `rad app graph` provides: - Conceptual consistency: both are "app graphs" (prospective vs. deployed) @@ -236,7 +237,37 @@ This feature extends the existing `rad app graph` command with file-based input ## Visual Design Guidelines -All graph visualizations — Mermaid diagrams, PR comment diff tables, and Markdown output — MUST follow a visual style consistent with the **native GitHub UI** to feel integrated rather than bolted-on. +All graph visualizations — topology images, PR comment diff tables, and Markdown output — MUST follow a visual style consistent with the **native GitHub UI** to feel integrated rather than bolted-on. + +### Image-Based Visualization Approach + +Instead of relying on Mermaid diagrams rendered by GitHub's Markdown engine, this feature generates **image files** (PNG with SVG fallback) of the application topology. Images are generated by an **AI/Copilot-powered rendering pipeline** that takes the app graph JSON as input and produces polished, publication-quality topology diagrams. + +**Why images instead of Mermaid?** + +| Concern | Mermaid (Previous) | AI-Generated Images (Current) | +|---------|-------------------|------------------------------| +| **Visual quality** | Limited to Mermaid's built-in styles and layout engine | Full control over layout, typography, iconography, and styling | +| **Consistency** | Rendering varies across GitHub, VS Code, and other Markdown viewers | Pixel-perfect consistency everywhere images are displayed | +| **Customization** | Constrained by Mermaid syntax and capabilities | Unlimited — custom icons per resource type, branded styling, adaptive layouts | +| **Dark/Light theme** | Requires CSS variable workarounds; inconsistent across renderers | Generate separate light and dark theme variants (or a single theme-adaptive SVG) | +| **Complex layouts** | Mermaid struggles with large graphs (50+ nodes) | AI can optimize layout for readability regardless of graph size | +| **Rich annotations** | Limited label formatting | Rich node cards with icons, property badges, and status indicators | + +### AI/Copilot Image Generation + +The image generation pipeline uses AI (e.g., GitHub Copilot, an LLM-based renderer, or a dedicated graph visualization model) to transform app graph JSON into topology images: + +1. **Input**: App graph JSON (resources, connections, properties, diff metadata) +2. **Processing**: AI interprets the graph structure and applies the Visual Design Guidelines to produce an optimized layout +3. **Output**: PNG image file(s) written to `.radius/` directory or embedded in PR comments + +**Generation Modes**: +- **Single graph**: Produce one topology image showing the current application architecture +- **Diff view**: Produce a single annotated image (or side-by-side pair) highlighting added, removed, and modified resources +- **Theme variants**: Generate both light-theme and dark-theme versions, or a single SVG that adapts via `prefers-color-scheme` media query + +**Fallback Strategy**: If AI image generation is unavailable (e.g., no API access, rate limits), the system falls back to a deterministic graph layout engine (e.g., Graphviz/DOT) to ensure images are always produced. The fallback produces correct but less polished output. ### Color Palette (Diff States) @@ -247,22 +278,18 @@ All graph visualizations — Mermaid diagrams, PR comment diff tables, and Markd | **Modified** | Yellow (`#bf8700` / `--color-attention-fg`) | Changed properties or connections | Same as GitHub warning/attention | | **Unchanged** | Gray (`#656d76` / `--color-fg-muted`) | Context resources shown for topology reference | Same as GitHub muted text | -### Mermaid Diagram Styling - -- **Node shapes**: Rounded rectangles (`([...])`) for all resource nodes to match GitHub's card/box aesthetic - - Containers: rounded rectangle with container icon annotation - - Gateways: rounded rectangle with gateway/diamond icon annotation - - Databases: cylinder shape (Mermaid built-in `[(...)]`) -- **Node labels**: Resource name as primary label, key properties (e.g., image tag, port) as secondary text -- **Edges**: Solid lines for direct connections, dashed lines for implicit/inferred dependencies -- **Diff coloring**: Use Mermaid `classDef` styles to apply the color palette above: - ```mermaid - classDef added fill:#dafbe1,stroke:#2da44e,color:#1a7f37 - classDef removed fill:#ffebe9,stroke:#cf222e,color:#82071e - classDef modified fill:#fff8c5,stroke:#bf8700,color:#6a5600 - classDef unchanged fill:#f6f8fa,stroke:#d0d7de,color:#656d76 - ``` -- **Layout**: Top-to-bottom (`TB`) flow direction for application topology +### Image Styling + +- **Node shapes**: Rounded rectangles for all resource nodes to match GitHub's card/box aesthetic + - Containers: rounded rectangle with container icon + - Gateways: rounded rectangle with gateway/network icon + - Databases: cylinder shape with database icon +- **Node labels**: Resource name as primary label, key properties (e.g., image tag, port) as secondary text below +- **Node cards**: Each node rendered as a compact card with an icon badge for the resource type, name, and 1-2 key properties +- **Edges**: Solid lines for direct connections, dashed lines for implicit/inferred dependencies; arrowheads indicate direction +- **Diff annotations**: Color-coded borders and background fills per the color palette above; removed nodes shown with strikethrough or faded treatment +- **Layout**: Top-to-bottom flow for application topology, with AI-optimized placement to minimize edge crossings +- **Resolution**: Images generated at 2x resolution for crisp rendering on high-DPI displays ### PR Comment Styling @@ -270,17 +297,19 @@ All graph visualizations — Mermaid diagrams, PR comment diff tables, and Markd - `🟢 Added` — green circle for new resources - `🔴 Removed` — red circle for deleted resources - `🟡 Modified` — yellow circle for changed resources -- **Before/After diagrams**: Labeled with `### Before` and `### After` headings inside a collapsible `
` block to avoid overwhelming long PRs +- **Before/After images**: Labeled with `### Before` and `### After` headings inside a collapsible `
` block to avoid overwhelming long PRs. Images are embedded as `![App Graph](url)` references. - **Comment header**: Include a brief summary line (e.g., "App Graph: +2 resources, -1 resource, ~1 modified") so reviewers can assess impact at a glance without expanding details +- **Image hosting**: Images are uploaded as PR comment attachments (GitHub supports image uploads in issue/PR comments) or stored in a temporary artifact and referenced by URL - **Borders and containers**: Use GitHub's native `> [!NOTE]`, `> [!WARNING]`, and `> [!IMPORTANT]` callout syntax where appropriate for status messages ### General Principles -- Visual output MUST render correctly in both GitHub light and dark themes -- Avoid hardcoded colors that become illegible in dark mode — use GitHub's CSS variable equivalents as reference and test both themes +- Generated images MUST render correctly when viewed in both GitHub light and dark themes (generate theme variants or use adaptive SVG) - Prefer semantic color usage (green = added, red = removed) over decorative color - Keep visualizations compact — prioritize information density over whitespace -- All visual elements should feel like a native part of the GitHub PR review experience, not an external tool's output +- Images should feel like a native part of the GitHub PR review experience, not an external tool's output +- Image file size should be optimized (target < 500KB per image) to ensure fast loading in PR comments +- All images MUST include alt text describing the graph topology for accessibility --- @@ -297,7 +326,8 @@ myapp/ │ └── database.bicep └── .radius/ ├── app-graph.json # Canonical graph data (committed) - └── app-graph.md # Optional preview (if --format markdown) + ├── app-graph.md # Optional preview (if --format markdown) + └── app-graph.png # Optional topology image (if --format markdown) ``` ### Developer Workflow @@ -361,8 +391,9 @@ Since the GitHub Action generates graphs on-the-fly from Bicep source, there is - **FR-003**: System MUST extract connection relationships from resource properties (connections, routes, ports) - **FR-004**: System MUST produce deterministic output (same input = byte-identical output) - **FR-005**: System MUST always generate JSON output as the canonical data format with stable key ordering for deterministic diffs -- **FR-006**: System MUST support Markdown output as an **additive** preview format (generated alongside JSON when `--format markdown` is specified), containing a resource table and embedded Mermaid diagram -- **FR-007**: System MUST perform all diff computations using JSON data, with Markdown used only as a rendering layer for human consumption +- **FR-006**: System MUST support Markdown output as an **additive** preview format (generated alongside JSON when `--format markdown` is specified), containing a resource table and an embedded topology image (PNG/SVG) generated by AI from the graph data +- **FR-006a**: System MUST provide an AI/Copilot-powered image generation pipeline that transforms app graph JSON into topology diagram images (PNG with SVG fallback), with a deterministic fallback renderer (e.g., Graphviz/DOT) when AI generation is unavailable +- **FR-007**: System MUST perform all diff computations using JSON data, with images and Markdown used only as a rendering/presentation layer for human consumption - **FR-008**: System MUST enrich graph nodes with git metadata (commit SHA, author, date, message) by default when in a git repository, with `--no-git` flag to disable - **FR-009**: System MUST track which Bicep file(s) define each resource for git blame integration - **FR-010**: System MUST write graph output to `.radius/app-graph.json` by default (relative to Bicep file location), with `--stdout` flag for stdout output and `-o` flag for custom path @@ -384,7 +415,8 @@ Since the GitHub Action generates graphs on-the-fly from Bicep source, there is - **NFR-003**: Feature MUST NOT require changes to existing deployment workflows (Constitution Principle IX - Incremental Adoption) - **NFR-004**: CLI commands MUST follow existing `rad` CLI patterns and conventions - **NFR-005**: Error messages MUST be actionable with clear guidance for resolution (Constitution Principle VI) -- **NFR-006**: All visual output (Mermaid diagrams, PR comment tables, Markdown previews) MUST follow a GitHub-native visual style — rounded corners, GitHub's diff color palette (green/red/yellow), and correct rendering in both light and dark themes +- **NFR-006**: All visual output (AI-generated topology images, PR comment tables, Markdown previews) MUST follow a GitHub-native visual style — rounded corners, GitHub's diff color palette (green/red/yellow), and correct rendering in both light and dark themes +- **NFR-007**: Generated images MUST be optimized for file size (target < 500KB per image) and include descriptive alt text for accessibility ### Key Entities @@ -439,7 +471,7 @@ Since the GitHub Action generates graphs on-the-fly from Bicep source, there is - **SC-002**: Generated graphs are 100% deterministic (identical input produces byte-identical output) - **SC-003**: Graph diff correctly identifies all added, removed, and modified resources with zero false positives - **SC-004**: GitHub Action posts PR comments within 60 seconds of workflow trigger -- **SC-005**: Markdown output (including embedded Mermaid diagram) renders correctly in GitHub in both light and dark themes, following the Visual Design Guidelines for color and layout +- **SC-005**: Generated topology images render correctly when embedded in GitHub Markdown and PR comments, with both light and dark theme variants available, following the Visual Design Guidelines for color and layout - **SC-006**: Git enrichment adds < 2 seconds overhead for repositories with up to 1000 commits - **SC-007**: System handles Bicep files up to 5000 lines without performance degradation - **SC-008**: Error messages include actionable guidance in 100% of failure cases @@ -453,7 +485,9 @@ This feature MUST include comprehensive testing across the testing pyramid: ### Unit Tests - Test individual graph parsing functions in isolation - Test git metadata extraction logic -- Test output formatters (JSON, Markdown with embedded Mermaid) with known inputs +- Test output formatters (JSON, Markdown with embedded topology images) with known inputs +- Test AI image generation pipeline with known graph inputs and verify output image structure +- Test fallback to deterministic renderer (Graphviz/DOT) when AI generation is unavailable - Test error handling for malformed Bicep files - All unit tests runnable with `make test` without external dependencies @@ -477,7 +511,7 @@ This feature MUST include comprehensive testing across the testing pyramid: 3. ~~**GitHub App vs Action**: Should the PR integration be a GitHub Action (user-managed) or a GitHub App (centrally managed)? Trade-off: flexibility vs. ease of setup.~~ **RESOLVED**: GitHub Action. Fork-friendly, no installation approval required, aligns with existing Radius workflow patterns, supports incremental adoption. -4. ~~**Diff Visualization**: What's the preferred format for showing diffs in PR comments—table-based, Mermaid side-by-side, or unified text diff?~~ **RESOLVED**: Table + Mermaid diagrams. Change table shows added/removed/modified resources with details; before/after Mermaid diagrams provide visual topology comparison. Both render natively in GitHub. +4. ~~**Diff Visualization**: What's the preferred format for showing diffs in PR comments—table-based, Mermaid side-by-side, or unified text diff?~~ **RESOLVED**: Table + AI-generated topology images. Change table shows added/removed/modified resources with details; before/after topology images provide visual comparison. Images are uploaded as PR comment attachments. 5. ~~**Parameter Handling**: How should we handle Bicep parameters without a params file—use defaults, require params, or mark as "unknown"?~~ **RESOLVED**: Require params file. If Bicep has required parameters (no defaults) but no `--parameters` flag is provided, fail with a clear error message listing the missing parameters. From c76997a5c38c25047107bbf8563800e13e2b527b Mon Sep 17 00:00:00 2001 From: Will Tsai <28876888+willtsai@users.noreply.github.com> Date: Mon, 23 Feb 2026 13:59:36 -0800 Subject: [PATCH 15/15] modify spec: add user story for auto-gen app graph in README to keep architecture diagram up to date Signed-off-by: Will Tsai <28876888+willtsai@users.noreply.github.com> --- specs/001-git-app-graph-preview/spec.md | 52 ++++++++++++++++++++++++- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/specs/001-git-app-graph-preview/spec.md b/specs/001-git-app-graph-preview/spec.md index 73961f7c..eafb52a4 100644 --- a/specs/001-git-app-graph-preview/spec.md +++ b/specs/001-git-app-graph-preview/spec.md @@ -113,10 +113,12 @@ As a PR reviewer, I want to see a visual diff of the app graph in PR comments, s **Operational Model**: The GitHub Action **generates the app graph automatically** as part of the workflow — developers do NOT need to run `rad app graph app.bicep` manually or commit graph artifacts. The Action checks out the PR head and base branches, runs `rad app graph app.bicep` on each, computes the diff, and posts the result as a PR comment. This keeps the developer experience friction-free at the cost of requiring Bicep/Radius tooling in CI. -**Trigger Events**: The Action supports two trigger modes: -- **`pull_request`**: Posts diff comments on PRs when `.radius/app-graph.json` changes +**Trigger Events**: This workflow focuses on **PR review**: +- **`pull_request`**: Posts diff comments on PRs when Bicep files change - **`push` to main/default branch**: Updates baseline tracking for historical comparison +> **Note**: A separate workflow handles updating the README with an architecture diagram on merge to `main` — see User Story 7. + **Monorepo Support**: The Action auto-detects all Bicep entry points (e.g., `**/app.bicep`) in the repository. Each application graph is generated and diffed independently, with separate PR comment sections per application. **Independent Test**: Create a PR with Bicep changes and updated graph JSON, verify the action posts a comment showing before/after graph comparison. @@ -179,6 +181,38 @@ As a platform engineer, I want to see how abstract Radius types resolve to concr --- +### User Story 7 - README Architecture Diagram (Priority: P1) + +As a **repository maintainer**, I want the repo's README to always display an up-to-date architecture diagram of my application, so that anyone visiting the repo can immediately understand the application topology without reading Bicep files. + +**Why this priority**: The README is the first thing contributors and users see when visiting a repository. An always-current architecture diagram provides immediate, high-visibility value and is a primary motivator for adopting app graph generation. Depends on P1 graph generation and image rendering being stable. + +**Operational Model**: A **separate GitHub Actions workflow** triggers on every push to the `main` (or default) branch. It generates the app graph from the Bicep entry points on `main`, produces a topology image, and commits it back to the repository. The workflow then updates the README to embed the image under an `## Architecture` heading near the top of the file. If the heading doesn't exist, the workflow inserts it after the first heading (assumed to be the project title). If the generated graph is identical to the previously committed image, the workflow skips the commit to avoid unnecessary noise. + +**Trigger Events**: +- **`push` to `main`/default branch**: Runs on every merge to `main` (i.e., when a PR is merged or a direct push occurs) +- **`workflow_dispatch`**: Supports manual trigger for on-demand regeneration + +**Monorepo Support**: For repositories with multiple Bicep entry points, the workflow generates a combined architecture section with one image per application, each labeled with its path (e.g., `apps/frontend/`, `apps/backend/`). + +**Independent Test**: Merge a PR with Bicep changes to `main`, verify the workflow runs and the README is updated with an architecture image reflecting the current graph. + +**Acceptance Scenarios**: + +1. **Given** a repository with an `app.bicep` file and a README without an Architecture section, **When** a PR is merged to `main`, **Then** the workflow generates the app graph, produces a topology image, inserts an `## Architecture` heading after the project title in the README, embeds the image under that heading, and commits the changes to `main`. + +2. **Given** a repository whose README already has an `## Architecture` section with a previous topology image, **When** a PR with Bicep changes is merged to `main`, **Then** the workflow regenerates the image, replaces the existing image reference under the Architecture heading, and commits the update. + +3. **Given** a merge to `main` that does NOT change any Bicep files (or where changes produce an identical graph), **When** the workflow runs, **Then** it detects no visual change, skips the commit, and logs "Architecture diagram is already up-to-date." + +4. **Given** a monorepo with `apps/frontend/app.bicep` and `apps/backend/app.bicep`, **When** a merge to `main` changes the frontend Bicep, **Then** the workflow regenerates only the affected image, updates the README's Architecture section with per-application subsections, and commits. + +5. **Given** the workflow is triggered manually via `workflow_dispatch`, **When** it runs, **Then** it regenerates the architecture image(s) and updates the README regardless of whether Bicep files changed. + +6. **Given** the AI image generation pipeline is unavailable, **When** the workflow runs, **Then** it falls back to the deterministic renderer (Graphviz/DOT), produces the image, and updates the README with a note indicating the image was generated with the fallback renderer. + +--- + ### Edge Cases - What happens when Bicep file references resources outside the current file/module that cannot be resolved? @@ -200,6 +234,12 @@ As a platform engineer, I want to see how abstract Radius types resolve to concr - **Static graph shows abstract types**: The declared `Radius.Data/store` is shown, not the resolved infrastructure (PostgreSQL, CosmosDB, etc.) - This is intentional—the static graph represents the **portable application architecture** independent of environment-specific recipe resolution - For environment-resolved views, see User Story 6 (P3) +- What happens if the README has an unexpected structure (no headings, or `## Architecture` already used for different content)? + - The workflow uses marker comments (`` / ``) to identify its managed section. If markers exist, content between them is replaced. If not, the workflow inserts markers + content after the first `#` heading. This avoids overwriting user-authored Architecture sections not managed by the workflow. +- What happens if the workflow commit conflicts with a concurrent push to `main`? + - The workflow pulls latest `main` before committing. If a conflict occurs, it retries once after rebasing. If still conflicting, it logs a warning and skips the commit (the next merge will trigger a fresh run). +- What happens if the generated image is too large for the README (e.g., 100+ resource graph)? + - The workflow generates both a full-size image and a simplified summary image (key resources only). The README embeds the summary with a link to the full-size image in `.radius/`. --- @@ -407,6 +447,9 @@ Since the GitHub Action generates graphs on-the-fly from Bicep source, there is - **FR-018**: System MUST work with Bicep files targeting any cloud provider (multi-cloud neutrality per Constitution Principle III) - **FR-019**: System MUST be compatible with the Radius Bicep extension type definitions - **FR-020**: System MUST extract and include resource-specific properties (e.g., container image, ports, hostnames, database names) in each graph node so that visualizations can display them as node labels or detail annotations. Secret values MUST NOT be captured. +- **FR-021**: System MUST provide a GitHub Actions workflow that triggers on push to the default branch (`main`), generates the app graph from Bicep entry points, produces a topology image, and commits it to the repository with an updated README embedding the image under an `## Architecture` heading +- **FR-022**: The README update workflow MUST use HTML marker comments (`` / ``) to identify the managed section, and MUST skip the commit if the generated graph is identical to the existing one +- **FR-023**: The README update workflow MUST support `workflow_dispatch` for manual on-demand regeneration ### Non-Functional Requirements @@ -475,6 +518,7 @@ Since the GitHub Action generates graphs on-the-fly from Bicep source, there is - **SC-006**: Git enrichment adds < 2 seconds overhead for repositories with up to 1000 commits - **SC-007**: System handles Bicep files up to 5000 lines without performance degradation - **SC-008**: Error messages include actionable guidance in 100% of failure cases +- **SC-009**: README update workflow commits the architecture image to the repo within 90 seconds of a merge to `main`, and skips the commit when no graph changes are detected --- @@ -500,6 +544,10 @@ This feature MUST include comprehensive testing across the testing pyramid: - End-to-end test: Bicep file → graph generation → output validation - Test GitHub Action in a real PR workflow - Test graph diff accuracy with known before/after states +- Test README update workflow: merge Bicep changes to `main`, verify README is updated with architecture image +- Test README update workflow skip: merge non-Bicep changes, verify no README commit is made +- Test README marker insertion: verify workflow correctly inserts markers when README has no Architecture section +- Test README marker replacement: verify workflow replaces content between existing markers without affecting surrounding content ---