role-os 1.5.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +49 -0
- package/README.md +4 -1
- package/bin/roleos.mjs +8 -0
- package/package.json +1 -1
- package/src/artifacts-cmd.mjs +180 -0
- package/src/artifacts.mjs +437 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,54 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.7.0
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
#### Completion Proof (Phase R)
|
|
8
|
+
- `roleos artifacts` CLI command: list, show, validate, chain subcommands
|
|
9
|
+
- 13 new CLI integration tests for artifact inspection
|
|
10
|
+
- Real task completion missions through the full stack
|
|
11
|
+
|
|
12
|
+
#### Completion Proof Evidence
|
|
13
|
+
- R1-1 Feature mission: `roleos artifacts` command shipped through feature pack
|
|
14
|
+
- Pack: feature (high confidence, correct)
|
|
15
|
+
- Chain: 5 roles, 0 escalations, 1 minor correction
|
|
16
|
+
- Artifact contracts: all 4 used and valid
|
|
17
|
+
- R1-2 Bugfix mission: README.zh.md npm anomaly
|
|
18
|
+
- Diagnosed correctly: npm auto-includes README* regardless of files field
|
|
19
|
+
- Escalated honestly: fix requires structural decision (translation file organization)
|
|
20
|
+
- Not force-closed: deferred to treatment pass
|
|
21
|
+
|
|
22
|
+
### Evidence
|
|
23
|
+
- 398 tests, zero failures
|
|
24
|
+
- 3 missions run through the full stack
|
|
25
|
+
- Completion metrics recorded per mission
|
|
26
|
+
|
|
27
|
+
## 1.6.0
|
|
28
|
+
|
|
29
|
+
### Added
|
|
30
|
+
|
|
31
|
+
#### Artifact Spine (Phase Q)
|
|
32
|
+
- 20 per-role artifact contracts: each defines artifact type, required sections, evidence references, downstream consumers, and completion rules
|
|
33
|
+
- `validateArtifact(role, content)` — structural validation against role contracts (missing sections, evidence references, content depth)
|
|
34
|
+
- 7 pack-level handoff contracts: define the expected artifact flow between steps for each pack (e.g., strategy-brief → implementation-spec → change-plan → test-package → verdict)
|
|
35
|
+
- `validatePackChain(pack, artifacts)` — validates an entire pack's artifact chain for completeness
|
|
36
|
+
- `getArtifactContract(role)` / `getHandoffContract(pack)` — lookup APIs
|
|
37
|
+
- `formatArtifactValidation()` / `formatPackChain()` — display formatters
|
|
38
|
+
|
|
39
|
+
#### Artifact contract coverage
|
|
40
|
+
- Product Strategist → strategy-brief (problem-framing, scope, non-goals, tradeoffs)
|
|
41
|
+
- Spec Writer → implementation-spec (acceptance-criteria, edge-cases, interface-spec)
|
|
42
|
+
- Backend/Frontend Engineer → change-plan (files-to-change, implementation-approach, risk-notes)
|
|
43
|
+
- Test Engineer → test-package (test-plan, test-cases, false-confidence-assessment)
|
|
44
|
+
- Security Reviewer → security-findings (findings, severity-assessment, recommendations)
|
|
45
|
+
- Critic Reviewer → verdict (verdict, evidence, required-corrections)
|
|
46
|
+
- And 14 more roles with full contracts
|
|
47
|
+
|
|
48
|
+
### Evidence
|
|
49
|
+
- 385 tests, zero failures
|
|
50
|
+
- 27 new artifact tests
|
|
51
|
+
|
|
3
52
|
## 1.5.0
|
|
4
53
|
|
|
5
54
|
### Added
|
package/README.md
CHANGED
|
@@ -180,6 +180,7 @@ Role OS operates **locally only**. It copies markdown templates and writes packe
|
|
|
180
180
|
| **Adaptive replanning** | Mid-run scope changes, findings, or new requirements update the plan without restarting. | ✓ Shipped |
|
|
181
181
|
| **Session spine** | `roleos init claude` scaffolds CLAUDE.md, /roleos-route, /roleos-review, /roleos-status. `roleos doctor` verifies wiring. Route cards prove engagement. | ✓ Shipped |
|
|
182
182
|
| **Hook spine** | 5 lifecycle hooks (SessionStart, PromptSubmit, PreToolUse, SubagentStart, Stop). Advisory enforcement: route card reminders, write-tool gating, subagent role injection, completion audit. | ✓ Shipped |
|
|
183
|
+
| **Artifact spine** | 20 per-role artifact contracts. 7 pack handoff contracts. Structural validation. Chain completeness checks. Downstream roles never guess what they received. | ✓ Shipped |
|
|
183
184
|
|
|
184
185
|
## Status
|
|
185
186
|
|
|
@@ -190,7 +191,9 @@ Role OS operates **locally only**. It copies markdown templates and writes packe
|
|
|
190
191
|
- v1.2.0: Calibrated packs promoted to default entry. Auto-selection, mismatch detection, alternative suggestion, free-routing fallback. 246 tests.
|
|
191
192
|
- v1.3.0: Outcome calibration, mixed-task decomposition, composite execution, adaptive replanning. 317 tests.
|
|
192
193
|
- v1.4.0: Session spine — `roleos init claude`, `roleos doctor`, route cards, /roleos-route + /roleos-review + /roleos-status commands. 335 tests.
|
|
193
|
-
-
|
|
194
|
+
- v1.5.0: Hook spine — 5 lifecycle hooks for runtime enforcement. 358 tests.
|
|
195
|
+
- v1.6.0: Artifact spine — 20 per-role artifact contracts, 7 pack handoff contracts, structural validation. 385 tests.
|
|
196
|
+
- **v1.7.0**: Completion proof — real tasks run through the full stack. `roleos artifacts` CLI. Honest escalation on structural fixes. 398 tests.
|
|
194
197
|
|
|
195
198
|
## License
|
|
196
199
|
|
package/bin/roleos.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import { reviewCommand } from "../src/review.mjs";
|
|
|
10
10
|
import { statusCommand } from "../src/status.mjs";
|
|
11
11
|
import { packsCommand } from "../src/packs-cmd.mjs";
|
|
12
12
|
import { scaffoldClaude, doctor, formatDoctor } from "../src/session.mjs";
|
|
13
|
+
import { artifactsCommand } from "../src/artifacts-cmd.mjs";
|
|
13
14
|
|
|
14
15
|
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
15
16
|
const VERSION = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf-8")).version;
|
|
@@ -30,6 +31,10 @@ Usage:
|
|
|
30
31
|
roleos packs list List all available team packs
|
|
31
32
|
roleos packs suggest <packet-file> Suggest a pack for a packet
|
|
32
33
|
roleos packs show <pack-key> Show full detail for a named pack
|
|
34
|
+
roleos artifacts List all role artifact contracts
|
|
35
|
+
roleos artifacts show <role> Show artifact contract for a role
|
|
36
|
+
roleos artifacts validate <role> <file> Validate a file against a contract
|
|
37
|
+
roleos artifacts chain <pack> Show pack handoff flow
|
|
33
38
|
roleos doctor Verify repo is wired for Role OS sessions
|
|
34
39
|
roleos help Show this help
|
|
35
40
|
|
|
@@ -103,6 +108,9 @@ try {
|
|
|
103
108
|
case "packs":
|
|
104
109
|
await packsCommand(args);
|
|
105
110
|
break;
|
|
111
|
+
case "artifacts":
|
|
112
|
+
await artifactsCommand(args);
|
|
113
|
+
break;
|
|
106
114
|
case "help":
|
|
107
115
|
case "--help":
|
|
108
116
|
case "-h":
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "role-os",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.7.0",
|
|
4
4
|
"description": "Role OS — a multi-Claude operating system where 31 specialized roles execute work through contracts, conflict detection, escalation, and structured evidence. 7 proven team packs for common task families.",
|
|
5
5
|
"homepage": "https://mcp-tool-shop-org.github.io/role-os/",
|
|
6
6
|
"bugs": {
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CLI surface for artifact contracts — roleos artifacts
|
|
3
|
+
*
|
|
4
|
+
* Subcommands:
|
|
5
|
+
* - artifacts (bare) — list all roles with contracts
|
|
6
|
+
* - artifacts show <role> — display artifact contract
|
|
7
|
+
* - artifacts validate <role> <file> — validate file against contract
|
|
8
|
+
* - artifacts chain <pack> — show pack handoff flow
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { readFileSync, existsSync } from "node:fs";
|
|
12
|
+
import {
|
|
13
|
+
ROLE_ARTIFACT_CONTRACTS,
|
|
14
|
+
PACK_HANDOFF_CONTRACTS,
|
|
15
|
+
validateArtifact,
|
|
16
|
+
getArtifactContract,
|
|
17
|
+
getHandoffContract,
|
|
18
|
+
formatArtifactValidation,
|
|
19
|
+
formatPackChain,
|
|
20
|
+
validatePackChain,
|
|
21
|
+
} from "./artifacts.mjs";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Main command handler.
|
|
25
|
+
* @param {string[]} args
|
|
26
|
+
*/
|
|
27
|
+
export async function artifactsCommand(args) {
|
|
28
|
+
const sub = args[0];
|
|
29
|
+
|
|
30
|
+
if (!sub || sub === "list") {
|
|
31
|
+
runList();
|
|
32
|
+
} else if (sub === "show") {
|
|
33
|
+
runShow(args.slice(1));
|
|
34
|
+
} else if (sub === "validate") {
|
|
35
|
+
runValidate(args.slice(1));
|
|
36
|
+
} else if (sub === "chain") {
|
|
37
|
+
runChain(args.slice(1));
|
|
38
|
+
} else {
|
|
39
|
+
const err = new Error(`Unknown artifacts subcommand: "${sub}"`);
|
|
40
|
+
err.exitCode = 1;
|
|
41
|
+
err.hint = "Valid subcommands: list, show <role>, validate <role> <file>, chain <pack>";
|
|
42
|
+
throw err;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function runList() {
|
|
47
|
+
const roles = Object.entries(ROLE_ARTIFACT_CONTRACTS);
|
|
48
|
+
console.log(`\nRole artifact contracts (${roles.length} roles)\n`);
|
|
49
|
+
console.log(`${"Role".padEnd(25)} ${"Artifact Type".padEnd(22)} ${"Required".padEnd(8)} Consumed By`);
|
|
50
|
+
console.log(`${"─".repeat(25)} ${"─".repeat(22)} ${"─".repeat(8)} ${"─".repeat(30)}`);
|
|
51
|
+
|
|
52
|
+
for (const [role, contract] of roles) {
|
|
53
|
+
const consumers = contract.consumedBy.length > 0 ? contract.consumedBy.join(", ") : "terminal";
|
|
54
|
+
console.log(
|
|
55
|
+
`${role.padEnd(25)} ${contract.artifactType.padEnd(22)} ${String(contract.requiredSections.length).padEnd(8)} ${consumers}`
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function runShow(args) {
|
|
61
|
+
if (args.length === 0) {
|
|
62
|
+
const err = new Error("Missing role name.");
|
|
63
|
+
err.exitCode = 1;
|
|
64
|
+
err.hint = "Usage: roleos artifacts show <role-name>";
|
|
65
|
+
throw err;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const input = args.join(" ");
|
|
69
|
+
const roleName = resolveRoleName(input);
|
|
70
|
+
if (!roleName) {
|
|
71
|
+
const err = new Error(`No artifact contract for role "${input}".`);
|
|
72
|
+
err.exitCode = 1;
|
|
73
|
+
err.hint = `Available roles: ${Object.keys(ROLE_ARTIFACT_CONTRACTS).join(", ")}`;
|
|
74
|
+
throw err;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const contract = getArtifactContract(roleName);
|
|
78
|
+
|
|
79
|
+
console.log(`\nArtifact Contract — ${roleName}\n`);
|
|
80
|
+
console.log(`Type: ${contract.artifactType}`);
|
|
81
|
+
console.log(`Required sections: ${contract.requiredSections.join(", ")}`);
|
|
82
|
+
if (contract.optionalSections.length > 0) {
|
|
83
|
+
console.log(`Optional sections: ${contract.optionalSections.join(", ")}`);
|
|
84
|
+
}
|
|
85
|
+
if (contract.requiredEvidence.length > 0) {
|
|
86
|
+
console.log(`Required evidence: ${contract.requiredEvidence.join(", ")}`);
|
|
87
|
+
}
|
|
88
|
+
console.log(`Consumed by: ${contract.consumedBy.length > 0 ? contract.consumedBy.join(", ") : "terminal (no downstream consumer)"}`);
|
|
89
|
+
console.log(`Completion rule: ${contract.completionRule}`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function runValidate(args) {
|
|
93
|
+
if (args.length < 2) {
|
|
94
|
+
const err = new Error("Missing arguments.");
|
|
95
|
+
err.exitCode = 1;
|
|
96
|
+
err.hint = "Usage: roleos artifacts validate <role-name> <file-path>";
|
|
97
|
+
throw err;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Last arg is the file, everything before is the role name
|
|
101
|
+
const filePath = args[args.length - 1];
|
|
102
|
+
const roleArgs = args.slice(0, -1);
|
|
103
|
+
const roleName = resolveRoleName(roleArgs.join(" "));
|
|
104
|
+
|
|
105
|
+
if (!roleName) {
|
|
106
|
+
const err = new Error(`Unknown role: "${roleArgs.join(" ")}".`);
|
|
107
|
+
err.exitCode = 1;
|
|
108
|
+
err.hint = `Available roles: ${Object.keys(ROLE_ARTIFACT_CONTRACTS).join(", ")}`;
|
|
109
|
+
throw err;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (!existsSync(filePath)) {
|
|
113
|
+
const err = new Error(`File not found: "${filePath}".`);
|
|
114
|
+
err.exitCode = 1;
|
|
115
|
+
err.hint = "Provide a path to the artifact file to validate.";
|
|
116
|
+
throw err;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const content = readFileSync(filePath, "utf-8");
|
|
120
|
+
const validation = validateArtifact(roleName, content);
|
|
121
|
+
console.log(formatArtifactValidation(roleName, validation));
|
|
122
|
+
|
|
123
|
+
if (!validation.valid) {
|
|
124
|
+
process.exitCode = 1;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function runChain(args) {
|
|
129
|
+
const packName = args[0];
|
|
130
|
+
if (!packName) {
|
|
131
|
+
const err = new Error("Missing pack name.");
|
|
132
|
+
err.exitCode = 1;
|
|
133
|
+
err.hint = `Usage: roleos artifacts chain <pack-name>. Available: ${Object.keys(PACK_HANDOFF_CONTRACTS).join(", ")}`;
|
|
134
|
+
throw err;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
const contract = getHandoffContract(packName);
|
|
138
|
+
if (!contract) {
|
|
139
|
+
const err = new Error(`No handoff contract for pack "${packName}".`);
|
|
140
|
+
err.exitCode = 1;
|
|
141
|
+
err.hint = `Available packs: ${Object.keys(PACK_HANDOFF_CONTRACTS).join(", ")}`;
|
|
142
|
+
throw err;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
console.log(`\nHandoff Contract — ${packName} pack\n`);
|
|
146
|
+
console.log(`${"Step".padEnd(5)} ${"Role".padEnd(25)} ${"Produces".padEnd(22)} Consumed By`);
|
|
147
|
+
console.log(`${"─".repeat(5)} ${"─".repeat(25)} ${"─".repeat(22)} ${"─".repeat(25)}`);
|
|
148
|
+
|
|
149
|
+
contract.flow.forEach((step, i) => {
|
|
150
|
+
const consumer = step.consumedBy || "terminal";
|
|
151
|
+
console.log(
|
|
152
|
+
`${String(i + 1).padEnd(5)} ${step.role.padEnd(25)} ${step.produces.padEnd(22)} ${consumer}`
|
|
153
|
+
);
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Resolve a role name from user input.
|
|
159
|
+
* Handles both exact names ("Product Strategist") and slug forms ("product-strategist").
|
|
160
|
+
*/
|
|
161
|
+
function resolveRoleName(input) {
|
|
162
|
+
if (!input || !input.trim()) return null;
|
|
163
|
+
const trimmed = input.trim();
|
|
164
|
+
|
|
165
|
+
// Exact match
|
|
166
|
+
if (ROLE_ARTIFACT_CONTRACTS[trimmed]) return trimmed;
|
|
167
|
+
|
|
168
|
+
// Case-insensitive match
|
|
169
|
+
for (const role of Object.keys(ROLE_ARTIFACT_CONTRACTS)) {
|
|
170
|
+
if (role.toLowerCase() === trimmed.toLowerCase()) return role;
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
// Slug match (product-strategist → Product Strategist)
|
|
174
|
+
const slugToName = trimmed.replace(/-/g, " ");
|
|
175
|
+
for (const role of Object.keys(ROLE_ARTIFACT_CONTRACTS)) {
|
|
176
|
+
if (role.toLowerCase() === slugToName.toLowerCase()) return role;
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Artifact Spine — Phase Q (v1.6.0)
|
|
3
|
+
*
|
|
4
|
+
* Every important role produces a declared artifact with a known shape.
|
|
5
|
+
* Downstream roles never guess what they received.
|
|
6
|
+
*
|
|
7
|
+
* 1. Per-role artifact contracts (required sections, evidence, references)
|
|
8
|
+
* 2. Structural validation (completeness, not semantic perfection)
|
|
9
|
+
* 3. Pack-level handoff contracts (expected artifact flow between steps)
|
|
10
|
+
* 4. Composite artifact ledger integration
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
// ── Per-role artifact contracts ───────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Each contract defines what a role MUST produce for its handoff to be valid.
|
|
17
|
+
* Not every role needs a contract — only chain-critical roles that hand off
|
|
18
|
+
* to other roles.
|
|
19
|
+
*/
|
|
20
|
+
export const ROLE_ARTIFACT_CONTRACTS = {
|
|
21
|
+
"Product Strategist": {
|
|
22
|
+
artifactType: "strategy-brief",
|
|
23
|
+
requiredSections: ["problem-framing", "scope", "non-goals", "tradeoffs"],
|
|
24
|
+
optionalSections: ["user-value", "risks", "success-criteria"],
|
|
25
|
+
requiredEvidence: [],
|
|
26
|
+
consumedBy: ["Spec Writer", "Orchestrator"],
|
|
27
|
+
completionRule: "All 4 required sections must be present and non-empty.",
|
|
28
|
+
},
|
|
29
|
+
"Spec Writer": {
|
|
30
|
+
artifactType: "implementation-spec",
|
|
31
|
+
requiredSections: ["acceptance-criteria", "edge-cases", "interface-spec"],
|
|
32
|
+
optionalSections: ["data-schema", "nfrs", "open-questions"],
|
|
33
|
+
requiredEvidence: ["strategy-brief"],
|
|
34
|
+
consumedBy: ["Backend Engineer", "Frontend Developer"],
|
|
35
|
+
completionRule: "At least 3 acceptance criteria. Edge cases identified. Interface shape defined.",
|
|
36
|
+
},
|
|
37
|
+
"Backend Engineer": {
|
|
38
|
+
artifactType: "change-plan",
|
|
39
|
+
requiredSections: ["files-to-change", "implementation-approach", "risk-notes"],
|
|
40
|
+
optionalSections: ["test-strategy", "migration-notes"],
|
|
41
|
+
requiredEvidence: ["implementation-spec"],
|
|
42
|
+
consumedBy: ["Test Engineer", "Critic Reviewer"],
|
|
43
|
+
completionRule: "Files named. Approach described. Risks surfaced.",
|
|
44
|
+
},
|
|
45
|
+
"Frontend Developer": {
|
|
46
|
+
artifactType: "change-plan",
|
|
47
|
+
requiredSections: ["files-to-change", "implementation-approach", "risk-notes"],
|
|
48
|
+
optionalSections: ["component-structure", "accessibility-notes"],
|
|
49
|
+
requiredEvidence: ["implementation-spec"],
|
|
50
|
+
consumedBy: ["Test Engineer", "Critic Reviewer"],
|
|
51
|
+
completionRule: "Files named. Approach described. Risks surfaced.",
|
|
52
|
+
},
|
|
53
|
+
"Test Engineer": {
|
|
54
|
+
artifactType: "test-package",
|
|
55
|
+
requiredSections: ["test-plan", "test-cases", "false-confidence-assessment"],
|
|
56
|
+
optionalSections: ["edge-case-coverage", "regression-defense"],
|
|
57
|
+
requiredEvidence: ["change-plan"],
|
|
58
|
+
consumedBy: ["Critic Reviewer"],
|
|
59
|
+
completionRule: "Test plan present. At least 3 test cases. False confidence assessment honest.",
|
|
60
|
+
},
|
|
61
|
+
"Security Reviewer": {
|
|
62
|
+
artifactType: "security-findings",
|
|
63
|
+
requiredSections: ["findings", "severity-assessment", "recommendations"],
|
|
64
|
+
optionalSections: ["threat-model", "exploitation-scenarios"],
|
|
65
|
+
requiredEvidence: ["change-plan"],
|
|
66
|
+
consumedBy: ["Backend Engineer", "Critic Reviewer"],
|
|
67
|
+
completionRule: "Each finding has severity + recommendation. No finding without remediation path.",
|
|
68
|
+
},
|
|
69
|
+
"Coverage Auditor": {
|
|
70
|
+
artifactType: "coverage-report",
|
|
71
|
+
requiredSections: ["well-defended", "poorly-defended", "false-confidence", "priority-recommendations"],
|
|
72
|
+
optionalSections: ["missing-defenses", "regression-vectors"],
|
|
73
|
+
requiredEvidence: [],
|
|
74
|
+
consumedBy: ["Test Engineer", "Critic Reviewer"],
|
|
75
|
+
completionRule: "Coverage honestly assessed. False confidence identified. Priorities ranked.",
|
|
76
|
+
},
|
|
77
|
+
"Docs Architect": {
|
|
78
|
+
artifactType: "doc-map",
|
|
79
|
+
requiredSections: ["page-structure", "content-gaps", "navigation-design"],
|
|
80
|
+
optionalSections: ["getting-started-flow", "search-requirements"],
|
|
81
|
+
requiredEvidence: [],
|
|
82
|
+
consumedBy: ["Metadata Curator", "Release Engineer"],
|
|
83
|
+
completionRule: "Pages listed. Gaps identified. Navigation designed.",
|
|
84
|
+
},
|
|
85
|
+
"Launch Strategist": {
|
|
86
|
+
artifactType: "launch-brief",
|
|
87
|
+
requiredSections: ["launch-sequence", "proof-packaging", "channel-selection", "success-criteria"],
|
|
88
|
+
optionalSections: ["risk-assessment", "what-not-to-say"],
|
|
89
|
+
requiredEvidence: [],
|
|
90
|
+
consumedBy: ["Launch Copywriter"],
|
|
91
|
+
completionRule: "Sequence defined. Proof mapped. Channels selected. Success measurable.",
|
|
92
|
+
},
|
|
93
|
+
"Launch Copywriter": {
|
|
94
|
+
artifactType: "copy-package",
|
|
95
|
+
requiredSections: ["release-notes", "short-announcement"],
|
|
96
|
+
optionalSections: ["npm-description", "social-variants", "messaging-angle"],
|
|
97
|
+
requiredEvidence: ["launch-brief"],
|
|
98
|
+
consumedBy: ["Critic Reviewer"],
|
|
99
|
+
completionRule: "Release notes present. At least one announcement variant.",
|
|
100
|
+
},
|
|
101
|
+
"Repo Researcher": {
|
|
102
|
+
artifactType: "repo-map",
|
|
103
|
+
requiredSections: ["entrypoints", "module-map", "build-test-commands"],
|
|
104
|
+
optionalSections: ["seams", "dependencies"],
|
|
105
|
+
requiredEvidence: [],
|
|
106
|
+
consumedBy: ["Backend Engineer", "Coverage Auditor", "Security Reviewer"],
|
|
107
|
+
completionRule: "Entrypoints listed. Module responsibilities described. Commands documented.",
|
|
108
|
+
},
|
|
109
|
+
"Metadata Curator": {
|
|
110
|
+
artifactType: "metadata-audit",
|
|
111
|
+
requiredSections: ["manifest-audit", "registry-alignment"],
|
|
112
|
+
optionalSections: ["badge-verification", "discovery-surface", "recommendations"],
|
|
113
|
+
requiredEvidence: [],
|
|
114
|
+
consumedBy: ["Release Engineer"],
|
|
115
|
+
completionRule: "Package.json audited. Registry alignment checked.",
|
|
116
|
+
},
|
|
117
|
+
"Release Engineer": {
|
|
118
|
+
artifactType: "release-plan",
|
|
119
|
+
requiredSections: ["version-decision", "changelog-draft", "pre-publish-checklist"],
|
|
120
|
+
optionalSections: ["packaging-check", "release-steps"],
|
|
121
|
+
requiredEvidence: [],
|
|
122
|
+
consumedBy: ["Deployment Verifier", "Critic Reviewer"],
|
|
123
|
+
completionRule: "Version decided with rationale. Changelog written. Checklist present.",
|
|
124
|
+
},
|
|
125
|
+
"Deployment Verifier": {
|
|
126
|
+
artifactType: "deployment-report",
|
|
127
|
+
requiredSections: ["live-state-assessment"],
|
|
128
|
+
optionalSections: ["npm-verification", "github-verification", "badge-verification", "translation-check"],
|
|
129
|
+
requiredEvidence: [],
|
|
130
|
+
consumedBy: ["Critic Reviewer"],
|
|
131
|
+
completionRule: "All deployed surfaces checked. Stale/mismatched items named.",
|
|
132
|
+
},
|
|
133
|
+
"Critic Reviewer": {
|
|
134
|
+
artifactType: "verdict",
|
|
135
|
+
requiredSections: ["verdict", "evidence", "required-corrections"],
|
|
136
|
+
optionalSections: ["notes", "next-owner", "chain-assessment"],
|
|
137
|
+
requiredEvidence: [],
|
|
138
|
+
consumedBy: [],
|
|
139
|
+
completionRule: "Verdict stated. Evidence cited. Corrections listed if not accept.",
|
|
140
|
+
},
|
|
141
|
+
"UX Researcher": {
|
|
142
|
+
artifactType: "ux-evaluation",
|
|
143
|
+
requiredSections: ["friction-inventory", "severity-ranking", "recommendations"],
|
|
144
|
+
optionalSections: ["flow-analysis", "heuristic-evaluation"],
|
|
145
|
+
requiredEvidence: [],
|
|
146
|
+
consumedBy: ["Product Strategist", "UI Designer"],
|
|
147
|
+
completionRule: "Friction points identified. Severity ranked. Evidence-based recommendations.",
|
|
148
|
+
},
|
|
149
|
+
"Competitive Analyst": {
|
|
150
|
+
artifactType: "landscape-analysis",
|
|
151
|
+
requiredSections: ["competitor-inventory", "differentiation", "honest-disadvantages"],
|
|
152
|
+
optionalSections: ["positioning-gaps", "recommendations"],
|
|
153
|
+
requiredEvidence: [],
|
|
154
|
+
consumedBy: ["Product Strategist"],
|
|
155
|
+
completionRule: "Competitors listed. Differentiation clear. Disadvantages honest.",
|
|
156
|
+
},
|
|
157
|
+
"Feedback Synthesizer": {
|
|
158
|
+
artifactType: "signal-synthesis",
|
|
159
|
+
requiredSections: ["theme-extraction", "theme-ranking", "confidence-assessment"],
|
|
160
|
+
optionalSections: ["contradictions", "complaint-to-action"],
|
|
161
|
+
requiredEvidence: [],
|
|
162
|
+
consumedBy: ["Product Strategist"],
|
|
163
|
+
completionRule: "Themes extracted. Ranking justified. Confidence assessed.",
|
|
164
|
+
},
|
|
165
|
+
"Refactor Engineer": {
|
|
166
|
+
artifactType: "refactor-plan",
|
|
167
|
+
requiredSections: ["structural-assessment", "module-boundaries", "migration-path"],
|
|
168
|
+
optionalSections: ["duplication-inventory", "proposed-structure"],
|
|
169
|
+
requiredEvidence: [],
|
|
170
|
+
consumedBy: ["Test Engineer", "Critic Reviewer"],
|
|
171
|
+
completionRule: "Current structure assessed. Target boundaries defined. Migration preserves tests.",
|
|
172
|
+
},
|
|
173
|
+
"Support Triage Lead": {
|
|
174
|
+
artifactType: "triage-report",
|
|
175
|
+
requiredSections: ["classification", "priority-assignment", "routing"],
|
|
176
|
+
optionalSections: ["recurring-patterns", "systemic-recommendations"],
|
|
177
|
+
requiredEvidence: [],
|
|
178
|
+
consumedBy: ["Feedback Synthesizer", "Docs Architect"],
|
|
179
|
+
completionRule: "Each item classified. Priority assigned. Route to owner named.",
|
|
180
|
+
},
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// ── Artifact validation ───────────────────────────────────────────────────────
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Validate an artifact against its role contract.
|
|
187
|
+
* Checks structural completeness, not semantic quality.
|
|
188
|
+
*
|
|
189
|
+
* @param {string} roleName
|
|
190
|
+
* @param {string} artifactContent - The artifact text
|
|
191
|
+
* @returns {{ valid: boolean, missing: string[], warnings: string[], contract: object|null }}
|
|
192
|
+
*/
|
|
193
|
+
export function validateArtifact(roleName, artifactContent) {
|
|
194
|
+
const contract = ROLE_ARTIFACT_CONTRACTS[roleName];
|
|
195
|
+
if (!contract) {
|
|
196
|
+
return { valid: true, missing: [], warnings: ["No artifact contract defined for this role."], contract: null };
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const lower = artifactContent.toLowerCase();
|
|
200
|
+
const missing = [];
|
|
201
|
+
const warnings = [];
|
|
202
|
+
|
|
203
|
+
// Check required sections
|
|
204
|
+
for (const section of contract.requiredSections) {
|
|
205
|
+
// Look for the section as a heading or key phrase
|
|
206
|
+
const patterns = [
|
|
207
|
+
section.toLowerCase(),
|
|
208
|
+
section.replace(/-/g, " ").toLowerCase(),
|
|
209
|
+
`## ${section.replace(/-/g, " ")}`.toLowerCase(),
|
|
210
|
+
`### ${section.replace(/-/g, " ")}`.toLowerCase(),
|
|
211
|
+
];
|
|
212
|
+
const found = patterns.some(p => lower.includes(p));
|
|
213
|
+
if (!found) {
|
|
214
|
+
missing.push(section);
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// Check required evidence references
|
|
219
|
+
for (const evidence of contract.requiredEvidence) {
|
|
220
|
+
const patterns = [
|
|
221
|
+
evidence.toLowerCase(),
|
|
222
|
+
evidence.replace(/-/g, " ").toLowerCase(),
|
|
223
|
+
];
|
|
224
|
+
const found = patterns.some(p => lower.includes(p));
|
|
225
|
+
if (!found) {
|
|
226
|
+
warnings.push(`Expected reference to "${evidence}" from upstream role.`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Check minimum content length (not just headers)
|
|
231
|
+
const contentLines = artifactContent.split("\n").filter(l => l.trim() && !l.trim().startsWith("#"));
|
|
232
|
+
if (contentLines.length < 5) {
|
|
233
|
+
warnings.push("Artifact appears thin — fewer than 5 content lines.");
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
return {
|
|
237
|
+
valid: missing.length === 0,
|
|
238
|
+
missing,
|
|
239
|
+
warnings,
|
|
240
|
+
contract,
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// ── Pack-level handoff contracts ──────────────────────────────────────────────
|
|
245
|
+
|
|
246
|
+
/**
|
|
247
|
+
* Defines the expected artifact flow for each pack.
|
|
248
|
+
* Each step produces an artifact that the next step consumes.
|
|
249
|
+
*/
|
|
250
|
+
export const PACK_HANDOFF_CONTRACTS = {
|
|
251
|
+
feature: {
|
|
252
|
+
flow: [
|
|
253
|
+
{ role: "Product Strategist", produces: "strategy-brief", consumedBy: "Spec Writer" },
|
|
254
|
+
{ role: "Spec Writer", produces: "implementation-spec", consumedBy: "Backend Engineer" },
|
|
255
|
+
{ role: "Backend Engineer", produces: "change-plan", consumedBy: "Test Engineer" },
|
|
256
|
+
{ role: "Test Engineer", produces: "test-package", consumedBy: "Critic Reviewer" },
|
|
257
|
+
{ role: "Critic Reviewer", produces: "verdict", consumedBy: null },
|
|
258
|
+
],
|
|
259
|
+
},
|
|
260
|
+
bugfix: {
|
|
261
|
+
flow: [
|
|
262
|
+
{ role: "Repo Researcher", produces: "repo-map", consumedBy: "Backend Engineer" },
|
|
263
|
+
{ role: "Backend Engineer", produces: "change-plan", consumedBy: "Test Engineer" },
|
|
264
|
+
{ role: "Test Engineer", produces: "test-package", consumedBy: "Critic Reviewer" },
|
|
265
|
+
{ role: "Critic Reviewer", produces: "verdict", consumedBy: null },
|
|
266
|
+
],
|
|
267
|
+
},
|
|
268
|
+
security: {
|
|
269
|
+
flow: [
|
|
270
|
+
{ role: "Security Reviewer", produces: "security-findings", consumedBy: "Critic Reviewer" },
|
|
271
|
+
{ role: "Dependency Auditor", produces: "metadata-audit", consumedBy: "Critic Reviewer" },
|
|
272
|
+
{ role: "Critic Reviewer", produces: "verdict", consumedBy: null },
|
|
273
|
+
],
|
|
274
|
+
},
|
|
275
|
+
docs: {
|
|
276
|
+
flow: [
|
|
277
|
+
{ role: "Support Triage Lead", produces: "triage-report", consumedBy: "Feedback Synthesizer" },
|
|
278
|
+
{ role: "Feedback Synthesizer", produces: "signal-synthesis", consumedBy: "Docs Architect" },
|
|
279
|
+
{ role: "Docs Architect", produces: "doc-map", consumedBy: "Metadata Curator" },
|
|
280
|
+
{ role: "Metadata Curator", produces: "metadata-audit", consumedBy: "Critic Reviewer" },
|
|
281
|
+
{ role: "Critic Reviewer", produces: "verdict", consumedBy: null },
|
|
282
|
+
],
|
|
283
|
+
},
|
|
284
|
+
launch: {
|
|
285
|
+
flow: [
|
|
286
|
+
{ role: "Launch Strategist", produces: "launch-brief", consumedBy: "Launch Copywriter" },
|
|
287
|
+
{ role: "Launch Copywriter", produces: "copy-package", consumedBy: "Critic Reviewer" },
|
|
288
|
+
{ role: "Critic Reviewer", produces: "verdict", consumedBy: null },
|
|
289
|
+
],
|
|
290
|
+
},
|
|
291
|
+
research: {
|
|
292
|
+
flow: [
|
|
293
|
+
{ role: "Product Strategist", produces: "strategy-brief", consumedBy: "UX Researcher" },
|
|
294
|
+
{ role: "UX Researcher", produces: "ux-evaluation", consumedBy: "Competitive Analyst" },
|
|
295
|
+
{ role: "Competitive Analyst", produces: "landscape-analysis", consumedBy: "Feedback Synthesizer" },
|
|
296
|
+
{ role: "Feedback Synthesizer", produces: "signal-synthesis", consumedBy: "Critic Reviewer" },
|
|
297
|
+
{ role: "Critic Reviewer", produces: "verdict", consumedBy: null },
|
|
298
|
+
],
|
|
299
|
+
},
|
|
300
|
+
treatment: {
|
|
301
|
+
flow: [
|
|
302
|
+
{ role: "Repo Researcher", produces: "repo-map", consumedBy: "Security Reviewer" },
|
|
303
|
+
{ role: "Security Reviewer", produces: "security-findings", consumedBy: "Coverage Auditor" },
|
|
304
|
+
{ role: "Coverage Auditor", produces: "coverage-report", consumedBy: "Docs Architect" },
|
|
305
|
+
{ role: "Docs Architect", produces: "doc-map", consumedBy: "Metadata Curator" },
|
|
306
|
+
{ role: "Metadata Curator", produces: "metadata-audit", consumedBy: "Release Engineer" },
|
|
307
|
+
{ role: "Release Engineer", produces: "release-plan", consumedBy: "Deployment Verifier" },
|
|
308
|
+
{ role: "Deployment Verifier", produces: "deployment-report", consumedBy: "Critic Reviewer" },
|
|
309
|
+
{ role: "Critic Reviewer", produces: "verdict", consumedBy: null },
|
|
310
|
+
],
|
|
311
|
+
},
|
|
312
|
+
};
|
|
313
|
+
|
|
314
|
+
/**
|
|
315
|
+
* Validate a pack's artifact chain — check that each step's output
|
|
316
|
+
* matches what the next step expects.
|
|
317
|
+
*
|
|
318
|
+
* @param {string} packName
|
|
319
|
+
* @param {Record<string, string>} artifacts - Map of role name → artifact content
|
|
320
|
+
* @returns {{ valid: boolean, steps: object[] }}
|
|
321
|
+
*/
|
|
322
|
+
export function validatePackChain(packName, artifacts) {
|
|
323
|
+
const contract = PACK_HANDOFF_CONTRACTS[packName];
|
|
324
|
+
if (!contract) {
|
|
325
|
+
return { valid: false, steps: [{ role: "unknown", status: "error", detail: `No handoff contract for pack "${packName}"` }] };
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
const steps = [];
|
|
329
|
+
let chainValid = true;
|
|
330
|
+
|
|
331
|
+
for (const step of contract.flow) {
|
|
332
|
+
const content = artifacts[step.role];
|
|
333
|
+
if (!content) {
|
|
334
|
+
steps.push({
|
|
335
|
+
role: step.role,
|
|
336
|
+
produces: step.produces,
|
|
337
|
+
status: "missing",
|
|
338
|
+
detail: `No artifact from ${step.role}.`,
|
|
339
|
+
});
|
|
340
|
+
chainValid = false;
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
const validation = validateArtifact(step.role, content);
|
|
345
|
+
steps.push({
|
|
346
|
+
role: step.role,
|
|
347
|
+
produces: step.produces,
|
|
348
|
+
status: validation.valid ? "valid" : "incomplete",
|
|
349
|
+
missing: validation.missing,
|
|
350
|
+
warnings: validation.warnings,
|
|
351
|
+
detail: validation.valid
|
|
352
|
+
? `${step.produces} is structurally complete.`
|
|
353
|
+
: `Missing sections: ${validation.missing.join(", ")}`,
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
if (!validation.valid) chainValid = false;
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
return { valid: chainValid, steps };
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
/**
|
|
363
|
+
* Get the artifact contract for a specific role.
|
|
364
|
+
*
|
|
365
|
+
* @param {string} roleName
|
|
366
|
+
* @returns {object|null}
|
|
367
|
+
*/
|
|
368
|
+
export function getArtifactContract(roleName) {
|
|
369
|
+
return ROLE_ARTIFACT_CONTRACTS[roleName] || null;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Get the handoff contract for a specific pack.
|
|
374
|
+
*
|
|
375
|
+
* @param {string} packName
|
|
376
|
+
* @returns {object|null}
|
|
377
|
+
*/
|
|
378
|
+
export function getHandoffContract(packName) {
|
|
379
|
+
return PACK_HANDOFF_CONTRACTS[packName] || null;
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
/**
|
|
383
|
+
* Format artifact validation result for display.
|
|
384
|
+
*
|
|
385
|
+
* @param {string} roleName
|
|
386
|
+
* @param {object} validation
|
|
387
|
+
* @returns {string}
|
|
388
|
+
*/
|
|
389
|
+
export function formatArtifactValidation(roleName, validation) {
|
|
390
|
+
const lines = [`Artifact validation — ${roleName}`];
|
|
391
|
+
|
|
392
|
+
if (validation.valid) {
|
|
393
|
+
lines.push(` ✓ Structurally complete`);
|
|
394
|
+
} else {
|
|
395
|
+
lines.push(` ✗ Incomplete — missing: ${validation.missing.join(", ")}`);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
if (validation.warnings.length > 0) {
|
|
399
|
+
for (const w of validation.warnings) {
|
|
400
|
+
lines.push(` ! ${w}`);
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (validation.contract) {
|
|
405
|
+
lines.push(` Contract: ${validation.contract.artifactType}`);
|
|
406
|
+
lines.push(` Consumed by: ${validation.contract.consumedBy.join(", ") || "terminal"}`);
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return lines.join("\n");
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
/**
|
|
413
|
+
* Format pack chain validation for display.
|
|
414
|
+
*
|
|
415
|
+
* @param {string} packName
|
|
416
|
+
* @param {object} chainValidation
|
|
417
|
+
* @returns {string}
|
|
418
|
+
*/
|
|
419
|
+
export function formatPackChain(packName, chainValidation) {
|
|
420
|
+
const lines = [
|
|
421
|
+
`\nPack Chain Validation — ${packName}`,
|
|
422
|
+
`─────────────────────────────────`,
|
|
423
|
+
];
|
|
424
|
+
|
|
425
|
+
for (const step of chainValidation.steps) {
|
|
426
|
+
const icon = step.status === "valid" ? "✓" : step.status === "missing" ? "○" : "!";
|
|
427
|
+
lines.push(` ${icon} ${step.role} → ${step.produces}: ${step.detail}`);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
lines.push(``);
|
|
431
|
+
lines.push(chainValidation.valid
|
|
432
|
+
? `Chain valid — all artifacts structurally complete.`
|
|
433
|
+
: `Chain incomplete — see above for missing artifacts.`
|
|
434
|
+
);
|
|
435
|
+
|
|
436
|
+
return lines.join("\n");
|
|
437
|
+
}
|