Skip to content

release: v0.2.0 — pipeline city - #209

Merged
db-tycoon-stephen merged 13 commits into
mainfrom
v0.2.0
Aug 28, 2026
Merged

release: v0.2.0 — pipeline city#209
db-tycoon-stephen merged 13 commits into
mainfrom
v0.2.0

Conversation

@db-tycoon-stephen

@db-tycoon-stephen db-tycoon-stephen commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

Release PR for v0.2.0 "pipeline city" — ship date Friday 2026-08-28. Draft until #205 lands on this branch. Reviewer: @JesuFemi-O — the code review happens on #205; this PR is the release surface (what merges to main, in what order, and the checklist).

What v0.2.0 is

Decided on the 2026-08-25 sync: v0.2.0 = tycoon city (#205) on top of the 0.1.11 CLI, nothing else. The 3D renderer serves a project's catalog as a city — schemas as districts, tables as buildings, lineage as roads — and is CLI-first: everything the UI does maps to an existing tycoon command.

Already on this branch (ahead of main):

Pending merge into this branch:

Ship checklist (in order)

  • feat(city-engine): tycoon city — the catalog as a 3D city (v0.2.0) #205 approved by @JesuFemi-O and merged into v0.2.0
  • Release-prep commit on v0.2.0: pyproject.toml0.2.0, CHANGELOG.md [0.2.0] - 2026-08-28 section, docs/releases/v0.2.0.md, mkdocs nav (drafted on local release/v0.2.0-prep, rebase onto this branch)
  • CI green on the release head; uv build + install smoke
  • Mark this PR ready → merge to main → tag v0.2.0 on mainpublish.yml → GitHub release "v0.2.0 — pipeline city"
  • Verify all four surfaces (PyPI, GitHub release, docs site, pip install database-tycoon && tycoon city --help)

Explicitly not in v0.2.0 (retarget to the next milestone)

#206, #207 (city-sim / data-cli), #191#192#193 (M3 source manifest stack), #196 (agent skills), #74 (DuckLake proposal). Known city gaps tracked for the follow-up: PTC-120 / PTC-127 (renderer + web CI gating, staged fixes on bugfix/city-ui-review-blockers).


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

db-tycoon-stephen and others added 11 commits August 12, 2026 08:33
`tycoon city` renders the project's catalog in the browser: schemas become
districts, tables become buildings, lineage becomes the road network.

The renderer ships inside this wheel as `src/tycoon_city`. An extra was
tried first and removed: `[project.optional-dependencies]` can only add
*distributions*, never gate files out of our own wheel, so every user
downloads the same bytes whether or not they ask for the feature. Keeping
the renderer out would have meant a second distribution on PyPI, and the
1.7 MB it saves is 0.75% of a 227 MB install next to pandas, duckdb and
babel. Not worth a second release process.

`sqlglot>=30.15.0` therefore joins the runtime dependencies rather than an
extra. This is a floor, not a new install: dbt-core (via metricflow) and
dlt already depended on it, so it has always been present -- the pin states
the version the renderer needs instead of inheriting whatever those two
resolve to. THIRD-PARTY.md comes across with the code, because the wheel
now redistributes three.js and zod compiled into web_dist.

The import stays inside `city_cmd` rather than at module scope. That is a
startup cost argument, not an optionality one: the renderer pulls in duckdb
and sqlglot, and no other command should pay for that because this module
was registered. An ImportError there now means a damaged install, so the
message reports the underlying error rather than advice to install an extra
that does not exist.

The project-root walk-up is reimplemented locally rather than borrowed from
tycoon.config. That module builds a cwd-bound singleton at import
(config.py:149), so importing it validates tycoon.yml as a side effect -- a
malformed file makes the import itself raise -- while the public
load_config() raises SystemExit on a newer schema_version. Neither is an
acceptable failure mode for a command that only needs a directory path, and
the singleton's root is fixed at import time, so it goes stale under any
chdir. Tests read city.py's source to keep both imports out.

Filesystem paths are resolved to absolute before being handed over.
`webserve.main` dispatches argv[0] == "demo" to the demo-catalog generator
before argparse runs, so `tycoon city --path demo` would have served the
built-in sample catalog instead of a user's directory named `demo` --
silently, with their own data nowhere in sight. An absolute path cannot
collide. Resolving unconditionally would have broken MotherDuck: `md:my_db`
and `md:_share/<name>/<uuid>` are URIs, not paths, and pathlib would have
mangled them into /cwd/md:my_db. The resolution skips them, and --path is
typed str rather than Path since a URI has no business in pathlib. Both
branches are mutation-tested; a no-op control mutant survives.

No `tycoon-city` console script is installed. Entry points are
unconditional, so one would land on $PATH for every install; the renderer
is reachable as `python -m tycoon_city.webserve` when it is wanted without
a tycoon project around it.

Vendored sources are reformatted to this repo's pinned ruff (0.15.20),
which CI checks with `ruff format --check src tests`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The previous commit vendored `src/tycoon_city` -- the engine, but not the
factory. The renderer's own repo is being deleted, so everything that cannot
be regenerated from what shipped comes across here.

web/ is the important one. `src/tycoon_city/web_dist/` is *minified build
output*: two files Vite emits from 47 TypeScript sources. Without web/ the
front end could never be changed again, only re-served. Carrying web/src,
web/e2e, vite.config.ts, tsconfig.json and the pinned package-lock.json makes
the bundle reproducible, and it is: `npm install && python
scripts/sync_web_bundle.py` rebuilds `index-CosJ8RPj.js` byte-identical to
the committed one. That script is not incidental -- it stashes web/public
(dev data) so a dev city.json cannot ride into a release.

contract/fixtures/demo.duckdb had to be exempted from `.gitignore`'s
`*.duckdb`, which would have dropped it silently. Nothing regenerates it, and
the demo.city.json golden beside it is derived *from* it, so losing it would
have cost both.

tests/tycoon_city/ is the renderer's 41-file suite, carried intact but not yet
collected -- see the note on `addopts`. Its paths are anchored with
`parents[N]` against the old repo root and need rewiring, which is not this
commit's job. Carrying it un-run beats losing it.

sim/contracts.py read its two JSON schemas from `<repo>/contract/fixtures/`,
four .parent hops out of the package and into the checkout. They were never
in the wheel, so `import tycoon_city.sim.logistics` raised FileNotFoundError
anywhere but a source tree -- in pipeline-city's own published wheel too. The
schemas are now package data read through importlib.resources, verified from
an installed wheel. Nothing on the `tycoon city` serve path imports logistics,
which is why the command worked regardless.

docs/city/ is the design record: the city.json and runs.json contracts (the
Python/renderer seam), the road grammar, and the handover and log that hold
the closed decisions. conventions.md is the old CLAUDE.md.

scripts/spike_cluster.py defaulted --src to ~/clients/dogfood, a client path
on one machine. It is now required, so a public repo neither names it nor
reads it by accident.

Left behind deliberately: node_modules and every build artifact (regenerable),
the four .mbox patch exports and demo-tycoon/ and the spike PNGs (regenerable
or spent), GEMINI.md (stale -- it still describes a `dbtycoon` module that was
renamed), and .superpowers/ (agent scaffolding whose durable output was
already promoted into docs/). The 185-commit history is archived separately as
a git bundle rather than grafted in, since the code arrived here as a clean
vendored commit and the history's value is archival.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…town_* modules

Gitignored in pipeline-city, so absent from the history bundle as well as the
repo -- deleting that checkout would have been their only copy. town_endings
has no other caller here; the rest are worked examples for the v5 planner
modules that shipped dormant.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Manifest parsing, lineage DAG derivation, source-freshness SLAs and OSI
semantic models -- tycoon's own domain, not renderer scaffolding. It was
gitignored in pipeline-city, so it is in neither that repo's history nor the
bundle, and clearing that checkout would have been its only copy.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The bundle was sitting untracked in ~/Projects, next to the very directory it
exists to outlive -- one copy, on one machine, backed by nothing. Committing it
puts the renderer's 185 commits behind the same remote as the code they
produced.

1.5 MB, every ref including tag v0.1.0 and feature/city-foundation (main trails
that by 158 commits, so a main-only capture would have archived the wrong
history). `git bundle verify` passes; the committed copy is byte-identical to
the original.

archive/README.md records how to restore it and, more importantly, what it does
NOT contain: a bundle carries tracked history only, so the gitignored
.superpowers/ records, .agents/skills/ and spikes/ are absent -- which is why
those were copied into this repo directly.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
CONTRIBUTING told contributors to run `uvx ruff check src tests`, both as the
description of the CI gate and in the pre-push checklist. `uvx` ignores the
project's pin and resolves to the latest ruff, so a clean checkout reports 106
errors today against rules this codebase has never been linted for — while the
pinned 0.15.20 passes clean. The documented workflow made the repo look broken
to exactly the audience least able to tell that it wasn't.

Switch both to `uv run`, which honours `ruff==0.15.20` from the dev group and
matches what CI actually executes. Also document `ruff format --check`, which
joined the lint job in PR #174 but never reached this file.

This is part 4 of #201; the pre-commit rev bump and the explicit
`[tool.ruff.lint] select` remain open there.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…end (#71)

analyze.md justified the Rill Parquet bridge by claiming SQLite-backed
DuckLake catalogs hold an exclusive lock. Wrong backend. The lock belongs
to the catalog's metadata database: a DuckDB-backed catalog
(`ducklake:foo.ducklake`, the default) is a DuckDB file, and DuckDB locks
a database file to one process. A SQLite-backed catalog
(`ducklake:sqlite:foo.sqlite`) is multi-process safe — Rill 0.86 was
verified reading one live during ingest with zero lock conflicts.

The v0.1.3 probe that produced the original claim attached
`ducklake:.../catalog.ducklake` — no `sqlite:` prefix — so it hit the
DuckDB-file backend while being labelled SQLite.

- analyze.md: replace the rationale with the bridge's actual merits
  (no extension, no attach, no live connection) plus a dated correction.
- motherduck-cloud-sync.md: same claim, same fix. Not named in #71.
- releases/v0.1.3.md: the "full rationale" analyze.md links to. Appended
  a dated correction instead of rewriting a shipped release note.

CHANGELOG entry under a new [Unreleased]. The 0.1.3 CHANGELOG entry keeps
its original wording — released entries are a record, and [Unreleased]
now documents the correction.

Refs #71

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
docs: attribute the DuckLake read-while-ingest lock to the right backend (#71)
…-command

docs(contributing): point ruff commands at the pinned version (#201)
Both entry points were lost in the pipeline-city absorb: the contract docs'
quickstart, docs/city/guide.md, and web/package.json's demo-data script all
invoke tycoon-city-export, and webserve/export.cli still argparse under these
prog names — but only `tycoon` was declared in [project.scripts]. Also points
demo-data at the fixture DB's post-absorb home (contract/fixtures/demo.duckdb),
so `npm run demo-data` can regenerate web/public from a clean checkout again.
Pinned by a subprocess_e2e test, per the console-script-drift convention.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
(cherry picked from commit fa11781259a137fd969b739ddcead03e78045e57)
@db-tycoon-stephen db-tycoon-stephen added this to the v0.2.0 milestone Aug 26, 2026
db-tycoon-stephen and others added 2 commits August 28, 2026 06:03
feat(city-engine): tycoon city — the catalog as a 3D city (v0.2.0)
Scope is PR #205 only, per the 2026-08-25 call decision — #206, #207,
#196, and #191-193 hold for the next cycle. The CHANGELOG section and
release notes drafted on 2026-08-16 credited all of them; both are
rescoped here to what actually ships.

Version pins move 0.1.11 -> 0.2.0 in pyproject.toml and
src/tycoon/__init__.py; the bump previously rode #207's tip, which is
not in this release.

Known limitations are stated plainly in the release notes: the city is
a static plan rather than a simulation, the achievements block is
emitted but not yet read by the renderer, and the vendored renderer
suite is not CI-gated yet.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@db-tycoon-stephen
db-tycoon-stephen marked this pull request as ready for review August 28, 2026 10:18
@db-tycoon-stephen
db-tycoon-stephen merged commit 444b13a into main Aug 28, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant