Files
soleprint/soleprint/station/tools/histgen/brief.py

112 lines
4.3 KiB
Python

"""
One pack per commit, for whoever writes the message.
This is the interface to the expensive reader, and it is a directory of
markdown rather than a network call. The tool does not know how to reach a
model and does not want to: an agent already in this repo reads the briefs and
writes titles and bodies back into plan.json, which keeps the messages editable
before a single commit exists and keeps an API key out of a tool that otherwise
runs offline.
What travels is the reasoning already in the code — each file's opening
comment — and not the file. That is both what makes the pack cheap and what
makes the message right: a commit message reconstructed from a diff restates
the diff, and the thing worth recording was never in the diff. It was in the
comment explaining why the port offsets have to match another project's, or why
the ignore rules exist before the code they exclude.
"""
import json
from .census import INDEX_FILE, state_dir
from .order import plan_path
BRIEF_DIR = "briefs"
WHY_CHARS = 700 # an opening comment past this is an essay; the head carries it
MAX_LISTED = 40
HEADER = """# {n:02d}{slug}
**{count} file(s), commit {n} of {total}.**
Write a title and a body for this commit, then put them in
`{plan}` under group {n} as `"title"` and `"body"`.
- The title says what this commit establishes, in the repo's own words.
- The body carries the **why** — take it from the reasoning already in the
comments below. Do not restate the diff; the diff is already in the commit.
- If a file below does not belong in this commit, move its path to another
group in plan.json. The grouping is a proposal.
"""
def _fmt_why(text):
text = (text or "").strip()
if not text:
return "_(no opening comment)_"
if len(text) > WHY_CHARS:
text = text[:WHY_CHARS].rsplit("\n", 1)[0] + "\n"
return "\n".join("> " + line if line.strip() else ">" for line in text.split("\n"))
def write_briefs(out, quiet=False):
state = state_dir(out)
plan = json.loads(plan_path(out).read_text())
index = json.loads((state / INDEX_FILE).read_text())
files = index["files"]
groups = plan["groups"]
# Which commit each path lands in, so a dependency can be named by the
# commit that introduced it rather than by a bare path. "stands on 04" is
# the sentence the ordering exists to make true.
landed = {p: g["n"] for g in groups for p in g["paths"]}
out_dir = state / BRIEF_DIR
out_dir.mkdir(parents=True, exist_ok=True)
for stale in out_dir.glob("*.md"):
stale.unlink()
for g in groups:
lines = [HEADER.format(n=g["n"], slug=g["slug"], count=len(g["paths"]),
total=len(groups), plan=plan_path(out))]
earlier, later = {}, set()
for p in g["paths"]:
for dep in files.get(p, {}).get("refs", []):
n = landed.get(dep)
if n is None or dep in g["paths"]:
continue
(earlier.setdefault(n, set()).add(dep) if n < g["n"] else later.add(dep))
if earlier:
lines.append("\n## Stands on\n")
for n in sorted(earlier):
names = ", ".join(f"`{d}`" for d in sorted(earlier[n])[:MAX_LISTED])
lines.append(f"- commit {n:02d}: {names}")
if later:
# Worth stating plainly rather than hiding: it is the one thing a
# reader of the finished history would notice and the tool cannot
# fix, because the fix is a judgement about which comes first.
names = ", ".join(f"`{d}`" for d in sorted(later)[:MAX_LISTED])
lines.append("\n## Forward references (this commit names things not yet committed)\n")
lines.append(f"- {names}")
lines.append("\n## Files\n")
for p in g["paths"]:
e = files.get(p, {})
meta = f"{e.get('role', '?')}, {e.get('lines', 0)} lines"
if e.get("binary"):
meta += ", binary"
lines.append(f"\n### `{p}`\n\n_{meta}_\n")
lines.append(_fmt_why(e.get("why")))
path = out_dir / f"{g['n']:02d}-{g['slug']}.md"
path.write_text("\n".join(lines) + "\n")
if not quiet:
print(f"Wrote {len(groups)} briefs -> {out_dir}")
print(f"Read them, then write title and body into {plan_path(out)}.")
return out_dir