@cratis/pi 7.2.0 → 7.2.2
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 +4 -2
- package/package/corpus/agents/coordinator.md +1 -1
- package/package/corpus/agents/orchestrator.md +1 -1
- package/package/corpus/agents/planner.md +1 -1
- package/package/corpus/harnesses/pi/extensions/cratis-path-guidance/SkillTrigger.ts +1 -1
- package/package/corpus/harnesses/pi/extensions/cratis-path-guidance/index.ts +1 -1
- package/package/corpus/harnesses/pi/extensions/cratis-path-guidance/skills.ts +3 -4
- package/package/corpus/harnesses/pi/extensions/shared/frontmatter.ts +100 -4
- package/package/corpus/harnesses/pi/extensions/shared/skillFrontmatter.ts +27 -5
- package/package/corpus/prompts/ship-changes.prompt.md +19 -4
- package/package/corpus/rules/pull-requests.md +72 -10
- package/package/corpus/skills/cratis-application-react-specifications/SKILL.md +2 -3
- package/package/corpus/skills/cratis-application-slice-specifications/SKILL.md +2 -3
- package/package/corpus/skills/cratis-documentation-writing/SKILL.md +5 -5
- package/package/corpus/skills/cratis-engineering-docs-authoring/SKILL.md +2 -2
- package/package/corpus/skills/cratis-release-notes/SKILL.md +44 -13
- package/package/corpus/skills/cratis-release-notes/verification.json +4 -1
- package/package/corpus/skills/cratis-specifications-csharp/SKILL.md +2 -2
- package/package/corpus/skills/cratis-specifications-typescript/SKILL.md +2 -3
- package/package/corpus/skills/cratis-technical-examples/SKILL.md +2 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -34,8 +34,10 @@ managed corpus.
|
|
|
34
34
|
The packaged `cratis-path-guidance` extension attaches a path-scoped rule to the
|
|
35
35
|
tool result the first time a matching file is touched, and after a successful
|
|
36
36
|
`write` or `edit` adds one advisory line naming every skill whose `SKILL.md`
|
|
37
|
-
`cratis-hint-paths` frontmatter
|
|
38
|
-
|
|
37
|
+
`metadata.cratis-hint-paths` frontmatter (one string of whitespace-separated globs)
|
|
38
|
+
matches the file. (It sits under `metadata` because Agent Skills allows only a fixed set
|
|
39
|
+
of top-level keys, and the key is Cratis-specific because Claude Code gives a plain
|
|
40
|
+
`paths` key its own meaning.) A skill is hinted at
|
|
39
41
|
most once per session and not when it is already in context: read (a shell command
|
|
40
42
|
counts only when `cat`, `sed`, `head`, `tail`, `less`, `bat`, `rg` or `grep` opens
|
|
41
43
|
its `SKILL.md` or a file under `references/`), preloaded by pi-subagents, or
|
|
@@ -128,7 +128,7 @@ For implementation, the applicable changed-lane gates must pass. Mark unrelated
|
|
|
128
128
|
- [ ] `Documentation/verify-markdown.sh` passes when documentation is added or changed
|
|
129
129
|
- [ ] `code-reviewer` finds no blocking issues
|
|
130
130
|
- [ ] `security-reviewer` finds no vulnerabilities
|
|
131
|
-
- [ ] PR description follows the pull request template
|
|
131
|
+
- [ ] PR description follows the pull request template and the release-note contract in `pull-requests.md`; test and review notes are in a PR comment
|
|
132
132
|
|
|
133
133
|
---
|
|
134
134
|
|
|
@@ -152,7 +152,7 @@ For implementation, the applicable changed-lane gates must pass. Mark unrelated
|
|
|
152
152
|
- [ ] `code-reviewer` finds no blocking issues
|
|
153
153
|
- [ ] `security-reviewer` finds no vulnerabilities
|
|
154
154
|
- [ ] All documentation is complete and accurate (if required)
|
|
155
|
-
- [ ] PR description follows the pull request template
|
|
155
|
+
- [ ] PR description follows the pull request template and the release-note contract in `pull-requests.md`; test and review notes are in a PR comment
|
|
156
156
|
|
|
157
157
|
---
|
|
158
158
|
|
|
@@ -115,7 +115,7 @@ For an implemented application slice, require the applicable changed-lane gates
|
|
|
115
115
|
- [ ] `Documentation/verify-markdown.sh` passes when documentation is added or changed
|
|
116
116
|
- [ ] Code review by `code-reviewer` finds no blocking issues
|
|
117
117
|
- [ ] Security review by `security-reviewer` finds no vulnerabilities
|
|
118
|
-
- [ ] PR description follows the pull request template
|
|
118
|
+
- [ ] PR description follows the pull request template and the release-note contract in `pull-requests.md`; test and review notes are in a PR comment
|
|
119
119
|
|
|
120
120
|
---
|
|
121
121
|
|
|
@@ -8,6 +8,6 @@ export interface SkillTrigger {
|
|
|
8
8
|
filePath: string;
|
|
9
9
|
/** Directory holding `SKILL.md` and the skill's references. */
|
|
10
10
|
baseDir: string;
|
|
11
|
-
/** The `cratis-hint-paths` frontmatter of the skill. Empty when the skill declares no trigger. */
|
|
11
|
+
/** The `metadata.cratis-hint-paths` frontmatter of the skill. Empty when the skill declares no trigger. */
|
|
12
12
|
globs: string[];
|
|
13
13
|
}
|
|
@@ -66,7 +66,7 @@ function expandedSkillNames(prompt: unknown): string[] {
|
|
|
66
66
|
*
|
|
67
67
|
* Path-scoped rules are attached to the tool result the first time a matching file is touched in the session,
|
|
68
68
|
* exactly as they were when `cratis-rules` delivered them. On a successful `write` or `edit`, one advisory line
|
|
69
|
-
* names every skill whose `cratis-hint-paths` frontmatter matches the file and where its `SKILL.md` is. A skill
|
|
69
|
+
* names every skill whose `metadata.cratis-hint-paths` frontmatter matches the file and where its `SKILL.md` is. A skill
|
|
70
70
|
* is not hinted when it is already in context: read in the session, preloaded by pi-subagents
|
|
71
71
|
* (`# Preloaded Skill: <name>` followed by its text in the system prompt), or expanded by `/skill:<name>`. Hints are advisory only:
|
|
72
72
|
* nothing is blocked and the system prompt is never touched.
|
|
@@ -4,9 +4,8 @@
|
|
|
4
4
|
import { existsSync, readFileSync, readdirSync } from 'node:fs';
|
|
5
5
|
import { dirname, join } from 'node:path';
|
|
6
6
|
import { corpusRoot } from '../shared/corpusRoot.ts';
|
|
7
|
-
import { frontmatter } from '../shared/frontmatter.ts';
|
|
8
7
|
import { globToRegExp } from '../shared/globs.ts';
|
|
9
|
-
import {
|
|
8
|
+
import { skillTriggerGlobs } from '../shared/skillFrontmatter.ts';
|
|
10
9
|
import { selectedSkillNames } from '../shared/skillSelection.ts';
|
|
11
10
|
import type { LoadedSkill } from './LoadedSkill.ts';
|
|
12
11
|
import type { SkillMatch } from './SkillMatch.ts';
|
|
@@ -35,7 +34,7 @@ function repositorySkills(cwd: string): LoadedSkill[] {
|
|
|
35
34
|
|
|
36
35
|
function triggerGlobs(filePath: string): string[] {
|
|
37
36
|
try {
|
|
38
|
-
return
|
|
37
|
+
return skillTriggerGlobs(readFileSync(filePath, 'utf8'));
|
|
39
38
|
} catch {
|
|
40
39
|
return [];
|
|
41
40
|
}
|
|
@@ -48,7 +47,7 @@ function triggerGlobs(filePath: string): string[] {
|
|
|
48
47
|
* it can be empty (a pi-subagents agent with `skills: false`, the usual setup for cheap workers), or non-empty
|
|
49
48
|
* with only personal skills (an agent with `skills: true` whose `extensions:` allowlist leaves `@cratis/pi`, and
|
|
50
49
|
* so its skill paths, out). The selected skills' `SKILL.md` can still be read by path, which is what the hint asks
|
|
51
|
-
* for; a corpus skill the repository did not select is never added. Skills without a `cratis-hint-paths` trigger
|
|
50
|
+
* for; a corpus skill the repository did not select is never added. Skills without a `metadata.cratis-hint-paths` trigger
|
|
52
51
|
* are left out.
|
|
53
52
|
*/
|
|
54
53
|
export function skillTriggers(loaded: LoadedSkill[] | undefined, cwd: string): SkillTrigger[] {
|
|
@@ -5,17 +5,23 @@ export function unquote(value: string): string {
|
|
|
5
5
|
return value.trim().replace(/^["']|["']$/g, '');
|
|
6
6
|
}
|
|
7
7
|
|
|
8
|
+
/** The text between the opening and closing `---` markers, or `undefined` when there is no terminated frontmatter. */
|
|
9
|
+
export function frontmatterText(content: string): string | undefined {
|
|
10
|
+
if (!content.startsWith('---\n')) return undefined;
|
|
11
|
+
const end = content.indexOf('\n---\n', 4);
|
|
12
|
+
return end < 0 ? undefined : content.slice(4, end);
|
|
13
|
+
}
|
|
14
|
+
|
|
8
15
|
/**
|
|
9
16
|
* Reads the YAML-ish frontmatter the corpus uses: scalar `key: value` lines and `key:` followed by
|
|
10
17
|
* ` - item` lines. Anything richer is not used by the rules and skills and is deliberately not supported.
|
|
11
18
|
*/
|
|
12
19
|
export function frontmatter(content: string): Map<string, string[]> {
|
|
13
20
|
const fields = new Map<string, string[]>();
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
if (end < 0) return fields;
|
|
21
|
+
const text = frontmatterText(content);
|
|
22
|
+
if (text === undefined) return fields;
|
|
17
23
|
let current: string | undefined;
|
|
18
|
-
for (const line of
|
|
24
|
+
for (const line of text.split('\n')) {
|
|
19
25
|
const item = /^\s+-\s+(.*)$/.exec(line);
|
|
20
26
|
if (item && current) {
|
|
21
27
|
fields.get(current)!.push(unquote(item[1]));
|
|
@@ -29,3 +35,93 @@ export function frontmatter(content: string): Map<string, string[]> {
|
|
|
29
35
|
}
|
|
30
36
|
return fields;
|
|
31
37
|
}
|
|
38
|
+
|
|
39
|
+
/** One `key: value` line under a top-level block map such as `metadata`, as written, not interpreted. */
|
|
40
|
+
export interface FrontmatterEntry {
|
|
41
|
+
/** The number of spaces the key is indented by, or -1 when the indentation contains a tab. */
|
|
42
|
+
indent: number;
|
|
43
|
+
/** The text after the colon, trimmed and otherwise untouched: quotes, a trailing comment and block scalar indicators included. */
|
|
44
|
+
raw: string;
|
|
45
|
+
/** True when more-indented lines follow the entry, so its value is a block scalar, a multi-line scalar or a nested map. */
|
|
46
|
+
continues: boolean;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** The result of reading the block map under a top-level key. */
|
|
50
|
+
export interface FrontmatterBlock {
|
|
51
|
+
/** The text after `name:` on the key's own line, trimmed. Non-empty for an inline value such as a flow map. */
|
|
52
|
+
inline: string;
|
|
53
|
+
/** The entries the block holds, in order. A more-indented line belongs to the entry above it and is not an entry itself. */
|
|
54
|
+
entries: Map<string, FrontmatterEntry>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Reads the block under a top-level `name:` key without interpreting the values. Blank and comment lines are skipped.
|
|
59
|
+
* Returns `undefined` when the key is absent or the frontmatter is unterminated.
|
|
60
|
+
*/
|
|
61
|
+
export function frontmatterBlock(content: string, name: string): FrontmatterBlock | undefined {
|
|
62
|
+
const text = frontmatterText(content);
|
|
63
|
+
if (text === undefined) return undefined;
|
|
64
|
+
let block: FrontmatterBlock | undefined;
|
|
65
|
+
let inside = false;
|
|
66
|
+
let last: FrontmatterEntry | undefined;
|
|
67
|
+
let skipAbove: number | undefined;
|
|
68
|
+
for (const line of text.split('\n')) {
|
|
69
|
+
// Any line that starts in column 0 with something other than a comment ends the current block, whatever its
|
|
70
|
+
// spelling (a quoted key, a space before the colon). Only a real `name:` key starts this block.
|
|
71
|
+
if (/^[^\s#]/.test(line)) {
|
|
72
|
+
const top = /^([A-Za-z][\w-]*):(.*)$/.exec(line);
|
|
73
|
+
inside = top?.[1] === name;
|
|
74
|
+
last = undefined;
|
|
75
|
+
skipAbove = undefined;
|
|
76
|
+
// A comment after the key (`metadata: # note`) is not a value, so the block below it is still the block form.
|
|
77
|
+
if (inside) block ??= { inline: /^\s+#/.test(top![2]) ? '' : top![2].trim(), entries: new Map() };
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (!inside || !block || line.trim() === '' || /^\s*#/.test(line)) continue;
|
|
81
|
+
const indent = /^ *\t/.test(line) ? -1 : /^( *)/.exec(line)![1].length;
|
|
82
|
+
// Lines under a sibling key the entry pattern does not read (a quoted key, `? complex`) are not entries.
|
|
83
|
+
if (skipAbove !== undefined && indent > skipAbove) continue;
|
|
84
|
+
skipAbove = undefined;
|
|
85
|
+
const entry = /^\s+([A-Za-z][\w-]*):(.*)$/.exec(line);
|
|
86
|
+
if (entry && (!last || indent <= last.indent)) {
|
|
87
|
+
last = { indent, raw: entry[2].trim(), continues: false };
|
|
88
|
+
block.entries.set(entry[1], last);
|
|
89
|
+
} else if (last && (indent < 0 || indent > last.indent)) {
|
|
90
|
+
last.continues = true;
|
|
91
|
+
} else {
|
|
92
|
+
// At the entry's indentation or less, so a sibling key ends the entry even when its spelling is not read here.
|
|
93
|
+
last = undefined;
|
|
94
|
+
skipAbove = indent;
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return block;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* The string a raw scalar spells when it is written as one complete double-quoted or single-quoted string on one
|
|
102
|
+
* line, with nothing after the closing quote. Anything else, including a string with an escape or an embedded
|
|
103
|
+
* quote, a trailing `# comment`, an unquoted value and a block scalar, yields `undefined`: it is not guessed at.
|
|
104
|
+
*/
|
|
105
|
+
export function quotedScalar(raw: string): string | undefined {
|
|
106
|
+
return /^"([^"\\]*)"$/.exec(raw)?.[1] ?? /^'([^']*)'$/.exec(raw)?.[1];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Reads the nested string map under a top-level `name:` key, as Agent Skills `metadata` uses. Only the one form the
|
|
111
|
+
* corpus supports is read: an entry indented two spaces whose value is a complete one-line quoted string (see
|
|
112
|
+
* `quotedScalar`). Values stay whole strings, without comma splitting. An entry in any other form is left out of the
|
|
113
|
+
* map, so a caller never sees a guess such as `>-`; `frontmatterBlock` gives the raw entries for reporting them.
|
|
114
|
+
* Returns `undefined` when the key is absent or the frontmatter is unterminated.
|
|
115
|
+
*/
|
|
116
|
+
export function frontmatterMap(content: string, name: string): Map<string, string> | undefined {
|
|
117
|
+
const block = frontmatterBlock(content, name);
|
|
118
|
+
if (!block) return undefined;
|
|
119
|
+
const map = new Map<string, string>();
|
|
120
|
+
// A value on the key's own line (a flow map, even one that continues below) is not the supported block form.
|
|
121
|
+
if (block.inline !== '') return map;
|
|
122
|
+
for (const [key, entry] of block.entries) {
|
|
123
|
+
const value = entry.indent === 2 && !entry.continues ? quotedScalar(entry.raw) : undefined;
|
|
124
|
+
if (value !== undefined) map.set(key, value);
|
|
125
|
+
}
|
|
126
|
+
return map;
|
|
127
|
+
}
|
|
@@ -1,9 +1,31 @@
|
|
|
1
1
|
// Copyright (c) Cratis. All rights reserved.
|
|
2
2
|
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
import { frontmatterMap } from './frontmatter.ts';
|
|
5
|
+
|
|
6
|
+
// The key, under the `metadata` map of `SKILL.md` frontmatter, whose value lists the globs a skill is hinted for.
|
|
7
|
+
// Agent Skills allows only `name`, `description`, `license`, `compatibility`, `metadata` and `allowed-tools` at the
|
|
8
|
+
// top level, and `metadata` maps strings to strings, so the value is one string of whitespace-separated globs:
|
|
9
|
+
//
|
|
10
|
+
// metadata:
|
|
11
|
+
// cratis-hint-paths: "**/for_*/**/*.cs **/Documentation/**/*.{md,mdx}"
|
|
12
|
+
//
|
|
13
|
+
// It is deliberately Cratis-specific: Claude Code gives a plain `paths` key in `SKILL.md` a meaning of its own (a
|
|
14
|
+
// "conditional skill" that stays out of the skill list until a matching file is touched), and `.claude/skills`
|
|
15
|
+
// exposes this corpus to it.
|
|
9
16
|
export const skillTriggerKey = 'cratis-hint-paths';
|
|
17
|
+
|
|
18
|
+
/** The raw `metadata.cratis-hint-paths` string of a `SKILL.md`, or `undefined` when the skill declares none. */
|
|
19
|
+
export function skillTriggerValue(content: string): string | undefined {
|
|
20
|
+
return frontmatterMap(content, 'metadata')?.get(skillTriggerKey);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/** Splits a trigger value into its globs. Globs contain no whitespace, and brace sets keep their commas. */
|
|
24
|
+
export function splitSkillTriggerGlobs(value: string): string[] {
|
|
25
|
+
return value.split(/\s+/).filter(Boolean);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/** The trigger globs a `SKILL.md` declares under `metadata.cratis-hint-paths`; empty when it declares none. */
|
|
29
|
+
export function skillTriggerGlobs(content: string): string[] {
|
|
30
|
+
return splitSkillTriggerGlobs(skillTriggerValue(content) ?? '');
|
|
31
|
+
}
|
|
@@ -2,20 +2,20 @@
|
|
|
2
2
|
agent: agent
|
|
3
3
|
description: >
|
|
4
4
|
Ship local changes: create a branch, make logical commits, push, open and
|
|
5
|
-
label a PR with a proper description, merge it, prepare
|
|
6
|
-
|
|
5
|
+
label a PR with a proper description, merge it, prepare related issue
|
|
6
|
+
dispositions the request does not authorize, and delete the branch.
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Ship Changes
|
|
10
10
|
|
|
11
11
|
Ship the current local modifications to `main` through the standard
|
|
12
|
-
branch → commits → PR → merge →
|
|
12
|
+
branch → commits → PR → merge → issue disposition → cleanup workflow.
|
|
13
13
|
|
|
14
14
|
## Inputs
|
|
15
15
|
|
|
16
16
|
- **What changed** — brief description of the work (used for branch name and PR title)
|
|
17
17
|
- **Label** — `no-release`, `patch`, `minor`, or `major`, or omit entirely if no label should be applied
|
|
18
|
-
- **Related issue** — optional exact repository and issue number; if unknown, search read-only first. Comment on or close it only when the user's request includes that effect.
|
|
18
|
+
- **Related issue** — optional exact repository and issue number; if unknown, search read-only first. Comment on or close it only when the user's request includes that effect; otherwise prepare the disposition as a proposal. In repositories released by `cratis/release-action`, a delivered issue is closed by release-action through `(#n)` in the description, but release-action does not run for a `no-release` PR, so `(#n)` closes nothing there; elsewhere follow the repository's own release and issue-closing process.
|
|
19
19
|
|
|
20
20
|
Invoking this prompt is direct authority for the standard branch, commit, push, pull-request,
|
|
21
21
|
requested-label, merge, and branch-cleanup effects. Do not pause to ask for separate approval at
|
|
@@ -32,3 +32,18 @@ Every other label proceeds through merge under this prompt's ordinary authority.
|
|
|
32
32
|
This prompt is for an explicit request to ship or land. If the user asked only to commit, only to
|
|
33
33
|
push, or only to open a PR, stop after that step: the narrower request does not become authority
|
|
34
34
|
for the rest of the chain because this prompt happens to be loaded.
|
|
35
|
+
|
|
36
|
+
## Pull request description
|
|
37
|
+
|
|
38
|
+
The description is published verbatim as the release notes, so it follows the contract in
|
|
39
|
+
[`pull-requests.md`](../rules/pull-requests.md#the-description-is-the-release-note).
|
|
40
|
+
|
|
41
|
+
- Before `gh pr create` or `gh pr edit`, write the body from `.github/pull_request_template.md` and
|
|
42
|
+
check it against the contract. The rules agents most often break:
|
|
43
|
+
- No development write-up: no Overview/Verification/Test plan headings (only a first `## Summary`
|
|
44
|
+
is allowed), no review, testing or provenance notes. Those go in a PR comment.
|
|
45
|
+
- `(#n)` ends the bullet that delivers an issue and `(part of #n)` marks anything that stays open;
|
|
46
|
+
never `Closes`/`Fixes`/`Refs` before a number, and never an issue reference inside an HTML comment,
|
|
47
|
+
which still closes the issue.
|
|
48
|
+
- Links are absolute `https://` URLs, never relative paths.
|
|
49
|
+
- After pushing, if `verify-release-notes` fails, fix it by editing the description; it re-runs on edit.
|
|
@@ -4,19 +4,81 @@ applyTo: "**/*"
|
|
|
4
4
|
|
|
5
5
|
# How to Do Pull Requests
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## The description is the release note
|
|
8
8
|
|
|
9
|
-
**The description is the release
|
|
9
|
+
**The PR description is published verbatim as the GitHub release. In repositories released by `cratis/release-action`, it also closes every issue written as `(#n)`; elsewhere, follow the repository's own release and issue-closing process.** Write the note the person upgrading should read, not a development write-up. The same contract applies to release notes typed into a manual workflow run or written straight into a release. Where it is installed, the `verify-release-notes` check enforces it on every PR into the default branch that carries exactly one of `major`/`minor`/`patch` (it re-runs when the label is added); it skips `no-release` and unlabelled PRs, which should still be written this way because any of them may become releasable. `release-action` does not run for a `no-release` PR, so `(#n)` closes nothing there. Editing the description re-runs the check. HTML comments are not shown on the release page and the check ignores them for headings, keywords and placeholders, but a `(#n)` inside a comment still closes the issue (`release-action` skips only fenced and inline code) and the check fails it, so never put an issue reference in a comment. The template's own comment can stay: its `(#123)` examples are inline code.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
### Allowed shape
|
|
12
12
|
|
|
13
|
-
- Follow the repository's pull request template (`.github/pull_request_template.md`).
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
-
|
|
18
|
-
-
|
|
19
|
-
-
|
|
13
|
+
- Follow the repository's pull request template (`.github/pull_request_template.md`) within this contract.
|
|
14
|
+
- An optional summary, first, only when one cohesive theme spans the bullets: **either** a `## Summary` section (short, user-facing prose) **or** one unheaded lead paragraph (1–3 sentences), never both. `Summary` is a level-2 heading; `# Summary` is the wrong level. No bullets before the first section.
|
|
15
|
+
- Then only these `##` sections, each at most once, in this order after any `## Summary`: `## Added`, `## Changed`, `## Fixed`, `## Removed`, `## Security`, `## Deprecated`. Keep only the sections that have bullets: a section holding only prose, a code example or `None.` fails the check, so delete it. `###` sub-headings inside a section are fine unless they use a forbidden name; inside a section even `### Added` is only a sub-heading, and a section itself is always level 2.
|
|
16
|
+
- A `major`/`minor`/`patch` PR needs at least one bullet under an allowed section.
|
|
17
|
+
- Bullets are short, self-contained and user-facing: what a consumer compiles against, runs or observes. A user-visible fix may add one sentence of root cause or regression guard, stated as observable behavior. Credit an external contributor by name or handle, unless they asked not to be named.
|
|
18
|
+
- Breaking changes and upgrade actions are bullets in `## Changed` or `## Removed` that state the action. A longer migration story goes in the docs, linked by absolute URL.
|
|
19
|
+
- Do not list internal plumbing (storage, converters, gRPC internals, specs, file lists, refactor narration).
|
|
20
|
+
|
|
21
|
+
### Issue references
|
|
22
|
+
|
|
23
|
+
| Write | Meaning |
|
|
24
|
+
| --- | --- |
|
|
25
|
+
| `(#351)` at the **end of the bullet that delivers it** | Delivered: in a repository released by `cratis/release-action`, it closes it at release |
|
|
26
|
+
| `(part of #351)` or prose `see #351` | Related, partial or follow-up: never closes |
|
|
27
|
+
| `Cratis/Repo#351` | Other repository: never closed |
|
|
28
|
+
| one `(#351)` per issue, never `(#351, #352)` | `(#351, #352)` closes nothing: write `(#351) (#352)` |
|
|
29
|
+
|
|
30
|
+
- Nothing but more issue references (also `(part of #n)`, `(see #n)` and `(Cratis/Repo#n)`), closing emphasis, `<br>` and sentence punctuation (a `:` may introduce nested bullets) may follow the delivering `(#n)`. Write one `(#n)` per issue: release-action only matches `(#n)`, so `(#56, #57)` closes nothing; write `(#56) (#57)`.
|
|
31
|
+
- Never use a linking or closing keyword before a number, anywhere: `Close`, `Closes`, `Closed`, `Fix`, `Fixes`, `Fixed`, `Resolve`, `Resolves`, `Resolved`, `Refs`, `Ref`, `References`, also as `Keyword: #n`, with `owner/repo#n`, followed by an issue URL, and wrapped in bold, italics or a link (`**Closes** #n`, `Closes [#n](url)`, `[Closes #n](url)`, and `[Closes](url)` where the link starts a clause). A `Refs #93` line closes nothing and is not a delivery marker: write `(#93)` at the end of the delivering bullet, or `(part of #93)` if it must stay open.
|
|
32
|
+
- Comment on or close an issue when the user's request includes that effect; otherwise prepare a bounded post-merge disposition without performing it. In repositories released by `cratis/release-action`, a delivered `(#n)` is closed by release-action at release, so do not close it by hand; for a `no-release` PR release-action does not run, so any disposition follows this same rule. Elsewhere, follow the repository's own release and issue-closing process.
|
|
33
|
+
- Use a bare `(#n)` only for an issue this PR fully delivers. No issue means no reference. Never use a placeholder such as `(#issue)` or the template's `(#123)`, and verify every number exists in the right repository; never guess.
|
|
34
|
+
|
|
35
|
+
### Forbidden anywhere outside code
|
|
36
|
+
|
|
37
|
+
- **Headings** (any level, also written as HTML `<h2>`, `<b>`, `<i>` or a possibly multi-line `<summary>`, or as an `*italic*` line; a heading line is checked like any other, so a keyword, relative link or `(#n)` in one fails too) other than a first `## Summary`, such as Overview, Description, What, Why, How, Context, Changes, What changed, Test plan, Testing, Tests, Verification, Verified, Validation, Quality, Review, Notes, Notes for reviewers, Limitations, Known follow-up, Acceptance, Details, and any `#`/`##` heading not in the allowed list.
|
|
38
|
+
- **Review, verification, testing and provenance notes**: `Review:`, `Reviewed:`, a stand-alone `Reviewed by` line, `Verification:`, `Tested:`, `Testing:`, `Validation:` lines that stand alone or report a result (`Tests: 400 passed`, `Review: approved`); same-provider, cross-provider, Opus-only or Anthropic-only review remarks; the review workflow having passed, returned or run; CI or gate results (`CI green`, `all tests passed`); and a line stating which agent or model wrote the description (`Generated with Claude Code`, `Co-Authored-By:`). A bullet that describes a product change and only mentions review, validation, tests or an AI model (`- Validation: rules now apply to commands (#3)`, `- Reviewed by status is now shown on the dashboard (#4)`) is fine: inside a bullet, a summary or the lead paragraph the check reads a note only when it ends the clause (`- Cross-provider review pending`).
|
|
39
|
+
- **Internal state** that is not a consumer change (for example "npm publication remains disabled").
|
|
40
|
+
- **Relative links** (`](Documentation/x.md)`, `](./x)`, `](Source/...)`): they 404 on the release page. Use `https://github.com/Cratis/<Repo>/blob/main/<path>`, a `#anchor` or `mailto:`.
|
|
41
|
+
- **Placeholders and transcripts**: template text, empty sections, Copilot "Original prompt" blocks, agent transcripts.
|
|
42
|
+
|
|
43
|
+
Reviewer-facing information (test plan, verification, review provenance, notes for reviewers) goes in a **PR comment**, never the description.
|
|
44
|
+
|
|
45
|
+
### Bad to good
|
|
46
|
+
|
|
47
|
+
Bad (Cratis/Arc.TypeScript v0.48.0 as first published, bullets abridged):
|
|
48
|
+
|
|
49
|
+
```markdown
|
|
50
|
+
Arc can now construct Chronicle reactors and reducers through its own dependency injection, one scope per delivered batch, as an opt-in preview. npm publication remains disabled.
|
|
51
|
+
|
|
52
|
+
## Added
|
|
53
|
+
|
|
54
|
+
- Preview option `withChronicle({ ..., activateArtifactsInScopes: true })` ... (#93)
|
|
55
|
+
|
|
56
|
+
See [Activate reactors and reducers in Arc scopes](Documentation/chronicle/reactors/scoped-activation.md).
|
|
57
|
+
|
|
58
|
+
Review: Opus-only (same-provider) review.
|
|
59
|
+
|
|
60
|
+
Refs #93
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Good (as corrected):
|
|
64
|
+
|
|
65
|
+
```markdown
|
|
66
|
+
Arc can now construct Chronicle reactors and reducers through its own dependency injection, one scope per delivered batch, as an opt-in preview.
|
|
67
|
+
|
|
68
|
+
## Added
|
|
69
|
+
|
|
70
|
+
- Preview option `withChronicle({ ..., activateArtifactsInScopes: true })` ... See [Activate reactors and reducers in Arc scopes](https://github.com/Cratis/Arc.TypeScript/blob/main/Documentation/chronicle/reactors/scoped-activation.md). (part of #93)
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
"npm publication remains disabled" is internal status, the link was relative (it 404s on the release page), the review line is provenance for reviewers, and `Refs #93` is a keyword line. #93 was only partly delivered by this release, so its bullets say `(part of #93)`; `(#93)` would have closed it.
|
|
74
|
+
|
|
75
|
+
### Before you create or edit a PR
|
|
76
|
+
|
|
77
|
+
1. Read the body against the forbidden list and the issue table above; delete every hit and verify every issue number.
|
|
78
|
+
2. Sections in order, none empty, at most one summary form; every bullet user-facing.
|
|
79
|
+
3. Test plan, verification and review notes are in a PR comment.
|
|
80
|
+
|
|
81
|
+
If `verify-release-notes` fails, fix it by editing the description, not by pushing code.
|
|
20
82
|
|
|
21
83
|
## Commits
|
|
22
84
|
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
name: cratis-application-react-specifications
|
|
3
3
|
description: Write specifications for the React and TypeScript surface of a Cratis application slice — view models, helpers, command orchestration, and narrow component behavior — using Vitest with Mocha-style describe/it, Sinon, and the Chai should interface. Use when adding or changing frontend behavior in an application that consumes Cratis. Do not use for backend scenarios or for specifications inside a Cratis framework package.
|
|
4
4
|
license: MIT
|
|
5
|
-
|
|
6
|
-
- "**/for_*/**/*.ts"
|
|
7
|
-
- "**/for_*/**/*.tsx"
|
|
5
|
+
metadata:
|
|
6
|
+
cratis-hint-paths: "**/for_*/**/*.ts **/for_*/**/*.tsx"
|
|
8
7
|
---
|
|
9
8
|
|
|
10
9
|
# Cratis application React specifications
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
name: cratis-application-slice-specifications
|
|
3
3
|
description: Specify the backend behavior of an event-sourced Cratis application slice with the in-process scenario family — CommandScenario, EventScenario, ReadModelScenario and ReactorScenario — including what to cover for each slice type and where the specification files live. Use when adding or changing backend behavior in an application built on Cratis. Do not use for framework library specifications and do not use for frontend behavior.
|
|
4
4
|
license: MIT
|
|
5
|
-
|
|
6
|
-
- "**/when_*/**/*.cs"
|
|
7
|
-
- "**/for_*/**/*.cs"
|
|
5
|
+
metadata:
|
|
6
|
+
cratis-hint-paths: "**/when_*/**/*.cs **/for_*/**/*.cs"
|
|
8
7
|
---
|
|
9
8
|
|
|
10
9
|
# Cratis application slice specifications
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
name: cratis-documentation-writing
|
|
3
3
|
description: Plan, write, and improve user-centered Cratis documentation with a clear reader journey and one primary Diátaxis purpose per page. Use for product docs, tutorials, how-to guides, reference, explanations, and documentation reviews. For executable examples use cratis-technical-examples; for release notes use cratis-release-notes. Do not invent APIs or publish content.
|
|
4
4
|
license: MIT
|
|
5
|
-
|
|
6
|
-
- "**/Documentation/**/*.{md,mdx}"
|
|
5
|
+
metadata:
|
|
6
|
+
cratis-hint-paths: "**/Documentation/**/*.{md,mdx}"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Documentation writing
|
|
@@ -136,9 +136,9 @@ An AI-drafted narrative is a first draft, not a finished page. Give the model
|
|
|
136
136
|
the reader, the scenario, the terminology and the source evidence up front;
|
|
137
137
|
then revise the result against that evidence and the
|
|
138
138
|
**cratis-writing-voice-and-cadence** constructions before it ships. Tell the
|
|
139
|
-
reviewer the narrative was AI-drafted (in the review request or
|
|
140
|
-
message,
|
|
141
|
-
prose, not only as a diff.
|
|
139
|
+
reviewer the narrative was AI-drafted (in the review request, a PR comment or
|
|
140
|
+
the commit message, never in the PR description, which is published as the
|
|
141
|
+
release note) so they read it as prose, not only as a diff.
|
|
142
142
|
|
|
143
143
|
For machine-readable delivery and retrieval checks, use
|
|
144
144
|
**cratis-llm-friendly-documentation**; publishing `llms-full.txt` alone does
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
name: cratis-engineering-docs-authoring
|
|
3
3
|
description: Draft accurate Cratis product or engineering documentation once the reader's goal, owning source, and product evidence are known. Use for tutorials, how-to guides, explanations, and references; use the owning repository's navigation and visual QA workflows for placement and rendering.
|
|
4
4
|
license: LICENSE
|
|
5
|
-
|
|
6
|
-
- "**/Documentation/**/*.{md,mdx}"
|
|
5
|
+
metadata:
|
|
6
|
+
cratis-hint-paths: "**/Documentation/**/*.{md,mdx}"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Cratis documentation authoring
|
|
@@ -20,10 +20,10 @@ required to draft notes.
|
|
|
20
20
|
Before writing, identify product, tag or candidate version, previous version,
|
|
21
21
|
artifact/package names, supported platforms, and the audience. Inventory
|
|
22
22
|
observable changes from the diff, specs, linked issues/PRs, and authoritative
|
|
23
|
-
product source at that revision. A PR description
|
|
24
|
-
release notes in a Cratis repository; follow its current template and
|
|
25
|
-
that behavior before drafting. Do not turn
|
|
26
|
-
about shipped behavior.
|
|
23
|
+
product source at that revision. A PR description is published verbatim as
|
|
24
|
+
release notes in a Cratis repository; follow its current template and the
|
|
25
|
+
pull-request contract, and confirm that behavior before drafting. Do not turn
|
|
26
|
+
an unreleased draft into a claim about shipped behavior.
|
|
27
27
|
|
|
28
28
|
For each change, ask: who uses this path, what happened before, what happens
|
|
29
29
|
now, is action required, and how would they notice? Check changed defaults,
|
|
@@ -42,15 +42,29 @@ one to add weight.
|
|
|
42
42
|
|
|
43
43
|
## Write for the right channel
|
|
44
44
|
|
|
45
|
-
- **Exact-version release note (GitHub/PR):**
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
45
|
+
- **Exact-version release note (GitHub/PR):** in a Cratis repository this is
|
|
46
|
+
the merged PR description, published verbatim, so it follows the contract in
|
|
47
|
+
[pull-requests.md](../../rules/pull-requests.md), which holds the full
|
|
48
|
+
rules. The ones most often broken: no development write-up (no Overview,
|
|
49
|
+
Verification or Test plan headings, no review or provenance notes; those go
|
|
50
|
+
in a PR comment); `(#n)` at the end of the bullet that delivers an issue and
|
|
51
|
+
`(part of #n)` for anything that stays open (in repositories released by
|
|
52
|
+
`cratis/release-action`, `(#n)` is what closes the issue), never `Closes`, `Fixes` or
|
|
53
|
+
`Refs` before a number (also in bold or a link), and no issue reference
|
|
54
|
+
inside an HTML comment, which still closes the issue; and absolute
|
|
55
|
+
`https://` links only. Sections are in
|
|
56
|
+
a fixed order (Added, Changed, Fixed, Removed, Security, Deprecated); within
|
|
57
|
+
a section, put changes requiring action first, then new capabilities and
|
|
58
|
+
fixes. State impact and the user's next action in plain language: state an
|
|
59
|
+
upgrade action in its `## Changed` or `## Removed` bullet (a first
|
|
60
|
+
`## Summary` section or an unheaded lead paragraph, not both, may mention it
|
|
61
|
+
too). Do not list internal
|
|
62
|
+
refactors or specs that change nothing users observe. For a
|
|
63
|
+
user-visible fix, a sentence of root cause and of what now guards against a
|
|
64
|
+
regression, stated as observable behavior rather than a list of specs, is
|
|
65
|
+
user-facing: it tells the reader whether to trust the fix. Credit an
|
|
66
|
+
external contributor by name or handle and say what they did, unless they
|
|
67
|
+
asked not to be named.
|
|
54
68
|
- **Migration guide (durable product docs):** a compact *old behavior → new
|
|
55
69
|
behavior → required action* table for each affected upgrade path, followed
|
|
56
70
|
by source-verified before/after code or commands. Distinguish required
|
|
@@ -75,6 +89,23 @@ boundary that makes a change safe to adopt. One item can follow this shape:
|
|
|
75
89
|
That is a checklist for facts, not a template to repeat word-for-word across
|
|
76
90
|
items. Use descriptive headings, meaningful links, and natural sentence rhythm.
|
|
77
91
|
|
|
92
|
+
## Repair an already-published release
|
|
93
|
+
|
|
94
|
+
Only when asked to fix a release that violates the contract:
|
|
95
|
+
|
|
96
|
+
1. Read the published body (`gh release view TAG --json body`) and the merged
|
|
97
|
+
PR. Keep the facts; remove headings, review and verification lines,
|
|
98
|
+
internal status and closing keywords, and turn relative links into
|
|
99
|
+
`https://` URLs. Do not invent changes, versions or issue numbers.
|
|
100
|
+
2. Publish the corrected text with `gh release edit TAG --notes-file FILE`,
|
|
101
|
+
only when the request authorizes editing that release.
|
|
102
|
+
3. In a repository released by `cratis/release-action`, editing a release
|
|
103
|
+
does not re-run release-action, so an issue that a `(#n)` would have
|
|
104
|
+
closed stays open. For each one, confirm the release
|
|
105
|
+
actually delivered it. Close it by hand with a comment naming the release
|
|
106
|
+
only when the request covers closing issues; otherwise list the issues as a
|
|
107
|
+
proposal. Leave an issue open when the release only partly delivered it.
|
|
108
|
+
|
|
78
109
|
## Verify before handing over
|
|
79
110
|
|
|
80
111
|
1. Reconcile every version, affected range, capability and API against the
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
{ "kind": "skill-contains", "value": "Exact-version release note" },
|
|
7
7
|
{ "kind": "skill-contains", "value": "old behavior → new" },
|
|
8
8
|
{ "kind": "skill-contains", "value": "migration from optional cleanup" },
|
|
9
|
-
{ "kind": "skill-contains", "value": "drafting never authorizes publishing" }
|
|
9
|
+
{ "kind": "skill-contains", "value": "drafting never authorizes publishing" },
|
|
10
|
+
{ "kind": "skill-contains", "value": "`(part of #n)`" },
|
|
11
|
+
{ "kind": "skill-contains", "value": "gh release edit TAG --notes-file" },
|
|
12
|
+
{ "kind": "skill-contains", "value": "does not re-run release-action" }
|
|
10
13
|
]
|
|
11
14
|
}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
name: cratis-specifications-csharp
|
|
3
3
|
description: Write C# specifications with Cratis.Specifications using the Establish/Because/should_ pattern and the for_/when_/and_ folder hierarchy. Use when adding or restructuring C# specs in any Cratis repository, choosing between an isolated unit spec and an in-process scenario spec, or building reusable given/ contexts. Do not use for TypeScript or React specs, and do not use it to decide what a command, projection, or reactor should do.
|
|
4
4
|
license: MIT
|
|
5
|
-
|
|
6
|
-
- "**/for_*/**/*.cs"
|
|
5
|
+
metadata:
|
|
6
|
+
cratis-hint-paths: "**/for_*/**/*.cs"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Cratis C# specifications
|
|
@@ -2,9 +2,8 @@
|
|
|
2
2
|
name: cratis-specifications-typescript
|
|
3
3
|
description: Write TypeScript specifications in the Cratis BDD style using the given() helper, reusable context classes, Sinon stubbing, and the Chai .should fluent interface. Use when adding or restructuring TypeScript specifications, building a given/ context class, or laying out a for_/when_ specification folder. Do not use for C# specifications, and do not use it to decide what the code under specification should do.
|
|
4
4
|
license: MIT
|
|
5
|
-
|
|
6
|
-
- "**/for_*/**/*.ts"
|
|
7
|
-
- "**/for_*/**/*.tsx"
|
|
5
|
+
metadata:
|
|
6
|
+
cratis-hint-paths: "**/for_*/**/*.ts **/for_*/**/*.tsx"
|
|
8
7
|
---
|
|
9
8
|
|
|
10
9
|
# Cratis TypeScript specifications
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
name: cratis-technical-examples
|
|
3
3
|
description: Design and verify developer-facing code samples, tutorial projects, and documentation snippets against real Cratis APIs. Use when adding or reviewing a runnable example, multi-client snippet, sample app, command/output pair, or migration before/after code. Do not invent API shapes or treat rendering as a compilation check.
|
|
4
4
|
license: MIT
|
|
5
|
-
|
|
6
|
-
- "**/Samples/**/*.{cs,ts,tsx}"
|
|
5
|
+
metadata:
|
|
6
|
+
cratis-hint-paths: "**/Samples/**/*.{cs,ts,tsx}"
|
|
7
7
|
---
|
|
8
8
|
|
|
9
9
|
# Technical examples readers can trust
|