diff --git a/.specify/memory/constitution.md b/.specify/memory/constitution.md index 190416bc..e77d24f3 100644 --- a/.specify/memory/constitution.md +++ b/.specify/memory/constitution.md @@ -1,6 +1,6 @@ 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' + push: + branches: + - main + 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..b25cdc60 --- /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**: ✅ 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/spec.md b/specs/001-git-app-graph-preview/spec.md new file mode 100644 index 00000000..eafb52a4 --- /dev/null +++ b/specs/001-git-app-graph-preview/spec.md @@ -0,0 +1,578 @@ +# Feature Specification: Git App Graph Preview + +**Feature Branch**: `001-git-app-graph-preview` +**Created**: January 30, 2026 +**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 + 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 + +## Problem Statement + +Radius currently generates application graphs only after deployment, which means: +1. Developers cannot preview the app graph structure before deploying +2. There's no way to track how the application architecture evolves over time +3. PR reviewers cannot see the impact of Bicep changes on the overall application topology +4. No mechanism exists to compare app graphs across commits or branches + +This feature introduces **static app graph generation** from Bicep files and **git-aware graph versioning** to enable visualization and diffing in GitHub workflows. + +## User Scenarios & Testing *(mandatory)* + +### User Story 1 - Generate App Graph from Bicep Files (Priority: P1) + +As a **developer**, I want to generate an app graph from my `app.bicep` file without deploying, so I can preview the application topology and validate my changes locally. + +As a **platform engineer**, I want to review app graph changes in PRs, so I can ensure architectural changes align with organizational standards before deployment. + +**Why this priority**: This is the foundational capability. Without static graph generation, no other features can function. It delivers immediate value by enabling local validation. + +**Independent Test**: Can be fully tested by running a CLI command against a Bicep file and verifying the graph output matches expected structure. + +**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, 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. + +3. **Given** a Bicep file referencing external modules, **When** I run `rad app graph app.bicep`, **Then** the graph includes resources from all referenced modules with proper dependency tracking. + +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 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. + +--- + +### User Story 2 - Export Graph as Diff-Friendly Format (Priority: P1) + +As a developer, I want the app graph exported in a deterministic, diff-friendly format, so I can commit it to version control and see meaningful diffs when it changes. + +**Why this priority**: Critical for enabling GitHub integration. Without a stable, diffable format, PR visualization is impossible. + +**Independent Test**: Generate graph twice from identical Bicep, verify outputs are byte-identical. Modify Bicep, regenerate, verify diff shows only the changed elements. + +**Output Model**: JSON is the canonical data format, always generated. Markdown is a rendered preview of the JSON data, generated additively when requested. + +**Acceptance Scenarios**: + +1. **Given** an app graph, **When** I export it, **Then** the JSON output is deterministically sorted (alphabetical by resource ID) producing identical output for identical inputs. + +2. **Given** an app graph, **When** I run `rad app graph app.bicep`, **Then** JSON is written to `.radius/app-graph.json` (default location) serving as the single source of truth for all automation and diff operations. + +3. **Given** an app graph, **When** I run `rad app graph app.bicep --stdout`, **Then** JSON is written to stdout instead of a file. + +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 topology image (PNG/SVG) generated by AI from the graph data + +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. + +--- + +### User Story 3 - Git Metadata Enrichment (Priority: P2) + +As a developer, I want the app graph to automatically include git commit information, so I can track when and why each resource was added or modified. + +**Why this priority**: Builds on P1 capabilities to enable historical tracking. Valuable but not blocking core functionality. + +**Independent Test**: Generate graph from a Bicep file in a git repository, verify each resource includes commit SHA, author, and timestamp of last modification by default. + +**Acceptance Scenarios**: + +1. **Given** a Bicep file in a git repository, **When** I run `rad app graph app.bicep`, **Then** each resource automatically includes the commit SHA, author, date, and message of its last modification. + +2. **Given** a resource defined across multiple Bicep files, **When** I generate the graph, **Then** the resource shows the most recent commit that affected any of its defining files. + +3. **Given** a newly added resource not yet committed, **When** I generate the graph, **Then** the resource is marked as "uncommitted" with the current working directory state. + +4. **Given** a graph with git metadata, **When** I export to markdown, **Then** each resource row includes a linked commit SHA (e.g., `[abc123](../../commit/abc123)`). + +5. **Given** a Bicep file in a git repository, **When** I run `rad app graph app.bicep --no-git`, **Then** the graph is generated without git metadata for faster execution. + +6. **Given** a Bicep file outside a git repository, **When** I run `rad app graph app.bicep`, **Then** the graph is generated successfully with git fields marked as "not available". + +--- + +### User Story 4 - GitHub Action for PR Graph Diff (Priority: P2) + +As a PR reviewer, I want to see a visual diff of the app graph in PR comments, so I can understand the architectural impact of code changes without deploying. + +**Why this priority**: High-value GitHub integration, but depends on P1 capabilities being stable. + +**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**: 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. + +**Acceptance Scenarios**: + +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 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 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/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. + +--- + +### User Story 5 - Historical Graph Timeline (Priority: P3) + +As a developer, I want to view how my app graph evolved across commits, so I can understand architectural decisions and identify when changes were introduced. + +**Why this priority**: Advanced feature for historical analysis. Valuable for debugging and auditing but not essential for core workflow. + +**Independent Test**: Generate timeline for last 10 commits, verify each entry shows the graph state and changes from previous commit. + +**Acceptance Scenarios**: + +1. **Given** a git repository with multiple commits affecting Bicep files, **When** I run `rad app graph history app.bicep --commits 10`, **Then** I receive a timeline showing graph snapshots at each commit with change summaries. + +2. **Given** a specific commit SHA, **When** I run `rad app graph app.bicep --at abc123`, **Then** I receive the app graph as it existed at that commit. + +3. **Given** two commit SHAs, **When** I run `rad app graph diff app.bicep --from abc123 --to def456`, **Then** I receive a detailed diff showing all graph changes between those commits. + +--- + +### User Story 6 - Environment-Resolved Graph (Priority: P3) + +As a platform engineer, I want to see how abstract Radius types resolve to concrete infrastructure in a specific environment, so I can understand the actual resources that will be deployed. + +**Why this priority**: Advanced feature for environment-specific analysis. The static graph (showing portable types) serves most PR review needs; resolved views are valuable for deployment planning and troubleshooting. + +**Background**: Radius portable types like `Radius.Data/store` resolve differently depending on the environment's recipe configuration: +- Environment → RecipePack → Recipe → Concrete Resource +- The same `Radius.Data/store` might become PostgreSQL in `dev` and CosmosDB in `prod` + +**Independent Test**: Generate resolved graph for an environment with known recipe bindings, verify concrete resource types appear instead of abstract Radius types. + +**Acceptance Scenarios**: + +1. **Given** a Bicep file with `Radius.Data/store` and a connected Radius environment with PostgreSQL recipes, **When** I run `rad app graph app.bicep --environment prod`, **Then** the graph shows the resolved `Azure.DBforPostgreSQL/flexibleServers` (or equivalent) instead of the abstract `Radius.Data/store`. + +2. **Given** a Bicep file with portable types, **When** I run `rad app graph app.bicep --environment dev` and `rad app graph app.bicep --environment prod`, **Then** I can compare how the same application resolves to different infrastructure across environments. + +3. **Given** an environment where a recipe is not configured for a portable type, **When** I run `rad app graph app.bicep --environment prod`, **Then** the graph shows the abstract type with an annotation indicating "no recipe bound". + +4. **Given** a Bicep file, **When** I run `rad app graph app.bicep` (no `--environment` flag), **Then** the graph shows the abstract portable types (default behavior unchanged). + +--- + +### 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? + - Generate partial graph with unresolved references marked as "external" placeholders +- How does the system handle circular dependencies in Bicep? + - Detect and report cycles with clear error messaging; still generate graph with cycle annotation +- What happens when git history is shallow (e.g., `--depth 1` clone)? + - Gracefully degrade: use available history, mark resources as "history unavailable" when git blame fails +- How does the system handle large graphs (100+ resources)? + - Paginate CLI output, provide `--filter` options, optimize JSON/Markdown output for size +- What happens when Bicep uses runtime expressions that can't be statically resolved? + - 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 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 + - 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/`. + +--- + +## CLI Design + +This feature extends the existing `rad app graph` command with file-based input for static graph generation. The command intelligently distinguishes between deployed apps and Bicep files based on the argument: + +| Command | Input Type | Output | +|---------|------------|--------| +| `rad app graph myapp` | App name | Deployed app graph (existing behavior) | +| `rad app graph myapp -e prod` | App name + environment | Deployed graph in specific environment | +| `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 + 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 | +| `rad app graph history app.bicep --commits 10` | Bicep file + count | Historical timeline | +| `rad app graph app.bicep --environment prod` | Bicep file + environment | JSON with resolved recipe types | + +**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, 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) +- Discoverability: all graph functionality in one place +- Intuitive disambiguation: `.bicep` extension clearly indicates file input +- Alignment with existing `rad app graph ` pattern + +--- + +## Visual Design Guidelines + +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) + +| 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 | + +### 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 + +- **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 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 + +- 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 +- 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 + +--- + +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 + +Locally, `rad app graph app.bicep` writes to `.radius/app-graph.json` relative to the Bicep file's directory: + +``` +myapp/ +├── app.bicep +├── modules/ +│ └── database.bicep +└── .radius/ + ├── app-graph.json # Canonical graph data (committed) + ├── app-graph.md # Optional preview (if --format markdown) + └── app-graph.png # Optional topology image (if --format markdown) +``` + +### Developer Workflow + +```bash +# 1. Make changes to Bicep files +vim 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 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 generates graphs and posts diff automatically +git push +``` + +**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 | +|---------|-------------| +| **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 | + +### Graph Metadata + +Generated graphs include metadata for traceability and debugging: + +```json +{ + "metadata": { + "generatedAt": "2026-01-30T10:15:00Z", + "sourceFiles": ["app.bicep", "modules/database.bicep"], + "sourceHash": "sha256:abc123...", + "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)* + +### Functional Requirements + +- **FR-001**: System MUST parse Bicep files and extract resource definitions without requiring deployment +- **FR-002**: System MUST resolve module references and build a complete graph across multiple Bicep files +- **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 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 +- **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 +- **FR-016**: System MUST report clear errors for invalid Bicep syntax with file/line/column information +- **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. +- **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 + +- **NFR-001**: All Go code MUST follow Effective Go patterns and pass `golangci-lint` (Constitution Principle II) +- **NFR-002**: All exported Go packages, types, and functions MUST have godoc comments (Constitution Code Quality Standards) +- **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 (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 + +- **AppGraph**: Root container holding all resources, connections, and metadata for a single application + - Resources: Collection of AppGraphResource nodes + - Metadata: Git commit info, generation timestamp, source files + +- **AppGraphResource**: Single resource node in the graph + - 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 + - Direction: Outbound/Inbound + - Type: Connection type (connection, route, port binding) + +- **GraphDiff**: Comparison result between two graphs + - AddedResources: Resources present in new but not old + - RemovedResources: Resources present in old but not new + - ModifiedResources: Resources with changed properties/connections + - AddedConnections: New edges + - RemovedConnections: Removed edges + +--- + +## Success Criteria *(mandatory)* + +### Measurable Outcomes + +- **SC-001**: Graph generation completes in < 5 seconds for applications with up to 50 resources +- **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**: 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 +- **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 + +--- + +## Testing Requirements *(per Constitution Principle IV)* + +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 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 + +### Integration Tests +- Test Bicep CLI integration for file parsing +- Test git operations (blame, log) with real git repositories +- Test module resolution across multiple Bicep files + +### Functional Tests +- 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 + +--- + +## Open Questions + +1. **Bicep Compiler Integration**: Should we use the official Bicep CLI for parsing, or implement a lightweight parser? Trade-off: accuracy vs. dependency management. **Recommendation**: Use official Bicep CLI per Constitution Principle VII (Simplicity Over Cleverness). + +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.~~ **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 + 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. + +--- + +## Cross-Repository Impact *(per Constitution Principle XVII)* + +This feature may affect multiple Radius repositories: + +| Repository | Impact | +|------------|--------| +| `radius` | CLI implementation (`pkg/cli/cmd/app/graph/`), core graph logic | +| `docs` | User documentation for new CLI commands, GitHub Action setup guide | +| `design-notes` | This specification and implementation plan | + +Coordinate changes across repositories per Constitution guidance on polyglot project coherence. \ No newline at end of file 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)