Skip to content

chore: salvage /tmp doc tools + reflow the v2.3.1 release notes - #349

Open
doublegate wants to merge 2 commits into
mainfrom
chore/salvage-and-notes-reflow
Open

chore: salvage /tmp doc tools + reflow the v2.3.1 release notes#349
doublegate wants to merge 2 commits into
mainfrom
chore/salvage-and-notes-reflow

Conversation

@doublegate

@doublegate doublegate commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Two small chores, no code changes.

1. Salvage three doc tools from /tmp before a reboot

An NVIDIA driver/kernel update needed a reboot, which wipes /tmp. The tmp-salvage dry run offered 305 candidates / ~600 MB; three files were taken. All three exist nowhere else on disk (checked across ~/Code).

file needs what it does
reflow.py stdlib Unwraps hard-wrapped markdown to full width, preserving headings, rules, fenced code, tables, blockquotes and raw HTML.
assemble.py bs4 Rendered HTML fragment → standalone document.
guardrails_assemble.py bs4 Same, for the provenance-guardrails doc.

Left behind deliberately, recorded in docs/SALVAGE_MANIFEST.md so the curation is auditable: a 586 MB perf.data and other perf recordings (regenerable), ~100 PR-review files already posted to #348, screenshots that may be commercial-ROM framebuffers, and /tmp/accuracycoin-build/ — third-party MIT source plus a Windows nesasm.exe, where vendoring is a deliberate provenance decision rather than a salvage side effect.

The two bs4 scripts are preserved verbatim and are not ruff-clean (SIM115, UP031). Deliberate: bs4 isn't installed and no sample fragment survives, so a lint rewrite couldn't be executed to prove it still behaved. reflow.py — testable, being stdlib-only — was fixed and round-trip verified.

2. Reflow the v2.3.1 release notes

They were published hard-wrapped at ~80 columns, which GitHub renders as artificially narrow. Measured across published bodies:

v2.2.4   median=86    p90=329   max=563    wide
v2.2.5   median=165   p90=383   max=1006   wide
v2.2.9   median=491   p90=820   max=1025   wide
v2.3.0   median=245   p90=639   max=725    wide
v2.3.1   median=75    p90=81    max=136    HARD-WRAPPED   <-- the regression

v2.3.1 was the only one out of style — a fresh regression, not a return of the earlier problem. Fixed with reflow.py (salvaged one commit earlier, which makes forgetting to run it a particularly avoidable miss): median 75 → 93, p90 81 → 240, max 136 → 463.

Content verified unchanged rather than assumed: word count identical at 719, 13 table rows and 6 headings preserved. The published release body was updated to match via gh release edit, and all three platform assets remain attached.

Verification

pre-commit clean (markdownlint, EOF, whitespace, line endings); reflow.py ruff-clean and round-trip tested against paragraphs, list continuations, fenced code, tables and headings; SHA-256 verified identical to the /tmp originals after copying. No Rust touched — AccuracyCoin and nestest unaffected by construction.

Summary by CodeRabbit

  • Documentation

    • Added guidance for recovering and using release-automation tools.
    • Documented salvage details, verification information, and artifact-handling records.
    • Reformatted release notes for improved readability without changing their content.
  • New Tools

    • Added utilities to reflow Markdown while preserving structured content.
    • Added tools to assemble formatted HTML release documents with titles, metadata, highlighted takeaways, and PDF-friendly styling.

…reboot

An NVIDIA driver/kernel update needed a reboot, which wipes /tmp. The
`tmp-salvage` dry run offered 305 candidates totalling ~600 MB; three files were
taken and everything else was deliberately left to be wiped.

The three exist NOWHERE ELSE on disk (checked across all of ~/Code) and were
written during the v2.2.5-v2.3.0 documentation work:

  reflow.py               stdlib-only. Unwraps hard-wrapped markdown into single
                          full-width lines while preserving blank lines, ATX
                          headings, horizontal rules, fenced code blocks, tables,
                          blockquotes and raw HTML -- the constructs that break
                          when naively joined. This is the tool that fixed the
                          GitHub release-notes formatting: 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.
  assemble.py             bs4. Rendered HTML fragment -> standalone document.
  guardrails_assemble.py  bs4. Same, for the provenance-guardrails doc: injects a
                          title block and reddens a curated set of hard takeaways.

WHAT WAS LEFT BEHIND, and why -- recorded in docs/SALVAGE_MANIFEST.md so the
curation is auditable rather than implicit:

  * perf.data (586 MB), probe.perf, fp.data, di.data, bolt.log -- perf recordings
    and CI logs, regenerable with one command;
  * ~100 .md/.json PR-review files -- already posted to PR #348;
  * fisk-*.png / v2p-*.png (8 MB) -- screenshots that may be commercial-ROM
    framebuffers, not swept into the tree unexamined;
  * /tmp/accuracycoin-build/ -- third-party MIT source plus a Windows nesasm.exe.
    Vendoring third-party source is a deliberate provenance decision under the
    project's firewall rules, not a salvage side effect;
  * agy-review.sh + selftest -- duplicates of files already in scripts/ and
    Local_Only-Projects/antigravity-pr-review/;
  * p3.sh/p4.sh/probe*.sh/mi.rs -- extraction artifacts (workflow YAML pulled out
    for shellcheck) and a temp probe already reverted on purpose.

Everything built during the session was already committed (frame_probe.rs,
ab_check.sh, frame_breakdown.sh, the perf_log_check.py p99 gate), so the
scratchpad held no unique source beyond these three.

Verification: SHA-256 checked identical to the /tmp originals after copying; all
three parse; reflow.py is ruff-clean and was round-trip tested against a case
covering paragraphs, list continuations, fenced code, tables and headings.

The two bs4 scripts are preserved VERBATIM and are not ruff-clean (SIM115,
UP031). 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
that cannot be run is a worse trade than a style nit; the README says so and says
to clean them up when someone next has real input to test against.

README-doc-tools.md is added alongside them, because three unexplained scripts in
release-automation/ six months from now are only marginally better than losing
them.
The v2.3.1 release notes were published hard-wrapped at ~80 columns, which
GitHub renders as artificially narrow text instead of using the full width
available. Every other recent release uses the wide format; measured line-length
profiles of the published bodies:

    v2.2.4   median=86    p90=329   max=563    wide
    v2.2.5   median=165   p90=383   max=1006   wide
    v2.2.9   median=491   p90=820   max=1025   wide
    v2.3.0   median=245   p90=639   max=725    wide
    v2.3.1   median=75    p90=81    max=136    HARD-WRAPPED  <-- the regression

v2.3.1 was the only one out of style: the earlier pass that fixed v2.2.5 onward
held, and this was a fresh regression introduced when the notes were authored.

Reflowed with scripts/release-automation/reflow.py -- the tool written for
exactly this and salvaged from /tmp one commit earlier, which makes forgetting to
run it a particularly avoidable miss. Result: median 75 -> 93, p90 81 -> 240,
max 136 -> 463, in line with the rest.

Content is unchanged, which was verified rather than assumed: word count
identical at 719 before and after, 13 table rows and 6 headings preserved on both
sides. reflow.py only joins wrapped continuation lines within a paragraph or list
item and explicitly preserves blank lines, ATX headings, horizontal rules, fenced
code blocks, tables, blockquotes and raw HTML.

The published release body was updated to match via `gh release edit
--notes-file`, so the source file and GitHub agree; all three platform assets
remain attached.
Copilot AI lite review requested due to automatic review settings August 6, 2026 09:24
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Added three release-automation scripts for Markdown reflow and HTML assembly. Documented their recovery and usage. Reformatted the v2.3.1 release notes without changing their content.

Changes

Release automation

Layer / File(s) Summary
Markdown reflow and release-note formatting
scripts/release-automation/reflow.py, .github/release-notes/v2.3.1.md
Added Markdown classification and reflow logic. Reflowed release-note paragraphs and list items without substantive content changes.
HTML release assembly
scripts/release-automation/assemble.py, scripts/release-automation/guardrails_assemble.py
Added HTML fragment assembly, curated takeaway highlighting, NOTE transformations, metadata injection, UTF-8 output, and status reporting.
Recovery documentation
scripts/release-automation/README-doc-tools.md, docs/SALVAGE_MANIFEST.md
Documented tool inputs, outputs, dependencies, commands, limitations, recovery destinations, and SHA-256 verification details.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Possibly related PRs

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 8 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (8 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes both main changes: salvaging documentation tools and reflowing the v2.3.1 release notes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Docs-As-Spec Sync ✅ Passed The complete PR changes only release notes, salvage documentation, and release-automation scripts; no target crate files or chip behavior changed.
Changelog Entry For User-Visible Changes ✅ Passed The PR changes only release-note formatting plus salvage documentation and one-off scripts; normalized note content is identical (719 words), and CHANGELOG.md has no delta under empty Unreleased.
No Unwrap/Expect/Panic On Untrusted Input ✅ Passed The PR adds only Markdown and Python tooling; searches of all added lines and AST checks found no new .unwrap(), .expect(), or panic!() calls.
Safety Comment On New Unsafe Blocks ✅ Passed The PR range adds no Rust files and no added lines matching unsafe { or unsafe fn; therefore it introduces no unsafe block requiring a // SAFETY: comment.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch chore/salvage-and-notes-reflow

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown

Antigravity review (Gemini via Ultra)

This PR reflows .github/release-notes/v2.3.1.md paragraphs to single full-width lines and commits three release automation scripts (reflow.py, assemble.py, guardrails_assemble.py) salvaged from /tmp alongside manifest documentation.

Blocking issues

None found.

Suggestions

  • assemble.py:6 & guardrails_assemble.py:8: Unhandled CLI arguments (sys.argv[1], sys.argv[2]). Running either script without arguments raises an uninformative IndexError. Validate len(sys.argv) >= 3 and print usage instructions before indexing.
  • assemble.py:27-29: Unchecked next(...) generator expressions. If the target HTML fragment lacks a <table> or a <p> starting with "NOTE", this raises an unhandled StopIteration exception. Provide default values or explicit error messages.
  • assemble.py:7,93 & guardrails_assemble.py:9,39: Unclosed file handles from direct open(...).read() and open(...).write() calls. Use with open(...) context managers to guarantee cleanup.

Nitpicks

  • reflow.py:24-26: is_hr fails to match valid Markdown horizontal rules containing internal spaces (e.g. - - - or * * *).
  • assemble.py:94-96 & guardrails_assemble.py:40: Uses legacy %-formatting for print strings instead of standard f-strings.

Automated first-pass review by agy on a self-hosted runner -- not a human review.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds salvaged documentation/release-note helper scripts to the repository and reformats the v2.3.1 GitHub release notes to remove hard-wrapping so they render at full width.

Changes:

  • Add reflow.py to unwrap hard-wrapped Markdown while preserving common structural Markdown constructs.
  • Add two BeautifulSoup-based HTML fragment “assembler” scripts plus a short README describing intended usage.
  • Reflow .github/release-notes/v2.3.1.md and record the /tmp salvage decision in docs/SALVAGE_MANIFEST.md.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
scripts/release-automation/reflow.py New stdlib-only Markdown reflow tool used to unwrap hard-wrapped release notes.
scripts/release-automation/assemble.py New BeautifulSoup-based HTML fragment → document assembler (one-off tooling).
scripts/release-automation/guardrails_assemble.py New variant of the assembler for the guardrails document with custom title block and highlighting.
scripts/release-automation/README-doc-tools.md Documents the purpose and usage of the salvaged doc/release-note helpers.
docs/SALVAGE_MANIFEST.md Records what was (and was not) salvaged from /tmp and why.
.github/release-notes/v2.3.1.md Reflows the v2.3.1 release notes to remove hard wrapping while keeping content intact.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +8 to +10
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 +6 to +8
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 +71 to +72
content = ln.lstrip()[1:]
content = content.removeprefix(" ")

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/SALVAGE_MANIFEST.md`:
- Around line 131-132: Clarify the SHA-256 verification statement in the
recovery evidence near the referenced release-automation documentation: state
whether hashes were captured before subsequent edits to reflow.py or other
files, and record the per-file hashes if they constitute release evidence.
Ensure the wording does not imply that the current files remain byte-identical
unless that is verified, while preserving provenance for recovered and
externally sourced artifacts.

In `@scripts/release-automation/assemble.py`:
- Around line 6-7: Validate that exactly two command-line arguments are provided
before assigning paths or reading files in assemble.py at lines 6-7 and
guardrails_assemble.py at lines 8-9; otherwise exit with a clear usage message.
Apply the same argument-count validation and message consistently in both
scripts.
- Around line 27-33: Validate the required table and NOTE paragraph structure
before slicing in the surrounding fragment assembly logic: handle missing
matches with a clear rejection error, and reject any fragment where the NOTE
index is not after the table index. Preserve the existing before, table, and
middle extraction only after confirming both elements exist and table_i <
note_i.

In `@scripts/release-automation/README-doc-tools.md`:
- Around line 33-41: Update the documentation for assemble.py and
guardrails_assemble.py to state that the rendered HTML input must contain a
top-level table and a top-level paragraph whose text starts with “NOTE”. Keep
the existing usage example, and either document that missing elements cause
failure or add explicit validation with an actionable error in assemble.py.

In `@scripts/release-automation/reflow.py`:
- Around line 46-52: Update flush() to join buffered lines without stripping
trailing spaces, preserving Markdown hard-break markers and each line’s
whitespace boundaries. Revise the blank-line handling around the logic at lines
92-98 so authored runs of blank lines are retained rather than collapsed, and
add round-trip coverage for hard breaks and multiple blank lines.
- Around line 56-78: Update the fence handling in the reflow loop to parse
container prefixes first, record the opening fence character and length, and
only close a code block with a matching delimiter of sufficient length. Ensure
fenced blocks inside blockquotes are recognized correctly, while non-fence code
lines remain unchanged; replace the current in_code toggle logic around flush()
and out.append().
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 72a90697-26bf-471f-a909-a9c06126a940

📥 Commits

Reviewing files that changed from the base of the PR and between 9bb6adb and 38a1862.

📒 Files selected for processing (6)
  • .github/release-notes/v2.3.1.md
  • docs/SALVAGE_MANIFEST.md
  • scripts/release-automation/README-doc-tools.md
  • scripts/release-automation/assemble.py
  • scripts/release-automation/guardrails_assemble.py
  • scripts/release-automation/reflow.py

Comment thread docs/SALVAGE_MANIFEST.md
Comment on lines +131 to +132
SHA-256 verified identical to source after copy. See
`scripts/release-automation/README-doc-tools.md` for usage.

Copy link
Copy Markdown

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.py was fixed after recovery, while this line says the files were SHA-256 identical to the /tmp sources 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
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/SALVAGE_MANIFEST.md` around lines 131 - 132, Clarify the SHA-256
verification statement in the recovery evidence near the referenced
release-automation documentation: state whether hashes were captured before
subsequent edits to reflow.py or other files, and record the per-file hashes if
they constitute release evidence. Ensure the wording does not imply that the
current files remain byte-identical unless that is verified, while preserving
provenance for recovered and externally sourced artifacts.

Source: Coding guidelines

Comment on lines +6 to +7
frag_path, out_path = sys.argv[1], sys.argv[2]
html = open(frag_path, encoding="utf-8").read()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The 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 IndexError. Extra arguments are ignored. Exit with a usage message unless exactly two paths are supplied.

  • scripts/release-automation/assemble.py#L6-L7: validate the argument count before reading the input and output paths.
  • scripts/release-automation/guardrails_assemble.py#L8-L9: apply the same validation and usage message.
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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
frag_path, out_path = sys.argv[1], sys.argv[2]
html = open(frag_path, encoding="utf-8").read()
if len(sys.argv) != 3:
raise SystemExit(f"usage: {sys.argv[0]} INPUT_FRAGMENT OUTPUT_HTML")
frag_path, out_path = sys.argv[1:]
🧰 Tools
🪛 ast-grep (0.45.0)

[warning] 6-6: File path is request-/variable-derived; validate and normalize to prevent path traversal.
Context: open(frag_path, encoding="utf-8")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(open-filename-from-request)

🪛 Ruff (0.16.1)

[warning] 7-7: Use a context manager for opening files

(SIM115)

📍 Affects 2 files
  • scripts/release-automation/assemble.py#L6-L7 (this comment)
  • scripts/release-automation/guardrails_assemble.py#L8-L9
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/release-automation/assemble.py` around lines 6 - 7, Validate that
exactly two command-line arguments are provided before assigning paths or
reading files in assemble.py at lines 6-7 and guardrails_assemble.py at lines
8-9; otherwise exit with a clear usage message. Apply the same argument-count
validation and message consistently in both scripts.

Source: Path instructions

Comment on lines +27 to +33
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])

Copy link
Copy Markdown

Choose a reason for hiding this comment

The 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, next() raises StopIteration. If the NOTE paragraph precedes the table, this code emits the NOTE twice. Reject fragments unless both elements exist and table_i < note_i.

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

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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])
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")
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])
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/release-automation/assemble.py` around lines 27 - 33, Validate the
required table and NOTE paragraph structure before slicing in the surrounding
fragment assembly logic: handle missing matches with a clear rejection error,
and reject any fragment where the NOTE index is not after the table index.
Preserve the existing before, table, and middle extraction only after confirming
both elements exist and table_i < note_i.

Source: Path instructions

Comment on lines +33 to +41
`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
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Document the required assemble.py input structure.

Line 33 describes both tools as accepting any rendered HTML fragment. The supplied scripts/release-automation/assemble.py implementation searches for a top-level <table> and a top-level paragraph whose text starts with NOTE; missing either element raises StopIteration. Document these preconditions, or add explicit validation with an actionable error.

As per path instructions, flag documentation that drifts from the code it describes rather than just prose nits.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/release-automation/README-doc-tools.md` around lines 33 - 41, Update
the documentation for assemble.py and guardrails_assemble.py to state that the
rendered HTML input must contain a top-level table and a top-level paragraph
whose text starts with “NOTE”. Keep the existing usage example, and either
document that missing elements cause failure or add explicit validation with an
actionable error in assemble.py.

Source: Path instructions

Comment on lines +46 to +52
def flush():
if buf:
first = buf[0]
indent = first[: len(first) - len(first.lstrip())]
joined = indent + " ".join(p.strip() for p in buf)
out.append(joined)
buf.clear()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Preserve Markdown hard breaks and blank-line runs.

p.strip() removes the two trailing spaces that encode a Markdown hard break. Lines 92-97 also collapse authored blank-line runs. Both behaviors conflict with the documented preservation guarantee and can change the rendered release body.

Preserve hard-break boundaries and retain each blank line instead of normalizing this whitespace. Add round-trip cases for first··\nsecond and paragraphs separated by multiple blank lines.

Also applies to: 92-98

🧰 Tools
🪛 Ruff (0.16.1)

[warning] 46-46: Missing return type annotation for private function flush

Add return type annotation: None

(ANN202)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/release-automation/reflow.py` around lines 46 - 52, Update flush() to
join buffered lines without stripping trailing spaces, preserving Markdown
hard-break markers and each line’s whitespace boundaries. Revise the blank-line
handling around the logic at lines 92-98 so authored runs of blank lines are
retained rather than collapsed, and add round-trip coverage for hard breaks and
multiple blank lines.

Comment on lines +56 to +78
# Fenced code block toggle.
if stripped.startswith(("```", "~~~")):
flush()
out.append(ln)
in_code = not in_code
continue
if in_code:
out.append(ln)
continue
if stripped == "":
flush()
out.append("")
continue
# Blockquote: join consecutive `>` lines into one full-width `> ...` line.
if stripped.startswith(">"):
content = ln.lstrip()[1:]
content = content.removeprefix(" ")
if buf and buf[0].lstrip().startswith(">"):
buf.append(content) # continuation of the current blockquote
else:
flush()
buf.append(ln.rstrip()) # seed with the full `> ...` line
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

Track the opening fence before closing a code block.

startswith(("```", "~~~")) toggles state for any fence-looking line. A three-backtick line closes a four-backtick block. A tilde fence can also close a backtick fence. Fences inside blockquotes are not recognized and their code lines are joined into one blockquote line.

Record the opening fence character and length. Only close on a matching delimiter. Parse container prefixes before applying fence handling.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/release-automation/reflow.py` around lines 56 - 78, Update the fence
handling in the reflow loop to parse container prefixes first, record the
opening fence character and length, and only close a code block with a matching
delimiter of sufficient length. Ensure fenced blocks inside blockquotes are
recognized correctly, while non-fence code lines remain unchanged; replace the
current in_code toggle logic around flush() and out.append().

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants