feat(lanes): add ship-feature pipeline template (E1)
This commit is contained in:
@@ -334,7 +334,7 @@ test.describe("rule precision against real command shapes", () => {
|
||||
tool_name: "Bash",
|
||||
tool_input: {
|
||||
command:
|
||||
'GIT_TERMINAL_PROMPT=0 git -c credential.helper=x push --force-with-lease origin main',
|
||||
"GIT_TERMINAL_PROMPT=0 git -c credential.helper=x push --force-with-lease origin main",
|
||||
},
|
||||
});
|
||||
assert.equal(r.nodeId, "ship");
|
||||
@@ -354,8 +354,8 @@ test.describe("ship detection against this repo's actual push command", () => {
|
||||
// `|`-free shell between `git` and `push`, so a same-segment pattern like
|
||||
// `git[^;&|]*push` never matches it. Six real pushes produced no signal.
|
||||
const REAL_PUSH =
|
||||
'cd /repo && git add -A && git commit -q --amend --no-edit && ' +
|
||||
'GIT_TERMINAL_PROMPT=0 git -c credential.helper=\'!f() { echo username=token; echo "password=$TK"; }; f\' push --force-with-lease origin main';
|
||||
"cd /repo && git add -A && git commit -q --amend --no-edit && " +
|
||||
"GIT_TERMINAL_PROMPT=0 git -c credential.helper='!f() { echo username=token; echo \"password=$TK\"; }; f' push --force-with-lease origin main";
|
||||
|
||||
test.it("detects ship from the real push command", () => {
|
||||
const r = detect(pipeline, { tool_name: "Bash", tool_input: { command: REAL_PUSH } });
|
||||
@@ -404,7 +404,7 @@ test.describe("ship must match a git push INVOCATION, not the word push", () =>
|
||||
const cases = [
|
||||
"git push origin main",
|
||||
"git push --force-with-lease",
|
||||
'GIT_TERMINAL_PROMPT=0 git -c credential.helper=\'!f() { echo x; }; f\' push --force-with-lease origin main',
|
||||
"GIT_TERMINAL_PROMPT=0 git -c credential.helper='!f() { echo x; }; f' push --force-with-lease origin main",
|
||||
"cd /repo && git -c core.hooksPath=/dev/null push origin main",
|
||||
];
|
||||
for (const command of cases) {
|
||||
@@ -448,3 +448,38 @@ test.describe("tests rule covers the runners this repo actually uses", () => {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("ship-feature pipeline template", () => {
|
||||
test.it("loads without throwing and has one node per skill stage", () => {
|
||||
const { reload } = require("../lib/pipelines");
|
||||
reload();
|
||||
const pipeline = getPipeline("ship-feature");
|
||||
const ids = pipeline.nodes.map((n) => n.id);
|
||||
assert.deepEqual(ids, [
|
||||
"intake",
|
||||
"plan",
|
||||
"implementing",
|
||||
"gates",
|
||||
"e2e-feature",
|
||||
"e2e-feature-passed",
|
||||
"review",
|
||||
"qc-plan",
|
||||
"qc",
|
||||
"gate",
|
||||
"publishing",
|
||||
"pr-open",
|
||||
"reported",
|
||||
"watching-pr",
|
||||
"merged",
|
||||
"done",
|
||||
]);
|
||||
});
|
||||
|
||||
test.it("every node id is unique", () => {
|
||||
const { reload } = require("../lib/pipelines");
|
||||
reload();
|
||||
const pipeline = getPipeline("ship-feature");
|
||||
const ids = pipeline.nodes.map((n) => n.id);
|
||||
assert.deepEqual(ids, [...new Set(ids)]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
{
|
||||
"id": "ship-feature",
|
||||
"name": "Ship feature (lane pipeline)",
|
||||
"nodes": [
|
||||
{ "id": "intake", "label": "intake", "icon": "📝", "gate": false, "aliases": [] },
|
||||
{ "id": "plan", "label": "plan", "icon": "🧭", "gate": false, "aliases": [] },
|
||||
{ "id": "implementing", "label": "implement (TDD)", "icon": "🛠", "gate": false, "aliases": [] },
|
||||
{ "id": "gates", "label": "CI gates + preflight", "icon": "🧪", "gate": true, "aliases": [] },
|
||||
{ "id": "e2e-feature", "label": "e2e on feature branch", "icon": "🧪", "gate": false, "aliases": [] },
|
||||
{ "id": "e2e-feature-passed", "label": "e2e passed", "icon": "🧪", "gate": true, "aliases": [] },
|
||||
{ "id": "review", "label": "code review", "icon": "👀", "gate": true, "aliases": [] },
|
||||
{ "id": "qc-plan", "label": "QC plan", "icon": "📋", "gate": false, "aliases": [] },
|
||||
{ "id": "qc", "label": "browser QC", "icon": "🔍", "gate": true, "aliases": [] },
|
||||
{ "id": "gate", "label": "senior GO/NO-GO gate", "icon": "🚦", "gate": true, "aliases": [] },
|
||||
{ "id": "publishing", "label": "publish PR", "icon": "🔀", "gate": false, "aliases": [] },
|
||||
{ "id": "pr-open", "label": "PR open", "icon": "🔀", "gate": false, "aliases": ["ship"] },
|
||||
{ "id": "reported", "label": "reported", "icon": "📣", "gate": false, "aliases": [] },
|
||||
{ "id": "watching-pr", "label": "watching PR", "icon": "👁", "gate": false, "aliases": [] },
|
||||
{ "id": "merged", "label": "merged — post-verify", "icon": "🔗", "gate": false, "aliases": [] },
|
||||
{ "id": "done", "label": "done", "icon": "✅", "gate": false, "aliases": ["complete", "completed"] }
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user