tracker
Generic work-tracker contract (TrackerService) + Linear adapter
| Latest | 0.0.4 |
|---|---|
| Versions | 4 |
| Category | Modules & tooling |
| Maintainers | Matt Marshall |
| Registry | https://registry.tbzl.dev/modules/tracker/ |
| Source | github.com/fastverk/tracker |
bazel_dep(name = "tracker", version = "0.0.4")
View source & releases on GitHub ↗
[!IMPORTANT] This repository is retired.
trackeris developed in thefastverk/platformship vehicle, attracker/. 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 andgit_overridepins stay valid. Thetracker.v1protos are published fromfastverk/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.Issuehas 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
- A new
Trackerenum value intracker.proto— append only, never renumber; consumers persist these values. - An adapter module implementing
Tracker, declaring honestcapabilities(). - A fixture +
conformance_suite!invocation. - A metadata key and an arm in
gateway::TrackerGateway::adapter. Unknown values are rejected there, never defaulted —forge.v1learned 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.
| version | toolchain |
|---|---|
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 |
| repo | extension |
|---|---|
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.
| Atom | Resolved here | Elsewhere |
|---|---|---|
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#
Depends on
Versions#
4 published versions, newest first. Each resolves to an immutable, integrity-checked archive.
| Version | Integrity (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 ↗ |