From dede6ecc60302cf78459466d42367fa3348f1181 Mon Sep 17 00:00:00 2001 From: robbond Date: Mon, 1 Jun 2026 17:43:02 +0100 Subject: [PATCH] chore: bootstrap rdb-discovery --- .rdb/project.json | 6 + AGENT_HANDOFF.md | 18 + PROJECT_STATE.md | 10 + README.md | 44 ++ RUN_LOG.md | 4 + TASKS.md | 23 + TEST_PLAN.md | 17 + context/agent-guidelines.md | 269 +++++++++ context/architecture.md | 0 context/assumptions.md | 0 context/company-context.md | 40 ++ context/current-environment.md | 542 ++++++++++++++++++ context/decisions.md | 0 context/development-context.md | 294 ++++++++++ context/discovery-log.md | 0 context/infrastructure-context.md | 193 +++++++ context/open-questions.md | 0 context/product-brief.md | 0 context/repository-context.md | 31 + context/risks.md | 0 pyproject.toml | 22 + src/rdb_discovery.egg-info/PKG-INFO | 55 ++ src/rdb_discovery.egg-info/SOURCES.txt | 17 + .../dependency_links.txt | 1 + src/rdb_discovery.egg-info/entry_points.txt | 2 + src/rdb_discovery.egg-info/requires.txt | 5 + src/rdb_discovery.egg-info/top_level.txt | 1 + src/rdb_discovery/__init__.py | 1 + .../__pycache__/__init__.cpython-314.pyc | Bin 0 -> 202 bytes .../__pycache__/cli.cpython-314.pyc | Bin 0 -> 8874 bytes .../__pycache__/discovery.cpython-314.pyc | Bin 0 -> 2250 bytes .../__pycache__/handoff.cpython-314.pyc | Bin 0 -> 1964 bytes .../__pycache__/status.cpython-314.pyc | Bin 0 -> 2159 bytes .../__pycache__/tasks.cpython-314.pyc | Bin 0 -> 4390 bytes .../__pycache__/templates.cpython-314.pyc | Bin 0 -> 3916 bytes src/rdb_discovery/cli.py | 133 +++++ src/rdb_discovery/discovery.py | 38 ++ src/rdb_discovery/handoff.py | 45 ++ src/rdb_discovery/status.py | 25 + src/rdb_discovery/tasks.py | 71 +++ src/rdb_discovery/templates.py | 155 +++++ .../test_tasks.cpython-314-pytest-9.0.3.pyc | Bin 0 -> 5611 bytes ...est_templates.cpython-314-pytest-9.0.3.pyc | Bin 0 -> 5118 bytes tests/test_tasks.py | 29 + tests/test_templates.py | 10 + 45 files changed, 2101 insertions(+) create mode 100644 .rdb/project.json create mode 100644 AGENT_HANDOFF.md create mode 100644 PROJECT_STATE.md create mode 100644 README.md create mode 100644 RUN_LOG.md create mode 100644 TASKS.md create mode 100644 TEST_PLAN.md create mode 100644 context/agent-guidelines.md create mode 100644 context/architecture.md create mode 100644 context/assumptions.md create mode 100644 context/company-context.md create mode 100644 context/current-environment.md create mode 100644 context/decisions.md create mode 100644 context/development-context.md create mode 100644 context/discovery-log.md create mode 100644 context/infrastructure-context.md create mode 100644 context/open-questions.md create mode 100644 context/product-brief.md create mode 100644 context/repository-context.md create mode 100644 context/risks.md create mode 100644 pyproject.toml create mode 100644 src/rdb_discovery.egg-info/PKG-INFO create mode 100644 src/rdb_discovery.egg-info/SOURCES.txt create mode 100644 src/rdb_discovery.egg-info/dependency_links.txt create mode 100644 src/rdb_discovery.egg-info/entry_points.txt create mode 100644 src/rdb_discovery.egg-info/requires.txt create mode 100644 src/rdb_discovery.egg-info/top_level.txt create mode 100644 src/rdb_discovery/__init__.py create mode 100644 src/rdb_discovery/__pycache__/__init__.cpython-314.pyc create mode 100644 src/rdb_discovery/__pycache__/cli.cpython-314.pyc create mode 100644 src/rdb_discovery/__pycache__/discovery.cpython-314.pyc create mode 100644 src/rdb_discovery/__pycache__/handoff.cpython-314.pyc create mode 100644 src/rdb_discovery/__pycache__/status.cpython-314.pyc create mode 100644 src/rdb_discovery/__pycache__/tasks.cpython-314.pyc create mode 100644 src/rdb_discovery/__pycache__/templates.cpython-314.pyc create mode 100644 src/rdb_discovery/cli.py create mode 100644 src/rdb_discovery/discovery.py create mode 100644 src/rdb_discovery/handoff.py create mode 100644 src/rdb_discovery/status.py create mode 100644 src/rdb_discovery/tasks.py create mode 100644 src/rdb_discovery/templates.py create mode 100644 tests/__pycache__/test_tasks.cpython-314-pytest-9.0.3.pyc create mode 100644 tests/__pycache__/test_templates.cpython-314-pytest-9.0.3.pyc create mode 100644 tests/test_tasks.py create mode 100644 tests/test_templates.py diff --git a/.rdb/project.json b/.rdb/project.json new file mode 100644 index 0000000..a7e3586 --- /dev/null +++ b/.rdb/project.json @@ -0,0 +1,6 @@ +{ + "project_name": "rdb-discovery", + "stage": "DISCOVERY", + "current_task": "TASK-000", + "next_task": "TASK-001" +} diff --git a/AGENT_HANDOFF.md b/AGENT_HANDOFF.md new file mode 100644 index 0000000..83d848c --- /dev/null +++ b/AGENT_HANDOFF.md @@ -0,0 +1,18 @@ +# Agent Handoff + +## Current Stage + +DISCOVERY + +## Current Task + +TASK-000 — Bootstrap RDB project standards + +## Instructions For Agent + +- Complete one task only +- Make the smallest useful change +- Update documentation +- Run tests +- Commit changes +- Stop after task completion diff --git a/PROJECT_STATE.md b/PROJECT_STATE.md new file mode 100644 index 0000000..f83bd5c --- /dev/null +++ b/PROJECT_STATE.md @@ -0,0 +1,10 @@ +# Project State + +Current Stage: DISCOVERY +Previous Stage: NONE +Next Stage: BOOTSTRAP_READY + +Current Task: TASK-000 +Active Branch: main + +Last Updated: 2026-06-01 diff --git a/README.md b/README.md new file mode 100644 index 0000000..6d517e0 --- /dev/null +++ b/README.md @@ -0,0 +1,44 @@ +# rdb-discovery + +A small CLI-first tool for repeatable software project discovery and delivery. + +It creates markdown files that help humans and AI coding agents understand: + +- what is being built +- why it exists +- what questions remain open +- what task should be done next +- what stage the project is currently in + +## Install for local development + +```bash +python3 -m venv .venv +source .venv/bin/activate +pip install -e '.[dev]' +``` + +## Commands + +```bash +rdb init +rdb discover +rdb status +rdb next +rdb start TASK-001 +rdb complete TASK-001 +rdb handoff +``` + +## Bootstrap workflow + +1. Run `rdb init` +2. Run `rdb discover` +3. Run `rdb status` +4. Run `rdb next` +5. Give the next task to Claude Code or Cline +6. Commit after each completed task + +## Principle + +Markdown files are the source of truth. No database is required. diff --git a/RUN_LOG.md b/RUN_LOG.md new file mode 100644 index 0000000..bc2db45 --- /dev/null +++ b/RUN_LOG.md @@ -0,0 +1,4 @@ + +Run Log +2026-06-01 +Bootstrap project structure created. diff --git a/TASKS.md b/TASKS.md new file mode 100644 index 0000000..f3b6318 --- /dev/null +++ b/TASKS.md @@ -0,0 +1,23 @@ +# TASKS + +## TASK-000 — Bootstrap RDB project standards + +Status: Todo + +Goal: Add the project control files used by RDB agent workflows. + +Acceptance Criteria: + +- PROJECT_STATE.md exists +- AGENT_HANDOFF.md exists +- .rdb/project.json exists +- context/repository-context.md exists +- TEST_PLAN.md exists +- RUN_LOG.md exists +- README.md explains the structure + +## TASK-001 — Implement rdb init + +Status: Todo + +Goal: Create the command that initialises the standard RDB project structure. diff --git a/TEST_PLAN.md b/TEST_PLAN.md new file mode 100644 index 0000000..fbb813f --- /dev/null +++ b/TEST_PLAN.md @@ -0,0 +1,17 @@ +# Test Plan + +## Bootstrap Validation + +Run: + +```bash +python3 -m venv .venv +source .venv/bin/activate +pip install -e '.[dev]' +pytest +rdb --help + +Expected result: + +Tests pass +CLI help displays diff --git a/context/agent-guidelines.md b/context/agent-guidelines.md new file mode 100644 index 0000000..93c59db --- /dev/null +++ b/context/agent-guidelines.md @@ -0,0 +1,269 @@ +# `/context/agent-guidelines.md` + +# Agent Operating Guidelines + +This file defines how AI coding agents should behave when working on RDB Solutions Ltd projects. + +These rules apply to: + +- OpenHands +- Claude Code +- Cursor Agents +- Copilot Agents +- Local coding agents +- Future autonomous development systems + +--- + +# Core Rule + +Make the smallest useful change possible. + +Agents should favour many small successful changes over large risky changes. + +--- + +# Before Starting Work + +Agents should first read: + +```text +/context/company-context.md +/context/infrastructure-context.md +/context/development-context.md +README.md +ARCHITECTURE.md +TASKS.md +``` + +If these files exist. + +--- + +# Task Execution Model + +Work should be completed using backlog items. + +Recommended process: + +1. Select task +2. Understand task +3. Implement task +4. Test task +5. Document task +6. Commit task +7. Move to next task + +Avoid combining unrelated tasks. + +--- + +# Safe Change Policy + +Agents should: + +✅ Add functionality + +✅ Fix defects + +✅ Improve documentation + +✅ Add tests + +✅ Refactor locally + +Agents should not: + +❌ Rewrite entire systems + +❌ Replace frameworks + +❌ Change infrastructure architecture + +❌ Change deployment methods + +❌ Delete large amounts of code + +Without explicit approval. + +--- + +# Code Generation Standards + +Generated code should: + +- Be readable +- Be maintainable +- Follow existing project patterns +- Include error handling +- Include logging where appropriate + +Avoid: + +- Clever code +- Unnecessary abstractions +- Premature optimisation + +--- + +# Infrastructure Changes + +Infrastructure changes require higher scrutiny. + +Examples: + +- Jenkins +- Gitea +- Supabase +- Reverse proxies +- DNS +- Production deployments + +For infrastructure work: + +1. Explain proposed change +2. Explain risk +3. Explain rollback +4. Generate implementation plan + +Do not execute destructive changes automatically. + +--- + +# Git Rules + +Preferred workflow: + +```text +Create branch +Make change +Run tests +Commit +Create PR +``` + +Commit frequently. + +Commit messages should be descriptive. + +--- + +# Testing Requirements + +Where practical: + +- Unit tests +- Integration tests +- Build validation + +Agents should never claim testing was performed if it was not. + +Use: + +```text +Verified +``` + +Only when actually verified. + +Use: + +```text +Not tested +``` + +When testing was not possible. + +--- + +# Documentation Requirements + +If behaviour changes: + +Update: + +- README +- Architecture docs +- API docs +- Task status + +where applicable. + +Documentation is part of the task. + +--- + +# Autonomous Work Limits + +Agents may autonomously: + +- Create files +- Modify files +- Add tests +- Add documentation +- Refactor small areas + +Agents should request approval before: + +- Deleting files +- Deleting databases +- Deploying to production +- Changing authentication +- Changing networking +- Modifying CI/CD pipelines + +--- + +# Preferred Deliverables + +When completing work, agents should provide: + +## Summary + +What was changed. + +## Files Modified + +List of modified files. + +## Validation + +Tests executed and results. + +## Risks + +Potential side effects. + +## Next Recommended Task + +Small logical follow-up task. + +--- + +# RDB-Specific Preferences + +Remember: + +- Gitea over GitHub +- Jenkins over cloud CI systems +- Docker preferred +- Proxmox preferred +- Local LLMs preferred +- Self-hosted services preferred +- Incremental change preferred + +Do not recommend cloud replacements unless there is a significant advantage. + +--- + +# Success Criteria + +A task is complete when: + +- Requirements are met +- Tests pass +- Documentation is updated +- Changes are understandable +- Rollback is possible + +Optimise for maintainability and long-term ownership rather than speed alone. diff --git a/context/architecture.md b/context/architecture.md new file mode 100644 index 0000000..e69de29 diff --git a/context/assumptions.md b/context/assumptions.md new file mode 100644 index 0000000..e69de29 diff --git a/context/company-context.md b/context/company-context.md new file mode 100644 index 0000000..8eba74e --- /dev/null +++ b/context/company-context.md @@ -0,0 +1,40 @@ +# `/context/company-context.md` + +# RDB Solutions Ltd Context + +RDB Solutions Ltd is developing internal and customer-facing software systems with a strong preference for self-hosted infrastructure, local control, and practical automation. + +The company favours owning its development stack rather than depending heavily on third-party SaaS platforms where avoidable. + +## Goals + +The main goals are: + +- Improve development efficiency +- Use local and self-hosted tools where practical +- Automate repetitive development and deployment tasks +- Support LLM-assisted development +- Maintain control over source code, infrastructure, and data +- Build systems that are maintainable by a small team or solo developer + +## Preferred Approach + +RDB Solutions Ltd prefers: + +- Self-hosted Git using Gitea +- Jenkins for CI/CD +- Docker-based deployments +- Proxmox for virtualisation +- Local LLMs where possible +- Clear task files and backlog-driven agent work +- Incremental changes over large risky rewrites +- Documentation that allows AI tools to understand the system quickly + +## General Constraints + +- Avoid assuming GitHub is used. +- Prefer Gitea-compatible workflows. +- Prefer local or self-hosted services where practical. +- Avoid vendor lock-in unless there is a clear benefit. +- Keep systems understandable and maintainable. +- Prefer simple architecture before complex orchestration. diff --git a/context/current-environment.md b/context/current-environment.md new file mode 100644 index 0000000..b495c97 --- /dev/null +++ b/context/current-environment.md @@ -0,0 +1,542 @@ +# `/context/current-environment.md` + +# Current Environment + +Last Updated: 2026-06-01 + +This file describes the current live RDB Solutions infrastructure. + +Update whenever hardware, VMs, services, networking, or development tooling changes. + +--- + +# Network Overview + +## Primary Site + +Location: + +- + +Internet: + +- ISP: Lightening Fibre +- Download: 2.5 Gbps +- Router: Zyxel EX5601-T0 +- Internal Wifi Extender: Deco +- Static IP: 212.221.168.135 + +Domain(s): + +- rdbsolutions.co.uk +- gitea.rdbcloud.co.uk +- prox.rdbcloud.co.uk +- pedw.rdbcloud.co.uk + +Internal Network: + +```text +LAN: 192.168.x.0/24 +Gateway: 192.168.x.1 +DNS: AdGuard +DHCP: +``` + +--- + +# Physical Infrastructure + +## Proxmox Host + +Hostname: + +- proxmox01 + +Purpose: + +- Primary virtualisation host + +Hardware: + +CPU: + +- + +RAM: + +- 16 GB + +Storage: + +-
+ +Network: + +- + +Notes: + +- Hosts infrastructure VMs and LXCs +- Main internal services + +--- + +## AI / Ollama Server + +Hostname: + +- rdbUltra + +Purpose: + +- Local LLM hosting + +Hardware: + +CPU: + +- Intel I9 + +RAM: + +- 32 GB + +GPU: + +- RTX 5070 Ti +- 16 GB VRAM + +Network: + +- 192.168.1.111 + +Services: + +- Ollama + +Models Installed: + +- Qwen3 +- Qwen Coder +- + +Endpoints: + +```text +http://:11434 +``` + +--- + +## Development Workstation + +Hostname: + +- rdbmacbook + +Type: + +- Intel MacBook Pro + +Purpose: + +- Main software development workstation + +Installed Tools: + +- VS Code +- Claude Code +- Docker +- Git +- SSH + +--- + +## OpenWebUI Host + +Hostname: + +- rdboffice + +Purpose: + +- Browser-based LLM interface + +Connected To: + +- Ollama Server + +URL: + +```text +https://ai.rdbcloud.co.uk +``` + +--- + +# Virtual Machines + +## VM Inventory + +| VM Name | Purpose | CPU | RAM | IP | +| ---------- | -------- | --- | ---- | ------- | +| supabase01 | Database | 4 | 8 GB | x.x.x.x | + +--- + +# LXC Containers + +## Container Inventory + +| IP | LXC Name | Proxmox ID | RAM | +|------------------------------------------------------------------------------------ +| 192.168.68.100 | adguard | 100 (LXC) | 512 MB +| 192.168.68.102 | gitea | 104 (LXC) | 1.00 GB +| 192.168.68.79 | nginxproxymanager | 105 (LXC) | 1.00 GB +| 192.168.68.90 | jenkins | 106 (LXC) | 2.00 GB + +--- + +# Infrastructure Services + +## AdGuard + +Purpose: + +- DNS +- Ad Blocking + +URL: + +```text +http://adguard.lan +``` + +Host: + +- adguard01 + +Port: + +- 3000 + +--- + +## Gitea + +Purpose: + +- Git repositories +- Source control + +URL: + +```text +https://gitea.rdbcloud.co.uk +``` + +Host: + +- https://gitea.rdbcloud.co.uk + +Version: + +- v1.25.0 + +Repositories: + +- RC Compatibility App +- RDB Mobile App +- Infrastructure +- robbond/pedwFrontend +- robbond/proxmox-monitor +- robbond/pleskSaas +- robbond/pedwV2 +- robbond/rc-compat +- robbond/gacapi + +--- + +## Jenkins + +Purpose: + +- CI/CD + +URL: + +```text +http://jenkins.lan:8080 +``` + +Host: + +- jenkins.lan + +Jobs: + +- RC Compatibility Build +- Mobile App Build +- Docker Deployments +- PEDW testing pipeline + +--- + +## Supabase + +Purpose: + +- PostgreSQL +- Authentication +- Storage + +Host: + +- supabase01 + +Environment: + +- Self-hosted + +Database Version: + +- PostgreSQL xx + +--- + +## Nginx Proxy Manager + +Purpose: + +- Reverse Proxy +- SSL + +Host: + +- npm01 + +Managed Hosts: + +| Hostname | Target | +| ------------------------- | -------------------------- | +| \*.coolify.rdbcloud.co.uk | https://192.168.68.115:443 | +| ai.rdbcloud.co.uk | http://192.168.68.97:3001 | +| coolify.rdbcloud.co.uk | http://192.168.68.115:8001 | +| gitea.rdbcloud.co.uk | http://192.168.68.102:3000 | +| gitmcp.rdbcloud.co.uk | http://192.168.68.180:8080 | +| ha.rdbcloud.co.uk | http://192.168.68.96:8123 | +| jellyfin.rdbcloud.co.uk | http://192.168.68.101:8096 | +| nextcloud.rdbcloud.co.uk | http://192.168.68.55:80 | +| pedw.rdbcloud.co.uk | http://192.168.68.109:3000 | +| portal.rdbcloud.co.uk | http://192.168.68.116:3000 | +| prox.rdbcloud.co.uk | https://192.168.68.70:8006 | +| supabase.rdbcloud.co.uk | http://192.168.68.115:8000 | + +--- + +# Development Stack + +## Source Control + +Primary: + +- Gitea + +Workflow: + +```text +Developer + ↓ +Feature Branch + ↓ +Commit + ↓ +Pull Request + ↓ +Merge +``` + +--- + +## CI/CD + +Primary: + +- Jenkins + +Build Flow: + +```text +Gitea + ↓ +Jenkins + ↓ +Tests + ↓ +Docker Build + ↓ +Deploy +``` + +--- + +## Containers + +Runtime: + +- Docker +- Docker Compose + +Container Registry: + +- +- Docker Hub +- Gitea Registry + +--- + +# AI Development Environment + +## Local Models + +| Model | Purpose | +| ----------------- | ------- | +| Qwen3.6:35b-a3b | Coding | +| Qwen2.5-coder:14b | Coding | + +--- + +## Agent Platforms + +Installed: + +- OpenWebUI +- Claude Code +- Cline in VScode + +Planned: + +- OpenHands + +--- + +## OpenHands + +Status: + +- Planned / Testing / Production + +Location: + +- VM / LXC / Docker + +Resources: + +CPU: + +- x + +RAM: + +- x GB + +Connected Systems: + +- Gitea +- Jenkins +- Ollama + +--- + +# Security + +## Secrets Storage + +Current Method: + +- + +Preferred Future: + +- Vault +- 1Password +- Bitwarden +- Environment Variables + +--- + +## Backup Strategy + +Infrastructure: + +- Proxmox backups +- VM snapshots + +Repositories: + +- Gitea backups + +Databases: + +- PostgreSQL backups + +Frequency: + +- Daily +- Weekly +- Monthly + +--- + +# Monitoring + +Current Tools: + +- + +Metrics: + +- Host health +- VM health +- Disk usage +- Jenkins status +- Database health + +--- + +# Known Constraints + +Current Limitations: + +1. Proxmox host has 16 GB RAM +2. AI server has single GPU +3. Limited staging environment +4. + +--- + +# Improvement Opportunities + +Potential Future Enhancements: + +- Dedicated monitoring stack +- Centralised logging +- Local container registry +- OpenHands integration +- Automated deployment pipelines +- Infrastructure-as-Code +- Agent task orchestration + +--- + +# Notes For AI Agents + +When making recommendations: + +- Prefer self-hosted solutions +- Prefer Gitea integrations +- Prefer Jenkins automation +- Prefer Docker deployments +- Consider current RAM limitations +- Assume Ollama is available locally +- Assume local AI workflows are preferred over cloud alternatives diff --git a/context/decisions.md b/context/decisions.md new file mode 100644 index 0000000..e69de29 diff --git a/context/development-context.md b/context/development-context.md new file mode 100644 index 0000000..27bbabb --- /dev/null +++ b/context/development-context.md @@ -0,0 +1,294 @@ +# `/context/development-context.md` + +# Development Environment Context + +This document describes the preferred development workflow, tools, standards, and technology choices used by RDB Solutions Ltd. + +AI coding assistants, IDE agents, and autonomous development tools should read this file before making recommendations or changes. + +--- + +# Primary Development Workstation + +## MacBook Pro (Intel) + +Primary software development machine. + +Typical activities: + +- Application development +- Repository management +- Code reviews +- Testing +- Infrastructure administration +- LLM-assisted development + +--- + +# Source Control + +## Gitea + +Primary Git platform. + +Preferences: + +- Gitea is the source of truth. +- Do not assume GitHub is available. +- Do not recommend GitHub-specific workflows unless explicitly requested. +- Pull Requests should be used for significant changes. +- Commit messages should be clear and descriptive. + +Preferred commit style: + +```text +feat: add VM status endpoint + +fix: correct Jenkins deployment script + +docs: update infrastructure documentation +``` + +--- + +# CI/CD + +## Jenkins + +Primary automation platform. + +Current and future responsibilities: + +- Build applications +- Run tests +- Build Docker images +- Deploy services +- Run scheduled maintenance tasks +- Execute agent-generated work after review + +Preferred pipeline behaviour: + +1. Pull code from Gitea +2. Run validation +3. Run tests +4. Build artefacts +5. Deploy to target environment + +Agents should favour Jenkins automation over manual deployment procedures. + +--- + +# Containerisation + +## Docker + +Preferred deployment format. + +Goals: + +- Consistent environments +- Easier deployment +- Simpler rollback +- Portable services + +When proposing software: + +Prefer: + +- Docker Compose +- Single-container deployments +- Simple architecture + +Avoid: + +- Kubernetes unless there is a clear requirement. + +--- + +# Database Platforms + +## PostgreSQL + +Preferred database platform. + +Current implementation: + +- Supabase-hosted PostgreSQL + +Preferred characteristics: + +- Relational data model +- Strong typing +- Auditable schema changes + +--- + +# IDEs and Editors + +Current tools may include: + +- VS Code +- Cursor +- Claude Code +- OpenWebUI +- Browser-based assistants + +Recommendations should remain editor-agnostic where possible. + +--- + +# Local LLM Infrastructure + +## Ollama + +Primary local inference platform. + +Purpose: + +- Local coding assistance +- Experimentation +- Agent backends +- Development support + +## Available Hardware + +LLM Server: + +- RTX 5070 Ti +- 16 GB VRAM +- 32 GB RAM + +Agents should assume local inference is available. + +--- + +# Preferred Models + +Potential models include: + +- Qwen Coder +- Qwen3 +- DeepSeek Coder +- Future coding-focused local models + +Model selection should prioritise: + +1. Code quality +2. Reasoning ability +3. Low operational cost +4. Local execution + +--- + +# AI-Assisted Development + +RDB actively uses AI-assisted development. + +Expected uses: + +- Code generation +- Refactoring +- Documentation +- Testing +- Architecture review +- Infrastructure planning + +Agents should produce: + +- Small reviewable changes +- Clear reasoning +- Minimal surprises + +Avoid: + +- Massive rewrites +- Unrequested architecture changes +- Hidden behaviour + +--- + +# Task Management + +Preferred approach: + +Backlog-driven development. + +Work should be decomposed into: + +- Small tasks +- Independent tasks +- Reviewable tasks + +Good example: + +```text +TASK-001 +Create API endpoint for VM status + +TASK-002 +Add frontend status widget + +TASK-003 +Write integration tests +``` + +Avoid: + +```text +Rewrite the entire application +``` + +--- + +# Documentation Standards + +Projects should contain: + +```text +README.md +TASKS.md +ARCHITECTURE.md +CHANGELOG.md +/context/ +``` + +Documentation should always be updated when functionality changes. + +--- + +# Deployment Philosophy + +Preferred order: + +Development +→ Test +→ Staging +→ Production + +Production deployments should be repeatable and automated. + +Avoid: + +- Manual server modifications +- Untracked configuration changes +- Snowflake servers + +--- + +# Development Principles + +Prefer: + +- Simplicity +- Maintainability +- Incremental improvements +- Clear documentation +- Automation +- Reproducibility + +Avoid: + +- Unnecessary complexity +- Vendor lock-in +- Excessive dependencies +- Platform-specific assumptions diff --git a/context/discovery-log.md b/context/discovery-log.md new file mode 100644 index 0000000..e69de29 diff --git a/context/infrastructure-context.md b/context/infrastructure-context.md new file mode 100644 index 0000000..3453024 --- /dev/null +++ b/context/infrastructure-context.md @@ -0,0 +1,193 @@ +# `/context/infrastructure-context.md` + +# RDB Solutions Ltd Infrastructure Context + +## Physical Machines + +### Proxmox Server + +Purpose: + +- Main virtualisation host +- Runs infrastructure services and VMs/LXCs + +Current known services: + +- AdGuard +- Jenkins +- Nginx Proxy Manager +- Supabase VM +- Potential future OpenHands runner + +Current constraint: + +- Proxmox host currently has 16 GB RAM, so resource allocation should be conservative. + +Preferred usage: + +- Infrastructure services +- CI/CD +- Reverse proxy +- Databases +- Lightweight agent runners +- Isolated test environments + +### LLM / Ollama Machine + +Hardware: + +- NVIDIA RTX 5070 Ti with 16 GB VRAM +- 32 GB RAM + +Purpose: + +- Hosts Ollama +- Runs local LLMs such as Qwen +- Provides local model inference for development assistants and agent tooling + +Preferred usage: + +- Local coding models +- Agent backend model provider +- OpenWebUI integration +- Possible API endpoint for tools like Claude Code-compatible wrappers or OpenHands + +### MacBook Pro Intel + +Purpose: + +- Main development workstation + +Preferred usage: + +- IDE work +- Local coding +- Git commits +- Testing frontend/mobile apps +- Managing repositories and project files + +### Mini PC + +Purpose: + +- Hosts OpenWebUI +- Points to the Ollama machine for local LLM access + +Preferred usage: + +- Browser-based LLM interface +- Local AI assistant access +- Shared AI UI across the network + +## Virtual Machines and Containers + +### Jenkins + +Purpose: + +- CI/CD automation +- Build pipelines +- Test execution +- Deployment workflows + +Preferred future use: + +- Pull code from Gitea +- Build Docker images +- Run tests +- Deploy to hosted or self-hosted targets + +### Gitea + +Purpose: + +- Self-hosted Git server +- Source code control +- Replacement for Bitbucket/GitHub where possible + +Preferences: + +- Use Gitea as the source of truth +- Avoid GitHub-specific assumptions +- Use issues, milestones, and pull requests where useful + +### Supabase VM + +Purpose: + +- Database backend +- Postgres services +- Possible authentication/storage/API support depending on project needs + +Preferred use: + +- Application database +- Internal APIs +- Development/staging database +- Storage where suitable + +### Nginx Proxy Manager + +Purpose: + +- Reverse proxy +- SSL termination +- Routing internal services to friendly hostnames + +Preferred use: + +- Route apps and tools through internal domains +- Manage certificates +- Avoid exposing unnecessary services directly + +### AdGuard + +Purpose: + +- DNS filtering +- Local DNS support +- Network-wide ad/tracker blocking + +Potential use: + +- Internal DNS names for services +- Easier access to tools like Gitea, Jenkins, OpenWebUI, Supabase, etc. + +## LLM and Agent Architecture + +RDB Solutions Ltd is interested in using local LLMs and coding agents to improve productivity. + +Candidate tools: + +- Ollama +- Qwen models +- OpenWebUI +- OpenHands +- Claude Code-style workflows pointed at local models where possible +- IDE assistants +- Task/backlog-driven development agents + +Important preference: + +- Agents should work from clear task files, context files, and isolated environments. +- Agents should not make broad uncontrolled changes. +- Agents should be able to run in a VM, LXC, or Docker container depending on risk level. + +## Recommended Isolation Model + +For autonomous coding agents: + +Preferred: + +- Dedicated VM or LXC on Proxmox +- Access only to required repositories +- Limited credentials +- Network access restricted where possible +- Disposable working directories +- Git-based review before merge + +Avoid: + +- Giving agents unrestricted access to the main development machine +- Giving agents broad production credentials +- Allowing agents to directly deploy without review diff --git a/context/open-questions.md b/context/open-questions.md new file mode 100644 index 0000000..e69de29 diff --git a/context/product-brief.md b/context/product-brief.md new file mode 100644 index 0000000..e69de29 diff --git a/context/repository-context.md b/context/repository-context.md new file mode 100644 index 0000000..da72488 --- /dev/null +++ b/context/repository-context.md @@ -0,0 +1,31 @@ + +Repository Context + +Git Server: + +https://gitea.rdbcloud.co.uk + +Repository: + +rdb-discovery + +Primary Workflow: + +Feature Branch +→ Commit +→ Pull Request +→ Merge + +Branch Naming: + +feature/TASK-001 +fix/TASK-001 +docs/TASK-001 + +Commit Format: + +feat: add command +fix: correct behaviour +docs: update documentation +test: add tests +chore: project maintenance diff --git a/context/risks.md b/context/risks.md new file mode 100644 index 0000000..e69de29 diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..b003935 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,22 @@ +[project] +name = "rdb-discovery" +version = "0.1.0" +description = "Markdown-first discovery and delivery workflow CLI for software projects." +readme = "README.md" +requires-python = ">=3.11" +dependencies = [ + "typer>=0.12.0", + "rich>=13.0.0" +] + +[project.optional-dependencies] +dev = [ + "pytest>=8.0.0" +] + +[project.scripts] +rdb = "rdb_discovery.cli:app" + +[build-system] +requires = ["setuptools>=68"] +build-backend = "setuptools.build_meta" diff --git a/src/rdb_discovery.egg-info/PKG-INFO b/src/rdb_discovery.egg-info/PKG-INFO new file mode 100644 index 0000000..250f67b --- /dev/null +++ b/src/rdb_discovery.egg-info/PKG-INFO @@ -0,0 +1,55 @@ +Metadata-Version: 2.4 +Name: rdb-discovery +Version: 0.1.0 +Summary: Markdown-first discovery and delivery workflow CLI for software projects. +Requires-Python: >=3.11 +Description-Content-Type: text/markdown +Requires-Dist: typer>=0.12.0 +Requires-Dist: rich>=13.0.0 +Provides-Extra: dev +Requires-Dist: pytest>=8.0.0; extra == "dev" + +# rdb-discovery + +A small CLI-first tool for repeatable software project discovery and delivery. + +It creates markdown files that help humans and AI coding agents understand: + +- what is being built +- why it exists +- what questions remain open +- what task should be done next +- what stage the project is currently in + +## Install for local development + +```bash +python3 -m venv .venv +source .venv/bin/activate +pip install -e '.[dev]' +``` + +## Commands + +```bash +rdb init +rdb discover +rdb status +rdb next +rdb start TASK-001 +rdb complete TASK-001 +rdb handoff +``` + +## Bootstrap workflow + +1. Run `rdb init` +2. Run `rdb discover` +3. Run `rdb status` +4. Run `rdb next` +5. Give the next task to Claude Code or Cline +6. Commit after each completed task + +## Principle + +Markdown files are the source of truth. No database is required. diff --git a/src/rdb_discovery.egg-info/SOURCES.txt b/src/rdb_discovery.egg-info/SOURCES.txt new file mode 100644 index 0000000..fda3f8b --- /dev/null +++ b/src/rdb_discovery.egg-info/SOURCES.txt @@ -0,0 +1,17 @@ +README.md +pyproject.toml +src/rdb_discovery/__init__.py +src/rdb_discovery/cli.py +src/rdb_discovery/discovery.py +src/rdb_discovery/handoff.py +src/rdb_discovery/status.py +src/rdb_discovery/tasks.py +src/rdb_discovery/templates.py +src/rdb_discovery.egg-info/PKG-INFO +src/rdb_discovery.egg-info/SOURCES.txt +src/rdb_discovery.egg-info/dependency_links.txt +src/rdb_discovery.egg-info/entry_points.txt +src/rdb_discovery.egg-info/requires.txt +src/rdb_discovery.egg-info/top_level.txt +tests/test_tasks.py +tests/test_templates.py \ No newline at end of file diff --git a/src/rdb_discovery.egg-info/dependency_links.txt b/src/rdb_discovery.egg-info/dependency_links.txt new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/src/rdb_discovery.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/src/rdb_discovery.egg-info/entry_points.txt b/src/rdb_discovery.egg-info/entry_points.txt new file mode 100644 index 0000000..37165c3 --- /dev/null +++ b/src/rdb_discovery.egg-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +rdb = rdb_discovery.cli:app diff --git a/src/rdb_discovery.egg-info/requires.txt b/src/rdb_discovery.egg-info/requires.txt new file mode 100644 index 0000000..fc3d545 --- /dev/null +++ b/src/rdb_discovery.egg-info/requires.txt @@ -0,0 +1,5 @@ +typer>=0.12.0 +rich>=13.0.0 + +[dev] +pytest>=8.0.0 diff --git a/src/rdb_discovery.egg-info/top_level.txt b/src/rdb_discovery.egg-info/top_level.txt new file mode 100644 index 0000000..8f562da --- /dev/null +++ b/src/rdb_discovery.egg-info/top_level.txt @@ -0,0 +1 @@ +rdb_discovery diff --git a/src/rdb_discovery/__init__.py b/src/rdb_discovery/__init__.py new file mode 100644 index 0000000..3dc1f76 --- /dev/null +++ b/src/rdb_discovery/__init__.py @@ -0,0 +1 @@ +__version__ = "0.1.0" diff --git a/src/rdb_discovery/__pycache__/__init__.cpython-314.pyc b/src/rdb_discovery/__pycache__/__init__.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a43f72e5f30c6bd4c0d5c5e8c63d510fce3ead79 GIT binary patch literal 202 zcmdPquSzPO4oI8_;x+ql!Te`oPS{$asTK Lw28Zj1;_;et)4Xv literal 0 HcmV?d00001 diff --git a/src/rdb_discovery/__pycache__/cli.cpython-314.pyc b/src/rdb_discovery/__pycache__/cli.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..87455b2d6528e435312bca969c03f70cfadff69e GIT binary patch literal 8874 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 O{bwzIc0h@3(tiV6nvxCx literal 0 HcmV?d00001 diff --git a/src/rdb_discovery/__pycache__/discovery.cpython-314.pyc b/src/rdb_discovery/__pycache__/discovery.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..6d97dd1407f3e1358054694743b3311578f51edf GIT binary patch literal 2250 zcmaJ?&2JM&6rc5ecz5kM#z_eww&Frb>w;s6S_KKzs3K{TmO5q$X(cEt$2&=u*t_oR zx`35~D^)6-pogkCMs20SsX69YiGLs=AV;GjAypi>8PO_IPkl4~NE%Sz?!0+3Z$970 zZ;nTLbOiLyZ;5NCB!vECjSdA`g77j2LLFU2hA@RD1z#9ua$qVjDf%Lp#VKi0_GOR- zLz+@1gMM%r8S(%!lmS$V`l=BGS>>{3Xh%^>f6f+92{cr29M>=Sw(EG%l&pe}S)DKV zi}wW@IX7)gm=`WhT%4Jj&Rr}DFpCvN5hx-wUuHq5qgiw{aQa(>Ea;1)3&Nsc2u8pV z4atz9C{Th>R4AIEXM%8w&~tW2DiPQ9iOj^%S5@q>!3Mb9Tx?etKk;CR(xX!PvqURS5R0#Gz8ob?|+5MP=S4<$~|Ab049(%q0&KFGJk9 zIoGi=<8HB9#*Pm?*4&Kimbj3%Y_I5kjmgc7M~X~k?)G%0(aKF4HqEvPv1yWnu!jc4 zYx!&k4GKdq7~9q(O%dv+2l|^b)J+BT#+pGUsi-H~)R?3r(1w^4MgxbNJxq!qMSsx? z(o0wt6&1$Y)B`nlKorc#9AKamp^g$kxa@6JlvyRetL#d7SYbbb+rV+B+5tF>U?%g< zK4NZX^meO43;oRTFgDok*i(ViWkcDsqL^o9+D8x|YPP-WYxNK~UHzV$c9!X!s1h_n zvqBgd;n@J^LCz)4W#Bzm!ksyJ7RLMNP1IQ_e_)>qnjIai6FJ#aQQ^jYuZ}W$_ulx7 zU<%bhRW!S^E847DK&@lhlh%;gW z*+S|74ML*$_=aDLb|W@jau-I+)=VlugOvhdaZgoz!}fgBU8?A(3*Hj6#sO)P z%ldLUo&MjV*W{`{pFRzltU}JamhCK1VYwE#vP=b?3dvevIaw27#hM1J^|LnU3b|B> zDy@rBxx8fAgev^H9@VcC+s7s|5DgMsDHQ-uWPlvILL~4Dm3`ML+@zZAxh({tl3T$J zmHEV!P8f6(7KVgnB4L3}Amsw@^9tAsta#0aM( z5Ml8^2MQI~#@Yp(_EeGqEe96{1$ESnqGz_W=!?!Nq`fR0d<%^Y1R*UzJ z-%G3&*WX=_|9){hG_;||SEhcke!ce7wVz9kk+Yj4XSYT^*f=(}?l(n2n-H2JYzDZe z$L=IQypWjL}wDcn_y>@V0job;}4&R-5q#oW8kv<9v_#bNNe>|474-Uo7W#tFi z(73Gq=Q(1y!O<|zt@HZlC?mkLBNAps!cx1Yl_2&ngV(<#EQwPB0Te^6w}QKaLpo>2 zn1T(eqa7p&hhH$ZqVb9hKz|WRmsk?>@K)ydhxVI6-)VjyywyPtc)f=-A&1yh%H!!36w7 F{{l>k?=%1a literal 0 HcmV?d00001 diff --git a/src/rdb_discovery/__pycache__/handoff.cpython-314.pyc b/src/rdb_discovery/__pycache__/handoff.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c3cbe2e78e6c5588b31d242af0ad13b3ec0be9ce GIT binary patch literal 1964 zcmZ`)-*3}K96vj@<0MX+LQ7i)<%myLW8Q&3hTT!+N7$M6JJ_`V@Kyp z8F|X2Nn4-jW00o7BYWDv5>y761sc*cO}u5WiPwG4Nq?x&lYIW(_xri;uer8m^zT`@?LXa?B*JCELJ=ce&v~04?tQaKg)VoP4%F8P)joL><@7614E=TQWfuQ3oUR6^_GhuQEi2l-mt_y2`=#N8O zQHR6S#WneDnT9TkAdBSXdZZ-I3;@%JS~!EyZSaL&D@B%hNJoY) zl_U)=MM(xL(w>j!(C3pp1C!|b@G<+811umCN7TX+JdWnYLnvY2^G`z0-@QvM3Cr5s z+QJ;fK$bd276%zWwj+b@%SR5pI5wWd0p{KDf=JA&4*q{a4+B~KzM~d6OC9eel%&$S_9lhY zRQGJ={@T5@pVzmq|G3cAhVL$IFSReWtL?8J=nv%ww>uMsC)x+EB4~2Hr=VEsuC}dp zMrR(YvoECB4@=)I?M&^so#FQ%OVfWu(_J;m0o~Nd&Noj-ryq?@cSdG9+H5y9){fmD zx;NAro9$@XZfd+e@pK~lXd>GgztGXp*%iVj0YZe-&@+%T#Qcq4er<~=ot*5S=^>~i>kggFk+ zFg#5-C|)`J-e2)|L_ I1qk>34_bD{hl52aP59y-#_oA=(#&iA~w z$_h6iTzFVN_p1fq589Lf_XO;45OE%+L17UXVFqg?+7hvhNQQ(oQ>2J>#AetUL9y0= zVyl6S*RU&gq#c5GD9#oLxc2CG0cOgHY*sT8Mq112xV26sj2pPRxtd7DjfDQ0o@At$ z*b%JwNo0o~i9Ez$+S2n4Knl-%aGBj;3R5hKq*#L%)MDEsyG<+CG=r0S+VnFYNww`6+a@uG8TJkBdoffGNy7EvBvfSc7G5L&hF({lecu{*?D5H1rz zKz0k4GiW2ZtU$XgZUj=;@!dozP)7{VU8LVGuCpRo3I^yJQU!=XcTqCOV(b*Bgq&oB zfDpTE{RmP%3V0#OG3G7n81?|1lpaM}8?X?@s$I0&$10jcX&jd-=`KkY(y2K9L9eq0 zj{o6b>OCd(H+o$ypt!MoWYdwiA&=cP z+5#W~%39!kDa`_wWjUSo=eRb@Rb3xnvzT^Wl%i?NXkV%v4=kPUm{!qK)E2FNUV)40 z>YG)w>SB)fn45#4k}{Zk7y6wcE|e#f>P{3ZPIkm}`Dpp8X`P3PcLIQ5d(Baa!m&vBN?{j}OuM?6 z)(xH8h*L^rmjV_}y>Hq?^p6{0R-A}AZ$&mSCYF)N!xN8prFYBYd)(Ny<>}gyd)MXO z9XYfihjy#{Pd#T>j7^U(AATl#cdKfjIox0C>y(vE_uy^)wtlO;+i?tTI0kpC&KG&m zy-jzh#8cSqI6@l^{I73UKF_OA{ePLf+<`w3PK&|IUASGYOr?{?CpdcB{pqanDW@?O zU>s)#6b7)UNHHn}qLh{ArInpI07NUPeOgw(61yWqkyii(9h-<9ev3pNf{p(saO}Y zs=?0|4n%R-liC74HJly@oyI_`zu2NOl})^O-G(T`)=;R>ksSJ>K-LF8+v}~+G^;#W50Lv?#)eS-GKzvO~1?4`7sf>@qp8G$K%%* z@KvN&iqx8p96rLC^h_ahZ{d-!Skxvb^&)XXzA!?Zee+ssA*1&5PQ0aH>(prb5@YN) VXnqdoo}Kmihny literal 0 HcmV?d00001 diff --git a/src/rdb_discovery/__pycache__/tasks.cpython-314.pyc b/src/rdb_discovery/__pycache__/tasks.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..2c02edd23e14b67964d78d44d8b30af4bdb79ece GIT binary patch literal 4390 zcmbtXO>7&-6`mz``M1kI{Zz88KT9$#O0rbjila1%V>Om-TC%{b^>;%3je6vX9C>k?lfZ|5xOnFk|ouMN##Ho>mnN zs_s-mPb4!5ja1lZLccPx*fP`A71R29rbZ^mj>J-ZVW=D-4Fi4Q?!~~&B>bM!eh|}o zX-l%=4K3_ug?V-gggm;0bVfwFZHCwF-LTS^SkV@O9xBGso4}+amQ#bCRiLk?P#p6n1UXI5NPdq-arg9k~3-S2- zxkRSY;=D{!H10VgBLX! zYlB#womG|8;Au6Po0k{^Z zPv{yMsuMR7ZCJ_5Qo*|71JG0tpSB;eJbI3TOmLfIx2Lv9QMgKL)N|@IsL|wxoI3pr zaECZ8qJA>{O3E@46SXF2H9~2H{ozX69G&C<>O!Co^6o^Y{(8ku))uQAe1u{SOEXG5 z*_9;QP~{)1%-RP~mW@?ClW3jos=@vaadFl8ZrEcdQCFQL=TrlH|5#mi0&;Yn@Xa z#3RWQB{8ro%`ny<(WYHpP_<@%ff4OlUSgyB{#PeP!iXkV!97R#mj3E2_wr!+(;?Oz z#qzfujcp?2Mr}wU0LgyiB6v4BFrWH{%juVo4Bul6m#ie!R9cxcoEMdAin^eLeTGB6 z4rfI(+*nSe;<|iYH&}9d3_mdtKQ}sZIx=xKE{+xYv->PxxQ)zwv_D zfAqn~A0mIUub%y*u+e{dX|9m?ZThjVqvQ*%`$8MOp1*cIO8q6gWG@FA3de8zm$;`* z9i^tj>rIDOxMI`5lJNIqCvUfuf?eyuuH}(pFjNZmuLt`#f&&jDOOCS3drP<}d^}Y+ zbhqzL-*V!211pE__ucDzkof&TsrTf1@5y5CsfVFrWAusZy=^br*S8fwLNGtR185dV zknlv#r)OGqg4pqX|;~Hh=zZJ zkO|3S-3TB#XKM#&CI?Jp;DFg6Sk;s|5w_Dh%G^-Uj*ZF4Ai zaNB`|z|MkL6Njszxgg|->_wg;C$geLbPf`vaP8hcLL!*hGCYdBK<^GYjczKro2Z%Z z+|2>_S2KNhj`k4}g{%N13dPF1v{;>_#ltDuXHKP-@|<9T?(LT^r&POSe|1VF`vv;7 z_Q5^WMsw7fnFoh3CIjL0Sl9nIXZ|&?vJ5R4G)_jSeMJfD-DJN2(-{;98@W6 zya8ag2OWRzZ+KNQraWA#@Gu`BczXN&U!E*XZ}8op3k{q9A2st0CH}xVf1og0;yXX% zJD)Zmyz9N=U2#3w|3Fv^y}g=yG*a*unn-z% zdJ<1UFCir@3E7sNVVEya=fBXwFHrN}kbASP;gjB9_pY_RwL1If%%_=R-B{jV7JRqP ze{_DWsdwdv_s8#z7lk+S?kxwh@y~56<0Cf#-!nT@w@CGCAH2uD?t$CEb03fjb>hEL C0X@k8 literal 0 HcmV?d00001 diff --git a/src/rdb_discovery/__pycache__/templates.cpython-314.pyc b/src/rdb_discovery/__pycache__/templates.cpython-314.pyc new file mode 100644 index 0000000000000000000000000000000000000000..f2d8a6ac79ed41ec0b2e8ffd0988475c0492dae4 GIT binary patch literal 3916 zcmai1O>7&-6&_NnAFnN2vaQIj?P_cVZiJea6B}q?6qOZ8j)myYD_Sm4B-DyKBG+8* zYBlEWZHCVqf4=nSuO}G$CoQti*@Vcwh(NgPfZbr4z0N*hPHs7OFR$}cMCX?W z?pfNJV%M2-aXqI8odL&k2Av^i*cow-Ikt1$IpK^tC!JHym^1F2cFs6woeAf)Sz8Y| zlkX2Z=dMD&bAFOJKbT~}m>zL1OtYCw-;ny5T>H2ihEluQm!ZPa+HiHXb*?N!EgtJx z&sSx+C*u7=AS>7FUh9)-zL@d&imcf76TZC2pYZz)p-2eG`A{`Q3}QisyS^vFvcTf5 z3sjT~)*WqN{P!eo`nDUir0q4~}B#HOjiM6Z>M9 z9{gyUZzMea4)JVz+HiIPQMc{lJBx<#K#5rOmYa=oSt!L9Vqc5ccY8ale(2X5b&dGd|3z2D{G7kLf2v?bi9pz}dHI@mEeN{6;SEji` zbXOp)AZ)oFxn&Jv)z^N-ycmm3q*g;66-2xBS|;CBs!@+j8q(9Z;ISU3tE&?*Q`1ct&Qk>s> zZxbfD=PkyjdA2k;K#RhS8*lOgQcX~{Z|v(TLb9;3%#q5EkV0J5L?AShEKzlvFCs~7 z`(5bA8+~^g4IgF(|HfoGJ4k29s)=QPe8xblK*ZM+#3x)zQ0K)Ja2izCx6tl9d z*O8mw8TWb5<8F2WVRp$Qp{ICLxV1D?%9sgp3SC7+2us(^yc^o1Ga*$EYh1`Z9CBpA zD?;lD0(|3~p3eT*yAjZZ8lRRgi*B7LE^QV|8!Pi`gcX0t8La4I8RxCKVFMLjM62zg z6Rm)TOOGR?ANivi0dbCoCiDVQXP^gdASv;nQaA1ZV18#hFOyN`!I;cHS;;S-)` z65ay4Ds znJXYgRe^i7(=~@ubH1Tv-BeuaV3>8=wzs#pcU)DqqkRoe)6#C)&V#km%KE$1s_vj6 zc30G?l5WbE_9(UCr(~FcF7cg>&~Bad{IdQ9!1o$~_WSM8h4r=K($9;fyUQy}n`Ptz zqc43%@EySN#Ty_F*cSUB|MqVf^T4~v?&qpGC+FnT8PTFCan{1LH}Xv$$JZ z@mn^O8~Zo?`PAyr&tEpp&);sEpU-cZy%)H^%J4$>GYHCf>W?4}*u&hlhmK&mWunwO<>U~j!Mnwo ze0w-T?}0blBc^4RWUV#W(7T1V5r`2?)6(<9%8b<>N;+zLC^_FAtk*n0Zre?oT1zy) z&sZ@fOni*~kooN)?Zy?M6(zydt54zVG`1GS+fAsaKv(GL2>FUR;Us;o1Sd*VxpXq1Tr*fr{dF)v^Hb6E|s?B^iV@P;|A)-AxM8b( zg_Kmh-T;{TntE(ovGYDtW%<&K?QZ?v4*E)MoHhGiUEEEu4K-gUC zT$c;cI|99l=67hHIo7^VDnXft*-eISkQD+Js#I#*rP6ML#v0TGB{$`Ut_J>2QpG11 z)}5u&{RhDE<@L4An6j~bs#{Ho5EI6kTzm`*bN-{a4J zI|I2~?jP*RGj`=)?A(8> Path: + return Path.cwd() + + +def append_run_log(root: Path, event: str, task_id: str = "", notes: str = "") -> None: + path = root / "RUN_LOG.md" + if not path.exists(): + path.write_text("# RUN_LOG.md\n\n| Date | Event | Task | Notes |\n|---|---|---|---|\n", encoding="utf-8") + line = f"| {datetime.now().isoformat(timespec='seconds')} | {event} | {task_id} | {notes} |\n" + with path.open("a", encoding="utf-8") as handle: + handle.write(line) + + +@app.command() +def init() -> None: + """Create the initial markdown project structure.""" + root = root_path() + created: list[str] = [] + skipped: list[str] = [] + + for relative_path, content in CONTEXT_FILES.items(): + if write_file_if_missing(root, relative_path, content): + created.append(relative_path) + else: + skipped.append(relative_path) + + console.print("[bold green]Initialised rdb-discovery files.[/bold green]") + if created: + console.print("\nCreated:") + for item in created: + console.print(f"- {item}") + if skipped: + console.print("\nSkipped existing files:") + for item in skipped: + console.print(f"- {item}") + + +@app.command() +def discover() -> None: + """Ask the core discovery questions and append answers to the discovery ledger.""" + root = root_path() + write_file_if_missing(root, "context/discovery-log.md", CONTEXT_FILES["context/discovery-log.md"]) + + for index, question in enumerate(core_questions(), start=1): + qid = f"Q-{index:03d}" + answer = typer.prompt(question) + confidence = typer.prompt("Confidence?", default="Medium") + follow_up = typer.prompt("Follow-up needed?", default="No") + append_discovery_answer(root, qid, question, answer, confidence, follow_up) + + append_run_log(root, "Discovery completed", notes="Core questions answered") + console.print("[bold green]Discovery complete.[/bold green]") + + +@app.command() +def status() -> None: + """Show current project stage and task counts.""" + root = root_path() + console.print(f"[bold]Project stage:[/bold] {project_stage(root)}") + + counts = task_counts(root) + table = Table(title="Task Counts") + table.add_column("Status") + table.add_column("Count") + + if counts: + for status_name, count in counts.items(): + table.add_row(status_name, str(count)) + else: + table.add_row("None", "0") + + console.print(table) + + +@app.command(name="next") +def next_task() -> None: + """Show the next Todo task.""" + task = get_next_task(root_path()) + if not task: + console.print("[yellow]No Todo task found.[/yellow]") + raise typer.Exit(code=0) + + console.print(f"[bold]{task.task_id}[/bold] - {task.title}") + console.print(f"Status: {task.status}") + console.print(task.body) + + +@app.command() +def start(task_id: str) -> None: + """Mark a task as In Progress.""" + root = root_path() + if not update_task_status(root, task_id, "In Progress"): + console.print(f"[red]Task not found:[/red] {task_id}") + raise typer.Exit(code=1) + append_run_log(root, "Task started", task_id=task_id) + console.print(f"[green]Started {task_id}.[/green]") + + +@app.command() +def complete(task_id: str) -> None: + """Mark a task as Done and record validation notes.""" + root = root_path() + notes = typer.prompt("Validation notes", default="Not tested") + if not update_task_status(root, task_id, "Done"): + console.print(f"[red]Task not found:[/red] {task_id}") + raise typer.Exit(code=1) + append_run_log(root, "Task completed", task_id=task_id, notes=notes) + console.print(f"[green]Completed {task_id}.[/green]") + + +@app.command() +def handoff() -> None: + """Print an AI-agent handoff summary.""" + console.print(build_handoff(root_path())) diff --git a/src/rdb_discovery/discovery.py b/src/rdb_discovery/discovery.py new file mode 100644 index 0000000..3ead654 --- /dev/null +++ b/src/rdb_discovery/discovery.py @@ -0,0 +1,38 @@ +from __future__ import annotations + +from datetime import date +from pathlib import Path + +from .templates import CORE_QUESTIONS + + +def append_discovery_answer( + root: Path, + question_id: str, + question: str, + answer: str, + confidence: str, + follow_up_needed: str, +) -> None: + log_path = root / "context" / "discovery-log.md" + log_path.parent.mkdir(parents=True, exist_ok=True) + + if not log_path.exists(): + log_path.write_text( + "# Discovery Log\n\n" + "| ID | Question | Answer | Confidence | Follow-up needed | Linked decision | Linked task | Date |\n" + "|---|---|---|---|---|---|---|---|\n", + encoding="utf-8", + ) + + safe_answer = answer.replace("|", "\\|").replace("\n", " ").strip() + line = ( + f"| {question_id} | {question} | {safe_answer} | {confidence} | " + f"{follow_up_needed} | | | {date.today().isoformat()} |\n" + ) + with log_path.open("a", encoding="utf-8") as handle: + handle.write(line) + + +def core_questions() -> list[str]: + return CORE_QUESTIONS diff --git a/src/rdb_discovery/handoff.py b/src/rdb_discovery/handoff.py new file mode 100644 index 0000000..aa1560c --- /dev/null +++ b/src/rdb_discovery/handoff.py @@ -0,0 +1,45 @@ +from __future__ import annotations + +from pathlib import Path + +from .status import project_stage, task_counts +from .tasks import get_next_task + + +def build_handoff(root: Path) -> str: + next_task = get_next_task(root) + counts = task_counts(root) + lines = [ + "# AI Agent Handoff", + "", + f"Project stage: {project_stage(root)}", + "", + "## Task Counts", + ] + if counts: + for status, count in counts.items(): + lines.append(f"- {status}: {count}") + else: + lines.append("- No tasks found") + + lines.extend(["", "## Next Task"]) + if next_task: + lines.extend([ + f"- ID: {next_task.task_id}", + f"- Title: {next_task.title}", + f"- Status: {next_task.status}", + ]) + else: + lines.append("No Todo task found.") + + lines.extend([ + "", + "## Agent Rules", + "- Implement one task only.", + "- Make the smallest useful change possible.", + "- Do not rewrite unrelated code.", + "- Run relevant tests.", + "- Update documentation if behaviour changes.", + "- Report files changed, validation, risks, and next recommended task.", + ]) + return "\n".join(lines) + "\n" diff --git a/src/rdb_discovery/status.py b/src/rdb_discovery/status.py new file mode 100644 index 0000000..b66c802 --- /dev/null +++ b/src/rdb_discovery/status.py @@ -0,0 +1,25 @@ +from __future__ import annotations + +from pathlib import Path + +from .tasks import read_tasks + + +def project_stage(root: Path) -> str: + if not (root / "context" / "discovery-log.md").exists(): + return "NOT_INITIALISED" + tasks = read_tasks(root) + if not tasks: + return "DISCOVERY" + if any(task.status.lower() == "in progress" for task in tasks): + return "BUILDING" + if any(task.status.lower() == "todo" for task in tasks): + return "TASKS_READY" + return "REVIEW_READY" + + +def task_counts(root: Path) -> dict[str, int]: + counts: dict[str, int] = {} + for task in read_tasks(root): + counts[task.status] = counts.get(task.status, 0) + 1 + return counts diff --git a/src/rdb_discovery/tasks.py b/src/rdb_discovery/tasks.py new file mode 100644 index 0000000..02faa78 --- /dev/null +++ b/src/rdb_discovery/tasks.py @@ -0,0 +1,71 @@ +from __future__ import annotations + +from dataclasses import dataclass +from pathlib import Path +import re + +TASK_HEADING_RE = re.compile(r"^##\s+(TASK-\d+)\s+-\s+(.+)$", re.MULTILINE) +STATUS_RE = re.compile(r"^Status:\s*(.+)$", re.MULTILINE) + + +@dataclass +class Task: + task_id: str + title: str + status: str + body: str + + +def read_tasks(root: Path) -> list[Task]: + tasks_path = root / "TASKS.md" + if not tasks_path.exists(): + return [] + + text = tasks_path.read_text(encoding="utf-8") + matches = list(TASK_HEADING_RE.finditer(text)) + tasks: list[Task] = [] + + for index, match in enumerate(matches): + start = match.start() + end = matches[index + 1].start() if index + 1 < len(matches) else len(text) + block = text[start:end] + status_match = STATUS_RE.search(block) + status = status_match.group(1).strip() if status_match else "Unknown" + tasks.append(Task(match.group(1), match.group(2).strip(), status, block)) + + return tasks + + +def get_next_task(root: Path) -> Task | None: + for task in read_tasks(root): + if task.status.lower() == "todo": + return task + return None + + +def update_task_status(root: Path, task_id: str, new_status: str) -> bool: + tasks_path = root / "TASKS.md" + if not tasks_path.exists(): + return False + + text = tasks_path.read_text(encoding="utf-8") + matches = list(TASK_HEADING_RE.finditer(text)) + + for index, match in enumerate(matches): + if match.group(1) != task_id: + continue + + start = match.start() + end = matches[index + 1].start() if index + 1 < len(matches) else len(text) + block = text[start:end] + if STATUS_RE.search(block): + new_block = STATUS_RE.sub(f"Status: {new_status}", block, count=1) + else: + lines = block.splitlines() + lines.insert(1, f"Status: {new_status}") + new_block = "\n".join(lines) + "\n" + + tasks_path.write_text(text[:start] + new_block + text[end:], encoding="utf-8") + return True + + return False diff --git a/src/rdb_discovery/templates.py b/src/rdb_discovery/templates.py new file mode 100644 index 0000000..3becffd --- /dev/null +++ b/src/rdb_discovery/templates.py @@ -0,0 +1,155 @@ +from __future__ import annotations + +from pathlib import Path + +CORE_QUESTIONS = [ + "What problem are we solving?", + "Who is the user?", + "What does success look like?", + "What is the minimum useful version?", + "What data do we need?", + "What systems must it connect to?", + "What are the risks?", + "What must not happen?", + "How will we test it?", + "How will it be deployed?", +] + +CONTEXT_FILES: dict[str, str] = { + "context/discovery-log.md": """# Discovery Log + +| ID | Question | Answer | Confidence | Follow-up needed | Linked decision | Linked task | Date | +|---|---|---|---|---|---|---|---| +""", + "context/product-brief.md": """# Product Brief + +## Problem + +TBD + +## Users + +TBD + +## Success Criteria + +TBD + +## Minimum Useful Version + +TBD +""", + "context/architecture.md": """# Architecture + +## Overview + +TBD + +## Components + +TBD + +## Integrations + +TBD + +## Deployment + +TBD +""", + "context/decisions.md": """# Decisions + +| ID | Decision | Reason | Date | +|---|---|---|---| +""", + "context/risks.md": """# Risks + +| ID | Risk | Impact | Mitigation | Status | +|---|---|---|---|---| +""", + "context/assumptions.md": """# Assumptions + +| ID | Assumption | Confidence | Validation Needed | +|---|---|---|---| +""", + "context/open-questions.md": """# Open Questions + +| ID | Question | Reason | Owner | Status | +|---|---|---|---|---| +""", + "TASKS.md": """# TASKS.md + +## TASK-001 - Create Python CLI project skeleton +Status: Done + +Goal: Create the initial package structure, CLI entry point, and markdown-first bootstrap. + +Acceptance Criteria: +- `rdb --help` runs +- `rdb init` creates project files +- README exists + +## TASK-002 - Run initial project discovery +Status: Todo + +Goal: Run `rdb discover` and capture the core project answers. + +Acceptance Criteria: +- all 10 core questions are answered +- discovery-log.md contains ledger entries +- low-confidence answers are marked for follow-up + +## TASK-003 - Review generated context files +Status: Todo + +Goal: Review the generated markdown files and fill in obvious gaps. + +Acceptance Criteria: +- product-brief.md reviewed +- architecture.md reviewed +- open-questions.md updated + +## TASK-004 - Implement ask-more command +Status: Todo + +Goal: Add a command that finds weak answers and asks deeper follow-up questions. + +Acceptance Criteria: +- command reads discovery-log.md +- low-confidence answers are detected +- follow-up answers are appended to discovery-log.md +""", + "TEST_PLAN.md": """# TEST_PLAN.md + +## Manual Tests + +| ID | Test | Expected Result | Status | +|---|---|---|---| +| TEST-001 | Run `rdb --help` | CLI help is displayed | Not run | +| TEST-002 | Run `rdb init` | Markdown files are created | Not run | +| TEST-003 | Run `rdb status` | Project status is displayed | Not run | +| TEST-004 | Run `rdb next` | Next Todo task is shown | Not run | + +## Automated Tests + +Run: + +```bash +pytest +``` +""", + "RUN_LOG.md": """# RUN_LOG.md + +| Date | Event | Task | Notes | +|---|---|---|---| +""", +} + + +def write_file_if_missing(root: Path, relative_path: str, content: str) -> bool: + target = root / relative_path + if target.exists(): + return False + target.parent.mkdir(parents=True, exist_ok=True) + target.write_text(content, encoding="utf-8") + return True diff --git a/tests/__pycache__/test_tasks.cpython-314-pytest-9.0.3.pyc b/tests/__pycache__/test_tasks.cpython-314-pytest-9.0.3.pyc new file mode 100644 index 0000000000000000000000000000000000000000..aa16ed3f7848ce3a9426200b7cc200066590e526 GIT binary patch literal 5611 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 AdH?_b literal 0 HcmV?d00001 diff --git a/tests/__pycache__/test_templates.cpython-314-pytest-9.0.3.pyc b/tests/__pycache__/test_templates.cpython-314-pytest-9.0.3.pyc new file mode 100644 index 0000000000000000000000000000000000000000..0436a15ff33a142e338b42dd8fa1201ff80819ce GIT binary patch literal 5118 zcmeHL%}*Og6rWu${#x^qPm`}=+O)+A!2vt5Q9|1&AvKYTU_{IXWwqFg*=p@w@2=ZG z6^TO*J@!xF8mUryqtno-^5wBVii+SXl7S<($n(U+8( z?l^kAT82;%sia6yLr`B2L9>QR=&o3JfKUZwXVI*%EMQ>)M*MB7k+VyFh$iZ`X$+T;#>kv2Zd!J|SIGXKbY;$qZtXDEO&2qD*)m@NO7UxXU zpv1+B?v%~1G0SG51}ywAS|4MZQD5TqP|N*}xgFlz`S3m(-H7udw0d7&ichz1lZgvG{a+uRUWep|@g8?y}o6 z#+Dt#7RHgb+231VV196PUtN*w#`e_>aoza7x)RgPgopX6Lso^cj1#4BM(B`67JAw0 zv?2E~S*ssJr3gc2J#5(StAZs!6`_W%3icfCX{?o^!(_Vz#fstHM!F=n%Np64qinp@ zTXEdixDB2pd?X)al_g`L@pCCrC?eGOjpZ2Zhk;!yxc7cIQ|gBOaCZKuj{Wf8&9S3x ztFU*DMcM2hD94WNiyP;-2gos;*cVskxX1U^O)%X|q$5jv@Ckgf)Uzvhdk>Jiy$qT4 zLGDH&Z)2_ewcQycral}3b}a2s@z}H%36NJK0$vcA5pDdHY;$` zAvj~CDNw7+onZL@EDu=AYQ3seZh^6=J85C{25|=p@oxemH9;e|ly-(_Bxz@u3bKs2 zoOaUj`zhV=BL&5@@Rv9-0bnkR-A5x&5x^Cg2OvN+M6tm%m7yr0m<~t+KVoZY1ujJQ z-1z+=_KXu0oVWw;6MmGVG#Gz+`h=~J|O zaAT{hE{u3F9&@_SUuBti^cQu^Yh!Zl%$0U#yqzidA^WaIM?1J|yW(g&(=n3wjr0N_7^S+;+UvbjJ6*ecO3ZptsgRxLmHG7ZbClrRWoB*)(e$Igw;1Sgz+l;k9b1~;4YzWD7HQ0Z#SZs=Rm ztiKxiQ@IGt=A_@qvpZ7o+m>dxWQeXM@oBmmf$#~1d0Pj-$o#jo8221N$!d@haIm3s zlKTSj<|MamC&2iBv>LEJkR>*`aiwK1%FZYoK7}Cfl;WRiBmTC9lLih)y5GDQ7kQCD zX2Zo4c@grZ%Fq{W@=?c2!lx&Ne{mY&pPA^XNjK?3M4kIPI(n^UR+@(PiTx>9lRre~ bBs8x=f*|~k&;#@g_5Xp!--O9GC~EgNJ{FHM literal 0 HcmV?d00001 diff --git a/tests/test_tasks.py b/tests/test_tasks.py new file mode 100644 index 0000000..b0ba5bd --- /dev/null +++ b/tests/test_tasks.py @@ -0,0 +1,29 @@ +from pathlib import Path + +from rdb_discovery.tasks import get_next_task, update_task_status + + +def test_get_next_task(tmp_path: Path) -> None: + (tmp_path / "TASKS.md").write_text( + "# TASKS.md\n\n" + "## TASK-001 - First\nStatus: Done\n\n" + "## TASK-002 - Second\nStatus: Todo\n\n", + encoding="utf-8", + ) + + task = get_next_task(tmp_path) + + assert task is not None + assert task.task_id == "TASK-002" + assert task.title == "Second" + + +def test_update_task_status(tmp_path: Path) -> None: + tasks = tmp_path / "TASKS.md" + tasks.write_text( + "# TASKS.md\n\n## TASK-001 - First\nStatus: Todo\n\n", + encoding="utf-8", + ) + + assert update_task_status(tmp_path, "TASK-001", "In Progress") is True + assert "Status: In Progress" in tasks.read_text(encoding="utf-8") diff --git a/tests/test_templates.py b/tests/test_templates.py new file mode 100644 index 0000000..c203dd1 --- /dev/null +++ b/tests/test_templates.py @@ -0,0 +1,10 @@ +from pathlib import Path + +from rdb_discovery.templates import write_file_if_missing + + +def test_write_file_if_missing(tmp_path: Path) -> None: + assert write_file_if_missing(tmp_path, "context/test.md", "hello") is True + assert (tmp_path / "context" / "test.md").read_text(encoding="utf-8") == "hello" + assert write_file_if_missing(tmp_path, "context/test.md", "changed") is False + assert (tmp_path / "context" / "test.md").read_text(encoding="utf-8") == "hello"