Welcome 👋
Every Neo class knows its parent, and Neo.manager.ClassHierarchy.isA(descendant, ancestor) answers "does this class extend that one?" by walking up that chain. It has a small bug, and fixing it is a good first PR: one method, a failing test first, then the fix.
This is a good first issue for Hacktoberfest 2026. It needs Node.js and a clone of this repository: no Docker, no AI setup.
[!IMPORTANT]
Reserved for first-time Neo.mjs contributors. The maintainer team will not claim, edit, or implement this issue through 2026-10-31. Comment here to claim it, and ask anything on this issue.
Set up once: follow Set up the engine from a clone, including npm run bundle-browser-deps: without it the unit suite selects zero tests.
The bug
isA() stops walking as soon as it reaches Neo.component.Base, unless the ancestor asked about is Neo.core.Base. That shortcut assumed nothing sits between the two. Since then, Neo.component.Base extends Neo.component.Abstract, so the shortcut now hides a real ancestor. Measured on dev (2026-09-23):
| Call |
Returns |
Should return |
isA('Neo.button.Base', 'Neo.component.Base') |
true |
true |
isA('Neo.button.Base', 'Neo.component.Abstract') |
false |
true |
isA('Neo.button.Base', 'Neo.core.Base') |
true |
true |
ClassHierarchy.get('Neo.component.Base').parentClassName is 'Neo.component.Abstract'.
Where things live
- Source:
src/manager/ClassHierarchy.mjs, method isA()
- New test file:
test/playwright/unit/manager/ClassHierarchy.spec.mjs
- Pattern to copy:
test/playwright/unit/manager/Instance.spec.mjs (the setup() call and imports). Import src/button/Base.mjs in the spec so the button classes are registered.
Done when
Out of scope
Other ClassHierarchy methods, and mixins (they are not in the parent chain).
While you wait for review
You just pinned one small piece of a multi-threaded application engine: in Neo.mjs your app's code runs inside a web worker, and the main thread only paints. Two things worth ten minutes:
- See the part nobody expects. Run
npm run build-themes -- -n -e dev -t all and npm run server-start, open apps/workstation/, and drag a tab out of the browser window. It becomes its own OS window, still running, with the same component instances and the same worker. Benefits: Introduction explains why that is possible.
- Your coding agent can go much further here. The skills this repository's AI maintainers work with are published on npm as NEO_INLINE_CODE_38, and they need no Knowledge Base and no Memory Core. With Claude or GPT beside you, the
help wanted issues are within reach after this one.
Maintainer notes
Curated under #18985 (Hacktoberfest 2026). Found work: the three calls above were run in the unit environment at dev (2026-09-23); isA has no caller in src/ today, so the fix carries no behaviour risk beyond the answer itself.
Live latest-open sweep: the latest 20 open issues at 2026-09-23T14:24Z; no equivalent (ClassHierarchy title search: one closed issue, isA: none). A2A: no claim on this scope. Memory Core: no prior decision.
Origin Session ID: 3be453e4-8b04-4865-be62-4cff34f4e0c6
Welcome 👋
Every Neo class knows its parent, and
Neo.manager.ClassHierarchy.isA(descendant, ancestor)answers "does this class extend that one?" by walking up that chain. It has a small bug, and fixing it is a good first PR: one method, a failing test first, then the fix.This is a good first issue for Hacktoberfest 2026. It needs Node.js and a clone of this repository: no Docker, no AI setup.
Set up once: follow Set up the engine from a clone, including
npm run bundle-browser-deps: without it the unit suite selects zero tests.The bug
isA()stops walking as soon as it reachesNeo.component.Base, unless the ancestor asked about isNeo.core.Base. That shortcut assumed nothing sits between the two. Since then,Neo.component.BaseextendsNeo.component.Abstract, so the shortcut now hides a real ancestor. Measured ondev(2026-09-23):isA('Neo.button.Base', 'Neo.component.Base')truetrueisA('Neo.button.Base', 'Neo.component.Abstract')falsetrueisA('Neo.button.Base', 'Neo.core.Base')truetrueClassHierarchy.get('Neo.component.Base').parentClassNameis'Neo.component.Abstract'.Where things live
src/manager/ClassHierarchy.mjs, methodisA()test/playwright/unit/manager/ClassHierarchy.spec.mjstest/playwright/unit/manager/Instance.spec.mjs(thesetup()call and imports). Importsrc/button/Base.mjsin the spec so the button classes are registered.Done when
Neo.component.Abstractcase: paste that failing output in your PR.isA()no longer stops early atNeo.component.Base: the walk ends atNeo.core.Baseanyway, and the answer cache (isAQueryMap) stays.true), a direct parent, an unrelated class (false), and a second identical call returning the same answer.npm run test-unit -- test/playwright/unit/manager/ClassHierarchy.spec.mjspasses.npm run test-unitpasses.Out of scope
Other
ClassHierarchymethods, and mixins (they are not in the parent chain).While you wait for review
You just pinned one small piece of a multi-threaded application engine: in Neo.mjs your app's code runs inside a web worker, and the main thread only paints. Two things worth ten minutes:
npm run build-themes -- -n -e dev -t allandnpm run server-start, openapps/workstation/, and drag a tab out of the browser window. It becomes its own OS window, still running, with the same component instances and the same worker. Benefits: Introduction explains why that is possible.help wantedissues are within reach after this one.Maintainer notes
Curated under #18985 (Hacktoberfest 2026). Found work: the three calls above were run in the unit environment at
dev(2026-09-23);isAhas no caller insrc/today, so the fix carries no behaviour risk beyond the answer itself. Live latest-open sweep: the latest 20 open issues at 2026-09-23T14:24Z; no equivalent (ClassHierarchytitle search: one closed issue,isA: none). A2A: no claim on this scope. Memory Core: no prior decision.Origin Session ID: 3be453e4-8b04-4865-be62-4cff34f4e0c6