
Using cybedtools with an AI assistant
Source:vignettes/articles/ai-assistants.Rmd
ai-assistants.RmdThis article has two readers: a person who wants an assistant to write cybedtools code, and the assistant itself, reading its Markdown twin. An assistant working from general R and SPARQL knowledge will get several things about this package wrong.
AGENTS.md in the repository root carries the same rules
for coding agents working inside a clone.
Machine-readable documentation
The site publishes a reference index at https://ryanstraight.github.io/cybedtools/llms.txt, and
every reference page and article has a Markdown twin at the same URL
with .md appended. The index points at pages, not at the
rules below.
The documentation is also indexed in Context7 at https://context7.com/ryanstraight/cybedtools. If your assistant supports Context7, point it there before it writes code.
Rules
Use the domain helpers, and keep SPARQL to one triple pattern
library(cybedtools)
library(dplyr)
role_framework_bindings(rdf) |>
inner_join(role_element_bindings(rdf), by = "role") |>
count(framework_name, name = "element_count")The librdf C library that rdflib wraps
hangs for many minutes on multi-pattern SPARQL joins and silently
returns zero rows on multi-property selects against a single subject.
Single basic graph patterns execute fast and correctly.
sparql_pairs() and sparql_subjects() issue
those single patterns; the domain helpers compose them and join in
dplyr. Never write COUNT, GROUP BY, or
HAVING into a SPARQL string. Aggregate in dplyr.
Load the graph with a loader, and know the package does not ship one
rdf <- make_demo_graph()
rdf_full <- load_combined_ntriples_graph()The installed package does not bundle the framework graph. It ships
ingestion code and no framework text. make_demo_graph()
returns a synthetic two-framework graph that exercises every domain
helper without staged data. load_combined_ntriples_graph()
needs a graph you assembled from staged sources.
Join on IRIs, never on bare statement codes
element_framework_bindings(rdf_full) |>
inner_join(element_text(rdf_full), by = "element")Statement codes are unique only within a framework. NICE and CCSSF
both print codes in the T0516 shape, and they denote
different statements. CyQUAL reuses the 2017 NICE task codes and shares
the T1xxx range with current NICE for unrelated text. Join
on the full IRI, or carry a framework column alongside the code.
element_text() returns element and
text. Parsed sub-points and examples are elements and carry
text, so parent-statement analyses must anti-join
subpoint_framework_bindings() and
example_framework_bindings(), renaming their
subpoint and example columns to
element.
Restrict to roles before counting per role
role_element_bindings(rdf_full) |>
semi_join(role_framework_bindings(rdf_full), by = "role") |>
count(role, name = "element_count")role_element_bindings() returns bindings for every
organizing unit, not only roles, because cybed:hasElement
is the universal parent-child link. A role-level count made without that
semi_join silently includes SFIA skills and DigComp
competence areas.
Say which element count you mean
cybedtools::framework_summary |>
select(framework_name, element_count_strict, element_count_with_examples)element_count_with_examples counts parents plus
cybed:Subpoint children plus cybed:Example
children. element_count_strict counts parents only. The
README headline density finding uses the with-examples count. The
related count in docs/framework-invariants.yml,
total_elements_with_subpoints, counts parents plus
Subpoints and excludes Examples.
Pick the right cross-framework type
organizing_unit_framework_bindings(rdf_full)
role_framework_bindings(rdf_full)cybed:OrganizingUnit reaches every framework in the
corpus. cybed:Role reaches only the frameworks that declare
roles: NICE v2.2.0, DCWF v5.1, ECSF v1, CyQUAL 1.2.0, CCSSF 2022, OTCCF
v1.1, SCyWF 1.5. A query written against cybed:Role when
the question is corpus-wide silently drops the other 7 frameworks.
Respect the upstream licenses
framework_metadata(rdf_full) |>
select(name, jurisdiction, sector)Package code is MIT. Framework content keeps its upstream terms.
docs/framework-invariants.yml records a
public_redistribution policy per framework: SFIA is
local_only, CSEC2017 and OTCCF are
structure_only, CyQUAL and CCSSF are
full_with_attribution. OTCCF, CyQUAL and CCSSF are in the
package by the written permission of their stewards. CCSSF’s attribution
is “Copyright Government of Canada. Used with permission of the Canadian
Centre for Cyber Security.” Do not reproduce statement text from a
framework marked local_only or structure_only.
Titles, counts, categories and mappings are fine. See
LICENSE.md and LICENSING.md.
Cite the package and the stewards
Cite the concept DOI, 10.5281/zenodo.20076116, which
resolves to the latest release. Cite the framework stewards for
framework content: CSA for OTCCF, CyQUAL and Masaryk University for
CyQUAL, and the Canadian Centre for Cyber Security for CCSSF, which asks
that its material be referenced when used. Do not credit the
assistant.
A preamble you can paste
When writing cybedtools code: use the domain helpers (
framework_metadata,organizing_unit_framework_bindings,role_framework_bindings,element_framework_bindings,role_element_bindings,subpoint_framework_bindings,example_framework_bindings,element_text) and the primitivessparql_pairsandsparql_subjects. Any rawrdflib::rdf_querymust be a single basic graph pattern, joined in dplyr; librdf hangs on multi-pattern joins and silently returns zero rows on multi-property selects. Load withload_combined_ntriples_graph(), ormake_demo_graph()when no data is staged; the installed package bundles no framework graph. Never join frameworks on a bare element code, only on full IRIs or framework-qualified ids.role_element_bindings()covers every organizing unit, so restrict it with asemi_joinonrole_framework_bindings()before counting per role. State whether a count is strict or with-examples. Do not reproduce framework statement text. Cite DOI 10.5281/zenodo.20076116 and the framework stewards.
What not to ask for
Do not ask an assistant to produce a crosswalk or alignment the graph
does not contain. CCSSF’s citations of 2017-era NICE work roles are
carried as cybed:niceCrossReference literals, never as
links, and they do not match the NICE v2.2.0 identifiers in this graph.
An assistant that invents the missing edges is fabricating.
Do not treat a similarity score as equivalence. The Concordance alignment tables report full-document Jaccard vocabulary overlap. A best match surfaces a structural candidate. Credentialing pathways, regulatory contexts, clearance requirements and language fluency are separate questions, and the equivalence judgment is human work.