diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml new file mode 100644 index 0000000..a228d71 --- /dev/null +++ b/.github/workflows/pages.yml @@ -0,0 +1,49 @@ +name: Deploy manuscript site to GitHub Pages + +# Serves docs/manuscript/ (landing page, slide deck, Figure 1) as a static site. +# The deck and figure vendor reveal.js + Mermaid under docs/manuscript/vendor/, +# so the published site needs no external CDN. + +on: + push: + branches: + - main + - claude/agentic-curation-research-docs-6dgt7l + paths: + - 'docs/manuscript/**' + - '.github/workflows/pages.yml' + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +# Allow one concurrent deployment; newer pushes supersede in-progress ones. +concurrency: + group: pages + cancel-in-progress: true + +jobs: + deploy: + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Configure Pages (auto-enable if needed) + uses: actions/configure-pages@v5 + with: + enablement: true + + - name: Upload manuscript directory as the site + uses: actions/upload-pages-artifact@v3 + with: + path: docs/manuscript + + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/docs/manuscript/00_abstract.md b/docs/manuscript/00_abstract.md new file mode 100644 index 0000000..eab406a --- /dev/null +++ b/docs/manuscript/00_abstract.md @@ -0,0 +1,29 @@ +# Abstract + +> **Status: SCAFFOLD.** To be written last, once results exist. Target ~200–250 +> words. Numbers are `[[PLACEHOLDER]]`. + +**Motivation.** Harmonizing heterogeneous environmental datasets into analysis-ready +form is a labor-intensive, expertise-bound bottleneck in Earth-science synthesis. +`[[1–2 sentences on the soil-moisture harmonization problem and its cost.]]` + +**Approach.** We evaluate a two-agent large language model (LLM) workflow — a +*curator* that ingests a dataset identifier and decides what and how to harmonize, +and a *harmonizer* that generates executable transformation code and a documented +change-mapping — on soil-moisture datasets from the DOE ESS-DIVE repository. We +benchmark it against a domain expert who harmonized 19 Watershed Function SFA +datasets, using grouped leave-one-out cross-validation (retrospective) and blind +prospective evaluation on novel datasets. Our primary endpoint is *output-data +equivalence*: agent and expert code are executed on identical raw inputs and the +resulting tables are compared cell-by-cell, so two correct harmonizations score +equally regardless of coding style. We additionally score each agent in isolation, +the harmonizer under an oracle bundle, and the full pipeline, enabling explicit +attribution of error between the two agents. + +**Results.** `[[PLACEHOLDER: headline end-to-end output equivalence; per-skill +performance; error-propagation gap; dominant failure mode; comparison to +single-call and no-exemplar baselines; similarity-generalization threshold.]]` + +**Conclusions.** `[[PLACEHOLDER: the workflow can/cannot reliably draft expert-grade +harmonizations; recommended human-in-the-loop operating point; implications for +scaling FAIR environmental data curation.]]` diff --git a/docs/manuscript/01_introduction.md b/docs/manuscript/01_introduction.md new file mode 100644 index 0000000..03070bc --- /dev/null +++ b/docs/manuscript/01_introduction.md @@ -0,0 +1,98 @@ +# Introduction + +> **Status:** Drafted. Prose is near-final; citations use author-year keys resolved +> in `references.md`. Any forward reference to a result is marked `[[PLACEHOLDER]]`. + +## The harmonization bottleneck in environmental science + +Synthesizing environmental observations across studies depends on first making +heterogeneous datasets comparable — a process of *harmonization* that maps disparate +source files onto a common schema with consistent variables, units, coordinates, and +time references. For soil moisture, the variable of interest in this work, the same +physical quantity is reported as volumetric water content (in % or m³ m⁻³), +gravimetric water content (g g⁻¹), or matric/water potential (kPa, MPa, or bar); +the gravimetric-to-volumetric conversion itself requires a bulk-density value that is +frequently missing [CEOS-SM; NIDIS-2024]. Sites are georeferenced in incompatible +coordinate systems (UTM vs. latitude/longitude, differing datums), depths follow +different conventions, timestamps are recorded in assorted local time zones, and +tables arrive in both wide and long layouts. Layered on top of these mechanical +differences are scientific judgments: whether a dataset measures soil moisture +directly or derives it from a model, whether observations form a continuous sensor +time series or a discrete sampling campaign, and whether an experimental manipulation +(warming, irrigation) confounds the natural signal. + +Resolving these issues is expert, labor-intensive work, and at scale it remains +largely manual. The International Soil Moisture Network — the field's canonical +harmonization effort, integrating thousands of stations to a common volumetric, UTC +standard — documents that data collection from providers is still done "either +manually (mostly by email) or is automated, depending on the degree of automation at +the data provider side" [Dorigo-2021]. Community pipelines such as AmeriFlux BASE +[Chu-2023] and FLUXNET ONEFlux [Pastorello-2020] show that turning heterogeneous +submissions into a standardized product requires substantial, purpose-built curation +infrastructure. More broadly, data practitioners report spending a large majority of +their time on data preparation and cleaning rather than analysis +[Kandel-2012; Lohr-2014]. This curation cost is a first-order bottleneck on the +reuse of the long tail of environmental data deposited in repositories such as the +U.S. Department of Energy's ESS-DIVE [Crystal-Ornelas-2022]. + +## LLM agents as a route to automated curation + +Large language models, embedded in tool-using *agentic* workflows, have rapidly +become candidates for exactly this kind of work. Recent systems apply LLMs to data +standardization [Qi-2024], autonomous data engineering [Wang-2025], +data preprocessing [Zhang-2024], schema and entity matching [KcMF-2024], and the +generation of executable data-transformation and ETL code [TabulaX-2024; +Prompt2DAG-2025], extending a long programming-by-example lineage from FlashFill +[Gulwani-2011] and the Wrangler/Trifacta tools. Most directly related to the present +work, *Harmonia* casts data harmonization itself as LLM-agent pipeline synthesis, +mapping diverse datasets to a target schema — though it is interactive +(human-in-the-loop), demonstrated on clinical data, and not evaluated against expert +ground truth via cross-validation [Santos-2025]. In parallel, benchmarks for +LLM agents on data tasks consistently find that *end-to-end* data pipelines remain +hard: KramaBench, which spans real-world pipelines including environmental science, +reports that the best system reaches only ~55% end-to-end accuracy [KramaBench-2025]. + +Two further threads frame our design. First, decomposing a task across multiple +agents introduces its own failure modes; recent taxonomies of multi-agent LLM +failures emphasize inter-agent misalignment and the difficulty of *attributing* a +failure to the responsible agent [Cemri-2025]. Second, evaluating stochastic LLM +systems reliably is non-trivial: LLM-as-judge scoring is unstable +[JudgeReliability-2026], motivating automated, execution-grounded metrics with known +answers [Zhang-2025-DataSci]. Our evaluation is built around both concerns. + +## This work + +We study a two-agent workflow, implemented as Anthropic Agent Skills +[AgentSkills-2025], for harmonizing soil-moisture datasets from ESS-DIVE. A +**curator** agent ingests a dataset identifier, inspects the package, decides whether +and how the dataset should be harmonized, and emits a structured bundle; a +**harmonizer** agent consumes that bundle and produces (a) executable Python +transformation code and (b) a documented change-mapping. We benchmark the pair +against a domain expert who harmonized 19 Watershed Function SFA datasets, combining +retrospective grouped leave-one-out cross-validation with prospective blind +evaluation on novel datasets. + +Our evaluation makes three methodological commitments designed to produce a credible, +decomposable estimate of real-world performance: + +1. **Output-data equivalence as the primary endpoint.** Rather than judging code text + or relying on an LLM judge, we *execute* both the agent's and the expert's code on + the same raw inputs and compare the resulting tables cell-by-cell. Two correct + harmonizations score identically regardless of how the code is written. +2. **Decomposed error attribution.** Each agent is scored in isolation, the + harmonizer is scored under an oracle (gold) bundle, and the full pipeline is scored + end-to-end. The gap between the oracle and end-to-end conditions quantifies how + much curator error propagates downstream, and an explicit taxonomy attributes each + end-to-end failure to the curator, the harmonizer, their interface, or genuine + ambiguity. +3. **Honest generalization.** Cross-validation holds out whole source/instrument + clusters, and a per-dataset similarity covariate converts the in-context-exemplar + leakage risk into a measured relationship between dataset novelty and performance. + +Our contributions are: (i) a formal, reproducible evaluation framework for agentic +environmental data harmonization, with an output-equivalence primary endpoint and a +cross-agent error-propagation taxonomy; (ii) retrospective and prospective estimates +of how closely the agent pair reproduces expert harmonization `[[PLACEHOLDER: +headline numbers]]`; and (iii) an analysis of where the workflow succeeds, where and +why it fails, and the dataset-novelty boundary beyond which it should defer to a human +`[[PLACEHOLDER]]`. diff --git a/docs/manuscript/02_background.md b/docs/manuscript/02_background.md new file mode 100644 index 0000000..8724787 --- /dev/null +++ b/docs/manuscript/02_background.md @@ -0,0 +1,202 @@ +# Background and Related Work + +> **Status:** Drafted from the literature research pass (see `research_notes.md` for +> per-source verification status). Author-year keys resolve in `references.md`. +> Sources flagged "snippet-only" there should be confirmed before submission. + +This work sits at the intersection of three literatures: (1) environmental and +Earth-science **data harmonization** practice and the standards it targets; (2) the +classical computer-science literature on **data cleaning, schema matching, and +integration**; and (3) the rapidly moving frontier of **LLM agents for data work**. +We review each in turn and then position our contribution. + +## 1. Harmonization in environmental and Earth science + +### 1.1 The problem and its cost + +Reusing observational environmental data requires reconciling deep heterogeneity in +how the same quantity is recorded. For soil moisture specifically, the same physical +property appears as volumetric water content (% or m³ m⁻³), gravimetric water content +(g g⁻¹), or matric/water potential (kPa, MPa, bar, or pF); converting between +gravimetric and volumetric bases requires a bulk-density value that source datasets +frequently omit [NIDIS-2024; CEOS-SM]. The canonical community effort, the +International Soil Moisture Network (ISMN), addresses this by converting all soil +moisture to volumetric m³ m⁻³ and resampling all series to an hourly UTC reference, +applying automated quality flags rather than deleting suspect values +[Dorigo-2011; Dorigo-2021; Dorigo-2013]. Tellingly for the motivation of this paper, +the ISMN reports that data ingest from providers is still performed "either manually +(mostly by email) or is automated, depending on the degree of automation at the data +provider side" [Dorigo-2021] — i.e., the schema/unit mapping itself remains +substantially manual, with automation largely confined to downstream QC. + +The same pattern holds across networks. AmeriFlux BASE [Chu-2023] and the FLUXNET2015 +ONEFlux pipeline [Pastorello-2020] standardize variable names and units across +hundreds of sites, but only through purpose-built, community-engineered processing +infrastructure. Cosmic-ray neutron networks (COSMOS-UK [Cooper-2021], +COSMOS-Europe [Bogena-2022]) require multi-stage, network-specific correction chains +and harmonized calibration to produce comparable products, and even within a single +"standardized" network (e.g., NEON soil water content [NEON-DP1]) changes in +calibration method over time introduce step discontinuities that complicate reuse. + +This curation burden is an instance of a general phenomenon. Qualitative study of +analysts finds data wrangling and integration to be a dominant, painful bottleneck +[Kandel-2012], and the widely repeated claim that practitioners spend 50–80% of their +time on data preparation traces to a *New York Times* article attributed only to +"interviews and expert estimates" [Lohr-2014] and a small industry survey +[CrowdFlower-2016]; the figure's weak provenance is itself documented [Dodds-2020], +so we cite it as illustrative rather than established. More rigorously, Heidorn's +"dark data in the long tail of science" frames the core risk: the heterogeneous, +hand-curated data of many small projects — exactly the kind harmonized here — is the +most expensive to integrate and the most likely to be lost [Heidorn-2008], and +quantified digitization-labor studies show curation cost is dominated by skilled human +time [Blagoderov-2012]. + +### 1.2 The standards being targeted + +Harmonization is harmonization *toward* something. The datasets in this study come +from the DOE **ESS-DIVE** repository [ESS-DIVE], whose team has developed a family of +community **reporting formats** — the product of reviewing 112 existing standards and +iterating with 247 representatives from 128 institutions — spanning CSV file structure, +file-level metadata, hydrologic monitoring, location metadata, and sample identifiers +[Crystal-Ornelas-2022; Velliquette-2021-CSV; Goldman-2021; Damerow-2021]. Notably, +ESS-DIVE has **no dedicated soil-moisture reporting format**, which is part of what +motivates an automated mapping to a project-defined target schema. The ground-truth +data derive from the Watershed Function SFA and its East River, Colorado community +observatory [Hubbard-2018; Varadharajan-2022], a multiscale hydro-biogeochemical +testbed whose sensor and sample datasets exhibit precisely the file-, unit-, and +coordinate-level heterogeneity our agents must resolve. + +More broadly, environmental data interoperability is served by a stack of information +models, vocabularies, and unit systems that any harmonizer implicitly reconciles: +information models such as ODM2 [Horsburgh-2016], CUAHSI WaterML/HydroShare +[CUAHSI-HIS], OGC Observations & Measurements / SensorThings [OGC-SensorThings], and +the SOSA/SSN ontology [Janowicz-2019]; variable vocabularies such as the CF Standard +Names (which provide canonical identifiers and units for soil-moisture variables) +[CF-Conventions], the NERC vocabulary server [NERC-NVS], ENVO [Buttigieg-2013], and +AGROVOC [AGROVOC]; and unit ontologies such as UCUM [UCUM], QUDT [QUDT], and OM +[Rijgersberg-2013]. All of this is underwritten by the FAIR principles +[Wilkinson-2016], whose interoperability and reusability tenets are, in effect, a +statement of the harmonization problem. Comparable common-schema efforts in adjacent +fields — Darwin Core in biodiversity [Wieczorek-2012], OMOP/OHDSI in health +[OHDSI-OMOP], the Maelstrom guidelines for retrospective cohort harmonization +[Fortier-2017], and large ecological syntheses such as plant-trait harmonization +[Schneider-2020] and BioTIME [Dornelas-2018] — establish both the importance of the +task and the predominantly manual, expert-driven way it is done today. + +### 1.3 Specific heterogeneity hazards + +The transformations our harmonizer must get right are exactly those with a long +history of silent, costly error. Unit mismatches are a classic failure mode in +quantitative science — the loss of NASA's Mars Climate Orbiter to a +pound-force-seconds vs. newton-seconds mismatch being the canonical example +[Mars-Orbiter] — motivating machine-actionable unit systems and conversion libraries +(UCUM, QUDT, OM, Pint) that surface dimensional errors rather than hiding them +[UCUM; QUDT; Rijgersberg-2013; Pint]; in soil science the volumetric–gravimetric +conversion's dependence on bulk density makes this especially treacherous +[Dorigo-2021]. Coordinate-reference heterogeneity (mixed UTM zones, datum +differences, axis order) is handled in software by PROJ/EPSG [PROJ], and automated +geocoordinate cleaning of biodiversity records shows such errors are pervasive at +scale — CoordinateCleaner flagged ~3.6% of ~91M GBIF plant records, including +systematic coordinate-conversion artifacts [Zizka-2019]. Time-zone and UTC conversion +of sensor time series is a frequent multi-hour error source absent explicit ISO-8601 +offsets [ISO-8601], depth conventions differ across sources (standardized depth +intervals such as GlobalSoilMap's being one response [Poggio-2021]), and the +reshaping between wide and long ("tidy") table layouts is itself a recognized, +error-prone operation [Wickham-2014]. Finally, distinguishing genuinely observational +data from experimentally manipulated data (warming, irrigation) is a scientific +judgment with direct downstream consequences: experimental and observational studies +can yield *opposite* directional responses, so naively pooling them confounds any +synthesis [Yue-2017] — a distinction our curator must make explicitly. + +### 1.4 Gold standards and benchmarks + +Evaluating automated harmonization against expert ground truth follows an established +paradigm: schema-matching, ontology-alignment, and entity-resolution work is scored +against expert-curated reference alignments using precision, recall, and F-measure +[OAEI]. The data-management community has built benchmarks for adjacent tasks — +error detection [Abedjan-2016], the impact of cleaning on downstream ML (CleanML) +[CleanML-2021], controlled error injection with known ground truth (BART) +[Arocena-2015], entity matching (DeepMatcher, WDC Products) +[Mudgal-2018; Peeters-2024], and semantic-type / table-to-KG interpretation +(Sherlock, SemTab) [Hulsebos-2019; SemTab]. Most directly, recent benchmarks for +*LLM agents* on data-science and data-engineering tasks — DSBench [Jing-2024], +InfiAgent-DABench [Hu-2024], and KramaBench [KramaBench-2025] — report that current +agents fall well short of expert performance (e.g., the best DSBench agent solves +~34% of data-analysis tasks). These results both motivate our study and frame our +methodological choice to benchmark against a real expert-harmonized corpus with +execution-grounded agreement metrics. + +## 2. Data cleaning, schema matching, and integration + +Our harmonizer is, in classical terms, a system that performs schema matching, value +transformation, and entity/record reconciliation, then emits a transformation +program. Each has a deep literature. Interactive cleaning and wrangling systems — +Potter's Wheel [Raman-2001], Wrangler/Trifacta [Kandel-2011], and OpenRefine +[OpenRefine] — established the value of generating reusable, inspectable +transformation scripts rather than one-off manual edits, and are the conceptual +ancestors of an agent that writes auditable harmonization code. Schema matching has a +canonical taxonomy [Rahm-2001] revisited a decade later [Bernstein-2011]; data +quality and constraint-based repair are surveyed in [Ilyas-2019] and exemplified by +HoloClean's probabilistic-inference approach [Rekatsinas-2017]; and entity resolution +descends from the Fellegi–Sunter model [Fellegi-1969] through managed pipelines such +as Magellan [Konda-2016]. Programming-by-example synthesis of transformations, from +FlashFill onward [Gulwani-2011], is the direct precursor to LLM code generation for +data wrangling. + +The transition to learned and then foundation-model methods is recent and rapid: DITTO +brought pre-trained language models to entity matching [Li-2020]; Narayan et al. showed +that a single foundation model can match or beat task-specific systems across five +cleaning/integration tasks with no fine-tuning ["Can Foundation Models Wrangle Your +Data?"] [Narayan-2022]; Table-GPT improved LLMs' tabular reasoning via table-tuning +[Li-2023]; and empirical studies of LLM schema matching find that prompt context must +be balanced — too little or too much degrades quality [Parciak-2024]. These works +establish that LLMs are credible engines for the *sub-tasks* of harmonization; +our contribution is to evaluate a structured *agent pair* that composes them +end-to-end against executed expert ground truth. + +## 3. LLM agents for data curation and harmonization + +The closest contemporary work casts harmonization itself as agentic pipeline +synthesis. **Harmonia** uses LLM reasoning plus an interactive UI and a library of +harmonization primitives to map diverse datasets to a standard schema, demonstrated on +clinical data [Santos-2025]; it is the most directly comparable system, but it is +human-in-the-loop, in a different domain, and is not evaluated with cross-validation +or cell-level output equivalence. Single-agent standardization and data-engineering +systems are also emerging: CleanAgent decomposes data standardization into iterative +LLM steps [Qi-2024]; Dataforge adds grounding-validation and dual-loop optimization +with cross-validated evaluation [Wang-2025]; Jellyfish instruction-tunes local models +as universal preprocessors [Zhang-2024]; and a line of schema/entity-matching and +ETL-generation work applies LLMs to the mapping and code-generation steps +[KcMF-2024; TabulaX-2024; Prompt2DAG-2025]. On the scientific-curation side, LLMs are +being used to extract and *validate* metadata against ground truth +[MOLE-2025; DCAT-LLM-2025], directly analogous to our conformance and accuracy checks. + +Three further threads shape our methodology. First, **multi-agent failure**: recent +taxonomies (MAST) catalogue how chained LLM systems fail through inter-agent +misalignment and weak verification, and emphasize the difficulty of *attributing* a +failure to the responsible agent [Cemri-2025] — exactly what our oracle-vs-end-to-end +decomposition and error-propagation taxonomy operationalize for a two-agent pipeline. +Second, **evaluation of agents on data tasks**: benchmarks such as DataSciBench +[Zhang-2025-DataSci] and KramaBench [KramaBench-2025] grapple with uncertain ground +truth and execution-based scoring, and KramaBench — which spans environmental science — +finds the best system reaches only ~55% end-to-end accuracy, underscoring that this is +an unsolved problem. Third, **the unreliability of LLM-as-judge** scoring +[JudgeReliability-2026] motivates our reliance on automated, execution-grounded +cell-level metrics with expert validation rather than model-graded outputs. Our agents +are implemented as Anthropic **Agent Skills** [AgentSkills-2025], a structured +instruction-and-tool packaging in which the in-context exemplars (the 19 expert +harmonizations) function as retrievable references. + +## 4. Positioning of this work + +Against this backdrop, our contribution is methodological and empirical rather than a +new system per se. Where Harmonia and CleanAgent demonstrate feasibility, and where +data-task benchmarks report aggregate success rates, we provide a **rigorous, +decomposable evaluation** of an autonomous curator–harmonizer pair on real +environmental data: an output-data-equivalence primary endpoint (executed code +compared cell-by-cell against an expert's), grouped cross-validation with a +similarity covariate to give honest generalization estimates, and a cross-agent +error-propagation taxonomy that attributes each failure to its source. To our +knowledge this is the first such evaluation in the environmental-data-harmonization +setting, and it directly addresses the verification and attribution gaps highlighted +by the multi-agent and agent-evaluation literature. diff --git a/docs/manuscript/03_methods.md b/docs/manuscript/03_methods.md new file mode 100644 index 0000000..ac7fd60 --- /dev/null +++ b/docs/manuscript/03_methods.md @@ -0,0 +1,235 @@ +# Methods + +> **Status:** Drafted from the locked evaluation design (`config/`, `src/`, `skills/`). +> All numbers, fold counts, and model identifiers are placeholders pending the +> final experimental run and should be reconciled against `config/experiment.yaml` +> and `config/cv_folds.yaml` before submission. + +## 1. Overview + +We evaluate a two-agent large language model (LLM) workflow that automates the +harmonization of heterogeneous soil-moisture datasets into a single analysis-ready +schema. The workflow is a *curator* agent (Skill 1) followed by a *harmonizer* +agent (Skill 2). The curator ingests a dataset identifier, retrieves and inspects +the package, makes an inclusion/exclusion decision, and emits a structured bundle; +the harmonizer consumes that bundle and emits (a) executable Python transformation +code and (b) a documented change-mapping. We measure how well this pair reproduces +the work of a domain expert, both retrospectively (cross-validation against a fixed +gold standard) and prospectively (blind comparison on previously unseen datasets). + +The design has three deliberate properties: + +1. **Output-centric scoring.** The primary endpoint is *output-data equivalence* — + the harmonized table produced by the agent's code is compared cell-by-cell with + the table produced by the expert's code on the *same* raw inputs. Two correct + harmonizations score identically even if the underlying Python differs entirely, + so we measure *what the code does*, not how it is written. +2. **Decomposable error attribution.** Each skill is scored in isolation and the + pipeline is scored end-to-end, allowing us to separate curator error from + harmonizer error and to quantify error propagation between the two agents. +3. **Honest generalization estimates.** Cross-validation holds out whole + source/instrument clusters, and a per-dataset similarity covariate converts the + in-context-exemplar leakage risk into a measured effect rather than an + uncontrolled confound. + +## 2. The agent pair under evaluation + +Both agents are implemented as Anthropic *Agent Skills* — versioned instruction +bundles (system prompt plus reference context) invoked inside an agentic +tool-using loop. Skill versions are pinned per run (`skill1_version`, +`skill2_version` in `config/experiment.yaml`). + +### 2.1 Skill 1 — Curator (`essdive_sm_curator`) + +Given one or more DOIs / ESS-DIVE package identifiers, the curator: + +- normalizes identifiers and retrieves package metadata (local cache first, then + the ESS-DIVE API); +- classifies each file into *data payload*, *location metadata*, *sensor metadata*, + or *documentation*; +- inspects payload headers to locate timestamp, moisture (VWC / GWC / water + potential), depth, site, and replicate columns; +- resolves site coordinates through a fixed fallback cascade (payload → ancillary + file → package metadata → external location registry → unresolvable), assigning a + `qc_flag` recommendation; +- infers time-series vs. discrete sampling and, for time series, a sampling + interval; +- detects experimental manipulations (warming, irrigation, fertilization, etc.) and + recommends include / exclude / flag; +- selects the most structurally similar gold exemplar; and +- emits a structured **curator bundle** (`src/schemas/skill1_bundle.py`) with an + `INCLUDE` / `EXCLUDE` / `FLAG_FOR_REVIEW` decision and a list of open questions. + +### 2.2 Skill 2 — Harmonizer (`wfsfa_sm_harmonization`) + +Given a curator bundle (or an operator-supplied equivalent), the harmonizer: + +- re-confirms the inclusion decision under a strict rule set; +- maps each source variable to the target schema, including unit conversions; +- resolves location, depth encoding, timestamp format/timezone, and replicate + encoding; +- generates a Python code block following fixed conventions (shared helper + functions, wide-to-long reshaping, UTC conversion, column-order enforcement); and +- emits a JSON **change-mapping** entry (`src/schemas/skill2_mapping.py`) + documenting every transformation with source pattern, destination variable, + transformation description, and unit conversion. + +### 2.3 Target schema + +Every harmonized table must contain exactly nine columns +(`src/schemas/target_schema.py`): + +`datetime_UTC`, `site_id`, `depth_m`, `replicate`, `is_timeseries`, +`interval_min`, `volumetric_water_content_m3_m3`, +`gravimetric_water_content_gH2O_gs`, `water_potential_kPa`. + +Canonical units are meters (depth), m³ m⁻³ (VWC), g H₂O g⁻¹ soil (GWC), and kPa +(water potential, negative). A controlled `qc_flag` vocabulary records depth +approximation (`d1`) and coordinate provenance (`g1` retrieved from an external +registry, `g2` unresolvable). Schema conformance is validated programmatically +(column presence/exclusivity, dtypes, value ranges, required-field non-nullity). + +## 3. Ground truth + +The reference standard is a corpus of **19 expert-harmonized** Watershed Function +SFA (WFSFA) soil-moisture datasets drawn from ESS-DIVE. For each, the expert +produced (a) a harmonized output table, (b) modular, documented Python +transformation code (`data/gold/expert_code/harmonize_sm/`, one +`dataset_NN.py` per dataset plus shared `common.py`), and (c) a structured + +free-text change-mapping (`data/gold/sm_data_harmonization_mapping.json`). The +mapping file also encodes excluded datasets with their plain-language exclusion +reasons, providing gold labels for the curator's decision task. + +> **TODO (results-time):** confirm the exact count of *included* vs. *excluded* +> datasets in the final gold set and report it here; the mapping JSON currently +> enumerates indices 0–27 with several excluded/auxiliary entries. + +## 4. Study design + +The study has two phases; within each, the agent pair is evaluated in three modes. + +### 4.1 Phase A — Retrospective cross-validation + +We perform **grouped leave-one-out cross-validation** over the gold datasets. +Datasets are grouped into source/instrument **clusters** (`config/cv_folds.yaml`) +so that datasets sharing a lab, site, or instrument family are held out together; +this prevents a near-duplicate sibling from leaking into the exemplar pool and +inflating apparent performance. For each fold, the held-out dataset(s) are +harmonized using only the *other* datasets as exemplars, and outputs are scored +against the expert version. + +Phase A serves a second purpose: it **validates the automated metrics** against +expert judgment. If automated output-equivalence scoring agrees with an expert +rubric on the gold data, we can rely on the cheaper automated scoring in Phase B. + +### 4.2 Phase B — Prospective blind evaluation + +Using all gold datasets as exemplars, the agent harmonizes genuinely new ESS-DIVE +datasets. In parallel and blind to the agent's output, the domain expert harmonizes +the same datasets; the expert output becomes the reference. This estimates +real-world deployment performance on data neither the agent nor the expert has +previously processed. + +### 4.3 Evaluation modes + +Both phases score the system three ways to localize error: + +1. **Skill 1 in isolation** — the curator bundle is scored field-by-field against + expert labels. +2. **Skill 2 with oracle input** — the harmonizer is fed the *correct* (gold) + curator bundle, so transformation quality is measured independently of curator + error. +3. **End-to-end** — the full pipeline runs and errors propagate. The gap between + modes (2) and (3) quantifies how much curator error degrades the final output. + +### 4.4 Baselines and controls + +- **Non-agentic single-call LLM** — one prompt, no skill structure or tool loop. +- **No-exemplars agent** — the agent with the gold exemplars removed, isolating the + value of the 19 references. +- **Naïve heuristic** — a string-matching baseline. +- **Similarity covariate** — for each evaluation dataset we compute similarity to + its nearest available exemplar and model it explicitly, turning a leakage risk + into a reported finding ("performance holds up to similarity *X*, then degrades"). +- **Reproducibility controls** — model ID/version, both skill versions, sampling + parameters, and random seed are pinned per run (`config/experiment.yaml`). + +> **TODO (results-time):** record the exact model identifier and version actually +> used (config currently pins a Claude model), temperature, `max_tokens`, and the +> number of stochastic repeats per dataset/mode (`n_stochastic_runs`). + +## 5. Metrics + +All scoring lives in `src/metrics/` and is phase-agnostic. + +### 5.1 Skill 1 (curator) + +Decision accuracy / precision / recall / F1 over `INCLUDE` / `EXCLUDE` / `FLAG`; +file-set precision/recall/F1 for payload, location, and sensor files; time-series +binary accuracy and sampling-interval numeric error; location-source and `qc_flag` +accuracy; manipulation-detection accuracy; exemplar-selection match quality; and +deferral (FLAG) calibration against expert confidence. A composite curator score +aggregates these with fixed, versioned weights (`config/metrics_weights.yaml`). + +### 5.2 Skill 2 (harmonizer) + +- **Primary endpoint — output-data equivalence.** Agent and expert code are each + executed on the same raw inputs in a sandbox; the resulting tables are aligned on + the natural key (`datetime_UTC`, `site_id`, `depth_m`, `replicate`) and compared. + We report row-alignment precision/recall/F1 and per-column cell agreement + (numeric columns within a float tolerance; categorical columns by exact match), + with sub-scores for datetime, coordinate, depth, and unit-conversion accuracy. +- **Schema conformance** — columns, dtypes, ranges, required fields. +- **Semantic mapping accuracy** — precision/recall/F1 of the change-mapping against + the expert's, over a controlled transformation-type vocabulary. +- **Code executability** — does the generated code run and reproduce its claimed + output. +- **Documentation completeness** — does the change-mapping cover all and only the + actual transformations. +- **Ontology / controlled-vocabulary validity** — correct use of schema terms and + `qc_flag` codes. + +A composite harmonizer score weights output equivalence most heavily +(`config/metrics_weights.yaml`). + +### 5.3 End-to-end + +Task success (output equivalence above threshold) plus an **error-propagation +taxonomy** classifying each failure as (i) Skill-1 error propagated, (ii) Skill-2 +error given correct input, (iii) inter-skill interface inconsistency, or (iv) +genuinely ambiguous (expert also uncertain). Classification logic is in +`src/analysis/error_taxonomy.py`. + +## 6. Statistical analysis + +The data are nested: stochastic runs within datasets within source-clusters. To +avoid inflating the effective sample size we: + +- report **cluster-level bootstrap confidence intervals** (resampling clusters, not + individual runs); +- fit **mixed-effects models** with dataset and cluster as random effects and mode + and similarity as fixed effects + (`metric ~ mode + similarity + (1|dataset) + (1|cluster)`); +- report the **error-propagation gap** (oracle minus end-to-end) as a paired + difference with a cluster-bootstrap CI; +- emphasize **effect sizes with CIs** over *p*-values, given the small number of + gold datasets (N ≈ 19); and +- report the **distribution across stochastic repeats** (variance, and pass@k where + appropriate), not only means. + +Inter-rater reliability between automated metrics and the expert rubric is reported +for the metric-validation analysis (`src/analysis/irr.py`). + +## 7. Reproducibility and provenance + +Per-run artifacts (raw agent outputs, scored tidy CSVs, figures, tables) are written +under `results/`. Each run records the model identifier/version, both skill +versions, sampling parameters, and the random seed. The gold corpus is treated as +read-only. Code execution occurs in a sandbox (`src/execution/sandbox.py`) and +output comparison in `src/execution/output_loader.py`. + +> **Reporting checklist (fill at results-time):** model + version; skill versions; +> temperature / sampling; seeds; `n_stochastic_runs`; number of folds and held-out +> clusters; counts of included/excluded gold datasets; number and provenance of +> prospective (Phase B) datasets; software/library versions; total compute / token +> budget. diff --git a/docs/manuscript/04_results.md b/docs/manuscript/04_results.md new file mode 100644 index 0000000..8465a62 --- /dev/null +++ b/docs/manuscript/04_results.md @@ -0,0 +1,169 @@ +# Results + +> **Status: SCAFFOLD ONLY — no results yet.** This section is structured so that +> figures, tables, and numbers can be dropped in once the experimental runs +> complete. Every `[[PLACEHOLDER]]` marks a value, table, or figure to be generated +> by the analysis pipeline. Do not report any number here that is not produced by +> `experiments/` and written under `results/`. + +## 0. Reporting conventions + +All point estimates are reported with 95% cluster-level bootstrap confidence +intervals in brackets. Cell-agreement and F1 metrics are fractions in [0, 1]. +"Pass" denotes output equivalence at or above the configured threshold +(`output_equivalence_min`, currently 0.95). Unless stated otherwise, per-mode +numbers pool stochastic repeats and we also report run-to-run variance. + +## 1. Gold corpus characterization + +> Descriptive, generated by `notebooks/01_explore_gold.ipynb`. + +- Number of gold datasets, and the include/exclude split: `[[PLACEHOLDER]]`. +- Distribution of structural types (wide vs. long; time-series vs. discrete; + coordinate system; unit conventions; presence of separate location/sensor files): + **Table 1** `[[PLACEHOLDER]]`. +- Cluster membership and sizes (`config/cv_folds.yaml`): `[[PLACEHOLDER]]`. +- Pairwise dataset-similarity distribution (motivates the grouped-CV design): + **Figure 1** `[[PLACEHOLDER]]`. + +## 2. Phase A — Retrospective cross-validation + +### 2.1 Skill 1 (curator) in isolation + +> Generated by `experiments/phase_a_crossval.py` (mode `skill1_isolated`), +> scored by `src/metrics/skill1_metrics.py`. + +**Table 2 — Curator performance.** `[[PLACEHOLDER]]` + +| Metric | Score [95% CI] | +|---|---| +| Decision accuracy (INCLUDE/EXCLUDE/FLAG) | `[[ ]]` | +| Decision macro-F1 | `[[ ]]` | +| File-selection F1 — payload / location / sensor | `[[ ]]` | +| Time-series binary accuracy | `[[ ]]` | +| Sampling-interval error (min) | `[[ ]]` | +| Location-source accuracy | `[[ ]]` | +| `qc_flag` accuracy | `[[ ]]` | +| Manipulation-detection accuracy | `[[ ]]` | +| Exemplar-selection match | `[[ ]]` | +| Deferral (FLAG) calibration | `[[ ]]` | +| **Composite curator score** | `[[ ]]` | + +- Confusion matrix over the three decisions: **Figure 2** `[[PLACEHOLDER]]`. +- Where does the curator most often err (file selection vs. time-series vs. + location)? `[[PLACEHOLDER narrative]]`. + +### 2.2 Skill 2 (harmonizer) with oracle input + +> Mode `skill2_oracle`; primary scoring by `src/metrics/skill2_output_equiv.py`. + +**Table 3 — Harmonizer performance given correct curator input.** `[[PLACEHOLDER]]` + +| Metric | Score [95% CI] | +|---|---| +| **Output-data equivalence (overall cell agreement)** | `[[ ]]` | +| Row-alignment F1 | `[[ ]]` | +| Datetime accuracy | `[[ ]]` | +| Coordinate accuracy | `[[ ]]` | +| Depth accuracy | `[[ ]]` | +| Unit-conversion accuracy | `[[ ]]` | +| Schema conformance (pass rate) | `[[ ]]` | +| Semantic mapping F1 | `[[ ]]` | +| Code executability (pass rate) | `[[ ]]` | +| Documentation completeness | `[[ ]]` | +| **Composite harmonizer score** | `[[ ]]` | +| **Output-equivalence pass rate (≥ threshold)** | `[[ ]]` | + +- Per-column cell-agreement breakdown: **Figure 3** `[[PLACEHOLDER]]`. +- Distribution of per-dataset output equivalence (how many are near-perfect vs. + partial): **Figure 4** `[[PLACEHOLDER]]`. + +### 2.3 End-to-end pipeline + +> Mode `end_to_end`. + +**Table 4 — End-to-end vs. oracle.** `[[PLACEHOLDER]]` + +| Metric | Oracle (Skill 2) | End-to-end | Gap [95% CI] | +|---|---|---|---| +| Output-data equivalence | `[[ ]]` | `[[ ]]` | `[[ ]]` | +| Output-equivalence pass rate | `[[ ]]` | `[[ ]]` | `[[ ]]` | +| Composite score | `[[ ]]` | `[[ ]]` | `[[ ]]` | + +The **error-propagation gap** (oracle − end-to-end) is `[[PLACEHOLDER]]` +(`src/analysis/stats.py::error_propagation_gap`), quantifying the fraction of +end-to-end degradation attributable to curator error. + +### 2.4 Error-propagation taxonomy + +> `src/analysis/error_taxonomy.py`. + +**Figure 5 / Table 5 — Attribution of end-to-end failures.** `[[PLACEHOLDER]]` + +| Failure category | Share of failures | +|---|---| +| Skill-1 error propagated | `[[ ]]` | +| Skill-2 error (correct input) | `[[ ]]` | +| Inter-skill interface inconsistency | `[[ ]]` | +| Genuinely ambiguous (expert also uncertain) | `[[ ]]` | + +### 2.5 Baselines + +> `config/experiment.yaml::baselines`. + +**Table 6 — Agent pair vs. baselines** (output equivalence, composite). +`[[PLACEHOLDER]]` + +| System | Output equivalence [95% CI] | Pass rate | +|---|---|---| +| Agent pair (end-to-end) | `[[ ]]` | `[[ ]]` | +| Single-call LLM (non-agentic) | `[[ ]]` | `[[ ]]` | +| Agent without exemplars | `[[ ]]` | `[[ ]]` | +| Naïve string-matching heuristic | `[[ ]]` | `[[ ]]` | + +Key contrasts to report: agent-pair vs. single-call (value of agentic structure); +agent vs. no-exemplars (value of the 19 references). + +### 2.6 Similarity–performance relationship + +> `src/analysis/similarity.py`; mixed-effects model in `src/analysis/stats.py`. + +- Output equivalence vs. nearest-exemplar similarity, with regression fit: + **Figure 6** `[[PLACEHOLDER]]`. +- Mixed-effects coefficients (mode, similarity) with CIs: **Table 7** + `[[PLACEHOLDER]]`. +- Interpretation: the agent generalizes up to similarity `[[X]]`, beyond which + output equivalence degrades to `[[Y]]`. `[[PLACEHOLDER narrative]]`. + +### 2.7 Stochasticity + +- Run-to-run variance per dataset/mode and pass@k: **Figure 7** `[[PLACEHOLDER]]`. + +## 3. Metric validation + +> `experiments/metric_validation.py`; `src/analysis/irr.py`. + +Agreement between automated output-equivalence scoring and an expert rubric on the +gold data: correlation `[[PLACEHOLDER]]`, inter-rater reliability +`[[PLACEHOLDER]]`. This determines whether automated scoring can stand in for +expert judgment in Phase B. **Figure 8** `[[PLACEHOLDER]]`. + +## 4. Phase B — Prospective blind evaluation + +> `experiments/phase_b_prospective.py`. To be run once novel datasets and the +> expert's blind harmonizations are available. + +- Number and provenance of prospective datasets: `[[PLACEHOLDER]]`. +- Agent vs. expert output equivalence on novel data: **Table 8** `[[PLACEHOLDER]]`. +- Comparison of Phase B performance to the Phase A end-to-end estimate (does + retrospective CV predict prospective performance?): `[[PLACEHOLDER narrative]]`. +- Curator decision agreement with the expert on novel inclusion/exclusion calls: + `[[PLACEHOLDER]]`. + +## 5. Summary of findings + +> One-paragraph synthesis to be written once the tables above are populated. +> Anticipated structure: headline output-equivalence number end-to-end; the size of +> the curator-induced error-propagation gap; the dominant failure category; the +> value added by agentic structure and by exemplars (vs. baselines); and the +> similarity threshold beyond which the system should defer to a human. diff --git a/docs/manuscript/05_discussion.md b/docs/manuscript/05_discussion.md new file mode 100644 index 0000000..f550363 --- /dev/null +++ b/docs/manuscript/05_discussion.md @@ -0,0 +1,112 @@ +# Discussion + +> **Status:** Structural scaffold. Argument skeleton and limitations are derivable +> from the design now; quantitative claims (`[[PLACEHOLDER]]`) are filled at +> results-time. Cross-references to related work (`§` in `02_background.md`) are +> marked `[[cite]]` and resolved against `references.md`. + +## 1. Principal findings + +> One or two paragraphs once results exist. Anticipated claims, each contingent on +> the data: + +- The agent pair reproduces expert harmonization with end-to-end output equivalence + of `[[PLACEHOLDER]]`, with `[[PLACEHOLDER]]`% of datasets meeting the strict + cell-agreement threshold. +- Given correct curator input, the harmonizer alone reaches `[[PLACEHOLDER]]`; the + error-propagation gap of `[[PLACEHOLDER]]` shows that `[[most / little]]` of the + end-to-end shortfall originates upstream in curation. +- The dominant failure mode is `[[category]]`, concentrated in + `[[structural type / unit / location resolution]]`. + +## 2. Why decompose the workflow + +Scoring each skill in isolation, the harmonizer under an oracle bundle, and the full +pipeline lets us attribute error rather than report an opaque success rate. This +matters for deployment: a system that fails mostly through *curator* errors (file +selection, inclusion decisions) calls for a different intervention — a human gate at +curation — than one that fails through *harmonizer* errors (unit conversions, +timezone handling). The error-propagation taxonomy operationalizes that distinction +and connects to broader work on chained/multi-agent LLM pipelines `[[cite]]`, where +compounding error across stages is a known failure mode. + +## 3. Output equivalence as the right endpoint + +Comparing executed outputs rather than code (or rather than an LLM-judge score) +sidesteps two problems: stylistic code variation (two correct programs differ +arbitrarily) and the reliability concerns of LLM-as-judge evaluation `[[cite]]`. It +also gives a domain-meaningful unit — the fraction of harmonized cells a downstream +scientist could trust. The cost is that output equivalence requires both code +artifacts to *execute* on shared raw data, which is why code executability is scored +separately and why sandboxed execution is part of the harness. + +## 4. Generalization and the exemplar question + +In-context exemplars are the mechanism by which the agent learns the target schema, +yet they also create a leakage risk under cross-validation: a held-out dataset with +a near-duplicate sibling in the exemplar pool would overstate performance. Grouped +cross-validation (holding out whole source/instrument clusters) and the explicit +similarity covariate convert this risk into a measured relationship. The +similarity–performance curve `[[PLACEHOLDER]]` indicates the boundary of reliable +generalization and, practically, where the system should defer to a human. The +no-exemplars baseline `[[PLACEHOLDER]]` separately quantifies how much the reference +corpus contributes at all. + +## 5. Relation to prior work + +> Resolve against `02_background.md` once research citations are integrated. + +- **Vs. classic data-wrangling / ETL synthesis** (`[[cite]]`): how the agentic, + schema-targeted, exemplar-driven approach differs from program-synthesis and + rule-based wrangling tools. +- **Vs. LLM-based schema/entity matching and data cleaning** (`[[cite]]`): we + evaluate end-to-end *transformation-code generation against executed expert + ground truth*, not a matching/labeling decision in isolation. +- **Vs. environmental-science harmonization practice** (`[[cite]]`): contrast with + manual, script-per-dataset harmonization and with standardized reporting formats / + ontologies; position the agent pair as an accelerator that still produces + human-auditable code and documented mappings. + +## 6. Limitations + +- **Small gold corpus.** With ≈19 expert-harmonized datasets, estimates are + uncertain; we report effect sizes with cluster-bootstrap CIs and avoid + over-reliance on *p*-values, but power is inherently limited. +- **Single domain, single repository.** The corpus is WFSFA soil moisture on + ESS-DIVE. Generalization to other variables, repositories, or Earth-science + domains is untested and is an explicit direction for future work. +- **Expert as gold standard.** The reference is one expert's harmonization; some + decisions are genuinely ambiguous (captured by the taxonomy's fourth category and + by expert-confidence labels), and a second independent expert would strengthen the + ground truth. +- **Model and skill drift.** Results are pinned to a specific model version and + skill versions; LLM and skill updates may change performance, so we report exact + versions and seeds for reproducibility. +- **Stochasticity.** LLM outputs vary run-to-run; we report variance and pass@k, but + any single deployed run may differ from the reported mean. +- **Automated-metric validity.** Phase B leans on automated scoring; its credibility + rests on the Phase A metric-validation agreement `[[PLACEHOLDER]]`, which bounds + how far the automated numbers can be trusted. +- **Prospective scope.** Phase B covers `[[N]]` novel datasets; broader prospective + deployment is needed to confirm the retrospective estimate. + +## 7. Implications and future work + +- **Human-in-the-loop deployment.** The natural operating point is agent-drafts / + expert-reviews, with automatic deferral above the similarity threshold and on + low-confidence curator decisions (the FLAG path). Report the implied human-effort + savings `[[PLACEHOLDER]]`. +- **Beyond soil moisture.** Extend to other ESS-DIVE reporting formats and to other + environmental variables; test whether the curator/harmonizer split transfers. +- **Richer provenance.** The change-mapping JSON is a step toward machine-readable, + FAIR-aligned transformation provenance `[[cite]]`; future work could emit + standardized provenance directly. +- **Active exemplar selection.** Given the similarity findings, selecting or + synthesizing exemplars to cover structural gaps may extend the generalization + frontier. + +## 8. Conclusion + +> Short — typically merged into `06_conclusion.md`. Restate the headline +> output-equivalence result, the decomposed error attribution, and the practical +> deployment recommendation, all contingent on the final numbers. diff --git a/docs/manuscript/06_conclusion.md b/docs/manuscript/06_conclusion.md new file mode 100644 index 0000000..1711833 --- /dev/null +++ b/docs/manuscript/06_conclusion.md @@ -0,0 +1,19 @@ +# Conclusion + +> **Status: SCAFFOLD.** Write last, ~1 paragraph. Restate the contribution and the +> headline result without hedging once numbers exist. + +We presented a rigorous evaluation of a two-agent LLM workflow — a curator and a +harmonizer — for automating the harmonization of heterogeneous environmental +(soil-moisture) datasets into an analysis-ready schema, benchmarked against expert +ground truth via retrospective grouped cross-validation and prospective blind +evaluation. By scoring output-data equivalence (executed code, compared cell-by-cell +against the expert's), decomposing performance across the two agents, and attributing +end-to-end failures with an explicit taxonomy, the study `[[PLACEHOLDER: states what +was learned — the achievable accuracy, where error originates, and the operating +point at which the system is deployable with human oversight]]`. + +`[[PLACEHOLDER closing sentence on broader significance: a path toward scalable, +auditable, FAIR-aligned curation of long-tail environmental data, with the agent +producing human-reviewable transformation code and documented mappings rather than +opaque outputs.]]` diff --git a/docs/manuscript/README.md b/docs/manuscript/README.md new file mode 100644 index 0000000..d89a69a --- /dev/null +++ b/docs/manuscript/README.md @@ -0,0 +1,71 @@ +# Manuscript + +Working drafts for the paper describing this study: +**Evaluating an LLM Agent Pair for Environmental Data Harmonization.** + +> **Status: pre-results scaffold (2026-06).** The methods and background are +> drafted; results, discussion, and the abstract contain `[[PLACEHOLDER]]` markers +> to be filled once the experimental pipeline (`experiments/`) has produced scored +> outputs under `results/`. Nothing here reports a number that the analysis code has +> not yet generated. + +## Files + +| File | Section | Status | +|---|---|---| +| `00_abstract.md` | Abstract | Scaffold — write last | +| `01_introduction.md` | Introduction & motivation | Drafted (cited) | +| `02_background.md` | Background / related work | Drafted (cited) | +| `03_methods.md` | Methods | Drafted from locked design | +| `04_results.md` | Results | Scaffold — tables/figures stubbed | +| `05_discussion.md` | Discussion | Scaffold — argument skeleton | +| `06_conclusion.md` | Conclusion | Scaffold | +| `figures_and_tables.md` | Display-item manifest | Mapped to analysis code | +| `references.md` | Bibliography | Drafted (research-backed) | +| `research_notes.md` | Raw literature research (provenance/verification) | Reference material | +| `figures/` | Figure 1 schematic (Mermaid source + HTML render) | Drafted | +| `slides/index.html` | HTML slide deck (reveal.js) | Drafted | + +A landing page (`index.html`) links the deck, figure, and sections; it is the +homepage of the published site (see **Hosting** below). + +Read in numeric order for a continuous draft. The intended journal/venue is TBD +(candidates: *Scientific Data*, *Environmental Modelling & Software*, *PVLDB / +SIGMOD data-systems venues*, or an agentic-AI venue — decide once results frame the +strongest contribution). + +## How this maps to the codebase + +- **Methods** are derived from `config/` (experiment, folds, metric weights), + `src/schemas/` (target schema, curator/harmonizer bundles), `src/metrics/`, + `src/analysis/`, and `skills/` (the two agents under evaluation). +- **Results** display items each name the `experiments/…` or `src/…` code that + generates them (see `figures_and_tables.md`). +- **Ground truth** is `data/gold/` (19 expert-harmonized datasets: code + mappings). + +## Filling in results + +1. Run `experiments/phase_a_crossval.py` → `results/scored/phase_a_results.csv`. +2. Run `experiments/metric_validation.py` (validates automated metrics vs. expert). +3. Run `experiments/phase_b_prospective.py` once novel + expert-blind data exist. +4. Generate figures/tables from `notebooks/` and `src/analysis/`. +5. Replace every `[[PLACEHOLDER]]` in `04_results.md`, then write `00_abstract.md`, + the discussion's quantitative claims, and `06_conclusion.md`. +6. Reconcile the reporting checklist at the end of `03_methods.md` (model version, + seeds, counts). + +## Hosting + +`.github/workflows/pages.yml` publishes this directory to GitHub Pages (it +auto-enables Pages and serves `index.html` as the homepage). reveal.js and +Mermaid are vendored under `vendor/`, so the deck and figure render with **no +external network** — both as a hosted site and when opened from disk. To preview +locally without hosting, open `index.html` (or `slides/index.html`) in a browser. + +## Citation policy + +`references.md` separates sources the research pass **verified by fetching** from +those captured **from search snippets only**; the latter are flagged and must be +confirmed before submission. Author-year keys in the prose (e.g. `[Dorigo-2021]`) +resolve to entries in `references.md`. Do not introduce a citation that is not in +`references.md`. diff --git a/docs/manuscript/figures/figure1_workflow.html b/docs/manuscript/figures/figure1_workflow.html new file mode 100644 index 0000000..c497adc --- /dev/null +++ b/docs/manuscript/figures/figure1_workflow.html @@ -0,0 +1,94 @@ + + +
+ + +A curator (Skill 1) turns a dataset identifier into a structured bundle; a harmonizer + (Skill 2) turns that bundle into executable transform code and a documented change-mapping. The + agent's code and the expert's code are each executed on the same raw files and the resulting + tables are compared cell-by-cell (primary endpoint). Three evaluation modes localize error, and + the Mode 2 − Mode 3 gap quantifies how much curator error propagates downstream.
+
+%%{init: {"theme":"base","themeVariables":{
+ "fontFamily":"ui-sans-serif, system-ui, Inter, Segoe UI, Roboto, sans-serif",
+ "fontSize":"14px","lineColor":"#8a8a8a","primaryBorderColor":"#c9bfa6",
+ "clusterBkg":"#ffffff","clusterBorder":"#e0d8c4"}}}%%
+flowchart TB
+ classDef agent fill:#0e7c7b,stroke:#0a5a59,color:#ffffff,stroke-width:1.5px;
+ classDef artifact fill:#f3efe6,stroke:#c9bfa6,color:#2b2b2b;
+ classDef gold fill:#c9962f,stroke:#9c7421,color:#ffffff;
+ classDef score fill:#3b5b8c,stroke:#28406a,color:#ffffff;
+ classDef io fill:#ffffff,stroke:#8a8a8a,color:#2b2b2b,stroke-dasharray:3 3;
+
+ ID["ESS-DIVE dataset identifier
(DOI / package ID)"]:::io
+ EX["19 expert exemplars
(in-context references)"]:::gold
+
+ subgraph PIPE["Agent pair under evaluation"]
+ direction TB
+ C["Curator — Skill 1
retrieve · classify files · resolve location
infer time series · detect manipulation · pick exemplar"]:::agent
+ B["Curator bundle
decision · payload/location files · coords · interval · qc_flag"]:::artifact
+ H["Harmonizer — Skill 2
map variables · convert units · generate code + change-mapping"]:::agent
+ OUT["Python transform code
+ change-mapping JSON"]:::artifact
+ C --> B --> H --> OUT
+ end
+
+ RAW[("Raw package files")]:::io
+ GC["Expert transform code"]:::gold
+ AGT["Agent harmonized table"]:::artifact
+ GLD["Expert harmonized table
(gold standard)"]:::gold
+ CMP{{"PRIMARY ENDPOINT
output-data equivalence
cell-by-cell on the natural key"}}:::score
+
+ ID --> C
+ EX -. exemplars .-> C
+ EX -. exemplars .-> H
+ OUT -- execute --> AGT
+ RAW --> AGT
+ RAW --> GLD
+ GC --> GLD
+ AGT --> CMP
+ GLD --> CMP
+
+ subgraph MODES["Evaluation modes (error attribution)"]
+ direction TB
+ M1["Mode 1 · Skill-1 isolated — bundle vs expert labels"]:::score
+ M2["Mode 2 · Skill-2 oracle — gold bundle into harmonizer"]:::score
+ M3["Mode 3 · end-to-end — full pipeline, errors propagate"]:::score
+ GAP["Error-propagation gap = Mode 2 − Mode 3"]:::score
+ M2 --- GAP
+ M3 --- GAP
+ end
+
+ B -. scored .-> M1
+ OUT -. scored .-> M3
+
+ Watershed Function SFA · Berkeley Lab
+A curator + harmonizer + agent pair that turns heterogeneous DOE ESS-DIVE soil-moisture datasets into an analysis-ready + schema, benchmarked against a domain expert with leave-one-out cross-validation and blind prospective + evaluation.
+ Pre-results scaffold — manuscript & framework +15-slide overview: motivation, the agent pair, design, metrics, positioning, and results placeholders. Figure 1 rendered live.
+ + + Figure 1 +The agent-pair pipeline, three evaluation modes, and the cell-by-cell output-equivalence endpoint. Also as .svg/.png.
Section-by-section status and how the drafts map onto the codebase.
+ +Markdown files open as source on a plain static host. For rendered prose, read them on the + repository page; the deck and figure are full HTML and render anywhere.
+b&&(b=h),w=v*v*C,T=Math.max(b/w,w/x),T>k){v-=h;break}k=T}s.push(u={value:v,dice:m ba))l.moveTo(0,0);else if(v>Sy-ba)l.moveTo(m*Wd(g),m*Jl(g)),l.arc(0,0,m,g,y,!x),p>ba&&(l.moveTo(p*Wd(y),p*Jl(y)),l.arc(0,0,p,y,g,x));else{var b=g,T=y,k=g,C=y,w=v,S=v,R=s.apply(this,arguments)/2,L=R>ba&&(n?+n.apply(this,arguments):hm(p*p+m*m)),N=wE(DN(m-p)/2,+r.apply(this,arguments)),I=N,_=N,A,M;if(L>ba){var D=IN(L/p*Jl(R)),P=IN(L/m*Jl(R));(w-=D*2)>ba?(D*=x?1:-1,k+=D,C-=D):(w=0,k=C=(g+y)/2),(S-=P*2)>ba?(P*=x?1:-1,b+=P,T-=P):(S=0,b=T=(g+y)/2)}var B=m*Wd(b),O=m*Jl(b),$=p*Wd(C),V=p*Jl(C);if(N>ba){var G=m*Wd(T),z=m*Jl(T),W=p*Wd(k),H=p*Jl(k),j;if(v "),e.result!==null&&g.kind!==e.kind&&dr(e,"unacceptable node kind for !<"+e.tag+'> tag; it should be "'+g.kind+'", not "'+e.kind+'"'),g.resolve(e.result,e.tag)?(e.result=g.construct(e.result,e.tag),e.anchor!==null&&(e.anchorMap[e.anchor]=e.result)):dr(e,"cannot resolve a node with !<"+e.tag+"> explicit tag")}return e.listener!==null&&e.listener("close",e),e.tag!==null||e.anchor!==null||d}function bKe(e){var t=e.position,r,n,i,a=!1,s;for(e.version=null,e.checkLineBreaks=e.legacy,e.tagMap=Object.create(null),e.anchorMap=Object.create(null);(s=e.input.charCodeAt(e.position))!==0&&(Ui(e,!0,-1),s=e.input.charCodeAt(e.position),!(e.lineIndent>0||s!==37));){for(a=!0,s=e.input.charCodeAt(++e.position),r=e.position;s!==0&&!ro(s);)s=e.input.charCodeAt(++e.position);for(n=e.input.slice(r,e.position),i=[],n.length<1&&dr(e,"directive name must not be less than one character in length");s!==0;){for(;xm(s);)s=e.input.charCodeAt(++e.position);if(s===35){do s=e.input.charCodeAt(++e.position);while(s!==0&&!tu(s));break}if(tu(s))break;for(r=e.position;s!==0&&!ro(s);)s=e.input.charCodeAt(++e.position);i.push(e.input.slice(r,e.position))}s!==0&&EP(e),Kd.call(Cae,n)?Cae[n](e,n,i):m4(e,'unknown document directive "'+n+'"')}if(Ui(e,!0,-1),e.lineIndent===0&&e.input.charCodeAt(e.position)===45&&e.input.charCodeAt(e.position+1)===45&&e.input.charCodeAt(e.position+2)===45?(e.position+=3,Ui(e,!0,-1)):a&&dr(e,"directives end mark is expected"),$y(e,e.lineIndent-1,p4,!1,!0),Ui(e,!0,-1),e.checkLineBreaks&&iKe.test(e.input.slice(t,e.position))&&m4(e,"non-ASCII line breaks are interpreted as content"),e.documents.push(e.result),e.position===e.lineStart&&v4(e)){e.input.charCodeAt(e.position)===46&&(e.position+=3,Ui(e,!0,-1));return}if(e.position ${this.parser.parseInline(e)} An error occurred: ${e.replace(/\\n|\n/g," ${i.tokens?.map(n).join("")}0;){if(h=mb(s,l,r),h===u)return n[i]=s,n[a]=l,t(n);if(h>0)s=Math.floor(s/h)*h,l=Math.ceil(l/h)*h;else if(h<0)s=Math.ceil(s*h)/h,l=Math.floor(l*h)/h;else break;u=h}return e},e}function Kl(){var e=Gb();return e.copy=function(){return hE(e,Kl())},zd.apply(e,arguments),pUe(e)}var Kre=F(()=>{"use strict";Md();cN();zb();Xre();o(pUe,"linearish");o(Kl,"linear")});function hN(e,t){e=e.slice();var r=0,n=e.length-1,i=e[r],a=e[n],s;return a{"use strict";o(hN,"nice")});function qn(e,t,r,n){function i(a){return e(a=arguments.length===0?new Date:new Date(+a)),a}return o(i,"interval"),i.floor=a=>(e(a=new Date(+a)),a),i.ceil=a=>(e(a=new Date(a-1)),t(a,1),e(a),a),i.round=a=>{let s=i(a),l=i.ceil(a);return a-s0))return u;let h;do u.push(h=new Date(+a)),t(a,l),e(a);while(hqn(s=>{if(s>=s)for(;e(s),!a(s);)s.setTime(s-1)},(s,l)=>{if(s>=s)if(l<0)for(;++l<=0;)for(;t(s,-1),!a(s););else for(;--l>=0;)for(;t(s,1),!a(s););}),r&&(i.count=(a,s)=>(dN.setTime(+a),fN.setTime(+s),e(dN),e(fN),Math.floor(r(dN,fN))),i.every=a=>(a=Math.floor(a),!isFinite(a)||!(a>0)?null:a>1?i.filter(n?s=>n(s)%a===0:s=>i.count(0,s)%a===0):i)),i}var dN,fN,fh=F(()=>{"use strict";dN=new Date,fN=new Date;o(qn,"timeInterval")});var Kc,Qre,pN=F(()=>{"use strict";fh();Kc=qn(()=>{},(e,t)=>{e.setTime(+e+t)},(e,t)=>t-e);Kc.every=e=>(e=Math.floor(e),!isFinite(e)||!(e>0)?null:e>1?qn(t=>{t.setTime(Math.floor(t/e)*e)},(t,r)=>{t.setTime(+t+r*e)},(t,r)=>(r-t)/e):Kc);Qre=Kc.range});var Bo,Jre,mN=F(()=>{"use strict";fh();Bo=qn(e=>{e.setTime(e-e.getMilliseconds())},(e,t)=>{e.setTime(+e+t*1e3)},(e,t)=>(t-e)/1e3,e=>e.getUTCSeconds()),Jre=Bo.range});var ph,mUe,dE,gUe,gN=F(()=>{"use strict";fh();ph=qn(e=>{e.setTime(e-e.getMilliseconds()-e.getSeconds()*1e3)},(e,t)=>{e.setTime(+e+t*6e4)},(e,t)=>(t-e)/6e4,e=>e.getMinutes()),mUe=ph.range,dE=qn(e=>{e.setUTCSeconds(0,0)},(e,t)=>{e.setTime(+e+t*6e4)},(e,t)=>(t-e)/6e4,e=>e.getUTCMinutes()),gUe=dE.range});var mh,yUe,fE,vUe,yN=F(()=>{"use strict";fh();mh=qn(e=>{e.setTime(e-e.getMilliseconds()-e.getSeconds()*1e3-e.getMinutes()*6e4)},(e,t)=>{e.setTime(+e+t*36e5)},(e,t)=>(t-e)/36e5,e=>e.getHours()),yUe=mh.range,fE=qn(e=>{e.setUTCMinutes(0,0,0)},(e,t)=>{e.setTime(+e+t*36e5)},(e,t)=>(t-e)/36e5,e=>e.getUTCHours()),vUe=fE.range});var hl,xUe,Wb,bUe,pE,TUe,vN=F(()=>{"use strict";fh();hl=qn(e=>e.setHours(0,0,0,0),(e,t)=>e.setDate(e.getDate()+t),(e,t)=>(t-e-(t.getTimezoneOffset()-e.getTimezoneOffset())*6e4)/864e5,e=>e.getDate()-1),xUe=hl.range,Wb=qn(e=>{e.setUTCHours(0,0,0,0)},(e,t)=>{e.setUTCDate(e.getUTCDate()+t)},(e,t)=>(t-e)/864e5,e=>e.getUTCDate()-1),bUe=Wb.range,pE=qn(e=>{e.setUTCHours(0,0,0,0)},(e,t)=>{e.setUTCDate(e.getUTCDate()+t)},(e,t)=>(t-e)/864e5,e=>Math.floor(e/864e5)),TUe=pE.range});function om(e){return qn(t=>{t.setDate(t.getDate()-(t.getDay()+7-e)%7),t.setHours(0,0,0,0)},(t,r)=>{t.setDate(t.getDate()+r*7)},(t,r)=>(r-t-(r.getTimezoneOffset()-t.getTimezoneOffset())*6e4)/6048e5)}function lm(e){return qn(t=>{t.setUTCDate(t.getUTCDate()-(t.getUTCDay()+7-e)%7),t.setUTCHours(0,0,0,0)},(t,r)=>{t.setUTCDate(t.getUTCDate()+r*7)},(t,r)=>(r-t)/6048e5)}var Zl,Gd,mE,gE,Qc,yE,vE,tne,CUe,wUe,kUe,SUe,EUe,AUe,cm,Cy,rne,nne,Vd,ine,ane,sne,RUe,_Ue,LUe,DUe,IUe,MUe,xN=F(()=>{"use strict";fh();o(om,"timeWeekday");Zl=om(0),Gd=om(1),mE=om(2),gE=om(3),Qc=om(4),yE=om(5),vE=om(6),tne=Zl.range,CUe=Gd.range,wUe=mE.range,kUe=gE.range,SUe=Qc.range,EUe=yE.range,AUe=vE.range;o(lm,"utcWeekday");cm=lm(0),Cy=lm(1),rne=lm(2),nne=lm(3),Vd=lm(4),ine=lm(5),ane=lm(6),sne=cm.range,RUe=Cy.range,_Ue=rne.range,LUe=nne.range,DUe=Vd.range,IUe=ine.range,MUe=ane.range});var gh,NUe,xE,PUe,bN=F(()=>{"use strict";fh();gh=qn(e=>{e.setDate(1),e.setHours(0,0,0,0)},(e,t)=>{e.setMonth(e.getMonth()+t)},(e,t)=>t.getMonth()-e.getMonth()+(t.getFullYear()-e.getFullYear())*12,e=>e.getMonth()),NUe=gh.range,xE=qn(e=>{e.setUTCDate(1),e.setUTCHours(0,0,0,0)},(e,t)=>{e.setUTCMonth(e.getUTCMonth()+t)},(e,t)=>t.getUTCMonth()-e.getUTCMonth()+(t.getUTCFullYear()-e.getUTCFullYear())*12,e=>e.getUTCMonth()),PUe=xE.range});var $o,OUe,Ql,BUe,TN=F(()=>{"use strict";fh();$o=qn(e=>{e.setMonth(0,1),e.setHours(0,0,0,0)},(e,t)=>{e.setFullYear(e.getFullYear()+t)},(e,t)=>t.getFullYear()-e.getFullYear(),e=>e.getFullYear());$o.every=e=>!isFinite(e=Math.floor(e))||!(e>0)?null:qn(t=>{t.setFullYear(Math.floor(t.getFullYear()/e)*e),t.setMonth(0,1),t.setHours(0,0,0,0)},(t,r)=>{t.setFullYear(t.getFullYear()+r*e)});OUe=$o.range,Ql=qn(e=>{e.setUTCMonth(0,1),e.setUTCHours(0,0,0,0)},(e,t)=>{e.setUTCFullYear(e.getUTCFullYear()+t)},(e,t)=>t.getUTCFullYear()-e.getUTCFullYear(),e=>e.getUTCFullYear());Ql.every=e=>!isFinite(e=Math.floor(e))||!(e>0)?null:qn(t=>{t.setUTCFullYear(Math.floor(t.getUTCFullYear()/e)*e),t.setUTCMonth(0,1),t.setUTCHours(0,0,0,0)},(t,r)=>{t.setUTCFullYear(t.getUTCFullYear()+r*e)});BUe=Ql.range});function lne(e,t,r,n,i,a){let s=[[Bo,1,1e3],[Bo,5,5*1e3],[Bo,15,15*1e3],[Bo,30,30*1e3],[a,1,6e4],[a,5,5*6e4],[a,15,15*6e4],[a,30,30*6e4],[i,1,36e5],[i,3,3*36e5],[i,6,6*36e5],[i,12,12*36e5],[n,1,864e5],[n,2,2*864e5],[r,1,6048e5],[t,1,2592e6],[t,3,3*2592e6],[e,1,31536e6]];function l(h,d,f){let p=d{"use strict";Md();pN();mN();gN();yN();vN();xN();bN();TN();o(lne,"ticker");[FUe,zUe]=lne(Ql,xE,cm,pE,fE,dE),[CN,wN]=lne($o,gh,Zl,hl,mh,ph)});var bE=F(()=>{"use strict";pN();mN();gN();yN();vN();xN();bN();TN();cne()});function kN(e){if(0<=e.y&&e.y<100){var t=new Date(-1,e.m,e.d,e.H,e.M,e.S,e.L);return t.setFullYear(e.y),t}return new Date(e.y,e.m,e.d,e.H,e.M,e.S,e.L)}function SN(e){if(0<=e.y&&e.y<100){var t=new Date(Date.UTC(-1,e.m,e.d,e.H,e.M,e.S,e.L));return t.setUTCFullYear(e.y),t}return new Date(Date.UTC(e.y,e.m,e.d,e.H,e.M,e.S,e.L))}function qb(e,t,r){return{y:e,m:t,d:r,H:0,M:0,S:0,L:0}}function EN(e){var t=e.dateTime,r=e.date,n=e.time,i=e.periods,a=e.days,s=e.shortDays,l=e.months,u=e.shortMonths,h=Hb(i),d=Ub(i),f=Hb(a),p=Ub(a),m=Hb(s),g=Ub(s),y=Hb(l),v=Ub(l),x=Hb(u),b=Ub(u),T={a:B,A:O,b:$,B:V,c:null,d:mne,e:mne,f:cYe,g:xYe,G:TYe,H:sYe,I:oYe,j:lYe,L:bne,m:uYe,M:hYe,p:G,q:z,Q:vne,s:xne,S:dYe,u:fYe,U:pYe,V:mYe,w:gYe,W:yYe,x:null,X:null,y:vYe,Y:bYe,Z:CYe,"%":yne},k={a:W,A:H,b:j,B:Q,c:null,d:gne,e:gne,f:EYe,g:OYe,G:$Ye,H:wYe,I:kYe,j:SYe,L:Cne,m:AYe,M:RYe,p:U,q:oe,Q:vne,s:xne,S:_Ye,u:LYe,U:DYe,V:IYe,w:MYe,W:NYe,x:null,X:null,y:PYe,Y:BYe,Z:FYe,"%":yne},C={a:N,A:I,b:_,B:A,c:M,d:fne,e:fne,f:rYe,g:dne,G:hne,H:pne,I:pne,j:QUe,L:tYe,m:ZUe,M:JUe,p:L,q:KUe,Q:iYe,s:aYe,S:eYe,u:HUe,U:UUe,V:YUe,w:qUe,W:jUe,x:D,X:P,y:dne,Y:hne,Z:XUe,"%":nYe};T.x=w(r,T),T.X=w(n,T),T.c=w(t,T),k.x=w(r,k),k.X=w(n,k),k.c=w(t,k);function w(te,le){return function(ie){var ae=[],Re=-1,be=0,Pe=te.length,Ge,Oe,ue;for(ie instanceof Date||(ie=new Date(+ie));++Re
"},r),xt.lineBreakRegex.test(e)))return e;let n=e.split(" ").filter(Boolean),i=[],a="";return n.forEach((s,l)=>{let u=Ca(`${s} `,r),h=Ca(a,r);if(u>t){let{hyphenatedStrings:p,remainingWord:m}=Aje(s,t,"-",r);i.push(a,...p),a=m}else h+u>=t?(i.push(a),a=s):a=[a,s].filter(Boolean).join(" ");l+1===n.length&&i.push(a)}),i.filter(s=>s!=="").join(r.joinWith)},(e,t,r)=>`${e}${t}${r.fontSize}${r.fontWeight}${r.fontFamily}${r.joinWith}`),Aje=gm((e,t,r="-",n)=>{n=Object.assign({fontSize:12,fontWeight:400,fontFamily:"Arial",margin:0},n);let i=[...e],a=[],s="";return i.forEach((l,u)=>{let h=`${s}${l}`;if(Ca(h,n)>=t){let f=u+1,p=i.length===f,m=`${h}${r}`;a.push(p?h:m),s=""}else s=h}),{hyphenatedStrings:a,remainingWord:s}},(e,t,r="-",n)=>`${e}${t}${r}${n.fontSize}${n.fontWeight}${n.fontFamily}`);o(l4,"calculateTextHeight");o(Ca,"calculateTextWidth");b2=gm((e,t)=>{let{fontSize:r=12,fontFamily:n="Arial",fontWeight:i=400}=t;if(!e)return{width:0,height:0};let[,a]=As(r),s=["sans-serif",n],l=e.split(xt.lineBreakRegex),u=[],h=et("body");if(!h.remove)return{width:0,height:0,lineHeight:0};let d=h.append("svg");for(let p of s){let m=0,g={width:0,height:0,lineHeight:0};for(let y of l){let v=Sje();v.text=y||oP;let x=Eje(d,v).style("font-size",a).style("font-weight",i).style("font-family",p),b=(x._groups||x)[0][0].getBBox();if(b.width===0&&b.height===0)throw new Error("svg element not in render tree");g.width=Math.round(Math.max(g.width,b.width)),m=Math.round(b.height),g.height+=m,g.lineHeight=Math.round(Math.max(g.lineHeight,m))}u.push(g)}d.remove();let f=isNaN(u[1].height)||isNaN(u[1].width)||isNaN(u[1].lineHeight)||u[0].height>u[1].height&&u[0].width>u[1].width&&u[0].lineHeight>u[1].lineHeight?0:1;return u[f]},(e,t)=>`${e}${t.fontSize}${t.fontWeight}${t.fontFamily}`),sP=class{constructor(t=!1,r){this.count=0;this.count=r?r.length:0,this.next=t?()=>this.count++:()=>Date.now()}static{o(this,"InitIDGenerator")}},Rje=o(function(e){return o4=o4||document.createElement("div"),e=escape(e).replace(/%26/g,"&").replace(/%23/g,"#").replace(/%3B/g,";"),o4.innerHTML=e,unescape(o4.textContent)},"entityDecode");o(fP,"isDetailedError");_je=o((e,t,r,n)=>{if(!n)return;let i=e.node()?.getBBox();i&&e.append("text").text(n).attr("text-anchor","middle").attr("x",i.x+i.width/2).attr("y",-r).attr("class",t)},"insertTitle"),As=o(e=>{if(typeof e=="number")return[e,e+"px"];let t=parseInt(e??"",10);return Number.isNaN(t)?[void 0,void 0]:e===String(t)?[t,e+"px"]:[t,e]},"parseFontSize");o(qr,"cleanAndMerge");Zt={assignWithDepth:Yn,wrapLabel:ym,calculateTextHeight:l4,calculateTextWidth:Ca,calculateTextDimensions:b2,cleanAndMerge:qr,detectInit:gje,detectDirective:nae,isSubstringInArray:yje,interpolateToCurve:lP,calcLabelPosition:Tje,calcCardinalityPosition:Cje,calcTerminalLabelPosition:wje,formatUrl:vje,getStylesFromArray:uP,generateId:hP,random:dP,runFunc:xje,entityDecode:Rje,insertTitle:_je,isLabelCoordinateInPath:Lje,parseFontSize:As,InitIDGenerator:sP},sae=o(function(e){let t=e;return t=t.replace(/style.*:\S*#.*;/g,function(r){return r.substring(0,r.length-1)}),t=t.replace(/classDef.*:\S*#.*;/g,function(r){return r.substring(0,r.length-1)}),t=t.replace(/#\w+;/g,function(r){let n=r.substring(1,r.length-1);return/^\+?\d+$/.test(n)?"\uFB02\xB0\xB0"+n+"\xB6\xDF":"\uFB02\xB0"+n+"\xB6\xDF"}),t},"encodeEntities"),Rs=o(function(e){return e.replace(/fl°°/g,"").replace(/fl°/g,"&").replace(/¶ß/g,";")},"decodeEntities"),eu=o((e,t,{counter:r=0,prefix:n,suffix:i},a)=>a||`${n?`${n}_`:""}${e}_${t}_${r}${i?`_${i}`:""}`,"getEdgeId");o(kn,"handleUndefinedAttr");o(Lje,"isLabelCoordinateInPath")});function ic(e,t,r,n,i){if(!t[e].width)if(r)t[e].text=ym(t[e].text,i,n),t[e].textLines=t[e].text.split(xt.lineBreakRegex).length,t[e].width=i,t[e].height=l4(t[e].text,n);else{let a=t[e].text.split(xt.lineBreakRegex);t[e].textLines=a.length;let s=0;t[e].height=0,t[e].width=0;for(let l of a)t[e].width=Math.max(Ca(l,n),t[e].width),s=l4(l,n),t[e].height=t[e].height+s}}function hae(e,t,r,n,i){let a=new d4(i);a.data.widthLimit=r.data.widthLimit/Math.min(pP,n.length);for(let[s,l]of n.entries()){let u=0;l.image={width:0,height:0,Y:0},l.sprite&&(l.image.width=48,l.image.height=48,l.image.Y=u,u=l.image.Y+l.image.height);let h=l.wrap&&sr.wrap,d=c4(sr);if(d.fontSize=d.fontSize+2,d.fontWeight="bold",ic("label",l,h,d,a.data.widthLimit),l.label.Y=u+8,u=l.label.Y+l.label.height,l.type&&l.type.text!==""){l.type.text="["+l.type.text+"]";let g=c4(sr);ic("type",l,h,g,a.data.widthLimit),l.type.Y=u+5,u=l.type.Y+l.type.height}if(l.descr&&l.descr.text!==""){let g=c4(sr);g.fontSize=g.fontSize-2,ic("descr",l,h,g,a.data.widthLimit),l.descr.Y=u+20,u=l.descr.Y+l.descr.height}if(s==0||s%pP===0){let g=r.data.startx+sr.diagramMarginX,y=r.data.stopy+sr.diagramMarginY+u;a.setData(g,g,y,y)}else{let g=a.data.stopx!==a.data.startx?a.data.stopx+sr.diagramMarginX:a.data.startx,y=a.data.starty;a.setData(g,g,y,y)}a.name=l.alias;let f=i.db.getC4ShapeArray(l.alias),p=i.db.getC4ShapeKeys(l.alias);p.length>0&&uae(a,e,f,p),t=l.alias;let m=i.db.getBoundaries(t);m.length>0&&hae(e,t,a,m,i),l.alias!=="global"&&cae(e,l,a),r.data.stopy=Math.max(a.data.stopy+sr.c4ShapeMargin,r.data.stopy),r.data.stopx=Math.max(a.data.stopx+sr.c4ShapeMargin,r.data.stopx),u4=Math.max(u4,r.data.stopx),h4=Math.max(h4,r.data.stopy)}}var u4,h4,lae,pP,sr,d4,mP,T2,c4,Dje,cae,uae,eo,oae,Ije,Mje,Nje,gP,dae=F(()=>{"use strict";$r();Aie();vt();p8();Vr();k8();Xt();G0();Qt();$n();u4=0,h4=0,lae=4,pP=2;ob.yy=pb;sr={},d4=class{static{o(this,"Bounds")}constructor(t){this.name="",this.data={},this.data.startx=void 0,this.data.stopx=void 0,this.data.starty=void 0,this.data.stopy=void 0,this.data.widthLimit=void 0,this.nextData={},this.nextData.startx=void 0,this.nextData.stopx=void 0,this.nextData.starty=void 0,this.nextData.stopy=void 0,this.nextData.cnt=0,mP(t.db.getConfig())}setData(t,r,n,i){this.nextData.startx=this.data.startx=t,this.nextData.stopx=this.data.stopx=r,this.nextData.starty=this.data.starty=n,this.nextData.stopy=this.data.stopy=i}updateVal(t,r,n,i){t[r]===void 0?t[r]=n:t[r]=i(n,t[r])}insert(t){this.nextData.cnt=this.nextData.cnt+1;let r=this.nextData.startx===this.nextData.stopx?this.nextData.stopx+t.margin:this.nextData.stopx+t.margin*2,n=r+t.width,i=this.nextData.starty+t.margin*2,a=i+t.height;(r>=this.data.widthLimit||n>=this.data.widthLimit||this.nextData.cnt>lae)&&(r=this.nextData.startx+t.margin+sr.nextLinePaddingX,i=this.nextData.stopy+t.margin*2,this.nextData.stopx=n=r+t.width,this.nextData.starty=this.nextData.stopy,this.nextData.stopy=a=i+t.height,this.nextData.cnt=1),t.x=r,t.y=i,this.updateVal(this.data,"startx",r,Math.min),this.updateVal(this.data,"starty",i,Math.min),this.updateVal(this.data,"stopx",n,Math.max),this.updateVal(this.data,"stopy",a,Math.max),this.updateVal(this.nextData,"startx",r,Math.min),this.updateVal(this.nextData,"starty",i,Math.min),this.updateVal(this.nextData,"stopx",n,Math.max),this.updateVal(this.nextData,"stopy",a,Math.max)}init(t){this.name="",this.data={startx:void 0,stopx:void 0,starty:void 0,stopy:void 0,widthLimit:void 0},this.nextData={startx:void 0,stopx:void 0,starty:void 0,stopy:void 0,cnt:0},mP(t.db.getConfig())}bumpLastMargin(t){this.data.stopx+=t,this.data.stopy+=t}},mP=o(function(e){Yn(sr,e),e.fontFamily&&(sr.personFontFamily=sr.systemFontFamily=sr.messageFontFamily=e.fontFamily),e.fontSize&&(sr.personFontSize=sr.systemFontSize=sr.messageFontSize=e.fontSize),e.fontWeight&&(sr.personFontWeight=sr.systemFontWeight=sr.messageFontWeight=e.fontWeight)},"setConf"),T2=o((e,t)=>({fontFamily:e[t+"FontFamily"],fontSize:e[t+"FontSize"],fontWeight:e[t+"FontWeight"]}),"c4ShapeFont"),c4=o(e=>({fontFamily:e.boundaryFontFamily,fontSize:e.boundaryFontSize,fontWeight:e.boundaryFontWeight}),"boundaryFont"),Dje=o(e=>({fontFamily:e.messageFontFamily,fontSize:e.messageFontSize,fontWeight:e.messageFontWeight}),"messageFont");o(ic,"calcC4ShapeTextWH");cae=o(function(e,t,r){t.x=r.data.startx,t.y=r.data.starty,t.width=r.data.stopx-r.data.startx,t.height=r.data.stopy-r.data.starty,t.label.y=sr.c4ShapeMargin-35;let n=t.wrap&&sr.wrap,i=c4(sr);i.fontSize=i.fontSize+2,i.fontWeight="bold";let a=Ca(t.label.text,i);ic("label",t,n,i,a),nc.drawBoundary(e,t,sr)},"drawBoundary"),uae=o(function(e,t,r,n){let i=0;for(let a of n){i=0;let s=r[a],l=T2(sr,s.typeC4Shape.text);switch(l.fontSize=l.fontSize-2,s.typeC4Shape.width=Ca("\xAB"+s.typeC4Shape.text+"\xBB",l),s.typeC4Shape.height=l.fontSize+2,s.typeC4Shape.Y=sr.c4ShapePadding,i=s.typeC4Shape.Y+s.typeC4Shape.height-4,s.image={width:0,height:0,Y:0},s.typeC4Shape.text){case"person":case"external_person":s.image.width=48,s.image.height=48,s.image.Y=i,i=s.image.Y+s.image.height;break}s.sprite&&(s.image.width=48,s.image.height=48,s.image.Y=i,i=s.image.Y+s.image.height);let u=s.wrap&&sr.wrap,h=sr.width-sr.c4ShapePadding*2,d=T2(sr,s.typeC4Shape.text);if(d.fontSize=d.fontSize+2,d.fontWeight="bold",ic("label",s,u,d,h),s.label.Y=i+8,i=s.label.Y+s.label.height,s.type&&s.type.text!==""){s.type.text="["+s.type.text+"]";let m=T2(sr,s.typeC4Shape.text);ic("type",s,u,m,h),s.type.Y=i+5,i=s.type.Y+s.type.height}else if(s.techn&&s.techn.text!==""){s.techn.text="["+s.techn.text+"]";let m=T2(sr,s.techn.text);ic("techn",s,u,m,h),s.techn.Y=i+5,i=s.techn.Y+s.techn.height}let f=i,p=s.label.width;if(s.descr&&s.descr.text!==""){let m=T2(sr,s.typeC4Shape.text);ic("descr",s,u,m,h),s.descr.Y=i+20,i=s.descr.Y+s.descr.height,p=Math.max(s.label.width,s.descr.width),f=i-s.descr.textLines*5}p=p+sr.c4ShapePadding,s.width=Math.max(s.width||sr.width,p,sr.width),s.height=Math.max(s.height||sr.height,f,sr.height),s.margin=s.margin||sr.c4ShapeMargin,e.insert(s),nc.drawC4Shape(t,s,sr)}e.bumpLastMargin(sr.c4ShapeMargin)},"drawC4ShapeArray"),eo=class{static{o(this,"Point")}constructor(t,r){this.x=t,this.y=r}},oae=o(function(e,t){let r=e.x,n=e.y,i=t.x,a=t.y,s=r+e.width/2,l=n+e.height/2,u=Math.abs(r-i),h=Math.abs(n-a),d=h/u,f=e.height/e.width,p=null;return n==a&&ri?p=new eo(r,l):r==i&&na&&(p=new eo(s,n)),r>i&&n=d?p=new eo(r,l+d*e.width/2):p=new eo(s-u/h*e.height/2,n+e.height):r=d?p=new eo(r+e.width,l+d*e.width/2):p=new eo(s+u/h*e.height/2,n+e.height):ra?f>=d?p=new eo(r+e.width,l-d*e.width/2):p=new eo(s+e.height/2*u/h,n):r>i&&n>a&&(f>=d?p=new eo(r,l-e.width/2*d):p=new eo(s-e.height/2*u/h,n)),p},"getIntersectPoint"),Ije=o(function(e,t){let r={x:0,y:0};r.x=t.x+t.width/2,r.y=t.y+t.height/2;let n=oae(e,r);r.x=e.x+e.width/2,r.y=e.y+e.height/2;let i=oae(t,r);return{startPoint:n,endPoint:i}},"getIntersectPoints"),Mje=o(function(e,t,r,n,i){let a=0;for(let s of t){a=a+1;let l=s.wrap&&sr.wrap,u=Dje(sr);n.db.getC4Type()==="C4Dynamic"&&(s.label.text=a+": "+s.label.text);let d=Ca(s.label.text,u);ic("label",s,l,u,d),s.techn&&s.techn.text!==""&&(d=Ca(s.techn.text,u),ic("techn",s,l,u,d)),s.descr&&s.descr.text!==""&&(d=Ca(s.descr.text,u),ic("descr",s,l,u,d));let f=r(s.from),p=r(s.to),m=Ije(f,p);s.startPoint=m.startPoint,s.endPoint=m.endPoint}nc.drawRels(e,t,sr,i)},"drawRels");o(hae,"drawInsideBoundary");Nje=o(function(e,t,r,n){sr=Ae().c4;let i=Ae().securityLevel,a;i==="sandbox"&&(a=et("#i"+t));let s=i==="sandbox"?et(a.nodes()[0].contentDocument.body):et("body"),l=n.db;n.db.setWrap(sr.wrap),lae=l.getC4ShapeInRow(),pP=l.getC4BoundaryInRow(),Z.debug(`C:${JSON.stringify(sr,null,2)}`);let u=i==="sandbox"?s.select(`[id="${t}"]`):et(`[id="${t}"]`);nc.insertComputerIcon(u,t),nc.insertDatabaseIcon(u,t),nc.insertClockIcon(u,t);let h=new d4(n);h.setData(sr.diagramMarginX,sr.diagramMarginX,sr.diagramMarginY,sr.diagramMarginY),h.data.widthLimit=screen.availWidth,u4=sr.diagramMarginX,h4=sr.diagramMarginY;let d=n.db.getTitle(),f=n.db.getBoundaries("");hae(u,"",h,f,n),nc.insertArrowHead(u,t),nc.insertArrowEnd(u,t),nc.insertArrowCrossHead(u,t),nc.insertArrowFilledHead(u,t),Mje(u,n.db.getRels(),n.db.getC4Shape,n,t),h.data.stopx=u4,h.data.stopy=h4;let p=h.data,g=p.stopy-p.starty+2*sr.diagramMarginY,v=p.stopx-p.startx+2*sr.diagramMarginX;d&&u.append("text").text(d).attr("x",(p.stopx-p.startx)/2-4*sr.diagramMarginX).attr("y",p.starty+sr.diagramMarginY),Wr(u,g,v,sr.useMaxWidth);let x=d?60:0;u.attr("viewBox",p.startx-sr.diagramMarginX+" -"+(sr.diagramMarginY+x)+" "+v+" "+(g+x)),Z.debug("models:",p)},"draw"),gP={drawPersonOrSystemArray:uae,drawBoundary:cae,setConf:mP,draw:Nje}});var Pje,fae,pae=F(()=>{"use strict";Pje=o(e=>`.person {
+ stroke: ${e.personBorder};
+ fill: ${e.personBkg};
+ }
+`,"getStyles"),fae=Pje});var mae={};ir(mae,{diagram:()=>Oje});var Oje,gae=F(()=>{"use strict";p8();k8();dae();pae();Oje={parser:oJ,db:pb,renderer:gP,styles:fae,init:o(({c4:e,wrap:t})=>{gP.setConf(e),pb.setWrap(t)},"init")}});function Mae(e){return typeof e>"u"||e===null}function zje(e){return typeof e=="object"&&e!==null}function Gje(e){return Array.isArray(e)?e:Mae(e)?[]:[e]}function Vje(e,t){var r,n,i,a;if(t)for(a=Object.keys(t),r=0,n=a.length;r"u"&&xh(e,t,null,!1,!1))&&(n!==""&&(n+=","+(e.condenseFlow?"":" ")),n+=e.dump);e.tag=i,e.dump="["+n+"]"}function Dae(e,t,r,n){var i="",a=e.tag,s,l,u;for(s=0,l=r.length;s tag resolver accepts not "'+u+'" style');e.dump=n}return!0}return!1}function xh(e,t,r,n,i,a,s){e.tag=null,e.dump=r,Iae(e,r,!1)||Iae(e,r,!0);var l=Xae.call(e.dump),u=n,h;n&&(n=e.flowLevel<0||e.flowLevel>t);var d=l==="[object Object]"||l==="[object Array]",f,p;if(d&&(f=e.duplicates.indexOf(r),p=f!==-1),(e.tag!==null&&e.tag!=="?"||p||e.indent!==2&&t>0)&&(i=!1),p&&e.usedDuplicates[f])e.dump="*ref_"+f;else{if(d&&p&&!e.usedDuplicates[f]&&(e.usedDuplicates[f]=!0),l==="[object Object]")n&&Object.keys(e.dump).length!==0?(rZe(e,t,e.dump,i),p&&(e.dump="&ref_"+f+e.dump)):(tZe(e,t,e.dump),p&&(e.dump="&ref_"+f+" "+e.dump));else if(l==="[object Array]")n&&e.dump.length!==0?(e.noArrayIndent&&!s&&t>0?Dae(e,t-1,e.dump,i):Dae(e,t,e.dump,i),p&&(e.dump="&ref_"+f+e.dump)):(eZe(e,t,e.dump),p&&(e.dump="&ref_"+f+" "+e.dump));else if(l==="[object String]")e.tag!=="?"&&ZKe(e,e.dump,t,a,u);else{if(l==="[object Undefined]")return!1;if(e.skipInvalid)return!1;throw new to("unacceptable kind of an object to dump "+l)}e.tag!==null&&e.tag!=="?"&&(h=encodeURI(e.tag[0]==="!"?e.tag.slice(1):e.tag).replace(/!/g,"%21"),e.tag[0]==="!"?h="!"+h:h.slice(0,18)==="tag:yaml.org,2002:"?h="!!"+h.slice(18):h="!<"+h+">",e.dump=h+" "+e.dump)}return!0}function nZe(e,t){var r=[],n=[],i,a;for(kP(e,r,n),i=0,a=n.length;i{"use strict";o(Mae,"isNothing");o(zje,"isObject");o(Gje,"toArray");o(Vje,"extend");o(Wje,"repeat");o(qje,"isNegativeZero");Hje=Mae,Uje=zje,Yje=Gje,jje=Wje,Xje=qje,Kje=Vje,sa={isNothing:Hje,isObject:Uje,toArray:Yje,repeat:jje,isNegativeZero:Xje,extend:Kje};o(Nae,"formatError");o(w2,"YAMLException$1");w2.prototype=Object.create(Error.prototype);w2.prototype.constructor=w2;w2.prototype.toString=o(function(t){return this.name+": "+Nae(this,t)},"toString");to=w2;o(yP,"getLine");o(vP,"padStart");o(Zje,"makeSnippet");Qje=Zje,Jje=["kind","multi","resolve","construct","instanceOf","predicate","represent","representName","defaultStyle","styleAliases"],eXe=["scalar","sequence","mapping"];o(tXe,"compileStyleAliases");o(rXe,"Type$1");ns=rXe;o(vae,"compileList");o(nXe,"compileMap");o(bP,"Schema$1");bP.prototype.extend=o(function(t){var r=[],n=[];if(t instanceof ns)n.push(t);else if(Array.isArray(t))n=n.concat(t);else if(t&&(Array.isArray(t.implicit)||Array.isArray(t.explicit)))t.implicit&&(r=r.concat(t.implicit)),t.explicit&&(n=n.concat(t.explicit));else throw new to("Schema.extend argument should be a Type, [ Type ], or a schema definition ({ implicit: [...], explicit: [...] })");r.forEach(function(a){if(!(a instanceof ns))throw new to("Specified list of YAML types (or a single Type object) contains a non-Type object.");if(a.loadKind&&a.loadKind!=="scalar")throw new to("There is a non-scalar type in the implicit list of a schema. Implicit resolving of such types is not supported.");if(a.multi)throw new to("There is a multi type in the implicit list of a schema. Multi tags can only be listed as explicit.")}),n.forEach(function(a){if(!(a instanceof ns))throw new to("Specified list of YAML types (or a single Type object) contains a non-Type object.")});var i=Object.create(bP.prototype);return i.implicit=(this.implicit||[]).concat(r),i.explicit=(this.explicit||[]).concat(n),i.compiledImplicit=vae(i,"implicit"),i.compiledExplicit=vae(i,"explicit"),i.compiledTypeMap=nXe(i.compiledImplicit,i.compiledExplicit),i},"extend");iXe=bP,aXe=new ns("tag:yaml.org,2002:str",{kind:"scalar",construct:o(function(e){return e!==null?e:""},"construct")}),sXe=new ns("tag:yaml.org,2002:seq",{kind:"sequence",construct:o(function(e){return e!==null?e:[]},"construct")}),oXe=new ns("tag:yaml.org,2002:map",{kind:"mapping",construct:o(function(e){return e!==null?e:{}},"construct")}),lXe=new iXe({explicit:[aXe,sXe,oXe]});o(cXe,"resolveYamlNull");o(uXe,"constructYamlNull");o(hXe,"isNull");dXe=new ns("tag:yaml.org,2002:null",{kind:"scalar",resolve:cXe,construct:uXe,predicate:hXe,represent:{canonical:o(function(){return"~"},"canonical"),lowercase:o(function(){return"null"},"lowercase"),uppercase:o(function(){return"NULL"},"uppercase"),camelcase:o(function(){return"Null"},"camelcase"),empty:o(function(){return""},"empty")},defaultStyle:"lowercase"});o(fXe,"resolveYamlBoolean");o(pXe,"constructYamlBoolean");o(mXe,"isBoolean");gXe=new ns("tag:yaml.org,2002:bool",{kind:"scalar",resolve:fXe,construct:pXe,predicate:mXe,represent:{lowercase:o(function(e){return e?"true":"false"},"lowercase"),uppercase:o(function(e){return e?"TRUE":"FALSE"},"uppercase"),camelcase:o(function(e){return e?"True":"False"},"camelcase")},defaultStyle:"lowercase"});o(yXe,"isHexCode");o(vXe,"isOctCode");o(xXe,"isDecCode");o(bXe,"resolveYamlInteger");o(TXe,"constructYamlInteger");o(CXe,"isInteger");wXe=new ns("tag:yaml.org,2002:int",{kind:"scalar",resolve:bXe,construct:TXe,predicate:CXe,represent:{binary:o(function(e){return e>=0?"0b"+e.toString(2):"-0b"+e.toString(2).slice(1)},"binary"),octal:o(function(e){return e>=0?"0o"+e.toString(8):"-0o"+e.toString(8).slice(1)},"octal"),decimal:o(function(e){return e.toString(10)},"decimal"),hexadecimal:o(function(e){return e>=0?"0x"+e.toString(16).toUpperCase():"-0x"+e.toString(16).toUpperCase().slice(1)},"hexadecimal")},defaultStyle:"decimal",styleAliases:{binary:[2,"bin"],octal:[8,"oct"],decimal:[10,"dec"],hexadecimal:[16,"hex"]}}),kXe=new RegExp("^(?:[-+]?(?:[0-9][0-9_]*)(?:\\.[0-9_]*)?(?:[eE][-+]?[0-9]+)?|\\.[0-9_]+(?:[eE][-+]?[0-9]+)?|[-+]?\\.(?:inf|Inf|INF)|\\.(?:nan|NaN|NAN))$");o(SXe,"resolveYamlFloat");o(EXe,"constructYamlFloat");AXe=/^[-+]?[0-9]+e/;o(RXe,"representYamlFloat");o(_Xe,"isFloat");LXe=new ns("tag:yaml.org,2002:float",{kind:"scalar",resolve:SXe,construct:EXe,predicate:_Xe,represent:RXe,defaultStyle:"lowercase"}),Pae=lXe.extend({implicit:[dXe,gXe,wXe,LXe]}),DXe=Pae,Oae=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9])-([0-9][0-9])$"),Bae=new RegExp("^([0-9][0-9][0-9][0-9])-([0-9][0-9]?)-([0-9][0-9]?)(?:[Tt]|[ \\t]+)([0-9][0-9]?):([0-9][0-9]):([0-9][0-9])(?:\\.([0-9]*))?(?:[ \\t]*(Z|([-+])([0-9][0-9]?)(?::([0-9][0-9]))?))?$");o(IXe,"resolveYamlTimestamp");o(MXe,"constructYamlTimestamp");o(NXe,"representYamlTimestamp");PXe=new ns("tag:yaml.org,2002:timestamp",{kind:"scalar",resolve:IXe,construct:MXe,instanceOf:Date,represent:NXe});o(OXe,"resolveYamlMerge");BXe=new ns("tag:yaml.org,2002:merge",{kind:"scalar",resolve:OXe}),SP=`ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=
+\r`;o($Xe,"resolveYamlBinary");o(FXe,"constructYamlBinary");o(zXe,"representYamlBinary");o(GXe,"isBinary");VXe=new ns("tag:yaml.org,2002:binary",{kind:"scalar",resolve:$Xe,construct:FXe,predicate:GXe,represent:zXe}),WXe=Object.prototype.hasOwnProperty,qXe=Object.prototype.toString;o(HXe,"resolveYamlOmap");o(UXe,"constructYamlOmap");YXe=new ns("tag:yaml.org,2002:omap",{kind:"sequence",resolve:HXe,construct:UXe}),jXe=Object.prototype.toString;o(XXe,"resolveYamlPairs");o(KXe,"constructYamlPairs");ZXe=new ns("tag:yaml.org,2002:pairs",{kind:"sequence",resolve:XXe,construct:KXe}),QXe=Object.prototype.hasOwnProperty;o(JXe,"resolveYamlSet");o(eKe,"constructYamlSet");tKe=new ns("tag:yaml.org,2002:set",{kind:"mapping",resolve:JXe,construct:eKe}),$ae=DXe.extend({implicit:[PXe,BXe],explicit:[VXe,YXe,ZXe,tKe]}),Kd=Object.prototype.hasOwnProperty,f4=1,Fae=2,zae=3,p4=4,xP=1,rKe=2,xae=3,nKe=/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F-\x84\x86-\x9F\uFFFE\uFFFF]|[\uD800-\uDBFF](?![\uDC00-\uDFFF])|(?:[^\uD800-\uDBFF]|^)[\uDC00-\uDFFF]/,iKe=/[\x85\u2028\u2029]/,aKe=/[,\[\]\{\}]/,Gae=/^(?:!|!!|![a-z\-]+!)$/i,Vae=/^(?:!|[^,\[\]\{\}])(?:%[0-9a-f]{2}|[0-9a-z\-#;\/\?:@&=\+\$,_\.!~\*'\(\)\[\]])*$/i;o(bae,"_class");o(tu,"is_EOL");o(xm,"is_WHITE_SPACE");o(ro,"is_WS_OR_EOL");o(Oy,"is_FLOW_INDICATOR");o(sKe,"fromHexCode");o(oKe,"escapedHexLen");o(lKe,"fromDecimalCode");o(Tae,"simpleEscapeSequence");o(cKe,"charFromCodepoint");o(Wae,"setProperty");qae=new Array(256),Hae=new Array(256);for(vm=0;vm<256;vm++)qae[vm]=Tae(vm)?1:0,Hae[vm]=Tae(vm);o(uKe,"State$1");o(Uae,"generateError");o(dr,"throwError");o(m4,"throwWarning");Cae={YAML:o(function(t,r,n){var i,a,s;t.version!==null&&dr(t,"duplication of %YAML directive"),n.length!==1&&dr(t,"YAML directive accepts exactly one argument"),i=/^([0-9]+)\.([0-9]+)$/.exec(n[0]),i===null&&dr(t,"ill-formed argument of the YAML directive"),a=parseInt(i[1],10),s=parseInt(i[2],10),a!==1&&dr(t,"unacceptable YAML version of the document"),t.version=n[0],t.checkLineBreaks=s<2,s!==1&&s!==2&&m4(t,"unsupported YAML version of the document")},"handleYamlDirective"),TAG:o(function(t,r,n){var i,a;n.length!==2&&dr(t,"TAG directive accepts exactly two arguments"),i=n[0],a=n[1],Gae.test(i)||dr(t,"ill-formed tag handle (first argument) of the TAG directive"),Kd.call(t.tagMap,i)&&dr(t,'there is a previously declared suffix for "'+i+'" tag handle'),Vae.test(a)||dr(t,"ill-formed tag prefix (second argument) of the TAG directive");try{a=decodeURIComponent(a)}catch{dr(t,"tag prefix is malformed: "+a)}t.tagMap[i]=a},"handleTagDirective")};o(Xd,"captureSegment");o(wae,"mergeMappings");o(By,"storeMappingPair");o(EP,"readLineBreak");o(Ui,"skipSeparationSpace");o(v4,"testDocumentSeparator");o(AP,"writeFoldedLines");o(hKe,"readPlainScalar");o(dKe,"readSingleQuotedScalar");o(fKe,"readDoubleQuotedScalar");o(pKe,"readFlowCollection");o(mKe,"readBlockScalar");o(kae,"readBlockSequence");o(gKe,"readBlockMapping");o(yKe,"readTagProperty");o(vKe,"readAnchorProperty");o(xKe,"readAlias");o($y,"composeNode");o(bKe,"readDocument");o(Yae,"loadDocuments");o(TKe,"loadAll$1");o(CKe,"load$1");wKe=TKe,kKe=CKe,jae={loadAll:wKe,load:kKe},Xae=Object.prototype.toString,Kae=Object.prototype.hasOwnProperty,RP=65279,SKe=9,k2=10,EKe=13,AKe=32,RKe=33,_Ke=34,TP=35,LKe=37,DKe=38,IKe=39,MKe=42,Zae=44,NKe=45,g4=58,PKe=61,OKe=62,BKe=63,$Ke=64,Qae=91,Jae=93,FKe=96,ese=123,zKe=124,tse=125,is={};is[0]="\\0";is[7]="\\a";is[8]="\\b";is[9]="\\t";is[10]="\\n";is[11]="\\v";is[12]="\\f";is[13]="\\r";is[27]="\\e";is[34]='\\"';is[92]="\\\\";is[133]="\\N";is[160]="\\_";is[8232]="\\L";is[8233]="\\P";GKe=["y","Y","yes","Yes","YES","on","On","ON","n","N","no","No","NO","off","Off","OFF"],VKe=/^[-+]?[0-9_]+(?::[0-9_]+)+(?:\.[0-9_]*)?$/;o(WKe,"compileStyleMap");o(qKe,"encodeHex");HKe=1,S2=2;o(UKe,"State");o(Sae,"indentString");o(CP,"generateNextLine");o(YKe,"testImplicitResolving");o(y4,"isWhitespace");o(E2,"isPrintable");o(Eae,"isNsCharOrWhitespace");o(Aae,"isPlainSafe");o(jKe,"isPlainSafeFirst");o(XKe,"isPlainSafeLast");o(C2,"codePointAt");o(rse,"needIndentIndicator");nse=1,wP=2,ise=3,ase=4,Py=5;o(KKe,"chooseScalarStyle");o(ZKe,"writeScalar");o(Rae,"blockHeader");o(_ae,"dropEndingNewline");o(QKe,"foldString");o(Lae,"foldLine");o(JKe,"escapeString");o(eZe,"writeFlowSequence");o(Dae,"writeBlockSequence");o(tZe,"writeFlowMapping");o(rZe,"writeBlockMapping");o(Iae,"detectType");o(xh,"writeNode");o(nZe,"getDuplicateReferences");o(kP,"inspectNode");o(iZe,"dump$1");aZe=iZe,sZe={dump:aZe};o(_P,"renamed");Zd=Pae,Qd=jae.load,hir=jae.loadAll,dir=sZe.dump,fir=_P("safeLoad","load"),pir=_P("safeLoadAll","loadAll"),mir=_P("safeDump","dump")});function MP(){return{async:!1,breaks:!1,extensions:null,gfm:!0,hooks:null,pedantic:!1,renderer:null,silent:!1,tokenizer:null,walkTokens:null}}function hse(e){Tm=e}function _n(e,t=""){let r=typeof e=="string"?e:e.source,n={replace:o((i,a)=>{let s=typeof a=="string"?a:a.source;return s=s.replace(_s.caret,"$1"),r=r.replace(i,s),n},"replace"),getRegex:o(()=>new RegExp(r,t),"getRegex")};return n}function ru(e,t){if(t){if(_s.escapeTest.test(e))return e.replace(_s.escapeReplace,ose)}else if(_s.escapeTestNoEncode.test(e))return e.replace(_s.escapeReplaceNoEncode,ose);return e}function lse(e){try{e=encodeURI(e).replace(_s.percentDecode,"%")}catch{return null}return e}function cse(e,t){let r=e.replace(_s.findPipe,(a,s,l)=>{let u=!1,h=s;for(;--h>=0&&l[h]==="\\";)u=!u;return u?"|":" |"}),n=r.split(_s.splitPipe),i=0;if(n[0].trim()||n.shift(),n.length>0&&!n.at(-1)?.trim()&&n.pop(),t)if(n.length>t)n.splice(t);else for(;n.length
+`:"'+(r?i:ru(i,!0))+`
+`}blockquote({tokens:e}){return`"+(r?i:ru(i,!0))+`
+${this.parser.parse(e)}
+`}html({text:e}){return e}def(e){return""}heading({tokens:e,depth:t}){return`
+`}list(e){let t=e.ordered,r=e.start,n="";for(let s=0;s
+
+`+t+`
+`+n+`
+`}tablerow({text:e}){return`
+${e}
+`}tablecell(e){let t=this.parser.parseInline(e.tokens),r=e.header?"th":"td";return(e.align?`<${r} align="${e.align}">`:`<${r}>`)+t+`${r}>
+`}strong({tokens:e}){return`${this.parser.parseInline(e)}`}em({tokens:e}){return`${this.parser.parseInline(e)}`}codespan({text:e}){return`${ru(e,!0)}`}br(e){return"
"}del({tokens:e}){return`${this.parser.parseInline(e)}`}link({href:e,title:t,tokens:r}){let n=this.parser.parseInline(r),i=lse(e);if(i===null)return n;e=i;let a='"+n+"",a}image({href:e,title:t,text:r,tokens:n}){n&&(r=this.parser.parseInline(n,this.parser.textRenderer));let i=lse(e);if(i===null)return ru(r);e=i;let a=`",a}text(e){return"tokens"in e&&e.tokens?this.parser.parseInline(e.tokens):"escaped"in e&&e.escaped?e.text:ru(e.text)}},GP=class{static{o(this,"$")}strong({text:e}){return e}em({text:e}){return e}codespan({text:e}){return e}del({text:e}){return e}html({text:e}){return e}text({text:e}){return e}link({text:e}){return""+e}image({text:e}){return""+e}br(){return""}},Th=class IP{static{o(this,"l")}options;renderer;textRenderer;constructor(t){this.options=t||Tm,this.options.renderer=this.options.renderer||new w4,this.renderer=this.options.renderer,this.renderer.options=this.options,this.renderer.parser=this,this.textRenderer=new GP}static parse(t,r){return new IP(r).parse(t)}static parseInline(t,r){return new IP(r).parseInline(t)}parse(t,r=!0){let n="";for(let i=0;i
"+ru(r.message+"",!0)+"
";return t?Promise.resolve(n):n}if(t)return Promise.reject(r);throw r}}},bm=new VZe;o(Sn,"d");Sn.options=Sn.setOptions=function(e){return bm.setOptions(e),Sn.defaults=bm.defaults,hse(Sn.defaults),Sn};Sn.getDefaults=MP;Sn.defaults=Tm;Sn.use=function(...e){return bm.use(...e),Sn.defaults=bm.defaults,hse(Sn.defaults),Sn};Sn.walkTokens=function(e,t){return bm.walkTokens(e,t)};Sn.parseInline=bm.parseInline;Sn.Parser=Th;Sn.parser=Th.parse;Sn.Renderer=w4;Sn.TextRenderer=GP;Sn.Lexer=bh;Sn.lexer=bh.lex;Sn.Tokenizer=C4;Sn.Hooks=L2;Sn.parse=Sn;yir=Sn.options,vir=Sn.setOptions,xir=Sn.use,bir=Sn.walkTokens,Tir=Sn.parseInline,Cir=Th.parse,wir=bh.lex});function WZe(e,{markdownAutoWrap:t}){let n=e.replace(/
/g,`
+`).replace(/\n{2,}/g,`
+`);return mS(n)}function wse(e){return e.split(/\\n|\n|
/gi).map(t=>t.trim().match(/<[^>]+>|[^\s<>]+/g)?.map(r=>({content:r,type:"normal"}))??[])}function kse(e,t={}){let r=WZe(e,t),n=Sn.lexer(r),i=[[]],a=0;function s(l,u="normal"){l.type==="text"?l.text.split(`
+`).forEach((d,f)=>{f!==0&&(a++,i.push([])),d.split(" ").forEach(p=>{p=p.replace(/'/g,"'"),p&&i[a].push({content:p,type:u})})}):l.type==="strong"||l.type==="em"?l.tokens.forEach(h=>{s(h,l.type)}):l.type==="html"&&i[a].push({content:l.text,type:"normal"})}return o(s,"processNode"),n.forEach(l=>{l.type==="paragraph"?l.tokens?.forEach(u=>{s(u)}):l.type==="html"?i[a].push({content:l.text,type:"normal"}):i[a].push({content:l.raw,type:"normal"})}),i}function Sse(e){return e?`
")}
").replace(/ /g," "):i.text.replace(/\n */g,"
"):i.type==="strong"?`${i.tokens?.map(n).join("")}`:i.type==="em"?`${i.tokens?.map(n).join("")}`:i.type==="paragraph"?`
/g,"
")),p=s?kse(f.replace("
","
"),d):wse(f),m=jZe(u,e,p,t?h:!1,!l);if(l){/stroke:/.exec(r)&&(r=r.replace("stroke:","lineColor:"));let g=r.replace(/stroke:[^;]+;?/g,"").replace(/stroke-width:[^;]+;?/g,"").replace(/fill:[^;]+;?/g,"").replace(/color:/g,"fill:");et(m).attr("style",g)}else{let g=r.replace(/stroke:[^;]+;?/g,"").replace(/stroke-width:[^;]+;?/g,"").replace(/fill:[^;]+;?/g,"").replace(/background:/g,"fill:");et(m).select("rect").attr("style",g.replace(/background:/g,"fill:"));let y=r.replace(/stroke:[^;]+;?/g,"").replace(/stroke-width:[^;]+;?/g,"").replace(/fill:[^;]+;?/g,"").replace(/color:/g,"fill:");et(m).select("text").attr("style",y)}return n?et(m).selectAll("tspan.text-outer-tspan").classed("title-row",!0):et(m).selectAll("tspan.text-outer-tspan").classed("row",!0),m}},"createText")});async function E4(e,t){let r=e.getElementsByTagName("img");if(!r||r.length===0)return;let n=t.replace(/]*>/g,"").trim()==="";await Promise.all([...r].map(i=>new Promise(a=>{function s(){if(i.style.display="flex",i.style.flexDirection="column",n){let l=Ae().fontSize?Ae().fontSize:window.getComputedStyle(document.body).fontSize,u=5,[h=cr.fontSize]=As(l),d=h*u+"px";i.style.minWidth=d,i.style.maxWidth=d}else i.style.width="100%";a(i)}o(s,"setupImage"),setTimeout(()=>{i.complete&&s()}),i.addEventListener("error",s),i.addEventListener("load",s)})))}var HP=F(()=>{"use strict";Xt();Wi();Qt();o(E4,"configureLabelImages")});function or(e){let t=e.map((r,n)=>`${n===0?"M":"L"}${r.x},${r.y}`);return t.push("Z"),t.join(" ")}function fl(e,t,r,n,i,a){let s=[],u=r-e,h=n-t,d=u/a,f=2*Math.PI/d,p=t+h/2;for(let m=0;m<=50;m++){let g=m/50,y=e+g*u,v=p+i*Math.sin(f*(y-e));s.push({x:y,y:v})}return s}function Cm(e,t,r,n,i,a){let s=[],l=i*Math.PI/180,d=(a*Math.PI/180-l)/(n-1);for(let f=0;f
"):p,t.labelStyle,!0,!0),y=g.children[0],v=et(g);f=y.getBoundingClientRect(),v.attr("width",f.width),v.attr("height",f.height);let x=(t.padding||0)/2;et(g).attr("transform","translate( "+(f.width>m.width?0:(m.width-f.width)/2)+", "+(m.height+x+5)+")"),et(d).attr("transform","translate( "+(f.width
").length,f.innerHTML.includes("")&&(d+=f.innerHTML.split("]*>/g,"").trim()==="";await Promise.all([...m].map(y=>new Promise(v=>{function x(){if(y.style.display="flex",y.style.flexDirection="column",g){let b=a.fontSize?.toString()??window.getComputedStyle(document.body).fontSize,k=parseInt(b,10)*5+"px";y.style.minWidth=k,y.style.maxWidth=k}else y.style.width="100%";v(y)}o(x,"setupImage"),setTimeout(()=>{y.complete&&x()}),y.addEventListener("error",x),y.addEventListener("load",x)})))}h=f.getBoundingClientRect(),p.attr("width",h.width),p.attr("height",h.height)}else{n.includes("font-weight: bolder")&&et(u).selectAll("tspan").attr("font-weight",""),d=u.children.length;let f=u.children[0];(u.textContent===""||u.textContent.includes(">"))&&(f.textContent=l[0]+l.substring(1).replaceAll(">",">").replaceAll("<","<").trim(),l[1]===" "&&(f.textContent=f.textContent[0]+" "+f.textContent.substring(1))),f.textContent==="undefined"&&(f.textContent=""),h=u.getBBox()}return i.attr("transform","translate(0,"+(-h.height/(2*d)+r)+")"),h.height}var wce=F(()=>{"use strict";$r();ur();Kt();Qt();Xt();Ls();Vr();o(Cce,"textHelper");o(V4,"addText")});async function kce(e,t){let r=Ae(),{themeVariables:n}=r,{useGradient:i}=n,a=r.class.padding??12,s=a,l=t.useHtmlLabels??ya(r.htmlLabels)??!0,u=t;u.annotations=u.annotations??[],u.members=u.members??[],u.methods=u.methods??[];let{shapeSvg:h,bbox:d}=await Cce(e,t,r,l,s),{labelStyles:f,nodeStyles:p}=ct(t);t.labelStyle=f,t.cssStyles=u.styles||"";let m=u.styles?.join(";")||p||"";t.cssStyles||(t.cssStyles=m.replaceAll("!important","").split(";"));let g=u.members.length===0&&u.methods.length===0&&!r.class?.hideEmptyMembersBox,y=ut.svg(h),v=dt(t,{});t.look!=="handDrawn"&&(v.roughness=0,v.fillStyle="solid");let x=Math.max(t.width??0,d.width),b=Math.max(t.height??0,d.height),T=(t.height??0)>d.height;u.members.length===0&&u.methods.length===0?b+=s:u.members.length>0&&u.methods.length===0&&(b+=s*2);let k=-x/2,C=-b/2,w=g?a*2:u.members.length===0&&u.methods.length===0?-a:0;T&&(w=a*2);let S=y.rectangle(k-a,C-a-(g?a:u.members.length===0&&u.methods.length===0?-a/2:0),x+2*a,b+2*a+w,v),R=h.insert(()=>S,":first-child");R.attr("class","basic label-container outer-path");let L=R.node().getBBox(),N=h.select(".annotation-group").node().getBBox().height-(g?a/2:0)||0,I=h.select(".label-group").node().getBBox().height-(g?a/2:0)||0,_=h.select(".members-group").node().getBBox().height-(g?a/2:0)||0,A=(N+I+C+a-(C-a-(g?a:u.members.length===0&&u.methods.length===0?-a/2:0)))/2;if(h.selectAll(".text").each((M,D,P)=>{let B=et(P[D]),O=B.attr("transform"),$=0;if(O){let W=RegExp(/translate\(([^,]+),([^)]+)\)/).exec(O);W&&($=parseFloat(W[2]))}let V=$+C+a-(g?a:u.members.length===0&&u.methods.length===0?-a/2:0);if(B.attr("class").includes("methods-group")){let z=Math.max(_,s/2);T?V=Math.max(A,N+I+z+C+s*2+a)+s*2:V=N+I+z+C+s*4+a}u.members.length===0&&u.methods.length===0&&r.class?.hideEmptyMembersBox&&(u.annotations.length>0?V=$-s:V=$),l||(V-=4);let G=k;(B.attr("class").includes("label-group")||B.attr("class").includes("annotation-group"))&&(G=-B.node()?.getBBox().width/2||0,h.selectAll("text").each(function(z,W,H){window.getComputedStyle(H[W]).textAnchor==="middle"&&(G=0)})),B.attr("transform",`translate(${G}, ${V})`)}),u.members.length>0||u.methods.length>0||g){let M=N+I+C+a,D=y.line(L.x,M,L.x+L.width,M+.001,v);h.insert(()=>D).attr("class",`divider${t.look==="neo"&&!i?" neo-line":""}`).attr("style",m)}if(g||u.members.length>0||u.methods.length>0){let M=N+I+_+C+s*2+a,D=y.line(L.x,T?Math.max(A,M):M,L.x+L.width,(T?Math.max(A,M):M)+.001,v);h.insert(()=>D).attr("class",`divider${t.look==="neo"&&!i?" neo-line":""}`).attr("style",m)}if(u.look!=="handDrawn"&&h.selectAll("path").attr("style",m),R.select(":nth-child(2)").attr("style",m),h.selectAll(".divider").select("path").attr("style",m),t.labelStyle?h.selectAll("span").attr("style",t.labelStyle):h.selectAll("span").attr("style",m),!l){let M=RegExp(/color\s*:\s*([^;]*)/),D=M.exec(m);if(D){let P=D[0].replace("color","fill");h.selectAll("tspan").attr("style",P)}else if(f){let P=M.exec(f);if(P){let B=P[0].replace("color","fill");h.selectAll("tspan").attr("style",B)}}}return pt(t,R),t.intersect=function(M){return ht.rect(t,M)},h}var Sce=F(()=>{"use strict";Kt();Xt();$r();tr();Jt();nr();wce();Vr();o(kce,"classBox")});async function Ece(e,t){let{labelStyles:r,nodeStyles:n}=ct(t);t.labelStyle=r;let i=t,a=t,s=20,l=20,u="verifyMethod"in t,h=Dt(t),{themeVariables:d}=Ae(),{borderColorArray:f,requirementEdgeLabelBackground:p}=d,m=e.insert("g").attr("class",h).attr("id",t.domId??t.id),g;u?g=await wh(m,`<<${i.type}>>`,0,t.labelStyle):g=await wh(m,"<<Element>>",0,t.labelStyle);let y=g,v=await wh(m,i.name,y,t.labelStyle+"; font-weight: bold;");if(y+=v+l,u){let L=await wh(m,`${i.requirementId?`ID: ${i.requirementId}`:""}`,y,t.labelStyle);y+=L;let N=await wh(m,`${i.text?`Text: ${i.text}`:""}`,y,t.labelStyle);y+=N;let I=await wh(m,`${i.risk?`Risk: ${i.risk}`:""}`,y,t.labelStyle);y+=I,await wh(m,`${i.verifyMethod?`Verification: ${i.verifyMethod}`:""}`,y,t.labelStyle)}else{let L=await wh(m,`${a.type?`Type: ${a.type}`:""}`,y,t.labelStyle);y+=L,await wh(m,`${a.docRef?`Doc Ref: ${a.docRef}`:""}`,y,t.labelStyle)}let x=(m.node()?.getBBox().width??200)+s,b=(m.node()?.getBBox().height??200)+s,T=-x/2,k=-b/2,C=ut.svg(m),w=dt(t,{});t.look!=="handDrawn"&&(w.roughness=0,w.fillStyle="solid");let S=C.rectangle(T,k,x,b,w),R=m.insert(()=>S,":first-child");if(R.attr("class","basic label-container outer-path").attr("style",n),f?.length){let L=t.colorIndex??0;m.attr("data-color-id",`color-${L%f.length}`)}if(m.selectAll(".label").each((L,N,I)=>{let _=et(I[N]),A=_.attr("transform"),M=0,D=0;if(A){let $=RegExp(/translate\(([^,]+),([^)]+)\)/).exec(A);$&&(M=parseFloat($[1]),D=parseFloat($[2]))}let P=D-b/2,B=T+s/2;(N===0||N===1)&&(B=M),_.attr("transform",`translate(${B}, ${P+s})`)}),y>g+v+l){let L=k+g+v+l,N;if(t.look==="neo"){let A=[[T,L],[T+x,L],[T+x,L+.001],[T,L+.001]];N=C.polygon(A,w)}else N=C.line(T,L,T+x,L,w);m.insert(()=>N).attr("class","divider")}return pt(t,R),t.intersect=function(L){return ht.rect(t,L)},n&&t.look!=="handDrawn"&&(p||f?.length)&&m.selectAll("path").attr("style",n),m}async function wh(e,t,r,n=""){if(t==="")return 0;let i=e.insert("g").attr("class","label").attr("style",n),a=Ae(),s=a.htmlLabels??!0,l=await Pn(i,ub(Rs(t)),{width:Ca(t,a)+50,classes:"markdown-node-label",useHtmlLabels:s,style:n},a),u;if(s){let h=l.children[0],d=et(l);u=h.getBoundingClientRect(),d.attr("width",u.width),d.attr("height",u.height)}else{let h=l.children[0];for(let d of h.children)n&&d.setAttribute("style",n);u=l.getBBox(),u.height+=6}return i.attr("transform",`translate(${-u.width/2},${-u.height/2+r})`),u.height}var Ace=F(()=>{"use strict";Kt();nr();Jt();tr();Qt();Xt();Ls();$r();o(Ece,"requirementBox");o(wh,"addText")});async function Rce(e,t,{config:r}){let{labelStyles:n,nodeStyles:i}=ct(t);t.labelStyle=n||"";let a=10,s=t.width;t.width=(t.width??200)-10;let{shapeSvg:l,bbox:u,label:h}=await It(e,t,Dt(t)),d=t.padding||10,f="",p;"ticket"in t&&t.ticket&&r?.kanban?.ticketBaseUrl&&(f=r?.kanban?.ticketBaseUrl.replace("#TICKET#",t.ticket),p=l.insert("svg:a",":first-child").attr("class","kanban-ticket-link").attr("xlink:href",f).attr("target","_blank"));let m={useHtmlLabels:t.useHtmlLabels,labelStyle:t.labelStyle||"",width:t.width,img:t.img,padding:t.padding||8,centerLabel:!1},g,y;p?{label:g,bbox:y}=await A4(p,"ticket"in t&&t.ticket||"",m):{label:g,bbox:y}=await A4(l,"ticket"in t&&t.ticket||"",m);let{label:v,bbox:x}=await A4(l,"assigned"in t&&t.assigned||"",m);t.width=s;let b=10,T=t?.width||0,k=Math.max(y.height,x.height)/2,C=Math.max(u.height+b*2,t?.height||0)+k,w=-T/2,S=-C/2;h.attr("transform","translate("+(d-T/2)+", "+(-k-u.height/2)+")"),g.attr("transform","translate("+(d-T/2)+", "+(-k+u.height/2)+")"),v.attr("transform","translate("+(d+T/2-x.width-2*a)+", "+(-k+u.height/2)+")");let R,{rx:L,ry:N}=t,{cssStyles:I}=t;if(t.look==="handDrawn"){let _=ut.svg(l),A=dt(t,{}),M=L||N?_.path(io(w,S,T,C,L||0),A):_.rectangle(w,S,T,C,A);R=l.insert(()=>M,":first-child"),R.attr("class","basic label-container").attr("style",I||null)}else{R=l.insert("rect",":first-child"),R.attr("class","basic label-container __APA__").attr("style",i).attr("rx",L??5).attr("ry",N??5).attr("x",w).attr("y",S).attr("width",T).attr("height",C);let _="priority"in t&&t.priority;if(_){let A=l.append("line"),M=w+2,D=S+Math.floor((L??0)/2),P=S+C-Math.floor((L??0)/2);A.attr("x1",M).attr("y1",D).attr("x2",M).attr("y2",P).attr("stroke-width","4").attr("stroke",AQe(_))}}return pt(t,R),t.height=C,t.intersect=function(_){return ht.rect(t,_)},l}var AQe,_ce=F(()=>{"use strict";Kt();nr();km();Jt();tr();AQe=o(e=>{switch(e){case"Very High":return"red";case"High":return"orange";case"Medium":return null;case"Low":return"blue";case"Very Low":return"lightblue"}},"colorFromPriority");o(Rce,"kanbanItem")});async function Lce(e,t){let{labelStyles:r,nodeStyles:n}=ct(t);t.labelStyle=r;let{shapeSvg:i,bbox:a,halfPadding:s,label:l}=await It(e,t,Dt(t)),u=a.width+10*s,h=a.height+8*s,d=.15*u,{cssStyles:f}=t,p=a.width+20,m=a.height+20,g=Math.max(u,p),y=Math.max(h,m);l.attr("transform",`translate(${-a.width/2}, ${-a.height/2})`);let v,x=`M0 0
+ a${d},${d} 1 0,0 ${g*.25},${-1*y*.1}
+ a${d},${d} 1 0,0 ${g*.25},0
+ a${d},${d} 1 0,0 ${g*.25},0
+ a${d},${d} 1 0,0 ${g*.25},${y*.1}
+
+ a${d},${d} 1 0,0 ${g*.15},${y*.33}
+ a${d*.8},${d*.8} 1 0,0 0,${y*.34}
+ a${d},${d} 1 0,0 ${-1*g*.15},${y*.33}
+
+ a${d},${d} 1 0,0 ${-1*g*.25},${y*.15}
+ a${d},${d} 1 0,0 ${-1*g*.25},0
+ a${d},${d} 1 0,0 ${-1*g*.25},0
+ a${d},${d} 1 0,0 ${-1*g*.25},${-1*y*.15}
+
+ a${d},${d} 1 0,0 ${-1*g*.1},${-1*y*.33}
+ a${d*.8},${d*.8} 1 0,0 0,${-1*y*.34}
+ a${d},${d} 1 0,0 ${g*.1},${-1*y*.33}
+ H0 V0 Z`;if(t.look==="handDrawn"){let b=ut.svg(i),T=dt(t,{}),k=b.path(x,T);v=i.insert(()=>k,":first-child"),v.attr("class","basic label-container").attr("style",kn(f))}else v=i.insert("path",":first-child").attr("class","basic label-container").attr("style",n).attr("d",x);return v.attr("transform",`translate(${-g/2}, ${-y/2})`),pt(t,v),t.calcIntersect=function(b,T){return ht.rect(b,T)},t.intersect=function(b){return Z.info("Bang intersect",t,b),ht.rect(t,b)},i}var Dce=F(()=>{"use strict";vt();Kt();nr();Jt();tr();Qt();o(Lce,"bang")});async function Ice(e,t){let{labelStyles:r,nodeStyles:n}=ct(t);t.labelStyle=r;let{shapeSvg:i,bbox:a,halfPadding:s,label:l}=await It(e,t,Dt(t)),u=a.width+2*s,h=a.height+2*s,d=.15*u,f=.25*u,p=.35*u,m=.2*u,{cssStyles:g}=t,y,v=`M0 0
+ a${d},${d} 0 0,1 ${u*.25},${-1*u*.1}
+ a${p},${p} 1 0,1 ${u*.4},${-1*u*.1}
+ a${f},${f} 1 0,1 ${u*.35},${u*.2}
+
+ a${d},${d} 1 0,1 ${u*.15},${h*.35}
+ a${m},${m} 1 0,1 ${-1*u*.15},${h*.65}
+
+ a${f},${d} 1 0,1 ${-1*u*.25},${u*.15}
+ a${p},${p} 1 0,1 ${-1*u*.5},0
+ a${d},${d} 1 0,1 ${-1*u*.25},${-1*u*.15}
+
+ a${d},${d} 1 0,1 ${-1*u*.1},${-1*h*.35}
+ a${m},${m} 1 0,1 ${u*.1},${-1*h*.65}
+ H0 V0 Z`;if(t.look==="handDrawn"){let x=ut.svg(i),b=dt(t,{}),T=x.path(v,b);y=i.insert(()=>T,":first-child"),y.attr("class","basic label-container").attr("style",kn(g))}else y=i.insert("path",":first-child").attr("class","basic label-container").attr("style",n).attr("d",v);return l.attr("transform",`translate(${-a.width/2}, ${-a.height/2})`),y.attr("transform",`translate(${-u/2}, ${-h/2})`),pt(t,y),t.calcIntersect=function(x,b){return ht.rect(x,b)},t.intersect=function(x){return Z.info("Cloud intersect",t,x),ht.rect(t,x)},i}var Mce=F(()=>{"use strict";tr();vt();Qt();nr();Jt();Kt();o(Ice,"cloud")});async function Nce(e,t){let{labelStyles:r,nodeStyles:n}=ct(t);t.labelStyle=r;let{shapeSvg:i,bbox:a,halfPadding:s,label:l}=await It(e,t,Dt(t)),u=a.width+8*s,h=a.height+2*s,d=5,f=t.look==="neo"?`
+ M${-u/2} ${h/2-d}
+ v${-h+2*d}
+ q0,-${d} ${d},-${d}
+ h${u-2*d}
+ q${d},0 ${d},${d}
+ v${h-d}
+ H${-u/2}
+ Z
+ `:`
+ M${-u/2} ${h/2-d}
+ v${-h+2*d}
+ q0,-${d} ${d},-${d}
+ h${u-2*d}
+ q${d},0 ${d},${d}
+ v${h-2*d}
+ q0,${d} ${-d},${d}
+ h${-(u-2*d)}
+ q${-d},0 ${-d},${-d}
+ Z
+ `;if(!t.domId)throw new Error(`defaultMindmapNode: node "${t.id}" is missing a domId \u2014 was render.ts domId prefixing skipped?`);let p=i.append("path").attr("id",t.domId).attr("class","node-bkg node-"+t.type).attr("style",n).attr("d",f);return i.append("line").attr("class","node-line-").attr("x1",-u/2).attr("y1",h/2).attr("x2",u/2).attr("y2",h/2),l.attr("transform",`translate(${-a.width/2}, ${-a.height/2})`),i.append(()=>l.node()),pt(t,p),t.calcIntersect=function(m,g){return ht.rect(m,g)},t.intersect=function(m){return ht.rect(t,m)},i}var Pce=F(()=>{"use strict";nr();Jt();Kt();o(Nce,"defaultMindmapNode")});async function Oce(e,t){let r={padding:t.padding??0};return z4(e,t,r)}var Bce=F(()=>{"use strict";hO();o(Oce,"mindmapCircle")});function $ce(e){return e in pO}var RQe,_Qe,pO,mO=F(()=>{"use strict";soe();coe();hoe();foe();hO();moe();yoe();xoe();Toe();woe();Aoe();_oe();Doe();Moe();Poe();Foe();Goe();Woe();Hoe();Yoe();Xoe();Zoe();Joe();tle();nle();ale();ole();cle();hle();fle();yle();xle();Tle();wle();Sle();Ale();_le();Dle();Mle();Ple();Ble();Fle();Gle();Wle();Hle();Yle();Xle();Zle();Jle();tce();ace();oce();cce();fce();mce();yce();xce();Tce();Sce();Ace();_ce();Dce();Mce();Pce();Bce();RQe=[{semanticName:"Process",name:"Rectangle",shortName:"rect",description:"Standard process shape",aliases:["proc","process","rectangle"],internalAliases:["squareRect"],handler:$le},{semanticName:"Event",name:"Rounded Rectangle",shortName:"rounded",description:"Represents an event",aliases:["event"],internalAliases:["roundedRect"],handler:Ile},{semanticName:"Terminal Point",name:"Stadium",shortName:"stadium",description:"Terminal point",aliases:["terminal","pill"],handler:zle},{semanticName:"Subprocess",name:"Framed Rectangle",shortName:"fr-rect",description:"Subprocess",aliases:["subprocess","subproc","framed-rectangle","subroutine"],handler:jle},{semanticName:"Database",name:"Cylinder",shortName:"cyl",description:"Database storage",aliases:["db","database","cylinder"],handler:Eoe},{semanticName:"Data Store",name:"Data Store",shortName:"datastore",description:"Data flow diagram data store",aliases:["data-store"],handler:Roe},{semanticName:"Start",name:"Circle",shortName:"circle",description:"Starting point",aliases:["circ"],handler:z4},{semanticName:"Bang",name:"Bang",shortName:"bang",description:"Bang",aliases:["bang"],handler:Lce},{semanticName:"Cloud",name:"Cloud",shortName:"cloud",description:"cloud",aliases:["cloud"],handler:Ice},{semanticName:"Decision",name:"Diamond",shortName:"diam",description:"Decision-making step",aliases:["decision","diamond","question"],handler:Ele},{semanticName:"Prepare Conditional",name:"Hexagon",shortName:"hex",description:"Preparation or condition step",aliases:["hexagon","prepare"],handler:qoe},{semanticName:"Data Input/Output",name:"Lean Right",shortName:"lean-r",description:"Represents input or output",aliases:["lean-right","in-out"],internalAliases:["lean_right"],handler:ule},{semanticName:"Data Input/Output",name:"Lean Left",shortName:"lean-l",description:"Represents output or input",aliases:["lean-left","out-in"],internalAliases:["lean_left"],handler:lle},{semanticName:"Priority Action",name:"Trapezoid Base Bottom",shortName:"trap-b",description:"Priority action",aliases:["priority","trapezoid-bottom","trapezoid"],handler:sce},{semanticName:"Manual Operation",name:"Trapezoid Base Top",shortName:"trap-t",description:"Represents a manual task",aliases:["manual","trapezoid-top","inv-trapezoid"],internalAliases:["inv_trapezoid"],handler:ile},{semanticName:"Stop",name:"Double Circle",shortName:"dbl-circ",description:"Represents a stop point",aliases:["double-circle"],internalAliases:["doublecircle"],handler:Ioe},{semanticName:"Text Block",name:"Text Block",shortName:"text",description:"Text block",handler:ece},{semanticName:"Card",name:"Notched Rectangle",shortName:"notch-rect",description:"Represents a card",aliases:["card","notched-rectangle"],handler:uoe},{semanticName:"Lined/Shaded Process",name:"Lined Rectangle",shortName:"lin-rect",description:"Lined process shape",aliases:["lined-rectangle","lined-process","lin-proc","shaded-process"],handler:Nle},{semanticName:"Start",name:"Small Circle",shortName:"sm-circ",description:"Small starting point",aliases:["start","small-circle"],internalAliases:["stateStart"],handler:Ule},{semanticName:"Stop",name:"Framed Circle",shortName:"fr-circ",description:"Stop point",aliases:["stop","framed-circle"],internalAliases:["stateEnd"],handler:qle},{semanticName:"Fork/Join",name:"Filled Rectangle",shortName:"fork",description:"Fork or join in process flow",aliases:["join"],internalAliases:["forkJoin"],handler:zoe},{semanticName:"Collate",name:"Hourglass",shortName:"hourglass",description:"Represents a collate operation",aliases:["hourglass","collate"],handler:Uoe},{semanticName:"Comment",name:"Curly Brace",shortName:"brace",description:"Adds a comment",aliases:["comment","brace-l"],handler:goe},{semanticName:"Comment Right",name:"Curly Brace",shortName:"brace-r",description:"Adds a comment",handler:voe},{semanticName:"Comment with braces on both sides",name:"Curly Braces",shortName:"braces",description:"Adds a comment",handler:boe},{semanticName:"Com Link",name:"Lightning Bolt",shortName:"bolt",description:"Communication link",aliases:["com-link","lightning-bolt"],handler:dle},{semanticName:"Document",name:"Document",shortName:"doc",description:"Represents a document",aliases:["doc","document"],handler:pce},{semanticName:"Delay",name:"Half-Rounded Rectangle",shortName:"delay",description:"Represents a delay",aliases:["half-rounded-rectangle"],handler:Voe},{semanticName:"Direct Access Storage",name:"Horizontal Cylinder",shortName:"h-cyl",description:"Direct access storage",aliases:["das","horizontal-cylinder"],handler:ice},{semanticName:"Disk Storage",name:"Lined Cylinder",shortName:"lin-cyl",description:"Disk storage",aliases:["disk","lined-cylinder"],handler:gle},{semanticName:"Display",name:"Curved Trapezoid",shortName:"curv-trap",description:"Represents a display",aliases:["curved-trapezoid","display"],handler:Coe},{semanticName:"Divided Process",name:"Divided Rectangle",shortName:"div-rect",description:"Divided process shape",aliases:["div-proc","divided-rectangle","divided-process"],handler:Loe},{semanticName:"Extract",name:"Triangle",shortName:"tri",description:"Extraction process",aliases:["extract","triangle"],handler:dce},{semanticName:"Internal Storage",name:"Window Pane",shortName:"win-pane",description:"Internal storage",aliases:["internal-storage","window-pane"],handler:vce},{semanticName:"Junction",name:"Filled Circle",shortName:"f-circ",description:"Junction point",aliases:["junction","filled-circle"],handler:Noe},{semanticName:"Loop Limit",name:"Trapezoidal Pentagon",shortName:"notch-pent",description:"Loop limit step",aliases:["loop-limit","notched-pentagon"],handler:lce},{semanticName:"Manual File",name:"Flipped Triangle",shortName:"flip-tri",description:"Manual file operation",aliases:["manual-file","flipped-triangle"],handler:$oe},{semanticName:"Manual Input",name:"Sloped Rectangle",shortName:"sl-rect",description:"Manual input step",aliases:["manual-input","sloped-rectangle"],handler:Ole},{semanticName:"Multi-Document",name:"Stacked Document",shortName:"docs",description:"Multiple documents",aliases:["documents","st-doc","stacked-document"],handler:Cle},{semanticName:"Multi-Process",name:"Stacked Rectangle",shortName:"st-rect",description:"Multiple processes",aliases:["procs","processes","stacked-rectangle"],handler:ble},{semanticName:"Stored Data",name:"Bow Tie Rectangle",shortName:"bow-rect",description:"Stored data",aliases:["stored-data","bow-tie-rectangle"],handler:loe},{semanticName:"Summary",name:"Crossed Circle",shortName:"cross-circ",description:"Summary",aliases:["summary","crossed-circle"],handler:poe},{semanticName:"Tagged Document",name:"Tagged Document",shortName:"tag-doc",description:"Tagged document",aliases:["tag-doc","tagged-document"],handler:Qle},{semanticName:"Tagged Process",name:"Tagged Rectangle",shortName:"tag-rect",description:"Tagged process",aliases:["tagged-rectangle","tag-proc","tagged-process"],handler:Kle},{semanticName:"Paper Tape",name:"Flag",shortName:"flag",description:"Paper tape",aliases:["paper-tape"],handler:gce},{semanticName:"Odd",name:"Odd",shortName:"odd",description:"Odd shape",internalAliases:["rect_left_inv_arrow"],handler:Rle},{semanticName:"Lined Document",name:"Lined Document",shortName:"lin-doc",description:"Lined document",aliases:["lined-document"],handler:vle}],_Qe=o(()=>{let t=[...Object.entries({state:Vle,choice:doe,note:kle,rectWithTitle:Lle,labelRect:sle,iconSquare:ele,iconCircle:Koe,icon:joe,iconRounded:Qoe,imageSquare:rle,anchor:aoe,kanbanItem:Rce,mindmapCircle:Oce,defaultMindmapNode:Nce,classBox:kce,erBox:fO,requirementBox:Ece}),...RQe.flatMap(r=>[r.shortName,..."aliases"in r?r.aliases:[],..."internalAliases"in r?r.internalAliases:[]].map(i=>[i,r.handler]))];return Object.fromEntries(t)},"generateShapeMap"),pO=_Qe();o($ce,"isValidShape")});var LQe,W4,Fce=F(()=>{"use strict";$r();A2();Xt();vt();mO();Qt();Vr();Nn();Hd();H0();LQe="flowchart-",W4=class{constructor(){this.vertexCounter=0;this.config=Ae();this.diagramId="";this.vertices=new Map;this.edges=[];this.classes=new Map;this.subGraphs=[];this.subGraphLookup=new Map;this.tooltips=new Map;this.subCount=0;this.firstGraphFlag=!0;this.secCount=-1;this.posCrossRef=[];this.funs=[];this.setAccTitle=kr;this.setAccDescription=Rr;this.setDiagramTitle=Or;this.getAccTitle=Ar;this.getAccDescription=_r;this.getDiagramTitle=Lr;this.funs.push(this.setupToolTips.bind(this)),this.addVertex=this.addVertex.bind(this),this.firstGraph=this.firstGraph.bind(this),this.setDirection=this.setDirection.bind(this),this.addSubGraph=this.addSubGraph.bind(this),this.addLink=this.addLink.bind(this),this.setLink=this.setLink.bind(this),this.updateLink=this.updateLink.bind(this),this.addClass=this.addClass.bind(this),this.setClass=this.setClass.bind(this),this.destructLink=this.destructLink.bind(this),this.setClickEvent=this.setClickEvent.bind(this),this.setTooltip=this.setTooltip.bind(this),this.updateLinkInterpolate=this.updateLinkInterpolate.bind(this),this.setClickFun=this.setClickFun.bind(this),this.bindFunctions=this.bindFunctions.bind(this),this.lex={firstGraph:this.firstGraph.bind(this)},this.clear(),this.setGen("gen-2")}static{o(this,"FlowDB")}sanitizeText(t){return xt.sanitizeText(t,this.config)}sanitizeNodeLabelType(t){switch(t){case"markdown":case"string":case"text":return t;default:return"markdown"}}setDiagramId(t){this.diagramId=t}lookUpDomId(t){for(let r of this.vertices.values())if(r.id===t)return this.diagramId?`${this.diagramId}-${r.domId}`:r.domId;return this.diagramId?`${this.diagramId}-${t}`:t}addVertex(t,r,n,i,a,s,l={},u){if(!t||t.trim().length===0)return;let h;if(u!==void 0){let m;u.includes(`
+`)?m=u+`
+`:m=`{
+`+u+`
+}`,h=Qd(m,{schema:Zd})}let d=this.edges.find(m=>m.id===t);if(d){let m=h;m?.animate!==void 0&&(d.animate=m.animate),m?.animation!==void 0&&(d.animation=m.animation),m?.curve!==void 0&&(d.interpolate=m.curve);return}let f,p=this.vertices.get(t);if(p===void 0&&(r===void 0&&n===void 0&&i!==void 0&&i!==null&&Z.warn(`Style applied to unknown node "${t}". This may indicate a typo. The node will be created automatically.`),p={id:t,labelType:"text",domId:LQe+t+"-"+this.vertexCounter,styles:[],classes:[]},this.vertices.set(t,p)),this.vertexCounter++,r!==void 0?(this.config=Ae(),f=this.sanitizeText(r.text.trim()),p.labelType=r.type,f.startsWith('"')&&f.endsWith('"')&&(f=f.substring(1,f.length-1)),p.text=f):p.text===void 0&&(p.text=t),n!==void 0&&(p.type=n),i?.forEach(m=>{p.styles.push(m)}),a?.forEach(m=>{p.classes.push(m)}),s!==void 0&&(p.dir=s),p.props===void 0?p.props=l:l!==void 0&&Object.assign(p.props,l),h!==void 0){if(h.shape){if(h.shape!==h.shape.toLowerCase()||h.shape.includes("_"))throw new Error(`No such shape: ${h.shape}. Shape names should be lowercase.`);if(!$ce(h.shape))throw new Error(`No such shape: ${h.shape}.`);p.type=h?.shape}h?.label&&(p.text=h?.label,p.labelType=this.sanitizeNodeLabelType(h?.labelType)),h?.icon&&(p.icon=h?.icon,!h.label?.trim()&&p.text===t&&(p.text="")),h?.form&&(p.form=h?.form),h?.pos&&(p.pos=h?.pos),h?.img&&(p.img=h?.img,!h.label?.trim()&&p.text===t&&(p.text="")),h?.constraint&&(p.constraint=h.constraint),h.w&&(p.assetWidth=Number(h.w)),h.h&&(p.assetHeight=Number(h.h))}}addSingleLink(t,r,n,i){let l={start:t,end:r,type:void 0,text:"",labelType:"text",classes:[],isUserDefinedId:!1,interpolate:this.edges.defaultInterpolate};Z.info("abc78 Got edge...",l);let u=n.text;if(u!==void 0&&(l.text=this.sanitizeText(u.text.trim()),l.text.startsWith('"')&&l.text.endsWith('"')&&(l.text=l.text.substring(1,l.text.length-1)),l.labelType=this.sanitizeNodeLabelType(u.type)),n!==void 0&&(l.type=n.type,l.stroke=n.stroke,l.length=n.length>10?10:n.length),i&&!this.edges.some(h=>h.id===i))l.id=i,l.isUserDefinedId=!0;else{let h=this.edges.filter(d=>d.start===l.start&&d.end===l.end);h.length===0?l.id=eu(l.start,l.end,{counter:0,prefix:"L"}):l.id=eu(l.start,l.end,{counter:h.length+1,prefix:"L"})}if(this.edges.length<(this.config.maxEdges??500))Z.info("Pushing edge..."),this.edges.push(l);else throw new Error(`Edge limit exceeded. ${this.edges.length} edges found, but the limit is ${this.config.maxEdges}.
+
+Initialize mermaid with maxEdges set to a higher number to allow more edges.
+You cannot set this config via configuration inside the diagram as it is a secure config.
+You have to call mermaid.initialize.`)}isLinkData(t){return t!==null&&typeof t=="object"&&"id"in t&&typeof t.id=="string"}addLink(t,r,n){let i=this.isLinkData(n)?n.id.replace("@",""):void 0;Z.info("addLink",t,r,i);for(let a of t)for(let s of r){let l=a===t[t.length-1],u=s===r[0];l&&u?this.addSingleLink(a,s,n,i):this.addSingleLink(a,s,n,void 0)}}updateLinkInterpolate(t,r){t.forEach(n=>{n==="default"?this.edges.defaultInterpolate=r:this.edges[n].interpolate=r})}updateLink(t,r){t.forEach(n=>{if(typeof n=="number"&&n>=this.edges.length)throw new Error(`The index ${n} for linkStyle is out of bounds. Valid indices for linkStyle are between 0 and ${this.edges.length-1}. (Help: Ensure that the index is within the range of existing edges.)`);n==="default"?this.edges.defaultStyle=r:(this.edges[n].style=r,(this.edges[n]?.style?.length??0)>0&&!this.edges[n]?.style?.some(i=>i?.startsWith("fill"))&&this.edges[n]?.style?.push("fill:none"))})}addClass(t,r){let n=r.join().replace(/\\,/g,"\xA7\xA7\xA7").replace(/,/g,";").replace(/§§§/g,",").split(";");t.split(",").forEach(i=>{let a=this.classes.get(i);a===void 0&&(a={id:i,styles:[],textStyles:[]},this.classes.set(i,a)),n?.forEach(s=>{if(/color/.exec(s)){let l=s.replace("fill","bgFill");a.textStyles.push(l)}a.styles.push(s)})})}setDirection(t){this.direction=t.trim(),/.*/.exec(this.direction)&&(this.direction="LR"),/.*v/.exec(this.direction)&&(this.direction="TB"),this.direction==="TD"&&(this.direction="TB")}setClass(t,r){for(let n of t.split(",")){let i=this.vertices.get(n);i&&i.classes.push(r);let a=this.edges.find(l=>l.id===n);a&&a.classes.push(r);let s=this.subGraphLookup.get(n);s&&s.classes.push(r)}}setTooltip(t,r){if(r!==void 0){r=this.sanitizeText(r);for(let n of t.split(","))this.tooltips.set(this.version==="gen-1"?this.lookUpDomId(n):n,r)}}setClickFun(t,r,n){if(Ae().securityLevel!=="loose"||r===void 0)return;let i=[];if(typeof n=="string"){i=n.split(/,(?=(?:(?:[^"]*"){2})*[^"]*$)/);for(let s=0;s