mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-05 16:34:00 +01:00
test: cover the Codex default-prompt render, the openai.yaml ownership proof and the unattended grant
Adds the /ship coverage audit's tests: test/host-renders-openai-yaml-proof.test.ts drives bin/gstack-host-renders.sh's weakened proof against a rendered yaml, a hand-written one and a generic-prompt one; test/unattended-grant.test.ts pins the docs/unattended.md grant section and the handoff pointer; test/gen-skill-docs.test.ts adds modernize to the skills whose codex_default_prompt must reach openai.yaml. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
6b14d7da4b
commit
d4793fa9be
3 files changed
+99
-1
No files matched your search
@@ -2933,7 +2933,8 @@ describe('Codex generation (--host codex)', () => {
|
||||
// A template's codex_default_prompt: key is the sentence Codex's picker
|
||||
// inserts for the skill; without the key the generic fallback keeps every
|
||||
// entry valid. The key is generator input only: no host render carries it.
|
||||
const CODEX_PROMPT_SKILLS = ['ship', 'review', 'qa', 'autoplan', 'office-hours', 'land-and-deploy'];
|
||||
// Value: protects=the new /modernize skill's picker prompt reaches its Codex render; fails_when=modernize's key is dropped, misquoted or unrendered; why_new=modernize-skill.test reads the template only; seam=none; no_claim=prompt wording quality
|
||||
const CODEX_PROMPT_SKILLS = ['ship', 'review', 'qa', 'autoplan', 'office-hours', 'land-and-deploy', 'modernize'];
|
||||
test('codex_default_prompt: in a template becomes that skill\'s default_prompt', () => {
|
||||
for (const dir of CODEX_PROMPT_SKILLS) {
|
||||
const tmpl = fs.readFileSync(path.join(ROOT, dir, 'SKILL.md.tmpl'), 'utf-8');
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
/**
|
||||
* bin/gstack-host-renders.sh: the WEAK proof that an agents/openai.yaml is the
|
||||
* generator's own render, now that a template's codex_default_prompt: makes
|
||||
* the default_prompt line vary per skill (it used to be one fixed sentence).
|
||||
*
|
||||
* Value: protects=_gstack_hr_generated accepts the generator's openai.yaml for any default_prompt and still rejects a foreign or edited one; fails_when=the shape check reverts to the generic-prompt literal, or drops display_name/policy/6-line limits; why_new=host-renders-prune asserts one backed-up line; no test exercises the rejected shapes; seam=none; no_claim=what the prune does after the proof
|
||||
*/
|
||||
import { describe, expect, test } from 'bun:test';
|
||||
import { spawnSync } from 'node:child_process';
|
||||
import * as fs from 'node:fs';
|
||||
import * as os from 'node:os';
|
||||
import * as path from 'node:path';
|
||||
import { generateOpenAIYaml } from '../scripts/gen-skill-docs';
|
||||
|
||||
const ROOT = path.resolve(import.meta.dir, '..');
|
||||
const HELPER = path.join(ROOT, 'bin/gstack-host-renders.sh');
|
||||
|
||||
describe.skipIf(process.platform === 'win32')('_gstack_hr_generated proves an openai.yaml by shape, not by its prompt sentence', () => {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'gstack-openai-yaml-proof-'));
|
||||
const tree = path.join(dir, '.agents');
|
||||
const real = path.join(dir, 'checkout');
|
||||
fs.mkdirSync(real, { recursive: true });
|
||||
|
||||
function proven(rel: string, content: string): boolean {
|
||||
const file = path.join(tree, rel);
|
||||
fs.mkdirSync(path.dirname(file), { recursive: true });
|
||||
fs.writeFileSync(file, content);
|
||||
const r = spawnSync('bash', ['-c', `. "${HELPER}"\n_gstack_hr_generated "${file}" "${tree}" "${real}" && echo proven || echo kept`], {
|
||||
encoding: 'utf8', timeout: 30_000, env: { PATH: process.env.PATH ?? '/usr/bin:/bin', HOME: os.tmpdir() },
|
||||
});
|
||||
expect(r.status, r.stderr).toBe(0);
|
||||
return r.stdout.trim() === 'proven';
|
||||
}
|
||||
|
||||
const custom = generateOpenAIYaml('gstack-ship', 'short', 'Use $gstack-ship to take this branch to a pull request: "quoted", colons: too.');
|
||||
const generic = generateOpenAIYaml('gstack-careful', 'short');
|
||||
|
||||
test('the generator\'s render is proven whether the prompt is the skill\'s own sentence or the fallback', () => {
|
||||
expect(custom).not.toContain('Use gstack-ship for this task.');
|
||||
expect(proven('skills/gstack-ship/agents/openai.yaml', custom)).toBe(true);
|
||||
expect(generic).toContain('default_prompt: "Use gstack-careful for this task."');
|
||||
expect(proven('skills/gstack-careful/agents/openai.yaml', generic)).toBe(true);
|
||||
});
|
||||
|
||||
test('a file that is not the generator\'s shape for that skill directory is kept, not backed up', () => {
|
||||
expect(proven('skills/gstack-qa/agents/openai.yaml', custom), 'display_name names another skill').toBe(false);
|
||||
expect(proven('skills/gstack-ship/agents/openai.yaml', custom.replace('policy:\n', 'rules:\n')), 'no policy: block').toBe(false);
|
||||
expect(proven('skills/gstack-ship/agents/openai.yaml', custom.replace(' default_prompt: "', ' default_prompt: \'')), 'prompt not double-quoted').toBe(false);
|
||||
expect(proven('skills/gstack-ship/agents/openai.yaml', custom + ' my_note: hand-written\n'), 'a seventh line').toBe(false);
|
||||
expect(proven('skills/gstack-ship/agents/openai.yaml', 'notes:\n' + custom.slice('interface:\n'.length)), 'first line is not interface:').toBe(false);
|
||||
expect(proven('skills/gstack-ship/openai.yaml', custom), 'not under agents/').toBe(false);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,44 @@
|
||||
/**
|
||||
* docs/unattended.md#what-an-unattended-session-may-do: the grant a parent
|
||||
* gives an unattended run, and the pointer /ship's handoff section carries so a
|
||||
* lane reads it where it reports done.
|
||||
*
|
||||
* Value: protects=the handoff pointer resolves to the grant section, and the grant states merge-never, the pinned-lease push form and the /modernize disposition; fails_when=the heading is renamed or moved, the pointer is dropped from the template or its render, or the grant loses a rule; why_new=no test reads the new section or the handoff pointer; seam=none; no_claim=that a run obeys the grant (eval-side)
|
||||
*/
|
||||
import { describe, expect, test } from 'bun:test';
|
||||
import * as fs from 'node:fs';
|
||||
import * as path from 'node:path';
|
||||
|
||||
const ROOT = path.resolve(import.meta.dir, '..');
|
||||
const read = (rel: string) => fs.readFileSync(path.join(ROOT, rel), 'utf8');
|
||||
const POINTER = 'docs/unattended.md#what-an-unattended-session-may-do';
|
||||
const slug = (heading: string) => heading.toLowerCase().replace(/[^a-z0-9 -]/g, '').trim().replace(/ /g, '-');
|
||||
|
||||
describe('the unattended grant and the /ship handoff pointer to it', () => {
|
||||
const docs = read('docs/unattended.md');
|
||||
const grantStart = docs.indexOf('\n## What an unattended session may do\n');
|
||||
const grant = docs.slice(grantStart + 1, (i => i === -1 ? docs.length : i)(docs.indexOf('\n## ', grantStart + 1)));
|
||||
|
||||
test('the handoff section points at a heading that exists, in the template and its render', () => {
|
||||
expect(grantStart).toBeGreaterThan(0);
|
||||
const heading = grant.slice(3, grant.indexOf('\n'));
|
||||
expect(`docs/unattended.md#${slug(heading)}`).toBe(POINTER);
|
||||
expect(read('ship/sections/handoff.md.tmpl')).toContain(`What an\nunattended run may do is ${POINTER}.`);
|
||||
expect(read('ship/sections/handoff.md')).toContain(POINTER);
|
||||
expect(grant).toContain('`/ship`\'s handoff section (`ship/sections/handoff.md`) points here');
|
||||
});
|
||||
|
||||
test('the grant names each rule once: merge never, a pinned-lease force-push on its own branch, named test methods, own-eyes proof', () => {
|
||||
const flat = grant.replace(/\s+/g, ' ');
|
||||
expect(flat).toContain('- **Merge: never.**');
|
||||
expect(flat).toContain('- **Force-push: its own branch only, after a rebase the plan named, with a pinned lease.**');
|
||||
expect(flat).toContain('- **Tests: the methods the repository\'s instructions name, without asking first.**');
|
||||
expect(flat).toContain('- **Proof is what the run saw itself.**');
|
||||
expect(flat).toContain('`git push --force-with-lease=<branch>:<sha observed before the rebase> origin <branch>`');
|
||||
expect(flat).toContain('a plain `--force` is never allowed');
|
||||
expect(flat).toContain('`git range-diff <old base>..<old head> <new base>..<new head>` runs before the push');
|
||||
expect(flat).toContain('a `/modernize` disposition names before it happens');
|
||||
expect(flat).toContain('enabling auto-merge, adding the PR to a merge queue, or `gh pr ready` on a PR the run did not open');
|
||||
expect(flat).toContain('appended to `decisions.jsonl` with `kind: "approval"` and `status: "pending"`');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user