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. |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
📝 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)
SoANode-->>SoATreeView: subtree rendered
SoATreeView-->>App: tree rendered
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes 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 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
| const hasDetails = | ||
| node.description || | ||
| node.confidence != null || | ||
| node.status || | ||
| node.tags || | ||
| node.priority != null || | ||
| node.source || | ||
| node.relationships.length > 0; |
There was a problem hiding this comment.
Use a real disclosure control only when there is hidden content.
hasDetails currently counts status and priority, but those values only render in the header. Nodes with only those fields still show a caret and toggle an empty details block. On top of that, the clickable header is a plain div, so keyboard users cannot expand or collapse it. Compute expandability from the fields that actually render below and switch the header to a button with aria-expanded.
🔧 Suggested direction
+ const tags = node.tags?.split(',').map((tag) => tag.trim()).filter(Boolean) ?? [];
const hasDetails =
- node.description ||
+ !!node.description ||
node.confidence != null ||
- node.status ||
- node.tags ||
- node.priority != null ||
- node.source ||
+ tags.length > 0 ||
+ !!node.source ||
node.relationships.length > 0;
+ const isExpandable = hasChildren || hasDetails;
- <div
+ <button
+ type="button"
className={`${styles.nodeHeader} ${expanded ? styles.expanded : ''}`}
- onClick={() => setExpanded(!expanded)}
+ onClick={() => isExpandable && setExpanded((value) => !value)}
+ aria-expanded={isExpandable ? expanded : undefined}
>
<span className={styles.toggle}>
- {hasChildren || hasDetails ? (expanded ? '\u25BE' : '\u25B8') : '\u00A0\u00A0'}
+ {isExpandable ? (expanded ? '\u25BE' : '\u25B8') : '\u00A0\u00A0'}
</span>
...
- </div>
+ </button>
- {node.tags && (
+ {tags.length > 0 && (
<div className={styles.property}>
<span className={styles.propLabel}>Tags</span>
<div className={styles.tagList}>
- {node.tags.split(',').map((tag) => tag.trim()).filter(Boolean).map((tag) => (
+ {tags.map((tag) => (
<span key={tag} className={styles.tag}>{tag}</span>
))}
</div>
</div>
)}Also applies to: 58-64, 87-133
🤖 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 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.
| function buildTree(soaLinks: LinkExpression[]): SoANodeData[] { | ||
| const nodeMap = new Map<string, SoANodeData>(); | ||
| const parentChildLinks: { parent: string; child: string }[] = []; | ||
|
|
||
| // First pass: find all nodes (anything with a soa://title link) | ||
| for (const link of soaLinks) { | ||
| const pred = link.data.predicate; | ||
| const base = link.data.source; | ||
|
|
||
| if (pred === 'soa://title') { | ||
| if (!nodeMap.has(base)) { | ||
| nodeMap.set(base, { | ||
| base, | ||
| title: '', | ||
| modality: 'observation', | ||
| children: [], | ||
| relationships: [], | ||
| }); | ||
| } | ||
| nodeMap.get(base)!.title = parseLiteralString(link.data.target); | ||
| } |
There was a problem hiding this comment.
Don't require soa://title to discover a node.
buildTree() only seeds nodeMap from title links, so any SoA item that is only referenced by soa://rel_parent or another relationship disappears entirely. That also makes the (untitled) fallback in SoANode unreachable. Register placeholder nodes for every SoA source/target you encounter, then fill in the title when it exists.
🔧 Suggested direction
function buildTree(soaLinks: LinkExpression[]): SoANodeData[] {
const nodeMap = new Map<string, SoANodeData>();
const parentChildLinks: { parent: string; child: string }[] = [];
+ const ensureNode = (base: string) => {
+ if (!nodeMap.has(base)) {
+ nodeMap.set(base, {
+ base,
+ title: '',
+ modality: 'observation',
+ children: [],
+ relationships: [],
+ });
+ }
+ return nodeMap.get(base)!;
+ };
- // First pass: find all nodes (anything with a soa://title link)
+ // First pass: register every SoA node we touch
for (const link of soaLinks) {
const pred = link.data.predicate;
const base = link.data.source;
+ const target = link.data.target;
+ ensureNode(base);
+ if (pred === 'soa://rel_parent' || RELATIONSHIP_PREDICATES.includes(pred)) {
+ ensureNode(target);
+ }
if (pred === 'soa://title') {
- if (!nodeMap.has(base)) {
- nodeMap.set(base, {
- base,
- title: '',
- modality: 'observation',
- children: [],
- relationships: [],
- });
- }
- nodeMap.get(base)!.title = parseLiteralString(link.data.target);
+ ensureNode(base).title = parseLiteralString(target);
}
}Also applies to: 89-98
🤖 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 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.
| } else if (pred === 'soa://priority') { | ||
| const prio = parseInt(parseLiteralString(target), 10); | ||
| node.priority = Number.isFinite(prio) ? prio : undefined; |
There was a problem hiding this comment.
Clamp priority before rendering the P-scale.
The UI exposes a bounded P1-P5 badge, but this parser accepts any finite integer. Invalid data will surface as P0 or P99 instead of the expected range.
🔧 Proposed fix
} else if (pred === 'soa://priority') {
const prio = parseInt(parseLiteralString(target), 10);
- node.priority = Number.isFinite(prio) ? prio : undefined;
+ node.priority = Number.isFinite(prio) ? Math.max(1, Math.min(5, prio)) : undefined;
}🤖 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 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.
| for (const { parent, child } of parentChildLinks) { | ||
| const parentNode = nodeMap.get(parent); | ||
| const childNode = nodeMap.get(child); | ||
| if (parentNode && childNode) { | ||
| parentNode.children.push(childNode); | ||
| childSet.add(child); |
There was a problem hiding this comment.
Guard soa://rel_parent against cycles.
This builder assumes the parent graph is acyclic. A back-edge like R -> A, A -> B, B -> A will make the recursive SoANode render walk forever once it reaches A. Reject back-edges while assembling children, or track a visited path during render/build.
🤖 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 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.
| setLoading(true); | ||
| // TODO: Optimize with server-side filtering by querying each soa:// predicate individually | ||
| // instead of fetching all links and filtering client-side | ||
| const allLinks = await perspective.queryLinks({}); |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In @coasys/ad4m v0.11.1, what is the public PerspectiveProxy API for fetching links? Does it include queryLinks(), or should callers use get(new LinkQuery(...))?
💡 Result:
In @coasys/ad4m v0.11.1, the public PerspectiveProxy API for fetching links is perspective.get(query: LinkQuery) (and convenience helpers like getSingleTarget). It does not expose queryLinks() on PerspectiveProxy. (docs.ad4m.dev)
queryLinks(uuid, query) exists on the lower-level client (PerspectiveClient / ad4mClient.perspective) and is what PerspectiveProxy.get() delegates to internally. (coasys.github.io)
So callers should use:
await perspective.get(new LinkQuery({ source: "...", predicate: "...", target: "..." }))(or an object typed as LinkQuery, depending on your TS setup). (docs.ad4m.dev)
Citations:
- 1: https://docs.ad4m.dev/jsdoc/classes/PerspectiveProxy/
- 2: https://coasys.github.io/ad4m-core-deprecated/api.html?utm_source=openai
- 3: https://docs.ad4m.dev/jsdoc/classes/PerspectiveProxy/
queryLinks() is not part of the public PerspectiveProxy API.
In @coasys/ad4m v0.11.1, the code calls perspective.queryLinks({}), but queryLinks() exists only on the lower-level PerspectiveClient, not on PerspectiveProxy. Use perspective.get(new LinkQuery(...)) instead, matching the pattern in packages/api/src/factory/SubjectRepository.ts (Lines 106-108), or the view will fail on first load.
🤖 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` at 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.
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
| type Props = { | ||
| agent: AgentClient; | ||
| perspective: PerspectiveProxy; | ||
| source: string; | ||
| }; | ||
|
|
||
| export default function App({ agent, perspective, source }: Props) { | ||
| if (!perspective?.uuid || !agent) { | ||
| return ( |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Inspect SoATreeView's props/implementation and all `agent` references in this package.
sed -n '1,220p' views/soa-tree-view/src/components/SoATreeView.tsx
rg -n -C2 --glob 'views/soa-tree-view/src/**' '\bagent\b'Repository: coasys/flux
Length of output: 7127
Remove the unnecessary agent guard, or make it optional if agent may be required later.
SoATreeView declares only perspective and source in its Props type and never references agent. The guard at line 14 (if (!perspective?.uuid || !agent)) blocks rendering even though agent is not used. For a read-only tree view that depends solely on perspective and source, either remove the agent check or make it optional (e.g., agent?: AgentClient) if agent is reserved for future use by child components.
Also applies to: lines 8, 18
🤖 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` around lines 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).
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