Skip to content

Commit ae31851

Browse files
authored
Merge pull request #82 from dmoliveira/my_opencode-e14-integrations-observability
Integrate E14-T3 plan execution observability
2 parents 86f23d8 + 45b08ba commit ae31851

File tree

10 files changed

+340
-7
lines changed

10 files changed

+340
-7
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ All notable changes to this project are documented in this file.
4646
- Added `instructions/plan_artifact_contract.md` defining `/start-work` plan metadata/checklist format, validation rules, step transitions, and deviation capture requirements.
4747
- Added `scripts/start_work_command.py` with `/start-work <plan>` execution, persisted checkpoint status, and deviation reporting (`status`, `deviations`).
4848
- Added `/start-work`, `/start-work-status`, and `/start-work-deviations` aliases in `opencode.json`.
49+
- Added `/start-work-bg` and `/start-work-doctor-json` aliases for background-safe queueing and execution health diagnostics.
4950

5051
### Changes
5152
- Documented extension evaluation outcomes and when each tool is the better fit.
@@ -88,6 +89,7 @@ All notable changes to this project are documented in this file.
8889
- Expanded README wizard/browser guidance with provider trade-offs, stable-first defaults, and `/browser` usage examples.
8990
- Expanded browser verification coverage to assert provider reset readiness and added install smoke checks that run browser status/doctor after switching across providers.
9091
- Expanded install/selftest coverage for `/start-work` plan validation, execution state persistence, and deviation diagnostics.
92+
- Expanded `/start-work` integrations with background queue handoff, digest recap payloads, and unified `/doctor` visibility.
9193

9294
## v0.2.0 - 2026-02-12
9395

IMPLEMENTATION_ROADMAP.md

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ Use this map to avoid overlapping implementations.
5050
| E11 | Context-Window Resilience Toolkit | done | High | E4 | bd-2tj, bd-n9y, bd-2t0, bd-18e | Improve long-session stability and recovery |
5151
| E12 | Provider/Model Fallback Visibility | done | Medium | E5 | bd-1jq, bd-298, bd-194, bd-2gq | Explain why model routing decisions happen |
5252
| E13 | Browser Automation Profile Switching | done | Medium | E1 | bd-3rs, bd-2qy, bd-f6g, bd-393 | Toggle Playwright/agent-browser with checks |
53-
| E14 | Plan-to-Execution Bridge Command | in_progress | Medium | E2, E3 | bd-1z6, bd-2te | Execute validated plans with progress tracking |
53+
| E14 | Plan-to-Execution Bridge Command | in_progress | Medium | E2, E3 | bd-1z6, bd-2te, bd-3sg | Execute validated plans with progress tracking |
5454
| E15 | Todo Enforcer and Plan Compliance | planned | High | E14 | TBD | Keep execution aligned with approved checklists |
5555
| E16 | Comment and Output Quality Checker Loop | merged | Medium | E23 | TBD | Merged into E23 (PR Review Copilot) |
5656
| E17 | Auto-Resume and Recovery Loop | planned | High | E11, E14 | TBD | Resume interrupted work from checkpoints safely |
@@ -578,10 +578,11 @@ Every command-oriented epic must ship all of the following:
578578
- [x] Subtask 14.2.2: Execute steps sequentially with checkpoint updates
579579
- [x] Subtask 14.2.3: Capture and report deviations from original plan
580580
- [x] Notes: Added `scripts/start_work_command.py` with plan parsing + validation, sequential checkpoint transitions, persisted execution status, and deviation reporting; wired aliases and smoke/selftest coverage.
581-
- [ ] Task 14.3: Integrations and observability
582-
- [ ] Subtask 14.3.1: Integrate with background subsystem where safe
583-
- [ ] Subtask 14.3.2: Integrate with digest summaries for end-of-run recap
584-
- [ ] Subtask 14.3.3: Expose execution status in doctor/debug outputs
581+
- [x] Task 14.3: Integrations and observability
582+
- [x] Subtask 14.3.1: Integrate with background subsystem where safe
583+
- [x] Subtask 14.3.2: Integrate with digest summaries for end-of-run recap
584+
- [x] Subtask 14.3.3: Expose execution status in doctor/debug outputs
585+
- [x] Notes: Added background-safe `/start-work` queueing (`--background` + `/start-work-bg`), digest `plan_execution` recap output, and `/doctor` integration via `/start-work doctor --json`.
585586
- [ ] Task 14.4: Validation and docs
586587
- [ ] Subtask 14.4.1: Add tests for plan parsing and execution flow
587588
- [ ] Subtask 14.4.2: Add recovery tests for interrupted plan runs

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ install-test: ## Run installer smoke test in temp HOME
4848
HOME="$$TMP_HOME" python3 "$$TMP_HOME/.config/opencode/my_opencode/scripts/stack_profile_command.py" apply focus; \
4949
HOME="$$TMP_HOME" python3 "$$TMP_HOME/.config/opencode/my_opencode/scripts/stack_profile_command.py" status; \
5050
if [ -f "$$TMP_HOME/.config/opencode/my_opencode/scripts/browser_command.py" ]; then HOME="$$TMP_HOME" python3 "$$TMP_HOME/.config/opencode/my_opencode/scripts/browser_command.py" status --json; HOME="$$TMP_HOME" python3 "$$TMP_HOME/.config/opencode/my_opencode/scripts/browser_command.py" profile agent-browser; HOME="$$TMP_HOME" python3 "$$TMP_HOME/.config/opencode/my_opencode/scripts/browser_command.py" doctor --json; HOME="$$TMP_HOME" python3 "$$TMP_HOME/.config/opencode/my_opencode/scripts/browser_command.py" profile playwright; HOME="$$TMP_HOME" python3 "$$TMP_HOME/.config/opencode/my_opencode/scripts/browser_command.py" status --json; HOME="$$TMP_HOME" python3 "$$TMP_HOME/.config/opencode/my_opencode/scripts/browser_command.py" doctor --json; fi; \
51-
if [ -f "$$TMP_HOME/.config/opencode/my_opencode/scripts/start_work_command.py" ]; then PLAN_FILE="$$TMP_HOME/.config/opencode/my_opencode/.install-test-plan.md"; python3 -c "from pathlib import Path; Path('$$PLAN_FILE').write_text('---\nid: install-test-plan\ntitle: Install Test Plan\nowner: install-test\ncreated_at: 2026-02-13T00:00:00Z\nversion: 1\n---\n\n# Plan\n\n- [ ] 1. Validate command availability\n- [ ] 2. Validate status persistence\n', encoding='utf-8')"; HOME="$$TMP_HOME" python3 "$$TMP_HOME/.config/opencode/my_opencode/scripts/start_work_command.py" "$$PLAN_FILE" --deviation "install smoke" --json; HOME="$$TMP_HOME" python3 "$$TMP_HOME/.config/opencode/my_opencode/scripts/start_work_command.py" status --json; HOME="$$TMP_HOME" python3 "$$TMP_HOME/.config/opencode/my_opencode/scripts/start_work_command.py" deviations --json; fi; \
51+
if [ -f "$$TMP_HOME/.config/opencode/my_opencode/scripts/start_work_command.py" ]; then PLAN_FILE="$$TMP_HOME/.config/opencode/my_opencode/.install-test-plan.md"; python3 -c "from pathlib import Path; Path('$$PLAN_FILE').write_text('---\nid: install-test-plan\ntitle: Install Test Plan\nowner: install-test\ncreated_at: 2026-02-13T00:00:00Z\nversion: 1\n---\n\n# Plan\n\n- [ ] 1. Validate command availability\n- [ ] 2. Validate status persistence\n', encoding='utf-8')"; HOME="$$TMP_HOME" python3 "$$TMP_HOME/.config/opencode/my_opencode/scripts/start_work_command.py" "$$PLAN_FILE" --deviation "install smoke" --json; HOME="$$TMP_HOME" python3 "$$TMP_HOME/.config/opencode/my_opencode/scripts/start_work_command.py" "$$PLAN_FILE" --background --json; HOME="$$TMP_HOME" python3 "$$TMP_HOME/.config/opencode/my_opencode/scripts/background_task_manager.py" run --max-jobs 1; HOME="$$TMP_HOME" python3 "$$TMP_HOME/.config/opencode/my_opencode/scripts/start_work_command.py" status --json; HOME="$$TMP_HOME" python3 "$$TMP_HOME/.config/opencode/my_opencode/scripts/start_work_command.py" deviations --json; HOME="$$TMP_HOME" python3 "$$TMP_HOME/.config/opencode/my_opencode/scripts/start_work_command.py" doctor --json; fi; \
5252
HOME="$$TMP_HOME" python3 "$$TMP_HOME/.config/opencode/my_opencode/scripts/nvim_integration_command.py" install minimal --link-init; \
5353
HOME="$$TMP_HOME" python3 "$$TMP_HOME/.config/opencode/my_opencode/scripts/nvim_integration_command.py" status; \
5454
HOME="$$TMP_HOME" python3 "$$TMP_HOME/.config/opencode/my_opencode/scripts/devtools_command.py" status; \

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -592,10 +592,18 @@ Epic 14 Task 14.1 defines the baseline plan format and execution-state rules for
592592
Use:
593593
```text
594594
/start-work path/to/plan.md --json
595+
/start-work-bg path/to/plan.md
596+
/bg run --id <job-id>
595597
/start-work status --json
596598
/start-work deviations --json
599+
/start-work-doctor-json
597600
```
598601

602+
Integration notes:
603+
- use `/start-work-bg` when you want queued, reviewable execution via the background subsystem before running `/bg run`
604+
- `/digest run` now includes a `plan_execution` recap block (status, plan id, step counts, deviation count)
605+
- `/doctor run` includes `start-work` health diagnostics for execution-state visibility
606+
599607
## Context resilience policy
600608

601609
Epic 11 Task 11.1 defines the baseline policy schema for context-window resilience:

install.sh

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,8 +114,11 @@ if [ "$SKIP_SELF_CHECK" = false ]; then
114114
SELF_CHECK_PLAN="$HOME/.config/opencode/my_opencode/.install-selfcheck-plan.md"
115115
python3 -c "from pathlib import Path; Path('$SELF_CHECK_PLAN').write_text('---\nid: install-selfcheck-plan\ntitle: Install Selfcheck Plan\nowner: installer\ncreated_at: 2026-02-13T00:00:00Z\nversion: 1\n---\n\n# Plan\n\n- [ ] 1. Confirm command wiring\n- [ ] 2. Confirm checkpoint persistence\n', encoding='utf-8')"
116116
python3 "$INSTALL_DIR/scripts/start_work_command.py" "$SELF_CHECK_PLAN" --deviation "install self-check" --json
117+
python3 "$INSTALL_DIR/scripts/start_work_command.py" "$SELF_CHECK_PLAN" --background --json
118+
python3 "$INSTALL_DIR/scripts/background_task_manager.py" run --max-jobs 1
117119
python3 "$INSTALL_DIR/scripts/start_work_command.py" status --json
118120
python3 "$INSTALL_DIR/scripts/start_work_command.py" deviations --json
121+
python3 "$INSTALL_DIR/scripts/start_work_command.py" doctor --json
119122
fi
120123
python3 "$INSTALL_DIR/scripts/nvim_integration_command.py" status
121124
python3 "$INSTALL_DIR/scripts/devtools_command.py" status
@@ -176,8 +179,10 @@ printf " /browser status\n"
176179
printf " /browser profile agent-browser\n"
177180
printf " /browser doctor --json\n"
178181
printf " /start-work ~/.config/opencode/my_opencode/plan.md --json\n"
182+
printf " /start-work-bg ~/.config/opencode/my_opencode/plan.md\n"
179183
printf " /start-work status --json\n"
180184
printf " /start-work deviations --json\n"
185+
printf " /start-work-doctor-json\n"
181186
printf " /nvim status\n"
182187
printf " /devtools status\n"
183188
printf " /devtools install all\n"

opencode.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,14 @@
282282
"description": "Show captured plan execution deviations",
283283
"template": "!`python3 \"$HOME/.config/opencode/my_opencode/scripts/start_work_command.py\" deviations --json`\nShow only the command output."
284284
},
285+
"start-work-bg": {
286+
"description": "Queue plan execution through background task manager",
287+
"template": "!`python3 \"$HOME/.config/opencode/my_opencode/scripts/start_work_command.py\" $ARGUMENTS --background --json`\nShow only the command output."
288+
},
289+
"start-work-doctor-json": {
290+
"description": "Run plan execution health diagnostics in JSON",
291+
"template": "!`python3 \"$HOME/.config/opencode/my_opencode/scripts/start_work_command.py\" doctor --json`\nShow only the command output."
292+
},
285293
"nvim": {
286294
"description": "Manage Neovim OpenCode integration (status|doctor|snippet|install|uninstall)",
287295
"template": "!`python3 \"$HOME/.config/opencode/my_opencode/scripts/nvim_integration_command.py\" $ARGUMENTS`\nShow only the command output."

scripts/doctor_command.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,18 @@ def script_path(name: str) -> Path:
180180
"--json",
181181
],
182182
},
183+
{
184+
"name": "start-work",
185+
"kind": "doctor-json",
186+
"optional": True,
187+
"required_path": str(script_path("start_work_command.py")),
188+
"command": [
189+
sys.executable,
190+
str(script_path("start_work_command.py")),
191+
"doctor",
192+
"--json",
193+
],
194+
},
183195
]
184196

185197

scripts/selftest.py

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1732,6 +1732,80 @@ def run_bg(*args: str) -> subprocess.CompletedProcess[str]:
17321732
"start-work deviations should return captured deviation entries",
17331733
)
17341734

1735+
start_work_background = subprocess.run(
1736+
[
1737+
sys.executable,
1738+
str(START_WORK_SCRIPT),
1739+
str(plan_path),
1740+
"--background",
1741+
"--json",
1742+
],
1743+
capture_output=True,
1744+
text=True,
1745+
env=refactor_env,
1746+
check=False,
1747+
cwd=REPO_ROOT,
1748+
)
1749+
expect(
1750+
start_work_background.returncode == 0,
1751+
"start-work should enqueue background-safe execution",
1752+
)
1753+
start_work_background_report = parse_json_output(start_work_background.stdout)
1754+
expect(
1755+
start_work_background_report.get("status") == "queued"
1756+
and bool(start_work_background_report.get("job_id")),
1757+
"start-work background mode should return queued job id",
1758+
)
1759+
queued_job_id = str(start_work_background_report.get("job_id"))
1760+
1761+
bg_run_plan = subprocess.run(
1762+
[
1763+
sys.executable,
1764+
str(BG_MANAGER_SCRIPT),
1765+
"run",
1766+
"--id",
1767+
queued_job_id,
1768+
"--max-jobs",
1769+
"1",
1770+
],
1771+
capture_output=True,
1772+
text=True,
1773+
env=refactor_env,
1774+
check=False,
1775+
cwd=REPO_ROOT,
1776+
)
1777+
expect(
1778+
bg_run_plan.returncode == 0,
1779+
"bg run should execute queued start-work job successfully",
1780+
)
1781+
1782+
digest_plan_path = home / ".config" / "opencode" / "digests" / "plan-run.json"
1783+
digest_plan_env = refactor_env.copy()
1784+
digest_plan_env["MY_OPENCODE_DIGEST_PATH"] = str(digest_plan_path)
1785+
digest_after_plan = subprocess.run(
1786+
[sys.executable, str(DIGEST_SCRIPT), "run", "--reason", "manual"],
1787+
capture_output=True,
1788+
text=True,
1789+
env=digest_plan_env,
1790+
check=False,
1791+
cwd=REPO_ROOT,
1792+
)
1793+
expect(
1794+
digest_after_plan.returncode == 0,
1795+
"digest run should succeed after start-work execution",
1796+
)
1797+
digest_after_plan_payload = load_json_file(digest_plan_path)
1798+
plan_digest_block = digest_after_plan_payload.get("plan_execution", {})
1799+
expect(
1800+
isinstance(plan_digest_block, dict)
1801+
and plan_digest_block.get("status") == "completed",
1802+
"digest should include completed plan execution recap",
1803+
)
1804+
expect(
1805+
plan_digest_block.get("plan_id") == "selftest-plan-001",
1806+
"digest plan execution recap should include plan id",
1807+
)
1808+
17351809
invalid_plan_path = tmp / "invalid_plan_execution_selftest.md"
17361810
invalid_plan_path.write_text(
17371811
"""---
@@ -2690,6 +2764,20 @@ def run_bg(*args: str) -> subprocess.CompletedProcess[str]:
26902764
"doctor browser check should pass",
26912765
)
26922766

2767+
start_work_checks = [
2768+
check
2769+
for check in report.get("checks", [])
2770+
if check.get("name") == "start-work"
2771+
]
2772+
expect(
2773+
bool(start_work_checks),
2774+
"doctor summary should include start-work check",
2775+
)
2776+
expect(
2777+
start_work_checks[0].get("ok") is True,
2778+
"doctor start-work check should pass",
2779+
)
2780+
26932781
print("selftest: PASS")
26942782
return 0
26952783

scripts/session_digest.py

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,55 @@ def build_digest(reason: str, cwd: Path) -> dict:
6767
"reason": reason,
6868
"cwd": str(cwd),
6969
"git": collect_git_snapshot(cwd),
70+
"plan_execution": collect_plan_execution_snapshot(),
71+
}
72+
73+
74+
def collect_plan_execution_snapshot() -> dict:
75+
try:
76+
layered, _ = load_layered_config()
77+
except Exception:
78+
return {"status": "unknown", "available": False}
79+
80+
section = layered.get("plan_execution")
81+
if not isinstance(section, dict) or not section:
82+
return {"status": "idle", "available": False}
83+
84+
raw_steps = section.get("steps")
85+
steps = raw_steps if isinstance(raw_steps, list) else []
86+
counts = {
87+
"total": len(steps),
88+
"completed": sum(
89+
1
90+
for step in steps
91+
if isinstance(step, dict) and step.get("state") == "completed"
92+
),
93+
"failed": sum(
94+
1
95+
for step in steps
96+
if isinstance(step, dict) and step.get("state") == "failed"
97+
),
98+
"in_progress": sum(
99+
1
100+
for step in steps
101+
if isinstance(step, dict) and step.get("state") == "in_progress"
102+
),
103+
}
104+
raw_plan = section.get("plan")
105+
plan: dict = raw_plan if isinstance(raw_plan, dict) else {}
106+
raw_metadata = plan.get("metadata")
107+
metadata: dict = raw_metadata if isinstance(raw_metadata, dict) else {}
108+
raw_deviations = section.get("deviations")
109+
deviations: list = raw_deviations if isinstance(raw_deviations, list) else []
110+
111+
return {
112+
"status": str(section.get("status") or "idle"),
113+
"available": True,
114+
"plan_id": metadata.get("id"),
115+
"plan_path": plan.get("path"),
116+
"finished_at": section.get("finished_at"),
117+
"step_counts": counts,
118+
"deviation_count": len(deviations),
70119
}
71120

72121

@@ -179,6 +228,15 @@ def print_summary(path: Path, digest: dict) -> None:
179228
if isinstance(post, dict) and post.get("attempted"):
180229
status = "timeout" if post.get("timed_out") else f"exit {post.get('exit_code')}"
181230
print(f"post_session: {status}")
231+
plan_exec = (
232+
digest.get("plan_execution")
233+
if isinstance(digest.get("plan_execution"), dict)
234+
else {}
235+
)
236+
if plan_exec:
237+
print(f"plan_execution: {plan_exec.get('status', 'idle')}")
238+
if plan_exec.get("plan_id"):
239+
print(f"plan_id: {plan_exec.get('plan_id')}")
182240

183241

184242
def usage() -> int:
@@ -282,6 +340,10 @@ def collect_doctor(path: Path) -> dict:
282340
if field not in digest:
283341
warnings.append(f"missing digest field: {field}")
284342

343+
plan_exec = digest.get("plan_execution")
344+
if plan_exec is not None and not isinstance(plan_exec, dict):
345+
warnings.append("plan_execution block is invalid")
346+
285347
git_block = digest.get("git")
286348
if not isinstance(git_block, dict):
287349
warnings.append("git block is missing or invalid")

0 commit comments

Comments
 (0)