mirror of
https://github.com/kuhyx/utils.git
synced 2026-11-13 11:10:58 +01:00
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file length / file-length (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
palette drift / palette (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
shared gate tests / gate unit tests (push) Waiting to run
The server stores a new_note task as '<repo> todos: <title>' plus a '## <title>' section, so the title no longer started with MANUAL WORK REQUIRED and the entry's own ## heading became a runnable part. After add_task the inbox now rewrites the note with update_task (exact title, section-free body). Marker is written first, format 2 only after the rewrite: a failed rewrite is retried, never re-added; old markers are repaired. install.sh counts folders from the Paths property. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01WzxdWXmfFFHX4fNhyFRqFq
96 lines
3.1 KiB
Python
96 lines
3.1 KiB
Python
# Copyright (c) 2026 Krzysztof Rudnicki
|
|
"""Builders for fake feedback bundles and a fake todo client."""
|
|
|
|
from __future__ import annotations
|
|
|
|
from dataclasses import dataclass, field
|
|
import json
|
|
import os
|
|
from typing import TYPE_CHECKING
|
|
|
|
from feedback_inbox._todo import TaskRef, TodoError
|
|
|
|
if TYPE_CHECKING:
|
|
from pathlib import Path
|
|
|
|
STAMP = "2026-11-11T13-52-33-162"
|
|
NOTE = "I would like to see the painting IN FULL\n\nsecond paragraph"
|
|
|
|
|
|
def entry_text(bundle: Path, note: str = NOTE, game: str = "museum") -> str:
|
|
"""The markdown entry a game appends for ``bundle``."""
|
|
head = note.splitlines()[0][:70] if note else ""
|
|
parts = [f"## 2026-11-11T13:52:33: {head}", ""]
|
|
if note:
|
|
parts += [note, ""]
|
|
parts += [
|
|
f"- Screenshot: `{bundle}/annotated.png`",
|
|
f"- Report: `{bundle}/report.json`",
|
|
f"- Game `{game}`, combo `loop=P2,carry=d,id=c`, build abc, session `s`",
|
|
"",
|
|
"Task: Read the annotated screenshot and fix it.",
|
|
"",
|
|
]
|
|
return "\n".join(parts)
|
|
|
|
|
|
def make_bundle(
|
|
data_root: Path,
|
|
*,
|
|
game: str = "cleanup-plate",
|
|
stamp: str = STAMP,
|
|
note: str = NOTE,
|
|
report: str | None = "ok",
|
|
entry: bool = True,
|
|
age: float = 60.0,
|
|
now: float = 1_000_000.0,
|
|
) -> Path:
|
|
"""Create ``<game>_binaries/feedback/<stamp>/`` and its md entry."""
|
|
feedback = data_root / f"{game}_binaries" / "feedback"
|
|
bundle = feedback / stamp
|
|
bundle.mkdir(parents=True, exist_ok=True)
|
|
if report is not None:
|
|
path = bundle / "report.json"
|
|
if report == "ok":
|
|
body = json.dumps({"note": note, "game": "museum", "combo": "loop=P2"})
|
|
else:
|
|
body = report
|
|
path.write_text(body, encoding="utf-8")
|
|
os.utime(path, (now - age, now - age))
|
|
if entry:
|
|
md = feedback / f"feedback-{stamp[:10]}.md"
|
|
with md.open("a", encoding="utf-8") as handle:
|
|
handle.write(entry_text(bundle, note))
|
|
return bundle
|
|
|
|
|
|
@dataclass
|
|
class FakeTodo:
|
|
"""A todo server stand-in that stores notes the way the real one does."""
|
|
|
|
calls: list[tuple[str, str, str]] = field(default_factory=list)
|
|
stored: dict[str, str] = field(default_factory=dict)
|
|
fail: bool = False
|
|
fail_update: bool = False
|
|
merged: bool = False
|
|
updated: list[str] = field(default_factory=list)
|
|
|
|
def add_task(self, repo: str, title: str, body: str) -> TaskRef:
|
|
if self.fail:
|
|
msg = "todo down"
|
|
raise TodoError(msg)
|
|
self.calls.append((repo, title, body))
|
|
task_id = f"id{len(self.calls):04d}" + "0" * 28
|
|
# A new_note task: title "<name> todos: <title>", then a "## <title>"
|
|
# section holding the body.
|
|
name = repo.rsplit("/", maxsplit=1)[-1]
|
|
self.stored[task_id] = f"{name} todos: {title}\n\n## {title}\n{body}"
|
|
return TaskRef(task_id=task_id, merged=self.merged)
|
|
|
|
def update_task(self, task_id: str, title: str, body: str) -> None:
|
|
if self.fail_update:
|
|
msg = "update refused"
|
|
raise TodoError(msg)
|
|
self.updated.append(task_id)
|
|
self.stored[task_id] = f"{title}\n\n{body}"
|