Jac Abstractions: Architectural Inventory#
This document is a complete inventory of every abstraction Jac exposes to user code, organized into the three categories established in the design essay Designing New Abstractions for Jac:
- Language-level keywords -- first-class syntax, parsed into dedicated AST nodes.
- Builtins -- names available without an import, resolved through the runtime.
- Standard library -- modules that must be reached with an explicit
import from.
For each abstraction, the table lists where it is parsed, where it is represented in the AST, and where it is implemented at runtime. The intent is to give contributors a single map from a user-facing concept to the file that owns it, and to make architectural drift visible: when the same abstraction is implemented two different ways, that shows up here.
Category 1 -- Language-Level Keywords#
All nine keywords flow through a single, unified pipeline: tokenized in
jac0core/parser/tokens.na.jac, parsed by jac0core/parser/impl/parser.impl.jac
into a small set of AST node types defined in jac0core/unitree.jac, and
implemented by JacRuntimeInterface in jac0core/runtime.jac. Both the
bootstrap compiler (jac0.py) and the full compiler share this front end.
| Keyword | Token | AST node | Runtime |
|---|---|---|---|
walker |
KW_WALKER -- tokens.na.jac:48 |
Archetype (discriminated by arch_type) -- unitree.jac:636 |
WalkerArchetype (constructs.jac); traversal in JacWalker |
node |
KW_NODE -- tokens.na.jac:46 |
Archetype |
NodeArchetype + NodeAnchor -- archetype.jac:108 |
edge |
KW_EDGE -- tokens.na.jac:47 |
Archetype |
EdgeArchetype + EdgeAnchor -- archetype.jac:122 |
visit |
KW_VISIT -- tokens.na.jac:88 |
VisitStmt -- unitree.jac:938 |
JacWalker.visit |
spawn |
KW_SPAWN -- tokens.na.jac:89 |
unpack-position modifier -- parser.impl.jac:1309 | spawn_call / spawn_walker -- runtime.jac:272,822 |
entry |
KW_ENTRY -- tokens.na.jac:90 |
Ability (in archetype) or module-level with entry block |
_jac_entry_funcs_ ClassVar; dispatched by _execute_entries -- runtime.jac:239 |
exit |
KW_EXIT -- tokens.na.jac:91 |
Ability |
_jac_exit_funcs_ ClassVar; _execute_exits -- runtime.jac:249 |
can |
KW_CAN -- tokens.na.jac:50 |
Ability -- unitree.jac:688 |
compiled to a plain Python method on the archetype class |
has |
KW_HAS -- tokens.na.jac:49 |
HasVar -- unitree.jac:781 |
dataclass field; wrapped by JacField (jac0) or _.field() (full compiler) |
Notes
walker/node/edgeshare the sameArchetypeAST node. The keyword is preserved as thearch_type: Tokendiscriminator rather than producing three distinct node types.entryis reused for two distinct purposes: a walker lifecycle hook inside an archetype (paired withexit) and a module-levelwith entryblock. Both flow through the same token but produce different AST shapes.spawnis parsed as a modifier on the unpack form rather than as a standalone statement.
Category 2 -- Builtins#
Builtins are co-located in a single module
(jaclang/runtimelib/builtin.jac)
and resolved through one mechanism -- except for printgraph, which is the
documented exception.
The pattern for every builtin except printgraph: a def _get_X thunk in
builtin.jac, an implementation in
runtimelib/impl/builtin.impl.jac
that returns _get_jac().<method>, and a module-level __getattr__ that
resolves the public name on first access. Every backing implementation lives
on JacRuntimeInterface in jac0core/runtime.jac.
| Builtin | Declaration | Resolver | Runtime impl |
|---|---|---|---|
jid() |
builtin.jac:33 _get_jid |
builtin.impl.jac:164 → object_ref |
JacRuntimeInterface.object_ref -- runtime.jac:390 |
jobj() |
builtin.jac:35 | builtin.impl.jac:159 → get_object |
runtime.jac:383 |
grant() |
builtin.jac:37 | builtin.impl.jac:154 → perm_grant |
JacAccessValidation.perm_grant -- runtime.jac:117 |
revoke() |
builtin.jac:39 | builtin.impl.jac:149 → perm_revoke |
runtime.jac:122 |
allroots() |
builtin.jac:41 | builtin.impl.jac:144 → get_all_root |
runtime.jac:526 |
save() |
builtin.jac:43 | builtin.impl.jac:139 → save |
runtime.jac:536 |
commit() |
builtin.jac:45 | builtin.impl.jac:100 → commit |
runtime.jac:373 |
store() |
builtin.jac:47 | builtin.impl.jac:105 → store |
runtime.jac:953 |
printgraph() |
builtin.jac:55-65 -- direct def (not a thunk) |
builtin.impl.jac:36-63 -- full body, dispatches DOT vs JSON | runtime.jac:345 |
printgraph is declared with a complete signature instead of a lazy thunk
because it has a rich keyword-argument surface (depth, traversal, edge type,
BFS toggle, edge/node limits, output file, format) that benefits from being
visible to IDE completion and the type checker. Functionally it still
delegates to JacRuntimeInterface.
The __all__ in builtin.jac also exports adjacent names that fall outside
the blog post's nine but follow the same registration pattern: llm,
archetype_alias, the access-level singletons (NoPerm, ReadPerm,
ConnectPerm, WritePerm), and the decorators restspec and schedule.
Category 3 -- Standard Library#
Each package ships its own lib.jac-style module. The blog post's principle
-- every standard-library abstraction needs an explicit import from -- holds
across all packages. The shape of the library, however, varies.
Core Jac (jac/jaclang/)#
The user-facing entry point is
jaclang/lib.jac,
which re-exports everything from
jac0core/jaclib.jac.
The consolidated __all__ covers:
- Archetypes:
Node,Edge,Walker,Obj,Root,GenericEdge,JsxElement - Graph operations:
spawn,visit,disengage,connect,disconnect,refs,arefs,filter_on,build_edge,destroy - Walker support types:
OPath,DSFunc,EdgeDir - Context & root:
root,create_j_context,get_context - Lifecycle:
on_entry,on_exit - AI/MTIR:
get_mtir,sem,call_llm,by_operator - Concurrency:
thread_run,thread_wait - Compiler/test plumbing:
field,impl_patch_filename,jac_test,jsx,log_report,assign_all,safe_subscript
byLLM (jac-byllm/byllm/)#
byllm/lib.jac
exposes its own __all__ under the byllm namespace:
- Runtime:
by(thebyoperator entry point),MockLLM,Model,ModelPool,MTIR,MTRuntime - Message/tool types:
Message,MessageRole,Tool,ToolCallResultMsg,Image,Video,StreamEvent,IterationAction,IterationContext,MockToolCall - MCP:
McpClient,McpTool - Errors:
ByLLMError,AuthenticationError,RateLimitError,ModelNotFoundError,OutputConversionError,UnknownToolError,FinishToolError,ConfigurationError,McpError - Telemetry & batching:
register_agent_callback,dispatch_batch,mark_serialize
jac-scale (jac-scale/jac_scale/)#
jac_scale/lib.jac
is intentionally minimal -- a single kvstore() factory that returns a Db
instance backed by MongoDB or Redis. The substantive abstractions live in
jac_scale/abstractions/
as interface contracts:
database_provider.jac-- provider interfacedeployment_target.jac-- deployment abstractionimage_registry.jac-- container registry interfacelogger.jac-- logging abstractionmetrics.jac-- metrics interfacemodels/--deployment_result.jac,resource_status.jac
jac-client (jac-client/jac_client/)#
No lib.jac. Client-side code is written in .cl.jac files that are
compiled to TypeScript/JavaScript by the client toolchain; the package
exposes its capabilities through the plugin system rather than a curated
re-export module.
jac-mcp (jac-mcp/jac_mcp/)#
No lib.jac. The package is structured around a plugin entry point
(plugin.jac) plus modules for server.jac, tools.jac, resources.jac,
prompts.jac, mode.jac, and compiler_bridge.jac.
Cross-Category Consistency#
Keywords -- uniform. All nine flow through the same parser, share a small
set of AST nodes (Archetype, Ability, HasVar, VisitStmt), and resolve
to methods on JacRuntimeInterface. There is no syntactic-sugar path that
bypasses the AST. Bootstrap and full compiler share the front end.
Builtins -- near-uniform. Eight of the nine use the lazy-thunk
registration pattern in runtimelib/builtin.jac. printgraph is the
deliberate exception, declared with a full signature for IDE/type-checker
ergonomics. All nine ultimately delegate to JacRuntimeInterface. The same
module also exports several adjacent names (llm, access levels, decorators)
that follow the builtin pattern but are not part of the canonical nine --
worth keeping in mind when reasoning about "what counts as a builtin".
Standard library -- convention varies. The import from requirement
holds everywhere, but the four library packages do not share a common shape:
| Package | lib.jac |
Style |
|---|---|---|
jaclang |
yes | single consolidated __all__ re-exporting jac0core/jaclib.jac |
byllm |
yes | own __all__ in own namespace |
jac_scale |
yes (minimal) | lib.jac exposes only kvstore; substantive abstractions in abstractions/ directory |
jac_client |
no | plugin-based, no curated re-export |
jac_mcp |
no | plugin-based, no curated re-export |
For someone learning the ecosystem, this means the surface for "what's
importable from this package" is discovered differently for each package. If
the goal is the blog post's vision of a uniform standard library, the gap
worth closing is giving each plugin a lib.jac with a curated __all__
analogous to jac0core/jaclib.jac.
How to Update This Document#
When adding a new abstraction:
- Keyword -- add a row to Category 1. The token must be defined in
jac0core/parser/tokens.na.jac, the AST node must live injac0core/unitree.jac, and the runtime entry point belongs onJacRuntimeInterfaceinjac0core/runtime.jac. - Builtin -- add the
_get_Xthunk inruntimelib/builtin.jac, the resolver inruntimelib/impl/builtin.impl.jac, the implementation onJacRuntimeInterface, and the public name in__all__. - Standard-library export -- add it to the relevant package's
lib.jac__all__. If the package does not yet have alib.jac, consider adding one rather than relying on per-module imports.