feat: SoA tree view channel plugin#555
Conversation
Renders State of Affairs nodes from AD4M perspectives as a collapsible tree view with: - Modality icons (🔭 observation, 💭 belief, 🎯 intention, etc.) - Confidence progress bars - Status badges (color-coded: active/completed/blocked) - Priority indicators - Tag pills - Relationship badges (supports, contradicts, requires, etc.) - Auto-discovery of parent-child hierarchy via soa://rel_parent Read-only for now - editing to follow. Follows the same architecture as table-view: - Preact custom element via @coasys/flux-react-web - Queries raw soa:// links and builds tree in memory - Uses j-text and flux-ui CSS variables for consistent styling
✅ Deploy Preview for fluxsocial-dev ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds a new SoA tree view package and integrates it into the app: new Preact components and styles, package/vite configs, app dependency and module declaration, ChannelView enum addition and view option, and dynamic import support. Changes
Sequence Diagram(s)sequenceDiagram
participant App as App (CustomElement)
participant SoATreeView as SoATreeView
participant Perspective as Perspective (AD4M)
participant SoANode as SoANode
App->>SoATreeView: render(perspective, agent, source)
SoATreeView->>Perspective: queryLinks(filter: soa://*)
Perspective-->>SoATreeView: LinkExpression[]
SoATreeView->>SoATreeView: parse links & buildTree()
SoATreeView->>SoANode: render(rootNode, depth=0)
SoANode->>SoANode: toggle expand / render details
SoANode->>SoANode: render children (recursive)
SoATreeView-->>App: tree rendered
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🧹 Nitpick comments (5)
views/soa-tree-view/package.json (2)
5-5: Consider filling in the author field.The
authorfield is currently an empty string. Consider adding the appropriate author information for package attribution.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@views/soa-tree-view/package.json` at line 5, The package.json currently has an empty "author" field; update the "author" value to include the package owner (e.g., a person name and optional email or organization) by populating the "author" property in package.json (the "author" key) with the appropriate string or object format so the package has correct attribution.
12-12: Placeholder test script noted.The test script currently exits with an error. Per the PR description, this is a read-only view with editing to follow in a separate PR. Consider adding at least basic unit tests for the tree-building logic in a follow-up.
Would you like me to help create a GitHub issue to track adding tests for this package?
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@views/soa-tree-view/package.json` at line 12, The package.json currently contains a placeholder failing "test" script; update the "test" script in package.json to a meaningful default (e.g., a no-op that exits 0 or a command that runs the chosen test runner) so CI won't fail, and create a follow-up GitHub issue to add unit tests for the tree-building logic (track functions/classes responsible for tree construction such as the module that builds/parses the SOA tree). Ensure the issue references the package.json "test" script and the specific tree-building module so adding tests (e.g., for buildTree/parseTree) is tracked.views/soa-tree-view/src/components/SoATreeView.tsx (2)
25-34: Unused constant:PROPERTY_PREDICATESis defined but never referenced.This constant is not used anywhere in the file. Consider removing it to avoid confusion, or add a comment if it's intended for future use.
🧹 Proposed fix
-const PROPERTY_PREDICATES = [ - 'soa://title', - 'soa://modality', - 'soa://description', - 'soa://confidence', - 'soa://status', - 'soa://tags', - 'soa://priority', - 'soa://source', -]; -🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@views/soa-tree-view/src/components/SoATreeView.tsx` around lines 25 - 34, PROPERTY_PREDICATES in SoATreeView.tsx is defined but never used; either remove the constant to eliminate dead code or keep it with an explanatory comment or export if it's intended for future use. Locate the const PROPERTY_PREDICATES declaration and either delete it or add a TODO comment explaining its planned usage (or change it to an exported symbol if other modules will reference it).
144-147: Consider using server-side filtering withperspective.get()andLinkQuery.The current approach fetches all links and filters client-side, which may be inefficient for large perspectives. AD4M's
LinkQuerysupports predicate filtering, and the codebase uses it widely. However, the API appears to support exact predicate matching (e.g.,new LinkQuery({ predicate: 'ad4m://has_child' })). For filtering bysoa://prefix, you can either:
- Query each predicate individually using the
PROPERTY_PREDICATESandRELATIONSHIP_PREDICATESarrays, or- Check if AD4M supports pattern matching in the
predicatefield for prefix queries.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@views/soa-tree-view/src/components/SoATreeView.tsx` around lines 144 - 147, The code in SoATreeView.tsx currently calls perspective.queryLinks({}) and filters client-side for predicates starting with "soa://"; change this to use server-side filtering via LinkQuery/perspective.get to avoid fetching all links. Either iterate the PROPERTY_PREDICATES and RELATIONSHIP_PREDICATES arrays and call perspective.get(new LinkQuery({ predicate: '<each-predicate>' })) to collect matching links, or (if supported) use a LinkQuery with a predicate pattern/prefix for "soa://". Replace the allLinks = await perspective.queryLinks({}) + client filter logic with the server-side queries and merge their results into soaLinks.views/soa-tree-view/src/App.tsx (1)
5-5: Use the public export instead of internal package paths.
AgentClientis exported from@coasys/ad4mand should be imported from the public entry point instead of relying on internal package structure. This is already used elsewhere in the codebase and is consistent with howPerspectiveProxyis imported in the same file (line 2).♻️ Proposed fix
-import { AgentClient } from '@coasys/ad4m/lib/src/agent/AgentClient'; +import { AgentClient } from '@coasys/ad4m';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@views/soa-tree-view/src/App.tsx` at line 5, Replace the internal-package import of AgentClient with the public export: change the import "import { AgentClient } from '@coasys/ad4m/lib/src/agent/AgentClient';" to import AgentClient from the package entry point (same style as PerspectiveProxy), i.e., import AgentClient from '@coasys/ad4m', so the file uses the public API surface instead of internal paths and stays consistent with PerspectiveProxy.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@views/soa-tree-view/src/App.tsx`:
- Line 14: The component currently returns a raw string when missing perspective
or agent (the line checking perspective?.uuid and agent); replace that plain
string return with a proper JSX error element consistent with SoATreeView's
error/loading UI (use the same structure and classNames/markup used by
SoATreeView) so the function returns a valid JSX.Element and is
styled/structured like other states; update the conditional in App (the if
(!perspective?.uuid || !agent) branch) to return that JSX error node instead of
the raw string.
- Line 4: Replace the no-op TypeScript declaration import
'@coasys/flux-ui/dist/main.d.ts' in App.tsx with a runtime import of the
library's actual entry (either the JS module '@coasys/flux-ui' or the CSS/JS
asset like '@coasys/flux-ui/dist/main.css' / '@coasys/flux-ui/dist/main.js'),
removing the .d.ts import and importing the real runtime artifact so
styles/components are loaded at runtime; ensure any type-only imports remain as
TypeScript import type statements elsewhere if needed.
In `@views/soa-tree-view/src/components/SoANode.tsx`:
- Around line 106-115: The tags rendering in SoANode.tsx currently does
node.tags.split(',') which yields [''] for an empty string and produces an empty
pill; update the mapping to filter out empty or whitespace-only entries (e.g.,
after splitting call .map(t=>t.trim()).filter(t => t !== '') or
.filter(Boolean)) before rendering so only non-empty tags produce <span
key={...} className={styles.tag}> elements; ensure the key uses the trimmed tag
to avoid duplicate/empty keys.
In `@views/soa-tree-view/src/components/SoATreeView.tsx`:
- Around line 88-95: Parsed numeric fields node.confidence and node.priority can
become NaN when parseLiteralString yields non-numeric input; update the parsing
in SoATreeView.tsx so that after calling parseLiteralString you validate the
numeric conversion (use Number.parseFloat/Number.parseInt and check
Number.isFinite or isNaN) and apply a safe fallback (e.g., set confidence to
null or 0 and priority to a default integer) and optionally clamp confidence to
a 0–1 range; ensure you only assign the validated value to node.confidence and
node.priority to avoid propagating NaN into the UI.
In `@views/soa-tree-view/vite.config.ts`:
- Around line 7-15: The plugins array in vite.config.ts contains a stray comma
after the preact(...) entry which creates a sparse array; remove the extra comma
following the preact({ ... }) plugin so the array is contiguous (plugins: [
preact(...), cssInjectedByJsPlugin(), ]) and ensure only valid plugin entries
(preact and cssInjectedByJsPlugin) remain.
---
Nitpick comments:
In `@views/soa-tree-view/package.json`:
- Line 5: The package.json currently has an empty "author" field; update the
"author" value to include the package owner (e.g., a person name and optional
email or organization) by populating the "author" property in package.json (the
"author" key) with the appropriate string or object format so the package has
correct attribution.
- Line 12: The package.json currently contains a placeholder failing "test"
script; update the "test" script in package.json to a meaningful default (e.g.,
a no-op that exits 0 or a command that runs the chosen test runner) so CI won't
fail, and create a follow-up GitHub issue to add unit tests for the
tree-building logic (track functions/classes responsible for tree construction
such as the module that builds/parses the SOA tree). Ensure the issue references
the package.json "test" script and the specific tree-building module so adding
tests (e.g., for buildTree/parseTree) is tracked.
In `@views/soa-tree-view/src/App.tsx`:
- Line 5: Replace the internal-package import of AgentClient with the public
export: change the import "import { AgentClient } from
'@coasys/ad4m/lib/src/agent/AgentClient';" to import AgentClient from the
package entry point (same style as PerspectiveProxy), i.e., import AgentClient
from '@coasys/ad4m', so the file uses the public API surface instead of internal
paths and stays consistent with PerspectiveProxy.
In `@views/soa-tree-view/src/components/SoATreeView.tsx`:
- Around line 25-34: PROPERTY_PREDICATES in SoATreeView.tsx is defined but never
used; either remove the constant to eliminate dead code or keep it with an
explanatory comment or export if it's intended for future use. Locate the const
PROPERTY_PREDICATES declaration and either delete it or add a TODO comment
explaining its planned usage (or change it to an exported symbol if other
modules will reference it).
- Around line 144-147: The code in SoATreeView.tsx currently calls
perspective.queryLinks({}) and filters client-side for predicates starting with
"soa://"; change this to use server-side filtering via LinkQuery/perspective.get
to avoid fetching all links. Either iterate the PROPERTY_PREDICATES and
RELATIONSHIP_PREDICATES arrays and call perspective.get(new LinkQuery({
predicate: '<each-predicate>' })) to collect matching links, or (if supported)
use a LinkQuery with a predicate pattern/prefix for "soa://". Replace the
allLinks = await perspective.queryLinks({}) + client filter logic with the
server-side queries and merge their results into soaLinks.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: baac8448-4149-477a-a76a-37ac5aec0a93
📒 Files selected for processing (11)
app/package.jsonapp/src/utils/fetchFluxApp.tsviews/soa-tree-view/package.jsonviews/soa-tree-view/src/App.module.cssviews/soa-tree-view/src/App.tsxviews/soa-tree-view/src/components/SoANode.module.cssviews/soa-tree-view/src/components/SoANode.tsxviews/soa-tree-view/src/components/SoATreeView.module.cssviews/soa-tree-view/src/components/SoATreeView.tsxviews/soa-tree-view/src/main.tsviews/soa-tree-view/vite.config.ts
- Add author field to package.json - Change test script to exit 0 (tests to be implemented in follow-up) - Fix AgentClient import to use public export from @coasys/ad4m - Remove unused PROPERTY_PREDICATES constant
- Add SoATree to ChannelView enum in flux-types - Add plugin to viewOptions in constants/index.ts (visible in UI now) - Add module declaration for TypeScript - Fix CodeRabbit review comments: - Add author field in package.json - Fix test script to exit 0 instead of 1 - Import AgentClient from public @coasys/ad4m export - Return JSX error element instead of raw string - Import main.css instead of .d.ts declaration - Remove unused PROPERTY_PREDICATES constant - Add NaN handling for confidence (clamped 0-1) and priority - Filter empty tags after split/trim - Remove stray comma in vite.config.ts plugins array - Add error style to App.module.css
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@views/soa-tree-view/src/components/SoANode.tsx`:
- Around line 45-52: The header currently uses hasDetails (which includes
node.status and node.priority) to show a caret and toggle details even when no
hidden content exists; update the expandability logic so hasDetails only
reflects fields that render inside the collapsible details (e.g.,
node.description, node.confidence, node.tags, node.source,
node.relationships.length > 0) and remove status/priority from that check. Then
change the clickable header element in SoANode from a plain div to a semantic
button and wire its aria-expanded to the component's expanded state; only render
the caret/toggle control when the new hasDetails is true so headers without
hidden content don’t show a disclosure affordance, and rely on the button for
keyboard accessibility instead of manual key handlers.
In `@views/soa-tree-view/src/components/SoATreeView.tsx`:
- Around line 41-61: buildTree currently only creates nodes when it sees a
'soa://title' link, causing items referenced only by relationships (e.g.,
'soa://rel_parent') to be omitted; update buildTree to register placeholder
SoANodeData entries in nodeMap for every unique source and target encountered in
soaLinks (use the existing nodeMap Map<string, SoANodeData> and the SoANodeData
shape) before or during processing, then when a 'soa://title' link is found
set/overwrite the title via parseLiteralString, leaving other fields as defaults
(modality 'observation', empty children/relationships) so the (untitled)
fallback in SoANode becomes reachable; ensure relationship handling
(parent/child link processing) uses these placeholders so rel_parent and other
predicates don't drop nodes.
- Around line 104-109: The parent-child assembly in SoATreeView.tsx currently
assumes an acyclic graph and can create cycles that cause infinite recursion in
SoANode render; update the loop over parentChildLinks to detect and reject
back-edges before pushing into parentNode.children (or alternatively record
ancestor paths during SoANode render). Concretely, for each { parent, child }
check that childNode is not an ancestor of parentNode (walk parent pointers or
use nodeMap to follow parent links) and skip/avoid adding the child if it would
create a cycle; also consider adding a boolean or Set (e.g., visitedPaths) to
SoANode render as a defensive check to stop recursion if a cycle is encountered.
Ensure checks reference parentChildLinks, nodeMap, parentNode.children, childSet
and the SoANode render/build path so cycles are rejected or short-circuited.
- Around line 84-86: The parser currently assigns any finite integer to
node.priority for pred 'soa://priority', which can produce out-of-range badges;
after parsing the integer (from parseLiteralString(target)), validate it's a
finite integer and clamp it into the expected 1–5 range before setting
node.priority (e.g., if Number.isFinite(prio) use Math.max(1, Math.min(5,
prio)), otherwise set node.priority = undefined). Update the assignment in the
'soa://priority' branch to apply this clamp and preserve undefined for invalid
parses.
- Line 137: The code calls perspective.queryLinks({}) but queryLinks is not on
the public PerspectiveProxy API; replace that call with the public API pattern
used elsewhere: construct a LinkQuery and call perspective.get(new
LinkQuery(...)) instead (mirror the approach in SubjectRepository where
perspective.get(new LinkQuery(...)) is used). Update the call site that
currently references perspective.queryLinks to use LinkQuery and
perspective.get, ensuring imports/reference to LinkQuery and the
PerspectiveProxy get method are used rather than the lower-level
PerspectiveClient API.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: e2de1a4e-84ab-4321-9ba0-62e28b5efa7c
📒 Files selected for processing (9)
app/src/constants/index.tsapp/src/modules.tspackages/types/src/index.tsviews/soa-tree-view/package.jsonviews/soa-tree-view/src/App.module.cssviews/soa-tree-view/src/App.tsxviews/soa-tree-view/src/components/SoANode.tsxviews/soa-tree-view/src/components/SoATreeView.tsxviews/soa-tree-view/vite.config.ts
🚧 Files skipped from review as they are similar to previous changes (4)
- views/soa-tree-view/package.json
- views/soa-tree-view/src/App.tsx
- views/soa-tree-view/src/App.module.css
- views/soa-tree-view/vite.config.ts
Address CodeRabbit review feedback: - Import AgentClient as type from public @coasys/ad4m export - Remove internal package path import
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@views/soa-tree-view/src/App.tsx`:
- Around line 7-15: The Props type and App component currently include an unused
agent and the render guard checks agent; remove the unnecessary agent usage by
either (A) deleting agent from the Props type and the App parameter list and
removing the agent check from the early-return condition (leave only
perspective?.uuid and source checks), or (B) if you intend to reserve agent for
later, mark it optional (agent?: AgentClient) in Props and remove the agent
check from the if condition so rendering depends only on perspective/source;
update the App signature and any references to match the chosen approach
(identify the Props type, App function, and the early-return conditional to
change).
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 2bc4ecf8-169e-47b2-9fcd-6f00817d91e7
📒 Files selected for processing (1)
views/soa-tree-view/src/App.tsx
- Fixed buildTree to discover nodes from all link sources/targets, not just soa://title - Added cycle detection to parent-child tree assembly to prevent infinite recursion - Replaced non-public queryLinks() with perspective.get(new LinkQuery()) - Fixed hasDetails to only count fields in collapsible section - Converted header div to semantic button with aria-expanded - Clamped priority to 1-5 range for UI badge rendering - Fixed empty tag rendering by filtering empty strings after split - Added runtime CSS import comment to clarify .css import purpose
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (2)
views/soa-tree-view/src/components/SoANode.tsx (1)
46-51: Minor edge case:hasDetailsmay be true for tags that resolve to empty.When
node.tagsis","or" , ",hasDetailsevaluates to true, but after filtering in the render (line 111), no pills appear. This causes the caret to show an expandable indicator for an effectively empty details section.Consider aligning the check with the render logic:
🔧 Suggested refinement
+ const parsedTags = node.tags?.split(',').map((t) => t.trim()).filter(Boolean) ?? []; const hasDetails = node.description || node.confidence != null || - node.tags || + parsedTags.length > 0 || node.source || node.relationships.length > 0;Then reuse
parsedTagsin the render section (lines 107-116).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@views/soa-tree-view/src/components/SoANode.tsx` around lines 46 - 51, The hasDetails check currently treats node.tags like a truthy string even when it parses to no tags; update SoANode to compute parsedTags (split on ',', map trim, filter(Boolean)) once (same logic used in the render) and change hasDetails to use parsedTags.length > 0 instead of node.tags, and then reuse parsedTags in the render (where pills are created) so the caret/expandable indicator only appears when there are actual non-empty tags; ensure other parts that referenced node.tags keep behavior consistent by using parsedTags where appropriate.views/soa-tree-view/src/components/SoATreeView.tsx (1)
128-150: Performance consideration for large graphs.
wouldCreateCycleiterates over allparentChildLinkson each DFS step, and it's called for every parent-child link. For a graph withLlinks andNnodes, this yields O(L × N × L) worst-case complexity.For typical SoA trees (dozens to hundreds of nodes), this is fine. If you anticipate larger graphs, consider building an adjacency list once before the loop.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@views/soa-tree-view/src/components/SoATreeView.tsx` around lines 128 - 150, The DFS in wouldCreateCycle is repeatedly scanning parentChildLinks causing O(L^2) work; fix by precomputing a reverse adjacency map (child -> parents) once (e.g., const reverseAdj = new Map<string,string[]>()) from parentChildLinks and then update wouldCreateCycle to iterate reverseAdj.get(current) instead of looping parentChildLinks; reference parentChildLinks and the wouldCreateCycle function so the map is built before calling wouldCreateCycle and used inside it for efficient lookups.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@views/soa-tree-view/src/components/SoATreeView.tsx`:
- Around line 178-218: The source prop is unused in SoATreeView causing a
misleading dependency; either remove it from Props and the effect dependency
array, or use it to scope the query by passing it into the LinkQuery when
calling perspective.get. Update the component by editing SoATreeView: if you
choose to use source, add it to the new LinkQuery({ source }) (or appropriate
filter field) and ensure loadTree filters/results respect source; if you choose
to remove it, delete source from the Props type and remove source from the
effect dependency list so only perspective?.uuid remains.
---
Nitpick comments:
In `@views/soa-tree-view/src/components/SoANode.tsx`:
- Around line 46-51: The hasDetails check currently treats node.tags like a
truthy string even when it parses to no tags; update SoANode to compute
parsedTags (split on ',', map trim, filter(Boolean)) once (same logic used in
the render) and change hasDetails to use parsedTags.length > 0 instead of
node.tags, and then reuse parsedTags in the render (where pills are created) so
the caret/expandable indicator only appears when there are actual non-empty
tags; ensure other parts that referenced node.tags keep behavior consistent by
using parsedTags where appropriate.
In `@views/soa-tree-view/src/components/SoATreeView.tsx`:
- Around line 128-150: The DFS in wouldCreateCycle is repeatedly scanning
parentChildLinks causing O(L^2) work; fix by precomputing a reverse adjacency
map (child -> parents) once (e.g., const reverseAdj = new
Map<string,string[]>()) from parentChildLinks and then update wouldCreateCycle
to iterate reverseAdj.get(current) instead of looping parentChildLinks;
reference parentChildLinks and the wouldCreateCycle function so the map is built
before calling wouldCreateCycle and used inside it for efficient lookups.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 0446ed84-83c6-4593-bd11-b93b1a87bec2
📒 Files selected for processing (3)
views/soa-tree-view/src/App.tsxviews/soa-tree-view/src/components/SoANode.tsxviews/soa-tree-view/src/components/SoATreeView.tsx
🚧 Files skipped from review as they are similar to previous changes (1)
- views/soa-tree-view/src/App.tsx
…etails check - Remove unused source prop from SoATreeView Props - Pre-compute parsedTags to avoid empty tags showing expandable caret - Reuse parsedTags in render for consistency Addresses CodeRabbit feedback on PR #555
SoA Tree View
A new Flux channel plugin that renders State of Affairs (SoA) nodes as a collapsible tree view.
What is SoA?
SoA is an ontology for knowledge representation in AD4M (see ad4m#704). Every node has a modality (belief, observation, intention, vision, plan, skill) and properties like confidence, status, and tags. Nodes connect via typed relationships (supports, contradicts, requires, etc.).
Features
soa://rel_parentlinksArchitecture
Follows the standard Flux view pattern:
@coasys/flux-react-websoa://links from the perspectivej-textand flux-ui CSS variables for consistent stylingRead-only for now
Editing will follow in a separate PR.
How to test
Related: ad4m#704 (SoA ontology + static soa language)
Summary by CodeRabbit
New Features
Chores
depends on d9011e34-8219-4ba1-bdd1-7d104a25cd1f/91215135-9143-46eb-8f5d-b38504a40392#704