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
17 changes: 12 additions & 5 deletions test/gh-aw-cast-validator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ Architecture, Implementation, Quality
function createFixture(): { root: string; payload: string; runnerTemp: string } {
const root = mkdtempSync(join(tmpdir(), 'gh-aw-cast-validator-'));
workspaces.push(root);
const runnerTemp = join(root, 'runner-temp');
mkdirSync(runnerTemp, { recursive: true });
const runnerTemp = mkdtempSync(join(tmpdir(), 'gh-aw-cast-runner-temp-'));
workspaces.push(runnerTemp);
write(root, '.squad/team.md', teamMarkdown());
Comment thread
bradygaster marked this conversation as resolved.
Resolved
write(root, '.squad/routing.md', routingMarkdown());
write(root, '.squad/casting/registry.json', JSON.stringify({
Expand All @@ -137,7 +137,8 @@ function createFixture(): { root: string; payload: string; runnerTemp: string }
}
write(root, '.github/agents/squad.agent.md', coordinatorMarkdown());
write(root, 'meet-the-squad.md', '# Meet the Squad\n');
const payload = join(runnerTemp, 'squad-cast-payload.json');
const payload = join(root, '.github', 'workflows', 'squad-cast-payload.json');
mkdirSync(dirname(payload), { recursive: true });
writeFileSync(payload, JSON.stringify(corePayload), 'utf8');
return { root, payload, runnerTemp };
}
Expand Down Expand Up @@ -211,7 +212,7 @@ function materializeRunner(
fixture: ReturnType<typeof createFixture>,
source = validatorRunnerSource(),
): string {
const runner = join(fixture.runnerTemp, 'run-squad-cast-validator');
const runner = join(fixture.root, '.github', 'workflows', 'run-squad-cast-validator');
writeFileSync(runner, source, 'utf8');
chmodSync(runner, 0o500);
return runner;
Expand Down Expand Up @@ -307,7 +308,9 @@ describe('GH-AW Cast final-tree validator', () => {
const command = validatorCommand();
const runner = validatorRunnerSource();
const workflow = readFileSync(workflowPath, 'utf8');
expect(command.trim()).toBe('"${RUNNER_TEMP:?}/run-squad-cast-validator"');
expect(command.trim()).toBe(
'"${GITHUB_WORKSPACE:?}/.github/workflows/run-squad-cast-validator"',
);
expect(command).not.toMatch(/[A-Za-z0-9+/]{256}/);
expect(command).not.toMatch(/cat\s+<<|base64|gzip|awk|validator_expected_sha256/);
expect(command).not.toContain('H4sI');
Expand All @@ -318,6 +321,7 @@ describe('GH-AW Cast final-tree validator', () => {
expect(runner).toContain(
'validator_script="${GITHUB_WORKSPACE:?}/.github/workflows/shared/squad-cast-validator.mjs"',
);
expect(runner).not.toContain('RUNNER_TEMP');
expect(runner.indexOf('validator_expected_sha256=')).toBeLessThan(
runner.indexOf('node --check "$validator_script"'),
);
Expand All @@ -335,8 +339,11 @@ describe('GH-AW Cast final-tree validator', () => {
expect(result.status, result.stderr).toBe(0);
expect(result.stdout).toBe('Cast validation passed.\n');
expect(authorizesPullRequest(result)).toBe(true);
expect(fixture.payload.startsWith(fixture.root)).toBe(true);
expect(fixture.payload.startsWith(fixture.runnerTemp)).toBe(false);
// Never copied or extracted into $RUNNER_TEMP -- executed in place from $GITHUB_WORKSPACE.
expect(existsSync(join(fixture.runnerTemp, 'validate-gh-aw-cast.mjs'))).toBe(false);
expect(existsSync(join(fixture.runnerTemp, 'run-squad-cast-validator'))).toBe(false);
});

it('fails clearly when the installed validator resource is missing', () => {
Expand Down
13 changes: 11 additions & 2 deletions test/gh-aw-quality.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1479,6 +1479,13 @@ describe('gh-aw: compiled workflow shell input security contract', () => {
)?.[0] ?? '';
const normalizedRunnerStep = runnerStep.replace(/\\"/g, '"');
expect(normalizedRunnerStep).toContain('cat > "$validator_runner"');
expect(normalizedRunnerStep).toContain(
'validator_runner="${GITHUB_WORKSPACE:?}/.github/workflows/run-squad-cast-validator"',
);
expect(normalizedRunnerStep).toContain(
'--payload "${GITHUB_WORKSPACE:?}/.github/workflows/squad-cast-payload.json"',
);
expect(normalizedRunnerStep).not.toContain('RUNNER_TEMP');
expect(normalizedRunnerStep).toContain('validator_expected_sha256="82aa5620d81e26513658fbde210b0f8d2ac3bc7572e672b421aaa17a2832e8cc"');
expect(normalizedRunnerStep).toContain("outcome: 'cast_failure'");
expect(normalizedRunnerStep).toContain('chmod 500 "$validator_runner"');
Expand Down Expand Up @@ -2670,7 +2677,9 @@ describe('gh-aw: Cast replaces disposable bootstrap state (#1909)', () => {
expect(candidateCast).toMatch(
/only exit status zero with stdout exactly\s+`Cast validation passed\.` authorizes exactly one `create-pull-request`/s,
);
expect(candidateCast).toContain('"${RUNNER_TEMP:?}/run-squad-cast-validator"');
expect(candidateCast).toContain(
'"${GITHUB_WORKSPACE:?}/.github/workflows/run-squad-cast-validator"',
);
expect(candidateCast).toMatch(/Do not transcribe validator bytes, and do not invoke or\s+load/);
expect(candidateCast).toContain('emits one JSON\nrecord on stdout');
expect(candidateCast.match(
Expand Down Expand Up @@ -2758,7 +2767,7 @@ describe('gh-aw: Cast replaces disposable bootstrap state (#1909)', () => {

it('enforces mutually exclusive factual Cast terminal outcomes', () => {
expect(cast).toContain('Deterministic final-tree validation');
expect(cast).toContain('$RUNNER_TEMP/squad-cast-payload.json');
expect(cast).toContain('$GITHUB_WORKSPACE/.github/workflows/squad-cast-payload.json');
expect(cast).toContain('squad-cast-validator');
assertTruthfulCastTerminalContract(squadContent);
});
Expand Down
27 changes: 14 additions & 13 deletions workflows/squad.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,16 +55,16 @@ steps:
shell: bash
run: |
set -euo pipefail
validator_runner="${RUNNER_TEMP:?}/run-squad-cast-validator"
validator_runner="${GITHUB_WORKSPACE:?}/.github/workflows/run-squad-cast-validator"
cat > "$validator_runner" <<'SQUAD_CAST_VALIDATOR_RUNNER'
#!/usr/bin/env bash
set -u -o pipefail
cd "${GITHUB_WORKSPACE:?}"

stderr_file="${RUNNER_TEMP:?}/squad-cast-validator.stderr"
stderr_file="${GITHUB_WORKSPACE:?}/.github/workflows/squad-cast-validator.stderr"
validator_script="${GITHUB_WORKSPACE:?}/.github/workflows/shared/squad-cast-validator.mjs"
validator_output="${RUNNER_TEMP:?}/squad-cast-validator.stdout"
expected_output="${RUNNER_TEMP:?}/squad-cast-validator.expected"
validator_output="${GITHUB_WORKSPACE:?}/.github/workflows/squad-cast-validator.stdout"
expected_output="${GITHUB_WORKSPACE:?}/.github/workflows/squad-cast-validator.expected"

fail_cast() {
local stage="$1"
Expand Down Expand Up @@ -122,7 +122,7 @@ steps:
: > "$stderr_file"
node "$validator_script" \
--root "$PWD" \
--payload "${RUNNER_TEMP:?}/squad-cast-payload.json" \
--payload "${GITHUB_WORKSPACE:?}/.github/workflows/squad-cast-payload.json" \
> "$validator_output" 2> "$stderr_file"
status=$?
if [ "$status" -ne 0 ]; then
Expand Down Expand Up @@ -1002,17 +1002,18 @@ its charter from scratch.
##### Step 7: Deterministic final-tree validation

Natural-language review is not the gate. Immediately before requesting safe
output, create `$RUNNER_TEMP/squad-cast-payload.json` as a JSON array containing
every concrete Step 6 payload path, then invoke the prepared runner with the
exact command below. Do not transcribe validator bytes, and do not invoke or
load `squad-cast-validator` into model context. The runner was prepared before
this turn and deterministically executes the plaintext validator resource that
gh-aw installed at `.github/workflows/shared/squad-cast-validator.mjs` under
`$GITHUB_WORKSPACE`.
output, create
`$GITHUB_WORKSPACE/.github/workflows/squad-cast-payload.json` as a JSON array
containing every concrete Step 6 payload path, then invoke the prepared runner
with the exact command below. Do not transcribe validator bytes, and do not invoke or
load `squad-cast-validator` into model context. The runner was
prepared before this turn under the sandbox-visible `$GITHUB_WORKSPACE` and
deterministically executes the plaintext validator resource that gh-aw installed
at `.github/workflows/shared/squad-cast-validator.mjs`.

<!-- SQUAD:CAST-VALIDATOR-COMMAND:BEGIN -->
```bash
"${RUNNER_TEMP:?}/run-squad-cast-validator"
"${GITHUB_WORKSPACE:?}/.github/workflows/run-squad-cast-validator"
```
<!-- SQUAD:CAST-VALIDATOR-COMMAND:END -->

Expand Down