Skip to content

feat(cache)!: record where each release in the local registry comes from - #674

Merged
karhu merged 4 commits into
tnet-157/19b-list-searchfrom
tnet-157/20-store-tools
Nov 10, 2026
Merged

karhu merged 4 commits into
tnet-157/19b-list-searchfrom
tnet-157/20-store-tools

Conversation

@karhu

@karhu karhu commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor

The Stack

This stack covers refactoring the internal logic of how releases are resolved during read, download & build, in particular also in the context of parent releases / parent datasets. On top of that it refactors the public TimeNet APIs and CLI interfaces around these actions, to streamline the behavior across them.

This PR

The store records where each release comes from, a copy or a build. The new groups timenet local downloads and timenet local builds use the records to show and clear the copies and the builds apart.

Details

The store did not know a copy from a build. A copy is easy to get again from its registry, but a build can take hours or need credentials. So a timenet local command deleted either every release in the store or none.

  • Each release in the store gets a record in .origin.json: copy or build, the registry or the backend package with its version, and the time in UTC. A URL in the record loses its user info, query and fragment.
  • run_plan() records each copy and each build. TimeNet.download() records each release that it copies. copy_release() returns the releases that it copied or repaired.
  • BaseRegistry.location names each registry in a record. The new InstalledBuilder.package names the package that builds a dataset.
  • The new groups timenet local downloads and timenet local builds split the store by the record. A download is a copy from a registry. A build is a release that a connector built, or a release without a record. Each group has info and clear, and each of these commands takes --local-registry.
  • info lists the releases of the group with the source and the size of each. The source is the registry of a download, or the package of a build.
  • clear deletes the releases of the group, but it keeps each one that a remaining release needs. It walks from each release that stays to the releases that it needs: the payload sources of a release, and the parents of a build. So the rule holds in both directions. local downloads clear keeps a download that a build needs, and local builds clear keeps a build that a download needs. A warning names each kept release and a release that needs it.
  • timenet local info shows the count and the size of the downloads and of the builds, instead of the whole store. timenet local clear still deletes every release, and its prompt counts the builds.
  • In timenet.cache, clear_releases(store, *, kinds) takes the kinds to delete, "download", "build" or both. Its result also names each kept release as a KeptRelease. releases_to_clear() gives the same answer without a delete. StoredRelease.kind gives the ReleaseKind of a release.
  • Size: about 1100 changed lines. The record and the clear read best as one change. The safety rules of the clear depend on the record.

Design decisions

  • A release without a record counts as a build, so local downloads clear never deletes a release that TimeNet did not copy.
  • The record stays in the store. It is not in the manifest, and a copy never carries it, so two stores hold the same release files.
  • A damaged record, a damaged manifest of a release that stays, or a symlinked dataset or release folder stops the clear before it deletes anything. The walk cannot see a release behind a link, so it cannot know which releases that release needs.
  • timenet local clear needs no walk, because every release goes. So a damaged manifest or a symlinked folder does not stop it.
  • The clear computes what to delete again after the prompt, and it skips a release whose record changed. So a build that lands during the prompt keeps the copies that it needs.

Breaking changes

  • clear_releases() takes kinds=, and ClearedReleases has the new field kept.
  • timenet local info shows the downloads and the builds instead of the local registry.
  • timenet.registry.base.ProgressCallback is now ByteCountCallback.
  • StoredRelease has the new field origin. TimeNet measures its size at the first read.
  • BaseRegistry.location is abstract, so a registry of another package must implement it.
  • find_card() raises TimeNetBuildError if no installed package declares the entry point of the backend.

Stack

Changes later in the stack:

Merge message

feat(cache)!: record where each release in the local registry comes from

Each release in the local registry gets a record in .origin.json that
says whether it is a copy from a registry or a build. A copy is easy to
get again, but a build can take hours or need credentials.

- Each copy and each build of a plan, and each copy of download(),
  writes the record. A URL in the record loses its user info, query and
  fragment.
- timenet local downloads and timenet local builds show and clear the
  two kinds apart. A release without a record counts as a build.
- clear keeps each release that a remaining release needs, in both
  directions, and a warning names it.

BREAKING CHANGE: BaseRegistry subclasses must implement location, which
names the registry in a record.

BREAKING CHANGE: timenet.registry.base.ProgressCallback becomes
ByteCountCallback.

BREAKING CHANGE: find_card() raises TimeNetBuildError if no installed
package declares the entry point of the backend.

@karhu
karhu added this pull request to stack #650 November 4, 2026 20:04
This was referenced Nov 4, 2026
@karhu
karhu force-pushed the tnet-157/20-store-tools branch from 418eaca to cb78c04 Compare November 5, 2026 07:24
@karhu
karhu force-pushed the tnet-157/20-store-tools branch from cb78c04 to d127126 Compare November 5, 2026 07:27
@karhu
karhu force-pushed the tnet-157/20-store-tools branch from d127126 to 8ceb46b Compare November 5, 2026 08:16
@karhu
karhu force-pushed the tnet-157/20-store-tools branch from 8ceb46b to 11f48ea Compare November 5, 2026 10:57
@karhu
karhu force-pushed the tnet-157/20-store-tools branch 2 times, most recently from 64830da to 9964154 Compare November 9, 2026 12:23
@karhu karhu changed the title feat(cache)!: record where each release in the store comes from feat(cache)!: record where each release in the local registry comes from Nov 9, 2026
@karhu
karhu force-pushed the tnet-157/20-store-tools branch from 9964154 to e88e748 Compare November 9, 2026 13:56
@karhu
karhu force-pushed the tnet-157/20-store-tools branch from e88e748 to 9752172 Compare November 9, 2026 15:06
@karhu
karhu force-pushed the tnet-157/20-store-tools branch 4 times, most recently from f79ae09 to f3dfc71 Compare November 10, 2026 14:39

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Findings marked 🟡 are optional suggestions and need no follow-up push.

Comment thread packages/timenet/src/timenet/cli/app.py Resolved
Comment thread packages/timenet/src/timenet/cache.py Resolved
@karhu
karhu force-pushed the tnet-157/20-store-tools branch from f3dfc71 to 2f519b1 Compare November 10, 2026 15:50
@karhu
karhu removed this pull request from stack #699 November 10, 2026 15:58

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review completed

Nothing new to post: everything this review found is already covered by existing comments on this pull request or didn't merit a separate one.

@karhu
karhu force-pushed the tnet-157/20-store-tools branch from 2f519b1 to a258b3c Compare November 10, 2026 16:24
@karhu
karhu added this pull request to stack #772 November 10, 2026 16:24

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No new blocking issues. 1 optional suggestion (a nit or a note on pre-existing code) was found and not posted. Nothing in this review needs a push before merging.

The local registry could not tell a copy of a release from a build of
it. A copy is easy to get again from its registry, but a build can take
hours or need credentials. So the timenet local commands could not
delete the copies and keep the builds.

The local registry now keeps a provenance record in the directory of
each release, in the file .origin.json. LocalRegistry.record_origin()
writes the record, and LocalRegistry.read_origin() reads it as an
Origin. The record gives the kind of the release, an OriginKind:
DOWNLOAD for a copy from a registry, or BUILD. It names the registry, or
the backend package and its version, and it gives the time in UTC. A URL
in the record loses its user info, query and fragment, so no credential
lands in it. A damaged record raises TimeFFormatError, which names its
first problem. A release without a record counts as built.

run_plan() writes a record for each copy and each build, and
TimeNet.download() for each release that it copies. copy_release()
returns the releases that it copied or repaired. publish() writes no
record, and a copy never carries one. Each registry names its
location, and the InstalledBuilder of a dataset holds the package
that builds it, with its version. A backend that no installed package
declares raises TimeNetBuildError when its builder is looked up. The
fake backends of timenet.testing come from the fake package
timenet-testing 0.

The new groups timenet local downloads and timenet local builds split
the local registry by the record. A download is a copy from a registry.
A build is a release that a connector built, or a release without a
record. In each group, info lists the releases with their source and
size, and clear deletes them. A clear never deletes a release that a
release that stays needs: its payload sources, and the parents of a
build. This rule holds in both directions. The command names each
release that it keeps and a release that needs it. A damaged record, a
damaged manifest of a release that stays, or a symlinked dataset or
release folder stops the command before it deletes anything. timenet
local info shows the size of the downloads and of the builds, and the
prompt of timenet local clear counts the builds.

In timenet.cache, clear_releases() takes the kinds to delete,
OriginKind.DOWNLOAD, OriginKind.BUILD or both, and returns the
releases that it keeps. It refuses an empty list of kinds with
TimeFValidationError. releases_to_clear() gives the same answer
without a delete. StoredRelease.kind tells the kind of a release, and
StoredRelease.size() measures it on disk.

The byte-count callback of the registries is now ByteCountCallback,
so ProgressCallback means only the callback of progress events.

BREAKING CHANGE: timenet.registry.base.ProgressCallback is now
ByteCountCallback.

Part of TNET-157.
Shorter docstrings for the code that this change adds or changes.

Part of TNET-157.
Drop tests that cannot fail or repeat other tests, and sharpen the rest.

Part of TNET-157.
…egistry in the client

The coordinator used three names for the registry that its copies and
builds go into: "store", "output" and "local registry". Now it uses one
name, the target registry. The parameters of plan_release_build(),
run_plan(), plan_steps(), plan_download(), plan_read() and
read_release() are now target_registry. In the planner, _Graph.output is
now target_registry, in_output() is now in_target(), and
needed_in_local_registry is now needed_in_target. The docstrings and
comments say "the target registry" in place of "the output" and "the
store".

In the client, TimeNet._store is now TimeNet._local_registry. The local
variables and the parameters of the client helpers that hold it follow.
In timenet-build build, the opened local registry is now local.

The tests follow. In the coordinator tests, the fixtures, the variables,
the test names and the case names for the registry that copies and
builds go into say target_registry. Where a test means the local
registry of the client, the SDK functions or the CLI, its names say
local_registry. Other meanings of "store" keep their names: the
store_dataset() stage of a connector, stored_releases and StoredRelease,
and the parameter store of Backend.build() in the fake backends.

The public names do not change, and the behavior does not change.

Part of TNET-157.
@karhu
karhu force-pushed the tnet-157/20-store-tools branch from a258b3c to 952a19d Compare November 10, 2026 17:41
@karhu
karhu merged commit 0782e65 into main Nov 10, 2026
4 of 8 checks passed
@karhu
karhu deleted the tnet-157/20-store-tools branch November 10, 2026 18:24
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.

2 participants