@oevery/rsp 3.1.0-beta.1 → 3.1.0-beta.3
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 +101 -87
- package/README.zh-CN.md +88 -64
- package/dist/{chunk-QLXIDVKK.mjs → chunk-7KKAUBKN.mjs} +309 -1255
- package/dist/chunk-JOMFFQRG.mjs +99 -0
- package/dist/chunk-NX55NIC3.mjs +358 -0
- package/dist/chunk-OTJFL6XH.mjs +1022 -0
- package/dist/cli.mjs +533 -561
- package/dist/entry-WZDVKDUM.mjs +174 -0
- package/dist/{entry-LAPR3TNI.mjs → entry-X23GPCVS.mjs} +8 -92
- package/package.json +11 -10
- package/rules/rsp-rules.md +16 -14
- package/skills/rsp/SKILL.md +48 -176
- package/skills/rsp/references/conflict-handling.md +11 -0
- package/skills/rsp/references/durable-review.md +17 -0
- package/skills/rsp/references/groups-dependencies.md +11 -0
- package/skills/rsp/references/managed-routing.md +51 -0
- package/skills/rsp/references/setup-repair.md +11 -0
- package/skills/rsp-commit/SKILL.md +42 -0
- package/skills/rsp-design/SKILL.md +14 -9
- package/skills/rsp-manage/SKILL.md +38 -26
- package/skills/rsp-release-docs/SKILL.md +36 -128
- package/skills/rsp-release-docs/references/evidence-and-surfaces.md +21 -0
- package/skills/rsp-release-docs/references/output-contracts.md +0 -18
- package/skills/rsp-release-docs/references/publication-lifecycle.md +24 -0
- package/skills/{rsp-address-review → rsp-resolve-findings}/SKILL.md +7 -7
- package/skills/rsp-shape/SKILL.md +19 -17
- package/skills/rsp-shape/references/complex-shaping.md +2 -2
- package/skills/rsp-structural-audit/SKILL.md +71 -0
- package/skills/rsp-structural-audit/references/structural-lenses.md +41 -0
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
inspectPackagedSkillInventory
|
|
4
|
+
} from "./chunk-NX55NIC3.mjs";
|
|
5
|
+
import {
|
|
6
|
+
openTerminalSession,
|
|
7
|
+
truncateDisplay
|
|
8
|
+
} from "./chunk-JOMFFQRG.mjs";
|
|
9
|
+
import "./chunk-OTJFL6XH.mjs";
|
|
10
|
+
|
|
11
|
+
// src/skills-tui/entry.tsx
|
|
12
|
+
import process from "process";
|
|
13
|
+
import { render } from "ink";
|
|
14
|
+
import ReactRuntime from "react";
|
|
15
|
+
|
|
16
|
+
// src/skills-tui/app.tsx
|
|
17
|
+
import { Box, Text, useApp, useInput, useStdout } from "ink";
|
|
18
|
+
import { useMemo, useState } from "react";
|
|
19
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
20
|
+
function SkillsApp({ initialWidth, inventory, messages, onComplete }) {
|
|
21
|
+
const { exit } = useApp();
|
|
22
|
+
const { stdout } = useStdout();
|
|
23
|
+
const width = Math.max(40, initialWidth ?? stdout.columns ?? 80);
|
|
24
|
+
const optional = useMemo(() => inventory.skills.filter((skill) => skill.kind === "optional"), [inventory]);
|
|
25
|
+
const defaults = useMemo(() => inventory.skills.filter((skill) => skill.kind === "default"), [inventory]);
|
|
26
|
+
const [selected, setSelected] = useState(/* @__PURE__ */ new Set());
|
|
27
|
+
const [cursor, setCursor] = useState(0);
|
|
28
|
+
const [phase, setPhase] = useState("select");
|
|
29
|
+
const finish = (selection) => {
|
|
30
|
+
onComplete(selection);
|
|
31
|
+
exit();
|
|
32
|
+
};
|
|
33
|
+
const selectedNames = () => [...defaults.map((skill) => skill.name), ...optional.filter((skill) => selected.has(skill.name)).map((skill) => skill.name)].sort();
|
|
34
|
+
useInput((input, key) => {
|
|
35
|
+
if (phase === "replace") {
|
|
36
|
+
if (input.toLowerCase() === "y") {
|
|
37
|
+
finish({ kind: "confirmed", names: selectedNames(), force: true });
|
|
38
|
+
} else if (input.toLowerCase() === "n" || key.escape) {
|
|
39
|
+
finish({ kind: "cancelled" });
|
|
40
|
+
}
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
if (key.escape || input === "q" || key.ctrl && input === "c") {
|
|
44
|
+
finish({ kind: "cancelled" });
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if ((key.upArrow || input === "k") && optional.length > 0) {
|
|
48
|
+
setCursor((value) => (value - 1 + optional.length) % optional.length);
|
|
49
|
+
} else if ((key.downArrow || input === "j") && optional.length > 0) {
|
|
50
|
+
setCursor((value) => (value + 1) % optional.length);
|
|
51
|
+
} else if (input === " " && optional[cursor]) {
|
|
52
|
+
const name = optional[cursor].name;
|
|
53
|
+
setSelected((current) => {
|
|
54
|
+
const next = new Set(current);
|
|
55
|
+
if (next.has(name))
|
|
56
|
+
next.delete(name);
|
|
57
|
+
else
|
|
58
|
+
next.add(name);
|
|
59
|
+
return next;
|
|
60
|
+
});
|
|
61
|
+
} else if (key.return) {
|
|
62
|
+
const names = selectedNames();
|
|
63
|
+
const divergent = inventory.skills.some((skill) => names.includes(skill.name) && skill.status === "divergent");
|
|
64
|
+
if (divergent)
|
|
65
|
+
setPhase("replace");
|
|
66
|
+
else
|
|
67
|
+
finish({ kind: "confirmed", names, force: false });
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
const status = (value) => messages[value];
|
|
71
|
+
return /* @__PURE__ */ jsxs(Box, { flexDirection: "column", width, children: [
|
|
72
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: messages.title }),
|
|
73
|
+
/* @__PURE__ */ jsx(Text, { children: truncateDisplay(`${inventory.package.name}@${inventory.package.version}`, width) }),
|
|
74
|
+
/* @__PURE__ */ jsx(Text, { children: truncateDisplay(`${messages.target}: ${inventory.target}`, width) }),
|
|
75
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
76
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: messages.defaultHeading }),
|
|
77
|
+
defaults.map((skill) => /* @__PURE__ */ jsx(Text, { children: truncateDisplay(` [x] ${skill.name} ${messages.locked} \xB7 ${status(skill.status)}`, width) }, skill.name)),
|
|
78
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
79
|
+
/* @__PURE__ */ jsx(Text, { bold: true, children: messages.optionalHeading }),
|
|
80
|
+
optional.length === 0 && /* @__PURE__ */ jsx(Text, { dimColor: true, children: ` ${messages.none}` }),
|
|
81
|
+
optional.map((skill, index) => /* @__PURE__ */ jsx(Text, { children: truncateDisplay(`${index === cursor ? "\u203A" : " "} ${selected.has(skill.name) ? "[x]" : "[ ]"} ${skill.name} ${status(skill.status)}`, width) }, skill.name)),
|
|
82
|
+
/* @__PURE__ */ jsx(Text, { children: " " }),
|
|
83
|
+
/* @__PURE__ */ jsx(Text, { color: phase === "replace" ? "yellow" : void 0, children: phase === "replace" ? messages.replaceTitle : messages.selectHelp }),
|
|
84
|
+
phase === "replace" && /* @__PURE__ */ jsx(Text, { children: messages.replaceHelp })
|
|
85
|
+
] });
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// src/skills-tui/messages.ts
|
|
89
|
+
var skillsCatalogs = {
|
|
90
|
+
"en": {
|
|
91
|
+
title: "RSP Skill manager",
|
|
92
|
+
target: "Target",
|
|
93
|
+
defaultHeading: "Default lifecycle Skills",
|
|
94
|
+
optionalHeading: "Optional project Skills",
|
|
95
|
+
missing: "missing",
|
|
96
|
+
unchanged: "unchanged",
|
|
97
|
+
divergent: "divergent",
|
|
98
|
+
locked: "locked",
|
|
99
|
+
none: "none",
|
|
100
|
+
selectHelp: "\u2191/\u2193 move \xB7 Space select \xB7 Enter confirm \xB7 Esc cancel",
|
|
101
|
+
replaceTitle: "Replace divergent selected Skills?",
|
|
102
|
+
replaceHelp: "y replace \xB7 n cancel"
|
|
103
|
+
},
|
|
104
|
+
"zh-CN": {
|
|
105
|
+
title: "RSP Skill \u7BA1\u7406\u5668",
|
|
106
|
+
target: "\u76EE\u6807",
|
|
107
|
+
defaultHeading: "\u9ED8\u8BA4\u751F\u547D\u5468\u671F Skills",
|
|
108
|
+
optionalHeading: "\u53EF\u9009\u9879\u76EE Skills",
|
|
109
|
+
missing: "\u672A\u5B89\u88C5",
|
|
110
|
+
unchanged: "\u4E00\u81F4",
|
|
111
|
+
divergent: "\u6709\u5DEE\u5F02",
|
|
112
|
+
locked: "\u9501\u5B9A",
|
|
113
|
+
none: "\u65E0",
|
|
114
|
+
selectHelp: "\u2191/\u2193 \u79FB\u52A8 \xB7 \u7A7A\u683C\u9009\u62E9 \xB7 \u56DE\u8F66\u786E\u8BA4 \xB7 Esc \u53D6\u6D88",
|
|
115
|
+
replaceTitle: "\u66FF\u6362\u6240\u9009\u7684\u5DEE\u5F02 Skill\uFF1F",
|
|
116
|
+
replaceHelp: "y \u66FF\u6362 \xB7 n \u53D6\u6D88"
|
|
117
|
+
}
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// src/skills-tui/entry.tsx
|
|
121
|
+
var signalExitCodes = { SIGHUP: 129, SIGINT: 130, SIGTERM: 143 };
|
|
122
|
+
var defaultRuntime = {
|
|
123
|
+
inspect: inspectPackagedSkillInventory,
|
|
124
|
+
render,
|
|
125
|
+
openSession: openTerminalSession,
|
|
126
|
+
host: process,
|
|
127
|
+
reportError: (message) => console.error(message)
|
|
128
|
+
};
|
|
129
|
+
async function runSkillsTui(locale, runtime = defaultRuntime) {
|
|
130
|
+
const screenReader = runtime.host.env.INK_SCREEN_READER === "true" || runtime.host.env.INK_SCREEN_READER === "1";
|
|
131
|
+
const session = runtime.openSession(runtime.host.stdout, { screenReader });
|
|
132
|
+
let instance;
|
|
133
|
+
let selection = { kind: "cancelled" };
|
|
134
|
+
const listeners = /* @__PURE__ */ new Map();
|
|
135
|
+
let cleaned = false;
|
|
136
|
+
const cleanup = () => {
|
|
137
|
+
if (cleaned)
|
|
138
|
+
return;
|
|
139
|
+
cleaned = true;
|
|
140
|
+
for (const [signal, listener] of listeners)
|
|
141
|
+
runtime.host.off(signal, listener);
|
|
142
|
+
listeners.clear();
|
|
143
|
+
instance?.unmount();
|
|
144
|
+
session.cleanup();
|
|
145
|
+
};
|
|
146
|
+
try {
|
|
147
|
+
const inventory = await runtime.inspect();
|
|
148
|
+
instance = runtime.render(ReactRuntime.createElement(SkillsApp, {
|
|
149
|
+
inventory,
|
|
150
|
+
messages: skillsCatalogs[locale],
|
|
151
|
+
onComplete: (value) => {
|
|
152
|
+
selection = value;
|
|
153
|
+
}
|
|
154
|
+
}), { exitOnCtrlC: false, patchConsole: false });
|
|
155
|
+
for (const signal of Object.keys(signalExitCodes)) {
|
|
156
|
+
const listener = () => {
|
|
157
|
+
cleanup();
|
|
158
|
+
runtime.host.exit(signalExitCodes[signal]);
|
|
159
|
+
};
|
|
160
|
+
listeners.set(signal, listener);
|
|
161
|
+
runtime.host.once(signal, listener);
|
|
162
|
+
}
|
|
163
|
+
await instance.waitUntilExit();
|
|
164
|
+
cleanup();
|
|
165
|
+
return selection;
|
|
166
|
+
} catch (error) {
|
|
167
|
+
cleanup();
|
|
168
|
+
runtime.reportError(` Error: ${error instanceof Error ? error.message : String(error)}`);
|
|
169
|
+
return { kind: "error" };
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
export {
|
|
173
|
+
runSkillsTui
|
|
174
|
+
};
|
|
@@ -7,7 +7,14 @@ import {
|
|
|
7
7
|
projectDependencyForest,
|
|
8
8
|
queryArchiveHistory,
|
|
9
9
|
readArchiveHistoryDetail
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-7KKAUBKN.mjs";
|
|
11
|
+
import {
|
|
12
|
+
displayWidth,
|
|
13
|
+
openTerminalSession,
|
|
14
|
+
truncateDisplay,
|
|
15
|
+
wrapDisplay
|
|
16
|
+
} from "./chunk-JOMFFQRG.mjs";
|
|
17
|
+
import "./chunk-OTJFL6XH.mjs";
|
|
11
18
|
|
|
12
19
|
// src/tui/entry.tsx
|
|
13
20
|
import process from "process";
|
|
@@ -18,81 +25,6 @@ import React from "react";
|
|
|
18
25
|
import { Box, Text, useApp, useInput, useStdout } from "ink";
|
|
19
26
|
import { useCallback, useEffect, useReducer, useRef } from "react";
|
|
20
27
|
|
|
21
|
-
// src/tui/display.ts
|
|
22
|
-
var segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
23
|
-
function isWide(codePoint) {
|
|
24
|
-
return codePoint >= 4352 && (codePoint <= 4447 || codePoint === 9001 || codePoint === 9002 || codePoint >= 11904 && codePoint <= 42191 && codePoint !== 12351 || codePoint >= 44032 && codePoint <= 55203 || codePoint >= 63744 && codePoint <= 64255 || codePoint >= 65040 && codePoint <= 65049 || codePoint >= 65072 && codePoint <= 65135 || codePoint >= 65280 && codePoint <= 65376 || codePoint >= 65504 && codePoint <= 65510 || codePoint >= 127744 && codePoint <= 129791 || codePoint >= 131072 && codePoint <= 262141);
|
|
25
|
-
}
|
|
26
|
-
function displayWidth(value) {
|
|
27
|
-
let width = 0;
|
|
28
|
-
for (const { segment } of segmenter.segment(value)) {
|
|
29
|
-
const codePoint = segment.codePointAt(0) ?? 0;
|
|
30
|
-
if (/^\p{Mark}$/u.test(segment) || codePoint === 8205)
|
|
31
|
-
continue;
|
|
32
|
-
width += isWide(codePoint) ? 2 : 1;
|
|
33
|
-
}
|
|
34
|
-
return width;
|
|
35
|
-
}
|
|
36
|
-
function truncateDisplay(value, maxWidth) {
|
|
37
|
-
if (displayWidth(value) <= maxWidth)
|
|
38
|
-
return value;
|
|
39
|
-
if (maxWidth <= 1)
|
|
40
|
-
return "\u2026".slice(0, maxWidth);
|
|
41
|
-
let result = "";
|
|
42
|
-
for (const { segment } of segmenter.segment(value)) {
|
|
43
|
-
if (displayWidth(result + segment) > maxWidth - 1)
|
|
44
|
-
break;
|
|
45
|
-
result += segment;
|
|
46
|
-
}
|
|
47
|
-
return `${result}\u2026`;
|
|
48
|
-
}
|
|
49
|
-
function wrapDisplay(value, maxWidth, firstPrefix = "", continuationPrefix = firstPrefix) {
|
|
50
|
-
const width = Math.max(1, maxWidth);
|
|
51
|
-
if (displayWidth(firstPrefix) >= width)
|
|
52
|
-
return [truncateDisplay(`${firstPrefix}${value}`, width)];
|
|
53
|
-
const lines = [];
|
|
54
|
-
let prefix = firstPrefix;
|
|
55
|
-
let line = prefix;
|
|
56
|
-
const pushLine = () => {
|
|
57
|
-
lines.push(line);
|
|
58
|
-
prefix = continuationPrefix;
|
|
59
|
-
line = prefix;
|
|
60
|
-
};
|
|
61
|
-
const appendUnbroken = (token) => {
|
|
62
|
-
for (const { segment } of segmenter.segment(token)) {
|
|
63
|
-
if (displayWidth(line + segment) > width && line !== prefix)
|
|
64
|
-
pushLine();
|
|
65
|
-
if (displayWidth(line + segment) > width) {
|
|
66
|
-
lines.push(truncateDisplay(line + segment, width));
|
|
67
|
-
prefix = continuationPrefix;
|
|
68
|
-
line = prefix;
|
|
69
|
-
continue;
|
|
70
|
-
}
|
|
71
|
-
line += segment;
|
|
72
|
-
}
|
|
73
|
-
};
|
|
74
|
-
let pendingWhitespace = "";
|
|
75
|
-
for (const token of value.split(/(\s+)/u).filter(Boolean)) {
|
|
76
|
-
if (token.trim().length === 0) {
|
|
77
|
-
if (line !== prefix)
|
|
78
|
-
pendingWhitespace += token;
|
|
79
|
-
continue;
|
|
80
|
-
}
|
|
81
|
-
const separator = line === prefix ? "" : pendingWhitespace;
|
|
82
|
-
if (displayWidth(line + separator + token) <= width) {
|
|
83
|
-
line += `${separator}${token}`;
|
|
84
|
-
} else {
|
|
85
|
-
if (line !== prefix && separator)
|
|
86
|
-
pushLine();
|
|
87
|
-
appendUnbroken(token);
|
|
88
|
-
}
|
|
89
|
-
pendingWhitespace = "";
|
|
90
|
-
}
|
|
91
|
-
if (line !== prefix || lines.length === 0)
|
|
92
|
-
lines.push(line);
|
|
93
|
-
return lines;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
28
|
// src/tui/history-display.ts
|
|
97
29
|
function formatHistoryRow(record, width) {
|
|
98
30
|
const boundedWidth = Math.max(1, width);
|
|
@@ -884,22 +816,6 @@ var catalogs = {
|
|
|
884
816
|
"zh-CN": zhCN
|
|
885
817
|
};
|
|
886
818
|
|
|
887
|
-
// src/tui/terminal.ts
|
|
888
|
-
function openTerminalSession(stdout, options) {
|
|
889
|
-
let cleaned = false;
|
|
890
|
-
if (!options.screenReader)
|
|
891
|
-
stdout.write("\x1B[?1049h\x1B[?25l");
|
|
892
|
-
return {
|
|
893
|
-
cleanup() {
|
|
894
|
-
if (cleaned)
|
|
895
|
-
return;
|
|
896
|
-
cleaned = true;
|
|
897
|
-
if (!options.screenReader)
|
|
898
|
-
stdout.write("\x1B[?25h\x1B[?1049l");
|
|
899
|
-
}
|
|
900
|
-
};
|
|
901
|
-
}
|
|
902
|
-
|
|
903
819
|
// src/tui/entry.tsx
|
|
904
820
|
var signalExitCodes = {
|
|
905
821
|
SIGHUP: 129,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@oevery/rsp",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "3.1.0-beta.
|
|
4
|
+
"version": "3.1.0-beta.3",
|
|
5
5
|
"description": "RSP (Reliable Software Practice), a repository-native engineering workflow for humans and AI agents",
|
|
6
6
|
"author": "oevery",
|
|
7
7
|
"license": "MIT",
|
|
@@ -38,9 +38,10 @@
|
|
|
38
38
|
"scripts": {
|
|
39
39
|
"prepare": "pnpm run build",
|
|
40
40
|
"release:check": "pnpm run release:metadata-check && pnpm run build && pnpm run typecheck && pnpm run lint && pnpm run test && pnpm run release:package-check",
|
|
41
|
+
"release:candidate-check": "node scripts/release-candidate-check.mjs && pnpm run release:check",
|
|
41
42
|
"release:metadata-check": "node scripts/release-metadata-check.mjs",
|
|
42
43
|
"release:package-check": "node scripts/clean-install-check.mjs",
|
|
43
|
-
"prepublishOnly": "pnpm run release:check",
|
|
44
|
+
"prepublishOnly": "pnpm run release:candidate-check",
|
|
44
45
|
"build": "tsup",
|
|
45
46
|
"dev": "tsup --watch",
|
|
46
47
|
"lint": "eslint .",
|
|
@@ -53,17 +54,17 @@
|
|
|
53
54
|
"citty": "^0.2.2",
|
|
54
55
|
"ink": "~7.1.1",
|
|
55
56
|
"picocolors": "^1.1.1",
|
|
56
|
-
"react": "~19.2.
|
|
57
|
+
"react": "~19.2.8",
|
|
57
58
|
"yaml": "^2.9.0"
|
|
58
59
|
},
|
|
59
60
|
"devDependencies": {
|
|
60
|
-
"@antfu/eslint-config": "^9.
|
|
61
|
-
"@types/node": "^22.
|
|
62
|
-
"@types/react": "19.2.
|
|
63
|
-
"eslint": "^10.
|
|
61
|
+
"@antfu/eslint-config": "^9.2.0",
|
|
62
|
+
"@types/node": "^22.20.1",
|
|
63
|
+
"@types/react": "19.2.17",
|
|
64
|
+
"eslint": "^10.8.0",
|
|
64
65
|
"ink-testing-library": "4.0.0",
|
|
65
|
-
"tsup": "^8.
|
|
66
|
-
"typescript": "^5.
|
|
67
|
-
"vitest": "^4.1.
|
|
66
|
+
"tsup": "^8.5.1",
|
|
67
|
+
"typescript": "^5.9.3",
|
|
68
|
+
"vitest": "^4.1.10"
|
|
68
69
|
}
|
|
69
70
|
}
|
package/rules/rsp-rules.md
CHANGED
|
@@ -18,30 +18,30 @@ This file is the minimal fallback protocol for agents that cannot load the `rsp`
|
|
|
18
18
|
|
|
19
19
|
## Core
|
|
20
20
|
|
|
21
|
-
- Choose response language and artifact language independently. Localize
|
|
21
|
+
- Choose response language and artifact language independently. Localize response prose from the explicit request, scoped instructions, then conversation language; write authorized artifacts from the explicit request, scoped instructions, existing artifact language, then conversation language. Preserve canonical headings, WorkRefs, paths, commands, identifiers, and machine values.
|
|
22
22
|
- Write persistent artifact prose in domain, system, user, or operator language. Mention AI or agents only when they are real product actors, consumers, interface participants, or constraints; agent authorship, execution narration, or convenience for a hypothetical future agent is not durable content.
|
|
23
23
|
- `changes/` stores open work; `archives/` stores completed history; `specs/` stores stable current facts; the configured Decision Record path stores lasting rationale.
|
|
24
24
|
- Every Change is one Markdown file with explicit `kind` and the fixed `Proposal`, `Spec`, `Design`, `Tasks`, `Verify`, and `Blockers` sections.
|
|
25
25
|
- A Change is a convergent current-plan and final-evidence snapshot, not an append-only execution log. Keep Tasks aligned with current delivery and Verify limited to final decisive commands or scenarios, results, proven scope, omitted or unavailable coverage, and remaining risk. Replace superseded plans and evidence; retain an earlier failure only when it still explains a current blocker, coverage gap, or risk.
|
|
26
26
|
- In a Change `Blockers` section, `- requires \`<change-work-ref>\`: <reason>` declares an exact dependency on another executable Change. Other meaningful blocker prose remains external and is never inferred as a dependency.
|
|
27
27
|
- Executable Change identities are either `<change>` or `<group>/<change>`. Recursive work paths are unsupported.
|
|
28
|
-
- A Change Group is the only composite
|
|
29
|
-
- `<group>/brief`, physically stored as `<group>/00-brief.md`, owns Goal, Scope, Shared Constraints, Slices, Completion Conditions, Durable Outcomes, and Blockers. Every direct child must be declared by `Slices`, and every declared slice must be open or archived.
|
|
30
|
-
- A Group Brief is not executable or focusable. Read it before a grouped child, archive children independently, and use `rsp group close <group>` only after derived completion passes. Archived Group identities cannot be reopened.
|
|
28
|
+
- A Change Group is the only composite shape and requires at least two independent direct children. Its non-executable, non-focusable `<group>/brief` at `<group>/00-brief.md` owns shared scope, constraints, declared Slices, completion, durable outcomes, and blockers. Read it before a child; archive children independently and close only a derived-complete Group.
|
|
31
29
|
- A file and directory cannot claim the same work identity.
|
|
32
|
-
-
|
|
33
|
-
- Initialization, repair, status inspection, Spec creation, and index generation use the same no-follow managed-path rules; recursive Specs may use only real directories and regular files.
|
|
34
|
-
- Final managed files, including the project `AGENTS.md`, focus markers, fallback/config files, indexes, and placeholders, must be regular files rather than symlinks or special entries.
|
|
30
|
+
- Managed roots, Group prefixes, recursive Spec paths, and final managed files must be real directories or regular files as applicable. Initialization, repair, inspection, Spec-index work, and mutations use shared no-follow checks; symlinks, special entries, missing roots, and incomplete inspection fail closed.
|
|
35
31
|
- `status`, `check`, and `doctor` share work-tree, Change Group, and dependency inspection. They fail visibly on unsupported structure, membership mismatches, malformed or missing dependency targets, self-dependencies, and cycles. With no focus, status uses Group Brief declaration order and derived blockers to recommend the first executable slice.
|
|
36
|
-
- `status` derives
|
|
32
|
+
- `status` derives blockers, exact edges and reasons, ready Changes, and stable waves without persisted graph or delivery state. Filtered views retain prerequisite context. Archived prerequisites resolve; invalid or incompletely inspected graphs fail closed.
|
|
37
33
|
- Parallel Changes in one Group follow the Brief `Slices` declaration order in plan output; unrelated work uses stable lexical ordering.
|
|
38
34
|
- A Group Brief blocker is inherited by its direct child Changes as an external blocker; it does not create inferred dependency edges.
|
|
39
35
|
- Persist only `open` and `archived`; readiness, blockers, verification, and next actions are derived.
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
36
|
+
- When fresh evidence shows that an archived executable Change did not meet its original acceptance, explicitly use `rsp reopen <work-ref> --reason <text>` to restore the same WorkRef. Reopen retains the selected archive, focuses the open Change, and adds unfinished Task and Verify evidence. Multiple archives require exact `--from .rsp/archives/...` selection; open work wins over same-WorkRef history in current dependency state. Use a new corrective Change for genuinely new scope or an independently delivered correction. Do not reopen a closed Change Group implicitly.
|
|
37
|
+
- Ordinary implementation is the default after unexplained failures are ruled out. Use test-first only when authority requires it or a concrete risk makes pre-mutation failure materially safer. Retain a new test only when it protects observable behavior or a real boundary with distinct proportionate value; otherwise remove the temporary probe and use decisive final verification.
|
|
38
|
+
- Before later-turn mutation under a direct report, tiny, or small route, rederive from the newly authorized objective and prospective work. Cross-module implementation, multiple acceptance surfaces, repeated production-path correction, real-host validation, or lifecycle delivery requires the smallest sufficient WorkRef before mutation; unchanged tiny/small follow-ups stay direct. Elapsed time and message count alone never escalate work.
|
|
39
|
+
- Before focusing or mutating a different WorkRef, compare dirty product or durable-truth paths with the prior owner's declared or observed paths. Overlap requires continuation of the same open WorkRef, an authorized reopen, an explicitly authorized integration owner, or a stop for boundary resolution. Disjoint authorized work may proceed without staging or forcing a commit; insufficient ownership evidence stops the transition.
|
|
40
|
+
- This fallback does not emulate `rsp-manage`, even when project configuration selects automatic activation or local closeout. Invalid configuration grants nothing and remains visible. Every `manage.closeout` preset stays advisory because fallback Core never selects or qualifies Manage. For an explicit managed request or an automatically eligible completion/continuation, resolve the smallest owner using ordinary Core rules: reuse one unambiguous selected ready Change, send tiny settled work direct without a synthetic Change, and shape only independently authorized planning artifacts for clear non-trivial work. Configuration grants no planning or product-mutation authority. Stop for material product, acceptance, interface, scope, mutation, or external-action decisions. Perform at most one ordinary Core or Discipline action, return its WorkRef and next action, and never dispatch, auto-continue successors, loop review corrections, archive, stage, commit, push, or persist controller state. Load the `rsp` and `rsp-manage` Skills when controller behavior is required.
|
|
41
|
+
- Route one explicit isolated domain, module/seam, or evidence-seeking question to installed `rsp-design`; use a compact manual design pass only when that Skill is unavailable. Without a selected Change, use report-only Pre-Change Design without inventing a WorkRef only when the question is already bounded; when outcome, scope, non-goals, acceptance, or decomposition remains materially unclear, route to Shape. With a selected Change, return evidence, alternatives, owner decisions, and any authorized planned-design update to the same WorkRef. Do not implement or write durable current truth from either mode.
|
|
42
|
+
- Route release documentation only for an explicit release operation with a confirmed identity or range and unfinished surfaces; no Release Change is required. Confirm identity only from explicit instruction or authoritative configuration. Until then stay version-neutral and do not mutate versioned surfaces. Keep ledgers transient; use an optional Release Change only for material decisions, coordination, recovery, blockers, or acceptance. This route grants no Git, publication, deployment, or approval authority.
|
|
43
43
|
- Do not infer current work from open Changes or filenames.
|
|
44
|
-
- Prefer RSP commands for deterministic setup, status, validation, repair, focus, index,
|
|
44
|
+
- Prefer RSP commands for deterministic setup, status, validation, repair, focus, Specs-index, history, archive, and reopen operations.
|
|
45
45
|
|
|
46
46
|
## Durable review
|
|
47
47
|
|
|
@@ -49,11 +49,12 @@ This file is the minimal fallback protocol for agents that cannot load the `rsp`
|
|
|
49
49
|
- Project-owned `CONTEXT.md` owns stable navigation or scoped current context, while project-owned `AGENTS.md` owns stable operating instructions. Preserve their conventions and never treat RSP as their owner.
|
|
50
50
|
- Before archive, decide independently whether current facts need a Spec or scoped-instruction update and whether lasting rationale needs a Decision Record.
|
|
51
51
|
- Before archive, compress the Change to its final outcome, current design, completed delivery tasks, final decisive verification, and unresolved risks. Keep RED/GREEN/REFACTOR cycles, temporary probes, command transcripts, and step-by-step review corrections in the response instead of the Change or Archive.
|
|
52
|
-
- After required durable updates, Core recommends explicit archive before final Git delivery.
|
|
52
|
+
- After required durable updates, Core recommends explicit archive before final Git delivery. Ordinary work requires separate Git and release authority; fallback Core never executes archive, stage, commit, push, or publication.
|
|
53
|
+
- When loaded Core or qualified Manage has already derived one authorized RSP-owned commit boundary, prefer the `rsp-commit` Skill for exact staging, repository-consistent structured message construction, one local commit, and complete post-commit observation. If that Skill alone is unavailable, loaded Core may provide its equivalent bounded manual action against the same owner; this minimal fallback still grants and executes no Git action.
|
|
53
54
|
- Decision Records are for hard-to-reverse choices with real tradeoffs; they do not duplicate current facts.
|
|
54
55
|
- Change Spec deltas are planning aids and are never promoted automatically into Specs or Decision Records.
|
|
55
56
|
- Keep task history, debugging notes, and transient execution state out of Specs and Decision Records.
|
|
56
|
-
- Generated
|
|
57
|
+
- Generated local Specs `00-index.md` files and archives are not current-truth owners. Use each index only to discover direct child Specs and child Spec directories; authoritative Archive Markdown is queried directly without a generated Archive Index.
|
|
57
58
|
|
|
58
59
|
## Continuation and conflicts
|
|
59
60
|
|
|
@@ -67,5 +68,6 @@ This file is the minimal fallback protocol for agents that cannot load the `rsp`
|
|
|
67
68
|
|
|
68
69
|
- Do not create RSP-managed files directly when a command owns the operation.
|
|
69
70
|
- Do not create archive entries directly; use `npx -y @oevery/rsp archive <name>`.
|
|
71
|
+
- Do not restore archived Change content directly; use `npx -y @oevery/rsp reopen <name> --reason <text>` and exact `--from` selection when required.
|
|
70
72
|
- Do not modify content outside the managed RSP block in `AGENTS.md` unless explicitly requested.
|
|
71
73
|
- Archive reports warnings but does not grant commit, push, publication, deletion, or external approval authority.
|