mirror of
https://github.com/garrytan/gstack.git
synced 2026-11-05 08:23:59 +01:00
feat(captain): postmortem template and step, first on #3787; Unverified line in READY and lane reports (GST-805) (#3807)
* test(captain): pin the Unverified line and the postmortem template (GST-805, failing first) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * feat(captain): postmortem template and step; Unverified line in READY and lane reports (GST-805) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes (GST-805: the lane guide says the lane writes Evidence and Unverified, /ship items 1-4; rewrap the READY contract paragraph) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: CREW template rules 6-7 and the rule map name the Unverified line (GST-805 docs audit a242-1) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(archaeology): allow the captain skill to name its postmortem template (GST-805) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(captain): trim Reports and READY-options prose so the merged skill stays under its 6,551-token ceiling (GST-805) Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
46c9f612c0
commit
15cbc274d9
11 files changed
+188
-49
No files matched your search
@@ -29,6 +29,7 @@ const ALLOWLIST: AllowEntry[] = [
|
||||
{ skills: ['landing-report'], anchor: 'feat/payments', reason: 'sample landing-report output (PR number of a sibling workspace)' },
|
||||
{ skills: ['retro'], anchor: 'Biggest ship: PR #605', reason: 'sample retro output' },
|
||||
{ skills: ['codex'], anchor: 'OpenAI issues #8545', reason: 'upstream OpenAI Codex issue IDs behind the reasoning-effort default' },
|
||||
{ skills: ['captain'], anchor: 'captain/templates/postmortem.md', reason: 'names the postmortem template the controller fills after a revert, a procedure rather than an incident story' },
|
||||
];
|
||||
|
||||
/** `#` + 3 or more digits (bare or parenthesized), `PR #N`, `issue #N`, incident stories. */
|
||||
|
||||
@@ -15,7 +15,7 @@ const read = (rel: string) => fs.readFileSync(path.join(ROOT, rel), 'utf8');
|
||||
const skill = read('captain/SKILL.md');
|
||||
const tmpl = read('captain/SKILL.md.tmpl');
|
||||
const flat = skill.replace(/\s+/g, ' ');
|
||||
const TEMPLATES = ['CREW.md', 'AGENTS.md', 'NOTES.md', 'DIGEST.md', 'flaky-ledger.md', 'report.md', 'READY.md', 'slot-call.md', 'plan-ask.md', 'merge-ask.md', 'automation-flaky-pass.md', 'brief.md', 'merge-train-peer.md'];
|
||||
const TEMPLATES = ['CREW.md', 'AGENTS.md', 'NOTES.md', 'DIGEST.md', 'flaky-ledger.md', 'report.md', 'READY.md', 'slot-call.md', 'plan-ask.md', 'merge-ask.md', 'automation-flaky-pass.md', 'brief.md', 'merge-train-peer.md', 'postmortem.md'];
|
||||
|
||||
describe('/captain skill', () => {
|
||||
test('frontmatter names the skill and its /crew alias trigger', () => {
|
||||
@@ -32,6 +32,33 @@ describe('/captain skill', () => {
|
||||
expect(flat).toContain('A green list state, a passing PR page, a "looks good" in chat and a cheap-tier pass are not READYs');
|
||||
});
|
||||
|
||||
test('READY and report carry an Unverified line that is asked for, never refused', () => {
|
||||
const ready = read('captain/templates/READY.md');
|
||||
expect(ready).toMatch(/^review passes: .*\nUnverified: <none \| [^>]+>\nHead frozen from now on;/m);
|
||||
const readyFlat = ready.replace(/\s+/g, ' ');
|
||||
for (const s of ['a review finding set aside on an assumption nobody tested', 'it never holds a PR out of a train, does not stale READYs sent before the line existed, and needs no new `ready` or /ship run']) {
|
||||
expect(readyFlat, s).toContain(s);
|
||||
}
|
||||
expect(read('captain/templates/report.md')).toMatch(/## Outcome\n\n<[^>]+>\n\nUnverified: <none \| [^>]+>\n\n## PR\n/);
|
||||
expect(skill).toMatch(/^\| `Unverified` \| each claim the lane did not check itself, or `none` \| never refused or staled; ask for it \|$/m);
|
||||
expect(flat).toContain('Outcome with its `Unverified:` line');
|
||||
const docFlat = read('docs/captain.md').replace(/\s+/g, ' ');
|
||||
expect(docFlat).toContain('The `Unverified:` line is the one exception: the controller asks for a missing line in a follow-up message, never refuses or stales a READY for it');
|
||||
expect(read('capy/skill/gstack/captain.md').replace(/\s+/g, ' ')).toContain('an `Unverified:` line: each claim you did not check yourself and why, or `none`');
|
||||
});
|
||||
|
||||
test('the postmortem template asks why every reviewer believed it, and the step ships its rule change to gstack', () => {
|
||||
const pm = read('captain/templates/postmortem.md');
|
||||
expect(pm).toMatch(/^## What broke$[^]*^## How it was found$[^]*^## Impact$[^]*^## Timeline$[^]*^## Root cause: why did the lane and every reviewer believe it was right\?$[^]*^## What caught it and what should have$[^]*^## Playbook or skill change$[^]*^## Links$/m);
|
||||
expect(pm).toMatch(/^\| Believer \| What it was asked \| What it checked \| What it did not check \| Why it passed \| Source \|$/m);
|
||||
expect(pm).toMatch(/^\| Change \| Home in gstack \| Owner \| Status \| Effectiveness check \|$/m);
|
||||
const pmFlat = pm.replace(/\s+/g, ' ');
|
||||
for (const s of ['not from the diff alone', 'mark any item `not retained`', 'label anything else `inferred`', 'A change kept only in a crew file is a gap']) expect(pmFlat, s).toContain(s);
|
||||
expect(pm, 'no real PR numbers in the generic template').not.toMatch(/#\d{2,}/);
|
||||
expect(flat).toContain('After a revert or S1 fix of a merged crew PR, the controller writes a postmortem (`captain/templates/postmortem.md`): why did the lane and every reviewer believe it was right? Its rule change ships as a gstack PR.');
|
||||
expect(read('docs/captain.md').replace(/\s+/g, ' ')).toContain('Each rule change it names ships as a gstack PR, because gstack holds the how');
|
||||
});
|
||||
|
||||
test('teaches the train loop, the red-train rules and the backstop', () => {
|
||||
for (const step of ['train build', 'train verdict', 'train bisect', 'train rebuild', 'train prune', 'verify --pr', '--match-head-commit', 'FLAKY N', 'exactly one unchanged', 'FREEZE: cleared', 'The tree rule', 'Gate-ahead (option)', 'Severe race (option)', 'beyond `STAMP_FILES`']) {
|
||||
expect(skill, step).toContain(step);
|
||||
|
||||
Reference in new issue
Block a user