mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 22:14:25 +01:00
fix: pre-landing review fixes (fragment exemption only for .md directly in the folder; rename lists keep the old path)
Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
34b8646c58
commit
ec82fe39c6
6 files changed
+23
-7
No files matched your search
@@ -1,7 +1,8 @@
|
||||
**A PR's changelog.d fragment no longer brings back every paid gate case.** Since fragments became the way every PR records its CHANGELOG entry (v1.91.342.0), the PR eval selector read each `changelog.d/<slug>.md` as an unknown dependency and restored the full gate, so it saved nothing on any PR (GST-400, found on #3668).
|
||||
|
||||
### Fixed
|
||||
- **`scripts/test-pr-profile.ts`** treats every file under the ship policy's `changelog_fragments.dir` as having no eval dependents. A touchfile that names a fragment still selects its case.
|
||||
- **`scripts/test-pr-profile.ts`** treats a `.md` file directly in the ship policy's `changelog_fragments.dir` as having no eval dependents. Other files in that folder, nested files, and a touchfile that names a fragment still select as before.
|
||||
- **`test/helpers/test-selection.ts`** lists both paths of a renamed file, so moving a file into `changelog.d/` or `docs/` still selects the cases its old path fed.
|
||||
|
||||
### Tests
|
||||
- `test/test-pr-profile.test.ts` covers a fragment-only PR (no full fallback, no cases) and a touchfile that names a fragment.
|
||||
- `test/test-pr-profile.test.ts` covers a fragment-only PR (no full fallback, no cases), other files in the folder, and a touchfile that names a fragment; `test/changed-files-union.test.ts` covers a rename.
|
||||
@@ -17,7 +17,7 @@
|
||||
"scripts/lib/published-text.ts": {"blob":"3f2e4d616b5dd0204035710c66b64d49055c5714","since":6},
|
||||
"scripts/lib/shard-engine.ts": {"blob":"dc714d7d897aed185c0ec3d2c92ad22d954039e5","since":6,"nonBehavioral":"createShardSandbox drops the Capy launch markers (GSTACK_SESSION_KIND, GSTACK_CAPY, GSTACK_EPHEMERAL, GSTACK_INSTALL_VERDICT) from shard environments; CI never sets them, so no case's input or verdict changes there, and on a Capy machine fixtures now see the session kind they set themselves instead of the installer's"},
|
||||
"scripts/test-paid-shards.ts": {"blob":"5439d0a8f6e1569fa7c924af20d67c30c81d5d30","since":6,"nonBehavioral":"credential rejections are classed infra instead of assertion: no case's pass or fail changes; only failed trials whose provider rejected the run's key move class, so no rerun spends on a rejected key"},
|
||||
"scripts/test-pr-profile.ts": {"blob":"ae76f90683d397be80e0368e520410157eefbc65","since":6,"nonBehavioral":"selection only: changelog.d fragments stop forcing the full PR gate; no case prompt, fixture, judge or verdict logic changes (GST-400)"},
|
||||
"scripts/test-pr-profile.ts": {"blob":"911e393d0c36c050c3a68db5a81ef21701bfd5ae","since":6,"nonBehavioral":"selection only: fragment exemption narrowed to .md files directly in the fragments dir; changed-file lists include a rename's old path; no case prompt, fixture, judge or verdict logic changes (GST-400)"},
|
||||
"scripts/test-strict-output.ts": {"blob":"01185d20dc9761b04b9b2f751ec98664fe0eaf93","since":6},
|
||||
"test/helpers/agent-sdk-runner.ts": {"blob":"210bba92d241a90df578b2c7fc943ac5755d0025","since":6},
|
||||
"test/helpers/auq-substance-panel.ts": {"blob":"4881260ef793668e4963a062db6d06a5e1f7a40f","since":6},
|
||||
@@ -50,7 +50,7 @@
|
||||
"test/helpers/session-runner.ts": {"blob":"cc65a66adafa9c053e3da3c6f17b5ac86c0eade7","since":6,"nonBehavioral":"Claude-host verdicts are unchanged: the Codex host (EVALS_HOST=codex, codex-host-runner) runs only when selected and records its own per-model series; the Codex runner refactor (execCodex extraction, onSpawn hook) keeps its events and verdicts and adds token cost accounting; host fields on the plan/report are absent for claude"},
|
||||
"test/helpers/skill-fixture.ts": {"blob":"8a654dae6b0a396d839b02179119ae3e3bb13f69","since":6,"nonBehavioral":"extractSkillBody and extractSkillSections follow the shared-preamble hoist (pointer plus sections/preamble.md); every fixture they build is byte-identical to the pre-hoist output (compared for 8 body and 4 section extractions), so no case's input or verdict changes; the /ship review then reworded only comments and two error messages, so no fixture byte changes"},
|
||||
"test/helpers/skill-startup.ts": {"blob":"a8d21e3817c3f35a1d06af325c92fde7d420e377","since":6,"nonBehavioral":"readSkillEffective also appends a variant's added ### subsections (office-hours' Open-ended questions); readSkillStartup, readSharedSections and readSkillEffective return bytes identical to the pinned helper for every tier 2+ render without added subsections (735 across all hosts) and for office-hours' SKILL.md before this PR, so office-hours' fixture input changes only because its prompt changed in this PR, not because the helper did"},
|
||||
"test/helpers/test-selection.ts": {"blob":"51655f89f66be7a5ec487e2abfe4744300ebd5f6","since":6},
|
||||
"test/helpers/test-selection.ts": {"blob":"e1b54ad5e3d3eadd8027de1b44e57554d6d99a41","since":6,"nonBehavioral":"selection only: fragment exemption narrowed to .md files directly in the fragments dir; changed-file lists include a rename's old path; no case prompt, fixture, judge or verdict logic changes (GST-400)"},
|
||||
"test/helpers/touchfiles.ts": {"blob":"15f369056d41b9dc42dcd86f81f60c878074704c","since":6}
|
||||
}
|
||||
}
|
||||
@@ -337,7 +337,7 @@ export function knownNonBehaviorFile(file: string): boolean {
|
||||
|| ['AGENTS.md', 'CLAUDE.md', 'agents-digest/gstack-AGENTS.md'].includes(file)
|
||||
|| FREE_ONLY_PR_FILES.some(pattern => matchGlob(file, pattern))
|
||||
|| RELEASE_METADATA_FILES.some(pattern => matchGlob(file, pattern))
|
||||
|| (CHANGELOG_FRAGMENTS_DIR !== null && file.startsWith(`${CHANGELOG_FRAGMENTS_DIR}/`))
|
||||
|| (CHANGELOG_FRAGMENTS_DIR !== null && path.posix.dirname(file) === CHANGELOG_FRAGMENTS_DIR && file.endsWith('.md'))
|
||||
|| (PERIODIC_ONLY_PR_FILES as readonly string[]).includes(file)
|
||||
|| (file.startsWith('test/fixtures/') && Object.keys(FREE_FIXTURES).some(pattern => matchGlob(file, pattern)))
|
||||
// Free test files anywhere (test/, browse/test/, design/test/, make-pdf/test/): no paid case runs or reads a free test.
|
||||
|
||||
@@ -105,6 +105,19 @@ describe('getChangedFiles union', () => {
|
||||
}
|
||||
});
|
||||
|
||||
// Value: protects=a rename lists its old path too, so a file moved into an exempt folder (changelog.d/, docs/) still selects the cases its old path fed; fails_when=getChangedFiles reports only the rename's new path (GST-400 adversarial finding);
|
||||
// why_new=no test covered renames; seam=git diff rename detection; no_claim=copies
|
||||
test('a committed rename lists both the old and the new path', () => {
|
||||
write('lib/foo.ts', 'export const foo = 1;\n'.repeat(20));
|
||||
git(['add', 'lib/foo.ts']);
|
||||
git(['commit', '-q', '-m', 'add foo']);
|
||||
git(['tag', '-f', 'base']);
|
||||
fs.mkdirSync(path.join(repo, 'changelog.d'));
|
||||
git(['mv', 'lib/foo.ts', 'changelog.d/foo.md']);
|
||||
git(['commit', '-q', '-m', 'move foo']);
|
||||
expect(getChangedFiles('base', repo).sort()).toEqual(['changelog.d/foo.md', 'lib/foo.ts']);
|
||||
});
|
||||
|
||||
test('missing base ref → throws naming EVALS_ALL and the failing command', () => {
|
||||
expect(() => getChangedFiles('no-such-ref', repo)).toThrow(/EVALS_ALL=1/);
|
||||
expect(() => getChangedFiles('no-such-ref', repo)).toThrow(/diff --name-only no-such-ref\.\.\.HEAD/);
|
||||
|
||||
@@ -123,9 +123,9 @@ export function getChangedFiles(
|
||||
// and the dependent test is silently DESELECTED — under-selection, the
|
||||
// exact direction selection must fail away from.
|
||||
const noQuote = ['-c', 'core.quotePath=false'];
|
||||
const committed = runGitOrThrow([...noQuote, 'diff', '--name-only', `${baseBranch}...HEAD`], cwd, spawnImpl)
|
||||
const committed = runGitOrThrow([...noQuote, 'diff', '--name-only', `${baseBranch}...HEAD`, '--no-renames'], cwd, spawnImpl)
|
||||
.trim().split('\n').filter(Boolean);
|
||||
const uncommitted = runGitOrThrow([...noQuote, 'diff', '--name-only', 'HEAD'], cwd, spawnImpl)
|
||||
const uncommitted = runGitOrThrow([...noQuote, 'diff', '--name-only', 'HEAD', '--no-renames'], cwd, spawnImpl)
|
||||
.trim().split('\n').filter(Boolean);
|
||||
const untracked = runGitOrThrow([...noQuote, 'status', '--porcelain', '--untracked-files=all'], cwd, spawnImpl)
|
||||
.split('\n')
|
||||
|
||||
@@ -67,6 +67,8 @@ describe('fast PR coverage policy', () => {
|
||||
test('a changelog.d fragment has no eval dependents; a touchfile naming one still selects its case (GST-400)', () => {
|
||||
const fragment = select({ selectedE2E: [], selectedJudges: [], changedFiles: ['changelog.d/gst-400.md', 'changelog.d/README.md'], derived: null });
|
||||
expect([fragment.mode, fragment.unknownFiles, fragment.e2e, fragment.needsFullValidation]).toEqual(['pr', [], [], false]);
|
||||
const outside = select({ selectedE2E: [], selectedJudges: [], changedFiles: ['changelog.d/x.ts', 'changelog.d/nested/x.md'], derived: null });
|
||||
expect([...outside.unknownFiles].sort()).toEqual(['changelog.d/nested/x.md', 'changelog.d/x.ts']);
|
||||
const real = selectPrProfile({ selectedE2E: [], selectedJudges: [], changedFiles: ['changelog.d/gst-400.md'] });
|
||||
expect([real.mode, real.unknownFiles]).toEqual(['pr', []]);
|
||||
const table = { ...maps.e2eTouchfiles, 'ceo-smoke': ['plan-ceo-review/**', 'changelog.d/ceo.md'] };
|
||||
|
||||
Reference in new issue
Block a user