TaskMaster Integration: Story-Based Task Management#6
Conversation
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Implemented archiver.py with:
- check_branch_change(): Detect git branch changes via .last-branch file
- archive_previous_run(): Archive prd.json and progress.txt to archive/{date}-{branch}/
- update_last_branch(): Update .last-branch tracking file
- reset_progress_file(): Reset progress.txt on branch change
- Uses pathlib.Path for all file operations
- Returns Result types from dry-python/returns
- Follows type hints pattern from file_manager.py
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Created ralph/runner.py with run_ralph function - Implements iteration loop with executor selection - Detects <promise>COMPLETE</promise> signal - Added missing dependencies from US-001, US-002, US-003: - ralph/config.py: Configuration management with env vars - ralph/executors.py: ToolExecutor protocol with amp/claude/codex - ralph/ralph_cli.py: CLI entry point with argparse - Fixed file_manager.py import (returns.option -> returns.maybe) - All modules pass mypy --strict typecheck Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Marked US-006 as passes: true in prd.json - Added detailed progress notes to progress.txt - Documented patterns and learnings for future iterations Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Moved from incorrect repo location. Implements: - US-004: PRD and progress file management (file_manager.py) - US-005: Branch change detection and archival (archiver.py) - US-006: Main iteration loop (runner.py) - US-007: UV-based entry point (pyproject.toml - to be added) - US-008: Error handling and logging (logging_utils.py, ExecutorError) - US-009: Unit tests for core modules (tests/) All modules follow b00t conventions: - PEP 484 type hints - dry-python/returns for Result/Option types - ruff linting - mypy --strict compliance Co-Authored-By: Ralph (OpenAI Codex) <codex@openai.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
All acceptance criteria verified and passing: - Logging module with emoji prefixes (✅, ℹ️,⚠️ , ❌) - Subprocess errors wrapped in Result types - Chained exceptions (raise X from Y) for context - Configuration logged at startup - Iteration start/end logged with clear separators - Errors logged with full tracebacks to stderr - Typecheck passes with mypy --strict Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Created comprehensive test suite covering all core modules - Tests for executors.py with mocked subprocess calls - Tests for file_manager.py covering PRD and progress operations - Tests for archiver.py covering branch change detection - Tests for runner.py covering iteration logic - Tests for config.py covering environment variable loading - Tests for ralph_cli.py covering CLI argument parsing - Tests for logging_utils.py covering logging functions - Achieved 92% code coverage (exceeds 80% requirement) - All 60 tests passing with pytest Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Created justfile with ralph commands - Added ralph, ralph-test, ralph-check, ralph-format commands - Added convenience commands: ralph-amp, ralph-claude, ralph-codex - Fixed linting issues in ralph/executors.py (use contextlib.suppress) - All quality checks passing Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Created comprehensive ralph/README.md with: * Installation and usage instructions * Environment variables table * Migration guide from bash version * Troubleshooting section * Examples for amp, claude, and codex * Architecture overview * Development workflow - Updated main README.md with: * Python rewrite callout and link to ralph/README.md * Updated prerequisites (Python 3.11+, uv) * Updated workflow section with Python and bash examples * Migration section explaining what's the same and what's better * Updated key files table - Updated ralph.sh with: * Deprecation notice * Backwards compatibility (--agent → --tool conversion) * Delegation to Python version Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace prd.json with TaskMaster-based task management using story format **Major changes:** - Created ralph/taskmaster_adapter.py with TaskMasterClient protocol - FileTaskMasterClient for tasks.json (working) - MCPTaskMasterClient stub for future MCP integration - Task dataclass with full TaskMaster schema support - Updated ralph/config.py with use_mcp, taskmaster_url, opencode_model fields - Updated ralph/runner.py to display task progress - Created schemas/taskmaster-schema.json JSON Schema definition - Deleted prd.json (replaced by tasks.json) - Updated CLAUDE.md agent instructions: - Emphasizes user stories over bare tasks for better AI cognition - "Stories provide narrative context that produces better results" - Updated all references from prd.json to tasks.json - Created tasks.json with 8 stories written in proper user story format - Each story answers: who, what, why, how - Includes rich context for autonomous implementation - Simplified ralph.sh wrapper to delegate to uv **Architecture decision:** Stories > Tasks for AI agent performance. Each "task" entry in tasks.json should be written as a contextual user story, not a bare instruction. This aligns with the insight that "taskmaster-ai would perform better if it was called storymaster-ai" - narrative context produces superior autonomous coding results. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
TaskMaster-AI owns all task CRUD operations. Ralph consumes via interface. - ralphython.py: Added preflight checks, uses taskmaster CLI - taskmaster_adapter.py: Replaced FileClient with CLIClient - .gitignore: Added .taskmaster/ and tasks.json Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Signed-off-by: Brian Horakh <35611074+elasticdotventures@users.noreply.github.com>
- Update pyproject.toml to use dependency-groups.dev (new uv format) - Prefix unused stub parameters with underscore in MCPTaskMasterClient - Fix ARG001/ARG002 unused argument warnings Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
Integrates TaskMaster-AI into Ralph’s Python runner to surface task/story progress, and introduces a TaskMaster-style tasks.json + schema alongside packaging, CI, tests, and docs updates.
Changes:
- Added TaskMaster adapter + runner integration to show “done / in-progress / pending” summaries.
- Added Python project scaffolding (pyproject/uv lock), CI workflow, and an expanded unit/integration test suite with tool stubs.
- Added TaskMaster-format
tasks.jsonand JSON Schema; updated docs/instructions to reference the new story-based format.
Reviewed changes
Copilot reviewed 33 out of 38 changed files in this pull request and generated 16 comments.
Show a summary per file
| File | Description |
|---|---|
| uv.lock | Adds locked dev/runtime dependencies for the Python rewrite. |
| pyproject.toml | Defines package metadata, CLI entrypoint, and tool configuration (ruff/mypy/pytest). |
| .github/workflows/python-ci.yml | Adds CI job to run ruff, mypy, and pytest with coverage. |
| .gitignore | Adds TaskMaster/Python ignore patterns (but currently also lists tasks.json). |
| tasks.json | Adds story-based TaskMaster task list content. |
| schemas/taskmaster-schema.json | Adds JSON Schema for TaskMaster-format tasks. |
| ralph/taskmaster_adapter.py | Introduces CLI/MCP abstraction layer for task operations. |
| ralph/runner.py | Integrates TaskMaster client + progress summaries into the loop. |
| ralph/config.py | Adds TaskMaster + OpenCode-related config fields. |
| ralph/executors.py | Implements tool executors and subprocess output capture. |
| ralph/file_manager.py | File utilities for PRD/progress (still PRD-based). |
| ralph/logging_utils.py | Adds emoji-prefixed logging helpers. |
| ralph/ralph_cli.py | Adds Python CLI entry point for running the loop. |
| ralph/archiver.py | Adds branch-change detection + archiving (still PRD-based). |
| ralph/init.py | Adds package version export. |
| ralph/README.md | Adds Python Ralph documentation (currently still PRD-centric). |
| ralphython.py | Adds TaskMaster preflight + MCP tools; removes PRD ingestion in this script. |
| ralph.sh | Simplifies wrapper to run uv run ralph. |
| README.md | Updates top-level docs for Python rewrite + CI badge. |
| CLAUDE.md | Updates agent instructions to use tasks.json story format. |
| AGENTS.md | Adds guidance for running tools via uv run python -m .... |
| progress.txt | Updates progress log content with additional iteration notes. |
| justfile | Adds convenience commands for running/checking/testing Ralph. |
| tests/conftest.py | Adds pytest fixture to reset logging between tests. |
| tests/test_runner.py | Adds unit tests for runner loop behavior. |
| tests/test_ralph_integration.py | Adds subprocess-based integration tests + stub tool wiring. |
| tests/test_ralph_cli.py | Adds CLI argument parsing tests. |
| tests/test_logging_utils.py | Adds tests for logging helpers. |
| tests/test_file_manager.py | Adds tests for progress/PRD file helpers. |
| tests/test_executors.py | Adds executor + subprocess wrapper tests. |
| tests/test_config.py | Adds config-from-env tests. |
| tests/test_archiver.py | Adds archiver behavior tests. |
| tests/tool_stubs/amp | Adds stub amp CLI for integration tests. |
| tests/tool_stubs/claude | Adds stub claude CLI for integration tests. |
| tests/tool_stubs/codex | Adds stub codex CLI for integration tests. |
| tests/pycache/conftest.cpython-311-pytest-9.0.2.pyc | Compiled artifact added (should not be committed). |
| tests/pycache/test_ralphython.cpython-311-pytest-9.0.2.pyc | Compiled artifact added (should not be committed). |
| ralph/pycache/archiver.cpython-311.pyc | Compiled artifact added (should not be committed). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| stdout_stream: IO[str] = cast(IO[str], tee_stream) | ||
|
|
||
| try: | ||
| completed = subprocess.run( | ||
| command, | ||
| input=input_text, | ||
| stdout=stdout_stream, | ||
| stderr=subprocess.STDOUT, | ||
| text=True, | ||
| cwd=cwd_value, | ||
| env=env_value, | ||
| check=False, | ||
| ) | ||
| # Close the write end so we can read remaining data | ||
| with contextlib.suppress(OSError): | ||
| os.close(tee_stream._pipe_write) | ||
| # Read any remaining data from the read end | ||
| with contextlib.suppress(OSError): | ||
| remaining = os.read(tee_stream._pipe_read, 1024 * 1024).decode("utf-8") | ||
| if remaining: | ||
| tee_stream._parts.append(remaining) | ||
| sys.stderr.write(remaining) | ||
| sys.stderr.flush() | ||
| except OSError as exc: | ||
| log_error(configure_logging(), f"Failed to execute {' '.join(command)}", exc) | ||
| detail = f"Failed to execute {' '.join(command)}" | ||
| return Failure(ExecutorError(detail=detail, command=tuple(command), output=str(exc))) | ||
| finally: | ||
| tee_stream.close() | ||
|
|
||
| output = tee_stream.value | ||
| if completed.returncode == 0: | ||
| return Success(output) | ||
|
|
||
| detail = f"Command {' '.join(command)} exited with {completed.returncode}" | ||
| return Failure( | ||
| ExecutorError( | ||
| detail=detail, | ||
| command=tuple(command), | ||
| returncode=completed.returncode, |
There was a problem hiding this comment.
This subprocess piping approach can deadlock: the child process writes to the pipe FD (via fileno()), but the parent only reads from the pipe after subprocess.run returns. If the tool outputs more than the OS pipe buffer, the child will block and hang the run. Consider switching to subprocess.Popen with stdout=PIPE and streaming reads (teeing to stderr) or using communicate() with capture_output (if real-time output isn't required).
| stdout_stream: IO[str] = cast(IO[str], tee_stream) | |
| try: | |
| completed = subprocess.run( | |
| command, | |
| input=input_text, | |
| stdout=stdout_stream, | |
| stderr=subprocess.STDOUT, | |
| text=True, | |
| cwd=cwd_value, | |
| env=env_value, | |
| check=False, | |
| ) | |
| # Close the write end so we can read remaining data | |
| with contextlib.suppress(OSError): | |
| os.close(tee_stream._pipe_write) | |
| # Read any remaining data from the read end | |
| with contextlib.suppress(OSError): | |
| remaining = os.read(tee_stream._pipe_read, 1024 * 1024).decode("utf-8") | |
| if remaining: | |
| tee_stream._parts.append(remaining) | |
| sys.stderr.write(remaining) | |
| sys.stderr.flush() | |
| except OSError as exc: | |
| log_error(configure_logging(), f"Failed to execute {' '.join(command)}", exc) | |
| detail = f"Failed to execute {' '.join(command)}" | |
| return Failure(ExecutorError(detail=detail, command=tuple(command), output=str(exc))) | |
| finally: | |
| tee_stream.close() | |
| output = tee_stream.value | |
| if completed.returncode == 0: | |
| return Success(output) | |
| detail = f"Command {' '.join(command)} exited with {completed.returncode}" | |
| return Failure( | |
| ExecutorError( | |
| detail=detail, | |
| command=tuple(command), | |
| returncode=completed.returncode, | |
| try: | |
| process = subprocess.Popen( | |
| command, | |
| stdin=subprocess.PIPE if input_text is not None else None, | |
| stdout=subprocess.PIPE, | |
| stderr=subprocess.STDOUT, | |
| text=True, | |
| cwd=cwd_value, | |
| env=env_value, | |
| ) | |
| # Send any provided input and close stdin so the child sees EOF. | |
| if input_text is not None and process.stdin is not None: | |
| try: | |
| process.stdin.write(input_text) | |
| process.stdin.close() | |
| except OSError: | |
| # Ignore errors writing to stdin (e.g., if the process exits early). | |
| with contextlib.suppress(OSError): | |
| process.stdin.close() | |
| # Stream output from the child, teeing to stderr and accumulating it. | |
| if process.stdout is not None: | |
| for chunk in process.stdout: | |
| if not chunk: | |
| break | |
| tee_stream.write(chunk) | |
| sys.stderr.write(chunk) | |
| sys.stderr.flush() | |
| returncode = process.wait() | |
| except OSError as exc: | |
| log_error(configure_logging(), f"Failed to execute {' '.join(command)}", exc) | |
| detail = f"Failed to execute {' '.join(command)}" | |
| return Failure(ExecutorError(detail=detail, command=tuple(command), output=str(exc))) | |
| finally: | |
| # Ensure the tee stream is closed even if an error occurs. | |
| tee_stream.close() | |
| output = tee_stream.value | |
| if returncode == 0: | |
| return Success(output) | |
| detail = f"Command {' '.join(command)} exited with {returncode}" | |
| return Failure( | |
| ExecutorError( | |
| detail=detail, | |
| command=tuple(command), | |
| returncode=returncode, |
| taskmaster = create_client( | ||
| prefer_mcp=config.use_mcp, | ||
| mcp_url=config.taskmaster_url, | ||
| tasks_file=WORKING_DIR / "tasks.json", |
There was a problem hiding this comment.
create_client() documents that tasks_file is deprecated and unused, but run_ralph still passes WORKING_DIR / "tasks.json". This is misleading (suggests a file-based fallback exists when it doesn’t). Consider removing the argument here, or (better) removing tasks_file from the factory signature if it’s not needed.
| tasks_file=WORKING_DIR / "tasks.json", |
| def _prepare_workspace(workdir: Path) -> None: | ||
| """Create the minimal files Ralph expects (prompt, CLAUDE, PRD).""" | ||
|
|
There was a problem hiding this comment.
The helper docstring says Ralph expects a PRD, but the current Python CLI path doesn’t read prd.json anymore (runner uses TaskMaster + prompt files). Consider updating this docstring to reflect the actual required workspace files.
| (workdir / "prd.json").write_text( | ||
| '{"project": "Test", "branchName": "test-branch"}', | ||
| encoding="utf-8", | ||
| ) |
There was a problem hiding this comment.
The integration-test workspace setup writes a prd.json, but the current CLI/integration path doesn’t appear to consume it. Consider removing this file creation (or switching to tasks.json if TaskMaster-based setup is required) so the test fixture reflects real prerequisites.
| 1. **Initialization**: Ralph reads `prd.json` and `progress.txt` from the project root | ||
| 2. **Branch Detection**: Checks if git branch has changed since last run | ||
| 3. **Archival**: If branch changed, archives previous run to `archive/{date}-{branch-name}/` | ||
| 4. **Iteration Loop**: Runs the selected tool (amp/claude/codex) repeatedly | ||
| 5. **Completion Detection**: Monitors tool output for `<promise>COMPLETE</promise>` signal | ||
| 6. **Progress Tracking**: Updates `progress.txt` after each iteration |
There was a problem hiding this comment.
This section claims Ralph reads prd.json and does branch detection/archival. In the current Python implementation, run_ralph() doesn’t use PRD/archiver; it uses TaskMaster for task status and only requires prompt files + progress file. Please update this doc to match the current runtime behavior (and/or wire archiver into the CLI if that’s still intended).
| 1. **Initialization**: Ralph reads `prd.json` and `progress.txt` from the project root | |
| 2. **Branch Detection**: Checks if git branch has changed since last run | |
| 3. **Archival**: If branch changed, archives previous run to `archive/{date}-{branch-name}/` | |
| 4. **Iteration Loop**: Runs the selected tool (amp/claude/codex) repeatedly | |
| 5. **Completion Detection**: Monitors tool output for `<promise>COMPLETE</promise>` signal | |
| 6. **Progress Tracking**: Updates `progress.txt` after each iteration | |
| 1. **Initialization**: Ralph loads the prompt file(s) and a progress file from the project root | |
| 2. **Task Management**: A TaskMaster instance tracks the current task state and iteration count | |
| 3. **Iteration Loop**: Runs the selected tool (amp/claude/codex) repeatedly under TaskMaster control | |
| 4. **Completion Detection**: Monitors tool output (e.g. for `<promise>COMPLETE</promise>`) to determine when the task is done | |
| 5. **Progress Tracking**: Updates the progress file after each iteration |
| from __future__ import annotations | ||
|
|
||
| import sys | ||
| from unittest.mock import MagicMock, patch |
There was a problem hiding this comment.
Import of 'MagicMock' is not used.
| import shutil | ||
| import subprocess | ||
| import sys | ||
| import textwrap |
There was a problem hiding this comment.
Import of 'textwrap' is not used.
|
|
||
| from __future__ import annotations | ||
|
|
||
| from pathlib import Path |
There was a problem hiding this comment.
Import of 'Path' is not used.
| from __future__ import annotations | ||
|
|
||
| from pathlib import Path | ||
| from unittest.mock import MagicMock, Mock, patch |
There was a problem hiding this comment.
Import of 'MagicMock' is not used.
| tee_stream = _TeeToStderr() | ||
| stdout_stream: IO[str] = cast(IO[str], tee_stream) | ||
|
|
||
| try: | ||
| completed = subprocess.run( | ||
| command, | ||
| input=input_text, | ||
| stdout=stdout_stream, | ||
| stderr=subprocess.STDOUT, | ||
| text=True, | ||
| cwd=cwd_value, | ||
| env=env_value, | ||
| check=False, | ||
| ) | ||
| # Close the write end so we can read remaining data | ||
| with contextlib.suppress(OSError): | ||
| os.close(tee_stream._pipe_write) | ||
| # Read any remaining data from the read end | ||
| with contextlib.suppress(OSError): | ||
| remaining = os.read(tee_stream._pipe_read, 1024 * 1024).decode("utf-8") | ||
| if remaining: | ||
| tee_stream._parts.append(remaining) | ||
| sys.stderr.write(remaining) | ||
| sys.stderr.flush() | ||
| except OSError as exc: | ||
| log_error(configure_logging(), f"Failed to execute {' '.join(command)}", exc) | ||
| detail = f"Failed to execute {' '.join(command)}" | ||
| return Failure(ExecutorError(detail=detail, command=tuple(command), output=str(exc))) | ||
| finally: | ||
| tee_stream.close() | ||
|
|
||
| output = tee_stream.value |
There was a problem hiding this comment.
Instance of context-manager class _TeeToStderr is closed in a finally block. Consider using 'with' statement.
| tee_stream = _TeeToStderr() | |
| stdout_stream: IO[str] = cast(IO[str], tee_stream) | |
| try: | |
| completed = subprocess.run( | |
| command, | |
| input=input_text, | |
| stdout=stdout_stream, | |
| stderr=subprocess.STDOUT, | |
| text=True, | |
| cwd=cwd_value, | |
| env=env_value, | |
| check=False, | |
| ) | |
| # Close the write end so we can read remaining data | |
| with contextlib.suppress(OSError): | |
| os.close(tee_stream._pipe_write) | |
| # Read any remaining data from the read end | |
| with contextlib.suppress(OSError): | |
| remaining = os.read(tee_stream._pipe_read, 1024 * 1024).decode("utf-8") | |
| if remaining: | |
| tee_stream._parts.append(remaining) | |
| sys.stderr.write(remaining) | |
| sys.stderr.flush() | |
| except OSError as exc: | |
| log_error(configure_logging(), f"Failed to execute {' '.join(command)}", exc) | |
| detail = f"Failed to execute {' '.join(command)}" | |
| return Failure(ExecutorError(detail=detail, command=tuple(command), output=str(exc))) | |
| finally: | |
| tee_stream.close() | |
| output = tee_stream.value | |
| completed = None | |
| output = "" | |
| with _TeeToStderr() as tee_stream: | |
| stdout_stream: IO[str] = cast(IO[str], tee_stream) | |
| try: | |
| completed = subprocess.run( | |
| command, | |
| input=input_text, | |
| stdout=stdout_stream, | |
| stderr=subprocess.STDOUT, | |
| text=True, | |
| cwd=cwd_value, | |
| env=env_value, | |
| check=False, | |
| ) | |
| # Close the write end so we can read remaining data | |
| with contextlib.suppress(OSError): | |
| os.close(tee_stream._pipe_write) | |
| # Read any remaining data from the read end | |
| with contextlib.suppress(OSError): | |
| remaining = os.read(tee_stream._pipe_read, 1024 * 1024).decode("utf-8") | |
| if remaining: | |
| tee_stream._parts.append(remaining) | |
| sys.stderr.write(remaining) | |
| sys.stderr.flush() | |
| except OSError as exc: | |
| log_error(configure_logging(), f"Failed to execute {' '.join(command)}", exc) | |
| detail = f"Failed to execute {' '.join(command)}" | |
| return Failure(ExecutorError(detail=detail, command=tuple(command), output=str(exc))) | |
| output = tee_stream.value | |
| assert completed is not None |
**Heavy lifting in bash, execution in Python** ralph.sh responsibilities: - Find git root - Sync dependencies with uv (never pip) - Initialize .taskmaster directory structure - Verify/update .gitignore - Validate preflight checks - Delegate to Python runtime ralphython.py responsibilities: - Execution only (no setup/preflight) - Run agent iterations - MCP server mode **Benefits:** - Clean separation of concerns - ralph.sh can initialize any repo for Ralph operations - DRY: No duplication of setup logic - NRtW: Reuses bash for what bash does best - TRIZ: System does the setup, user just runs **Initialization creates:** - .taskmaster/config.json - .taskmaster/tasks/ directory - Moves tasks.json to .taskmaster/tasks/tasks.json - Ensures .taskmaster in .gitignore Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
ralph.sh handles .taskmaster initialization, so tasks_file parameter is no longer needed. TaskMaster CLI will find tasks in .taskmaster/ automatically. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Replace subprocess.run() with Popen and real-time output streaming to avoid pipe buffer overflow deadlock. **Problem:** subprocess.run() blocks waiting for process completion, but if child writes more data than OS pipe buffer can hold, it blocks waiting for parent to read, causing deadlock. **Solution:** - Use subprocess.Popen with stdout=PIPE - Stream output line-by-line in real-time - Tee to stderr while accumulating for return value - Parent reads continuously so child never blocks on pipe Fixes Copilot review comment on ralph/executors.py:153 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
**#2 Update documentation:** - ralph/README.md: Updated 'How It Works' to reflect TaskMaster architecture - Removed references to prd.json, branch detection, archival - Added architecture explanation (ralph.sh + ralphython.py + TaskMaster-AI) **#3 Fix test fixtures:** - test_ralph_integration.py: Replace prd.json with .taskmaster structure - Create .taskmaster/tasks/tasks.json and config.json in test workspace - Update docstring to reflect current requirements **#4 Clean up unused imports:** - test_archiver.py: Remove unused Failure, ARCHIVE_DIR, LAST_BRANCH_PATH imports - test_archiver.py: Remove unused prd_path, progress_path variables - test_executors.py: Remove unused MagicMock import - test_logging_utils.py: Remove unused patch import - test_ralph_cli.py: Remove unused MagicMock import - test_runner.py: Remove unused Path, MagicMock imports - test_ralph_integration.py: Remove unused textwrap import Resolves Copilot review comments on PR #6 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Summary
Integrates TaskMaster-AI for story-based task management with proper separation of concerns.
Key Innovation: Stories > Tasks for AI performance
Architecture
Separation of Concerns:
Changes
1. TaskMaster Integration (
ralph/taskmaster_adapter.py)taskmasterCLI commands2. Runner Integration (
ralph/runner.py)<promise>COMPLETE</promise>detection3. Agent Instructions (
CLAUDE.md)4. Preflight Safety (
ralphython.py).taskmaster/directory exists in git root.taskmasteris in.gitignore(prevents accidental commits)5. Story-Based Format (
tasks.json)6. Configuration (
ralph/config.py)use_mcp: boolfieldtaskmaster_url: Optional[str]fieldopencode_modelandopencode_extra_argsfields7. Schema Definition (
schemas/taskmaster-schema.json)Files Changed
ralph/taskmaster_adapter.py(NEW)ralph/runner.pyralph/config.pyCLAUDE.mdralphython.pytasks.json(NEW)schemas/taskmaster-schema.json(NEW).gitignoreprd.json(DELETED)ralph.sh(simplified)Testing
Requires taskmaster-ai CLI to be installed:
Breaking Changes
prd.json- replaced bytasks.json(TaskMaster format).taskmaster/directory in git repo roottaskmasterCLI or MCP serverMigration Path
For existing prd.json users:
taskmaster init.taskmaster/is in.gitignoreWhy This Matters
Cognitive Context = Better AI:
Stories provide the narrative context that autonomous AI agents need to make better implementation decisions.
🤖 Generated with Claude Code