-
-
Notifications
You must be signed in to change notification settings - Fork 1
chore: salvage /tmp doc tools + reflow the v2.3.1 release notes #349
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| # Release-notes / doc rendering helpers | ||
|
|
||
| Three one-off tools rescued from `/tmp` before a reboot (2026-08-06). They were | ||
| written during the v2.2.5–v2.3.0 documentation work, existed **nowhere else on | ||
| disk**, and would have been lost. Recorded here so they are findable rather than | ||
| rediscovered. | ||
|
|
||
| | script | needs | what it does | | ||
| | --- | --- | --- | | ||
| | `reflow.py` | stdlib only | Unwraps hard-wrapped markdown into single full-width lines. | | ||
| | `assemble.py` | `bs4` | Assembles a rendered HTML fragment into a full document. | | ||
| | `guardrails_assemble.py` | `bs4` | Same, for the provenance-guardrails doc: injects a title block and reddens a curated set of hard takeaways. | | ||
|
|
||
| ## `reflow.py` — the one you will want again | ||
|
|
||
| This is the tool that fixed the GitHub release-notes formatting complaint: notes | ||
| published from v2.2.5 onward had been hard-wrapped at ~80 columns, which GitHub | ||
| renders as artificially narrow text instead of using the full width available. | ||
|
|
||
| It unwraps paragraphs and list items to one line each while **preserving** | ||
| blank lines, ATX headings, horizontal rules, fenced code blocks, tables, | ||
| blockquotes, and raw HTML lines — the things that break if naively joined. | ||
|
|
||
| ```bash | ||
| python3 scripts/release-automation/reflow.py < in.md > out.md | ||
| ``` | ||
|
|
||
| Worth running over any hand-wrapped `.github/release-notes/vX.Y.Z.md` before | ||
| publishing. | ||
|
|
||
| ## The `bs4` pair | ||
|
|
||
| `assemble.py` and `guardrails_assemble.py` take a rendered HTML fragment and | ||
| produce a standalone document. They need BeautifulSoup, which is **not** a | ||
| project dependency — install it in a throwaway venv rather than adding it to the | ||
| repo: | ||
|
|
||
| ```bash | ||
| python3 -m venv /tmp/venv && /tmp/venv/bin/pip install beautifulsoup4 | ||
| /tmp/venv/bin/python scripts/release-automation/assemble.py frag.html out.html | ||
| ``` | ||
|
Comment on lines
+33
to
+41
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win Document the required Line 33 describes both tools as accepting any rendered HTML fragment. The supplied As per path instructions, flag documentation that drifts from the code it describes rather than just prose nits. 🤖 Prompt for AI AgentsSource: Path instructions |
||
|
|
||
| Both are specific to the one-time provenance/guardrails PDF build | ||
| (`ref-docs/`) and are kept for reproducing those artifacts, not for routine use. | ||
|
|
||
| **They are preserved verbatim and are not ruff-clean** (`SIM115` context | ||
| managers, `UP031` percent-format). That is deliberate: `bs4` is not installed | ||
| here and no sample fragment survives, so a lint rewrite could not be executed to | ||
| prove it still behaved. Rewriting code you cannot run is a worse trade than a | ||
| style nit. Clean them up the first time you actually need them, with a real | ||
| input to test against. `reflow.py` — which *is* testable, being stdlib-only — was | ||
| fixed and verified. | ||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,96 @@ | ||||||||||||||||||||||||||||||||||||||||
| #!/usr/bin/env python3 | ||||||||||||||||||||||||||||||||||||||||
| import sys | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| from bs4 import BeautifulSoup, NavigableString | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| frag_path, out_path = sys.argv[1], sys.argv[2] | ||||||||||||||||||||||||||||||||||||||||
| html = open(frag_path, encoding="utf-8").read() | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+6
to
+7
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Validate the two required command-line arguments. Missing arguments raise
Proposed fix-frag_path, out_path = sys.argv[1], sys.argv[2]
+if len(sys.argv) != 3:
+ raise SystemExit(f"usage: {sys.argv[0]} INPUT_FRAGMENT OUTPUT_HTML")
+frag_path, out_path = sys.argv[1:]As per path instructions, prioritize correctness and clear error messages over style. 📝 Committable suggestion
Suggested change
🧰 Tools🪛 ast-grep (0.45.0)[warning] 6-6: File path is request-/variable-derived; validate and normalize to prevent path traversal. (open-filename-from-request) 🪛 Ruff (0.16.1)[warning] 7-7: Use a context manager for opening files (SIM115) 📍 Affects 2 files
🤖 Prompt for AI AgentsSource: Path instructions |
||||||||||||||||||||||||||||||||||||||||
| soup = BeautifulSoup(html, "html.parser") | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+6
to
+8
|
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| # --- RED for curated genuine takeaways (exact <strong> text contains) --- | ||||||||||||||||||||||||||||||||||||||||
| KEY = [ | ||||||||||||||||||||||||||||||||||||||||
| "more serious LLM error", | ||||||||||||||||||||||||||||||||||||||||
| "did not follow it", | ||||||||||||||||||||||||||||||||||||||||
| "AI self-attestation of license compliance is not trustworthy", | ||||||||||||||||||||||||||||||||||||||||
| "single most important piece of evidence", | ||||||||||||||||||||||||||||||||||||||||
| "Correct.", | ||||||||||||||||||||||||||||||||||||||||
| "not written purely from hardware documentation", | ||||||||||||||||||||||||||||||||||||||||
| ] | ||||||||||||||||||||||||||||||||||||||||
| for s in soup.find_all("strong"): | ||||||||||||||||||||||||||||||||||||||||
| t = s.get_text() | ||||||||||||||||||||||||||||||||||||||||
| if any(k in t for k in KEY): | ||||||||||||||||||||||||||||||||||||||||
| cls = s.get("class", []) | ||||||||||||||||||||||||||||||||||||||||
| s["class"] = cls + ["key"] | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| # --- top-level elements, in order --- | ||||||||||||||||||||||||||||||||||||||||
| els = [c for c in soup.contents if getattr(c, "name", None)] | ||||||||||||||||||||||||||||||||||||||||
| table_i = next(i for i, e in enumerate(els) if e.name == "table") | ||||||||||||||||||||||||||||||||||||||||
| note_i = next(i for i, e in enumerate(els) | ||||||||||||||||||||||||||||||||||||||||
| if e.name == "p" and e.get_text().lstrip().startswith("NOTE")) | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| before = "".join(str(e) for e in els[:table_i]) | ||||||||||||||||||||||||||||||||||||||||
| table = str(els[table_i]) | ||||||||||||||||||||||||||||||||||||||||
| middle = "".join(str(e) for e in els[table_i + 1:note_i]) | ||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+27
to
+33
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win Validate the required fragment structure before slicing. If the table or NOTE paragraph is absent, Proposed fix-table_i = next(i for i, e in enumerate(els) if e.name == "table")
-note_i = next(i for i, e in enumerate(els)
- if e.name == "p" and e.get_text().lstrip().startswith("NOTE"))
+table_i = next((i for i, e in enumerate(els) if e.name == "table"), None)
+note_i = next(
+ (i for i, e in enumerate(els)
+ if e.name == "p" and e.get_text().lstrip().startswith("NOTE")),
+ None,
+)
+if table_i is None or note_i is None or table_i >= note_i:
+ raise ValueError("expected a top-level table followed by a NOTE paragraph")As per path instructions, prioritize correctness and clear error messages over style. 📝 Committable suggestion
Suggested change
🤖 Prompt for AI AgentsSource: Path instructions |
||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| # --- italicize everything after "): " in the NOTE paragraph (PDF only) --- | ||||||||||||||||||||||||||||||||||||||||
| note_p = els[note_i] | ||||||||||||||||||||||||||||||||||||||||
| MARKER = "): " | ||||||||||||||||||||||||||||||||||||||||
| kids = list(note_p.children) | ||||||||||||||||||||||||||||||||||||||||
| head, tail_nodes, splitting = [], [], True | ||||||||||||||||||||||||||||||||||||||||
| for k in kids: | ||||||||||||||||||||||||||||||||||||||||
| if splitting and isinstance(k, NavigableString) and MARKER in k: | ||||||||||||||||||||||||||||||||||||||||
| pre, post = str(k).split(MARKER, 1) | ||||||||||||||||||||||||||||||||||||||||
| head.append(NavigableString(pre + MARKER)) | ||||||||||||||||||||||||||||||||||||||||
| if post: | ||||||||||||||||||||||||||||||||||||||||
| tail_nodes.append(NavigableString(post)) | ||||||||||||||||||||||||||||||||||||||||
| splitting = False | ||||||||||||||||||||||||||||||||||||||||
| elif splitting: | ||||||||||||||||||||||||||||||||||||||||
| head.append(k) | ||||||||||||||||||||||||||||||||||||||||
| else: | ||||||||||||||||||||||||||||||||||||||||
| tail_nodes.append(k.extract() if hasattr(k, "extract") else k) | ||||||||||||||||||||||||||||||||||||||||
| if tail_nodes: # only rebuild if the marker was found | ||||||||||||||||||||||||||||||||||||||||
| note_p.clear() | ||||||||||||||||||||||||||||||||||||||||
| for h in head: | ||||||||||||||||||||||||||||||||||||||||
| note_p.append(h) | ||||||||||||||||||||||||||||||||||||||||
| em = soup.new_tag("em") | ||||||||||||||||||||||||||||||||||||||||
| for t in tail_nodes: | ||||||||||||||||||||||||||||||||||||||||
| em.append(t) | ||||||||||||||||||||||||||||||||||||||||
| # Bold + upright "Fiskbit" inside the italic note body (PDF styling only) | ||||||||||||||||||||||||||||||||||||||||
| for tnode in list(em.find_all(string=True)): | ||||||||||||||||||||||||||||||||||||||||
| if "Fiskbit" in tnode: | ||||||||||||||||||||||||||||||||||||||||
| parts = str(tnode).split("Fiskbit") | ||||||||||||||||||||||||||||||||||||||||
| repl = [] | ||||||||||||||||||||||||||||||||||||||||
| for i, seg in enumerate(parts): | ||||||||||||||||||||||||||||||||||||||||
| if i > 0: | ||||||||||||||||||||||||||||||||||||||||
| st = soup.new_tag("strong") | ||||||||||||||||||||||||||||||||||||||||
| st["class"] = ["upright"] | ||||||||||||||||||||||||||||||||||||||||
| st.string = "Fiskbit" | ||||||||||||||||||||||||||||||||||||||||
| repl.append(st) | ||||||||||||||||||||||||||||||||||||||||
| if seg: | ||||||||||||||||||||||||||||||||||||||||
| repl.append(NavigableString(seg)) | ||||||||||||||||||||||||||||||||||||||||
| tnode.replace_with(*repl) | ||||||||||||||||||||||||||||||||||||||||
| break | ||||||||||||||||||||||||||||||||||||||||
| note_p.append(em) | ||||||||||||||||||||||||||||||||||||||||
| note = str(note_p) | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| TITLE = """<div class="titlewrap"> | ||||||||||||||||||||||||||||||||||||||||
| <p class="eyebrow">RUSTYNES · INCIDENT RECORD · GPL PROVENANCE</p> | ||||||||||||||||||||||||||||||||||||||||
| <h1 class="title">Provenance Failure Post-Mortem</h1> | ||||||||||||||||||||||||||||||||||||||||
| <p class="subtitle">How GPL Emulator Code Was Lifted Despite a Black-Box Instruction</p> | ||||||||||||||||||||||||||||||||||||||||
| <p class="docdate">Forensic Root-Cause Analysis · 2026-08-04 · RustyNES v2.2.9</p> | ||||||||||||||||||||||||||||||||||||||||
| </div>""" | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| body = (TITLE | ||||||||||||||||||||||||||||||||||||||||
| + '<div class="cols">' + before + '</div>' | ||||||||||||||||||||||||||||||||||||||||
| + table | ||||||||||||||||||||||||||||||||||||||||
| + '<div class="cols">' + middle + '</div>' | ||||||||||||||||||||||||||||||||||||||||
| + '<div class="note-box">' + note + '</div>') | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| doc = ('<!doctype html><html lang="en"><head><meta charset="utf-8">' | ||||||||||||||||||||||||||||||||||||||||
| '<title>Provenance Failure Post-Mortem</title></head><body>' | ||||||||||||||||||||||||||||||||||||||||
| + body + '</body></html>') | ||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||
| open(out_path, "w", encoding="utf-8").write(doc) | ||||||||||||||||||||||||||||||||||||||||
| print("assembled ->", out_path, | ||||||||||||||||||||||||||||||||||||||||
| "| before/middle/note split at table_i=%d note_i=%d" % (table_i, note_i), | ||||||||||||||||||||||||||||||||||||||||
| "| reddened %d strong tags" % len(soup.select("strong.key"))) | ||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,40 @@ | ||
| #!/usr/bin/env python3 | ||
| """Assemble the guardrails HTML: inject a title block, redden a curated | ||
| set of hard takeaways. Single-column theme handles the rest of the flow.""" | ||
| import sys | ||
|
|
||
| from bs4 import BeautifulSoup | ||
|
|
||
| frag_path, out_path = sys.argv[1], sys.argv[2] | ||
| html = open(frag_path, encoding="utf-8").read() | ||
| soup = BeautifulSoup(html, "html.parser") | ||
|
Comment on lines
+8
to
+10
|
||
|
|
||
| # --- RED reserved for a few genuine, full-span takeaways --- | ||
| KEY = [ | ||
| "capability plus availability plus an accuracy objective", | ||
| "C used as if it were A", | ||
| "source physically unavailable to the agent", | ||
| "Never launder", | ||
| ] | ||
| reddened = 0 | ||
| for s in soup.find_all("strong"): | ||
| t = s.get_text() | ||
| if any(k in t for k in KEY): | ||
| s["class"] = s.get("class", []) + ["key"] | ||
| reddened += 1 | ||
|
|
||
| body = "".join(str(c) for c in soup.contents) | ||
|
|
||
| TITLE = """<div class="titlewrap"> | ||
| <p class="eyebrow">COMMUNITY BEST-GUIDANCE · AI-ASSISTED EMULATOR DEVELOPMENT</p> | ||
| <h1 class="title">Provenance & License Guardrails</h1> | ||
| <p class="subtitle">A ready-to-ingest ruleset for Claude Code and other agentic / AI-assisted development tools</p> | ||
| <p class="docdate">Preventing the copyleft-source-lifting trap · 2026-08-04</p> | ||
| </div>""" | ||
|
|
||
| doc = ('<!doctype html><html lang="en"><head><meta charset="utf-8">' | ||
| '<title>Provenance & License Guardrails</title></head><body>' | ||
| + TITLE + body + '</body></html>') | ||
|
|
||
| open(out_path, "w", encoding="utf-8").write(doc) | ||
| print("assembled ->", out_path, "| reddened %d strong tags" % reddened) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Clarify the SHA-256 verification timeline.
The README states that
reflow.pywas fixed after recovery, while this line says the files were SHA-256 identical to the/tmpsources after copying. State whether the hashes were captured before later edits, and record per-file hashes if this is the release evidence. Otherwise, readers may treat the current files as byte-identical recovered artifacts.As per coding guidelines, preserve provenance and clearly document recovered, discarded, or externally sourced artifacts.
🤖 Prompt for AI Agents
Source: Coding guidelines