Skip to content

chore: unwrap Markdown prose - #85

Merged
michen00 merged 2 commits into
mainfrom
chore/unwrap-markdown-prose
Sep 15, 2026
Merged

michen00 merged 2 commits into
mainfrom
chore/unwrap-markdown-prose

Conversation

@michen00

@michen00 michen00 commented Sep 15, 2026

Copy link
Copy Markdown
Owner

Stacked on #84 — its base is ci/release-pr-worthiness-gate, because both branches touch CONTRIBUTING.md and basing this on main would conflict. Merge #84 first.

There is nothing to port

markdown-prose-hooks publishes the hooks as a pre-commit repository and both PR-body halves as reusable workflows, so this consumes them. Vendoring a copy would have created a fork to keep in sync with no upside.

Localization came to the two checks its README asks for, and both already held here:

check this repo verdict
markdownlint line-length false in .markdownlint.yml no conflict
prettier proseWrap unset in .prettierrc, so preserve no conflict

That mattered more than it sounds: a rule that wraps prose and a hook that unwraps it undo each other on every run.

What changed

The hook. unwrap-markdown-prose-py, placed before prettier — this rewrites prose and prettier is what normalizes the result. The -py mirror rather than -rs because a language: rust hook builds from source, so pre-commit would install a whole Rust toolchain before checking the first commit, and nothing here needs cargo otherwise.

The repo's prose. Smaller than expected — README.md and everything under .github/ were already unwrapped. Only two files carried manual breaks:

file paragraphs breaks removed
CONTRIBUTING.md 11 23
CLAUDE.md 1 3

The three blocks #84 itself writes are unwrapped in #84, so it no longer introduces prose this PR has to clean up afterwards. Those were spliced by hand and then checked byte-for-byte against the hook's own output, so the two branches agree on what unwrapped means rather than merely looking similar.

The PR-body pair. prose-body.yml reports, prose-body-write.yml rewrites. They take different trigger types deliberately: sharing an event fires both at once and the report then describes a body the rewrite is about to replace. The report is narrowed to synchronize and edited, dropping the opened and reopened the upstream example carries, which belong to the write half.

prose-body-write.yml takes effect only once it is on mainpull_request_target reads the workflow from the default branch, which is exactly why a pull request cannot try it out.

What I did not take

The fork-safe file pair (unwrap-propose.yml + unwrap-comment.yml). pre-commit.ci already runs the hook on every pull request here, forks included, and autofixes. Taking the pair as well would mean two bots reporting the same defect on the same pull request and two comments to read before either is actionable — that is the cost, not that the gain is small. If pre-commit.ci is ever dropped, this is the replacement.

One consequence worth knowing

Both new workflows are added to bot-automerge.yml's actions-major hold list, re-derived as that file explicitly asks when triggers change. Neither is exercised by the pull request proposing its own bump: prose-body-write.yml runs from the default branch under pull_request_target, and prose-body.yml fires only on synchronize and edited, neither of which a freshly opened bot pull request produces. So a green check would say nothing about the new version, and a major bump of either now waits for a human.

Test plan

  • unwrap-markdown-prose-py --all-files run through pre-commit; re-running is a no-op, so the result is a fixed point.
  • Structure preserved across the rewrite — heading, list, fence and ordered-item counts in CONTRIBUTING.md are identical before and after (21 headings, 22 list items, 4 fences, 7 ordered items), and no tables exist in either file to disturb.
  • actionlint clean on both new workflows and on the edited bot-automerge.yml.
  • make check green.
  • This PR's own body was written through the tool, as was fix(release-pr): only propose worthwhile releases #84's.

🤖 Generated with Claude Code

@qodo-code-review

Copy link
Copy Markdown

PR Summary by Qodo

Automate Markdown prose unwrapping in files and PR bodies

✨ Enhancement ⚙️ Configuration changes 📝 Documentation 🕐 20-40 Minutes

Grey Divider

AI Description

• Adds pre-commit enforcement to unwrap Markdown prose before Prettier normalization.
• Rewrites or reports hard-wrapped pull request bodies through event-specific reusable workflows.
• Unwraps existing prose and guards unexercised workflow upgrades from automatic major-version
 merges.
Diagram

graph TD
  Files["Markdown files"] --> Hook["Unwrap hook"] --> Prettier["Prettier"]
  Events["PR events"] -->|open/reopen/ready| Write["Body rewrite"] --> Body["PR body"]
  Events -->|sync/edit| Report["Body report"] --> Feedback["PR feedback"]
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Vendor the hook and workflows
  • ➕ Provides complete control over implementation and release timing.
  • ➕ Avoids runtime dependence on an external reusable-workflow repository.
  • ➖ Creates a local fork requiring ongoing synchronization.
  • ➖ Duplicates maintained upstream functionality without repository-specific requirements.
2. Use the Rust hook mirror
  • ➕ Uses the native Rust implementation directly.
  • ➕ May execute faster after installation.
  • ➖ Requires pre-commit to build the hook from source.
  • ➖ Introduces Rust toolchain installation where the repository otherwise needs no Cargo tooling.

Recommendation: Keep the PR’s upstream reusable-workflow and Python-mirror approach. It avoids duplicated maintenance and unnecessary Rust installation, while the separate report and rewrite triggers prevent conflicting runs and preserve author edits after PR creation.

Files changed (6) +98 / -63

Enhancement (2) +57 / -0
prose-body-write.ymlRewrite hard-wrapped PR bodies on initial lifecycle events +31/-0

Rewrite hard-wrapped PR bodies on initial lifecycle events

• Adds a pull_request_target workflow that invokes the upstream body-unwrapping workflow on open, reopen, and ready-for-review events. It grants only pull-request write permission and avoids repeated rewrites that could overwrite later author edits.

.github/workflows/prose-body-write.yml

prose-body.ymlReport hard wrapping after PR synchronization or editing +26/-0

Report hard wrapping after PR synchronization or editing

• Adds a pull_request workflow that invokes the upstream body-check workflow after synchronization or body edits. Its event scope avoids racing with the workflow that rewrites newly opened or reopened PR bodies.

.github/workflows/prose-body.yml

Documentation (2) +19 / -63
CLAUDE.mdUnwrap squash-merge guidance prose +1/-4

Unwrap squash-merge guidance prose

• Removes manual line breaks from the squash-merge guidance while preserving its wording and Markdown structure.

CLAUDE.md

CONTRIBUTING.mdUnwrap release-process documentation +18/-59

Unwrap release-process documentation

• Removes manual prose wrapping throughout the release workflow, signing, fallback, and key-management guidance. The documented behavior remains unchanged.

CONTRIBUTING.md

Other (2) +22 / -0
bot-automerge.ymlHold prose workflow major upgrades for manual review +10/-0

Hold prose workflow major upgrades for manual review

• Adds both prose-body workflows to the GitHub Actions major-version hold list. The accompanying rationale explains that neither workflow is meaningfully exercised when a dependency-update PR first opens.

.github/workflows/bot-automerge.yml

.pre-commit-config.yamlAdd Markdown prose unwrapping before Prettier +12/-0

Add Markdown prose unwrapping before Prettier

• Adds the Python mirror of markdown-prose-hooks at v0.4.0 and runs it before Prettier. The ordering preserves normalized output without introducing a Rust toolchain or conflicting prose-wrapping rules.

.pre-commit-config.yaml

@qodo-code-review

qodo-code-review Bot commented Sep 15, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (0) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Remediation recommended

1. Fork contributors receive no prose report 🐞
Description
The report job requests pull-requests: write under the pull_request event even though
fork-originated runs receive a read-only token. When a fork contributor edits or synchronizes a pull
request whose body needs reporting, the reusable workflow cannot create its comment and the run may
fail without delivering the intended guidance.
Code

.github/workflows/prose-body.yml[R24-26]

+    permissions:
+      pull-requests: write
+    uses: michen00/markdown-prose-hooks/.github/workflows/unwrap-pr-body-check.yml@v0.4.0
Evidence
The new workflow applies pull_request to all matching pull requests without excluding forks and
explicitly states that its report comment requires write permission. The repository separately
recognizes fork heads as untrusted and distinguishes them from same-repository pull requests,
confirming that fork contributions are part of the event population but receive different trust
handling.

.github/workflows/prose-body.yml[16-26]
.github/workflows/bot-automerge.yml[57-62]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The prose-body report runs under `pull_request`, whose token cannot retain `pull-requests: write` for fork-originated pull requests. As a result, the reusable workflow cannot post its report for affected fork contributors.

## Fix Focus Areas
- .github/workflows/prose-body.yml[16-26]

## Recommended Fix
Change the report workflow to use `pull_request_target` with the same `synchronize` and `edited` activity types so the trusted default-branch workflow can post comments. Keep it free of pull-request code checkout or execution while it holds write permission.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context sources
✅ Compliance rules (platform): 26 rules
Review mode: ⚖️ Balanced: The PR adds multiple GitHub Actions workflows and a pre-commit hook, including pull_request_target write permissions and reusable workflow integrations, so it has meaningful CI and security-sensitive behavior despite the prose-only edits.

Grey Divider

Tip of the day
💡 Did you know, you can reply 'qodo' on any finding to push back, ask questions, or dig deeper

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment thread .github/workflows/prose-body.yml
@michen00
michen00 force-pushed the ci/release-pr-worthiness-gate branch from 0e6e66f to 4771153 Compare September 15, 2026 08:53
@michen00
michen00 force-pushed the chore/unwrap-markdown-prose branch from 9d3d24e to 3ae46e1 Compare September 15, 2026 08:54
Base automatically changed from ci/release-pr-worthiness-gate to main September 15, 2026 09:07
Adopts michen00/markdown-prose-hooks, which needed no porting: the
hooks ship as a pre-commit repository and the PR-body halves as
reusable workflows, so this consumes them rather than vendoring a
copy that would then drift.

Localization came to the two checks its README asks for, and both
already held. markdownlint's `line-length` is `false` here and
prettier's `proseWrap` is unset, so nothing wraps the prose back --
a rule that wraps and a hook that unwraps would each undo the other
on every run.

The `-py` mirror, not `-rs`: a `language: rust` hook builds from
source, so pre-commit would install a Rust toolchain before
checking the first commit, and nothing here needs cargo otherwise.

The two PR-body workflows take different trigger types on purpose.
Sharing an event fires both, and the report then describes a body
the rewrite is about to replace.

Both are held in bot-automerge's actions-major list, re-derived as
that file asks. prose-body-write.yml runs from the default branch
under `pull_request_target`, and prose-body.yml fires only on
`synchronize` and `edited`, so neither is exercised by the pull
request proposing its own bump.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@michen00
michen00 force-pushed the chore/unwrap-markdown-prose branch from 3ae46e1 to ae9477c Compare September 15, 2026 09:08
@codecov-commenter

codecov-commenter commented Sep 15, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 47.93%. Comparing base (a7cff17) to head (ca01cb4).

Additional details and impacted files
@@           Coverage Diff           @@
##             main      #85   +/-   ##
=======================================
  Coverage   47.93%   47.93%           
=======================================
  Files          31       31           
  Lines        1863     1863           
=======================================
  Hits          893      893           
  Misses        970      970           
Flag Coverage Δ
integration 1.05% <ø> (ø)
unit 47.26% <ø> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

A fork's token is read-only whatever the caller's `permissions:` block
asks for, so under `pull_request` the report's comment was refused for
every outside contributor -- the people least likely to know this
repository's prose convention, and the ones the report is for.

Not a failure, and the report was not lost: the reusable workflow falls
back to the job summary and does not fail the check. But a summary has
to be opened to be read, and a comment does not.

Safe here because that workflow checks out nothing -- it carries no
`actions/checkout` step at all, reading the body from the event payload
and posting a comment. The usual `pull_request_target` hazard is a
writable token handed to unreviewed code, and there is no code here for
it to attach to. Upstream accepts either trigger and names this one for
fork coverage.

The cost matches the write half: the workflow is read from the default
branch, so a pull request cannot test a change to it.

Reported by Qodo on #85.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@michen00
michen00 merged commit db4e21b into main Sep 15, 2026
12 checks passed
@michen00
michen00 deleted the chore/unwrap-markdown-prose branch September 15, 2026 09:34
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