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
34 changes: 34 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,40 @@
> completion state and remaining P0 gates. No version bump or release claim is
> made here while that status holds.

## [1.61.2.0] - 2026-07-23

## **The founder-resources pitch now takes no for an answer.**
## **Never means never.**

Office-hours ends by recommending 2-3 items from a 34-item pool of Paul Graham essays and Garry Tan / YC videos, every single session. Until now your only choices were open them or skip them this once, and the reporter on issue #538 found that even memory instructions telling the agent to stop kept getting overridden on every update. This release adds a "Never show me these again" option to that offer. Pick it once and the opt-out is stored in config, where session context cannot override it: every future session skips the entire resources phase silently, no resources, no "skipped as requested" mention. The rest of the handoff is untouched.

### The three numbers that matter

Source: the executable parity inventory (`bun run scripts/gstack2/run-parity.ts`) and the office-hours resource pool in `skills/plan/references/sections/office-hours/design-and-handoff.md`.

| Metric | Before | After |
|---|---|---|
| Ways to permanently decline the 34-resource pool | 0 | 1 selection |
| Sessions that re-pitch after you decline | every one | 0 |
| Judgment overlays / parity checks | 29 / 5,027 | 30 / 5,032 |

The middle row is the fix. A stored `false` in `~/.gstack` beats a memory instruction because config survives updates and the overlay forbids the agent from re-litigating it.

### What this means for builders

Run `/plan` product mode as often as you want. The essays and videos show up until the day you say never, and then they stay gone. Changed your mind later? `gstack-config set founder_resources true` brings them back. That is the whole contract.

### Itemized changes

#### Added

- **"Never show me these again"** option in the office-hours Founder Resources offer. Selecting it runs `gstack-config set founder_resources false`, confirms in one line with the re-enable command, and continues the handoff.
- New `founder_resources` config key (default `true`, validated to `true`/`false`), shipped with the managed runtime.

#### For contributors

- Judgment overlay #538 (`GSTACK2_FIX_538_FOUNDER_RESOURCES_OPTOUT`, office-hours only) with executable regression fixture `evals/parity/regressions/pr-538.json`; parity inventory grows 5,027 to 5,032.

## [1.61.1.0] - 2026-07-22

## **Design docs now read like decision records.**
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.61.1.0
1.61.2.0
4 changes: 4 additions & 0 deletions bin/gstack-config
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ const defaults = Object.freeze({
plan_tune_hooks: "prompt",
redact_repo_visibility: "",
redact_prepush_hook: false,
// Founder resources (PG essays, YC videos) in /office-hours. "Never show
// me these again" sets this to false.
founder_resources: true,
salience_allowlist: "",
// Remote pair-agent (ngrok tunnel) is opt-in: OFF exposes nothing to the
// internet. Set "on" to allow the tunnel to start.
Expand Down Expand Up @@ -135,6 +138,7 @@ function validateClosedValue(key, value) {
[/^artifacts_sync_mode$/, ["off", "artifacts-only", "full"], "off"],
[/^redact_repo_visibility$/, ["public", "private", "unknown"], "unknown"],
[/^redact_prepush_hook$/, ["true", "false"], "false"],
[/^founder_resources$/, ["true", "false"], "true"],
[/^plan_tune_hooks$/, ["prompt", "yes", "no"], "prompt"],
[/^pair_agent$/, ["off", "on"], "off"],
];
Expand Down
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.

13 changes: 12 additions & 1 deletion docs/gstack-2/JUDGMENT-PROVENANCE.json

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

2 changes: 1 addition & 1 deletion docs/gstack-2/SKILL-MIGRATION.md

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

3 changes: 2 additions & 1 deletion evals/parity/contracts/office-hours.json

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

13 changes: 12 additions & 1 deletion evals/parity/manifest.json

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

22 changes: 22 additions & 0 deletions evals/parity/regressions/pr-538.json

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

1 change: 1 addition & 0 deletions evals/parity/runtime-helper-closure.json

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

7 changes: 7 additions & 0 deletions evals/parity/transcripts/deterministic/office-hours.json

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

28 changes: 28 additions & 0 deletions scripts/gstack2/bug-fix-overlays.ts
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,25 @@ A question is only answerable if the user can see what it refers to. Before any
},
},
},
{
pr: 538,
url: 'https://github.com/garrytan/gstack/issues/538',
title: 'Founder resources honor a persistent never-show-again opt-out',
targets: ['office-hours'],
anchor: 'GSTACK2_FIX_538_FOUNDER_RESOURCES_OPTOUT',
body: `### Founder-resources opt-out

Before sharing any founder resources (Paul Graham essays, Garry Tan or YC videos, or similar motivational recommendations), check the persistent opt-out with \`"$GSTACK_BIN/gstack-config" get founder_resources\`. If it prints \`false\`, skip the entire resources phase silently — no resources, no mention that they were skipped — and continue with the rest of the handoff, which is unaffected. When resources are shown, the offer-to-open question must include a **"Never show me these again"** option alongside the open/skip options. Choosing it runs \`"$GSTACK_BIN/gstack-config" set founder_resources false\`, confirms in one line that resources will not be recommended again and that \`gstack-config set founder_resources true\` re-enables them, then continues. The opt-out is a durable user decision: never re-pitch the resources, never ask the user to reconsider, and never let a session's context override the stored \`false\`.`,
regression: {
input: { founder_resources_config: 'false', phase: 'founder-resources' },
expected: {
resources_shown: false,
skip_is_silent: true,
never_again_option_when_shown: true,
opt_out_persisted_via: 'gstack-config set founder_resources false',
},
},
},
];

export function overlaysForSource(source: string): BugFixOverlay[] {
Expand Down Expand Up @@ -663,6 +682,15 @@ export function evaluateBugFixRegression(pr: number, rawInput: unknown): Record<
step_unit: ['hours', 'days', 'weeks', 'stage-appropriate', 'stage-appropriate'][rank],
};
}
case 538: {
const optedOut = String(input.founder_resources_config ?? '') === 'false';
return {
resources_shown: !optedOut,
skip_is_silent: optedOut,
never_again_option_when_shown: true,
opt_out_persisted_via: 'gstack-config set founder_resources false',
};
}
default:
throw new Error(`No executable GStack 2 regression evaluator for PR #${pr}`);
}
Expand Down
2 changes: 1 addition & 1 deletion scripts/gstack2/generate-skill-tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ function assertInventory(): void {
}
if (legacySections().length !== 16) throw new Error(`Expected 16 section templates, found ${legacySections().length}`);
if (SCENARIOS.length !== 25) throw new Error(`Expected 25 parity scenarios, found ${SCENARIOS.length}`);
if (BUG_FIX_OVERLAYS.length !== 29) throw new Error(`Expected 29 upstream judgment overlays, found ${BUG_FIX_OVERLAYS.length}`);
if (BUG_FIX_OVERLAYS.length !== 30) throw new Error(`Expected 30 upstream judgment overlays, found ${BUG_FIX_OVERLAYS.length}`);
}

function toc(body: string): string {
Expand Down
9 changes: 5 additions & 4 deletions scripts/gstack2/run-parity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@ const ALLOWED_DISPOSITIONS = new Set(['VERBATIM_PORT', 'MECHANICAL_PORT', 'JUDGM
// targets) added 13 more. The design-doc concision overlay for issue #2000
// (27 -> 28, office-hours only) added 5 more. The self-contained-questions
// overlay for issue #879 (28 -> 29, targets '*', all 55 modules) added 113
// more (2 per module + 3 regression checks).
export const EXPECTED_PARITY_CHECKS = 5027;
// more (2 per module + 3 regression checks). The founder-resources opt-out
// overlay for issue #538 (29 -> 30, office-hours only) added 5 more.
export const EXPECTED_PARITY_CHECKS = 5032;

function sha256(value: string | Uint8Array): string {
return createHash('sha256').update(value).digest('hex');
Expand Down Expand Up @@ -260,8 +261,8 @@ export function runParity(): ParityResult {
check(JSON.stringify(json(path.join(ROOT, 'evals', 'parity', 'scenarios', `${scenario.id}.json`))) === JSON.stringify(scenario), `${scenario.id} generated fixture drift`);
}

check(BUG_FIX_OVERLAYS.length === 29, `Expected 29 regression definitions; got ${BUG_FIX_OVERLAYS.length}`);
check(files(path.join(ROOT, 'evals', 'parity', 'regressions'), '.json').length === 29, 'Generated regression fixture count is not 29');
check(BUG_FIX_OVERLAYS.length === 30, `Expected 30 regression definitions; got ${BUG_FIX_OVERLAYS.length}`);
check(files(path.join(ROOT, 'evals', 'parity', 'regressions'), '.json').length === 30, 'Generated regression fixture count is not 30');
for (const overlay of BUG_FIX_OVERLAYS) {
const fixture = json(path.join(ROOT, 'evals', 'parity', 'regressions', `pr-${overlay.pr}.json`));
check(JSON.stringify(fixture) === JSON.stringify(overlay), `PR #${overlay.pr} regression fixture drift`);
Expand Down
10 changes: 10 additions & 0 deletions skills/plan/references/legacy/office-hours.md

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

2 changes: 1 addition & 1 deletion test/gstack2-skills.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ describe('GStack 2 skill parity', () => {
const result = runParity();
expect(result.sources).toBe(55);
expect(result.sections).toBe(16);
expect(result.regressions).toBe(29);
expect(result.regressions).toBe(30);
}, 30_000);

test('keeps image generation host-native, optional, and provider-free', () => {
Expand Down
Loading