You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Add a CSS-organization discipline to stardust:prototype that classifies
every CSS rule in a rendered prototype into one of four scopes — global, section, block, or default-content — with
mandatory marker comments and a brief/craft-time validator. The discipline
makes downstream block extraction (the planned future EDS converter
referenced in data-attributes.md § Why and artifact-map.md) mechanical instead of LLM-driven, at no cost to the existing review /
iteration / audit loop.
Authored against stardust v0.7.1 (per plugins/stardust/tile.json
on 2026-05-22). Future agent picking this up: verify the current
version and that the file paths / Discipline-numbering referenced
below still match HEAD before drafting the PR.
Background
Stardust v0.7.1 produces a self-contained styled HTML file per page
at stardust/prototypes/<slug>-proposed.html and a deployable static
bundle at stardust/migrated/. The README + master SKILL.md + migrate/SKILL.md are explicit that EDS / CMS / framework output is out of scope for stardust itself; a separate downstream plugin is
expected to convert the static HTML into EDS blocks (and similar for
other targets).
The handoff contract is already partially designed:
:root token contract — stable cross-tool vocabulary per skills/stardust/reference/token-contract.md.
canon.css — site-wide compound visual language (.btn-primary, .card, .link, form inputs) lifted from approved prototypes per skills/prototype/reference/canon-extraction.md § 2.
The DOM and data attributes are already EDS-shaped. The page CSS
inside the prototype's <style> block is not.
Problem
Today the proposed file's <style> block has:
:root { ... } at the top (required, well-defined).
canon.css injected as the second block at migrate time (well-defined).
Page-specific styles after that — unstructured. Any class name
(.headline, .featured-coffee, .hero-grid), any descendant
selector, any media query is acceptable as long as the source-order
rule for @media (per skills/prototype/reference/mobile-nav-collapse.md § Source order)
and the cascade requirements are honored.
token-contract.md § Per-section overrides documents section[data-section="X"] { ... } as one optional pattern, not
as the required organization model.
A downstream converter that wants to extract block CSS faces a
classification problem per rule: "does .headline belong to the
hero block, the features block, the catalogue block, or none of them?"
The answer requires LLM judgment over the DOM. The extraction is not
mechanical, not deterministic, and not cheap to build.
Proposal
Every CSS rule emitted by stardust (in the proposed file's <style>
block and in canon.css) is classifiable into exactly one of four
scopes, grouped contiguously, preceded by a marker comment:
The four scopes
Scope
Selector pattern
Purpose
EDS handoff target
Global
:root, html, body, main, and class selectors with no [data-section] / [data-module] ancestor (e.g. .btn-primary from canon.css)
Tokens, base resets, site-wide compound utility
styles/styles.css
Section
Selector starts with section[data-section="<name>"] (or [data-section="<name>"])
Per-section context — typically token redefinition; cascades to default-content within
styles/styles.css (scoped via section class)
Block
Selector starts with [data-module="<name>"] (or [data-template="<name>"] for page-template scope)
Self-contained component styling
blocks/<name>/<name>.css
Default-content
Element selectors descending directly from a section (section > h1, section > p, section[data-section="hero"] > p) — does not match deep-descended elements inside modules
Default styling for un-blocked prose inside sections
styles/styles.css (default-content rules)
Inheritance via cascade
Default-content inherits both global and section context for free
via the normal CSS cascade:
The hero's h1 picks up the lifted size automatically because var(--heading-xxl) resolves against the section's redefinition.
The direct-child combinator (>) in default-content selectors is
load-bearing. It isolates default-content rules from block internals: section > h1 does NOT match an h1 deep-descended inside a [data-module]. Block authors are free to style their own headings
inside the module subtree without colliding with defaults.
No engineered inheritance machinery is required. The cascade does the
work.
Section- or block-specific media queries live inside their owning
group (per mobile-nav-collapse.md § Source order — same-specificity
later-wins rule still applies).
Discipline 11 — Four-scope CSS organization
Lands in skills/prototype/SKILL.md § Craft-time disciplines
(currently disciplines 6, 7, 8 exist per PR #131; this would be the
next addition, numbered per the current spec at PR time).
Validator behavior (brief/craft-time, pre-write)
After craft returns the rendered file, the validator parses every
rule selector in the <style> block. Each selector classifies as one of:
global — selector matches:
:root declaration, OR
element selectors against html / body / main, OR
a class selector not preceded by any attribute selector that
scopes to [data-section] or [data-module]. canon.css
classes (.btn-primary, .card, etc.) land here.
section — selector starts with section[data-section="<name>"] (or [data-section="<name>"]).
Descendants of the section selector still classify as section.
block — selector starts with [data-module="<name>"] OR [data-template="<name>"] (page-template scope).
Descendants of the module selector classify as block.
default-content — selector pattern section[> direct child] or section[data-section="X"][> direct child]
where the direct child is one of the default-content HTML elements
(h1–h6, p, ul, ol, li, img, picture, figure, figcaption, a, blockquote, hr).
Unclassifiable — refuse the write.
Marker comments
Mandatory and machine-parseable. Format:
/* === <SCOPE>: <name|kind> === */
Where:
<SCOPE> is GLOBAL / SECTION / BLOCK / MEDIA.
For GLOBAL: <name> is one of tokens / resets / default-content / compound utility (the canon.css block).
For SECTION: <name> matches the data-section attribute value.
For BLOCK: <name> matches the data-module or data-template
attribute value.
For MEDIA: <kind> describes the breakpoint, e.g. ≤ 640px.
The validator regex over the <style> text is straightforward:
When a rule cannot be classified, refuse the write with a structured
message naming the offending selector and the suggested remediation:
unscoped CSS rule: `.headline { ... }` at line 142
A page-specific rule must be one of:
- canon.css class (lifted because used across templates), OR
- section-scoped (`section[data-section="X"] .headline`), OR
- block-scoped (`[data-module="Y"] .headline`), OR
- default-content (`section > h1` if it's a default heading style).
Pick one and re-render. See
skills/prototype/reference/proposed-file-shell.md § Four-scope CSS
skills/stardust/reference/data-attributes.md § Why
Block rules without a matching data-module/data-template value in
the DOM also refuse (typo / dead-block detection).
Site-global escape hatch
Some rules legitimately span scopes (e.g. section { padding: var(--section-padding); }
applies to every section). Those live in GLOBAL: resets or a
dedicated GLOBAL: section-defaults group. The escape hatch keeps
the discipline from forcing absurd duplication.
Add a paragraph naming data-module as the block primitive for downstream EDS extraction, and section > <element> selectors as the default-content selector pattern. Add a cross-link to Discipline 11.
Add one bullet: "The migrated page's first <style> block follows the four-scope organization per Discipline 11. Downstream converters can rely on the marker comments to extract block CSS mechanically."
New eval directory with task.md + criteria.json per the format in evals/README.md. Positive fixture: prototype that classifies cleanly. Negative fixture: prototype with unscoped .headline rule that the validator must refuse.
Reference fixture mirroring mobile-nav-collapse-example.html's role — shows the canonical organization on a realistic page.
Spec-only changes; no JS code is added. The validator is implemented
by the agent at craft time reading the discipline (same pattern as
Disciplines 6 / 7 / 8 today — those are also agent-implemented, not
code).
Downstream extraction algorithm (for the future EDS converter)
With the discipline in place, the converter is mechanical:
No LLM judgment required per rule. ~200 lines of script with regex +
file split + path mapping.
Alternatives considered
Two-scope (global + section only)
Rejected. A converter still has to LLM-judge which rules within a
section belong to a block vs to default content. Block extraction
stays AI-driven. The four-scope version separates blocks from
sections explicitly via [data-module] and is strictly more useful
downstream at the same authoring cost.
CSS-in-JS / Shadow DOM / @scope
Rejected. Stardust's iteration loop depends on a single
self-contained styled HTML file viewable via file://. Shadow DOM
breaks file:// rendering. CSS-in-JS adds runtime. @scope has
limited browser support and adds parser complexity without solving
the "which block does .headline belong to" question.
Apply discipline retroactively to existing prototypes
Rejected. Existing approved prototypes (e.g. lovesac, wasatch
references in the spec) that pass current critique+audit+adapt should
not be invalidated. New craft output applies the discipline; old
output remains valid. Phrasing in the spec: "Discipline 11 fires at
craft time; existing prototypes are grandfathered until re-craft."
Acceptance criteria
proposed-file-shell.md § Required structure includes item 8
describing the four-scope CSS organization with the marker comment
format.
prototype/SKILL.md § Craft-time disciplines includes Discipline
11 with the validator behavior, classification rules, and refusal
messages.
data-attributes.md cross-references Discipline 11 and names data-module as the block primitive.
token-contract.md § Per-section overrides is re-framed as the
canonical section-scope pattern (not just one optional option).
canon-extraction.md § 2 names the canon.css output as the
"GLOBAL: compound utility" group.
migrate-output-format.md documents the four-scope organization
as a property downstream converters can rely on.
(Optional) An eval under evals/css-four-scopes/ that exercises
both positive and negative fixtures.
npm run validate passes.
The Tessl Skill Review scores ≥ 50% (CONTRIBUTING.md gate).
Out of scope
Implementing the future EDS converter. This issue specifies only
the upstream contract.
Refactoring existing approved prototypes to the new discipline.
Adding new data-* attributes. The proposal uses the existing
v2.1 vocabulary verbatim.
Changing the :root token contract. The :root block is unchanged
in shape and lives in the GLOBAL: tokens group by definition.
Open questions for the maintainer
Discipline numbering. Is "Discipline 11" still the next free
slot at HEAD, or did another PR land disciplines 11+ in the
interim? Confirm before drafting the PR.
data-template block scope. Should page-template-level CSS
([data-template="article"] { ... }) classify as a BLOCK (per
this proposal) or as a separate TEMPLATE scope? Current
proposal folds them together because both extract 1:1 to a
downstream block file; a maintainer may prefer the split.
canon.css authoring vs the discipline. canon.css is currently
"compound CSS" per canon-extraction.md § 2. The discipline
labels its contents as GLOBAL: compound utility. Is the rename
merely cosmetic, or does the maintainer want canon.css to also
carry section / block markers for its own contents (e.g. if a
canon module's CSS lives in canon.css)?
Eval scope. Is a single positive+negative eval sufficient, or
does the maintainer want per-scope coverage (one eval per scope
type) in line with the granular eval pattern in evals/?
Retroactivity exception for --publish-sample. The --publish-sample flow (per skills/prototype/reference/publish-sample.md) submits a
prototype to the showcase. Should published-sample prototypes
already in the showcase be back-filled to the new organization,
or only new submissions?
For future agents picking this up
If you are an agent (Claude or otherwise) opening this issue in a
later session, here's the minimum you need to do before drafting
the PR:
Re-read this issue end-to-end. It's self-contained — every
architectural reason and design decision is here.
Read plugins/stardust/skills/prototype/SKILL.md § Craft-time
disciplines to confirm the next free discipline number.
Read plugins/stardust/skills/prototype/reference/proposed-file-shell.md
to confirm the "Required structure" section still has the
numbered list shape this proposal extends.
Verify plugins/stardust/skills/stardust/reference/data-attributes.md
still uses data-module as the block primitive (not renamed).
Check plugins/stardust/tile.json for current version (this
issue was authored against v0.7.1; if >= v0.8.0 at PR
time, expect refactors that may shift the file shape).
Confirm no overlapping PR is open. Search: gh pr list --repo adobe/skills --search "css scope OR section-scoped OR block-scoped OR Discipline 11".
Branch off main with conventional name. git checkout -b feat/stardust-four-scope-css.
Make edits per the Implementation plan table above. File-by-file.
Commit using conventional commits format:
feat(stardust): four-scope CSS organization for mechanical block extraction
<body explaining what changed and why; reference this issue>
Refs #<this-issue-number>
Open PR with --label ai-generated per CONTRIBUTING.md if
the PR is AI-drafted. Reference this issue in the body. PR title
should mirror the commit subject. Drop a note in
#agentskills Slack per CONTRIBUTING.md.
Verify gates.npm run validate locally; Tessl Skill Review
runs on CI. Tessl Evals require an empty eval:-prefixed commit
and aren't available to fork PRs (CONTRIBUTING.md § Requesting Evals).
Honest signal. If the maintainer's response is "we already
have a different plan for this," abandon this PR gracefully and
close the issue with a note pointing at their plan.
Drafted with assistance from Claude (Anthropic) on 2026-05-22.
Per CONTRIBUTING.md, the corresponding PR should carry the ai-generated label.
Summary
Add a CSS-organization discipline to
stardust:prototypethat classifiesevery CSS rule in a rendered prototype into one of four scopes —
global, section, block, or default-content — with
mandatory marker comments and a brief/craft-time validator. The discipline
makes downstream block extraction (the planned future EDS converter
referenced in
data-attributes.md§ Why andartifact-map.md)mechanical instead of LLM-driven, at no cost to the existing review /
iteration / audit loop.
Authored against stardust v0.7.1 (per
plugins/stardust/tile.jsonon 2026-05-22). Future agent picking this up: verify the current
version and that the file paths / Discipline-numbering referenced
below still match
HEADbefore drafting the PR.Background
Stardust v0.7.1 produces a self-contained styled HTML file per page
at
stardust/prototypes/<slug>-proposed.htmland a deployable staticbundle at
stardust/migrated/. The README + master SKILL.md +migrate/SKILL.mdare explicit that EDS / CMS / framework output isout of scope for stardust itself; a separate downstream plugin is
expected to convert the static HTML into EDS blocks (and similar for
other targets).
The handoff contract is already partially designed:
data-attributevocabulary —data-template,data-module,data-slot,data-canon,data-deviation,data-bespoke,data-section,data-fragment,data-broken-link,data-nav-collapse(v2 / v2.1 / v2.2 sets perskills/stardust/reference/data-attributes.md§ Versioning).:roottoken contract — stable cross-tool vocabulary perskills/stardust/reference/token-contract.md..btn-primary,.card,.link, form inputs) lifted from approved prototypes perskills/prototype/reference/canon-extraction.md§ 2._meta.jsonsidecars +state.json.migrateblock —machine-readable per-page handoff per
skills/stardust/reference/migrate-output-format.md.The DOM and data attributes are already EDS-shaped. The page CSS
inside the prototype's
<style>block is not.Problem
Today the proposed file's
<style>block has::root { ... }at the top (required, well-defined).(
.headline,.featured-coffee,.hero-grid), any descendantselector, any media query is acceptable as long as the source-order
rule for
@media(perskills/prototype/reference/mobile-nav-collapse.md§ Source order)and the cascade requirements are honored.
token-contract.md§ Per-section overrides documentssection[data-section="X"] { ... }as one optional pattern, notas the required organization model.
A downstream converter that wants to extract block CSS faces a
classification problem per rule: "does
.headlinebelong to thehero block, the features block, the catalogue block, or none of them?"
The answer requires LLM judgment over the DOM. The extraction is not
mechanical, not deterministic, and not cheap to build.
Proposal
Every CSS rule emitted by stardust (in the proposed file's
<style>block and in
canon.css) is classifiable into exactly one of fourscopes, grouped contiguously, preceded by a marker comment:
The four scopes
:root,html,body,main, and class selectors with no[data-section]/[data-module]ancestor (e.g..btn-primaryfrom canon.css)styles/styles.csssection[data-section="<name>"](or[data-section="<name>"])styles/styles.css(scoped via section class)[data-module="<name>"](or[data-template="<name>"]for page-template scope)blocks/<name>/<name>.csssection > h1,section > p,section[data-section="hero"] > p) — does not match deep-descended elements inside modulesstyles/styles.css(default-content rules)Inheritance via cascade
Default-content inherits both global and section context for free
via the normal CSS cascade:
section > h1 { font: var(--heading-xxl)/var(--lh-heading) var(--heading-font); }section[data-section="hero"] { --heading-xxl: clamp(80px, 12vw, 160px); }h1picks up the lifted size automatically becausevar(--heading-xxl)resolves against the section's redefinition.The direct-child combinator (
>) in default-content selectors isload-bearing. It isolates default-content rules from block internals:
section > h1does NOT match anh1deep-descended inside a[data-module]. Block authors are free to style their own headingsinside the module subtree without colliding with defaults.
No engineered inheritance machinery is required. The cascade does the
work.
Example
<style>block (canonical organization)Section- or block-specific media queries live inside their owning
group (per
mobile-nav-collapse.md§ Source order — same-specificitylater-wins rule still applies).
Discipline 11 — Four-scope CSS organization
Lands in
skills/prototype/SKILL.md§ Craft-time disciplines(currently disciplines 6, 7, 8 exist per
PR #131; this would be the
next addition, numbered per the current spec at PR time).
Validator behavior (brief/craft-time, pre-write)
After craft returns the rendered file, the validator parses every
rule selector in the
<style>block. Each selector classifies asone of:
global— selector matches::rootdeclaration, ORhtml/body/main, ORscopes to
[data-section]or[data-module]. canon.cssclasses (
.btn-primary,.card, etc.) land here.section— selector starts withsection[data-section="<name>"](or[data-section="<name>"]).Descendants of the section selector still classify as
section.block— selector starts with[data-module="<name>"]OR[data-template="<name>"](page-template scope).Descendants of the module selector classify as
block.default-content— selector patternsection[> direct child]orsection[data-section="X"][> direct child]where the direct child is one of the default-content HTML elements
(
h1–h6,p,ul,ol,li,img,picture,figure,figcaption,a,blockquote,hr).Unclassifiable — refuse the write.
Marker comments
Mandatory and machine-parseable. Format:
Where:
<SCOPE>isGLOBAL/SECTION/BLOCK/MEDIA.GLOBAL:<name>is one oftokens/resets/default-content/compound utility(the canon.css block).SECTION:<name>matches thedata-sectionattribute value.BLOCK:<name>matches thedata-moduleordata-templateattribute value.
MEDIA:<kind>describes the breakpoint, e.g.≤ 640px.The validator regex over the
<style>text is straightforward:Refusal messages
When a rule cannot be classified, refuse the write with a structured
message naming the offending selector and the suggested remediation:
Block rules without a matching
data-module/data-templatevalue inthe DOM also refuse (typo / dead-block detection).
Site-global escape hatch
Some rules legitimately span scopes (e.g.
section { padding: var(--section-padding); }applies to every section). Those live in
GLOBAL: resetsor adedicated
GLOBAL: section-defaultsgroup. The escape hatch keepsthe discipline from forcing absurd duplication.
Implementation plan (file-by-file)
plugins/stardust/skills/prototype/reference/proposed-file-shell.md§ Required structure<style>block above.plugins/stardust/skills/prototype/SKILL.md§ Craft-time disciplinesplugins/stardust/skills/stardust/reference/token-contract.md§ Per-section overridesplugins/stardust/skills/stardust/reference/data-attributes.md§ Why → Downstream consumersdata-moduleas the block primitive for downstream EDS extraction, andsection > <element>selectors as the default-content selector pattern. Add a cross-link to Discipline 11.plugins/stardust/skills/prototype/reference/canon-extraction.md§ 2 Compound CSS — Selection ruleplugins/stardust/skills/stardust/reference/migrate-output-format.md§ Asset reference shape<style>block follows the four-scope organization per Discipline 11. Downstream converters can rely on the marker comments to extract block CSS mechanically."plugins/stardust/evals/css-four-scopes/(NEW, optional)task.md+criteria.jsonper the format inevals/README.md. Positive fixture: prototype that classifies cleanly. Negative fixture: prototype with unscoped.headlinerule that the validator must refuse.plugins/stardust/skills/prototype/fixtures/four-scope-css-example.html(NEW, optional)mobile-nav-collapse-example.html's role — shows the canonical organization on a realistic page.Spec-only changes; no JS code is added. The validator is implemented
by the agent at craft time reading the discipline (same pattern as
Disciplines 6 / 7 / 8 today — those are also agent-implemented, not
code).
Downstream extraction algorithm (for the future EDS converter)
With the discipline in place, the converter is mechanical:
No LLM judgment required per rule. ~200 lines of script with regex +
file split + path mapping.
Alternatives considered
Two-scope (global + section only)
Rejected. A converter still has to LLM-judge which rules within a
section belong to a block vs to default content. Block extraction
stays AI-driven. The four-scope version separates blocks from
sections explicitly via
[data-module]and is strictly more usefuldownstream at the same authoring cost.
CSS-in-JS / Shadow DOM /
@scopeRejected. Stardust's iteration loop depends on a single
self-contained styled HTML file viewable via
file://. Shadow DOMbreaks file:// rendering. CSS-in-JS adds runtime.
@scopehaslimited browser support and adds parser complexity without solving
the "which block does
.headlinebelong to" question.Apply discipline retroactively to existing prototypes
Rejected. Existing approved prototypes (e.g. lovesac, wasatch
references in the spec) that pass current critique+audit+adapt should
not be invalidated. New craft output applies the discipline; old
output remains valid. Phrasing in the spec: "Discipline 11 fires at
craft time; existing prototypes are grandfathered until re-craft."
Acceptance criteria
proposed-file-shell.md§ Required structure includes item 8describing the four-scope CSS organization with the marker comment
format.
prototype/SKILL.md§ Craft-time disciplines includes Discipline11 with the validator behavior, classification rules, and refusal
messages.
data-attributes.mdcross-references Discipline 11 and namesdata-moduleas the block primitive.token-contract.md§ Per-section overrides is re-framed as thecanonical section-scope pattern (not just one optional option).
canon-extraction.md§ 2 names the canon.css output as the"GLOBAL: compound utility" group.
migrate-output-format.mddocuments the four-scope organizationas a property downstream converters can rely on.
evals/css-four-scopes/that exercisesboth positive and negative fixtures.
npm run validatepasses.Out of scope
the upstream contract.
data-*attributes. The proposal uses the existingv2.1 vocabulary verbatim.
:roottoken contract. The:rootblock is unchangedin shape and lives in the
GLOBAL: tokensgroup by definition.Open questions for the maintainer
slot at HEAD, or did another PR land disciplines 11+ in the
interim? Confirm before drafting the PR.
data-templateblock scope. Should page-template-level CSS(
[data-template="article"] { ... }) classify as aBLOCK(perthis proposal) or as a separate
TEMPLATEscope? Currentproposal folds them together because both extract 1:1 to a
downstream block file; a maintainer may prefer the split.
"compound CSS" per
canon-extraction.md§ 2. The disciplinelabels its contents as
GLOBAL: compound utility. Is the renamemerely cosmetic, or does the maintainer want canon.css to also
carry section / block markers for its own contents (e.g. if a
canon module's CSS lives in canon.css)?
does the maintainer want per-scope coverage (one eval per scope
type) in line with the granular eval pattern in
evals/?--publish-sample. The--publish-sampleflow (perskills/prototype/reference/publish-sample.md) submits aprototype to the showcase. Should published-sample prototypes
already in the showcase be back-filled to the new organization,
or only new submissions?
For future agents picking this up
Re-read this issue end-to-end. It's self-contained — every
architectural reason and design decision is here.
Verify state at HEAD. stardust evolves fast (PR stardust: ia-fidelity axis + 10 non-slop disciplines + approval fold-back #131 landed
10 disciplines in one sweep; PRs feat(stardust): migrate produces self-contained zip-and-deploy bundle #132 / fix(stardust): make migrate output genuinely zip-and-deploy portable #133 reshaped the
migrate bundle). Before writing the PR:
git fetch origin && git log --oneline origin/main -- plugins/stardust/ | head -20plugins/stardust/skills/prototype/SKILL.md§ Craft-timedisciplines to confirm the next free discipline number.
plugins/stardust/skills/prototype/reference/proposed-file-shell.mdto confirm the "Required structure" section still has the
numbered list shape this proposal extends.
plugins/stardust/skills/stardust/reference/data-attributes.mdstill uses
data-moduleas the block primitive (not renamed).plugins/stardust/tile.jsonfor current version (thisissue was authored against
v0.7.1; if>= v0.8.0at PRtime, expect refactors that may shift the file shape).
Confirm no overlapping PR is open. Search:
gh pr list --repo adobe/skills --search "css scope OR section-scoped OR block-scoped OR Discipline 11".Branch off main with conventional name.
git checkout -b feat/stardust-four-scope-css.Make edits per the Implementation plan table above. File-by-file.
Commit using conventional commits format:
Open PR with
--label ai-generatedper CONTRIBUTING.md ifthe PR is AI-drafted. Reference this issue in the body. PR title
should mirror the commit subject. Drop a note in
#agentskills Slack per CONTRIBUTING.md.
Verify gates.
npm run validatelocally; Tessl Skill Reviewruns on CI. Tessl Evals require an empty
eval:-prefixed commitand aren't available to fork PRs (CONTRIBUTING.md § Requesting Evals).
Honest signal. If the maintainer's response is "we already
have a different plan for this," abandon this PR gracefully and
close the issue with a note pointing at their plan.
Drafted with assistance from Claude (Anthropic) on 2026-05-22.
Per CONTRIBUTING.md, the corresponding PR should carry the
ai-generatedlabel.