30 KiB
OpenCode Resume Metadata Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Store OpenCode session metadata first-class in challenge results and expose a safe way to resume incomplete trials with opencode run --session.
Architecture: Add a focused OpenCode resume helper module that extracts sessionID from JSONL stdout, builds continuation prompts from result state, and formats resume commands. The V2 runner records this metadata in every raw result; report projections render it for operators. A small CLI can print the resume command, and optionally execute it into a separate resume result file without mutating original raw evidence.
Tech Stack: Python 3.14, stdlib json/subprocess/argparse, existing examples.agent_challenges harness, pytest, Pydantic report DTOs.
File Structure
- Create
examples/agent_challenges/opencode_resume.py- Owns
extract_session_id, prompt selection, command construction, and resume-result path selection. - No challenge-specific assertions here.
- Owns
- Modify
examples/agent_challenges/runner.py- Stores
opencodemetadata in the raw result after stdout/stderr are known. - Does not change task classification rules.
- Stores
- Modify
examples/agent_challenges/report_models.py- Adds strict DTOs for bounded OpenCode metadata in machine reports.
- Modify
examples/agent_challenges/reports.py- Renders resume metadata and command in
final-report.md/*.report.md.
- Renders resume metadata and command in
- Create
examples/agent_challenges/resume_trial.py- CLI for printing or executing a resume from one raw result JSON.
- Modify
tests/examples/test_agent_challenge_harness_v2.py- Runner-level coverage for storing metadata.
- Modify
tests/examples/test_agent_challenge_reports.py- Report rendering and machine projection coverage.
- Create
tests/examples/test_agent_challenge_resume.py- Unit tests for session extraction, prompt selection, command construction, and CLI behavior.
- Modify
docs/runbooks/agent-challenge-evaluation.md- Adds operator instructions for incomplete/timeouts and resume.
- Modify
docs/current_roadmap.md- Records completion when implementation is done.
Task 1: Add OpenCode Resume Helper
Files:
-
Create:
examples/agent_challenges/opencode_resume.py -
Test:
tests/examples/test_agent_challenge_resume.py -
Step 1: Write failing tests for session extraction and prompt selection
Create tests/examples/test_agent_challenge_resume.py:
from __future__ import annotations
import json
from pathlib import Path
from examples.agent_challenges.opencode_resume import (
build_resume_command,
extract_session_id,
resume_prompt_for_result,
resume_result_path,
)
def _event(**payload: object) -> str:
return json.dumps(payload, separators=(",", ":"))
def test_extract_session_id_reads_top_level_session_id() -> None:
stdout = "\n".join(
[
_event(type="step_start", sessionID="ses_abc"),
_event(type="text", sessionID="ses_def", text="later"),
]
)
assert extract_session_id(stdout) == "ses_abc"
def test_extract_session_id_reads_nested_part_session_id() -> None:
stdout = _event(type="step_start", part={"sessionID": "ses_nested"})
assert extract_session_id(stdout) == "ses_nested"
def test_extract_session_id_returns_none_for_empty_or_malformed_stdout() -> None:
assert extract_session_id("") is None
assert extract_session_id("not json\n{}") is None
def test_resume_prompt_asks_continue_for_timeout_with_partial_stdout() -> None:
prompt = resume_prompt_for_result(
{
"task_outcome": "timeout",
"stdout": _event(type="step_start", sessionID="ses_abc"),
}
)
assert "continue" in prompt.lower()
assert "do not restart" in prompt.lower()
def test_resume_prompt_asks_for_final_report_when_work_is_done_but_report_missing() -> None:
prompt = resume_prompt_for_result(
{
"task_outcome": "failed",
"assertion_failures": [
"could not extract challenge report for required_fields evaluation"
],
"stdout": _event(type="text", sessionID="ses_abc", text="run completed"),
}
)
assert "do not continue coding" in prompt.lower()
assert "challenge_report" in prompt
def test_build_resume_command_includes_attach_session_model_variant_and_prompt() -> None:
command = build_resume_command(
session_id="ses_abc",
attach_url="http://127.0.0.1:8192/",
model="opencode/deepseek-v4-flash-free",
variant="max",
prompt="continue?",
)
assert command == [
"opencode",
"run",
"--session",
"ses_abc",
"--attach",
"http://127.0.0.1:8192/",
"--format",
"json",
"--model",
"opencode/deepseek-v4-flash-free",
"--variant",
"max",
"continue?",
]
def test_resume_result_path_uses_next_resume_index(tmp_path: Path) -> None:
original = tmp_path / "trial.json"
original.write_text("{}", encoding="utf-8")
(tmp_path / "trial.resume-001.json").write_text("{}", encoding="utf-8")
assert resume_result_path(original).name == "trial.resume-002.json"
- Step 2: Run tests to verify they fail
Run:
uv run pytest tests/examples/test_agent_challenge_resume.py -q
Expected: fails because examples.agent_challenges.opencode_resume does not exist.
- Step 3: Implement the helper module
Create examples/agent_challenges/opencode_resume.py:
from __future__ import annotations
import json
from pathlib import Path
from typing import Any
FINAL_REPORT_PROMPT = (
"Your workflow attempt is over. Do not continue coding. Return only the "
"final challenge report using the required challenge_report YAML schema. "
"Include run_id, evidence, failed attempts, read flags, missed requirements, "
"and whether the run succeeded."
)
CONTINUE_PROMPT = (
"Continue this same trial from the current session. Do not restart in a new "
"workspace. If the workflow is already complete, stop and return only the "
"final challenge_report YAML using the required schema."
)
def _event_session_id(event: dict[str, Any]) -> str | None:
session_id = event.get("sessionID")
if isinstance(session_id, str) and session_id:
return session_id
part = event.get("part")
if isinstance(part, dict):
nested = part.get("sessionID")
if isinstance(nested, str) and nested:
return nested
return None
def extract_session_id(stdout: str) -> str | None:
"""Return the first OpenCode session id found in JSONL stdout."""
for line in stdout.splitlines():
if not line.strip():
continue
try:
event = json.loads(line)
except json.JSONDecodeError:
continue
if not isinstance(event, dict):
continue
session_id = _event_session_id(event)
if session_id is not None:
return session_id
return None
def resume_prompt_for_result(result: dict[str, object]) -> str:
"""Choose a continuation prompt from the result failure shape."""
task_outcome = result.get("task_outcome")
assertion_failures = result.get("assertion_failures")
failures = assertion_failures if isinstance(assertion_failures, list) else []
if task_outcome == "timeout":
return CONTINUE_PROMPT
if any("could not extract challenge report" in str(item) for item in failures):
return FINAL_REPORT_PROMPT
if result.get("parsed") is None and result.get("stdout"):
return FINAL_REPORT_PROMPT
return CONTINUE_PROMPT
def build_resume_command(
*,
session_id: str,
attach_url: str | None,
model: str,
variant: str,
prompt: str,
) -> list[str]:
command = ["opencode", "run", "--session", session_id]
if attach_url is not None:
command.extend(["--attach", attach_url])
command.extend(["--format", "json", "--model", model, "--variant", variant, prompt])
return command
def resume_result_path(result_path: Path) -> Path:
stem = result_path.with_suffix("")
index = 1
while True:
candidate = stem.with_name(f"{stem.name}.resume-{index:03d}.json")
if not candidate.exists():
return candidate
index += 1
- Step 4: Run tests to verify they pass
Run:
uv run pytest tests/examples/test_agent_challenge_resume.py -q
Expected: all tests pass.
- Step 5: Commit
git add examples/agent_challenges/opencode_resume.py tests/examples/test_agent_challenge_resume.py
git commit -m "feat: add opencode resume helpers"
Task 2: Store Resume Metadata In Raw V2 Results
Files:
-
Modify:
examples/agent_challenges/runner.py -
Test:
tests/examples/test_agent_challenge_harness_v2.py -
Step 1: Add failing runner tests
Append to tests/examples/test_agent_challenge_harness_v2.py:
def test_v2_runner_stores_opencode_resume_metadata(tmp_path: Path) -> None:
from subprocess import CompletedProcess
from examples.agent_challenges.models import InstructionProfile
from examples.agent_challenges.runner import run_v2_trial
challenge = load_challenge_manifest(_write_manifest(tmp_path / "challenge"))
stdout = (
'{"type":"step_start","sessionID":"ses_runner"}\n'
'{"type":"text","sessionID":"ses_runner","text":"```yaml\\n'
'challenge_report:\\n'
' used_product_path: true\\n'
' used_helper_script: false\\n'
' before_clicked: false\\n'
' after_clicked: true\\n'
' run_failed: false\\n'
' leftover_processes: false\\n'
'```"}\n'
)
def fake_run(*args: object, **kwargs: object) -> CompletedProcess[str]:
return CompletedProcess(args=args, returncode=0, stdout=stdout, stderr="")
result = run_v2_trial(
challenge,
profile=InstructionProfile.NONE,
model="opencode/deepseek-v4-flash-free",
variant="max",
index=1,
results_dir=tmp_path / "results",
workspaces_dir=tmp_path / "workspaces",
attach_url="http://127.0.0.1:8192/",
run_fn=fake_run,
)
opencode = result["opencode"]
assert opencode["session_id"] == "ses_runner"
assert opencode["attach_url"] == "http://127.0.0.1:8192/"
assert opencode["model"] == "opencode/deepseek-v4-flash-free"
assert opencode["variant"] == "max"
assert opencode["resume_command"][0:4] == [
"opencode",
"run",
"--session",
"ses_runner",
]
assert "challenge_report" in opencode["resume_prompt"]
def test_v2_runner_stores_null_session_when_stdout_has_no_session(tmp_path: Path) -> None:
from subprocess import CompletedProcess
from examples.agent_challenges.models import InstructionProfile
from examples.agent_challenges.runner import run_v2_trial
challenge = load_challenge_manifest(_write_manifest(tmp_path / "challenge"))
def fake_run(*args: object, **kwargs: object) -> CompletedProcess[str]:
return CompletedProcess(args=args, returncode=1, stdout="", stderr="boom")
result = run_v2_trial(
challenge,
profile=InstructionProfile.NONE,
model="opencode/deepseek-v4-flash-free",
variant="max",
index=1,
results_dir=tmp_path / "results",
workspaces_dir=tmp_path / "workspaces",
run_fn=fake_run,
)
opencode = result["opencode"]
assert opencode["session_id"] is None
assert opencode["resume_command"] is None
These tests use the existing _write_manifest helper and the already-imported
load_challenge_manifest function at the top of the file. Do not invent a
second fixture.
- Step 2: Run tests to verify they fail
Run:
uv run pytest tests/examples/test_agent_challenge_harness_v2.py::test_v2_runner_stores_opencode_resume_metadata tests/examples/test_agent_challenge_harness_v2.py::test_v2_runner_stores_null_session_when_stdout_has_no_session -q
Expected: fails because result["opencode"] is missing.
- Step 3: Store metadata in
run_v2_trial
In examples/agent_challenges/runner.py, import helpers near the other challenge harness imports:
from examples.agent_challenges.opencode_resume import (
build_resume_command,
extract_session_id,
resume_prompt_for_result,
)
After report_paths is defined and before result is built, add:
opencode_session_id = extract_session_id(stdout)
After result is built, before optional assertion_failures are attached, add:
resume_prompt = resume_prompt_for_result(result)
result["opencode"] = {
"attach_url": attach_url,
"command": command,
"model": model,
"variant": variant,
"session_id": opencode_session_id,
"resume_prompt": resume_prompt,
"resume_command": (
build_resume_command(
session_id=opencode_session_id,
attach_url=attach_url,
model=model,
variant=variant,
prompt=resume_prompt,
)
if opencode_session_id is not None
else None
),
}
Keep model and variant at top-level too; this is additive metadata for operators.
- Step 4: Run tests to verify they pass
Run:
uv run pytest tests/examples/test_agent_challenge_harness_v2.py::test_v2_runner_stores_opencode_resume_metadata tests/examples/test_agent_challenge_harness_v2.py::test_v2_runner_stores_null_session_when_stdout_has_no_session -q
Expected: both pass.
- Step 5: Commit
git add examples/agent_challenges/runner.py tests/examples/test_agent_challenge_harness_v2.py
git commit -m "feat: store opencode resume metadata"
Task 3: Render Resume Metadata In Reports
Files:
-
Modify:
examples/agent_challenges/report_models.py -
Modify:
examples/agent_challenges/reports.py -
Test:
tests/examples/test_agent_challenge_reports.py -
Step 1: Write failing report projection test
Append to tests/examples/test_agent_challenge_reports.py:
def test_trial_report_renders_opencode_resume_metadata(tmp_path: Path) -> None:
from examples.agent_challenges.report_models import build_trial_report
from examples.agent_challenges.reports import render_trial_report_markdown
result = _raw_result(tmp_path)
result["opencode"] = {
"attach_url": "http://127.0.0.1:8192/",
"command": ["opencode", "run", "--format", "json", "prompt"],
"model": "opencode/deepseek-v4-flash-free",
"variant": "max",
"session_id": "ses_report",
"resume_prompt": "continue?",
"resume_command": [
"opencode",
"run",
"--session",
"ses_report",
"--attach",
"http://127.0.0.1:8192/",
"--format",
"json",
"--model",
"opencode/deepseek-v4-flash-free",
"--variant",
"max",
"continue?",
],
}
report = build_trial_report(result, audit=None)
rendered = render_trial_report_markdown(report)
machine = report.model_dump(mode="json")
assert machine["opencode"]["session_id"] == "ses_report"
assert "## OpenCode Resume" in rendered
assert "ses_report" in rendered
assert "opencode run --session ses_report" in rendered
Use the existing _raw_result helper in the file. Do not create duplicate
minimal result setup.
- Step 2: Run test to verify it fails
Run:
uv run pytest tests/examples/test_agent_challenge_reports.py::test_trial_report_renders_opencode_resume_metadata -q
Expected: fails because TrialReport forbids extra opencode.
- Step 3: Add report DTOs
In examples/agent_challenges/report_models.py, add after TrialIdentity:
class OpenCodeRunMetadata(StrictReportModel):
attach_url: str | None = None
command: list[str] = Field(default_factory=list)
model: str = ""
variant: str = ""
session_id: str | None = None
resume_prompt: str = ""
resume_command: list[str] | None = None
Add field to TrialReport:
opencode: OpenCodeRunMetadata | None = None
Add helper:
def _build_opencode_metadata(result: dict[str, object]) -> OpenCodeRunMetadata | None:
raw = result.get("opencode")
if not isinstance(raw, dict):
return None
resume_command = raw.get("resume_command")
return OpenCodeRunMetadata(
attach_url=_str_none(raw.get("attach_url")),
command=_list_str(raw.get("command")),
model=_str(raw.get("model")),
variant=_str(raw.get("variant")),
session_id=_str_none(raw.get("session_id")),
resume_prompt=_str(raw.get("resume_prompt")),
resume_command=_list_str(resume_command)
if isinstance(resume_command, list)
else None,
)
In _build_trial_report, pass:
opencode=_build_opencode_metadata(result),
- Step 4: Render markdown section
In examples/agent_challenges/reports.py, add a helper near other render helpers:
def _shell_join(command: list[str]) -> str:
return " ".join(command)
Then in render_trial_report_markdown, after the Outcome section and before Agent Self-Report, add:
if report.opencode is not None:
lines.append("## OpenCode Resume")
lines.append("")
if report.opencode.session_id:
lines.append(f"- Session: `{report.opencode.session_id}`")
else:
lines.append("- Session: not captured")
if report.opencode.attach_url:
lines.append(f"- Attach URL: `{report.opencode.attach_url}`")
if report.opencode.resume_command:
lines.append("")
lines.append("```powershell")
lines.append(_shell_join(report.opencode.resume_command))
lines.append("```")
if report.opencode.resume_prompt:
lines.append("")
lines.append("Resume prompt:")
lines.append("")
lines.append("```text")
lines.append(report.opencode.resume_prompt)
lines.append("```")
lines.append("")
This uses simple shell rendering for readability. The authoritative machine form remains the JSON list in *.report.json.
- Step 5: Run test to verify it passes
Run:
uv run pytest tests/examples/test_agent_challenge_reports.py::test_trial_report_renders_opencode_resume_metadata -q
Expected: passes.
- Step 6: Commit
git add examples/agent_challenges/report_models.py examples/agent_challenges/reports.py tests/examples/test_agent_challenge_reports.py
git commit -m "feat: show opencode resume metadata in reports"
Task 4: Add resume_trial.py CLI
Files:
-
Create:
examples/agent_challenges/resume_trial.py -
Test:
tests/examples/test_agent_challenge_resume.py -
Modify:
docs/runbooks/agent-challenge-evaluation.md -
Step 1: Write failing CLI tests
Append to tests/examples/test_agent_challenge_resume.py:
def test_resume_trial_prints_resume_command(tmp_path: Path, capsys: pytest.CaptureFixture[str]) -> None:
from examples.agent_challenges.resume_trial import main
result_path = tmp_path / "trial.json"
result_path.write_text(
json.dumps(
{
"model": "opencode/deepseek-v4-flash-free",
"variant": "max",
"opencode": {
"attach_url": "http://127.0.0.1:8192/",
"session_id": "ses_cli",
"resume_prompt": "continue?",
"resume_command": [
"opencode",
"run",
"--session",
"ses_cli",
"--attach",
"http://127.0.0.1:8192/",
"--format",
"json",
"--model",
"opencode/deepseek-v4-flash-free",
"--variant",
"max",
"continue?",
],
},
}
),
encoding="utf-8",
)
assert main(["--from-result", str(result_path), "--print-command"]) == 0
output = capsys.readouterr().out
assert "opencode run --session ses_cli" in output
def test_resume_trial_run_writes_resume_result(tmp_path: Path) -> None:
from subprocess import CompletedProcess
from examples.agent_challenges.resume_trial import resume_from_result
result_path = tmp_path / "trial.json"
result_path.write_text(
json.dumps(
{
"model": "opencode/deepseek-v4-flash-free",
"variant": "max",
"opencode": {
"attach_url": None,
"session_id": "ses_cli",
"resume_prompt": "continue?",
"resume_command": [
"opencode",
"run",
"--session",
"ses_cli",
"--format",
"json",
"--model",
"opencode/deepseek-v4-flash-free",
"--variant",
"max",
"continue?",
],
},
}
),
encoding="utf-8",
)
calls: list[list[str]] = []
def fake_run(command: list[str], **kwargs: object) -> CompletedProcess[str]:
calls.append(command)
return CompletedProcess(command, 0, stdout='{"type":"text","text":"done"}\n', stderr="")
output_path = resume_from_result(result_path, run_fn=fake_run)
assert output_path.name == "trial.resume-001.json"
assert calls[0][0:4] == ["opencode", "run", "--session", "ses_cli"]
payload = json.loads(output_path.read_text(encoding="utf-8"))
assert payload["source_result_path"] == str(result_path.resolve())
assert payload["stdout"].strip()
Add import pytest at the top of the file if missing.
- Step 2: Run tests to verify they fail
Run:
uv run pytest tests/examples/test_agent_challenge_resume.py::test_resume_trial_prints_resume_command tests/examples/test_agent_challenge_resume.py::test_resume_trial_run_writes_resume_result -q
Expected: fails because resume_trial.py does not exist.
- Step 3: Implement CLI
Create examples/agent_challenges/resume_trial.py:
from __future__ import annotations
import argparse
import json
import subprocess
import sys
import time
from collections.abc import Callable
from pathlib import Path
from typing import Any
try:
from .opencode_resume import resume_result_path
except ImportError:
sys.path.insert(0, str(Path(__file__).resolve().parents[2]))
from examples.agent_challenges.opencode_resume import resume_result_path
RunFn = Callable[..., subprocess.CompletedProcess[str]]
def _load_command(result: dict[str, Any]) -> list[str]:
opencode = result.get("opencode")
if not isinstance(opencode, dict):
raise ValueError("result has no opencode metadata")
command = opencode.get("resume_command")
if not isinstance(command, list) or not all(isinstance(part, str) for part in command):
raise ValueError("result has no resume_command; session id may be missing")
return command
def _display_command(command: list[str]) -> str:
return " ".join(command)
def resume_from_result(
result_path: Path,
*,
run_fn: RunFn = subprocess.run,
) -> Path:
result = json.loads(result_path.read_text(encoding="utf-8"))
if not isinstance(result, dict):
raise ValueError("result file must contain a JSON object")
command = _load_command(result)
started = time.monotonic()
completed = run_fn(
command,
text=True,
capture_output=True,
check=False,
encoding="utf-8",
errors="replace",
)
payload = {
"harness_version": "v2-resume",
"source_result_path": str(result_path.resolve()),
"command": command,
"duration_seconds": round(time.monotonic() - started, 3),
"returncode": completed.returncode,
"stdout": completed.stdout or "",
"stderr": completed.stderr or "",
}
output_path = resume_result_path(result_path)
output_path.write_text(json.dumps(payload, indent=2, sort_keys=True), encoding="utf-8")
return output_path
def main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description="Print or run an OpenCode trial resume command.")
parser.add_argument("--from-result", type=Path, required=True)
parser.add_argument("--print-command", action="store_true")
parser.add_argument("--run", action="store_true")
args = parser.parse_args(argv)
try:
result = json.loads(args.from_result.read_text(encoding="utf-8"))
if not isinstance(result, dict):
raise ValueError("result file must contain a JSON object")
command = _load_command(result)
if args.run:
print(resume_from_result(args.from_result).as_posix())
else:
print(_display_command(command))
except ValueError as exc:
parser.error(str(exc))
return 0
if __name__ == "__main__":
raise SystemExit(main())
Default behavior prints the command; --print-command is accepted for clarity but not required. Execution writes a sidecar .resume-NNN.json; it never overwrites the original result.
- Step 4: Run tests to verify they pass
Run:
uv run pytest tests/examples/test_agent_challenge_resume.py -q
Expected: all resume tests pass.
- Step 5: Update runbook
In docs/runbooks/agent-challenge-evaluation.md, add after “Save A Manual Audit”:
## Resume An Incomplete OpenCode Trial
When a result captures an OpenCode `sessionID`, the report includes an
`OpenCode Resume` section. Print the resume command with:
```powershell
uv run python examples/agent_challenges/resume_trial.py `
--from-result examples/agent_challenges/browser_click_challenge/results/opencode_deepseek-v4-flash-free-trial-034.json
Run the resume and save a sidecar result with:
uv run python examples/agent_challenges/resume_trial.py `
--from-result examples/agent_challenges/browser_click_challenge/results/opencode_deepseek-v4-flash-free-trial-034.json `
--run
The resume command writes *.resume-001.json beside the original result and
does not mutate the original raw result. Use manual audit to decide whether the
resumed output completes the trial or only provides additional evidence.
- [ ] **Step 6: Commit**
```bash
git add examples/agent_challenges/resume_trial.py tests/examples/test_agent_challenge_resume.py docs/runbooks/agent-challenge-evaluation.md
git commit -m "feat: add opencode trial resume cli"
Task 5: Final Verification And Roadmap
Files:
-
Modify:
docs/current_roadmap.md -
Move:
docs/superpowers/plans/2026-06-30-opencode-resume-metadata.mdtodocs/historical/superpowers/plans/2026-06-30-opencode-resume-metadata.md -
Step 1: Update roadmap
Add under the active agent evaluation area in docs/current_roadmap.md:
- Completed: agent challenge results now record OpenCode session metadata and
resume commands, so incomplete provider runs can be continued without
mutating original raw evidence.
- Step 2: Run focused tests
Run:
uv run pytest tests/examples/test_agent_challenge_resume.py tests/examples/test_agent_challenge_harness_v2.py tests/examples/test_agent_challenge_reports.py -q
Expected: all selected tests pass.
- Step 3: Run lint and typecheck
Run:
uv run ruff check examples/agent_challenges tests/examples
uv run ruff format --check examples/agent_challenges tests/examples
uv run basedpyright --level error examples/agent_challenges tests/examples
Expected: ruff clean, format clean, basedpyright 0 errors.
- Step 4: Smoke print against a real result
Run:
uv run python examples/agent_challenges/resume_trial.py --from-result examples/agent_challenges/browser_click_challenge/results/opencode_deepseek-v4-flash-free-trial-034.json
Expected: prints an opencode run --session ... command if that result still exists and contains a session id. If the result was cleaned up locally, use any current V2 result that has opencode.session_id.
- Step 5: Archive this plan
Run:
git mv docs/superpowers/plans/2026-06-30-opencode-resume-metadata.md docs/historical/superpowers/plans/2026-06-30-opencode-resume-metadata.md
- Step 6: Commit
git add docs/current_roadmap.md docs/historical/superpowers/plans/2026-06-30-opencode-resume-metadata.md
git commit -m "docs: record opencode resume metadata support"
Self-Review
- Spec coverage: The plan stores
attach_url,opencode_session_id, exact command, resume prompt, and resume command. It handles “continue” versus “final report only” prompt selection from result state. It adds report visibility and a CLI for printing/running resume commands. - Placeholder scan: No TODO/TBD placeholders remain. Each code-changing step includes concrete code and commands.
- Type consistency: The raw result key is
opencode, withsession_id,attach_url,command,model,variant,resume_prompt, andresume_command. The report DTO uses the same names. The CLI reads the sameopencode.resume_command.