Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 36 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,42 @@
> completion state and remaining P0 gates. No version bump or release claim is
> made here while that status holds.

## [1.63.0.0] - 2026-07-23

## **"Go register an API key" is no longer your homework.**
## **gstack asks once, drives the browser, and proves the key works.**

Every gstack skill now recognizes the moment a plan or workflow needs something done on a third-party website: registering an API key, creating a vendor account, configuring a dashboard or OAuth app. Instead of dumping a manual step list, the skill checks for an agentic browser that can work across your logged-in accounts (Aside today), names the exact site and exact actions, and asks one question: drive it now, give me the steps, or defer. Say yes and the agent does the browsing while the parts that should stay human stay human. Passwords, payment, identity checks, and CAPTCHAs are handed to you, never faked. Captured keys go straight to an owner-only file or your secret store, never into chat output or logs. And before the skill claims success, it proves the credential works with one read-only API call.

That last rule exists because dashboards lie. The token field on a real vendor dashboard held a masked placeholder, the real value only existed behind the Copy button, and the verify call caught it with a 401 before it could land in a .env file as garbage.

### The numbers that matter

Source: `bun run scripts/gstack2/run-parity.ts` on this release, plus two live vendor flows (Duffel, OpenSky Network) run end to end through the contract before shipping.

| Metric | Before | After |
|---|---|---|
| Dispatchers that offer agentic-browser help for third-party web actions | 0 | 6 of 6 |
| Consent asked per task, never persisted or inferred | n/a | always |
| Pinned parity checks | 5,050 | 5,074 |

The +24 checks mean nobody can quietly strip the consent gate or the secret-handling rules; the suite fails if the contract loses either.

### What this means for builders

The gap between "the plan says you need an OpenSky key" and "the key is in your .env and verified" used to be your browser tab, your copy-paste, and your typo. Now it is one question. Answer it and keep building.

### Itemized changes

### Added

- `references/THIRD-PARTY-ACTIONS.md` generated into all six skill trees: agentic-browser detection (Aside via the existing browser-provider registry), a mandatory per-task consent question naming the exact site and actions, user-performed credential steps, no-echo secret capture, and a non-mutating verification call before any success claim.
- Dispatch protocol step directing every dispatcher to read the contract before telling the user to act on a third-party website.

### For contributors

- New `thirdPartyActionsContract()` in `scripts/gstack2/generate-skill-tree.ts`; 4 pinned checks per tree in `run-parity.ts` (5,050 to 5,074).

## [1.62.0.0] - 2026-07-23

## **Open a large repo and gstack offers to index it.**
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.62.0.0
1.63.0.0
2 changes: 1 addition & 1 deletion docs/gstack-2/JUDGMENT-PARITY.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 9 additions & 9 deletions evals/parity/transcripts/policy-units.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "gstack",
"version": "1.62.0.0",
"version": "1.63.0.0",
"description": "GStack 2 — six portable Agent Skills with an optional host-neutral runtime.",
"license": "MIT",
"type": "module",
Expand Down
22 changes: 21 additions & 1 deletion scripts/gstack2/generate-skill-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ Web context: <none, optional, local-browser, or production>
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/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. When the target is a repository, read \`references/CODE-INTELLIGENCE.md\` once before substantive specialist work and follow its one-time indexing offer.
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. When the target is a repository, read \`references/CODE-INTELLIGENCE.md\` once before substantive specialist work and follow its one-time indexing offer. Read \`references/THIRD-PARTY-ACTIONS.md\` before directing the user to act on a third-party website (API key registration, vendor accounts, dashboards).
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.
Expand Down Expand Up @@ -513,6 +513,25 @@ function webContextContract(): string {
].join('\n');
}

function thirdPartyActionsContract(): string {
return [GENERATED,
'# Third-party web actions',
'',
'A specialist step sometimes requires action on an external website the user controls: registering an API key, creating a vendor or developer account, configuring a dashboard, webhook, OAuth app, billing plan, or domain verification. This contract governs that moment. It grants no browsing authority; `SHARED-JUDGMENT.md` and `AUTHORITY-POLICY.md` remain binding, including approval before spending.',
'',
'1. Never hand the user a manual step list for a third-party site without first checking for an agentic browser that can act across their logged-in accounts. Today that is the Aside AI browser (its section in `references/BROWSER-PROVIDERS.md` owns detection and readiness); confirm the `aside` CLI is callable, for example `aside --version`. Never install it, and never treat binary presence as consent to browse.',
'',
'2. If such a browser is available, STOP and ask one explicit question before any browsing: name the exact site and the exact actions (for example "create a test-mode API token in the Duffel dashboard"), then offer A) drive it now through the agentic browser, B) manual instructions, C) defer. The selection is per-task consent; never persist it as standing permission and never infer it from an earlier task.',
'',
'3. When driving, touch only the named site and actions. Password entry, new-account credential choice, payment, and identity verification remain user-performed steps: hand off and wait instead of acting. Prefer credential flows that never expose the secret to the agent, such as password-manager autofill or host-native copy.',
'',
'4. A captured secret (API key, token, webhook signing secret) never appears in chat output, logs, or shell history. Write it to a user-approved local file with owner-only permissions or the user\'s secret store, and keep generated-file destinations out of version control. Dashboard fields are often masked placeholders; verify the captured credential with one non-mutating API call before claiming success.',

@cubic-dev-ai cubic-dev-ai Bot Jul 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Webhook-signing-secret setup can never satisfy this success gate because that secret is not an API credential and cannot be validated by an authenticated read-only API call. Keep the API probe for API credentials and specify an appropriate non-exposing verification path for other secret types.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/gstack2/generate-skill-tree.ts, line 528:

<comment>Webhook-signing-secret setup can never satisfy this success gate because that secret is not an API credential and cannot be validated by an authenticated read-only API call. Keep the API probe for API credentials and specify an appropriate non-exposing verification path for other secret types.</comment>

<file context>
@@ -513,6 +513,25 @@ function webContextContract(): string {
+    '',
+    '3. When driving, touch only the named site and actions. Password entry, new-account credential choice, payment, and identity verification remain user-performed steps: hand off and wait instead of acting. Prefer credential flows that never expose the secret to the agent, such as password-manager autofill or host-native copy.',
+    '',
+    '4. A captured secret (API key, token, webhook signing secret) never appears in chat output, logs, or shell history. Write it to a user-approved local file with owner-only permissions or the user\'s secret store, and keep generated-file destinations out of version control. Dashboard fields are often masked placeholders; verify the captured credential with one non-mutating API call before claiming success.',
+    '',
+    '5. If no agentic browser is available, or the user declines or defers, provide the manual steps and mark the step blocked on the user. Do not recommend or install new products to close the gap.',
</file context>
Suggested change
'4. A captured secret (API key, token, webhook signing secret) never appears in chat output, logs, or shell history. Write it to a user-approved local file with owner-only permissions or the user\'s secret store, and keep generated-file destinations out of version control. Dashboard fields are often masked placeholders; verify the captured credential with one non-mutating API call before claiming success.',
'4. A captured secret (API key, token, webhook signing secret) never appears in chat output, logs, or shell history. Write it to a user-approved local file with owner-only permissions or the user\'s secret store, and keep generated-file destinations out of version control. Dashboard fields are often masked placeholders; verify an API credential with one non-mutating API call before claiming success. For secrets that do not authenticate API calls, use the service\'s appropriate non-exposing verification flow and report exactly what was verified.',
Fix with cubic

'',
'5. If no agentic browser is available, or the user declines or defers, provide the manual steps and mark the step blocked on the user. Do not recommend or install new products to close the gap.',

@cubic-dev-ai cubic-dev-ai Bot Jul 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: The defer choice does not actually defer the workflow because it still emits the manual step list, making options B and C behaviorally identical. A deferred task should be marked blocked and stop without providing the instructions.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/gstack2/generate-skill-tree.ts, line 530:

<comment>The defer choice does not actually defer the workflow because it still emits the manual step list, making options B and C behaviorally identical. A deferred task should be marked blocked and stop without providing the instructions.</comment>

<file context>
@@ -513,6 +513,25 @@ function webContextContract(): string {
+    '',
+    '4. A captured secret (API key, token, webhook signing secret) never appears in chat output, logs, or shell history. Write it to a user-approved local file with owner-only permissions or the user\'s secret store, and keep generated-file destinations out of version control. Dashboard fields are often masked placeholders; verify the captured credential with one non-mutating API call before claiming success.',
+    '',
+    '5. If no agentic browser is available, or the user declines or defers, provide the manual steps and mark the step blocked on the user. Do not recommend or install new products to close the gap.',
+    '',
+  ].join('\n');
</file context>
Suggested change
'5. If no agentic browser is available, or the user declines or defers, provide the manual steps and mark the step blocked on the user. Do not recommend or install new products to close the gap.',
'5. If no agentic browser is available or the user selects manual instructions, provide the manual steps and mark the step blocked on the user. If the user defers, mark the step blocked and stop without providing the manual list. Do not recommend or install new products to close the gap.',
Fix with cubic

'',
].join('\n');
}

function codeIntelligenceContract(): string {
return [GENERATED,
'# Optional code-intelligence indexing',
Expand Down Expand Up @@ -621,6 +640,7 @@ function writeSharedContracts(): void {
write(path.join(ROOT, 'skills', tree, 'references', 'AUTHORITY-POLICY.md'), authorityPolicyContract());
write(path.join(ROOT, 'skills', tree, 'references', 'WEB-CONTEXT.md'), webContextContract());
write(path.join(ROOT, 'skills', tree, 'references', 'CODE-INTELLIGENCE.md'), codeIntelligenceContract());
write(path.join(ROOT, 'skills', tree, 'references', 'THIRD-PARTY-ACTIONS.md'), thirdPartyActionsContract());
write(path.join(ROOT, 'skills', tree, 'references', 'RUNTIME.md'), runtimeContract());
write(path.join(ROOT, 'skills', tree, 'references', 'BROWSER-PROVIDERS.md'), `${GENERATED}\n${renderBrowserProviderContract()}`);
write(path.join(ROOT, 'skills', tree, 'references', 'support', 'runtime-bootstrap.mjs'), bootstrap);
Expand Down
20 changes: 17 additions & 3 deletions scripts/gstack2/run-parity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,10 @@ const ALLOWED_DISPOSITIONS = new Set(['VERBATIM_PORT', 'MECHANICAL_PORT', 'JUDGM
// overlay for issue #879 (28 -> 29, targets '*', all 55 modules) added 113
// more (2 per module + 3 regression checks). The founder-resources opt-out
// overlay for issue #538 (29 -> 30, office-hours only) added 5 more. The
// per-tree code-intelligence offer contract added 18 (3 per dispatcher).
export const EXPECTED_PARITY_CHECKS = 5050;
// per-tree code-intelligence offer contract added 18 (3 per dispatcher). The
// third-party web-action contract added 24 more (4 per tree: exists, marker
// format, dispatcher load, consent/secret content).
export const EXPECTED_PARITY_CHECKS = 5074;

function sha256(value: string | Uint8Array): string {
return createHash('sha256').update(value).digest('hex');
Expand Down Expand Up @@ -182,6 +184,18 @@ export function runParity(): ParityResult {
`${tree} code-intelligence offer lost its silent-degrade, decline, or no-auto-install behavior`,
);
}
check(dispatcher.includes('references/THIRD-PARTY-ACTIONS.md'), `${tree} dispatcher does not load the third-party web-action contract`);
const thirdPartyPath = path.join(ROOT, 'skills', tree, 'references', 'THIRD-PARTY-ACTIONS.md');
if (fs.existsSync(thirdPartyPath)) {
const thirdParty = fs.readFileSync(thirdPartyPath, 'utf8');
check(
thirdParty.includes('STOP and ask one explicit question before any browsing')
&& thirdParty.includes('per-task consent')
&& thirdParty.includes('never appears in chat output, logs, or shell history')
&& thirdParty.includes('verify the captured credential with one non-mutating API call'),
Comment on lines +192 to +195

@cubic-dev-ai cubic-dev-ai Bot Jul 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: Parity can still pass after losing several safety boundaries in THIRD-PARTY-ACTIONS.md, including no-install, named-site scope, user-only payment/identity steps, protected storage, and blocked manual fallback. Consider asserting each contract section’s critical invariant, not only the consent and API-verification phrases.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/gstack2/run-parity.ts, line 176:

<comment>Parity can still pass after losing several safety boundaries in `THIRD-PARTY-ACTIONS.md`, including no-install, named-site scope, user-only payment/identity steps, protected storage, and blocked manual fallback. Consider asserting each contract section’s critical invariant, not only the consent and API-verification phrases.</comment>

<file context>
@@ -168,6 +168,18 @@ export function runParity(): ParityResult {
+    if (fs.existsSync(thirdPartyPath)) {
+      const thirdParty = fs.readFileSync(thirdPartyPath, 'utf8');
+      check(
+        thirdParty.includes('STOP and ask one explicit question before any browsing')
+          && thirdParty.includes('per-task consent')
+          && thirdParty.includes('never appears in chat output, logs, or shell history')
</file context>
Suggested change
thirdParty.includes('STOP and ask one explicit question before any browsing')
&& thirdParty.includes('per-task consent')
&& thirdParty.includes('never appears in chat output, logs, or shell history')
&& thirdParty.includes('verify the captured credential with one non-mutating API call'),
thirdParty.includes('Never install it, and never treat binary presence as consent to browse')
&& thirdParty.includes('STOP and ask one explicit question before any browsing')
&& thirdParty.includes('then offer A) drive it now through the agentic browser, B) manual instructions, C) defer')
&& thirdParty.includes('per-task consent')
&& thirdParty.includes('touch only the named site and actions')
&& thirdParty.includes('payment, and identity verification remain user-performed steps')
&& thirdParty.includes('never appears in chat output, logs, or shell history')
&& thirdParty.includes('owner-only permissions')
&& thirdParty.includes('Dashboard fields are often masked placeholders')
&& thirdParty.includes('verify the captured credential with one non-mutating API call')
&& thirdParty.includes('provide the manual steps and mark the step blocked on the user'),
Fix with cubic

`${tree} third-party web-action contract lost its consent gate or secret-handling rules`,
);
}
}
const effectsPath = path.join(ROOT, 'skills', 'ship', 'references', 'EXTERNAL-EFFECTS.md');
check(fs.existsSync(effectsPath), 'Ship lacks the durable external-effect protocol');
Expand Down Expand Up @@ -382,7 +396,7 @@ export function runParity(): ParityResult {
for (const tree of TREE_NAMES) {
const compatibility = fs.readFileSync(path.join(ROOT, 'skills', tree, 'references', 'COMPATIBILITY.md'), 'utf8');
check(!compatibility.includes('../../../') && !compatibility.includes('compat/README.md'), `${tree} compatibility map escapes the selected package`);
for (const reference of ['SHARED-JUDGMENT.md', 'WEB-CONTEXT.md', 'RUNTIME.md']) {
for (const reference of ['SHARED-JUDGMENT.md', 'WEB-CONTEXT.md', 'RUNTIME.md', 'THIRD-PARTY-ACTIONS.md']) {
const referencePath = path.join(ROOT, 'skills', tree, 'references', reference);
check(fs.existsSync(referencePath), `${tree} lacks ${reference}`);
if (fs.existsSync(referencePath)) {
Expand Down
2 changes: 1 addition & 1 deletion skills/debug/SKILL.md

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading