Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion .github/workflows/quality-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,8 @@ jobs:
cargento.skills.cargento.tests.test_server \
scripts.tests.test_validate_plugins \
scripts.tests.test_bump_version \
scripts.tests.test_build_release_assets \
scripts.tests.test_installer \
scripts.tests.test_lint_embedded

# The threshold lives in pyproject.toml ([tool.coverage.report]
Expand Down Expand Up @@ -215,7 +217,7 @@ jobs:
- name: Confirm sqlite3 is present on this runner
run: python -c "import sqlite3; print(sqlite3.sqlite_version)"
- name: Run unittest suite
run: python -m unittest cargento.skills.cargento.tests.test_server scripts.tests.test_validate_plugins scripts.tests.test_bump_version scripts.tests.test_lint_embedded
run: python -m unittest cargento.skills.cargento.tests.test_server scripts.tests.test_validate_plugins scripts.tests.test_bump_version scripts.tests.test_build_release_assets scripts.tests.test_installer scripts.tests.test_lint_embedded

# The single required status check. Branch protection requires this job, so
# every job above must succeed (a skip or failure anywhere fails the gate).
Expand Down
23 changes: 20 additions & 3 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,8 @@ jobs:
python3 scripts/validate_plugins.py
python3 -m unittest scripts/tests/test_validate_plugins.py
python3 -m unittest scripts/tests/test_bump_version.py
python3 -m unittest scripts/tests/test_build_release_assets.py
python3 -m unittest scripts/tests/test_installer.py
python3 -m unittest cargento/skills/cargento/tests/test_server.py

- name: Bump version fields, re-validate, and push the release commit
Expand Down Expand Up @@ -206,16 +208,31 @@ jobs:
TARGET="${RELEASE_COMMIT:-$(git rev-parse HEAD)}"
git push --force origin "$TARGET:refs/heads/stable"

- name: Publish the GitHub Release
- name: Build release assets from the released commit
run: |
set -euo pipefail
TARGET="${RELEASE_COMMIT:-$(git rev-parse HEAD)}"
git checkout --detach "$TARGET"
python3 scripts/build_release_assets.py --tag "$TAG" --output-dir dist

- name: Publish the GitHub Release and assets
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
if gh release view "$TAG" > /dev/null 2>&1; then
echo "Release $TAG already exists — nothing to publish."
gh release upload "$TAG" \
"dist/install.sh" \
"dist/cargento-runtime-$VERSION.tar.gz" \
"dist/cargento-runtime-$VERSION.tar.gz.sha256" \
--clobber
echo "Release $TAG already exists — assets refreshed."
exit 0
fi
gh release create "$TAG" \
--title "Cargento $VERSION" \
--generate-notes \
--verify-tag
--verify-tag \
"dist/install.sh" \
"dist/cargento-runtime-$VERSION.tar.gz" \
"dist/cargento-runtime-$VERSION.tar.gz.sha256"
4 changes: 4 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,10 @@ jobs:
run: python3 -m unittest scripts/tests/test_validate_plugins.py
- name: Run release bump-script unit tests
run: python3 -m unittest scripts/tests/test_bump_version.py
- name: Run release-asset construction tests
run: python3 -m unittest scripts/tests/test_build_release_assets.py
- name: Run POSIX installer tests
run: python3 -m unittest scripts/tests/test_installer.py
- name: Check version-field parity across all owned locations
run: python3 scripts/bump_version.py --current
- name: Run dashboard server tests
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,6 @@
__pycache__/
*.py[cod]
.coverage

# Per-workspace Spacedock entity state stays in its independent local checkout.
docs/dev/.spacedock-state/
15 changes: 14 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,16 @@ cargento/ # plugin root
├── notify_hook.py # loopback POST forwarder for the user-installed Claude hooks
├── agents/openai.yaml # Codex presentation metadata
└── tests/test_server.py # server unit tests
scripts/
├── build_release_assets.py # deterministic runtime/checksum/installer builder
├── install.sh.in # rendered POSIX installer
└── tests/
├── test_build_release_assets.py
└── test_installer.py
docs/dev/ # tracked Spacedock development workflow
├── README.md # workflow schema and evidence discipline
├── ledger.csv # measurement ledger
└── _mods/ # workflow-specific lifecycle behavior
```

The Codex/AGY marketplace lives at `.agents/plugins/marketplace.json`. There is no Claude
Expand All @@ -45,6 +55,8 @@ shipped skill body, lives in the `sync-docs` skill at `.claude/skills/sync-docs/
| `COMPATIBILITY.md` | The cross-harness and cross-platform contract, and the Python floor. |
| `SECURITY.md` | Security invariants, accepted exposures, and private reporting. |
| `cargento/skills/cargento/SKILL.md` | The shipped product surface. A validated artifact — see the portability rules below. |
| `docs/dev/README.md` | The tracked Spacedock development workflow; its split-root entity state remains local and ignored. |
| `docs/design-installation.md` | Installer ownership, trust boundary, and rejected distribution alternatives. |
| `docs/design-*.md` | Durable design rationale, including alternatives that were tried and rejected. |
| `docs/plans/*.md` | Transient plans for unshipped work. Delete a plan once its work ships. |
| `.claude/skills/*/SKILL.md` | Repository development skills (`sync-docs`). Not shipped with the plugin, so the portability rules below do not apply to them. |
Expand Down Expand Up @@ -93,6 +105,7 @@ git diff "$(git merge-base origin/main HEAD)"..HEAD \
-- '*plugin.json' '*marketplace.json' '*gemini-extension.json' | grep -E '^[+-].*"version"'
coverage run -m unittest cargento.skills.cargento.tests.test_server \
scripts.tests.test_validate_plugins scripts.tests.test_bump_version \
scripts.tests.test_build_release_assets scripts.tests.test_installer \
scripts.tests.test_lint_embedded
coverage report # enforces the fail_under threshold from pyproject.toml
# Native validators, if the CLIs are installed (they are not available on stock runners):
Expand Down Expand Up @@ -135,7 +148,7 @@ git tag v0.2.0 # v-prefixed is canonical (bare 0.2.0 also works — pick
git push origin v0.2.0
```

The Release workflow validates the tag (must be on main, strict semver, strictly greater than every existing release tag — back-tagging is impossible), runs the contract validator plus the validator, bump-version and server test modules on the main tip — not the whole quality gate, which already ran on every commit that reached main — writes one `chore(release)` bump commit via `scripts/bump_version.py`, moves the tag onto that commit, and publishes a GitHub Release. Every step is idempotent: a re-run after a partial failure resumes cleanly, and tagging the version the manifests already carry releases it as-is (that is how the initial 0.1.0 ships). If main advances between tag push and the run, the release includes those extra commits. Release tags are immutable — a tag ruleset blocks deleting or moving them.
The Release workflow validates the tag (must be on main, strict semver, strictly greater than every existing release tag — back-tagging is impossible), runs the contract validator plus the validator, bump-version, release-asset, installer, and server test modules on the main tip — not the whole quality gate, which already ran on every commit that reached main — writes one `chore(release)` bump commit via `scripts/bump_version.py`, and moves the tag onto that commit. It then builds `install.sh`, the runtime archive, and its SHA-256 checksum from that released commit and uploads all three to the GitHub Release. Every step is idempotent: a re-run after a partial failure resumes cleanly and refreshes the assets, and tagging the version the manifests already carry releases it as-is (that is how the initial 0.1.0 ships). If main advances between tag push and the run, the release includes those extra commits. Release tags are immutable — a tag ruleset blocks deleting or moving them.

## Portability Rules

Expand Down
17 changes: 11 additions & 6 deletions COMPATIBILITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,21 +9,23 @@ The repository keeps one shared skill implementation for all clients. Platform-n
| Skill UI metadata | `agents/openai.yaml` | Ignored | Ignored | Ignored | Optional Codex presentation data beside the shared skill |
| MCP | Not used | Not used | Not used | Not used | Cargento reads local session stores directly; no MCP server is bundled |
| Hooks | None | None | None | None | The dashboard's optional Claude `Notification` and `SessionEnd` hooks are user-installed, never bundled (see [SKILL.md](cargento/skills/cargento/SKILL.md#notifications)) |
| Release installer | Not yet | `--plugin claude` | Not yet | Not yet | Installs the standalone CLI and exact `cargento@spacedock` identity together |
| Recurring runs | Invoke the skill one pass at a time | Invoke the skill one pass at a time; a scheduler plugin can repeat it | Invoke the skill one pass at a time | Invoke the skill one pass at a time | The skill remains useful as a one-shot workflow |

## Platform-specific behavior

This file owns the Python floor. The dashboard server is stdlib-only Python 3.11+, with
`datetime.UTC` setting the floor, and it runs identically regardless of which harness launched it.
The floor is restated in six other places across four files, which must all move together:
`README.md`, `CONTRIBUTING.md` (twice, in the prerequisites and in the `server.py` design
constraints), `cargento/skills/cargento/SKILL.md`, and `pyproject.toml` (`[tool.ruff] target-version`
and `[tool.mypy] python_version`). The documentation-matches-code test guards the `SKILL.md` copy.
The rest are on you.
The floor is also restated in `README.md`, `CONTRIBUTING.md` (the prerequisites and the `server.py`
design constraints), `cargento/skills/cargento/SKILL.md`, `scripts/install.sh.in`, and
`pyproject.toml` (`[tool.ruff] target-version` and `[tool.mypy] python_version`). These declarations
must move together. The documentation-matches-code test guards the `SKILL.md` copy. The rest are on
you.

| Capability | macOS | Linux | Windows | WSL2 |
|---|---|---|---|---|
| Harness discovery, dashboard, `/api/data` | yes | yes | yes | yes (Linux-side stores) |
| Release installer and `cargento` launcher | yes | yes | no | yes |
| Turn ETA, token rate | yes | yes | yes | yes |
| Task age from file birthtime | yes | falls back to mtime | Python 3.12+ only | falls back to mtime |
| Needs-input popup, browser (tab open) | not needed | yes | yes | yes (host browser) |
Expand All @@ -40,6 +42,9 @@ Other notes:
- Store locations resolve per platform, and `CLAUDE_CONFIG_DIR`, `CODEX_HOME`, `GEMINI_CLI_HOME`, and `COPILOT_HOME` are honored. Run `server.py --diagnose` to see every path searched and what was found there.
- WSL2's `localhostForwarding` defaults on but can be switched off, and mirrored/NAT networking modes or corporate policy can also break host-browser access to `127.0.0.1:4553`. Probe before assuming; the fallback is `ssh -L` or a browser inside WSL.
- Supported WSL topology is server and agents on the same side of the boundary. Reading a Windows-side store from inside WSL works over `/mnt/c`, but 9p latency and mtime granularity make state detection unreliable, so it is not supported.
- The release installer is POSIX-only. It supports macOS, Linux, and WSL with Python 3.11+, `curl`,
`gzip`, `tar`, a SHA-256 tool, and Claude Code. Native Windows and all non-Claude plugin selectors
are deferred. WSL needs a release smoke before shipping because hosted CI has no WSL runner.
- `sqlite3` is an optional stdlib module. On a build without it (some musl/Alpine images) OpenCode, Cursor and Goose report undiscovered. Antigravity still appears, since its discovery and state come from store mtime and CLI logs, but without a token rate or turn ETA.

## Validation
Expand All @@ -53,4 +58,4 @@ claude plugin validate ./cargento --strict
agy plugin validate ./cargento
```

<!-- docs-synced-through: 6c5cbd4 (2026-07-28) -->
<!-- docs-synced-through: ce01e9c (2026-07-28) -->
24 changes: 19 additions & 5 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,12 @@ A flipped comparison is the cheapest mutation to try, and the most revealing: ch
`<=`, or one `and` to `or`, and run the suite. Anything that still passes is a boundary nothing
pins.

Installer changes belong in `scripts/tests/test_installer.py`; release-asset changes belong in
`scripts/tests/test_build_release_assets.py`. Installer tests use isolated homes, real locally built
runtime archives, and a stateful Claude command fixture. Keep the lagging marketplace-version case:
the plugin version is selected by the marketplace and is not required to equal the runtime archive
version.

Known flake: the page tests shell out to `node` with a 30-second timeout. On the Windows runner that
occasionally expires on process start, surfacing as
`subprocess.TimeoutExpired: … page_test.js`. It is a runner-speed artifact rather than a page bug,
Expand Down Expand Up @@ -184,11 +190,19 @@ git push origin v0.2.0

The [Release workflow](.github/workflows/release.yml) refuses the tag unless it is on main, is
strict semver, and is strictly greater than every existing release tag. Semver only moves forward,
and back-tagging is impossible. It then runs the contract validator plus the validator, bump-version
and server test modules on the main tip, rather than the whole quality gate, which already ran on
every commit that reached main. From there it writes one bump commit updating all owned version
fields, moves the tag onto the released commit, advances the `stable` branch to it, and publishes a
GitHub Release with generated notes. `stable` is what the shared
and back-tagging is impossible. It then runs the contract validator and the focused release test
modules on the main tip, rather than the whole quality gate, which already ran on every commit that
reached main. From there it writes one bump commit updating all owned version fields, moves the tag
onto the released commit, advances the `stable` branch to it, and builds three assets from that exact
commit:

- `install.sh`, rendered with the release tag and asset names;
- `cargento-runtime-<version>.tar.gz`, built from the authored `cargento/` tree;
- the archive's `.sha256` checksum.

The workflow publishes those assets with generated notes. If a run stops after the release exists,
a rerun rebuilds the same deterministic assets and uploads them with replacement enabled. It does
not make a second release or another version bump. `stable` is what the shared
[spacedock-dev/marketplace](https://github.com/spacedock-dev/marketplace) listing tracks, so a
release that did not move it would leave the marketplace serving an older Cargento.
The bump is skipped when the manifests already carry the tagged version, which is also how you
Expand Down
62 changes: 52 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,16 +21,56 @@ This repo contains one plugin, `cargento`, the agent cartography dashboard skill
### Prerequisites

- Python 3.11+. The server is stdlib-only, so there is nothing to install alongside it.
- To install it as a plugin: Codex, Claude Code, Antigravity/AGY, or Gemini CLI.
- For the supported installer: Claude Code, `curl`, `gzip`, `tar`, and either `sha256sum` or
`shasum`.
- For manual plugin setup: Codex, Claude Code, Antigravity/AGY, or Gemini CLI.

You do not need all four. The dashboard maps every harness it finds on the machine regardless of
which one launched it, and it runs standalone with no client installed at all:
### Install the CLI and Claude plugin

Install the latest release:

```bash
curl -fsSL https://github.com/spacedock-dev/cargento/releases/latest/download/install.sh \
| sh -s -- --plugin claude
```

The latest URL selects only the rendered bootstrap. The bootstrap's archive, checksum, recovery,
and runtime downloads stay pinned to that release's exact tag. For a reproducible install or
rollback, choose the exact tag yourself:

```bash
CARGENTO_TAG=vX.Y.Z # replace with the exact release tag you want
curl -fsSL "https://github.com/spacedock-dev/cargento/releases/download/$CARGENTO_TAG/install.sh" \
| sh -s -- --plugin claude
```

The installer verifies the release checksum, installs a user-local `cargento` command, and sets up
the exact `cargento@spacedock` Claude plugin. A complete run ends with:

```text
CLI: verified
Plugin (claude): verified
```

If `~/.local/bin` is not already on `PATH`, the result includes an `export PATH=...` line you can
copy. The installer does not edit shell startup files. A plugin failure after CLI activation is
reported as a partial installation; rerun the same installer command to repair it.

`cargento` starts the server in the foreground on `127.0.0.1:4553`; then open
`http://127.0.0.1:4553/`. Use `cargento --port 4553` to choose the port explicitly, and press
Ctrl-C in that terminal to stop the server. `cargento --diagnose` reports the paths and stores
Cargento sees, then exits without starting the server.

### Manual and plugin-only setup

You do not need every harness. The dashboard maps every harness it finds on the machine regardless
of which one launched it. From a checkout, it also runs without plugin installation:

```bash
python3 cargento/skills/cargento/server.py --port 4553
```

### Claude Code installation
#### Claude Code

Cargento is listed in the shared Spacedock marketplace, so if you already have that marketplace you
only need the second line.
Expand All @@ -45,7 +85,7 @@ claude plugin install cargento@spacedock

Restart Claude Code after installation.

### Antigravity / AGY installation
#### Antigravity / AGY

```bash
# From a local checkout, install the native AGY plugin
Expand All @@ -54,7 +94,7 @@ agy plugin install "$PWD/cargento"

Restart AGY after installation.

### Gemini CLI installation
#### Gemini CLI

```bash
# From a local checkout, install the native Gemini CLI extension
Expand All @@ -63,7 +103,7 @@ gemini extensions install "$PWD/cargento"

Restart Gemini CLI after installation.

### Codex installation
#### Codex

```bash
# Add the marketplace from a local checkout, then install the plugin
Expand Down Expand Up @@ -108,9 +148,11 @@ See [COMPATIBILITY.md](COMPATIBILITY.md) for the cross-platform contract.
## 5. Contributing

Contributions are welcome, and new harness support is especially useful. Start with
[CONTRIBUTING.md](CONTRIBUTING.md) for setup, validation, and PR conventions. This project follows
the [Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md). Please report security issues
privately, as described in [SECURITY.md](SECURITY.md).
[CONTRIBUTING.md](CONTRIBUTING.md) for setup, validation, and PR conventions. Maintainers using
Spacedock can follow the tracked [development workflow](docs/dev/README.md) without publishing its
per-workspace entity state. This project follows the
[Contributor Covenant Code of Conduct](CODE_OF_CONDUCT.md). Please report security issues privately,
as described in [SECURITY.md](SECURITY.md).

## 6. License

Expand Down
Loading