Open tasks and design questions for the wiki system itself.
Subject-matter research belongs in question notes,
which Index and just query "type=question" enumerate.
A 2026-07-25 design session worked through the architecture items
and recorded conclusions inline below,
so the reasoning survives without needing the conversation.
Two findings reordered the list.
The frontend assets live inside bin/wiki.py as string literals,
which is the concrete cause of the recurring sense
that agents work in two systems at once.
And dist/wiki-data.js is 1.6MB against 1.86MB of source Markdown,
because its search field stores each note’s visible prose,
so what the build calls an index is very nearly a second copy of the vault.
Architecture
The static build’s ceiling is set by the eagerly loaded index,
not by note count and not by how the client is built.
Scale is a question about where rendering happens;
whether navigation is client-side is a separate question
about the state model,
and it does not change how much data reaches the browser.
At the measured 4.5KB per note,
the current design stays comfortable to roughly 1,000–1,500 notes
and becomes a problem past that.
Replacing the stored-prose field with a real inverted index,
and splitting metadata from bodies,
moves the ceiling well past 10,000
without giving up anything.
Past that, raising it further means chunking the index,
which forfeits offline search,
because a browser reading from file://
can inject a script but cannot fetch.
Reading the vault from disk is therefore a property with a scale limit,
not one the architecture preserves indefinitely.
- Extract the CSS and JS from
bin/wiki.pyinto real files undershare/assets/, read at build time and cached. The first pass took the two page-wide assets and left the force-graph, Mermaid, and Vega scripts behind, solayoutwas still over a thousand lines; a second pass took those three as well, bringing the function to 168 lines and the module from 5,569 to 3,842. What remains insidelayoutis the HTML page template, an f-string interpolating about a dozen Python values, which is structure rather than a foreign language. Every rendered page is byte-identical to the previous build. The literals had been escaped for Python, so nine double backslashes in the search tokenizer became single ones on the way out; searchingövergreppenandovergreppenboth return four notes, which exercises the diacritic-folding expression that carried them.node --checknow validates the script, which was not possible while it was a string. - Split
bin/wiki.pyinto modules along its existing seams: note and frontmatter I/O, link graph, search, render, lint, commands. Do this after the asset extraction, which removes most of the bulk. - Fix
masked_body_linesto mask inline code spans, not just fenced blocks, so a note can discuss the syntax it documents. Span contents are blanked rather than deleted to keep column offsets valid. Dataview’s inline query form is itself backtick-delimited, so that check and the fenced-block check read the unmasked line.wanted_linksturned out to scan the body directly rather than the masked lines, so quoted and fenced wikilinks were entering the research queue; it now scans the masked lines too. - Make
page_headingsfence-aware. It line-scanned the raw body, so a commented line inside a fenced block became a phantom heading, and becausedecorate_headingspairs that list with the rendered tags by position, one phantom shifted every later heading’s copy link onto the wrong heading. No note in the vault triggered it, so the fix is latent rather than user-visible. The structural fix below is still worth doing. - Move heading decoration into
WikiTreeprocessor.decorate_headingsregex-matches heading tags out of rendered HTML and zips them against a separately parsed heading list by position, so two independent parses have to stay in lockstep or a heading silently gets another heading’s copy link. The treeprocessor already exists and receives the real elements with their real identifiers, in order. - Consider a fenced-code-block formatter hook for the Mermaid and Vega fences, which are currently rewritten by regex over rendered HTML. Lower priority than the heading defect, since the substitution is contained.
- Reconsider the Markdown library only if Obsidian and the wiki
are observed to render the same source differently.
python-markdownis not CommonMark-compliant and Obsidian roughly is, so nested lists, emphasis boundaries, and raw HTML blocks are where any divergence would show up.markdown-it-pywould be the replacement, for CommonMark compliance and a token stream that transforms more cleanly than ElementTree, but a rewrite of the render path is not worth doing on its own. The current extension use is otherwise sound: the abbreviation glossary, the preprocessor and treeprocessor registration, and the calloutmarkdown="1"reprocessing all use the real library API rather than working around it. - Scope Playwright as an optional dependency
so
lint,build, andteststop resolving a browser automation library none of them can reach. It moved to adownloaddependency group, and the default environment went from fifteen packages to eleven. - Fix the failing round-trip test for
Securing schools, protecting minds.
The note was not at fault:
the YAML writer never set a width,
so ruamel’s 80-column default folded its
PRISMA-ScRexpansion onto a second line andjust fixwould have reformatted the note on any pass. Setting the width fixes every long frontmatter value at once, rather than shortening one expansion and leaving the next long one to hit the same edge. - Replace
blackwithruffand wire it intojust lintandjust fix.blackwas a dev dependency and a config block that no recipe ever invoked. Ruff formats near-identically, so adopting it cost no churn, and its linter immediately found a real defectblackcould not:bin/wiki.pyused a parenthesis-free multiple-exceptionexcept, which is Python 3.14 syntax, while the project declares support from 3.11. The file failed to parse on 3.11, 3.12, and 3.13 and only worked because the tooling pins a managed 3.14. Theblackconfig hadtarget-version = ["py314"], which is how the gap stayed invisible; the ruff target now tracksrequires-python. - Move the structural data out of
wiki-data.jsinto the pages that need it. Of the ten fields it publishes, only the prose field is about search; the rest are structure, and the browser recomputes the whole note graph and the tag co-occurrence graph from them on every page load, from data the build already held in memory and already derives forjust linksandjust clusters. Backlinks, the per-note neighborhood graph, and tag co-occurrence are all build-time facts. Baking them into each page makes a note work without JavaScript, and leaves search as the only thing needing a global payload. The blob exists because one global file sidesteps build-time invalidation, which was a fair trade at 408 notes and stops being one at 1.6MB. The dependency set is small and computable in the pass that already builds the edge set: a note’s page depends on its own content, the titles it links to, and the notes linking back to it. - Give the whole-vault graph its own script tag instead of letting every page carry its data. The graph lives on one page, so this needs no on-demand loading and costs nothing offline.
- Load the search payload on demand rather than on every page,
by injecting a script element rather than fetching.
A reader who never searches should not pay for the index,
but
fetchagainstfile://is CORS-blocked, so the deferred file has to stay a script that assigns to a global rather than a JSON document. That keeps offline search working at the cost of an awkward loader. - Replace the prose field in the search index with an inverted term index. Whether to chunk it is a fork, not a detail: chunking means fetching the postings for whatever terms were typed, which script injection cannot do responsively for an unbounded set of chunks, so a chunked index and offline search are mutually exclusive. Below the size where the index ships whole, keep it whole. Decide the fork when the measured index approaches that size, and record which property was chosen.
- Emit a metadata tier with a short excerpt per note, separate from bodies, to serve the sidebar, link resolution, and hover previews without shipping full prose.
- Decide between a single-page app and client-side navigation over prerendered pages. Either one fixes the problem worth fixing, which is that cross-note UI state such as sidebar scroll has to be rehydrated from localStorage on every load and flickers when it is. Two axes were conflated in the 2026-07-25 session and are worth keeping apart: where rendering happens decides scale, while how navigation works decides the state model, and only the first bears on how much data reaches the browser. The case for prerendered pages is that the build already does whole-vault work, so an app either duplicates it in JavaScript or consumes prerendered fragments and becomes the same thing, and that notes stay independently addressable documents. The case for the app is a cleaner state model. The deciding question is whether a note should remain shareable by link, indexable, and readable with JavaScript off. Note that this decision and the build-time structural data above pull in opposite directions: baking structure into pages makes each note more self-sufficient, while an app wants the global data resident to render anything, so doing the first well raises the cost of the second.
- Decide whether to port
bin/wiki.pyto a Node build script. This is a question about the build tool’s language, not about where rendering happens: a Node build readssrc/, resolves links across the whole vault, converts math ahead of time, and writesdist/, exactly as the Python one does. The case for it is one language instead of two and genuine code sharing, since the wikilink parser, the heading-slug rules, and the search scoring currently exist as a Python implementation and a JavaScript one that have to agree. The case against is mostlylatex2mathml, which has no clean JavaScript equivalent: KaTeX needs its own stylesheet and does not emit standalone MathML, MathJax is heavy, andtemmlis less proven, so every equation in the vault would need re-verifying. Round-trip-safe frontmatter and the existing tests would also need reproducing. Try the asset extraction first. The complaint that motivates this is largely that the stylesheet and the browser script live inside the Python as string literals, and moving the structural data to build time shrinks the client further, which weakens the code-sharing argument on both ends. - Make the whole-vault graph neighborhood-scoped rather than complete. A force simulation stops being interactive well before 10,000 nodes, and the ontology proposals below increase link density deliberately.
- Consider moving cluster graphs to index pages for each cluster.
- System for building and publishing a subset of the wiki.
- Add a git pre-commit hook that stamps
updatedfor staged notes. Revisits the closed answer below. That decision settled on stamping duringjust fix, but the--fixpath runs the mechanical fixes and stops short of frontmatter, so neither the closed decision nor any current command does what the decision claims, and the field has already drifted behind the actual edit date on notes touched without runningjust fix. A pre-commit hook fires on every commit, so the field is current at the moment the change goes in. Uncommitted edits still have to be stamped by hand, so decide whetherjust fixshould also stamp as a fallback for in-progress work, and whether the hook belongs in.git/hooks/or is checked in undershare/and wired byjust setup-git.
Settled in the 2026-07-25 session
- Should rendering move to the browser, with the build reduced to emitting data? No, but not because it cannot work. The browser already does whole-vault work: the note graph and the tag co-occurrence graph are both computed client-side over every note on each page load. What that demonstrates is the cost, not the impossibility. Wikilink resolution, heading maps, and graph markers would need the whole vault resident to render one page, which is the expense the build-time structural data task above exists to stop paying. Abbreviations and callouts are not whole-vault work at all: abbreviations come from the note’s own frontmatter and callouts are line-local. The argument that holds on its own is the math: converting to MathML at build time is why a note needs no math library at view time.
- Would server-side rendering scale better? Yes, without limit, but nothing on the roadmap needs it. A tiered index reaches the projected scale while keeping the zero-operations deploy, and keeps offline reading for as long as the index ships whole. Chunking would raise the ceiling further but costs offline search either way, so past that point the question is which property to keep, not which architecture to run. Revisit only if a live global query becomes necessary, which is the use case the Dataview exclusion already declines.
- Is
updatedin the frontmatter necessary when Git and mtime both exist? Keep the field, stop maintaining it by hand. Mtime does not survive clone, checkout, or worktree operations, and per-file Git calls are too slow for the renderer. Stamping it duringjust fixis the fix.
Renderer and reading experience
- Redesign against mockups. Shipped 2026-08-01 and 2026-08-02 from the D prototype in Wiki reading interface prototypes; Reading interface redesign proposals records what was adopted. The sidebar listing every note is gone, replaced by a summoned finder with facets, a landmark ribbon, a proportional section spine that drives the running head, sidenotes in the margin, a facts row, and a tabbed tail whose connections group by kind.
- Verify the redesign below 900 pixels. The ribbon becomes a bottom bar and the spine turns horizontal, and neither has been rendered at that width.
- Verify the local graph refits when its tab is first shown.
A graph built inside a hidden panel measures zero;
the fix is in
graph.jsbut has only been read, not seen. - Cover the scroll wiring with a test.
Three regressions in the redesign — a dropped
updateProgress, a staleh1lookup, and grid placement that collapsed the article — all passed the suite, because nothing asserts that the handler is attached or that its selectors still resolve. - Decide the header’s remaining mockup differences: whether to keep tags in it, and whether to add the action row (copy, provenance, argument ledger) the prototype carries.
- Change how section wikilinks render,
both local same-page sections and page-plus-section links.
[Graphs <span class="link-section">§</span> Child safety cluster](Graphs.html#child-safety-cluster)now renders asGraphs § Child safety cluster, and a same-page section link as§ Section, which is the ordinary cross-reference shape. A#is a URL fragment marker rather than a typographic one, and it set two names solid in the middle of a sentence. An em dash and a parenthesis were both considered and rejected: each carries sentence-level meaning, so mid-clause the reader parses it as punctuation of the sentence rather than as structure inside the link. The mark sits in a.link-sectionspan and is dimmed against the link colour, so the two names carry the label. - Preview pages and footnotes on hover. Cheap once the metadata tier carries excerpts. The prototype’s peek popovers are the model.
- Add support for various custom checkboxes?
- Add syntax highlighting for code blocks.
- Enforce six spaces of indent for task lists? Make rendered wiki align text similarly?
- Breadcrumbs tracing each note’s linked path from the Index.
The type mark replaced the
Library /breadcrumb, which named no real parent in a flat vault; a linked path would be a different thing. - Allow the rendered title to differ from the note name.
The link resolver never read the H1:
build_resolverkeys on the filename stem andaliases, andNote.titlereturns the stem, so the identity rule was enforced by lint and consumed by nothing. The H1 was also redundant on both surfaces, since Obsidian shows the filename as a header and the wiki rendered an H1 from the body, which is why a note displayed its title twice. Titles now live in frontmatter astitle, with an optionalsubtitle, and the body starts at##. rumdl enforces the choice with no custom rule: it treats frontmattertitleas a top-level heading, soMD041 level = 2rejects a body H1 andMD025rejects carrying both. Wikilinks still target the filename, as Obsidian and Markdown Oxide require, soNote.titlestays the stem and no link text changed.titleandsubtitleare indexed for search so a real title stays findable from a shortened filename. All 413 notes migrated, andjust newandjust renamenow take a free-form title and derive the stem, so the shortening that lost information is no longer done by hand. - Repair the note names the old rule deformed,
and backfill
titleandsubtitlewhere the real title was lost. Fourteen notes were renamed and the titles restored on 22: works truncated at their colon, comma splices standing in for a colon, em dashes flattened to a spaced hyphen, and citation identifiers stripped of the punctuation that made them citations, soSOU 2024 75andC-136 17areSOU 2024:75andC-136/17again in their titles and close toSOU 2024-75andC-136-17in their handles. Source notes were worst affected, because they name external works whose titles are not ours to choose.cmd_renamewas also quotingupdated, which it had done since long before this pass; 15 notes carried a quoted date and no longer do. - Split the source notes that bundle unrelated issuers.
A bundle is legitimate when several artifacts
document one subject at one moment,
as Redact.dev official product and policy pages does
for one vendor’s product, policy, terms, and pricing;
splitting those would create four notes
that co-occur in every citation using them.
It stops being legitimate when the artifacts share only a topic.
Swedish ecommerce compliance guidance holds ten pages
from eight issuers spanning consumer law, VAT, packaging,
radio jammers, knives, and chemicals,
so a note citing it for a VAT claim
points a reader at nine documents that do not support it.
Its
sourceslist also runs one entry ahead of its body links. Privacy gear market scan (14 artifacts, 14 hosts), Cryptocurrency protocol documentation (6 and 6), and Swedish privacy voucher compliance guidance (5 and 4) are the other candidates. Multi-host alone is not the signal: a paper plus its DOI landing page is two hosts and one work. - Check the eight source notes with no local artifact.
At least one is legitimate:
Securing schools, protecting minds records that PMC
fronts the PDF with a proof-of-work challenge,
which is the fallback
AGENTS.mdprescribes. No source note has a broken artifact link, so this is a gap in capture rather than drift. Titles and subtitles are now repaired on 22 notes, and all 17 question titles carry their question mark again, so what remains is the filenames. A vault-wide audit flags 20 stems: four comma splices, two flattened em dashes, six citation identifiers, and eight source handles that truncate a work’s title at its colon. Each rename rewrites wikilinks across the vault and leaves the old stem as an alias, so propose the list before executing it and drop the aliases that are only truncations. - Record in
AGENTS.mdthat a title and its handle refer to the same subject but need not be the same string. A first pass assumed they should differ only in punctuation, which is right for concept notes and wrong everywhere else: a question note wants a nominal handle and an interrogative title, and a source note wants a short handle and the external work’s full title. The failure worth naming is not divergence but drift, where the title names a different subject than the handle, sojust searchand the rendered page disagree about the note. That is not mechanically checkable, so state the convention and add no lint rule: a stem-substring check would reject the good cases.
Ontology redesign
Ontology redesign proposals records the 2026-07-23 design session. Nothing there is adopted, and its open decisions stay in that note. The tasks here are the ones with scheduling consequences.
- Run the extraction pilot by hand on the evidence-law cluster before writing any skill, and encode the decisions the pilot actually produces.
- Decide the
type-to-kindswap early if it is happening at all. The sweep touches frontmatter, lint, queries, templates, tests, the skills, and agent memory files, and its cost is linear in note count and instruction surfaces. Staletype=references in skills fail silently rather than under lint, so the sweep must cover them explicitly. - Cost the ratchet pass against re-reading rate, not note count. Current syntheses carry note-level provenance with little claim-level mapping, so extraction is a claim-attribution pass first and a moving-citations pass second.
- Write a
distill-synthesesskill, after the pilot. - Consider adding a
description,abstract, orsummaryfield to frontmatter.
Agent workflow
- Add a workflow for searching for news from e.g. the past week, relevant to topics covered by the wiki, and integrate interesting sources. This is the only item that grows the vault rather than maintaining it.
- Research claims against F-Droid’s security model more deeply.
The claim is that F-Droid signs nearly every app on the main repo,
with outdated build infra and poor moderation,
creating serious security issues.
Done 2026-07-31.
All nine listed sources were read;
the blocked ones were recovered through the Wayback Machine
(the GrapheneOS tweet)
and a crawler view (the forum thread).
archive.ph/j7qql turned out to be
fdroid-website MR 834,
the July 2022 permissions-blog discussion
where the critique’s misleading-permissions point
was conceded in substance
and the F-Droid–GrapheneOS feud escalated;
it is imported as fdroid-website mr 834.
Findings:
the certificate-pinning claim is real and worse than described —
six demonstrated
AllowedAPKSigningKeysbypasses over 2023–2025, fixed in fdroidserver 2.3.5, root cause unreworked through 2.4.4 — but never observed exploited, with new installs ofBinaries:apps as the blast radius. “Signs nearly every app” is down to about two thirds and falling. “Outdated build infra” is a recurring-lag pattern (AGP 8.12 blocked dozens of apps in mid-2025) whose named instance was retired in 2025. “Poor moderation” resolves to automated scanning rather than review, in both directions: a false-positive uninstall recommendation (Shattered Pixel) and a policy-violating updater shipped for six months (WireGuard). The one realized supply-chain incident (Nextcloud News) was dependency poisoning that left source correspondence intact. New source notes: fdroidserver pinning bypass disclosures, wireguard inclusion policy violation, Malware in F-Droid build of Nextcloud News App; synthesis landed in F-Droid, Android app distribution trust models, and PrivSec.dev on F-Droid security issues. Sources: https://discuss.grapheneos.org/d/18731-f-droid-vulnerability-allows-bypassing-certificate-pinning/21, https://xcancel.com/GrapheneOS/status/1883895255142932816#m, https://archive.ph/j7qql#note_1025646994, https://privsec.dev/posts/android/f-droid-security-issues/, https://codeberg.org/ironfox-oss/bugs/issues/7, https://www.openwall.com/lists/oss-security/2024/04/08/8, https://github.com/CatimaLoyalty/Android/issues/2608, https://gitlab.com/fdroid/admin/-/issues/593, https://github.com/00-Evan/shattered-pixel-dungeon/issues/1394. - Research Rust and secure Android app development.
- Research setting up secure sandboxes for AI agents.
- Decide whether to reserve
questionnotes for unresolved questions that arise during research, with ideas for new research topics instead placed in the todo, or if the latter should also getquestionnotes. - Create a skill for assisting with writing essay notes, where the point is understanding the user’s idea rather than doing any deep research or ensuring factuality. Agents should ask follow-up questions for anything the user didn’t sufficiently specify, or for gaps in the general idea, that the agent needs for a complete write-up, or to explicitly note the gap. They should reference existing notes where relevant, and create redlinks for anything that would make for a useful note to add, and they should create question notes for research that would enable a grounded synthesis to evaluate the essay’s idea.
- Make agent-written notes use richer markup and more redlinks.
The prose is lean: most pages are paragraphs with the occasional list,
and the structural forms the wiki supports
(tables, Mermaid diagrams, callouts, block quotes, Vega-Lite charts)
appear in a small minority of notes.
The gap is execution, not instruction.
The renderer supports them,
the agent prompt advertises them,
and
AGENTS.mddevotes a section to “Choose structural forms by meaning.” Treat that list as active choices on every pass. Redlinks are the same kind of underuse.AGENTS.mdand the agent prompt say to link independently nameable subjects liberally, andjust wantedreports a queue of unresolved links that surrounding prose could have named without any new research. Both are habits, not gaps in the rule, and both are observable in notes written this year. - Consider creating subagents or skills for post-edit review.
- Make a skill for producing reports based on the knowledge base?
- Define what makes a source worth preserving under
src/sources/.AGENTS.mdsays “worth keeping” without saying what that means, which is the actual gap behind the download, link, and footnote question: those three are not alternatives. Download is durability, an external link is reachability, and a footnote is claim-level attribution, and one source often warrants all three. - Tighten the file-naming rules in
AGENTS.md. The filename is a handle, not the title: short, typeable, Windows-safe, and unambiguous, and not required to reproduce the work’s title now thattitlecarries that. The rules name the substitutions to stop making: no comma standing in for a colon, no em dash flattened to a spaced hyphen, no diacritics dropped, sinceä,ö, andåare legal and already appear in 27 names, and no citation identifier with its punctuation punched out.stem_for_titlenow implements this, so the rule is executable rather than advisory. - Decide where per-note provenance lives,
and whether that needs a frontmatter field.
The gap appeared on 2026-07-28.
Desktop operating system security comparison rests on
project self-documentation because no independent evaluation exists,
and establishing that took a scholarly sweep
across several indexes and query framings.
The note carried the method for an hour
before it was cut as process narration,
which was right for the reader
and lost the thing that makes the absence checkable:
which engines were searched, and what was excluded.
That detail now sits in
Log.md, which is chronological and not indexed by note. A future agent doubting the absence has no route from the claim to the evidence for it. Before adding a field, settle three things. WhetherLog.mdbacklinks already close the gap: its entries wikilink the notes they touch, but it links most of the vault, so the backlink may be present and useless. Whether the field would render, since anything appearing in the page recreates the problem that motivated the cut. And what the field is actually for, becausenotesis the least specific name available in a wiki made of notes, and a junk drawer is the predictable outcome: the candidates are a dated record of what was checked and not found, which is narrow enough to namecheckedorverified, versus general editorial commentary, which is not. The bar is the oneAGENTS.mdalready sets after thesubtypeepisode: propose a field only with evidence that some class of note needs different handling. One instance is not that evidence. Collect the cases first — negative findings are the likeliest recurring class, since What is the production false-positive rate of automated CSAM detection is the same shape and solved it by being a question note. - Add a convention for an inbox type dir?
- Reuse the Smart Connections embeddings? Scope this before committing: reading a plugin’s private vector cache carries the same portability problem as Dataview.
Obsidian feature scope
The 2026-07-25 session replaced the blanket exclusion with a narrower and more defensible rule: a note must stay readable as Markdown and renderable without the plugin that produced it. Client-side JS in the static renderer makes more of these viable than the original framing assumed.
- Record the scope rule in
AGENTS.mdso the question stops recurring. - Decide on Canvas and Kanban. Both pass the rule. Canvas is a documented JSON sidecar, and the Kanban board format is standard Markdown lists with a small metadata comment.
- Decide on task due dates and recurring tasks. These are plain text inside a standard task line, so they need no plugin to read and break no invariant.
- Should Dataview be supported? No. Its queries evaluate against a live index, so a static build would have to reimplement the query language, and notes carrying DQL stop being readable as prose. This is the case that earns the constraint.
- Should Excalidraw be supported? Not now. The scene is embedded JSON and could render in principle, but the library needed to do it is heavy, and Mermaid already covers diagrams and mind maps.
Done
- What happens to a question note once it is answered?
- Scan vault for further potential question notes to create
- Did the commit “Queue the pornography cluster’s missing counter-evidence as questions” miss the point?
- run
/maintain-tags - run
/audit-structure - Per-note link graphs?
- Use the Git trailers to attribute each AI author in the rendered wiki, perhaps by percentage of contribution? Might be useful for discovering which AI’s writing style one likes best.
- Document the wiki architecture and design choices as notes in the wiki itself
This note cites no sources of its own.
Working out connections…
No sources are recorded.
Working out the neighbourhood…
Model contributions
Measured by git-blame lines per AI model (645 total).
{"width": 320, "height": 320, "data": {"values": [{"model": "Claude Opus 5", "label": "Claude Opus 5 (85%)", "lines": 546, "share": 0.8465116279069768}, {"model": "MiniMax M3", "label": "MiniMax M3 (7%)", "lines": 42, "share": 0.06511627906976744}, {"model": "Kimi K3", "label": "Kimi K3 (6%)", "lines": 40, "share": 0.06201550387596899}, {"model": "DeepSeek V4 Pro", "label": "DeepSeek V4 Pro (3%)", "lines": 17, "share": 0.02635658914728682}]}, "mark": {"type": "arc"}, "encoding": {"theta": {"field": "lines", "type": "quantitative"}, "color": {"field": "label", "type": "nominal", "legend": {"title": null, "orient": "right"}}, "tooltip": [{"field": "model", "type": "nominal"}, {"field": "lines", "type": "quantitative"}, {"field": "share", "type": "quantitative", "format": ".1%"}], "order": {"field": "lines", "type": "quantitative", "sort": "descending"}}}