diff --git a/CHANGELOG.md b/CHANGELOG.md index c5a7eacfc..627a0d3ed 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,19 @@ Format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ## [Unreleased](https://github.com/gsd-build/get-shit-done/compare/v1.37.1...HEAD) +### Enhancement: richer architecture docs from `/gsd-map-codebase` (#2500) + +`/gsd-map-codebase` (arch focus) now produces a `.planning/codebase/ARCHITECTURE.md` with the same richness as the research version created at project creation: + +- **ASCII system overview diagram** — component boxes and request-flow arrows, generated from actual codebase analysis +- **Component responsibility table** — Component / Responsibility / File columns for at-a-glance orientation +- **Data flow traces** — Primary request path and secondary flows with numbered steps and code references (`file:line`) +- **Architectural constraints** — Threading model, global state inventory, circular import chains +- **Anti-patterns** — Codebase-specific patterns to avoid, with the correct alternative +- **``** marker at the top so users can see when the doc was last generated + +Running `/gsd-map-codebase` or `/gsd-scan --focus arch` after a major refactor now produces an up-to-date architectural reference that includes the visual diagrams previously only available in the (non-refreshable) research version. + ### SDK query layer — Phase 3 (what you get) If you use GSD **as a workflow**—milestones, phases, `.planning/` artifacts, bundled workflows, and `**/gsd:`** commands—Phase 3 is about **behavior matching what the docs and steps promise**, and **a bit less overhead** when the framework advances a phase or bootstraps a new project for you. diff --git a/agents/gsd-codebase-mapper.md b/agents/gsd-codebase-mapper.md index e747a37a5..c507f49c1 100644 --- a/agents/gsd-codebase-mapper.md +++ b/agents/gsd-codebase-mapper.md @@ -339,10 +339,42 @@ Ready for orchestrator summary. ## ARCHITECTURE.md Template (arch focus) ```markdown + # Architecture **Analysis Date:** [YYYY-MM-DD] +## System Overview + +```text +┌─────────────────────────────────────────────────────────────┐ +│ [Top Layer Name] │ +├──────────────────┬──────────────────┬───────────────────────┤ +│ [Component A] │ [Component B] │ [Component C] │ +│ `[path/to/a]` │ `[path/to/b]` │ `[path/to/c]` │ +└────────┬─────────┴────────┬─────────┴──────────┬────────────┘ + │ │ │ + ▼ ▼ ▼ +┌─────────────────────────────────────────────────────────────┐ +│ [Middle Layer Name] │ +│ `[path/to/layer]` │ +└─────────────────────────────────────────────────────────────┘ + │ + ▼ +┌─────────────────────────────────────────────────────────────┐ +│ [Store / Output / External] │ +│ `[path/to/store]` │ +└─────────────────────────────────────────────────────────────┘ +``` + +## Component Responsibilities + +| Component | Responsibility | File | +|-----------|----------------|------| +| [Name] | [What it owns] | `[path]` | +| [Name] | [What it owns] | `[path]` | +| [Name] | [What it owns] | `[path]` | + ## Pattern Overview **Overall:** [Pattern name] @@ -363,7 +395,13 @@ Ready for orchestrator summary. ## Data Flow -**[Flow Name]:** +### Primary Request Path + +1. [Step 1 — entry point] (`[file:line]`) +2. [Step 2 — processing] (`[file:line]`) +3. [Step 3 — output/response] (`[file:line]`) + +### [Secondary Flow Name] 1. [Step 1] 2. [Step 2] @@ -386,6 +424,27 @@ Ready for orchestrator summary. - Triggers: [What invokes it] - Responsibilities: [What it does] +## Architectural Constraints + +- **Threading:** [Threading model — e.g., single-threaded event loop, worker threads used for X] +- **Global state:** [Any module-level singletons or shared mutable state — list files] +- **Circular imports:** [Known circular dependency chains, if any] +- **[Other constraint]:** [Description] + +## Anti-Patterns + +### [Anti-Pattern Name] + +**What happens:** [The incorrect pattern observed in this codebase] +**Why it's wrong:** [The problem it causes here] +**Do this instead:** [The correct pattern with file reference] + +### [Anti-Pattern Name] + +**What happens:** [The incorrect pattern observed in this codebase] +**Why it's wrong:** [The problem it causes here] +**Do this instead:** [The correct pattern with file reference] + ## Error Handling **Strategy:** [Approach] diff --git a/tests/enh-2500-codebase-mapper-arch-rich-format.test.cjs b/tests/enh-2500-codebase-mapper-arch-rich-format.test.cjs new file mode 100644 index 000000000..fb35ae2b7 --- /dev/null +++ b/tests/enh-2500-codebase-mapper-arch-rich-format.test.cjs @@ -0,0 +1,132 @@ +/** + * Enhancement #2500: gsd-codebase-mapper (arch focus) rich architecture output + * + * The codebase/ARCHITECTURE.md produced by gsd-codebase-mapper was a sparse + * structural inventory — file listings and module relationships. After a major + * refactor, research/ARCHITECTURE.md (created at /gsd-new-project) goes stale + * with no refresh command. This enhancement enriches the codebase mapper's + * arch-focus template to match the richness of the research version: + * - ASCII system overview diagram + * - Data flow traces with numbered steps and code references + * - Component responsibility table (component → responsibility → file) + * - Critical architectural constraints + * - Anti-patterns specific to the codebase + * - marker at top (maintainer request) + * + * The agent's template text IS what the runtime executes, so testing + * the template content directly tests the deployed contract. + */ + +'use strict'; + +// allow-test-rule: source-text-is-the-product +// The gsd-codebase-mapper ARCHITECTURE.md template is the instruction set +// executed by the LLM at runtime. Testing its text content tests whether the +// deployed agent will produce rich architecture docs as required by #2500. + +const { describe, test, before } = require('node:test'); +const assert = require('node:assert/strict'); +const fs = require('fs'); +const path = require('path'); + +const AGENT_PATH = path.join(__dirname, '..', 'agents', 'gsd-codebase-mapper.md'); + +describe('enh-2500: gsd-codebase-mapper arch focus — rich architecture output', () => { + let agentContent; + let archTemplate; + + before(() => { + assert.ok(fs.existsSync(AGENT_PATH), 'agents/gsd-codebase-mapper.md must exist'); + agentContent = fs.readFileSync(AGENT_PATH, 'utf-8'); + + // Isolate the ARCHITECTURE.md template section from the agent file. + // End boundary is the STRUCTURE.md Template heading that immediately follows it. + const archStart = agentContent.indexOf('## ARCHITECTURE.md Template (arch focus)'); + assert.ok(archStart !== -1, 'agent must contain an ARCHITECTURE.md Template (arch focus) section'); + + const archEnd = agentContent.indexOf('## STRUCTURE.md Template (arch focus)', archStart + 1); + archTemplate = archEnd !== -1 + ? agentContent.slice(archStart, archEnd) + : agentContent.slice(archStart); + }); + + test('template includes a refreshed date marker', () => { + assert.ok( + archTemplate.includes(' marker so users can see when the doc was last generated (#2500 maintainer requirement)' + ); + }); + + test('template includes an ASCII system overview diagram', () => { + // ASCII diagrams use box-drawing characters or at minimum ┌/└/│/─ or +/|/- + const hasAsciiDiagram = + archTemplate.includes('┌') || + archTemplate.includes('└') || + archTemplate.includes('│') || + archTemplate.includes('+--') || + archTemplate.includes('+-') || + archTemplate.includes('→') || + archTemplate.includes('↓') || + archTemplate.includes('↑'); + + assert.ok( + hasAsciiDiagram, + 'ARCHITECTURE.md template must include an ASCII system overview diagram (box-drawing characters or flow arrows) as required by #2500' + ); + }); + + test('template includes System Overview section header', () => { + assert.ok( + archTemplate.includes('System Overview') || archTemplate.includes('system overview'), + 'ARCHITECTURE.md template must include a "System Overview" section for the ASCII diagram (#2500)' + ); + }); + + test('template includes a component responsibility table with required columns', () => { + // Must have a markdown table with component, responsibility, and file columns + const hasComponentCol = + archTemplate.includes('Component') || archTemplate.includes('component'); + const hasResponsibilityCol = + archTemplate.includes('Responsibility') || archTemplate.includes('responsibility'); + const hasFileCol = + archTemplate.includes('File') || archTemplate.includes('file'); + + assert.ok( + hasComponentCol && hasResponsibilityCol && hasFileCol, + 'ARCHITECTURE.md template must include a component responsibility table with Component, Responsibility, and File columns (#2500)' + ); + }); + + test('template includes data flow traces with numbered steps', () => { + const hasPrimaryRequestPath = /###\s+Primary Request Path/i.test(archTemplate); + const hasThreeNumberedSteps = /^\s*1\..+\n\s*2\..+\n\s*3\./m.test(archTemplate); + const hasFileLineRefs = /\(`\[.*:(?:line|\d+)\]`\)/.test(archTemplate); + + assert.ok( + hasPrimaryRequestPath && hasThreeNumberedSteps && hasFileLineRefs, + 'ARCHITECTURE.md template must include a "Primary Request Path" section with numbered steps and file:line references (#2500)' + ); + }); + + test('template includes architectural constraints section', () => { + const hasConstraints = + /##\s+Architectural Constraints/i.test(archTemplate) && + /\bThreading\b/.test(archTemplate) && + /\bGlobal state\b/i.test(archTemplate) && + /\bCircular imports\b/i.test(archTemplate); + + assert.ok( + hasConstraints, + 'ARCHITECTURE.md template must include an "Architectural Constraints" section with Threading, Global state, and Circular imports categories (#2500)' + ); + }); + + test('template includes anti-patterns section', () => { + assert.ok( + archTemplate.includes('Anti-pattern') || + archTemplate.includes('Anti-Pattern') || + archTemplate.includes('anti-pattern'), + 'ARCHITECTURE.md template must include an anti-patterns section specific to the codebase (#2500)' + ); + }); +});