Skip to main content

spec-kitty-tracker

Shared task-tracker abstraction layer for Spec Kitty CLI and SaaS.

Scope

  1. Canonical issue model (CanonicalIssue, ExternalRef, CanonicalLink)
  2. Tracker connector protocol with capability negotiation
  3. Doctrine-style source-of-truth ownership policies
  4. Conflict resolution and deterministic sync engine
  5. Bidirectional mission/issue sync bridge with decision reference traceability
  6. Connector registry and vendor connector implementations

Included Connectors

SaaS-Backed (via create_hosted_connector)

  1. LinearConnector
  2. JiraConnector
  3. GitHubConnector
  4. GitLabConnector

Out of Scope for Hosted Transport (this release)

  1. AzureDevOpsConnector

Local/Native (direct construction)

  1. BeadsConnector — local-first bd CLI adapter
  2. FPConnector — local-first fp CLI adapter

Test/Reference

  1. InMemoryConnector — fully functional reference connector

Installation

pip install -e .

For development tools:

pip install -e ".[dev]"

Quick Example

The primary integration path for SaaS-backed providers (Linear, Jira, GitHub, GitLab) uses create_hosted_connector with host-provided transport context. See Local/Native and Test Connectors for direct construction.

import asyncio
from spec_kitty_tracker import (
    create_hosted_connector,
    HostedConnectorRequest,
    LinearHostedParams,
    NangoConnectionContext,
    InMemoryIssueStore,
    OwnershipPolicy,
    OwnershipMode,
    SyncEngine,
)


async def main() -> None:
    # SaaS host provides identity context per operation
    nango_ctx = NangoConnectionContext(
        connection_id="user-connection-id",
        provider_config_key="linear",
        nango_secret_key="nango-secret",
    )

    # Factory constructs a connector routed through SaaS-owned transport
    connector = create_hosted_connector(HostedConnectorRequest(
        provider="linear",
        nango_context=nango_ctx,
        params=LinearHostedParams(team_id="TEAM-UUID"),
    ))

    store = InMemoryIssueStore()
    policy = OwnershipPolicy(mode=OwnershipMode.EXTERNAL_AUTHORITATIVE)
    engine = SyncEngine(connector=connector, store=store, policy=policy)

    async with connector:
        await engine.pull()


asyncio.run(main())

Hosted Discovery

The canonical path for downstream hosts to discover provider workspaces and bindable resources is the function-based public API: discover_workspaces and discover_resources. Both are exported from the top-level spec_kitty_tracker package and return typed dataclasses.

import asyncio

from spec_kitty_tracker import (
    create_hosted_connector,
    discover_resources,
    discover_workspaces,
    ConnectorRequestError,
    DiscoveryContractError,
    HostedConnectorRequest,
    NangoConnectionContext,
)
from spec_kitty_tracker.discovery.hosted_adapter import (
    connector_params_to_hosted_params,
)


async def integrate_linear() -> None:
    # 1. Build the per-user transport context. The host owns identity.
    nango_ctx = NangoConnectionContext(
        connection_id="user-connection-id",
        provider_config_key="linear",
        nango_secret_key="nango-secret",
    )

    # 2. Discover workspaces. Catch contract failures separately.
    try:
        workspaces = await discover_workspaces("linear", nango_ctx)
    except DiscoveryContractError as exc:
        # Provider returned malformed metadata — alert operators.
        print(f"contract violation: {exc.provider} {exc.field_path}")
        return
    except ConnectorRequestError as exc:
        # Network / auth / API error — retryable based on failure_class.
        print(f"request failed: {exc}")
        return

    workspace = workspaces.items[0]
    print(f"id={workspace.id} display={workspace.display}")

    # Read normalized optional metadata defensively.
    metadata = workspace.provider_context or {}
    handle = metadata.get("workspace_handle")  # str | None
    url = metadata.get("workspace_url")        # str | None

    # 3. Discover bindable resources within the chosen workspace.
    resources = await discover_resources("linear", workspace, nango_ctx)
    resource = resources.items[0]

    routing = resource.routing_metadata
    display_key = routing.get("display_key")     # str | None
    resource_url = routing.get("resource_url")   # str | None

    # 4. Translate connector_params to typed HostedParams + construct connector.
    hosted_params = connector_params_to_hosted_params(
        provider=resource.provider,
        connector_params=resource.connector_params,
    )
    connector = create_hosted_connector(HostedConnectorRequest(
        provider=resource.provider,
        nango_context=nango_ctx,
        params=hosted_params,
    ))

    async with connector:
        ...  # Use the connector with SyncEngine, mission_seed_from_issue, etc.


asyncio.run(integrate_linear())

The shape above is identical for linear, jira, github, and gitlab — only the value of the provider argument and the upstream Nango configuration change.

Hybrid metadata contract

Discovery results carry a hybrid metadata schema. Top-level dataclass fields (id, name, display, kind, provider, stable_ref, display_name, etc.) are the canonical source of truth for IDs and labels — they are always present and never duplicated into metadata. The two metadata containers (provider_context on workspaces and routing_metadata on resources) carry provider-native keys plus four optional normalized keys:

Container Key Type Semantic
provider_context workspace_handle str | None Short URL-safe slug for the workspace
provider_context workspace_url str | None Browser URL pointing to the workspace home
routing_metadata display_key str | None Short human-readable resource identifier
routing_metadata resource_url str | None Browser URL pointing to the resource home

These keys are optional: when a provider has no meaningful value, the key is absent. When present, the type is enforced at runtime by discover_workspaces / discover_resources — a malformed payload raises DiscoveryContractError. Hosts that want generic, cross-provider behavior should prefer top-level fields and the four normalized keys over provider-native extras.

See kitty-specs/006-hosted-discovery-contract-hardening/contracts/discovery-contract.md for the full contract specification, data-model.md for the per-provider expected-keys table, and quickstart.md for the full host integration walkthrough.

Deprecated shim modules

The modules spec_kitty_tracker.workspace_discovery and spec_kitty_tracker.resource_discovery are compatibility-only shims. They re-export the canonical symbols (discover_workspaces, discover_resources, DiscoveredWorkspace, DiscoveredResource, DiscoveryResult) for backward compatibility with older pinned downstream consumers, but they emit a DeprecationWarning on import. New code should import from the top-level package:

# Deprecated — emits DeprecationWarning
from spec_kitty_tracker.workspace_discovery import discover_workspaces
from spec_kitty_tracker.resource_discovery import discover_resources

# Canonical
from spec_kitty_tracker import discover_workspaces, discover_resources

The shim modules will not be removed in a backward-incompatible way without a separate migration mission.

Local/Native and Test Connectors

For connectors that do not use SaaS-hosted transport, construct them directly.

Local/Native (Beads)

from spec_kitty_tracker import BeadsConnector, BeadsConnectorConfig

config = BeadsConnectorConfig(workspace="my-project", cwd="/path/to/beads")
connector = BeadsConnector(config)

Test/Reference (InMemory)

from spec_kitty_tracker import InMemoryConnector

connector = InMemoryConnector(name="test", workspace="test-ws")

Migration/Advanced (direct SaaS-backed, non-product path)

# For test, migration, or advanced SDK use cases only.
# The Spec Kitty CLI/SaaS product path should use create_hosted_connector().
from spec_kitty_tracker import LinearConnector, LinearConnectorConfig

config = LinearConnectorConfig(api_key="test-key", team_id="TEAM-1")
connector = LinearConnector(config)

Design Notes

  1. The core contract is intentionally tracker-agnostic.
  2. OwnershipPolicy makes source-of-truth behavior explicit and auditable.
  3. SyncEngine supports pull, push, and bidirectional sync with conflict records.
  4. Capability flags gate optional features (e.g., hierarchy, dependencies, webhooks).
  5. Mission updates are idempotent and persist decision references back to source issues.

Docs

  1. Architecture
  2. Connector Contract
  3. Doctrine Policy Modes
  4. P0 Provider Matrix
  5. WP04 Contract Alignment Notes
  6. WP11 Sync Core Notes

Metadata

Release files for spec-kitty-tracker 0.5.2

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for spec-kitty-tracker 0.5.2
File Size Uploaded
spec_kitty_tracker-0.5.2.tar.gz 134.0 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for spec-kitty-tracker 0.5.2
File Interpreter ABI Platform
spec_kitty_tracker-0.5.2-py3-none-any.whl Python 3 none any Details

Total release size: 219.0 kB

Release files / spec_kitty_tracker-0.5.2.tar.gz

Download URL spec_kitty_tracker-0.5.2.tar.gz
Size 134.0 kB
Tags Source
SHA-256 checksum
How to use checksums
192f56a9247f0e4ea90002a6d9ef25f5c47efa54989c33766c1aca8966eaa1f8
BLAKE2b-256 checksum
How to use checksums
cb8a60677d184ffc0817c818e369c7a37a62063f5372d3370ad25208a666822e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 1, 2026.

Transparency log

Release files / spec_kitty_tracker-0.5.2-py3-none-any.whl

Download URL spec_kitty_tracker-0.5.2-py3-none-any.whl
Size 85.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
f614967c214cc29b5664572d7bbbee44706ceff22b8106cfa462f3bb84fcc39f
BLAKE2b-256 checksum
How to use checksums
5bac69f37ae99cacb50cb4a6e76386fb83704cc3657f2a949630bf626a96dd1d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 1, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.5.2 This release

2 release files

0.4.3

2 release files

0.4.2

2 release files

0.4.1

2 release files

0.3.0

2 release files

0.2.0

2 release files

0.1.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page