Tya v0.26 Specification

This document is the specification for Tya v0.26 after v0.25 bit-level operations and byte sequences.

Theme

Tya v0.26 introduces external package management.

A Tya project declares its dependencies in tya.toml, runs tya install to resolve and pin a coherent set of versions in tya.lock, and from then on all builds load the same packages reproducibly. This is the foundation for a Tya ecosystem; it does not yet open a central registry.

The design is modeled on Gleam's gleam.toml + manifest.toml and Bundler's Gemfile + Gemfile.lock, simplified for Tya's current size.

Goals

CLI commands.

flags in v0.26.

Included in v0.26

v0.26 includes all v0.25 behavior and adds:

exact x.y.z)

Not Included in v0.26

v0.26 does not include:

tya.toml (manifest)

tya.toml lives at the project root and is human-edited.

name = "my-app"
version = "0.1.0"
description = "My Tya application"
authors = ["komagata <[email protected]>"]
license = "MIT"

[dependencies]
colorhash = "^1.2.0"
markdown  = { version = ">= 0.5.0, < 1.0.0" }
fancy     = { git = "https://github.com/foo/fancy", tag = "v2.1.0" }
shared    = { path = "../shared" }

[dev-dependencies]
mock = "^0.3.0"

Required fields

digits, and _; starts with a letter).

Optional fields

Dependency entry forms

Short form:

colorhash = "^1.2.0"

is equivalent to

colorhash = { version = "^1.2.0" }

Long forms select a source:

# git source
fancy = { git = "https://github.com/foo/fancy", tag = "v2.1.0" }
fancy = { git = "https://github.com/foo/fancy", branch = "main" }
fancy = { git = "https://github.com/foo/fancy", rev = "abc1234" }

# local path source
shared = { path = "../shared" }

# explicit version constraint with git source
fancy = { version = "^2.1.0", git = "https://github.com/foo/fancy" }

Exactly one of tag, branch, rev is required for git when no version is given. When version is given, the resolver still locks a specific git revision in the lockfile.

Version constraint syntax

| Form | Meaning | |---|---| | "1.2.3" | exact 1.2.3 | | "^1.2.3" | >=1.2.3, <2.0.0 (caret) | | "~1.2.3" | >=1.2.3, <1.3.0 (tilde) | | ">= 1.2.3" | open lower bound | | ">= 1.2.3, < 2.0.0" | comma-separated AND |

Caret of a 0.x.y version is >=0.x.y, <0.(x+1).0 (matching Cargo's zero-major rule).

tya.lock (lockfile)

tya.lock is auto-generated by tya install and tya update. It records the exact versions chosen, their sources, and content checksums so that every machine resolves the same dependency graph.

# This file is auto-generated. Do not edit manually.
version = 1

[[package]]
name = "colorhash"
version = "1.2.5"
source = "git"
git = "https://github.com/foo/colorhash"
rev = "abc1234567890abc..."
checksum = "sha256:9f3..."
dependencies = []

[[package]]
name = "markdown"
version = "0.5.3"
source = "git"
git = "https://github.com/baz/markdown"
rev = "def4567890..."
checksum = "sha256:7a1..."
dependencies = ["colorhash"]

[[package]]
name = "shared"
version = "0.0.0"
source = "path"
path = "../shared"
checksum = ""
dependencies = []

[requirements]
colorhash = { version = "^1.2.0" }
markdown  = { version = ">= 0.5.0, < 1.0.0" }
shared    = { path = "../shared" }

Fields per package entry

branch are recorded only if they were used to select rev.

this and uses an empty string).

on.

Lockfile lifecycle

or honors the existing lock if it already satisfies the manifest.

fixed.

the diff to see version movement.

Resolution

The resolver is **single-version-per-package**: in any successful resolution there is exactly one chosen version of each named package across the whole dependency graph.

It uses **backtracking**:

  1. Build a dependency graph from tya.toml.

2. For each unresolved package, list candidate versions sorted from highest to lowest that satisfy the union of requirements seen so far. 3. Pick the highest candidate, recursively resolve its declared dependencies. 4. On a conflict (no candidate satisfies the merged constraints), backtrack to the previous decision and try the next candidate. 5. If all options are exhausted, report an unsolvable diamond conflict with the full path of conflicting requirements.

Equality for source identity is (name, source-info):

the resolver reports a name conflict.

Sources

git source

example = { git = "https://github.com/foo/example", tag = "v1.0.0" }

Behavior:

per Tya install).

The package directory must contain its own tya.toml at the repo root.

path source

shared = { path = "../shared" }

Behavior:

the local directory contains at build time.

field comes from the linked tya.toml.

Package layout

A package directory looks like:

<package>/
├── tya.toml          # required, declares name + version + dependencies
├── src/              # required, contains public modules
│   └── <name>.tya
├── tests/            # optional
└── README.md         # optional

Public modules

import <name> from a downstream package resolves to <package>/src/<name>.tya. A package with name foo must expose a top-level module foo defined in src/foo.tya. Sub-modules at src/foo/bar.tya are imported as import foo/bar (read as foo/bar).

(The slash form foo/bar is parsed but is also a future-compatibility hook; v0.26 ships only the top-level import foo form, with sub-paths deferred to a later minor version unless straightforward to include.)

Manifest of a dependency

A dependency's tya.toml is read to discover its **own** dependencies (the [dependencies] section). Dev-dependencies of dependencies are ignored; only the top-level project's dev-dependencies are loaded.

Import resolution

In v0.26, the import lookup order becomes:

  1. The importing file's directory.

2. **Manifest-declared dependencies (via tya.lock).** 3. Directories listed in TYA_PATH. 4. The bundled stdlib/ shipped with Tya.

Step 2 is new in v0.26. The resolver maps a package name to its source directory under .tya/packages/<name>-<version>/ (git source) or directly to the path (path source).

Existing stdlib imports such as import string continue to resolve through step 4. Stdlib names are never overridden by user manifests in v0.26.

CLI

tya install

Reads tya.toml. If tya.lock exists and satisfies the manifest, uses it as-is, downloading any missing packages. Otherwise resolves from scratch and writes a new lockfile.

tya install

Exit code is 0 on success. On unsolvable resolution it exits non-zero with a diagnostic listing the conflicting requirements.

tya update

Recomputes the lockfile from the manifest. Pass a package name to update only that package and its transitive dependencies; pass nothing to update everything.

tya update              # update all
tya update colorhash    # update colorhash, hold others

tya add

Adds an entry under [dependencies] in tya.toml, then runs install.

tya add colorhash             # latest published version, caret constraint
tya add colorhash ^1.2.0      # explicit constraint
tya add fancy --git https://github.com/foo/fancy --tag v2.1.0
tya add shared --path ../shared
tya add --dev mock ^0.3.0     # add to [dev-dependencies]

tya remove

Removes an entry from [dependencies] (or [dev-dependencies]), updates the lockfile.

tya remove colorhash

tya outdated

Reports packages whose latest available version is greater than the currently locked version.

tya outdated

For git sources, "latest" means the highest semver tag reachable from the configured branch (or the current rev if no branch is configured). For path sources, the local manifest is consulted directly.

Cache and storage

Tya stores resolved packages in two places:

across projects on the same machine if the user shares a ~/.tya/cache via the TYA_CACHE environment variable.

directories used at build time.

Both directories are safe to delete; tya install recreates them.

.tya/packages/ and .tya/cache/ should be added to .gitignore. tya.lock should be committed.

Diagnostics

v0.26 implementations should report source-oriented errors for:

hint to run tya install)

stdlib module

on the next tya install)

Diagnostics should mention the manifest field, the package name, the constraint that failed, and the source identity when relevant.

Examples

Minimal app

# tya.toml
name = "hello-app"
version = "0.1.0"

[dependencies]
greeting = { git = "https://github.com/example/greeting", tag = "v1.0.0" }
# main.tya
import greeting

print greeting.hello("world")
tya install
tya run main.tya

Tracked path dependency for monorepo-lite layout

project/
├── tya.toml
├── main.tya
└── lib/
    └── shared/
        ├── tya.toml
        └── src/
            └── shared.tya
# project/tya.toml
name = "project"
version = "0.1.0"

[dependencies]
shared = { path = "lib/shared" }