Summary
onRouteChange() is render-scoped but is missing from RENDER_SCOPED_CONCEPTS in src/analyze/catalog.ts, so askr/stable-render-call never reports a conditional or module-scope call.
Why this is an Askr violation
askr/src/router/activity.ts calls claimHookIndex(instance, 'route-change'), exactly like on / timer / task / stream. A conditional call shifts every subsequent hook index and trips the runtime's render-sequence enforcement:
[askr] Component blueprint reactive shape changed.
Not caught today
function Page() {
const [tracking] = state(true);
if (tracking()) {
onRouteChange((current) => analytics.page(current.path)); // not reported
}
return <Content />;
}
Audit of the full hook-index set
Grepping claimHookIndex(instance, '...') across the askr runtime yields exactly these render-scoped slots:
state, derive, selector, on, timer, task, stream, query, mutation, route-change, For
Against catalog.ts today:
| Slot |
In catalog |
Notes |
state, derive, selector |
yes |
ASKR_CONCEPTS.reactive |
on, timer, task, stream |
yes |
ASKR_CONCEPTS.lifecycle |
query, mutation |
yes |
POSITIONAL_DATA_CONCEPTS |
route-change |
no |
this issue |
For |
no |
tracked separately in the eager-control-boundary issue |
resource is also in the catalog and is render-scoped via getNextRenderKey() rather than claimHookIndex, so it is correctly covered.
Proposed change
Add onRouteChange to RENDER_SCOPED_CONCEPTS (and to ASKR_CONCEPTS.lifecycle, since it is exported from @askrjs/askr/resources alongside the other lifecycle helpers).
A regression test asserting catalog coverage against the runtime's claimHookIndex call sites would keep this from drifting again.
Rule metadata
- id: existing
askr/stable-render-call
- category:
correctness
- severity:
error
- autofix: none
Summary
onRouteChange()is render-scoped but is missing fromRENDER_SCOPED_CONCEPTSinsrc/analyze/catalog.ts, soaskr/stable-render-callnever reports a conditional or module-scope call.Why this is an Askr violation
askr/src/router/activity.tscallsclaimHookIndex(instance, 'route-change'), exactly likeon/timer/task/stream. A conditional call shifts every subsequent hook index and trips the runtime's render-sequence enforcement:Not caught today
Audit of the full hook-index set
Grepping
claimHookIndex(instance, '...')across theaskrruntime yields exactly these render-scoped slots:Against
catalog.tstoday:state,derive,selectorASKR_CONCEPTS.reactiveon,timer,task,streamASKR_CONCEPTS.lifecyclequery,mutationPOSITIONAL_DATA_CONCEPTSroute-changeForresourceis also in the catalog and is render-scoped viagetNextRenderKey()rather thanclaimHookIndex, so it is correctly covered.Proposed change
Add
onRouteChangetoRENDER_SCOPED_CONCEPTS(and toASKR_CONCEPTS.lifecycle, since it is exported from@askrjs/askr/resourcesalongside the other lifecycle helpers).A regression test asserting catalog coverage against the runtime's
claimHookIndexcall sites would keep this from drifting again.Rule metadata
askr/stable-render-callcorrectnesserror