Skip to content

Let a subsection hang under another subsection - #216

Merged
ssavutu merged 1 commit into
mainfrom
feat/food-subsection-nesting
Aug 13, 2026
Merged

Let a subsection hang under another subsection#216
ssavutu merged 1 commit into
mainfrom
feat/food-subsection-nesting

Conversation

@ssavutu

@ssavutu ssavutu commented Aug 13, 2026

Copy link
Copy Markdown
Member

A&E asked for a Food subsection holding Beer Reviews, Wine Reviews, Restaurant Reviews and Cooking. That is a third level the taxonomy did not have: parent_slug had to name a section, and the rollup was a single non-recursive hop.

The shape

Depth is a property of the parent chain, not a new kind. Both levels stay kind='subsection', so everything that switches on kind — the public site's slug router, the article filters, the sections screen — keeps working unchanged. MaxTaxonomyDepth caps the chain at three, because the walks are recursive over rows editors can edit.

What actually mattered

The rollup is now transitive. This is the load-bearing change. One hop would have counted Beer Reviews for Food while dropping it from A&E, leaving a section quietly shorter than the sum of its subsections — the same silent-omission failure the alias seeding exists to fix. Recounts walk every ancestor for the same reason, and a subsection resolves to its root section rather than its parent, since that is what ?section_slug= is checked against.

A subsection can be a container. /food matches its descendants; matching food alone would render it empty, as its articles are filed under the four categories beneath it. /v1/subsections/<slug>/articles also returns subsections now, so a middle row gets the link strip a section page has.

The guards widened. Delete and slug-change only asked about sections. Deleting Food would have stranded four rows pointing at a parent that no longer exists.

One behaviour change worth reviewing: converting a section that has children into a subsection used to be refused outright, on the premise that a subsection could not parent one. It is now allowed when the resulting subtree still fits in three levels. The existing test asserting the old rule was updated to assert the depth rule instead.

Migration

SeedFoodSubsection creates Food and moves the four rows, once, under its own flag — legacy_subsections_seeded is already set in production. The move is guarded on the current parent, so a row an editor pulls back out stays where they put it.

Cooking is the one visible change to a section page readers use today: unlike the three review categories it is a live, visible subsection, so moving it takes its link out of A&E's strip and puts it in Food's. That is the intent.

Testing

Tree-walk unit tests including cycle termination, plus DB-backed tests for the three-level rollup, the fourth-level rejection, and the seed's idempotence against an editor's change. Full Go suite green against MariaDB; frontend 19/19.

Needs DrexelTriangle/Scalene#94 for the public side, which links a subsection's own children in the nav strip.

🤖 Generated with Claude Code

A&E asked for a Food subsection holding Beer Reviews, Wine Reviews,
Restaurant Reviews and Cooking -- a third level the taxonomy did not
have. parent_slug had to name a section, and the rollup was a single
non-recursive hop.

Depth is a property of the parent chain, not a new kind: both levels
stay kind='subsection', so everything that switches on kind -- the
public site's slug router, the article filters, the sections screen --
keeps working. MaxTaxonomyDepth caps the chain at three, because the
walks are recursive over editor-editable rows.

The load-bearing part is the rollup, which is now transitive. One hop
would have counted Beer Reviews for Food while dropping it from A&E,
leaving a section quietly shorter than the sum of its subsections --
the same silent-omission failure the alias seeding exists to fix.
Recounts walk every ancestor for the same reason, and a subsection
resolves to its ROOT section, not its parent, since that is what
?section_slug= is checked against.

A subsection can be a container now, so its page matches its
descendants -- /food alone would render empty, as its articles are
filed under the four categories beneath it -- and the delete and
slug-change guards, which only asked about sections, now ask about
anything with children.

SeedFoodSubsection creates Food and moves the four rows, once, under
its own flag: legacy_subsections_seeded is already set in production.
The move is guarded on the current parent, so a row an editor pulls
back out stays where they put it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@ssavutu
ssavutu merged commit 2e63b4e into main Aug 13, 2026
6 checks passed
@ssavutu
ssavutu deleted the feat/food-subsection-nesting branch August 13, 2026 20:20
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.

1 participant