mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-13 03:48:06 +01:00
W7c (t06 #19). One test file. Removed assertion groups: - "A genuine blocker may require a safety or named documentation-risk decision" and "routine interactions and blocking decisions above" sentence pins. Invariant: routine release authorization never waives a blocking safety/docs decision. Remaining protection: kept free meaning check `/stop for that decision rather than treating release authorization as a waiver/i` plus `/named documentation-risk exception before distribution/i`. - Full-sentence "in `read-only` mode against the selected release source". Invariant: the docs preflight is read-only. Remaining protection: kept free check on "`read-only` mode". Loosened (case-insensitive, kept): apply-only-to-repository-landing scope line, CLASSIFY-before-credentials (credential boundary), never abort a store release over branch topology (demonstrated failure), two routine interactions (numeric limit), stop-for-decision (consent/stop line). Kept unchanged: manifest trigger routing, Apple read before the branch gate, byte-exact branch gate appearing once, docs preflight before the authorization moment, fastlane/iris/appPriceSchedules command anchors, stale-wording bans. No gate-tier E2E covers the Apple adapter, so every invariant keeps a free check here. Cosmetic pins dropped: capitals on ONLY / CLASSIFY / STOP. Mutation log: .context/mutation-ship-apple-gate.md (14 mutations, 0 unexpected: 2 reorders, 1 marker and 6 safety mutations red; 5 rewordings green).
73 lines
3.7 KiB
TypeScript
73 lines
3.7 KiB
TypeScript
/**
|
|
* R2 pin (fork port wave 2): the Apple release adapter loads BEFORE ship's
|
|
* branch gate, and the non-Apple gate is byte-unchanged.
|
|
*
|
|
* Two failure modes this prevents: a future ship-template refactor that
|
|
* re-blocks store releases behind "ship from a feature branch" (the exact
|
|
* live failure the fork hit — a solo dev with a clean tree on main shipping
|
|
* to TestFlight got aborted over branch topology), and the reverse — the
|
|
* Apple path accidentally weakening the branch gate for normal
|
|
* repository-landing ships.
|
|
*/
|
|
|
|
import { describe, test, expect } from "bun:test";
|
|
import { readFileSync } from "fs";
|
|
import { join } from "path";
|
|
|
|
const ROOT = join(import.meta.dir, "..");
|
|
const SKELETON = readFileSync(join(ROOT, "ship", "SKILL.md"), "utf-8");
|
|
|
|
const GATE_TEXT =
|
|
'If on the base branch or the repo\'s default branch, **abort**: "You\'re on the base branch. Ship from a feature branch."';
|
|
|
|
describe("ship Apple gate ordering (R2)", () => {
|
|
test("the section index requires a store-distribution request, not merely an Apple repository", () => {
|
|
const manifest = JSON.parse(readFileSync(join(ROOT, "ship", "sections", "manifest.json"), "utf-8"));
|
|
const apple = manifest.sections.find((section: { id: string }) => section.id === "apple-release");
|
|
expect(apple.trigger).toContain("App Store/TestFlight distribution is requested for an Apple app");
|
|
expect(apple.trigger).toContain("an Apple repository-landing request follows the normal pipeline");
|
|
expect(SKELETON).toContain("is App Store/TestFlight distribution");
|
|
});
|
|
|
|
test("the Apple adapter read directive precedes the branch gate", () => {
|
|
const appleRead = SKELETON.indexOf("sections/apple-release.md");
|
|
const gate = SKELETON.indexOf(GATE_TEXT);
|
|
expect(appleRead).toBeGreaterThan(-1);
|
|
expect(gate).toBeGreaterThan(-1);
|
|
expect(appleRead).toBeLessThan(gate);
|
|
});
|
|
|
|
test("store distribution explicitly bypasses the branch/PR ceremony", () => {
|
|
expect(SKELETON).toContain("Store distribution proceeds");
|
|
expect(SKELETON).toMatch(/branch gate and repository-landing pipeline below apply only to\s+repository-landing asks/i);
|
|
});
|
|
|
|
test("the non-Apple branch gate is byte-unchanged and appears exactly once", () => {
|
|
const first = SKELETON.indexOf(GATE_TEXT);
|
|
expect(first).toBeGreaterThan(-1);
|
|
expect(SKELETON.indexOf(GATE_TEXT, first + 1)).toBe(-1);
|
|
});
|
|
|
|
test("the adapter section exists in the union with its battle-tested spine", () => {
|
|
const section = readFileSync(join(ROOT, "ship", "sections", "apple-release.md"), "utf-8");
|
|
for (const anchor of ["one authorization moment", "fastlane spaceauth", "iris/v1/apiKeys", "appPriceSchedules"]) {
|
|
expect(section).toContain(anchor);
|
|
}
|
|
expect(section).toMatch(/classify the error before touching credentials/i);
|
|
expect(section).toMatch(/never abort an App Store release over branch topology/i);
|
|
});
|
|
|
|
test("routine interaction limits cannot waive blocking documentation or safety decisions", () => {
|
|
for (const name of ["apple-release.md.tmpl", "apple-release.md"]) {
|
|
const section = readFileSync(join(ROOT, "ship", "sections", name), "utf-8").replace(/\s+/g, " ");
|
|
expect(section).toMatch(/two routine interactions/i);
|
|
expect(section).toMatch(/stop for that decision rather than treating release authorization as a waiver/i);
|
|
expect(section).not.toContain("exactly two interactions, and no others");
|
|
expect(section).not.toContain("two permitted interactions");
|
|
expect(section.indexOf("**Documentation preflight:**")).toBeLessThan(section.indexOf("## The one authorization moment"));
|
|
expect(section).toContain("`read-only` mode");
|
|
expect(section).toMatch(/named documentation-risk exception before distribution/i);
|
|
}
|
|
});
|
|
});
|