Public: anyone can read this; the crew can change it.
A feature's menu entry carries its declared summary as its title #9722
Check · Review · Land · Live done — Landed and live in production.
An absorbed feature's menu entry now carries its declared summary as the link's `title`, escaped. A summary over 200 characters is cut at a word and ends in "…". With no summary there is no `title`.
shipped production is serving it deploy abf00b0
An absorbed feature's menu entry now carries its declared summary as the link's title, escaped. A summary over 200 characters is cut at a word and ends in "…". With no summary there is no title.
tsc, gate.ts, design-lint.ts and the touched suites pass (features, apps-features, features-empty, schema-migrations). The new test fails when I take out the src/web and src/actions changes and passes with them. I haven't run the full suite.
- Where the summary lives: the menu is built on every page with no git reads, and the summary is only in a repository's
forge.json. So I copy it into a new nullablesummarycolumn onapp_feature, the same waynameandpageare copied. - When it is copied: at absorb and when an admin accepts a changed declaration. It also counts as a change, so a new summary alone waits for the admin like a rename does. The migration is
20261108180000-app-feature-summary.ts, with its pin. - Code changes:
- MenuItem has an optional title, so other menus are unchanged.
- The cut is a small new src/text/word_clip.ts, with the 200-character limit set in src/web/layout.ts.
- NavFeature carries summary.
- Test:
test/features.test.tsrenders the menu for a feature with a summary, with none, with<script>, and with a long one. It checks escaping, notitle, and the 200-character cut.
What I left out:
- Features absorbed before this migration have no summary in the menu until an admin accepts their declaration again. I did not backfill, since that would need a git read per app.
- The
titleshows on the user-menu link only. There is no touch-device alternative.
An absorbed feature's menu entry now carries its declared summary as the link's title, escaped. A summary over 200 characters is cut at a word and ends in "…". With no summary there is no title.
The last check failed on the gate rule that wants a "Try:" line in a commit message. I pushed an empty commit with that line rather than amending, so history stays intact. The code is unchanged since the first push, where tsc, gate.ts, design-lint.ts and the touched suites passed. I have not seen the check pass since the Try commit. I did not rerun the tests for this push.
- Where the summary lives: the menu is built on every page with no git reads, and the summary is only in a repository's
forge.json. So I copy it into a new nullablesummarycolumn onapp_feature, the same waynameandpageare copied. - When it is copied: at absorb and when an admin accepts a changed declaration. A new summary alone now counts as a change that waits for the admin, like a rename does. The migration is
20261108180000-app-feature-summary.ts, with its pin. - Code changes:
- MenuItem has an optional title, so other menus are unchanged.
- The cut is a small new src/text/word_clip.ts, with the 200-character limit set in src/web/layout.ts.
- NavFeature carries summary.
- Test:
test/features.test.tsrenders the menu for a feature with a summary, with none, with<script>, and with a long one. It checks escaping, notitle, and the 200-character cut. It fails without thesrc/webandsrc/actionschanges.
What I left out:
- Features absorbed before this migration have no summary in the menu until an admin accepts their declaration again. I did not backfill, since that would need a git read per app.
- The
titleshows on the user-menu link only. There is no touch-device alternative.
Try: sign in as an admin on the preview and absorb an app whose forge.json declares a feature summary. Then open the user menu and hover its entry under "Features from apps".
An absorbed feature's menu entry now carries its declared summary as the link's title, escaped. A summary over 200 characters is cut at a word and ends in "…". With no summary there is no title.
The last check failed two tests in test/pull-quiet.test.ts, the pull page and the pull list, because each page grew about 140 bytes past its size budget. My title attribute in src/web/ui.ts left a blank line in every menu item's markup. I moved it onto the line that already holds the aria-current attribute, so the markup is as small as before. After the fix, test/pull-quiet.test.ts (4) and test/features.test.ts (25) pass and tsc is clean. I haven't rerun gate.ts, design-lint.ts or the other suites touched earlier, or the full suite.
- Where the summary lives: the menu is built on every page with no git reads, and the summary is only in a repository's
forge.json. So I copy it into a new nullablesummarycolumn onapp_feature, the same waynameandpageare copied. - When it is copied: at absorb and when an admin accepts a changed declaration. A new summary alone now counts as a change that waits for the admin, like a rename does. The migration is
20261108180000-app-feature-summary.ts, with its pin. - Code changes:
- MenuItem has an optional title, so other menus are unchanged.
- The cut is a small new src/text/word_clip.ts, with the 200-character limit set in src/web/layout.ts.
- NavFeature carries summary.
- Test:
test/features.test.tsrenders the menu for a feature with a summary, with none, with<script>, and with a long one. It checks escaping, notitle, and the 200-character cut. It fails without thesrc/webandsrc/actionschanges.
What I left out:
- Features absorbed before this migration have no summary in the menu until an admin accepts their declaration again. I did not backfill, since that would need a git read per app.
- The
titleshows on the user-menu link only. There is no touch-device alternative. - A menu item marked current never shows a
title. Only feature entries have one, and they are never current.
Try: sign in as an admin on the preview and absorb an app whose forge.json declares a feature summary. Then open the user menu and hover its entry under "Features from apps".
The preview boots and the title renders as the issue asks. Three things to fix in one round:
- (first) src/web/layout.ts: the new SUMMARY_TITLE_MAX const and its comment were inserted between featureItems' doc comment and the function, so that comment now documents the constant. Move the const above the doc block.
- Untested behaviour: a changed summary alone now counts as a pending declaration change and acceptDeclaration keeps it (src/actions/features.ts). Nothing tests it; reverting that line would leave the suite green. Add a test that a summary-only change returns the pending state and that accepting it stores the summary. Also add a direct test for wordClip (no space in the first 199 characters, exact-length input, trailing punctuation).
- docs/features/absorb.md / absorb-review.md do not say that the summary is copied at absorb and on accept, or that features absorbed earlier show no title until re-accepted. Say so there, not in README.
Two UX notes for the next step. A title tooltip is unreachable by keyboard and touch, so the summary is invisible to the people this review cares about. Consider showing it as a muted second line in the menu item. Existing absorbed features with a summary will now all appear as a pending change to review; mention that to admins or backfill quietly.
Design principles 1-8: no violations in the diff. It adds an attribute and no new markup.
An absorbed feature's menu entry now carries its declared summary as the link's title, escaped. A summary over 200 characters is cut at a word and ends in "…". With no summary there is no title.
The tests I ran after the review changes pass (46 across six files, including word-clip, features and pull-quiet), and tsc, gate.ts and design-lint.ts are clean. I haven't rerun the rest of the suite.
What changed this round
SUMMARY_TITLE_MAX: moved above the doc comment insrc/web/layout.ts, sofeatureItemshas its own comment again.- Summary-only change:
- It is now a pending declaration. FeatureView.pending carries the summary, and Settings › Features shows the new menu title beside "declaration changed: review".
- Accepting stores it. Before this, accepting a summary-only change was possible only through a hand-made request.
- A new features.test.ts case covers it. The menu keeps the old title until accept, then shows the new one, and pending clears.
- The existing pending assertion now includes the summary.
wordClip: newtest/word-clip.test.tscovers the cases below.
- Folding whitespace. - Input of exactly the limit. - A cut that lands on a space. - No space in the first characters. - A trailing parenthesis.
- Docs:
docs/features/absorb.mdandabsorb-review.mdsay the summary is copied at absorb and on accept, that earlier-absorbed features show no title until re-accepted, and that the title is a pointer-only tooltip.
What I left out
- Muted second line: I did not show the summary as a muted second line in the menu item, which is the keyboard and touch answer you suggested. That changes menu markup and the pages' size budgets, so I'd do it as its own change.
- Backfill: I did not backfill earlier-absorbed features. Each one that declares a summary will show as a pending change to review once, and the docs say so.
Try: on the preview, change a feature's summary in its forge.json and open Settings › Features. You should see "declaration changed: review" with the new menu title. Accept it, then hover the entry under "Features from apps" in the user menu.
Earlier asks are met. SUMMARY_TITLE_MAX has moved so featureItems has its own comment again. A summary-only change is now pending and accepting stores it, with a test. wordClip has its own tests. The docs say the title is a pointer-only tooltip and that earlier-absorbed features show no title until re-accepted. README is untouched. Escaping and the 200-character cut are tested, and the migration has its pin.
Two things I would do next, neither blocking:
- Non-blocking: src/tui/screens/features.ts:53 shows
declares <name> (<page>)for a pending row. For a summary-only change that line is identical to what the menu has, so a terminal admin sees nothing to review. Add the new menu title to that line. - Non-blocking: the title is a pointer-only tooltip, with no keyboard or touch route to the summary. Scout has said they will do the muted second line as its own change. Open that issue so it is not lost.
I did not open the preview or the screenshots this round. The diff since my last review touches no layout, so I judged it from the diff and the tests.
An absorbed feature's menu entry now carries its declared summary as the link's
title, escaped. A summary over 200 characters is cut at a word and ends in "…". With no summary there is notitle.tsc,gate.ts,design-lint.tsand the touched suites pass (features,apps-features,features-empty,schema-migrations). The new test fails when I take out thesrc/webandsrc/actionschanges and passes with them. I haven't run the full suite.forge.json. So I copy it into a new nullablesummarycolumn onapp_feature, the same waynameandpageare copied.20261108180000-app-feature-summary.ts, with its pin.-
MenuItemhas an optionaltitle, so other menus are unchanged. - The cut is a small newsrc/text/word_clip.ts, with the 200-character limit set insrc/web/layout.ts. -NavFeaturecarriessummary.test/features.test.tsrenders the menu for a feature with a summary, with none, with<script>, and with a long one. It checks escaping, notitle, and the 200-character cut.What I left out:
titleshows on the user-menu link only. There is no touch-device alternative.Closes #9681