-
Notifications
You must be signed in to change notification settings - Fork 3
docs(trackers): publish compact adapter profiles #772
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -85,13 +85,14 @@ touches a layer, check that its slice (and the linked documents) still holds. | |
|
|
||
| ### 5. Integration — tracker adapters (tracker-specific code lives only here) | ||
|
|
||
| - GitHub Project V2: `packages/tracker-github` (including the adapter-owned linked-PR canonical-subject extension; opaque `nativeRef` data never crosses into orchestration). Source issue state and linked-PR metadata remain distinct from Project workflow status; it returns all active scoped items and derives GitHub assignment, repository-scope, pickup-label, and fork-PR eligibility as `dispatchable` with an explainable reason. | ||
| - GitHub Project V2: `packages/tracker-github` (including the adapter-owned linked-PR canonical-subject extension; opaque `nativeRef` data never crosses into orchestration). Source issue state and linked-PR metadata remain distinct from Project workflow status; candidate polling excludes terminal states and can include other non-terminal items. It derives GitHub assignment, repository-scope, pickup-label, and fork-PR eligibility as `dispatchable` with an explainable reason. | ||
| - Linear: `packages/tracker-linear`; it derives provider-native assignment eligibility as the normalized `dispatchable` contract. Its pickup labels instead filter the candidate list before dispatch, so label-ineligible Linear items are not retained for explain surfaces as `dispatchable: false` records. This adapter-side label filtering is a repository-level divergence from the upstream scheduler-owned label boundary and differs from the GitHub adapter's retained, reason-bearing records. | ||
| - File-based (E2E only): `packages/tracker-file`; fixtures may set `dispatchable` and `dispatchReason` directly to exercise the adapter-neutral scheduler gate. | ||
| - GitHub-specific planning/approval/PR-reporting extensions: `packages/extension-github-workflow` | ||
| - GitHub adapter profile: `packages/tracker-github/README.md`. Its synthetic | ||
| `Archived` state is a GitHub-specific implementation choice, not normalized | ||
| Symphony core behavior. | ||
| - Compact adapter profiles: [GitHub Project](trackers/github-project.md), | ||
| [Linear](trackers/linear.md), and [file](trackers/file.md). GitHub's | ||
| synthetic `Archived` state is a GitHub-specific implementation choice, not | ||
| normalized Symphony core behavior. | ||
|
Comment on lines
+92
to
+95
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P3 — repointing §5 away from the package README leaves two competing "adapter profile" sources. This hunk moves the canonical pointer from
They agree with the new profiles today, so nothing is wrong right now — but two independently-editable copies of the same normalization rules is exactly how the drift this epic is closing got started. Suggest reducing the package READMEs to a one-line pointer at the canonical Generated by Claude Code
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Reduced both package READMEs to pointers to their canonical compact profiles, removing duplicate normalization and pagination prose. |
||
|
|
||
| ### 6. Observability — events and status surfaces | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,49 @@ | ||
| # File adapter profile | ||
|
|
||
| This compact §11.2 profile documents the `file` adapter used by local and | ||
| Docker E2E environments. It is intentionally a fixture adapter, not a | ||
| production tracker provider. | ||
|
|
||
| ## Configuration and scope | ||
|
|
||
| | Item | Contract | | ||
| | -------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | `tracker.kind` | `file` | | ||
| | Provider scope | A local JSON issue array selected by `tracker.settings.issuesPath`; it has no project, board, team, or remote repository selection. | | ||
| | Provider keys | `tracker.provider.path` is accepted and validated for forward compatibility but is currently inert. The effective required path is `tracker.settings.issuesPath`. Unknown provider keys are preserved by core parsing. | | ||
| | Defaults | Lifecycle defaults are `Status`, active `Ready`/`In Progress`, terminal `Done`/`Cancelled`, and no planning states. Unless explicitly configured, blocker checks use the first active state (`Ready` with these defaults). | | ||
| | Credentials | `secretEnvironmentNames()` is empty: file fixtures require no secret or environment variable. | | ||
| | Validation | A supplied `path` must be a non-empty string and fails configuration parsing with `WorkflowValidationError` when invalid. A missing runtime `issuesPath` instead fails each adapter operation with a plain `Error`. | | ||
|
|
||
| There is no provider request pagination, page size, timeout, or rate limit: the | ||
| adapter reads one local JSON file per operation. A missing file yields an empty | ||
| candidate list. An unreadable file, malformed JSON, or a top-level non-array is | ||
| a file/response failure rather than a silent partial read. | ||
|
|
||
| ## Normalized issue contract | ||
|
|
||
| | Field or condition | File mapping | | ||
| | ----------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | ||
| | `id` and `native_ref` | Fixture `id` is retained unchanged. `native_ref` is retained when supplied; otherwise it defaults to `{ itemId: tracker.itemId }` only for a string item ID, or `{ itemId: null }`. | | ||
| | State, labels, priority, timestamps | Valid fixture values are preserved; fixture authors supply their normalized form. The adapter does not invent provider-specific state, label, priority, or timestamp mappings. | | ||
| | `dispatchable` | An omitted value defaults to `true`; `false` and `dispatchReason` can be supplied to exercise the adapter-neutral scheduler gate. Pickup-label filtering remains available through the shared normalized filter. | | ||
| | Malformed and optional fields | Entries lacking the checked shape (`id`, `identifier`, `state`, object `repository`, object `tracker`, or a boolean `dispatchable` when supplied) are skipped with a diagnostic. This check is not exhaustive: for example, a missing `title` is currently not rejected. Optional `assigneeId` defaults to `null`. | | ||
|
|
||
| ## Native tools and errors | ||
|
|
||
| The file adapter exposes no provider-native agent tool. | ||
|
|
||
| This table records the Symphony §11.4 target mapping required by §11.2. The | ||
| file adapter does not currently emit any structured error `category`; its | ||
| configuration parse failure is `WorkflowValidationError` and its runtime file | ||
| failures are plain errors. | ||
|
|
||
| | Provider-native failure | §11.4 target category | Current adapter surface | | ||
| | ---------------------------------------------- | -------------------------------------- | ------------------------------------ | | ||
| | File read failure or invalid JSON payload | `tracker_request` / `tracker_response` | Plain read/JSON error | | ||
| | No corresponding provider HTTP status | `tracker_status` | Not applicable | | ||
| | No pagination | `tracker_pagination` | Not applicable | | ||
| | No remote quota | `tracker_rate_limited` | Not applicable | | ||
| | Invalid `tracker.provider.path` | `invalid_tracker_config` | Parse-time `WorkflowValidationError` | | ||
| | Missing required `tracker.settings.issuesPath` | `invalid_tracker_config` | Per-operation plain `Error` | | ||
| | No credentials required | `missing_tracker_secret` | Not applicable | |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,67 @@ | ||
| # GitHub Project adapter profile | ||
|
|
||
| This is the compact profile required by Symphony specification §11.2 for the | ||
| `github-project` tracker adapter. It describes the repository's current | ||
| Integration-layer behavior; it does not add provider semantics to core. | ||
|
|
||
| ## Configuration and scope | ||
|
|
||
| | Item | Contract | | ||
| | -------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | `tracker.kind` | `github-project` | | ||
| | Provider scope | `tracker.provider.project_id` selects one GitHub Project V2. `repository` is derived from each issue; the optional runtime repository filter and `--assigned-only` are adapter dispatchability rules. | | ||
| | Provider keys | `project_id`, `endpoint`, `state_field`, `priority_field`, `priority`, `pickup_labels`, `active_states`, `terminal_states`, `blocker_check_states`, and `planning_states`. Unknown provider keys are preserved by core configuration parsing. Flat `tracker.*` keys are deprecated compatibility aliases. | | ||
| | Defaults | Lifecycle defaults are `Status`, active `Todo`/`In Progress`, terminal `Done`, and no planning states. Unless explicitly configured, blocker checks use the first active state (`Todo` with these defaults). Priority is `null` unless the configured `priority` policy or deprecated `priority_field` resolves a value. | | ||
| | Credentials | `GITHUB_GRAPHQL_TOKEN` is the polling credential. `secretEnvironmentNames()` declares `GH_TOKEN`, `GH_ENTERPRISE_TOKEN`, `GITHUB_TOKEN`, and `GITHUB_GRAPHQL_TOKEN`; these names are removed from agent-child inheritance. | | ||
| | Validation | Declared string keys must be non-empty; `endpoint` must be an HTTP(S) URL; state lists must contain non-empty strings; `priority` and `pickup_labels` must be objects. Missing `project_id` or `GITHUB_GRAPHQL_TOKEN` prevents adapter use. See the error table for the §11.4 target mapping and current unnormalized surfaces. | | ||
|
|
||
| Candidate polling excludes configured terminal states with the GitHub `query` | ||
| argument; it can therefore return non-terminal items outside `active_states`. | ||
| State and ID refreshes are unfiltered so terminal items remain reconcilable. The | ||
| adapter uses cursor pagination with a default page size of 25; a next-page | ||
| response without a cursor is an integrity failure. Each GraphQL request has a | ||
| 30-second default timeout (or the configured positive `timeoutMs`). It records | ||
| GraphQL rate-limit metadata and applies the shared GitHub policy: it honors the | ||
| later of `Retry-After` and a known primary reset. If that required wait exceeds | ||
| 60 seconds, the request is not retried and the rate-limit error is surfaced. | ||
|
|
||
| The [2026-07-19 GitHub API rate-limit audit](../reports/2026-07-19-github-api-rate-limit-audit.md) | ||
| records why the profile keeps the page size at 25 and measures query cost by | ||
| returned `rateLimit` data rather than guessing from page count. | ||
|
|
||
| ## Normalized issue contract | ||
|
|
||
| | Field or condition | GitHub Project mapping | | ||
| | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | ||
| | `id` | GitHub Issue or Pull Request **node ID** (not the Project item ID). | | ||
| | `native_ref` | Opaque adapter data containing Project `itemId`, content type, source state, and linked-PR metadata. The Project item ID is used for Project mutations and never replaces `id`. | | ||
| | State | The configured Project status field is used. An archived Project item becomes synthetic `Archived`; this is a GitHub-specific extension, not a core state. | | ||
| | Labels and priority | Labels are trimmed, lowercased, deduplicated, and sorted. Priority is explicit Project-field or label policy output; unmapped/disabled values are `null`. | | ||
| | Timestamps | Issue and Project-item timestamps are parsed to canonical RFC 3339/ISO 8601; invalid or absent values become `null`. The newer item timestamp wins. | | ||
| | `dispatchable` | Starts `true`, then the adapter applies assignment, repository, fork-PR, pickup-label, and configured blocker rules. Ineligible items remain listed with `dispatchable: false` and an explainable `dispatchReason`. | | ||
| | Malformed and optional fields | Candidate polling silently drops non-Issue/PR Project content. Items missing the configured state are skipped with a structured event and recorded in `skippedItems`; requested ID refreshes fail instead. Optional body, labels, assignee, timestamps, priority, and linked metadata normalize to `null`, empty collections, or documented defaults rather than leaking provider shape. | | ||
|
|
||
| ## Native tool | ||
|
|
||
| The host-side [`github_graphql`](github.md) tool accepts one GraphQL query or | ||
| mutation plus optional variables/operation name. It is always advertised for | ||
| repository and PR operations, but its credential and opaque `native_ref` stay | ||
| in the host process. See the linked tool document for scope and safe-use rules. | ||
|
|
||
| ## Error-category mapping | ||
|
|
||
| This table records the Symphony §11.4 target mapping required by §11.2. Today, | ||
| only `tracker_pagination` is emitted as a structured adapter `category`. | ||
| The other rows describe the intended normalized category; current failures are | ||
| surfaced as `GitHubTrackerHttpError`, `GitHubTrackerQueryError`, or | ||
|
Comment on lines
+55
to
+56
Owner
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit — this intro now under-counts the table below it by one. Non-blocking; noting it only because the round-2 fix created it. The sentence enumerates three current surfaces as if exhaustive, but the Suggested: Take it or leave it — approving either way. Generated by Claude Code |
||
| `WorkflowValidationError` and are not yet normalized. | ||
|
|
||
| | Provider-native failure | §11.4 target category | Current adapter surface | | ||
| | ------------------------------------------------------ | ------------------------ | --------------------------------------------------------------------------- | | ||
| | Network/transport failure or timeout | `tracker_request` | `GitHubTrackerHttpError` | | ||
| | Non-rate-limited HTTP status | `tracker_status` | `GitHubTrackerHttpError` | | ||
| | Invalid JSON, GraphQL errors, or missing expected data | `tracker_response` | `GitHubTrackerQueryError` | | ||
| | Missing cursor or incomplete cursor traversal | `tracker_pagination` | Structured `tracker_pagination` | | ||
| | GitHub quota exhaustion or `Retry-After` response | `tracker_rate_limited` | `GitHubGraphQLRateLimitError` (core `TrackerRateLimitError`, no `category`) | | ||
| | Invalid Project/provider setting or configured shape | `invalid_tracker_config` | `WorkflowValidationError` | | ||
| | No resolved GitHub credential | `missing_tracker_secret` | Adapter initialization/configure error | | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P3 —
trackers/github.mdjust fell out of the living-documents index but is still live.The old
- [GitHub](trackers/github.md) — github_graphqlentry was replaced rather than joined by the new one. The file still exists and is still referenced from two places:docs/trackers/github-project.md:45— "The host-sidegithub_graphqltool…"docs/adr/2026-08-28_agent-tool-isolation.md:217Issue #674's acceptance criteria is registration in the
docs/README.mdliving documents list, so leaving a still-referenced tracker doc unlisted works against the point of the change. Note the asymmetry too:linear.mdkeeps its tool contract inline and stays indexed, while GitHub's tool contract now lives in an unindexed sibling. Either addgithub.mdback as its own entry, or fold it intogithub-project.mdthe waylinear.mddoes.Generated by Claude Code
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Restored docs/trackers/github.md as a separate GitHub tool entry in the living-documents index.