Skip to content

Testing targets — sanctioned real-backend test fixtures

"TokenWorld is for testing — go crazy, it's safe." — project owner, 2026-04-24

reposix's architecture claims (pure-git agent UX, sub-second sim latencies, FUSE-free transport) are not satisfied by simulator-only coverage per project CLAUDE.md OP-6. This document enumerates the three real-backend targets the project considers safe-to-mutate during automated tests, the env vars each target requires, the rate-limit notes, and the cleanup procedure each target expects.

The literal phrase "go crazy, it's safe" appears verbatim above and at the end of each section so any future agent reading this doc has zero hesitation about running automated tests that mutate state on these specific targets.

Linked from: project CLAUDE.md (Phase 36 wires the cross-link from the "Commands you'll actually use" section).


Pre-flight verification

Before running any real-backend test (the cargo test … --ignored dark_factory_real_* invocations below, or any P91+ phase that gates on a real backend), run:

bash scripts/preflight-real-backends.sh

The script auto-sources .env if present and probes each sanctioned target with a single read-only GET. Exit codes:

Exit Meaning Next action
0 All configured targets reachable + the named test target exists. Safe to start.
1 At least one configured target failed (auth, network, or named target missing). Fix .env or the target before invoking real-backend tests.
2 No backend creds configured at all. Populate .env per the per-backend sections below.

The script is idempotent + read-only (no mutations). It is the recommended first step at the boundary between any code phase and a real-backend smoke test.


Confluence — TokenWorld space

There is exactly one sanctioned Confluence test space, not two. Verified live against GET /wiki/api/v2/spaces?keys=TokenWorld: Atlassian key is REPOSIX, id is 360450, name is "TokenWorld reposix demo space", and currentActiveAlias is TokenWorld — the alias is what resolves via reposix-confluence's resolve_space_id (its ?keys= lookup matches the active alias, not only the raw key). This doc, confluence::TokenWorld specs, and agent_flow_real.rs's confluence_test_space() default all use the TokenWorld spelling; the "Protected durable fixtures" section below uses the raw REPOSIX key — same space, same id 360450, same tenant (reuben-john), just two valid spellings for the one ?keys= lookup.

The TokenWorld Confluence space at https://${REPOSIX_CONFLUENCE_TENANT}.atlassian.net/wiki/spaces/TokenWorld is the project owner's test-only Confluence space. Tests that create pages, mutate bodies, attach files, and delete content are explicitly sanctioned.

Env vars

Name Purpose
ATLASSIAN_API_KEY Atlassian Cloud API token. NEVER logged.
ATLASSIAN_EMAIL The account the API token is associated with.
REPOSIX_CONFLUENCE_TENANT Atlassian Cloud subdomain, e.g. reuben-john.
REPOSIX_ALLOWED_ORIGINS Must include https://${tenant}.atlassian.net.

Rate-limit notes

Atlassian Cloud applies per-tenant rate limits (see crates/reposix-confluence/src/rate_limit.rs). The reposix-confluence adapter honors Retry-After and falls back to exponential backoff with a 4-attempt cap. For aggressive test loops (>10 mutations per second) serialize the test run via --test-threads=1.

Protected durable fixtures — NEVER delete

The TokenWorld space (key REPOSIX, id 360450 — same space as above, not a second one) carries a durable parent/child page pair that crates/reposix-confluence/tests/contract.rs::contract_confluence_live_hierarchy depends on (D91-08):

Role Page id Label
parent 7766017 reposix-durable-fixture
child 7798785 reposix-durable-fixture

These two ids are load-bearing for the hierarchy test's read-only fast path (verify-then-assert, no mutation) — but the test does NOT require them: if either is missing, it self-seeds a fresh kind=test-labeled pair, asserts against it, and deletes both in teardown instead. The reposix-durable-fixture label is deliberately distinct from the sweepable kind=test label precisely so cleanup below does not catch them.

Any cleanup sweep (manual or automated) of the REPOSIX or TokenWorld spaces MUST spare page ids 7766017 and 7798785. This constraint previously lived only in oral tradition / research notes — it is now a committed, discoverable fact.

Sacrificial editable page — the litmus needs a THIRD page

The full TokenWorld litmus fixture is NOT "exactly 2 pages." The Pattern-C milestone-close vision litmus (quality/gates/agent-ux/dark-factory/dvcs-third-arm.sh) round-trips an edit through a non-protected, editable record — its target-selection loop (quality/gates/agent-ux/lib/litmus-flow.sh) deliberately SKIPS the protected pair, so with only the two durable fixtures present it hard-fails ("no editable non-protected pages/<id>.md record"). Page 2818063 is that sacrificial editable page. It may be current or trashed between runs; restore it with python3 scripts/confluence_tokenworld.py restore 2818063 (note the explicit id — a bare restore no-ops) when a litmus run needs it.

So the correct fixture shape = 2 protected pages never deleted (7766017 + 7798785) + 1 sacrificial editable page (2818063). The earlier "TokenWorld = exactly 2 pages" doctrine was wrong.

Cleanup

Tests that create pages SHOULD tag them with a kind=test label so the Phase 36 cleanup procedure (deferred per the v0.9.0 plan) can sweep them. For now, manually delete leftover kind=test pages from https://${REPOSIX_CONFLUENCE_TENANT}.atlassian.net/wiki/spaces/TokenWorld (or the REPOSIX space) after a test session — except page ids 7766017 and 7798785 (see "Protected durable fixtures" above, label reposix-durable-fixture). Do not leave junk pages lying around.

"TokenWorld is for testing — go crazy, it's safe." — project owner, 2026-04-24


GitHub — reubenjohn/reposix issues

The project's own GitHub repo at https://github.com/reubenjohn/reposix. Tests that create issues, update labels, close issues, and add comments are explicitly sanctioned (but DO NOT push code to branches or modify releases).

Env vars

Name Purpose
GITHUB_TOKEN Personal access token (or gh auth token). NEVER logged.
REPOSIX_ALLOWED_ORIGINS Must include https://api.github.com.

Rate-limit notes

GitHub applies a 5000 req/hr limit for authenticated requests (and 1000 req/hr for the issues endpoint specifically). The reposix-github adapter parks the next call until reset, capped at 60s. For aggressive test loops serialize via --test-threads=1.

Cleanup

Tests that create issues SHOULD tag them with a kind:test label so they can be located via is:issue label:kind:test and bulk-closed at session end. No cleanup automation exists yet — close them manually at https://github.com/reubenjohn/reposix/issues.

"TokenWorld is for testing — go crazy, it's safe." — project owner, 2026-04-24 (the same permission applies to reubenjohn/reposix issues — they are owner-controlled and safe to mutate during tests.)

Scratch repo — reposix-scope-test-DELETEME (KEEP-policy)

reposix-scope-test-DELETEME is a throwaway GitHub scratch repo (private, under reubenjohn/) used for scope/init smoke tests. It is a git remote target, NOT an issues target like the parent repo above.

KEEP-policy: NEVER delete the repo. Reset it via force-push (git push --force) instead of deletion, so its URL/identity (https://github.com/reubenjohn/reposix-scope-test-DELETEME) stays stable across sessions and successor agents don't have to recreate it.

It is currently ARCHIVED on GitHub (verified archived: true, private: true, last pushed 2026-07-14). Archived repos reject pushes. On first reuse, unarchive it via the GitHub API before running tests:

gh api -X PATCH repos/reubenjohn/reposix-scope-test-DELETEME -f archived=false

Leave it unarchived for the session, or re-archive when done, per session norms.


JIRA — project TEST (overridable)

JIRA Cloud project key TEST is the default test target. The project key can be overridden per-test via JIRA_TEST_PROJECT or REPOSIX_JIRA_PROJECT; if both are unset, tests fall back to TEST.

Env vars

Name Purpose
JIRA_EMAIL The Atlassian account the token is associated with.
JIRA_API_TOKEN Atlassian Cloud API token. NEVER logged.
REPOSIX_JIRA_INSTANCE Atlassian Cloud subdomain, e.g. reuben-john.
JIRA_TEST_PROJECT (optional) Project key override.
REPOSIX_JIRA_PROJECT (optional) Alternative project key override.
REPOSIX_ALLOWED_ORIGINS Must include https://${instance}.atlassian.net.

The project key resolution precedence is: JIRA_TEST_PROJECTREPOSIX_JIRA_PROJECTTEST.

Rate-limit notes

JIRA Cloud applies the same per-tenant rate limits as Confluence; the reposix-jira adapter honors Retry-After (Phase 28). For aggressive test loops (>5 mutations per second) serialize via --test-threads=1.

Cleanup

Tests that create issues SHOULD apply the kind=test label and a descriptive summary prefix (e.g. [reposix-test]) so they can be located via the JIRA query project = TEST AND labels = kind=test. Bulk-close at session end.

"TokenWorld is for testing — go crazy, it's safe." — project owner, 2026-04-24 (the same permission applies to JIRA project TEST — it is the owner's test-only project and safe to mutate during tests.)


Running real-backend tests

Each backend's test surface is #[ignore]-gated and additionally skip_if_no_env!-guarded. Without env vars, all tests skip cleanly with SKIP: env vars unset: … to stderr. With env vars, tests exercise the real backend.

As of v0.10.0 the helper actually dispatches by URL schemegit fetch against a reposix::https://api.github.com/... remote hits GitHub, not the local sim. Pre-v0.10.0 the helper hardcoded SimBackend and only reposix init exercised the real adapter; see ADR-008 for details. Note the Atlassian URL form picked up a /confluence/ or /jira/ path-segment marker so the helper can tell the two adapters apart.

# Default cargo test stays green without any secrets:
cargo test --workspace

# Real-backend exercise (creds present):
export GITHUB_TOKEN=export ATLASSIAN_API_KEY= ATLASSIAN_EMAIL= REPOSIX_CONFLUENCE_TENANT=export JIRA_EMAIL= JIRA_API_TOKEN= REPOSIX_JIRA_INSTANCE=export REPOSIX_ALLOWED_ORIGINS='https://api.github.com,https://${REPOSIX_CONFLUENCE_TENANT}.atlassian.net'

cargo test -p reposix-confluence --features live -- --ignored
cargo test -p reposix-github --features live -- --ignored
cargo test -p reposix-jira --features live -- --ignored

# v0.9.0 dark-factory + init flow:
cargo test -p reposix-cli --test agent_flow_real -- --ignored

Milestone-close cadence self-sources .env. python3 quality/runners/run.py --cadence pre-release-real-backend self-sources ./.env when present (present-only, non-clobbering; quality/runners/_env_load.py, P123/DRAIN-03), so a manual set -a; . ./.env; set +a prefix is no longer required — the cadence exercises creds-in-.env instead of silently skipping every real-backend row to NOT-VERIFIED (the earlier false-green where preflight-real-backends.sh sourced .env but the runner did not). OP-1 is unchanged: a real backend is still hit only when creds are present AND REPOSIX_ALLOWED_ORIGINS is non-default. The cargo test … --ignored blocks above do not go through the runner, so they still need their own exports or a pre-sourced shell — only the run.py cadence self-sources.

Phase 36 wires three CI integration jobs (integration-contract-confluence-v09, -github-v09, -jira-v09) that decrypt the relevant secret pack and run these test commands on every push to main.


Mirror-refresh pre-step (GitHub-mirror drift)

The milestone-close vision litmus and the pre-release-real-backend cadence (python3 quality/runners/run.py --cadence pre-release-real-backend) clone a plain-git GitHub mirror of the TokenWorld pages, edit one page, and round-trip the edit back to the Confluence backend. The litmus's own prior push can leave that mirror clone trailing the backend, so a second same-day run can hit a stale-base rebase conflict on its own earlier edit. This pre-step refreshes the mirror clone to backend-current first:

bash scripts/refresh-tokenworld-mirror.sh

It fetches the backend-materialized pages/ tree and overlays it wholesale onto the GitHub mirror so the mirror matches the Confluence backend (the source of truth), then fast-forwards the mirror head — no backend write, no force-push. It converges the external GitHub mirror, which reposix sync --reconcile does not do (that command rebuilds only the local cache and leaves the external mirror byte-identical). The two are not interchangeable — do not substitute reposix sync --reconcile here.

You usually do NOT need to run this manually. As of P125 (P125/DRAIN-02) the vision litmus self-reconciles the mirror clone against the backend before its marker edit (quality/gates/agent-ux/lib/litmus-flow.sh), so a routine litmus / pre-release-real-backend run heals mirror drift on its own. Keep this script as a faster escape hatch to pre-warm or verify mirror state without a full litmus run — the same way reposix sync --reconcile is safe to run any time even though most flows never need it.