From 4d59556ef39f7531d88e595094ac29a96bd26192 Mon Sep 17 00:00:00 2001 From: "Joseph T. French" Date: Thu, 24 Sep 2026 18:48:08 -0500 Subject: [PATCH 1/2] chore(deps): xbrlkit 0.18.1; the Tavi now carries style and fact sets xbrlkit 0.18.1 writes an authored report's reporting style, legal name, and each network's structure, block type and fact set, and each fact's structure, as rs: properties on the Tavi. A tenant's published Tavi anchor now carries them, so omitted_content stops naming reporting_style and fact_sets, and the download field's description says so. --- pyproject.toml | 2 +- robosystems/models/api/extensions/reports.py | 11 +++--- .../operations/serialization/xbrl/tavi.py | 5 +-- .../serialization/test_tavi_emitter.py | 37 ++++++++++++++++++- uv.lock | 8 ++-- 5 files changed, 48 insertions(+), 15 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index a638a224d..174fa0e1f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -68,7 +68,7 @@ dependencies = [ "pandas>=2.3.0,<3.0", "pyarrow>=24.0.0,<25", # hold: 25 is a fresh major feeding the duckdb/ladybug arrow seams; bump deliberately, paired with icebug 13 "rdflib>=7.0.0,<8.0", - "xbrlkit>=0.18.0,<0.19.0", + "xbrlkit>=0.18.1,<0.19.0", # External API Integrations "intuit-oauth>=1.2.0,<2.0", "python-quickbooks>=0.9.0,<1.0", diff --git a/robosystems/models/api/extensions/reports.py b/robosystems/models/api/extensions/reports.py index c2956e968..646a19898 100644 --- a/robosystems/models/api/extensions/reports.py +++ b/robosystems/models/api/extensions/reports.py @@ -569,12 +569,11 @@ class ReportBundleDownloadResponse(BaseModel): "`disclosure_notes`: the XBRL 2.1 zip ships statements only — " "tenant-authored disclosure notes render on screen and ride the " "Tavi and holon flavors, but are excluded from this file. " - "The Tavi compiled model carries the statements and notes but has no " - "home for `ib_envelopes` (the per-Network Information Block " - "payloads), `definition_links` (equivalence, general-special, " - "essence-alias and mapping arcs), `reporting_style`, " - "`framework_pins`, `fact_sets` (the FactSet partition and each " - "fact's structure pin) or `filing_lifecycle` (filing status, " + "The Tavi compiled model carries the statements, notes, reporting " + "style and FactSet partition but has no home for `ib_envelopes` " + "(the per-Network Information Block payloads), `definition_links` " + "(equivalence, general-special, essence-alias and mapping arcs), " + "`framework_pins` or `filing_lifecycle` (filing status, " "supersession, share provenance); all of it rides the holon flavor." ), ) diff --git a/robosystems/operations/serialization/xbrl/tavi.py b/robosystems/operations/serialization/xbrl/tavi.py index 5cfeed119..0ddcb3e75 100644 --- a/robosystems/operations/serialization/xbrl/tavi.py +++ b/robosystems/operations/serialization/xbrl/tavi.py @@ -20,13 +20,12 @@ TAVI_MEDIA_TYPE = "application/json" -# The holon keeps all of it. +# The holon keeps all of it. The reporting style and the fact-set partition +# ride the Tavi as `rs:` properties since xbrlkit 0.18.1. TAVI_OMITTED_CONTENT: tuple[str, ...] = ( "ib_envelopes", "definition_links", - "reporting_style", "framework_pins", - "fact_sets", "filing_lifecycle", ) diff --git a/tests/operations/serialization/test_tavi_emitter.py b/tests/operations/serialization/test_tavi_emitter.py index 3e8c0d41d..d8c3f15cd 100644 --- a/tests/operations/serialization/test_tavi_emitter.py +++ b/tests/operations/serialization/test_tavi_emitter.py @@ -61,7 +61,13 @@ def test_envelope_is_a_compiled_model_scoped_on_the_report() -> None: @pytest.mark.unit def test_entity_is_named_under_its_scheme_and_labelled() -> None: document = _document() - assert document["xbrlModel"]["entities"] == [{"name": "entity:ent_01"}] + # The legal name differs from the name, so it rides as a property. + assert document["xbrlModel"]["entities"] == [ + { + "name": "entity:ent_01", + "properties": [{"property": "rs:legalName", "value": "Test Co LLC"}], + } + ] assert _labels_of(document, "entity:ent_01") == {"xbrl:label": "Test Co"} assert _fact(document, "rs-gaap:Assets")["factDimensions"]["xbrl:entity"] == ( "entity:ent_01" @@ -160,6 +166,32 @@ def test_serialization_is_compact_deterministic_and_dispatched() -> None: assert json.loads(first)["documentInfo"]["documentType"] == DOCTYPE_COMPILED +@pytest.mark.unit +def test_the_reporting_style_and_fact_set_partition_ride_as_rs_properties() -> None: + """What `omitted_content` no longer names has to be in the file.""" + bundle = _bundle() + document = json.loads(serialize_to_tavi(bundle)) + model = document["xbrlModel"] + assert document["documentInfo"]["namespaces"]["rs"] == "https://robosystems.ai/vocab/" + assert {"property": "rs:reportingStyle", "value": bundle.reporting_style} in model[ + "properties" + ] + network_properties = { + p["property"]: p["value"] + for n in model["networks"] + for p in n.get("properties", []) + } + assert network_properties["rs:structureId"] in {"struct_bs", "struct_note1"} + assert "rs:blockType" in network_properties + pins = [ + p["value"] + for f in model["facts"] + for p in f.get("properties", []) + if p["property"] == "rs:structureId" + ] + assert len(pins) == sum(1 for f in bundle.facts if f.structure_id) + + @pytest.mark.unit def test_description_and_omitted_content_name_what_the_file_lacks() -> None: assert tavi_description( @@ -169,3 +201,6 @@ def test_description_and_omitted_content_name_what_the_file_lacks() -> None: ).startswith("RoboLedger live snapshot (Test Co)") assert "ib_envelopes" in TAVI_OMITTED_CONTENT assert "definition_links" in TAVI_OMITTED_CONTENT + # Carried as `rs:` properties since xbrlkit 0.18.1. + assert "reporting_style" not in TAVI_OMITTED_CONTENT + assert "fact_sets" not in TAVI_OMITTED_CONTENT diff --git a/uv.lock b/uv.lock index 35842431e..7adb92cc1 100644 --- a/uv.lock +++ b/uv.lock @@ -3813,7 +3813,7 @@ requires-dist = [ { name = "uvicorn", specifier = ">=0.48.0,<1.0" }, { name = "uvloop", marker = "sys_platform != 'win32'", specifier = ">=0.21.0,<1.0" }, { name = "webauthn", specifier = ">=2.7.0,<3.0" }, - { name = "xbrlkit", specifier = ">=0.18.0,<0.19.0" }, + { name = "xbrlkit", specifier = ">=0.18.1,<0.19.0" }, { name = "zstandard", specifier = ">=0.23,<1" }, ] provides-extras = ["dev"] @@ -4640,7 +4640,7 @@ wheels = [ [[package]] name = "xbrlkit" -version = "0.18.0" +version = "0.18.1" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "arelle-release" }, @@ -4652,9 +4652,9 @@ dependencies = [ { name = "regex" }, { name = "requests" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/76/3a/9d6c43a81956f939d0206db4a8ca73ea884afdd4379ae572f08eff57f9ba/xbrlkit-0.18.0.tar.gz", hash = "sha256:540f006e1c1b7a854ae16f6c88f3d402e5b5fd2bb1d8c04e3d04dc3b9b5950b0", size = 2107480, upload-time = "2026-09-24T23:22:31.057Z" } +sdist = { url = "https://files.pythonhosted.org/packages/16/c8/f5972b91e0c5a501ded25eef32d90fb7f0b43f40ce920d16643671763a4f/xbrlkit-0.18.1.tar.gz", hash = "sha256:b99b46dc7d372cceca5c86f43c2d3fb76e042da5da3fd31ed1c062dabc912765", size = 2108968, upload-time = "2026-09-24T23:42:02.372Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/25/8e/7a8c838cff3aa0919ca1360900a2594cdfba4067d450d7aeac51536674ad/xbrlkit-0.18.0-py3-none-any.whl", hash = "sha256:25a46e8be53a2b82bd025a3966ab22e711df1be727eccf0a32594885260b59f3", size = 2063721, upload-time = "2026-09-24T23:22:29.3Z" }, + { url = "https://files.pythonhosted.org/packages/a5/d5/367752ae27befd860f3782c09b4575b835b84f02a702ddfc5fdc280812c9/xbrlkit-0.18.1-py3-none-any.whl", hash = "sha256:a129433bcb6cb6df17b6091f99b788126c7daa4468b7c47366dc558d6d8ca288", size = 2064968, upload-time = "2026-09-24T23:42:00.346Z" }, ] [[package]] From bafc62ea60e53d7034943ca34fa01348b108f9a7 Mon Sep 17 00:00:00 2001 From: "Joseph T. French" Date: Thu, 24 Sep 2026 18:50:27 -0500 Subject: [PATCH 2/2] docs: update README for clarity and consistency in terminology --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cc3a9fcdd..d8ba9e3d1 100644 --- a/README.md +++ b/README.md @@ -203,7 +203,7 @@ Because tenancy is enforced at the graph rather than in application predicates, ### Identity & Access -Programmatic access uses `X-API-Key`; the browser apps use short-lived JWTs. How a person *logs in* is a deployment decision — password, WebAuthn passkey, or an enterprise identity provider — published at `GET /v1/auth/providers`, so one frontend build renders whichever posture the backend is configured for. +Programmatic access uses `X-API-Key`; the browser apps use short-lived JWTs. How a person _logs in_ is a deployment decision — password, WebAuthn passkey, or an enterprise identity provider — published at `GET /v1/auth/providers`, so one frontend build renders whichever posture the backend is configured for. Enterprise SSO (OIDC) and SCIM 2.0 provisioning ship in the repository, off by default — available to any fork without a license gate. Provisioning is link-only: SCIM creates accounts and OIDC only resolves already-provisioned ones, so the identity provider owns the account lifecycle in both directions. @@ -341,7 +341,7 @@ pip install robosystems-client ## Documentation -### Documentation +### Technical Documentation **Getting Started & Platform:** @@ -367,7 +367,7 @@ pip install robosystems-client - [RoboLedger Demo Walkthrough](https://robosystems.ai/docs/technical/roboledger-demo-walkthrough) · [SEC XBRL Pipeline](https://robosystems.ai/docs/technical/sec-xbrl-pipeline) · [Custom Graph Schema](https://robosystems.ai/docs/technical/custom-graph-schema) -### Developer Documentation (Codebase) +### Codebase Documentation Each package documents itself — read the README for a directory before working in it.