From cde994108e6a729d99ee778a87a75ade954fecb4 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 19 Jul 2026 20:02:28 +0000 Subject: [PATCH 1/5] =?UTF-8?q?docs:=20ADR-0020=20=E2=80=94=20the=20graph?= =?UTF-8?q?=20explorer?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The corpus graph as one self-contained interactive HTML page, generated on demand: report graph writes it, publish site stages it, nothing is committed; d3-force is vendored as the pinned, digest-recorded layout engine and the interaction shell stays project code; documents by default, artefacts as a switchable layer. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Gie4jfrHKCxQktD2UsvH4Q --- .../adr/ADR-0020-the-graph-explorer.md | 65 +++++++++++++++++++ .../unit-arc42-09-architecture-decisions.md | 1 + 2 files changed, 66 insertions(+) create mode 100644 docs/en/architecture/adr/ADR-0020-the-graph-explorer.md diff --git a/docs/en/architecture/adr/ADR-0020-the-graph-explorer.md b/docs/en/architecture/adr/ADR-0020-the-graph-explorer.md new file mode 100644 index 0000000..d8a470f --- /dev/null +++ b/docs/en/architecture/adr/ADR-0020-the-graph-explorer.md @@ -0,0 +1,65 @@ +--- +id: ADR-0020 +title: The Graph Explorer +slug: the-graph-explorer +iri: arqix:adrs/adr-0020 + +rdf: + type: + - arqix:classes/adr + +triples: [] + +properties: + decision-status: accepted + +external-references: [] + +meta: + lifecycle-status: draft + owner: hcf + created: 2026-07-19 + updated: 2026-07-19 + lang: en + translation-of: + generated: false +--- + +## The Graph Explorer + +### Context + +The corpus is a graph, and the tooling already serialises it: the trace core graph carries every document node and every resolved edge under a stable schema (ADR-0006), with one identity rule for its nodes (ADR-0007). +The existing projections answer counting questions — matrices, coverage numbers, catalogs. +Navigation questions stay unanswered: what surrounds this story, which clusters exist, where are the hubs and the orphans. +A node-link view with filters answers them at a glance; a table answers them not at all. + +Two constraints shape the form. +Published artefacts must stand alone — a page that loads its engine from a third-party network breaks offline reading and ties publication to someone else's availability. +And the dependency bar (ADR-0014) demands the judgement per ingredient: vendor what removes real algorithmic work, write what is plain project code. + +### Decision + +**The corpus graph ships as one self-contained interactive HTML page, generated on demand.** + +- `arqix report graph [--out ]` writes the page — an export product on the `report` noun (ADR-0005) — and `publish site` stages it with the published site. +- The page embeds the trace core graph, enriched with each document's title and declared lifecycle status; enrichment decorates existing nodes and never invents new ones (ADR-0007). +- The layout engine is vendored: the four d3-force modules (d3-dispatch, d3-quadtree, d3-timer, d3-force), pinned minified builds concatenated into one file, recorded as a source record whose digest the SRC contract verifies. + The interaction shell — canvas rendering, pan and zoom, filters, search, neighbourhood highlight — is project code in the page. +- The default view shows document nodes, coloured by type and filterable by type and lifecycle status; artefact nodes and their marker edges are a switchable code layer, off by default. +- The page is never committed: its embedded data changes with every corpus edit, so a committed copy either churns every change under a freshness gate or rots without one. + +### Alternatives Considered + +- **A committed snapshot page:** rejected — graph data ages with every commit; the snapshot treadmill buys no reader value here. +- **A hand-rolled layout:** rejected — a usable force layout means Barnes–Hut approximation, velocity integration, and collision handling; d3-force delivers exactly that in seventeen kilobytes, which is the case where vendoring beats rewriting. +- **Loading the library from a CDN:** rejected — the page stops being self-contained, and offline reading dies with the network. +- **Vendoring the full d3 bundle:** rejected — selection, scales, and transitions serve nothing here; the shell is a few hundred lines over canvas. +- **A server-rendered or baked SVG view:** rejected — a static picture answers none of the navigation questions; the filters and the highlight are the point. + +### Consequences + +- The Report & Export component owns `report graph`; the publisher stages the page for the default language. +- Third-party code enters the repository once, pinned and digest-recorded; updating it is a deliberate re-vendoring with a new digest, never a silent drift. +- The explorer reads the same core graph every other projection reads — a new data layer (claims, freshness) is a new toggle over the same embedded model, not a new pipeline. +- The page works from the filesystem, from the published site, and offline alike. diff --git a/docs/en/architecture/arc42/units/unit-arc42-09-architecture-decisions.md b/docs/en/architecture/arc42/units/unit-arc42-09-architecture-decisions.md index 27e27d7..11e6309 100644 --- a/docs/en/architecture/arc42/units/unit-arc42-09-architecture-decisions.md +++ b/docs/en/architecture/arc42/units/unit-arc42-09-architecture-decisions.md @@ -50,5 +50,6 @@ Decisions are kept as ADRs under `../adr/`, maintained per the path model with a | [ADR-0017](../../adr/ADR-0017-process-profiles-and-the-layered-ontology.md) | Process profiles and the layered ontology: rules stay code, their activation and binding become configuration, vocabulary becomes layered corpus data (reserved core, module vocabularies, project ontology) validated by an ONT meta-rule family; guarded lifecycle stays core, domain status is declared vocabulary | accepted | | [ADR-0018](../../adr/ADR-0018-evidence-anchors-and-derived-triples.md) | Evidence anchors and derived triples: a claim is a body marker above the supported block plus a `derived-triples` edge that `fmt` generates from it; position-bound attributes stay on the marker, the edge joins the validated graph; confidence is a declared vocabulary; only claim markers are lifted | accepted | | [ADR-0019](../../adr/ADR-0019-provenance-layers.md) | Provenance layers: one vocabulary, three carriers of increasing depth — computed from history (informational, never gated), inline on the marker (validated dictionary), and the claim record as the fullest form (analysis block, declared review verdict); records are derivable from the lower carriers, several markers may share one record, `supported-by` stays the only edge | accepted | +| [ADR-0020](../../adr/ADR-0020-the-graph-explorer.md) | The graph explorer: the corpus graph as one self-contained interactive HTML page, generated on demand (`report graph`, staged by `publish site`, never committed); vendored d3-force as the pinned, digest-recorded layout engine, the interaction shell as project code; documents by default, artefacts as a switchable layer | accepted | Decision records predating the ADR directory live in the planning packages under `docs/en/plans/` (ID scheme, canonical-owner model, atomicity, subject conventions). From 342e0e7295b94d909aa7c1b49fbc1b9bc8380e0a Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 19 Jul 2026 20:02:28 +0000 Subject: [PATCH 2/5] spec: the graph-explorer story MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit US-08-01-42 (explore the corpus as a graph — a genuinely new goal) with REQ-08-01-42-01/-02/-03: the embedded corpus graph with type and lifecycle on every document node, the self-contained page, and the publish-site staging. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Gie4jfrHKCxQktD2UsvH4Q --- ...d-the-corpus-graph-in-the-explorer-page.md | 41 +++++++++++++++ ...2-keep-the-explorer-page-self-contained.md | 41 +++++++++++++++ ...ge-the-explorer-with-the-published-site.md | 41 +++++++++++++++ ...-08-01-42-explore-the-corpus-as-a-graph.md | 51 +++++++++++++++++++ 4 files changed, 174 insertions(+) create mode 100644 docs/en/architecture/req/REQ-08-01-42-01-embed-the-corpus-graph-in-the-explorer-page.md create mode 100644 docs/en/architecture/req/REQ-08-01-42-02-keep-the-explorer-page-self-contained.md create mode 100644 docs/en/architecture/req/REQ-08-01-42-03-stage-the-explorer-with-the-published-site.md create mode 100644 docs/en/architecture/stories/US-08-01-42-explore-the-corpus-as-a-graph.md diff --git a/docs/en/architecture/req/REQ-08-01-42-01-embed-the-corpus-graph-in-the-explorer-page.md b/docs/en/architecture/req/REQ-08-01-42-01-embed-the-corpus-graph-in-the-explorer-page.md new file mode 100644 index 0000000..2f98df6 --- /dev/null +++ b/docs/en/architecture/req/REQ-08-01-42-01-embed-the-corpus-graph-in-the-explorer-page.md @@ -0,0 +1,41 @@ +--- +id: REQ-08-01-42-01 +title: Embed the Corpus Graph in the Explorer Page +slug: embed-the-corpus-graph-in-the-explorer-page +iri: arqix:requirements/req-08-01-42-01 + +rdf: + type: + - arqix:classes/functional-requirement + +triples: + - predicate: arqix:properties/derived-from + object: + - arqix:user-stories/us-08-01-42 + - predicate: arqix:properties/has-verification-method + object: + +properties: + priority: medium + fit-criterion: The generated page carries every trace-graph node and edge as embedded data; document nodes carry type and declared lifecycle status, artefact nodes stay distinguishable so the code layer can be toggled. + +external-references: [] + +meta: + lifecycle-status: active + owner: hcf + created: 2026-07-19 + updated: 2026-07-19 + lang: en + translation-of: + generated: false +--- + +## Requirement + +When `arqix report graph` runs, arqix SHALL embed every trace-graph node and edge in the generated explorer page, each document node carrying its type and declared lifecycle status. + +### Notes + +The embedded model is the trace core graph (ADR-0006) enriched with title and lifecycle status; enrichment decorates existing nodes and never invents new ones (ADR-0007, ADR-0020). +Derived from US-08-01-42. diff --git a/docs/en/architecture/req/REQ-08-01-42-02-keep-the-explorer-page-self-contained.md b/docs/en/architecture/req/REQ-08-01-42-02-keep-the-explorer-page-self-contained.md new file mode 100644 index 0000000..ef79cb2 --- /dev/null +++ b/docs/en/architecture/req/REQ-08-01-42-02-keep-the-explorer-page-self-contained.md @@ -0,0 +1,41 @@ +--- +id: REQ-08-01-42-02 +title: Keep the Explorer Page Self-Contained +slug: keep-the-explorer-page-self-contained +iri: arqix:requirements/req-08-01-42-02 + +rdf: + type: + - arqix:classes/functional-requirement + +triples: + - predicate: arqix:properties/derived-from + object: + - arqix:user-stories/us-08-01-42 + - predicate: arqix:properties/has-verification-method + object: + +properties: + priority: medium + fit-criterion: The generated page embeds the graph data and the vendored layout engine inline and references no external script, stylesheet, or other remote resource. + +external-references: [] + +meta: + lifecycle-status: active + owner: hcf + created: 2026-07-19 + updated: 2026-07-19 + lang: en + translation-of: + generated: false +--- + +## Requirement + +When `arqix report graph` writes the explorer page, arqix SHALL embed the graph data and the vendored layout engine inline so that the page references no external resource. + +### Notes + +Self-containment is the ADR-0020 form: the page works from the filesystem, from the published site, and offline alike; the vendored engine is pinned and digest-recorded through its source record. +Derived from US-08-01-42. diff --git a/docs/en/architecture/req/REQ-08-01-42-03-stage-the-explorer-with-the-published-site.md b/docs/en/architecture/req/REQ-08-01-42-03-stage-the-explorer-with-the-published-site.md new file mode 100644 index 0000000..c9c6ded --- /dev/null +++ b/docs/en/architecture/req/REQ-08-01-42-03-stage-the-explorer-with-the-published-site.md @@ -0,0 +1,41 @@ +--- +id: REQ-08-01-42-03 +title: Stage the Explorer With the Published Site +slug: stage-the-explorer-with-the-published-site +iri: arqix:requirements/req-08-01-42-03 + +rdf: + type: + - arqix:classes/functional-requirement + +triples: + - predicate: arqix:properties/derived-from + object: + - arqix:user-stories/us-08-01-42 + - predicate: arqix:properties/has-verification-method + object: + +properties: + priority: medium + fit-criterion: After publish site for the default language, the staging tree carries the generated explorer page alongside the staged pages. + +external-references: [] + +meta: + lifecycle-status: active + owner: hcf + created: 2026-07-19 + updated: 2026-07-19 + lang: en + translation-of: + generated: false +--- + +## Requirement + +When `arqix publish site` stages the default language's corpus, arqix SHALL stage the generated graph explorer page alongside the staged pages. + +### Notes + +The page reaches readers through the site without a committed copy (ADR-0020): staging regenerates it from the current corpus, exactly as the specification catalogue is staged on demand. +Derived from US-08-01-42. diff --git a/docs/en/architecture/stories/US-08-01-42-explore-the-corpus-as-a-graph.md b/docs/en/architecture/stories/US-08-01-42-explore-the-corpus-as-a-graph.md new file mode 100644 index 0000000..40c2adf --- /dev/null +++ b/docs/en/architecture/stories/US-08-01-42-explore-the-corpus-as-a-graph.md @@ -0,0 +1,51 @@ +--- +id: US-08-01-42 +title: Explore the Corpus as a Graph +slug: explore-the-corpus-as-a-graph +iri: arqix:user-stories/us-08-01-42 + +rdf: + type: + - arqix:classes/user-story + +triples: + - predicate: arqix:properties/has-persona + object: arqix:personas/per-08 + - predicate: arqix:properties/has-requirement + object: + - arqix:requirements/req-08-01-42-01 + - arqix:requirements/req-08-01-42-02 + - arqix:requirements/req-08-01-42-03 + - predicate: arqix:properties/is-part-of-workflow + object: arqix:workflows/wf-08-01 + +properties: + priority: medium + edge-case: false + +external-references: [] + +meta: + lifecycle-status: draft + owner: hcf + created: 2026-07-19 + updated: 2026-07-19 + lang: en + translation-of: + generated: false +--- + +## Explore the Corpus as a Graph + +As a knowledge engineer, I want the corpus as a navigable node-link view with filters, so that its structure — clusters, hubs, orphans, neighbourhoods — is visible at a glance instead of assembled from tables. + +### Acceptance Criteria + +- [ ] `arqix report graph [--out ]` writes a single self-contained HTML page: the graph data and the vendored layout engine are embedded, no external resource is referenced. +- [ ] Document nodes carry id, title, type, and declared lifecycle status; every edge carries its kind; artefact nodes and their marker edges ride along flagged as the code layer. +- [ ] The default view shows documents coloured by type, with type and lifecycle filters, search, and neighbourhood highlight; the code layer is a switch, off by default. +- [ ] `arqix publish site` stages the explorer page with the published site. + +### Notes + +The page projects the trace core graph (ADR-0006) under its identity rules (ADR-0007); the form — self-contained export, vendored engine, on-demand generation — is fixed in ADR-0020. From 3fb244584a1c4d38bb35dd2c7015dc587e3ac779 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 19 Jul 2026 20:03:33 +0000 Subject: [PATCH 3/5] test: red for the graph explorer Three failing tests against US-08-01-42: the embedded corpus graph with lifecycle on the nodes, the self-contained page (embedded engine, no external reference), and the publish-site staging. Red evidence: 'unrecognized subcommand graph' on report, missing site-src/graph.html on publish. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Gie4jfrHKCxQktD2UsvH4Q --- tests/cli_publish.rs | 18 ++++++++++++++++ tests/cli_report.rs | 51 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 69 insertions(+) diff --git a/tests/cli_publish.rs b/tests/cli_publish.rs index ee2f6f0..cd0ca1c 100644 --- a/tests/cli_publish.rs +++ b/tests/cli_publish.rs @@ -914,3 +914,21 @@ fn publish_site_removes_stale_staged_pages() { "staging starts from a clean tree" ); } + +// arqix:verifies REQ-08-01-42-03 +#[test] +fn publish_site_stages_the_graph_explorer() { + // ADR-0020: the explorer reaches readers through the site without a + // committed copy — staging regenerates it from the current corpus. + let repo = scratch_copy("minimal", "publish_site_stages_the_graph_explorer"); + std::fs::write(repo.join("arqix.toml"), NOOP_TOOLCHAIN).unwrap(); + + common::assert_success(&run_arqix_in(&repo, &["publish", "site"])); + let staged = repo.join("site-src/graph.html"); + assert!(staged.is_file(), "the explorer page stages with the site"); + let page = std::fs::read_to_string(&staged).unwrap(); + assert!( + page.contains("forceSimulation"), + "the staged page embeds the layout engine" + ); +} diff --git a/tests/cli_report.rs b/tests/cli_report.rs index b8ed98e..f216773 100644 --- a/tests/cli_report.rs +++ b/tests/cli_report.rs @@ -744,3 +744,54 @@ fn report_claims_projects_computed_provenance_on_demand() { "expected computed provenance columns: {stdout}" ); } + +// arqix:verifies REQ-08-01-42-01 +#[test] +fn report_graph_embeds_the_corpus_graph() { + // ADR-0020: the explorer page carries the trace core graph as embedded + // data — nodes with type and lifecycle status, edges with their kind. + let repo = scratch_copy("minimal", "report_graph_embeds_the_corpus_graph"); + let dir = repo.join("docs/en/architecture/stories"); + std::fs::create_dir_all(&dir).unwrap(); + std::fs::write( + dir.join("US-09-09-09-sample-story.md"), + "---\nid: US-09-09-09\ntitle: Sample Story\nslug: sample-story\niri: arqix:user-stories/us-09-09-09\n\nrdf:\n type:\n - rdfs:Class\n\ntriples:\n - predicate: arqix:properties/has-requirement\n object:\n - arqix:requirements/req-99-99-99-01\n\nproperties: {}\n\nexternal-references: []\n\nmeta:\n lifecycle-status: draft\n owner: hcf\n created: 2026-07-13\n updated: 2026-07-13\n lang: en\n generated: false\n---\n\n## Sample Story\n\nBody.\n", + ) + .unwrap(); + + let out = run_arqix_in(&repo, &["report", "graph", "--out", "graph.html"]); + assert_success(&out); + let page = std::fs::read_to_string(repo.join("graph.html")).expect("explorer page written"); + assert!( + page.contains("US-09-09-09") + && page.contains("REQ-99-99-99-01") + && page.contains("has-requirement"), + "expected the story node, its requirement, and the edge kind embedded" + ); + assert!( + page.contains("\"lifecycle\"") && page.contains("draft"), + "expected document nodes to carry their declared lifecycle status" + ); +} + +// arqix:verifies REQ-08-01-42-02 +#[test] +fn report_graph_writes_a_self_contained_page() { + // ADR-0020: data and the vendored layout engine are embedded — the page + // references no external resource. + let repo = scratch_copy("minimal", "report_graph_writes_a_self_contained_page"); + let out = run_arqix_in(&repo, &["report", "graph", "--out", "graph.html"]); + assert_success(&out); + let page = std::fs::read_to_string(repo.join("graph.html")).expect("explorer page written"); + assert!( + page.contains("forceSimulation"), + "expected the vendored layout engine embedded in the page" + ); + assert!( + !page.contains(" + + + diff --git a/src/main.rs b/src/main.rs index 8601e18..eb4c945 100644 --- a/src/main.rs +++ b/src/main.rs @@ -322,6 +322,12 @@ enum ReportCommand { #[arg(long)] provenance: bool, }, + /// Render the corpus graph as a self-contained interactive HTML page + Graph { + /// Target file for the explorer page (stdout when omitted) + #[arg(long)] + out: Option, + }, /// Render the test-coverage unit from a cargo-llvm-cov JSON export Coverage { /// The cargo-llvm-cov JSON export to render from @@ -461,6 +467,7 @@ fn main() -> ExitCode { ReportCommand::Knowledge { out } => reporter::knowledge(out.as_deref(), cli.format), ReportCommand::Statements => reporter::statements(cli.format), ReportCommand::Claims { provenance } => reporter::claims(provenance, cli.format), + ReportCommand::Graph { out } => reporter::graph(out.as_deref(), cli.format), ReportCommand::Coverage { input, stamp, out } => { reporter::coverage_unit(&input, &stamp, out.as_deref(), cli.format) } diff --git a/src/publisher.rs b/src/publisher.rs index 6ff7aad..187c55a 100644 --- a/src/publisher.rs +++ b/src/publisher.rs @@ -166,6 +166,19 @@ pub fn site(lang: Option<&str>, format: OutputFormat) -> ExitCode { return code; } + // The graph explorer stages with the site (ADR-0020): regenerated from + // the current corpus, never committed. Default language only — the page + // projects the whole corpus, not a translation. + // arqix:implements REQ-08-01-42-03 + if lang == default_lang { + let target = staging.join("graph.html"); + if let Err(err) = std::fs::write(&target, crate::reporter::graph_page()) { + eprintln!("error: cannot write {}: {err}", target.display()); + return ExitCode::from(2); + } + staged += 1; + } + // Configured assets ride along so the toolchain can reference them. An // asset under a doc root's language dir is staged at the same // language-root-relative path as the pages (which strip that prefix), so a diff --git a/src/reporter.rs b/src/reporter.rs index 2bbdb6f..41b3cbe 100644 --- a/src/reporter.rs +++ b/src/reporter.rs @@ -483,6 +483,90 @@ pub fn claims(provenance: bool, _format: OutputFormat) -> ExitCode { ExitCode::SUCCESS } +/// The explorer shell and the vendored layout engine (ADR-0020): the shell +/// is project code, the engine is the pinned d3-force concatenation whose +/// digest the SRC contract verifies (SRC-0005). +const EXPLORER_TEMPLATE: &str = include_str!("assets/graph-explorer.html"); +const VENDOR_JS: &str = include_str!("assets/d3-force-bundle.min.js"); + +// arqix:implements REQ-08-01-42-01 +// arqix:implements REQ-08-01-42-02 +/// The rendered explorer page: the trace core graph enriched with each +/// document's title and declared lifecycle status, embedded with the +/// vendored engine into the self-contained shell — no external resource. +pub(crate) fn graph_page() -> String { + let mut payload = crate::trace::graph_json(); + let mut titles: BTreeMap = BTreeMap::new(); + let mut lifecycles: BTreeMap = BTreeMap::new(); + for doc in crate::store::documents() { + let Some(id) = doc.id.clone() else { continue }; + if let Some(title) = &doc.title { + titles.insert(id.clone(), title.clone()); + } + if let Some(status) = crate::store::lifecycle_status(&doc) { + lifecycles.insert(id, status.to_string()); + } + } + // Enrichment decorates existing nodes and never invents new ones + // (ADR-0007): unknown ids simply stay bare. + if let Some(nodes) = payload["nodes"].as_array_mut() { + for node in nodes { + let Some(id) = node["id"].as_str().map(str::to_string) else { + continue; + }; + let Some(node) = node.as_object_mut() else { + continue; + }; + if let Some(title) = titles.get(&id) { + node.insert("title".into(), serde_json::json!(title)); + } + if let Some(status) = lifecycles.get(&id) { + node.insert("lifecycle".into(), serde_json::json!(status)); + } + } + } + let data = serde_json::json!({ + "schema_version": payload["schema_version"], + "nodes": payload["nodes"], + "edges": payload["edges"], + }); + // `]` — the corpus graph as one +/// self-contained interactive HTML page (ADR-0020), written to `--out` or +/// stdout. +pub fn graph(out: Option<&str>, _format: OutputFormat) -> ExitCode { + let page = graph_page(); + match out { + Some(path) => { + if let Some(parent) = std::path::Path::new(path).parent() + && !parent.as_os_str().is_empty() + && let Err(err) = std::fs::create_dir_all(parent) + { + eprintln!("error: cannot create {}: {err}", parent.display()); + return ExitCode::from(2); + } + if let Err(err) = std::fs::write(path, page) { + eprintln!("error: cannot write {path}: {err}"); + return ExitCode::from(2); + } + println!("graph explorer written to {path}"); + } + None => print!("{page}"), + } + ExitCode::SUCCESS +} + // arqix:implements REQ-08-01-40-05 /// Q-12: the evidence numbers — never a gate (ADR-0018). fn unit_evidence_coverage( diff --git a/src/store.rs b/src/store.rs index 5d42d51..9576511 100644 --- a/src/store.rs +++ b/src/store.rs @@ -68,7 +68,7 @@ fn document_json(d: &Document) -> Value { } /// The declared `meta.lifecycle-status` frontmatter value, if any. -fn lifecycle_status(d: &Document) -> Option<&str> { +pub(crate) fn lifecycle_status(d: &Document) -> Option<&str> { d.frontmatter .iter() .find_map(|line| line.trim().strip_prefix("lifecycle-status:")) diff --git a/src/trace.rs b/src/trace.rs index 2bc75d8..98965fc 100644 --- a/src/trace.rs +++ b/src/trace.rs @@ -372,6 +372,13 @@ fn story_of(req_id: &str) -> Value { } } +// arqix:implements REQ-08-01-42-01 +/// The core graph of the current corpus, as `trace scan --format json` emits +/// it — the shared model behind the machine surface and the explorer page. +pub(crate) fn graph_json() -> Value { + graph(&build_model(&read_corpus())) +} + fn graph(model: &Model) -> Value { let mut nodes = Vec::new(); let known: std::collections::BTreeSet<&String> = model.documents.keys().collect(); From 7c93cf91a32c0120461f2951697c9603ed1e9523 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 19 Jul 2026 20:08:31 +0000 Subject: [PATCH 5/5] docs: program log for the graph explorer The roadmap candidate becomes a landed 0.3.0 item and the program status records the slice, including the triage note that FR-E1 is no dependency of the explorer. Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_01Gie4jfrHKCxQktD2UsvH4Q --- docs/en/plans/knowledge-repository-2026-07-15/STATUS.md | 2 ++ docs/en/project/roadmap.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/en/plans/knowledge-repository-2026-07-15/STATUS.md b/docs/en/plans/knowledge-repository-2026-07-15/STATUS.md index f93679a..b83bc3b 100644 --- a/docs/en/plans/knowledge-repository-2026-07-15/STATUS.md +++ b/docs/en/plans/knowledge-repository-2026-07-15/STATUS.md @@ -61,6 +61,8 @@ Progress log for the knowledge-repository program (see PLANS.md; agents update t - [x] FR-B3 close-out by investigation: duplicate id and iri are corpus-wide findings (FM-006, linter duplicate-id) and `doc new` checks id uniqueness at creation; the missing slug-per-context check needs the bounded-context concept (FR-C3) and moves there. - [x] B2 provenance layer: ADR-0019 (provenance layers) accepted 2026-07-19 on the owner's hybrid decision — git optional as the computed floor, the marker carries the same vocabulary as an inline dictionary, records (`arqix:classes/claim`) are derivable and the fullest form, several markers may share one record. REQ-08-01-40-06/-07/-08 on US-08-01-40, spec-first red/green: CLM-003 (review verdict against the effective vocabulary), CLM-004 (`record=` must resolve to a claim document), and `report claims --provenance` appending the history-computed columns on demand (never in the gated export); the `[kinds.claim]` creation surface with template and declared review vocabulary lands alongside. +- [x] Graph explorer (owner request 2026-07-19, design decided the same day: `report graph` + site staging, vendored d3-force, documents by default with the code layer switchable): ADR-0020 accepted; US-08-01-42 + REQ-08-01-42-01/-02/-03, spec-first red/green — the self-contained explorer page with embedded core graph (title/lifecycle enrichment), the vendored engine digest-recorded as SRC-0005, publish-site staging for the default language; verified live in headless Chromium (545 nodes, 2029 edges, no console errors). + Triage note: the uniform machine-readable findings format (FR-E1) is not a dependency — the explorer reads the ADR-0006 core graph, FR-E1 concerns the findings surface of the lint/verify commands and stays an independent slice. Band 2 is decided; the remaining PLANS.md table slices are A2-A4 (ontology/entity), B1-B2 (evidence/provenance), C5 (lifecycle), D6 (crosswalk), and D7 (queries). diff --git a/docs/en/project/roadmap.md b/docs/en/project/roadmap.md index 291160b..27608fa 100644 --- a/docs/en/project/roadmap.md +++ b/docs/en/project/roadmap.md @@ -130,7 +130,7 @@ The full record is the second intake in the plan package (`docs/en/plans/knowled - Landed with the first band-3 slices: the frontmatter vocabularies as configuration (US-08-01-29), the ONT-003 resolution pinned with its scanning boundary recorded (US-08-01-30, FR-A1), the coupling rules bound to the story-driven module via `[process].modules` (US-08-01-31), `--set` placeholder filling (US-08-01-32, FR-B1), kind-declared id and dir templates (US-08-01-33, FR-B2), the local-copy digest verification SRC-006 (US-08-01-34, the FR-A2 integrity half), and declared property vocabularies with FM-009 (US-08-01-35, FR-C1). - Later bands: FR-A3 (configurable catalog projections) alongside crosswalks and queries; FR-D1/FR-D2 (semantic projections, competency-question traceability) after the evidence layer. -Candidate for 0.3.0 (owner idea 2026-07-12): an interactive graph explorer over the trace graph — the corpus as a navigable node-link view with kind/status filters, in the spirit of Obsidian's graph view; likely a self-contained page generated into the published site from the `trace scan` JSON. +Landed for 0.3.0 (owner idea 2026-07-12, decided as ADR-0020): the interactive graph explorer over the trace graph — `report graph` renders the corpus as a self-contained navigable node-link view with type and lifecycle filters, and `publish site` stages it with the site (US-08-01-42). ## What this roadmap is not