tomato·bazeldocs v0 · latest
Docs/Reference/Modules/tracker

tracker

Generic work-tracker contract (TrackerService) + Linear adapter

Latest0.0.4
Versions4
CategoryModules & tooling
MaintainersMatt Marshall
Registryhttps://registry.tbzl.dev/modules/tracker/
Sourcegithub.com/fastverk/tracker
MODULE.bazelstarlark
bazel_dep(name = "tracker", version = "0.0.4")

View source & releases on GitHub ↗

[!IMPORTANT] This repository is retired. tracker is developed in the fastverk/platform ship vehicle, at tracker/. Open issues and pull requests there.

The published module is unchanged — bazel_dep(name = "tracker", version = "0.0.4") resolves exactly as before. This remote keeps its full history and every tag, so existing registry entries and git_override pins stay valid. The tracker.v1 protos are published from fastverk/contracts.

Retired at 3927db9, the commit the vehicle imported — nothing here is unimported. Background: Consolidation.

tracker

Generic work-tracker contract (TrackerService) + provider adapters.

The operations a work tracker (Linear, Jira, GitHub/GitLab issues, …) provides that an autonomous agent fleet needs, modeled once as a proto service so a single contract covers every provider:

  • read a backlog, and read one item in full;
  • learn the workspace’s projects and workflow states;
  • move an item through those states, comment on it, and link the change that resolves it.

proto/tracker/v1/tracker.proto defines the gRPC TrackerService; in-process consumers use the hand-written async Tracker trait whose methods take/return the same proto messages (TrackerRef, WorkItemRef, WorkItem, WorkItemState, WorkItemQuery). LinearTracker (GraphQL) implements it.

This is the deliberate sibling of forge: forge is “where the code lives”, tracker is “where the work is tracked”. Same layout, same conventions, same conformance-suite discipline — an adapter author learns the pattern once.

Why a separate contract from forge.v1

forge.v1.ForgeDiscovery already lists GitHub and GitLab issues, so folding trackers into it looks cheaper. It isn’t:

  • a tracker has concepts a code host does not — workflow states, projects, cycles, estimates, sub-items — and bending Linear into forge.v1.Issue (a seven-field discovery row) loses exactly the fields a campaign needs;
  • forge.v1.Issue has no write surface at all, and the flywheel is not read-only: it must move an item to In Progress, comment, and link the PR;
  • keeping them separate makes GitHub and GitLab issues one adapter here rather than the model every other provider is bent to fit.

Credentials

No service in this repo holds a tracker credential. tracker-gateway builds a per-request adapter from the caller’s gRPC metadata (x-fastverk-linear-token), exactly as forge-gateway takes x-fastverk-gitlab-token, so every operation runs as the caller and adding a provider never means giving a shared daemon another standing secret.

What is deliberately NOT here

Routing a work item to a repository (and to a Bazel blast radius) is not a tracker operation — no provider knows it. It is fleet policy, expressed by the caller: a campaign’s routing table, or a triage agent. WorkItem carries the evidence a router needs (labels, project, group, free text) and stops there.

The conformance suite is the contract

tracker::conformance is a library module behind the non-default testing feature, not a tests/ file — so an adapter in its own Bazel module runs the identical battery instead of copying it. An adapter integrates in one line:

tracker::conformance_suite!(my_adapter, MyFixture::new());

The cases pin behavior, not shape: transitions are idempotent, keyed comments dedupe, links dedupe by URL, paging terminates without duplicating, get returns the full body, an item resolves by human key alone, and — the most consequential one — a filter that matches nothing returns nothing. An adapter that silently drops a filter it cannot express does not return slightly wrong results; it returns the entire backlog and a campaign dispatches an agent at every row.

Correspondingly, a surface an adapter cannot serve must fail loudly. The Linear adapter declares text_search: false and rejects a text query rather than answering with the unfiltered backlog.

Build

.bazelrc:

common --registry=https://registry.fastverk.com/
common --registry=https://bcr.bazel.build/

MODULE.bazel:

bazel_dep(name = "tracker", version = "0.0.1")

Then depend on @tracker//:tracker from a rust_library.

bazel build //:tracker //:tracker-gateway
bazel test  //:tracker_test //:conformance

The OCI image needs a linux target platform — gcr.io/distroless/cc-debian12 publishes no darwin variant, so a bare bazel build //... fails at analysis on a Mac. Build it the way the RBE does (same as the sibling forge module):

bazel build //:tracker-gateway-image --platforms=//tools/oci:linux_amd64
bazel build //:tracker-gateway-image_push --config=rbe          # what CI runs

Bazel is the only build system here. The crate also carries a Cargo.toml because crate_universe resolves dependencies from it — not as a second build path.

Run

TRACKER_GATEWAY_BIND=0.0.0.0:50068 tracker-gateway

Callers pass their own credential per request:

grpcurl -plaintext \
  -H "x-fastverk-linear-token: $LINEAR_API_KEY" \
  -d '{"tracker":{"tracker":"TRACKER_LINEAR"},"query":{"projects":["DEV"],"state_categories":["STATE_CATEGORY_TODO"],"labels":["agent/ready"]}}' \
  localhost:50068 tracker.v1.TrackerService/ListWorkItems

Adding a provider

  1. A new Tracker enum value in tracker.protoappend only, never renumber; consumers persist these values.
  2. An adapter module implementing Tracker, declaring honest capabilities().
  3. A fixture + conformance_suite! invocation.
  4. A metadata key and an arm in gateway::TrackerGateway::adapter. Unknown values are rejected there, never defaulted — forge.v1 learned that the hard way when “not GitHub therefore GitLab” became a wrong-API write.

Conformance#

5 findings across 2 invariants. 15 contested atoms. See how gating works or the full report.

D2 a non-dev register_toolchains propagates to every transitive consumer why this matters ↗
versiontoolchain
0.0.4@rust_toolchains//:all
0.0.4@zig_sdk//toolchain:linux_amd64_gnu.2.28
0.0.4@zig_sdk//toolchain:linux_arm64_gnu.2.28
D3 a repo name CHOSEN on a SHARED extension must be namespaced why this matters ↗
repoextension
crates@rules_rust//crate_universe:extension.bzl
distroless_cc@rules_oci//oci:extensions.bzl

Contested atoms

Third-party modules where this module resolves a different version than others do. Not a violation of anything this module did — it is the actionable form of a registry-level convergence finding, and the sentence a maintainer can act on.

AtomResolved hereElsewhere
apple_support 1.24.2 2.2.0 ×1
aspect_bazel_lib 2.22.5 2.8.1 ×1
bazel_lib 3.0.0 3.2.2 ×17
bazel_skylib 1.8.2 1.9.0 ×2
gawk 5.3.2.bcr.1 5.3.2.bcr.3 ×17
jq.bzl 0.1.0 0.4.0 ×17
nlohmann_json 3.6.1 3.12.0.bcr.1 ×1
package_metadata 0.0.2 0.0.5 ×3
protobuf 33.4 34.0.bcr.1 ×2
rules_jvm_external 6.7 6.8 ×4
rules_python 1.7.0 2.0.1 ×1
rules_swift 3.1.2 3.6.1 ×1
tar.bzl 0.5.1 0.10.4 ×170.6.0 ×1
upb 0.0.0-20220923-a547704 0.0.0-20230516-61a97ef ×1
yq.bzl 0.1.1 0.3.4 ×17

Dependencies#

tracker in the registry graph — what it depends on (left) and what depends on it (right).

Depends on

platforms1.0.0bazel_skylib1.8.2rules_rust0.70.0protobuf33.4hermetic_cc_toolchain4.1.0rules_oci2.2.6rules_pkg1.0.1aspect_bazel_lib2.22.5rules_shell0.6.1devstardoc0.7.2dev

Versions#

4 published versions, newest first. Each resolves to an immutable, integrity-checked archive.

VersionIntegrity (sha256)Source archive
0.0.4 latest NazT3cktpKIB2Jm+… tag archive ↗
0.0.3 D5En+9B+ex/coIhM… tag archive ↗
0.0.2 SXb539HTaFtZc42E… tag archive ↗
0.0.1 rKix5SYf5r15S4ow… tag archive ↗

← All modules