vouchington-tooling 0.22.1 → 0.23.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -0
- package/dist/markdown/details-scope.d.mts +13 -0
- package/dist/markdown/details-scope.mjs +73 -0
- package/dist/markdown/document-sections.d.mts +3 -0
- package/dist/markdown/document-sections.mjs +64 -0
- package/dist/markdown/document-types.d.mts +21 -0
- package/dist/markdown/document-types.mjs +1 -0
- package/dist/markdown/index.d.mts +3 -0
- package/dist/markdown/index.mjs +2 -0
- package/dist/markdown/validate-sections.d.mts +3 -0
- package/dist/markdown/validate-sections.mjs +34 -0
- package/package.json +1 -1
- package/skills/agent-workflow/SKILL.md +5 -0
- package/skills/agent-workflow/references/review.md +3 -0
- package/skills/manifest.json +21 -21
- package/skills/pr-description/SKILL.md +44 -10
- package/skills/pr-description/references/examples.md +136 -0
- package/skills/review-ci-logs/SKILL.md +10 -0
package/README.md
CHANGED
|
@@ -338,6 +338,32 @@ shape, including short table delimiters. `extractLooseMarkdownTableRows` is inte
|
|
|
338
338
|
recovery for malformed pipe rows; callers supply table positions to exclude and retain ownership of
|
|
339
339
|
their policy interpretation.
|
|
340
340
|
|
|
341
|
+
`parseMarkdownSections` returns visible root H2 sections, their exact original `content`,
|
|
342
|
+
`startOffset`/`endOffset` source ranges, heading line, and `hasVisibleContent`. Heading text is
|
|
343
|
+
normalized by collapsing whitespace and removing Markdown formatting/comments and collapsed text; matching remains
|
|
344
|
+
case-sensitive. Source offsets are JavaScript string offsets, so `source.slice(startOffset,
|
|
345
|
+
endOffset)` preserves Unicode and CRLF exactly. Sections end at the next visible root H2 or EOF;
|
|
346
|
+
fenced examples, quoted headings, and headings inside `<details>` do not create boundaries.
|
|
347
|
+
|
|
348
|
+
```ts
|
|
349
|
+
import { parseMarkdownSections, validateMarkdownSections } from 'vouchington-tooling/markdown'
|
|
350
|
+
|
|
351
|
+
const document = parseMarkdownSections(body)
|
|
352
|
+
const diagnostics = validateMarkdownSections(document, {
|
|
353
|
+
requiredHeadings: ['Summary', 'Impact'],
|
|
354
|
+
})
|
|
355
|
+
const relatedIssues = document.sections.find((section) => section.heading === 'Related issues')
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
The caller owns required headings. Validation returns diagnostics with `code`, `message`, and
|
|
359
|
+
optional `heading`/`line`, for `missing-heading`, `duplicate-heading`, `empty-section`, or
|
|
360
|
+
`malformed-details`. Nonempty visible paragraphs, lists, and populated table bodies satisfy the
|
|
361
|
+
content check; comments, fenced/indented code, images, disclosure labels, and collapsed-only content
|
|
362
|
+
do not. The parser tracks balanced nested details/summary tags in GFM HTML nodes, accepting optional
|
|
363
|
+
summaries and quoted attributes; it is not a general HTML validator or sanitizer. It never rewrites
|
|
364
|
+
the input or infers semantic relevance, cost, schema conformance, or failure causes. The existing
|
|
365
|
+
`markdownSectionBetweenHeadings` API retains its original boundary behavior.
|
|
366
|
+
|
|
341
367
|
`agent-harness-config` merges classifier-auto and sandbox keys into Claude, Codex, Grok, and Cursor
|
|
342
368
|
config files. See [docs/agent-harness-config.md](./docs/agent-harness-config.md). `--global` updates
|
|
343
369
|
home-directory configs; `--repo` updates a checkout. It does not copy allowlists, hooks, or plugins.
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { MarkdownSectionsDiagnostic } from './document-types.mts';
|
|
2
|
+
/** Tracks disclosure tags from HTML nodes, never from fenced code or Markdown comments. */
|
|
3
|
+
export declare class DetailsScope {
|
|
4
|
+
private readonly markdown;
|
|
5
|
+
readonly diagnostics: MarkdownSectionsDiagnostic[];
|
|
6
|
+
private readonly frames;
|
|
7
|
+
constructor(markdown: string);
|
|
8
|
+
get visible(): boolean;
|
|
9
|
+
acceptHtml(html: string, startOffset: number): void;
|
|
10
|
+
finish(): void;
|
|
11
|
+
private acceptTag;
|
|
12
|
+
private problem;
|
|
13
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/** Tracks disclosure tags from HTML nodes, never from fenced code or Markdown comments. */
|
|
2
|
+
export class DetailsScope {
|
|
3
|
+
markdown;
|
|
4
|
+
diagnostics = [];
|
|
5
|
+
frames = [];
|
|
6
|
+
constructor(markdown) {
|
|
7
|
+
this.markdown = markdown;
|
|
8
|
+
}
|
|
9
|
+
get visible() {
|
|
10
|
+
return this.frames.length === 0;
|
|
11
|
+
}
|
|
12
|
+
acceptHtml(html, startOffset) {
|
|
13
|
+
const tokens = /<!--[\s\S]*?(?:-->|$)|<\/?[a-z][a-z\d:-]*(?:[^"'<>]|"[^"]*"|'[^']*')*>/giu;
|
|
14
|
+
for (const token of html.matchAll(tokens)) {
|
|
15
|
+
if (token[0].startsWith('<!--'))
|
|
16
|
+
continue;
|
|
17
|
+
const tag = /^<(\/?)([a-z][a-z\d:-]*)/iu.exec(token[0]);
|
|
18
|
+
const name = tag[2].toLowerCase();
|
|
19
|
+
if (name !== 'details' && name !== 'summary')
|
|
20
|
+
continue;
|
|
21
|
+
const line = this.markdown.slice(0, startOffset + token.index).split('\n').length;
|
|
22
|
+
if (/\/\s*>$/u.test(token[0])) {
|
|
23
|
+
this.problem(line, `${tag[2]} cannot be self-closing`);
|
|
24
|
+
continue;
|
|
25
|
+
}
|
|
26
|
+
this.acceptTag(name, tag[1] === '/', line);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
finish() {
|
|
30
|
+
for (const frame of this.frames)
|
|
31
|
+
this.problem(frame.line, 'details has no closing tag');
|
|
32
|
+
}
|
|
33
|
+
acceptTag(name, closing, line) {
|
|
34
|
+
if (name === 'details') {
|
|
35
|
+
if (!closing) {
|
|
36
|
+
if (this.frames.at(-1)?.summary === 'open') {
|
|
37
|
+
this.problem(line, 'details cannot open inside summary');
|
|
38
|
+
}
|
|
39
|
+
this.frames.push({ line, summary: 'absent' });
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
const frame = this.frames.pop();
|
|
43
|
+
if (!frame)
|
|
44
|
+
this.problem(line, 'details closes without an opening tag');
|
|
45
|
+
else if (frame.summary === 'open')
|
|
46
|
+
this.problem(line, 'summary has no closing tag');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
const frame = this.frames.at(-1);
|
|
50
|
+
if (!frame) {
|
|
51
|
+
this.problem(line, 'summary must be inside details');
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
if (closing) {
|
|
55
|
+
if (frame.summary !== 'open')
|
|
56
|
+
this.problem(line, 'summary closes without an opening tag');
|
|
57
|
+
else
|
|
58
|
+
frame.summary = 'closed';
|
|
59
|
+
}
|
|
60
|
+
else if (frame.summary !== 'absent') {
|
|
61
|
+
this.problem(line, 'details contains more than one summary or a nested summary');
|
|
62
|
+
}
|
|
63
|
+
else
|
|
64
|
+
frame.summary = 'open';
|
|
65
|
+
}
|
|
66
|
+
problem(line, reason) {
|
|
67
|
+
this.diagnostics.push({
|
|
68
|
+
code: 'malformed-details',
|
|
69
|
+
line,
|
|
70
|
+
message: `Malformed details: ${reason}.`,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { parseGfmMarkdown } from './ast.mjs';
|
|
2
|
+
import { DetailsScope } from './details-scope.mjs';
|
|
3
|
+
/** Parses visible root H2 sections while retaining exact source ranges and disclosure scope. */
|
|
4
|
+
export function parseMarkdownSections(markdown) {
|
|
5
|
+
const sections = [];
|
|
6
|
+
const scope = new DetailsScope(markdown);
|
|
7
|
+
let current;
|
|
8
|
+
const visit = (node, collectText) => {
|
|
9
|
+
if (node.type === 'html') {
|
|
10
|
+
scope.acceptHtml(node.value, node.position.start.offset);
|
|
11
|
+
return;
|
|
12
|
+
}
|
|
13
|
+
if (node.type === 'code' || node.type === 'image')
|
|
14
|
+
return;
|
|
15
|
+
if (collectText &&
|
|
16
|
+
scope.visible &&
|
|
17
|
+
current &&
|
|
18
|
+
(node.type === 'text' || node.type === 'inlineCode') &&
|
|
19
|
+
node.value.trim()) {
|
|
20
|
+
current.hasVisibleContent = true;
|
|
21
|
+
}
|
|
22
|
+
if (!('children' in node))
|
|
23
|
+
return;
|
|
24
|
+
for (const [index, child] of node.children.entries()) {
|
|
25
|
+
visit(child, collectText && node.type !== 'heading' && (node.type !== 'table' || index > 0));
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
for (const node of parseGfmMarkdown(markdown).children) {
|
|
29
|
+
if (node.type === 'heading') {
|
|
30
|
+
const wasVisible = scope.visible;
|
|
31
|
+
const heading = headingText(node, scope).replace(/\s+/gu, ' ').trim();
|
|
32
|
+
if (node.depth === 2 && wasVisible && heading) {
|
|
33
|
+
if (current)
|
|
34
|
+
current.endOffset = node.position.start.offset;
|
|
35
|
+
current = {
|
|
36
|
+
heading,
|
|
37
|
+
content: '',
|
|
38
|
+
startOffset: node.position.end.offset,
|
|
39
|
+
endOffset: markdown.length,
|
|
40
|
+
line: node.position.start.line,
|
|
41
|
+
hasVisibleContent: false,
|
|
42
|
+
};
|
|
43
|
+
sections.push(current);
|
|
44
|
+
}
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
visit(node, node.type === 'paragraph' || node.type === 'list' || node.type === 'table');
|
|
48
|
+
}
|
|
49
|
+
scope.finish();
|
|
50
|
+
for (const section of sections)
|
|
51
|
+
section.content = markdown.slice(section.startOffset, section.endOffset);
|
|
52
|
+
return { sections, diagnostics: scope.diagnostics };
|
|
53
|
+
}
|
|
54
|
+
function headingText(node, scope) {
|
|
55
|
+
if (node.type === 'html') {
|
|
56
|
+
scope.acceptHtml(node.value, node.position.start.offset);
|
|
57
|
+
return '';
|
|
58
|
+
}
|
|
59
|
+
if (node.type === 'text' || node.type === 'inlineCode')
|
|
60
|
+
return scope.visible ? node.value : '';
|
|
61
|
+
if (!('children' in node))
|
|
62
|
+
return '';
|
|
63
|
+
return node.children.map((child) => headingText(child, scope)).join('');
|
|
64
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type MarkdownVisibleSection = {
|
|
2
|
+
heading: string;
|
|
3
|
+
content: string;
|
|
4
|
+
startOffset: number;
|
|
5
|
+
endOffset: number;
|
|
6
|
+
line: number;
|
|
7
|
+
hasVisibleContent: boolean;
|
|
8
|
+
};
|
|
9
|
+
export type MarkdownSectionsDiagnostic = {
|
|
10
|
+
code: 'missing-heading' | 'duplicate-heading' | 'empty-section' | 'malformed-details';
|
|
11
|
+
message: string;
|
|
12
|
+
heading?: string;
|
|
13
|
+
line?: number;
|
|
14
|
+
};
|
|
15
|
+
export type MarkdownSectionsDocument = {
|
|
16
|
+
sections: MarkdownVisibleSection[];
|
|
17
|
+
diagnostics: MarkdownSectionsDiagnostic[];
|
|
18
|
+
};
|
|
19
|
+
export type ValidateMarkdownSectionsOptions = {
|
|
20
|
+
requiredHeadings: readonly string[];
|
|
21
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export { findMarkdownNode, findMarkdownNodes, parseGfmMarkdown, walkMarkdown } from './ast.mts';
|
|
2
2
|
export { extractLooseMarkdownTableRows } from './loose-pipe-rows.mts';
|
|
3
|
+
export { parseMarkdownSections } from './document-sections.mts';
|
|
4
|
+
export { validateMarkdownSections } from './validate-sections.mts';
|
|
5
|
+
export type { MarkdownVisibleSection, MarkdownSectionsDiagnostic, MarkdownSectionsDocument, ValidateMarkdownSectionsOptions, } from './document-types.mts';
|
|
3
6
|
export { markdownSectionBetweenHeadings } from './sections.mts';
|
|
4
7
|
export { extractMarkdownTables, parseMarkdownTables } from './tables.mts';
|
|
5
8
|
export { markdownNodeText } from './text.mts';
|
package/dist/markdown/index.mjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
export { findMarkdownNode, findMarkdownNodes, parseGfmMarkdown, walkMarkdown } from './ast.mjs';
|
|
2
2
|
export { extractLooseMarkdownTableRows } from './loose-pipe-rows.mjs';
|
|
3
|
+
export { parseMarkdownSections } from './document-sections.mjs';
|
|
4
|
+
export { validateMarkdownSections } from './validate-sections.mjs';
|
|
3
5
|
export { markdownSectionBetweenHeadings } from './sections.mjs';
|
|
4
6
|
export { extractMarkdownTables, parseMarkdownTables } from './tables.mjs';
|
|
5
7
|
export { markdownNodeText } from './text.mjs';
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import type { MarkdownSectionsDiagnostic, MarkdownSectionsDocument, ValidateMarkdownSectionsOptions } from './document-types.mts';
|
|
2
|
+
/** Validates caller-owned headings and visible core content without inferring semantic relevance. */
|
|
3
|
+
export declare function validateMarkdownSections(document: MarkdownSectionsDocument, options: ValidateMarkdownSectionsOptions): MarkdownSectionsDiagnostic[];
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** Validates caller-owned headings and visible core content without inferring semantic relevance. */
|
|
2
|
+
export function validateMarkdownSections(document, options) {
|
|
3
|
+
const diagnostics = [...document.diagnostics];
|
|
4
|
+
for (const heading of options.requiredHeadings) {
|
|
5
|
+
const sections = document.sections.filter((section) => section.heading === heading);
|
|
6
|
+
if (sections.length === 0) {
|
|
7
|
+
diagnostics.push({
|
|
8
|
+
code: 'missing-heading',
|
|
9
|
+
heading,
|
|
10
|
+
message: `Missing required section: ${heading}.`,
|
|
11
|
+
});
|
|
12
|
+
continue;
|
|
13
|
+
}
|
|
14
|
+
if (sections.length > 1) {
|
|
15
|
+
diagnostics.push({
|
|
16
|
+
code: 'duplicate-heading',
|
|
17
|
+
heading,
|
|
18
|
+
line: sections[1].line,
|
|
19
|
+
message: `Duplicate required section: ${heading}.`,
|
|
20
|
+
});
|
|
21
|
+
}
|
|
22
|
+
for (const section of sections) {
|
|
23
|
+
if (!section.hasVisibleContent) {
|
|
24
|
+
diagnostics.push({
|
|
25
|
+
code: 'empty-section',
|
|
26
|
+
heading,
|
|
27
|
+
line: section.line,
|
|
28
|
+
message: `Required section has no visible prose, list, or table content: ${heading}.`,
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return diagnostics;
|
|
34
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vouchington-tooling",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.23.0",
|
|
4
4
|
"description": "Vouchington CLI and extractable tooling libraries.",
|
|
5
5
|
"homepage": "https://github.com/vouchington/vouchington-tooling/tree/main/packages/vouchington-tooling#readme",
|
|
6
6
|
"bugs": {
|
|
@@ -13,6 +13,11 @@ Use before implementation. Inspect checkout status without discarding work; foll
|
|
|
13
13
|
worktree, contribution, and CI policy. Read every applicable `AGENTS.md` from the repository root
|
|
14
14
|
through changed files, plus relevant documentation and tests. Local instructions govern this skill.
|
|
15
15
|
|
|
16
|
+
Use [PR descriptions](../pr-description/SKILL.md) before creating or updating a pull request.
|
|
17
|
+
Diagnose failed CI with [CI-log review](../review-ci-logs/SKILL.md), then update confirmed harness
|
|
18
|
+
gaps before handoff. Keep those records after checks pass; routine local successes stay in the
|
|
19
|
+
evidence record.
|
|
20
|
+
|
|
16
21
|
- During implementation, use [implementation](references/implementation.md).
|
|
17
22
|
- Before handoff, use [evidence sweep](references/evidence-sweep.md).
|
|
18
23
|
- For review, use [review](references/review.md).
|
|
@@ -5,3 +5,6 @@ contract compatibility, failure paths, authorization, untrusted input, secrets,
|
|
|
5
5
|
boundaries. Remove dead paths and stale documentation rather than labeling them as legacy.
|
|
6
6
|
|
|
7
7
|
Record unresolved risk as an explicit accepted decision or follow-up, never as an unnoticed gap.
|
|
8
|
+
Check the pull-request description against the final behavior and audience impact. After CI
|
|
9
|
+
diagnosis, ensure confirmed local-verification gaps or local/CI mismatches and their correction
|
|
10
|
+
remain disclosed; passing checks do not erase the gap discovered earlier.
|
package/skills/manifest.json
CHANGED
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
{
|
|
5
5
|
"name": "agent-workflow",
|
|
6
6
|
"plugin": "vouchington-workflow",
|
|
7
|
-
"pluginVersion": "0.
|
|
7
|
+
"pluginVersion": "0.9.0",
|
|
8
8
|
"path": "agent-workflow/SKILL.md",
|
|
9
|
-
"prerequisites": ["github-issue"]
|
|
9
|
+
"prerequisites": ["github-issue", "pr-description", "review-ci-logs"]
|
|
10
10
|
},
|
|
11
11
|
{
|
|
12
12
|
"name": "backend-vitest-test-authoring",
|
|
@@ -18,13 +18,13 @@
|
|
|
18
18
|
{
|
|
19
19
|
"name": "blackboard",
|
|
20
20
|
"plugin": "vouchington-workflow",
|
|
21
|
-
"pluginVersion": "0.
|
|
21
|
+
"pluginVersion": "0.9.0",
|
|
22
22
|
"path": "blackboard/SKILL.md"
|
|
23
23
|
},
|
|
24
24
|
{
|
|
25
25
|
"name": "dependabot",
|
|
26
26
|
"plugin": "vouchington-workflow",
|
|
27
|
-
"pluginVersion": "0.
|
|
27
|
+
"pluginVersion": "0.9.0",
|
|
28
28
|
"path": "dependabot/SKILL.md",
|
|
29
29
|
"prerequisites": ["github-actions-checklist"]
|
|
30
30
|
},
|
|
@@ -38,26 +38,26 @@
|
|
|
38
38
|
{
|
|
39
39
|
"name": "git-commit-checklist",
|
|
40
40
|
"plugin": "vouchington-workflow",
|
|
41
|
-
"pluginVersion": "0.
|
|
41
|
+
"pluginVersion": "0.9.0",
|
|
42
42
|
"path": "git-commit-checklist/SKILL.md"
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
45
|
"name": "github-actions-authoring",
|
|
46
46
|
"plugin": "vouchington-workflow",
|
|
47
|
-
"pluginVersion": "0.
|
|
47
|
+
"pluginVersion": "0.9.0",
|
|
48
48
|
"path": "github-actions-authoring/SKILL.md",
|
|
49
49
|
"prerequisites": ["github-actions-checklist"]
|
|
50
50
|
},
|
|
51
51
|
{
|
|
52
52
|
"name": "github-actions-checklist",
|
|
53
53
|
"plugin": "vouchington-workflow",
|
|
54
|
-
"pluginVersion": "0.
|
|
54
|
+
"pluginVersion": "0.9.0",
|
|
55
55
|
"path": "github-actions-checklist/SKILL.md"
|
|
56
56
|
},
|
|
57
57
|
{
|
|
58
58
|
"name": "github-issue",
|
|
59
59
|
"plugin": "vouchington-workflow",
|
|
60
|
-
"pluginVersion": "0.
|
|
60
|
+
"pluginVersion": "0.9.0",
|
|
61
61
|
"path": "github-issue/SKILL.md"
|
|
62
62
|
},
|
|
63
63
|
{
|
|
@@ -70,26 +70,26 @@
|
|
|
70
70
|
{
|
|
71
71
|
"name": "npm-publishing",
|
|
72
72
|
"plugin": "vouchington-workflow",
|
|
73
|
-
"pluginVersion": "0.
|
|
73
|
+
"pluginVersion": "0.9.0",
|
|
74
74
|
"path": "npm-publishing/SKILL.md"
|
|
75
75
|
},
|
|
76
76
|
{
|
|
77
77
|
"name": "organize-github-issues",
|
|
78
78
|
"plugin": "vouchington-workflow",
|
|
79
|
-
"pluginVersion": "0.
|
|
79
|
+
"pluginVersion": "0.9.0",
|
|
80
80
|
"path": "organize-github-issues/SKILL.md",
|
|
81
81
|
"prerequisites": ["github-issue"]
|
|
82
82
|
},
|
|
83
83
|
{
|
|
84
84
|
"name": "package-json-checklist",
|
|
85
85
|
"plugin": "vouchington-workflow",
|
|
86
|
-
"pluginVersion": "0.
|
|
86
|
+
"pluginVersion": "0.9.0",
|
|
87
87
|
"path": "package-json-checklist/SKILL.md"
|
|
88
88
|
},
|
|
89
89
|
{
|
|
90
90
|
"name": "planning",
|
|
91
91
|
"plugin": "vouchington-workflow",
|
|
92
|
-
"pluginVersion": "0.
|
|
92
|
+
"pluginVersion": "0.9.0",
|
|
93
93
|
"path": "planning/SKILL.md",
|
|
94
94
|
"prerequisites": ["github-issue"]
|
|
95
95
|
},
|
|
@@ -115,53 +115,53 @@
|
|
|
115
115
|
{
|
|
116
116
|
"name": "pr-description",
|
|
117
117
|
"plugin": "vouchington-workflow",
|
|
118
|
-
"pluginVersion": "0.
|
|
118
|
+
"pluginVersion": "0.9.0",
|
|
119
119
|
"path": "pr-description/SKILL.md"
|
|
120
120
|
},
|
|
121
121
|
{
|
|
122
122
|
"name": "retrospective",
|
|
123
123
|
"plugin": "vouchington-workflow",
|
|
124
|
-
"pluginVersion": "0.
|
|
124
|
+
"pluginVersion": "0.9.0",
|
|
125
125
|
"path": "retrospective/SKILL.md"
|
|
126
126
|
},
|
|
127
127
|
{
|
|
128
128
|
"name": "retrospective-distill",
|
|
129
129
|
"plugin": "vouchington-workflow",
|
|
130
|
-
"pluginVersion": "0.
|
|
130
|
+
"pluginVersion": "0.9.0",
|
|
131
131
|
"path": "retrospective-distill/SKILL.md",
|
|
132
132
|
"prerequisites": ["github-issue"]
|
|
133
133
|
},
|
|
134
134
|
{
|
|
135
135
|
"name": "review-ci-logs",
|
|
136
136
|
"plugin": "vouchington-workflow",
|
|
137
|
-
"pluginVersion": "0.
|
|
137
|
+
"pluginVersion": "0.9.0",
|
|
138
138
|
"path": "review-ci-logs/SKILL.md",
|
|
139
|
-
"prerequisites": ["github-actions-checklist"]
|
|
139
|
+
"prerequisites": ["github-actions-checklist", "pr-description"]
|
|
140
140
|
},
|
|
141
141
|
{
|
|
142
142
|
"name": "review-github-issue-taxonomy",
|
|
143
143
|
"plugin": "vouchington-workflow",
|
|
144
|
-
"pluginVersion": "0.
|
|
144
|
+
"pluginVersion": "0.9.0",
|
|
145
145
|
"path": "review-github-issue-taxonomy/SKILL.md",
|
|
146
146
|
"prerequisites": ["github-issue"]
|
|
147
147
|
},
|
|
148
148
|
{
|
|
149
149
|
"name": "revisit-followups",
|
|
150
150
|
"plugin": "vouchington-workflow",
|
|
151
|
-
"pluginVersion": "0.
|
|
151
|
+
"pluginVersion": "0.9.0",
|
|
152
152
|
"path": "revisit-followups/SKILL.md",
|
|
153
153
|
"prerequisites": ["github-issue"]
|
|
154
154
|
},
|
|
155
155
|
{
|
|
156
156
|
"name": "stacked-prs",
|
|
157
157
|
"plugin": "vouchington-workflow",
|
|
158
|
-
"pluginVersion": "0.
|
|
158
|
+
"pluginVersion": "0.9.0",
|
|
159
159
|
"path": "stacked-prs/SKILL.md"
|
|
160
160
|
},
|
|
161
161
|
{
|
|
162
162
|
"name": "static-analysis-checklist",
|
|
163
163
|
"plugin": "vouchington-workflow",
|
|
164
|
-
"pluginVersion": "0.
|
|
164
|
+
"pluginVersion": "0.9.0",
|
|
165
165
|
"path": "static-analysis-checklist/SKILL.md",
|
|
166
166
|
"prerequisites": ["github-actions-checklist"]
|
|
167
167
|
},
|
|
@@ -9,20 +9,54 @@ Read applicable `AGENTS.md` and their declared adapter alias, otherwise
|
|
|
9
9
|
`.agents/skills/pr-description/SKILL.md` from the repository root if present.
|
|
10
10
|
Local policy overrides these defaults; do not reload an already-read adapter or canonical skill.
|
|
11
11
|
|
|
12
|
-
Use before opening or updating a pull request,
|
|
12
|
+
Use before opening or updating a pull request, after diagnosing a CI failure, or at handoff. Read local
|
|
13
13
|
`AGENTS.md`, pull-request template, and issue-linking policy first.
|
|
14
14
|
|
|
15
|
-
|
|
15
|
+
Keep the description understandable without the conversation. Its short visible core explains:
|
|
16
16
|
|
|
17
|
-
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
-
|
|
21
|
-
or
|
|
22
|
-
|
|
23
|
-
- A compact diagram only when it materially clarifies a multi-component flow or lifecycle.
|
|
17
|
+
- **Summary:** what changed, why, and the concrete before/after behavior. For a fix, explain its
|
|
18
|
+
root cause as well as the symptom. A reader should understand the outcome before implementation
|
|
19
|
+
details.
|
|
20
|
+
- **Impact:** who or what is affected and how their behavior changes. Name the actual audience,
|
|
21
|
+
such as end users, staff, API clients, operators, or contributors. Keep material risks and
|
|
22
|
+
limitations visible. For internal work, describe the affected development or operational flow.
|
|
24
23
|
|
|
25
|
-
|
|
24
|
+
Add detail only where the change warrants it:
|
|
25
|
+
|
|
26
|
+
- For cost changes, state the direction and scope. Label figures as measured or estimated, name the
|
|
27
|
+
workload and assumptions, and link evidence. Say unmeasured when no defensible figure exists;
|
|
28
|
+
do not invent savings or claim a neutral cost without evidence. Follow the consumer's privacy
|
|
29
|
+
policy before reporting actual spending, and use approved aggregated or redacted evidence.
|
|
30
|
+
- For database changes, inventory added, removed, and changed tables, columns, constraints, and
|
|
31
|
+
indexes. Explain the relationships and conformance to the consumer's schema rules, including
|
|
32
|
+
producer/consumer changes and any justified exceptions. Link the policy and concrete evidence.
|
|
33
|
+
- For API, workflow, or independently deployed changes, explain the affected contract, lifecycle,
|
|
34
|
+
or operational safety. Follow the consumer's compatibility and deployment policy.
|
|
35
|
+
- Use a before/after table or meaningful Mermaid diagram when it makes behavior, relationships,
|
|
36
|
+
workflow topology, or lifecycle easier to understand. Prose is sufficient for a simple change.
|
|
37
|
+
|
|
38
|
+
Put long comparisons, inventories, diagrams, calculations, and failure evidence in collapsed
|
|
39
|
+
`<details>` sections. Keep the short conclusions and material risks outside them. Keep the
|
|
40
|
+
consumer's required visible core headings outside disclosure containers. Put opening/closing tags
|
|
41
|
+
on separate lines, with a blank line after `</summary>` and before `</details>` so Markdown renders.
|
|
42
|
+
Read [description examples](references/examples.md) when choosing a comparison, diagram, or failure
|
|
43
|
+
record.
|
|
44
|
+
|
|
45
|
+
Do not fill the description with routine local successful-test lists. This is a reporting rule,
|
|
46
|
+
not permission to skip checks: execute required validation and retain its revision, exact scope,
|
|
47
|
+
result, and any omission reason in the work's evidence record.
|
|
48
|
+
|
|
49
|
+
Include **Harness gaps** only after CI diagnosis establishes an omitted local verification or a
|
|
50
|
+
proven local/CI mismatch. Link the first failing run/job and its exact head revision, compare the actual local and
|
|
51
|
+
CI scope, explain the omission or difference, and record the durable correction or authorized
|
|
52
|
+
linked follow-up. Distinguish an omitted check from one that passed locally. Unknown coverage is
|
|
53
|
+
not evidence of a miss; classify genuine CI-only or external infrastructure failures separately.
|
|
54
|
+
Retain confirmed gap records after CI turns green. Diagnose the failure through the repository's
|
|
55
|
+
CI investigation workflow; a failed status alone does not establish a gap.
|
|
56
|
+
|
|
57
|
+
Use the repository's approved PR tooling and preserve sections managed by its review automation
|
|
58
|
+
verbatim. Close only issues fully resolved and explain non-closing issue links. Apply this guidance
|
|
59
|
+
to descriptions being created or updated, not a bulk rewrite of historical pull requests.
|
|
26
60
|
|
|
27
61
|
Consumer wrapper owns: required headings, source-issue rule, merge policy, repository command, and
|
|
28
62
|
hosted review system.
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
# Description examples
|
|
2
|
+
|
|
3
|
+
These illustrative descriptions show how to keep conclusions visible while disclosing longer
|
|
4
|
+
evidence. Use the consumer's actual headings, policy links, issue relationships, and run evidence.
|
|
5
|
+
Do not copy a claim or figure unless the current change supports it.
|
|
6
|
+
|
|
7
|
+
## Small behavior fix
|
|
8
|
+
|
|
9
|
+
```markdown
|
|
10
|
+
## Summary
|
|
11
|
+
|
|
12
|
+
Reopening the review dialog now cancels its pending reset. Previously, the reset cleared the
|
|
13
|
+
new selection because the timer outlived the closed state.
|
|
14
|
+
|
|
15
|
+
## Impact
|
|
16
|
+
|
|
17
|
+
Staff can close and reopen a review without losing the new selection. Closing normally still
|
|
18
|
+
clears the old selection after the exit animation.
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
No diagram, cost calculation, database inventory, or local successful-test list is needed.
|
|
22
|
+
|
|
23
|
+
## Database relationship change
|
|
24
|
+
|
|
25
|
+
```markdown
|
|
26
|
+
## Summary
|
|
27
|
+
|
|
28
|
+
Recovery attempts now reference retained account identities through a foreign key. Previously,
|
|
29
|
+
an unvalidated identifier could refer to an account that no longer existed.
|
|
30
|
+
|
|
31
|
+
## Impact
|
|
32
|
+
|
|
33
|
+
Operators can trace each attempt to a retained identity, and deletion cannot orphan recovery
|
|
34
|
+
history. The API's recovery response is unchanged. Storage cost is unmeasured; retaining an
|
|
35
|
+
identity row per deleted account adds storage proportional to account deletions.
|
|
36
|
+
|
|
37
|
+
<details>
|
|
38
|
+
<summary>Schema inventory and conformance</summary>
|
|
39
|
+
|
|
40
|
+
| Object | Before | After |
|
|
41
|
+
| --------------------------------------------- | ----------------------- | --------------------------------- |
|
|
42
|
+
| `account_identity` table | Absent | Retains each account's identity |
|
|
43
|
+
| `recovery_attempt.account_id` column | Unvalidated UUID | Removed |
|
|
44
|
+
| `recovery_attempt.account_identity_id` column | Absent | Required foreign key |
|
|
45
|
+
| Foreign-key constraint | Absent | References `account_identity(id)` |
|
|
46
|
+
| Lookup index | Uses the removed column | Uses `account_identity_id` |
|
|
47
|
+
|
|
48
|
+
The relationship uses a typed column and concrete foreign key, as required by the repository's
|
|
49
|
+
relational-storage policy. The current schema, recovery writer, account-deletion path, and
|
|
50
|
+
operator query change together. There is no dual reader or writer.
|
|
51
|
+
|
|
52
|
+
</details>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
In a real description, link the consumer's policy and implementation evidence. Do not prescribe
|
|
56
|
+
this consumer-specific retention or deployment policy for other repositories.
|
|
57
|
+
|
|
58
|
+
## Workflow and cost change
|
|
59
|
+
|
|
60
|
+
````markdown
|
|
61
|
+
## Summary
|
|
62
|
+
|
|
63
|
+
Test jobs now run once per revision. Previously, changing a pull request's draft status started
|
|
64
|
+
a duplicate test run even though no test job consumed draft state.
|
|
65
|
+
|
|
66
|
+
## Impact
|
|
67
|
+
|
|
68
|
+
Contributors keep the checks already attached to their revision when marking it ready. Estimated
|
|
69
|
+
runner use falls by one duplicate suite per draft-to-ready transition; the estimate assumes
|
|
70
|
+
one transition and a 12-minute suite. Aggregate savings are unmeasured.
|
|
71
|
+
|
|
72
|
+
<details>
|
|
73
|
+
<summary>Before and after event flow</summary>
|
|
74
|
+
|
|
75
|
+
```mermaid
|
|
76
|
+
flowchart LR
|
|
77
|
+
subgraph Before
|
|
78
|
+
B1[Revision pushed] --> B3[Test suite]
|
|
79
|
+
B2[Draft marked ready] --> B4[Duplicate test suite]
|
|
80
|
+
end
|
|
81
|
+
subgraph After
|
|
82
|
+
A1[Revision pushed] --> A3[Test suite]
|
|
83
|
+
A2[Draft marked ready] --> A4[Merge eligibility only]
|
|
84
|
+
end
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
</details>
|
|
88
|
+
````
|
|
89
|
+
|
|
90
|
+
The diagram explains event ownership; a file list or arrows between the Summary and Impact sections
|
|
91
|
+
would not clarify the behavior. Link the run measurements supporting a real estimate.
|
|
92
|
+
|
|
93
|
+
## Harness gap: omitted local verification
|
|
94
|
+
|
|
95
|
+
```markdown
|
|
96
|
+
## Harness gaps
|
|
97
|
+
|
|
98
|
+
CI found that the local pre-push command omitted the compiler-contract suite. The harness now
|
|
99
|
+
includes that suite, and the missing public export is fixed.
|
|
100
|
+
|
|
101
|
+
<details>
|
|
102
|
+
<summary>Failure and durable correction</summary>
|
|
103
|
+
|
|
104
|
+
The failed compiler-contract job on the first pushed revision reported the missing export.
|
|
105
|
+
The pre-push record ran the component project only; the compiler-contract project was omitted
|
|
106
|
+
because the command catalog did not include it. The catalog and pre-push selection now include
|
|
107
|
+
the project, and a public-import regression covers this export.
|
|
108
|
+
|
|
109
|
+
</details>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The actual record must link the failing run/job and exact head SHA, plus the local scope and
|
|
113
|
+
correction evidence. Retain it when the corrected revision passes CI.
|
|
114
|
+
|
|
115
|
+
## Harness gap: passed locally, failed in CI
|
|
116
|
+
|
|
117
|
+
```markdown
|
|
118
|
+
## Harness gaps
|
|
119
|
+
|
|
120
|
+
The contract suite passed locally but failed in CI because the local runner selected the host
|
|
121
|
+
architecture instead of the deployment target. Local validation now selects that target explicitly.
|
|
122
|
+
|
|
123
|
+
<details>
|
|
124
|
+
<summary>Local and CI scope difference</summary>
|
|
125
|
+
|
|
126
|
+
The pre-push record and failing job used the same suite on the same revision. Their target
|
|
127
|
+
configurations differed: local selected the host target, while CI compiled the deployment target.
|
|
128
|
+
The local command now uses the deployment target, and a compiler regression covers the rejected
|
|
129
|
+
cross-target import. This was a configuration mismatch, not an omitted suite.
|
|
130
|
+
|
|
131
|
+
</details>
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Link the concrete revision, failed job, local evidence, and correction in the actual record.
|
|
135
|
+
An external service outage, unavailable CI-only credentials, or unknown local execution history
|
|
136
|
+
does not support this claim. Explain such limitations separately when they affect the outcome.
|
|
@@ -18,6 +18,11 @@ Use when investigating a CI failure, repeated workflow noise, or misleading diag
|
|
|
18
18
|
failed steps and representative large entries, then remove the temporary artifacts.
|
|
19
19
|
3. Classify each finding as a real error, misleading output, a downstream cascade, a necessary
|
|
20
20
|
diagnostic, or a volume-only concern. Identify the first repository-owned root cause.
|
|
21
|
+
Compare the first failing run/job, its exact head revision, and scope with the pre-push verification record.
|
|
22
|
+
Establish whether the relevant local verification was omitted, passed under a different scope
|
|
23
|
+
or environment, genuinely CI-only, or unknown. A shared command name does not prove shared
|
|
24
|
+
project, target, fixtures, or configuration. Do not infer an omission from missing evidence or
|
|
25
|
+
label an external infrastructure failure a harness miss.
|
|
21
26
|
4. For a persistent-workspace failure, name the producer of sparse state or unsafe ownership before
|
|
22
27
|
proposing cleanup. Reject an unconditional pre-checkout workspace traversal. A compliant repair
|
|
23
28
|
is the one in [github-actions-checklist](../github-actions-checklist/SKILL.md).
|
|
@@ -27,6 +32,11 @@ Use when investigating a CI failure, repeated workflow noise, or misleading diag
|
|
|
27
32
|
6. Add focused regression evidence, run local workflow validation, and compare before and after
|
|
28
33
|
output where that comparison is meaningful. Report deferred findings. Do not create issues unless
|
|
29
34
|
authorized.
|
|
35
|
+
7. Update [the PR description](../pr-description/SKILL.md) before handoff with confirmed omitted
|
|
36
|
+
verification or proven local/CI mismatch. Link the first failed run/job and its exact revision, the actual local
|
|
37
|
+
scope, the reason it missed the failure, and the durable harness correction or authorized
|
|
38
|
+
follow-up. Keep the distinction between omitted and locally passed checks explicit, and retain
|
|
39
|
+
confirmed gap records after CI turns green. Classify CI-only and unknown coverage separately.
|
|
30
40
|
|
|
31
41
|
Consumer wrapper owns: workflow names, log retention, the CI provider command, retry policy, and
|
|
32
42
|
scheduled automation.
|