MarshalFleet 0.2.0__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.
- marshalfleet-0.2.0/.claude-plugin/marketplace.json +25 -0
- marshalfleet-0.2.0/.claude-plugin/plugin.json +39 -0
- marshalfleet-0.2.0/.github/ISSUE_TEMPLATE/bug_report.yml +71 -0
- marshalfleet-0.2.0/.github/ISSUE_TEMPLATE/config.yml +5 -0
- marshalfleet-0.2.0/.github/ISSUE_TEMPLATE/feature_request.yml +40 -0
- marshalfleet-0.2.0/.github/dependabot.yml +23 -0
- marshalfleet-0.2.0/.github/pull_request_template.md +16 -0
- marshalfleet-0.2.0/.github/workflows/ci.yml +49 -0
- marshalfleet-0.2.0/.github/workflows/release.yml +108 -0
- marshalfleet-0.2.0/.gitignore +58 -0
- marshalfleet-0.2.0/.python-version +1 -0
- marshalfleet-0.2.0/CHANGELOG.md +1056 -0
- marshalfleet-0.2.0/CLAUDE.md +130 -0
- marshalfleet-0.2.0/CODE_OF_CONDUCT.md +116 -0
- marshalfleet-0.2.0/CONTRIBUTING.md +189 -0
- marshalfleet-0.2.0/LICENSE +21 -0
- marshalfleet-0.2.0/PKG-INFO +207 -0
- marshalfleet-0.2.0/README.md +171 -0
- marshalfleet-0.2.0/SECURITY.md +188 -0
- marshalfleet-0.2.0/SETUP.md +246 -0
- marshalfleet-0.2.0/assets/README.md +35 -0
- marshalfleet-0.2.0/assets/architecture.svg +124 -0
- marshalfleet-0.2.0/assets/logo-dark.svg +8 -0
- marshalfleet-0.2.0/assets/logo-mono.svg +8 -0
- marshalfleet-0.2.0/assets/logo.svg +8 -0
- marshalfleet-0.2.0/assets/wordmark.png +0 -0
- marshalfleet-0.2.0/docs/chauffeur-future.md +163 -0
- marshalfleet-0.2.0/docs/config.md +182 -0
- marshalfleet-0.2.0/docs/design.md +557 -0
- marshalfleet-0.2.0/docs/mcp-tools.md +573 -0
- marshalfleet-0.2.0/docs/model-playbook.md +129 -0
- marshalfleet-0.2.0/docs/nerds.md +143 -0
- marshalfleet-0.2.0/docs/sources.md +32 -0
- marshalfleet-0.2.0/docs/status.md +188 -0
- marshalfleet-0.2.0/docs/usage.md +534 -0
- marshalfleet-0.2.0/examples/README.md +42 -0
- marshalfleet-0.2.0/examples/adversarial_review.py +107 -0
- marshalfleet-0.2.0/examples/benchmark-output.md +42 -0
- marshalfleet-0.2.0/examples/library_quickstart.py +40 -0
- marshalfleet-0.2.0/examples/multi_workspace.py +117 -0
- marshalfleet-0.2.0/examples/per_client_env.yaml +20 -0
- marshalfleet-0.2.0/examples/pipelined_review.py +95 -0
- marshalfleet-0.2.0/examples/read_paths.py +91 -0
- marshalfleet-0.2.0/examples/workflows/build-adapters.yaml +73 -0
- marshalfleet-0.2.0/examples/workflows/compare.yaml +14 -0
- marshalfleet-0.2.0/examples/workflows/review.yaml +19 -0
- marshalfleet-0.2.0/fleet.config.example.yaml +136 -0
- marshalfleet-0.2.0/fleet.config.yaml.bak +95 -0
- marshalfleet-0.2.0/pyproject.toml +118 -0
- marshalfleet-0.2.0/skills/marshal-adversarial-review/SKILL.md +121 -0
- marshalfleet-0.2.0/skills/marshal-benchmark/SKILL.md +45 -0
- marshalfleet-0.2.0/skills/marshal-orchestrate/SKILL.md +187 -0
- marshalfleet-0.2.0/skills/marshal-plan-consensus/SKILL.md +91 -0
- marshalfleet-0.2.0/skills/marshal-review-gate/SKILL.md +103 -0
- marshalfleet-0.2.0/skills/marshal-workflow/SKILL.md +82 -0
- marshalfleet-0.2.0/src/marshal_engine/__init__.py +45 -0
- marshalfleet-0.2.0/src/marshal_engine/backends/__init__.py +10 -0
- marshalfleet-0.2.0/src/marshal_engine/backends/antigravity.py +633 -0
- marshalfleet-0.2.0/src/marshal_engine/backends/base.py +383 -0
- marshalfleet-0.2.0/src/marshal_engine/backends/claude_code.py +224 -0
- marshalfleet-0.2.0/src/marshal_engine/backends/codex.py +201 -0
- marshalfleet-0.2.0/src/marshal_engine/backends/command_code.py +206 -0
- marshalfleet-0.2.0/src/marshal_engine/backends/cursor.py +620 -0
- marshalfleet-0.2.0/src/marshal_engine/backends/goose.py +442 -0
- marshalfleet-0.2.0/src/marshal_engine/backends/opencode.py +452 -0
- marshalfleet-0.2.0/src/marshal_engine/budgets.py +460 -0
- marshalfleet-0.2.0/src/marshal_engine/cli.py +962 -0
- marshalfleet-0.2.0/src/marshal_engine/config.py +564 -0
- marshalfleet-0.2.0/src/marshal_engine/doctor.py +505 -0
- marshalfleet-0.2.0/src/marshal_engine/eastrouter.py +290 -0
- marshalfleet-0.2.0/src/marshal_engine/env.py +191 -0
- marshalfleet-0.2.0/src/marshal_engine/fleet.py +2566 -0
- marshalfleet-0.2.0/src/marshal_engine/layout.py +41 -0
- marshalfleet-0.2.0/src/marshal_engine/logs.py +87 -0
- marshalfleet-0.2.0/src/marshal_engine/mcp_server.py +867 -0
- marshalfleet-0.2.0/src/marshal_engine/py.typed +0 -0
- marshalfleet-0.2.0/src/marshal_engine/registry.py +41 -0
- marshalfleet-0.2.0/src/marshal_engine/retry.py +88 -0
- marshalfleet-0.2.0/src/marshal_engine/scaffold.py +147 -0
- marshalfleet-0.2.0/src/marshal_engine/service.py +979 -0
- marshalfleet-0.2.0/src/marshal_engine/state.py +303 -0
- marshalfleet-0.2.0/src/marshal_engine/teams.py +680 -0
- marshalfleet-0.2.0/src/marshal_engine/types.py +209 -0
- marshalfleet-0.2.0/src/marshal_engine/usage.py +429 -0
- marshalfleet-0.2.0/src/marshal_engine/workflow.py +479 -0
- marshalfleet-0.2.0/src/marshal_engine/workspaces.py +767 -0
- marshalfleet-0.2.0/src/marshal_engine/worktree.py +710 -0
- marshalfleet-0.2.0/tests/conftest.py +47 -0
- marshalfleet-0.2.0/tests/test_antigravity.py +86 -0
- marshalfleet-0.2.0/tests/test_antigravity_backend.py +742 -0
- marshalfleet-0.2.0/tests/test_backend_contract.py +112 -0
- marshalfleet-0.2.0/tests/test_base_run.py +382 -0
- marshalfleet-0.2.0/tests/test_budgets.py +553 -0
- marshalfleet-0.2.0/tests/test_cancel.py +197 -0
- marshalfleet-0.2.0/tests/test_ci_workflows.py +207 -0
- marshalfleet-0.2.0/tests/test_claude_code_backend.py +224 -0
- marshalfleet-0.2.0/tests/test_cli.py +1122 -0
- marshalfleet-0.2.0/tests/test_client_env.py +165 -0
- marshalfleet-0.2.0/tests/test_codex_backend.py +199 -0
- marshalfleet-0.2.0/tests/test_command_code_backend.py +272 -0
- marshalfleet-0.2.0/tests/test_config.py +626 -0
- marshalfleet-0.2.0/tests/test_cursor_backend.py +772 -0
- marshalfleet-0.2.0/tests/test_docs_sync.py +162 -0
- marshalfleet-0.2.0/tests/test_doctor.py +627 -0
- marshalfleet-0.2.0/tests/test_eastrouter.py +273 -0
- marshalfleet-0.2.0/tests/test_edge_cases.py +469 -0
- marshalfleet-0.2.0/tests/test_env.py +522 -0
- marshalfleet-0.2.0/tests/test_fleet.py +4819 -0
- marshalfleet-0.2.0/tests/test_goose_backend.py +388 -0
- marshalfleet-0.2.0/tests/test_invariants.py +352 -0
- marshalfleet-0.2.0/tests/test_logs.py +94 -0
- marshalfleet-0.2.0/tests/test_mcp_server.py +809 -0
- marshalfleet-0.2.0/tests/test_opencode_backend.py +586 -0
- marshalfleet-0.2.0/tests/test_retry.py +105 -0
- marshalfleet-0.2.0/tests/test_run_many_then.py +414 -0
- marshalfleet-0.2.0/tests/test_service.py +1476 -0
- marshalfleet-0.2.0/tests/test_skills.py +54 -0
- marshalfleet-0.2.0/tests/test_state.py +354 -0
- marshalfleet-0.2.0/tests/test_teams.py +680 -0
- marshalfleet-0.2.0/tests/test_teams_service.py +379 -0
- marshalfleet-0.2.0/tests/test_usage.py +496 -0
- marshalfleet-0.2.0/tests/test_workflow.py +525 -0
- marshalfleet-0.2.0/tests/test_workflow_service.py +117 -0
- marshalfleet-0.2.0/tests/test_workspaces.py +1595 -0
- marshalfleet-0.2.0/tests/test_worktree.py +951 -0
- marshalfleet-0.2.0/uv.lock +1243 -0
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "marshal",
|
|
3
|
+
"description": "Marshal: run a fleet of headless coding agents in parallel, each isolated in its own git worktree, with per-provider cost tracking and measured routing benchmarks.",
|
|
4
|
+
"owner": {
|
|
5
|
+
"name": "chiruu12",
|
|
6
|
+
"url": "https://github.com/chiruu12"
|
|
7
|
+
},
|
|
8
|
+
"plugins": [
|
|
9
|
+
{
|
|
10
|
+
"name": "marshal",
|
|
11
|
+
"source": "./",
|
|
12
|
+
"description": "Drive a fleet of headless coding agents (Cursor, OpenCode, Codex, Antigravity, Claude Code, Command Code, Goose) from one driver: parallel git-worktree isolation, per-provider cost tracking, and measured routing benchmarks. Bundles the marshal-orchestrate, marshal-benchmark, marshal-workflow, marshal-review-gate, marshal-plan-consensus, and marshal-adversarial-review skills and the Marshal MCP server (see docs/mcp-tools.md; multi-workspace).",
|
|
13
|
+
"version": "0.2.0",
|
|
14
|
+
"license": "MIT",
|
|
15
|
+
"homepage": "https://github.com/chiruu12/marshal",
|
|
16
|
+
"keywords": [
|
|
17
|
+
"ai",
|
|
18
|
+
"coding-agents",
|
|
19
|
+
"orchestration",
|
|
20
|
+
"mcp",
|
|
21
|
+
"cost-tracking"
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "marshal",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "The control plane for AI coding agents. Drive a fleet of headless workers (Cursor, OpenCode, Codex, Antigravity, Claude Code, Command Code, Goose) from one driver: each task runs isolated in its own git worktree, in parallel, with per-provider cost tracking and measured routing benchmarks. Bundles the marshal-orchestrate, marshal-benchmark, marshal-workflow, marshal-review-gate, marshal-plan-consensus, and marshal-adversarial-review skills plus the Marshal MCP server (see docs/mcp-tools.md; multi-workspace: one server drives several repos, registered in ~/.marshal/workspaces.yaml).",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "chiruu12",
|
|
7
|
+
"url": "https://github.com/chiruu12"
|
|
8
|
+
},
|
|
9
|
+
"homepage": "https://github.com/chiruu12/marshal",
|
|
10
|
+
"repository": "https://github.com/chiruu12/marshal",
|
|
11
|
+
"license": "MIT",
|
|
12
|
+
"keywords": [
|
|
13
|
+
"ai",
|
|
14
|
+
"coding-agents",
|
|
15
|
+
"orchestration",
|
|
16
|
+
"mcp",
|
|
17
|
+
"fleet",
|
|
18
|
+
"cost-tracking",
|
|
19
|
+
"cursor",
|
|
20
|
+
"opencode",
|
|
21
|
+
"codex",
|
|
22
|
+
"antigravity",
|
|
23
|
+
"claude-code"
|
|
24
|
+
],
|
|
25
|
+
"mcpServers": {
|
|
26
|
+
"marshal": {
|
|
27
|
+
"command": "uv",
|
|
28
|
+
"args": [
|
|
29
|
+
"run",
|
|
30
|
+
"--project",
|
|
31
|
+
"${CLAUDE_PLUGIN_ROOT}",
|
|
32
|
+
"--extra",
|
|
33
|
+
"mcp",
|
|
34
|
+
"marshal",
|
|
35
|
+
"mcp"
|
|
36
|
+
]
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
name: Bug report
|
|
2
|
+
description: Report a problem with Marshal
|
|
3
|
+
labels: ["bug"]
|
|
4
|
+
body:
|
|
5
|
+
- type: markdown
|
|
6
|
+
attributes:
|
|
7
|
+
value: |
|
|
8
|
+
Thanks for filing a bug. Please do **not** report security vulnerabilities here - see SECURITY.md.
|
|
9
|
+
- type: input
|
|
10
|
+
id: version
|
|
11
|
+
attributes:
|
|
12
|
+
label: Marshal version / commit
|
|
13
|
+
description: Output of `uv run marshal --version` or the git commit SHA.
|
|
14
|
+
validations:
|
|
15
|
+
required: true
|
|
16
|
+
- type: input
|
|
17
|
+
id: environment
|
|
18
|
+
attributes:
|
|
19
|
+
label: OS and Python version
|
|
20
|
+
placeholder: "e.g. macOS 14.5, Python 3.12.11"
|
|
21
|
+
validations:
|
|
22
|
+
required: true
|
|
23
|
+
- type: dropdown
|
|
24
|
+
id: backend
|
|
25
|
+
attributes:
|
|
26
|
+
label: Which backend(s)?
|
|
27
|
+
multiple: true
|
|
28
|
+
options:
|
|
29
|
+
- cursor
|
|
30
|
+
- opencode
|
|
31
|
+
- codex
|
|
32
|
+
- antigravity
|
|
33
|
+
- claude-code
|
|
34
|
+
- not backend-specific
|
|
35
|
+
validations:
|
|
36
|
+
required: true
|
|
37
|
+
- type: input
|
|
38
|
+
id: auth
|
|
39
|
+
attributes:
|
|
40
|
+
label: How is that backend authenticated?
|
|
41
|
+
placeholder: "e.g. opencode auth login / codex login / OPENAI_API_KEY env"
|
|
42
|
+
- type: textarea
|
|
43
|
+
id: doctor
|
|
44
|
+
attributes:
|
|
45
|
+
label: Output of `uv run marshal doctor`
|
|
46
|
+
render: text
|
|
47
|
+
- type: textarea
|
|
48
|
+
id: config
|
|
49
|
+
attributes:
|
|
50
|
+
label: fleet.config.yaml (redact any secrets / client names you don't want public)
|
|
51
|
+
render: yaml
|
|
52
|
+
- type: textarea
|
|
53
|
+
id: repro
|
|
54
|
+
attributes:
|
|
55
|
+
label: Steps to reproduce
|
|
56
|
+
placeholder: |
|
|
57
|
+
1. ...
|
|
58
|
+
2. ...
|
|
59
|
+
validations:
|
|
60
|
+
required: true
|
|
61
|
+
- type: textarea
|
|
62
|
+
id: expected_actual
|
|
63
|
+
attributes:
|
|
64
|
+
label: Expected vs actual behavior
|
|
65
|
+
validations:
|
|
66
|
+
required: true
|
|
67
|
+
- type: textarea
|
|
68
|
+
id: logs
|
|
69
|
+
attributes:
|
|
70
|
+
label: Relevant logs / error output
|
|
71
|
+
render: text
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
name: Feature request
|
|
2
|
+
description: Suggest an improvement or new capability
|
|
3
|
+
labels: ["enhancement"]
|
|
4
|
+
body:
|
|
5
|
+
- type: textarea
|
|
6
|
+
id: problem
|
|
7
|
+
attributes:
|
|
8
|
+
label: Problem
|
|
9
|
+
description: What are you trying to do, and what's getting in the way?
|
|
10
|
+
validations:
|
|
11
|
+
required: true
|
|
12
|
+
- type: textarea
|
|
13
|
+
id: proposal
|
|
14
|
+
attributes:
|
|
15
|
+
label: Proposed solution
|
|
16
|
+
validations:
|
|
17
|
+
required: true
|
|
18
|
+
- type: textarea
|
|
19
|
+
id: alternatives
|
|
20
|
+
attributes:
|
|
21
|
+
label: Alternatives considered
|
|
22
|
+
- type: dropdown
|
|
23
|
+
id: area
|
|
24
|
+
attributes:
|
|
25
|
+
label: Area
|
|
26
|
+
options:
|
|
27
|
+
- new backend adapter
|
|
28
|
+
- MCP tool surface
|
|
29
|
+
- usage / cost tracking
|
|
30
|
+
- benchmark / report
|
|
31
|
+
- worktree / integrate
|
|
32
|
+
- CLI
|
|
33
|
+
- docs
|
|
34
|
+
- other
|
|
35
|
+
validations:
|
|
36
|
+
required: true
|
|
37
|
+
- type: textarea
|
|
38
|
+
id: context
|
|
39
|
+
attributes:
|
|
40
|
+
label: Additional context
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
version: 2
|
|
2
|
+
updates:
|
|
3
|
+
# GitHub Actions used by the CI/release workflows.
|
|
4
|
+
- package-ecosystem: "github-actions"
|
|
5
|
+
directory: "/"
|
|
6
|
+
schedule:
|
|
7
|
+
interval: "weekly"
|
|
8
|
+
commit-message:
|
|
9
|
+
prefix: "ci"
|
|
10
|
+
groups:
|
|
11
|
+
actions:
|
|
12
|
+
patterns: ["*"]
|
|
13
|
+
|
|
14
|
+
# Python dependencies, resolved from pyproject.toml + uv.lock.
|
|
15
|
+
- package-ecosystem: "uv"
|
|
16
|
+
directory: "/"
|
|
17
|
+
schedule:
|
|
18
|
+
interval: "weekly"
|
|
19
|
+
commit-message:
|
|
20
|
+
prefix: "deps"
|
|
21
|
+
groups:
|
|
22
|
+
python:
|
|
23
|
+
patterns: ["*"]
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
## Summary
|
|
2
|
+
|
|
3
|
+
<!-- One line describing WHAT this change ships (not how, not iteration history). -->
|
|
4
|
+
|
|
5
|
+
## Details
|
|
6
|
+
|
|
7
|
+
<!-- What changed and why, if not obvious from the summary. -->
|
|
8
|
+
|
|
9
|
+
## Checklist
|
|
10
|
+
|
|
11
|
+
- [ ] Ran the gate locally: `uv run pytest -q && uv run ruff check src tests && uv run mypy`
|
|
12
|
+
- [ ] Updated docs (`README.md` / `docs/` / `CLAUDE.md`) if behavior changed
|
|
13
|
+
- [ ] Added a `## [Unreleased]` entry to `CHANGELOG.md`
|
|
14
|
+
- [ ] No internal process / planning notes / cost figures in commits, PR text, or docs
|
|
15
|
+
- [ ] **New backend?** Added contract tests for `build_invocation` and `map_permission`, and
|
|
16
|
+
registered the factory in `registry.py`
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
workflow_dispatch:
|
|
8
|
+
|
|
9
|
+
concurrency:
|
|
10
|
+
group: ci-${{ github.ref }}
|
|
11
|
+
cancel-in-progress: true
|
|
12
|
+
|
|
13
|
+
permissions:
|
|
14
|
+
contents: read
|
|
15
|
+
|
|
16
|
+
jobs:
|
|
17
|
+
gate:
|
|
18
|
+
name: gate (${{ matrix.os }} py${{ matrix.python-version }})
|
|
19
|
+
runs-on: ${{ matrix.os }}
|
|
20
|
+
strategy:
|
|
21
|
+
fail-fast: false
|
|
22
|
+
matrix:
|
|
23
|
+
os: ["ubuntu-latest"]
|
|
24
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
25
|
+
# Also exercise the POSIX process-group logic (killpg / start_new_session / worktrees) on
|
|
26
|
+
# macOS, the platform Marshal is developed on, for one Python version.
|
|
27
|
+
include:
|
|
28
|
+
- os: "macos-latest"
|
|
29
|
+
python-version: "3.12"
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@v7
|
|
32
|
+
|
|
33
|
+
- name: Install uv
|
|
34
|
+
uses: astral-sh/setup-uv@v9.0.0
|
|
35
|
+
with:
|
|
36
|
+
python-version: ${{ matrix.python-version }}
|
|
37
|
+
enable-cache: true
|
|
38
|
+
|
|
39
|
+
- name: Sync dependencies
|
|
40
|
+
run: uv sync --frozen --extra mcp --extra dev
|
|
41
|
+
|
|
42
|
+
- name: Tests (with coverage gate)
|
|
43
|
+
run: uv run pytest -q --cov=marshal_engine --cov-report=term-missing --cov-fail-under=90
|
|
44
|
+
|
|
45
|
+
- name: Lint (ruff)
|
|
46
|
+
run: uv run ruff check src tests
|
|
47
|
+
|
|
48
|
+
- name: Type-check (mypy strict)
|
|
49
|
+
run: uv run mypy
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
name: Release
|
|
2
|
+
|
|
3
|
+
# Publish to PyPI only when a GitHub Release is published, or via manual
|
|
4
|
+
# workflow_dispatch. Never on push to a branch / tag.
|
|
5
|
+
#
|
|
6
|
+
# Maintainer setup (one-time) for PyPI Trusted Publishing (OIDC) — no API token:
|
|
7
|
+
# 1. Create a GitHub Environment named `PYPI` on this repo AND configure protection
|
|
8
|
+
# rules (required reviewers, deployment branches limited to `v*` tags).
|
|
9
|
+
# Treat this as REQUIRED, not optional. `workflow_dispatch` runs the workflow
|
|
10
|
+
# file as it exists ON THE SELECTED REF, so the tag/version guard below — and
|
|
11
|
+
# the gate above it — can be edited away on an attacker-controlled branch. PyPI
|
|
12
|
+
# validates the workflow *filename* and environment, never the step contents.
|
|
13
|
+
# Environment protection is the only gate that lives outside the ref being built.
|
|
14
|
+
# 2. On https://pypi.org/manage/account/publishing/ (or the project’s Publishing
|
|
15
|
+
# settings once it exists), add a Trusted Publisher:
|
|
16
|
+
# PyPI project name: MarshalFleet # must equal [project].name in pyproject.toml
|
|
17
|
+
# Owner: chiruu12
|
|
18
|
+
# Repository: marshal
|
|
19
|
+
# Workflow: release.yml
|
|
20
|
+
# Environment: PYPI
|
|
21
|
+
# 3. First upload can create the project if you registered a *pending* publisher
|
|
22
|
+
# before the name exists. Confirm the name is still free (or switch
|
|
23
|
+
# pyproject `[project].name` to the fallback) before cutting the first release.
|
|
24
|
+
|
|
25
|
+
on:
|
|
26
|
+
release:
|
|
27
|
+
types: [published]
|
|
28
|
+
workflow_dispatch:
|
|
29
|
+
|
|
30
|
+
permissions:
|
|
31
|
+
contents: read
|
|
32
|
+
id-token: write # required for PyPI Trusted Publishing (OIDC)
|
|
33
|
+
|
|
34
|
+
# A PyPI version can never be replaced, so two runs racing the same upload is not a retry - it is
|
|
35
|
+
# one confusing partial failure. Never cancel in progress: killing a run mid-upload is worse.
|
|
36
|
+
concurrency:
|
|
37
|
+
group: release-${{ github.ref }}
|
|
38
|
+
cancel-in-progress: false
|
|
39
|
+
|
|
40
|
+
jobs:
|
|
41
|
+
release:
|
|
42
|
+
runs-on: ubuntu-latest
|
|
43
|
+
environment:
|
|
44
|
+
# MUST match the GitHub Environment name EXACTLY - they are case-sensitive, and a mismatch
|
|
45
|
+
# silently resolves to a different (non-existent) environment, so its protection rules would
|
|
46
|
+
# not apply and its secrets would not be in scope. The configured environment is `PYPI`.
|
|
47
|
+
name: PYPI
|
|
48
|
+
url: https://pypi.org/p/MarshalFleet
|
|
49
|
+
steps:
|
|
50
|
+
# Every action in this job is pinned by commit SHA. The job holds `id-token: write`, so any
|
|
51
|
+
# step in it can reach the publishing credential - a mutable tag anywhere here is a way in.
|
|
52
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7
|
|
53
|
+
|
|
54
|
+
- name: Install uv
|
|
55
|
+
uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0
|
|
56
|
+
with:
|
|
57
|
+
python-version: "3.12"
|
|
58
|
+
enable-cache: true
|
|
59
|
+
|
|
60
|
+
- name: Gate
|
|
61
|
+
run: |
|
|
62
|
+
uv sync --frozen --extra mcp --extra dev
|
|
63
|
+
uv run pytest -q --cov=marshal_engine --cov-fail-under=90 && uv run ruff check src tests && uv run mypy
|
|
64
|
+
|
|
65
|
+
- name: Build sdist + wheel
|
|
66
|
+
run: uv build
|
|
67
|
+
|
|
68
|
+
# A publish is irreversible: PyPI never lets a version be replaced. `workflow_dispatch` lets
|
|
69
|
+
# a maintainer pick ANY ref, so without this a mis-selected branch publishes whatever it
|
|
70
|
+
# happens to contain under whatever version it happens to declare. Refuse unless the ref is a
|
|
71
|
+
# tag and the built version is exactly that tag - which also catches the far likelier mistake
|
|
72
|
+
# of cutting a release without bumping `[project].version`.
|
|
73
|
+
- name: Refuse to publish a ref whose version is not the tag
|
|
74
|
+
# A git tag may legally contain shell metacharacters, so `github.ref_name` reaches the
|
|
75
|
+
# script through `env` and is only ever read as "$TAG". Interpolating it into the script
|
|
76
|
+
# body would let a crafted tag run commands here - in a job holding `id-token: write`,
|
|
77
|
+
# before this very check.
|
|
78
|
+
env:
|
|
79
|
+
TAG: ${{ github.ref_name }}
|
|
80
|
+
REF_TYPE: ${{ github.ref_type }}
|
|
81
|
+
run: |
|
|
82
|
+
set -euo pipefail
|
|
83
|
+
if [ "$REF_TYPE" != "tag" ]; then
|
|
84
|
+
echo "::error::refusing to publish from $REF_TYPE '$TAG' - release from a tag"
|
|
85
|
+
exit 1
|
|
86
|
+
fi
|
|
87
|
+
# Read the version off the BUILT WHEEL, not the source tree. hatchling builds from
|
|
88
|
+
# `[project].version` in pyproject.toml, so checking `marshal_engine.__version__` would
|
|
89
|
+
# verify a value the artifact does not necessarily carry: if the two drift, the guard
|
|
90
|
+
# passes while PyPI receives a different version than the tag claims.
|
|
91
|
+
wheel="$(ls dist/*.whl)"
|
|
92
|
+
version="$(basename "$wheel" | cut -d- -f2)"
|
|
93
|
+
if [ -z "$version" ]; then
|
|
94
|
+
echo "::error::could not read a version from the built wheel '$wheel'"
|
|
95
|
+
exit 1
|
|
96
|
+
fi
|
|
97
|
+
if [ "$TAG" != "v$version" ] && [ "$TAG" != "$version" ]; then
|
|
98
|
+
echo "::error::tag '$TAG' does not match built version '$version' - bump [project].version or retag"
|
|
99
|
+
exit 1
|
|
100
|
+
fi
|
|
101
|
+
echo "publishing $version from tag $TAG"
|
|
102
|
+
|
|
103
|
+
- name: Publish to PyPI
|
|
104
|
+
# OIDC trusted publishing — do not add password / API-token secrets.
|
|
105
|
+
# Pinned to a commit SHA, not the `v1.14.0` tag: a tag is mutable, and this step runs in a
|
|
106
|
+
# job holding `id-token: write`, so whatever it resolves to can publish as us. Bump the SHA
|
|
107
|
+
# and the trailing version comment together.
|
|
108
|
+
uses: pypa/gh-action-pypi-publish@ba38be9e461d3875417946c167d0b5f3d385a247 # v1.14.1
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# OS
|
|
2
|
+
.DS_Store
|
|
3
|
+
|
|
4
|
+
# Dependencies
|
|
5
|
+
node_modules/
|
|
6
|
+
.venv/
|
|
7
|
+
__pycache__/
|
|
8
|
+
*.pyc
|
|
9
|
+
|
|
10
|
+
# Build
|
|
11
|
+
dist/
|
|
12
|
+
build/
|
|
13
|
+
.next/
|
|
14
|
+
.turbo/
|
|
15
|
+
|
|
16
|
+
# Environment
|
|
17
|
+
.env
|
|
18
|
+
.env.local
|
|
19
|
+
.env.*.local
|
|
20
|
+
|
|
21
|
+
# IDE
|
|
22
|
+
.idea/
|
|
23
|
+
.vscode/settings.json
|
|
24
|
+
*.swp
|
|
25
|
+
*.swo
|
|
26
|
+
|
|
27
|
+
# Claude Code (local tooling - the public driver Skills live in skills/, not here)
|
|
28
|
+
.context/
|
|
29
|
+
.claude/
|
|
30
|
+
.commandcode/
|
|
31
|
+
tmp/
|
|
32
|
+
|
|
33
|
+
# Internal notes - strategy / decisions / plans, kept local, not part of the public docs set
|
|
34
|
+
docs/internal/
|
|
35
|
+
|
|
36
|
+
# Python
|
|
37
|
+
.ruff_cache/
|
|
38
|
+
.pytest_cache/
|
|
39
|
+
.mypy_cache/
|
|
40
|
+
.coverage
|
|
41
|
+
*.egg-info/
|
|
42
|
+
|
|
43
|
+
# A user's real fleet config (copied from fleet.config.example.yaml) - never commit it
|
|
44
|
+
fleet.config.yaml
|
|
45
|
+
|
|
46
|
+
# A user's own workflow recipes (they reference local client names). Templates ship in
|
|
47
|
+
# examples/workflows/; user recipes live in the repo-root workflows/, local-only.
|
|
48
|
+
/workflows/
|
|
49
|
+
|
|
50
|
+
# Marshal runtime state (worktrees, fleet.json, usage)
|
|
51
|
+
.marshal/
|
|
52
|
+
|
|
53
|
+
# Live agent-run artifacts + playground (review locally, don't commit)
|
|
54
|
+
marshal-runs/
|
|
55
|
+
|
|
56
|
+
# Local dogfood review panels (they reference clients from the gitignored fleet.config.yaml).
|
|
57
|
+
# Shareable starters live in examples/teams/.
|
|
58
|
+
teams/
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
3.12
|