From 2af8a0d508eb0c91be5ba15812562758563404c1 Mon Sep 17 00:00:00 2001 From: Lasha Efremidze Date: Mon, 9 Feb 2026 13:12:49 -0800 Subject: [PATCH] remapped --- .planning/codebase/ARCHITECTURE.md | 237 +++++++++-------------- .planning/codebase/CONCERNS.md | 278 ++++++++++----------------- .planning/codebase/CONVENTIONS.md | 125 +++--------- .planning/codebase/INTEGRATIONS.md | 165 +++------------- .planning/codebase/STACK.md | 92 +++------ .planning/codebase/STRUCTURE.md | 294 +++++++++-------------------- .planning/codebase/TESTING.md | 241 ++++------------------- 7 files changed, 396 insertions(+), 1036 deletions(-) diff --git a/.planning/codebase/ARCHITECTURE.md b/.planning/codebase/ARCHITECTURE.md index 2a941e0..455ef6b 100644 --- a/.planning/codebase/ARCHITECTURE.md +++ b/.planning/codebase/ARCHITECTURE.md @@ -1,183 +1,122 @@ # Architecture -**Analysis Date:** 2026-01-23 +**Analysis Date:** 2026-02-09 ## Pattern Overview -**Overall:** Layered pipeline architecture with stage-based processing and LLM integration. +**Overall:** Deterministic, schema-validated stage pipeline orchestrated by a CLI with pluggable LLM runtimes. **Key Characteristics:** -- Sequential phase/stage execution with context passing -- Schema-driven validation at stage boundaries -- Pluggable LLM runtime providers (OpenAI, Copilot) -- Configuration-driven task scheduling and action execution -- Repository analysis and automated maintenance workflows +- Fixed stage sequence with typed I/O enforced by JSON schema (`packages/core/src/stage.ts`, `packages/core/src/runner.ts`, `packages/schemas/stages/*.json`) +- CLI-driven orchestration with report/output generation (`packages/cli/src/index.ts`) +- Runtime adapters for LLM providers separated from orchestration (`packages/runtimes/openai/src/index.ts`, `packages/runtimes/copilot/src/index.ts`) ## Layers -**CLI Layer:** -- Purpose: Command-line interface and entry point for all workflows -- Location: `packages/cli/src/index.ts`, `src/cli.ts` -- Contains: Command definitions, stage orchestration, runtime initialization -- Depends on: Core layer, Runtime providers -- Used by: End users via CLI commands +**CLI Orchestration:** +- Purpose: Load config, enforce scope/budgets, build stages, run pipeline, write reports/publish payloads. +- Location: `packages/cli/src/index.ts` +- Contains: Command parsing, stage assembly, prompt/schema loading, output writing. +- Depends on: Core library, runtimes, prompt files, schema files. +- Used by: CLI executable (`package.json` -> `dist/cli/src/index.js`) and GitHub Action (`action.yml`). -**Core Layer:** -- Purpose: Core domain logic for repository analysis and workflow execution +**Core Library:** +- Purpose: Provide shared primitives for config, scope, budgets, schema validation, and stage execution. - Location: `packages/core/src/` -- Contains: Stage definitions, schema validation, config loading, repository fact collection, prompt management -- Depends on: None (no external dependencies) -- Used by: CLI layer, phase implementations - -**Phase Layer:** -- Purpose: Specific maintenance workflow steps -- Location: `src/phases/` -- Contains: Scan, Analyze, Plan, Propose, Act phases -- Depends on: Core types, configuration -- Used by: Groundkeeper orchestrator - -**Runtime Layer:** -- Purpose: LLM provider abstraction -- Location: `packages/runtimes/openai/src/`, `packages/runtimes/copilot/src/` -- Contains: OpenAI and Copilot SDK integrations -- Depends on: External APIs (OpenAI, Copilot) -- Used by: CLI stage builders - -**Configuration Layer:** -- Purpose: Configuration loading and validation -- Location: `src/config.ts`, `packages/core/src/config.ts` -- Contains: YAML parsing, schema validation, defaults merging -- Depends on: js-yaml library -- Used by: Groundkeeper, CLI runner +- Contains: `config.ts`, `scope.ts`, `budgets.ts`, `schema.ts`, `runner.ts`, `stage.ts`, `repo.ts`, `prompt.ts`. +- Depends on: Node FS/path and JSON/YAML parsing (`packages/core/src/config.ts`, `packages/core/src/schema.ts`). +- Used by: CLI (`packages/cli/src/index.ts`). + +**Runtime Adapters:** +- Purpose: Implement provider-specific `generateJson` behavior for LLM calls. +- Location: `packages/runtimes/*/src/index.ts` +- Contains: OpenAI fetch-based adapter and Copilot SDK adapter. +- Depends on: Provider SDKs or APIs (`packages/runtimes/openai/src/index.ts`, `packages/runtimes/copilot/src/index.ts`). +- Used by: CLI runtime selection (`packages/cli/src/index.ts`). + +**Schemas:** +- Purpose: Define and validate stage input/output contracts and agent config schema. +- Location: `packages/schemas/` +- Contains: `agent.schema.json`, `stages/*.schema.json`. +- Depends on: JSON schema validation in `packages/core/src/schema.ts`. +- Used by: CLI stage setup and validation in `packages/cli/src/index.ts` and `packages/core/src/runner.ts`. + +**Prompts:** +- Purpose: Stage-specific prompt templates for LLM interaction. +- Location: `prompts/` +- Contains: `system.md`, `analysis.md`, `plan.md`, `patch.md`, `verify.md`, `publish.md`. +- Depends on: Prompt loader in `packages/core/src/prompt.ts`. +- Used by: CLI stage execution in `packages/cli/src/index.ts`. + +**GitHub Action Wrapper:** +- Purpose: Provide a composite action that runs the CLI and publishes reports. +- Location: `action.yml` +- Contains: Setup, install/build, CLI invocation, optional issue publishing. +- Depends on: Built CLI (`dist/cli/src/index.js`) or global `groundkeeper` binary. +- Used by: GitHub Actions workflows (e.g., `action.yml`). ## Data Flow -**Main Workflow (CLI):** - -1. Load agent configuration (`packages/core/src/config.ts`) -2. Collect repository facts (`packages/core/src/repo.ts`) -3. Validate scope and budgets (`packages/core/src/scope.ts`, `packages/core/src/budgets.ts`) -4. Build stage pipeline (`packages/cli/src/index.ts` - `buildStages()`) -5. Execute stages sequentially with: - - Input validation via schema (`packages/core/src/schema.ts`) - - Stage-specific logic (preflight, analysis, plan, patch, verify, publish) - - Output validation via schema - - Log persistence to `.groundkeeper/logs/` -6. Generate report and optional publish payload - -**Phase Workflow (Legacy/Alternative):** - -1. Load Groundkeeper config (`src/config.ts`) -2. Initialize Groundkeeper context (`src/groundkeeper.ts`) -3. Execute phases sequentially: - - **Scan**: Repository structure and metadata collection (`src/phases/ScanPhase.ts`) - - **Analyze**: Finding generation from scan results (`src/phases/AnalyzePhase.ts`) - - **Plan**: Action planning from findings (`src/phases/PlanPhase.ts`) - - **Propose**: Concrete proposal generation (`src/phases/ProposePhase.ts`) - - **Act**: Output generation and optional execution (`src/phases/ActPhase.ts`) -4. Stop on first failure; continue on success +**CLI Run Pipeline:** -**State Management:** +1. Load and validate agent config (`packages/core/src/config.ts`) from `.github/agent.yml`. +2. Collect repo facts (`packages/core/src/repo.ts`) and file list (`packages/cli/src/index.ts`). +3. Enforce scope and budgets (`packages/core/src/scope.ts`, `packages/core/src/budgets.ts`). +4. Build stage definitions with prompt/templates and schemas (`packages/cli/src/index.ts`, `prompts/*.md`, `packages/schemas/stages/*.json`). +5. Run stages sequentially with schema validation and logging (`packages/core/src/runner.ts`). +6. Write report and optional publish payload to `.groundkeeper/` (`packages/cli/src/index.ts`). -- Context object passed through all phases/stages: `GroundkeeperContext` (`src/types.ts`) -- Each phase adds results to `context.results` dictionary -- Stage results accumulated in array and logged -- No global state; all data flows through parameters +**State Management:** +- Stage data is passed via `StageContext` and stage outputs chained as the next input (`packages/core/src/stage.ts`, `packages/core/src/runner.ts`). ## Key Abstractions -**Phase:** -- Purpose: Base class for workflow steps -- Examples: `src/phases/ScanPhase.ts`, `src/phases/AnalyzePhase.ts`, `src/phases/PlanPhase.ts` -- Pattern: Template method with `execute()` (logging/timing) and abstract `run()` (logic) - -**StageDefinition:** -- Purpose: Typed stage with input/output schemas -- Location: `packages/core/src/stage.ts` -- Pattern: Generic type with `name`, schema paths, async `run()` function - -**Finding:** -- Purpose: Represents a discovery from analysis -- Location: `src/types.ts` -- Fields: id, type, severity, message, file, suggestion -- Used in: Analysis and planning phases - -**ProposedChange:** -- Purpose: Represents actionable change (create, update, delete) -- Location: `src/types.ts` -- Fields: type, target, content, reason -- Used in: Propose and Act phases - -**LlmRuntime:** -- Purpose: Abstract LLM provider interface -- Pattern: `{ generateJson(messages: LlmMessage[]): Promise }` -- Implementations: `packages/runtimes/openai/src/index.ts`, `packages/runtimes/copilot/src/index.ts` +**StageDefinition/StageContext/StageResult:** +- Purpose: Standardize stage execution contract. +- Examples: `packages/core/src/stage.ts`, `packages/core/src/runner.ts`, `packages/cli/src/index.ts`. +- Pattern: Stage pipeline with validated I/O and fail-fast behavior. + +**AgentConfig:** +- Purpose: Configuration contract for scope, budgets, runtime, publish settings. +- Examples: `packages/core/src/config.ts`, `.github/agent.yml`. +- Pattern: Load/validate YAML then enforce constraints before running stages. + +**PromptPayload:** +- Purpose: Normalize system/stage/schema/context payload passed to LLM. +- Examples: `packages/core/src/prompt.ts`, `packages/cli/src/index.ts`. +- Pattern: Build + serialize JSON payload for provider runtimes. + +**Runtime Adapters:** +- Purpose: Provider-specific JSON generation from prompts. +- Examples: `packages/runtimes/openai/src/index.ts`, `packages/runtimes/copilot/src/index.ts`. +- Pattern: `generateJson(messages)` returns parsed JSON or throws errors. ## Entry Points -**CLI Command: `groundkeeper run`:** -- Location: `packages/cli/src/index.ts` (main), `src/cli.ts` (legacy) -- Triggers: User executes `npm start` or `groundkeeper run` command -- Responsibilities: - - Parse command-line options (config, directory) - - Load configuration and validate - - Collect repository facts - - Build and execute stage pipeline - - Generate report output - - Write publish payload if configured - -**CLI Command: `groundkeeper init`:** -- Location: `src/cli.ts` -- Triggers: User runs `groundkeeper init` -- Responsibilities: Generate default `groundkeeper.yml` config template - -**CLI Command: `groundkeeper scan`:** -- Location: `src/cli.ts` -- Triggers: User runs `groundkeeper scan` -- Responsibilities: Execute only scan phase - -**CLI Command: `groundkeeper analyze`:** -- Location: `src/cli.ts` -- Triggers: User runs `groundkeeper analyze` -- Responsibilities: Execute scan and analyze phases - -**Groundkeeper.run():** -- Location: `src/groundkeeper.ts` -- Entry for programmatic use -- Executes all phases in sequence +**CLI Binary:** +- Location: `packages/cli/src/index.ts` +- Triggers: `groundkeeper run` via `package.json` bin (`dist/cli/src/index.js`). +- Responsibilities: Parse args, load config, run stage pipeline, write outputs. + +**GitHub Action:** +- Location: `action.yml` +- Triggers: GitHub Actions workflows. +- Responsibilities: Install/build CLI, run it, publish issue payload, upload artifacts. ## Error Handling -**Strategy:** Fail-fast on validation or phase failure; continue on info-level findings. +**Strategy:** Fail fast on schema or runtime errors, return structured stage results. **Patterns:** - -- **Schema Validation**: `validateSchema()` in `packages/core/src/schema.ts` returns error array; stage stops if errors present -- **Phase Execution**: `Phase.execute()` wraps `run()` with try-catch; logs error and returns failed result -- **Config Validation**: `ConfigLoader.validateConfig()` throws on invalid format or missing required fields -- **Pipeline Stopping**: `Groundkeeper.executePhases()` breaks loop on first failed phase result -- **Stage Dependency**: `runStages()` breaks loop on validation failure or execution error +- Input/output validation errors stop the pipeline with `success: false` (`packages/core/src/runner.ts`). +- Config validation throws with aggregated error list (`packages/core/src/config.ts`). ## Cross-Cutting Concerns -**Logging:** -- Approach: Console-based logging with phase/stage prefixes -- Pattern: `console.log()` with `[${phaseName}]` or `[${stageName}]` prefix -- Timing: Phase base class logs start/completion with duration -- Files: Stage logs written to `.groundkeeper/logs/{stageName}.input.json` and `.groundkeeper/logs/{stageName}.output.json` - -**Validation:** -- Approach: JSON schema validation at stage boundaries -- Location: `packages/core/src/schema.ts` - `validateSchema()` recursive validator -- Applied to: Stage input/output before/after execution -- Configuration: YAML schema validation via regex patterns - -**Authentication:** -- Approach: Environment variable-based token management -- Tokens used: `GITHUB_TOKEN`, `OPENAI_API_KEY` (OpenAI runtime), Copilot SDK tokens -- Read at: CLI initialization in `packages/cli/src/index.ts` -- Scope checking: `packages/core/src/scope.ts` enforces allowed file patterns +**Logging:** Stage start logs and JSON input/output logs in `.groundkeeper/logs` (`packages/core/src/runner.ts`). +**Validation:** JSON schema validation for stages (`packages/core/src/schema.ts`) and manual config validation (`packages/core/src/config.ts`). +**Authentication:** Provider credentials via env vars (`packages/cli/src/index.ts`) and GitHub token injection in action runtime (`action.yml`). --- -*Architecture analysis: 2026-01-23* +*Architecture analysis: 2026-02-09* diff --git a/.planning/codebase/CONCERNS.md b/.planning/codebase/CONCERNS.md index 8532919..a72138e 100644 --- a/.planning/codebase/CONCERNS.md +++ b/.planning/codebase/CONCERNS.md @@ -1,196 +1,122 @@ # Codebase Concerns -**Analysis Date:** 2026-01-23 +**Analysis Date:** 2026-02-09 ## Tech Debt -**Loose Type Safety with `any` Parameters:** -- Issue: Multiple phase classes accept `any` typed parameters instead of properly typed interfaces -- Files: `src/phases/AnalyzePhase.ts` (line 56, 131), `src/phases/PlanPhase.ts` (lines 50, 75, 108, 130), `src/phases/ProposePhase.ts` (line 42), `src/phases/Phase.ts` (line 51) -- Impact: Loss of compile-time type checking and IDE autocompletion; increases runtime errors related to unexpected data shapes in phase transitions -- Fix approach: Create specific interfaces for `Task`, `Finding`, and `PlannedAction` parameter types instead of using `any`. Define formal interfaces in `src/types.ts` for each phase parameter type. - -**Record for Metadata:** -- Issue: Metadata collection in `src/phases/ScanPhase.ts` (line 115-116) and untyped context data in `src/types.ts` (lines 88, 113, 142) uses `Record` -- Files: `src/phases/ScanPhase.ts`, `src/types.ts` (lines 88, 113, 142, 239) -- Impact: Schema validation is bypassed; metadata structure is undocumented and fragile; difficult to evolve data structures safely -- Fix approach: Define explicit interfaces for each metadata type (PackageJsonMetadata, GitHubMetadata, etc.) and strongly type all phase data flows. - -**Unimplemented/Stub Features in ActPhase:** -- Issue: The `ActPhase.ts` class explicitly does not perform actions; it only generates reports. Comments indicate future implementation (lines 39-45) -- Files: `src/phases/ActPhase.ts` -- Impact: Phase pipeline completes successfully but produces no actual changes; misleading to users who expect automated fixes; all "actions" are deferred -- Fix approach: Either rename phase to `ReportPhase` and adjust pipeline documentation, or implement actual file operations with permission gates. Document current limitations clearly. - -## Error Handling Gaps - -**Process Exit Called Directly in CLI:** -- Issue: CLI calls `process.exit(0)` and `process.exit(1)` directly instead of returning/throwing -- Files: `src/cli.ts` (lines 22, 25, 66, 102) -- Impact: Difficult to test CLI commands; cannot gracefully integrate CLI into other tools; no chance for cleanup/teardown in automated environments -- Fix approach: Return exit codes from CLI command handlers instead of calling `process.exit()`. Let the entry point handle process termination. - -**Silent Fallbacks in Config Loading:** -- Issue: `ConfigLoader.loadConfig()` silently returns DEFAULT_CONFIG when file is missing (line 40-42) without distinguishing between file not found vs. file exists but invalid -- Files: `src/config.ts` (lines 39-43) -- Impact: User misconfigurations may go unnoticed; hard to debug why configuration is not being applied -- Fix approach: Separate "file not found" (use defaults) from "file invalid" (throw error). Return `null` or raise specific error for missing files so callers can decide behavior. - -**Inconsistent Error Logging:** -- Issue: Some phases log errors to console, others throw; console.warn/error are called directly at 23 locations -- Files: Multiple across `src/phases/*.ts`, `src/config.ts`, `src/cli.ts` -- Impact: No structured logging; difficult to capture/parse/redirect errors in automation; mixing logs and exceptions makes error handling fragile -- Fix approach: Implement a Logger interface and dependency-inject it throughout phases. Centralize error handling in phase executor. +**Rules configuration unused:** +- Issue: `rules.allow` and `rules.deny` are validated but never enforced in runtime logic. +- Files: `packages/core/src/config.ts`, `packages/cli/src/index.ts` +- Impact: Config-driven policy intent is ignored, so reports may violate stated allow/deny rules. +- Fix approach: Enforce rule checks before or after LLM stages and fail or annotate output when violated. + +**Budget enforcement incomplete:** +- Issue: `diffLines` and `tokens` are always passed as `0`, so budgets never enforce changes or token usage. +- Files: `packages/cli/src/index.ts`, `packages/core/src/budgets.ts` +- Impact: Budget checks are effectively limited to file count, defeating the purpose of diff/token limits. +- Fix approach: Compute diff line counts and token usage per stage, then re-run `checkBudgets` with real values. + +**Schema validation partial:** +- Issue: JSON schema validation only checks `type`, `required`, `properties`, and `items`, ignoring `enum`, `pattern`, `additionalProperties`, and numeric constraints. +- Files: `packages/core/src/schema.ts`, `packages/schemas/stages/analysis.output.schema.json` +- Impact: Stage outputs can pass validation while violating schema constraints, reducing determinism. +- Fix approach: Use a full JSON Schema validator or extend `validateSchema` to enforce the missing constraints. + +## Known Bugs + +**Scope glob patterns never match:** +- Symptoms: Running `groundkeeper run` fails with “Out of scope” even when files are intended to be included. +- Files: `packages/core/src/scope.ts`, `.github/agent.yml` +- Trigger: `scope.include` uses glob patterns like `src/**`, but `checkScope` only uses `startsWith` on literal strings. +- Workaround: Use literal prefixes (e.g., `src/`) or implement real glob matching. + +**Action build step fails in repo context:** +- Symptoms: GitHub Action fails on `npm run build` because `typescript` is not installed. +- Files: `action.yml`, `package.json` +- Trigger: `npm install --production` skips devDependencies required for `tsc`. +- Workaround: Install devDependencies for build or skip build when `dist/` is already present. + +**Jest config points to a non-existent root:** +- Symptoms: `npm test` runs zero tests despite project code existing. +- Files: `jest.config.js`, `packages/core/src/index.ts` +- Trigger: `roots` is set to `/src` while code lives under `packages/`. +- Workaround: Update Jest roots to `packages/` or add tests under `src/`. -## Test Coverage Gaps +## Security Considerations -**Phase Execution Logic Not Fully Tested:** -- Issue: Phase execution framework tests (groundkeeper.test.ts) only verify phases run successfully, not what they do internally -- Files: `src/__tests__/groundkeeper.test.ts` (98 lines for 5 phases) -- Impact: Phase logic bugs go undetected; refactoring phases risks breaking functionality; hard to verify analysis findings are correct -- Fix approach: Add unit tests for each phase with isolated mocks. Test AnalyzePhase.analyzeSecurity(), PlanPhase.planActionForFinding() individually. Add negative test cases. - -**CLI Commands Lack Integration Tests:** -- Issue: CLI tests mock Groundkeeper class; actual end-to-end CLI execution is not tested -- Files: `src/__tests__/cli.test.ts` (68 lines) -- Impact: CLI edge cases undetected; breaking changes to CLI surface go unnoticed -- Fix approach: Add integration tests that spawn CLI as subprocess, verify exit codes, output directory creation, and report generation. - -**No Tests for Scan Phase File Discovery:** -- Issue: ScanPhase.scanFiles() recursively scans directory but has no unit tests -- Files: `src/phases/ScanPhase.ts` (lines 85-110) -- Impact: File filtering logic (ignoreDirs set) could break silently; performance regressions undetected on large repos -- Fix approach: Add tests for symlink handling, circular reference handling, and large directory structures. Test ignore patterns. - -**Missing Security Tests:** -- Issue: AnalyzePhase.analyzeSecurity() checks for sensitive files, but no tests verify this detection works -- Files: `src/phases/AnalyzePhase.ts` (lines 164-184) -- Impact: Sensitive files could be missed; security findings might not be generated -- Fix approach: Add test fixtures with .env, secrets.json files; verify findings are generated correctly. +**Report and publish payload expose internal context:** +- Risk: Generated reports include configuration and stage results and may be published to issues or uploaded as artifacts without redaction. +- Files: `packages/cli/src/index.ts`, `action.yml` +- Current mitigation: None detected. +- Recommendations: Add redaction for sensitive fields and allow disabling report content publication. + +**Stage logs written without redaction:** +- Risk: Raw stage input/output is persisted to `.groundkeeper/logs`, which can include sensitive content. +- Files: `packages/core/src/runner.ts`, `action.yml` +- Current mitigation: Excludes `.groundkeeper` from scope checks but still uploads it as an artifact. +- Recommendations: Add configurable log redaction or disable log writing by default. + +## Performance Bottlenecks + +**Full repository scan is synchronous and unfiltered:** +- Problem: `collectRepoFileList` recursively reads every file except `.git`, `node_modules`, and `.groundkeeper`. +- Files: `packages/cli/src/index.ts` +- Cause: No `.gitignore` integration and no early exit on budget overages. +- Improvement path: Respect `.gitignore` and short-circuit scanning once budgets are exceeded. ## Fragile Areas -**Pipeline Dependency Chain Is Strict:** -- Issue: Each phase assumes previous phase completed successfully (`context.results.scan`, `context.results.analyze`, etc.) -- Files: `src/phases/AnalyzePhase.ts` (line 11), `src/phases/PlanPhase.ts` (line 14), `src/phases/ProposePhase.ts` (line 11), `src/phases/ActPhase.ts` (line 13) -- Impact: Single phase failure halts entire pipeline; no partial execution; difficult to re-run individual phases -- Fix approach: Allow phases to run independently with fallback data. Store phase outputs durably so phases can resume. Add `--resume` CLI flag. - -**Regex Pattern for Git Remote Parsing:** -- Issue: GIT_REMOTE_URL_PATTERN in `src/phases/ScanPhase.ts` (line 8) may not match all git remote formats -- Files: `src/phases/ScanPhase.ts` (line 8) -- Impact: Non-standard git remote URLs could fail to parse; owner/name would default to 'unknown'; reports would be incomplete -- Fix approach: Use a git library instead of regex. Add tests with various SSH, HTTPS, and file:// URL formats. - -**Finding ID Hard-coded in PlanPhase:** -- Issue: PlanPhase checks for specific finding IDs like 'doc-001', 'doc-002' to plan actions -- Files: `src/phases/PlanPhase.ts` (lines 76, 87) -- Impact: If AnalyzePhase changes finding IDs, PlanPhase breaks silently; new finding types cannot be added without modifying two phases -- Fix approach: Add a `type` field to Finding interface and use that for dispatch. Create a registry/factory pattern for finding-to-action mapping. - -**Metadata Collection Depends on Filesystem Existence:** -- Issue: If a file check throws an exception (permissions, etc.), metadata collection silently catches and continues -- Files: `src/phases/ScanPhase.ts` (lines 129, 131, 170) -- Impact: Missing metadata could lead to incorrect analysis; errors are swallowed -- Fix approach: Separate file access errors from expected "file not found"; log warnings for unexpected errors; consider failing early. +**Scope enforcement relies on prefix matching:** +- Files: `packages/core/src/scope.ts`, `.github/agent.yml` +- Why fragile: Using literal prefixes makes config sensitive to minor path changes and glob usage. +- Safe modification: Replace prefix checks with a glob matcher and add tests for include/exclude behavior. +- Test coverage: No tests for scope matching logic. -## Security Considerations +**Publish target mismatch between schema and action:** +- Files: `packages/schemas/agent.schema.json`, `packages/core/src/config.ts`, `action.yml` +- Why fragile: Schema allows `publish.target: pr`, but the action fails for any non-`issue` target. +- Safe modification: Update action to support `pr` or restrict schema to `issue` only. +- Test coverage: No tests for publish target handling. -**GitHub Token Not Validated on Load:** -- Issue: `context.githubToken` is read from env but never validated; no token format check or expiration check -- Files: `src/groundkeeper.ts` (line 26) -- Impact: Invalid or expired tokens would be silently accepted, causing failures later in execution -- Fix approach: Validate token format on load. Add token health check phase before analysis. - -**Sensitive File Detection Only on Filename:** -- Issue: `AnalyzePhase.analyzeSecurity()` only checks hardcoded filenames; does not scan file contents for secrets -- Files: `src/phases/AnalyzePhase.ts` (lines 168-181) -- Impact: Secrets embedded in code (API keys, passwords) would not be detected; only obvious files like `.env` are caught -- Fix approach: Integrate a secrets scanning library like `gitleaks` or `truffleHog`. Document current limitations. - -**No Sanitization of Report Output:** -- Issue: ActPhase generates markdown reports by interpolating findings directly without escaping -- Files: `src/phases/ActPhase.ts` (lines 123-192) -- Impact: If finding messages contain markdown/code injection, reports could be malformed or misleading -- Fix approach: Escape special characters in report generation. Use a markdown builder library for safety. - -**Configuration Files Not Validated for Injection:** -- Issue: Config is loaded from YAML without limiting nested depth or string sizes -- Files: `src/config.ts` (line 47) -- Impact: Malicious YAML could cause memory exhaustion (billion laughs attack) or stack overflow (nested objects) -- Fix approach: Use `js-yaml` with options: `maxAliasCount: 10`, set max string length limits, max nesting depth. +## Scaling Limits -## Performance Bottlenecks +**Budget checks do not scale with repo size or changes:** +- Current capacity: Only file count is enforced at runtime. +- Limit: Large repos can exceed time/memory limits before budgets are enforced. +- Files: `packages/cli/src/index.ts`, `packages/core/src/budgets.ts` +- Scaling path: Track file count during scan, and compute diff/token budgets after each stage. -**Recursive Directory Scan Not Optimized:** -- Issue: `ScanPhase.scanFiles()` recursively scans entire working directory, creating a potentially large file list in memory -- Files: `src/phases/ScanPhase.ts` (lines 85-110) -- Impact: On large monorepos (50k+ files), scan could be slow and memory-intensive; no pagination or streaming -- Fix approach: Implement early termination if file count exceeds threshold. Use streaming/generator approach. Add `--max-files` flag. - -**All Phase Results Stored in Context:** -- Issue: `context.results` accumulates all phase outputs without pruning; large analysis results are kept in memory -- Files: `src/types.ts` (lines 309-314), `src/groundkeeper.ts` (lines 24-27) -- Impact: On large repos, memory usage grows unbounded; no garbage collection between phases -- Fix approach: Store only current phase output. Write intermediate results to disk (.groundkeeper/ logs). Clear old results after phase completion. - -**Package.json Parsing Not Validated:** -- Issue: `ScanPhase.collectMetadata()` reads entire package.json into memory and parses without size limits -- Files: `src/phases/ScanPhase.ts` (lines 120-132) -- Impact: Malformed or huge package.json could cause crashes; no error recovery -- Fix approach: Add try-catch around JSON.parse. Validate package.json size before reading. Extract only needed fields. - -## Known Limitations / By Design - -**Read-Only Mode is Hard-Coded:** -- Issue: ActPhase always generates reports only; actual file modifications commented out as "future version" -- Files: `src/phases/ActPhase.ts` (lines 39-45) -- Impact: Tool cannot automatically fix issues; all proposals require manual intervention -- Design: Intentional safety mechanism; documented in README but limits usefulness -- Recommendation: Clarify in docs that this is a reporting tool, not an automated fixer. - -**Single Phase Execution Flow Only:** -- Issue: Phases must run in fixed order: scan → analyze → plan → propose → act; cannot run subsets except via CLI -- Files: `src/groundkeeper.ts` (lines 69-81) -- Impact: If only analysis is needed, full pipeline still runs -- Design: Intentional for consistency; `runPhases()` method allows partial runs -- Recommendation: Consider lazy evaluation or caching phase outputs. - -**No Caching of Scan Results:** -- Issue: Every execution rescans the entire repository -- Files: `src/phases/ScanPhase.ts` -- Impact: Inefficient for repeated runs; duplicates work -- Recommendation: Add optional caching flag. Store scan results with timestamp. Invalidate on HEAD change. +**Log accumulation without cleanup:** +- Current capacity: Logs accumulate under `.groundkeeper/logs` across runs. +- Limit: Repeated runs can bloat repository artifacts and slow down artifact upload. +- Files: `packages/core/src/runner.ts`, `action.yml` +- Scaling path: Add log rotation or cleanup at the start of each run. ## Dependencies at Risk -**js-yaml Without Safe Load Options:** -- Issue: `yaml.load()` is used without options; safe by default in v4+ but could be unsafe if downgraded -- Files: `src/config.ts` (line 47) -- Impact: Potential for YAML deserialization attacks -- Fix approach: Explicitly pass `{ safe: true }` to yaml.load(). Add version constraint in package.json: `"js-yaml": ">=4.1.0"`. - -**Commander.js Dependency Chain Not Pinned:** -- Issue: commander is pinned to `^11.1.0` (caret allows minor/patch updates) -- Files: `package.json` (line 30) -- Impact: Minor releases could introduce subtle CLI behavior changes -- Recommendation: Consider pinning to exact version or at least `~11.1.0` (tilde for patch only). - -## Integration Concerns - -**GitHub Actions Integration Not Fully Tested:** -- Issue: Code checks `process.env.GITHUB_ACTIONS` but no tests run in GitHub Actions environment -- Files: `src/groundkeeper.ts` (line 25) -- Impact: GitHub Actions paths might not work correctly; env vars might be missing -- Fix approach: Add GitHub Actions test job. Verify `GITHUB_TOKEN` is available and valid. - -**No Support for Custom Task Types:** -- Issue: AnalyzePhase switches on hardcoded task types; new types require code changes -- Files: `src/phases/AnalyzePhase.ts` (lines 59-74) -- Impact: Tool is not extensible; users cannot add custom analyses -- Design: Intentional for safety; reduces attack surface -- Recommendation: Document as design constraint. If extensibility needed, use plugin architecture with sandboxing. +Not detected. + +## Missing Critical Features + +**Verify stage does not run real checks:** +- Problem: The verify stage only delegates to the LLM runtime and never executes tests or linters. +- Files: `packages/cli/src/index.ts` +- Blocks: Automated verification of patches before publishing. + +## Test Coverage Gaps + +**No automated tests cover runtime behavior:** +- What's not tested: Scope matching, budget enforcement, runtime JSON parsing, and stage runner error handling. +- Files: `packages/core/src/scope.ts`, `packages/core/src/budgets.ts`, `packages/runtimes/openai/src/index.ts`, `packages/core/src/runner.ts` +- Risk: Regressions in core safety logic can ship unnoticed. +- Priority: High + +**Jest configured but no tests detected:** +- What's not tested: CLI command flow and GitHub Action integration paths. +- Files: `jest.config.js`, `packages/cli/src/index.ts`, `action.yml` +- Risk: Breaking CLI behavior or action runtime steps without noticing. +- Priority: High --- -*Concerns audit: 2026-01-23* +*Concerns audit: 2026-02-09* diff --git a/.planning/codebase/CONVENTIONS.md b/.planning/codebase/CONVENTIONS.md index 744a885..da4c121 100644 --- a/.planning/codebase/CONVENTIONS.md +++ b/.planning/codebase/CONVENTIONS.md @@ -1,155 +1,80 @@ # Coding Conventions -**Analysis Date:** 2026-01-23 +**Analysis Date:** 2026-02-09 ## Naming Patterns **Files:** -- Class files use PascalCase (e.g., `ScanPhase.ts`, `ConfigLoader.ts`, `Groundkeeper.ts`) -- Utility/module files use camelCase or descriptive names (e.g., `types.ts`, `config.ts`, `cli.ts`) -- Test files follow pattern: `[name].test.ts` or `[name].spec.ts` (stored in `__tests__` directories) +- Use lowercase filenames with short nouns/verbs, e.g. `packages/core/src/runner.ts`, `packages/core/src/config.ts`, `packages/runtimes/openai/src/index.ts` **Functions:** -- Methods use camelCase (e.g., `executePhases`, `validateConfig`, `scanFiles`) -- Private methods prefixed with underscore (e.g., `_getRepositoryInfo`, `_scanFiles`, `_collectMetadata`) -- Abstract methods marked with `abstract` keyword in base classes +- camelCase for functions and helpers, e.g. `runStages`, `loadAgentConfig`, `collectRepoFacts` in `packages/core/src/runner.ts`, `packages/core/src/config.ts`, `packages/core/src/repo.ts` **Variables:** -- Local variables and parameters use camelCase (e.g., `workingDirectory`, `configPath`, `findings`) -- Constants use UPPER_SNAKE_CASE (e.g., `GIT_REMOTE_URL_PATTERN`, `DEFAULT_CONFIG`) -- Configuration object names use camelCase (e.g., `context`, `results`, `metadata`) +- camelCase for locals/params and SCREAMING_SNAKE_CASE for constants, e.g. `workingDirectory` in `packages/cli/src/index.ts`, `DEFAULT_ENDPOINT` in `packages/runtimes/openai/src/index.ts` **Types:** -- Interfaces use PascalCase with descriptive names (e.g., `GroundkeeperConfig`, `PhaseResult`, `ScanResult`) -- All interfaces properly documented with JSDoc -- Literal types used in union types (e.g., `'create' | 'update' | 'delete'` for Change type) +- PascalCase for types/interfaces, e.g. `AgentConfig` in `packages/core/src/config.ts`, `StageDefinition` in `packages/core/src/stage.ts`, `RepoFacts` in `packages/core/src/repo.ts` ## Code Style **Formatting:** -- TypeScript is compiled to ES2020 target -- Module system: CommonJS -- Strict mode enabled: All TypeScript strict checks enforced -- Line length: No explicit limit configured, follows readable code practices -- Indentation: Standard JavaScript conventions observed (2-space indent in config files, standard editor defaults in code) +- No formatter config detected; code uses 2-space indentation, semicolons, single quotes, and trailing commas in multiline lists/params (see `packages/cli/src/index.ts`, `packages/core/src/runner.ts`, `packages/runtimes/openai/src/index.ts`) **Linting:** -- ESLint configured with `@typescript-eslint/parser` and `@typescript-eslint/eslint-plugin` -- ESLint configuration not committed to repo (no `.eslintrc` file) -- Run linting: `npm run lint` (targets `src/**/*.ts`) -- No Prettier configuration found; formatting handled by editor defaults or developer preference +- ESLint runs via `npm run lint` in `package.json` and relies on `@typescript-eslint/*` deps in `package.json`; no `.eslintrc*` or `eslint.config.*` present ## Import Organization **Order:** -1. Node.js built-in modules (e.g., `import * as fs from 'fs'`, `import * as path from 'path'`) -2. External dependencies (e.g., `import { Command } from 'commander'`, `import * as yaml from 'js-yaml'`) -3. Internal project modules (e.g., `import { Phase } from './Phase'`, `import { GroundkeeperContext } from '../types'`) +1. External packages (e.g. `commander`) in `packages/cli/src/index.ts` +2. Node built-ins (`fs`, `path`) in `packages/cli/src/index.ts` and `packages/core/src/runner.ts` +3. Local relative imports in `packages/cli/src/index.ts` and `packages/core/src/runner.ts` **Path Aliases:** -- No path aliases configured; uses relative paths throughout -- Relative paths use dot notation (e.g., `'./phases'`, `'../types'`, `'../../dist/cli.js'`) +- None detected; imports are relative and `tsconfig.json` has no `paths` mapping ## Error Handling **Patterns:** -- Try-catch blocks used for file I/O and configuration loading (see `config.ts`, lines 36-54) -- Phase execution wrapped in try-catch to capture and report errors (see `Phase.ts`, lines 21-43) -- Errors converted to strings using: `error instanceof Error ? error.message : String(error)` -- Error messages logged to console.error with context prefix (e.g., `[phase-name] Phase failed...`) -- Functions throw errors for validation failures (see `ConfigLoader.validateConfig`) -- Warning logs used for non-fatal issues (e.g., `console.warn('Could not read git information:', error)`) +- Throw `Error` with descriptive messages on invalid config or runtime failures in `packages/core/src/config.ts`, `packages/cli/src/index.ts`, `packages/runtimes/openai/src/index.ts`, `packages/runtimes/copilot/src/index.ts` +- Return structured error arrays with `ok`/`success` flags for validations and stage execution in `packages/core/src/budgets.ts`, `packages/core/src/scope.ts`, `packages/core/src/runner.ts`, `packages/core/src/schema.ts` ## Logging -**Framework:** Native `console` object (no logging library) +**Framework:** console **Patterns:** -- Informational logs: `console.log()` with context prefix in brackets (e.g., `[scan] Starting phase...`) -- Phase progress: Logged with duration timing (milliseconds) -- Warnings: `console.warn()` for recoverable issues (e.g., missing configuration file, parse errors) -- Errors: `console.error()` for failures and exceptions -- Visual indicators used: checkmark (✅), error cross (❌), wrench (🔧) for status -- Logs include timing information: `Date.now() - startTime` for performance tracking - -Example from `Phase.ts` (lines 18-25): -```typescript -console.log(`[${this.name}] Starting phase...`); -const startTime = Date.now(); -// ... execution ... -const duration = Date.now() - startTime; -console.log(`[${this.name}] Phase completed in ${duration}ms`); -``` +- CLI prints progress and outputs using `console.log` in `packages/cli/src/index.ts` +- Stage runner logs via injected logger (`context.logger.log`) in `packages/core/src/runner.ts` ## Comments **When to Comment:** -- File-level comments use JSDoc format for public classes and modules -- Regex patterns require inline comments explaining the pattern (see `ScanPhase.ts`, line 6-7) -- Complex logic receives explanatory comments (e.g., git URL parsing explanation) -- Validation rules documented in comments (e.g., version format requirements) +- Rare; only used for non-obvious behavior like the dynamic import note in `packages/cli/src/index.ts` **JSDoc/TSDoc:** -- All public classes documented with JSDoc blocks -- All public methods include JSDoc with `@param` and `@returns` annotations -- All interfaces include JSDoc descriptions for properties -- Example from `Phase.ts`: -```typescript -/** - * Base class for all phases - */ -export abstract class Phase { - /** - * Phase name - */ - abstract name: string; - - /** - * Execute the phase - * @param context Groundkeeper context - * @returns Phase result - */ - async execute(context: GroundkeeperContext): Promise -``` +- Not detected in source files such as `packages/core/src/index.ts`; CONTRIBUTING guidance requests JSDoc for public APIs in `CONTRIBUTING.md` ## Function Design -**Size:** Generally kept between 20-50 lines; complex phases use private helper methods +**Size:** +- Functions are small and single-purpose with early returns, e.g. `collectRepoFacts` in `packages/core/src/repo.ts` and `validateSchema` in `packages/core/src/schema.ts` **Parameters:** -- Single context object passed to phase methods for consistency (see phase `run()` signature) -- Helper methods may take specific typed parameters (e.g., `private analyzeTask(task: any, context: GroundkeeperContext)`) -- Optional parameters use TypeScript optional operator (`?`) +- Explicitly typed parameters and generics are common, e.g. `runStages` in `packages/core/src/runner.ts` and `loadAgentConfig(configPath: string)` in `packages/core/src/config.ts` **Return Values:** -- Async functions return typed Promises (e.g., `Promise`) -- Functions return descriptive objects with multiple properties rather than primitives -- Null returns used sparingly; prefer empty arrays or default values - -Examples from phases: -- `ScanPhase.run()` returns `Promise` with nested structure -- `AnalyzePhase.run()` returns `Promise` with findings and summary -- Config methods return merged configuration objects or validation booleans +- Prefer plain objects or typed result shapes, e.g. `ScopeCheckResult` in `packages/core/src/scope.ts` and `BudgetCheckResult` in `packages/core/src/budgets.ts` ## Module Design **Exports:** -- Barrel files used in `/phases` and root `index.ts` -- Named exports for classes and types: `export { Phase }`, `export * from './types'` -- All phases exported from `phases/index.ts` for convenience +- Named exports only; no default exports in `packages/core/src/runner.ts`, `packages/core/src/config.ts`, `packages/runtimes/openai/src/index.ts` **Barrel Files:** -- `src/index.ts`: Exports main class, config loader, types, and all phases -- `src/phases/index.ts`: Exports all phase classes and base Phase class - -Example from `src/index.ts`: -```typescript -export { Groundkeeper } from './groundkeeper'; -export { ConfigLoader } from './config'; -export * from './types'; -export * from './phases'; -``` +- Centralized exports via `packages/core/src/index.ts` --- -*Convention analysis: 2026-01-23* +*Convention analysis: 2026-02-09* diff --git a/.planning/codebase/INTEGRATIONS.md b/.planning/codebase/INTEGRATIONS.md index 884cb59..7fc46cc 100644 --- a/.planning/codebase/INTEGRATIONS.md +++ b/.planning/codebase/INTEGRATIONS.md @@ -1,180 +1,75 @@ # External Integrations -**Analysis Date:** 2026-01-23 +**Analysis Date:** 2026-02-09 ## APIs & External Services **LLM Providers:** -- OpenAI API - LLM-powered analysis and code generation - - SDK/Client: Native fetch-based HTTP client in `packages/runtimes/openai/src/index.ts` - - Auth: `OPENAI_API_KEY` environment variable - - Endpoint: `https://api.openai.com/v1/chat/completions` (default, configurable) - - Model selection: `OPENAI_MODEL` environment variable - - Configuration: Optional custom endpoint via `OPENAI_ENDPOINT` env var - -- GitHub Copilot - Alternative LLM runtime for analysis and generation - - SDK/Client: `@github/copilot-sdk` npm package (v0.1.16) - - Auth: GitHub authentication (handled by SDK) - - Model selection: `COPILOT_MODEL` environment variable - - Configuration: Session-based with configurable timeout via `CopilotRuntimeOptions.timeoutMs` - -**GitHub:** -- GitHub Actions - CI/CD execution and workflow triggers - - Read/write permissions for repository operations - - Token-based auth via `GITHUB_TOKEN` (from secrets) - - API access via `actions/github-script@v7` -- GitHub Issues API - Report publishing and comments - - Used in `action.yml` lines 91-96 for creating issues - - Used in `.github/workflows/groundkeeper.yml` for PR comments +- OpenAI API - chat completions for analysis/plan stages in `packages/runtimes/openai/src/index.ts` + - SDK/Client: direct HTTP via `fetch` in `packages/runtimes/openai/src/index.ts` + - Auth: `OPENAI_API_KEY` env var in `packages/cli/src/index.ts` +- GitHub Copilot - Copilot runtime for JSON generation in `packages/runtimes/copilot/src/index.ts` + - SDK/Client: `@github/copilot-sdk` in `packages/runtimes/copilot/src/index.ts` + - Auth: Copilot SDK session auth handled by SDK in `packages/runtimes/copilot/src/index.ts` (no explicit env var) + +**GitHub Platform:** +- GitHub Issues API - publish report as issue in `action.yml` + - SDK/Client: `actions/github-script@v7` in `action.yml` + - Auth: `GITHUB_TOKEN` env var in `action.yml` ## Data Storage **Databases:** -- None detected - This is a stateless repository maintenance tool +- Not detected (no DB clients in `package.json` and no DB usage in `packages/**/src/*.ts`) **File Storage:** -- Local filesystem only - Reports written to `.groundkeeper/` directory - - Report output: `.groundkeeper/report.md` - - Publish payload: `.groundkeeper/publish.json` - -**Configuration Storage:** -- YAML files - Agent configuration loaded from `agent.yml` or custom path - - Config loader: `loadAgentConfig()` in `packages/core/src/config.ts` - - Schema validation: Custom schema validation in same module +- Local filesystem only - writes reports/logs to `.groundkeeper/` in `packages/cli/src/index.ts`, `packages/core/src/runner.ts` **Caching:** -- None detected +- None (no cache clients in `package.json` and no cache usage in `packages/**/src/*.ts`) ## Authentication & Identity **Auth Provider:** -- GitHub Token - Primary authentication mechanism - - Default: `github.token` from GitHub Actions context - - Override: `github-token` input in `action.yml` (line 18-20) - - Usage: Passed as `GITHUB_TOKEN` environment variable to runtime - - Implementation: GitHub API access for issue creation and PR comments - -**OpenAI Authentication:** -- API Key based - - Variable: `OPENAI_API_KEY` - - Bearer token in Authorization header (line 34 in `packages/runtimes/openai/src/index.ts`) - -**Copilot Authentication:** -- SDK-managed via `@github/copilot-sdk` - - Automatic authentication through GitHub session - - No explicit token management required +- GitHub Actions token - used to call GitHub APIs via `actions/github-script` in `action.yml` +- OpenAI API key - used for runtime provider in `packages/cli/src/index.ts` ## Monitoring & Observability **Error Tracking:** -- None detected - Errors logged to console/stderr +- None (no tracking SDKs in `package.json` and no error tracking usage in `packages/**/src/*.ts`) **Logs:** -- Console logging approach: - - `console.log()` for info messages in `packages/cli/src/index.ts` - - Error messages thrown as Error objects with descriptive text - - GitHub Actions logs captured automatically - -**Report Generation:** -- Report written to `.groundkeeper/report.md` with: - - Configuration dump (JSON) - - Repository facts (JSON) - - Stage results (JSON) - - Execution timestamp +- Console logs in CLI runtime in `packages/cli/src/index.ts` +- JSON stage logs written to `.groundkeeper/logs/` in `packages/core/src/runner.ts` ## CI/CD & Deployment **Hosting:** -- GitHub Actions - Primary deployment and execution platform - - Runs on: `ubuntu-latest` - - Node.js: 20 (installed via `actions/setup-node@v4`) +- GitHub Actions composite action in `action.yml` **CI Pipeline:** -- GitHub Actions workflows in `.github/workflows/`: - - `ci.yml` - Build and test on push/PR - - `groundkeeper.yml` - Repository maintenance workflow (weekly schedule + manual trigger) - - `agent.yml` - Agent execution workflow - - `example-advanced.yml` - Example workflow - -**Build Process:** -1. Checkout repository (`actions/checkout@v4`) -2. Setup Node.js 20 with npm cache (`actions/setup-node@v4`) -3. Install dependencies (`npm ci`) -4. Build TypeScript (`npm run build`) -5. Optional: Test CLI execution - -**Deployment:** -- NPM package distribution: - - Published to npm registry - - Installed globally as `npm install -g groundkeeper` - - Binary: `dist/cli.js` with Node.js shebang - -- GitHub Action distribution: - - Composite action in `action.yml` - - Deployed via GitHub Actions marketplace - - Can run from local action path or global npm install +- GitHub Actions steps defined in `action.yml` ## Environment Configuration **Required env vars:** -- `GITHUB_TOKEN` - GitHub API authentication (from actions context or secrets) - -**Optional env vars (for LLM runtimes):** -- `OPENAI_API_KEY` - OpenAI API key (required if using OpenAI runtime) -- `OPENAI_MODEL` - OpenAI model name, e.g., "gpt-4" (required if using OpenAI) -- `OPENAI_ENDPOINT` - Custom OpenAI endpoint (optional, defaults to https://api.openai.com/v1/chat/completions) -- `COPILOT_MODEL` - GitHub Copilot model selection (required if using Copilot runtime) +- `OPENAI_API_KEY`, `OPENAI_MODEL`, `OPENAI_ENDPOINT` (optional) for OpenAI runtime in `packages/cli/src/index.ts` +- `COPILOT_MODEL` for Copilot runtime in `packages/cli/src/index.ts` +- `GITHUB_TOKEN` for GitHub API calls in `action.yml` **Secrets location:** -- GitHub Actions secrets (via `${{ secrets.GITHUB_TOKEN }}` or custom secrets) -- Environment variables passed to action via inputs or runner environment - -## Configuration Schema - -**YAML Configuration File:** -- Location: `.github/agent.yml` (default, configurable via CLI `--config` flag) -- Validation: Comprehensive schema validation in `packages/core/src/config.ts` - -**Configuration Structure:** -- `version` - Configuration format version (X.Y format) -- `mode` - Execution mode (currently only "report-only" supported) -- `scope.include[]` - Glob patterns of files to include -- `scope.exclude[]` - Glob patterns of files to exclude -- `budgets.maxFiles` - Maximum files to process -- `budgets.maxDiffLines` - Maximum diff lines per operation -- `budgets.maxTokens` - Maximum tokens for LLM requests -- `rules.allow[]` - Allowed actions -- `rules.deny[]` - Blocked actions -- `runtime.provider` - LLM provider: "openai" or "copilot" -- `publish.enabled` - Enable publishing results -- `publish.target` - Publish destination: "pr" or "issue" +- GitHub Actions secrets/input values resolved in `action.yml` ## Webhooks & Callbacks **Incoming:** -- GitHub Actions webhook triggers (automatic): - - Push to main branch - - Pull requests - - Scheduled cron (0 0 * * 0 - weekly) - - Workflow dispatch (manual trigger) - - Path filters for config file changes +- None (no webhook handlers in `packages/**/src/*.ts`) **Outgoing:** -- GitHub Issues API calls to create issues - - Triggered when `publish.enabled=true` and `publish.target="issue"` -- GitHub PR comment creation - - Triggered on pull_request events - -**LLM API Calls:** -- OpenAI Chat Completions endpoint (POST) - - Response: JSON with choices array containing generated message - - Error handling: Throws on non-200 status or missing content - -- GitHub Copilot session-based requests - - Session creation: `client.createSession({ model })` - - Prompt submission: `session.sendAndWait({ prompt })` - - Session cleanup: `session.destroy()` + `client.stop()` +- OpenAI chat completions endpoint `https://api.openai.com/v1/chat/completions` in `packages/runtimes/openai/src/index.ts` +- Copilot service calls via `@github/copilot-sdk` in `packages/runtimes/copilot/src/index.ts` --- -*Integration audit: 2026-01-23* +*Integration audit: 2026-02-09* diff --git a/.planning/codebase/STACK.md b/.planning/codebase/STACK.md index 3b990a8..5bd3d1b 100644 --- a/.planning/codebase/STACK.md +++ b/.planning/codebase/STACK.md @@ -1,105 +1,67 @@ # Technology Stack -**Analysis Date:** 2026-01-23 +**Analysis Date:** 2026-02-09 ## Languages **Primary:** -- TypeScript 5.3.3 - Core application and CLI implementation +- TypeScript 5.3.3 - CLI/runtime/source code in `packages/cli/src/index.ts`, `packages/core/src/index.ts`, `packages/runtimes/openai/src/index.ts`, `packages/runtimes/copilot/src/index.ts` (version in `package.json`) **Secondary:** -- Node.js - Runtime environment and scripting +- JavaScript - tooling config in `jest.config.js` +- YAML - GitHub Action and config files in `action.yml`, `groundkeeper.yml` ## Runtime **Environment:** -- Node.js 20 (specified in CI and GitHub Actions) -- CommonJS module format (compiled from TypeScript) +- Node.js 20 - GitHub Action runtime in `action.yml` **Package Manager:** -- npm - Root package manager -- Lockfile: Not detected (project structure suggests monorepo without committed lockfile) +- npm - scripts and dependencies in `package.json` +- Lockfile: present (`package-lock.json`) ## Frameworks **Core:** -- Commander 11.1.0 - CLI argument parsing and command definition - -**Build/Dev:** -- TypeScript 5.3.3 - Language and compilation -- ts-jest 29.1.1 - Jest testing with TypeScript support -- ESLint 8.56.0 with @typescript-eslint plugins 6.17.0 - Code linting +- Commander 11.1.0 - CLI command parsing in `packages/cli/src/index.ts` (dependency in `package.json`) **Testing:** -- Jest 29.7.0 - Test runner and assertion library +- Jest 29.7.0 + ts-jest 29.1.1 - test runner and TS preset in `jest.config.js` (dependencies in `package.json`) + +**Build/Dev:** +- TypeScript compiler (tsc) 5.3.3 - build pipeline in `package.json` (config in `tsconfig.json`) +- ESLint 8.56.0 + @typescript-eslint/* 6.17.0 - linting entry point in `package.json` ## Key Dependencies **Critical:** -- @github/copilot-sdk 0.1.16 - GitHub Copilot LLM runtime integration (used in `packages/runtimes/copilot/src/index.ts`) -- commander 11.1.0 - CLI framework for groundkeeper command-line tool -- js-yaml 4.1.0 - YAML configuration file parsing (used in `packages/core/src/config.ts`) - -**Utilities:** -- @types/node 20.10.6 - TypeScript definitions for Node.js APIs -- @types/js-yaml 4.0.9 - TypeScript definitions for js-yaml +- @github/copilot-sdk 0.1.16 - Copilot runtime client in `packages/runtimes/copilot/src/index.ts` (dependency in `package.json`) +- commander 11.1.0 - CLI interface in `packages/cli/src/index.ts` (dependency in `package.json`) +- js-yaml 4.1.0 - config parsing in `packages/core/src/config.ts` (dependency in `package.json`) -**Development:** -- @typescript-eslint/eslint-plugin 6.17.0 - TypeScript-specific ESLint rules -- @typescript-eslint/parser 6.17.0 - TypeScript parser for ESLint -- @types/jest 29.5.11 - Jest TypeScript definitions +**Infrastructure:** +- @types/node 20.10.6 - Node type definitions for TS in `package.json` +- typescript 5.3.3 - compilation target in `tsconfig.json` (dependency in `package.json`) ## Configuration **Environment:** -TypeScript compilation target ES2020 with CommonJS modules. Configuration through YAML files loaded via `loadAgentConfig()` in `packages/core/src/config.ts`. +- LLM runtime env vars: `OPENAI_API_KEY`, `OPENAI_MODEL`, `OPENAI_ENDPOINT`, `COPILOT_MODEL` in `packages/cli/src/index.ts` +- GitHub Action inputs mapped to env: `GITHUB_TOKEN` in `action.yml` **Build:** -- `tsconfig.json` - Root TypeScript configuration - - Target: ES2020 - - Module: commonjs - - OutDir: ./dist - - RootDir: ./src - - Strict mode enabled - - Source maps and declaration maps enabled - -**Scripts:** -- `npm run build` - Compile TypeScript via `tsc` -- `npm start` - Run CLI from compiled dist -- `npm test` - Run Jest test suite -- `npm run lint` - Run ESLint on src/**/*.ts +- TypeScript config in `tsconfig.json` +- Jest config in `jest.config.js` ## Platform Requirements **Development:** -- Node.js 20+ -- npm with CommonJS support -- TypeScript compiler -- ESLint and ts-jest for local development +- Node.js 20 for local build/run parity with Action in `action.yml` +- npm for install/build scripts in `package.json` **Production:** -- Node.js 20+ runtime -- GitHub Actions execution environment (Ubuntu latest) -- CLI deployment as: - - NPM global package (`npm install -g groundkeeper`) - - GitHub Action composite step (runs in Ubuntu with Node.js 20) - - Local binary from `dist/cli.js` - -## Entry Points - -**CLI:** -- `packages/cli/src/index.ts` - Main CLI entry point -- Compiled to: `dist/cli.js` (with shebang `#!/usr/bin/env node`) -- Executable name: `groundkeeper` (configured in package.json bin field) - -**Core Library:** -- `packages/core/src/index.ts` - Barrel export of core utilities -- Used internally by CLI and runtimes - -**LLM Runtimes:** -- `packages/runtimes/openai/src/index.ts` - OpenAI API integration -- `packages/runtimes/copilot/src/index.ts` - GitHub Copilot integration +- GitHub Actions composite action using Node.js 20 in `action.yml` --- -*Stack analysis: 2026-01-23* +*Stack analysis: 2026-02-09* diff --git a/.planning/codebase/STRUCTURE.md b/.planning/codebase/STRUCTURE.md index 1261a75..d83fe11 100644 --- a/.planning/codebase/STRUCTURE.md +++ b/.planning/codebase/STRUCTURE.md @@ -1,243 +1,123 @@ # Codebase Structure -**Analysis Date:** 2026-01-23 +**Analysis Date:** 2026-02-09 ## Directory Layout ``` -groundkeeper/ -├── src/ # Legacy Groundkeeper phases (old workflow) -│ ├── index.ts # Public exports -│ ├── cli.ts # CLI commands (scan, analyze, init, run) -│ ├── groundkeeper.ts # Main orchestrator -│ ├── types.ts # Type definitions -│ ├── config.ts # Configuration loader -│ ├── phases/ # Workflow phases -│ │ ├── Phase.ts # Base class -│ │ ├── ScanPhase.ts # Repository scanning -│ │ ├── AnalyzePhase.ts # Finding generation -│ │ ├── PlanPhase.ts # Action planning -│ │ ├── ProposePhase.ts # Proposal generation -│ │ ├── ActPhase.ts # Execution/reporting -│ │ └── index.ts # Phase exports -│ └── __tests__/ # Phase and CLI tests -├── packages/ -│ ├── cli/ # Main CLI entry point (new workflow) -│ │ └── src/ -│ │ └── index.ts # CLI runner with stage orchestration -│ ├── core/ # Core domain logic (shared) -│ │ └── src/ -│ │ ├── index.ts # Public exports -│ │ ├── stage.ts # Stage type definitions -│ │ ├── runner.ts # Stage pipeline executor -│ │ ├── schema.ts # JSON schema validator -│ │ ├── config.ts # Agent config loader -│ │ ├── repo.ts # Repository fact collection -│ │ ├── scope.ts # Scope enforcement -│ │ ├── budgets.ts # Budget tracking -│ │ └── prompt.ts # Prompt template loading/building -│ ├── runtimes/ # LLM provider implementations -│ │ ├── openai/ -│ │ │ └── src/ -│ │ │ └── index.ts # OpenAI API runtime -│ │ └── copilot/ -│ │ └── src/ -│ │ └── index.ts # GitHub Copilot runtime -│ └── schemas/ # JSON schemas for stage I/O -│ ├── agent.schema.json # Agent configuration schema -│ └── stages/ -│ ├── preflight.input.schema.json -│ ├── preflight.output.schema.json -│ ├── analysis.input.schema.json -│ ├── analysis.output.schema.json -│ ├── plan.input.schema.json -│ ├── plan.output.schema.json -│ ├── patch.input.schema.json -│ ├── patch.output.schema.json -│ ├── verify.input.schema.json -│ ├── verify.output.schema.json -│ ├── publish.input.schema.json -│ └── publish.output.schema.json -├── prompts/ # LLM prompt templates -│ ├── system.md # System prompt -│ ├── analysis.md # Analysis stage prompt -│ ├── plan.md # Planning stage prompt -│ ├── patch.md # Patch generation prompt -│ ├── verify.md # Verification stage prompt -│ └── publish.md # Publishing stage prompt -├── docs/ # Documentation -├── examples/ # Example configurations -├── .github/ # GitHub Actions workflows -├── .groundkeeper/ # Output directory (generated) -├── .planning/ # Planning docs -│ └── codebase/ # Codebase analysis -├── package.json # Root dependencies -├── tsconfig.json # TypeScript configuration -├── groundkeeper.yml # Groundkeeper config (legacy) -├── jest.config.js # Jest testing config -└── action.yml # GitHub Actions definition +[project-root]/ +├── action.yml # GitHub Action entrypoint +├── dist/ # Compiled build output +├── docs/ # Architecture/concept docs +├── examples/ # Sample configs +├── packages/ # Monorepo packages +│ ├── cli/ # CLI package +│ ├── core/ # Core library +│ ├── runtimes/ # LLM provider adapters +│ └── schemas/ # JSON schemas +├── prompts/ # Stage prompt templates +├── .github/ # Workflow and config defaults +├── .planning/ # Planning artifacts +├── package.json # Workspace manifest +├── tsconfig.json # TypeScript build config +└── jest.config.js # Test config ``` ## Directory Purposes -**`src/`:** -- Purpose: Legacy Groundkeeper workflow implementation (phases-based) -- Contains: Phase implementations, CLI commands, type definitions, configuration -- Key files: `groundkeeper.ts` (orchestrator), `cli.ts` (commands) -- Status: Active but less preferred; new workflows use `packages/cli` - -**`packages/cli/`:** -- Purpose: Main CLI entry point for deterministic agent workflow -- Contains: Stage orchestration, runtime creation, report generation -- Key files: `src/index.ts` (all CLI logic) -- Status: Primary implementation path - -**`packages/core/`:** -- Purpose: Shared domain logic and stage infrastructure -- Contains: Stage definitions, schema validation, configuration, repository analysis -- Key files: `stage.ts` (types), `runner.ts` (executor), `schema.ts` (validator) -- Status: Core shared library - -**`packages/runtimes/`:** -- Purpose: LLM provider adapters -- Contains: OpenAI and Copilot SDK integrations -- Files: `openai/src/index.ts`, `copilot/src/index.ts` -- Status: Pluggable; new providers added here - -**`packages/schemas/`:** -- Purpose: JSON Schema definitions for stage validation -- Contains: Input/output schemas for all 6 stages -- Files: Named `{stage-name}.{input|output}.schema.json` -- Status: Source of truth for stage I/O contracts - -**`prompts/`:** -- Purpose: LLM prompt templates -- Contains: Markdown-formatted prompts for each stage -- Files: `system.md`, `{stage-name}.md` -- Usage: Loaded by `packages/core/src/prompt.ts` - -**`tests/`:** -- Purpose: Test suites for phases and CLI -- Location: `src/__tests__/` -- Files: `*.test.ts`, `*.spec.ts` -- Config: `jest.config.js` +**packages/cli:** +- Purpose: CLI entrypoint and orchestration. +- Contains: `packages/cli/src/index.ts`. +- Key files: `packages/cli/src/index.ts`. + +**packages/core:** +- Purpose: Shared stage/config/scope/budget/schema logic. +- Contains: Core primitives and helpers. +- Key files: `packages/core/src/runner.ts`, `packages/core/src/stage.ts`, `packages/core/src/config.ts`. + +**packages/runtimes:** +- Purpose: Provider-specific LLM runtimes. +- Contains: OpenAI and Copilot adapters. +- Key files: `packages/runtimes/openai/src/index.ts`, `packages/runtimes/copilot/src/index.ts`. + +**packages/schemas:** +- Purpose: JSON schema contracts for config and stage I/O. +- Contains: `agent.schema.json`, stage schemas in `packages/schemas/stages/*.json`. +- Key files: `packages/schemas/agent.schema.json`, `packages/schemas/stages/*.json`. + +**prompts:** +- Purpose: Prompt templates used by stages. +- Contains: Markdown prompt files per stage. +- Key files: `prompts/system.md`, `prompts/analysis.md`, `prompts/plan.md`, `prompts/patch.md`, `prompts/verify.md`, `prompts/publish.md`. + +**docs:** +- Purpose: Project documentation. +- Contains: Architecture and concept docs. +- Key files: `docs/ARCHITECTURE.md`, `docs/CONCEPT.md`. + +**examples:** +- Purpose: Sample configuration files. +- Contains: `examples/node/agent.yml`. +- Key files: `examples/node/agent.yml`. ## Key File Locations **Entry Points:** -- `packages/cli/src/index.ts`: Main CLI for new workflow (stages-based) -- `src/cli.ts`: Legacy CLI for old workflow (phases-based) -- `src/groundkeeper.ts`: Programmatic entry for phase-based workflow +- `packages/cli/src/index.ts`: CLI executable and stage orchestration. +- `action.yml`: GitHub Action wrapper for running the CLI. **Configuration:** -- `packages/core/src/config.ts`: Loads and validates `agent.yml` -- `src/config.ts`: Loads and validates `groundkeeper.yml` -- `packages/core/src/schema.ts`: JSON schema validation engine +- `.github/agent.yml`: Default agent configuration (repo-local). +- `packages/schemas/agent.schema.json`: JSON schema for config validation. +- `tsconfig.json`: TypeScript build configuration. +- `package.json`: NPM scripts and CLI bin mapping. **Core Logic:** -- `packages/core/src/stage.ts`: Stage type definitions and contracts -- `packages/core/src/runner.ts`: Stage pipeline executor with logging -- `packages/core/src/repo.ts`: Repository fact collection (Node version, package manager, etc.) -- `src/phases/Phase.ts`: Base class for all phase implementations +- `packages/core/src/runner.ts`: Stage execution and schema validation. +- `packages/core/src/stage.ts`: Stage contracts and context types. +- `packages/core/src/config.ts`: Config loading and validation. +- `packages/core/src/scope.ts`: Scope enforcement. +- `packages/core/src/budgets.ts`: Budget enforcement. **Testing:** -- `src/__tests__/cli.test.ts`: CLI command tests -- `src/__tests__/groundkeeper.test.ts`: Orchestrator tests -- `src/__tests__/config.test.ts`: Configuration tests - -**Outputs:** -- `.groundkeeper/logs/`: Stage input/output JSON logs (created at runtime) -- `.groundkeeper/report.md`: Final report -- `.groundkeeper/publish.json`: Publish payload if enabled +- `jest.config.js`: Test patterns and coverage configuration. +- `packages/**/__tests__/**/*.ts`: Primary test location (per `jest.config.js`). +- `packages/**/*.test.ts`: Alternate test naming (per `jest.config.js`). ## Naming Conventions **Files:** -- Phase files: `{PhaseName}Phase.ts` (e.g., `ScanPhase.ts`, `AnalyzePhase.ts`) -- Schema files: `{stage-name}.{input|output}.schema.json` -- Prompt files: `{stage-name}.md` or `system.md` -- Test files: `*.test.ts` or `*.spec.ts` -- Config files: `*.yml`, `*.yaml`, or `*schema.json` +- Lowercase TypeScript modules (e.g., `packages/core/src/runner.ts`). +- Stage schema files use `..schema.json` (e.g., `packages/schemas/stages/analysis.input.schema.json`). +- Prompt templates use `.md` (e.g., `prompts/plan.md`). **Directories:** -- Phases: lowercase `phases/` -- Sources: lowercase `src/` -- Tests: `__tests__/` or alongside source with `.test.ts` suffix -- Schemas: lowercase `schemas/` - -**Classes:** -- PascalCase: `Phase`, `ScanPhase`, `AnalyzePhase`, `Groundkeeper`, `ConfigLoader` - -**Functions:** -- camelCase: `collectRepoFacts()`, `validateSchema()`, `runStages()`, `checkBudgets()` +- Lowercase package directories (e.g., `packages/cli`, `packages/core`). -**Types:** -- PascalCase: `ScanResult`, `AnalysisResult`, `PhaseResult`, `StageDefinition`, `Finding` +## Where to Add New Code -**Interfaces:** -- PascalCase with I prefix for contracts or no prefix for data: `GroundkeeperContext`, `Finding`, `Change` +**New Feature:** +- Primary code: `packages/core/src/` for shared logic or `packages/cli/src/` for CLI behavior. +- Tests: `packages/**/__tests__/**/*.ts` or `packages/**/*.test.ts` (per `jest.config.js`). -## Where to Add New Code +**New Component/Module:** +- Implementation: `packages//src/.ts`. -**New Phase (Legacy Workflow):** -- Implementation: `src/phases/{NewName}Phase.ts` -- Extend: `Phase` base class from `src/phases/Phase.ts` -- Export: Add to `src/phases/index.ts` -- Register: Add to phase array in `src/groundkeeper.ts` (createPhases method) -- Test: `src/__tests__/{newname}.test.ts` - -**New Stage (New Workflow):** -- Entry point: `packages/cli/src/index.ts` (buildStages function) -- Schema files: `packages/schemas/stages/{stage-name}.input.schema.json` and `.output.schema.json` -- Prompt: `prompts/{stage-name}.md` -- Implementation: Logic in stage `run()` function in `packages/cli/src/index.ts` or extracted to separate module - -**New CLI Command:** -- Location: `packages/cli/src/index.ts` (for new workflow) or `src/cli.ts` (for legacy) -- Pattern: Use `program.command()` chain with `.action()` handler -- Options: Add with `.option()` before `.action()` - -**New LLM Runtime Provider:** -- Location: `packages/runtimes/{provider-name}/src/index.ts` -- Interface: Implement `{ generateJson(messages: LlmMessage[]): Promise }` -- Export: Create factory function `create{Provider}Runtime(options)` -- Register: Add creation logic in `createRuntime()` function in `packages/cli/src/index.ts` - -**Utility Functions:** -- Shared helpers in `packages/core/src/`: e.g., `repo.ts`, `budgets.ts`, `scope.ts` -- Phase-specific in `src/phases/`: nested helper methods -- CLI-specific in `packages/cli/src/index.ts`: helper functions at module level - -**Type Definitions:** -- Legacy types: `src/types.ts` -- Core types: `packages/core/src/stage.ts`, `packages/core/src/config.ts` -- Runtime types: `packages/runtimes/{provider}/src/index.ts` +**Utilities:** +- Shared helpers: `packages/core/src/`. ## Special Directories -**`.groundkeeper/`:** -- Purpose: Output directory for reports and logs -- Generated: Yes (created at runtime) -- Committed: No (ignored by .gitignore) -- Contents: `report.md`, `publish.json`, `logs/{stage}.input.json`, `logs/{stage}.output.json` - -**`.github/`:** -- Purpose: GitHub configuration and workflows -- Generated: No -- Committed: Yes -- Contents: GitHub Actions workflows, issue templates - -**`dist/`:** -- Purpose: Compiled JavaScript output -- Generated: Yes (`npm run build`) -- Committed: No (ignored by .gitignore) -- Contents: Compiled `.js` files and `.d.ts` type definitions - -**`node_modules/`:** -- Purpose: Installed dependencies -- Generated: Yes (`npm install`) -- Committed: No (ignored by .gitignore) +**dist:** +- Purpose: Build artifacts produced by `tsc`. +- Generated: Yes. +- Committed: Yes. + +**.groundkeeper:** +- Purpose: Runtime outputs (logs, report, publish payload). +- Generated: Yes (created by `packages/cli/src/index.ts`). +- Committed: No. --- -*Structure analysis: 2026-01-23* +*Structure analysis: 2026-02-09* diff --git a/.planning/codebase/TESTING.md b/.planning/codebase/TESTING.md index 7ed9e04..c232d54 100644 --- a/.planning/codebase/TESTING.md +++ b/.planning/codebase/TESTING.md @@ -1,285 +1,118 @@ # Testing Patterns -**Analysis Date:** 2026-01-23 +**Analysis Date:** 2026-02-09 ## Test Framework **Runner:** -- Jest 29.7.0 -- TypeScript support via ts-jest 29.1.1 +- Jest 29.7.0 with ts-jest 29.1.1 from `package.json` - Config: `jest.config.js` **Assertion Library:** -- Jest's built-in matchers (expect API) +- Jest `expect` (documented example in `CONTRIBUTING.md`) **Run Commands:** ```bash -npm test # Run all tests -npm test -- --watch # Watch mode -npm test -- --coverage # Coverage report +npm test # Run all tests (`package.json`) +Not configured # Watch mode script not present in `package.json` +Not configured # Coverage script not present in `package.json` ``` ## Test File Organization **Location:** -- Tests are co-located in `src/__tests__/` directory (separate from implementation) -- Test files match implementation scope: - - `src/__tests__/groundkeeper.test.ts` for `src/groundkeeper.ts` - - `src/__tests__/config.test.ts` for `src/config.ts` - - `src/__tests__/cli.test.ts` for `src/cli.ts` +- Jest roots set to `src` and testMatch targets `**/__tests__/**/*.ts` plus `**/?(*.)+(spec|test).ts` in `jest.config.js`; no matching test files detected under `packages/**/*.ts` **Naming:** -- Pattern: `[module-name].test.ts` -- All tests use `.test.ts` suffix (not `.spec.ts`) +- Use `*.test.ts` or `*.spec.ts` or `__tests__` directory per `jest.config.js` **Structure:** ``` -src/ -├── __tests__/ -│ ├── groundkeeper.test.ts (97 lines) -│ ├── config.test.ts (133 lines) -│ └── cli.test.ts (68 lines) -├── groundkeeper.ts -├── config.ts -├── cli.ts -└── ... -``` - -Jest config targets tests: -```javascript -testMatch: ['**/__tests__/**/*.ts', '**/?(*.)+(spec|test).ts'] +src/__tests__/**/*.ts +src/**/?(*.)+(spec|test).ts ``` ## Test Structure **Suite Organization:** -Tests use Jest's `describe()` blocks organized by class/module and sub-organized by method: - ```typescript -describe('Groundkeeper', () => { - describe('run', () => { - it('should execute all phases successfully', async () => { - // Test implementation - }); - }); +describe('MyFeature', () => { + it('should do something specific', () => { + // Arrange + const input = 'test'; + + // Act + const result = myFunction(input); - describe('runPhases', () => { - it('should execute only specified phases', async () => { - // Test implementation - }); + // Assert + expect(result).toBe('expected'); }); }); ``` **Patterns:** - -Setup (beforeEach): -```typescript -beforeEach(() => { - tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'groundkeeper-test-')); - - // Create test artifacts - const configContent = `version: '1.0'...`; - fs.writeFileSync(path.join(tempDir, 'groundkeeper.yml'), configContent); - fs.writeFileSync(path.join(tempDir, 'README.md'), '# Test Project'); -}); -``` - -Teardown (afterEach): -```typescript -afterEach(() => { - if (fs.existsSync(tempDir)) { - fs.rmSync(tempDir, { recursive: true }); - } -}); -``` - -Assertion Pattern: -```typescript -expect(results).toHaveLength(5); -expect(results[0].phase).toBe('scan'); -expect(results[0].success).toBe(true); -expect(() => ConfigLoader.validateConfig(config)).toThrow('Configuration must have a version'); -expect(fs.existsSync(reportPath)).toBe(true); -``` +- Setup pattern: Not detected beyond the documented example in `CONTRIBUTING.md` +- Teardown pattern: Not detected beyond the documented example in `CONTRIBUTING.md` +- Assertion pattern: Jest `expect` as shown in `CONTRIBUTING.md` ## Mocking -**Framework:** Jest's built-in mocking (no external mocking library) +**Framework:** Jest (no mocks detected in `packages/**/*.ts` and only a basic example in `CONTRIBUTING.md`) **Patterns:** - -File system operations are not mocked; temporary directories created with `fs.mkdtempSync()`: ```typescript -// From config.test.ts -tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'groundkeeper-test-')); -const configPath = path.join(tempDir, 'groundkeeper.yml'); -fs.writeFileSync(configPath, configContent); -``` - -Command execution not mocked in CLI tests; uses `execSync` for integration testing: -```typescript -// From cli.test.ts -const output = execSync(`node ${cliPath} init -d ${tempDir}`, { - encoding: 'utf8', -}); +// Not detected in repository source or tests (`packages/**/*.ts`). ``` **What to Mock:** -- External HTTP calls (none currently in codebase; would use `jest.mock()`) -- Timer-based operations (none currently used) -- GitHub API interactions (when implemented) +- Not specified in code; no guidance beyond general testing notes in `CONTRIBUTING.md` **What NOT to Mock:** -- File system operations (use real temp directories instead for clarity) -- Phase execution logic (test the actual phases) -- Configuration loading (test with real YAML files) +- Not specified in code; no guidance beyond general testing notes in `CONTRIBUTING.md` ## Fixtures and Factories **Test Data:** - -Config fixtures created inline in tests: ```typescript -// From config.test.ts, lines 22-30 -const configContent = ` -version: '1.0' -repository: - defaultBranch: main -tasks: - - id: test - type: test-type - enabled: true -`; -fs.writeFileSync(configPath, configContent); +// Not detected in repository source or tests (`packages/**/*.ts`). ``` -Standard fixtures: -- Default YAML config for Groundkeeper tests -- README.md file -- LICENSE file -- Minimal package.json if needed - **Location:** -- Test fixtures created dynamically within test files -- No separate fixtures directory; fixtures are YAML strings or created files in temp directories -- All fixtures properly cleaned up in afterEach +- Not detected in repository; no fixture directories referenced outside `CONTRIBUTING.md` ## Coverage -**Requirements:** No coverage threshold enforced +**Requirements:** +- None enforced; only collection targets configured in `jest.config.js` **View Coverage:** ```bash -npm test -- --coverage -``` - -Jest configuration collects coverage from: -```javascript -collectCoverageFrom: [ - 'src/**/*.ts', - '!src/**/*.d.ts', - '!src/**/*.test.ts', - '!src/**/*.spec.ts', -] +Not configured # No coverage script in `package.json` ``` ## Test Types **Unit Tests:** -- Scope: Individual modules (ConfigLoader, Phase classes) -- Approach: Test methods in isolation with mocked/temporary state -- Examples: - - `config.test.ts`: Tests `loadConfig()`, `validateConfig()`, `findConfigFile()` methods - - `groundkeeper.test.ts`: Tests context initialization, phase execution, and result collection -- Pattern: Arrange-Act-Assert with setup in beforeEach and cleanup in afterEach +- Not detected in repository source; test patterns only defined in `jest.config.js` **Integration Tests:** -- Scope: End-to-end phase execution and file system interactions -- Approach: Create temporary repositories with test files, run full execution, verify outputs -- Examples: - - `groundkeeper.test.ts`: Tests full `run()` execution across all phases - - CLI tests: Execute compiled CLI commands with real arguments -- Pattern: Real file creation, actual phase execution, verification of generated reports +- Not detected in repository source; no integration test files under `packages/**/*.ts` **E2E Tests:** -- Framework: Not formally used; CLI tests serve as E2E through `execSync` command execution -- Coverage: `cli.test.ts` tests actual CLI behavior with shell commands +- Not used; no E2E framework or config found in `package.json` or repo files ## Common Patterns **Async Testing:** ```typescript -// From groundkeeper.test.ts -it('should execute all phases successfully', async () => { - const groundkeeper = new Groundkeeper(tempDir); - const results = await groundkeeper.run(); - - expect(results).toHaveLength(5); -}); +// Not detected in repository source or tests (`packages/**/*.ts`). ``` **Error Testing:** ```typescript -// From config.test.ts -it('should throw error if version format is invalid', () => { - const config = { - version: 'invalid', - }; - - expect(() => ConfigLoader.validateConfig(config)) - .toThrow('Version must be in format X.Y'); -}); +// Not detected in repository source or tests (`packages/**/*.ts`). ``` -**File System Testing:** -```typescript -// From cli.test.ts -it('should create groundkeeper.yml in non-existent directory', () => { - const targetDir = path.join(tempDir, 'new-dir'); - - execSync(`node ${cliPath} init -d ${targetDir}`, { - encoding: 'utf8', - }); - - expect(fs.existsSync(path.join(targetDir, 'groundkeeper.yml'))) - .toBe(true); - - const content = fs.readFileSync( - path.join(targetDir, 'groundkeeper.yml'), - 'utf8' - ); - expect(content).toContain("version: '1.0'"); -}); -``` - -**Temp Directory Cleanup Pattern:** -Uses Node.js built-in `fs.mkdtempSync()` and `fs.rmSync()`: -```typescript -beforeEach(() => { - tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'groundkeeper-test-')); -}); - -afterEach(() => { - if (fs.existsSync(tempDir)) { - fs.rmSync(tempDir, { recursive: true }); - } -}); -``` - -## Test Coverage Summary - -**Tests written:** -- 3 test files covering core functionality -- Total test lines: 298 -- Focus areas: Configuration loading, Groundkeeper orchestration, CLI commands - -**Coverage gaps:** -- Phase implementations (ScanPhase, AnalyzePhase, PlanPhase, ProposePhase, ActPhase) have no dedicated tests -- Phase-specific logic (file scanning, analysis rules, report generation) untested -- Error recovery paths not extensively tested -- Edge cases in regex patterns (Git URL parsing) not covered - --- -*Testing analysis: 2026-01-23* +*Testing analysis: 2026-02-09*