Deterministic supervisor + per-slice runner machinery for an AFK, board-driven Claude implementation fleet (ADR-0007).
Project description
squadra
Deterministic supervisor + per-slice runner machinery for an AFK, board-driven
Claude implementation fleet. squadra runs the implementation phase of an
engineering pipeline (/tdd → /qa) unattended across many vertical slices at
once, with board work-item state as the single source of truth at every tier.
squadra speaks a provider-neutral 3-bucket Lifecycle (queued / active / done);
a BoardAccess adapter translates that to a concrete board's native semantics at
the boundary. Azure DevOps (the az CLI) is the adapter that ships today; GitHub
and GitLab are tracked backlog adapters the same contract-test suite will
validate. Every fleet process is stateless or short-lived and reconstructs its
view from the board on each run.
squadra is the packaged, reusable extraction of the fleet originally built in
the gswa backend (design: ADR-0007 and its 2026-06-10 addendum, which lives in
that repo; the provider-neutral seam is ADR-0001
and its design note). The package ships the
deterministic machinery + its tests + scaffolding — the agent-side skills
(/afk-slice-runner, /tdd, /qa, /cleanup-merged-branches) are
consumer-owned and live in the consuming repo. squadra scaffolds genericized
templates for the ones it drives (squadra init), then invokes them only by
skill name through claude: scaffolding the template is not owning it, so the
runtime boundary is unchanged.
squadra operates on a target repository, identified by
FLEET_HOME(the current working directory by default). The installed package is never the working repo; the shell glue it drives ships as package data and is resolved viaimportlib.resources.
Install
squadra is built with uv + Hatchling (PEP 621,
src layout). It has no third-party runtime dependencies (pure standard
library); tmux, git, the board provider's CLI (the az CLI for the ADO
adapter that ships today), and the claude CLI must be available on the host that
runs the fleet.
# From a clone (development):
uv sync # create .venv, install squadra + dev tools
# As a dependency of another project (consumer):
# editable path dep while iterating, or a git+HTTPS pin once stable
uv pip install -e /path/to/squadra
A single unified squadra CLI is installed (the API / composition root):
| Command | Role |
|---|---|
squadra init [--provider P] [--check] |
scaffold an annotated squadra.toml; --check validates it against the live board |
squadra tick [--dry-run] |
run one supervisor tick in-process |
squadra {start|stop|status|log} |
hands-on ticker control (shells to the packaged fleetctl.sh) |
squadra slice {init|update|heartbeat|show} |
the per-slice status-file ops (used by the runner wrapper) |
python -m squadra.supervisor (one tick) and python -m squadra.status (the
status-file CLI) remain as internal module entry points. The deprecated
squadra-status console script has been dropped — use squadra slice instead.
Configuration
squadra reads a squadra.toml in the target repo (the what — which board,
which states, which skills) and layers it under environment + flag overrides. The
precedence, lowest to highest:
built-in defaults < squadra.toml < FLEET_* env < CLI flag
squadra.toml describes the target (like a kubeconfig context) and defaults the
how wherever it can. Schema:
| Section / key | Default | Meaning |
|---|---|---|
[board].provider |
ado |
ado | github | gitlab. Selects the BoardAccess adapter (registry in the CLI composition root). ADO ships today; GitHub/GitLab are tracked backlog adapters. |
[board].base_branch |
main |
The branch a slice PR must complete against for finalize-eligibility. |
[board].tag_prefix |
fleet: |
Configurable namespace for the fleet's tags; detection is prefix-based (startswith). The five suffixes are fixed (see Tag vocabulary). |
[board].parent_scope_ids |
[] (whole project) |
Optional claim-scope filter — only slices under these parents are claimable. Supersedes the legacy FLEET_EPIC_IDS env, which is still honored. |
[board.states].queued / .active / .done |
— | Lists of the board's native state names mapped onto the three neutral Lifecycle buckets (many-native→one-neutral allowed). REQUIRED unless the provider is ADO-Basic, which defaults to ["To Do"] / ["Doing"] / ["Done"]. GitHub/GitLab statuses are user-defined, so they must be declared. |
[pipeline].branch_template |
feat/slice-{id}-{slug} |
Slice branch naming. squadra owns the -a{attempt} retry suffix (fixed rule, not templated). |
[pipeline].worktree_dir |
.claude/worktrees |
Where slice worktrees are created. |
[pipeline].runner_skill |
/afk-slice-runner |
Skill the runner wrapper invokes per slice. |
[pipeline].tdd_skill |
/tdd |
TDD skill name, threaded into the runner prompt. |
[pipeline].qa_skill |
/qa |
QA skill name, threaded into the runner prompt. |
[pipeline].cleanup_skill |
/cleanup-merged-branches |
Skill the finalize pass runs headlessly per merged branch. |
[board]
provider = "ado" # ado | github | gitlab (REQUIRED)
base_branch = "main"
tag_prefix = "fleet:"
parent_scope_ids = [105] # optional; empty = whole project (was FLEET_EPIC_IDS)
[board.states] # REQUIRED unless provider is ADO-Basic; many-native→one allowed
queued = ["To Do"]
active = ["Doing"]
done = ["Done"]
[pipeline]
branch_template = "feat/slice-{id}-{slug}"
worktree_dir = ".claude/worktrees"
runner_skill = "/afk-slice-runner"
tdd_skill = "/tdd"
qa_skill = "/qa"
cleanup_skill = "/cleanup-merged-branches"
Operational and secret knobs stay env-only with the defaults in
Constants: FLEET_MAX_RUNNERS, the intervals
(FLEET_TICK_INTERVAL_SECONDS, FLEET_HEARTBEAT_INTERVAL_SECONDS,
FLEET_STALENESS_THRESHOLD_SECONDS), FLEET_MAX_ATTEMPTS,
FLEET_MODEL/FLEET_EFFORT, FLEET_HOME/FLEET_ROOT/FLEET_PYTHON, and the PAT.
These are not in squadra.toml.
Safety is validate-against-board, not mandatory typing. validate_config()
resolves the configured state names, tag prefix, and base branch against the
live board — at startup of every tick and on squadra init --check — and fails
loud on any mismatch (e.g. "configured active state 'Doing' not found among this
project's states"). A typo can't silently strand or mis-claim slices.
Scaffolding (squadra init)
squadra init [--provider ado] makes adoption one command plus a few edits. It
emits:
- a complete, annotated
squadra.toml— every key written with its default andprovidertaken from--provider; and - the genericized, consumer-owned runner-skill and cleanup-skill templates.
The skill templates are provider/repo-agnostic (a neutral lifecycle: claim-verify
→ worktree → seams → tdd → qa → park) with clearly-marked fill-in sections (e.g.
## Gates, shared-seam conventions) that work out of the box. squadra copies
them out, then drives them only by skill name through claude — copying the
template is not owning it, so the "machinery + tests + scaffolding" runtime
boundary holds. squadra init --check runs validate_config() against the live
board without writing anything.
The unified
squadra initCLI surface lands with the PR2 core; the scaffolding engine itself ships on this branch.
Status file + heartbeat convention
Each in-flight slice has one status file — the micro view of its runner:
$FLEET_HOME/.claude/fleet/<issue-id>/status.json
The fleet root defaults under the target repo's .claude/fleet; on a
bind-mounted checkout it survives container restart. Exclude it from the target
repo's git (**/.claude/fleet/).
Schema
| Field | Type | Meaning |
|---|---|---|
issue_id |
int | The slice's board work-item id |
runner_id |
str | Unique id of the runner attempt (e.g. runner-41-a1-…) |
branch |
str | Slice branch (feat/slice-<id>-<kebab>) |
worktree |
str | Absolute path of the slice's git worktree |
pr_url |
str | null | The slice's PR once opened |
phase |
enum | claiming → seams → tdd → qa → parked → done |
parked_state |
enum | null | needs-decision, qa-ready, awaiting-pr-approval, failed — non-null iff phase is parked |
worker_roster |
list[str] | Worker sub-agents the runner fanned out |
started_at |
str | ISO 8601 UTC, set at init |
last_heartbeat |
str | ISO 8601 UTC, stamped every heartbeat interval while the runner process is alive |
attempt |
int | 1-based attempt counter (transient failures retry up to 3) |
last_error |
str | null | Last recorded failure diagnostic |
Concurrency contract
Two writers touch the file: the runner wrapper's deterministic heartbeat loop
(liveness = process alive, independent of what the agent is doing) and the
agent updating phase/parked_state/worker_roster at transitions. Every
read-modify-write therefore happens under a sidecar flock
(<issue-id>/.status.lock) and lands atomically via tmp-file + rename. Readers
never see partial JSON; interleaved writers never lose fields.
CLI
Shell callers (the runner wrapper, skills) use the status CLI:
squadra slice init \
--issue-id 41 --runner-id runner-41-a1 \
--branch feat/slice-41-example --worktree "$FLEET_HOME/.claude/worktrees/feat+slice-41-example"
squadra slice update --issue-id 41 --phase tdd
squadra slice update --issue-id 41 \
--phase parked --parked-state awaiting-pr-approval --pr-url <url>
squadra slice update --issue-id 41 --phase tdd --parked-state none
squadra slice heartbeat --issue-id 41
squadra slice show --issue-id 41
--fleet-root overrides the location (used by tests; defaults to
$FLEET_HOME/.claude/fleet). python -m squadra.status … is equivalent.
Constants
All addendum constants live in squadra/constants.py and are env-tunable
(read at process start — every fleet process is fresh per fire):
| Constant | Default | Env override |
|---|---|---|
| Fleet home (target repo) | current working directory | FLEET_HOME |
| Fleet root | $FLEET_HOME/.claude/fleet |
FLEET_ROOT |
| Interpreter for the fleet | the supervisor's sys.executable (shell default python3) |
FLEET_PYTHON |
| Max parallel runners (the claim budget — 0 stops new claims only, it is not a safety lever) | 2 | FLEET_MAX_RUNNERS |
| Dry-run tick (plan + report, suppress every side effect) | off | FLEET_DRY_RUN |
| Heartbeat interval | 60s | FLEET_HEARTBEAT_INTERVAL_SECONDS |
| Staleness threshold | 600s | FLEET_STALENESS_THRESHOLD_SECONDS |
| Max attempts | 3 | FLEET_MAX_ATTEMPTS |
| Model | claude-opus-4-8 |
FLEET_MODEL |
| Reasoning effort | high |
FLEET_EFFORT |
The compute tier (FLEET_MODEL / FLEET_EFFORT) is pinned in constants.py and
applied as explicit claude --model … --effort … flags to every model-backed
fleet call — the slice runner, the cleanup pass, and the auth probe (the probe
pins --model only, since it does no reasoning). This is deliberate: a headless
claude -p otherwise inherits whatever model an interactive session's
settings.json happens to pin, so the fleet's tier would be an ambient side
effect rather than a choice. Effort accepts the CLI levels
low|medium|high|xhigh|max (model-dependent). To run hotter or cheaper, export
FLEET_MODEL / FLEET_EFFORT — it flows through autostart → ticker → supervisor
→ runner panes by environment inheritance.
FLEET_PYTHON must point at an interpreter that has squadra installed; the
supervisor injects its own sys.executable into each runner pane so the runner
reaches squadra.* regardless of what python3 resolves to on PATH.
Tag vocabulary (parked sub-states are tags, not states — the ADO Basic
process has only To Do / Doing / Done). The five suffixes —
claimed, failed, needs-decision, qa-ready, awaiting-pr-approval — are
fixed canonical vocabulary, carried under a configurable namespace prefix
(default fleet:, set via [board].tag_prefix / FLEET_TAG_PREFIX), so the
shipping defaults read fleet:claimed, fleet:failed, fleet:needs-decision,
fleet:qa-ready, fleet:awaiting-pr-approval. Fleet-tag detection is
prefix-based (startswith(prefix)), not a hardcoded literal set.
The neutral comment the fleet attaches at each transition is emitted by core as a structured event and rendered to the board's native markup at the adapter boundary (ADO → HTML, GitHub → Markdown) — core itself emits no markup.
Tuning path for the cap (addendum §1): raise as cores/headroom grow; back off on CPU saturation or 429s.
Slice runners
A runner is one short-lived, headless Claude session driving one slice. The
supervisor launches each into its own per-slice ephemeral Docker compose project
via SandboxAccess (ADR-0002 §5): the compose agent service's command is the
deterministic wrapper (squadra/_scripts/runner-wrap.sh, resolved from the
installed package and invoked as runner-wrap.sh <issue-id> <branch> [attempt]),
so container lifecycle == agent lifecycle and docker inspect .State.ExitCode
is the agent exit code.
The wrapper owns everything that must not depend on an LLM:
- seeds
status.json(init), records therunner.pid/pane-id/heartbeat.pidsidecars, and appends all output to<issue-id>/runner.log; - runs the heartbeat loop —
last_heartbeatadvances every heartbeat interval for exactly as long as the wrapper process lives, so liveness means process alive, independent of how long the agent's current tool call runs; - invokes the headless session, threading the configured skill names into the
prompt:
claude -p "<runner_skill> issue-id=… branch=… attempt=… tdd-skill=… qa-skill=…" --dangerously-skip-permissions --model "$FLEET_MODEL" --effort "$FLEET_EFFORT". Because the runner/tdd/qa skill names are config (not hardcoded), the runner skill no longer hardcodes/tdd,/qa— it runs whatever names it is handed; - backstops an unexpected death: a healthy runner always exits
parked(ordone); if the session exits in any other phase, the wrapper stampsparked_state=failed+last_errorand propagates the non-zero exit.
The afk-slice-runner skill (in the consuming repo) is the agent side of the
contract: verify the claim, enter the slice worktree, write the shared seams
before any fan-out, execute the configured tdd then qa skills unchanged,
update phase/pr_url/worker_roster at transitions, park with the matching
fleet tag + comment, exit. Parked states are never a hung session — they are queryable board
state plus the status file.
Runner wrapper env knobs: FLEET_HOME, FLEET_ROOT,
FLEET_HEARTBEAT_INTERVAL_SECONDS, FLEET_MODEL, FLEET_EFFORT,
FLEET_RUNNER_SKILL, FLEET_TDD_SKILL, FLEET_QA_SKILL (the supervisor injects
these into the pane env; when any is unset — e.g. a manual run — the wrapper
resolves the default from squadra.config / squadra.constants, the single
source of truth, the same fallback pattern as FLEET_MODEL/FLEET_EFFORT/the
interval), FLEET_PYTHON, FLEET_CLAUDE_CMD (stubbed in the hermetic tests).
Supervisor
squadra/supervisor.py is the deterministic, token-free tick: no LLM anywhere,
so it cannot hallucinate a board mutation, and it is unit-tested against in-memory
fakes (including a divergent GitHub-shaped fake, which catches any hardcoded
native-state leak and proves the core is provider-blind). It speaks the neutral
Lifecycle throughout; the BoardAccess adapter maps to native states at the
boundary. Each tick runs three ordered passes under one lock — finalize → reap →
claim — so cap accounting is fresh before anything new launches (addendum §5).
The native state names below (To Do/Doing/Done) are the ADO-Basic
mapping of the neutral queued/active/done Lifecycle buckets; under
another provider the adapter substitutes that board's configured names.
- Serialize — take a non-blocking
flockon<fleet-root>/supervisor.lock; a tick that cannot get the lock exits 0 without touching the board. - Count inflight — Issues in
Doingcarryingfleet:claimed. A human's manually-movedDoingIssue is invisible to the fleet (no tag): never counted, never reaped. - Claim up to
FLEET_MAX_RUNNERS − inflightavailable Issues, lowest id first. Available =To Do, nofleet:*tag, every Predecessor-linked IssueDone. Claim =To Do → Doing+ tagfleet:claimed+ a stamped comment, plus a localclaimed-atmarker for the watchdog. The branch is derived deterministically:feat/slice-<id>-<kebab-of-title>(suffix-aNon retries). - Launch — one
runner-wrap.sh <issue-id> <branch> <attempt>per claimed slice, as theagent-service command of its own per-slice ephemeral Docker compose project viaSandboxAccess(build +compose up -d, non-blocking;docker compose logsagainst the slice project is the live view). A failed launch rolls the claim back (tag removed,Doing → To Do, comment), so no slice is stranded.
Only claim/launch depends on credentials beyond the board reads: a working
ADO PAT (claiming a slice does host-side git remote ops — worktree create off
origin/main, then push — over HTTPS+PAT, no SSH key) and a working claude
(the contained runner is the fleet's single LLM call — finalize and reap are
deterministic). A tick with claim work pending runs two preflights first, in
order, and short-circuits on the first failure:
- PAT preflight —
git ls-remoteagainst the target remote (the live checkout'sorigin, elseFLEET_APP_REPO_URL) using the env-var PAT credential helper, non-interactively (GIT_TERMINAL_PROMPT=0) with a 30s timeout. This exercises the exact auth path every host-side git op uses, so it cannot pass while a real claim's git op fails. A rejected/expired/wrong-scope PAT, an unreachable remote, a timeout, or nogitall read as a failure. - claude preflight — a throwaway
claude -p 'reply READY' --dangerously-skip-permissions --model "$FLEET_MODEL"probe with a 120s hard timeout, passing only on exit 0 plusREADYin stdout (dead auth, a transient API outage, and an unavailable model read identically).
On either failed probe the tick degrades to the reap pass only — every
claim/launch decision is dropped (no slice is claimed, no To Do → Doing, no
fleet:claimed), while in-flight finalize and reap still proceed — and retries
next tick. The PAT failure logs one actionable line naming the fleet-ado-pat
Key Vault secret and the rotation runbook (the consuming repo's
docs/contributing/afk-fleet.md → Key Vault secrets & PAT rotation). The PAT
probe runs first, so a dead PAT never pays to spawn claude. Idle, saturated,
and finalize-only ticks never pay for either probe.
Finalize retires slices that are truly done: Issue Done and a completed
PR for the slice branch. For each, it runs the consuming repo's
/cleanup-merged-branches skill headlessly for that branch, drops every fleet:*
tag, comments the PR link, and sets the status phase to done. A failed cleanup
is retried next tick.
Reap (watchdog) recovers from dead runners, with two independent guards
before it acts: stale (best liveness evidence older than the staleness
threshold) and dead (the runner.pid sidecar process no longer exists). A
stale-but-alive runner is always left alone. A failed park
(parked_state=failed with no parked tag) is positive failure evidence and skips
the staleness wait — reaped immediately once the pid is confirmed dead.
Deliberately parked runners are never reaped. Reaping archives the dead worktree
to .claude/fleet/<issue-id>/archive/attempt-N/, records the reap, drops
fleet:claimed, and moves Doing → To Do; the next claim retries with
attempt+1, and exhausted retries escalate to fleet:failed instead.
Dry run — squadra tick --dry-run (or FLEET_DRY_RUN=1, or
python -m squadra.supervisor --dry-run) runs the same three passes' read+plan logic
and reports the would-be actions, with every side effect suppressed at the
TickSeams boundary (dry_run_seams): board writes become logged
[dry-run] WOULD … no-ops, no runner pane launches, neither preflight probe runs
(no git ls-remote for the PAT, no claude spawn for auth), no worktree is
archived, no local status/marker file is written. Safety is the wrapped boundary,
not a flag inside the passes, so a future pass cannot forget to honor it. Only the
tick lock and the supervisor log are still written — coordination artifacts,
not fleet state.
Scoping: [board].parent_scope_ids (a list of parent work-item ids, optional)
restricts claiming to slices under those parents; it supersedes the legacy
FLEET_EPIC_IDS env (comma-separated Epic ids), which is still honored. Empty
(the default) means every unblocked queued work item in the project is
eligible.
Host-side git hardening (sandbox-escape control)
The slice agent runs in a sandbox with the slice worktree bind-mounted as
/work, and it commits there — so a prompt-injected or misbehaving agent can
plant a git hook (pre-push, post-checkout, …) or set core.hooksPath to an
agent-controlled directory inside the worktree. Host-side git ops later run
against that same worktree in the supervisor context, which holds the ADO PAT
and the VM managed identity. A hook firing there would be a sandbox escape into
the credential-holding host.
squadra closes this by routing every host-side git invocation through
squadra.git_host, which:
- pins
-c core.hooksPath=/dev/nullon the argv (a command-line-coutranks any config the agent set, so neither a planted hook file nor an agent-setcore.hooksPathcan execute); and - on a checkout op, adds
-c safe.directory=<that exact path>to clear git's dubious-ownership refusal when the op runs as a different OS user than theFLEET_HOME/worktree owner — scoped to the path, neversafe.directory=*(a wildcard would trust every repo on the host and, paired with a planted hook, reopen the escape).
Both are transient command-line overrides — never written to .git/config, so the
agent cannot strip them. The worktree create/archive/prune, branch delete, the
base..HEAD commit count, and the app-repo bootstrap all inherit this automatically
by going through the builder. The host-side push of a slice's commits — the
credential-holding op the agent does not perform — is not yet wired in code (the
deferred write-tail in the supervisor's _handoff); when it is, it must be built as
host_git_argv(*credential_helper, "push", "origin", branch, work_dir=worktree), and
tests/test_git_host.py already proves that contract neutralizes a planted pre-push
hook (with a bare-push control), so a push that bypasses the builder is a reviewable
regression rather than a silent escape.
Manual operator pushes against a fleet worktree must carry the same guard —
run e.g. git -c core.hooksPath=/dev/null -c safe.directory="$PWD" push … (or
export GIT_CONFIG_PARAMETERS) rather than a bare git push, so an operator's
hands-on op cannot trip a planted hook either.
Activation (manual, opt-in)
Nothing starts the fleet automatically. Scope claiming with
[board].parent_scope_ids (or the legacy FLEET_EPIC_IDS) before enabling. Two
levers compose:
- A dry run first (the safest first step):
squadra tick --dry-run(orFLEET_DRY_RUN=1) runs the full finalize/reap/claim read+plan logic and logs every action a real tick WOULD take, but cannot mutate — board writes, runner launches, the preflight probes (the PATgit ls-remoteand theclaudeauth spawn), and local fleet-state writes are all suppressed at the seams. Ticks log to$FLEET_ROOT/supervisor.log, so review the plan withsquadra log. - One tick by hand:
squadra tick— logs to$FLEET_ROOT/supervisor.log. Note thatFLEET_MAX_RUNNERS=0is not a read-only tick: it only zeroes the claim budget; finalize and reap still mutate the board (drop fleet tags, comment PR links, run the headless cleanup, moveDoing → To Do). For a tick that cannot mutate, use--dry-run. - The fleet-host (production): systemd. The dedicated fleet-host VM schedules
ticks with systemd, not tmux (ADR-0002 §11): a
squadra.timerfires a oneshotsquadra.servicerunningsquadra fleet-tick, which fetches the PAT +ANTHROPIC_API_KEYfrom Key Vault via the VM managed identity, syncs the app repo, and runs one tick. Render + install the units withsquadra install-units --key-vault <kv> --fleet-home <checkout> [...]; this does not enable the timer. Provisioning, the on-host acceptance (goss), and activation (systemctl enable --now squadra.timer) live indocs/fleet-host/. - Local / dev on demand: tmux.
squadra {start|stop|status|log}drives a detachedfleet-tickertmux session whose loop fires one tick everyFLEET_TICK_INTERVAL_SECONDS(default 180):startis idempotent,stopkills it (in-flight runners in the separatefleetsession keep going),statusreports + tails,logtails (-fto follow). This is for hands-on local runs; the fleet-host uses systemd, above. (The boot-timefleet-autostart.sh/ cron autostart paths have been retired in favor of systemd.)
Each fire is a fresh supervisor process under the same lock (the timer is only the
schedule, so crash-only semantics are preserved). In-flight slice state is
reconstructed from the board plus the bind-mounted .claude/fleet/ status files,
so a re-started ticker resumes cleanly.
Watch the fleet: squadra status (is-it-running + recent log), squadra log -f (follow the supervisor log live), the board (fleet:* tags) is the macro
view, per-slice status.json is the micro view, docker compose logs against a
slice's compose project is the live agent view.
Development
uv sync # env + deps
uv run ruff check . # lint
uv run ruff format --check . # format
uv run pyright # strict type check
uv run pytest # unit + hermetic shell tests
The shell glue (runner-wrap.sh, fleet-tick.sh, fleetctl.sh) ships as package
data under src/squadra/_scripts/. Anything that needs to invoke it — the
supervisor's SandboxAccess launch, the squadra dispatcher, the tick entry
point — resolves it via squadra._resources.resolve_script(...) (importlib.resources +
chmod +x), never a path relative to FLEET_HOME. The fleet-host systemd unit
templates ship the same way under src/squadra/_units/ (resolved via
resolve_unit(...), rendered by squadra.units).
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file squadra-0.1.0a1.tar.gz.
File metadata
- Download URL: squadra-0.1.0a1.tar.gz
- Upload date:
- Size: 147.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
96428bb277190d21ffcd976421614954312b56bfdd75620c79d1eb8304ba8114
|
|
| MD5 |
d71958dd317f0dce376ffc8320837743
|
|
| BLAKE2b-256 |
c54e960f4ad8c46600fd1d97ee3b2b51dc544b4f5b5389f0c6e2bc4beb93577c
|
Provenance
The following attestation bundles were made for squadra-0.1.0a1.tar.gz:
Publisher:
release.yml on rinman24/squadra
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
squadra-0.1.0a1.tar.gz -
Subject digest:
96428bb277190d21ffcd976421614954312b56bfdd75620c79d1eb8304ba8114 - Sigstore transparency entry: 1944257871
- Sigstore integration time:
-
Permalink:
rinman24/squadra@4eb5b3de2e3c7e74e993ac228a0e7847569945f7 -
Branch / Tag:
refs/tags/v0.1.0a1 - Owner: https://github.com/rinman24
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4eb5b3de2e3c7e74e993ac228a0e7847569945f7 -
Trigger Event:
push
-
Statement type:
File details
Details for the file squadra-0.1.0a1-py3-none-any.whl.
File metadata
- Download URL: squadra-0.1.0a1-py3-none-any.whl
- Upload date:
- Size: 103.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
47db2f64a92127b92dab0d92cf1f8c1ed2ff999a77c37ad759aa1b54939b5984
|
|
| MD5 |
28f30ceefec0beca0067f8ec09588500
|
|
| BLAKE2b-256 |
e1528270ecf3cdbdc54091de0e8a6911ba84183b6c324af33ab54d4952bb7cb7
|
Provenance
The following attestation bundles were made for squadra-0.1.0a1-py3-none-any.whl:
Publisher:
release.yml on rinman24/squadra
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
squadra-0.1.0a1-py3-none-any.whl -
Subject digest:
47db2f64a92127b92dab0d92cf1f8c1ed2ff999a77c37ad759aa1b54939b5984 - Sigstore transparency entry: 1944258070
- Sigstore integration time:
-
Permalink:
rinman24/squadra@4eb5b3de2e3c7e74e993ac228a0e7847569945f7 -
Branch / Tag:
refs/tags/v0.1.0a1 - Owner: https://github.com/rinman24
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@4eb5b3de2e3c7e74e993ac228a0e7847569945f7 -
Trigger Event:
push
-
Statement type: