Skip to content

v0.2.2 — project-local environments

Choose a tag to compare

@db-tycoon-stephen db-tycoon-stephen released this 09 Oct 13:09
· 91 commits to main since this release
e4508b1

v0.2.2

Released: 2026-10-09

Headline: project-local environments.

Two problems shaped this release. Adding a source could succeed while its
dependencies never got installed, so the first run failed on a missing
import with nothing having warned you. And a filesystem load that found
nothing to read could still empty a table that already held rows, then
report "load complete".

Every project now gets its own uv-managed environment, with source code
and dependencies tracked inside the project. A load that finds nothing
leaves your table alone, and a run that loaded zero rows is flagged
instead of reading as a healthy sync. Around those two: MotherDuck
warehouse targets work end to end, --max-records caps every source
type, and saving tycoon.yml keeps your comments.

Landed

Each project gets its own environment

  • tycoon init builds a project-local .venv. It seeds a
    pyproject.toml and runs uv sync right after scaffolding, so there is
    no separate tycoon setup step to forget. tycoon setup and
    tycoon doctor --fix build the same way, and tycoon init --no-venv
    opts out.
  • Source code and dependencies live in the project. A catalog source
    downloads into <project>/.tycoon/sources/ rather than one
    ~/.tycoon/sources/ shared by every project on the machine. Its
    dependencies go into the project's pyproject.toml with uv add, so
    deleting .venv and running uv sync rebuilds the same environment,
    source dependencies included. When an install fails, you see uv's own
    error, so a version conflict and a timeout no longer look the same.
  • Every source's requirements get installed, not just the first one's.
    dlt init writes a sources directory's shared requirements.txt only
    once, so a second catalog source's dependencies were never recorded or
    installed, and its first run failed on an import. The file now holds
    every source's requirements.
  • tycoon data sources migrate <type> copies a source installed in the
    old shared location into the project, along with its requirements, and
    installs them. It never overwrites a directory that already holds your
    files. tycoon data sources run points at it when it finds a source
    sitting in the old directory, instead of reporting "not installed".
  • You're told where an install is going. In a project with a hand-made
    .venv but no pyproject.toml, sources add now warns that a dlt extra
    will go into the shared environment and names that destination in the
    prompt, instead of installing there silently.
  • A failed download says why. sources add printed only "Failed to
    download" whether you were offline or had mistyped the source name. It
    now shows dlt's own error line, and the retry hint names a command that
    exists.
  • tycoon doctor tells you which model a project is on. It reports
    whether the project has its own .venv and whether tycoon is actually
    running from it. The same warning fires before a run or an install falls
    back to the old shared behaviour. Nothing forces a migration.
  • --no-prompt installs a source's dependencies.
    tycoon data sources add <type> --no-prompt used to register the source
    without downloading or installing anything, because the install step was
    skipped along with the prompt. It now installs, and a failed install
    rolls the tycoon.yml change back.

A load that finds nothing leaves your data alone

  • A glob that matches no local files no longer empties the table.
    Filesystem sources load with replace, so a rerun whose glob matched
    nothing (a typo, or a folder that emptied out) used to truncate the
    table and report success. That resource is now left out of the run with
    a warning, and the other resources in the source still load. If nothing
    matched at all, the run exits 0 with "nothing to load".
  • A path that names a single file loads that file. Answering
    data/sales.csv at the path prompt used to produce a source that loaded
    zero rows, every time. A file path, plain or file://, now reads as its
    directory plus that file name. A file_glob set alongside a file path
    is an error, since it could never match.
  • Zero-row runs are flagged. The run_completed ledger event carries
    zero_rows and a warnings list. A run is flagged when a replace
    resource loaded zero rows, even if others loaded some, or when a glob
    matched nothing. An append, merge or incremental run with no new records
    is a normal sync and is not flagged. tycoon data status measures
    freshness from the last run that loaded something, and
    tycoon data history marks a flagged run with a yellow !.
  • --fail-on-empty on data sources run, data sources run-all and
    data run-all turns those cases into a failure: the command exits 1 with
    an error naming the table, and the ledger records run_failed. Use it on
    a schedule, where an exit code is the only thing anyone sees.

Fixes

  • MotherDuck warehouses work with data query, data schema and
    data clean.
    With database.warehouse: md:<name> in tycoon.yml,
    tycoon used to look for a local file of that name, and data clean --all
    could delete a local file called md:<name>_raw. An md: target now
    goes to DuckDB unchanged. Commands that still need a local raw database,
    such as data sources run, stop with a clear error instead.
  • --max-records caps filesystem, rest_api and sql_database
    sources.
    These printed the cap and then loaded every row.
  • Saving tycoon.yml keeps your comments, blank lines and indentation.
    Only the values tycoon changes are rewritten, and a ${ENV} reference
    stays a reference. tycoon register now saves the same way.
  • Rill source files use relative paths. Generated Rill sources used to
    carry the absolute path to the Parquet export, which broke on every
    other clone and in CI.
  • tycoon city builds faster from cold. A cold city.json on an
    87-object catalog dropped from 43.6s to 10.3s, with identical lineage.
  • A quoted motherduck_token is masked in printed errors.
    motherduck_token='...' used to pass through the redaction with the
    token intact.
  • The data run-all docs list the options the command has. The page
    described flags that didn't exist.

Upgrading from 0.2.1

  • An existing project keeps working as it did. Until it has its own
    .venv, sources stay in the shared ~/.tycoon/sources/ and installs go
    where they went before. tycoon doctor warns about this. To move a
    project over, run tycoon setup, then
    tycoon data sources migrate <type> for each catalog source it uses.
  • --no-prompt now installs dependencies. A CI job that ran
    tycoon data sources add ... --no-prompt and then installed packages
    itself can drop its own install step. If you relied on --no-prompt
    not touching the environment, that has changed.
  • tycoon data status may show an older Last Sync. A zero-row run no
    longer counts as a sync, so a source whose recent runs loaded nothing
    shows the last run that did. Ledger events written before 0.2.2 read
    back as not zero-row.
  • A filesystem run that matches nothing exits 0 with "nothing to
    load"
    , not "load complete", and leaves the table as it was. A script
    that parsed the old message needs updating. Add --fail-on-empty if
    you want it to fail instead.
  • A negative --max-records is now a usage error.
  • A MotherDuck query is not opened read-only. DuckDB supports
    read_only only for local files, so what data query can change on
    MotherDuck depends on your token's permissions.

Known limitations

  • The legacy-table warning does not reach multi-resource sources.
    Migrating a flat filesystem source to the resources: shape leaves the
    old generic table behind without a warning. Tracked in
    #244.
  • The city is a plan, not yet a simulation. Vehicles and guests are
    simulated but not drawn, and coverage milestones are not surfaced. The
    fixes (#250, #251) and the renderer's CI gate (#256, #252) move to
    0.2.3.
  • Remote object storage is still unsupported. s3://, gs:// and
    az:// are recognised only well enough to be skipped by the local-glob
    check.

For contributors

  • The build is reproducible. hatchling is pinned in
    [build-system] requires, since an unpinned upgrade changed the wheel's
    metadata version between 0.1.11 and 0.2.0 and the publisher rejected it.
    The sdist now holds src/ and the package metadata only, down from
    2.9 MB to 523 KB.
  • The release docs match the publish workflow.
    docs/publishing-to-pypi.md describes the four jobs that actually run
    and the fix-forward-and-re-tag procedure for a failed publish.
  • A root CLAUDE.md points coding agents at CONTRIBUTING.md, the PR
    template and the writing-style skill. It holds no rules of its own.
  • Merging into a release branch is documented. CONTRIBUTING now covers
    merge commits, pinning a merge to the reviewed head, merging stacked PRs
    and checking a CHANGELOG after a rebase.
  • Upgrade safety is part of the contributor guide. CONTRIBUTING
    covers testing that a change doesn't break an existing project, and the
    PR template asks for a short mental model of each change: input,
    change, outcome.
  • Project tracking moved to GitHub. Issues, per-release milestones and
    the public Tycoon CLI project board
    are the record of what's planned. The Jira board is no longer updated.
    The decision is in Discussion #353.

What's next

0.2.3 picks up the city stack (#248 through #256): the renderer's tests
in the default pytest run, the vehicle, guest and achievements fixes,
and a CI gate for the web front end. It also brings #260, which creates a
new dbt project inside the tycoon project instead of next to it.