anyplot follows a plot-centric repository pattern where everything for one plot type lives in a single directory:
plots/{specification-id}/
├── specification.md # Description, Applications, Data, Notes, What a good version looks like
├── specification.yaml # Spec-level metadata (tags, created, issue, suggested)
├── metadata/ # Per-library metadata (one file per library), grouped by language
│ ├── python/matplotlib.yaml
│ └── ...
└── implementations/ # Library implementations, grouped by language
├── python/matplotlib.py
├── r/ggplot2.R
├── julia/makie.jl
├── javascript/d3.js
└── ...
Key Principle: The repository is the source of truth for all code, specs, and quality data. Preview images are stored in GCS. Database is derived via sync.
Key Benefit: Per-library metadata files eliminate merge conflicts when multiple implementations are generated in parallel.
anyplot/
├── plots/ # Plot-centric directories
│ ├── scatter-basic/ # Everything for basic scatter plot
│ │ ├── specification.md # Library-agnostic specification
│ │ ├── specification.yaml # Spec-level metadata (tags, created, issue)
│ │ ├── metadata/ # Per-library metadata, by language
│ │ │ ├── python/ # matplotlib.yaml, seaborn.yaml, …
│ │ │ ├── r/ # ggplot2.yaml
│ │ │ ├── julia/ # makie.yaml
│ │ │ └── javascript/ # chartjs.yaml, d3.yaml, …
│ │ └── implementations/ # Library-specific code, by language
│ │ ├── python/ # matplotlib.py, seaborn.py, plotly.py, bokeh.py,
│ │ │ # altair.py, plotnine.py, pygal.py, letsplot.py
│ │ ├── r/ # ggplot2.R
│ │ ├── julia/ # makie.jl
│ │ └── javascript/ # chartjs.js, d3.js, echarts.js, highcharts.js, muix.tsx
│ │
│ ├── bar-basic/
│ │ ├── specification.md
│ │ ├── specification.yaml
│ │ ├── metadata/
│ │ └── implementations/
│ │ └── ...
│ │
│ └── heatmap-correlation/
│ ├── specification.md
│ ├── specification.yaml
│ ├── metadata/
│ └── implementations/
│ └── ...
│
├── prompts/ # AI agent prompts
│ ├── plot-generator.md # Base rules for code generation
│ ├── adaptable-code.md # The Data block convention every implementation follows
│ ├── quality-criteria.md # Quality evaluation criteria
│ ├── quality-evaluator.md # AI quality evaluation prompt
│ ├── spec-id-generator.md # Assigns spec IDs
│ ├── default-style-guide.md # Default visual style rules
│ ├── library/ # Library-specific rules (15 files: 8 Python + ggplot2 + makie + 5 JavaScript)
│ │ ├── matplotlib.md
│ │ ├── seaborn.md
│ │ └── ...
│ ├── templates/ # Templates for new specs
│ │ ├── specification.md
│ │ └── specification.yaml
│ └── workflow-prompts/ # Workflow-specific prompts
│ └── ...
│
├── core/ # Shared business logic
│ ├── __init__.py
│ ├── adaptability.py # Adaptability checker (adaptable-code convention)
│ ├── adaptability_mirror.py # Builds the checker's single-file mirror
│ ├── canvas.py # Canvas gate + PNG auto-reject checks
│ ├── config.py # Configuration (.env-based)
│ ├── constants.py # Library metadata, constants
│ ├── defects.py # Review feedback grammar (defect lines)
│ ├── images.py # Image processing utilities
│ ├── library_names.py # The names people write for a library (d3, chart js, mui x)
│ ├── perturbation.py # Perturbed tables for the smoke run
│ ├── render_identity.py # Pixel difference between two renders
│ ├── smoke_run.py # Perturbation smoke run, render pair, generator self-check
│ ├── spec_roles.py # Data roles, data shape and color role of a spec
│ ├── utils.py # General utilities
│ ├── database/ # Database layer
│ │ ├── __init__.py
│ │ ├── connection.py # Async connection management
│ │ ├── models.py # SQLAlchemy ORM models
│ │ ├── repositories.py # Repository pattern
│ │ └── types.py # Custom SQLAlchemy types
│
├── api/ # FastAPI backend
│ ├── __init__.py
│ ├── main.py # Application entry point
│ └── Dockerfile # Cloud Run deployment
│
├── agents/ # Agent network on Google ADK (see concepts/agent-network.md)
│ ├── main.py # anyplot-agents service: the private /v1 API around the ADK runner
│ ├── stream.py # anyplot/1 SSE translator and output sanitiser
│ ├── Dockerfile # anyplot-agents image: agents extra, source tree with prompts and fonts, uid 1000
│ ├── cloudbuild.yaml # Build, candidate deploy, IAM and /v1/status smoke, promote; owner steps in its header
│ ├── evals/ # Model-regression harness (see agents/README.md)
│ │ ├── matrix.py # Runs eval cases through /v1 in process; report, diff, gallery
│ │ ├── make_fixtures.py # Generates the synthetic spike-X cases, 10 specs x 6 datasets per library (seeded, rerunnable)
│ │ ├── cases.py # Case loading and --cases selection
│ │ ├── pricing.py # List prices and the cost of one model call
│ │ ├── report.py # Summary, baseline diff, Markdown, galleries; blind two-run gallery
│ │ ├── eligibility.py # Model-free sweep: which catalogue files can be adapted, and why not
│ │ ├── baselines/ # Committed baseline reports, one per model
│ │ ├── fixtures/cases/ # Eval cases (case.json + data.csv), also the adk web seed
│ │ └── reports/ # Run reports, renders and galleries (git-ignored)
│ ├── renderer/ # anyplot-renderer service: runs the code in Cloud Run sandboxes, no ADK import
│ │ ├── main.py # POST /render and GET /status behind the IAM caller check, one render slot
│ │ ├── executor.py # sandbox do per theme: kill path, byte watchdog, launcher retry, bounded output
│ │ ├── auth.py # Caller check on the IAM-forwarded ID-token claims
│ │ ├── settings.py # RendererSettings (RENDERER_* variables)
│ │ ├── wire.py # JSON contract shared with the agents' remote render backend
│ │ ├── Dockerfile # Renderer image: plotting venv, Noto fallback fonts, baked font cache, harness
│ │ └── cloudbuild.yaml # Build, candidate deploy, IAM and render smoke, promote
│ └── anyplot/
│ ├── agent.py # Root agent "anyplot", ALL_AGENTS registry, App with plugins
│ ├── models.py # The only model and client factory (Claude on Vertex AI, Gemini)
│ ├── policy.py # Instructions composed from prompts/ and the catalogue prompts
│ ├── pipeline.py # plot_pipeline node: adapt, check, render, review, repair
│ ├── prompts/ # root, adapter, reviewer, scope and dataset judge prompts, refusals
│ ├── sub_agents/ # Per-library adapters and the tool-less reviewer
│ ├── tools/session.py # Root's session tools and the plot_pipeline NodeTool
│ ├── plugins/ # ScopeGuard, Budget, ToolSafety and the request ledger
│ ├── render/ # Render contract, harness, host gates, PNG hardening, render store, backends (remote, local, fake)
│ │ ├── watermark.py # Footer strip on served PNGs ("made with any.plot()", "anyplot.ai/<spec-id>")
│ │ └── fonts/ # JetBrains Mono 2.304 Regular + Bold (SIL OFL 1.1, unmodified) and their notice
│ ├── schemas.py # Pydantic contracts of the plot pipeline
│ ├── settings.py # AgentSettings (AGENT_* variables)
│ ├── data/ # Deterministic data handling, no ADK import
│ │ ├── parse.py # Pasted text -> canonical data.csv + DatasetProfile
│ │ ├── bindings.py # Default role-to-column bindings and their checks (roles: core/spec_roles.py)
│ │ └── store.py # Session-scoped in-memory dataset store
│ └── code/ # Deterministic code handling, no ADK import
│ ├── validate.py # Two-profile AST validator: SECURITY and ADAPTATION
│ ├── regions.py # AST spans: THEME block, palette, placeholder, savefig, canvas
│ ├── normalise.py # Header, title, sys.path guard, savefig target, exact canvas
│ ├── readiness.py # blocked / coupled / clean scan with adapter hints
│ ├── edits.py # Applies an AdaptPlan; protected regions
│ ├── loader.py # load_user_data() placeholder -> pd.read_csv("data.csv")
│ └── export.py # Attribution header for the downloaded plot.py
│
├── app/ # React frontend
│ ├── src/
│ │ ├── components/
│ │ ├── pages/ # AgentChatPage.tsx: the agent chat (/agent, visitors and admins)
│ │ ├── sections/ # agent-chat/: data panel, thread, result card; debug/: agent case triage on /debug
│ │ └── lib/ # api.ts; agent.ts and sse.ts for the agent chat BFF and its case routes
│ ├── scripts/
│ │ └── agent-bff-mock.mjs # Local mock of the agent chat BFF (no API, no model)
│ ├── package.json
│ └── Dockerfile
│
├── automation/ # Workflow automation
│ ├── gcp/budget-guard/ # Cloud Run function: disables Vertex AI at its budget
│ └── scripts/ # Workflow-specific utilities
│ ├── sync_to_postgres.py # Sync plots/ to database
│ ├── workflow_utils.py # Utilities for GitHub Actions
│ ├── label_manager.py # Label operations
│ └── workflow_cli.py # CLI for workflows
│
├── tests/ # Test suite
│ ├── conftest.py # Shared fixtures
│ ├── unit/ # Fast, mocked tests
│ │ ├── api/
│ │ ├── core/
│ │ └── ...
│ ├── integration/ # SQLite in-memory tests
│ └── e2e/ # Real PostgreSQL tests
│
├── .github/
│ └── workflows/ # GitHub Actions CI/CD
│ ├── spec-create.yml # Creates specification (branch → PR → approval)
│ ├── impl-generate.yml # Generates single library implementation
│ ├── impl-review.yml # AI quality evaluation
│ ├── impl-repair.yml # Repairs rejected implementation
│ ├── impl-merge.yml # Merges approved implementation
│ ├── bulk-generate.yml # Batch operations (multiple specs/libraries)
│ ├── sync-postgres.yml # Sync to database on push to main
│ └── ...
│
├── alembic/ # Database migrations
│ └── versions/
│
├── infra/ # Infrastructure that lives outside the code
│ └── cloudflare/ # Source for the apex Worker + its rollout notes
│
├── scripts/ # One-time and manual scripts
│ ├── site-smoke.mjs # Does the site render? After every merge, and in CI
│ ├── evaluate-plot.py # Manual plot evaluation
│ ├── regenerate-thumbnails.py # Image processing
│ ├── backfill_review_metadata.py # One-time migration
│ ├── fix_library_versions.py # One-time fix
│ ├── migrate_metadata_format.py # One-time migration
│ └── migrate_to_new_structure.py # One-time migration
│
├── docs/ # Documentation
│ ├── concepts/
│ ├── reference/
│ ├── workflows/
│ ├── contributing.md
│ └── index.md
│
├── pyproject.toml # Python project config (uv)
├── uv.lock # Dependency lock file
├── .env.example # Environment variables template
├── CLAUDE.md # AI assistant instructions
└── README.md
Purpose: Plot-centric directories containing everything for one plot type
Structure:
plots/{specification-id}/
├── specification.md # Library-agnostic specification
├── specification.yaml # Spec-level metadata (tags, created, issue, suggested)
├── metadata/ # Per-library metadata, grouped by language
│ ├── python/matplotlib.yaml # Quality score, preview URL, generation history
│ └── ...
└── implementations/ # Library-specific implementations, grouped by language
├── python/matplotlib.py
├── javascript/muix.tsx
└── ...
Characteristics:
- ✅ Self-contained (spec + metadata + code + quality reports together)
- ✅ One folder per plot type keeps navigation predictable
- ✅ Synced to PostgreSQL via
sync-postgres.yml - ✅ No merge conflicts (per-library metadata files)
- ✅ Quality reports in
metadata/{language}/{library}.yaml(review section) - ❌ NO preview images (stored in GCS)
Example: plots/scatter-basic/ contains everything for the basic scatter plot.
Purpose: Library-agnostic plot specification
Contents:
- Title and description
- Data requirements (columns, types)
- Use cases with domain context
- Visual requirements
Naming: Always specification.md (consistent across all plots)
Purpose: Spec-level metadata synced to PostgreSQL
Contents:
spec_id: scatter-basic
title: Basic Scatter Plot
created: 2025-01-10T08:00:00Z
updated: 2025-01-15T10:30:00Z
issue: 42
suggested: CoolContributor123
tags:
plot_type: [scatter, point]
domain: [statistics, general]
features: [basic, 2d]
data_type: [numeric]Key Points:
- Tags are at spec level (same for all libraries)
suggestedcredits the contributor who proposed the specupdatedtimestamp set on any change (git history for details)
Purpose: Per-library metadata (one file per library, in the folder of its language: python, r, julia,
or javascript)
Contents:
library: matplotlib
language: python
specification_id: scatter-basic
# Timestamps
created: 2025-01-10T08:00:00Z
updated: 2025-01-15T10:30:00Z
# Generation. An adopted file names the model that wrote it and its source
# instead, for example:
# generated_by: gemini-4-argon
# adopted_from: gemini/muix-9 (<commit sha>)
generated_by: claude-opus-4-7
workflow_run: 12345678
issue: 42
# Versions: the runtime of the implementation's language, and the library
language_version: 3.13.14
library_version: 3.11.0
# Previews, one per theme (the HTML variants only for interactive libraries)
preview_url_light: https://storage.googleapis.com/anyplot-images/plots/scatter-basic/python/matplotlib/plot-light.png
preview_url_dark: https://storage.googleapis.com/anyplot-images/plots/scatter-basic/python/matplotlib/plot-dark.png
preview_html_light: null
preview_html_dark: null
# Quality
quality_score: 92
# Review feedback (used by AI for regeneration)
review:
# AI's visual description of the generated plot
image_description: |
The plot shows a scatter plot with 100 data points displaying
a positive correlation. Points are rendered in blue with 70%
opacity. Axes are clearly labeled and a subtle grid is visible.
# Detailed scoring breakdown by category
criteria_checklist:
visual_quality:
score: 36
max: 40
items:
- id: VQ-01
name: Text Legibility
score: 10
max: 10
passed: true
comment: "All text readable at full size"
- id: VQ-02
name: No Overlap
score: 8
max: 8
passed: true
comment: "No element overlap detected"
spec_compliance:
score: 23
max: 25
items: [...]
data_quality:
score: 18
max: 20
items: [...]
code_quality:
score: 10
max: 10
items: [...]
library_features:
score: 5
max: 5
items: [...]
# Final verdict
verdict: APPROVED
# Summary feedback
strengths:
- "Clean code structure"
- "Good use of alpha for overlapping points"
weaknesses: # defect lines first, then at most three "Suggestion:" lines
- "VQ-01 (both): tick labels at 8 pt are hard to read at full size → about 10 pt (+2 pt). Likely cause: tick_params labelsize=8."
- "Suggestion: a slightly lighter grid would recede further"
# Provenance of the review (filled by impl-review, not synced to the database)
model: claude-opus-5-5 # resolved model ID, not the alias
criteria_version: qc-1f3a9c0b2d.aqr-8e41d7a6c5.sg-0b9d2e7f41.lib-5c6a1e8d93
rendered_at: "2026-09-27T12:04:31Z" # creation time of the reviewed renderKey Points:
- Each library has its own file (no merge conflicts!)
- Created by
impl-generate.yml, updated byimpl-review.yml - Review feedback persisted for AI to improve on regeneration
- A kept regeneration replaces
quality_scoreand the review with its re-score of the live implementation, through areview-writebackPR thatimpl-merge.ymlmerges;updated,review.rendered_at, andimpl_tagsstay, because the code didn't change (see Stored review on a keep) - Extended review data includes
image_description,criteria_checklist, andverdictfor targeted fixes review.model,review.criteria_version(git blob IDs ofprompts/quality-criteria.md,ai-quality-review.md,default-style-guide.md, and the library prompt), andreview.rendered_atrecord which model and rules produced the review; they aren't synced to the database. A value the review run can't determine is left out (removed if an earlier review wrote it), never filled with the model alias
Preview images are stored in Google Cloud Storage (not in repo):
gs://anyplot-images/
├── plots/{spec-id}/{library}/ # Production (after merge)
│ ├── plot.png # Full-size optimized image
│ └── plot.html # Optional (interactive libraries)
│
└── staging/{spec-id}/{library}/ # Temp (during review)
└── plot.png, plot.html
Interactive libraries (.html): plotly, bokeh, altair, highcharts, pygal, letsplot
Image Processing: Images are optimized with pngquant. Branding is included in the plot title itself (e.g., scatter-basic · matplotlib · anyplot.ai).
Purpose: Library-specific implementations, organized by language
Layout:
plots/{spec-id}/implementations/
├── python/ # 8 Python libraries (.py)
├── r/ # ggplot2 (.R)
├── julia/ # Makie.jl (.jl)
└── javascript/ # 5 JavaScript libraries (.js, MUI X as .tsx)
File Naming (by language):
Python (python/, .py):
matplotlib.pyseaborn.pyplotly.pybokeh.pyaltair.pyplotnine.pypygal.pyletsplot.py
R (r/, .R):
ggplot2.R
Julia (julia/, .jl):
makie.jl
JavaScript (javascript/):
chartjs.jsd3.jsecharts.jshighcharts.jsmuix.tsx(MUI X Charts, a React module)
Code Style (KISS):
""" anyplot.ai
scatter-basic: Basic Scatter Plot
Library: matplotlib 3.10.0 | Python 3.14
Quality: 92/100 | Created: 2025-01-10
"""
import matplotlib.pyplot as plt
import numpy as np
# Data
np.random.seed(42)
x = np.random.randn(100)
y = x * 0.8 + np.random.randn(100) * 0.5
# Plot
fig, ax = plt.subplots(figsize=(16, 9))
ax.scatter(x, y, alpha=0.7, s=50)
ax.set_title('Basic Scatter Plot')
plt.tight_layout()
plt.savefig('plot.png', dpi=300)Header Format (4 lines):
""" anyplot.ai- Branding{spec-id}: {Title}- IdentificationLibrary: {lib} {version} | Python {version}- VersionsQuality: {score}/100 | Created: {date}- Quality + date
Rules:
- No functions, no classes
- No
if __name__ == '__main__': - Only imports → data → plot → save
Purpose: AI agent prompts for code generation and quality evaluation
Subdirectories:
library/- Library-specific rules (15 files: 8 Python + ggplot2 + makie + 5 JavaScript)templates/- Templates for new specs (specification.md,specification.yaml)workflow-prompts/- Workflow-specific prompt templates
Files:
plot-generator.md- Base rules for all implementationsadaptable-code.md- The Data block convention: one replaceable block that ends indf, columns named once, plot code derived fromdfquality-criteria.md- Definition of qualityquality-evaluator.md- AI quality evaluationspec-id-generator.md- Assigns spec IDsdefault-style-guide.md- Default visual style rules
Purpose: Shared business logic and reusable components
Key Components:
database/connection.py- Async database connectiondatabase/models.py- SQLAlchemy ORM modelsdatabase/repositories.py- Repository pattern for data accesscanvas.py- Canvas gate (3200×1800 or 2400×2400 within ±16 px, the VQ-05 defect line) and the PNG auto-reject checks AR-04 and AR-07;python -m core.canvas <png>runs the gatedefects.py- Review feedback grammar: defect and suggestion lines, the criterion IDs, spec characteristics, andformat_defect;automation/scripts/regen_gate.pykeeps a parity-tested copyspec_roles.py- Data roles from a spec's## Databullets, the data shape (table, matrix, network, generated, excluded) and the color-driving role; shared by the agent and the adaptability checkerlibrary_names.py- The names people write for each catalogue library (any case, with or without.jsor.jl, any separator, the short forms such asggplotandmui x) and for the tools outside the catalogue; the agent chat's scope guard and itsswitch_plottool read library names through itadaptability.py- Adaptability checker for Python implementations (Adaptable catalogue code): the record, the CQ-01 defect lines, and the block and constant substitution;python -m core.adaptability report plots/prints the catalogue baselineadaptability_mirror.py- Buildsautomation/scripts/adaptability_check.py, the checker as one standard-library file;uv run python -m core.adaptability_mirrorafter a change toadaptability.pyorspec_roles.pyperturbation.py- The perturbation smoke run's tables (renamed, more and fewer categories, one group, fewer and more rows, scaled values, a missing value in the loader's dtypes) and the source to run on eachrender_identity.py- Share of differing pixels between two renders against a bound (0.5 % per theme);python -m core.render_identity old.png new.pngsmoke_run.py- Runs an implementation:runexecutes it on each perturbed table in both themes,pairrenders a new source and its predecessor in one environment and compares them, andselfcheckis the generator's Step 3c (static check plus smoke run, logged per attempt);python -m core.smoke_run run plots/{spec}/implementations/python/{library}.py
Purpose: Workflow-specific automation used by GitHub Actions
Key Components:
scripts/sync_to_postgres.py- Database sync (used by sync-postgres.yml)scripts/workflow_utils.py- Parsing and utilities for workflowsscripts/label_manager.py- Label operations and transitionsscripts/workflow_cli.py- CLI interface for workflow stepsscripts/regen_gate.py- Regen gate: keeps or replaces a regeneration (used by impl-review.yml)scripts/adaptability_check.py- The adaptability checker as one generated standard-library file for the workflow runner (impl-review.yml, "Adaptability check (record-only)")scripts/adapt_record.py- Combines the checker mirror's record and the staged smoke run into one adapt record per source (used by impl-review.yml)scripts/regen_writeback.py- Stores a kept regeneration's re-score as the live implementation's review (used by impl-review.yml and impl-merge.yml)gcp/budget-guard/- Cloud Run function that disables the Vertex AI API when its Cloud Billing budget is reached; a deploy unit, not a Python package (see GCP budget guard)
Principle: Only contains components actively used by workflows or deployed as GCP automation. One-time or manual scripts belong in scripts/.
Purpose: One-time migrations and manually-run utilities
Key Components:
evaluate-plot.py- Manual plot quality evaluationregenerate-thumbnails.py- Image processing utilitiesbackfill_review_metadata.py- One-time backfill migrationmigrate_*.py- One-time structure migrations
Principle: Scripts that are not part of automated workflows. Used for maintenance, migrations, and manual operations.
Purpose: FastAPI REST API
Structure:
main.py- Application entry point and app factoryrouters/- Modular route handlers (specs, plots, analytics, etc.)dependencies.py- Shared FastAPI dependenciesschemas.py- Pydantic request/response schemasanalytics.py- Server-side Plausible trackingcache.py- Server-side TTL caching layer
Purpose: React frontend (Vite + TypeScript + MUI)
The agent chat ("Use with my data", one page at /agent for visitors and admins since 2026-11-06; /debug/agent only redirects there) lives in src/pages/AgentChatPage.tsx, src/sections/agent-chat/, src/hooks/useAgentSession.ts, src/lib/agent.ts and src/lib/sse.ts; it is built only with VITE_ENABLE_AGENT_CHAT=true. scripts/agent-bff-mock.mjs is a local mock of its backend for driving the page in a browser (see agents/README.md). The triage of the chat's quick-feedback cases is the "agent cases" section of src/pages/DebugPage.tsx, built from src/sections/debug/ in every build; while the API's agent kill switch is off, it shows one line instead of the list.
Purpose: CI/CD automation via GitHub Actions
Specification Workflows:
spec-create.yml- Creates specification (branch → PR → approval → merge)
Implementation Workflows:
impl-generate.yml- Generates single library (generate:{lib}label OR dispatch)impl-review.yml- AI quality evaluationimpl-repair.yml- Repairs rejected implementation (max 4 attempts)impl-merge.yml- Merges approved PR, creates per-library metadatabulk-generate.yml- Batch operations (max 3 parallel)
Supporting Workflows:
sync-postgres.yml- Syncs plots/ to database on push to main
Format: {type}-{variant} or {type}-{variant}-{modifier}
Examples:
scatter-basic- Basic scatter plotscatter-color-groups- Scatter with color-coded groupsbar-grouped-horizontal- Horizontal grouped barsheatmap-correlation- Correlation matrix heatmap
Rules:
- All lowercase
- Hyphens as separators
- Descriptive names (no numbers needed)
Always named by library, under the language's directory: implementations/{language}/{library}.{ext}
python/matplotlib.py,r/ggplot2.R,julia/makie.jl,javascript/d3.js,javascript/muix.tsx
- Where: Google Cloud Storage (
gs://anyplot-images/plots/...) - Why: Binary files bloat git history
- Where: Environment variables, Cloud Secret Manager
- Why: Security
- Note:
.env.exampleshows required variables without values
Note: Quality reports ARE stored in the repository in metadata/{language}/{library}.yaml (the review: section with strengths, weaknesses, criteria_checklist, verdict).
The sync-postgres.yml workflow syncs plots/ to PostgreSQL on push to main:
What's Synced:
- Spec content (full markdown from specification.md)
- Spec metadata (title, description, tags from specification.yaml)
- Implementation code (full Python source)
- Implementation metadata (quality score, generation info from metadata/*.yaml)
- Preview URLs from per-library metadata files
- Quality review data (strengths, weaknesses, criteria_checklist, verdict)
Source of Truth: The plots/ directory is authoritative. Database is derived.
For contribution guidelines, see contributing.md