@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.
@@ -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-QLXIDVKK.mjs";
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.1",
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.3",
57
+ "react": "~19.2.8",
57
58
  "yaml": "^2.9.0"
58
59
  },
59
60
  "devDependencies": {
60
- "@antfu/eslint-config": "^9.0.0",
61
- "@types/node": "^22.19.19",
62
- "@types/react": "19.2.14",
63
- "eslint": "^10.4.0",
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.4.0",
66
- "typescript": "^5.8.0",
67
- "vitest": "^4.1.7"
66
+ "tsup": "^8.5.1",
67
+ "typescript": "^5.9.3",
68
+ "vitest": "^4.1.10"
68
69
  }
69
70
  }
@@ -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 human-facing response headings, labels, explanations, and conclusions according to the requested response language, response-specific project instructions, then conversation language. Write artifact prose according to the requested artifact language, artifact-specific project instructions, existing artifact language, then conversation language. Preserve canonical RSP artifact headings, WorkRef values, paths, commands, identifiers, and machine-consumed values. Response-only Continuation and Durable Decision labels are not artifact headings; a technical token may appear in parentheses but cannot replace the localized label.
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 work shape. Use it only when at least two independently executable direct child Changes share one goal or completion contract.
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
- - `changes/` must exist as a real directory. Existing `focus.d/`, `archives/`, and direct group prefixes must also be real directories; symlinks, missing open-work roots, and incomplete inspection fail closed.
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 minimal graph nodes, dependency edges with their reasons, ready Changes, blockers, and stable execution waves from exact blocker references. Filtered plans retain transitive prerequisite context and distinguish selected nodes from prerequisite nodes; human output renders the same flat graph as a dependency forest whose parent requires its children. Archived prerequisites resolve automatically; incomplete dependency or archive inspection fails closed and marks open Changes blocked for readiness; no graph file or delivery state is persisted; and Group Briefs are not dependency targets.
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
- - Ordinary implementation is the default after unexplained failures have been ruled out. Use test-first work only when user, Change, or project authority requires it or a concrete changed risk makes a focused pre-mutation failure materially safer. Retain a new test only when it protects observable behavior or a real boundary, adds distinct future confidence, avoids duplicate or implementation-detail coverage, and has proportionate maintenance cost; otherwise remove the temporary probe and use the smallest decisive final verification.
41
- - Route one isolated domain, module/seam, or evidence-seeking design question to `rsp-design` when available, or use a compact manual design pass. Return evidence, alternatives, owner decisions, and any authorized planned-design update to the same selected Change; do not implement or write durable current truth from that pass.
42
- - Route release documentation only when a dedicated Release Change explicitly owns a confirmed release identity or range and still has unfinished changelog, release-note, or migration work. Identity is confirmed only by explicit user instruction or authoritative release configuration; never infer it from version order, prior prereleases, commits, or planned prose. Until confirmation, keep release planning version-neutral and leave manifests, target changelog headings, exact-version documentation, versioned release notes, and tag comparisons unchanged. Select `rsp-release-docs` when available, or use the same evidence-led manual fallback; lifecycle stage, completed implementation, and archive readiness alone are insufficient. This route never grants commit, tag, push, release creation, publication, deployment, or approval authority.
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, and archive operations.
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. Keep each completed Change independently reviewable; with explicit Git authority deliver it as one logical commit before a separate Release Change and dedicated release commit finalize versioned shipped surfaces. Core does not execute archive or grant authority to stage, commit, push, or publish.
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 indexes and archives are not current-truth owners.
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.