Skip to content

fix(release): fence-aware UPGRADING extraction, absolute links on the release page, and the composer suite in ci-required - #2684

Merged
Chris0Jeky merged 5 commits into
mainfrom
issue-2250/composer-fences-and-suite
Sep 6, 2026
Merged

fix(release): fence-aware UPGRADING extraction, absolute links on the release page, and the composer suite in ci-required#2684
Chris0Jeky merged 5 commits into
mainfrom
issue-2250/composer-fences-and-suite

Conversation

@Chris0Jeky

@Chris0Jeky Chris0Jeky commented Sep 5, 2026

Copy link
Copy Markdown
Owner

Summary

Issue #2250 items 4 and 5 only. Items 1 to 3 (rehearsal preview tag, legacy-tag re-dispatch, semver
changelog base) stay open.

Item 5a, fence-aware extraction. extractUpgradingSection now tracks fenced code blocks while it
scans, so a # or ## line inside a fence is sample text rather than a heading. That applies in
both directions: a fenced block no longer truncates the section it sits in, and a ## <tag> line
inside a fence is no longer taken as the section start. Fence tracking follows CommonMark closely
enough for this document: up to three leading spaces, backtick or tilde runs of three or more, a
close only on the same character at least as long as the opener with nothing but whitespace after
it, and no opening backtick fence whose info string contains a backtick. An unterminated fence FAILS
CLOSED (round 1 review fix): the section would otherwise run to the end of the document and publish
every older version's notes under ## Breaking changes, and the body-length guard cannot catch that
on a ~20 KB document. extractUpgradingSection throws MalformedUpgradingSectionError, which
composeReleaseNotes turns into an ::error for BOTH tag classes — missing is recoverable by a
pointer, silently wrong is not. The policy is recorded in the header comment and on the function.

Item 5b, links on the release page. New pure function rewriteRelativeLinks(markdown, { repo, tag })
makes relative Markdown destinations absolute. A bare #anchor resolves against
blob/<tag>/UPGRADING.md#anchor; a relative path resolves against blob/<tag>/<path> with ./
stripped and any #fragment kept. It uses the same encodeURIComponent(tag) convention as
upgradingUrl, and leaves path segments exactly as written. Untouched: any scheme-bearing
destination (https:, mailto:, and the rest), root-relative /path, fenced code blocks, and
inline code spans. Reference-style definitions ([id]: dest) are handled and tested. Image
destinations share the ]( shape and get the same rewrite; a relative image is broken on a release
page either way, and UPGRADING.md has none today, so that is noted in a comment rather than
special-cased. Round 1 review fixes: CommonMark angle-bracket destinations (](<docs/x.md>)) are
unwrapped, rewritten and re-wrapped instead of being concatenated onto the blob base verbatim; and the
reference-definition match now requires end-of-line or a CommonMark title after the destination, so
prose shaped [Note]: see the guide … keeps its first word and still has its inline links rewritten. The rewrite is applied to the UPGRADING section only. The curated notes file has no
relative-link shapes today (checked docs/releases/notes/), and the generated changelog is already
absolute, so neither is rewritten.

MAX_RELEASE_BODY_LENGTH behaviour is unchanged: the guard still runs on the final composed body
after the rewrite, and a test pins that it still fires when the rewrite has made the body longer.

Item 4, wiring. One step, Validate release notes composer, added immediately after
Validate release cache trust contract in the release-workflow-contract job of
.github/workflows/ci-required.yml; the file's topology comment now names all three contract suites
that job runs (round 1 review fix). Nothing pins that job's step list: grepping scripts/ci and
scripts/ci/smart-ci for release-cache-contract, release-desktop-dispatch and the job name
turns up no step-list assertion, and the only ci-required.yml references in the Smart CI tests are
glob and control-path fixtures in plan.test.mjs. ci/policy.v1.json already routes scripts/ci/**
to the control-path tier, so policy is left alone.

Root cause

Both defects come from treating the lifted Markdown as if it were still a file in the tree.

The extractor scanned line by line with /^#{1,2} +/ and no state, so any fenced sample line
starting with # looked exactly like a heading. In the shipped UPGRADING.md no fenced block
currently starts a line with #, so no section is truncated today; the failure is latent and would
land the first time a powershell or bash sample carries a # comment, which is the ordinary way
to write one.

The link defect is live now. The section is rendered into a GitHub Release body, which is not a file,
so a destination that resolves against UPGRADING.md in the repository resolves against nothing on
the page. Composing the real v0.3.0-rc.1 section against the base commit produces three dead bare
anchors.

Verification

All commands run in the worktree at .worktrees/codex-2250-composer-fences-and-suite, Node 24.13.1.

Red first. The new tests were added before the implementation, with only an identity stub for the
missing export so the module would load:

node --test scripts/ci/compose-release-notes.test.mjs
tests 49, pass 34, fail 15. The fifteen failures were:

  • a fenced block inside the section is kept whole, hash lines and all
  • a fence closed before the next heading restores heading detection
  • a tag heading that only appears inside a fence is not a section start
  • an unterminated fence runs to the end of the document
  • a closing fence must match the opening character and length
  • a fence indented up to three spaces still opens a block
  • a bare anchor resolves against UPGRADING.md at the tag
  • a relative path becomes a blob URL at the tag, with and without a ./ prefix
  • a fragment on a relative path is preserved
  • a link inside a fenced code block is left untouched
  • a link inside an inline code span is left untouched
  • a reference-style link definition gets the same treatment
  • the tag is percent-encoded exactly as the UPGRADING fallback link encodes it
  • the composed page carries absolute links for the UPGRADING section
  • the release-body length guard still fires after links are rewritten

Two representative failing assertions:

a fenced block inside the section is kept whole, hash lines and all
  AssertionError [ERR_ASSERTION]: Run the migration by hand:

  ```bash
a bare anchor resolves against UPGRADING.md at the tag
  AssertionError [ERR_ASSERTION]: Expected values to be strictly equal:
  + actual - expected
  + 'restore the [snapshot](#automatic-pre-migration-backups) first'
  - 'restore the [snapshot](https://github.com/Chris0Jeky/Taskdeck/blob/v0.3.0-rc.1/UPGRADING.md#automatic-pre-migration-backups) first'

Green after the implementation:

  • node --test scripts/ci/compose-release-notes.test.mjs — tests 49, pass 49, fail 0 (33 before this PR, 16 added)
  • node --test scripts/ci/release-desktop-dispatch.test.mjs — tests 76, pass 76, fail 0
  • node --test scripts/ci/release-cache-contract.test.mjs — tests 28, pass 28, fail 0
  • node --test scripts/ci/smart-ci/*.test.mjs — tests 91, pass 91, fail 0
  • node scripts/check-github-ops-governance.mjs — exit 0, "GitHub operations governance check passed."
  • git diff --check 0b3787d81...HEAD — no output, exit 0

Dry run over the real UPGRADING.md. parseArgs requires --tag, --repo, --asset and --out;
the checksum is optional as a flag but a missing digest is a hard error even for an RC, so a fake
sha256sum line (64 a characters plus the asset name) was written to the scratch directory and
passed as --checksum-file. --generated-notes was omitted, which produces the expected placeholder
warning.

node scripts/ci/compose-release-notes.mjs --tag v0.3.0-rc.1 --prerelease true \
  --repo Chris0Jeky/Taskdeck --asset taskdeck-v0.3.0-rc.1-win-x64.zip \
  --checksum-file <scratch>/fake.sha256 --upgrading UPGRADING.md \
  --notes docs/releases/notes/v0.3.0-rc.1.md --out <scratch>/dryrun-body.md

Exit 0, 8933 bytes written, one ::warning:: about absent generated notes. Grepping the output:

  • grep -c "](#" — 0
  • grep -c "blob/v0.3.0-rc.1/" — 4

The four are two occurrences of blob/v0.3.0-rc.1/UPGRADING.md#automatic-pre-migration-backups, one
of blob/v0.3.0-rc.1/UPGRADING.md#general-upgrade-procedure, and the pre-existing
blob/v0.3.0-rc.1/docs/releases/WINDOWS_QUICK_START.md from the download block.

Before and after. The same command run against the base commit's copy of the composer
(git show 0b3787d81:scripts/ci/compose-release-notes.mjs) writes 8729 bytes and
grep -c "](#" returns 3. Those three are the dead anchors this PR removes.

Not verified

  • The hosted ci-required run. This is a CI-control change and R4 says the proving check is the
    hosted run on the exact PR head; the local runs above are additive only. The new step has not
    executed on a runner yet.
  • No backend, frontend or E2E check was run. Nothing under backend/, frontend/ or docs/ is
    touched by this diff.
  • No real release was published or re-dispatched. The dry run renders a body from the real
    UPGRADING.md but uses a fabricated checksum and no generated-notes JSON, so it is not a rehearsal
    of release-desktop.yml.
  • Image destinations under the rewrite are documented in a comment but have no test, because
    UPGRADING.md contains no images.
  • Angle-bracket link destinations are handled and tested for the no-space form (](<docs/x.md>)).
    The form CONTAINING SPACES (](<path with spaces>)) is still not handled and not tested: the
    inline-link pattern only matches whitespace-free destinations, so such a link is left as written.
  • The fence tracker is a close reading of CommonMark, not a conformance implementation. Indented
    code blocks (four-space) are not treated as code, and neither are HTML blocks.

Risk notes

  • The rewrite only runs on the UPGRADING section of a composed release body. It cannot affect the
    repository files themselves, the curated notes, or the generated changelog.
  • The extractor change widens what a section can contain (fenced content that used to cut it short),
    so a composed body can get longer. The MAX_RELEASE_BODY_LENGTH guard is unchanged and still runs
    on the final body; a test pins that it fires when a rewritten section overflows.
  • An unterminated fence in UPGRADING.md fails the compose (exit 1, ::error, nothing written to
    --out) rather than being published. Residual, measured while building the round 1 dry run and NOT
    fixed here: the guard fires only when the fence is open at END OF DOCUMENT. An unterminated backtick
    fence that a LATER fence in the document closes still swallows content silently (a scratch copy
    composed 12801 bytes instead of 8933, with ## v0.2.0 content under ## Breaking changes, exit 0).
    Detecting that needs a policy call about the correct parse of an odd-fence-count document, which is
    a separate decision from this PR.
  • The new ci-required step is one node --test invocation in an existing job that already has
    Node set up and only contents: read. It adds no permissions, no network access and no new job.

Round 1's review fixes are applied (five findings: one MEDIUM fail-closed policy, one MEDIUM doc gap,
two LOW link-rewriting shapes, one comment-only CI topology line) — see the Fix round comment for
the red-first counts and both dry runs. Two rounds is the ceiling; no further review round is owed
unless the fixes introduced a new CRITICAL.

Refs #2250 (items 4 and 5; items 1 to 3 stay open). Refs #2248, #2234, #1308. Control-plane change: parks under SC-10 for the maintainer's review after the fresh-context review.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

…ten link rewriting

An unterminated fence made extractUpgradingSection run to the end of the document, publishing every older version's notes under '## Breaking changes'; the body-length guard could not catch it because UPGRADING.md is ~20 KB. It now throws MalformedUpgradingSectionError, which composeReleaseNotes turns into an error for BOTH tag classes.

Also: angle-bracketed destinations are unwrapped, rewritten and re-wrapped instead of being concatenated onto the blob base verbatim; the reference-definition match now requires end-of-line or a CommonMark title, so prose shaped '[Note]: see ...' is left alone and its inline links are still processed.
@Chris0Jeky

Copy link
Copy Markdown
Owner Author

Fix round

Round 1's fix, on 6150eb1a5 (two commits: a6ad4aff9 code + tests, 6150eb1a5 docs + workflow comment).
Two rounds is the ceiling; after this the PR parks under SC-10 for the maintainer.

# Finding Disposition
1 MEDIUM — unterminated fence "runs to the end of the document" silently publishes older versions' notes; MAX_RELEASE_BODY_LENGTH cannot fire on a ~20 KB document fixed
2 MEDIUM — the canonical "Release page layout" item 2 does not describe the link rewrite or the fail-closed fence fixed
3 LOW — angle-bracketed destinations ](<docs/x.md>) concatenated onto the blob base verbatim fixed (handled, not left untouched)
4 LOW — REFERENCE_DEFINITION false positives on prose shaped [Word]: text … fixed
5 LOW — ci-required.yml topology comment still calls release-workflow-contract the dispatch hardening gate fixed

1 — fail closed on an unterminated fence

extractUpgradingSection now throws the exported MalformedUpgradingSectionError when the section's
fence is still open at the end of the document. composeReleaseNotes catches it and pushes
compose-release-notes: unterminated fenced code block in the UPGRADING section for <tag> — close the fence in UPGRADING.md before tagging onto errors, which main writes as the existing ::error::
line and turns into exit 1 with nothing written to --out. It is an error for both tag classes
missing is recoverable by a pointer, silently wrong is not — and the header comment's missing-source
policy block now says so, as does the function's own comment.

Tests: the old an unterminated fence runs to the end of the document is replaced by an unterminated fence in the section is a malformed document, not a longer section (pins the error type, the tag in
the message, and the phrase); plus a malformed UPGRADING section is an ERROR for an RC as well as a stable tag (composeReleaseNotes, both classes) and the CLI end-to-end case the CLI fails closed on a malformed UPGRADING section, naming it on the ::error line (exit non-zero, ::error::, the phrase,
the tag, and --out never written).

3 — angle-bracketed destinations

rewriteDestination unwraps <…>, rewrites the inner destination through the same rules, and
re-wraps. [x](<docs/x.md>)[x](<…/blob/<tag>/docs/x.md>); <#anchor> resolves against
UPGRADING.md; <https://…> is still untouched; reference definitions get the same treatment.
Test: an angle-bracketed destination is rewritten inside its brackets. Angle-bracket destinations
containing spaces remain out of reach of INLINE_LINK's [^()\s]+ and are still untouched.

4 — reference-definition shape

REFERENCE_DEFINITION now requires the destination to be followed by end-of-line or a CommonMark
title ("…", '…', (…)) and nothing else. A prose line falls through to the inline-link pass, so
its real links are still rewritten. Test: prose shaped like a reference definition is left alone, but its inline links are not[Note]: see [the guide](docs/x.md) before upgrading previously became
[Note]: <blob-url>/see [the guide](docs/x.md) …; all three title forms are pinned as still-rewriting.

Verification

All commands in the worktree at .worktrees/codex-2250-composer-fences-and-suite, Node 24.13.1.

Red first — the five new/changed tests added before the implementation, with only the error class
exported so the module would load:

node --test scripts/ci/compose-release-notes.test.mjs — tests 53, pass 48, fail 5:

✖ an unterminated fence in the section is a malformed document, not a longer section
✖ a malformed UPGRADING section is an ERROR for an RC as well as a stable tag
✖ the CLI fails closed on a malformed UPGRADING section, naming it on the ::error line
✖ an angle-bracketed destination is rewritten inside its brackets
✖ prose shaped like a reference definition is left alone, but its inline links are not

Two representative failing assertions:

+ actual - expected
+ '[x](https://github.com/Chris0Jeky/Taskdeck/blob/v0.3.0-rc.1/<docs/x.md>)'
- '[x](<https://github.com/Chris0Jeky/Taskdeck/blob/v0.3.0-rc.1/docs/x.md>)'

+ '[Note]: https://github.com/Chris0Jeky/Taskdeck/blob/v0.3.0-rc.1/see [the guide](docs/x.md) before upgrading'
- '[Note]: see [the guide](https://github.com/Chris0Jeky/Taskdeck/blob/v0.3.0-rc.1/docs/x.md) before upgrading'

Green after the implementation:

  • node --test scripts/ci/compose-release-notes.test.mjs — tests 53, pass 53, fail 0 (49 before this round, 4 added, 1 replaced)
  • node --test scripts/ci/release-desktop-dispatch.test.mjs — tests 76, pass 76, fail 0
  • node --test scripts/ci/release-cache-contract.test.mjs — tests 28, pass 28, fail 0
  • node --test scripts/ci/smart-ci/*.test.mjs — tests 91, pass 91, fail 0
  • node scripts/check-github-ops-governance.mjs — exit 0, "GitHub operations governance check passed."
  • node scripts/check-docs-governance.mjs — exit 0, "Docs governance check passed."
  • git diff --check — no output, exit 0

Dry run over the real UPGRADING.md (same invocation as the PR body records, fabricated sha256sum
line in a scratch directory, --generated-notes omitted): exit 0, 8933 bytes, one ::warning::
about absent generated notes, grep -c "](#"0, grep -c "blob/v0.3.0-rc.1/"4. Byte
count and both greps are unchanged from the pre-fix-round run, so the tightened reference-definition
match and the angle-bracket handling change nothing on the real document.

Second dry run over a scratch copy of UPGRADING.md with ~~~bash + one line inserted after line 170,
inside the ## v0.3.0-rc.1 section and never closed:

::error::compose-release-notes: unterminated fenced code block in the UPGRADING section for v0.3.0-rc.1 — close the fence in UPGRADING.md before tagging
exit=1
ls: cannot access '.../dryrun-broken.md': No such file or directory

Non-zero exit, the ::error:: line, and no body written.

Residual, not fixed (outside the finding, flagging rather than expanding scope)

The guard fires when the section's fence is open at end of document. While building the second dry
run I first inserted an unterminated ```bash fence at the same place: UPGRADING.md has six
backtick fence lines below it, so the next one closed my fence, the guard did not fire, and the
composer wrote a 12801-byte body (vs 8933) that had swallowed ## v0.2.0 content into
## Breaking changes — exit 0. That is the same silently-wrong outcome for a document whose backtick
fences are unbalanced somewhere below the section. Detecting it needs a policy call about what the
correct parse of an odd-fence-count document is, which is a separate decision from this finding; happy
to open it as a tracked issue rather than widen this PR.

Not verified

  • The hosted ci-required run on this head. R4 says the proving check is the hosted run; the local
    runs above are additive.
  • No backend, frontend or E2E check ran — nothing under backend/ or frontend/ is touched.
  • No real release was published or re-dispatched; both dry runs use a fabricated checksum and no
    generated-notes JSON.
  • Angle-bracket destinations containing spaces, images under the rewrite, and CommonMark indented/HTML
    code blocks remain untested, as recorded in the PR body.

@Chris0Jeky

Copy link
Copy Markdown
Owner Author

Review disposition (beta-platform-integrity lane): parked under SC-10

This PR changes scripts/ci/** and .github/workflows/ci-required.yml, so it is control-plane (T2) under the ADR-0066 amendment: it merges only after the maintainer's own review plus the fresh-context review below. It is left open, ready for review, not merged by the lane.

Round 1 (fresh-context, read-only reviewer at 9b17d4e): SHIP, no CRITICAL or HIGH; two MEDIUM, three LOW. Confirmed: the fence change is behaviour-identical on the shipped UPGRADING.md (all fences balanced, no # at column 0 inside them), the link rewrite fixes three genuinely dead anchors, tilde and backtick fences cannot cross-close, the tag is encoded exactly as the existing UPGRADING fallback link encodes it, and the workflow edit is one step in an existing job with no permission, trigger, job-name or needs change.
Round 2 (scoped re-review at 6150eb1): SHIP, all five findings closed, two LOW residuals. Two rounds is the ceiling.

Fixed in the fix round (a6ad4af, 6150eb1)

  • MEDIUM, an unterminated fence used to run to the end of the document and publish every older version's notes under "Breaking changes" with no guard that could fire at the real document size: the extractor now throws a typed MalformedUpgradingSectionError when the section's fence is still open at end of document, composeReleaseNotes turns it into the existing ::error path with exit 1 and nothing written, for both tag classes (silently wrong is worse than missing); unit, both-classes and CLI end-to-end tests pin it; a dry run over a broken copy of UPGRADING.md exits 1 with the named error and no output file.
  • MEDIUM, the canonical release-page layout doc (docs/ops/RELEASE_TRUST_AND_DISTRIBUTION.md, item 2) now describes the link rewrite, what stays as written, and the fail-closed behaviour.
  • LOW, angle-bracketed destinations are unwrapped, rewritten and re-wrapped, with a test.
  • LOW, reference-definition matching now requires CommonMark's shape (destination then end of line or a valid title), so prose shaped [Word]: text is left alone and its inline links still rewrite, with a test.
  • LOW, the workflow's topology comment names the three contract suites the job runs.

Residuals, recorded for the maintainer's review and on #2250 (no third round)

  • Odd fence count below the section (found by the fix worker while building the dry run): a fence left open inside the tag's section but closed by an unrelated fence further down the document is not caught by the end-of-document guard; the composer then swallows the following version's section into "Breaking changes" and exits 0. Catching it needs a policy call on how to parse an odd-fence-count document. A cheap heuristic for the follow-up: treat a version heading (## v followed by a digit) that appears inside a fence within the lifted section as malformed, since UPGRADING.md never has one legitimately.
  • An unterminated fence opened before the tag's heading swallows the heading during the first scan, so the section reads as missing (error for a stable tag, warning plus pointer for a release candidate) rather than malformed; the layout doc's sentence is slightly broader than the code on this point.
  • Fences indented four or more spaces inside list items are not recognised, so a link inside such a sample would be rewritten despite the "fenced code blocks untouched" line.
  • Angle-bracketed destinations containing spaces are not matched by the inline-link pattern.

Verification at 6150eb1 (local; the hosted run on the exact head is the R4 proving check)

compose-release-notes.test.mjs red first (15 failing on the old logic, then 5 for the fix round) and green at 53/53; dispatch-hardening 76/76, cache-trust 28/28 and Smart CI 91/91 unchanged; GitHub-operations and docs governance green; git diff --check clean. Dry run over the real UPGRADING.md for v0.3.0-rc.1: exit 0, 8933 bytes, zero ](# left, four blob/v0.3.0-rc.1/ links (three rewritten anchors plus the existing quick-start link), byte-identical before and after the fix round. No smart-ci contract test pins this job's step list; ci/policy.v1.json already routes scripts/ci/** to the control-path tier and is untouched. Not exercised: a real release publish or re-dispatch; both dry runs use a fabricated checksum and no generated-notes JSON.

Chris0Jeky added a commit that referenced this pull request Sep 5, 2026
Chris0Jeky added a commit that referenced this pull request Sep 5, 2026
…D-14's defaults; note D-10's superseded audit description
Chris0Jeky added a commit that referenced this pull request Sep 5, 2026
…026-09-05

docs(outstanding): D-10 lane ruling, #2608 as #2378's disposition, #2684 under SC-10, D-14 mirror questions
Chris0Jeky added a commit that referenced this pull request Sep 5, 2026
Readiness: the human-gate table was not unchanged at 15:15Z, its SC-10 row now counts twelve
(#2684 and #2687 named); the cancelled 6cf9ef7 run had 15 green, Secret Scan skipped, one
cancelled; #2691's seeding time and its 03:26Z red are exact; the dropped clause-4 evidence
(cancelled-run range, run ids, timestamps) is restored; #2378 and #2588 occurrence lists gain the
sweep's sightings. STATUS: same job tally; range note that the sweep's last merge is one past the
block's declared bound. OUTSTANDING_TASKS.md: the rule-3 Changelog entry for the D-12 tick and the
section K exception sentence.
@Chris0Jeky

Copy link
Copy Markdown
Owner Author

Fresh-context re-review against the moved base (coordinator, 2026-09-06; SC-10 delegated, q-1 = A; read-only Opus 5 reviewer over the diff of the updated head against current main).

VERDICT: SHIP. main had touched ci-required.yml since the two SHIP rounds (#2502 pins, #2610 cancel-in-progress, #2608 header). Verified: the ci-required.yml hunk appends only the suite step as the last step of release-workflow-contract and contains no uses:, concurrency or header line; no test pins that job's step list; the fail-closed fence logic can only exit at EOF with an open fence, which composeReleaseNotes turns into an error for both tag classes, and compose-notes is a needs: of create-release, so nothing publishes; link rewriting is scheme-checked and applied once to the UPGRADING section only. LOWs recorded for #2250: the release-desktop.yml:678-680 step comment still says an RC "warns and falls back" (an RC now also fails on a malformed fence); the ops doc's "fails for an RC as well" holds only when the fence opens at or after the tag heading (an earlier open fence makes the section scan return null and the RC publishes the fallback pointer, still safe); the body's "49 tests" is the round-0 count (53 now); compose-release-notes.test.mjs:491 reads the real UPGRADING.md, so a future unterminated fence there reds every PR (intended fail-closed, flagged for awareness); a reference-definition line with inline code would get its backticked token prefixed (none exists). Merge follows the hosted run at 1b8109aa6.

@Chris0Jeky
Chris0Jeky merged commit b34df45 into main Sep 6, 2026
35 checks passed
@Chris0Jeky
Chris0Jeky deleted the issue-2250/composer-fences-and-suite branch September 6, 2026 13:30
@github-project-automation github-project-automation Bot moved this from Pending to Done in Taskdeck Execution Sep 6, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant