From 377b0747f7db9b1331b6e311f31556f39f45da78 Mon Sep 17 00:00:00 2001 From: t Date: Tue, 14 Jul 2026 12:57:32 -0700 Subject: [PATCH 1/2] fix(autoplan): preserve matching task records (#2021) --- autoplan/SKILL.md | 2 +- scripts/resolvers/tasks-section.ts | 2 +- test/autoplan-tasks-aggregator.test.ts | 46 ++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 2 deletions(-) create mode 100644 test/autoplan-tasks-aggregator.test.ts diff --git a/autoplan/SKILL.md b/autoplan/SKILL.md index 5346f1d437..49abaf4604 100644 --- a/autoplan/SKILL.md +++ b/autoplan/SKILL.md @@ -1667,7 +1667,7 @@ if command -v jq >/dev/null 2>&1; then # latest run_id only. (Single phase may have multiple files if the user # re-ran the review; aggregator takes the newest.) jq -c --arg branch "$BRANCH" --arg commits "$COMMITS_RECENT" \ - 'select(.branch == $branch and ($commits | split("|") | index(.commit) != null))' \ + 'select(.branch == $branch and (.commit as $commit | ($commits | split("|") | index($commit)) != null))' \ "$f" 2>/dev/null >> "$ALL_JSONL" || true done < <(find "$TASKS_DIR" -maxdepth 1 -name "tasks-$phase-*.jsonl" 2>/dev/null | sort) # Reduce to latest run_id per phase diff --git a/scripts/resolvers/tasks-section.ts b/scripts/resolvers/tasks-section.ts index 2f86f588b3..892f19a896 100644 --- a/scripts/resolvers/tasks-section.ts +++ b/scripts/resolvers/tasks-section.ts @@ -119,7 +119,7 @@ if command -v jq >/dev/null 2>&1; then # latest run_id only. (Single phase may have multiple files if the user # re-ran the review; aggregator takes the newest.) jq -c --arg branch "$BRANCH" --arg commits "$COMMITS_RECENT" \\ - 'select(.branch == $branch and ($commits | split("|") | index(.commit) != null))' \\ + 'select(.branch == $branch and (.commit as $commit | ($commits | split("|") | index($commit)) != null))' \\ "$f" 2>/dev/null >> "$ALL_JSONL" || true done < <(find "$TASKS_DIR" -maxdepth 1 -name "tasks-$phase-*.jsonl" 2>/dev/null | sort) # Reduce to latest run_id per phase diff --git a/test/autoplan-tasks-aggregator.test.ts b/test/autoplan-tasks-aggregator.test.ts new file mode 100644 index 0000000000..b1276862ff --- /dev/null +++ b/test/autoplan-tasks-aggregator.test.ts @@ -0,0 +1,46 @@ +import { describe, expect, test } from 'bun:test'; +import { readFileSync } from 'fs'; +import { join } from 'path'; +import { spawnSync } from 'child_process'; + +const skill = readFileSync(join(import.meta.dir, '..', 'autoplan', 'SKILL.md'), 'utf8'); + +function shippedFilter(): string { + const match = skill.match(/'(select\(\.branch == \$branch and [^']+\))'/); + if (!match) throw new Error('autoplan task filter not found'); + return match[1]; +} + +function filter(record: object, branch: string, commits: string) { + return spawnSync( + 'jq', + ['-c', '--arg', 'branch', branch, '--arg', 'commits', commits, shippedFilter()], + { input: `${JSON.stringify(record)}\n`, encoding: 'utf8' }, + ); +} + +describe('autoplan task aggregation', () => { + test('keeps a task from the current branch and recent commit', () => { + const result = filter( + { branch: 'feature', commit: 'abc123', phase: 'ceo-review' }, + 'feature', + 'abc123|def456', + ); + + expect(result.status).toBe(0); + expect(JSON.parse(result.stdout)).toMatchObject({ commit: 'abc123' }); + }); + + test('excludes unrelated branches and commits without jq errors', () => { + for (const record of [ + { branch: 'other', commit: 'abc123' }, + { branch: 'feature', commit: 'unrelated' }, + { branch: 'feature' }, + ]) { + const result = filter(record, 'feature', 'abc123|def456'); + expect(result.status).toBe(0); + expect(result.stderr).toBe(''); + expect(result.stdout).toBe(''); + } + }); +}); From b9d48195d2899cee217f00a88627d0f645645232 Mon Sep 17 00:00:00 2001 From: t Date: Tue, 14 Jul 2026 12:57:32 -0700 Subject: [PATCH 2/2] fix(setup-deploy): avoid exposing Render key bytes (#1096) --- setup-deploy/SKILL.md | 9 +++++++- setup-deploy/SKILL.md.tmpl | 9 +++++++- test/setup-deploy-render-key.test.ts | 33 ++++++++++++++++++++++++++++ 3 files changed, 49 insertions(+), 2 deletions(-) create mode 100644 test/setup-deploy-render-key.test.ts diff --git a/setup-deploy/SKILL.md b/setup-deploy/SKILL.md index 3465dc564b..d82afe6d5a 100644 --- a/setup-deploy/SKILL.md +++ b/setup-deploy/SKILL.md @@ -855,7 +855,14 @@ Ask the user to confirm the production URL. Some Fly apps use custom domains. If `render.yaml` detected: 1. Extract service name and type from render.yaml -2. Check for Render API key: `echo $RENDER_API_KEY | head -c 4` (don't expose the full key) +2. Check for Render API key without printing any part of it: +```bash +if [ -n "${RENDER_API_KEY:-}" ]; then + echo "RENDER_API_KEY: set" +else + echo "RENDER_API_KEY: not set" +fi +``` 3. Infer URL: `https://{service-name}.onrender.com` 4. Render deploys automatically on push to the connected branch — no deploy workflow needed 5. Set health check: the inferred URL diff --git a/setup-deploy/SKILL.md.tmpl b/setup-deploy/SKILL.md.tmpl index 587a993c01..5c120eb9d6 100644 --- a/setup-deploy/SKILL.md.tmpl +++ b/setup-deploy/SKILL.md.tmpl @@ -99,7 +99,14 @@ Ask the user to confirm the production URL. Some Fly apps use custom domains. If `render.yaml` detected: 1. Extract service name and type from render.yaml -2. Check for Render API key: `echo $RENDER_API_KEY | head -c 4` (don't expose the full key) +2. Check for Render API key without printing any part of it: +```bash +if [ -n "${RENDER_API_KEY:-}" ]; then + echo "RENDER_API_KEY: set" +else + echo "RENDER_API_KEY: not set" +fi +``` 3. Infer URL: `https://{service-name}.onrender.com` 4. Render deploys automatically on push to the connected branch — no deploy workflow needed 5. Set health check: the inferred URL diff --git a/test/setup-deploy-render-key.test.ts b/test/setup-deploy-render-key.test.ts new file mode 100644 index 0000000000..fc8c67ce6c --- /dev/null +++ b/test/setup-deploy-render-key.test.ts @@ -0,0 +1,33 @@ +import { describe, expect, test } from 'bun:test'; +import { readFileSync } from 'fs'; +import { join } from 'path'; +import { spawnSync } from 'child_process'; + +const skill = readFileSync(join(import.meta.dir, '..', 'setup-deploy', 'SKILL.md'), 'utf8'); + +function renderKeyCheck(): string { + const section = skill.match(/Check for Render API key[^\n]*\n```bash\n([\s\S]*?)\n```/); + if (!section) throw new Error('Render API key check not found'); + return section[1]; +} + +describe('setup-deploy Render credential check', () => { + test('reports presence without printing secret bytes', () => { + const result = spawnSync('bash', ['-c', renderKeyCheck()], { + encoding: 'utf8', + env: { ...process.env, RENDER_API_KEY: 's3cr3t-value' }, + }); + + expect(result.status).toBe(0); + expect(result.stdout.trim()).toBe('RENDER_API_KEY: set'); + expect(result.stdout).not.toContain('s3cr'); + }); + + test('reports an absent key', () => { + const env = { ...process.env }; + delete env.RENDER_API_KEY; + const result = spawnSync('bash', ['-c', renderKeyCheck()], { encoding: 'utf8', env }); + expect(result.status).toBe(0); + expect(result.stdout.trim()).toBe('RENDER_API_KEY: not set'); + }); +});