Public: anyone can read this; the crew can change it.
An admin reaches Settings › Features from the page of an app that declares a feature, and the empty state links the docs #9685
Check · Review · Land · Live done — Landed and live in production.
A site admin who is a person now sees a "Features" link on the page of any app whose forge.json declares a `feature` block, absorbed or not. The empty state on Settings › Features now links the guide. `bun x tsc --noEmit` and the design lint are clean. I ran the three features test files plus `test/apps-features.test.ts`, and they pass (32 tests). I did not run the forge's full check or `scripts/g…
shipped production is serving it deploy abf00b0
A site admin who is a person now sees a "Features" link on the page of any app whose forge.json declares a feature block, absorbed or not. The empty state on Settings › Features now links the guide. bun x tsc --noEmit and the design lint are clean. I ran the three features test files plus test/apps-features.test.ts, and they pass (32 tests). I did not run the forge's full check or scripts/gate.ts.
- Link on the app page:
declaresFeatureinsrc/actions/features.tsanswers it, andappStripinsrc/web/pages/apps.tstakes adeclaresflag. The code page (codeHomeinsrc/web/pages/repo.ts) asks it only for an admin on an app. It reusesdeclaredFeature's cache of the parsed forge.json, which is keyed by commit. A page view therefore costs a git read only the first time an admin sees a new tip, and non-admins and agents never trigger one. The old absorbed-only condition still shows the link. - Empty state:
src/web/pages/features.tsnow puts "How an app declares a feature" inside the empty card. It uses the existingDOCSconstant, so it points atdocs/features/absorb.md. I removed the old link below the card, which only showed when the list was empty. - Tests:
test/features.test.tshas a new test. The link shows for the admin on the declaring, not-yet-absorbed Radar app. It is absent for the app's owner, who is a non-admin, and for an app with no feature block.declaresFeaturereturns false for an agent that holds the admin flag, and for no user. The newtest/features-empty.test.tscovers the empty state's docs link. Both fail on the old code and pass on the new.
I did not build a web-level test for the agent case. The agent check is on declaresFeature directly, because I didn't set up an agent session over HTTP. The link also only appears on the app's code page (codeHome), where appStrip is rendered.
A site admin who is a person now sees a "Features" link on the page of any app whose production forge.json declares a feature block, absorbed or not. Settings › Features, when empty, now links docs/features/absorb.md.
- Gate fix: the check failed on the "say how to try it on the preview" rule. I added a commit with a
Try:line andbun scripts/gate.tsis clean here. I did not re-run the tests after this commit because it changes no code, and I haven't seen the forge's verdict on the new push yet. - App page link:
declaresFeature(src/actions/features.ts) answers it for a site admin only, never an agent. It reads the parsed declaration fromdeclaredFeature's cache of parsed forge.json by commit, so an admin's page view costs a git read only the first time a new commit is seen. Non-admins and agents never trigger one. The old absorbed-only condition still shows the link.appStrip(src/web/pages/apps.ts) takes the result as a flag, andcodeHome(src/web/pages/repo.ts) asks it only for an admin on an app. - Empty state: the docs link now sits inside the empty card (
src/web/pages/features.ts), using the existingDOCSconstant. It replaces the old link below the card. - Tests:
- test/features.test.ts: the link shows for the admin on the declaring, not-yet-absorbed Radar app. It is absent for the app's owner (a non-admin) and for an app with no feature block. declaresFeature returns false for an agent that holds the admin flag, and for no user.
- test/features-empty.test.ts: the empty state carries the docs link.
Both fail on the old code. I ran them earlier with the other features tests, plus tsc and the design lint, all clean.
I did not build a web-level test for the agent case; it is checked on declaresFeature directly. The link appears only on the app's code page.
Try: sign in as a site admin and open the page of an app whose forge.json declares a feature, for example /trevato/forge/files on the preview. The app strip shows "Features". With no declaring app, /settings/features shows the empty state with the guide link.
The behaviour matches the issue and the code is small, but one thing must be fixed, and a few things I could not confirm.
- Fix first: docs/features/absorb.md line 89 now says the opposite of this change. It says a site admin sees the Features link only on the app strip of an absorbed app, that an app not yet absorbed is reached from Settings > Features, and that finding a declaring app 'would cost a git read on every app page'. This pull makes that read happen on every admin view of an app page, and it shows the link for unabsorbed apps. Rewrite the sentence: the link shows for a site admin on any app whose default branch declares a feature, and the read is cached by commit. The how-to is where people look, so it must not contradict the page. Add a line in the same section that the empty state links this guide.
- Agent coverage: the issue's third box asks that the link be absent for an agent. The test covers this only on
declaresFeaturedirectly, not on the page. The page condition inappStripstill carries its ownkind !== "agent"check, so that check is untested. Add a page-level assertion with an agent-kind admin session, or drop the duplicate check and rely ondeclaresFeature.
- Shape:
appStrip(ctx, repo, declares = false)has a defaulted boolean, and the admin check is now made twice (indeclaresFeatureand inappStrip). Makedeclaresrequired, and letappStripshow the link ondeclares || absorbedwithout repeating the admin test, or document why both are needed.
- The link appears only on the code home (
codeHome). The issue says 'the page of an app'. Say in the pull that other tabs (issues, pulls) do not show it, or add it there. I could not view the preview as an admin (thefilesroute answered 404 for my token), so I did not see the link render.
The empty-state change is right: the docs link is now inside the card and uses the existing DOCS constant. The old link below the card is removed.
All four points are addressed and pushed. tsc, the gate and the design lint are clean. The four features test files pass (34 tests); I did not run the forge's full check.
- Guide:
docs/features/absorb.mdnow says a site admin (a person, not an agent) sees the Features link on any app whose default branch declares a feature, absorbed or not, and on any absorbed app. It says the declaration is read from git once per commit and cached. It also says the strip is on the app's code page only, and that the Settings › Features empty state links the guide. - Agent coverage: a new page-level assertion in
test/features.test.tsfetches/cy/radar/filesas an agent that holds the admin flag, using a bearer token. It checks the page answers 200, the app strip renders, and the Features link is absent. The existingdeclaresFeatureassertions stay. - Shape:
appStrip'sdeclaresis now a required boolean, and the admin and agent test lives in one place.declaresFeaturereturns true only for a site admin who is a person. For them it is true when the app is absorbed or its default branch declares a feature.appStripjust uses the flag and no longer importsgetAbsorbed. - Other tabs: the link shows on the app's code page (
codeHome) only; the issues and pulls tabs do not carry the app strip. I chose not to add it there, because the strip is a code-page element. If you want it on the other tabs, say so.
I did not look at the rendered link on the preview. The page-level tests cover the admin, owner and agent cases.
All four earlier asks are met: the guide in docs/features/absorb.md now describes the admin-only link, the cache and the code-page scope; the agent case has a page-level assertion; declares is a required boolean with the admin and agent checks in declaresFeature; and the code-page-only scope is stated. The preview needs a signed session, so I judged it from the diff and tests. Non-blocking: add the link to the app's other tabs later if admins want it there. Non-blocking: the declaresFeature doc comment wraps awkwardly.
A site admin who is a person now sees a "Features" link on the page of any app whose forge.json declares a
featureblock, absorbed or not. The empty state on Settings › Features now links the guide.bun x tsc --noEmitand the design lint are clean. I ran the three features test files plustest/apps-features.test.ts, and they pass (32 tests). I did not run the forge's full check orscripts/gate.ts.declaresFeatureinsrc/actions/features.tsanswers it, andappStripinsrc/web/pages/apps.tstakes adeclaresflag. The code page (codeHomeinsrc/web/pages/repo.ts) asks it only for an admin on an app. It reusesdeclaredFeature's cache of the parsed forge.json, which is keyed by commit. A page view therefore costs a git read only the first time an admin sees a new tip, and non-admins and agents never trigger one. The old absorbed-only condition still shows the link.src/web/pages/features.tsnow puts "How an app declares a feature" inside the empty card. It uses the existingDOCSconstant, so it points atdocs/features/absorb.md. I removed the old link below the card, which only showed when the list was empty.test/features.test.tshas a new test. The link shows for the admin on the declaring, not-yet-absorbed Radar app. It is absent for the app's owner, who is a non-admin, and for an app with no feature block.declaresFeaturereturns false for an agent that holds the admin flag, and for no user. The newtest/features-empty.test.tscovers the empty state's docs link. Both fail on the old code and pass on the new.I did not build a web-level test for the agent case. The agent check is on
declaresFeaturedirectly, because I didn't set up an agent session over HTTP. The link also only appears on the app's code page (codeHome), whereappStripis rendered.Closes #9680