coord-mcp-server 0.27.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. coord_mcp_server-0.27.1/.env.example +22 -0
  2. coord_mcp_server-0.27.1/.github/ISSUE_TEMPLATE/bug_report.yml +85 -0
  3. coord_mcp_server-0.27.1/.github/ISSUE_TEMPLATE/config.yml +8 -0
  4. coord_mcp_server-0.27.1/.github/ISSUE_TEMPLATE/feature_request.yml +37 -0
  5. coord_mcp_server-0.27.1/.github/PULL_REQUEST_TEMPLATE.md +29 -0
  6. coord_mcp_server-0.27.1/.github/dependabot.yml +46 -0
  7. coord_mcp_server-0.27.1/.github/workflows/README.md +293 -0
  8. coord_mcp_server-0.27.1/.github/workflows/ci.yml +124 -0
  9. coord_mcp_server-0.27.1/.github/workflows/release.yml +257 -0
  10. coord_mcp_server-0.27.1/.gitignore +28 -0
  11. coord_mcp_server-0.27.1/Dockerfile +65 -0
  12. coord_mcp_server-0.27.1/LICENSE +201 -0
  13. coord_mcp_server-0.27.1/PKG-INFO +446 -0
  14. coord_mcp_server-0.27.1/README.md +395 -0
  15. coord_mcp_server-0.27.1/compose.yaml +14 -0
  16. coord_mcp_server-0.27.1/coordination/__init__.py +14 -0
  17. coord_mcp_server-0.27.1/coordination/assets.py +360 -0
  18. coord_mcp_server-0.27.1/coordination/cli.py +252 -0
  19. coord_mcp_server-0.27.1/coordination/cli_doctor.py +638 -0
  20. coord_mcp_server-0.27.1/coordination/cli_init.py +444 -0
  21. coord_mcp_server-0.27.1/coordination/cli_ops.py +482 -0
  22. coord_mcp_server-0.27.1/coordination/cli_outbox.py +535 -0
  23. coord_mcp_server-0.27.1/coordination/cli_shared.py +171 -0
  24. coord_mcp_server-0.27.1/coordination/cli_start.py +195 -0
  25. coord_mcp_server-0.27.1/coordination/cli_update_notice.py +123 -0
  26. coord_mcp_server-0.27.1/coordination/cli_upgrade.py +159 -0
  27. coord_mcp_server-0.27.1/coordination/config.py +95 -0
  28. coord_mcp_server-0.27.1/coordination/dashboard.py +1477 -0
  29. coord_mcp_server-0.27.1/coordination/db.py +2772 -0
  30. coord_mcp_server-0.27.1/coordination/deps.py +10 -0
  31. coord_mcp_server-0.27.1/coordination/engine.py +311 -0
  32. coord_mcp_server-0.27.1/coordination/logging.py +147 -0
  33. coord_mcp_server-0.27.1/coordination/main.py +681 -0
  34. coord_mcp_server-0.27.1/coordination/mcp_server.py +1019 -0
  35. coord_mcp_server-0.27.1/coordination/metrics.py +194 -0
  36. coord_mcp_server-0.27.1/coordination/overlap_symbols.py +486 -0
  37. coord_mcp_server-0.27.1/coordination/ownership.py +524 -0
  38. coord_mcp_server-0.27.1/coordination/repo_config.py +44 -0
  39. coord_mcp_server-0.27.1/coordination/schemas.py +273 -0
  40. coord_mcp_server-0.27.1/coordination/service.py +1873 -0
  41. coord_mcp_server-0.27.1/coordination/symbols/__init__.py +218 -0
  42. coord_mcp_server-0.27.1/coordination/symbols/go_regex.py +180 -0
  43. coord_mcp_server-0.27.1/coordination/symbols/go_treesitter.py +319 -0
  44. coord_mcp_server-0.27.1/coordination/symbols/py_regex.py +328 -0
  45. coord_mcp_server-0.27.1/coordination/symbols/py_treesitter.py +379 -0
  46. coord_mcp_server-0.27.1/coordination/symbols/ts_regex.py +443 -0
  47. coord_mcp_server-0.27.1/coordination/symbols/ts_treesitter.py +394 -0
  48. coord_mcp_server-0.27.1/deploy/k8s/README.md +56 -0
  49. coord_mcp_server-0.27.1/deploy/k8s/prod/README.md +54 -0
  50. coord_mcp_server-0.27.1/docs/README.md +17 -0
  51. coord_mcp_server-0.27.1/docs/api-reference.md +569 -0
  52. coord_mcp_server-0.27.1/docs/architecture.md +185 -0
  53. coord_mcp_server-0.27.1/docs/deployment.md +215 -0
  54. coord_mcp_server-0.27.1/docs/design/multi-namespace.md +597 -0
  55. coord_mcp_server-0.27.1/docs/design/roadmap.md +97 -0
  56. coord_mcp_server-0.27.1/docs/design/sub-file-claims.md +383 -0
  57. coord_mcp_server-0.27.1/docs/getting-started.md +132 -0
  58. coord_mcp_server-0.27.1/docs/integrations/claude-code.md +121 -0
  59. coord_mcp_server-0.27.1/docs/integrations/codex-cli.md +110 -0
  60. coord_mcp_server-0.27.1/docs/quickstart.md +92 -0
  61. coord_mcp_server-0.27.1/docs/troubleshooting.md +188 -0
  62. coord_mcp_server-0.27.1/docs/usage-guide.md +454 -0
  63. coord_mcp_server-0.27.1/pyproject.toml +131 -0
  64. coord_mcp_server-0.27.1/scripts/auto-rebase.sh +21 -0
  65. coord_mcp_server-0.27.1/scripts/completions/_coord +79 -0
  66. coord_mcp_server-0.27.1/scripts/completions/coord.bash +90 -0
  67. coord_mcp_server-0.27.1/scripts/git-hooks/pre-push +34 -0
  68. coord_mcp_server-0.27.1/scripts/smoke-test.sh +65 -0
  69. coord_mcp_server-0.27.1/scripts/worktree-add.sh +26 -0
  70. coord_mcp_server-0.27.1/templates/.codex/config.toml.example +10 -0
  71. coord_mcp_server-0.27.1/templates/.coordination/MODULE_GUIDE.md +13 -0
  72. coord_mcp_server-0.27.1/templates/.coordination/eslint.restricted-imports.example.cjs +21 -0
  73. coord_mcp_server-0.27.1/templates/.coordination/hooks/pre-push +52 -0
  74. coord_mcp_server-0.27.1/templates/.coordination/owners.example.yaml +25 -0
  75. coord_mcp_server-0.27.1/templates/.cursor/mcp.json.example +12 -0
  76. coord_mcp_server-0.27.1/templates/.cursor/rules/coordination.mdc +21 -0
  77. coord_mcp_server-0.27.1/templates/.mcp.json.example +12 -0
  78. coord_mcp_server-0.27.1/templates/AGENTS.md.snippet.md +31 -0
  79. coord_mcp_server-0.27.1/templates/CLAUDE.md.snippet.md +33 -0
  80. coord_mcp_server-0.27.1/templates/MERGE_QUEUE.md +26 -0
  81. coord_mcp_server-0.27.1/templates/README.md +33 -0
  82. coord_mcp_server-0.27.1/templates/github-coordination-semantic.yml +31 -0
@@ -0,0 +1,22 @@
1
+ # Core server settings
2
+ COORD_AUTH_TOKEN=change-me
3
+ COORD_DATABASE_PATH=data/coordination.db
4
+ COORD_HOST=0.0.0.0
5
+ COORD_PORT=8080
6
+ COORD_LOG_LEVEL=info
7
+
8
+ # Optional local-only bypass. Prefer leaving this false.
9
+ COORD_ALLOW_INSECURE_NO_AUTH=false
10
+
11
+ # Optional: point at an application repo checkout for better overlap matching.
12
+ # COORD_REPO_ROOT=/absolute/path/to/your-app
13
+
14
+ # Claim sizing and cleanup
15
+ COORD_MAX_CLAIM_FILES=100
16
+ COORD_MAX_CLAIM_RATIO=0.2
17
+ COORD_CLEANUP_INTERVAL_SEC=900
18
+ COORD_DEFAULT_TTL_HOURS=4
19
+ COORD_SHARED_TTL_HOURS=2
20
+
21
+ # MCP bridge defaults
22
+ COORD_API_URL=http://127.0.0.1:8080
@@ -0,0 +1,85 @@
1
+ name: Bug report
2
+ description: Report a defect in coord (HTTP API, MCP bridge, dashboard, CLI).
3
+ title: "[bug]: "
4
+ labels: ["bug"]
5
+ body:
6
+ - type: markdown
7
+ attributes:
8
+ value: |
9
+ Thanks for filing a report. Please run `coord doctor` first; it catches the majority of setup issues without needing a maintainer.
10
+
11
+ - type: input
12
+ id: version
13
+ attributes:
14
+ label: coord version
15
+ description: Output of `coord --version` or the tag you installed.
16
+ placeholder: "0.27.0"
17
+ validations:
18
+ required: true
19
+
20
+ - type: dropdown
21
+ id: mcp_client
22
+ attributes:
23
+ label: MCP client
24
+ description: Which agent harness are you driving coord with?
25
+ options:
26
+ - Claude Code
27
+ - Codex CLI
28
+ - Cursor
29
+ - Other (describe in steps to reproduce)
30
+ validations:
31
+ required: true
32
+
33
+ - type: input
34
+ id: os
35
+ attributes:
36
+ label: OS
37
+ description: Operating system and version. Include container runtime if applicable.
38
+ placeholder: "macOS 14.5 / Ubuntu 24.04 / Docker 27 on Debian 12"
39
+ validations:
40
+ required: true
41
+
42
+ - type: textarea
43
+ id: steps
44
+ attributes:
45
+ label: Steps to reproduce
46
+ description: Minimal sequence of commands or API calls that triggers the bug.
47
+ placeholder: |
48
+ 1. `coord start --background`
49
+ 2. From a second shell: `coord init --tool claude --mode local --yes`
50
+ 3. ...
51
+ validations:
52
+ required: true
53
+
54
+ - type: textarea
55
+ id: expected
56
+ attributes:
57
+ label: Expected behavior
58
+ description: What did you expect to happen?
59
+
60
+ - type: textarea
61
+ id: actual
62
+ attributes:
63
+ label: Actual behavior
64
+ description: What actually happened? Include exit codes, error messages, or HTTP status.
65
+ validations:
66
+ required: true
67
+
68
+ - type: textarea
69
+ id: logs
70
+ attributes:
71
+ label: Logs
72
+ description: Relevant output from the service log, MCP stderr, or `coord doctor`. Redact any token values before pasting.
73
+ render: shell
74
+
75
+ - type: checkboxes
76
+ id: checks
77
+ attributes:
78
+ label: Have you checked
79
+ options:
80
+ - label: I searched existing issues (open and closed) for a duplicate.
81
+ required: true
82
+ - label: I read `./docs/troubleshooting.md`.
83
+ required: true
84
+ - label: I ran `coord doctor` and reviewed its output.
85
+ required: true
@@ -0,0 +1,8 @@
1
+ blank_issues_enabled: false
2
+ contact_links:
3
+ - name: Setup or runtime troubleshooting
4
+ url: https://github.com/amittell/coord/blob/main/docs/troubleshooting.md
5
+ about: Most setup and runtime issues are covered here. Try this before opening a bug report.
6
+ - name: Report a security vulnerability
7
+ url: https://github.com/amittell/coord/security/advisories/new
8
+ about: Do not file a public issue for security reports. Open a private advisory instead. See SECURITY.md for the policy.
@@ -0,0 +1,37 @@
1
+ name: Feature request
2
+ description: Suggest a new capability or behaviour change for coord.
3
+ title: "[feat]: "
4
+ labels: ["enhancement"]
5
+ body:
6
+ - type: markdown
7
+ attributes:
8
+ value: |
9
+ Before filing, please skim `./docs/design/roadmap.md` to see whether your idea overlaps something already queued. If it does, comment on the linked issue instead of opening a new one.
10
+
11
+ - type: textarea
12
+ id: problem
13
+ attributes:
14
+ label: Problem you're trying to solve
15
+ description: What's the underlying user need? Describe the friction, not the proposed implementation.
16
+ validations:
17
+ required: true
18
+
19
+ - type: textarea
20
+ id: solution
21
+ attributes:
22
+ label: Proposed solution
23
+ description: What would the API, MCP tool, dashboard surface, or CLI command look like? Sketch the smallest version that solves the problem.
24
+ validations:
25
+ required: true
26
+
27
+ - type: textarea
28
+ id: alternatives
29
+ attributes:
30
+ label: Alternatives considered
31
+ description: What workarounds exist today, and why are they insufficient?
32
+
33
+ - type: textarea
34
+ id: roadmap
35
+ attributes:
36
+ label: Roadmap fit
37
+ description: Optional. If you've checked `./docs/design/roadmap.md`, note any related queued item or explain where this would slot in.
@@ -0,0 +1,29 @@
1
+ ## Summary
2
+
3
+ <!-- One or two sentences on what this PR does and why. -->
4
+
5
+ ## Changes
6
+
7
+ <!-- Bullet list of files / modules touched and the nature of the change. -->
8
+
9
+ -
10
+ -
11
+
12
+ ## Test plan
13
+
14
+ <!-- Commands you ran locally to verify the change. -->
15
+
16
+ ```
17
+ make check
18
+ ```
19
+
20
+ ## Related issue
21
+
22
+ <!-- Optional. Use `Closes #N` to auto-close on merge. -->
23
+
24
+ ## Checklist
25
+
26
+ - [ ] `make check` passes locally (ruff + mypy + pytest)
27
+ - [ ] `CHANGELOG.md` updated under `## [Unreleased]` if the change is user-visible
28
+ - [ ] Any new third-party GitHub Actions are pinned by full commit SHA with a trailing `# vX.Y.Z` comment
29
+ - [ ] No production code / tests / deploy / templates files touched outside the PR's stated scope
@@ -0,0 +1,46 @@
1
+ version: 2
2
+ updates:
3
+ # Python dependencies declared in the root pyproject.toml. Group
4
+ # patch + minor bumps together so they land as a single PR per week;
5
+ # majors still open as their own PR for focused review.
6
+ - package-ecosystem: "pip"
7
+ directory: "/"
8
+ schedule:
9
+ interval: "weekly"
10
+ day: "monday"
11
+ open-pull-requests-limit: 5
12
+ groups:
13
+ python-minor-and-patch:
14
+ update-types:
15
+ - "minor"
16
+ - "patch"
17
+
18
+ # GitHub Actions used in .github/workflows/. Dependabot is allowed
19
+ # to auto-update action SHAs here, which is the whole point of the
20
+ # pinning discipline. Group patch + minor bumps; majors stay
21
+ # individual.
22
+ - package-ecosystem: "github-actions"
23
+ directory: "/"
24
+ schedule:
25
+ interval: "weekly"
26
+ day: "monday"
27
+ open-pull-requests-limit: 5
28
+ groups:
29
+ actions-minor-and-patch:
30
+ update-types:
31
+ - "minor"
32
+ - "patch"
33
+
34
+ # Dockerfile base image. Container ships via the release workflow,
35
+ # so keep base-image updates visible. Weekly cadence, all bumps in
36
+ # one PR.
37
+ - package-ecosystem: "docker"
38
+ directory: "/"
39
+ schedule:
40
+ interval: "weekly"
41
+ day: "monday"
42
+ open-pull-requests-limit: 2
43
+ groups:
44
+ docker-all:
45
+ patterns:
46
+ - "*"
@@ -0,0 +1,293 @@
1
+ # GitHub Actions workflows
2
+
3
+ This directory contains the CI and release automation for `coord`.
4
+
5
+ ## `ci.yml`
6
+
7
+ Runs on every push to `main` and every pull request.
8
+
9
+ Jobs:
10
+
11
+ 1. `test` (matrix: Ubuntu + Python 3.11, Ubuntu + Python 3.12,
12
+ Ubuntu + Python 3.14, macOS + Python 3.12, Windows + Python 3.12) -
13
+ installs the project with dev extras, runs `ruff check .`, then
14
+ `pytest -q`. All runners use `bash` as the default shell so quoting
15
+ and pipelines behave consistently on Windows (via Git for Windows'
16
+ bash). The 3.14 row matches the Python version shipped in the
17
+ release container (`python:3.14-slim`) so CI exercises what we
18
+ ship; 3.11 is kept as the baseline per `requires-python = ">=3.11"`
19
+ in `pyproject.toml`. 3.14 is Ubuntu-only to avoid tripling runner
20
+ usage for incremental coverage.
21
+ 2. `lint-workflows` - runs `actionlint` (via `reviewdog/action-actionlint@v1`)
22
+ over `.github/workflows/` to catch YAML and Actions mistakes.
23
+ 3. `type-check` - runs `mypy coordination` against Python 3.12 with a
24
+ permissive starter config (see `[tool.mypy]` in `pyproject.toml`).
25
+ Tests are excluded, missing import stubs are ignored, and strict
26
+ mode is off. The intent is to guard against regressions on the
27
+ production package without blocking PRs on unrelated type-noise.
28
+ Tighten the config in follow-up PRs as the codebase gains annotations.
29
+ 4. `docker-build` - depends on `test`; builds the container image with
30
+ `docker/build-push-action@v6` (no push, `load: true`) to prove the
31
+ Dockerfile still works. Uses GitHub Actions cache (`type=gha`).
32
+
33
+ `test`, `lint-workflows`, and `type-check` run in parallel; `docker-build`
34
+ is gated on `test` so a broken test suite does not waste Docker build
35
+ minutes.
36
+
37
+ Concurrency: superseded CI runs on the same ref are cancelled
38
+ (`cancel-in-progress: true`).
39
+
40
+ Permissions: `contents: read` only.
41
+
42
+ ## `release.yml`
43
+
44
+ Runs on git tag pushes matching `v*`, and can also be triggered manually
45
+ via `workflow_dispatch`.
46
+
47
+ Jobs:
48
+
49
+ - `publish-image` - builds a multi-arch (`linux/amd64`, `linux/arm64`)
50
+ container image and pushes to `ghcr.io/<owner>/coord`, emits an
51
+ SPDX SBOM plus SLSA provenance attestations (BuildKit-native and
52
+ GitHub-native), signs the image keyless with cosign, and (for real
53
+ tag pushes) creates a GitHub Release with auto-generated notes.
54
+ - `publish-pypi` - on real tag pushes only (skipped on
55
+ `workflow_dispatch`), builds the sdist + wheel, validates that the
56
+ tag matches `pyproject.toml`'s `version` field, and publishes to
57
+ PyPI via OIDC trusted publishing (no API token stored in repo
58
+ secrets). Bootstrap requires a one-time pending-publisher
59
+ registration on PyPI; see "PyPI trusted publishing" below.
60
+
61
+ Tag behaviour:
62
+
63
+ | Trigger | Pushed tags | GitHub Release |
64
+ |---------------------|-------------------------------------|----------------|
65
+ | `push` (git tag) | `:<tag>` and `:latest` | yes |
66
+ | `workflow_dispatch` | `:<inputs.version>` only (no latest)| no |
67
+
68
+ The `workflow_dispatch` path deliberately does not push `:latest` so
69
+ release candidates and ad-hoc rebuilds cannot clobber the production
70
+ `:latest` pointer.
71
+
72
+ Concurrency: release runs are NOT cancelled in flight
73
+ (`cancel-in-progress: false`) so a half-pushed multi-arch image cannot
74
+ be left behind by a second run.
75
+
76
+ ### Required permissions
77
+
78
+ Set on the `release.yml` workflow itself:
79
+
80
+ - `contents: write` - `softprops/action-gh-release@v2` needs this to
81
+ create the GitHub Release.
82
+ - `packages: write` - push to GHCR.
83
+ - `id-token: write` - OIDC token for
84
+ `actions/attest-build-provenance@v2`.
85
+ - `attestations: write` - store the attestation on the repo.
86
+
87
+ ### Required repository secrets
88
+
89
+ None beyond the default `GITHUB_TOKEN`. GHCR login uses
90
+ `${{ secrets.GITHUB_TOKEN }}` directly.
91
+
92
+ ### GitHub Enterprise
93
+
94
+ The release workflow defaults to `ghcr.io/<owner>/coord`, but both the
95
+ registry host and the repo path are overridable through repo or org
96
+ Actions variables (no secret values required):
97
+
98
+ - `IMAGE_REGISTRY` - registry hostname, for example
99
+ `containers.ghe.example.com`. If unset, defaults to `ghcr.io`.
100
+ - `IMAGE_REPO` - path under the registry, for example
101
+ `platform/coord`. If unset, defaults to
102
+ `<github.repository_owner>/coord`.
103
+
104
+ Set them under Settings -> Secrets and variables -> Actions -> Variables.
105
+ The workflow resolves them at runtime and falls back to the GHCR
106
+ defaults when unset, so upstream behavior is preserved.
107
+
108
+ Login continues to use `${{ secrets.GITHUB_TOKEN }}` against the
109
+ configured registry. Some GHE setups require a separate Personal Access
110
+ Token (PAT) with `write:packages` (or the enterprise-equivalent) scope
111
+ if the default `GITHUB_TOKEN` cannot push to your registry; in that
112
+ case, store the PAT as a repo secret and swap `password:` in the
113
+ `docker/login-action` step accordingly. This is a common GHE gotcha
114
+ rather than a hard requirement - try the default token first.
115
+
116
+ `actions/attest-build-provenance@v2` requires **GitHub Enterprise
117
+ Server 3.10 or later** with the attestations feature enabled, OR a
118
+ public repository, OR a private repository owned by an organization
119
+ on a plan that supports attestations. It **does NOT work on personal
120
+ (user-owned) private repositories** - the step fails with
121
+ "Feature not available for user-owned private repositories".
122
+
123
+ In any of those unsupported cases, set the repo or org Actions
124
+ variable `SKIP_ATTESTATION=true` and the provenance step will be
125
+ skipped cleanly. The image push, BuildKit-native SBOM/provenance
126
+ attestations, and cosign signing are all unaffected. This project's
127
+ own `amittell/coord` repo (user-owned private) sets this var.
128
+
129
+ ```
130
+ Settings -> Secrets and variables -> Actions -> Variables
131
+ SKIP_ATTESTATION = true
132
+ ```
133
+
134
+ ### SBOM and image attestations
135
+
136
+ Every successful release now emits four independent supply-chain
137
+ signals against the pushed image:
138
+
139
+ 1. **BuildKit SBOM attestation** (SPDX JSON) - produced by
140
+ `docker/build-push-action` with `sbom: true`. Attached to the image
141
+ manifest as an OCI referrer and retrievable with
142
+ `docker buildx imagetools inspect <ref> --format '{{json .SBOM}}'`
143
+ or `cosign download sbom <ref>`.
144
+ 2. **BuildKit SLSA provenance attestation** (`mode=max`) - produced by
145
+ the same action with `provenance: mode=max`. Includes the full build
146
+ invocation, materials, and builder identity. Inspect with
147
+ `docker buildx imagetools inspect <ref> --format '{{json .Provenance}}'`.
148
+ 3. **GitHub-native provenance attestation** - produced by
149
+ `actions/attest-build-provenance@v2` and stored in the repo-level
150
+ attestations store (separate from the OCI-attached BuildKit output).
151
+ Verifiable with `gh attestation verify`.
152
+ 4. **Cosign keyless signature** - see below.
153
+
154
+ The BuildKit attestations (1, 2) and the GitHub attestation (3) are
155
+ independent. Keeping both is intentional: the OCI-attached artifacts
156
+ travel with the image across registry mirrors, while the GitHub-stored
157
+ attestation is queryable through the GitHub API without pulling the
158
+ image.
159
+
160
+ ### Cosign signing
161
+
162
+ The workflow signs the pushed image with `cosign sign --yes` in
163
+ keyless mode. The signing identity is the workflow's ephemeral OIDC
164
+ token (no long-lived keys), and the signature is anchored in the
165
+ public Rekor transparency log.
166
+
167
+ Verify a signed image against this repository's workflow identity:
168
+
169
+ ```
170
+ cosign verify \
171
+ --certificate-identity-regexp '^https://github.com/<owner>/<repo>/\.github/workflows/release\.yml@' \
172
+ --certificate-oidc-issuer 'https://token.actions.githubusercontent.com' \
173
+ ghcr.io/<owner>/coord@sha256:<digest>
174
+ ```
175
+
176
+ Replace `<owner>/<repo>` and the digest to match your fork and target
177
+ image. The `--certificate-identity-regexp` value pins the expected
178
+ signer to this exact workflow file - accept no others.
179
+
180
+ The `cosign-installer` action is pinned by commit SHA
181
+ (`sigstore/cosign-installer@d7543c93...` = v3.10.0) and fetches a
182
+ fixed cosign CLI release (currently v3.0.6) via `cosign-release:`.
183
+
184
+ ### Disabling individual signals
185
+
186
+ Each signing or attestation step is gated on a repo or org Actions
187
+ variable so operators can disable any one without editing the
188
+ workflow:
189
+
190
+ | Variable | Effect when set to `true` |
191
+ |----------|---------------------------|
192
+ | `SKIP_ATTESTATION` | Skips `actions/attest-build-provenance`. Required on GHES older than 3.10 or where the attestations feature is disabled. |
193
+ | `SKIP_SIGNING` | Skips both `Install cosign` and `Sign image with cosign (keyless)`. Use on runners that cannot reach the Sigstore public good instance (Fulcio / Rekor). |
194
+
195
+ The BuildKit SBOM and BuildKit provenance attestations are emitted
196
+ unconditionally by the build step and have no skip variable; they are
197
+ produced locally by BuildKit and pushed alongside the image manifest,
198
+ so they do not depend on external services.
199
+
200
+ ```
201
+ Settings -> Secrets and variables -> Actions -> Variables
202
+ SKIP_ATTESTATION = true # older GHES without attestations API
203
+ SKIP_SIGNING = true # environments without Sigstore access
204
+ ```
205
+
206
+ ### PyPI trusted publishing
207
+
208
+ The `publish-pypi` job uploads to PyPI without an API token by
209
+ exchanging GitHub's OIDC identity for a short-lived PyPI token at
210
+ publish time. This requires a one-time pending-publisher
211
+ registration on PyPI before the first publish succeeds.
212
+
213
+ Bootstrap steps (one-time, by a project maintainer with PyPI
214
+ account access):
215
+
216
+ 1. Sign in at `https://pypi.org`.
217
+ 2. Go to "Your account" -> "Publishing" -> "Add a new pending
218
+ publisher" (the form is at
219
+ `https://pypi.org/manage/account/publishing/`).
220
+ 3. Fill in:
221
+ - PyPI Project Name: `coord-mcp-server`
222
+ - Owner: `amittell`
223
+ - Repository name: `coord`
224
+ - Workflow name: `release.yml`
225
+ - Environment name: `pypi`
226
+ 4. Save. PyPI now trusts this exact workflow file on this exact
227
+ repo to publish under the named project. The next tagged push
228
+ that runs the workflow will create the project on first
229
+ publish.
230
+
231
+ GitHub side: create an environment called `pypi` under Settings
232
+ -> Environments. Optionally add deployment protection rules
233
+ (required reviewers, deployment branches, etc.) -- the
234
+ environment binding alone is enough for OIDC to work.
235
+
236
+ After bootstrap, every `git tag v0.X.Y && git push --tags`
237
+ triggers a build + publish. The workflow refuses to publish if
238
+ the tag version does not match the `version` field in
239
+ `pyproject.toml`, so an accidental tag without a version bump is
240
+ caught at the verification step.
241
+
242
+ To pause PyPI publishing without disabling the whole release:
243
+ remove the pending publisher in PyPI, or change the workflow's
244
+ environment to a name that PyPI does not trust. The `:latest`
245
+ container image and GitHub Release still publish from
246
+ `publish-image`.
247
+
248
+ ### Manual trigger (workflow_dispatch)
249
+
250
+ Via the GitHub UI: Actions tab -> `release` -> Run workflow -> supply
251
+ `version` (for example `v0.1.0-rc1`).
252
+
253
+ Via `gh`:
254
+
255
+ ```
256
+ gh workflow run release.yml -f version=v0.1.0-rc1
257
+ ```
258
+
259
+ The resulting image will be pushed as
260
+ `ghcr.io/<owner>/coord:v0.1.0-rc1` and `:latest` will not be touched.
261
+
262
+ ## Verification
263
+
264
+ Locally:
265
+
266
+ ```
267
+ python -c "import yaml; list(yaml.safe_load_all(open('.github/workflows/ci.yml')))"
268
+ python -c "import yaml; list(yaml.safe_load_all(open('.github/workflows/release.yml')))"
269
+ ```
270
+
271
+ If you have `actionlint` installed:
272
+
273
+ ```
274
+ actionlint .github/workflows/*.yml
275
+ ```
276
+
277
+ ## Pinning policy
278
+
279
+ Third-party actions are pinned to a full 40-char commit SHA, with the
280
+ readable version tag preserved in a trailing comment, for example:
281
+
282
+ ```
283
+ uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
284
+ ```
285
+
286
+ This blocks tag-retargeting supply-chain attacks: the workflow runs the
287
+ exact commit reviewed at pin time, regardless of what the upstream tag
288
+ later points at.
289
+
290
+ Updates are handled by Dependabot (`.github/dependabot.yml`), which
291
+ opens a weekly grouped PR that bumps both the SHA and the trailing
292
+ version comment in lockstep. The same config also keeps `requirements.txt`
293
+ and the Dockerfile base image current.
@@ -0,0 +1,124 @@
1
+ name: ci
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ pull_request:
8
+
9
+ permissions:
10
+ contents: read
11
+
12
+ # Cancel superseded CI runs on the same ref to save minutes.
13
+ concurrency:
14
+ group: ${{ github.workflow }}-${{ github.ref }}
15
+ cancel-in-progress: true
16
+
17
+ jobs:
18
+ test:
19
+ name: test (${{ matrix.os }} py${{ matrix.python-version }})
20
+ runs-on: ${{ matrix.os }}
21
+ strategy:
22
+ fail-fast: false
23
+ matrix:
24
+ include:
25
+ - os: ubuntu-latest
26
+ python-version: "3.11"
27
+ - os: ubuntu-latest
28
+ python-version: "3.12"
29
+ - os: ubuntu-latest
30
+ python-version: "3.14"
31
+ - os: macos-latest
32
+ python-version: "3.12"
33
+ - os: windows-latest
34
+ python-version: "3.12"
35
+ # Force bash on all runners so quoting and pipelines behave consistently.
36
+ # Windows GHA runners ship Git for Windows' bash at /usr/bin/bash.
37
+ defaults:
38
+ run:
39
+ shell: bash
40
+ steps:
41
+ - name: Check out repository
42
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
43
+
44
+ - name: Set up Python ${{ matrix.python-version }}
45
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
46
+ with:
47
+ python-version: ${{ matrix.python-version }}
48
+ cache: pip
49
+ cache-dependency-path: |
50
+ pyproject.toml
51
+ requirements.txt
52
+
53
+ - name: Install project with dev extras
54
+ run: |
55
+ python -m pip install --upgrade pip
56
+ python -m pip install ".[dev]"
57
+
58
+ - name: Lint with ruff
59
+ run: ruff check .
60
+
61
+ - name: Run tests
62
+ run: pytest -q
63
+
64
+ lint-workflows:
65
+ name: lint workflows (actionlint)
66
+ runs-on: ubuntu-latest
67
+ steps:
68
+ - name: Check out repository
69
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
70
+
71
+ - name: Run actionlint
72
+ uses: reviewdog/action-actionlint@6fb7acc99f4a1008869fa8a0f09cfca740837d9d # v1.72.0
73
+ with:
74
+ reporter: github-check
75
+ level: error
76
+ fail_level: error
77
+
78
+ type-check:
79
+ name: type check (mypy)
80
+ runs-on: ubuntu-latest
81
+ steps:
82
+ - name: Check out repository
83
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
84
+
85
+ - name: Set up Python 3.12
86
+ uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0
87
+ with:
88
+ python-version: "3.12"
89
+ cache: pip
90
+ cache-dependency-path: |
91
+ pyproject.toml
92
+ requirements.txt
93
+
94
+ - name: Install project with dev extras
95
+ run: |
96
+ python -m pip install --upgrade pip
97
+ python -m pip install ".[dev]"
98
+
99
+ - name: Run mypy
100
+ run: mypy coordination
101
+
102
+ docker-build:
103
+ name: docker build smoke
104
+ needs: test
105
+ runs-on: ubuntu-latest
106
+ steps:
107
+ - name: Check out repository
108
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
109
+
110
+ - name: Set up Docker Buildx
111
+ uses: docker/setup-buildx-action@d7f5e7f509e45cec5c76c4d5afdd7de93d0b3df5 # v4.1.0
112
+
113
+ # Build the image once (amd64 only, no push) to catch Dockerfile
114
+ # regressions on every PR without spending time on multi-arch.
115
+ - name: Build image (no push)
116
+ uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0
117
+ with:
118
+ context: .
119
+ file: ./Dockerfile
120
+ push: false
121
+ load: true
122
+ tags: coord:ci
123
+ cache-from: type=gha
124
+ cache-to: type=gha,mode=max