fix: add production runtime RC release channel

Publish signed prerelease artifacts from v2.0.0-rc.* tags, bind bootstrap trust to the immutable RC tag, make missing-release errors actionable, and install the six public skills from the canonical subpath.
This commit is contained in:
Sinabina
2026-07-20 16:51:59 -07:00
parent d6ef673e4d
commit b0047cc525
84 changed files with 367 additions and 140 deletions
@@ -2,11 +2,19 @@
import fs from "node:fs/promises";
import path from "node:path";
const [directory, repository = process.env.GITHUB_REPOSITORY, version = "2.0.0"] = process.argv.slice(2);
const [
directory,
repository = process.env.GITHUB_REPOSITORY,
version = "2.0.0",
releaseTag = `v${version}`,
] = process.argv.slice(2);
if (!directory || !repository) {
console.error("Usage: create-runtime-release-manifest.mjs <artifact-dir> <owner/repo> [version]");
console.error("Usage: create-runtime-release-manifest.mjs <artifact-dir> <owner/repo> [version] [release-tag]");
process.exit(2);
}
if (!/^v\d+\.\d+\.\d+(?:-rc\.\d+)?$/.test(releaseTag)) {
throw new Error(`Invalid runtime release tag: ${releaseTag}`);
}
const targets = [
"darwin-arm64",
@@ -35,8 +43,8 @@ const capabilityComponents = {
ios: ["ios"],
};
const commonComponents = ["core", "browser-code", "browser-headless", "browser-visible", "design", "diagram", "pdf"];
const release = `https://github.com/${repository}/releases/download/v${version}`;
const certificateIdentity = `https://github.com/${repository}/.github/workflows/release-artifacts.yml@refs/tags/v${version}`;
const release = `https://github.com/${repository}/releases/download/${releaseTag}`;
const certificateIdentity = `https://github.com/${repository}/.github/workflows/release-artifacts.yml@refs/tags/${releaseTag}`;
const targetRecords = {};
for (const target of targets) {
+7 -3
View File
@@ -2,7 +2,9 @@ name: Release runtime artifacts
on:
push:
tags: [v2.0.0]
tags:
- v2.0.0
- v2.0.0-rc.*
workflow_dispatch:
permissions:
@@ -170,7 +172,7 @@ jobs:
- uses: sigstore/cosign-installer@d7543c93d881b35a8faa02e8e3605f69b7a1ce62 # v3.10.0
- name: Create strict six-target manifest
run: node .github/scripts/create-runtime-release-manifest.mjs release-output "$GITHUB_REPOSITORY" 2.0.0
run: node .github/scripts/create-runtime-release-manifest.mjs release-output "$GITHUB_REPOSITORY" 2.0.0 "$GITHUB_REF_NAME"
- name: Checksum and keyless-sign manifest
run: |
@@ -188,11 +190,13 @@ jobs:
- name: Publish immutable release assets
env:
GH_TOKEN: ${{ github.token }}
PRERELEASE_FLAG: ${{ contains(github.ref_name, '-rc.') && '--prerelease' || '' }}
run: |
set -euo pipefail
gh release create "$GITHUB_REF_NAME" \
--verify-tag \
--title "GStack runtime 2.0.0" \
$PRERELEASE_FLAG \
--title "GStack runtime $GITHUB_REF_NAME" \
--notes "Signed optional runtime artifacts for the six portable GStack skills." \
release-output/*
shell: bash
+1 -1
View File
@@ -60,7 +60,7 @@ they contain no copied judgment. Representative mappings:
Canonical installation is standards-based:
```bash
npx skills add time-attack/gstack
npx skills add time-attack/gstack/skills
```
Delegate host detection, placement, project/global scope, selected-skill
+1 -1
View File
@@ -13,7 +13,7 @@ mutation boundaries, recommendations, or voice. Compatibility files under
Specification, and Full chain. Do not expose its internal aliases as more
top-level modes.
Canonical install is `npx skills add time-attack/gstack`. Standard installers
Canonical install is `npx skills add time-attack/gstack/skills`. Standard installers
own host placement and scope. Do not add host-specific install logic to the 2.0
path. Pure judgment must work without the optional `bin/gstack` runtime.
The host-neutral `./setup` installs only that managed runtime/capability bundle;
+4 -4
View File
@@ -35,7 +35,7 @@ Install with the standard Agent Skills installer; it owns host detection,
scope, destination paths, updates, removal, and selected-skill installation:
```bash
npx skills add time-attack/gstack
npx skills add time-attack/gstack/skills
```
That installs the six judgment skills. Install a subset with the installer's
@@ -87,7 +87,7 @@ extension, connector, MCP, settings entry, profile, alternate host, or browser
runtime is configured without consent, and `./setup` is never a GStack 2
browser setup command.
Install through `npx skills add time-attack/gstack`; do not clone the repository
Install through `npx skills add time-attack/gstack/skills`; do not clone the repository
under `.agents/skills/gstack`. A standards installation exposes exactly one
canonical QA skill at `.agents/skills/qa/SKILL.md`. The cloned compatibility
tree contains legacy GStack 1 entry points and is not the GStack 2 install
@@ -96,7 +96,7 @@ surface.
The npm package is deliberately not the skill installer and does not contain
the six skill tree or compiled browser/design/PDF payloads. It is the small
host-neutral runtime control/bootstrap surface used by release tooling. New
users should install skills with `npx skills add time-attack/gstack`; optional
users should install skills with `npx skills add time-attack/gstack/skills`; optional
capabilities are downloaded by a skill after consent.
Public web research is optional. Context.dev is the only new external service,
@@ -161,7 +161,7 @@ archive. It is not the GStack 2 installation or first-run path.
## Legacy 1.x host-specific install (compatibility only)
Do not use this section for a new GStack 2 install. Use
`npx skills add time-attack/gstack` above. These instructions remain while old
`npx skills add time-attack/gstack/skills` above. These instructions remain while old
commands are documented as opt-in aliases. They describe a 1.x release/tag:
the current branch's `./setup` is runtime-only and will not perform the
host-specific actions shown below.
+1 -1
View File
@@ -85,7 +85,7 @@ judgment. This prevents an alias from drifting away from the canonical source.
The standard Agent Skills tree is the unit of distribution:
```bash
npx skills add time-attack/gstack
npx skills add time-attack/gstack/skills
```
The installer—not GStack—owns host detection, project/global scope, destination
+2 -2
View File
@@ -18,7 +18,7 @@ bundle.
## Canonical installation
```bash
npx skills add time-attack/gstack
npx skills add time-attack/gstack/skills
```
The standards installer owns host detection, destination paths, project/global
@@ -32,7 +32,7 @@ Examples supported by the installer interface:
npx skills add time-attack/gstack/skills --skill qa
# Installer-managed global scope
npx skills add time-attack/gstack -g
npx skills add time-attack/gstack/skills -g
```
Run `npx skills add --help` for the installed CLI version before scripting
+1 -1
View File
@@ -4552,7 +4552,7 @@
"source_path": "gstack-upgrade/SKILL.md.tmpl",
"base_sha": "bb57306d98c97011b0919c6132705a15b1579781",
"blob_sha": "5402a1da3c857cbf50668085fce53172b72bba0d",
"normalized_render_sha256": "a913cf77f76c4d68c576a06190b498e3d8d3b60b85498a68f173b23a7f800828",
"normalized_render_sha256": "2c8cf9e505b7da27730bf646a33ee38e8092259f3335a524c71e57ba5b602541",
"target": "skills/ship/references/legacy/gstack-upgrade.md",
"disposition": "DUPLICATE_INFRASTRUCTURE",
"overlays": [
+13 -4
View File
@@ -5,10 +5,11 @@ GStack has two explicit version identities during the 2.0 migration:
- `VERSION` and `package.json.version` are the repository/package release
counter. They remain byte-equal and retain the existing four-slot format so
the 1.x compatibility ship queue does not silently fail open.
- `package.json.gstack.runtimeVersion`, `runtime/index.js`,
`runtime/install.js`, and every standards-installed bootstrap declare the
managed-runtime protocol release `2.0.0`. The official artifact tag and
manifest use that value.
- `package.json.gstack.runtimeVersion`, `runtime/index.js`, and
`runtime/install.js` declare the managed-runtime protocol release `2.0.0`.
Each standards-installed bootstrap separately pins one immutable artifact
release tag. Candidate bootstraps use `v2.0.0-rc.N`; the manifest and bundle
remain runtime-compatible with `2.0.0`. Stable bootstraps use `v2.0.0`.
They are intentionally different namespaces. CI fails if either identity
drifts inside its own namespace.
@@ -24,6 +25,14 @@ linux-arm64 linux-x64 (glibc)
windows-arm64 windows-x64
```
Both `v2.0.0-rc.*` and `v2.0.0` tags use the same build, signing, manifest,
attestation, and smoke path. RC tags publish GitHub prereleases so the exact
fresh-machine production bootstrap can be exercised before the stable tag is
created. Runtime compatibility and release-channel identity are deliberately
separate: archive names and manifest `version` remain `2.0.0`, while URLs and
Sigstore certificate identity bind to the immutable RC or stable tag that
actually published them.
Each archive has one `gstack/` root and no symlinks. CI records an exact byte
count and SHA-256, signs the archive keylessly with Cosign, emits a Sigstore
bundle, and also creates a GitHub build-provenance attestation. The release
+1 -1
View File
@@ -10,7 +10,7 @@ Install the canonical Agent Skills source, then use that installer's tracked
source and lock metadata for discovery, updates, and removal:
```bash
npx skills add time-attack/gstack
npx skills add time-attack/gstack/skills
npx skills update # interactive scope
npx skills update -p # project installs only
npx skills update -g # global installs only
+1 -1
View File
@@ -9,7 +9,7 @@
"source_path": "gstack-upgrade/SKILL.md.tmpl",
"base_sha": "bb57306d98c97011b0919c6132705a15b1579781",
"blob_sha": "5402a1da3c857cbf50668085fce53172b72bba0d",
"normalized_render_sha256": "a913cf77f76c4d68c576a06190b498e3d8d3b60b85498a68f173b23a7f800828",
"normalized_render_sha256": "2c8cf9e505b7da27730bf646a33ee38e8092259f3335a524c71e57ba5b602541",
"target": "skills/ship/references/legacy/gstack-upgrade.md",
"overlays": [
679
+1 -1
View File
@@ -4552,7 +4552,7 @@
"source_path": "gstack-upgrade/SKILL.md.tmpl",
"base_sha": "bb57306d98c97011b0919c6132705a15b1579781",
"blob_sha": "5402a1da3c857cbf50668085fce53172b72bba0d",
"normalized_render_sha256": "a913cf77f76c4d68c576a06190b498e3d8d3b60b85498a68f173b23a7f800828",
"normalized_render_sha256": "2c8cf9e505b7da27730bf646a33ee38e8092259f3335a524c71e57ba5b602541",
"target": "skills/ship/references/legacy/gstack-upgrade.md",
"disposition": "DUPLICATE_INFRASTRUCTURE",
"overlays": [
+20 -6
View File
@@ -13,8 +13,12 @@ import { fileURLToPath } from "node:url";
export const BOOTSTRAP_SCHEMA_VERSION = 2;
export const BOOTSTRAP_RUNTIME_VERSION = "2.0.0";
// Keep the runtime compatibility version separate from the immutable release
// channel. Release candidates carry the 2.0.0 runtime contract while letting
// fresh-machine production journeys run before the stable v2.0.0 tag exists.
export const BOOTSTRAP_RELEASE_TAG = "v2.0.0-rc.1";
export const OFFICIAL_MANIFEST_URL =
`https://github.com/time-attack/gstack/releases/download/v${BOOTSTRAP_RUNTIME_VERSION}/gstack-runtime-manifest.json`;
`https://github.com/time-attack/gstack/releases/download/${BOOTSTRAP_RELEASE_TAG}/gstack-runtime-manifest.json`;
const CAPABILITIES = new Set(["browser", "browser-visible", "design", "pdf", "diagram", "ios"]);
const CAPABILITY_DEPENDENCIES = Object.freeze({
browser: Object.freeze([]),
@@ -47,9 +51,9 @@ const ALLOWED_DOWNLOAD_HOSTS = new Set([
"objects.githubusercontent.com",
"release-assets.githubusercontent.com",
]);
const OFFICIAL_RELEASE_PREFIX = `/time-attack/gstack/releases/download/v${BOOTSTRAP_RUNTIME_VERSION}/`;
const OFFICIAL_RELEASE_PREFIX = `/time-attack/gstack/releases/download/${BOOTSTRAP_RELEASE_TAG}/`;
const OFFICIAL_CERTIFICATE_IDENTITY =
`https://github.com/time-attack/gstack/.github/workflows/release-artifacts.yml@refs/tags/v${BOOTSTRAP_RUNTIME_VERSION}`;
`https://github.com/time-attack/gstack/.github/workflows/release-artifacts.yml@refs/tags/${BOOTSTRAP_RELEASE_TAG}`;
const GITHUB_OIDC_ISSUER = "https://token.actions.githubusercontent.com";
export async function main(argv = process.argv.slice(2), options = {}) {
@@ -90,7 +94,9 @@ export async function main(argv = process.argv.slice(2), options = {}) {
);
const manifestUrl = options.manifestUrl ?? OFFICIAL_MANIFEST_URL;
assertOfficialUrl(manifestUrl, { manifest: true });
const manifest = await fetchJson(fetch_, manifestUrl);
const manifest = await fetchJson(fetch_, manifestUrl, {
official: manifestUrl === OFFICIAL_MANIFEST_URL,
});
validateManifest(manifest, target);
const home = path.resolve(parsed.home ?? process.env.GSTACK_HOME ?? path.join(os.homedir(), ".gstack"));
const reusable = await inspectReusableRuntime(home, manifest.version).catch(() => null);
@@ -300,10 +306,18 @@ function sha256File(file) {
});
}
async function fetchJson(fetch_, url) {
async function fetchJson(fetch_, url, options = {}) {
const response = await fetch_(url, { headers: { Accept: "application/json" }, redirect: "follow" });
assertFinalDownloadUrl(response.url || url);
if (!response.ok) throw bootstrapError(`Download failed with HTTP ${response.status}`, "BOOTSTRAP_DOWNLOAD_FAILED");
if (!response.ok) {
if (options.official && response.status === 404) {
throw bootstrapError(
`Official runtime release ${BOOTSTRAP_RELEASE_TAG} is not published at ${url}. No files were downloaded or installed.`,
"BOOTSTRAP_RELEASE_UNAVAILABLE",
);
}
throw bootstrapError(`Manifest download failed with HTTP ${response.status} from ${url}. No files were downloaded or installed.`, "BOOTSTRAP_DOWNLOAD_FAILED");
}
const value = await response.json();
if (!value || typeof value !== "object") throw bootstrapError("Manifest returned invalid JSON", "BOOTSTRAP_MANIFEST_INVALID");
return value;
+2 -2
View File
@@ -384,7 +384,7 @@ Print this replacement invocation, then dispatch to it exactly:
\`${assignment.replacement}\`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved \`${assignment.source}\` module. If that dispatcher is not installed, tell the user to install it with \`npx skills add time-attack/gstack --skill ${assignment.tree}\`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved \`${assignment.source}\` module. If that dispatcher is not installed, tell the user to install it with \`npx skills add time-attack/gstack/skills --skill ${assignment.tree}\`.
`);
rows.push(`| \`/${assignment.source}\` | \`${assignment.replacement}\` | \`skills/${assignment.tree}/references/legacy/${assignment.source}.md\` |`);
aliases.push({
@@ -483,7 +483,7 @@ function webContextContract(): string {
'',
'Persist only the explicit choice with `gstack context select host`, `gstack context select local-browser`, or `gstack context select none`. For Context.dev, show `gstack context options`, then use `gstack context setup` and its hidden key prompt; consent and key storage belong to the runtime, never this judgment prompt. Do not infer Context choice or consent.',
'',
'Capability-dependent work follows `references/RUNTIME.md`. Pure judgment never requires the runtime. Skill placement remains owned by `npx skills add time-attack/gstack` and is never inferred from runtime state.',
'Capability-dependent work follows `references/RUNTIME.md`. Pure judgment never requires the runtime. Skill placement remains owned by `npx skills add time-attack/gstack/skills` and is never inferred from runtime state.',
'',
].join('\n');
}
+2 -2
View File
@@ -188,7 +188,7 @@ export function renderLegacyBody(source: string): string {
*/
function portLegacyText(value: string, source: string): string {
if (source === 'gstack-upgrade') {
return `# Legacy upgrade compatibility\n\nThe 1.x host-directory detector, vendored-copy synchronizer, and destructive Git replacement blocks were duplicated installation infrastructure. GStack 2 delegates skill placement and updates to the standard Agent Skills installer and manages the optional shared runtime atomically.\n\n- Update selected skills with \`npx skills add time-attack/gstack\` using the user's existing project/global choice. Never infer or enroll a host.\n- Upgrade a complete local runtime package with \`gstack upgrade --source <complete-gstack-package> --version <version>\`.\n- Roll back the runtime with \`gstack upgrade --rollback\`.\n- Run \`gstack doctor\` after either operation.\n- Do not reset, delete, move, or rewrite a host skill directory. Do not infer Context.dev choice or consent.\n\nThis compatibility module contains no specialist judgment; release readiness and rollback judgment remain in the preserved ship modules.\n`;
return `# Legacy upgrade compatibility\n\nThe 1.x host-directory detector, vendored-copy synchronizer, and destructive Git replacement blocks were duplicated installation infrastructure. GStack 2 delegates skill placement and updates to the standard Agent Skills installer and manages the optional shared runtime atomically.\n\n- Update selected skills with \`npx skills add time-attack/gstack/skills\` using the user's existing project/global choice. Never infer or enroll a host.\n- Upgrade a complete local runtime package with \`gstack upgrade --source <complete-gstack-package> --version <version>\`.\n- Roll back the runtime with \`gstack upgrade --rollback\`.\n- Run \`gstack doctor\` after either operation.\n- Do not reset, delete, move, or rewrite a host skill directory. Do not infer Context.dev choice or consent.\n\nThis compatibility module contains no specialist judgment; release readiness and rollback judgment remain in the preserved ship modules.\n`;
}
let body = value;
@@ -280,7 +280,7 @@ function portLegacyText(value: string, source: string): string {
.replaceAll('$GSTACK_ROOT/lib/redact-audit-log.ts', '$GSTACK_BIN/gstack-redact-audit-log')
.replaceAll('bun $GSTACK_BIN/gstack-redact-audit-log', '$GSTACK_BIN/gstack-redact-audit-log')
.replaceAll('Disk paths stay `$GSTACK_ROOT/[skill-name]/SKILL.md`.', 'Resolve retired names through `references/COMPATIBILITY.md`; skill placement is installer-owned.')
.replaceAll('Tell the user: "Done. Each developer now runs: `cd $GSTACK_ROOT && ./setup --team`"', 'Tell the user: "Done. Each developer installs the selected canonical skills with `npx skills add time-attack/gstack`; the optional runtime remains user-scoped."');
.replaceAll('Tell the user: "Done. Each developer now runs: `cd $GSTACK_ROOT && ./setup --team`"', 'Tell the user: "Done. Each developer installs the selected canonical skills with `npx skills add time-attack/gstack/skills`; the optional runtime remains user-scoped."');
body = body
.replace(/_VENDORED="no"\nif \[ -d "\.agents\/skills\/gstack" \][\s\S]*?echo "VENDORED_GSTACK: \$_VENDORED"/g, '_VENDORED="managed-by-standard-installer"\necho "VENDORED_GSTACK: $_VENDORED"')
+18 -9
View File
@@ -438,6 +438,15 @@ function verifyInstalledCase(
const installedSkills = listInstalledSkills(targetRoot);
const sortedExpected = [...expectedSkills].sort();
record(checks, `${id}.command`, command.exitCode === 0, `exit=${command.exitCode}; signal=${command.signal ?? 'none'}`);
if (sourceSkillSegments.length === 1 && sourceSkillSegments[0] === 'skills') {
const reported = Number(stripTerminalControls(command.stdout).match(/Found\s+(\d+)\s+skills?/)?.[1]);
record(
checks,
`${id}.public-discovery-count`,
reported === PUBLIC_SKILLS.length,
`expected installer to report 6 public skills; found ${Number.isFinite(reported) ? reported : '(unparsed)'}`,
);
}
record(
checks,
`${id}.selected-skills`,
@@ -595,10 +604,10 @@ export function runFullMatrix(options: FullMatrixOptions): InstallMatrixEvidence
const supportsRemoval = /remove\s+\[skills\]/.test(helpCommand.stdout) && /Remove Options/.test(helpCommand.stdout);
const discoveryCommand = execute(
// Exercise the repository root exactly as the documented
// `npx skills add time-attack/gstack` path will after checkout. The
// curated projection alone could hide stray root-level SKILL.md files.
skillsCliArgv(npxExecutable, ['add', repoRoot, '--list']),
// Exercise the documented public source directly. The repository root
// also contains opt-in 1.x compatibility aliases; skills@1.5.19 counts
// those internal entries before applying an explicit --skill filter.
skillsCliArgv(npxExecutable, ['add', path.join(repoRoot, 'skills'), '--list']),
controlProject,
controlEnv,
);
@@ -626,7 +635,7 @@ export function runFullMatrix(options: FullMatrixOptions): InstallMatrixEvidence
entry,
scope,
sourceKind,
sourceArgument: sourceKind === 'source-symlink' ? sourceLink : sourceRoot,
sourceArgument: path.join(sourceKind === 'source-symlink' ? sourceLink : sourceRoot, 'skills'),
sourceRoot,
expectedSkills: PUBLIC_SKILLS,
explicitSelection: false,
@@ -643,7 +652,7 @@ export function runFullMatrix(options: FullMatrixOptions): InstallMatrixEvidence
entry: cursor,
scope: 'project',
sourceKind: 'repository-root',
sourceArgument: repoRoot,
sourceArgument: path.join(repoRoot, 'skills'),
sourceRoot: repoRoot,
expectedSkills: COLLISION_SKILLS,
explicitSelection: true,
@@ -659,7 +668,7 @@ export function runFullMatrix(options: FullMatrixOptions): InstallMatrixEvidence
entry: codex,
scope: 'global',
sourceKind: 'path-with-spaces',
sourceArgument: sourceRoot,
sourceArgument: path.join(sourceRoot, 'skills'),
sourceRoot,
expectedSkills: COLLISION_SKILLS,
explicitSelection: true,
@@ -675,7 +684,7 @@ export function runFullMatrix(options: FullMatrixOptions): InstallMatrixEvidence
entry: openclaw,
scope: 'project',
sourceKind: 'path-with-spaces',
sourceArgument: sourceRoot,
sourceArgument: path.join(sourceRoot, 'skills'),
sourceRoot,
expectedSkills: ['ship'],
explicitSelection: true,
@@ -747,7 +756,7 @@ export function runFullMatrix(options: FullMatrixOptions): InstallMatrixEvidence
platform: process.platform,
architecture: process.arch,
repositoryRoot: repoRoot,
sourceProjection: 'repository-root-and-canonical-projection',
sourceProjection: 'canonical-skills-subpath-and-opt-in-compatibility-root',
cli: {
executable: npxExecutable,
version,
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$plan --mode Full chain --module autoplan`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `autoplan` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill plan`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `autoplan` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill plan`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$qa --mode Report --module benchmark-models`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `benchmark-models` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill qa`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `benchmark-models` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill qa`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$qa --mode Report --module benchmark`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `benchmark` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill qa`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `benchmark` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill qa`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$qa --mode Report --module browse`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `browse` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill qa`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `browse` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill qa`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$qa --mode Report --module canary`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `canary` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill qa`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `canary` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill qa`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$debug --mode Diagnose-only --module careful`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `careful` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill debug`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `careful` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill debug`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$review --mode Deep --module claude`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `claude` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill review`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `claude` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill review`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$review --mode Deep --module codex`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `codex` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill review`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `codex` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill review`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$plan --mode Discovery --module context-restore`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `context-restore` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill plan`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `context-restore` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill plan`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$plan --mode Discovery --module context-save`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `context-save` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill plan`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `context-save` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill plan`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$review --mode Security --module cso`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `cso` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill review`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `cso` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill review`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$design --mode Generate --module design-consultation`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `design-consultation` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill design`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `design-consultation` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill design`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$design --mode Implement --module design-html`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `design-html` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill design`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `design-html` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill design`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$design --mode Implement --module design-review`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `design-review` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill design`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `design-review` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill design`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$design --mode Explore --module design-shotgun`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `design-shotgun` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill design`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `design-shotgun` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill design`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$qa --mode Report --module devex-review`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `devex-review` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill qa`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `devex-review` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill qa`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$design --mode Generate --module diagram`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `diagram` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill design`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `diagram` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill design`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$ship --mode Prepare --module document-generate`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `document-generate` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill ship`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `document-generate` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill ship`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$ship --mode Prepare --module document-release`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `document-release` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill ship`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `document-release` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill ship`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$debug --mode Diagnose-only --module freeze`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `freeze` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill debug`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `freeze` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill debug`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$ship --mode Prepare --module gstack-upgrade`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `gstack-upgrade` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill ship`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `gstack-upgrade` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill ship`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$plan --mode Discovery --module gstack`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `gstack` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill plan`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `gstack` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill plan`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$debug --mode Diagnose-only --module guard`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `guard` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill debug`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `guard` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill debug`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$review --mode Deep --module health`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `health` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill review`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `health` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill review`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$debug --mode Diagnose-only --module investigate`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `investigate` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill debug`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `investigate` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill debug`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$ship --mode Prepare --module ios-clean`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `ios-clean` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill ship`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `ios-clean` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill ship`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$design --mode Critique --module ios-design-review`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `ios-design-review` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill design`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `ios-design-review` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill design`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$debug --mode Fix --module ios-fix`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `ios-fix` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill debug`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `ios-fix` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill debug`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$qa --mode Report --module ios-qa`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `ios-qa` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill qa`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `ios-qa` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill qa`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$ship --mode Prepare --module ios-sync`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `ios-sync` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill ship`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `ios-sync` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill ship`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$ship --mode Land --module land-and-deploy`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `land-and-deploy` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill ship`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `land-and-deploy` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill ship`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$ship --mode Prepare --module landing-report`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `landing-report` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill ship`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `landing-report` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill ship`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$plan --mode Discovery --module learn`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `learn` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill plan`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `learn` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill plan`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$design --mode Generate --module make-pdf`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `make-pdf` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill design`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `make-pdf` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill design`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$plan --mode Discovery --module office-hours`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `office-hours` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill plan`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `office-hours` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill plan`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$qa --mode Report --module open-gstack-browser`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `open-gstack-browser` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill qa`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `open-gstack-browser` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill qa`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$qa --mode Report --module pair-agent`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `pair-agent` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill qa`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `pair-agent` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill qa`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$plan --mode Product --module plan-ceo-review`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `plan-ceo-review` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill plan`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `plan-ceo-review` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill plan`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$design --mode Critique --module plan-design-review`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `plan-design-review` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill design`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `plan-design-review` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill design`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$plan --mode DX --module plan-devex-review`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `plan-devex-review` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill plan`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `plan-devex-review` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill plan`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$plan --mode Engineering --module plan-eng-review`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `plan-eng-review` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill plan`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `plan-eng-review` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill plan`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$plan --mode Discovery --module plan-tune`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `plan-tune` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill plan`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `plan-tune` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill plan`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$qa --mode Report --module qa-only`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `qa-only` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill qa`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `qa-only` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill qa`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$plan --mode Discovery --module retro`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `retro` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill plan`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `retro` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill plan`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$qa --mode Report --module scrape`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `scrape` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill qa`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `scrape` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill qa`.
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$qa --mode Report --module setup-browser-cookies`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `setup-browser-cookies` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill qa`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `setup-browser-cookies` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill qa`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$ship --mode Deploy --module setup-deploy`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `setup-deploy` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill ship`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `setup-deploy` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill ship`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$plan --mode Discovery --module setup-gbrain`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `setup-gbrain` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill plan`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `setup-gbrain` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill plan`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$qa --mode Report --module skillify`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `skillify` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill qa`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `skillify` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill qa`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$plan --mode Specification --module spec`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `spec` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill plan`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `spec` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill plan`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$plan --mode Discovery --module sync-gbrain`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `sync-gbrain` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill plan`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `sync-gbrain` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill plan`.
+1 -1
View File
@@ -12,4 +12,4 @@ Print this replacement invocation, then dispatch to it exactly:
`$debug --mode Diagnose-only --module unfreeze`
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `unfreeze` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack --skill debug`.
Do not reproduce or summarize the retired specialist here. The canonical dispatcher must load its preserved `unfreeze` module. If that dispatcher is not installed, tell the user to install it with `npx skills add time-attack/gstack/skills --skill debug`.
+1 -1
View File
@@ -7,4 +7,4 @@ When no public-web choice is stored, present: A) Context.dev free setup (recomme
Persist only the explicit choice with `gstack context select host`, `gstack context select local-browser`, or `gstack context select none`. For Context.dev, show `gstack context options`, then use `gstack context setup` and its hidden key prompt; consent and key storage belong to the runtime, never this judgment prompt. Do not infer Context choice or consent.
Capability-dependent work follows `references/RUNTIME.md`. Pure judgment never requires the runtime. Skill placement remains owned by `npx skills add time-attack/gstack` and is never inferred from runtime state.
Capability-dependent work follows `references/RUNTIME.md`. Pure judgment never requires the runtime. Skill placement remains owned by `npx skills add time-attack/gstack/skills` and is never inferred from runtime state.
@@ -13,8 +13,12 @@ import { fileURLToPath } from "node:url";
export const BOOTSTRAP_SCHEMA_VERSION = 2;
export const BOOTSTRAP_RUNTIME_VERSION = "2.0.0";
// Keep the runtime compatibility version separate from the immutable release
// channel. Release candidates carry the 2.0.0 runtime contract while letting
// fresh-machine production journeys run before the stable v2.0.0 tag exists.
export const BOOTSTRAP_RELEASE_TAG = "v2.0.0-rc.1";
export const OFFICIAL_MANIFEST_URL =
`https://github.com/time-attack/gstack/releases/download/v${BOOTSTRAP_RUNTIME_VERSION}/gstack-runtime-manifest.json`;
`https://github.com/time-attack/gstack/releases/download/${BOOTSTRAP_RELEASE_TAG}/gstack-runtime-manifest.json`;
const CAPABILITIES = new Set(["browser", "browser-visible", "design", "pdf", "diagram", "ios"]);
const CAPABILITY_DEPENDENCIES = Object.freeze({
browser: Object.freeze([]),
@@ -47,9 +51,9 @@ const ALLOWED_DOWNLOAD_HOSTS = new Set([
"objects.githubusercontent.com",
"release-assets.githubusercontent.com",
]);
const OFFICIAL_RELEASE_PREFIX = `/time-attack/gstack/releases/download/v${BOOTSTRAP_RUNTIME_VERSION}/`;
const OFFICIAL_RELEASE_PREFIX = `/time-attack/gstack/releases/download/${BOOTSTRAP_RELEASE_TAG}/`;
const OFFICIAL_CERTIFICATE_IDENTITY =
`https://github.com/time-attack/gstack/.github/workflows/release-artifacts.yml@refs/tags/v${BOOTSTRAP_RUNTIME_VERSION}`;
`https://github.com/time-attack/gstack/.github/workflows/release-artifacts.yml@refs/tags/${BOOTSTRAP_RELEASE_TAG}`;
const GITHUB_OIDC_ISSUER = "https://token.actions.githubusercontent.com";
export async function main(argv = process.argv.slice(2), options = {}) {
@@ -90,7 +94,9 @@ export async function main(argv = process.argv.slice(2), options = {}) {
);
const manifestUrl = options.manifestUrl ?? OFFICIAL_MANIFEST_URL;
assertOfficialUrl(manifestUrl, { manifest: true });
const manifest = await fetchJson(fetch_, manifestUrl);
const manifest = await fetchJson(fetch_, manifestUrl, {
official: manifestUrl === OFFICIAL_MANIFEST_URL,
});
validateManifest(manifest, target);
const home = path.resolve(parsed.home ?? process.env.GSTACK_HOME ?? path.join(os.homedir(), ".gstack"));
const reusable = await inspectReusableRuntime(home, manifest.version).catch(() => null);
@@ -300,10 +306,18 @@ function sha256File(file) {
});
}
async function fetchJson(fetch_, url) {
async function fetchJson(fetch_, url, options = {}) {
const response = await fetch_(url, { headers: { Accept: "application/json" }, redirect: "follow" });
assertFinalDownloadUrl(response.url || url);
if (!response.ok) throw bootstrapError(`Download failed with HTTP ${response.status}`, "BOOTSTRAP_DOWNLOAD_FAILED");
if (!response.ok) {
if (options.official && response.status === 404) {
throw bootstrapError(
`Official runtime release ${BOOTSTRAP_RELEASE_TAG} is not published at ${url}. No files were downloaded or installed.`,
"BOOTSTRAP_RELEASE_UNAVAILABLE",
);
}
throw bootstrapError(`Manifest download failed with HTTP ${response.status} from ${url}. No files were downloaded or installed.`, "BOOTSTRAP_DOWNLOAD_FAILED");
}
const value = await response.json();
if (!value || typeof value !== "object") throw bootstrapError("Manifest returned invalid JSON", "BOOTSTRAP_MANIFEST_INVALID");
return value;
+1 -1
View File
@@ -7,4 +7,4 @@ When no public-web choice is stored, present: A) Context.dev free setup (recomme
Persist only the explicit choice with `gstack context select host`, `gstack context select local-browser`, or `gstack context select none`. For Context.dev, show `gstack context options`, then use `gstack context setup` and its hidden key prompt; consent and key storage belong to the runtime, never this judgment prompt. Do not infer Context choice or consent.
Capability-dependent work follows `references/RUNTIME.md`. Pure judgment never requires the runtime. Skill placement remains owned by `npx skills add time-attack/gstack` and is never inferred from runtime state.
Capability-dependent work follows `references/RUNTIME.md`. Pure judgment never requires the runtime. Skill placement remains owned by `npx skills add time-attack/gstack/skills` and is never inferred from runtime state.
@@ -13,8 +13,12 @@ import { fileURLToPath } from "node:url";
export const BOOTSTRAP_SCHEMA_VERSION = 2;
export const BOOTSTRAP_RUNTIME_VERSION = "2.0.0";
// Keep the runtime compatibility version separate from the immutable release
// channel. Release candidates carry the 2.0.0 runtime contract while letting
// fresh-machine production journeys run before the stable v2.0.0 tag exists.
export const BOOTSTRAP_RELEASE_TAG = "v2.0.0-rc.1";
export const OFFICIAL_MANIFEST_URL =
`https://github.com/time-attack/gstack/releases/download/v${BOOTSTRAP_RUNTIME_VERSION}/gstack-runtime-manifest.json`;
`https://github.com/time-attack/gstack/releases/download/${BOOTSTRAP_RELEASE_TAG}/gstack-runtime-manifest.json`;
const CAPABILITIES = new Set(["browser", "browser-visible", "design", "pdf", "diagram", "ios"]);
const CAPABILITY_DEPENDENCIES = Object.freeze({
browser: Object.freeze([]),
@@ -47,9 +51,9 @@ const ALLOWED_DOWNLOAD_HOSTS = new Set([
"objects.githubusercontent.com",
"release-assets.githubusercontent.com",
]);
const OFFICIAL_RELEASE_PREFIX = `/time-attack/gstack/releases/download/v${BOOTSTRAP_RUNTIME_VERSION}/`;
const OFFICIAL_RELEASE_PREFIX = `/time-attack/gstack/releases/download/${BOOTSTRAP_RELEASE_TAG}/`;
const OFFICIAL_CERTIFICATE_IDENTITY =
`https://github.com/time-attack/gstack/.github/workflows/release-artifacts.yml@refs/tags/v${BOOTSTRAP_RUNTIME_VERSION}`;
`https://github.com/time-attack/gstack/.github/workflows/release-artifacts.yml@refs/tags/${BOOTSTRAP_RELEASE_TAG}`;
const GITHUB_OIDC_ISSUER = "https://token.actions.githubusercontent.com";
export async function main(argv = process.argv.slice(2), options = {}) {
@@ -90,7 +94,9 @@ export async function main(argv = process.argv.slice(2), options = {}) {
);
const manifestUrl = options.manifestUrl ?? OFFICIAL_MANIFEST_URL;
assertOfficialUrl(manifestUrl, { manifest: true });
const manifest = await fetchJson(fetch_, manifestUrl);
const manifest = await fetchJson(fetch_, manifestUrl, {
official: manifestUrl === OFFICIAL_MANIFEST_URL,
});
validateManifest(manifest, target);
const home = path.resolve(parsed.home ?? process.env.GSTACK_HOME ?? path.join(os.homedir(), ".gstack"));
const reusable = await inspectReusableRuntime(home, manifest.version).catch(() => null);
@@ -300,10 +306,18 @@ function sha256File(file) {
});
}
async function fetchJson(fetch_, url) {
async function fetchJson(fetch_, url, options = {}) {
const response = await fetch_(url, { headers: { Accept: "application/json" }, redirect: "follow" });
assertFinalDownloadUrl(response.url || url);
if (!response.ok) throw bootstrapError(`Download failed with HTTP ${response.status}`, "BOOTSTRAP_DOWNLOAD_FAILED");
if (!response.ok) {
if (options.official && response.status === 404) {
throw bootstrapError(
`Official runtime release ${BOOTSTRAP_RELEASE_TAG} is not published at ${url}. No files were downloaded or installed.`,
"BOOTSTRAP_RELEASE_UNAVAILABLE",
);
}
throw bootstrapError(`Manifest download failed with HTTP ${response.status} from ${url}. No files were downloaded or installed.`, "BOOTSTRAP_DOWNLOAD_FAILED");
}
const value = await response.json();
if (!value || typeof value !== "object") throw bootstrapError("Manifest returned invalid JSON", "BOOTSTRAP_MANIFEST_INVALID");
return value;
+1 -1
View File
@@ -7,4 +7,4 @@ When no public-web choice is stored, present: A) Context.dev free setup (recomme
Persist only the explicit choice with `gstack context select host`, `gstack context select local-browser`, or `gstack context select none`. For Context.dev, show `gstack context options`, then use `gstack context setup` and its hidden key prompt; consent and key storage belong to the runtime, never this judgment prompt. Do not infer Context choice or consent.
Capability-dependent work follows `references/RUNTIME.md`. Pure judgment never requires the runtime. Skill placement remains owned by `npx skills add time-attack/gstack` and is never inferred from runtime state.
Capability-dependent work follows `references/RUNTIME.md`. Pure judgment never requires the runtime. Skill placement remains owned by `npx skills add time-attack/gstack/skills` and is never inferred from runtime state.
@@ -13,8 +13,12 @@ import { fileURLToPath } from "node:url";
export const BOOTSTRAP_SCHEMA_VERSION = 2;
export const BOOTSTRAP_RUNTIME_VERSION = "2.0.0";
// Keep the runtime compatibility version separate from the immutable release
// channel. Release candidates carry the 2.0.0 runtime contract while letting
// fresh-machine production journeys run before the stable v2.0.0 tag exists.
export const BOOTSTRAP_RELEASE_TAG = "v2.0.0-rc.1";
export const OFFICIAL_MANIFEST_URL =
`https://github.com/time-attack/gstack/releases/download/v${BOOTSTRAP_RUNTIME_VERSION}/gstack-runtime-manifest.json`;
`https://github.com/time-attack/gstack/releases/download/${BOOTSTRAP_RELEASE_TAG}/gstack-runtime-manifest.json`;
const CAPABILITIES = new Set(["browser", "browser-visible", "design", "pdf", "diagram", "ios"]);
const CAPABILITY_DEPENDENCIES = Object.freeze({
browser: Object.freeze([]),
@@ -47,9 +51,9 @@ const ALLOWED_DOWNLOAD_HOSTS = new Set([
"objects.githubusercontent.com",
"release-assets.githubusercontent.com",
]);
const OFFICIAL_RELEASE_PREFIX = `/time-attack/gstack/releases/download/v${BOOTSTRAP_RUNTIME_VERSION}/`;
const OFFICIAL_RELEASE_PREFIX = `/time-attack/gstack/releases/download/${BOOTSTRAP_RELEASE_TAG}/`;
const OFFICIAL_CERTIFICATE_IDENTITY =
`https://github.com/time-attack/gstack/.github/workflows/release-artifacts.yml@refs/tags/v${BOOTSTRAP_RUNTIME_VERSION}`;
`https://github.com/time-attack/gstack/.github/workflows/release-artifacts.yml@refs/tags/${BOOTSTRAP_RELEASE_TAG}`;
const GITHUB_OIDC_ISSUER = "https://token.actions.githubusercontent.com";
export async function main(argv = process.argv.slice(2), options = {}) {
@@ -90,7 +94,9 @@ export async function main(argv = process.argv.slice(2), options = {}) {
);
const manifestUrl = options.manifestUrl ?? OFFICIAL_MANIFEST_URL;
assertOfficialUrl(manifestUrl, { manifest: true });
const manifest = await fetchJson(fetch_, manifestUrl);
const manifest = await fetchJson(fetch_, manifestUrl, {
official: manifestUrl === OFFICIAL_MANIFEST_URL,
});
validateManifest(manifest, target);
const home = path.resolve(parsed.home ?? process.env.GSTACK_HOME ?? path.join(os.homedir(), ".gstack"));
const reusable = await inspectReusableRuntime(home, manifest.version).catch(() => null);
@@ -300,10 +306,18 @@ function sha256File(file) {
});
}
async function fetchJson(fetch_, url) {
async function fetchJson(fetch_, url, options = {}) {
const response = await fetch_(url, { headers: { Accept: "application/json" }, redirect: "follow" });
assertFinalDownloadUrl(response.url || url);
if (!response.ok) throw bootstrapError(`Download failed with HTTP ${response.status}`, "BOOTSTRAP_DOWNLOAD_FAILED");
if (!response.ok) {
if (options.official && response.status === 404) {
throw bootstrapError(
`Official runtime release ${BOOTSTRAP_RELEASE_TAG} is not published at ${url}. No files were downloaded or installed.`,
"BOOTSTRAP_RELEASE_UNAVAILABLE",
);
}
throw bootstrapError(`Manifest download failed with HTTP ${response.status} from ${url}. No files were downloaded or installed.`, "BOOTSTRAP_DOWNLOAD_FAILED");
}
const value = await response.json();
if (!value || typeof value !== "object") throw bootstrapError("Manifest returned invalid JSON", "BOOTSTRAP_MANIFEST_INVALID");
return value;
+1 -1
View File
@@ -7,4 +7,4 @@ When no public-web choice is stored, present: A) Context.dev free setup (recomme
Persist only the explicit choice with `gstack context select host`, `gstack context select local-browser`, or `gstack context select none`. For Context.dev, show `gstack context options`, then use `gstack context setup` and its hidden key prompt; consent and key storage belong to the runtime, never this judgment prompt. Do not infer Context choice or consent.
Capability-dependent work follows `references/RUNTIME.md`. Pure judgment never requires the runtime. Skill placement remains owned by `npx skills add time-attack/gstack` and is never inferred from runtime state.
Capability-dependent work follows `references/RUNTIME.md`. Pure judgment never requires the runtime. Skill placement remains owned by `npx skills add time-attack/gstack/skills` and is never inferred from runtime state.
@@ -13,8 +13,12 @@ import { fileURLToPath } from "node:url";
export const BOOTSTRAP_SCHEMA_VERSION = 2;
export const BOOTSTRAP_RUNTIME_VERSION = "2.0.0";
// Keep the runtime compatibility version separate from the immutable release
// channel. Release candidates carry the 2.0.0 runtime contract while letting
// fresh-machine production journeys run before the stable v2.0.0 tag exists.
export const BOOTSTRAP_RELEASE_TAG = "v2.0.0-rc.1";
export const OFFICIAL_MANIFEST_URL =
`https://github.com/time-attack/gstack/releases/download/v${BOOTSTRAP_RUNTIME_VERSION}/gstack-runtime-manifest.json`;
`https://github.com/time-attack/gstack/releases/download/${BOOTSTRAP_RELEASE_TAG}/gstack-runtime-manifest.json`;
const CAPABILITIES = new Set(["browser", "browser-visible", "design", "pdf", "diagram", "ios"]);
const CAPABILITY_DEPENDENCIES = Object.freeze({
browser: Object.freeze([]),
@@ -47,9 +51,9 @@ const ALLOWED_DOWNLOAD_HOSTS = new Set([
"objects.githubusercontent.com",
"release-assets.githubusercontent.com",
]);
const OFFICIAL_RELEASE_PREFIX = `/time-attack/gstack/releases/download/v${BOOTSTRAP_RUNTIME_VERSION}/`;
const OFFICIAL_RELEASE_PREFIX = `/time-attack/gstack/releases/download/${BOOTSTRAP_RELEASE_TAG}/`;
const OFFICIAL_CERTIFICATE_IDENTITY =
`https://github.com/time-attack/gstack/.github/workflows/release-artifacts.yml@refs/tags/v${BOOTSTRAP_RUNTIME_VERSION}`;
`https://github.com/time-attack/gstack/.github/workflows/release-artifacts.yml@refs/tags/${BOOTSTRAP_RELEASE_TAG}`;
const GITHUB_OIDC_ISSUER = "https://token.actions.githubusercontent.com";
export async function main(argv = process.argv.slice(2), options = {}) {
@@ -90,7 +94,9 @@ export async function main(argv = process.argv.slice(2), options = {}) {
);
const manifestUrl = options.manifestUrl ?? OFFICIAL_MANIFEST_URL;
assertOfficialUrl(manifestUrl, { manifest: true });
const manifest = await fetchJson(fetch_, manifestUrl);
const manifest = await fetchJson(fetch_, manifestUrl, {
official: manifestUrl === OFFICIAL_MANIFEST_URL,
});
validateManifest(manifest, target);
const home = path.resolve(parsed.home ?? process.env.GSTACK_HOME ?? path.join(os.homedir(), ".gstack"));
const reusable = await inspectReusableRuntime(home, manifest.version).catch(() => null);
@@ -300,10 +306,18 @@ function sha256File(file) {
});
}
async function fetchJson(fetch_, url) {
async function fetchJson(fetch_, url, options = {}) {
const response = await fetch_(url, { headers: { Accept: "application/json" }, redirect: "follow" });
assertFinalDownloadUrl(response.url || url);
if (!response.ok) throw bootstrapError(`Download failed with HTTP ${response.status}`, "BOOTSTRAP_DOWNLOAD_FAILED");
if (!response.ok) {
if (options.official && response.status === 404) {
throw bootstrapError(
`Official runtime release ${BOOTSTRAP_RELEASE_TAG} is not published at ${url}. No files were downloaded or installed.`,
"BOOTSTRAP_RELEASE_UNAVAILABLE",
);
}
throw bootstrapError(`Manifest download failed with HTTP ${response.status} from ${url}. No files were downloaded or installed.`, "BOOTSTRAP_DOWNLOAD_FAILED");
}
const value = await response.json();
if (!value || typeof value !== "object") throw bootstrapError("Manifest returned invalid JSON", "BOOTSTRAP_MANIFEST_INVALID");
return value;
+1 -1
View File
@@ -7,4 +7,4 @@ When no public-web choice is stored, present: A) Context.dev free setup (recomme
Persist only the explicit choice with `gstack context select host`, `gstack context select local-browser`, or `gstack context select none`. For Context.dev, show `gstack context options`, then use `gstack context setup` and its hidden key prompt; consent and key storage belong to the runtime, never this judgment prompt. Do not infer Context choice or consent.
Capability-dependent work follows `references/RUNTIME.md`. Pure judgment never requires the runtime. Skill placement remains owned by `npx skills add time-attack/gstack` and is never inferred from runtime state.
Capability-dependent work follows `references/RUNTIME.md`. Pure judgment never requires the runtime. Skill placement remains owned by `npx skills add time-attack/gstack/skills` and is never inferred from runtime state.
@@ -13,8 +13,12 @@ import { fileURLToPath } from "node:url";
export const BOOTSTRAP_SCHEMA_VERSION = 2;
export const BOOTSTRAP_RUNTIME_VERSION = "2.0.0";
// Keep the runtime compatibility version separate from the immutable release
// channel. Release candidates carry the 2.0.0 runtime contract while letting
// fresh-machine production journeys run before the stable v2.0.0 tag exists.
export const BOOTSTRAP_RELEASE_TAG = "v2.0.0-rc.1";
export const OFFICIAL_MANIFEST_URL =
`https://github.com/time-attack/gstack/releases/download/v${BOOTSTRAP_RUNTIME_VERSION}/gstack-runtime-manifest.json`;
`https://github.com/time-attack/gstack/releases/download/${BOOTSTRAP_RELEASE_TAG}/gstack-runtime-manifest.json`;
const CAPABILITIES = new Set(["browser", "browser-visible", "design", "pdf", "diagram", "ios"]);
const CAPABILITY_DEPENDENCIES = Object.freeze({
browser: Object.freeze([]),
@@ -47,9 +51,9 @@ const ALLOWED_DOWNLOAD_HOSTS = new Set([
"objects.githubusercontent.com",
"release-assets.githubusercontent.com",
]);
const OFFICIAL_RELEASE_PREFIX = `/time-attack/gstack/releases/download/v${BOOTSTRAP_RUNTIME_VERSION}/`;
const OFFICIAL_RELEASE_PREFIX = `/time-attack/gstack/releases/download/${BOOTSTRAP_RELEASE_TAG}/`;
const OFFICIAL_CERTIFICATE_IDENTITY =
`https://github.com/time-attack/gstack/.github/workflows/release-artifacts.yml@refs/tags/v${BOOTSTRAP_RUNTIME_VERSION}`;
`https://github.com/time-attack/gstack/.github/workflows/release-artifacts.yml@refs/tags/${BOOTSTRAP_RELEASE_TAG}`;
const GITHUB_OIDC_ISSUER = "https://token.actions.githubusercontent.com";
export async function main(argv = process.argv.slice(2), options = {}) {
@@ -90,7 +94,9 @@ export async function main(argv = process.argv.slice(2), options = {}) {
);
const manifestUrl = options.manifestUrl ?? OFFICIAL_MANIFEST_URL;
assertOfficialUrl(manifestUrl, { manifest: true });
const manifest = await fetchJson(fetch_, manifestUrl);
const manifest = await fetchJson(fetch_, manifestUrl, {
official: manifestUrl === OFFICIAL_MANIFEST_URL,
});
validateManifest(manifest, target);
const home = path.resolve(parsed.home ?? process.env.GSTACK_HOME ?? path.join(os.homedir(), ".gstack"));
const reusable = await inspectReusableRuntime(home, manifest.version).catch(() => null);
@@ -300,10 +306,18 @@ function sha256File(file) {
});
}
async function fetchJson(fetch_, url) {
async function fetchJson(fetch_, url, options = {}) {
const response = await fetch_(url, { headers: { Accept: "application/json" }, redirect: "follow" });
assertFinalDownloadUrl(response.url || url);
if (!response.ok) throw bootstrapError(`Download failed with HTTP ${response.status}`, "BOOTSTRAP_DOWNLOAD_FAILED");
if (!response.ok) {
if (options.official && response.status === 404) {
throw bootstrapError(
`Official runtime release ${BOOTSTRAP_RELEASE_TAG} is not published at ${url}. No files were downloaded or installed.`,
"BOOTSTRAP_RELEASE_UNAVAILABLE",
);
}
throw bootstrapError(`Manifest download failed with HTTP ${response.status} from ${url}. No files were downloaded or installed.`, "BOOTSTRAP_DOWNLOAD_FAILED");
}
const value = await response.json();
if (!value || typeof value !== "object") throw bootstrapError("Manifest returned invalid JSON", "BOOTSTRAP_MANIFEST_INVALID");
return value;
+1 -1
View File
@@ -7,4 +7,4 @@ When no public-web choice is stored, present: A) Context.dev free setup (recomme
Persist only the explicit choice with `gstack context select host`, `gstack context select local-browser`, or `gstack context select none`. For Context.dev, show `gstack context options`, then use `gstack context setup` and its hidden key prompt; consent and key storage belong to the runtime, never this judgment prompt. Do not infer Context choice or consent.
Capability-dependent work follows `references/RUNTIME.md`. Pure judgment never requires the runtime. Skill placement remains owned by `npx skills add time-attack/gstack` and is never inferred from runtime state.
Capability-dependent work follows `references/RUNTIME.md`. Pure judgment never requires the runtime. Skill placement remains owned by `npx skills add time-attack/gstack/skills` and is never inferred from runtime state.
@@ -1,5 +1,5 @@
<!-- GENERATED by scripts/gstack2/generate-skill-tree.ts; do not edit. -->
<!-- GSTACK2_PROVENANCE source=gstack-upgrade/SKILL.md.tmpl base=bb57306d98c97011b0919c6132705a15b1579781 blob=5402a1da3c857cbf50668085fce53172b72bba0d baseline_render_sha256=609971d414a31d49180e2630bd79a10b8d09085203f7e48c03d2ca69c2ef129a ported_render_sha256=a913cf77f76c4d68c576a06190b498e3d8d3b60b85498a68f173b23a7f800828 disposition=DUPLICATE_INFRASTRUCTURE -->
<!-- GSTACK2_PROVENANCE source=gstack-upgrade/SKILL.md.tmpl base=bb57306d98c97011b0919c6132705a15b1579781 blob=5402a1da3c857cbf50668085fce53172b72bba0d baseline_render_sha256=609971d414a31d49180e2630bd79a10b8d09085203f7e48c03d2ca69c2ef129a ported_render_sha256=2c8cf9e505b7da27730bf646a33ee38e8092259f3335a524c71e57ba5b602541 disposition=DUPLICATE_INFRASTRUCTURE -->
<!-- GSTACK2_ROUTING replacement=$ship --mode Prepare --module gstack-upgrade visibility=internal depth=standard mutation=installation web=none -->
<!-- GSTACK2_LEGACY_BODY_START source=gstack-upgrade -->
@@ -7,7 +7,7 @@
The 1.x host-directory detector, vendored-copy synchronizer, and destructive Git replacement blocks were duplicated installation infrastructure. GStack 2 delegates skill placement and updates to the standard Agent Skills installer and manages the optional shared runtime atomically.
- Update selected skills with `npx skills add time-attack/gstack` using the user's existing project/global choice. Never infer or enroll a host.
- Update selected skills with `npx skills add time-attack/gstack/skills` using the user's existing project/global choice. Never infer or enroll a host.
- Upgrade a complete local runtime package with `gstack upgrade --source <complete-gstack-package> --version <version>`.
- Roll back the runtime with `gstack upgrade --rollback`.
- Run `gstack doctor` after either operation.
@@ -13,8 +13,12 @@ import { fileURLToPath } from "node:url";
export const BOOTSTRAP_SCHEMA_VERSION = 2;
export const BOOTSTRAP_RUNTIME_VERSION = "2.0.0";
// Keep the runtime compatibility version separate from the immutable release
// channel. Release candidates carry the 2.0.0 runtime contract while letting
// fresh-machine production journeys run before the stable v2.0.0 tag exists.
export const BOOTSTRAP_RELEASE_TAG = "v2.0.0-rc.1";
export const OFFICIAL_MANIFEST_URL =
`https://github.com/time-attack/gstack/releases/download/v${BOOTSTRAP_RUNTIME_VERSION}/gstack-runtime-manifest.json`;
`https://github.com/time-attack/gstack/releases/download/${BOOTSTRAP_RELEASE_TAG}/gstack-runtime-manifest.json`;
const CAPABILITIES = new Set(["browser", "browser-visible", "design", "pdf", "diagram", "ios"]);
const CAPABILITY_DEPENDENCIES = Object.freeze({
browser: Object.freeze([]),
@@ -47,9 +51,9 @@ const ALLOWED_DOWNLOAD_HOSTS = new Set([
"objects.githubusercontent.com",
"release-assets.githubusercontent.com",
]);
const OFFICIAL_RELEASE_PREFIX = `/time-attack/gstack/releases/download/v${BOOTSTRAP_RUNTIME_VERSION}/`;
const OFFICIAL_RELEASE_PREFIX = `/time-attack/gstack/releases/download/${BOOTSTRAP_RELEASE_TAG}/`;
const OFFICIAL_CERTIFICATE_IDENTITY =
`https://github.com/time-attack/gstack/.github/workflows/release-artifacts.yml@refs/tags/v${BOOTSTRAP_RUNTIME_VERSION}`;
`https://github.com/time-attack/gstack/.github/workflows/release-artifacts.yml@refs/tags/${BOOTSTRAP_RELEASE_TAG}`;
const GITHUB_OIDC_ISSUER = "https://token.actions.githubusercontent.com";
export async function main(argv = process.argv.slice(2), options = {}) {
@@ -90,7 +94,9 @@ export async function main(argv = process.argv.slice(2), options = {}) {
);
const manifestUrl = options.manifestUrl ?? OFFICIAL_MANIFEST_URL;
assertOfficialUrl(manifestUrl, { manifest: true });
const manifest = await fetchJson(fetch_, manifestUrl);
const manifest = await fetchJson(fetch_, manifestUrl, {
official: manifestUrl === OFFICIAL_MANIFEST_URL,
});
validateManifest(manifest, target);
const home = path.resolve(parsed.home ?? process.env.GSTACK_HOME ?? path.join(os.homedir(), ".gstack"));
const reusable = await inspectReusableRuntime(home, manifest.version).catch(() => null);
@@ -300,10 +306,18 @@ function sha256File(file) {
});
}
async function fetchJson(fetch_, url) {
async function fetchJson(fetch_, url, options = {}) {
const response = await fetch_(url, { headers: { Accept: "application/json" }, redirect: "follow" });
assertFinalDownloadUrl(response.url || url);
if (!response.ok) throw bootstrapError(`Download failed with HTTP ${response.status}`, "BOOTSTRAP_DOWNLOAD_FAILED");
if (!response.ok) {
if (options.official && response.status === 404) {
throw bootstrapError(
`Official runtime release ${BOOTSTRAP_RELEASE_TAG} is not published at ${url}. No files were downloaded or installed.`,
"BOOTSTRAP_RELEASE_UNAVAILABLE",
);
}
throw bootstrapError(`Manifest download failed with HTTP ${response.status} from ${url}. No files were downloaded or installed.`, "BOOTSTRAP_DOWNLOAD_FAILED");
}
const value = await response.json();
if (!value || typeof value !== "object") throw bootstrapError("Manifest returned invalid JSON", "BOOTSTRAP_MANIFEST_INVALID");
return value;
+7
View File
@@ -29,6 +29,13 @@ afterEach(() => {
});
describe('GStack 2 standard installer surface', () => {
test('documents the canonical public subpath instead of the legacy-bearing repository root', () => {
for (const file of ['AGENTS.md', 'CLAUDE.md', 'README.md']) {
const content = fs.readFileSync(path.join(DEFAULT_REPO_ROOT, file), 'utf8');
expect(content, file).toContain('npx skills add time-attack/gstack/skills');
}
});
test('publishes exactly six uniquely named canonical skills', () => {
const result = inspectRepository(DEFAULT_REPO_ROOT);
@@ -0,0 +1,66 @@
import { describe, expect, test } from "bun:test";
import fs from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { spawnSync } from "node:child_process";
const ROOT = path.resolve(import.meta.dir, "..");
const SCRIPT = path.join(ROOT, ".github", "scripts", "create-runtime-release-manifest.mjs");
const WORKFLOW = path.join(ROOT, ".github", "workflows", "release-artifacts.yml");
const TARGETS = ["darwin-arm64", "darwin-x64", "linux-arm64", "linux-x64", "windows-arm64", "windows-x64"];
const COMMON = ["core", "browser-code", "browser-headless", "browser-visible", "design", "diagram", "pdf"];
async function stageFixture(directory: string) {
for (const target of TARGETS) {
const components = [...COMMON, ...(target.startsWith("darwin-") ? ["ios"] : [])];
for (const component of components) {
const name = `gstack-runtime-2.0.0-${target}-${component}.tar.gz`;
await fs.writeFile(path.join(directory, name), "fixture\n");
await fs.writeFile(path.join(directory, `${name}.sha256`), `${"a".repeat(64)} ${name}\n`);
await fs.writeFile(path.join(directory, `${name}.sigstore.json`), "{}\n");
}
}
}
describe("GStack runtime release channel", () => {
test("release candidates retain runtime compatibility while binding URLs and signatures to the RC tag", async () => {
const directory = await fs.mkdtemp(path.join(os.tmpdir(), "gstack-runtime-release-channel-"));
try {
await stageFixture(directory);
const result = spawnSync(process.execPath, [SCRIPT, directory, "time-attack/gstack", "2.0.0", "v2.0.0-rc.1"], {
encoding: "utf8",
});
expect(result.status).toBe(0);
const manifest = JSON.parse(await fs.readFile(path.join(directory, "gstack-runtime-manifest.json"), "utf8"));
expect(manifest.version).toBe("2.0.0");
expect(manifest.targets["darwin-arm64"].components["browser-visible"]).toMatchObject({
url: "https://github.com/time-attack/gstack/releases/download/v2.0.0-rc.1/gstack-runtime-2.0.0-darwin-arm64-browser-visible.tar.gz",
certificateIdentity: "https://github.com/time-attack/gstack/.github/workflows/release-artifacts.yml@refs/tags/v2.0.0-rc.1",
});
} finally {
await fs.rm(directory, { recursive: true, force: true });
}
});
test("release manifest generation rejects non-runtime tags before reading artifacts", async () => {
const directory = await fs.mkdtemp(path.join(os.tmpdir(), "gstack-runtime-invalid-tag-"));
try {
const result = spawnSync(process.execPath, [SCRIPT, directory, "time-attack/gstack", "2.0.0", "main"], {
encoding: "utf8",
});
expect(result.status).not.toBe(0);
expect(`${result.stdout}${result.stderr}`).toContain("Invalid runtime release tag");
} finally {
await fs.rm(directory, { recursive: true, force: true });
}
});
test("release workflow publishes both RC and stable tags through the same signed manifest path", async () => {
const workflow = await fs.readFile(WORKFLOW, "utf8");
expect(workflow).toContain("v2.0.0-rc.*");
expect(workflow).toContain('2.0.0 "$GITHUB_REF_NAME"');
expect(workflow).toContain("PRERELEASE_FLAG:");
expect(workflow).toContain("--prerelease");
expect(workflow).toContain('gh release create "$GITHUB_REF_NAME"');
});
});
+27 -1
View File
@@ -10,6 +10,7 @@ import { setupRuntime } from "../runtime/setup.js";
import { bashCandidates, resolveBashCommand } from "../runtime/tooling.js";
import {
BOOTSTRAP_SCHEMA_VERSION,
BOOTSTRAP_RELEASE_TAG,
BOOTSTRAP_RUNTIME_VERSION,
CAPABILITY_COMPONENTS,
COMPONENT_DEPENDENCIES,
@@ -27,7 +28,7 @@ function officialManifestFixture(target: string, customize?: (component: string,
.filter((component) => component !== "ios" || target.startsWith("darwin-"))
.map((component) => {
const artifact: Record<string, unknown> = {
url: `https://github.com/time-attack/gstack/releases/download/v${BOOTSTRAP_RUNTIME_VERSION}/${component}.tar.gz`,
url: `https://github.com/time-attack/gstack/releases/download/${BOOTSTRAP_RELEASE_TAG}/${component}.tar.gz`,
sha256: "0".repeat(64),
bytes: 8,
format: "tar.gz",
@@ -296,6 +297,31 @@ describe("GStack runtime setup UX", () => {
expect(fetches).toBe(0);
});
test("missing official release stops before install with an actionable immutable-tag error", async () => {
const root = await fs.mkdtemp(path.join(os.tmpdir(), "gstack-bootstrap-missing-release-"));
const output = capture();
let calls = 0;
try {
expect(await bootstrapMain([
"preview", "--capability", "browser-visible", "--home", path.join(root, "home"),
], {
stdout: output.stream,
stderr: output.stream,
fetch: async (url: string) => {
calls += 1;
return { ok: false, status: 404, url };
},
})).toBe(1);
expect(calls).toBe(1);
expect(output.value()).toContain(`Official runtime release ${BOOTSTRAP_RELEASE_TAG} is not published`);
expect(output.value()).toContain(OFFICIAL_MANIFEST_URL);
expect(output.value()).toContain("No files were downloaded or installed");
expect(await fs.readdir(root)).toEqual([]);
} finally {
await fs.rm(root, { recursive: true, force: true });
}
});
test("bootstrap executes through a symlinked or aliased filesystem path", async () => {
if (process.platform === "win32") return;
const root = await fs.mkdtemp(path.join(os.tmpdir(), "gstack-bootstrap-link-"));