Trim GStack 2: remove sidebar/terminal/classifier, add anti-slop contribution bar, pair-agent opt-in#15
Conversation
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a strict "evidence bar" section to CONTRIBUTING.md and a PR template that force every contribution to prove a human exercised the change: a human-written why, a live before/after evidence block, DCO sign-off, and a scope statement. Enumerate auto-closed categories (ETHOS edits, voice/YC cleanup, generated-file-only diffs, blind-AI-sweep PRs). CI enforcement (changeset-required, DCO check, generated-file guard) is documented as the next step tied to the Changesets migration rather than built speculatively. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Every contribution must attach a screenshot with "GSTACK PR" typed live into a real surface (terminal, search bar, editor) — not overlaid or edited onto the image — to confirm a human on a real machine opened the PR. Remove the Changesets-migration reference from the CI-enforcement note since that migration is not planned. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The ngrok pair-agent tunnel could auto-start whenever ngrok was installed and shipped active on every install, despite ~0.01% usage. Gate all three activation points behind a single opt-in config flag so nothing is exposed to the internet unless the user explicitly enables it. - New `pair_agent` config key (off | on), default off, read via the shared fail-closed `isPairAgentEnabled()` guard in browse/src/config.ts (honors `GSTACK_PAIR_AGENT` env override for tests/emergency). - CLI no longer auto-starts the tunnel when disabled, even if ngrok is installed/authed; prints the enable command instead. - `/tunnel/start` returns 403 with the enable hint when disabled (tunnel listener never binds). - `BROWSE_TUNNEL=1` startup path skips the tunnel bind when disabled. Local browse/QA (local listener, /command, /browse, /qa, cookie import, /inspector, /health) is unchanged. When enabled, behavior is identical to before. The /pair-agent skill doc is parity-locked GStack 2 legacy (evals/parity/ contracts/pair-agent.json pins the render + blob SHAs). Its up-front "enable pair_agent first" wording needs a separate parity-aware regen and is intentionally not touched here. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
There was a problem hiding this comment.
17 issues found across 97 files
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".github/PULL_REQUEST_TEMPLATE.md">
<violation number="1" location=".github/PULL_REQUEST_TEMPLATE.md:32">
P2: The liveness proof is replayable across PRs, so it does not establish that a human exercised this submission. Including a per-change value such as the current short commit SHA would tie the screenshot to the PR's actual revision.</violation>
<violation number="2" location=".github/PULL_REQUEST_TEMPLATE.md:45">
P3: The "Linked issue or reproduction: #" checklist item mixes a fill-in field with a checkbox, creating ambiguity. The contributor must replace `#` with an issue number AND decide whether to check the box off. The previous item clearly uses "(or N/A)" to mark something as optional, but this item omits that pattern. As written, if a contributor replaces `#` with "42", the checkbox text becomes "Linked issue or reproduction: 42" but the unchecked `- [ ]` syntax remains, leaving it unclear whether checking the checkbox is also expected. Consider moving this outside the checklist as a fill-in section (e.g., "**Linked issue:** #") like the Scope section does, or adding "(or N/A)" to match the pattern used on the previous row.</violation>
</file>
<file name="gstack/llms.txt">
<violation number="1" location="gstack/llms.txt:48">
P2: This continues advertising the internal GStack 1 `/open-gstack-browser` alias as a directly invokable public skill, conflicting with the six-skill GStack 2 surface. The generator should omit internal aliases here or describe the replacement routing rather than presenting this as a standalone launcher.</violation>
</file>
<file name="scripts/test-free-shards.ts">
<violation number="1" location="scripts/test-free-shards.ts:99">
P2: Windows CI still drops six passing test files because this raw-text regex matches the comment-only references that the new comment explicitly says remain. Removing this obsolete exclusion would restore coverage; any real dependency on the deleted module should fail normally on every platform rather than silently removing the test from Windows.</violation>
</file>
<file name="browse/src/security.ts">
<violation number="1" location="browse/src/security.ts:8">
P1: Headless page-content commands no longer receive the documented L5 canary/L6 verdict protection: removal of the sidebar caller left these exports connected only to tests. Rewire canary injection/checking and verdict handling into the retained browse flow, or explicitly treat these layers as removed rather than claiming they remain.</violation>
<violation number="2" location="browse/src/security.ts:15">
P2: The retained `/health` security status can now be false: no process updates `session-state.json`, yet `getStatus()` still reports removed TestSavant/transcript layers from that file. Replace this obsolete cross-process state/status model with status derived from the surviving page-content protections and ignore stale classifier state.</violation>
</file>
<file name="BROWSER.md">
<violation number="1" location="BROWSER.md:488">
P2: Domain skills cannot auto-promote after three uses as documented: saves retain `classifier_score = 0`, while `recordSkillUse` requires a positive score and is not called by production code. The docs should describe manual promotion/remain-quarantined behavior unless the deterministic scan is wired into this lifecycle.</violation>
<violation number="2" location="BROWSER.md:737">
P1: The documented “every layer” protection is not active: only scoped-token page output runs L1-L3, while L5/L6 have no runtime callers and root-token output is merely wrapped. This security section should distinguish active enforcement from retained helpers, or the missing canary/verdict wiring should be restored.</violation>
<violation number="3" location="BROWSER.md:764">
P2: `GSTACK_SECURITY_OFF=1` does not disable the content scan, so operators following this new documentation still receive the configured filter behavior. The implemented kill switch is `BROWSE_CONTENT_FILTER=off`, and the canary-still-injects claim should be removed unless canary wiring is restored.</violation>
</file>
<file name="CONTRIBUTING.md">
<violation number="1" location="CONTRIBUTING.md:106">
P2: Contributors receive an inaccurate explanation of the legal attestation: reviewing a change alone does not satisfy DCO 1.1. Linking the DCO and describing sign-off as certification that its terms are met would avoid misleading signers.</violation>
<violation number="2" location="CONTRIBUTING.md:114">
P2: The liveness screenshot documents a live-looking UI but does not prove a human opened the PR; automation can type `GSTACK PR` and capture the same artifact. Framing it as supporting evidence rather than identity proof avoids false assurance in the anti-slop review process.</violation>
<violation number="3" location="CONTRIBUTING.md:126">
P2: The documented glob omits the six generated GStack 2 `skills/*/SKILL.md` files, so contributors and the planned guard may treat generated-only edits there as valid. Include that nested path class explicitly.</violation>
</file>
<file name="ARCHITECTURE.md">
<violation number="1" location="ARCHITECTURE.md:247">
P2: Root-token page-content reads do not receive the documented L1-L3 stack, and there is no remaining sidebar tool-output path. Please scope this guarantee to paired/scoped-token commands so the authoritative security model does not overstate protection.</violation>
</file>
<file name="browse/src/browser-manager.ts">
<violation number="1" location="browse/src/browser-manager.ts:344">
P3: `useHeadless` is now always true, so the variable and `useHeadless &&` branch retain dead complexity from the removed extension path. Inlining `headless: true` and checking `managedHeadlessChannel()` directly would make the launch modes explicit.</violation>
<violation number="2" location="browse/src/browser-manager.ts:412">
P3: This documentation is inaccurate for configured/custom Chromium launches. Describing the browser as configured or Playwright-managed would match the executable-selection code below.</violation>
<violation number="3" location="browse/src/browser-manager.ts:417">
P3: The headed-launch API still accepts and receives an auth token even though the extension removal leaves no consumer for it. Removing the parameter and updating the server call/comments would avoid preserving an obsolete sensitive-data contract.</violation>
<violation number="4" location="browse/src/browser-manager.ts:439">
P2: Component-baked GBrowser builds can still run the removed sidebar because both persistent-context launches continue filtering out Playwright’s extension-disabling defaults. An extension-free ignore list for `launchHeaded()` and `handoff()` would ensure the removed surface and its background page cannot load.</violation>
</file>
Tip: instead of fixing issues one by one fix them all with cubic
Re-trigger cubic
| * does not load onnxruntime-node or other native modules. | ||
| * does not load onnxruntime-node or other native modules. The ML prompt- | ||
| * injection classifier (and its in-browser sidebar/terminal caller) was | ||
| * removed; only these page-content layers remain. |
There was a problem hiding this comment.
P1: Headless page-content commands no longer receive the documented L5 canary/L6 verdict protection: removal of the sidebar caller left these exports connected only to tests. Rewire canary injection/checking and verdict handling into the retained browse flow, or explicitly treat these layers as removed rather than claiming they remain.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At browse/src/security.ts, line 8:
<comment>Headless page-content commands no longer receive the documented L5 canary/L6 verdict protection: removal of the sidebar caller left these exports connected only to tests. Rewire canary injection/checking and verdict handling into the retained browse flow, or explicitly treat these layers as removed rather than claiming they remain.</comment>
<file context>
@@ -3,20 +3,16 @@
- * does not load onnxruntime-node or other native modules.
+ * does not load onnxruntime-node or other native modules. The ML prompt-
+ * injection classifier (and its in-browser sidebar/terminal caller) was
+ * removed; only these page-content layers remain.
*
- * ML classifier code lives in `security-classifier.ts`, which is only
</file context>
| every user message and every tool output that could carry untrusted content | ||
| (Read, Glob, Grep, WebFetch, page text from `$B`). | ||
| Layered defense against prompt injection in the page content the browser | ||
| reads. Every layer runs synchronously on every page-content command and every |
There was a problem hiding this comment.
P1: The documented “every layer” protection is not active: only scoped-token page output runs L1-L3, while L5/L6 have no runtime callers and root-token output is merely wrapped. This security section should distinguish active enforcement from retained helpers, or the missing canary/verdict wiring should be restored.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At BROWSER.md, line 737:
<comment>The documented “every layer” protection is not active: only scoped-token page output runs L1-L3, while L5/L6 have no runtime callers and root-token output is merely wrapped. This security section should distinguish active enforcement from retained helpers, or the missing canary/verdict wiring should be restored.</comment>
<file context>
@@ -811,58 +733,43 @@ startup.
-every user message and every tool output that could carry untrusted content
-(Read, Glob, Grep, WebFetch, page text from `$B`).
+Layered defense against prompt injection in the page content the browser
+reads. Every layer runs synchronously on every page-content command and every
+tool output that could carry untrusted content. All of it is pure-string —
+no ML model, no native runtime.
</file context>
|
|
||
| ## Liveness proof (required) | ||
|
|
||
| <!-- Attach a screenshot of your own machine with the text `GSTACK PR` typed LIVE |
There was a problem hiding this comment.
P2: The liveness proof is replayable across PRs, so it does not establish that a human exercised this submission. Including a per-change value such as the current short commit SHA would tie the screenshot to the PR's actual revision.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/PULL_REQUEST_TEMPLATE.md, line 32:
<comment>The liveness proof is replayable across PRs, so it does not establish that a human exercised this submission. Including a per-change value such as the current short commit SHA would tie the screenshot to the PR's actual revision.</comment>
<file context>
@@ -0,0 +1,45 @@
+
+## Liveness proof (required)
+
+<!-- Attach a screenshot of your own machine with the text `GSTACK PR` typed LIVE
+into a real surface — terminal prompt, a shell command, your browser
+address/search bar, an editor buffer. It must be TYPED INTO A LIVE UI, not drawn,
</file context>
| - [/make-pdf](make-pdf/SKILL.md): Turn any markdown file into a publication-quality PDF. | ||
| - [/office-hours](office-hours/SKILL.md): YC Office Hours — two modes. | ||
| - [/open-gstack-browser](open-gstack-browser/SKILL.md): Launch GStack Browser — AI-controlled Chromium with the sidebar extension baked in. | ||
| - [/open-gstack-browser](open-gstack-browser/SKILL.md): Launch GStack Browser — AI-controlled Chromium you can watch in real time. |
There was a problem hiding this comment.
P2: This continues advertising the internal GStack 1 /open-gstack-browser alias as a directly invokable public skill, conflicting with the six-skill GStack 2 surface. The generator should omit internal aliases here or describe the replacement routing rather than presenting this as a standalone launcher.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At gstack/llms.txt, line 48:
<comment>This continues advertising the internal GStack 1 `/open-gstack-browser` alias as a directly invokable public skill, conflicting with the six-skill GStack 2 surface. The generator should omit internal aliases here or describe the replacement routing rather than presenting this as a standalone launcher.</comment>
<file context>
@@ -45,7 +45,7 @@ Conventions:
- [/make-pdf](make-pdf/SKILL.md): Turn any markdown file into a publication-quality PDF.
- [/office-hours](office-hours/SKILL.md): YC Office Hours — two modes.
-- [/open-gstack-browser](open-gstack-browser/SKILL.md): Launch GStack Browser — AI-controlled Chromium with the sidebar extension baked in.
+- [/open-gstack-browser](open-gstack-browser/SKILL.md): Launch GStack Browser — AI-controlled Chromium you can watch in real time.
- [/pair-agent](pair-agent/SKILL.md): Pair a remote AI agent with your browser.
- [/plan-ceo-review](plan-ceo-review/SKILL.md): CEO/founder-mode plan review.
</file context>
| // read it are gone; remaining hits are comment-level references in tests | ||
| // that pass. Kept as a cheap tripwire so a reintroduced read can't sneak | ||
| // a Windows-CI hard-fail back in. | ||
| { pattern: /sidebar-agent\.ts/, reason: 'names deleted browse/src/sidebar-agent.ts' }, |
There was a problem hiding this comment.
P2: Windows CI still drops six passing test files because this raw-text regex matches the comment-only references that the new comment explicitly says remain. Removing this obsolete exclusion would restore coverage; any real dependency on the deleted module should fail normally on every platform rather than silently removing the test from Windows.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At scripts/test-free-shards.ts, line 99:
<comment>Windows CI still drops six passing test files because this raw-text regex matches the comment-only references that the new comment explicitly says remain. Removing this obsolete exclusion would restore coverage; any real dependency on the deleted module should fail normally on every platform rather than silently removing the test from Windows.</comment>
<file context>
@@ -91,13 +91,12 @@ const WINDOWS_FRAGILE_PATTERNS: Array<{ pattern: RegExp; reason: string }> = [
+ // read it are gone; remaining hits are comment-level references in tests
+ // that pass. Kept as a cheap tripwire so a reintroduced read can't sneak
+ // a Windows-CI hard-fail back in.
+ { pattern: /sidebar-agent\.ts/, reason: 'names deleted browse/src/sidebar-agent.ts' },
];
</file context>
| @@ -17,7 +17,6 @@ | |||
|
|
|||
There was a problem hiding this comment.
P2: Component-baked GBrowser builds can still run the removed sidebar because both persistent-context launches continue filtering out Playwright’s extension-disabling defaults. An extension-free ignore list for launchHeaded() and handoff() would ensure the removed surface and its background page cannot load.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At browse/src/browser-manager.ts, line 439:
<comment>Component-baked GBrowser builds can still run the removed sidebar because both persistent-context launches continue filtering out Playwright’s extension-disabling defaults. An extension-free ignore list for `launchHeaded()` and `handoff()` would ensure the removed surface and its background page cannot load.</comment>
<file context>
@@ -499,38 +435,9 @@ export class BrowserManager {
- // Extensions REQUIRE launchPersistentContext (not launch + newContext).
- // Real Chrome (executablePath/channel) silently blocks --load-extension,
- // so we use Playwright's bundled Chromium which reliably loads extensions.
+ // Launch headed Chromium via Playwright's persistent context so the
+ // profile (cookies, storage) persists across runs.
const fs = require('fs');
</file context>
| const { STEALTH_LAUNCH_ARGS, buildGStackLaunchArgs } = await import('./stealth'); | ||
| const launchArgs: string[] = [...STEALTH_LAUNCH_ARGS, ...buildGStackLaunchArgs()]; | ||
| let useHeadless = true; | ||
| const useHeadless = true; |
There was a problem hiding this comment.
P3: useHeadless is now always true, so the variable and useHeadless && branch retain dead complexity from the removed extension path. Inlining headless: true and checking managedHeadlessChannel() directly would make the launch modes explicit.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At browse/src/browser-manager.ts, line 344:
<comment>`useHeadless` is now always true, so the variable and `useHeadless &&` branch retain dead complexity from the removed extension path. Inlining `headless: true` and checking `managedHeadlessChannel()` directly would make the launch modes explicit.</comment>
<file context>
@@ -378,14 +339,9 @@ export class BrowserManager {
const { STEALTH_LAUNCH_ARGS, buildGStackLaunchArgs } = await import('./stealth');
const launchArgs: string[] = [...STEALTH_LAUNCH_ARGS, ...buildGStackLaunchArgs()];
- let useHeadless = true;
+ const useHeadless = true;
const executablePath = configuredChromiumExecutable();
</file context>
| * Chrome extension auto-loaded. Uses launchPersistentContext() which | ||
| * is required for extension loading (launch() + newContext() can't | ||
| * load extensions). | ||
| * Launch Playwright's bundled Chromium in headed mode. |
There was a problem hiding this comment.
P3: This documentation is inaccurate for configured/custom Chromium launches. Describing the browser as configured or Playwright-managed would match the executable-selection code below.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At browse/src/browser-manager.ts, line 412:
<comment>This documentation is inaccurate for configured/custom Chromium launches. Describing the browser as configured or Playwright-managed would match the executable-selection code below.</comment>
<file context>
@@ -468,23 +409,18 @@ export class BrowserManager {
- * Chrome extension auto-loaded. Uses launchPersistentContext() which
- * is required for extension loading (launch() + newContext() can't
- * load extensions).
+ * Launch Playwright's bundled Chromium in headed mode.
*
* The browser launches headed with a visible window — the user sees
</file context>
| * Launch Playwright's bundled Chromium in headed mode. | |
| * Launch the configured or Playwright-managed Chromium in headed mode. |
| * every action Claude takes in real time. | ||
| */ | ||
| async launchHeaded(authToken?: string): Promise<void> { | ||
| async launchHeaded(_authToken?: string): Promise<void> { |
There was a problem hiding this comment.
P3: The headed-launch API still accepts and receives an auth token even though the extension removal leaves no consumer for it. Removing the parameter and updating the server call/comments would avoid preserving an obsolete sensitive-data contract.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At browse/src/browser-manager.ts, line 417:
<comment>The headed-launch API still accepts and receives an auth token even though the extension removal leaves no consumer for it. Removing the parameter and updating the server call/comments would avoid preserving an obsolete sensitive-data contract.</comment>
<file context>
@@ -468,23 +409,18 @@ export class BrowserManager {
* every action Claude takes in real time.
*/
- async launchHeaded(authToken?: string): Promise<void> {
+ async launchHeaded(_authToken?: string): Promise<void> {
assertHeadedBrowserProvider();
// Clear old state before repopulating
</file context>
| - [ ] This is not a generated-file-only diff (I edited the source/template and regenerated) | ||
| - [ ] No ETHOS.md edits, and no changes to voice / founder perspective / YC references | ||
| - [ ] New public command / external service / host adapter has an accepted issue linked (or N/A) | ||
| - [ ] Linked issue or reproduction: # |
There was a problem hiding this comment.
P3: The "Linked issue or reproduction: #" checklist item mixes a fill-in field with a checkbox, creating ambiguity. The contributor must replace # with an issue number AND decide whether to check the box off. The previous item clearly uses "(or N/A)" to mark something as optional, but this item omits that pattern. As written, if a contributor replaces # with "42", the checkbox text becomes "Linked issue or reproduction: 42" but the unchecked - [ ] syntax remains, leaving it unclear whether checking the checkbox is also expected. Consider moving this outside the checklist as a fill-in section (e.g., "Linked issue: #") like the Scope section does, or adding "(or N/A)" to match the pattern used on the previous row.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/PULL_REQUEST_TEMPLATE.md, line 45:
<comment>The "Linked issue or reproduction: #" checklist item mixes a fill-in field with a checkbox, creating ambiguity. The contributor must replace `#` with an issue number AND decide whether to check the box off. The previous item clearly uses "(or N/A)" to mark something as optional, but this item omits that pattern. As written, if a contributor replaces `#` with "42", the checkbox text becomes "Linked issue or reproduction: 42" but the unchecked `- [ ]` syntax remains, leaving it unclear whether checking the checkbox is also expected. Consider moving this outside the checklist as a fill-in section (e.g., "**Linked issue:** #") like the Scope section does, or adding "(or N/A)" to match the pattern used on the previous row.</comment>
<file context>
@@ -0,0 +1,45 @@
+- [ ] This is not a generated-file-only diff (I edited the source/template and regenerated)
+- [ ] No ETHOS.md edits, and no changes to voice / founder perspective / YC references
+- [ ] New public command / external service / host adapter has an accepted issue linked (or N/A)
+- [ ] Linked issue or reproduction: #
</file context>
|
Merged into codex/gstack-2 (via the trim commit + sync of #16/contract). Verified: bun test + bun run build both green (binaries built), deep review confirmed no dangling imports, headless L1-L3/L5-L6 defenses intact, pair-agent fail-closed. Dep conflict resolved (dropped xterm + @huggingface/transformers per the trim, kept socks 2.8.9 / claude-agent-sdk 0.3.216 / braintrust+autoevals). |
What this does
This branch trims GStack 2 down to what it actually needs, on top of the current GStack 2 candidate (
codex/gstack-2). Three things:Removes the browser sidebar extension, the in-browser PTY terminal, and the prompt-injection ML classifier (−16,299 lines). Users already run gstack from a real terminal, so the in-browser terminal was a redundant surface carrying a large, security-sensitive PTY/WebSocket stack. Deleting it also closes the browser-content→terminal injection pathway the ML classifier existed to guard — so the weak classifier (56.2% detection / 22.9% false-positive), its 112MB/721MB model downloads, and the Hugging Face/ONNX runtime all go with it. The headless
browsebinary, CDP inspector, QA automation, cookie import, and pair-agent tunnel are all kept working.Adds an anti-slop contribution bar. Every PR must now prove a human exercised the change: a human-written "why", a live before/after evidence block, DCO sign-off, a scope statement, and a liveness screenshot —
GSTACK PRtyped live into a real surface (terminal, search bar, editor), not overlaid or edited onto the image. NewCONTRIBUTING.mdsection +PULL_REQUEST_TEMPLATE.md.Makes the remote pair-agent tunnel opt-in (default off). Telemetry showed 0.01% usage. A single fail-closed
isPairAgentEnabled()guard gates the ngrok auto-start,/tunnel/start, and theBROWSE_TUNNEL=1path, so no tunnel binds unless the user runsgstack-config set pair_agent on. Local browse/QA is untouched.Commits
ab4e9ae5d5962322b6a007a7438e843bd57b1796adb3bce0d977070f7baffc00Evidence
gstack2-skills6/6.skill-validation+gen-skill-docs: 703 pass, 0 fail.pair-agent-optin-gate(10 new tests),server-auth,dual-listener,adversarial-security— 74 pass, 0 fail.browsecli + node-server bundle compile clean after every commit.codex/gstack-2, so this PR is exactly the 8-commit delta, not the whole RC.Kept intact (verified)
pair-agent dual-listener tunnel,
/inspector/*(CDP),/cookie-picker,/welcome,/refs,/health, the L1-L3/L5/L6 page-content security stack (content-security.ts+security.tscanary/combiner), andtest/helpers/claude-pty-runner.ts(the E2E harness, unrelated to the removed in-browser terminal).Follow-ups deliberately NOT in this PR
/pair-agentskill doc is parity-locked, so the "rungstack-config set pair_agent on" enable hint in the skill text needs a separate parity-aware regeneration (gen:gstack2+ re-baseline). The runtime gate already fully enforces the opt-in.🤖 Generated with Claude Code
Summary by cubic
Trims GStack 2 to the essentials: removes the Chrome sidebar, in-browser PTY, and the ML prompt‑injection classifier; adds a proof‑based contribution bar; and makes the remote pair‑agent tunnel opt‑in by default. This reduces surface area and deps while keeping headless
browse, inspector, QA, cookie import, and pair workflows intact.New Features
pair_agentconfig (default off) with a singleisPairAgentEnabled()guard for CLI auto-start,/tunnel/start, andBROWSE_TUNNEL=1. Enable viagstack-config set pair_agent onorGSTACK_PAIR_AGENT=on. Added tests.Refactors
/connectand/command.@huggingface/transformers,xterm, andxterm-addon-fit; stopped bundlingextension/; cleaned scripts and docs to match.Written for commit 7baffc0. Summary will update on new commits.