Repository navigation
architecture: touch behavior comes from shared internal modules; Core and Lab reach them through ./internal/* (INV13) - #8464
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
AI review status for this pull request.
|
PR Analysis Report
View Storybook for this PR · View Sandbox for this PR Generated by PR Enrichment workflow | View current CI run |
|
Posted by Cindy's agent on her behalf. Cindy's direction on touch code: there should be one global touch and gesture module that components can leverage when it exists.
The install shape (how an app or the server installs the module, for example a module-level install vs a provider) is left to vjeux's dependency-injection proposal. This record holds that open until then. Evidence to work from:
Where component touch logic lives (measured, gzip, upper bounds per component):
|
There was a problem hiding this comment.
Approval of this proposal waits on an engineering owner's decision. INV13 adds a new architecture invariant and the INV6 rewrite changes a current one, so an engineering owner records the decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so if they are an engineering owner. That decision should also settle whether a touch device whose app never installs the module may fall back to fine-pointer behavior. Whatever is decided, these fixes are needed first, since a new commit resets approval:
- INV13 conflicts with
spec:AST-094DEC-1 ("by default, without an opt-in"): either require the module on every coarse-pointer page or amend DEC-1 in this pull request. - The INV6 rewrite drops the lazy-loading remedy that
spec:AST-080DEC-4 still grants: keep lazy loading for touch-only code outside the module, or amend DEC-4 to match. - INV13's verification row should fail on a second registry or a second installation.
[Automated review]
| gesture core and the animator, held in one module-level registry and | ||
| installed once for the page by the app or its server. Each Core component | ||
| ships one build, detects the module, and uses it when it is installed; | ||
| without it the component keeps its plain behavior, the one it has under a | ||
| fine pointer, and stays fully usable, and a tap acts as a click and is never | ||
| lost. No component loads the module itself or holds an `import()` of it, so |
There was a problem hiding this comment.
As written, a phone page whose app or server never installs the module gets each component's fine-pointer behavior. spec:AST-094 DEC-1 says "Every touch device gets one native-feeling touch behavior by default, without an opt-in", and this pull request leaves it unchanged. Either require the module on every coarse-pointer page (OQ2 then decides only how), or amend AST-094's Intent and DEC-1 here.
| to one component is flagged. Its pull request either takes that JavaScript | ||
| from the global touch and gesture module (`architecture:react-component-runtime` | ||
| INV13, proposed), so the component's import does not carry it, or justifies | ||
| the cost. For flagged CSS, its pull request |
There was a problem hiding this comment.
INV6 still names spec:AST-080/DEC-4 as its deciding spec, and DEC-4 says "JavaScript can load lazily, only when a coarse pointer needs it, or be justified". This rewrite drops that remedy for every touch-only feature, including layer chunks the description keeps outside INV13. Keep lazy loading for touch-only code outside the global module, or amend DEC-4 to match.
| | INV9 | Provider/external-store identity and render-count tests | Unchanged semantic payload changes identity, or snapshot identity creates a render loop | | ||
| | INV10 | Shared test over every adopter of the announcement module | An adopter holding its own Effect, observer, timer, or listener for its own-content announcement | | ||
| | INV11 | Source boundary check plus hook tests across StrictMode replay and unmount | A second keyboard listener or measurement in Core; a consumer reading a CSS custom property on the root or a shared ancestor for the height; an export of the hook; a component outside Core measuring by a rule other than AST-099 FR2, or riding the keyboard; a listener left after unmount; a Core rider driven by JavaScript per frame or by its own transition; or more than one layout read per keyboard change | | ||
| | INV13 (proposed) | Source boundary check plus real-browser tests on a throttled phone profile, with and without the module installed, and on a hybrid device | An `import()` of the module in a component, touch code in a component's own build beyond OQ3's answer, a lost first tap, a component unusable without the runtime, or a listener under a fine pointer fails | |
There was a problem hiding this comment.
INV13's core claim is one registry, installed once for the page, and react-component-runtime INV5 lets a document-owned singleton persist only when "tests prove one shared installation". Consider adding "a second registry or a second installation" to this failure signal.
|
Posted by Cindy's agent on her behalf. Cindy's decision on where component touch code lives: a dependency check. Each component checks whether the global touch and gesture module is loaded. If it is, the component runs its touch behavior using that module's primitives; if not, it behaves as it does today.
|
a0c9649 to
3a58015
Compare
There was a problem hiding this comment.
Approval of this proposal waits on an engineering owner's decision: INV13 adds a new architecture invariant and the INV6 rewrite changes a current one, so an engineering owner records the decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so if they are an engineering owner. That decision should also settle whether a phone whose app never installs the module may keep fine-pointer behavior, as "loads only when the app installs it" in the latest comment implies. Because a new commit resets approval, these fixes belong first:
- INV13 conflicts with
spec:AST-094DEC-1 and its Intent ("by default, without an opt-in"): amend them in this pull request, or require the module on every coarse-pointer page. - The INV6 rewrite drops the lazy-loading remedy that
spec:AST-080DEC-4 still grants: keep lazy loading for touch-only code outside the module, or amend DEC-4. - INV13's verification row should fail on a second registry or installation (react-component-runtime INV5); its stray trailing "fails" and the "(decided by …)" note should go.
[Automated review]
| installed once for the page by the app or its server. Each Core component | ||
| ships one build, detects the module, and uses it when it is installed; | ||
| without it the component keeps its plain behavior, the one it has under a | ||
| fine pointer, and stays fully usable, and a tap acts as a click and is never |
There was a problem hiding this comment.
As written, a phone page whose app or server never installs the module keeps each component's fine-pointer behavior. spec:AST-094 DEC-1 says "Every touch device gets one native-feeling touch behavior by default, without an opt-in", and its Intent says the same; this pull request leaves both unchanged. Either amend AST-094's Intent and DEC-1 here, or require the module on every coarse-pointer page and leave OQ2 to decide only how.
| to one component is flagged. Its pull request either takes that JavaScript | ||
| from the global touch and gesture module (`architecture:react-component-runtime` | ||
| INV13, proposed), so the component's import does not carry it, or justifies | ||
| the cost. For flagged CSS, its pull request |
There was a problem hiding this comment.
INV6 still names spec:AST-080/DEC-4 as its deciding spec, and DEC-4 says "JavaScript can load lazily, only when a coarse pointer needs it, or be justified". This rewrite drops that remedy for every touch-only feature, including lazy layer chunks the description keeps outside INV13. Keep lazy loading for touch-only code outside the global module, or amend DEC-4 to match.
| | INV9 | Provider/external-store identity and render-count tests | Unchanged semantic payload changes identity, or snapshot identity creates a render loop | | ||
| | INV10 | Shared test over every adopter of the announcement module | An adopter holding its own Effect, observer, timer, or listener for its own-content announcement | | ||
| | INV11 | Source boundary check plus hook tests across StrictMode replay and unmount | A second keyboard listener or measurement in Core; a consumer reading a CSS custom property on the root or a shared ancestor for the height; an export of the hook; a component outside Core measuring by a rule other than AST-099 FR2, or riding the keyboard; a listener left after unmount; a Core rider driven by JavaScript per frame or by its own transition; or more than one layout read per keyboard change | | ||
| | INV13 (proposed) | Source boundary check plus real-browser tests on a throttled phone profile, with and without the module installed, and on a hybrid device | An `import()` of the module in a component, touch code in a component's build other than glue over the module's primitives, a lost first tap, a component unusable without the runtime, or a listener under a fine pointer fails | |
There was a problem hiding this comment.
INV13's core claim is one registry installed once for the page, and react-component-runtime INV5 lets a document-owned singleton persist only when "tests prove one shared installation". Add "a second registry or a second installation" to this failure signal, and drop the trailing "fails".
| glue in that component over the module's primitives: the component checks | ||
| whether the module is present and, if it is, runs its touch behavior with | ||
| the module's primitives, so each component carries its glue in its own | ||
| build (decided by `cixzhang`, 2026-10-25). |
There was a problem hiding this comment.
Minor: "(decided by cixzhang, 2026-10-25)" is decision history. architecture:knowledge-contracts INV10 keeps it out of a record ("a record states the fact a change established, not the change"), and the rubric's R1 flags it; the decision can stay in this pull request's conversation.
|
Posted by Cindy's agent on her behalf. Cindy's decision on how the touch module is installed: import TouchSupport from '@astryxdesign/core/touch-support';
TouchSupport.install(); // once, in the client entry, before render or hydrate
vjeux, please build your install proposal on this name and shape. What this means for the "by default" wording: touch presentations come by default on pages that install |
3a58015 to
0b00fc8
Compare
There was a problem hiding this comment.
Approval of this proposal waits on an engineering owner's decision: INV13 adds a new architecture invariant with a public TouchSupport entry and the INV6 rewrite changes a current one, so an engineering owner records the decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so if they are an engineering owner. Because a new commit resets approval, these fixes belong first:
- Amend
spec:AST-094Intent and DEC-1 ("by default, without an opt-in"), andarchitecture:public-component-apiINV16's default sentence, for the app-installed module. - Scope INV13's fallback ("the one it has under a fine pointer") and its failure signal to behavior that runs on the module, so released touch behavior such as press feedback, swipes and sheets keeps working without the call.
- Reconcile the INV6 rewrite with
spec:AST-080DEC-4, which still grants lazy loading. - Give Lab a path to the internals that resolves outside Core's exports map, say whether
install()skips the module under a fine pointer, and add thespec:AST-002FR5 admission argument to the description.
[Automated review]
| touch logic is small glue in its own build. Without it the component keeps | ||
| its plain behavior, the one it has under a fine pointer, and stays fully | ||
| usable, and a tap acts as a click and is never lost. No component loads the |
There was a problem hiding this comment.
As written, a phone whose app never calls TouchSupport.install() keeps every component's fine-pointer behavior. spec:AST-094 DEC-1 says "Every touch device gets one native-feeling touch behavior by default, without an opt-in", and this pull request leaves it and the Intent unchanged. The sentence also reaches released touch behavior that needs no module, such as the usePressFeedback press paint, Item swipes and the menu and Selector sheets. Consider "Without it, behavior that needs the module's primitives keeps its fine-pointer form, and the component's other behavior is unchanged", with an AST-094 amendment for the opt-in.
| and Lab components reach them through a package-private path that is not in | ||
| the exports map and that lint limits to Core and Lab, where Lab's access | ||
| follows Lab's own module-sharing rule. `TouchSupport`'s documentation says |
There was a problem hiding this comment.
@astryxdesign/lab imports Core only through Core's exports map, so a path outside it does not resolve from Lab's published files (#8076 hits "Package path ./gestures is not exported"). Either limit this path to Core, or name an exported internal channel, which architecture:public-component-api INV12 would count as public.
| that motion such as `useAnimatedLayout` works without it, that it is called | ||
| once in the client entry before render or hydrate, that people using only a | ||
| mouse pay nothing, that a swipe-only action still needs a non-touch path, | ||
| and that Reduce Motion is respected. |
There was a problem hiding this comment.
Nothing in INV13 makes "people using only a mouse pay nothing" true for an app that calls install() in its client entry, which runs for every visitor. Say that install() loads the gesture core and animator only where the primary pointer is coarse, or state what a fine-pointer visitor downloads.
| to one component is flagged. Its pull request either takes that JavaScript | ||
| from the global touch and gesture module (`architecture:react-component-runtime` | ||
| INV13), so the component's import does not carry it, or justifies | ||
| the cost. For flagged CSS, its pull request |
There was a problem hiding this comment.
INV6 still names spec:AST-080/DEC-4 as its deciding spec, and DEC-4 says "JavaScript can load lazily, only when a coarse pointer needs it, or be justified". This drops that remedy for every touch-only feature, including code outside the module. Keep lazy loading for touch-only JavaScript outside the module, or amend DEC-4 here.
0b00fc8 to
ba2ea2e
Compare
There was a problem hiding this comment.
Approval of this proposal waits on an engineering owner's decision: INV13 adds a new architecture invariant with a public TouchSupport entry, and the INV6, spec:AST-094 DEC-12 and INV16 edits change current records, so an engineering owner records the decision by commenting /approve-spec <full head SHA> on this pull request, and the author may do so if they are an engineering owner. That decision should also settle how Lab reaches the touch internals. The latest revision fixes the AST-094 opt-in contradiction, the fallback's scope and the admission argument; because a new commit resets approval, these fixes belong first:
- Supersede
spec:AST-080DEC-4's lazy-load remedy in AST-080 itself and list that decision as INV6's deciding spec, instead of retiring it from INV13. - Aim the INV13 row's "touch code in a component's build other than glue" signal at the module's primitives, so released press-feedback and
useMenuPresscode passes it. - Replace "people using only a mouse pay nothing", which
install()loading the module under every pointer makes untrue, or skip the load under a fine pointer. - Give Lab a path that resolves from its published build, say what "press tracking" covers, and carry the install condition into AST-094 FR31 and FR33.
[Automated review]
| mouse pay nothing, that a swipe-only action still needs a non-touch path, | ||
| and that Reduce Motion is respected. Touch JavaScript comes from this module | ||
| rather than a lazy load (`architecture:component-style-authoring` INV6), | ||
| which replaces the lazy-load allowance `spec:AST-080` DEC-4 recorded. |
There was a problem hiding this comment.
INV13 says it replaces spec:AST-080 DEC-4's lazy-load allowance, but DEC-4 stays current and still says "JavaScript can load lazily, only when a coarse pointer needs it, or be justified", and INV6 still names spec:AST-080/DEC-4 as its deciding spec. Supersede that remedy in AST-080 itself, as DEC-12 does for AST-094, and list the new decision as INV6's deciding spec; or keep lazy loading for touch-only code outside the module.
| | INV9 | Provider/external-store identity and render-count tests | Unchanged semantic payload changes identity, or snapshot identity creates a render loop | | ||
| | INV10 | Shared test over every adopter of the announcement module | An adopter holding its own Effect, observer, timer, or listener for its own-content announcement | | ||
| | INV11 | Source boundary check plus hook tests across StrictMode replay and unmount | A second keyboard listener or measurement in Core; a consumer reading a CSS custom property on the root or a shared ancestor for the height; an export of the hook; a component outside Core measuring by a rule other than AST-099 FR2, or riding the keyboard; a listener left after unmount; a Core rider driven by JavaScript per frame or by its own transition; or more than one layout read per keyboard change | | ||
| | INV13 | Source boundary check plus real-browser tests on a throttled phone profile, with and without the module installed, and on a hybrid device | An `import()` of the module in a component, touch code in a component's build other than glue over the module's primitives, a lost first tap, a component unusable without the module, a module-free touch behavior such as press feedback that stops without it, or a listener under a fine pointer fails | |
There was a problem hiding this comment.
This row fails "touch code in a component's build other than glue over the module's primitives" and also "a module-free touch behavior such as press feedback that stops without it". Press feedback is touch code in every pressable's build that does not run on the module (utils/pressFeedback.ts, installed by usePressFeedback()), and so is useMenuPress's finger handling, so no implementation passes both. Consider "a copy of a recognizer, the arena or the animator in a component's build".
| once in the client entry before render or hydrate, that people using only a | ||
| mouse pay nothing, that a swipe-only action still needs a non-touch path, |
There was a problem hiding this comment.
"People using only a mouse pay nothing" contradicts "install() loads the module under every pointer" above: the install runs in the client entry for every visitor, so an installing app's mouse users download the module, and every import carries its component's glue. Say what a fine-pointer visitor pays, or have install() skip loading where the primary pointer is fine.
| and Lab components reach them through a package-private path that resolves | ||
| without Core's exports map and that lint limits to Core and Lab, where Lab's access |
There was a problem hiding this comment.
@astryxdesign/lab is a separate package published to npm's canary tag as Babel output, so its imports of Core resolve through Core's exports map; a path that "resolves without Core's exports map" is out of its reach, and no record defines "Lab's own module-sharing rule". Limit the path to Core, as INV10 and INV11 do, or name an exported internal subpath, which architecture:public-component-api INV12 counts as public.
| it, such as a desktop page, loads nothing extra. `install()` loads the module | ||
| under every pointer, and where the primary pointer is fine it registers | ||
| nothing (`spec:AST-094` FR24). The recognizers, the arena, the animator, and press | ||
| tracking are not public exports and are not documented for consumers; Core |
There was a problem hiding this comment.
"Press tracking" names no module, and Core's press-tracking exports are the released, documented useMenuPress and usePressFeedback hooks, which the INV13 install row's "an exported or documented internal" signal would then fail. Name the internal piece, or say those hooks stay public.
| default on pages that install TouchSupport, and module-free behavior such as | ||
| press feedback works without it, following the owner's 2026-10-25 decisions | ||
| that components use the module when it is present and that an app installs it | ||
| with `TouchSupport.install()`. This supersedes DEC-1's "without an opt-in". |
There was a problem hiding this comment.
DEC-12 qualifies DEC-1, but FR31 ("Under a finger, on every touch device, an anchored menu or a Popover MUST open by growing out of its trigger") and FR33 still carry no install condition. State the condition once in the Requirements, or in FR31 and FR33.
There was a problem hiding this comment.
The direction in this pull request has engineering-owner approval for this head, but two record defects need fixing first:
- INV13 says Core and Lab reach the touch internals through a package-private entry "that Lab's published build resolves, that is not in Core's exports map", yet Lab's published package resolves Core only through that map and
spec:AST-017FR21 keeps gesture modules out of it, so limit the entry to Core (Lab keeping its own touch handling, as INV10 and INV11 have it) or name an exported internal subpath and amend FR21 andarchitecture:public-component-apiINV12. spec:AST-094FR33's lift sentence still says "On every touch device"; give it the install condition FR31 and FR33's first sentence now carry, or state that the lift runs without the module.
Also consider replacing the useAnimatedLayout example, which does not exist on main yet, and adding INV13 to the record's react-runtime routing. A new commit resets the approval, so an engineering owner re-approves the fixed head by commenting /approve-spec <full head SHA>.
[Automated review]
| and Lab components reach them through a package-private entry that Lab's | ||
| published build resolves, that is not in Core's exports map, and that lint | ||
| limits to Core and Lab, where Lab's access | ||
| follows Lab's own module-sharing rule. `TouchSupport`'s documentation says |
There was a problem hiding this comment.
@astryxdesign/lab is published to npm's canary tag as Babel output, so its Core imports resolve only through Core's exports map; an entry "that is not in Core's exports map" does not resolve from Lab's published build, and adding it to the map conflicts with spec:AST-017 FR21. No record defines "Lab's own module-sharing rule" either. Limit the entry to Core (Lab keeps its own touch handling, as INV10 and INV11 have it), or name an exported internal subpath and amend FR21 and architecture:public-component-api INV12.
| published build resolves, that is not in Core's exports map, and that lint | ||
| limits to Core and Lab, where Lab's access | ||
| follows Lab's own module-sharing rule. `TouchSupport`'s documentation says | ||
| that motion such as `useAnimatedLayout` works without it, that it is called |
There was a problem hiding this comment.
useAnimatedLayout does not exist on main yet; it is only proposed elsewhere. Name motion that exists, or describe it without a name.
| - **FR33 — A ContextMenu under a finger.** On a page that installs | ||
| TouchSupport (DEC-12), with a finger or pen, the long |
There was a problem hiding this comment.
This sentence now carries the install condition, but the lift sentence a few lines below still reads "On every touch device, a ContextMenu that a held finger opens on a block trigger MUST lift the trigger". ContextMenu's long press runs without the module, so give the lift the same condition, or state that it runs without the module.
40df6fb to
da0b25c
Compare
|
Posted by Cindy's agent on her behalf. Cindy's decision: Lab components may use Core's internals. Core exports undocumented internal subpaths ( What this means here:
|
da0b25c to
027620c
Compare
|
Posted by Cindy's agent on her behalf. Thanks, vjeux. Cindy has simplified the touch model: there's no install step. The gesture and touch code is a set of reusable internal modules that components import directly, and the bundler keeps one copy per page. That replaces
What this means per PR:
If these stay idle, we'll make the changes after about 30 minutes, keeping your authorship on the commits. |
There was a problem hiding this comment.
Approval of this proposal waits on an engineering owner's decision. INV13 and spec:AST-080 DEC-5 are new, and the head rewrites architecture:public-component-api INV12, spec:AST-017 FR21 and the INV6 remedy, so an engineering owner records the decision by commenting /approve-spec <full head SHA> on this pull request; the author, who is an engineering owner, may do so. A new commit resets that approval, so these fixes belong first:
- Carve
./internal/*out ofspec:AST-017's FR21–FR24 verification row, its Current-state impact line and FR25 ("An internal label ... does not weaken this rule"), which still forbid the new exports. - Do the same for public-component-api's INV12/INV13 verification row ("A reachable supporting type or behavior is called internal"), and give INV12 the Core-and-Lab limit that FR21 and INV13 state.
- Add a failure signal for importing
@astryxdesign/core/internal/*outside Core and Lab to INV13's verification row, and list DEC-5 in component-style-authoring's Deciding specs section.
[Automated review]
| never exported, as experimental or otherwise, except through Core's | ||
| `./internal/*` subpaths, which are reserved for Astryx's own packages, | ||
| undocumented, outside the compatibility promise, versioned in lockstep with | ||
| Core, and limited by lint to Core and Lab (`architecture:react-component-runtime` INV13). In Core, an experimental public |
There was a problem hiding this comment.
This exception still fails this record's own checks. The FR21–FR24 verification row fails when "a behavior, utility, or internal module without a public contract is exported" (line 916), Current-state impact says Core's behavior modules "stay unexported (FR21)" (878-879), and FR25 says "An internal label ... does not weaken this rule" for export changes (430-439). Carve ./internal/* out of each, or state the exception where FR25 and Platform support set the promise's scope.
| behind a non-exported boundary. Core's `./internal/*` subpaths are the one | ||
| exception: reachable, but reserved for Astryx's own packages, undocumented, | ||
| and outside the compatibility promise (`spec:AST-017` FR21). |
There was a problem hiding this comment.
The INV12/INV13 verification row (line 276) still fails "A reachable supporting type or behavior is called internal", which is exactly what ./internal/* is, so it needs the same carve-out. This sentence also says "reserved for Astryx's own packages" without the "limited by lint to Core and Lab" bound that AST-017 FR21 and INV13 state.
| | INV10 | Shared test over every adopter of the announcement module | An adopter holding its own Effect, observer, timer, or listener for its own-content announcement | | ||
| | INV11 | Source boundary check plus hook tests across StrictMode replay and unmount | A second keyboard listener or measurement in Core; a consumer reading a CSS custom property on the root or a shared ancestor for the height; an export of the hook; a component outside Core measuring by a rule other than AST-099 FR2, or riding the keyboard; a listener left after unmount; a Core rider driven by JavaScript per frame or by its own transition; or more than one layout read per keyboard change | | ||
| | INV12 | Source boundary check plus real-browser tests on a throttled phone profile over a 10-level by 50-sibling tree and two changes in one frame | A per-frame height or layout, a walk past the area or into a subtree other than `display: contents`, an element moved twice for one frame, an added element, or an exported reporting function fails | | ||
| | INV13 | Source boundary check plus bundle analysis of a page with several touch components, and real-browser tests on a throttled phone profile, under a fine pointer, and on a hybrid device | A second copy of a touch module on one page, a component bundling its own copy of a module's primitive, an `import()` of a module in a component, a public export or doc entry for the modules, an install call or installed check in the public API, or a touch listener under a fine primary pointer fails | |
There was a problem hiding this comment.
INV13 says the subpath is "limited by lint to Core and Lab" (line 191), but this row has no failure signal for it and no such lint rule exists yet. Suggest adding "an import of @astryxdesign/core/internal/* outside Core and Lab" to the failure signal.
| deciding_specs: | ||
| [ | ||
| spec:AST-080/DEC-4, | ||
| spec:AST-080/DEC-5, |
There was a problem hiding this comment.
The ## Deciding specs section (line 279) still credits INV6 to DEC-4 alone. A line for DEC-5 there would tell its readers that the lazy-loading remedy was replaced.
…upport; Core and Lab reach internals through ./internal/* (INV13, AST-017 FR21, public-component-api INV12)
…l modules; no install (INV13; AST-080 DEC-5; AST-094 back to plain defaults)
…INV12 rows; Core-and-Lab limit; DEC-5 listed
ad6db58 to
b1d6f9a
Compare
|
/approve-spec b1d6f9a |
There was a problem hiding this comment.
This looks ready to merge: an engineering owner approved this exact head, and the records now carve Core's reserved ./internal/* subpaths out of the spec:AST-017 and architecture:public-component-api checks that forbade them, with INV13's verification row covering the Core-and-Lab lint limit. No further changes are needed in this pull request.
[Automated review]
Decided (Cindy, 2026-10-26): touch behavior comes from a set of reusable internal modules that components import. There's no install step. This supersedes the 2026-10-25 install-entry and dependency-check designs. Core and Lab components may use Core's internals (2026-10-26).
Summary
Every touch component shares one copy of the gesture and touch code instead of each carrying its own. An app does nothing to get touch behavior, and a page loads the shared code once however many components use it. People using a mouse get no touch listeners.
architecture:react-component-runtimeINV13: one always-present set of internal gesture and touch modules (gesture core, recognizers, arena, animator, touch press recognizer).spec:AST-094FR19, FR24).@astryxdesign/core/internal/touch. Core's./internal/*subpaths are reserved for Astryx's own packages: undocumented, outside the compatibility promise, versioned in lockstep with Core, and lint-limited to Core and Lab. The rule is general, so other internals Lab needs (for example a reorder module) use the same text.spec:AST-017FR21: behavior modules stay unexported except through these subpaths.architecture:public-component-apiINV12: these subpaths are the one reachable-but-reserved exception.spec:AST-080DEC-5 (cixzhang 2026-10-26): touch primitives over the budget come from the shared internal modules, or the cost is justified. This supersedes DEC-4's per-component lazy-load remedy; DEC-4's reporting and budget stand.architecture:component-style-authoringINV6 says the same.spec:AST-094is unchanged: touch defaults stay plain, with no install condition.Writing rubric results
import(), no public export or install API, and no touch listener under a fine pointer.