first commit
This commit is contained in:
116
.planning/codebase/ARCHITECTURE.md
Normal file
116
.planning/codebase/ARCHITECTURE.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# Architecture
|
||||
|
||||
**Analysis Date:** 2026-03-12
|
||||
|
||||
## Pattern Overview
|
||||
|
||||
**Overall:** Agent-based task automation framework built on OpenCode
|
||||
|
||||
**Key Characteristics:**
|
||||
- Modular command/agent system with specialized agents for different phases
|
||||
- Workflow-driven execution with named phases (discovery, research, plan, execute, verify)
|
||||
- Direct file output pattern - agents write to `.planning/codebase/` directly
|
||||
- Subagent spawning for parallel exploration tasks
|
||||
|
||||
## Layers
|
||||
|
||||
**Commands Layer:**
|
||||
- Purpose: User-facing entry points for GSD operations
|
||||
- Location: `.opencode/commands/gsd/`
|
||||
- Contains: Markdown command definitions with YAML frontmatter
|
||||
- Files: 33 command files (gsd-*.md)
|
||||
- Used by: OpenCode CLI when user invokes `/gsd-*` commands
|
||||
|
||||
**Agents Layer:**
|
||||
- Purpose: Specialized AI agents for focused tasks
|
||||
- Location: `.opencode/agents/`
|
||||
- Contains: Agent role definitions with tools and skills
|
||||
- Files: 12 agent definitions (gsd-*.md)
|
||||
- Used by: Commands that need parallel or complex task execution
|
||||
|
||||
**Workflows Layer:**
|
||||
- Purpose: Execution logic connecting commands and agents
|
||||
- Location: `.opencode/get-shit-done/workflows/`
|
||||
- Contains: Step-by-step workflow definitions
|
||||
- Files: 36 workflow files
|
||||
- Depends on: Commands layer (referenced in command execution_context)
|
||||
- Used by: Command execution context
|
||||
|
||||
**Supporting Layers:**
|
||||
- Skills: `.opencode/skills/` - Specialized skills (e.g., model selection)
|
||||
- Rules: `.opencode/rules/` - Behavior rules (e.g., work-hard rule)
|
||||
- Configuration: `.opencode/package.json`, `bun.lock` - Dependencies and lockfile
|
||||
|
||||
## Data Flow
|
||||
|
||||
**Command Invocation Flow:**
|
||||
1. User invokes `/gsd-*` command
|
||||
2. Command definition loads execution_context (workflow)
|
||||
3. Workflow orchestrates agent spawning and task execution
|
||||
4. Agents write outputs directly to `.planning/` directories
|
||||
5. Confirmation returned to orchestrator (not full content)
|
||||
|
||||
**Parallel Agent Pattern:**
|
||||
- map-codebase spawns 4 parallel mapper agents (tech, arch, quality, concerns)
|
||||
- Each agent explores focus area and writes specific documents
|
||||
- Agents return confirmations only to minimize context load
|
||||
|
||||
## Key Abstractions
|
||||
|
||||
**Command Pattern:**
|
||||
- Format: Markdown with YAML frontmatter
|
||||
- Structure: name, description, argument-hint, permissions, objective, execution_context, context, when_to_use, process, success_criteria
|
||||
- Location: `.opencode/commands/gsd/gsd-{name}.md`
|
||||
|
||||
**Agent Pattern:**
|
||||
- Format: Markdown with YAML frontmatter
|
||||
- Structure: name, description, mode, tools, color, skills
|
||||
- Location: `.opencode/agents/gsd-{name}.md`
|
||||
- Types: orchestrator agents (main), subagents (parallel workers)
|
||||
|
||||
**Workflow Pattern:**
|
||||
- Format: Markdown with step-by-step instructions
|
||||
- Location: `.opencode/get-shit-done/workflows/{name}.md`
|
||||
- Called from: Command's `<execution_context>` directive
|
||||
|
||||
## Entry Points
|
||||
|
||||
**Primary Entry Point:**
|
||||
- Location: `.opencode/commands/gsd/gsd-*.md` (33 commands)
|
||||
- Triggers: OpenCode CLI `/gsd-*` invocation
|
||||
- Responsibilities: Define command interface, load workflow, return success criteria
|
||||
|
||||
**Command Categories:**
|
||||
- Project management: new-project, new-milestone, resume-work, pause-work
|
||||
- Phase management: add-phase, remove-phase, insert-phase, list-phase-assumptions
|
||||
- Execution: plan-phase, execute-phase, verify-phase, validate-phase
|
||||
- Discovery: research-phase, map-codebase, discuss-phase, quick
|
||||
- Verification: audit-milestone, complete-milestone, verify-work, health
|
||||
- Debugging: debug, diagnose-issues, reapply-patches
|
||||
- Utilities: add-todo, add-tests, set-profile, settings, help
|
||||
|
||||
## Error Handling
|
||||
|
||||
**Workflow Error Handling:**
|
||||
- Phase failures trigger diagnosis workflows
|
||||
- verify-work performs health checks on each phase
|
||||
- audit-milestone catches incomplete work before completion
|
||||
|
||||
**Agent Error Patterns:**
|
||||
- Subagent failures reported to orchestrator with error details
|
||||
- Parallel agents - one failure doesn't cascade to others
|
||||
|
||||
## Cross-Cutting Concerns
|
||||
|
||||
**State Management:**
|
||||
- Project state stored in `.planning/STATE.md`
|
||||
- Milestone data in `.gsd/milestones/`
|
||||
- Codebase docs in `.planning/codebase/`
|
||||
|
||||
**Permissions System:**
|
||||
- Commands declare required permissions (read, bash, glob, grep, write, task)
|
||||
- Agents declare tools they can use
|
||||
|
||||
---
|
||||
|
||||
*Architecture analysis: 2026-03-12*
|
||||
186
.planning/codebase/CONCERNS.md
Normal file
186
.planning/codebase/CONCERNS.md
Normal file
@@ -0,0 +1,186 @@
|
||||
# Codebase Concerns
|
||||
|
||||
**Analysis Date:** 2026-03-12
|
||||
|
||||
## Project Status: Greenfield
|
||||
|
||||
**Critical Finding:** This is a greenfield project with no actual implementation code. The repository contains only:
|
||||
|
||||
- `THE_IDEA.md` - Requirements specification document
|
||||
- `.opencode/` - OpenCode/GSD framework configuration
|
||||
|
||||
**Impact:** There are no code-level technical concerns to document because nothing has been built yet. This document outlines concerns for the future implementation.
|
||||
|
||||
---
|
||||
|
||||
## Implementation Concerns
|
||||
|
||||
### No Source Code Exists
|
||||
|
||||
**Issue:** No application code has been implemented
|
||||
- Files: None (project has not been initialized with source code)
|
||||
- Impact: Cannot assess code quality, patterns, or technical debt
|
||||
- Fix approach: Initialize project with proper scaffolding before implementation begins
|
||||
|
||||
### Missing Project Initialization
|
||||
|
||||
**Issue:** No package.json, build configuration, or source directories at project root
|
||||
- Files: `package.json`, `tsconfig.json`, `src/` missing
|
||||
- Impact: No standard development workflow established
|
||||
- Fix approach: Run project initialization to create proper project structure
|
||||
|
||||
---
|
||||
|
||||
## Hardware-Specific Concerns
|
||||
|
||||
### Low-Power Device Targeting
|
||||
|
||||
**Issue:** Target hardware is Raspberry Pi Zero 2W (limited resources)
|
||||
- Device: Pi Zero 2 W with 512MB RAM, single-core ARMv8
|
||||
- Concern: UI must be lightweight to run smoothly in Chromium Kiosk Mode
|
||||
- Mitigation needed:
|
||||
- Minimal JavaScript bundle size
|
||||
- Efficient rendering (avoid heavy frameworks if possible)
|
||||
- Consider static HTML/CSS over SPA for main interface
|
||||
- Lazy-load any heavy components
|
||||
|
||||
### Dual Display Resolution Support
|
||||
|
||||
**Issue:** Must support two different display modes
|
||||
- 7-inch touchscreen: 1024x600 resolution
|
||||
- Full HD remote access: 1920x1080 resolution
|
||||
- Concern: Responsive design complexity
|
||||
- Mitigation needed:
|
||||
- Clear responsive breakpoints
|
||||
- Different layouts for touch vs desktop
|
||||
- Performance testing on actual hardware
|
||||
|
||||
---
|
||||
|
||||
## Requirements Complexity
|
||||
|
||||
### Feature Scope (3 Milestones)
|
||||
|
||||
**Issue:** Large feature set across three milestones
|
||||
- Milestone 1: Modern compact UI
|
||||
- Milestone 2: CSV processing and workflow
|
||||
- Milestone 3: Network stack
|
||||
- Concern: Scope creep and milestone creep
|
||||
- Priority: High
|
||||
|
||||
### Data Types and Processing
|
||||
|
||||
**Issue:** Multiple sensor data types require handling
|
||||
- Rainfall (today, hourly, MAR, yearly)
|
||||
- Battery voltage
|
||||
- Solar voltage
|
||||
- Water level sensors (4-20mA, 0-10vDC)
|
||||
- GPS coordinates
|
||||
- Mobile network status
|
||||
- Concern: Data normalization and storage complexity
|
||||
|
||||
### Network Protocol Support
|
||||
|
||||
**Issue:** Multiple transfer protocols specified in requirements
|
||||
- FTP, SCP, SFTP, WEBDAV
|
||||
- TCP socket connections
|
||||
- Concern: Implementation complexity and security
|
||||
- Mitigation: Start with simplest protocol (HTTP), add others as needed
|
||||
|
||||
---
|
||||
|
||||
## Dependencies at Risk
|
||||
|
||||
### OpenCode Framework Dependency
|
||||
|
||||
**Issue:** Project configured to use OpenCode/GSD workflow
|
||||
- Package: `@opencode-ai/plugin` v1.2.24
|
||||
- Location: `.opencode/package.json`
|
||||
- Risk: Vendor lock-in to specific workflow tool
|
||||
- Mitigation: Document standard development practices that work independently
|
||||
|
||||
### Zod in node_modules
|
||||
|
||||
**Issue:** Zod library present in `.opencode/node_modules/`
|
||||
- Purpose: Likely used for configuration validation
|
||||
- Concern: Not yet integrated into project (in .)
|
||||
- Recommendation: If validationopencode only needed, use zod; otherwise remove dependency
|
||||
|
||||
---
|
||||
|
||||
## Missing Infrastructure
|
||||
|
||||
### No Testing Framework
|
||||
|
||||
**Issue:** No test files or configuration detected
|
||||
- No `jest.config.*`, `vitest.config.*`, or similar
|
||||
- No `*.test.*` or `*.spec.*` files
|
||||
- Impact: No test coverage for future implementation
|
||||
- Priority: High - Testing should be established before implementation
|
||||
|
||||
### No Linting/Formatting
|
||||
|
||||
**Issue:** No ESLint, Prettier, or biome configuration
|
||||
- Impact: Inconsistent code style as team grows
|
||||
- Priority: Medium
|
||||
|
||||
### No CI/CD Configuration
|
||||
|
||||
**Issue:** No GitHub Actions, GitLab CI, or similar
|
||||
- Impact: No automated testing or deployment
|
||||
- Priority: Low (can be added later)
|
||||
|
||||
---
|
||||
|
||||
## Security Considerations (Future)
|
||||
|
||||
### Network Credentials Storage
|
||||
|
||||
**Issue:** Requirements mention FTP/SCP credentials
|
||||
- Files referenced: `THE_IDEA.md` lines 175-184
|
||||
- Concern: Plain-text credentials in configuration
|
||||
- Future mitigation:
|
||||
- Use environment variables, not config files
|
||||
- Consider OAuth/API keys over username/password
|
||||
- Encrypt any stored credentials
|
||||
|
||||
### GPRS/Mobile Network
|
||||
|
||||
**Issue:** Mobile settings stored in system
|
||||
- Location: `THE_IDEA.md` section 1.7
|
||||
- Concern: Phone numbers and device IDs stored
|
||||
- Future mitigation: Encrypt sensitive station data
|
||||
|
||||
---
|
||||
|
||||
## Performance Targets
|
||||
|
||||
### Startup Time
|
||||
|
||||
**Target:** Fast enough for Pi Zero 2W Chromium Kiosk
|
||||
- Concern: Cold start must be under 3 seconds
|
||||
- Mitigation: Minimize JavaScript, use progressive loading
|
||||
|
||||
### Memory Usage
|
||||
|
||||
**Target:** Stay within 256MB RAM for UI
|
||||
- Concern: Chromium + web app + system services
|
||||
- Mitigation: Avoid large in-memory data structures
|
||||
|
||||
---
|
||||
|
||||
## Summary of Priorities
|
||||
|
||||
| Concern | Priority | Status |
|
||||
|---------|----------|--------|
|
||||
| No source code exists | Critical | Not started |
|
||||
| No testing framework | High | Not started |
|
||||
| No project initialization | High | Not started |
|
||||
| Hardware performance | High | Future |
|
||||
| Requirements scope | Medium | Planned |
|
||||
| No CI/CD | Low | Not started |
|
||||
| Security hardening | Medium | Future |
|
||||
|
||||
---
|
||||
|
||||
*Concerns audit: 2026-03-12*
|
||||
239
.planning/codebase/CONVENTIONS.md
Normal file
239
.planning/codebase/CONVENTIONS.md
Normal file
@@ -0,0 +1,239 @@
|
||||
# Coding Conventions
|
||||
|
||||
**Analysis Date:** 2026-03-12
|
||||
|
||||
## Language
|
||||
|
||||
**Primary:** JavaScript (Node.js)
|
||||
- All source files use CommonJS (`.cjs` extension)
|
||||
- ESM imports used in test files with `.test.cjs` extension
|
||||
|
||||
## File Naming
|
||||
|
||||
**Pattern:** kebab-case
|
||||
- Commands: `allow-read-config.cjs`, `get-profile.cjs`, `set-profile.cjs`
|
||||
- Libraries: `oc-core.cjs`, `oc-config.cjs`, `oc-models.cjs`
|
||||
- Tests: `get-profile.test.cjs`, `allow-read-config.test.cjs`
|
||||
|
||||
**Directories:**
|
||||
- Commands: `bin/gsd-oc-commands/`
|
||||
- Libraries: `bin/gsd-oc-lib/` or `bin/lib/`
|
||||
- Tests: `bin/test/`
|
||||
|
||||
## Code Style
|
||||
|
||||
### Formatting
|
||||
|
||||
**No explicit formatter configured.** Code uses:
|
||||
- 2-space indentation
|
||||
- Consistent brace placement
|
||||
- Max line length ~100 characters
|
||||
|
||||
**Example from `bin/gsd-oc-commands/get-profile.cjs`:**
|
||||
```javascript
|
||||
function getProfile(cwd, args) {
|
||||
const verbose = args.includes('--verbose');
|
||||
const raw = args.includes('--raw');
|
||||
const log = verbose ? (...args) => console.error('[get-profile]', ...args) : () => {};
|
||||
// ...
|
||||
}
|
||||
```
|
||||
|
||||
### Linting
|
||||
|
||||
**No ESLint or Prettier configuration detected.**
|
||||
|
||||
## Import Organization
|
||||
|
||||
**Order in source files:**
|
||||
1. Node.js built-ins (`fs`, `path`, `os`)
|
||||
2. Local modules relative paths
|
||||
|
||||
**Example from `bin/gsd-oc-commands/allow-read-config.cjs`:**
|
||||
```javascript
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const os = require('os');
|
||||
const { output, error, createBackup } = require('../gsd-oc-lib/oc-core.cjs');
|
||||
```
|
||||
|
||||
## Function Design
|
||||
|
||||
### Naming
|
||||
|
||||
- Commands: lowercase with hyphens (e.g., `getProfile`, `allowReadConfig`)
|
||||
- Helper functions: camelCase
|
||||
- Error code constants: UPPER_SNAKE_CASE
|
||||
|
||||
### Documentation
|
||||
|
||||
**Every file includes a JSDoc-style header:**
|
||||
```javascript
|
||||
/**
|
||||
* get-profile.cjs — Retrieve profile definitions from oc_config.json
|
||||
*
|
||||
* Command module that exports getProfile(cwd, args) function with two operation modes:
|
||||
* 1. No parameters: Returns current profile definition
|
||||
* 2. Profile name parameter: Returns specified profile definition
|
||||
*
|
||||
* Output format: JSON envelope {success: true, data: {...}}
|
||||
* Flags: --raw (output raw JSON without envelope), --verbose (output diagnostics to stderr)
|
||||
*
|
||||
* Usage: node get-profile.cjs [profile-name] [--raw] [--verbose]
|
||||
*/
|
||||
```
|
||||
|
||||
**Function JSDoc:**
|
||||
```javascript
|
||||
/**
|
||||
* Main command function
|
||||
*
|
||||
* @param {string} cwd - Current working directory
|
||||
* @param {string[]} args - Command line arguments
|
||||
*/
|
||||
function getProfile(cwd, args) {
|
||||
```
|
||||
|
||||
## Error Handling
|
||||
|
||||
### Error Codes
|
||||
|
||||
**Pattern:** Constant object with error codes, used with custom error function
|
||||
|
||||
From `bin/gsd-oc-lib/oc-profile-config.cjs`:
|
||||
```javascript
|
||||
const ERROR_CODES = {
|
||||
CONFIG_NOT_FOUND: 'CONFIG_NOT_FOUND',
|
||||
INVALID_JSON: 'INVALID_JSON',
|
||||
PROFILE_NOT_FOUND: 'PROFILE_NOT_FOUND',
|
||||
INVALID_MODELS: 'INVALID_MODELS',
|
||||
INCOMPLETE_PROFILE: 'INCOMPLETE_PROFILE',
|
||||
WRITE_FAILED: 'WRITE_FAILED',
|
||||
APPLY_FAILED: 'APPLY_FAILED',
|
||||
ROLLBACK_FAILED: 'ROLLBACK_FAILED'
|
||||
};
|
||||
```
|
||||
|
||||
### Output Pattern
|
||||
|
||||
**Success output:** JSON envelope with `success: true` and `data`
|
||||
```javascript
|
||||
output({ success: true, data: result });
|
||||
```
|
||||
|
||||
**Error output:** Uses centralized `error()` function from `oc-core.cjs`
|
||||
```javascript
|
||||
error('current_oc_profile not set in oc_config.json', 'MISSING_CURRENT_PROFILE');
|
||||
```
|
||||
|
||||
From `bin/gsd-oc-lib/oc-core.cjs`:
|
||||
```javascript
|
||||
function error(message, code = 'UNKNOWN_ERROR') {
|
||||
const errorEnvelope = {
|
||||
success: false,
|
||||
error: {
|
||||
code,
|
||||
message
|
||||
}
|
||||
};
|
||||
console.error(JSON.stringify(errorEnvelope, null, 2));
|
||||
process.exit(1);
|
||||
}
|
||||
```
|
||||
|
||||
## Return Patterns
|
||||
|
||||
### Result Objects
|
||||
|
||||
**Pattern:** Return object with `success: boolean` and either `data` or `error`
|
||||
|
||||
From `bin/gsd-oc-lib/oc-profile-config.cjs`:
|
||||
```javascript
|
||||
function loadOcProfileConfig(cwd) {
|
||||
try {
|
||||
const configPath = path.join(cwd, '.planning', 'oc_config.json');
|
||||
if (!fs.existsSync(configPath)) {
|
||||
return {
|
||||
success: false,
|
||||
error: {
|
||||
code: ERROR_CODES.CONFIG_NOT_FOUND,
|
||||
message: `.planning/oc_config.json not found at ${configPath}`
|
||||
}
|
||||
};
|
||||
}
|
||||
return { success: true, config, configPath };
|
||||
} catch (err) {
|
||||
// ...
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Module Design
|
||||
|
||||
### Exports
|
||||
|
||||
**Pattern:** Single function export via `module.exports`
|
||||
```javascript
|
||||
module.exports = getProfile;
|
||||
```
|
||||
|
||||
### Command Module Structure
|
||||
|
||||
1. JSDoc header with description
|
||||
2. Require statements
|
||||
3. Error code constants
|
||||
4. Helper functions
|
||||
5. Main command function
|
||||
6. `module.exports`
|
||||
|
||||
## CLI Patterns
|
||||
|
||||
### Flag Handling
|
||||
|
||||
**Pattern:** Simple array filtering
|
||||
```javascript
|
||||
const verbose = args.includes('--verbose');
|
||||
const raw = args.includes('--raw');
|
||||
const profileArgs = args.filter(arg => !arg.startsWith('--'));
|
||||
```
|
||||
|
||||
### Output Functions
|
||||
|
||||
From `bin/gsd-oc-lib/oc-core.cjs`:
|
||||
```javascript
|
||||
function output(result, raw = false, rawValue = null) {
|
||||
let outputStr;
|
||||
if (raw && rawValue !== null) {
|
||||
outputStr = rawValue;
|
||||
} else {
|
||||
outputStr = JSON.stringify(result, null, 2);
|
||||
}
|
||||
// Large payload handling (>50KB)
|
||||
if (outputStr.length > 50 * 1024) {
|
||||
const tempFile = path.join(require('os').tmpdir(), `gsd-oc-${Date.now()}.json`);
|
||||
fs.writeFileSync(tempFile, outputStr, 'utf8');
|
||||
console.log(`@file:${tempFile}`);
|
||||
} else {
|
||||
console.log(outputStr);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Logging
|
||||
|
||||
### Verbose Logging Pattern
|
||||
|
||||
**Conditional logging based on flag:**
|
||||
```javascript
|
||||
const log = verbose ? (...args) => console.error('[get-profile]', ...args) : () => {};
|
||||
log('Loading oc_config.json');
|
||||
log(`Config loaded from ${configPath}`);
|
||||
```
|
||||
|
||||
## Testing Conventions
|
||||
|
||||
Tests co-located in `bin/test/` directory with `.test.cjs` suffix.
|
||||
|
||||
---
|
||||
|
||||
*Convention analysis: 2026-03-12*
|
||||
190
.planning/codebase/INTEGRATIONS.md
Normal file
190
.planning/codebase/INTEGRATIONS.md
Normal file
@@ -0,0 +1,190 @@
|
||||
# External Integrations
|
||||
|
||||
**Analysis Date:** 2026-03-12
|
||||
|
||||
## Project Status
|
||||
|
||||
This is a **greenfield project** - no implementation code exists yet. The following analysis is based on the requirements defined in `THE_IDEA.md`.
|
||||
|
||||
---
|
||||
|
||||
## Hardware Interfaces
|
||||
|
||||
### Sensors
|
||||
|
||||
**Rainfall Sensors:**
|
||||
- Rainfall-1 ID (RF) - Primary rainfall sensor (e.g., `123456RF`)
|
||||
- Rainfall-2 ID - Secondary rainfall sensor
|
||||
- Tip bucket counting for rainfall accumulation
|
||||
|
||||
**Water Level Sensors:**
|
||||
- 4-20mA type sensors (Channels 1-2)
|
||||
- 0-10vDC type sensors (Channels 3-4)
|
||||
- SDI-12 interface for additional sensors
|
||||
|
||||
**Environmental Sensors:**
|
||||
- Solar voltage monitoring
|
||||
- Battery voltage monitoring
|
||||
- Soil moisture sensors (SDI-12)
|
||||
- Evapotranspiration sensors
|
||||
|
||||
### GPS Module
|
||||
|
||||
- USB GPS module for location data
|
||||
- Latitude/longitude retrieval
|
||||
- Manual coordinate setting as fallback
|
||||
|
||||
---
|
||||
|
||||
## Data Storage
|
||||
|
||||
### Local Storage
|
||||
|
||||
**CSV Files:**
|
||||
- Data logging to local filesystem
|
||||
- File naming: `SP8020_FTP.txt`, `SP8021_FTP.txt`, etc.
|
||||
- Flash memory management for stored files
|
||||
- File operations: Navigate, View, Delete, Save
|
||||
|
||||
**Data Types Logged:**
|
||||
- Rainfall (today, hourly, monthly accumulated, yearly)
|
||||
- Water levels
|
||||
- Battery voltage
|
||||
- Solar voltage
|
||||
- Sensor readings (ADC channels)
|
||||
|
||||
---
|
||||
|
||||
## Network & External Services
|
||||
|
||||
### Data Server (myvscada)
|
||||
|
||||
**Server Details:**
|
||||
- Remote server for receiving CSV data
|
||||
- Location: Configurable IP address
|
||||
- Protocol: FTP, SCP, SFTP, or WEBDAV
|
||||
|
||||
**Transfer Flow:**
|
||||
```
|
||||
RTU → CSV file → myvscada server → received folder → processed/error folder
|
||||
```
|
||||
|
||||
### Mobile Network (GPRS)
|
||||
|
||||
**GPRS Module:**
|
||||
- Mobile network connectivity for remote transmission
|
||||
- RTU Mobile number configuration
|
||||
- Referenced in "Mobile Setting" submenu (THE_IDEA.md section 1.7)
|
||||
|
||||
### Protocols Supported
|
||||
|
||||
| Protocol | Purpose | Reference |
|
||||
|----------|---------|-----------|
|
||||
| FTP | CSV file transfer | Primary protocol mentioned |
|
||||
| SCP | Secure copy | Section 1.6 |
|
||||
| SFTP | Secure FTP | Section 1.6 |
|
||||
| WebDAV | Web-based file access | Section 1.6 |
|
||||
| TCP Socket | Real-time data requests | Section 1.6 |
|
||||
|
||||
---
|
||||
|
||||
## Authentication & Identity
|
||||
|
||||
**Station Configuration:**
|
||||
- Station ID (e.g., `D007`)
|
||||
- Device version number (e.g., `v4.0.4`)
|
||||
- Login status for remote access
|
||||
|
||||
**Network Credentials:**
|
||||
- FTP/SCP username and password
|
||||
- Referenced in THE_IDEA.md section 1.6 (Protocol Setting)
|
||||
- **Note:** Credentials should be stored securely (env vars, not plain config)
|
||||
|
||||
---
|
||||
|
||||
## Alert Systems
|
||||
|
||||
### SMS/Call Alerts
|
||||
|
||||
**Contact Types:**
|
||||
- Master phones (2 numbers)
|
||||
- SubMaster phones (2 numbers)
|
||||
- Engineer phones (up to 10 numbers)
|
||||
- RTU Mobile number
|
||||
|
||||
**Alert Triggers:**
|
||||
- Danger rainfall threshold (configurable mm per hour)
|
||||
- Warning rainfall threshold
|
||||
- Water level thresholds (Danger, Warning, Alert, Normal)
|
||||
|
||||
---
|
||||
|
||||
## Display Interfaces
|
||||
|
||||
### Local Touchscreen
|
||||
|
||||
- **Resolution:** 1024x600
|
||||
- **Connection:** Direct video output (HDMI/CSI)
|
||||
- **Browser:** Chromium Kiosk Mode
|
||||
- **URL:** `http://pihostname:8080`
|
||||
|
||||
### Remote Web Access
|
||||
|
||||
- **Resolution:** Full HD (1920x1080)
|
||||
- **Authentication:** Login required
|
||||
- **URL:** `http://pihostname:9090/`
|
||||
|
||||
---
|
||||
|
||||
## Communication Status
|
||||
|
||||
**Monitoring Data:**
|
||||
- Communication status display (e.g., `0ASU/-113dBm(0%)`)
|
||||
- Signal strength (dBm)
|
||||
- Connection percentage
|
||||
|
||||
---
|
||||
|
||||
## Environment Configuration
|
||||
|
||||
### Required Configuration Items
|
||||
|
||||
**Network:**
|
||||
- Local IP Address
|
||||
- Subnet Mask
|
||||
- Gateway
|
||||
- DNS Server
|
||||
- MAC Address
|
||||
- Transfer Protocol (FTP/SCP/SFTP/WEBDAV)
|
||||
|
||||
**Server:**
|
||||
- Server IP Address
|
||||
- TCP Port
|
||||
- File Directory (e.g., `/myvscada/stationA`)
|
||||
|
||||
**Sensors:**
|
||||
- ADC channel types (4-20mA, 0-10vDC)
|
||||
- Sensor datum offsets
|
||||
- Sensor ranges and units
|
||||
|
||||
---
|
||||
|
||||
## Data Flow Summary
|
||||
|
||||
```
|
||||
Sensors (ADC, RF, WL)
|
||||
↓
|
||||
Python Backend (data collection)
|
||||
↓
|
||||
CSV File Generation
|
||||
↓
|
||||
Transfer Protocol (FTP/SCP/SFTP/WebDAV)
|
||||
↓
|
||||
myvscada Server
|
||||
↓
|
||||
Processed/Error folder handling
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
*Integration audit: 2026-03-12*
|
||||
137
.planning/codebase/STACK.md
Normal file
137
.planning/codebase/STACK.md
Normal file
@@ -0,0 +1,137 @@
|
||||
# Technology Stack
|
||||
|
||||
**Analysis Date:** 2026-03-12
|
||||
|
||||
## Project Status
|
||||
|
||||
This is a **greenfield project** - no implementation code exists yet. The repository currently contains only:
|
||||
- `THE_IDEA.md` - Requirements specification document
|
||||
- `.opencode/` - OpenCode/GSD framework configuration
|
||||
|
||||
The following stack analysis is based on the requirements defined in `THE_IDEA.md`.
|
||||
|
||||
---
|
||||
|
||||
## Languages
|
||||
|
||||
**Primary:**
|
||||
- **HTML/CSS/JavaScript** - Web-based UI for the 7-inch touchscreen and remote full HD interface
|
||||
- Expected to run in Chromium Kiosk Mode on Raspberry Pi
|
||||
- Must support dual resolution: 1024x600 (touchscreen) and 1920x1080 (remote)
|
||||
|
||||
**Secondary:**
|
||||
- **Python** (likely) - Backend for sensor data collection, CSV processing, and network operations
|
||||
- Raspberry Pi standard runtime
|
||||
- Used for serial communication with sensors (ADC, rainfall sensors)
|
||||
- CSV file generation and management
|
||||
|
||||
---
|
||||
|
||||
## Runtime
|
||||
|
||||
**Environment:**
|
||||
- **Raspberry Pi OS** - Primary runtime on Pi Zero 2 W and Pi 3B
|
||||
- Pi Zero 2 W: ARMv8 single-core, 512MB RAM
|
||||
- Pi 3B: ARMv7 quad-core, 1GB RAM
|
||||
|
||||
**Browser:**
|
||||
- **Chromium** - Kiosk mode for touchscreen interface
|
||||
- Target URL: `http://pihostname:8080` (touchscreen)
|
||||
- Target URL: `http://pihostname:9090/` (remote full HD)
|
||||
|
||||
**Package Manager:**
|
||||
- Not yet established (pending project initialization)
|
||||
|
||||
---
|
||||
|
||||
## Frameworks
|
||||
|
||||
**Core:**
|
||||
- **Lightweight web framework** (TBD) - For serving the UI
|
||||
- Options: Flask, FastAPI (Python), or simple HTTP server
|
||||
- Must be performant on low-power Pi Zero 2 W
|
||||
- **Frontend** - Plain HTML/CSS or lightweight framework
|
||||
- Recommendation: Static HTML/CSS for main interface (performance)
|
||||
- Avoid heavy SPA frameworks to minimize memory footprint
|
||||
|
||||
**Sensor Interfaces:**
|
||||
- **Serial communication** - For ADC, rainfall sensors, GPS
|
||||
- **SDI-12** - For soil moisture and evapotranspiration sensors (referenced in requirements)
|
||||
|
||||
**Data Handling:**
|
||||
- **CSV processing** - For data logging and transfer (per flowchart in THE_IDEA.md)
|
||||
|
||||
**Build/Dev:**
|
||||
- Not yet established (pending project initialization)
|
||||
|
||||
---
|
||||
|
||||
## Key Dependencies
|
||||
|
||||
**Hardware Interfaces:**
|
||||
- Serial port communication (Python pyserial or similar)
|
||||
- ADC reading libraries
|
||||
- GPS module support (USB)
|
||||
|
||||
**Network:**
|
||||
- FTP client (for data transfer to myvscada server)
|
||||
- SCP/SFTP client
|
||||
- WebDAV client
|
||||
- TCP socket libraries
|
||||
|
||||
**Data Processing:**
|
||||
- CSV generation and parsing libraries
|
||||
- Data validation utilities
|
||||
|
||||
---
|
||||
|
||||
## Configuration
|
||||
|
||||
**Network Settings:**
|
||||
- Local IP configuration (static IP)
|
||||
- Subnet mask, gateway, DNS
|
||||
- MAC address
|
||||
- Transfer protocol selection (FTP/SCP/SFTP/WEBDAV)
|
||||
|
||||
**Sensor Configuration:**
|
||||
- ADC channels (4-20mA, 0-10vDC types)
|
||||
- Rainfall sensor settings
|
||||
- Water level thresholds
|
||||
- Evapotranspiration settings
|
||||
|
||||
**Server Settings:**
|
||||
- Server IP and TCP port
|
||||
- Tide data hours
|
||||
- GPS coordinates (manual or USB GPS)
|
||||
|
||||
**Mobile Settings:**
|
||||
- Phone numbers for alerts (Master, SubMaster, Engineer contacts)
|
||||
|
||||
---
|
||||
|
||||
## Platform Requirements
|
||||
|
||||
**Development:**
|
||||
- Standard web development tools (editor, browser)
|
||||
- Python development environment
|
||||
- Git for version control
|
||||
|
||||
**Production:**
|
||||
- Raspberry Pi Zero 2 W or 3B
|
||||
- 7-inch capacitive touchscreen (1024x600)
|
||||
- Chromium browser (Kiosk mode)
|
||||
- Network connectivity (Ethernet or mobile/GPRS)
|
||||
|
||||
---
|
||||
|
||||
## Performance Constraints
|
||||
|
||||
| Resource | Target | Concern |
|
||||
|----------|--------|---------|
|
||||
| RAM | <256MB for UI | Pi Zero 2 W has only 512MB total |
|
||||
| Startup time | <3 seconds | Cold start in Chromium Kiosk |
|
||||
| Bundle size | Minimal | Optimize for low-power device |
|
||||
|
||||
---
|
||||
|
||||
*Stack analysis: 2026-03-12*
|
||||
148
.planning/codebase/STRUCTURE.md
Normal file
148
.planning/codebase/STRUCTURE.md
Normal file
@@ -0,0 +1,148 @@
|
||||
# Codebase Structure
|
||||
|
||||
**Analysis Date:** 2026-03-12
|
||||
|
||||
## Directory Layout
|
||||
|
||||
```
|
||||
/root/tckuiyo/
|
||||
├── THE_IDEA.md # Project concept documentation
|
||||
├── .gitignore # Git ignore rules
|
||||
├── .bg-shell/ # Background shell config (manifest.json)
|
||||
├── .gsd/ # GSD project data
|
||||
│ └── milestones/ # Milestone definitions (empty)
|
||||
├── .opencode/ # GSD Framework (main project code)
|
||||
│ ├── package.json # Dependencies (OpenCode plugin)
|
||||
│ ├── bun.lock # Dependency lockfile
|
||||
│ ├── opencode.db # OpenCode database
|
||||
│ ├── commands/ # User-facing commands
|
||||
│ ├── agents/ # AI agent definitions
|
||||
│ ├── get-shit-done/ # Workflows
|
||||
│ │ └── workflows/ # Execution logic
|
||||
│ ├── skills/ # Specialized skills
|
||||
│ ├── rules/ # Behavior rules
|
||||
│ ├── node_modules/ # Dependencies
|
||||
│ └── .gitignore # OpenCode-specific ignores
|
||||
├── .planning/ # Project planning output
|
||||
│ └── codebase/ # Codebase analysis docs (where mappers write)
|
||||
└── (empty directories for future use)
|
||||
```
|
||||
|
||||
## Directory Purposes
|
||||
|
||||
**`.opencode/`:**
|
||||
- Purpose: Core GSD framework configuration
|
||||
- Contains: Commands, agents, workflows, skills, rules
|
||||
- Key files: All 33 command files, 12 agent files, 36 workflow files
|
||||
|
||||
**`.opencode/commands/gsd/`:**
|
||||
- Purpose: User-invokable GSD commands
|
||||
- Contains: Markdown command definitions with YAML frontmatter
|
||||
- Key files: `gsd-new-project.md`, `gsd-map-codebase.md`, `gsd-plan-phase.md`, `gsd-execute-phase.md`
|
||||
|
||||
**`.opencode/agents/`:**
|
||||
- Purpose: AI agent role definitions
|
||||
- Contains: Agent configurations with tools and skills
|
||||
- Key files: `gsd-codebase-mapper.md`, `gsd-planner.md`, `gsd-executor.md`, `gsd-verifier.md`
|
||||
|
||||
**`.opencode/get-shit-done/workflows/`:**
|
||||
- Purpose: Execution logic for commands
|
||||
- Contains: Step-by-step workflow instructions
|
||||
- Key files: `map-codebase.md`, `new-project.md`, `plan-phase.md`, `execute-phase.md`
|
||||
|
||||
**`.opencode/skills/`:**
|
||||
- Purpose: Specialized capability modules
|
||||
- Contains: Skill definitions with scripts
|
||||
- Key files: `gsd-oc-select-model/` (model selection skill)
|
||||
|
||||
**`.opencode/rules/`:**
|
||||
- Purpose: Global behavior rules
|
||||
- Contains: Rule definitions for agent behavior
|
||||
- Key files: `gsd-oc-work-hard.md`
|
||||
|
||||
**`.planning/codebase/`:**
|
||||
- Purpose: Output location for codebase mapper agents
|
||||
- Contains: Analysis documents written by mapper agents
|
||||
- Target files: STACK.md, INTEGRATIONS.md, ARCHITECTURE.md, STRUCTURE.md, CONVENTIONS.md, TESTING.md, CONCERNS.md
|
||||
|
||||
**`.gsd/milestones/`:**
|
||||
- Purpose: Milestone definitions for projects
|
||||
- Contains: Milestone JSON files (currently empty)
|
||||
- Used by: new-milestone, complete-milestone commands
|
||||
|
||||
## Key File Locations
|
||||
|
||||
**Entry Points:**
|
||||
- `.opencode/commands/gsd/gsd-*.md`: Command definitions invoked via `/gsd-*`
|
||||
|
||||
**Configuration:**
|
||||
- `.opencode/package.json`: Dependencies - `@opencode-ai/plugin: 1.2.24`
|
||||
- `.opencode/bun.lock`: Dependency lockfile
|
||||
- `.gitignore`: Excludes node_modules, .opencode/, .gsd/, .planning/, *.swp
|
||||
|
||||
**Core Logic:**
|
||||
- All logic in `.opencode/` - this IS the application (OpenCode configuration)
|
||||
|
||||
**Testing:**
|
||||
- No separate test directory - OpenCode handles testing via verify-work workflow
|
||||
|
||||
## Naming Conventions
|
||||
|
||||
**Files:**
|
||||
- Commands: `gsd-{command-name}.md` (kebab-case)
|
||||
- Agents: `gsd-{agent-name}.md` (kebab-case)
|
||||
- Workflows: `{workflow-name}.md` (kebab-case)
|
||||
- Skills: `{skill-name}/` directory with SKILL.md inside
|
||||
|
||||
**Directories:**
|
||||
- `.opencode/commands/gsd/`: GSD commands group
|
||||
- `.opencode/agents/`: Agent definitions
|
||||
- `.opencode/get-shit-done/workflows/`: Workflow definitions
|
||||
- `.opencode/skills/`: Skill packages
|
||||
- `.opencode/node_modules/`: npm dependencies
|
||||
- `.planning/codebase/`: Codebase analysis outputs
|
||||
|
||||
## Where to Add New Code
|
||||
|
||||
**New Command:**
|
||||
- Implementation: `.opencode/commands/gsd/gsd-{name}.md`
|
||||
- Workflow: Add corresponding `.opencode/get-shit-done/workflows/{name}.md`
|
||||
|
||||
**New Agent:**
|
||||
- Implementation: `.opencode/agents/gsd-{name}.md`
|
||||
|
||||
**New Workflow:**
|
||||
- Implementation: `.opencode/get-shit-done/workflows/{name}.md`
|
||||
- Referenced by: Command's `<execution_context>` tag
|
||||
|
||||
**New Skill:**
|
||||
- Implementation: `.opencode/skills/{skill-name}/SKILL.md`
|
||||
|
||||
**Codebase Analysis Output:**
|
||||
- Location: `.planning/codebase/` (written by gsd-codebase-mapper agents)
|
||||
|
||||
## Special Directories
|
||||
|
||||
**`.opencode/`:**
|
||||
- Purpose: OpenCode project root - contains all project code
|
||||
- Generated: No - this IS the codebase
|
||||
- Committed: Yes - core project files
|
||||
|
||||
**`.opencode/node_modules/`:**
|
||||
- Purpose: Dependencies for OpenCode plugin
|
||||
- Generated: Yes (via bun install)
|
||||
- Committed: No (in .gitignore)
|
||||
|
||||
**`.planning/`:**
|
||||
- Purpose: Planning output and project state
|
||||
- Generated: Yes (by GSD commands)
|
||||
- Committed: Yes (project state is versioned)
|
||||
|
||||
**`.gsd/`:**
|
||||
- Purpose: Project milestone and tracking data
|
||||
- Generated: Yes (by GSD commands)
|
||||
- Committed: Yes (project progress is versioned)
|
||||
|
||||
---
|
||||
|
||||
*Structure analysis: 2026-03-12*
|
||||
345
.planning/codebase/TESTING.md
Normal file
345
.planning/codebase/TESTING.md
Normal file
@@ -0,0 +1,345 @@
|
||||
# Testing Patterns
|
||||
|
||||
**Analysis Date:** 2026-03-12
|
||||
|
||||
## Test Framework
|
||||
|
||||
**Mixed approach detected:**
|
||||
|
||||
### Primary: Node.js built-in `node:test`
|
||||
- Used in `bin/gsd-tools.test.cjs`
|
||||
- Version: Node.js built-in (no package needed)
|
||||
- Config: None detected
|
||||
|
||||
### Secondary: Vitest
|
||||
- Used in `bin/test/*.test.cjs` files
|
||||
- Version: `^3.2.4` (from package.json)
|
||||
- Config: None detected (uses defaults)
|
||||
|
||||
**Run Commands:**
|
||||
```bash
|
||||
npm test # Run all tests (vitest run)
|
||||
npm run test:watch # Watch mode (vitest)
|
||||
```
|
||||
|
||||
## Test File Organization
|
||||
|
||||
**Location:** `bin/test/`
|
||||
|
||||
**Naming:** `{command-name}.test.cjs`
|
||||
|
||||
**Example files:**
|
||||
- `bin/test/get-profile.test.cjs`
|
||||
- `bin/test/set-profile.test.cjs`
|
||||
- `bin/test/allow-read-config.test.cjs`
|
||||
- `bin/test/oc-profile-config.test.cjs`
|
||||
- `bin/test/pivot-profile.test.cjs`
|
||||
|
||||
## Test Structure
|
||||
|
||||
### Vitest Style (Recommended)
|
||||
|
||||
From `bin/test/get-profile.test.cjs`:
|
||||
|
||||
```javascript
|
||||
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import os from 'os';
|
||||
|
||||
describe('get-profile.cjs', () => {
|
||||
let testDir;
|
||||
let planningDir;
|
||||
let configPath;
|
||||
let capturedLog;
|
||||
let capturedError;
|
||||
let exitCode;
|
||||
|
||||
beforeEach(() => {
|
||||
// Create isolated test directory
|
||||
testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'get-profile-test-'));
|
||||
planningDir = path.join(testDir, '.planning');
|
||||
configPath = path.join(planningDir, 'oc_config.json');
|
||||
fs.mkdirSync(planningDir, { recursive: true });
|
||||
|
||||
// Reset captured output
|
||||
capturedLog = null;
|
||||
capturedError = null;
|
||||
exitCode = null;
|
||||
|
||||
// Mock console.log to capture all output
|
||||
console.log = (msg) => {
|
||||
allLogs.push(msg);
|
||||
capturedLog = msg;
|
||||
};
|
||||
console.error = (msg) => {
|
||||
allErrors.push(msg);
|
||||
capturedError = msg;
|
||||
};
|
||||
process.exit = (code) => {
|
||||
exitCode = code;
|
||||
throw new Error(`process.exit(${code})`);
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Restore original functions
|
||||
console.log = originalLog;
|
||||
console.error = originalError;
|
||||
process.exit = originalExit;
|
||||
|
||||
// Cleanup test directory
|
||||
try {
|
||||
fs.rmSync(testDir, { recursive: true, force: true });
|
||||
} catch (err) {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
});
|
||||
|
||||
it('returns current profile when current_oc_profile is set', () => {
|
||||
fs.writeFileSync(configPath, JSON.stringify(VALID_CONFIG_WITH_CURRENT, null, 2));
|
||||
const getProfile = importGetProfile();
|
||||
|
||||
try {
|
||||
getProfile(testDir, []);
|
||||
} catch (err) {
|
||||
// Expected to throw due to process.exit mock
|
||||
}
|
||||
|
||||
expect(exitCode).toBe(0);
|
||||
const output = JSON.parse(capturedLog);
|
||||
expect(output.success).toBe(true);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Node:test Style
|
||||
|
||||
From `bin/gsd-tools.test.cjs`:
|
||||
|
||||
```javascript
|
||||
const { test, describe, beforeEach, afterEach } = require('node:test');
|
||||
const assert = require('node:assert');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
describe('history-digest command', () => {
|
||||
let tmpDir;
|
||||
|
||||
beforeEach(() => {
|
||||
tmpDir = createTempProject();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
cleanup(tmpDir);
|
||||
});
|
||||
|
||||
test('empty phases directory returns valid schema', () => {
|
||||
const result = runGsdTools('history-digest', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
const digest = JSON.parse(result.output);
|
||||
assert.deepStrictEqual(digest.phases, {}, 'phases should be empty object');
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
### Manual Test Runner Style
|
||||
|
||||
From `bin/test/allow-read-config.test.cjs`:
|
||||
|
||||
```javascript
|
||||
function testCreatePermission() {
|
||||
console.log('Test: Create new opencode.json with permission...');
|
||||
|
||||
const testDir = createTestDir();
|
||||
|
||||
try {
|
||||
const result = runCLI(testDir, []);
|
||||
|
||||
if (!result.success) {
|
||||
throw new Error(`Expected success, got: ${JSON.stringify(result)}`);
|
||||
}
|
||||
|
||||
// Verify opencode.json was created
|
||||
const opencodePath = path.join(testDir, 'opencode.json');
|
||||
if (!fs.existsSync(opencodePath)) {
|
||||
throw new Error('opencode.json was not created');
|
||||
}
|
||||
|
||||
console.log('✓ PASS: Create permission\n');
|
||||
return true;
|
||||
} catch (err) {
|
||||
console.error('✗ FAIL:', err.message, '\n');
|
||||
return false;
|
||||
} finally {
|
||||
cleanupTestDir(testDir);
|
||||
}
|
||||
}
|
||||
|
||||
function runTests() {
|
||||
console.log('Running allow-read-config tests...\n');
|
||||
|
||||
const results = [
|
||||
testCreatePermission(),
|
||||
testIdempotency(),
|
||||
testDryRun(),
|
||||
];
|
||||
|
||||
const passed = results.filter(r => r).length;
|
||||
console.log(`Results: ${passed}/${total} tests passed`);
|
||||
|
||||
if (passed === total) {
|
||||
process.exit(0);
|
||||
} else {
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
runTests();
|
||||
```
|
||||
|
||||
## Mocking Patterns
|
||||
|
||||
### Console/Process Mocking
|
||||
|
||||
**Vitest approach:**
|
||||
```javascript
|
||||
const originalLog = console.log;
|
||||
const originalError = console.error;
|
||||
const originalExit = process.exit;
|
||||
|
||||
beforeEach(() => {
|
||||
console.log = (msg) => {
|
||||
allLogs.push(msg);
|
||||
capturedLog = msg;
|
||||
};
|
||||
console.error = (msg) => {
|
||||
allErrors.push(msg);
|
||||
capturedError = msg;
|
||||
};
|
||||
process.exit = (code) => {
|
||||
exitCode = code;
|
||||
throw new Error(`process.exit(${code})`);
|
||||
};
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
console.log = originalLog;
|
||||
console.error = originalError;
|
||||
process.exit = originalExit;
|
||||
});
|
||||
```
|
||||
|
||||
### Require Cache Clearing
|
||||
|
||||
```javascript
|
||||
const importGetProfile = () => {
|
||||
const modulePath = '../gsd-oc-commands/get-profile.cjs';
|
||||
delete require.cache[require.resolve(modulePath)];
|
||||
return require(modulePath);
|
||||
};
|
||||
```
|
||||
|
||||
## Test Fixtures
|
||||
|
||||
**Location:** `bin/test/fixtures/`
|
||||
|
||||
**Pattern:** In-file constants
|
||||
|
||||
```javascript
|
||||
const VALID_CONFIG_WITH_CURRENT = {
|
||||
current_oc_profile: 'smart',
|
||||
profiles: {
|
||||
presets: {
|
||||
simple: {
|
||||
planning: 'bailian-coding-plan/qwen3.5-plus',
|
||||
execution: 'bailian-coding-plan/qwen3.5-plus',
|
||||
verification: 'bailian-coding-plan/qwen3.5-plus'
|
||||
},
|
||||
smart: { /* ... */ }
|
||||
}
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
## Test Helpers
|
||||
|
||||
### CLI Execution
|
||||
|
||||
```javascript
|
||||
function runCLI(testDir, args) {
|
||||
const cmd = `node ${TOOLS_PATH} allow-read-config ${args.join(' ')}`;
|
||||
const output = execSync(cmd, { cwd: testDir, encoding: 'utf8' });
|
||||
return JSON.parse(output);
|
||||
}
|
||||
```
|
||||
|
||||
### Temp Directory Creation
|
||||
|
||||
```javascript
|
||||
function createTestDir() {
|
||||
const testDir = path.join(os.tmpdir(), `gsd-oc-test-${Date.now()}`);
|
||||
fs.mkdirSync(testDir, { recursive: true });
|
||||
return testDir;
|
||||
}
|
||||
|
||||
function cleanupTestDir(testDir) {
|
||||
if (fs.existsSync(testDir)) {
|
||||
fs.rmSync(testDir, { recursive: true, force: true });
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Assertion Patterns
|
||||
|
||||
### Vitest
|
||||
```javascript
|
||||
expect(exitCode).toBe(0);
|
||||
expect(output.success).toBe(true);
|
||||
expect(output.data).toHaveProperty('smart');
|
||||
expect(output.data.smart).toEqual(VALID_CONFIG_WITH_CURRENT.profiles.presets.smart);
|
||||
```
|
||||
|
||||
### Node.js assert
|
||||
```javascript
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
assert.deepStrictEqual(digest.phases, {}, 'phases should be empty object');
|
||||
```
|
||||
|
||||
## Coverage
|
||||
|
||||
**No coverage configuration detected.** No enforcement of coverage thresholds.
|
||||
|
||||
## Test Types
|
||||
|
||||
### Unit Tests
|
||||
- Individual function testing
|
||||
- Use mocking for dependencies
|
||||
- Test isolated behavior
|
||||
|
||||
### Integration/CLI Tests
|
||||
- Execute CLI commands via `execSync`
|
||||
- Test with real temporary directories
|
||||
- Verify file creation/state changes
|
||||
|
||||
### Patterns
|
||||
- Test both success and error paths
|
||||
- Test flag combinations (--verbose, --dry-run)
|
||||
- Test idempotency (running same command twice)
|
||||
|
||||
## Common Issues
|
||||
|
||||
### Inconsistent Test Frameworks
|
||||
- Some tests use Vitest, others use Node.js built-in `node:test`
|
||||
- Some tests use manual test runner (no framework)
|
||||
- **Recommendation:** Standardize on one framework
|
||||
|
||||
### Missing Test Config
|
||||
- No `vitest.config.js` found
|
||||
- No `jest.config.js` found
|
||||
- **Recommendation:** Add configuration file
|
||||
|
||||
---
|
||||
|
||||
*Testing analysis: 2026-03-12*
|
||||
Reference in New Issue
Block a user