chore(release): define 1.8.12 publication gates

This commit is contained in:
archipelago
2026-09-11 05:37:21 -04:00
parent f5c0ba85cd
commit ef8c3a76be
14 changed files with 350 additions and 33 deletions
+1 -1
View File
@@ -40,7 +40,7 @@
- **The App Store now makes Archipelago's source an invitation to contribute.** GitWorkshop has its real upstream icon and source-focused description, plus a dedicated “Your node. Your source.” banner explaining that users can browse the code, clone with ngit, and send issues, patches, and reviews over Nostr.
- **Source now packages GitWorkshop instead of maintaining a separate Nostr Git interface.** The pinned upstream client runs read-only behind the authenticated app gate, launches at the dashboard's same origin under `/app/archipelago-source/`, and uses the node's consent-gated NIP-07 bridge. This remains development-node-only pending owner UAT, a clear upstream redistribution license, dependency review, and canonical Archipelago NIP-34/GRASP testing.
- **Source now packages GitWorkshop instead of maintaining a separate Nostr Git interface.** The pinned upstream client runs read-only behind the authenticated app gate, launches at the dashboard's same origin under `/app/archipelago-source/`, and uses the node's consent-gated NIP-07 bridge. The upstream revision declares no license; Archipelago's owner accepted that redistribution risk without representing the client as licensed. Production publication still requires dependency-risk disposition and a tested canonical Archipelago NIP-34/GRASP announcement.
- **Changing the node password now reports a wrong current password directly.** The backend was already rejecting the request before changing either the web or SSH password, but its error sanitizer replaced that safe, actionable explanation with “check server logs.” The real validation error now reaches the password dialog.
+7
View File
@@ -57,6 +57,13 @@ ElevenLabs TTS under a commercial-use plan.
## Redistributed software (ISO and container registry)
- **GitWorkshop** — https://github.com/DanConwayDev/gitworkshop — pinned at
`dc36db64f6a2cca29d109829eabaf0a49d4bf4da`. The upstream revision declares
no software license. Archipelago applies a documented integration patch and
redistributes the resulting static application under an explicit owner risk
acceptance dated 2026-09-11; this notice does not claim or grant upstream
copyright permission. See `docker/archipelago-source/UPSTREAM.md`.
The Archipelago OS image is based on Debian and redistributes Debian packages
(including the Linux kernel, GRUB, and non-free firmware/microcode blobs
required for hardware support); per-package license texts are preserved at
+4 -1
View File
@@ -51,6 +51,7 @@ promotional banners placed before the remaining `All Apps` grid:
"headline": "Build together.",
"description": "Catalog-controlled promotional copy.",
"tag": "NOSTR // SOURCE",
"path": "/npub1maintainer/project",
"launchLabel": "Open",
"installLabel": "Install",
"detailsLabel": "Learn more →"
@@ -59,7 +60,9 @@ promotional banners placed before the remaining `All Apps` grid:
}
```
Only IDs present in `apps` render. New dashboards prefer `storefront` from the
Only IDs present in `apps` render. An optional promotion `path` deep-links into
the installed app; Archipelago uses this to open the canonical signed Nostr
repository rather than GitWorkshop's generic dashboard. New dashboards prefer `storefront` from the
daemon-verified signed catalog and use the bundled community copy as a local
fallback. `scripts/generate-app-catalog.sh` carries this block into the signed
release artifact; changing it does not require a node OS release once that
+1
View File
@@ -26,6 +26,7 @@
"headline": "Your node. Your source.",
"description": "Install GitWorkshop to browse Archipelago's code from your own node, clone it with ngit, and contribute issues, patches, and reviews over Nostr.",
"tag": "NGIT // NOSTR // NO SILO",
"path": "/npub1w3sqdkrhn0gyuvsex32effzgnfpyde6qrrc4u467flg5e9txh4wsfn5vjg/archy",
"launchLabel": "Open GitWorkshop",
"installLabel": "Install GitWorkshop",
"detailsLabel": "How contribution works →"
+5 -4
View File
@@ -17,7 +17,8 @@ browser, issue, pull-request, or review interfaces.
The pinned revision and current upstream `main` do not contain a license file,
the package metadata declares no license, and GitHub reports no detected
license. This development image is for local evaluation only. Do not publish
it to the production app registry until upstream adds an explicit license (the
preferred auditable resolution) or its maintainer provides a written grant
covering compilation, modification, and redistribution.
license. Archipelago's owner explicitly accepted the resulting redistribution
risk on 2026-09-11. This is a project risk decision, not a claim that
GitWorkshop is licensed or that downstream recipients receive rights from its
copyright holders. An explicit upstream license remains the preferred,
auditable resolution.
+242
View File
@@ -0,0 +1,242 @@
# Archipelago 1.8.12-alpha Release Plan
**Target date:** 2026-09-11
**Scope:** OTA, signed app registry, canonical Nostr source publication,
Companion 0.5.32 alpha, and an x86_64 installer ISO
**Candidate base:** `f5c0ba85` plus the release-preparation changes documented
here
## Release Outcome
This release is complete only when all of the following are true:
1. Existing nodes can install the signed OTA and retain AIUI, app data, app
manifests, Nostr identities, Lightning state, and network configuration.
2. A fresh ISO installation boots, reports `1.8.12-alpha`, and serves the same
dashboard/runtime payload as the OTA.
3. The signed app registry contains GitWorkshop, its embedded manifest, and the
registry-owned Source promotion.
4. Archipelago's canonical repository is announced by
`npub1w3sqdkrhn0gyuvsex32effzgnfpyde6qrrc4u467flg5e9txh4wsfn5vjg` as the
stable identifier `archy`, is cloneable through `nostr://`, and opens in
GitWorkshop from the Source promotion.
5. Contributors can create an issue or proposal without gaining canonical
merge, release-tag, catalog-signing, or OTA-signing authority.
Shipping GitWorkshop alone does not satisfy item 4. The client is the interface;
the signed NIP-34 announcement and reachable Git object servers are the source
publication.
## Audited Candidate State
### Ready
- The accumulated candidate is committed and the development node is healthy.
- GitWorkshop is healthy behind the authenticated same-origin app gate.
- The app works as a top-level Companion WebView and as a browser tab; the node
signer remains consent-gated and identity-switchable.
- The Source icon, description, banner, Popular/All Apps placement, and
maintainer npub are present in the development catalog.
- Payment receipts, Minibits claim persistence, update cancellation, app
credential interstitials, Gitea limits, certificate guidance, and the pasta
listener repair are included in the candidate.
- `ngit 2.6.3` and `git-remote-nostr 2.6.3` are installed on the node.
- The existing public Gitea source and Smart HTTP clone endpoints respond with
HTTP 200.
- The release-root signer exists locally as a binary, while its mnemonic
remains correctly offline.
- ISO dependencies (`xorriso`, `mksquashfs`, and QEMU) are installed.
- The corrected release gate passed on 2026-09-11: 1,091 frontend tests,
production UI build, registry candidate/trust checks, Rust check, 114 focused
backend tests plus orchestration, and live dashboard/AIUI/RPC probes. Android
lint and the mobile Chromium signer-handoff regression also pass.
- The locally served Companion candidate is 28,157,847 bytes with SHA-256
`30e0b6257f17bb5e072941ccce6fde04b6c8ae59a49b45169e1920aec1b108cf`.
- The owner accepted the risk of redistributing the pinned, unlicensed
GitWorkshop revision on 2026-09-11. `NOTICE` and `UPSTREAM.md` disclose that
decision without claiming an upstream license.
### Not Yet Complete
- This checkout is not logged into `ngit` and has no Nostr repository remote or
cached kind `30617` announcement.
- The production signed catalog still contains 61 apps but not
`archipelago-source` or `storefront`; a freshly generated candidate contains
62 apps, 61 embedded manifests, GitWorkshop 0.4.0, and the Source promotion.
- The previous public Companion download was 0.5.28-debug. A 0.5.32-debug
candidate has now been built with the same certificate and can update that
alpha channel; the accepted 0.5.32-uat build remains a separate package.
- The shared debug certificate is intentionally public and its private key is
reachable in an existing public Git branch. It provides upgrade continuity,
not production authenticity. A future production Companion must use the
`com.archipelago.app` package with a privately held release key.
- The pinned GitWorkshop revision has 4 high and 6 moderate production npm
advisories. The final image is static nginx, reducing exposure to Node-server
advisories, but browser dependencies remain reachable. Release requires an
owner accept/fix decision.
- The repository is large: this checkout has about 5.4 GiB of packed objects,
and the Gitea repository is about 5 GiB. Public GRASP replication must be
allowed enough time and verified rather than assumed complete.
- The provided secrets audit reports known false positives in documentation,
path-variable names, and deliberate test keys. Before Nostr publication,
record that review and ensure local Git remote credentials are rotated; local
`.git/config` credentials are not tracked and must never be copied into an
announcement or release log.
## Required Owner Inputs
These secrets must be entered interactively and must not be sent in chat,
committed, placed in shell history, or stored on the node:
1. Access to the Nostr maintainer identity matching the npub above. Prefer an
interactive NIP-46 connection through `ngit account connect`; do not pass an
nsec on a command line.
2. The 24-word Archipelago release-master mnemonic for three signing actions:
the OTA manifest, the app catalog, and the ISO checksum document.
3. A decision to accept the recorded GitWorkshop dependency advisories for
1.8.12-alpha or pause for a dependency update.
4. A decision to publish 0.5.32 on the existing explicitly non-production
`.debug` Companion channel, omit it, or pause for a secure production-app
signing ceremony.
The shared Companion update certificate is not a production secret. It was
temporarily recovered from the public branch, verified against the distributed
0.5.28 APK, used to build the 0.5.32 alpha upgrade candidate, and removed from
the working tree. Neither it nor the UAT keystore belongs in the release commit.
## Execution Order
### Gate A — Freeze And Production Companion
1. Verify the shared Companion certificate against the existing public APK
before building anything. This is complete for the alpha candidate.
2. Build the 0.5.32 shared-key alpha APK with v1, v2, and v3 signature schemes.
3. Verify package ID, version code/name, signing digest, and install-as-upgrade
behavior on the owner's phone.
4. Stage the APK and its JSON version metadata in the dashboard payload.
5. Run formatting, frontend type checking/unit tests/build, Android lint/build,
focused backend tests, manifest validation, registry-candidate validation,
secret review, and live node smoke probes.
6. Deploy that exact build on this node and perform the short final UAT:
GitWorkshop and IndeedHub first login, identity switching, Companion WebView,
external tab, certificate install, payment receipt, update cancellation, and
credential interstitial.
Stop on any failure. Do not sign or publish a workaround build.
### Gate B — Publish Archipelago Source Through Nostr
Run Nostr publication from a disposable clean clone so `ngit init` cannot
repoint or rewrite the release checkout's remotes.
1. Confirm the clean clone matches the final candidate and contains `LICENSE`,
`CONTRIBUTING.md`, `SECURITY.md`, and `NOTICE`.
2. Connect `ngit` interactively to the canonical maintainer identity and verify
`ngit account whoami` reports the expected npub.
3. Publish identifier `archy`, title `Archipelago`, the public project
description/homepage, the existing Gitea HTTPS clone URL, and multiple
current public GRASP services. Use at least `relay.ngit.dev` and
`gitnostr.com`; add an Archipelago-operated GRASP service later without
changing the repository coordinate.
4. Allow the complete repository push to finish. Do not treat a published
announcement as proof that all Git objects arrived.
5. Verify the announcement author, identifier, relays, clone URLs, default
branch, and release tags with `ngit repo`.
6. In a second empty directory, clone the printed `nostr://` URL and compare its
`HEAD` tree to the candidate.
7. Open
`/npub1w3sqdkrhn0gyuvsex32effzgnfpyde6qrrc4u467flg5e9txh4wsfn5vjg/archy`
in the packaged GitWorkshop and confirm README/code browsing works.
8. Publish a test issue/proposal from a non-maintainer identity, review it from
the maintainer identity, and confirm it cannot change canonical refs without
maintainer authorization.
9. Confirm clone/browse still work with one GRASP server unavailable. A later
infrastructure release must add an Archipelago-operated GRASP endpoint; the
signed coordinate remains stable when servers change.
The Source promotion carries the deterministic npub/identifier deep-link. It
must not be published to the registry until steps 5–8 pass.
### Gate C — Prepare And Publish OTA 1.8.12-alpha
1. Convert the top `Unreleased` changelog section into
`v1.8.12-alpha (2026-09-11)` and synchronize What's New.
2. Run `bash scripts/create-release.sh 1.8.12-alpha`. Enter the release mnemonic
once when prompted and terminate input with Ctrl-D.
3. Verify the new release commit, annotated tag, staged backend/frontend
artifacts, signed pending manifest, embedded AIUI, APK version, and clean
tree.
4. Publish with
`SKIP_ISO=1 scripts/publish-release-assets.sh 1.8.12-alpha gitea-vps2`.
The script uploads and hashes artifacts before pushing the fleet-visible
manifest. Never push `main` manually around this ordering.
5. Verify both local and public manifests, both asset hashes, public HTTPS
download links, and update availability from this node.
6. Apply the OTA to this node first, reboot if requested, and rerun live smoke
probes before allowing the catalog publication.
### Gate D — Publish The Signed App Registry
This follows the OTA canary because GitWorkshop's manifest builds from
`/opt/archipelago/docker/archipelago-source`, which older nodes do not have.
1. Generate `releases/app-catalog.json` from the final manifests.
2. Require 62 entries, 61 embedded manifests, `archipelago-source` 0.4.0,
registry-owned storefront metadata, and the canonical Source deep-link.
3. Run strict metadata, manifest, and deployed-registry trust-floor checks.
4. Sign the exact catalog bytes using `bash scripts/sign-catalog.sh` and verify
the pinned release-root DID.
5. Commit and push only the signed catalog after the OTA canary succeeds.
6. Confirm `/api/app-catalog` verifies the signature and a fresh node can see,
install, launch, stop, start, and uninstall GitWorkshop.
### Gate E — Build, Test, Sign, And Attach The ISO
1. From clean `main` with the live signed 1.8.12 manifest and tag, run
`bash scripts/build-iso-release.sh`. Do not use `--skip-gates` or `--no-qemu`
for the publishable candidate.
2. Require the release harness, strict catalog check, full Rust test suite,
version/artifact checks, ISO mount smoke test, and QEMU boot attempt.
3. Record the ISO filename, byte size, SHA-256, build commit, and whether QEMU
was conclusive. If QEMU is inconclusive, boot the ISO on the target test
machine before publication.
4. Sign the checksum document with
`bash scripts/sign-iso-checksums.sh <iso>` and cryptographically verify it.
5. Attach the ISO, plain checksum, and signed checksum JSON by rerunning
`scripts/publish-release-assets.sh 1.8.12-alpha gitea-vps2`.
6. Verify Gitea's stored sizes and public HTTPS links, then download/hash the
public checksum documents. Flash/boot/install on real hardware before
calling the ISO generally available.
## Rollback Rules
- OTA: the live manifest remains 1.8.11-alpha until all 1.8.12 artifacts have
uploaded and verified. If canary application fails, do not publish the new
catalog; restore the previous manifest/artifacts through the established
rollback path.
- Registry: retain the previously signed catalog. Roll back by publishing a
newly signed catalog that removes GitWorkshop or restores its prior entry;
never edit signed JSON in place.
- Nostr source: repository state is authoritative only when signed by the
configured maintainer. Add replacement servers before removing failed ones,
then republish and run `ngit sync`.
- ISO: an ISO is not referenced by the OTA manifest. A failed ISO build/upload
cannot block or corrupt an already verified OTA; do not advertise a partial
attachment.
- Companion: retain 0.5.28 and the verified alpha signer until 0.5.32 installs
as an upgrade. Never replace the public APK with the differently signed UAT
build. Do not describe the public debug-key channel as production-secure.
## Final Release Record
Before announcing completion, record in the release notes:
- release/tag/commit and signed manifest DID;
- backend and frontend SHA-256 values;
- signed catalog commit, app count, and verification result;
- canonical `nostr://` URL, NIP-34 coordinate, GRASP/clone endpoints, and clone
verification commit;
- Companion package/version/signing digest and APK SHA-256;
- ISO filename/size/SHA-256/signature and smoke/QEMU/real-hardware results;
- owner UAT acceptance and the explicit GitWorkshop no-license/dependency-risk
decisions.
+25 -19
View File
@@ -1,11 +1,13 @@
# Nostr Git Source Hosting Plan
**Reviewed:** 2026-09-08
**Reviewed:** 2026-09-11
**Status:** GitWorkshop integration is deployed and engineering-tested on the
development node, ready for owner UAT. Canonical repository publication and
release work remain separate gates. No app-registry, OTA, ISO, or production
artifact may be published until the owner accepts the node deployment.
development node. The owner reported the corrected flows working and requested
a production release on 2026-09-11. Canonical repository publication remains
incomplete: the checkout has no Nostr repository configuration and `ngit` is
not logged into the canonical maintainer identity. The signed production app
catalog also does not yet carry GitWorkshop or its storefront promotion.
The Android companion opens Source as a top-level page in its native in-app
WebView. GitWorkshop's injected NIP-07 provider creates a small authenticated
@@ -70,7 +72,7 @@ Primary references:
- Relay and GRASP data is untrusted. Canonical status is derived only from the
signed repository announcement and configured maintainer identities.
## Upstream Pin And Redistribution Gate
## Upstream Pin And Redistribution Decision
The development image currently pins GitWorkshop commit
`dc36db64f6a2cca29d109829eabaf0a49d4bf4da` (2026-07-28). The integration patch
@@ -79,11 +81,13 @@ only adds base-path support and the Archipelago NIP-07 provider.
The pinned revision and current upstream `main` have no license file, the npm
package metadata declares no license, and GitHub reports no detected license.
An earlier project-site description of “MIT” is not a license grant bundled
with the code. Local engineering and owner evaluation may continue, but the
compiled image must not be published to the production app registry until its
redistribution terms are unambiguous.
with the code. On 2026-09-11 the Archipelago owner explicitly accepted the
risk of publishing the patched build without an upstream license. That removes
the project's internal release hold, but it does not turn the upstream code
into open-source software or grant downstream rights. The absence is disclosed
in `NOTICE` and the packaged `UPSTREAM.md`.
Preferred resolution: ask upstream to add an SPDX-recognized license file
Preferred follow-up: ask upstream to add an SPDX-recognized license file
(MIT if that remains their intent), then re-pin at or after that commit and add
GitWorkshop plus its copyright/license notice to Archipelago's `NOTICE` and
generated image inventory. A written grant that explicitly permits compiling,
@@ -170,12 +174,12 @@ The live same-origin route reaches the authenticated app gate through the
node's loopback, LAN, Tailscale, and FIPS addresses. A rollback snapshot is at
`/var/backups/archipelago/pre-uat-fixes-20260908-1140` on the development node.
### 5. Owner UAT — pending owner action
### 5. Owner UAT — accepted, final release-candidate retest required
The owner validates install, launch, navigation, repository discovery, identity
selection, consent prompts, source browsing, and available contribution flows.
Engineering fixes UAT findings on this node and repeats the gate. Owner UAT is
not inferred from automated tests.
The owner exercised the corrected node deployment and requested release on
2026-09-11. A short final retest remains required after the 0.5.32 release-channel
Companion APK and canonical Archipelago repository deep-link replace their UAT
counterparts; those two release inputs were not present in the accepted build.
For companion testing, the node hosts a local-only Archipelago Companion
`0.5.32-uat` at `/packages/archipelago-companion-0.5.32-uat.apk`. It uses the
@@ -244,10 +248,10 @@ remains pending owner visual retest.
- Deep-link/configure GitWorkshop to the verified repository.
- Run the real-node proposal and recovery drills listed above.
### 7. Release — explicitly blocked pending prior gates
### 7. Release — pending canonical publication and signing gates
Only after engineering tests, owner UAT acceptance, canonical launch tests,
license confirmation, and dependency review may the team:
Only after engineering tests, the final release-candidate retest, canonical
launch tests, and dependency-risk disposition may the team:
- build and publish a production multi-architecture app image;
- sign/update the production app-registry entry;
@@ -274,5 +278,7 @@ APK.
## Open Decisions Before Canonical Launch
- Which Archipelago-operated and independent relay/GRASP endpoints are used?
- Will upstream add an explicit GitWorkshop license file, or provide another
written redistribution grant suitable for registry publication?
- Does the owner accept the recorded 4 high and 6 moderate GitWorkshop npm
advisories for this release, or must the integration patch update them first?
- Will upstream add an explicit GitWorkshop license as a post-release
clarification for downstream users?
+1
View File
@@ -26,6 +26,7 @@
"headline": "Your node. Your source.",
"description": "Install GitWorkshop to browse Archipelago's code from your own node, clone it with ngit, and contribute issues, patches, and reviews over Nostr.",
"tag": "NGIT // NOSTR // NO SILO",
"path": "/npub1w3sqdkrhn0gyuvsex32effzgnfpyde6qrrc4u467flg5e9txh4wsfn5vjg/archy",
"launchLabel": "Open GitWorkshop",
"installLabel": "Install GitWorkshop",
"detailsLabel": "How contribution works →"
Binary file not shown.
@@ -1,4 +1,4 @@
{
"versionName": "0.5.28",
"versionCode": 48
"versionName": "0.5.32",
"versionCode": 52
}
+3 -3
View File
@@ -248,7 +248,7 @@
<div class="flex flex-wrap items-center gap-3">
<button
v-if="isInstalled(promotion.app.id) && !isStartingUp(promotion.app.id)"
@click.stop="launchInstalledApp(promotion.app)"
@click.stop="launchInstalledApp(promotion.app, promotion.path)"
class="glass-button rounded-lg px-6 py-2.5 text-sm font-medium"
>{{ promotion.launchLabel || 'Launch' }}</button>
<button
@@ -660,8 +660,8 @@ function getAppTier(appId: string): string {
return 'optional'
}
function launchInstalledApp(app: MarketplaceApp) {
appLauncher.openSession(app.id)
function launchInstalledApp(app: MarketplaceApp, path?: string) {
appLauncher.openSession(app.id, path ? { path } : undefined)
}
async function handleInstall(app: MarketplaceApp) {
@@ -22,6 +22,9 @@ export interface CatalogPromotion {
headline: string
description: string
tag: string
/** Optional route within the installed app. Used by the Source promotion to
* open the canonical Archipelago repository rather than a generic client. */
path?: string
launchLabel?: string
installLabel?: string
detailsLabel?: string
+15 -3
View File
@@ -42,6 +42,7 @@ INTERNAL_MANIFEST_IDS = {
}
LEGACY_STACK_CATALOG_IDS = {
"btcpay",
"immich",
"netbird",
"tailscale",
@@ -159,7 +160,12 @@ def main() -> int:
)
args = parser.parse_args()
catalog = load_catalog(Path(args.catalog))
catalog_path = Path(args.catalog)
with catalog_path.open("r", encoding="utf-8") as fh:
raw_catalog = json.load(fh)
registry_catalog = isinstance(raw_catalog.get("apps"), dict)
catalog = load_catalog(catalog_path)
manifests = load_manifests(Path(args.apps_dir))
catalog_ids = set(catalog)
@@ -172,14 +178,20 @@ def main() -> int:
compared_fields = [
"title",
"version",
"description",
"dockerImage",
"category",
"tier",
"icon",
"repoUrl",
]
# The signed registry catalog deliberately overrides versions and images:
# that is how app updates are shipped independently of an OS release. The
# public storefront list, on the other hand, should match the manifest's
# advertised version/image. Treating registry overrides as metadata drift
# made a freshly generated, otherwise valid release artifact fail on every
# app whose image tag had advanced since its disk manifest was authored.
if not registry_catalog:
compared_fields[1:1] = ["version", "dockerImage"]
drift: list[str] = []
for app_id in sorted(catalog_ids & manifest_ids):
catalog_app = catalog[app_id]
+41
View File
@@ -73,6 +73,47 @@ stage "git-diff-check" git diff --check
stage "cargo-fmt" timeout 240 cargo fmt --manifest-path core/Cargo.toml --all --check
stage "manifest-shell" python3 scripts/check-manifest-shell.py
stage "catalog-drift" python3 scripts/check-app-catalog-drift.py --release --strict
# Validate the artifact that will actually be signed and published, not only
# the dashboard's list-form source catalog. This catches missing embedded
# manifests, an untrusted image registry, or a storefront promotion that names
# an app absent from the signed registry before the release-root ceremony.
registry_candidate() {
local candidate rc=0
candidate=$(mktemp -t archipelago-app-catalog.XXXXXX.json)
bash scripts/generate-app-catalog.sh "$candidate" || rc=$?
if [[ $rc -eq 0 ]]; then
python3 scripts/check-app-catalog-drift.py \
--catalog "$candidate" --release --strict || rc=$?
fi
if [[ $rc -eq 0 ]]; then
python3 scripts/check-catalog-registry-trust.py \
--repo "$REPO" --catalog "$candidate" || rc=$?
fi
if [[ $rc -eq 0 ]]; then
python3 - "$candidate" <<'PY' || rc=$?
import json
import sys
catalog = json.load(open(sys.argv[1], encoding="utf-8"))
apps = catalog.get("apps", {})
storefront = catalog.get("storefront", {})
promotions = storefront.get("promotions", [])
if "archipelago-source" not in apps:
raise SystemExit("registry candidate omits archipelago-source")
source_promotions = [item for item in promotions if item.get("id") == "archipelago-source"]
if not source_promotions:
raise SystemExit("registry candidate omits the Archipelago source promotion")
expected_path = "/npub1w3sqdkrhn0gyuvsex32effzgnfpyde6qrrc4u467flg5e9txh4wsfn5vjg/archy"
if source_promotions[0].get("path") != expected_path:
raise SystemExit("source promotion does not open the canonical Archipelago repository")
print("registry candidate includes GitWorkshop and its source promotion")
PY
fi
rm -f -- "$candidate"
return "$rc"
}
stage "registry-candidate" registry_candidate
# Every release must surface its CHANGELOG entry in the Settings "What's New"
# modal. The modal hardcodes a block per version and has drifted behind before
# (sat at v1.7.84 while the fleet shipped to v1.7.92). Fail if any CHANGELOG