Files
stack/packages/organization/tests/resolve-workflows.test.mjs
jason.woltje 4bd260457e feat(harness): workflow guidance in the prompt (starter packs SP7)
resolveInstance carries the organization's workflows, plus the given
project's, the project's winning by name; the key is added only when
there is one, so an organization without workflows digests as before.

The prompt renders them under one fixed heading, "## Organization
workflows", after the contract and persona and before "## This
session". The chief of staff and roles holding task.create or
task.assign see every step; a role a step names sees only its own
steps; any other role sees nothing. Each workflow is one JSON line
between <workflows-data> markers with <, >, &, line separators, C1
controls and bidi overrides escaped, so no title, description or step
role can start a heading or close the block. The section says
workflows grant nothing and the contract and rails win. The contract
text is unchanged.
2026-11-12 15:47:32 -06:00

78 lines
4.3 KiB
JavaScript

// Starter packs SP7 (#1873, design section 8): resolveInstance carries the organization's
// workflows, plus the given project's, the project's winning by name. An organization with no
// workflows resolves, and digests, exactly as before.
import { test } from "node:test";
import assert from "node:assert/strict";
import { join } from "node:path";
import { projectFilePath, resolveInstance, validateBusinessDocument, validateProjectDocument } from "../src/index.mjs";
import { businessDoc, REPO_ROLES, systemFor, tmp } from "./helpers.mjs";
const CHANGE = { title: "Change", description: "One change from task to merge.", steps: [
{ title: "Write the task", state: "todo", roles: ["pm"] },
{ title: "Implement", state: "in-progress", roles: ["coder"] },
{ title: "Approve the merge", state: "done", roles: ["human"] },
] };
const HOTFIX = { steps: [{ title: "Patch", state: "in-progress", roles: ["coder"] }] };
const PROJECT_CHANGE = { title: "Project change", steps: [{ title: "Ship it", state: "done", roles: ["reviewer"] }] };
function load(root, mutate) {
const doc = businessDoc(root);
mutate?.(doc);
return { doc, business: validateBusinessDocument(doc, join(root, "businesses", "acme.json"), { rolesDir: REPO_ROLES }) };
}
const projectOf = (doc) => validateProjectDocument({ projectVersion: 1, id: "stack" }, projectFilePath(doc.projects.stack.root));
test("SP7 resolveInstance puts the organization's workflows on the resolved record", () => {
const root = tmp();
const { business } = load(root, (doc) => { doc.workflows = { change: CHANGE, hotfix: HOTFIX }; });
for (const instance of ["chief", "pm", "coder", "cto"]) {
const r = resolveInstance({ system: systemFor(root), business, instance });
assert.deepEqual(r.workflows, { change: CHANGE, hotfix: HOTFIX }, instance);
assert.ok(Object.isFrozen(r.workflows.change.steps[0].roles), "the record is frozen");
}
// The record holds its own copy, not the loaded business's objects.
const r = resolveInstance({ system: systemFor(root), business, instance: "chief" });
assert.notEqual(r.workflows.change, business.workflows.change);
});
test("SP7 with a project, the project's workflows join the organization's and win by name", () => {
const root = tmp();
const { doc, business } = load(root, (d) => {
d.workflows = { change: CHANGE, hotfix: HOTFIX };
d.projects.stack.workflows = { change: PROJECT_CHANGE, review: HOTFIX };
});
const system = systemFor(root);
const withProject = resolveInstance({ system, business, project: projectOf(doc), instance: "coder" });
assert.equal(withProject.project, "stack");
assert.deepEqual(withProject.workflows, { change: PROJECT_CHANGE, hotfix: HOTFIX, review: HOTFIX });
// The launcher names no project today (cli/src/launcher.mjs), so a launch sees the org's only.
const noProject = resolveInstance({ system, business, instance: "coder" });
assert.deepEqual(noProject.workflows, { change: CHANGE, hotfix: HOTFIX });
});
test("SP7 an organization with no workflows resolves and digests as before", () => {
const root = tmp();
const system = systemFor(root);
const plain = load(root).business;
// Project workflows only: a launch with no project sees none, so the record is the plain one.
const projectOnly = load(root, (d) => { d.projects.stack.workflows = { review: HOTFIX }; });
for (const instance of ["chief", "pm", "coder"]) {
const a = resolveInstance({ system, business: plain, instance });
const b = resolveInstance({ system, business: projectOnly.business, instance });
assert.ok(!Object.hasOwn(a, "workflows"), instance);
assert.ok(!Object.hasOwn(b, "workflows"), instance);
assert.equal(b.digest, a.digest, instance);
}
// With the project named, the project's workflows reach the record and change the digest.
const named = resolveInstance({ system, business: projectOnly.business, project: projectOf(projectOnly.doc), instance: "coder" });
assert.deepEqual(named.workflows, { review: HOTFIX });
});
test("SP7 a step role naming no definition stays on the record as written", () => {
const root = tmp();
const ghost = { steps: [{ title: "Survey", state: "todo", roles: ["scout", "human"] }] };
const { business } = load(root, (d) => { d.workflows = { ghost }; });
const r = resolveInstance({ system: systemFor(root), business, instance: "chief" });
assert.deepEqual(r.workflows, { ghost });
});