Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion SPECS/ARCHIVE/INDEX.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# mcpbridge-wrapper Tasks Archive

**Last Updated:** 2026-02-28 (REVIEW_p15_t1_next_release_readiness.md)
**Last Updated:** 2026-03-01 (Workplan_0.4.0.md)

## Archived Tasks

Expand Down Expand Up @@ -167,6 +167,7 @@

| File | Description |
|------|-------------|
| [Workplan_0.4.0.md](_Historical/Workplan_0.4.0.md) | Archived workplan snapshot for release 0.4.0 |
| [REVIEW_p15_t1_next_release_readiness.md](_Historical/REVIEW_p15_t1_next_release_readiness.md) | Review report for P15-T1 |
| [REVIEW_fu_p13_t19_broker_webui_observability.md](_Historical/REVIEW_fu_p13_t19_broker_webui_observability.md) | Review report for FU-P13-T19 |
| [REVIEW_fu_p13_t18_unified_config_docs.md](_Historical/REVIEW_fu_p13_t18_unified_config_docs.md) | Review report for FU-P13-T18 |
Expand Down Expand Up @@ -283,6 +284,7 @@

| Date | Task ID | Action |
|------|---------|--------|
| 2026-03-01 | WORKPLAN | Archived Workplan_0.4.0.md to _Historical and reset SPECS/Workplan.md |
| 2026-02-28 | P15-T1 | Archived REVIEW_p15_t1_next_release_readiness report |
| 2026-02-28 | P15-T1 | Archived Validate_project_readiness_for_the_next_release (PASS) |
| 2026-02-28 | FU-P13-T19 | Archived REVIEW_fu_p13_t19_broker_webui_observability report |
Expand Down
3,210 changes: 3,210 additions & 0 deletions SPECS/ARCHIVE/_Historical/Workplan_0.4.0.md

Large diffs are not rendered by default.

3,210 changes: 6 additions & 3,204 deletions SPECS/Workplan.md

Large diffs are not rendered by default.

57 changes: 49 additions & 8 deletions scripts/calc_progress.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
from dataclasses import dataclass
from typing import List, Optional

EMPTY_CYCLE_MARKER = "intentionally reset for the next planning cycle"


@dataclass
class Task:
Expand Down Expand Up @@ -95,6 +97,32 @@ def parse_workplan(filepath: Path) -> List[Task]:
return tasks


def is_intentionally_empty_workplan(filepath: Path) -> bool:
"""Return True when workplan is a deliberate empty-cycle placeholder."""
try:
content = filepath.read_text().lower()
except OSError:
return False
return EMPTY_CYCLE_MARKER in content


def empty_progress() -> dict:
"""Return a zeroed progress payload for empty planning cycles."""
return {
'total': 0,
'completed': 0,
'pending': 0,
'percent': 0.0,
'by_priority': {
'P0': {'total': 0, 'completed': 0},
'P1': {'total': 0, 'completed': 0},
'P2': {'total': 0, 'completed': 0},
'P3': {'total': 0, 'completed': 0},
},
'by_phase': {},
}


def calculate_progress(tasks: List[Task]) -> dict:
"""Calculate progress statistics."""
if not tasks:
Expand Down Expand Up @@ -201,23 +229,36 @@ def list_tasks(tasks: List[Task], phase: Optional[str] = None, pending_only: boo

def main():
workplan_path = Path(__file__).parent.parent / "SPECS" / "Workplan.md"
args = sys.argv[1:]

if not workplan_path.exists():
print(f"Error: Workplan not found at {workplan_path}")
sys.exit(1)


if '--help' in args or '-h' in args:
print(__doc__)
sys.exit(0)

tasks = parse_workplan(workplan_path)

if not tasks:
if is_intentionally_empty_workplan(workplan_path):
if '--phase' in args:
idx = args.index('--phase')
phase = args[idx + 1] if idx + 1 < len(args) else None
list_tasks([], phase=phase)
elif '--todo' in args:
list_tasks([], pending_only=True)
elif '--markdown' in args:
print(format_progress(empty_progress(), markdown=True))
elif '--json' in args:
import json
print(json.dumps(empty_progress(), indent=2))
else:
print("No tasks available. Workplan is intentionally reset for the next planning cycle.")
sys.exit(0)
print("No tasks found in workplan")
sys.exit(1)

args = sys.argv[1:]

if '--help' in args or '-h' in args:
print(__doc__)
sys.exit(0)

if '--phase' in args:
idx = args.index('--phase')
phase = args[idx + 1] if idx + 1 < len(args) else None
Expand Down
20 changes: 20 additions & 0 deletions scripts/pick_next_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
from pathlib import Path
from typing import Optional

EMPTY_CYCLE_MARKER = "intentionally reset for the next planning cycle"


@dataclass
class Task:
Expand Down Expand Up @@ -144,6 +146,15 @@ def parse_workplan(workplan_path: Path) -> list[Task]:
return tasks


def is_intentionally_empty_workplan(workplan_path: Path) -> bool:
"""Return True when workplan is a deliberate empty-cycle placeholder."""
try:
content = workplan_path.read_text().lower()
except OSError:
return False
return EMPTY_CYCLE_MARKER in content


def get_completed_tasks(state_file: Path) -> set[str]:
"""Load the set of completed task IDs."""
if not state_file.exists():
Expand Down Expand Up @@ -369,6 +380,15 @@ def main():

tasks = parse_workplan(args.workplan)
if not tasks:
if is_intentionally_empty_workplan(args.workplan):
if args.progress:
print("No tasks available. Workplan is intentionally reset for the next planning cycle.")
print("Progress: 0/0 tasks completed (0.0%)")
elif args.list:
print("No tasks available. Workplan is intentionally reset for the next planning cycle.")
else:
print("No tasks available in current cycle. Add tasks to SPECS/Workplan.md.")
sys.exit(0)
print("Error: No tasks found in workplan", file=sys.stderr)
sys.exit(1)

Expand Down
12 changes: 12 additions & 0 deletions tests/fixtures/intentionally_empty_workplan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# Workplan: mcpbridge-wrapper

## Archived Baseline

The previous workplan for release `0.4.0` was archived at:

- [Workplan_0.4.0.md](ARCHIVE/_Historical/Workplan_0.4.0.md)

## Current Cycle

This file is intentionally reset for the next planning cycle.
Add new tasks using the canonical template in [TASK_TEMPLATE.md](TASK_TEMPLATE.md).
52 changes: 52 additions & 0 deletions tests/test_calc_progress.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,24 @@ def test_completed_tasks(self):
assert result["by_priority"]["P0"]["completed"] == 2


class TestEmptyCycleHandling:
"""Tests for intentionally empty workplan handling."""

def test_detects_intentionally_empty_workplan(self):
"""Placeholder workplan marker should be recognized."""
assert cp.is_intentionally_empty_workplan(FIXTURES_DIR / "intentionally_empty_workplan.md")

def test_empty_progress_payload(self):
"""Zeroed payload should be returned for empty cycles."""
progress = cp.empty_progress()
assert progress["total"] == 0
assert progress["completed"] == 0
assert progress["pending"] == 0
assert progress["percent"] == 0.0
assert progress["by_priority"]["P0"]["total"] == 0
assert progress["by_phase"] == {}


class TestFormatProgress:
"""Tests for format_progress function."""

Expand Down Expand Up @@ -304,3 +322,37 @@ def test_script_with_test_fixture(self):
if backup_workplan.exists():
shutil.copy(backup_workplan, original_workplan)
backup_workplan.unlink()

def test_script_with_intentionally_empty_workplan(self):
"""Reset workplan placeholder should return success with empty progress."""
import shutil

spec_dir = Path(__file__).parent.parent / "SPECS"
original_workplan = spec_dir / "Workplan.md"
backup_workplan = spec_dir / "Workplan.md.bak"
empty_fixture = FIXTURES_DIR / "intentionally_empty_workplan.md"

if original_workplan.exists():
shutil.copy(original_workplan, backup_workplan)

try:
shutil.copy(empty_fixture, original_workplan)

result = subprocess.run(
[sys.executable, "scripts/calc_progress.py", "--json"],
capture_output=True,
text=True,
cwd=Path(__file__).parent.parent,
)

assert result.returncode == 0
data = json.loads(result.stdout)
assert data["total"] == 0
assert data["completed"] == 0
assert data["pending"] == 0
assert data["percent"] == 0.0

finally:
if backup_workplan.exists():
shutil.copy(backup_workplan, original_workplan)
backup_workplan.unlink()
25 changes: 25 additions & 0 deletions tests/unit/test_pick_next_task.py
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,31 @@ def test_missing_workplan(self, tmp_path, capsys):
main()
assert exc_info.value.code == 1

def test_intentionally_empty_workplan_exits_zero(self, tmp_path, capsys):
"""A reset workplan placeholder should be treated as a valid empty cycle."""
state_file = tmp_path / "state.json"
empty_workplan = tmp_path / "Workplan.md"
empty_workplan.write_text(
"# Workplan: Test\n\n"
"## Current Cycle\n\n"
"This file is intentionally reset for the next planning cycle.\n"
)

with pytest.raises(SystemExit) as exc_info, patch(
"sys.argv",
[
"pick_next_task.py",
"--workplan",
str(empty_workplan),
"--state",
str(state_file),
],
):
main()
assert exc_info.value.code == 0
captured = capsys.readouterr()
assert "No tasks available in current cycle" in captured.out


# =============================================================================
# Integration tests
Expand Down