@oevery/rsp 3.1.0-beta.3 → 3.1.0-beta.4
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 +20 -13
- package/README.zh-CN.md +20 -13
- package/dist/{chunk-NX55NIC3.mjs → chunk-D7XDXBFO.mjs} +1 -1
- package/dist/{chunk-7KKAUBKN.mjs → chunk-FI3SYHVH.mjs} +560 -429
- package/dist/{chunk-OTJFL6XH.mjs → chunk-FJVH7ZYU.mjs} +344 -216
- package/dist/cli.mjs +107 -63
- package/dist/{entry-WZDVKDUM.mjs → entry-5OPKUZHF.mjs} +2 -2
- package/dist/{entry-X23GPCVS.mjs → entry-FVUG6YW2.mjs} +2 -2
- package/package.json +1 -1
- package/rules/rsp-rules.md +4 -3
- package/skills/rsp/SKILL.md +4 -4
- package/skills/rsp/references/managed-routing.md +6 -2
- package/skills/rsp-commit/SKILL.md +2 -2
- package/skills/rsp-manage/SKILL.md +7 -3
- package/skills/rsp-release-docs/references/convention-discovery.md +2 -2
package/dist/cli.mjs
CHANGED
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
installPackagedSkills,
|
|
5
5
|
printPackagedSkillInventory,
|
|
6
6
|
printSkillInstallResult
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-D7XDXBFO.mjs";
|
|
8
8
|
import {
|
|
9
9
|
ArchiveHistoryError,
|
|
10
10
|
GROUP_BRIEF_FILENAME,
|
|
@@ -21,6 +21,8 @@ import {
|
|
|
21
21
|
inspectFocusTree,
|
|
22
22
|
inspectProjectStatus,
|
|
23
23
|
inspectWorkTree,
|
|
24
|
+
normalizeExecutableWorkRef,
|
|
25
|
+
normalizeWorkRefSegment,
|
|
24
26
|
projectDependencyForest,
|
|
25
27
|
queryArchiveHistory,
|
|
26
28
|
readArchiveHistoryDetail,
|
|
@@ -29,12 +31,14 @@ import {
|
|
|
29
31
|
resolveFocusMarkerPath,
|
|
30
32
|
resolveWorkRef,
|
|
31
33
|
selectArchiveHistoryRecord
|
|
32
|
-
} from "./chunk-
|
|
34
|
+
} from "./chunk-FI3SYHVH.mjs";
|
|
33
35
|
import {
|
|
34
36
|
ARCHIVES_DIR,
|
|
35
37
|
CHANGES_DIR,
|
|
38
|
+
CHANGE_DOCUMENT_SCHEMA,
|
|
36
39
|
CONFIG_PATH,
|
|
37
40
|
DEFAULT_DECISION_RECORDS_PATH,
|
|
41
|
+
DocumentSectionCardinalityError,
|
|
38
42
|
FOCUS_DIR,
|
|
39
43
|
LOCK_PATH,
|
|
40
44
|
OBSOLETE_RSP_RULES_PATH,
|
|
@@ -42,6 +46,7 @@ import {
|
|
|
42
46
|
RSP_DIR,
|
|
43
47
|
RSP_RULES_PATH,
|
|
44
48
|
VALID_KINDS,
|
|
49
|
+
appendDocumentSectionItem,
|
|
45
50
|
buildDurableReviewGuidance,
|
|
46
51
|
cleanupEmptyParentDirs,
|
|
47
52
|
clearConfigCache,
|
|
@@ -56,6 +61,8 @@ import {
|
|
|
56
61
|
generateChangeContent,
|
|
57
62
|
generateDesignContent,
|
|
58
63
|
generateSpecContent,
|
|
64
|
+
getDocumentSectionDefinitionByHeading,
|
|
65
|
+
getDocumentSections,
|
|
59
66
|
getDurableReviewCandidateTargets,
|
|
60
67
|
getVersion,
|
|
61
68
|
guardRspInitialized,
|
|
@@ -70,6 +77,7 @@ import {
|
|
|
70
77
|
loadRspConfig,
|
|
71
78
|
normalizeLogicalPath,
|
|
72
79
|
parseFrontmatter,
|
|
80
|
+
parseRspDocument,
|
|
73
81
|
parseScenarios,
|
|
74
82
|
pc,
|
|
75
83
|
recordRuntimeDiagnostic,
|
|
@@ -83,7 +91,7 @@ import {
|
|
|
83
91
|
validateDecisionRecordsFilesystemPath,
|
|
84
92
|
walkMarkdownFiles,
|
|
85
93
|
writeManagedFile
|
|
86
|
-
} from "./chunk-
|
|
94
|
+
} from "./chunk-FJVH7ZYU.mjs";
|
|
87
95
|
|
|
88
96
|
// src/cli.ts
|
|
89
97
|
import { resolve } from "path";
|
|
@@ -418,7 +426,7 @@ async function addSpec(name, projectName = "project") {
|
|
|
418
426
|
}
|
|
419
427
|
|
|
420
428
|
// src/commands/archive.ts
|
|
421
|
-
import { existsSync } from "fs";
|
|
429
|
+
import { existsSync, readdirSync } from "fs";
|
|
422
430
|
import { mkdir as mkdir3, readFile as readFile3, rename, unlink as unlink4 } from "fs/promises";
|
|
423
431
|
import { basename as basename2, join as join3 } from "path";
|
|
424
432
|
async function archiveChange(name) {
|
|
@@ -436,7 +444,7 @@ async function archiveChange(name) {
|
|
|
436
444
|
const content = await readFile3(srcPath, "utf-8");
|
|
437
445
|
const dependencyInspection = await inspectChangeDependencies();
|
|
438
446
|
const checklist = collectArchiveChecklist(content, {
|
|
439
|
-
activeBlockers: dependencyInspection.activeBlockers.get(name)
|
|
447
|
+
activeBlockers: dependencyInspection.activeBlockers.get(workRef.name)
|
|
440
448
|
});
|
|
441
449
|
for (const line of checklist)
|
|
442
450
|
console.log(` ${pc.yellow("\u26A0")} ${line}`);
|
|
@@ -491,12 +499,12 @@ ${clearedMsg}`);
|
|
|
491
499
|
}
|
|
492
500
|
function resolveArchiveName(archiveSubdir, date, base) {
|
|
493
501
|
const initialName = `${date}_${base}.md`;
|
|
494
|
-
if (!
|
|
502
|
+
if (!archiveNameExists(archiveSubdir, initialName))
|
|
495
503
|
return initialName;
|
|
496
504
|
const MAX_SUFFIX = 9999;
|
|
497
505
|
for (let index = 2; index <= MAX_SUFFIX; index++) {
|
|
498
506
|
const candidate = `${date}_${base}-${index}.md`;
|
|
499
|
-
if (!
|
|
507
|
+
if (!archiveNameExists(archiveSubdir, candidate))
|
|
500
508
|
return candidate;
|
|
501
509
|
}
|
|
502
510
|
throw new WorkRefError(
|
|
@@ -505,6 +513,22 @@ function resolveArchiveName(archiveSubdir, date, base) {
|
|
|
505
513
|
base
|
|
506
514
|
);
|
|
507
515
|
}
|
|
516
|
+
function archiveNameExists(archiveSubdir, name) {
|
|
517
|
+
if (!existsSync(archiveSubdir))
|
|
518
|
+
return false;
|
|
519
|
+
return readdirSync(archiveSubdir).some((entry) => {
|
|
520
|
+
if (entry.normalize("NFC") !== name)
|
|
521
|
+
return false;
|
|
522
|
+
if (entry !== name) {
|
|
523
|
+
throw new WorkRefError(
|
|
524
|
+
"work_ref_collision",
|
|
525
|
+
`archive name has a Unicode normalization collision: ${entry} and ${name}`,
|
|
526
|
+
name
|
|
527
|
+
);
|
|
528
|
+
}
|
|
529
|
+
return true;
|
|
530
|
+
});
|
|
531
|
+
}
|
|
508
532
|
|
|
509
533
|
// src/commands/check.ts
|
|
510
534
|
import { readFile as readFile4 } from "fs/promises";
|
|
@@ -696,8 +720,10 @@ async function runCheck(options = {}) {
|
|
|
696
720
|
message: "missing YAML frontmatter"
|
|
697
721
|
});
|
|
698
722
|
}
|
|
723
|
+
const document = parseRspDocument(content, CHANGE_DOCUMENT_SCHEMA);
|
|
699
724
|
for (const section of requiredSections) {
|
|
700
|
-
|
|
725
|
+
const definition = getDocumentSectionDefinitionByHeading(CHANGE_DOCUMENT_SCHEMA, section);
|
|
726
|
+
if (!definition || !getDocumentSections(document, definition.id).some((candidate) => candidate.canonical)) {
|
|
701
727
|
addDiagnostic({
|
|
702
728
|
severity: "error",
|
|
703
729
|
code: "missing_section",
|
|
@@ -734,16 +760,14 @@ async function runCheck(options = {}) {
|
|
|
734
760
|
});
|
|
735
761
|
}
|
|
736
762
|
}
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
const heading = headingLine.slice("# Change:".length).trim();
|
|
740
|
-
if (heading !== name) {
|
|
763
|
+
if (document.title !== null) {
|
|
764
|
+
if (document.title !== name) {
|
|
741
765
|
addDiagnostic({
|
|
742
766
|
severity: "error",
|
|
743
767
|
code: "heading_mismatch",
|
|
744
768
|
change: name,
|
|
745
769
|
path: fp,
|
|
746
|
-
message: `# Change: heading "${
|
|
770
|
+
message: `# Change: heading "${document.title}" differs from change name`
|
|
747
771
|
});
|
|
748
772
|
}
|
|
749
773
|
} else {
|
|
@@ -866,7 +890,7 @@ async function runCheck(options = {}) {
|
|
|
866
890
|
return result;
|
|
867
891
|
}
|
|
868
892
|
function findUnfinishedTemplateLines(content) {
|
|
869
|
-
const placeholderRe = /<[^>\n]*(?:choose:|one-line|what |why |which |who |how |new |updated |expected |actual |specific |concrete |exact |verifiable |user |capability |behavior |context |action
|
|
893
|
+
const placeholderRe = /<(?:…|[^>\n]*(?:choose:|one-line|what |why |which |who |how |new |updated |expected |actual |specific |concrete |exact |verifiable |user |capability |behavior |context |action|path|directory|module|subsystem|command|scenario|constraint|requirement|question|uncertainty|implementation)[^>\n]*)>/i;
|
|
870
894
|
return collectMatchingLines(content, (line) => {
|
|
871
895
|
const trimmed = stripInlineCodeSpans(line).trim();
|
|
872
896
|
return trimmed.startsWith("<!--") === false && placeholderRe.test(trimmed);
|
|
@@ -906,7 +930,7 @@ async function createChange(name, summary = "", kind, options = {}) {
|
|
|
906
930
|
const existed = existsSync2(changePath);
|
|
907
931
|
if (!existed) {
|
|
908
932
|
const focusEntry = resolveFocusMarkerPath(workRef);
|
|
909
|
-
const content = generateChangeContent(name, summary, kind, { lite: Boolean(options.lite) });
|
|
933
|
+
const content = generateChangeContent(workRef.name, summary, kind, { lite: Boolean(options.lite) });
|
|
910
934
|
await mkdir4(dirname2(changePath), { recursive: true });
|
|
911
935
|
let changeCreated = false;
|
|
912
936
|
try {
|
|
@@ -929,9 +953,9 @@ async function createChange(name, summary = "", kind, options = {}) {
|
|
|
929
953
|
const label = existed ? "Using" : pc.green("Created");
|
|
930
954
|
console.log(` ${label}: ${changePath}`);
|
|
931
955
|
if (existed)
|
|
932
|
-
console.log(` ${pc.dim("Unchanged focus.")} Run: rsp focus ${name}`);
|
|
956
|
+
console.log(` ${pc.dim("Unchanged focus.")} Run: rsp focus ${workRef.name}`);
|
|
933
957
|
else
|
|
934
|
-
console.log(` ${pc.dim("focused via focus.d")} \u2192 ${name}`);
|
|
958
|
+
console.log(` ${pc.dim("focused via focus.d")} \u2192 ${workRef.name}`);
|
|
935
959
|
console.log(` ${pc.cyan("Next:")} ${options.lite ? "fill the lite change details, then implement and verify" : "fill proposal/spec/design first, then implement and complete the tasks"}
|
|
936
960
|
`);
|
|
937
961
|
});
|
|
@@ -1565,8 +1589,8 @@ async function focusChange(name) {
|
|
|
1565
1589
|
await mkdir6(FOCUS_DIR, { recursive: true });
|
|
1566
1590
|
await mkdir6(dirname3(focusEntry), { recursive: true });
|
|
1567
1591
|
await writeManagedFile(focusEntry, "", "focus marker");
|
|
1568
|
-
console.log(` ${pc.green("Focused:")} ${name}`);
|
|
1569
|
-
console.log(` ${pc.dim("focus.d")} \u2192 ${name}`);
|
|
1592
|
+
console.log(` ${pc.green("Focused:")} ${workRef.name}`);
|
|
1593
|
+
console.log(` ${pc.dim("focus.d")} \u2192 ${workRef.name}`);
|
|
1570
1594
|
console.log();
|
|
1571
1595
|
});
|
|
1572
1596
|
} catch (error) {
|
|
@@ -1587,8 +1611,8 @@ async function unfocusChange(name) {
|
|
|
1587
1611
|
throw new WorkRefError("focus_marker_not_found", `.rsp/focus.d/${name}`, name);
|
|
1588
1612
|
await unlink7(focusEntry);
|
|
1589
1613
|
await cleanupEmptyParentDirs(focusEntry, FOCUS_DIR);
|
|
1590
|
-
console.log(` ${pc.green("Unfocused:")} ${name}`);
|
|
1591
|
-
console.log(` ${pc.dim("focus.d cleared")} \u2192 ${name}`);
|
|
1614
|
+
console.log(` ${pc.green("Unfocused:")} ${workRef.name}`);
|
|
1615
|
+
console.log(` ${pc.dim("focus.d cleared")} \u2192 ${workRef.name}`);
|
|
1592
1616
|
console.log();
|
|
1593
1617
|
});
|
|
1594
1618
|
} catch (error) {
|
|
@@ -1623,20 +1647,21 @@ async function createChangeGroup(name, goal = "") {
|
|
|
1623
1647
|
const ref = resolveWorkRef(`${name}/brief`);
|
|
1624
1648
|
if (ref.kind !== "group-brief")
|
|
1625
1649
|
throw new WorkRefError("invalid_work_ref", `invalid Change Group name: ${name}`, name);
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1650
|
+
const groupName = ref.group;
|
|
1651
|
+
if (await hasArchivedGroupBrief(groupName))
|
|
1652
|
+
throw new ChangeGroupError(`archived Change Group cannot be reopened: ${groupName}`);
|
|
1653
|
+
const groupDirectory = resolveManagedDirectoryChain(CHANGES_DIR, [groupName], "Change Group path");
|
|
1629
1654
|
await mkdir7(groupDirectory, { recursive: true });
|
|
1630
1655
|
try {
|
|
1631
|
-
await writeFile4(ref.path, generateGroupBriefContent(
|
|
1656
|
+
await writeFile4(ref.path, generateGroupBriefContent(groupName, goal), { flag: "wx" });
|
|
1632
1657
|
} catch (error) {
|
|
1633
1658
|
if (error.code === "EEXIST")
|
|
1634
|
-
throw new ChangeGroupError(`Change Group already exists: ${
|
|
1659
|
+
throw new ChangeGroupError(`Change Group already exists: ${groupName}`);
|
|
1635
1660
|
throw error;
|
|
1636
1661
|
}
|
|
1637
|
-
console.log(` ${pc.green("Created Change Group:")} ${
|
|
1662
|
+
console.log(` ${pc.green("Created Change Group:")} ${groupName}`);
|
|
1638
1663
|
console.log(` ${pc.dim("Unfocused Group Brief:")} ${ref.path}`);
|
|
1639
|
-
console.log(` ${pc.cyan("Next:")} fill the brief, then create direct child Changes with rsp create ${
|
|
1664
|
+
console.log(` ${pc.cyan("Next:")} fill the brief, then create direct child Changes with rsp create ${groupName}/<change>
|
|
1640
1665
|
`);
|
|
1641
1666
|
});
|
|
1642
1667
|
} catch (error) {
|
|
@@ -1659,12 +1684,13 @@ async function closeChangeGroup(name) {
|
|
|
1659
1684
|
if (ref.kind !== "group-brief")
|
|
1660
1685
|
throw new ChangeGroupError(`invalid Change Group name: ${name}`);
|
|
1661
1686
|
const inspection = await inspectChangeGroups();
|
|
1662
|
-
const
|
|
1687
|
+
const groupName = ref.group;
|
|
1688
|
+
const group = inspection.groups.find((candidate) => candidate.name === groupName);
|
|
1663
1689
|
if (!group)
|
|
1664
|
-
throw new ChangeGroupError(`Change Group not found: ${
|
|
1665
|
-
const reasons = collectCloseReasons(group, inspection.diagnostics.filter((diagnostic) => diagnostic.change ===
|
|
1690
|
+
throw new ChangeGroupError(`Change Group not found: ${groupName}`);
|
|
1691
|
+
const reasons = collectCloseReasons(group, inspection.diagnostics.filter((diagnostic) => diagnostic.change === groupName && diagnostic.severity === "error").map((diagnostic) => diagnostic.message));
|
|
1666
1692
|
if (reasons.length > 0)
|
|
1667
|
-
throw new ChangeGroupError(`Change Group "${
|
|
1693
|
+
throw new ChangeGroupError(`Change Group "${groupName}" is not ready to close: ${reasons.join("; ")}`);
|
|
1668
1694
|
const archiveDirectory = resolveArchiveDirectory(ref);
|
|
1669
1695
|
const date = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
1670
1696
|
const archiveName = resolveArchiveName(archiveDirectory, date, "brief");
|
|
@@ -1676,7 +1702,7 @@ async function closeChangeGroup(name) {
|
|
|
1676
1702
|
} catch (error) {
|
|
1677
1703
|
warnings.push(`changes directory cleanup failed: ${toErrorMessage(error)}`);
|
|
1678
1704
|
}
|
|
1679
|
-
console.log(` ${pc.green("Closed Change Group:")} ${
|
|
1705
|
+
console.log(` ${pc.green("Closed Change Group:")} ${groupName}`);
|
|
1680
1706
|
console.log(` ${pc.dim("Archived Group Brief:")} ${join7(archiveDirectory, archiveName)}
|
|
1681
1707
|
`);
|
|
1682
1708
|
for (const warning of warnings)
|
|
@@ -1712,8 +1738,6 @@ function collectCloseReasons(group, errors) {
|
|
|
1712
1738
|
|
|
1713
1739
|
// src/commands/history.ts
|
|
1714
1740
|
var DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
1715
|
-
var GROUP_RE = /^[a-z0-9-]+$/;
|
|
1716
|
-
var WORK_REF_RE = /^[a-z0-9-]+(?:\/[a-z0-9-]+)?$/;
|
|
1717
1741
|
async function showHistory(input = {}, options = {}) {
|
|
1718
1742
|
guardRspInitialized();
|
|
1719
1743
|
const mode = input.workRef ? "detail" : "list";
|
|
@@ -1727,15 +1751,15 @@ async function showHistory(input = {}, options = {}) {
|
|
|
1727
1751
|
message: "archive history inspection is incomplete; resolve the reported diagnostics before querying history"
|
|
1728
1752
|
}, inspection.diagnostics, inspection.diagnosticSummary, inspection.runtime, options);
|
|
1729
1753
|
}
|
|
1730
|
-
if (
|
|
1754
|
+
if (validation.workRef) {
|
|
1731
1755
|
try {
|
|
1732
|
-
const selected = selectArchiveHistoryRecord(inspection.records, { workRef:
|
|
1756
|
+
const selected = selectArchiveHistoryRecord(inspection.records, { workRef: validation.workRef });
|
|
1733
1757
|
const record = await readArchiveHistoryDetail(selected);
|
|
1734
1758
|
const result2 = {
|
|
1735
1759
|
command: "history",
|
|
1736
1760
|
ok: true,
|
|
1737
1761
|
mode: "detail",
|
|
1738
|
-
query: { workRef:
|
|
1762
|
+
query: { workRef: validation.workRef },
|
|
1739
1763
|
record,
|
|
1740
1764
|
diagnostics: [],
|
|
1741
1765
|
diagnosticSummary: emptyCollectionSummary(),
|
|
@@ -1788,11 +1812,15 @@ function validateHistoryQuery(input) {
|
|
|
1788
1812
|
if ((input.positionalCount ?? (input.workRef ? 1 : 0)) > 1)
|
|
1789
1813
|
return invalid("history_positional_args_unsupported", "history accepts at most one positional WorkRef");
|
|
1790
1814
|
if (input.workRef) {
|
|
1791
|
-
|
|
1815
|
+
let workRef;
|
|
1816
|
+
try {
|
|
1817
|
+
workRef = normalizeExecutableWorkRef(input.workRef);
|
|
1818
|
+
} catch {
|
|
1792
1819
|
return invalid("invalid_history_work_ref", "history WorkRef must be a flat or one-Group-level executable Change identity");
|
|
1820
|
+
}
|
|
1793
1821
|
if (input.limit !== void 0 || input.since !== void 0 || input.until !== void 0 || input.kind !== void 0 || input.group !== void 0 || input.search !== void 0)
|
|
1794
1822
|
return invalid("history_detail_filters_unsupported", "list filters cannot be combined with an exact history detail lookup");
|
|
1795
|
-
return { ok: true, query: {} };
|
|
1823
|
+
return { ok: true, query: {}, workRef };
|
|
1796
1824
|
}
|
|
1797
1825
|
const limit = input.limit === void 0 ? HISTORY_DEFAULT_LIMIT : Number(input.limit);
|
|
1798
1826
|
if (input.limit !== void 0 && !/^[1-9]\d*$/.test(input.limit) || !Number.isInteger(limit) || limit < 1 || limit > HISTORY_MAX_LIMIT)
|
|
@@ -1805,8 +1833,14 @@ function validateHistoryQuery(input) {
|
|
|
1805
1833
|
return invalid("invalid_history_date_range", "--since must be on or before --until");
|
|
1806
1834
|
if (input.kind !== void 0 && input.kind.trim() === "")
|
|
1807
1835
|
return invalid("invalid_history_kind", "--kind must be a non-empty exact historical kind");
|
|
1808
|
-
|
|
1809
|
-
|
|
1836
|
+
let group = input.group;
|
|
1837
|
+
if (group !== void 0) {
|
|
1838
|
+
try {
|
|
1839
|
+
group = normalizeWorkRefSegment(group);
|
|
1840
|
+
} catch {
|
|
1841
|
+
return invalid("invalid_history_group", "--group must be one safe Unicode Group name");
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1810
1844
|
if (input.search !== void 0 && input.search.trim() === "")
|
|
1811
1845
|
return invalid("invalid_history_search", "--search must be non-empty");
|
|
1812
1846
|
return {
|
|
@@ -1816,7 +1850,7 @@ function validateHistoryQuery(input) {
|
|
|
1816
1850
|
since: input.since,
|
|
1817
1851
|
until: input.until,
|
|
1818
1852
|
kind: input.kind?.trim(),
|
|
1819
|
-
group
|
|
1853
|
+
group,
|
|
1820
1854
|
search: input.search?.trim()
|
|
1821
1855
|
}
|
|
1822
1856
|
};
|
|
@@ -1925,6 +1959,13 @@ function generateConfigTemplate() {
|
|
|
1925
1959
|
# kinds:
|
|
1926
1960
|
${fmtList(VALID_KINDS, 0)}
|
|
1927
1961
|
|
|
1962
|
+
# Set one shared project default for durable artifact and commit prose.
|
|
1963
|
+
# Optional artifacts or commit values override that durable surface independently.
|
|
1964
|
+
# Response language remains user/session-owned and is never read from this project mapping.
|
|
1965
|
+
# Omit this mapping to keep durable language selection under scoped project authority.
|
|
1966
|
+
# language:
|
|
1967
|
+
# default: en
|
|
1968
|
+
|
|
1928
1969
|
# New projects default to automatic Manage routing with lifecycle-only closeout.
|
|
1929
1970
|
# Use activation: explicit to require a named managed request.
|
|
1930
1971
|
# closeout accepts manual, lifecycle, or local; local routes a qualified clean terminal non-small boundary to one local commit but never push or publication.
|
|
@@ -2196,6 +2237,7 @@ async function reopenChange(name, options) {
|
|
|
2196
2237
|
guardRspInitialized();
|
|
2197
2238
|
try {
|
|
2198
2239
|
await withRspLock("reopen-change", async () => {
|
|
2240
|
+
const normalizedName = normalizeExecutableWorkRef(name);
|
|
2199
2241
|
const inspection = await inspectArchiveHistory();
|
|
2200
2242
|
if (!historyInspectionComplete(inspection)) {
|
|
2201
2243
|
throw new ArchiveHistoryError(
|
|
@@ -2205,17 +2247,17 @@ async function reopenChange(name, options) {
|
|
|
2205
2247
|
}
|
|
2206
2248
|
const record = selectArchiveHistoryRecord(
|
|
2207
2249
|
inspection.records,
|
|
2208
|
-
options.from ? { path: options.from } : { workRef:
|
|
2250
|
+
options.from ? { path: options.from } : { workRef: normalizedName }
|
|
2209
2251
|
);
|
|
2210
|
-
if (record.workRef !==
|
|
2252
|
+
if (record.workRef !== normalizedName) {
|
|
2211
2253
|
throw new ArchiveHistoryError(
|
|
2212
2254
|
"archive_identity_mismatch",
|
|
2213
|
-
`selected archive belongs to ${record.workRef}, not ${
|
|
2255
|
+
`selected archive belongs to ${record.workRef}, not ${normalizedName}`
|
|
2214
2256
|
);
|
|
2215
2257
|
}
|
|
2216
2258
|
let workRef;
|
|
2217
2259
|
try {
|
|
2218
|
-
workRef = resolveWorkRef(
|
|
2260
|
+
workRef = resolveWorkRef(normalizedName, { executable: true });
|
|
2219
2261
|
} catch (error) {
|
|
2220
2262
|
if (error instanceof WorkRefError && error.code === "group_brief_missing") {
|
|
2221
2263
|
throw new ArchiveHistoryError(
|
|
@@ -2232,8 +2274,8 @@ async function reopenChange(name, options) {
|
|
|
2232
2274
|
throw new ArchiveHistoryError("focus_marker_exists", `focus marker already exists for non-open Change: ${name}; run rsp doctor`);
|
|
2233
2275
|
const archivedContent = await readFile10(record.sourcePath, "utf-8");
|
|
2234
2276
|
const reopenedContent = appendChecklistItem(
|
|
2235
|
-
appendChecklistItem(archivedContent, "
|
|
2236
|
-
"
|
|
2277
|
+
appendChecklistItem(archivedContent, "tasks", `- [ ] Resolve reopened concern: ${reason}`),
|
|
2278
|
+
"verify",
|
|
2237
2279
|
`- [ ] Verify reopened concern: ${reason}`
|
|
2238
2280
|
);
|
|
2239
2281
|
await mkdir9(dirname4(workRef.path), { recursive: true });
|
|
@@ -2254,9 +2296,9 @@ async function reopenChange(name, options) {
|
|
|
2254
2296
|
}
|
|
2255
2297
|
throw error;
|
|
2256
2298
|
}
|
|
2257
|
-
console.log(` ${pc.green("Reopened:")} ${name}`);
|
|
2299
|
+
console.log(` ${pc.green("Reopened:")} ${workRef.name}`);
|
|
2258
2300
|
console.log(` ${pc.dim("retained archive")} \u2192 ${record.path}`);
|
|
2259
|
-
console.log(` ${pc.dim("focused via focus.d")} \u2192 ${name}`);
|
|
2301
|
+
console.log(` ${pc.dim("focused via focus.d")} \u2192 ${workRef.name}`);
|
|
2260
2302
|
console.log(` ${pc.cyan("Next:")} refine the reopened Task and Verify evidence, then resolve the concern
|
|
2261
2303
|
`);
|
|
2262
2304
|
if (existsSync7(".git")) {
|
|
@@ -2280,21 +2322,18 @@ async function reopenChange(name, options) {
|
|
|
2280
2322
|
throw error;
|
|
2281
2323
|
}
|
|
2282
2324
|
}
|
|
2283
|
-
function appendChecklistItem(content,
|
|
2284
|
-
|
|
2285
|
-
|
|
2325
|
+
function appendChecklistItem(content, sectionId, item) {
|
|
2326
|
+
try {
|
|
2327
|
+
return appendDocumentSectionItem(parseRspDocument(content, CHANGE_DOCUMENT_SCHEMA), sectionId, item);
|
|
2328
|
+
} catch (error) {
|
|
2329
|
+
if (!(error instanceof DocumentSectionCardinalityError))
|
|
2330
|
+
throw error;
|
|
2331
|
+
const heading = CHANGE_DOCUMENT_SCHEMA.sections.find((section) => section.id === sectionId).heading;
|
|
2286
2332
|
throw new ArchiveHistoryError(
|
|
2287
2333
|
"archive_section_invalid",
|
|
2288
2334
|
`archived Change must contain exactly one required section: ${heading}`
|
|
2289
2335
|
);
|
|
2290
2336
|
}
|
|
2291
|
-
const sectionBodyStart = headings[0].index + headings[0][0].length;
|
|
2292
|
-
const nextHeading = content.indexOf("\n## ", sectionBodyStart);
|
|
2293
|
-
const insertAt = nextHeading < 0 ? content.length : nextHeading;
|
|
2294
|
-
const before = content.slice(0, insertAt);
|
|
2295
|
-
const separator = before.endsWith("\n\n") ? "" : before.endsWith("\n") ? "\n" : "\n\n";
|
|
2296
|
-
return `${before}${separator}${item}
|
|
2297
|
-
${content.slice(insertAt)}`;
|
|
2298
2337
|
}
|
|
2299
2338
|
|
|
2300
2339
|
// src/commands/show.ts
|
|
@@ -2488,6 +2527,7 @@ function deriveStatusView(snapshot, options = {}) {
|
|
|
2488
2527
|
const nextActions = ok ? buildStatusNextActions(snapshot.focused.length, snapshot.records.map((record) => record.output), snapshot.groups, filtered) : ["Run: rsp doctor"];
|
|
2489
2528
|
return {
|
|
2490
2529
|
manage: snapshot.manage,
|
|
2530
|
+
language: snapshot.language,
|
|
2491
2531
|
query,
|
|
2492
2532
|
focused: snapshot.focused,
|
|
2493
2533
|
records,
|
|
@@ -2584,6 +2624,8 @@ function printStatusPlain(view) {
|
|
|
2584
2624
|
console.log(` ${pc.bold("RSP status")}`);
|
|
2585
2625
|
console.log();
|
|
2586
2626
|
console.log(` ${pc.bold("Manage:")} activation ${view.manage.activation} \xB7 closeout ${view.manage.closeout}`);
|
|
2627
|
+
if (view.language.artifacts !== null || view.language.commit !== null)
|
|
2628
|
+
console.log(` ${pc.bold("Language:")} artifacts ${view.language.artifacts ?? "unset"} \xB7 commit ${view.language.commit ?? "unset"}`);
|
|
2587
2629
|
console.log();
|
|
2588
2630
|
for (const diagnostic of view.diagnostics) {
|
|
2589
2631
|
const label = diagnostic.change ?? diagnostic.path;
|
|
@@ -2703,6 +2745,7 @@ function toStatusJson(view) {
|
|
|
2703
2745
|
command: "status",
|
|
2704
2746
|
ok: view.ok,
|
|
2705
2747
|
manage: view.manage,
|
|
2748
|
+
language: view.language,
|
|
2706
2749
|
filters: view.query,
|
|
2707
2750
|
focused: view.focused,
|
|
2708
2751
|
records: view.records.map((record) => record.output),
|
|
@@ -2720,6 +2763,7 @@ function toStatusJsonError(error, options) {
|
|
|
2720
2763
|
command: "status",
|
|
2721
2764
|
ok: false,
|
|
2722
2765
|
manage: { activation: "explicit", closeout: "manual" },
|
|
2766
|
+
language: { artifacts: null, commit: null },
|
|
2723
2767
|
filters: {
|
|
2724
2768
|
focused: options.focused,
|
|
2725
2769
|
blocked: options.blocked,
|
|
@@ -3352,7 +3396,7 @@ async function runCli(rawArgs = process.argv.slice(2)) {
|
|
|
3352
3396
|
if (shouldLaunchSkillsUi(rawArgs, terminalEnvironment)) {
|
|
3353
3397
|
const { resolveUiLocale } = await import("./locale-HT44UIUP.mjs");
|
|
3354
3398
|
const locale = resolveUiLocale("auto", process.env.RSP_UI_LANG, process.env.LC_ALL ?? process.env.LC_MESSAGES ?? process.env.LANG ?? new Intl.DateTimeFormat().resolvedOptions().locale);
|
|
3355
|
-
const { runSkillsTui } = await import("./entry-
|
|
3399
|
+
const { runSkillsTui } = await import("./entry-5OPKUZHF.mjs");
|
|
3356
3400
|
const selection = await runSkillsTui(locale);
|
|
3357
3401
|
if (selection.kind === "confirmed") {
|
|
3358
3402
|
const result = await installPackagedSkills({ names: selection.names, force: selection.force });
|
|
@@ -3370,7 +3414,7 @@ async function runCli(rawArgs = process.argv.slice(2)) {
|
|
|
3370
3414
|
} else {
|
|
3371
3415
|
const { resolveUiLocale } = await import("./locale-HT44UIUP.mjs");
|
|
3372
3416
|
const locale = resolveUiLocale(lang, process.env.RSP_UI_LANG, process.env.LC_ALL ?? process.env.LC_MESSAGES ?? process.env.LANG ?? new Intl.DateTimeFormat().resolvedOptions().locale);
|
|
3373
|
-
const { runTui } = await import("./entry-
|
|
3417
|
+
const { runTui } = await import("./entry-FVUG6YW2.mjs");
|
|
3374
3418
|
const exitCode = await runTui(locale);
|
|
3375
3419
|
if (exitCode !== 0)
|
|
3376
3420
|
process.exitCode = exitCode;
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
inspectPackagedSkillInventory
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-D7XDXBFO.mjs";
|
|
5
5
|
import {
|
|
6
6
|
openTerminalSession,
|
|
7
7
|
truncateDisplay
|
|
8
8
|
} from "./chunk-JOMFFQRG.mjs";
|
|
9
|
-
import "./chunk-
|
|
9
|
+
import "./chunk-FJVH7ZYU.mjs";
|
|
10
10
|
|
|
11
11
|
// src/skills-tui/entry.tsx
|
|
12
12
|
import process from "process";
|
|
@@ -7,14 +7,14 @@ import {
|
|
|
7
7
|
projectDependencyForest,
|
|
8
8
|
queryArchiveHistory,
|
|
9
9
|
readArchiveHistoryDetail
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-FI3SYHVH.mjs";
|
|
11
11
|
import {
|
|
12
12
|
displayWidth,
|
|
13
13
|
openTerminalSession,
|
|
14
14
|
truncateDisplay,
|
|
15
15
|
wrapDisplay
|
|
16
16
|
} from "./chunk-JOMFFQRG.mjs";
|
|
17
|
-
import "./chunk-
|
|
17
|
+
import "./chunk-FJVH7ZYU.mjs";
|
|
18
18
|
|
|
19
19
|
// src/tui/entry.tsx
|
|
20
20
|
import process from "process";
|
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.4",
|
|
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",
|
package/rules/rsp-rules.md
CHANGED
|
@@ -18,7 +18,8 @@ This file is the minimal fallback protocol for agents that cannot load the `rsp`
|
|
|
18
18
|
|
|
19
19
|
## Core
|
|
20
20
|
|
|
21
|
-
-
|
|
21
|
+
- Keep response language user/session-owned and durable project language repository-owned. For response prose, use an explicit current response language, user/host personal instructions, then conversation language; project `.rsp/config.yaml` never selects it. For a new authorized artifact, use an explicit artifact language, then the configured effective artifact language, artifact-scoped project instructions, and conversation language. Preserve an existing artifact's established language unless translation is explicitly authorized; changing configuration never rewrites it. Commit-message prose follows an explicit current commit-language instruction, configured effective commit language, nearest repository commit authority, then clear recent non-merge history. Preserve canonical headings, paths, commands, identifiers, Conventional Commit types/scopes, trailers, and machine values. WorkRefs follow the Unicode identity contract rather than the natural-language fallback chain.
|
|
22
|
+
- Treat CLI-generated artifact scaffolds as deterministic structure, not natural-language generation: keep canonical structural tokens, neutral `<…>` placeholders, and verbatim user input; fill authorized prose from the effective artifact-language policy without a locale catalog or implicit English fallback.
|
|
22
23
|
- 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
24
|
- `changes/` stores open work; `archives/` stores completed history; `specs/` stores stable current facts; the configured Decision Record path stores lasting rationale.
|
|
24
25
|
- Every Change is one Markdown file with explicit `kind` and the fixed `Proposal`, `Spec`, `Design`, `Tasks`, `Verify`, and `Blockers` sections.
|
|
@@ -35,9 +36,9 @@ This file is the minimal fallback protocol for agents that cannot load the `rsp`
|
|
|
35
36
|
- Persist only `open` and `archived`; readiness, blockers, verification, and next actions are derived.
|
|
36
37
|
- 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
38
|
- 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,
|
|
39
|
+
- Before later-turn mutation under a direct report, design, 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, bounded review convergence, lifecycle delivery, or a clear ready successor requires the smallest sufficient WorkRef and fresh qualification before mutation; unchanged tiny/small follow-ups stay direct. Elapsed time and message count alone never escalate work.
|
|
39
40
|
- 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
|
+
- 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. Automatic eligibility includes independent slices, interruption recovery, or prospective execution spanning implementation plus integration/review/lifecycle, cross-module or cross-process mutation, real-host/provider/hardware verification, bounded finding convergence, or a clear ready successor. Decline as direct one-step work only when one owner, one local seam, one mutation pass, one decisive check, no managed lifecycle coordination, and no ready successor all hold; a selected ready completion or continuation that fails any one condition is non-small and requires loaded Manage rather than an unclassified fallback. Report that Manage is unavailable rather than selected, the decisive eligibility or exclusion, the exact direct action, and any applicable serial/parallel overlap reason; do not persist that reasoning. 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
42
|
- 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
43
|
- 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
44
|
- Do not infer current work from open Changes or filenames.
|
package/skills/rsp/SKILL.md
CHANGED
|
@@ -4,14 +4,14 @@ description: Use this skill when initializing RSP, operating an existing .rsp pr
|
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
6
|
author: oevery
|
|
7
|
-
version: "2026.07.28.
|
|
7
|
+
version: "2026.07.28.4"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# RSP Skill
|
|
11
11
|
|
|
12
12
|
Use RSP to derive one current action from project authority and durable work artifacts. This Skill is the preferred operational guide; `.rsp/rsp-rules.md` is the runtime fallback when the Skill is unavailable.
|
|
13
13
|
|
|
14
|
-
Nearest project instructions and relevant `CONTEXT.md` remain authoritative.
|
|
14
|
+
Nearest project instructions and relevant `CONTEXT.md` remain authoritative. Keep response language user/session-owned and durable project language repository-owned. Response prose follows an explicit current response language, user/host personal instructions, then conversation language; project `.rsp/config.yaml` never selects it. A new authorized artifact follows an explicit artifact language, then the configured effective artifact language, artifact-scoped project instructions, and conversation language; an existing artifact keeps its established language unless translation is explicitly authorized. Configuration changes never rewrite existing artifacts. Commit-message prose follows an explicit current commit-language instruction, configured effective commit language, nearest repository commit authority, then clear recent non-merge history. Preserve canonical headings, paths, commands, identifiers, Conventional Commit types/scopes, trailers, and machine values; WorkRefs follow the Unicode identity contract rather than the natural-language fallback chain. Write persistent artifacts in domain, system, user, or operator language; mention AI or agents only when they are actual product actors or constraints.
|
|
15
15
|
|
|
16
16
|
## Scope
|
|
17
17
|
|
|
@@ -23,7 +23,7 @@ Read user intent, nearest authority, `rsp status --json`, the selected Change an
|
|
|
23
23
|
|
|
24
24
|
Apply these routes in order:
|
|
25
25
|
|
|
26
|
-
Before later-turn mutation under a direct report, tiny, or small route, rederive routing from the now-authorized objective and prospective work. If it materially expands into cross-module implementation, multiple acceptance surfaces, repeated production-path correction, real-host validation,
|
|
26
|
+
Before later-turn mutation under a direct report, design, tiny, or small route, rederive routing from the now-authorized objective and prospective work. If it materially expands into cross-module implementation, multiple acceptance surfaces, repeated production-path correction, real-host validation, bounded review convergence, lifecycle delivery, or a clear ready successor, establish or reuse the smallest sufficient WorkRef, then apply managed preflight and fresh qualification before mutation. Follow-ups that remain tiny/small stay direct. Elapsed time and message count are never escalation evidence.
|
|
27
27
|
|
|
28
28
|
1. Stop for ambiguous authority or selection and return the one required input and its owner. For a material owner decision inside an explicit managed request, continue to the Shape preflight in route 2 so repository inspection precedes the single owner question; otherwise stop here.
|
|
29
29
|
2. For a requested completion or continuation that is not a report-only review or release operation, read [managed routing](references/managed-routing.md) when the request is explicitly managed or the effective status policy has `manage.activation: auto`. It owns owner preflight, qualification, successor discovery, bounded review convergence, and lifecycle/Git separation. Select `rsp-manage` only after that preflight for an eligible selected ready Change or shallow Group; otherwise return the ordinary Core or Discipline action. Automatic activation grants controller selection only; planning, product mutation, lifecycle, Git, and external authority remain separately derived.
|
|
@@ -35,7 +35,7 @@ Before later-turn mutation under a direct report, tiny, or small route, rederive
|
|
|
35
35
|
8. For incomplete implementation or stale, missing, or failed verification, use [Implementation evidence](#implementation-evidence).
|
|
36
36
|
9. When Tasks and required verification pass without blockers, perform the durable decision. Do not infer a release operation from Change completion.
|
|
37
37
|
|
|
38
|
-
State the derived stage, one next action, required input, returned owner, and decisive evidence. Name at most one optional capability, only when it is the next action and appears in the host's loaded Skill inventory. Missing capabilities never invalidate RSP: give the manual fallback against the same owner. When Core or qualified Manage has derived one authorized RSP-owned local commit boundary, route exact staging, structured message construction, local commit execution, and post-commit observation to `rsp-commit`; if unavailable, use the bounded manual action in durable review against the same owner. Do not preload, enumerate, or recursively invoke optional capabilities.
|
|
38
|
+
State the derived stage, one next action, required input, returned owner, and decisive evidence. When managed routing is evaluated, state `selected` or `declined` with the decisive qualification signal or complete direct-work exclusion; when dispatch applies, also state why it is parallel or sequential. Name at most one optional capability, only when it is the next action and appears in the host's loaded Skill inventory. Missing capabilities never invalidate RSP: give the manual fallback against the same owner. When Core or qualified Manage has derived one authorized RSP-owned local commit boundary, route exact staging, structured message construction, local commit execution, and post-commit observation to `rsp-commit`; if unavailable, use the bounded manual action in durable review against the same owner. Do not preload, enumerate, or recursively invoke optional capabilities.
|
|
39
39
|
|
|
40
40
|
### Implementation evidence
|
|
41
41
|
|
|
@@ -13,13 +13,17 @@ Before focusing, dispatching, or mutating a different WorkRef, inspect dirty pat
|
|
|
13
13
|
- For clear non-trivial work with no sufficient ready owner, an explicit managed request authorizes only in-scope RSP planning artifacts unless the user requests no edits. Under automatic activation, route to Shape only when the current request or nearer authority independently permits those artifacts; configuration alone never does. Then re-read status/readiness and re-evaluate without another authorization round.
|
|
14
14
|
- If repository evidence leaves a material product, acceptance, public-interface, scope, mutation-authority, external-action, or human choice, Shape returns the single highest-impact owner decision and creates no implementation or controller artifact.
|
|
15
15
|
|
|
16
|
-
A prior direct report, tiny, or small route is not sticky. Before later-turn mutation, rederive from the now-authorized objective and prospective work. Material expansion into cross-module implementation, multiple acceptance surfaces, repeated production-path correction, real-host validation,
|
|
16
|
+
A prior direct report, design, tiny, or small route is not sticky. Before later-turn mutation, rederive from the now-authorized objective and prospective work. Material expansion into cross-module implementation, multiple acceptance surfaces, repeated production-path correction, real-host validation, bounded review convergence, lifecycle delivery, or a clear ready successor requires Core to establish or reuse the smallest sufficient WorkRef and rerun this preflight plus fresh Manage qualification before mutation. Unchanged tiny/small follow-ups remain direct; elapsed time and message count alone never trigger escalation.
|
|
17
17
|
|
|
18
18
|
PREFLIGHT is complete only when one selected ready Change or shallow Group owns the requested outcome and no material decision remains.
|
|
19
19
|
|
|
20
20
|
## QUALIFY — select or decline Manage
|
|
21
21
|
|
|
22
|
-
Select `rsp-manage` only for one selected ready Change or shallow Group that qualifies through at least one independent path: genuinely independent slices,
|
|
22
|
+
Select `rsp-manage` only for one selected ready Change or shallow Group that qualifies through at least one independent path: genuinely independent slices, interruption recovery, or prospective execution signals showing more than one bounded phase or authority surface. Prospective signals are implementation followed by integration verification, managed review, or lifecycle work; cross-module or cross-process mutation; real-host, provider, or hardware verification; bounded finding convergence; or a clear ready successor. A Change with prospective or recovery work does not also need independent or parallelizable slices. Derive these signals before dispatch from the authorized objective and expected phases; elapsed wall-clock minutes are never qualification evidence.
|
|
23
|
+
|
|
24
|
+
Under automatic activation, bias non-small continuation toward Manage. Decline as direct one-step work only when all of these are true: one owner, one local seam, one mutation pass, one decisive check, no managed lifecycle coordination, and no ready successor. A selected ready completion or continuation that fails any one of these conditions qualifies as non-small through this automatic path even when no separate prospective signal is obvious; do not leave the middle case unclassified. Worker-only work remains ineligible. Readiness alone does not qualify work without a requested completion or continuation, and automatic routing remains bounded by the requested goal.
|
|
25
|
+
|
|
26
|
+
Make the route observable: report `selected` with the decisive qualification signal, or `declined` with the complete direct-work exclusion and exact Core or Discipline action. If dispatch applies, report the concrete overlap/isolation evidence that makes it sequential or parallel. Selection, decline, and dispatch reasoning remain transient and create no controller state.
|
|
23
27
|
|
|
24
28
|
## CONTINUE — rederive from evidence
|
|
25
29
|
|
|
@@ -4,7 +4,7 @@ description: Create one authorized, exact-scope local commit for an RSP-owned Ch
|
|
|
4
4
|
license: MIT
|
|
5
5
|
metadata:
|
|
6
6
|
author: oevery
|
|
7
|
-
version: "2026.07.
|
|
7
|
+
version: "2026.07.28.2"
|
|
8
8
|
---
|
|
9
9
|
|
|
10
10
|
# RSP Commit
|
|
@@ -19,7 +19,7 @@ Stop without staging when the owner, allowed paths, commit authority, verificati
|
|
|
19
19
|
|
|
20
20
|
## Derive the message
|
|
21
21
|
|
|
22
|
-
Choose subject language from explicit current instruction, then nearest repository authority,
|
|
22
|
+
Choose subject and body prose language from explicit current commit-language instruction, then the configured effective commit language, nearest repository commit authority, and finally the clear style of recent non-merge commits. Response language and preferences remembered from another repository do not select it. Preserve Conventional Commit types/scopes and trailers as technical values. When recent history is materially mixed and no nearer rule resolves it, return the single language decision to its owner.
|
|
23
23
|
|
|
24
24
|
Use the repository's established Conventional Commit form when present. Derive type and scope from the owned outcome and repository history, not from the conversation. Keep the subject concise and imperative or otherwise repository-consistent.
|
|
25
25
|
|