From c271815130f0ad9e777101b0d3a0014c5fd03307 Mon Sep 17 00:00:00 2001 From: Sinabina Date: Mon, 20 Jul 2026 16:22:24 -0700 Subject: [PATCH 1/4] feat(gstack2): add capability readiness process --- README.md | 8 +- docs/gstack-2/CAPABILITY-READINESS.md | 75 ++++++++++++++ runtime/cli.js | 25 +++-- runtime/doctor.js | 97 +++++++++++++++++++ scripts/gstack2/generate-skill-tree.ts | 2 + skills/debug/references/RUNTIME.md | 2 + skills/design/references/RUNTIME.md | 2 + skills/plan/references/RUNTIME.md | 2 + skills/qa/references/RUNTIME.md | 2 + skills/review/references/RUNTIME.md | 2 + skills/ship/references/RUNTIME.md | 2 + test/gstack2-capability-readiness.test.ts | 113 ++++++++++++++++++++++ 12 files changed, 324 insertions(+), 8 deletions(-) create mode 100644 docs/gstack-2/CAPABILITY-READINESS.md create mode 100644 test/gstack2-capability-readiness.test.ts diff --git a/README.md b/README.md index ed00ef336f..dc368a644f 100644 --- a/README.md +++ b/README.md @@ -611,8 +611,12 @@ Data is stored in [Supabase](https://supabase.com) (open source Firebase alterna ## Troubleshooting -For GStack 2, first run `gstack doctor` (or `gstack doctor --json`) when the -optional runtime is installed. For skill discovery problems, use the standard +For GStack 2, run `gstack doctor --capability browser|design|diagram|pdf|ios` +(optionally `--json`) for a focused, non-mutating readiness result. It reports +pure-judgment availability, platform support, preview consent, install consent, +and runtime readiness separately; see +[`docs/gstack-2/CAPABILITY-READINESS.md`](docs/gstack-2/CAPABILITY-READINESS.md). +For skill discovery problems, use the standard installer's list and reinstall commands; do not manually copy host paths. The entries below apply to legacy 1.x installations. diff --git a/docs/gstack-2/CAPABILITY-READINESS.md b/docs/gstack-2/CAPABILITY-READINESS.md new file mode 100644 index 0000000000..c52f11fb88 --- /dev/null +++ b/docs/gstack-2/CAPABILITY-READINESS.md @@ -0,0 +1,75 @@ +# Capability readiness + +GStack's six public skills provide pure judgment without the optional runtime. +When a workflow needs executable evidence, inspect one of the five user-facing +capabilities without changing the machine: + +```bash +gstack doctor --capability browser +gstack doctor --capability design --json +gstack doctor --capability diagram +gstack doctor --capability pdf +gstack doctor --capability ios +``` + +The focused result deliberately keeps five questions separate: + +| Axis | Meaning | +|---|---| +| `judgment` | `available` even when no optional runtime exists. | +| `platform` | `supported` or `unsupported`; physical iOS is macOS-only. | +| `consent.preview` | Whether consent is required before an uncached public signed-manifest metadata request. Doctor never makes that request or grants consent. | +| `consent.install` | Separate consent required only after the complete preview. Doctor never installs or records consent. | +| `readiness` | Whether the selected local runtime capability is usable now. | + +Readiness has exactly five states: + +- `ready`: capability and managed-runtime checks passed. +- `degraded`: the capability passed, but the managed runtime reported a warning + such as recovery from an interrupted upgrade. +- `unavailable`: the capability is not selected, no runtime is active, or runtime + inspection could not establish availability. +- `unsupported`: the platform cannot provide the capability with GStack's + existing architecture. +- `failed`: an installed/selected capability failed its readiness evidence. + +The JSON form follows the runtime's result convention: `ok` is true only for +`ready` and `degraded`; a non-ready focused doctor exits 1. Usage errors still +exit 2 through the standard CLI error envelope. + +## Consent and setup flow + +```text +pure judgment (always available) + | + v +capability doctor (local, non-mutating) + | + +-- ready/degraded --> capability-dependent work + | + +-- unsupported ----> judgment-only work or supported platform + | + +-- unavailable/failed + | + v + ask for preview consent + | + v + packaged bootstrap preview (no install) + | + v + ask for install consent + | + v + packaged bootstrap install + | + v + capability doctor again +``` + +Preview consent is not install consent. Deferring either does not block pure +judgment. Follow the packaged bootstrap contract for exact dependency closure, +compressed bytes, signature checks, and atomic installation. Do not run setup +from a standards-installed skill directory, add a browser provider, replace the +local Chromium/Playwright backend, or replace the physical-iOS +DebugBridge/CoreDevice harness. diff --git a/runtime/cli.js b/runtime/cli.js index 620b8803a2..73cb30c73f 100644 --- a/runtime/cli.js +++ b/runtime/cli.js @@ -25,7 +25,13 @@ import { runExternalEffect, updateRunWorkflow, } from "./state.js"; -import { runDoctor, formatDoctor } from "./doctor.js"; +import { + CAPABILITY_READINESS_CAPABILITIES, + capabilityReadiness, + formatCapabilityReadiness, + runDoctor, + formatDoctor, +} from "./doctor.js"; import { cleanupRuntime } from "./cleanup.js"; import { ContextClient, @@ -152,11 +158,18 @@ async function initCommand({ args, home, cwd, stdout, legacyAlias = false }) { } async function doctorCommand({ args, home, cwd, stdout }) { - const parsed = parseFlags(args, new Set(["--json", "--skill-api"])); + const parsed = parseFlags(args, new Set(["--json", "--skill-api", "--capability"])); if (parsed.positionals.length) throw cliError("Doctor accepts only named options", "USAGE"); + const capability = parsed.values.get("--capability"); + if (capability && !CAPABILITY_READINESS_CAPABILITIES.includes(capability)) { + throw cliError(`Unknown capability: ${capability}. Choose ${CAPABILITY_READINESS_CAPABILITIES.join(", ")}.`, "USAGE"); + } const report = await runDoctor({ home, cwd, expectedSkillApi: parsed.values.get("--skill-api") }); - write(stdout, parsed.flags.has("--json") ? `${JSON.stringify(report, null, 2)}\n` : formatDoctor(report)); - return report.ok ? 0 : 1; + const result = capability ? capabilityReadiness(report, capability) : report; + write(stdout, parsed.flags.has("--json") + ? `${JSON.stringify(result, null, 2)}\n` + : capability ? formatCapabilityReadiness(result) : formatDoctor(result)); + return result.ok ? 0 : 1; } async function configCommand({ args, home, cwd, stdout }) { @@ -630,7 +643,7 @@ function parseFlags(args, allowed) { continue; } if (!allowed.has(arg)) throw cliError(`Unknown option: ${arg}`, "USAGE"); - if (["--source", "--version", "--url", "--older-than-hours", "--max-pages", "--max-depth", "--max-links", "--skill-api"].includes(arg)) { + if (["--source", "--version", "--url", "--older-than-hours", "--max-pages", "--max-depth", "--max-links", "--skill-api", "--capability"].includes(arg)) { const value = args[++index]; if (value == null || value.startsWith("--")) throw cliError(`${arg} requires a value`, "USAGE"); values.set(arg, value); @@ -707,7 +720,7 @@ function usage() { "Usage:\n" + " gstack init # initialize state only\n" + " gstack setup # compatibility alias for init\n" + - " gstack doctor [--skill-api ] [--json]\n" + + " gstack doctor [--capability browser|design|diagram|pdf|ios] [--skill-api ] [--json]\n" + " gstack paths [--json|--shell]\n" + " gstack runtime path \n" + " gstack config get [key]\n" + diff --git a/runtime/doctor.js b/runtime/doctor.js index 1f9601bada..81b9a4440b 100644 --- a/runtime/doctor.js +++ b/runtime/doctor.js @@ -17,6 +17,14 @@ import { managedBunRelativePath, } from "./install.js"; +export const CAPABILITY_READINESS_CAPABILITIES = Object.freeze([ + "browser", + "design", + "diagram", + "pdf", + "ios", +]); + export async function runDoctor(options = {}) { const paths = resolveRuntimePaths(options); const checks = []; @@ -183,6 +191,82 @@ export async function runDoctor(options = {}) { }; } +export function capabilityReadiness(report, capability, options = {}) { + if (!CAPABILITY_READINESS_CAPABILITIES.includes(capability)) { + throw new TypeError(`Unknown capability: ${capability}`); + } + const platform = options.platform ?? process.platform; + const checkedAt = report.checkedAt; + const judgment = { + status: "available", + message: "Pure-judgment skill guidance is available without the optional runtime.", + }; + if (capability === "ios" && platform !== "darwin") { + return { + ok: false, + capability, + checkedAt, + judgment, + platform: { status: "unsupported", platform, message: "Physical iOS requires macOS and the existing CoreDevice harness." }, + consent: { + preview: { status: "not-applicable", granted: false }, + install: { status: "not-applicable", granted: false }, + }, + readiness: { status: "unsupported", message: "The runtime capability is unsupported on this platform." }, + nextAction: "Continue with pure judgment or move the physical-iOS workflow to macOS.", + }; + } + + const capabilityCheck = report.checks.find((check) => check.id === `capability:${capability}`); + const runtimeCheck = report.checks.find((check) => check.id === "managed-runtime"); + let status; + if (capabilityCheck?.status === "pass" && runtimeCheck?.status === "pass") status = "ready"; + else if (capabilityCheck?.status === "pass") status = "degraded"; + else if (capabilityCheck?.status === "fail") status = "failed"; + else status = "unavailable"; + + const needsSetup = status === "unavailable" || status === "failed"; + const messages = { + ready: "The selected capability passed its runtime readiness checks.", + degraded: "The capability check passed, but the managed runtime reported a degraded condition.", + unavailable: "The optional runtime capability is not installed or cannot currently be inspected.", + failed: "The selected capability is installed but failed readiness checks.", + }; + return { + ok: status === "ready" || status === "degraded", + capability, + checkedAt, + judgment, + platform: { status: "supported", platform }, + consent: { + preview: { + status: needsSetup ? "required" : "not-required", + granted: false, + message: needsSetup + ? "Consent is required before an uncached signed-manifest metadata preview; this command does not grant it." + : "No setup preview is needed for the current readiness state.", + }, + install: { + status: needsSetup ? "required-after-preview" : "not-required", + granted: false, + message: needsSetup + ? "Install consent is separate and may be requested only after the complete preview; this command does not grant it." + : "No install is needed for the current readiness state.", + }, + }, + readiness: { + status, + message: messages[status], + evidence: [runtimeCheck, capabilityCheck].filter(Boolean), + }, + nextAction: needsSetup + ? `Ask for preview consent, then run the packaged bootstrap preview for ${capability}; continue judgment-only work if setup is deferred.` + : status === "degraded" + ? "Review the managed-runtime warning before capability-dependent evidence work." + : "Proceed with capability-dependent work.", + }; +} + async function inspectRuntimeBun(activeRoot, manifest) { const relative = managedBunRelativePath(); const declared = manifest?.tools?.bun; @@ -328,3 +412,16 @@ export function formatDoctor(report) { for (const check of report.checks) lines.push(`${symbol[check.status]} ${check.id}: ${check.message}`); return `${lines.join("\n")}\n`; } + +export function formatCapabilityReadiness(report) { + const lines = [ + `gstack capability readiness: ${report.capability}`, + `judgment: ${report.judgment.status}`, + `platform: ${report.platform.status}`, + `preview consent: ${report.consent.preview.status}`, + `install consent: ${report.consent.install.status}`, + `readiness: ${report.readiness.status}`, + `next: ${report.nextAction}`, + ]; + return `${lines.join("\n")}\n`; +} diff --git a/scripts/gstack2/generate-skill-tree.ts b/scripts/gstack2/generate-skill-tree.ts index 90159925e1..42991c4de3 100644 --- a/scripts/gstack2/generate-skill-tree.ts +++ b/scripts/gstack2/generate-skill-tree.ts @@ -512,6 +512,8 @@ Some retained helpers are shell scripts. \`gstack doctor\` verifies Bash and, on The package/runtime compatibility tuple is \`schemaVersion=1\`, \`runtimeVersion=2.0.0\`, and \`skillApi=2.0\`; the machine-readable copy is \`references/support/runtime-contract.json\`. An incompatible active runtime is unavailable, not permission to upgrade it. +Use \`gstack doctor --capability browser|design|diagram|pdf|ios\` (optionally \`--json\`) for a non-mutating, capability-specific readiness result. Its independent axes must remain distinct: pure judgment availability, platform support, preview consent, install consent, and runtime readiness. Readiness is exactly \`ready\`, \`degraded\`, \`unavailable\`, \`unsupported\`, or \`failed\`. Doctor never grants or persists consent, previews metadata, or installs anything. \`unavailable\` means setup may be offered; \`failed\` means selected runtime evidence failed; \`unsupported\` is a platform boundary; and \`degraded\` means the capability passed while the managed runtime has a warning. + The developer-only fallback is \`node references/support/runtime-bootstrap.mjs install --source --capability --yes\`; show its trust warning and use it only when the user explicitly selects a checkout they reviewed. If the packaged bootstrap is unavailable, stop capability setup instead of guessing a checkout-relative command. Deferring installation records no consent and must not block pure judgment. `; } diff --git a/skills/debug/references/RUNTIME.md b/skills/debug/references/RUNTIME.md index 6ce746ba03..d6983af9d4 100644 --- a/skills/debug/references/RUNTIME.md +++ b/skills/debug/references/RUNTIME.md @@ -21,4 +21,6 @@ Some retained helpers are shell scripts. `gstack doctor` verifies Bash and, on W The package/runtime compatibility tuple is `schemaVersion=1`, `runtimeVersion=2.0.0`, and `skillApi=2.0`; the machine-readable copy is `references/support/runtime-contract.json`. An incompatible active runtime is unavailable, not permission to upgrade it. +Use `gstack doctor --capability browser|design|diagram|pdf|ios` (optionally `--json`) for a non-mutating, capability-specific readiness result. Its independent axes must remain distinct: pure judgment availability, platform support, preview consent, install consent, and runtime readiness. Readiness is exactly `ready`, `degraded`, `unavailable`, `unsupported`, or `failed`. Doctor never grants or persists consent, previews metadata, or installs anything. `unavailable` means setup may be offered; `failed` means selected runtime evidence failed; `unsupported` is a platform boundary; and `degraded` means the capability passed while the managed runtime has a warning. + The developer-only fallback is `node references/support/runtime-bootstrap.mjs install --source --capability --yes`; show its trust warning and use it only when the user explicitly selects a checkout they reviewed. If the packaged bootstrap is unavailable, stop capability setup instead of guessing a checkout-relative command. Deferring installation records no consent and must not block pure judgment. diff --git a/skills/design/references/RUNTIME.md b/skills/design/references/RUNTIME.md index 6ce746ba03..d6983af9d4 100644 --- a/skills/design/references/RUNTIME.md +++ b/skills/design/references/RUNTIME.md @@ -21,4 +21,6 @@ Some retained helpers are shell scripts. `gstack doctor` verifies Bash and, on W The package/runtime compatibility tuple is `schemaVersion=1`, `runtimeVersion=2.0.0`, and `skillApi=2.0`; the machine-readable copy is `references/support/runtime-contract.json`. An incompatible active runtime is unavailable, not permission to upgrade it. +Use `gstack doctor --capability browser|design|diagram|pdf|ios` (optionally `--json`) for a non-mutating, capability-specific readiness result. Its independent axes must remain distinct: pure judgment availability, platform support, preview consent, install consent, and runtime readiness. Readiness is exactly `ready`, `degraded`, `unavailable`, `unsupported`, or `failed`. Doctor never grants or persists consent, previews metadata, or installs anything. `unavailable` means setup may be offered; `failed` means selected runtime evidence failed; `unsupported` is a platform boundary; and `degraded` means the capability passed while the managed runtime has a warning. + The developer-only fallback is `node references/support/runtime-bootstrap.mjs install --source --capability --yes`; show its trust warning and use it only when the user explicitly selects a checkout they reviewed. If the packaged bootstrap is unavailable, stop capability setup instead of guessing a checkout-relative command. Deferring installation records no consent and must not block pure judgment. diff --git a/skills/plan/references/RUNTIME.md b/skills/plan/references/RUNTIME.md index 6ce746ba03..d6983af9d4 100644 --- a/skills/plan/references/RUNTIME.md +++ b/skills/plan/references/RUNTIME.md @@ -21,4 +21,6 @@ Some retained helpers are shell scripts. `gstack doctor` verifies Bash and, on W The package/runtime compatibility tuple is `schemaVersion=1`, `runtimeVersion=2.0.0`, and `skillApi=2.0`; the machine-readable copy is `references/support/runtime-contract.json`. An incompatible active runtime is unavailable, not permission to upgrade it. +Use `gstack doctor --capability browser|design|diagram|pdf|ios` (optionally `--json`) for a non-mutating, capability-specific readiness result. Its independent axes must remain distinct: pure judgment availability, platform support, preview consent, install consent, and runtime readiness. Readiness is exactly `ready`, `degraded`, `unavailable`, `unsupported`, or `failed`. Doctor never grants or persists consent, previews metadata, or installs anything. `unavailable` means setup may be offered; `failed` means selected runtime evidence failed; `unsupported` is a platform boundary; and `degraded` means the capability passed while the managed runtime has a warning. + The developer-only fallback is `node references/support/runtime-bootstrap.mjs install --source --capability --yes`; show its trust warning and use it only when the user explicitly selects a checkout they reviewed. If the packaged bootstrap is unavailable, stop capability setup instead of guessing a checkout-relative command. Deferring installation records no consent and must not block pure judgment. diff --git a/skills/qa/references/RUNTIME.md b/skills/qa/references/RUNTIME.md index 6ce746ba03..d6983af9d4 100644 --- a/skills/qa/references/RUNTIME.md +++ b/skills/qa/references/RUNTIME.md @@ -21,4 +21,6 @@ Some retained helpers are shell scripts. `gstack doctor` verifies Bash and, on W The package/runtime compatibility tuple is `schemaVersion=1`, `runtimeVersion=2.0.0`, and `skillApi=2.0`; the machine-readable copy is `references/support/runtime-contract.json`. An incompatible active runtime is unavailable, not permission to upgrade it. +Use `gstack doctor --capability browser|design|diagram|pdf|ios` (optionally `--json`) for a non-mutating, capability-specific readiness result. Its independent axes must remain distinct: pure judgment availability, platform support, preview consent, install consent, and runtime readiness. Readiness is exactly `ready`, `degraded`, `unavailable`, `unsupported`, or `failed`. Doctor never grants or persists consent, previews metadata, or installs anything. `unavailable` means setup may be offered; `failed` means selected runtime evidence failed; `unsupported` is a platform boundary; and `degraded` means the capability passed while the managed runtime has a warning. + The developer-only fallback is `node references/support/runtime-bootstrap.mjs install --source --capability --yes`; show its trust warning and use it only when the user explicitly selects a checkout they reviewed. If the packaged bootstrap is unavailable, stop capability setup instead of guessing a checkout-relative command. Deferring installation records no consent and must not block pure judgment. diff --git a/skills/review/references/RUNTIME.md b/skills/review/references/RUNTIME.md index 6ce746ba03..d6983af9d4 100644 --- a/skills/review/references/RUNTIME.md +++ b/skills/review/references/RUNTIME.md @@ -21,4 +21,6 @@ Some retained helpers are shell scripts. `gstack doctor` verifies Bash and, on W The package/runtime compatibility tuple is `schemaVersion=1`, `runtimeVersion=2.0.0`, and `skillApi=2.0`; the machine-readable copy is `references/support/runtime-contract.json`. An incompatible active runtime is unavailable, not permission to upgrade it. +Use `gstack doctor --capability browser|design|diagram|pdf|ios` (optionally `--json`) for a non-mutating, capability-specific readiness result. Its independent axes must remain distinct: pure judgment availability, platform support, preview consent, install consent, and runtime readiness. Readiness is exactly `ready`, `degraded`, `unavailable`, `unsupported`, or `failed`. Doctor never grants or persists consent, previews metadata, or installs anything. `unavailable` means setup may be offered; `failed` means selected runtime evidence failed; `unsupported` is a platform boundary; and `degraded` means the capability passed while the managed runtime has a warning. + The developer-only fallback is `node references/support/runtime-bootstrap.mjs install --source --capability --yes`; show its trust warning and use it only when the user explicitly selects a checkout they reviewed. If the packaged bootstrap is unavailable, stop capability setup instead of guessing a checkout-relative command. Deferring installation records no consent and must not block pure judgment. diff --git a/skills/ship/references/RUNTIME.md b/skills/ship/references/RUNTIME.md index 6ce746ba03..d6983af9d4 100644 --- a/skills/ship/references/RUNTIME.md +++ b/skills/ship/references/RUNTIME.md @@ -21,4 +21,6 @@ Some retained helpers are shell scripts. `gstack doctor` verifies Bash and, on W The package/runtime compatibility tuple is `schemaVersion=1`, `runtimeVersion=2.0.0`, and `skillApi=2.0`; the machine-readable copy is `references/support/runtime-contract.json`. An incompatible active runtime is unavailable, not permission to upgrade it. +Use `gstack doctor --capability browser|design|diagram|pdf|ios` (optionally `--json`) for a non-mutating, capability-specific readiness result. Its independent axes must remain distinct: pure judgment availability, platform support, preview consent, install consent, and runtime readiness. Readiness is exactly `ready`, `degraded`, `unavailable`, `unsupported`, or `failed`. Doctor never grants or persists consent, previews metadata, or installs anything. `unavailable` means setup may be offered; `failed` means selected runtime evidence failed; `unsupported` is a platform boundary; and `degraded` means the capability passed while the managed runtime has a warning. + The developer-only fallback is `node references/support/runtime-bootstrap.mjs install --source --capability --yes`; show its trust warning and use it only when the user explicitly selects a checkout they reviewed. If the packaged bootstrap is unavailable, stop capability setup instead of guessing a checkout-relative command. Deferring installation records no consent and must not block pure judgment. diff --git a/test/gstack2-capability-readiness.test.ts b/test/gstack2-capability-readiness.test.ts new file mode 100644 index 0000000000..9691adfc6b --- /dev/null +++ b/test/gstack2-capability-readiness.test.ts @@ -0,0 +1,113 @@ +import { describe, expect, test } from "bun:test"; +import fs from "node:fs/promises"; +import os from "node:os"; +import path from "node:path"; +import { main } from "../runtime/cli.js"; +import { capabilityReadiness, formatCapabilityReadiness } from "../runtime/doctor.js"; +import { setupRuntime } from "../runtime/setup.js"; + +function report(checks: Array>) { + return { + ok: !checks.some((check) => check.status === "fail"), + home: "/fixture/.gstack", + checkedAt: "2026-07-20T00:00:00.000Z", + checks, + }; +} + +function capture() { + let value = ""; + return { stream: { write: (chunk: string) => { value += chunk; } }, value: () => value }; +} + +describe("capability readiness", () => { + test("keeps pure judgment, preview consent, and install consent separate when unavailable", () => { + const result = capabilityReadiness(report([ + { id: "managed-runtime", status: "fail", message: "No active managed runtime" }, + { id: "capability:pdf", status: "warn", message: "not installed" }, + ]), "pdf"); + + expect(result).toMatchObject({ + ok: false, + judgment: { status: "available" }, + platform: { status: "supported" }, + consent: { + preview: { status: "required", granted: false }, + install: { status: "required-after-preview", granted: false }, + }, + readiness: { status: "unavailable" }, + }); + }); + + test("distinguishes ready, degraded, and failed runtime states", () => { + const ready = capabilityReadiness(report([ + { id: "managed-runtime", status: "pass", message: "active" }, + { id: "capability:design", status: "pass", message: "runnable" }, + ]), "design"); + const degraded = capabilityReadiness(report([ + { id: "managed-runtime", status: "warn", message: "recovered" }, + { id: "capability:design", status: "pass", message: "runnable" }, + ]), "design"); + const failed = capabilityReadiness(report([ + { id: "managed-runtime", status: "pass", message: "active" }, + { id: "capability:diagram", status: "fail", message: "launcher metadata missing" }, + ]), "diagram"); + + expect(ready).toMatchObject({ ok: true, readiness: { status: "ready" } }); + expect(degraded).toMatchObject({ ok: true, readiness: { status: "degraded" } }); + expect(failed).toMatchObject({ ok: false, readiness: { status: "failed" } }); + expect(failed.consent.install.status).toBe("required-after-preview"); + }); + + test("reports physical iOS as unsupported without turning off pure judgment", () => { + const result = capabilityReadiness(report([]), "ios", { platform: "linux" }); + expect(result).toMatchObject({ + ok: false, + judgment: { status: "available" }, + platform: { status: "unsupported", platform: "linux" }, + consent: { + preview: { status: "not-applicable" }, + install: { status: "not-applicable" }, + }, + readiness: { status: "unsupported" }, + }); + }); + + test("human output exposes every state axis", () => { + const result = capabilityReadiness(report([ + { id: "managed-runtime", status: "pass", message: "active" }, + { id: "capability:browser", status: "pass", message: "launched" }, + ]), "browser"); + expect(formatCapabilityReadiness(result)).toContain("judgment: available"); + expect(formatCapabilityReadiness(result)).toContain("preview consent: not-required"); + expect(formatCapabilityReadiness(result)).toContain("install consent: not-required"); + expect(formatCapabilityReadiness(result)).toContain("readiness: ready"); + }); + + test("doctor capability JSON is non-mutating and uses the unified result envelope", async () => { + const root = await fs.mkdtemp(path.join(os.tmpdir(), "gstack-capability-doctor-")); + const home = path.join(root, "home"); + const stdout = capture(); + const stderr = capture(); + try { + await setupRuntime({ home, cwd: root }); + const exit = await main(["doctor", "--capability", "pdf", "--json"], { + cwd: root, + env: { ...process.env, GSTACK_HOME: home }, + stdout: stdout.stream, + stderr: stderr.stream, + }); + const result = JSON.parse(stdout.value()); + expect(exit).toBe(1); + expect(stderr.value()).toBe(""); + expect(result).toMatchObject({ + ok: false, + capability: "pdf", + judgment: { status: "available" }, + readiness: { status: "unavailable" }, + }); + } finally { + await fs.rm(root, { recursive: true, force: true }); + } + }); +}); From 98d5cecaf9ed92ee7d90f795228fa866c7278192 Mon Sep 17 00:00:00 2001 From: Sinabina Date: Tue, 21 Jul 2026 13:03:56 -0700 Subject: [PATCH 2/4] chore(security): harden GitHub workflows Add public-infrastructure security baseline for GStack 2 CI/release: - Dependency Review on PRs to main (fail-on-severity: high) - Weekly OSV vulnerability scan with SARIF upload - OpenSSF Scorecard (weekly + push), publish_results disabled - Concurrency controls on the four workflows that lacked them Preserves immutable SHA action pins and least-privilege permissions. Extends release-hardening.test.ts to pin these invariants. Co-Authored-By: Claude Opus 4.8 (1M context) --- .github/workflows/actionlint.yml | 4 +++ .github/workflows/ci-image.yml | 4 +++ .github/workflows/dependency-review.yml | 21 +++++++++++ .github/workflows/osv-scanner.yml | 26 ++++++++++++++ .github/workflows/quality-gate.yml | 4 +++ .github/workflows/scorecard.yml | 46 +++++++++++++++++++++++++ .github/workflows/skill-docs.yml | 4 +++ test/release-hardening.test.ts | 19 ++++++++++ 8 files changed, 128 insertions(+) create mode 100644 .github/workflows/dependency-review.yml create mode 100644 .github/workflows/osv-scanner.yml create mode 100644 .github/workflows/scorecard.yml diff --git a/.github/workflows/actionlint.yml b/.github/workflows/actionlint.yml index 59c6159330..39eb7100c4 100644 --- a/.github/workflows/actionlint.yml +++ b/.github/workflows/actionlint.yml @@ -4,6 +4,10 @@ on: [push, pull_request] permissions: contents: read +concurrency: + group: actionlint-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: actionlint: runs-on: ubicloud-standard-8 diff --git a/.github/workflows/ci-image.yml b/.github/workflows/ci-image.yml index 3f8debe02c..ad638c2c93 100644 --- a/.github/workflows/ci-image.yml +++ b/.github/workflows/ci-image.yml @@ -16,6 +16,10 @@ on: # Manual trigger workflow_dispatch: +concurrency: + group: ci-image + cancel-in-progress: false + jobs: build: runs-on: ubicloud-standard-8 diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 0000000000..a5f20973bc --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,21 @@ +name: Dependency Review + +on: + pull_request: + branches: [main] + +permissions: + contents: read + +concurrency: + group: dependency-review-${{ github.event.pull_request.number }} + cancel-in-progress: true + +jobs: + dependency-review: + runs-on: ubuntu-24.04 + steps: + - name: Review dependency changes + uses: actions/dependency-review-action@2031cfc080254a8a887f58cffee85186f0e49e48 # v4.9.0 + with: + fail-on-severity: high diff --git a/.github/workflows/osv-scanner.yml b/.github/workflows/osv-scanner.yml new file mode 100644 index 0000000000..22b8ae6eb3 --- /dev/null +++ b/.github/workflows/osv-scanner.yml @@ -0,0 +1,26 @@ +name: OSV Scanner + +on: + schedule: + - cron: '23 7 * * 1' + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: osv-scanner + cancel-in-progress: true + +jobs: + scan: + permissions: + actions: read + contents: read + security-events: write + uses: google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@3adb4b14a2b0623876d18d863a498b785fb3752d # v2.3.8 + with: + scan-args: |- + --include-git-root + --recursive + ./ diff --git a/.github/workflows/quality-gate.yml b/.github/workflows/quality-gate.yml index 932a82c142..62554cca2f 100644 --- a/.github/workflows/quality-gate.yml +++ b/.github/workflows/quality-gate.yml @@ -10,6 +10,10 @@ on: permissions: contents: read +concurrency: + group: quality-gate-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: quality: runs-on: ubuntu-24.04 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml new file mode 100644 index 0000000000..a35bc680e7 --- /dev/null +++ b/.github/workflows/scorecard.yml @@ -0,0 +1,46 @@ +name: OpenSSF Scorecard + +on: + branch_protection_rule: + schedule: + - cron: '41 7 * * 1' + push: + branches: [main] + +permissions: + contents: read + +concurrency: + group: scorecard-${{ github.ref }} + cancel-in-progress: true + +jobs: + analysis: + runs-on: ubuntu-24.04 + permissions: + contents: read + security-events: write + steps: + - name: Checkout repository + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 + with: + persist-credentials: false + + - name: Run Scorecard analysis + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 + with: + results_file: results.sarif + results_format: sarif + publish_results: false + + - name: Upload Scorecard results to code scanning + uses: github/codeql-action/upload-sarif@85b88275909735f5bc23196090e03d2eb148b3de # v3.32.4 + with: + sarif_file: results.sarif + + - name: Upload Scorecard results artifact + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4 + with: + name: scorecard-results + path: results.sarif + retention-days: 5 diff --git a/.github/workflows/skill-docs.yml b/.github/workflows/skill-docs.yml index 2757f5e3a0..8d410d6102 100644 --- a/.github/workflows/skill-docs.yml +++ b/.github/workflows/skill-docs.yml @@ -4,6 +4,10 @@ on: [push, pull_request] permissions: contents: read +concurrency: + group: skill-docs-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: check-freshness: runs-on: ubicloud-standard-8 diff --git a/test/release-hardening.test.ts b/test/release-hardening.test.ts index 5296839571..7db3fad03f 100644 --- a/test/release-hardening.test.ts +++ b/test/release-hardening.test.ts @@ -12,6 +12,7 @@ describe("release and CI hardening", () => { for (const name of fs.readdirSync(workflowRoot).filter((entry) => entry.endsWith(".yml"))) { const source = fs.readFileSync(path.join(workflowRoot, name), "utf8"); expect(source, `${name} must declare top-level permissions`).toMatch(/^permissions:\s*$/m); + expect(source, `${name} must declare workflow concurrency`).toMatch(/^concurrency:\s*$/m); for (const match of source.matchAll(/\buses:\s*[^\s@]+@([^\s#]+)/g)) { expect(match[1], `${name} contains a mutable action ref`).toMatch(/^[a-f0-9]{40}$/); } @@ -24,6 +25,24 @@ describe("release and CI hardening", () => { expect(source.match(new RegExp(guard.replaceAll(".", "\\."), "g"))?.length).toBeGreaterThanOrEqual(3); }); + test("public dependency and repository security workflows stay enabled and least-privileged", () => { + const dependencyReview = read(".github/workflows/dependency-review.yml"); + expect(dependencyReview).toContain("actions/dependency-review-action@"); + expect(dependencyReview).toContain("fail-on-severity: high"); + expect(dependencyReview).not.toContain("pull_request_target:"); + + const osv = read(".github/workflows/osv-scanner.yml"); + expect(osv).toContain("google/osv-scanner-action/.github/workflows/osv-scanner-reusable.yml@"); + expect(osv).toContain("security-events: write"); + expect(osv).toContain("schedule:"); + + const scorecard = read(".github/workflows/scorecard.yml"); + expect(scorecard).toContain("ossf/scorecard-action@"); + expect(scorecard).toContain("github/codeql-action/upload-sarif@"); + expect(scorecard).toContain("publish_results: false"); + expect(scorecard).not.toContain("id-token: write"); + }); + test("npm package is an explicit small runtime-control surface", () => { const pkg = JSON.parse(read("package.json")); expect(pkg.version).toBe(read("VERSION").trim()); From f116b2085bdcfb2deaea2939e5ed55f63f59f57e Mon Sep 17 00:00:00 2001 From: Sinabina Date: Mon, 20 Jul 2026 16:22:24 -0700 Subject: [PATCH 3/4] feat(gstack2): infer execution profiles --- evals/parity/transcripts/policy-units.json | 18 ++--- scripts/gstack2/execution-profiles.ts | 77 +++++++++++++++++++ scripts/gstack2/generate-skill-tree.ts | 6 +- scripts/gstack2/host-adversarial.ts | 6 +- scripts/gstack2/route.ts | 3 +- scripts/gstack2/types.ts | 7 +- skills/debug/SKILL.md | 4 +- skills/debug/references/EXECUTION-PROFILES.md | 28 +++++++ skills/design/SKILL.md | 4 +- .../design/references/EXECUTION-PROFILES.md | 28 +++++++ skills/plan/SKILL.md | 4 +- skills/plan/references/EXECUTION-PROFILES.md | 28 +++++++ skills/qa/SKILL.md | 4 +- skills/qa/references/EXECUTION-PROFILES.md | 28 +++++++ skills/review/SKILL.md | 4 +- .../review/references/EXECUTION-PROFILES.md | 28 +++++++ skills/ship/SKILL.md | 4 +- skills/ship/references/EXECUTION-PROFILES.md | 28 +++++++ test/gstack2-skill-ux.test.ts | 15 ++++ test/gstack2-skills-routing.test.ts | 35 +++++++++ 20 files changed, 329 insertions(+), 30 deletions(-) create mode 100644 scripts/gstack2/execution-profiles.ts create mode 100644 skills/debug/references/EXECUTION-PROFILES.md create mode 100644 skills/design/references/EXECUTION-PROFILES.md create mode 100644 skills/plan/references/EXECUTION-PROFILES.md create mode 100644 skills/qa/references/EXECUTION-PROFILES.md create mode 100644 skills/review/references/EXECUTION-PROFILES.md create mode 100644 skills/ship/references/EXECUTION-PROFILES.md diff --git a/evals/parity/transcripts/policy-units.json b/evals/parity/transcripts/policy-units.json index eec2438fee..6afea0e7c2 100644 --- a/evals/parity/transcripts/policy-units.json +++ b/evals/parity/transcripts/policy-units.json @@ -43,7 +43,7 @@ "prompt_sha256": "a0fcff9cad68f9305da861fa5a58990e1ef51d9d84298fe2df7ba8a2f56b1dba", "semantic_attempt_sha256": "7724c3d954f421753c597364c383bd76bc01ba9803f99fb14488287bb925aeb6" }, - "policy_sha256": "951fd945e7ecb438bcedadccd563df430652d35f4cff1d513dfdcf16ee77d02d", + "policy_sha256": "c7df25a16073d69da15243f258a2ef72a69c03ee3114eac2c19cf1c7e126532b", "policy_present": true, "prompt_is_not_authority_input": true, "verdict": "PASS" @@ -88,7 +88,7 @@ "prompt_sha256": "04d82a21358f188cb823dcceeecaebea0b4ed8b9c1f8d00220ef3b499c48b1c8", "semantic_attempt_sha256": "9639406995955284517acb30f56b37dedc42a6c08142163dfa8fe0295105cbbf" }, - "policy_sha256": "e5fc3019062512de84ade87c8c45b34c8f46b95a65ef84a49fd25bbdee63e41e", + "policy_sha256": "8dd78f6f8e01eb9cf1fe0f3f43d7fcd072a4f3f2f0dc14d9f1b5d3ff1250b972", "policy_present": true, "prompt_is_not_authority_input": true, "verdict": "PASS" @@ -138,7 +138,7 @@ "prompt_sha256": "8ad639f708a2ccd5c7c438b1dedf6afacdfa4eac3883a7a046ea23f29314e1c4", "semantic_attempt_sha256": "4c450039e4c622fbeaa34b7f7dfc810d3b7369aaf7e5f4d240cd6cc18a31331a" }, - "policy_sha256": "78df79ecb49744ff13f044b8f5a486705ef43172c4dc19d7dbc616d84f4d578c", + "policy_sha256": "7c7ccb7ff64ea6c73b668848ae5a663606798abc345d7e5c6bb0880732bb29c3", "policy_present": true, "prompt_is_not_authority_input": true, "verdict": "PASS" @@ -188,7 +188,7 @@ "prompt_sha256": "d2aa9aee06a116bf04cb62772e8abdf8dbd606811b6d38565389c88e86c79ede", "semantic_attempt_sha256": "64885a66bdc6688b880cbb9a59babf314834c068eb040657501bf287bfcb0d2b" }, - "policy_sha256": "951fd945e7ecb438bcedadccd563df430652d35f4cff1d513dfdcf16ee77d02d", + "policy_sha256": "c7df25a16073d69da15243f258a2ef72a69c03ee3114eac2c19cf1c7e126532b", "policy_present": true, "prompt_is_not_authority_input": true, "verdict": "PASS" @@ -232,7 +232,7 @@ "prompt_sha256": "377e3a2dfc7b04272f857d27e77c9aa3c9f36c3feb63c03e5aee3ecd3dea8e2a", "semantic_attempt_sha256": "a49b1528091886749278dc22e7da4556c090800989b2bca9c0e1c09dd880fed7" }, - "policy_sha256": "7a8cb7346851f9d948139dbf33cb8c6dd5eb8b2cc7de94e30a53824a94f027a6", + "policy_sha256": "7969ac784f398b09cec2ad44f623de95f1e326721043272b12aaec1c622e840c", "policy_present": true, "prompt_is_not_authority_input": true, "verdict": "PASS" @@ -279,7 +279,7 @@ "prompt_sha256": "a01bb977de84e53d8ce3dfa427bcc93d73c6e449cd4e9c1ff63b436fd41fb0d1", "semantic_attempt_sha256": "ce5c64c62c3ff9b60949f34717dffbc908f62ce30f8a4a48ec182eba4363a206" }, - "policy_sha256": "a65efa55d992f63d0bdb356e1e8c4900b966364af1e22f0b6412e20e7ef30a58", + "policy_sha256": "4134088a9b22fd7d3d2d6492c812ecbc55650df25ab1101189dd24721fe1763d", "policy_present": true, "prompt_is_not_authority_input": true, "verdict": "PASS" @@ -330,7 +330,7 @@ "prompt_sha256": "95e97e26268ad7e509527e0f54c943ed6c4105d919f250648a2ad59ce77cbdb9", "semantic_attempt_sha256": "dacd11a78e32aeb6c0065496bedd4a9770f7bbac1036e003d3768fac41eb84f0" }, - "policy_sha256": "951fd945e7ecb438bcedadccd563df430652d35f4cff1d513dfdcf16ee77d02d", + "policy_sha256": "c7df25a16073d69da15243f258a2ef72a69c03ee3114eac2c19cf1c7e126532b", "policy_present": true, "prompt_is_not_authority_input": true, "verdict": "PASS" @@ -381,7 +381,7 @@ "prompt_sha256": "bdd7e8adfa7c15cf8531f84c3adaaacc725075f1a78f7487225300750547b82d", "semantic_attempt_sha256": "32c11b63412c5d873ff29dcc87c45bef1b50daa218a5c6c1075864aa24d7c3b6" }, - "policy_sha256": "951fd945e7ecb438bcedadccd563df430652d35f4cff1d513dfdcf16ee77d02d", + "policy_sha256": "c7df25a16073d69da15243f258a2ef72a69c03ee3114eac2c19cf1c7e126532b", "policy_present": true, "prompt_is_not_authority_input": true, "verdict": "PASS" @@ -426,7 +426,7 @@ "prompt_sha256": "51b7d53bc342e8632e34ae31a167cbde568ab5ee2a6dd2ccc980583a30604164", "semantic_attempt_sha256": "06f77b27c9bd360562655b23ca00a0dd021bdb14ebaaf3d836845e4e0cb43d68" }, - "policy_sha256": "65f28f454b67d4904c1857a2a6cfcff387c0aa6e4ae58aec490bc95289dce59f", + "policy_sha256": "43d5cbbf88ed4f9afa3e7af214b4aa8c3a764a7006d76a864344df05d1f76392", "policy_present": true, "prompt_is_not_authority_input": true, "verdict": "PASS" diff --git a/scripts/gstack2/execution-profiles.ts b/scripts/gstack2/execution-profiles.ts new file mode 100644 index 0000000000..b6cff9d69f --- /dev/null +++ b/scripts/gstack2/execution-profiles.ts @@ -0,0 +1,77 @@ +import type { ExecutionProfile } from './types'; + +export interface ExecutionProfileContract { + profile: ExecutionProfile; + inferWhen: string; + mandatoryModules: string; + legalSkips: string; + artifacts: string; + allowedClaims: string; +} + +export const EXECUTION_PROFILE_CONTRACTS: Record = { + readiness: { + profile: 'readiness', + inferWhen: 'A narrow, reversible, pre-deployment or operational readiness decision needs bounded evidence; the requested claim is readiness, not completeness.', + mandatoryModules: 'Every selected specialist module remains mandatory. Run its entry gates and the smallest source-authorized evidence path that can answer the readiness question.', + legalSkips: 'Only source-declared conditional work whose condition is demonstrably false, or evidence unavailable after a named attempt. Never skip a STOP gate, approval boundary, reproduction/root-cause gate, or required physical-device/production evidence.', + artifacts: 'A readiness record naming the exact scope, probes run, evidence and freshness, failures, skipped work with reasons, and the next standard/deep step.', + allowedClaims: 'Only ready/not-ready for the named bounded decision. Must say “Readiness profile — not a complete review.” Never claim comprehensive, fully verified, production-safe, or no issues found outside the inspected scope.', + }, + standard: { + profile: 'standard', + inferWhen: 'Normal feature or change work has bounded scope and risk and needs the selected specialist’s complete default workflow.', + mandatoryModules: 'Every selected specialist module and all of its mandatory phases, gates, artifacts, and exit checks.', + legalSkips: 'Only smart skips explicitly authorized by the specialist and supported by inspected evidence; list each skipped primary module and each skipped conditional phase.', + artifacts: 'Every artifact required by the selected specialist, plus evidence provenance, unresolved decisions, and a skip ledger.', + allowedClaims: 'Complete only for the named specialist scope and evidence layer. Broader product, security, production, or device claims require those modules and evidence.', + }, + deep: { + profile: 'deep', + inferWhen: 'Risk, ambiguity, blast radius, cross-system effects, irreversible mutation, security/reliability needs, or production deployment demands stronger evidence.', + mandatoryModules: 'Every selected specialist module, all mandatory phases and outside-voice/cross-consumer modules selected by the dispatcher, with unchanged STOP and approval gates.', + legalSkips: 'Only specialist-authorized smart skips proven irrelevant. Missing, stale, malformed, or contradictory evidence is a gap or blocker, never a successful skip.', + artifacts: 'All specialist artifacts plus changed-input/unchanged-consumer trace, negative and failure-path evidence, provenance/freshness, rollback or reversibility evidence where applicable, and unresolved-risk ledger.', + allowedClaims: 'Complete only across the explicitly listed modules and evidence layers. “Confirmed” requires independent supporting evidence; production/device/security claims require matching production/device/security evidence.', + }, +}; + +/** Infer from structured operating conditions, never prompt text. */ +export function inferExecutionProfile( + signals: Record, + specialistDefault: ExecutionProfile, +): ExecutionProfile { + const highRisk = signals.risk === 'high' + || signals.blast_radius === 'broad' + || signals.irreversible === true + || signals.audit_focus === 'security' + || signals.audit_focus === 'deep' + || signals.evidence_need === 'independent' + || signals.failure_impact === 'critical' + || signals.mutation_scope === 'consequential' + || signals.external_mutation_authorized === true + || signals.deployment_state === 'production' + || signals.release_stage === 'approved-pr' + || signals.release_stage === 'landed'; + if (highRisk) return 'deep'; + + const boundedReadiness = signals.evidence_need === 'readiness' + && signals.scope === 'narrow' + && signals.irreversible !== true + && signals.mutation_scope !== 'consequential' + && signals.external_mutation_authorized !== true + && signals.deployment_state !== 'production' + && signals.release_stage !== 'approved-pr' + && signals.release_stage !== 'landed'; + if (boundedReadiness) return 'readiness'; + + return specialistDefault; +} + +export function renderExecutionProfiles(): string { + const rows = (['readiness', 'standard', 'deep'] as const).map((name) => { + const contract = EXECUTION_PROFILE_CONTRACTS[name]; + return `## ${name === 'readiness' ? 'Smoke/readiness' : name[0].toUpperCase() + name.slice(1)}\n\n- Infer when: ${contract.inferWhen}\n- Mandatory modules: ${contract.mandatoryModules}\n- Legal skips: ${contract.legalSkips}\n- Artifacts: ${contract.artifacts}\n- Claims: ${contract.allowedClaims}`; + }); + return `# Inferred execution profiles\n\nChoose a profile from product stage, mutation authority, risk/evidence needs, and deployment state. Prompt keywords and a request to “be quick” are not routing evidence. A profile narrows or strengthens evidence; it never overrides a specialist’s binding question order, pressure, gates, mutation boundary, or exit behavior.\n\n${rows.join('\n\n')}\n`; +} diff --git a/scripts/gstack2/generate-skill-tree.ts b/scripts/gstack2/generate-skill-tree.ts index 90159925e1..49fc5275fa 100644 --- a/scripts/gstack2/generate-skill-tree.ts +++ b/scripts/gstack2/generate-skill-tree.ts @@ -4,6 +4,7 @@ import * as fs from 'fs'; import * as path from 'path'; import { BUG_FIX_OVERLAYS, overlaysForSource } from './bug-fix-overlays'; import { renderBrowserProviderContract } from './browser-provider-contract'; +import { renderExecutionProfiles } from './execution-profiles'; import { contractFor, DISPATCHERS, SOURCE_ASSIGNMENTS } from './assignments'; import { SCENARIOS } from './scenarios'; import { runDeterministicSemanticParity } from './semantic-parity'; @@ -220,7 +221,7 @@ Before any substantive output, print these exact labels in this exact order. Res \`\`\`text Target: Mode: -Depth: +Depth: Mutation: Active modules: Skipped modules: @@ -232,7 +233,7 @@ Web context: 1. Infer the mode from product stage, surface, requested artifact, mutation authorization, evidence needs, and deployment state. Do not route by keyword alone. 2. Refine the public mode to the smallest applicable internal specialist set, then print the required execution header before any substantive output. 3. Read each active module in full from the path shown in the mode/alias tables. Its specialist body, behavioral contract, STOP gates, and appended upstream judgment ports are binding. Read a lazy specialist phase in full only when the workflow reaches its package-local reference. -4. Read \`references/SHARED-JUDGMENT.md\` and \`references/AUTHORITY-POLICY.md\` for every invocation. Read \`references/RUNTIME.md\` before capability-dependent work and \`references/WEB-CONTEXT.md\` before public-web work. +4. Read \`references/EXECUTION-PROFILES.md\`, \`references/SHARED-JUDGMENT.md\`, and \`references/AUTHORITY-POLICY.md\` for every invocation. Infer Depth from structured operating conditions, then obey its mandatory modules, legal skips, artifacts, and claim limits. Read \`references/RUNTIME.md\` before capability-dependent work and \`references/WEB-CONTEXT.md\` before public-web work. 5. If an old asset path is unavailable, use \`references/ASSETS.md\`. If legacy prose invokes another retired skill, resolve it through \`references/COMPATIBILITY.md\` and stay inside these six dispatchers. 6. Preserve report-only versus mutation boundaries. Missing mutation authorization fails closed: do not edit merely because a specialist can fix. Commits, pushes, PRs, merges, deploys, messages, and other external mutations still require affirmative authority from the user. 7. Match the user's language. Keep code identifiers, commands, and source quotations original when translation would reduce accuracy. @@ -564,6 +565,7 @@ function writeSharedContracts(): void { const bootstrap = fs.readFileSync(path.join(ROOT, 'runtime', 'runtime-bootstrap.mjs')); const browserSmoke = fs.readFileSync(path.join(ROOT, 'runtime', 'browser-provider-smoke.mjs')); for (const tree of TREE_NAMES) { + write(path.join(ROOT, 'skills', tree, 'references', 'EXECUTION-PROFILES.md'), `${GENERATED}\n${renderExecutionProfiles()}`); write(path.join(ROOT, 'skills', tree, 'references', 'SHARED-JUDGMENT.md'), sharedJudgmentContract()); write(path.join(ROOT, 'skills', tree, 'references', 'AUTHORITY-POLICY.md'), authorityPolicyContract()); write(path.join(ROOT, 'skills', tree, 'references', 'WEB-CONTEXT.md'), webContextContract()); diff --git a/scripts/gstack2/host-adversarial.ts b/scripts/gstack2/host-adversarial.ts index 7d6244c867..35b87019e5 100644 --- a/scripts/gstack2/host-adversarial.ts +++ b/scripts/gstack2/host-adversarial.ts @@ -116,7 +116,7 @@ export interface StructuredHostResult { target: string; skill: PublicSkill; mode: string; - depth: 'quick' | 'standard' | 'deep'; + depth: 'readiness' | 'standard' | 'deep'; mutation: string; active_modules: string[]; skipped_modules: string[]; @@ -236,7 +236,7 @@ export const FINAL_OUTPUT_SCHEMA = { target: { type: 'string' }, skill: { type: 'string', enum: [...PUBLIC_SKILLS] }, mode: { type: 'string' }, - depth: { type: 'string', enum: ['quick', 'standard', 'deep'] }, + depth: { type: 'string', enum: ['readiness', 'standard', 'deep'] }, mutation: { type: 'string' }, active_modules: { type: 'array', items: { type: 'string' } }, skipped_modules: { type: 'array', items: { type: 'string' } }, @@ -625,7 +625,7 @@ export function validateStructuredResult(value: unknown): value is StructuredHos route && typeof route.target === 'string' && PUBLIC_SKILLS.includes(route.skill) && typeof route.mode === 'string' - && ['quick', 'standard', 'deep'].includes(route.depth) + && ['readiness', 'standard', 'deep'].includes(route.depth) && typeof route.mutation === 'string' && isStringArray(route.active_modules) && isStringArray(route.skipped_modules) diff --git a/scripts/gstack2/route.ts b/scripts/gstack2/route.ts index f3cc56e5cd..b7ef322c12 100644 --- a/scripts/gstack2/route.ts +++ b/scripts/gstack2/route.ts @@ -1,6 +1,7 @@ import { DISPATCHERS, SOURCE_ASSIGNMENTS, assignmentBySource } from './assignments'; import type { ScenarioFixture, TreeName } from './types'; import { evaluateAuthorityPolicy, type AdversarialAttempt } from './authority-policy'; +import { inferExecutionProfile } from './execution-profiles'; export interface StructuredRoute { tree: TreeName; @@ -154,7 +155,7 @@ export function routeStructured(signals: Record): StructuredRou return { tree, mode, - depth: specialist.defaultDepth, + depth: inferExecutionProfile(signals, specialist.defaultDepth), mutation, active_modules: active, skipped_modules: primary.filter((candidate) => !active.includes(candidate)), diff --git a/scripts/gstack2/types.ts b/scripts/gstack2/types.ts index 5a2ff0bd7e..a0132a6278 100644 --- a/scripts/gstack2/types.ts +++ b/scripts/gstack2/types.ts @@ -2,6 +2,7 @@ export const GSTACK2_BASE_SHA = 'bb57306d98c97011b0919c6132705a15b1579781'; export const TREE_NAMES = ['plan', 'design', 'qa', 'debug', 'review', 'ship'] as const; export type TreeName = (typeof TREE_NAMES)[number]; +export type ExecutionProfile = 'readiness' | 'standard' | 'deep'; export type ModuleVisibility = 'primary' | 'internal'; @@ -27,7 +28,7 @@ export interface SourceAssignment { mandatory: boolean; replacement: string; summary: string; - defaultDepth: 'quick' | 'standard' | 'deep'; + defaultDepth: ExecutionProfile; defaultMutation: string; webContext: 'none' | 'optional' | 'local-browser' | 'production'; overlays?: number[]; @@ -39,7 +40,7 @@ export interface DispatcherMode { target: string; modules: string[]; inferWhen: string; - depth: 'quick' | 'standard' | 'deep'; + depth: ExecutionProfile; mutation: string; webContext: 'none' | 'optional' | 'local-browser' | 'production'; } @@ -75,7 +76,7 @@ export interface ScenarioFixture { expected: { tree: TreeName; mode: string; - depth: 'quick' | 'standard' | 'deep'; + depth: ExecutionProfile; mutation: string; active_modules: string[]; skipped_modules: string[]; diff --git a/skills/debug/SKILL.md b/skills/debug/SKILL.md index abdbcff642..7c8cf14db6 100644 --- a/skills/debug/SKILL.md +++ b/skills/debug/SKILL.md @@ -15,7 +15,7 @@ Before any substantive output, print these exact labels in this exact order. Res ```text Target: Mode: -Depth: +Depth: Mutation: Active modules: Skipped modules: @@ -27,7 +27,7 @@ Web context: 1. Infer the mode from product stage, surface, requested artifact, mutation authorization, evidence needs, and deployment state. Do not route by keyword alone. 2. Refine the public mode to the smallest applicable internal specialist set, then print the required execution header before any substantive output. 3. Read each active module in full from the path shown in the mode/alias tables. Its specialist body, behavioral contract, STOP gates, and appended upstream judgment ports are binding. Read a lazy specialist phase in full only when the workflow reaches its package-local reference. -4. Read `references/SHARED-JUDGMENT.md` and `references/AUTHORITY-POLICY.md` for every invocation. Read `references/RUNTIME.md` before capability-dependent work and `references/WEB-CONTEXT.md` before public-web work. +4. Read `references/EXECUTION-PROFILES.md`, `references/SHARED-JUDGMENT.md`, and `references/AUTHORITY-POLICY.md` for every invocation. Infer Depth from structured operating conditions, then obey its mandatory modules, legal skips, artifacts, and claim limits. Read `references/RUNTIME.md` before capability-dependent work and `references/WEB-CONTEXT.md` before public-web work. 5. If an old asset path is unavailable, use `references/ASSETS.md`. If legacy prose invokes another retired skill, resolve it through `references/COMPATIBILITY.md` and stay inside these six dispatchers. 6. Preserve report-only versus mutation boundaries. Missing mutation authorization fails closed: do not edit merely because a specialist can fix. Commits, pushes, PRs, merges, deploys, messages, and other external mutations still require affirmative authority from the user. 7. Match the user's language. Keep code identifiers, commands, and source quotations original when translation would reduce accuracy. diff --git a/skills/debug/references/EXECUTION-PROFILES.md b/skills/debug/references/EXECUTION-PROFILES.md new file mode 100644 index 0000000000..758e32c86d --- /dev/null +++ b/skills/debug/references/EXECUTION-PROFILES.md @@ -0,0 +1,28 @@ + +# Inferred execution profiles + +Choose a profile from product stage, mutation authority, risk/evidence needs, and deployment state. Prompt keywords and a request to “be quick” are not routing evidence. A profile narrows or strengthens evidence; it never overrides a specialist’s binding question order, pressure, gates, mutation boundary, or exit behavior. + +## Smoke/readiness + +- Infer when: A narrow, reversible, pre-deployment or operational readiness decision needs bounded evidence; the requested claim is readiness, not completeness. +- Mandatory modules: Every selected specialist module remains mandatory. Run its entry gates and the smallest source-authorized evidence path that can answer the readiness question. +- Legal skips: Only source-declared conditional work whose condition is demonstrably false, or evidence unavailable after a named attempt. Never skip a STOP gate, approval boundary, reproduction/root-cause gate, or required physical-device/production evidence. +- Artifacts: A readiness record naming the exact scope, probes run, evidence and freshness, failures, skipped work with reasons, and the next standard/deep step. +- Claims: Only ready/not-ready for the named bounded decision. Must say “Readiness profile — not a complete review.” Never claim comprehensive, fully verified, production-safe, or no issues found outside the inspected scope. + +## Standard + +- Infer when: Normal feature or change work has bounded scope and risk and needs the selected specialist’s complete default workflow. +- Mandatory modules: Every selected specialist module and all of its mandatory phases, gates, artifacts, and exit checks. +- Legal skips: Only smart skips explicitly authorized by the specialist and supported by inspected evidence; list each skipped primary module and each skipped conditional phase. +- Artifacts: Every artifact required by the selected specialist, plus evidence provenance, unresolved decisions, and a skip ledger. +- Claims: Complete only for the named specialist scope and evidence layer. Broader product, security, production, or device claims require those modules and evidence. + +## Deep + +- Infer when: Risk, ambiguity, blast radius, cross-system effects, irreversible mutation, security/reliability needs, or production deployment demands stronger evidence. +- Mandatory modules: Every selected specialist module, all mandatory phases and outside-voice/cross-consumer modules selected by the dispatcher, with unchanged STOP and approval gates. +- Legal skips: Only specialist-authorized smart skips proven irrelevant. Missing, stale, malformed, or contradictory evidence is a gap or blocker, never a successful skip. +- Artifacts: All specialist artifacts plus changed-input/unchanged-consumer trace, negative and failure-path evidence, provenance/freshness, rollback or reversibility evidence where applicable, and unresolved-risk ledger. +- Claims: Complete only across the explicitly listed modules and evidence layers. “Confirmed” requires independent supporting evidence; production/device/security claims require matching production/device/security evidence. diff --git a/skills/design/SKILL.md b/skills/design/SKILL.md index 01b06cf8e3..c1c8f83a13 100644 --- a/skills/design/SKILL.md +++ b/skills/design/SKILL.md @@ -15,7 +15,7 @@ Before any substantive output, print these exact labels in this exact order. Res ```text Target: Mode: -Depth: +Depth: Mutation: Active modules: Skipped modules: @@ -27,7 +27,7 @@ Web context: 1. Infer the mode from product stage, surface, requested artifact, mutation authorization, evidence needs, and deployment state. Do not route by keyword alone. 2. Refine the public mode to the smallest applicable internal specialist set, then print the required execution header before any substantive output. 3. Read each active module in full from the path shown in the mode/alias tables. Its specialist body, behavioral contract, STOP gates, and appended upstream judgment ports are binding. Read a lazy specialist phase in full only when the workflow reaches its package-local reference. -4. Read `references/SHARED-JUDGMENT.md` and `references/AUTHORITY-POLICY.md` for every invocation. Read `references/RUNTIME.md` before capability-dependent work and `references/WEB-CONTEXT.md` before public-web work. +4. Read `references/EXECUTION-PROFILES.md`, `references/SHARED-JUDGMENT.md`, and `references/AUTHORITY-POLICY.md` for every invocation. Infer Depth from structured operating conditions, then obey its mandatory modules, legal skips, artifacts, and claim limits. Read `references/RUNTIME.md` before capability-dependent work and `references/WEB-CONTEXT.md` before public-web work. 5. If an old asset path is unavailable, use `references/ASSETS.md`. If legacy prose invokes another retired skill, resolve it through `references/COMPATIBILITY.md` and stay inside these six dispatchers. 6. Preserve report-only versus mutation boundaries. Missing mutation authorization fails closed: do not edit merely because a specialist can fix. Commits, pushes, PRs, merges, deploys, messages, and other external mutations still require affirmative authority from the user. 7. Match the user's language. Keep code identifiers, commands, and source quotations original when translation would reduce accuracy. diff --git a/skills/design/references/EXECUTION-PROFILES.md b/skills/design/references/EXECUTION-PROFILES.md new file mode 100644 index 0000000000..758e32c86d --- /dev/null +++ b/skills/design/references/EXECUTION-PROFILES.md @@ -0,0 +1,28 @@ + +# Inferred execution profiles + +Choose a profile from product stage, mutation authority, risk/evidence needs, and deployment state. Prompt keywords and a request to “be quick” are not routing evidence. A profile narrows or strengthens evidence; it never overrides a specialist’s binding question order, pressure, gates, mutation boundary, or exit behavior. + +## Smoke/readiness + +- Infer when: A narrow, reversible, pre-deployment or operational readiness decision needs bounded evidence; the requested claim is readiness, not completeness. +- Mandatory modules: Every selected specialist module remains mandatory. Run its entry gates and the smallest source-authorized evidence path that can answer the readiness question. +- Legal skips: Only source-declared conditional work whose condition is demonstrably false, or evidence unavailable after a named attempt. Never skip a STOP gate, approval boundary, reproduction/root-cause gate, or required physical-device/production evidence. +- Artifacts: A readiness record naming the exact scope, probes run, evidence and freshness, failures, skipped work with reasons, and the next standard/deep step. +- Claims: Only ready/not-ready for the named bounded decision. Must say “Readiness profile — not a complete review.” Never claim comprehensive, fully verified, production-safe, or no issues found outside the inspected scope. + +## Standard + +- Infer when: Normal feature or change work has bounded scope and risk and needs the selected specialist’s complete default workflow. +- Mandatory modules: Every selected specialist module and all of its mandatory phases, gates, artifacts, and exit checks. +- Legal skips: Only smart skips explicitly authorized by the specialist and supported by inspected evidence; list each skipped primary module and each skipped conditional phase. +- Artifacts: Every artifact required by the selected specialist, plus evidence provenance, unresolved decisions, and a skip ledger. +- Claims: Complete only for the named specialist scope and evidence layer. Broader product, security, production, or device claims require those modules and evidence. + +## Deep + +- Infer when: Risk, ambiguity, blast radius, cross-system effects, irreversible mutation, security/reliability needs, or production deployment demands stronger evidence. +- Mandatory modules: Every selected specialist module, all mandatory phases and outside-voice/cross-consumer modules selected by the dispatcher, with unchanged STOP and approval gates. +- Legal skips: Only specialist-authorized smart skips proven irrelevant. Missing, stale, malformed, or contradictory evidence is a gap or blocker, never a successful skip. +- Artifacts: All specialist artifacts plus changed-input/unchanged-consumer trace, negative and failure-path evidence, provenance/freshness, rollback or reversibility evidence where applicable, and unresolved-risk ledger. +- Claims: Complete only across the explicitly listed modules and evidence layers. “Confirmed” requires independent supporting evidence; production/device/security claims require matching production/device/security evidence. diff --git a/skills/plan/SKILL.md b/skills/plan/SKILL.md index f9913003ce..8c06285b8c 100644 --- a/skills/plan/SKILL.md +++ b/skills/plan/SKILL.md @@ -15,7 +15,7 @@ Before any substantive output, print these exact labels in this exact order. Res ```text Target: Mode: -Depth: +Depth: Mutation: Active modules: Skipped modules: @@ -27,7 +27,7 @@ Web context: 1. Infer the mode from product stage, surface, requested artifact, mutation authorization, evidence needs, and deployment state. Do not route by keyword alone. 2. Refine the public mode to the smallest applicable internal specialist set, then print the required execution header before any substantive output. 3. Read each active module in full from the path shown in the mode/alias tables. Its specialist body, behavioral contract, STOP gates, and appended upstream judgment ports are binding. Read a lazy specialist phase in full only when the workflow reaches its package-local reference. -4. Read `references/SHARED-JUDGMENT.md` and `references/AUTHORITY-POLICY.md` for every invocation. Read `references/RUNTIME.md` before capability-dependent work and `references/WEB-CONTEXT.md` before public-web work. +4. Read `references/EXECUTION-PROFILES.md`, `references/SHARED-JUDGMENT.md`, and `references/AUTHORITY-POLICY.md` for every invocation. Infer Depth from structured operating conditions, then obey its mandatory modules, legal skips, artifacts, and claim limits. Read `references/RUNTIME.md` before capability-dependent work and `references/WEB-CONTEXT.md` before public-web work. 5. If an old asset path is unavailable, use `references/ASSETS.md`. If legacy prose invokes another retired skill, resolve it through `references/COMPATIBILITY.md` and stay inside these six dispatchers. 6. Preserve report-only versus mutation boundaries. Missing mutation authorization fails closed: do not edit merely because a specialist can fix. Commits, pushes, PRs, merges, deploys, messages, and other external mutations still require affirmative authority from the user. 7. Match the user's language. Keep code identifiers, commands, and source quotations original when translation would reduce accuracy. diff --git a/skills/plan/references/EXECUTION-PROFILES.md b/skills/plan/references/EXECUTION-PROFILES.md new file mode 100644 index 0000000000..758e32c86d --- /dev/null +++ b/skills/plan/references/EXECUTION-PROFILES.md @@ -0,0 +1,28 @@ + +# Inferred execution profiles + +Choose a profile from product stage, mutation authority, risk/evidence needs, and deployment state. Prompt keywords and a request to “be quick” are not routing evidence. A profile narrows or strengthens evidence; it never overrides a specialist’s binding question order, pressure, gates, mutation boundary, or exit behavior. + +## Smoke/readiness + +- Infer when: A narrow, reversible, pre-deployment or operational readiness decision needs bounded evidence; the requested claim is readiness, not completeness. +- Mandatory modules: Every selected specialist module remains mandatory. Run its entry gates and the smallest source-authorized evidence path that can answer the readiness question. +- Legal skips: Only source-declared conditional work whose condition is demonstrably false, or evidence unavailable after a named attempt. Never skip a STOP gate, approval boundary, reproduction/root-cause gate, or required physical-device/production evidence. +- Artifacts: A readiness record naming the exact scope, probes run, evidence and freshness, failures, skipped work with reasons, and the next standard/deep step. +- Claims: Only ready/not-ready for the named bounded decision. Must say “Readiness profile — not a complete review.” Never claim comprehensive, fully verified, production-safe, or no issues found outside the inspected scope. + +## Standard + +- Infer when: Normal feature or change work has bounded scope and risk and needs the selected specialist’s complete default workflow. +- Mandatory modules: Every selected specialist module and all of its mandatory phases, gates, artifacts, and exit checks. +- Legal skips: Only smart skips explicitly authorized by the specialist and supported by inspected evidence; list each skipped primary module and each skipped conditional phase. +- Artifacts: Every artifact required by the selected specialist, plus evidence provenance, unresolved decisions, and a skip ledger. +- Claims: Complete only for the named specialist scope and evidence layer. Broader product, security, production, or device claims require those modules and evidence. + +## Deep + +- Infer when: Risk, ambiguity, blast radius, cross-system effects, irreversible mutation, security/reliability needs, or production deployment demands stronger evidence. +- Mandatory modules: Every selected specialist module, all mandatory phases and outside-voice/cross-consumer modules selected by the dispatcher, with unchanged STOP and approval gates. +- Legal skips: Only specialist-authorized smart skips proven irrelevant. Missing, stale, malformed, or contradictory evidence is a gap or blocker, never a successful skip. +- Artifacts: All specialist artifacts plus changed-input/unchanged-consumer trace, negative and failure-path evidence, provenance/freshness, rollback or reversibility evidence where applicable, and unresolved-risk ledger. +- Claims: Complete only across the explicitly listed modules and evidence layers. “Confirmed” requires independent supporting evidence; production/device/security claims require matching production/device/security evidence. diff --git a/skills/qa/SKILL.md b/skills/qa/SKILL.md index a3dd07f4a1..a51f977467 100644 --- a/skills/qa/SKILL.md +++ b/skills/qa/SKILL.md @@ -15,7 +15,7 @@ Before any substantive output, print these exact labels in this exact order. Res ```text Target: Mode: -Depth: +Depth: Mutation: Active modules: Skipped modules: @@ -27,7 +27,7 @@ Web context: 1. Infer the mode from product stage, surface, requested artifact, mutation authorization, evidence needs, and deployment state. Do not route by keyword alone. 2. Refine the public mode to the smallest applicable internal specialist set, then print the required execution header before any substantive output. 3. Read each active module in full from the path shown in the mode/alias tables. Its specialist body, behavioral contract, STOP gates, and appended upstream judgment ports are binding. Read a lazy specialist phase in full only when the workflow reaches its package-local reference. -4. Read `references/SHARED-JUDGMENT.md` and `references/AUTHORITY-POLICY.md` for every invocation. Read `references/RUNTIME.md` before capability-dependent work and `references/WEB-CONTEXT.md` before public-web work. +4. Read `references/EXECUTION-PROFILES.md`, `references/SHARED-JUDGMENT.md`, and `references/AUTHORITY-POLICY.md` for every invocation. Infer Depth from structured operating conditions, then obey its mandatory modules, legal skips, artifacts, and claim limits. Read `references/RUNTIME.md` before capability-dependent work and `references/WEB-CONTEXT.md` before public-web work. 5. If an old asset path is unavailable, use `references/ASSETS.md`. If legacy prose invokes another retired skill, resolve it through `references/COMPATIBILITY.md` and stay inside these six dispatchers. 6. Preserve report-only versus mutation boundaries. Missing mutation authorization fails closed: do not edit merely because a specialist can fix. Commits, pushes, PRs, merges, deploys, messages, and other external mutations still require affirmative authority from the user. 7. Match the user's language. Keep code identifiers, commands, and source quotations original when translation would reduce accuracy. diff --git a/skills/qa/references/EXECUTION-PROFILES.md b/skills/qa/references/EXECUTION-PROFILES.md new file mode 100644 index 0000000000..758e32c86d --- /dev/null +++ b/skills/qa/references/EXECUTION-PROFILES.md @@ -0,0 +1,28 @@ + +# Inferred execution profiles + +Choose a profile from product stage, mutation authority, risk/evidence needs, and deployment state. Prompt keywords and a request to “be quick” are not routing evidence. A profile narrows or strengthens evidence; it never overrides a specialist’s binding question order, pressure, gates, mutation boundary, or exit behavior. + +## Smoke/readiness + +- Infer when: A narrow, reversible, pre-deployment or operational readiness decision needs bounded evidence; the requested claim is readiness, not completeness. +- Mandatory modules: Every selected specialist module remains mandatory. Run its entry gates and the smallest source-authorized evidence path that can answer the readiness question. +- Legal skips: Only source-declared conditional work whose condition is demonstrably false, or evidence unavailable after a named attempt. Never skip a STOP gate, approval boundary, reproduction/root-cause gate, or required physical-device/production evidence. +- Artifacts: A readiness record naming the exact scope, probes run, evidence and freshness, failures, skipped work with reasons, and the next standard/deep step. +- Claims: Only ready/not-ready for the named bounded decision. Must say “Readiness profile — not a complete review.” Never claim comprehensive, fully verified, production-safe, or no issues found outside the inspected scope. + +## Standard + +- Infer when: Normal feature or change work has bounded scope and risk and needs the selected specialist’s complete default workflow. +- Mandatory modules: Every selected specialist module and all of its mandatory phases, gates, artifacts, and exit checks. +- Legal skips: Only smart skips explicitly authorized by the specialist and supported by inspected evidence; list each skipped primary module and each skipped conditional phase. +- Artifacts: Every artifact required by the selected specialist, plus evidence provenance, unresolved decisions, and a skip ledger. +- Claims: Complete only for the named specialist scope and evidence layer. Broader product, security, production, or device claims require those modules and evidence. + +## Deep + +- Infer when: Risk, ambiguity, blast radius, cross-system effects, irreversible mutation, security/reliability needs, or production deployment demands stronger evidence. +- Mandatory modules: Every selected specialist module, all mandatory phases and outside-voice/cross-consumer modules selected by the dispatcher, with unchanged STOP and approval gates. +- Legal skips: Only specialist-authorized smart skips proven irrelevant. Missing, stale, malformed, or contradictory evidence is a gap or blocker, never a successful skip. +- Artifacts: All specialist artifacts plus changed-input/unchanged-consumer trace, negative and failure-path evidence, provenance/freshness, rollback or reversibility evidence where applicable, and unresolved-risk ledger. +- Claims: Complete only across the explicitly listed modules and evidence layers. “Confirmed” requires independent supporting evidence; production/device/security claims require matching production/device/security evidence. diff --git a/skills/review/SKILL.md b/skills/review/SKILL.md index fce9c7c3be..1f511d469b 100644 --- a/skills/review/SKILL.md +++ b/skills/review/SKILL.md @@ -15,7 +15,7 @@ Before any substantive output, print these exact labels in this exact order. Res ```text Target: Mode: -Depth: +Depth: Mutation: Active modules: Skipped modules: @@ -27,7 +27,7 @@ Web context: 1. Infer the mode from product stage, surface, requested artifact, mutation authorization, evidence needs, and deployment state. Do not route by keyword alone. 2. Refine the public mode to the smallest applicable internal specialist set, then print the required execution header before any substantive output. 3. Read each active module in full from the path shown in the mode/alias tables. Its specialist body, behavioral contract, STOP gates, and appended upstream judgment ports are binding. Read a lazy specialist phase in full only when the workflow reaches its package-local reference. -4. Read `references/SHARED-JUDGMENT.md` and `references/AUTHORITY-POLICY.md` for every invocation. Read `references/RUNTIME.md` before capability-dependent work and `references/WEB-CONTEXT.md` before public-web work. +4. Read `references/EXECUTION-PROFILES.md`, `references/SHARED-JUDGMENT.md`, and `references/AUTHORITY-POLICY.md` for every invocation. Infer Depth from structured operating conditions, then obey its mandatory modules, legal skips, artifacts, and claim limits. Read `references/RUNTIME.md` before capability-dependent work and `references/WEB-CONTEXT.md` before public-web work. 5. If an old asset path is unavailable, use `references/ASSETS.md`. If legacy prose invokes another retired skill, resolve it through `references/COMPATIBILITY.md` and stay inside these six dispatchers. 6. Preserve report-only versus mutation boundaries. Missing mutation authorization fails closed: do not edit merely because a specialist can fix. Commits, pushes, PRs, merges, deploys, messages, and other external mutations still require affirmative authority from the user. 7. Match the user's language. Keep code identifiers, commands, and source quotations original when translation would reduce accuracy. diff --git a/skills/review/references/EXECUTION-PROFILES.md b/skills/review/references/EXECUTION-PROFILES.md new file mode 100644 index 0000000000..758e32c86d --- /dev/null +++ b/skills/review/references/EXECUTION-PROFILES.md @@ -0,0 +1,28 @@ + +# Inferred execution profiles + +Choose a profile from product stage, mutation authority, risk/evidence needs, and deployment state. Prompt keywords and a request to “be quick” are not routing evidence. A profile narrows or strengthens evidence; it never overrides a specialist’s binding question order, pressure, gates, mutation boundary, or exit behavior. + +## Smoke/readiness + +- Infer when: A narrow, reversible, pre-deployment or operational readiness decision needs bounded evidence; the requested claim is readiness, not completeness. +- Mandatory modules: Every selected specialist module remains mandatory. Run its entry gates and the smallest source-authorized evidence path that can answer the readiness question. +- Legal skips: Only source-declared conditional work whose condition is demonstrably false, or evidence unavailable after a named attempt. Never skip a STOP gate, approval boundary, reproduction/root-cause gate, or required physical-device/production evidence. +- Artifacts: A readiness record naming the exact scope, probes run, evidence and freshness, failures, skipped work with reasons, and the next standard/deep step. +- Claims: Only ready/not-ready for the named bounded decision. Must say “Readiness profile — not a complete review.” Never claim comprehensive, fully verified, production-safe, or no issues found outside the inspected scope. + +## Standard + +- Infer when: Normal feature or change work has bounded scope and risk and needs the selected specialist’s complete default workflow. +- Mandatory modules: Every selected specialist module and all of its mandatory phases, gates, artifacts, and exit checks. +- Legal skips: Only smart skips explicitly authorized by the specialist and supported by inspected evidence; list each skipped primary module and each skipped conditional phase. +- Artifacts: Every artifact required by the selected specialist, plus evidence provenance, unresolved decisions, and a skip ledger. +- Claims: Complete only for the named specialist scope and evidence layer. Broader product, security, production, or device claims require those modules and evidence. + +## Deep + +- Infer when: Risk, ambiguity, blast radius, cross-system effects, irreversible mutation, security/reliability needs, or production deployment demands stronger evidence. +- Mandatory modules: Every selected specialist module, all mandatory phases and outside-voice/cross-consumer modules selected by the dispatcher, with unchanged STOP and approval gates. +- Legal skips: Only specialist-authorized smart skips proven irrelevant. Missing, stale, malformed, or contradictory evidence is a gap or blocker, never a successful skip. +- Artifacts: All specialist artifacts plus changed-input/unchanged-consumer trace, negative and failure-path evidence, provenance/freshness, rollback or reversibility evidence where applicable, and unresolved-risk ledger. +- Claims: Complete only across the explicitly listed modules and evidence layers. “Confirmed” requires independent supporting evidence; production/device/security claims require matching production/device/security evidence. diff --git a/skills/ship/SKILL.md b/skills/ship/SKILL.md index 152cdc2114..599c120f0d 100644 --- a/skills/ship/SKILL.md +++ b/skills/ship/SKILL.md @@ -15,7 +15,7 @@ Before any substantive output, print these exact labels in this exact order. Res ```text Target: Mode: -Depth: +Depth: Mutation: Active modules: Skipped modules: @@ -27,7 +27,7 @@ Web context: 1. Infer the mode from product stage, surface, requested artifact, mutation authorization, evidence needs, and deployment state. Do not route by keyword alone. 2. Refine the public mode to the smallest applicable internal specialist set, then print the required execution header before any substantive output. 3. Read each active module in full from the path shown in the mode/alias tables. Its specialist body, behavioral contract, STOP gates, and appended upstream judgment ports are binding. Read a lazy specialist phase in full only when the workflow reaches its package-local reference. -4. Read `references/SHARED-JUDGMENT.md` and `references/AUTHORITY-POLICY.md` for every invocation. Read `references/RUNTIME.md` before capability-dependent work and `references/WEB-CONTEXT.md` before public-web work. +4. Read `references/EXECUTION-PROFILES.md`, `references/SHARED-JUDGMENT.md`, and `references/AUTHORITY-POLICY.md` for every invocation. Infer Depth from structured operating conditions, then obey its mandatory modules, legal skips, artifacts, and claim limits. Read `references/RUNTIME.md` before capability-dependent work and `references/WEB-CONTEXT.md` before public-web work. 5. If an old asset path is unavailable, use `references/ASSETS.md`. If legacy prose invokes another retired skill, resolve it through `references/COMPATIBILITY.md` and stay inside these six dispatchers. 6. Preserve report-only versus mutation boundaries. Missing mutation authorization fails closed: do not edit merely because a specialist can fix. Commits, pushes, PRs, merges, deploys, messages, and other external mutations still require affirmative authority from the user. 7. Match the user's language. Keep code identifiers, commands, and source quotations original when translation would reduce accuracy. diff --git a/skills/ship/references/EXECUTION-PROFILES.md b/skills/ship/references/EXECUTION-PROFILES.md new file mode 100644 index 0000000000..758e32c86d --- /dev/null +++ b/skills/ship/references/EXECUTION-PROFILES.md @@ -0,0 +1,28 @@ + +# Inferred execution profiles + +Choose a profile from product stage, mutation authority, risk/evidence needs, and deployment state. Prompt keywords and a request to “be quick” are not routing evidence. A profile narrows or strengthens evidence; it never overrides a specialist’s binding question order, pressure, gates, mutation boundary, or exit behavior. + +## Smoke/readiness + +- Infer when: A narrow, reversible, pre-deployment or operational readiness decision needs bounded evidence; the requested claim is readiness, not completeness. +- Mandatory modules: Every selected specialist module remains mandatory. Run its entry gates and the smallest source-authorized evidence path that can answer the readiness question. +- Legal skips: Only source-declared conditional work whose condition is demonstrably false, or evidence unavailable after a named attempt. Never skip a STOP gate, approval boundary, reproduction/root-cause gate, or required physical-device/production evidence. +- Artifacts: A readiness record naming the exact scope, probes run, evidence and freshness, failures, skipped work with reasons, and the next standard/deep step. +- Claims: Only ready/not-ready for the named bounded decision. Must say “Readiness profile — not a complete review.” Never claim comprehensive, fully verified, production-safe, or no issues found outside the inspected scope. + +## Standard + +- Infer when: Normal feature or change work has bounded scope and risk and needs the selected specialist’s complete default workflow. +- Mandatory modules: Every selected specialist module and all of its mandatory phases, gates, artifacts, and exit checks. +- Legal skips: Only smart skips explicitly authorized by the specialist and supported by inspected evidence; list each skipped primary module and each skipped conditional phase. +- Artifacts: Every artifact required by the selected specialist, plus evidence provenance, unresolved decisions, and a skip ledger. +- Claims: Complete only for the named specialist scope and evidence layer. Broader product, security, production, or device claims require those modules and evidence. + +## Deep + +- Infer when: Risk, ambiguity, blast radius, cross-system effects, irreversible mutation, security/reliability needs, or production deployment demands stronger evidence. +- Mandatory modules: Every selected specialist module, all mandatory phases and outside-voice/cross-consumer modules selected by the dispatcher, with unchanged STOP and approval gates. +- Legal skips: Only specialist-authorized smart skips proven irrelevant. Missing, stale, malformed, or contradictory evidence is a gap or blocker, never a successful skip. +- Artifacts: All specialist artifacts plus changed-input/unchanged-consumer trace, negative and failure-path evidence, provenance/freshness, rollback or reversibility evidence where applicable, and unresolved-risk ledger. +- Claims: Complete only across the explicitly listed modules and evidence layers. “Confirmed” requires independent supporting evidence; production/device/security claims require matching production/device/security evidence. diff --git a/test/gstack2-skill-ux.test.ts b/test/gstack2-skill-ux.test.ts index 8f7424a55f..27412887ec 100644 --- a/test/gstack2-skill-ux.test.ts +++ b/test/gstack2-skill-ux.test.ts @@ -45,6 +45,21 @@ describe('GStack 2 canonical skill UX', () => { } }); + test('packages one binding inferred execution-profile contract in every dispatcher', () => { + for (const tree of TREE_NAMES) { + const dispatcher = fs.readFileSync(path.join(ROOT, 'skills', tree, 'SKILL.md'), 'utf8'); + const profiles = fs.readFileSync(path.join(ROOT, 'skills', tree, 'references', 'EXECUTION-PROFILES.md'), 'utf8'); + expect(dispatcher, tree).toContain('Depth: '); + expect(dispatcher, tree).toContain('Read `references/EXECUTION-PROFILES.md`'); + expect(profiles, tree).toContain('## Smoke/readiness'); + expect(profiles, tree).toContain('Readiness profile — not a complete review.'); + expect(profiles, tree).toContain('Every selected specialist module remains mandatory.'); + expect(profiles, tree).toContain('## Standard'); + expect(profiles, tree).toContain('## Deep'); + expect(profiles, tree).toContain('never overrides a specialist’s binding question order'); + } + }); + test('resolves retired user-facing recommendations without rewriting package paths', () => { for (const assignment of SOURCE_ASSIGNMENTS) { const body = fs.readFileSync(ownerModule(assignment.source), 'utf8'); diff --git a/test/gstack2-skills-routing.test.ts b/test/gstack2-skills-routing.test.ts index bda2f4ce15..44bb85abb9 100644 --- a/test/gstack2-skills-routing.test.ts +++ b/test/gstack2-skills-routing.test.ts @@ -24,6 +24,41 @@ describe('GStack 2 structured dispatch', () => { expect(routeStructured({ ...scenario.signals })).toEqual(original); }); + test('infers readiness only from bounded structured operating conditions', () => { + const readiness = routeStructured({ + surface: 'web', + implementation_exists: true, + evidence_need: 'readiness', + scope: 'narrow', + deployment_state: 'pre-deployment', + }); + expect(readiness.depth).toBe('readiness'); + expect(readiness.active_modules).toEqual(['qa-only']); + + expect(routeStructured({ + surface: 'web', + implementation_exists: true, + evidence_need: 'readiness', + scope: 'broad', + }).depth).not.toBe('readiness'); + }); + + test('risk and deployment evidence promote work to deep', () => { + expect(routeStructured({ surface: 'web', implementation_exists: true, risk: 'high' }).depth) + .toBe('deep'); + expect(routeStructured({ surface: 'web', implementation_exists: true, deployment_state: 'production' }).depth) + .toBe('deep'); + expect(routeStructured({ surface: 'web', implementation_exists: true, mutation_scope: 'consequential' }).depth) + .toBe('deep'); + expect(routeStructured({ + surface: 'web', + implementation_exists: true, + evidence_need: 'readiness', + scope: 'narrow', + irreversible: true, + }).depth).toBe('deep'); + }); + test('explicit mutation denials override otherwise mutating modes', () => { const review = routeStructured({ audit_focus: 'broad', mutation_authorized: false }); expect(review.mode).toBe('Normal'); From e6f0018b7393a9439107308389ec4607807292c6 Mon Sep 17 00:00:00 2001 From: Sinabina Date: Tue, 21 Jul 2026 13:59:29 -0700 Subject: [PATCH 4/4] fix(gstack2): treat hard runtime failure as not-ready in capability readiness A capability whose binary launches while the managed runtime hard-fails (e.g. skill-API mismatch) was reported as `degraded`/ok:true/exit 0, diverging from plain `gstack doctor` (ok:false/exit 1) for the identical report. Split the branch so runtime `warn` stays `degraded` and runtime `fail` maps to `failed`, and add a regression test for the runtime-fail + capability-pass case. Co-Authored-By: Claude Opus 4.8 (1M context) --- runtime/doctor.js | 3 ++- test/gstack2-capability-readiness.test.ts | 7 +++++++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/runtime/doctor.js b/runtime/doctor.js index 70fcff94b2..21ab58c985 100644 --- a/runtime/doctor.js +++ b/runtime/doctor.js @@ -247,7 +247,8 @@ export function capabilityReadiness(report, capability, options = {}) { const runtimeCheck = report.checks.find((check) => check.id === "managed-runtime"); let status; if (capabilityCheck?.status === "pass" && runtimeCheck?.status === "pass") status = "ready"; - else if (capabilityCheck?.status === "pass") status = "degraded"; + else if (capabilityCheck?.status === "pass" && runtimeCheck?.status === "warn") status = "degraded"; + else if (capabilityCheck?.status === "pass") status = "failed"; else if (capabilityCheck?.status === "fail") status = "failed"; else status = "unavailable"; diff --git a/test/gstack2-capability-readiness.test.ts b/test/gstack2-capability-readiness.test.ts index 9691adfc6b..d6787e2464 100644 --- a/test/gstack2-capability-readiness.test.ts +++ b/test/gstack2-capability-readiness.test.ts @@ -52,11 +52,18 @@ describe("capability readiness", () => { { id: "managed-runtime", status: "pass", message: "active" }, { id: "capability:diagram", status: "fail", message: "launcher metadata missing" }, ]), "diagram"); + // A hard runtime failure under a launchable capability is not a warning: + // it must not report ok:true, matching plain `gstack doctor`'s exit code. + const runtimeFailed = capabilityReadiness(report([ + { id: "managed-runtime", status: "fail", message: "incompatible skill API" }, + { id: "capability:browser", status: "pass", message: "launched" }, + ]), "browser"); expect(ready).toMatchObject({ ok: true, readiness: { status: "ready" } }); expect(degraded).toMatchObject({ ok: true, readiness: { status: "degraded" } }); expect(failed).toMatchObject({ ok: false, readiness: { status: "failed" } }); expect(failed.consent.install.status).toBe("required-after-preview"); + expect(runtimeFailed).toMatchObject({ ok: false, readiness: { status: "failed" } }); }); test("reports physical iOS as unsupported without turning off pure judgment", () => {