OSINT cockpit: dissolve property-node islands → tenant, reason dual-use as a Person×Situation causal chain#69
Conversation
The basin belongs in the node's own detail (GUID family byte + EdgeBlock mixin adapters), not in an outsourced helper node. A materialized hub — whether a SOA_HUB_CLASS basin node or a SchemaValue property-node — cannot dock as an edge; it only invites 'look up a property as a node', which fragments the graph into islands. Backend (osint_soa_bytes): - Stop emitting per-basin SOA_HUB_CLASS hub nodes; node_count = members only. - member-of / interfaces now dock to the basin's ANCHOR ENTITY (the real top-degree node that names the basin) via anchor_idx, never a synthetic hub. No self-loop on the anchor. - Drop the basin-hub label loop; remove the now-dead SOA_HUB_CLASS const. Frontend (OsintGraph.tsx): - Render entity<->entity relations only: real relations (2..7,9) PLUS basin tissue (member-of 0 / interfaces 1, now real anchor edges). Schema property-nodes (cls 5/6) and their facet spokes (VALID_FOR 8, facets 10..20) are no longer rendered — a dimension is a prefix carried ON the node (read live by the facet lens), never a node to spoke into. - Give member-of/interfaces visible tissue colours. Retrieval stays explicit-prefix: to gather a basin you filter the family prefix, you don't chase a hub. The facet lens (tenant colouring + raw-edge naming) is unaffected.
…lette Wire the dual-use dimensions ON the node into the OSO1 wire and make them navigable: the explicit prefix you select is the query, and the graph filters to matches immediately. Backend (osint_soa_bytes): - Emit an additive tenant tail: node_count x 11 facet bytes = value[1..=11] (militaryUse, civicUse, airo:type, MLType, purpose, capacity, currentStatus, type, output, impact, stakeholder). No schema node — the dimension is a prefix carried on the node. Frontend (OsintGraph.tsx): - decode picks the tenant stride that fits (11 current / 6 legacy), exposed as soa.tenantStride; all tenant reads are stride-aware. - FACET_AXES_UI widened 6 -> 11 to match the backend axis order. - New lower-right EXPANDABLE PROPERTY PALETTE: per-axis value catalogue (value -> label + count), click a value to add/remove it from the filter. A node survives if, for every axis carrying >=1 selected value, its code is selected (AND across axes, OR within an axis); non-matches dim, edges survive only between two matches. Live '<n> match' count + clear. - This subsumes the single-axis facet legend and expresses the quid-pro-quo query directly: militaryUse=* + stakeholder=* + a purpose value. Note: McClelland motive (GUID2) is not yet populated in the bake, so 'filter by motivation' uses purpose:vair (the AIRO purpose dim) — the real 'why' signal in this data. The '<> dimensions' toggle is now inert (schema nodes no longer rendered); the palette replaces it.
…+ McClelland/Freud power flow Measures the model as distance during reasoning, never materialized: - CAUSALITY axis (intent->impact drift) = divergence, per capability the Jaccard distance of the impact sets between the militaryUse branch and the civicUse branch of the SAME offer (the shared pivot that makes the fork measurable). - DEMAND fork = mil vs civ count for that offer. - POWER level read straight from airo:type bits (the adjacency): P1 oral consume (Subject) / P3 phallic control-others (Deployer) / P4 genital empower-others (Developer/Provider) — McClelland nPow as Freud's gradient. nAch/nAff from intent/use labels (keyword heuristic). '◆ dual-use' lens paints every node by the causality distance of the capability it offers (cool->hot) and streams the ranked capabilities (demand fork · Δimpact · power level · motive) into the readout, with a Macht-adjacency % (how much high-divergence skews to power/nPow). Codebooks stay separate (no merge — distance measured at reason time); nothing materialized as a node or edge. Replaces the now-inert dimensions toggle.
Reframe the divergence readout as the CAUSAL CHAIN AIwar builds to prove the
harm companies deny ('as long as dual-use isn't proven harmful, we continue'):
SITUATION (4 outside factors, the two orthogonal axes):
capability → [mil/civ demand] → explicit purpose ⟹ implicit impact
— the intent→impact drift, chained end to end.
PERSON (personality trait):
POWER_LEVEL from airo:type (Freud gradient P1 consume / P3 control-others /
P4 empower), else the McClelland motive (nPow/nAch/nAff), reasoned AGAINST
the situational chain.
Per capability the readout now shows the chain + the driving trait, and a
'Macht-driven %' = how much of the high-divergence is carried by a power trait
(P3/P4 or nPow) — the Person→Situation attribution that closes the causal chain
the 'can't prove it's harmful' defense relies on staying open.
Model additions: per-capability dominant explicit purpose + implicit impact
(the chain's two ends), collected from the tenant purpose/impact bytes. No
demand-axis distance fabricated (no semantic table in the cockpit — the honest
gap); the causality axis is what's computed. Lewin/Atkinson/Rheinberg grounding.
…LType Verified every tenant position matches between the bake (osint_gotham.rs value[1..=11]) and the frontend reasoning (OsintGraph.tsx AX 0..10), plus the airo:type bitset bit-values (P4=Dev|Prov|Supp, P3=Deployer|Operator, P1=Subject) and stride=11. One naming blemish fixed: byte 4 is fed from MLTask/MLTasks (the NEED per the need/offer typing) but was labeled MLType (the OFFER) on both sides. Renamed FACET_MLTYPE→FACET_MLTASK, REL_FACET_MLTYPE→REL_FACET_MLTASK (bake) and AX.mlType→mlTask, FACET_AXES_UI[3]/REL_NAME[13] 'MLType'→'MLTask' (reasoning). The codebook stays ML_TYPE (the value register) — dimension name ≠ codebook name.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 31348d26c0
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| // DEMAND fork = mil vs civ count for that offer. | ||
| // POWER level = airo:type bits (Freud gradient), the adjacency to nPow. | ||
| const duModel = useMemo(() => { | ||
| if (!soa || !soa.tenants || !soa.tenantStride || !view) return null; |
There was a problem hiding this comment.
Require 11-wide tenants for the divergence lens
When /osint.soa is a legacy 6-wide tenant asset (which decodeSoa explicitly accepts, and the committed fallback crates/cockpit-server/assets/osint_scene.soa is 6-wide), this guard still enables the new dual-use model. The code below then indexes axes such as AX.impact/AX.stakeholder with i * stride + ax; with stride === 6 those reads come from the next node's tenant bytes rather than missing columns, so the ◆ dual-use heatmap/readout is computed from unrelated data. Return null unless tenantStride >= 11 (or avoid the 11-wide-only axes) before enabling this lens.
Useful? React with 👍 / 👎.
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
📝 WalkthroughWalkthroughThe PR changes the OSINT SoA wire format and facet encoding on the server (renaming the MLTask facet, emitting members-only nodes with anchor-docked edges, and appending a per-node facet tenant tail), and rewrites the cockpit OsintGraph component to decode that tail, filter/color nodes by tenant facets, and drive a new dual-use divergence heat lens. ChangesServer-side SoA and facet encoding
OsintGraph cockpit rewrite
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant OsintGraph
participant duModel
participant HeatOverlay
User->>OsintGraph: click dual-use button
OsintGraph->>duModel: compute causality/divergence from tenant signals
duModel-->>OsintGraph: ranked Person x Situation rows and heat map
OsintGraph->>HeatOverlay: heatNodes(heat)
OsintGraph->>OsintGraph: stream ranked chain lines to readout
Possibly related PRs
Poem
✨ Finishing Touches📝 Generate docstrings
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 |
What
Follow-up to the merged #68. Takes the OSINT cockpit from "11 dims stacked as tenant bytes + materialized schema hubs" to the substrate as intended: the dimension is a prefix carried on the node, and the cross-cutting structure is reasoned as logical edges — never a property-as-node hub. Then wires a real analysis on top: dual-use divergence as a Person × Situation causal chain.
Why
The rendered graph was "still islands": each dimension value (
MLTask,SpatialReasoning, a basin) was materialized as a node with spokes into it — a category error (key: valueis a tenant slot, not two nodes + an edge). A materialized property-node has no logic to dock as an edge; it just invites "look up a property as a node." This PR removes that and replaces it with reasoning over the tenant.Changes (5 commits)
1 — dissolve the hubs into anchor-entity tissue (
osint_gotham.rs,OsintGraph.tsx)SOA_HUB_CLASShubs;member-of/interfacesnow dock to the basin's anchor entity (a real node with its own edges), not a synthetic hub.SchemaValue/SchemaAxisproperty-nodes + their facet spokes are no longer rendered — the dimension lives on the node.2 — 11-wide facet tenant tail + expandable property palette (
osint_gotham.rs,OsintGraph.tsx)node_count × 11facet bytes (value[1..=11]); client decodes stride 11 (legacy 6 still decodes).3 — dual-use divergence lens (
OsintGraph.tsx)airo:typebits (P1 consume · P3 control-others · P4 empower).4 — the causal chain: Person × Situation (
OsintGraph.tsx)capability → [mil/civ] → explicit purpose ⟹ implicit impact; Person = the power trait reasoned against it.Macht-driven %= the Person→Situation attribution (Lewin/Atkinson/Rheinberg;Tendency = Motive × Incentive).5 — align bake ⟷ reasoning schema
MLTask(the NEED), was mislabeledMLType(the OFFER) on both sides — renamed consistently. Codebook staysML_TYPE(value register ≠ dimension name).Test on Railway
Deploy this branch → the default OSINT graph:
N match.◆ dual-uselens — nodes painted cool→hot by causality divergence; readout streams the causal chain per capability (capability → [mil/civ] → explicit ⟹ implicit │ trait) withMacht-driven %.Honest gaps
cargo/npm-verify locally (session network-gated); edits are structurally balanced and mirror existing patterns — Railway's build is the verification.🤖 Generated with Claude Code
Generated by Claude Code
Summary by CodeRabbit
New Features
Bug Fixes