@macedon-technologies/batman 1.17.3 → 1.18.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/.jsii +124 -17
- package/.swamp.yaml +6 -0
- package/API.md +107 -0
- package/CLAUDE.md +44 -0
- package/architecture-rules/README.md +42 -0
- package/architecture-rules/check.ts +162 -0
- package/architecture-rules/rules.json +3 -0
- package/architecture-rules/tsconfig.json +14 -0
- package/attestation/manifest.ts +40 -0
- package/attestation/tsconfig.json +13 -0
- package/attestation/verify.ts +142 -0
- package/lib/BatmanProject.js +8 -2
- package/lib/BootcampProject.js +8 -2
- package/lib/CodeOwnersFile.js +1 -1
- package/lib/ProductionDeployWorkflow.js +1 -1
- package/lib/SoftwareFactoryArchitectureRules.d.ts +10 -0
- package/lib/SoftwareFactoryArchitectureRules.js +245 -0
- package/lib/SoftwareFactoryAttestation.d.ts +18 -0
- package/lib/SoftwareFactoryAttestation.js +221 -0
- package/lib/SoftwareFactoryFiles.d.ts +45 -0
- package/lib/SoftwareFactoryFiles.js +102 -0
- package/lib/SoftwareFactoryModels.d.ts +25 -0
- package/lib/SoftwareFactoryModels.js +306 -0
- package/lib/SoftwareFactoryOpenSpecGates.d.ts +8 -0
- package/lib/SoftwareFactoryOpenSpecGates.js +75 -0
- package/lib/SoftwareFactoryWorkflows.d.ts +28 -0
- package/lib/SoftwareFactoryWorkflows.js +326 -0
- package/lib/StagingDeployWorkflow.js +1 -1
- package/lib/WorkflowAutoDiscover.js +1 -1
- package/lib/index.d.ts +1 -0
- package/lib/index.js +2 -1
- package/lib/utils.js +29 -1
- package/models/@goodcraft/github/platform-common-pr.yaml +10 -0
- package/models/@swamp/software-factory/platform-common-factory.yaml +240 -0
- package/models/@webframp/github/platform-common-issues.yaml +8 -0
- package/models/command/shell/platform-common-shell-architecture.yaml +8 -0
- package/models/command/shell/platform-common-shell-manifest.yaml +8 -0
- package/models/command/shell/platform-common-shell.yaml +8 -0
- package/openspec-gates/check-tasks-complete.ts +44 -0
- package/openspec-gates/tsconfig.json +13 -0
- package/package.json +6 -1
- package/workflows/workflow-openspec-plan-verify.yaml +51 -0
- package/workflows/workflow-openspec-tasks-verify.yaml +51 -0
- package/workflows/workflow-pre-pr-verify.yaml +118 -0
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SoftwareFactoryFiles = void 0;
|
|
4
|
+
const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
|
|
5
|
+
const SoftwareFactoryArchitectureRules_1 = require("./SoftwareFactoryArchitectureRules");
|
|
6
|
+
const SoftwareFactoryAttestation_1 = require("./SoftwareFactoryAttestation");
|
|
7
|
+
const SoftwareFactoryModels_1 = require("./SoftwareFactoryModels");
|
|
8
|
+
const SoftwareFactoryOpenSpecGates_1 = require("./SoftwareFactoryOpenSpecGates");
|
|
9
|
+
const SoftwareFactoryWorkflows_1 = require("./SoftwareFactoryWorkflows");
|
|
10
|
+
const GITHUB_OWNER = 'macedon-technologies';
|
|
11
|
+
/**
|
|
12
|
+
* Backports exman's swamp "software factory" pattern
|
|
13
|
+
* (macedon-technologies/exman#13) as generated scaffolding: a
|
|
14
|
+
* `@swamp/software-factory` state machine that drives a GitHub issue from
|
|
15
|
+
* intake through an opened PR, enforcing OpenSpec usage, an architecture
|
|
16
|
+
* rule registry, and a pre-PR verification gate whose attestation is
|
|
17
|
+
* re-checked at merge time.
|
|
18
|
+
*
|
|
19
|
+
* What swamp itself would generate (extension docs pulled via
|
|
20
|
+
* `swamp extension pull`, `.swamp.yaml`, the vault, and the "swamp managed
|
|
21
|
+
* section" in CLAUDE.md) is deliberately NOT hand-templated here — those are
|
|
22
|
+
* produced by the real `swamp` CLI. `swamp init` is idempotent (a no-op if
|
|
23
|
+
* the repo is already initialized), so it's wired into the project's
|
|
24
|
+
* `install`/`install:ci` tasks and runs automatically on every install,
|
|
25
|
+
* guarded so it's a silent no-op on a machine without `swamp` on PATH (e.g.
|
|
26
|
+
* CI). The extension pulls and vault creation are one-time, not idempotent
|
|
27
|
+
* in a way that's safe to repeat on every install, so they stay in the
|
|
28
|
+
* `swamp:bootstrap` task this construct adds. Only exman's own authored
|
|
29
|
+
* config (the factory's state machine, the architecture-rule engine, the
|
|
30
|
+
* attestation mechanism, the OpenSpec task-completion gate, and the 3 swamp
|
|
31
|
+
* workflows that wire them together) is templated.
|
|
32
|
+
*/
|
|
33
|
+
class SoftwareFactoryFiles {
|
|
34
|
+
static [JSII_RTTI_SYMBOL_1] = { fqn: "@macedon-technologies/batman.SoftwareFactoryFiles", version: "1.18.0" };
|
|
35
|
+
constructor(project, options) {
|
|
36
|
+
const { repoName, actionVersions } = options;
|
|
37
|
+
const baseBranch = options.baseBranch || 'main';
|
|
38
|
+
const secretsVault = `${repoName}-secrets`;
|
|
39
|
+
// A single redirect only: projen's built-in cross-platform task shell
|
|
40
|
+
// (dax) rejects a command with more than one redirect (e.g. combining
|
|
41
|
+
// `>/dev/null` with `2>&1`) with "Multiple redirects are currently not
|
|
42
|
+
// supported" — confirmed by dogfooding this construct on this very repo.
|
|
43
|
+
const swampInitStep = 'command -v swamp >/dev/null && swamp init --tool claude || true';
|
|
44
|
+
new SoftwareFactoryModels_1.SoftwareFactoryModels(project, {
|
|
45
|
+
repoName,
|
|
46
|
+
githubOwner: GITHUB_OWNER,
|
|
47
|
+
baseBranch,
|
|
48
|
+
});
|
|
49
|
+
new SoftwareFactoryArchitectureRules_1.SoftwareFactoryArchitectureRules(project);
|
|
50
|
+
new SoftwareFactoryAttestation_1.SoftwareFactoryAttestation(project, { repoName });
|
|
51
|
+
new SoftwareFactoryOpenSpecGates_1.SoftwareFactoryOpenSpecGates(project);
|
|
52
|
+
new SoftwareFactoryWorkflows_1.SoftwareFactoryWorkflows(project, { repoName, actionVersions });
|
|
53
|
+
project.addTask('validate:architecture', {
|
|
54
|
+
description: 'Run the architecture rule registry (architecture-rules/rules.json) against the repo',
|
|
55
|
+
exec: 'ts-node --project architecture-rules/tsconfig.json architecture-rules/check.ts',
|
|
56
|
+
});
|
|
57
|
+
project.addTask('openspec:check-tasks-complete', {
|
|
58
|
+
description: 'Fail unless every task in the named OpenSpec change is complete (usage: -- <change-slug>)',
|
|
59
|
+
exec: 'ts-node --project openspec-gates/tsconfig.json openspec-gates/check-tasks-complete.ts',
|
|
60
|
+
receiveArgs: true,
|
|
61
|
+
});
|
|
62
|
+
project.addTask('attestation:manifest', {
|
|
63
|
+
description: 'Print SHA256 checksums of the files a PR attestation is verified against (see attestation/manifest.ts)',
|
|
64
|
+
exec: 'ts-node --project attestation/tsconfig.json attestation/manifest.ts',
|
|
65
|
+
});
|
|
66
|
+
project.addTask('attestation:verify', {
|
|
67
|
+
description: 'Verify a PR\'s embedded attestation-manifest against the current checkout (usage: -- --body-file <path> --commit <sha>)',
|
|
68
|
+
exec: 'ts-node --project attestation/tsconfig.json attestation/verify.ts',
|
|
69
|
+
receiveArgs: true,
|
|
70
|
+
});
|
|
71
|
+
project.addTask('swamp:bootstrap', {
|
|
72
|
+
description: 'One-time setup: pull the software-factory/github extensions and create the secrets vault (swamp init itself already runs on every install)',
|
|
73
|
+
steps: [
|
|
74
|
+
{ exec: 'swamp extension pull @swamp/software-factory' },
|
|
75
|
+
{ exec: 'swamp extension pull @webframp/github' },
|
|
76
|
+
{ exec: 'swamp extension pull @goodcraft/github' },
|
|
77
|
+
{ exec: `swamp vault create local_encryption ${secretsVault}` },
|
|
78
|
+
{
|
|
79
|
+
exec: `echo "Next: swamp vault put ${secretsVault} GITHUB_TOKEN --refresh-from 'gh auth token'"`,
|
|
80
|
+
},
|
|
81
|
+
],
|
|
82
|
+
});
|
|
83
|
+
// `swamp init` is safe to re-run (idempotent when already initialized),
|
|
84
|
+
// so every install keeps the repo initialized without a separate manual
|
|
85
|
+
// step — including on a fresh clone that hasn't run swamp:bootstrap yet.
|
|
86
|
+
project.package.installTask.exec(swampInitStep);
|
|
87
|
+
project.package.installCiTask.exec(swampInitStep);
|
|
88
|
+
// Mirrors the patterns `swamp init` itself would append to .gitignore.
|
|
89
|
+
// projen's IgnoreFile fully regenerates .gitignore from its own tracked
|
|
90
|
+
// entries on every synth, so anything `swamp init` appends out-of-band
|
|
91
|
+
// gets silently wiped by the next `npx projen` run (caught by CI's
|
|
92
|
+
// self-mutation check) unless it's also added here.
|
|
93
|
+
project.addGitIgnore('.swamp/');
|
|
94
|
+
project.addGitIgnore('.swamp-sources.yaml');
|
|
95
|
+
project.addGitIgnore('.claude/worktrees/');
|
|
96
|
+
project.addGitIgnore('.claude/settings.local.json');
|
|
97
|
+
project.addGitIgnore('.claude/scheduled_tasks.lock');
|
|
98
|
+
project.addGitIgnore('.claude/scheduled_tasks.json');
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
exports.SoftwareFactoryFiles = SoftwareFactoryFiles;
|
|
102
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU29mdHdhcmVGYWN0b3J5RmlsZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvU29mdHdhcmVGYWN0b3J5RmlsZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6Ijs7OztBQUNBLHlGQUFzRjtBQUN0Riw2RUFBMEU7QUFDMUUsbUVBQWdFO0FBQ2hFLGlGQUE4RTtBQUM5RSx5RUFBc0U7QUFHdEUsTUFBTSxZQUFZLEdBQUcsc0JBQXNCLENBQUM7QUF1QjVDOzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7R0FxQkc7QUFDSCxNQUFhLG9CQUFvQjs7SUFDL0IsWUFBWSxPQUFvQixFQUFFLE9BQW9DO1FBQ3BFLE1BQU0sRUFBRSxRQUFRLEVBQUUsY0FBYyxFQUFFLEdBQUcsT0FBTyxDQUFDO1FBQzdDLE1BQU0sVUFBVSxHQUFHLE9BQU8sQ0FBQyxVQUFVLElBQUksTUFBTSxDQUFDO1FBQ2hELE1BQU0sWUFBWSxHQUFHLEdBQUcsUUFBUSxVQUFVLENBQUM7UUFDM0Msc0VBQXNFO1FBQ3RFLHNFQUFzRTtRQUN0RSx1RUFBdUU7UUFDdkUseUVBQXlFO1FBQ3pFLE1BQU0sYUFBYSxHQUFHLGlFQUFpRSxDQUFDO1FBRXhGLElBQUksNkNBQXFCLENBQUMsT0FBTyxFQUFFO1lBQ2pDLFFBQVE7WUFDUixXQUFXLEVBQUUsWUFBWTtZQUN6QixVQUFVO1NBQ1gsQ0FBQyxDQUFDO1FBRUgsSUFBSSxtRUFBZ0MsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUU5QyxJQUFJLHVEQUEwQixDQUFDLE9BQU8sRUFBRSxFQUFFLFFBQVEsRUFBRSxDQUFDLENBQUM7UUFFdEQsSUFBSSwyREFBNEIsQ0FBQyxPQUFPLENBQUMsQ0FBQztRQUUxQyxJQUFJLG1EQUF3QixDQUFDLE9BQU8sRUFBRSxFQUFFLFFBQVEsRUFBRSxjQUFjLEVBQUUsQ0FBQyxDQUFDO1FBRXBFLE9BQU8sQ0FBQyxPQUFPLENBQUMsdUJBQXVCLEVBQUU7WUFDdkMsV0FBVyxFQUFFLHFGQUFxRjtZQUNsRyxJQUFJLEVBQUUsZ0ZBQWdGO1NBQ3ZGLENBQUMsQ0FBQztRQUVILE9BQU8sQ0FBQyxPQUFPLENBQUMsK0JBQStCLEVBQUU7WUFDL0MsV0FBVyxFQUFFLDJGQUEyRjtZQUN4RyxJQUFJLEVBQUUsdUZBQXVGO1lBQzdGLFdBQVcsRUFBRSxJQUFJO1NBQ2xCLENBQUMsQ0FBQztRQUVILE9BQU8sQ0FBQyxPQUFPLENBQUMsc0JBQXNCLEVBQUU7WUFDdEMsV0FBVyxFQUFFLHdHQUF3RztZQUNySCxJQUFJLEVBQUUscUVBQXFFO1NBQzVFLENBQUMsQ0FBQztRQUVILE9BQU8sQ0FBQyxPQUFPLENBQUMsb0JBQW9CLEVBQUU7WUFDcEMsV0FBVyxFQUFFLHlIQUF5SDtZQUN0SSxJQUFJLEVBQUUsbUVBQW1FO1lBQ3pFLFdBQVcsRUFBRSxJQUFJO1NBQ2xCLENBQUMsQ0FBQztRQUVILE9BQU8sQ0FBQyxPQUFPLENBQUMsaUJBQWlCLEVBQUU7WUFDakMsV0FBVyxFQUFFLDRJQUE0STtZQUN6SixLQUFLLEVBQUU7Z0JBQ0wsRUFBRSxJQUFJLEVBQUUsOENBQThDLEVBQUU7Z0JBQ3hELEVBQUUsSUFBSSxFQUFFLHVDQUF1QyxFQUFFO2dCQUNqRCxFQUFFLElBQUksRUFBRSx3Q0FBd0MsRUFBRTtnQkFDbEQsRUFBRSxJQUFJLEVBQUUsdUNBQXVDLFlBQVksRUFBRSxFQUFFO2dCQUMvRDtvQkFDRSxJQUFJLEVBQUUsK0JBQStCLFlBQVksK0NBQStDO2lCQUNqRzthQUNGO1NBQ0YsQ0FBQyxDQUFDO1FBRUgsd0VBQXdFO1FBQ3hFLHdFQUF3RTtRQUN4RSx5RUFBeUU7UUFDekUsT0FBTyxDQUFDLE9BQU8sQ0FBQyxXQUFXLENBQUMsSUFBSSxDQUFDLGFBQWEsQ0FBQyxDQUFDO1FBQ2hELE9BQU8sQ0FBQyxPQUFPLENBQUMsYUFBYSxDQUFDLElBQUksQ0FBQyxhQUFhLENBQUMsQ0FBQztRQUVsRCx1RUFBdUU7UUFDdkUsd0VBQXdFO1FBQ3hFLHVFQUF1RTtRQUN2RSxtRUFBbUU7UUFDbkUsb0RBQW9EO1FBQ3BELE9BQU8sQ0FBQyxZQUFZLENBQUMsU0FBUyxDQUFDLENBQUM7UUFDaEMsT0FBTyxDQUFDLFlBQVksQ0FBQyxxQkFBcUIsQ0FBQyxDQUFDO1FBQzVDLE9BQU8sQ0FBQyxZQUFZLENBQUMsb0JBQW9CLENBQUMsQ0FBQztRQUMzQyxPQUFPLENBQUMsWUFBWSxDQUFDLDZCQUE2QixDQUFDLENBQUM7UUFDcEQsT0FBTyxDQUFDLFlBQVksQ0FBQyw4QkFBOEIsQ0FBQyxDQUFDO1FBQ3JELE9BQU8sQ0FBQyxZQUFZLENBQUMsOEJBQThCLENBQUMsQ0FBQztJQUN2RCxDQUFDOztBQTdFSCxvREE4RUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBOb2RlUHJvamVjdCB9IGZyb20gJ3Byb2plbi9saWIvamF2YXNjcmlwdCc7XG5pbXBvcnQgeyBTb2Z0d2FyZUZhY3RvcnlBcmNoaXRlY3R1cmVSdWxlcyB9IGZyb20gJy4vU29mdHdhcmVGYWN0b3J5QXJjaGl0ZWN0dXJlUnVsZXMnO1xuaW1wb3J0IHsgU29mdHdhcmVGYWN0b3J5QXR0ZXN0YXRpb24gfSBmcm9tICcuL1NvZnR3YXJlRmFjdG9yeUF0dGVzdGF0aW9uJztcbmltcG9ydCB7IFNvZnR3YXJlRmFjdG9yeU1vZGVscyB9IGZyb20gJy4vU29mdHdhcmVGYWN0b3J5TW9kZWxzJztcbmltcG9ydCB7IFNvZnR3YXJlRmFjdG9yeU9wZW5TcGVjR2F0ZXMgfSBmcm9tICcuL1NvZnR3YXJlRmFjdG9yeU9wZW5TcGVjR2F0ZXMnO1xuaW1wb3J0IHsgU29mdHdhcmVGYWN0b3J5V29ya2Zsb3dzIH0gZnJvbSAnLi9Tb2Z0d2FyZUZhY3RvcnlXb3JrZmxvd3MnO1xuaW1wb3J0IHsgV29ya2Zsb3dBY3Rpb25WZXJzaW9ucyB9IGZyb20gJy4vV29ya2Zsb3dBY3Rpb25WZXJzaW9ucyc7XG5cbmNvbnN0IEdJVEhVQl9PV05FUiA9ICdtYWNlZG9uLXRlY2hub2xvZ2llcyc7XG5cbmV4cG9ydCBpbnRlcmZhY2UgU29mdHdhcmVGYWN0b3J5RmlsZXNPcHRpb25zIHtcbiAgLyoqXG4gICAqIFRoZSBiYXJlIHJlcG8gbmFtZSAoZS5nLiBgZXhwZW5zZS10cmFja2VyYCksIHVzZWQgdG8gZGVyaXZlIHRoZSBmYWN0b3J5J3NcbiAgICogbW9kZWwgbmFtZXMgKGA8cmVwb05hbWU+LWZhY3RvcnlgLCBgPHJlcG9OYW1lPi1pc3N1ZXNgLCBgPHJlcG9OYW1lPi1wcmAsXG4gICAqIGA8cmVwb05hbWU+LXNoZWxsKmApIGFuZCB2YXVsdCBuYW1lIChgPHJlcG9OYW1lPi1zZWNyZXRzYCkuXG4gICAqL1xuICByZWFkb25seSByZXBvTmFtZTogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBUaGUgYnJhbmNoIHRoZSBmYWN0b3J5IG9wZW5zIHB1bGwgcmVxdWVzdHMgYWdhaW5zdC5cbiAgICpcbiAgICogQGRlZmF1bHQgJ21haW4nXG4gICAqL1xuICByZWFkb25seSBiYXNlQnJhbmNoPzogc3RyaW5nO1xuXG4gIC8qKlxuICAgKiBPdmVycmlkZXMgZm9yIHRoZSBwaW5uZWQgYWN0aW9uIHZlcnNpb25zIHVzZWQgYnkgYHZlcmlmeS1hdHRlc3RhdGlvbi55bWxgLlxuICAgKi9cbiAgcmVhZG9ubHkgYWN0aW9uVmVyc2lvbnM/OiBXb3JrZmxvd0FjdGlvblZlcnNpb25zO1xufVxuXG4vKipcbiAqIEJhY2twb3J0cyBleG1hbidzIHN3YW1wIFwic29mdHdhcmUgZmFjdG9yeVwiIHBhdHRlcm5cbiAqIChtYWNlZG9uLXRlY2hub2xvZ2llcy9leG1hbiMxMykgYXMgZ2VuZXJhdGVkIHNjYWZmb2xkaW5nOiBhXG4gKiBgQHN3YW1wL3NvZnR3YXJlLWZhY3RvcnlgIHN0YXRlIG1hY2hpbmUgdGhhdCBkcml2ZXMgYSBHaXRIdWIgaXNzdWUgZnJvbVxuICogaW50YWtlIHRocm91Z2ggYW4gb3BlbmVkIFBSLCBlbmZvcmNpbmcgT3BlblNwZWMgdXNhZ2UsIGFuIGFyY2hpdGVjdHVyZVxuICogcnVsZSByZWdpc3RyeSwgYW5kIGEgcHJlLVBSIHZlcmlmaWNhdGlvbiBnYXRlIHdob3NlIGF0dGVzdGF0aW9uIGlzXG4gKiByZS1jaGVja2VkIGF0IG1lcmdlIHRpbWUuXG4gKlxuICogV2hhdCBzd2FtcCBpdHNlbGYgd291bGQgZ2VuZXJhdGUgKGV4dGVuc2lvbiBkb2NzIHB1bGxlZCB2aWFcbiAqIGBzd2FtcCBleHRlbnNpb24gcHVsbGAsIGAuc3dhbXAueWFtbGAsIHRoZSB2YXVsdCwgYW5kIHRoZSBcInN3YW1wIG1hbmFnZWRcbiAqIHNlY3Rpb25cIiBpbiBDTEFVREUubWQpIGlzIGRlbGliZXJhdGVseSBOT1QgaGFuZC10ZW1wbGF0ZWQgaGVyZSDigJQgdGhvc2UgYXJlXG4gKiBwcm9kdWNlZCBieSB0aGUgcmVhbCBgc3dhbXBgIENMSS4gYHN3YW1wIGluaXRgIGlzIGlkZW1wb3RlbnQgKGEgbm8tb3AgaWZcbiAqIHRoZSByZXBvIGlzIGFscmVhZHkgaW5pdGlhbGl6ZWQpLCBzbyBpdCdzIHdpcmVkIGludG8gdGhlIHByb2plY3Qnc1xuICogYGluc3RhbGxgL2BpbnN0YWxsOmNpYCB0YXNrcyBhbmQgcnVucyBhdXRvbWF0aWNhbGx5IG9uIGV2ZXJ5IGluc3RhbGwsXG4gKiBndWFyZGVkIHNvIGl0J3MgYSBzaWxlbnQgbm8tb3Agb24gYSBtYWNoaW5lIHdpdGhvdXQgYHN3YW1wYCBvbiBQQVRIIChlLmcuXG4gKiBDSSkuIFRoZSBleHRlbnNpb24gcHVsbHMgYW5kIHZhdWx0IGNyZWF0aW9uIGFyZSBvbmUtdGltZSwgbm90IGlkZW1wb3RlbnRcbiAqIGluIGEgd2F5IHRoYXQncyBzYWZlIHRvIHJlcGVhdCBvbiBldmVyeSBpbnN0YWxsLCBzbyB0aGV5IHN0YXkgaW4gdGhlXG4gKiBgc3dhbXA6Ym9vdHN0cmFwYCB0YXNrIHRoaXMgY29uc3RydWN0IGFkZHMuIE9ubHkgZXhtYW4ncyBvd24gYXV0aG9yZWRcbiAqIGNvbmZpZyAodGhlIGZhY3RvcnkncyBzdGF0ZSBtYWNoaW5lLCB0aGUgYXJjaGl0ZWN0dXJlLXJ1bGUgZW5naW5lLCB0aGVcbiAqIGF0dGVzdGF0aW9uIG1lY2hhbmlzbSwgdGhlIE9wZW5TcGVjIHRhc2stY29tcGxldGlvbiBnYXRlLCBhbmQgdGhlIDMgc3dhbXBcbiAqIHdvcmtmbG93cyB0aGF0IHdpcmUgdGhlbSB0b2dldGhlcikgaXMgdGVtcGxhdGVkLlxuICovXG5leHBvcnQgY2xhc3MgU29mdHdhcmVGYWN0b3J5RmlsZXMge1xuICBjb25zdHJ1Y3Rvcihwcm9qZWN0OiBOb2RlUHJvamVjdCwgb3B0aW9uczogU29mdHdhcmVGYWN0b3J5RmlsZXNPcHRpb25zKSB7XG4gICAgY29uc3QgeyByZXBvTmFtZSwgYWN0aW9uVmVyc2lvbnMgfSA9IG9wdGlvbnM7XG4gICAgY29uc3QgYmFzZUJyYW5jaCA9IG9wdGlvbnMuYmFzZUJyYW5jaCB8fCAnbWFpbic7XG4gICAgY29uc3Qgc2VjcmV0c1ZhdWx0ID0gYCR7cmVwb05hbWV9LXNlY3JldHNgO1xuICAgIC8vIEEgc2luZ2xlIHJlZGlyZWN0IG9ubHk6IHByb2plbidzIGJ1aWx0LWluIGNyb3NzLXBsYXRmb3JtIHRhc2sgc2hlbGxcbiAgICAvLyAoZGF4KSByZWplY3RzIGEgY29tbWFuZCB3aXRoIG1vcmUgdGhhbiBvbmUgcmVkaXJlY3QgKGUuZy4gY29tYmluaW5nXG4gICAgLy8gYD4vZGV2L251bGxgIHdpdGggYDI+JjFgKSB3aXRoIFwiTXVsdGlwbGUgcmVkaXJlY3RzIGFyZSBjdXJyZW50bHkgbm90XG4gICAgLy8gc3VwcG9ydGVkXCIg4oCUIGNvbmZpcm1lZCBieSBkb2dmb29kaW5nIHRoaXMgY29uc3RydWN0IG9uIHRoaXMgdmVyeSByZXBvLlxuICAgIGNvbnN0IHN3YW1wSW5pdFN0ZXAgPSAnY29tbWFuZCAtdiBzd2FtcCA+L2Rldi9udWxsICYmIHN3YW1wIGluaXQgLS10b29sIGNsYXVkZSB8fCB0cnVlJztcblxuICAgIG5ldyBTb2Z0d2FyZUZhY3RvcnlNb2RlbHMocHJvamVjdCwge1xuICAgICAgcmVwb05hbWUsXG4gICAgICBnaXRodWJPd25lcjogR0lUSFVCX09XTkVSLFxuICAgICAgYmFzZUJyYW5jaCxcbiAgICB9KTtcblxuICAgIG5ldyBTb2Z0d2FyZUZhY3RvcnlBcmNoaXRlY3R1cmVSdWxlcyhwcm9qZWN0KTtcblxuICAgIG5ldyBTb2Z0d2FyZUZhY3RvcnlBdHRlc3RhdGlvbihwcm9qZWN0LCB7IHJlcG9OYW1lIH0pO1xuXG4gICAgbmV3IFNvZnR3YXJlRmFjdG9yeU9wZW5TcGVjR2F0ZXMocHJvamVjdCk7XG5cbiAgICBuZXcgU29mdHdhcmVGYWN0b3J5V29ya2Zsb3dzKHByb2plY3QsIHsgcmVwb05hbWUsIGFjdGlvblZlcnNpb25zIH0pO1xuXG4gICAgcHJvamVjdC5hZGRUYXNrKCd2YWxpZGF0ZTphcmNoaXRlY3R1cmUnLCB7XG4gICAgICBkZXNjcmlwdGlvbjogJ1J1biB0aGUgYXJjaGl0ZWN0dXJlIHJ1bGUgcmVnaXN0cnkgKGFyY2hpdGVjdHVyZS1ydWxlcy9ydWxlcy5qc29uKSBhZ2FpbnN0IHRoZSByZXBvJyxcbiAgICAgIGV4ZWM6ICd0cy1ub2RlIC0tcHJvamVjdCBhcmNoaXRlY3R1cmUtcnVsZXMvdHNjb25maWcuanNvbiBhcmNoaXRlY3R1cmUtcnVsZXMvY2hlY2sudHMnLFxuICAgIH0pO1xuXG4gICAgcHJvamVjdC5hZGRUYXNrKCdvcGVuc3BlYzpjaGVjay10YXNrcy1jb21wbGV0ZScsIHtcbiAgICAgIGRlc2NyaXB0aW9uOiAnRmFpbCB1bmxlc3MgZXZlcnkgdGFzayBpbiB0aGUgbmFtZWQgT3BlblNwZWMgY2hhbmdlIGlzIGNvbXBsZXRlICh1c2FnZTogLS0gPGNoYW5nZS1zbHVnPiknLFxuICAgICAgZXhlYzogJ3RzLW5vZGUgLS1wcm9qZWN0IG9wZW5zcGVjLWdhdGVzL3RzY29uZmlnLmpzb24gb3BlbnNwZWMtZ2F0ZXMvY2hlY2stdGFza3MtY29tcGxldGUudHMnLFxuICAgICAgcmVjZWl2ZUFyZ3M6IHRydWUsXG4gICAgfSk7XG5cbiAgICBwcm9qZWN0LmFkZFRhc2soJ2F0dGVzdGF0aW9uOm1hbmlmZXN0Jywge1xuICAgICAgZGVzY3JpcHRpb246ICdQcmludCBTSEEyNTYgY2hlY2tzdW1zIG9mIHRoZSBmaWxlcyBhIFBSIGF0dGVzdGF0aW9uIGlzIHZlcmlmaWVkIGFnYWluc3QgKHNlZSBhdHRlc3RhdGlvbi9tYW5pZmVzdC50cyknLFxuICAgICAgZXhlYzogJ3RzLW5vZGUgLS1wcm9qZWN0IGF0dGVzdGF0aW9uL3RzY29uZmlnLmpzb24gYXR0ZXN0YXRpb24vbWFuaWZlc3QudHMnLFxuICAgIH0pO1xuXG4gICAgcHJvamVjdC5hZGRUYXNrKCdhdHRlc3RhdGlvbjp2ZXJpZnknLCB7XG4gICAgICBkZXNjcmlwdGlvbjogJ1ZlcmlmeSBhIFBSXFwncyBlbWJlZGRlZCBhdHRlc3RhdGlvbi1tYW5pZmVzdCBhZ2FpbnN0IHRoZSBjdXJyZW50IGNoZWNrb3V0ICh1c2FnZTogLS0gLS1ib2R5LWZpbGUgPHBhdGg+IC0tY29tbWl0IDxzaGE+KScsXG4gICAgICBleGVjOiAndHMtbm9kZSAtLXByb2plY3QgYXR0ZXN0YXRpb24vdHNjb25maWcuanNvbiBhdHRlc3RhdGlvbi92ZXJpZnkudHMnLFxuICAgICAgcmVjZWl2ZUFyZ3M6IHRydWUsXG4gICAgfSk7XG5cbiAgICBwcm9qZWN0LmFkZFRhc2soJ3N3YW1wOmJvb3RzdHJhcCcsIHtcbiAgICAgIGRlc2NyaXB0aW9uOiAnT25lLXRpbWUgc2V0dXA6IHB1bGwgdGhlIHNvZnR3YXJlLWZhY3RvcnkvZ2l0aHViIGV4dGVuc2lvbnMgYW5kIGNyZWF0ZSB0aGUgc2VjcmV0cyB2YXVsdCAoc3dhbXAgaW5pdCBpdHNlbGYgYWxyZWFkeSBydW5zIG9uIGV2ZXJ5IGluc3RhbGwpJyxcbiAgICAgIHN0ZXBzOiBbXG4gICAgICAgIHsgZXhlYzogJ3N3YW1wIGV4dGVuc2lvbiBwdWxsIEBzd2FtcC9zb2Z0d2FyZS1mYWN0b3J5JyB9LFxuICAgICAgICB7IGV4ZWM6ICdzd2FtcCBleHRlbnNpb24gcHVsbCBAd2ViZnJhbXAvZ2l0aHViJyB9LFxuICAgICAgICB7IGV4ZWM6ICdzd2FtcCBleHRlbnNpb24gcHVsbCBAZ29vZGNyYWZ0L2dpdGh1YicgfSxcbiAgICAgICAgeyBleGVjOiBgc3dhbXAgdmF1bHQgY3JlYXRlIGxvY2FsX2VuY3J5cHRpb24gJHtzZWNyZXRzVmF1bHR9YCB9LFxuICAgICAgICB7XG4gICAgICAgICAgZXhlYzogYGVjaG8gXCJOZXh0OiBzd2FtcCB2YXVsdCBwdXQgJHtzZWNyZXRzVmF1bHR9IEdJVEhVQl9UT0tFTiAtLXJlZnJlc2gtZnJvbSAnZ2ggYXV0aCB0b2tlbidcImAsXG4gICAgICAgIH0sXG4gICAgICBdLFxuICAgIH0pO1xuXG4gICAgLy8gYHN3YW1wIGluaXRgIGlzIHNhZmUgdG8gcmUtcnVuIChpZGVtcG90ZW50IHdoZW4gYWxyZWFkeSBpbml0aWFsaXplZCksXG4gICAgLy8gc28gZXZlcnkgaW5zdGFsbCBrZWVwcyB0aGUgcmVwbyBpbml0aWFsaXplZCB3aXRob3V0IGEgc2VwYXJhdGUgbWFudWFsXG4gICAgLy8gc3RlcCDigJQgaW5jbHVkaW5nIG9uIGEgZnJlc2ggY2xvbmUgdGhhdCBoYXNuJ3QgcnVuIHN3YW1wOmJvb3RzdHJhcCB5ZXQuXG4gICAgcHJvamVjdC5wYWNrYWdlLmluc3RhbGxUYXNrLmV4ZWMoc3dhbXBJbml0U3RlcCk7XG4gICAgcHJvamVjdC5wYWNrYWdlLmluc3RhbGxDaVRhc2suZXhlYyhzd2FtcEluaXRTdGVwKTtcblxuICAgIC8vIE1pcnJvcnMgdGhlIHBhdHRlcm5zIGBzd2FtcCBpbml0YCBpdHNlbGYgd291bGQgYXBwZW5kIHRvIC5naXRpZ25vcmUuXG4gICAgLy8gcHJvamVuJ3MgSWdub3JlRmlsZSBmdWxseSByZWdlbmVyYXRlcyAuZ2l0aWdub3JlIGZyb20gaXRzIG93biB0cmFja2VkXG4gICAgLy8gZW50cmllcyBvbiBldmVyeSBzeW50aCwgc28gYW55dGhpbmcgYHN3YW1wIGluaXRgIGFwcGVuZHMgb3V0LW9mLWJhbmRcbiAgICAvLyBnZXRzIHNpbGVudGx5IHdpcGVkIGJ5IHRoZSBuZXh0IGBucHggcHJvamVuYCBydW4gKGNhdWdodCBieSBDSSdzXG4gICAgLy8gc2VsZi1tdXRhdGlvbiBjaGVjaykgdW5sZXNzIGl0J3MgYWxzbyBhZGRlZCBoZXJlLlxuICAgIHByb2plY3QuYWRkR2l0SWdub3JlKCcuc3dhbXAvJyk7XG4gICAgcHJvamVjdC5hZGRHaXRJZ25vcmUoJy5zd2FtcC1zb3VyY2VzLnlhbWwnKTtcbiAgICBwcm9qZWN0LmFkZEdpdElnbm9yZSgnLmNsYXVkZS93b3JrdHJlZXMvJyk7XG4gICAgcHJvamVjdC5hZGRHaXRJZ25vcmUoJy5jbGF1ZGUvc2V0dGluZ3MubG9jYWwuanNvbicpO1xuICAgIHByb2plY3QuYWRkR2l0SWdub3JlKCcuY2xhdWRlL3NjaGVkdWxlZF90YXNrcy5sb2NrJyk7XG4gICAgcHJvamVjdC5hZGRHaXRJZ25vcmUoJy5jbGF1ZGUvc2NoZWR1bGVkX3Rhc2tzLmpzb24nKTtcbiAgfVxufVxuIl19
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { NodeProject } from 'projen/lib/javascript';
|
|
2
|
+
export interface SoftwareFactoryModelsOptions {
|
|
3
|
+
/**
|
|
4
|
+
* The bare repo name (e.g. `expense-tracker`), used to derive model names
|
|
5
|
+
* like `<repoName>-factory`, `<repoName>-shell`, `<repoName>-secrets`.
|
|
6
|
+
*/
|
|
7
|
+
readonly repoName: string;
|
|
8
|
+
/**
|
|
9
|
+
* The GitHub org the repo lives in.
|
|
10
|
+
*/
|
|
11
|
+
readonly githubOwner: string;
|
|
12
|
+
/**
|
|
13
|
+
* The branch the factory opens pull requests against.
|
|
14
|
+
*/
|
|
15
|
+
readonly baseBranch: string;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Seed swamp model instances wiring the `@swamp/software-factory` state
|
|
19
|
+
* machine to this repo's GitHub issues/PRs and shell steps. These are
|
|
20
|
+
* `SampleFile`s (written once, then owned by the team) since the factory's
|
|
21
|
+
* stages/gates are the kind of thing a team tunes for its own workflow.
|
|
22
|
+
*/
|
|
23
|
+
export declare class SoftwareFactoryModels {
|
|
24
|
+
constructor(project: NodeProject, options: SoftwareFactoryModelsOptions);
|
|
25
|
+
}
|
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SoftwareFactoryModels = void 0;
|
|
4
|
+
const crypto_1 = require("crypto");
|
|
5
|
+
const projen_1 = require("projen");
|
|
6
|
+
/**
|
|
7
|
+
* Seed swamp model instances wiring the `@swamp/software-factory` state
|
|
8
|
+
* machine to this repo's GitHub issues/PRs and shell steps. These are
|
|
9
|
+
* `SampleFile`s (written once, then owned by the team) since the factory's
|
|
10
|
+
* stages/gates are the kind of thing a team tunes for its own workflow.
|
|
11
|
+
*/
|
|
12
|
+
class SoftwareFactoryModels {
|
|
13
|
+
constructor(project, options) {
|
|
14
|
+
const { repoName, githubOwner, baseBranch } = options;
|
|
15
|
+
const repo = `${githubOwner}/${repoName}`;
|
|
16
|
+
const issuesModel = `${repoName}-issues`;
|
|
17
|
+
const prModel = `${repoName}-pr`;
|
|
18
|
+
const shellModel = `${repoName}-shell`;
|
|
19
|
+
const shellArchitectureModel = `${repoName}-shell-architecture`;
|
|
20
|
+
const shellManifestModel = `${repoName}-shell-manifest`;
|
|
21
|
+
const secretsVault = `${repoName}-secrets`;
|
|
22
|
+
const factoryModel = `${repoName}-factory`;
|
|
23
|
+
new projen_1.SampleFile(project, `models/@goodcraft/github/${prModel}.yaml`, {
|
|
24
|
+
contents: `type: '@goodcraft/github'
|
|
25
|
+
typeVersion: 2026.06.14.1
|
|
26
|
+
id: ${(0, crypto_1.randomUUID)()}
|
|
27
|
+
name: ${prModel}
|
|
28
|
+
version: 1
|
|
29
|
+
tags: {}
|
|
30
|
+
globalArguments:
|
|
31
|
+
token: '\${{ vault.get(${secretsVault}, GITHUB_TOKEN) }}'
|
|
32
|
+
owner: ${githubOwner}
|
|
33
|
+
methods: {}
|
|
34
|
+
`,
|
|
35
|
+
});
|
|
36
|
+
new projen_1.SampleFile(project, `models/@webframp/github/${issuesModel}.yaml`, {
|
|
37
|
+
contents: `type: '@webframp/github'
|
|
38
|
+
typeVersion: 2026.08.28.1
|
|
39
|
+
id: ${(0, crypto_1.randomUUID)()}
|
|
40
|
+
name: ${issuesModel}
|
|
41
|
+
version: 1
|
|
42
|
+
tags: {}
|
|
43
|
+
globalArguments: {}
|
|
44
|
+
methods: {}
|
|
45
|
+
`,
|
|
46
|
+
});
|
|
47
|
+
for (const name of [shellModel, shellArchitectureModel, shellManifestModel]) {
|
|
48
|
+
new projen_1.SampleFile(project, `models/command/shell/${name}.yaml`, {
|
|
49
|
+
contents: `type: command/shell
|
|
50
|
+
typeVersion: 2026.02.09.1
|
|
51
|
+
id: ${(0, crypto_1.randomUUID)()}
|
|
52
|
+
name: ${name}
|
|
53
|
+
version: 1
|
|
54
|
+
tags: {}
|
|
55
|
+
globalArguments: {}
|
|
56
|
+
methods: {}
|
|
57
|
+
`,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
new projen_1.SampleFile(project, `models/@swamp/software-factory/${factoryModel}.yaml`, {
|
|
61
|
+
contents: `type: '@swamp/software-factory'
|
|
62
|
+
typeVersion: 2026.06.24.1
|
|
63
|
+
id: ${(0, crypto_1.randomUUID)()}
|
|
64
|
+
name: ${factoryModel}
|
|
65
|
+
version: 1
|
|
66
|
+
tags: {}
|
|
67
|
+
reports:
|
|
68
|
+
require:
|
|
69
|
+
- name: "@swamp/software-factory/work-item-summary"
|
|
70
|
+
methods: [summary]
|
|
71
|
+
globalArguments:
|
|
72
|
+
stages:
|
|
73
|
+
- id: intake
|
|
74
|
+
initial: true
|
|
75
|
+
description: >-
|
|
76
|
+
Fetch the GitHub issue for this work item (workItem is the bare
|
|
77
|
+
issue number) and record its context.
|
|
78
|
+
work:
|
|
79
|
+
mode: interactive
|
|
80
|
+
systemPrompt: >-
|
|
81
|
+
Fetch the GitHub issue numbered \${{ self.workItem }} for
|
|
82
|
+
${repo} using the ${issuesModel} model
|
|
83
|
+
(list_issues, then swamp data query filtered by number — see the
|
|
84
|
+
${issuesModel} data), and record the issue-context artifact.
|
|
85
|
+
artifacts:
|
|
86
|
+
- name: issue-context
|
|
87
|
+
schema:
|
|
88
|
+
type: object
|
|
89
|
+
required: [number, title, url]
|
|
90
|
+
properties:
|
|
91
|
+
number: { type: integer }
|
|
92
|
+
title: { type: string, minLength: 1 }
|
|
93
|
+
body: { type: string }
|
|
94
|
+
url: { type: string, minLength: 1 }
|
|
95
|
+
labels: { type: array, items: { type: string } }
|
|
96
|
+
transitions:
|
|
97
|
+
- name: submit
|
|
98
|
+
to: planning
|
|
99
|
+
gates:
|
|
100
|
+
- type: artifact-exists
|
|
101
|
+
config: { artifact: issue-context }
|
|
102
|
+
|
|
103
|
+
- id: planning
|
|
104
|
+
description: >-
|
|
105
|
+
Turn the issue into a real OpenSpec change (proposal, design,
|
|
106
|
+
specs, tasks) — OpenSpec's task list is the plan, not a
|
|
107
|
+
freeform artifact.
|
|
108
|
+
work:
|
|
109
|
+
mode: interactive
|
|
110
|
+
systemPrompt: >-
|
|
111
|
+
Using the recorded issue-context artifact, use the
|
|
112
|
+
openspec-propose skill to create a complete OpenSpec change
|
|
113
|
+
(proposal.md, design.md, specs with deltas, tasks.md) for this
|
|
114
|
+
work item under openspec/changes/<changeSlug>/ — do not write a
|
|
115
|
+
freeform plan instead of this. Then record the plan artifact
|
|
116
|
+
with the change's slug and a one-line summary.
|
|
117
|
+
artifacts:
|
|
118
|
+
- name: plan
|
|
119
|
+
schema:
|
|
120
|
+
type: object
|
|
121
|
+
required: [changeSlug, summary]
|
|
122
|
+
properties:
|
|
123
|
+
changeSlug: { type: string, minLength: 1 }
|
|
124
|
+
summary: { type: string, minLength: 1 }
|
|
125
|
+
transitions:
|
|
126
|
+
- name: submit
|
|
127
|
+
to: openspec-plan-verify
|
|
128
|
+
gates:
|
|
129
|
+
- type: artifact-exists
|
|
130
|
+
config: { artifact: plan }
|
|
131
|
+
|
|
132
|
+
- id: openspec-plan-verify
|
|
133
|
+
description: >-
|
|
134
|
+
Verify the OpenSpec change is real and well-formed (not just an
|
|
135
|
+
artifact claiming one exists) before implementation starts.
|
|
136
|
+
work:
|
|
137
|
+
mode: workflow
|
|
138
|
+
workflow:
|
|
139
|
+
name: openspec-plan-verify
|
|
140
|
+
inputs:
|
|
141
|
+
changeSlug: '\${{ data.latest(self.name, "artifact-plan").payload.changeSlug }}'
|
|
142
|
+
resultEvidence: plan-verify-run
|
|
143
|
+
transitions:
|
|
144
|
+
- name: pass
|
|
145
|
+
to: implementing
|
|
146
|
+
gates:
|
|
147
|
+
- type: workflow-succeeded
|
|
148
|
+
config: { workflow: openspec-plan-verify }
|
|
149
|
+
- name: fail
|
|
150
|
+
to: planning
|
|
151
|
+
gates:
|
|
152
|
+
- type: evidence-recorded
|
|
153
|
+
config: { name: plan-verify-run, requireField: { status: failed } }
|
|
154
|
+
|
|
155
|
+
- id: implementing
|
|
156
|
+
description: Implement the OpenSpec change's tasks on a branch.
|
|
157
|
+
work:
|
|
158
|
+
mode: interactive
|
|
159
|
+
systemPrompt: >-
|
|
160
|
+
Use the openspec-apply-change skill against the change recorded
|
|
161
|
+
in the plan artifact's changeSlug — work through its tasks.md
|
|
162
|
+
one task at a time, checking each off (\`- [ ]\` -> \`- [x]\`) as
|
|
163
|
+
you complete it, until every task is done. Commit the work on a
|
|
164
|
+
feature branch. Record the change-summary artifact and the
|
|
165
|
+
change-request evidence (branch name and head commit sha — no
|
|
166
|
+
PR yet, that happens after pre-PR verification passes).
|
|
167
|
+
artifacts:
|
|
168
|
+
- name: change-summary
|
|
169
|
+
schema:
|
|
170
|
+
type: object
|
|
171
|
+
required: [summary, headSha]
|
|
172
|
+
properties:
|
|
173
|
+
summary: { type: string, minLength: 1 }
|
|
174
|
+
headSha: { type: string, minLength: 7 }
|
|
175
|
+
evidence:
|
|
176
|
+
- name: change-request
|
|
177
|
+
schema:
|
|
178
|
+
type: object
|
|
179
|
+
required: [branch, headSha]
|
|
180
|
+
properties:
|
|
181
|
+
branch: { type: string, minLength: 1 }
|
|
182
|
+
headSha: { type: string, minLength: 7 }
|
|
183
|
+
url: { type: string }
|
|
184
|
+
transitions:
|
|
185
|
+
- name: submit
|
|
186
|
+
to: openspec-tasks-verify
|
|
187
|
+
gates:
|
|
188
|
+
- type: artifact-exists
|
|
189
|
+
config: { artifact: change-summary }
|
|
190
|
+
- type: evidence-recorded
|
|
191
|
+
config: { name: change-request }
|
|
192
|
+
|
|
193
|
+
- id: openspec-tasks-verify
|
|
194
|
+
description: >-
|
|
195
|
+
Verify every task in the OpenSpec change is actually checked off
|
|
196
|
+
before pre-PR verification runs.
|
|
197
|
+
work:
|
|
198
|
+
mode: workflow
|
|
199
|
+
workflow:
|
|
200
|
+
name: openspec-tasks-verify
|
|
201
|
+
inputs:
|
|
202
|
+
changeSlug: '\${{ data.latest(self.name, "artifact-plan").payload.changeSlug }}'
|
|
203
|
+
resultEvidence: tasks-verify-run
|
|
204
|
+
transitions:
|
|
205
|
+
- name: pass
|
|
206
|
+
to: pre-pr-verify
|
|
207
|
+
gates:
|
|
208
|
+
- type: workflow-succeeded
|
|
209
|
+
config: { workflow: openspec-tasks-verify }
|
|
210
|
+
- name: fail
|
|
211
|
+
to: implementing
|
|
212
|
+
gates:
|
|
213
|
+
- type: evidence-recorded
|
|
214
|
+
config: { name: tasks-verify-run, requireField: { status: failed } }
|
|
215
|
+
|
|
216
|
+
- id: pre-pr-verify
|
|
217
|
+
description: >-
|
|
218
|
+
Pre-PR verification: build/synth/test/lint plus the architecture
|
|
219
|
+
rule registry, run before the PR is opened rather than relying on
|
|
220
|
+
CI after the fact.
|
|
221
|
+
work:
|
|
222
|
+
mode: workflow
|
|
223
|
+
workflow:
|
|
224
|
+
name: pre-pr-verify
|
|
225
|
+
inputs:
|
|
226
|
+
commit: '\${{ data.latest(self.name, "evidence-change-request").payload.headSha }}'
|
|
227
|
+
branch: '\${{ data.latest(self.name, "evidence-change-request").payload.branch }}'
|
|
228
|
+
resultEvidence: verify-run
|
|
229
|
+
transitions:
|
|
230
|
+
- name: pass
|
|
231
|
+
to: open-pr
|
|
232
|
+
gates:
|
|
233
|
+
- type: workflow-succeeded
|
|
234
|
+
config: { workflow: pre-pr-verify }
|
|
235
|
+
- name: fail
|
|
236
|
+
to: implementing
|
|
237
|
+
gates:
|
|
238
|
+
- type: evidence-recorded
|
|
239
|
+
config: { name: verify-run, requireField: { status: failed } }
|
|
240
|
+
|
|
241
|
+
- id: open-pr
|
|
242
|
+
description: Open the pull request now that pre-PR verification has passed.
|
|
243
|
+
work:
|
|
244
|
+
mode: interactive
|
|
245
|
+
systemPrompt: >-
|
|
246
|
+
Before opening the PR, fetch the real pre-pr-verify attestation
|
|
247
|
+
with \`swamp data get --workflow pre-pr-verify
|
|
248
|
+
report-swamp-verification-attestation --json\` and include its
|
|
249
|
+
\`content\` verbatim (as a markdown block, e.g. under a "Pre-PR
|
|
250
|
+
verification" heading) in the PR body — never just assert in
|
|
251
|
+
prose that checks passed. Also fetch this same run's manifest step
|
|
252
|
+
output (\`swamp data query 'modelName == "${shellManifestModel}" &&
|
|
253
|
+
name == "result"' --select attributes.stdout --json\`, take the
|
|
254
|
+
latest) and append it to the PR body as
|
|
255
|
+
\`<!-- attestation-manifest {"commit": "<headSha>", "status":
|
|
256
|
+
"succeeded", ...the manifest step's JSON, spread in...} -->\` — an
|
|
257
|
+
HTML comment so it's invisible when rendered but still present in
|
|
258
|
+
the raw body text verify-attestation.yml reads via the GitHub API.
|
|
259
|
+
Also fetch the plan artifact's changeSlug and include an explicit
|
|
260
|
+
"OpenSpec change" line in the PR body naming it and its path
|
|
261
|
+
(\`openspec/changes/<changeSlug>/\`) — every PR the factory opens
|
|
262
|
+
must call out which OpenSpec change(s) drove the session, not
|
|
263
|
+
just imply it. If planning cycled through more than one change
|
|
264
|
+
(a rework loop produced a new changeSlug), list every distinct
|
|
265
|
+
changeSlug recorded across the run's plan artifact versions, not
|
|
266
|
+
only the latest. Then call ${prModel}'s openPr method
|
|
267
|
+
(repo=${repo}, head=<the branch recorded in
|
|
268
|
+
change-request evidence>, base=${baseBranch}, dryRun=false), with
|
|
269
|
+
title/body referencing the intake stage's issue number, the
|
|
270
|
+
OpenSpec change(s), the attestation block, and the manifest
|
|
271
|
+
comment, then record the pr-opened evidence with the returned PR
|
|
272
|
+
url and number.
|
|
273
|
+
evidence:
|
|
274
|
+
- name: pr-opened
|
|
275
|
+
schema:
|
|
276
|
+
type: object
|
|
277
|
+
required: [url, number]
|
|
278
|
+
properties:
|
|
279
|
+
url: { type: string, minLength: 1 }
|
|
280
|
+
number: { type: integer }
|
|
281
|
+
transitions:
|
|
282
|
+
- name: submit
|
|
283
|
+
to: done
|
|
284
|
+
gates:
|
|
285
|
+
- type: evidence-recorded
|
|
286
|
+
config: { name: pr-opened }
|
|
287
|
+
|
|
288
|
+
- id: done
|
|
289
|
+
terminal: true
|
|
290
|
+
|
|
291
|
+
- id: aborted
|
|
292
|
+
terminal: true
|
|
293
|
+
|
|
294
|
+
globalTransitions:
|
|
295
|
+
- name: abort
|
|
296
|
+
to: aborted
|
|
297
|
+
gates:
|
|
298
|
+
- type: human-approval
|
|
299
|
+
config: { id: abort-confirmation }
|
|
300
|
+
methods: {}
|
|
301
|
+
`,
|
|
302
|
+
});
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
exports.SoftwareFactoryModels = SoftwareFactoryModels;
|
|
306
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU29mdHdhcmVGYWN0b3J5TW9kZWxzLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL1NvZnR3YXJlRmFjdG9yeU1vZGVscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7QUFBQSxtQ0FBb0M7QUFDcEMsbUNBQW9DO0FBcUJwQzs7Ozs7R0FLRztBQUNILE1BQWEscUJBQXFCO0lBQ2hDLFlBQVksT0FBb0IsRUFBRSxPQUFxQztRQUNyRSxNQUFNLEVBQUUsUUFBUSxFQUFFLFdBQVcsRUFBRSxVQUFVLEVBQUUsR0FBRyxPQUFPLENBQUM7UUFDdEQsTUFBTSxJQUFJLEdBQUcsR0FBRyxXQUFXLElBQUksUUFBUSxFQUFFLENBQUM7UUFDMUMsTUFBTSxXQUFXLEdBQUcsR0FBRyxRQUFRLFNBQVMsQ0FBQztRQUN6QyxNQUFNLE9BQU8sR0FBRyxHQUFHLFFBQVEsS0FBSyxDQUFDO1FBQ2pDLE1BQU0sVUFBVSxHQUFHLEdBQUcsUUFBUSxRQUFRLENBQUM7UUFDdkMsTUFBTSxzQkFBc0IsR0FBRyxHQUFHLFFBQVEscUJBQXFCLENBQUM7UUFDaEUsTUFBTSxrQkFBa0IsR0FBRyxHQUFHLFFBQVEsaUJBQWlCLENBQUM7UUFDeEQsTUFBTSxZQUFZLEdBQUcsR0FBRyxRQUFRLFVBQVUsQ0FBQztRQUMzQyxNQUFNLFlBQVksR0FBRyxHQUFHLFFBQVEsVUFBVSxDQUFDO1FBRTNDLElBQUksbUJBQVUsQ0FBQyxPQUFPLEVBQUUsNEJBQTRCLE9BQU8sT0FBTyxFQUFFO1lBQ2xFLFFBQVEsRUFBRTs7TUFFVixJQUFBLG1CQUFVLEdBQUU7UUFDVixPQUFPOzs7OzJCQUlZLFlBQVk7V0FDNUIsV0FBVzs7Q0FFckI7U0FDSSxDQUFDLENBQUM7UUFFSCxJQUFJLG1CQUFVLENBQUMsT0FBTyxFQUFFLDJCQUEyQixXQUFXLE9BQU8sRUFBRTtZQUNyRSxRQUFRLEVBQUU7O01BRVYsSUFBQSxtQkFBVSxHQUFFO1FBQ1YsV0FBVzs7Ozs7Q0FLbEI7U0FDSSxDQUFDLENBQUM7UUFFSCxLQUFLLE1BQU0sSUFBSSxJQUFJLENBQUMsVUFBVSxFQUFFLHNCQUFzQixFQUFFLGtCQUFrQixDQUFDLEVBQUUsQ0FBQztZQUM1RSxJQUFJLG1CQUFVLENBQUMsT0FBTyxFQUFFLHdCQUF3QixJQUFJLE9BQU8sRUFBRTtnQkFDM0QsUUFBUSxFQUFFOztNQUVaLElBQUEsbUJBQVUsR0FBRTtRQUNWLElBQUk7Ozs7O0NBS1g7YUFDTSxDQUFDLENBQUM7UUFDTCxDQUFDO1FBRUQsSUFBSSxtQkFBVSxDQUFDLE9BQU8sRUFBRSxrQ0FBa0MsWUFBWSxPQUFPLEVBQUU7WUFDN0UsUUFBUSxFQUFFOztNQUVWLElBQUEsbUJBQVUsR0FBRTtRQUNWLFlBQVk7Ozs7Ozs7Ozs7Ozs7Ozs7OztZQWtCUixJQUFJLGNBQWMsV0FBVzs7WUFFN0IsV0FBVzs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O3NEQXdLK0Isa0JBQWtCOzs7Ozs7Ozs7Ozs7Ozt1Q0FjakMsT0FBTztrQkFDNUIsSUFBSTsyQ0FDcUIsVUFBVTs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7O0NBaUNwRDtTQUNJLENBQUMsQ0FBQztJQUNMLENBQUM7Q0FDRjtBQXhTRCxzREF3U0MiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyByYW5kb21VVUlEIH0gZnJvbSAnY3J5cHRvJztcbmltcG9ydCB7IFNhbXBsZUZpbGUgfSBmcm9tICdwcm9qZW4nO1xuaW1wb3J0IHsgTm9kZVByb2plY3QgfSBmcm9tICdwcm9qZW4vbGliL2phdmFzY3JpcHQnO1xuXG5leHBvcnQgaW50ZXJmYWNlIFNvZnR3YXJlRmFjdG9yeU1vZGVsc09wdGlvbnMge1xuICAvKipcbiAgICogVGhlIGJhcmUgcmVwbyBuYW1lIChlLmcuIGBleHBlbnNlLXRyYWNrZXJgKSwgdXNlZCB0byBkZXJpdmUgbW9kZWwgbmFtZXNcbiAgICogbGlrZSBgPHJlcG9OYW1lPi1mYWN0b3J5YCwgYDxyZXBvTmFtZT4tc2hlbGxgLCBgPHJlcG9OYW1lPi1zZWNyZXRzYC5cbiAgICovXG4gIHJlYWRvbmx5IHJlcG9OYW1lOiBzdHJpbmc7XG5cbiAgLyoqXG4gICAqIFRoZSBHaXRIdWIgb3JnIHRoZSByZXBvIGxpdmVzIGluLlxuICAgKi9cbiAgcmVhZG9ubHkgZ2l0aHViT3duZXI6IHN0cmluZztcblxuICAvKipcbiAgICogVGhlIGJyYW5jaCB0aGUgZmFjdG9yeSBvcGVucyBwdWxsIHJlcXVlc3RzIGFnYWluc3QuXG4gICAqL1xuICByZWFkb25seSBiYXNlQnJhbmNoOiBzdHJpbmc7XG59XG5cbi8qKlxuICogU2VlZCBzd2FtcCBtb2RlbCBpbnN0YW5jZXMgd2lyaW5nIHRoZSBgQHN3YW1wL3NvZnR3YXJlLWZhY3RvcnlgIHN0YXRlXG4gKiBtYWNoaW5lIHRvIHRoaXMgcmVwbydzIEdpdEh1YiBpc3N1ZXMvUFJzIGFuZCBzaGVsbCBzdGVwcy4gVGhlc2UgYXJlXG4gKiBgU2FtcGxlRmlsZWBzICh3cml0dGVuIG9uY2UsIHRoZW4gb3duZWQgYnkgdGhlIHRlYW0pIHNpbmNlIHRoZSBmYWN0b3J5J3NcbiAqIHN0YWdlcy9nYXRlcyBhcmUgdGhlIGtpbmQgb2YgdGhpbmcgYSB0ZWFtIHR1bmVzIGZvciBpdHMgb3duIHdvcmtmbG93LlxuICovXG5leHBvcnQgY2xhc3MgU29mdHdhcmVGYWN0b3J5TW9kZWxzIHtcbiAgY29uc3RydWN0b3IocHJvamVjdDogTm9kZVByb2plY3QsIG9wdGlvbnM6IFNvZnR3YXJlRmFjdG9yeU1vZGVsc09wdGlvbnMpIHtcbiAgICBjb25zdCB7IHJlcG9OYW1lLCBnaXRodWJPd25lciwgYmFzZUJyYW5jaCB9ID0gb3B0aW9ucztcbiAgICBjb25zdCByZXBvID0gYCR7Z2l0aHViT3duZXJ9LyR7cmVwb05hbWV9YDtcbiAgICBjb25zdCBpc3N1ZXNNb2RlbCA9IGAke3JlcG9OYW1lfS1pc3N1ZXNgO1xuICAgIGNvbnN0IHByTW9kZWwgPSBgJHtyZXBvTmFtZX0tcHJgO1xuICAgIGNvbnN0IHNoZWxsTW9kZWwgPSBgJHtyZXBvTmFtZX0tc2hlbGxgO1xuICAgIGNvbnN0IHNoZWxsQXJjaGl0ZWN0dXJlTW9kZWwgPSBgJHtyZXBvTmFtZX0tc2hlbGwtYXJjaGl0ZWN0dXJlYDtcbiAgICBjb25zdCBzaGVsbE1hbmlmZXN0TW9kZWwgPSBgJHtyZXBvTmFtZX0tc2hlbGwtbWFuaWZlc3RgO1xuICAgIGNvbnN0IHNlY3JldHNWYXVsdCA9IGAke3JlcG9OYW1lfS1zZWNyZXRzYDtcbiAgICBjb25zdCBmYWN0b3J5TW9kZWwgPSBgJHtyZXBvTmFtZX0tZmFjdG9yeWA7XG5cbiAgICBuZXcgU2FtcGxlRmlsZShwcm9qZWN0LCBgbW9kZWxzL0Bnb29kY3JhZnQvZ2l0aHViLyR7cHJNb2RlbH0ueWFtbGAsIHtcbiAgICAgIGNvbnRlbnRzOiBgdHlwZTogJ0Bnb29kY3JhZnQvZ2l0aHViJ1xudHlwZVZlcnNpb246IDIwMjYuMDYuMTQuMVxuaWQ6ICR7cmFuZG9tVVVJRCgpfVxubmFtZTogJHtwck1vZGVsfVxudmVyc2lvbjogMVxudGFnczoge31cbmdsb2JhbEFyZ3VtZW50czpcbiAgdG9rZW46ICdcXCR7eyB2YXVsdC5nZXQoJHtzZWNyZXRzVmF1bHR9LCBHSVRIVUJfVE9LRU4pIH19J1xuICBvd25lcjogJHtnaXRodWJPd25lcn1cbm1ldGhvZHM6IHt9XG5gLFxuICAgIH0pO1xuXG4gICAgbmV3IFNhbXBsZUZpbGUocHJvamVjdCwgYG1vZGVscy9Ad2ViZnJhbXAvZ2l0aHViLyR7aXNzdWVzTW9kZWx9LnlhbWxgLCB7XG4gICAgICBjb250ZW50czogYHR5cGU6ICdAd2ViZnJhbXAvZ2l0aHViJ1xudHlwZVZlcnNpb246IDIwMjYuMDguMjguMVxuaWQ6ICR7cmFuZG9tVVVJRCgpfVxubmFtZTogJHtpc3N1ZXNNb2RlbH1cbnZlcnNpb246IDFcbnRhZ3M6IHt9XG5nbG9iYWxBcmd1bWVudHM6IHt9XG5tZXRob2RzOiB7fVxuYCxcbiAgICB9KTtcblxuICAgIGZvciAoY29uc3QgbmFtZSBvZiBbc2hlbGxNb2RlbCwgc2hlbGxBcmNoaXRlY3R1cmVNb2RlbCwgc2hlbGxNYW5pZmVzdE1vZGVsXSkge1xuICAgICAgbmV3IFNhbXBsZUZpbGUocHJvamVjdCwgYG1vZGVscy9jb21tYW5kL3NoZWxsLyR7bmFtZX0ueWFtbGAsIHtcbiAgICAgICAgY29udGVudHM6IGB0eXBlOiBjb21tYW5kL3NoZWxsXG50eXBlVmVyc2lvbjogMjAyNi4wMi4wOS4xXG5pZDogJHtyYW5kb21VVUlEKCl9XG5uYW1lOiAke25hbWV9XG52ZXJzaW9uOiAxXG50YWdzOiB7fVxuZ2xvYmFsQXJndW1lbnRzOiB7fVxubWV0aG9kczoge31cbmAsXG4gICAgICB9KTtcbiAgICB9XG5cbiAgICBuZXcgU2FtcGxlRmlsZShwcm9qZWN0LCBgbW9kZWxzL0Bzd2FtcC9zb2Z0d2FyZS1mYWN0b3J5LyR7ZmFjdG9yeU1vZGVsfS55YW1sYCwge1xuICAgICAgY29udGVudHM6IGB0eXBlOiAnQHN3YW1wL3NvZnR3YXJlLWZhY3RvcnknXG50eXBlVmVyc2lvbjogMjAyNi4wNi4yNC4xXG5pZDogJHtyYW5kb21VVUlEKCl9XG5uYW1lOiAke2ZhY3RvcnlNb2RlbH1cbnZlcnNpb246IDFcbnRhZ3M6IHt9XG5yZXBvcnRzOlxuICByZXF1aXJlOlxuICAgIC0gbmFtZTogXCJAc3dhbXAvc29mdHdhcmUtZmFjdG9yeS93b3JrLWl0ZW0tc3VtbWFyeVwiXG4gICAgICBtZXRob2RzOiBbc3VtbWFyeV1cbmdsb2JhbEFyZ3VtZW50czpcbiAgc3RhZ2VzOlxuICAgIC0gaWQ6IGludGFrZVxuICAgICAgaW5pdGlhbDogdHJ1ZVxuICAgICAgZGVzY3JpcHRpb246ID4tXG4gICAgICAgIEZldGNoIHRoZSBHaXRIdWIgaXNzdWUgZm9yIHRoaXMgd29yayBpdGVtICh3b3JrSXRlbSBpcyB0aGUgYmFyZVxuICAgICAgICBpc3N1ZSBudW1iZXIpIGFuZCByZWNvcmQgaXRzIGNvbnRleHQuXG4gICAgICB3b3JrOlxuICAgICAgICBtb2RlOiBpbnRlcmFjdGl2ZVxuICAgICAgICBzeXN0ZW1Qcm9tcHQ6ID4tXG4gICAgICAgICAgRmV0Y2ggdGhlIEdpdEh1YiBpc3N1ZSBudW1iZXJlZCBcXCR7eyBzZWxmLndvcmtJdGVtIH19IGZvclxuICAgICAgICAgICR7cmVwb30gdXNpbmcgdGhlICR7aXNzdWVzTW9kZWx9IG1vZGVsXG4gICAgICAgICAgKGxpc3RfaXNzdWVzLCB0aGVuIHN3YW1wIGRhdGEgcXVlcnkgZmlsdGVyZWQgYnkgbnVtYmVyIOKAlCBzZWUgdGhlXG4gICAgICAgICAgJHtpc3N1ZXNNb2RlbH0gZGF0YSksIGFuZCByZWNvcmQgdGhlIGlzc3VlLWNvbnRleHQgYXJ0aWZhY3QuXG4gICAgICBhcnRpZmFjdHM6XG4gICAgICAgIC0gbmFtZTogaXNzdWUtY29udGV4dFxuICAgICAgICAgIHNjaGVtYTpcbiAgICAgICAgICAgIHR5cGU6IG9iamVjdFxuICAgICAgICAgICAgcmVxdWlyZWQ6IFtudW1iZXIsIHRpdGxlLCB1cmxdXG4gICAgICAgICAgICBwcm9wZXJ0aWVzOlxuICAgICAgICAgICAgICBudW1iZXI6IHsgdHlwZTogaW50ZWdlciB9XG4gICAgICAgICAgICAgIHRpdGxlOiB7IHR5cGU6IHN0cmluZywgbWluTGVuZ3RoOiAxIH1cbiAgICAgICAgICAgICAgYm9keTogeyB0eXBlOiBzdHJpbmcgfVxuICAgICAgICAgICAgICB1cmw6IHsgdHlwZTogc3RyaW5nLCBtaW5MZW5ndGg6IDEgfVxuICAgICAgICAgICAgICBsYWJlbHM6IHsgdHlwZTogYXJyYXksIGl0ZW1zOiB7IHR5cGU6IHN0cmluZyB9IH1cbiAgICAgIHRyYW5zaXRpb25zOlxuICAgICAgICAtIG5hbWU6IHN1Ym1pdFxuICAgICAgICAgIHRvOiBwbGFubmluZ1xuICAgICAgICAgIGdhdGVzOlxuICAgICAgICAgICAgLSB0eXBlOiBhcnRpZmFjdC1leGlzdHNcbiAgICAgICAgICAgICAgY29uZmlnOiB7IGFydGlmYWN0OiBpc3N1ZS1jb250ZXh0IH1cblxuICAgIC0gaWQ6IHBsYW5uaW5nXG4gICAgICBkZXNjcmlwdGlvbjogPi1cbiAgICAgICAgVHVybiB0aGUgaXNzdWUgaW50byBhIHJlYWwgT3BlblNwZWMgY2hhbmdlIChwcm9wb3NhbCwgZGVzaWduLFxuICAgICAgICBzcGVjcywgdGFza3MpIOKAlCBPcGVuU3BlYydzIHRhc2sgbGlzdCBpcyB0aGUgcGxhbiwgbm90IGFcbiAgICAgICAgZnJlZWZvcm0gYXJ0aWZhY3QuXG4gICAgICB3b3JrOlxuICAgICAgICBtb2RlOiBpbnRlcmFjdGl2ZVxuICAgICAgICBzeXN0ZW1Qcm9tcHQ6ID4tXG4gICAgICAgICAgVXNpbmcgdGhlIHJlY29yZGVkIGlzc3VlLWNvbnRleHQgYXJ0aWZhY3QsIHVzZSB0aGVcbiAgICAgICAgICBvcGVuc3BlYy1wcm9wb3NlIHNraWxsIHRvIGNyZWF0ZSBhIGNvbXBsZXRlIE9wZW5TcGVjIGNoYW5nZVxuICAgICAgICAgIChwcm9wb3NhbC5tZCwgZGVzaWduLm1kLCBzcGVjcyB3aXRoIGRlbHRhcywgdGFza3MubWQpIGZvciB0aGlzXG4gICAgICAgICAgd29yayBpdGVtIHVuZGVyIG9wZW5zcGVjL2NoYW5nZXMvPGNoYW5nZVNsdWc+LyDigJQgZG8gbm90IHdyaXRlIGFcbiAgICAgICAgICBmcmVlZm9ybSBwbGFuIGluc3RlYWQgb2YgdGhpcy4gVGhlbiByZWNvcmQgdGhlIHBsYW4gYXJ0aWZhY3RcbiAgICAgICAgICB3aXRoIHRoZSBjaGFuZ2UncyBzbHVnIGFuZCBhIG9uZS1saW5lIHN1bW1hcnkuXG4gICAgICBhcnRpZmFjdHM6XG4gICAgICAgIC0gbmFtZTogcGxhblxuICAgICAgICAgIHNjaGVtYTpcbiAgICAgICAgICAgIHR5cGU6IG9iamVjdFxuICAgICAgICAgICAgcmVxdWlyZWQ6IFtjaGFuZ2VTbHVnLCBzdW1tYXJ5XVxuICAgICAgICAgICAgcHJvcGVydGllczpcbiAgICAgICAgICAgICAgY2hhbmdlU2x1ZzogeyB0eXBlOiBzdHJpbmcsIG1pbkxlbmd0aDogMSB9XG4gICAgICAgICAgICAgIHN1bW1hcnk6IHsgdHlwZTogc3RyaW5nLCBtaW5MZW5ndGg6IDEgfVxuICAgICAgdHJhbnNpdGlvbnM6XG4gICAgICAgIC0gbmFtZTogc3VibWl0XG4gICAgICAgICAgdG86IG9wZW5zcGVjLXBsYW4tdmVyaWZ5XG4gICAgICAgICAgZ2F0ZXM6XG4gICAgICAgICAgICAtIHR5cGU6IGFydGlmYWN0LWV4aXN0c1xuICAgICAgICAgICAgICBjb25maWc6IHsgYXJ0aWZhY3Q6IHBsYW4gfVxuXG4gICAgLSBpZDogb3BlbnNwZWMtcGxhbi12ZXJpZnlcbiAgICAgIGRlc2NyaXB0aW9uOiA+LVxuICAgICAgICBWZXJpZnkgdGhlIE9wZW5TcGVjIGNoYW5nZSBpcyByZWFsIGFuZCB3ZWxsLWZvcm1lZCAobm90IGp1c3QgYW5cbiAgICAgICAgYXJ0aWZhY3QgY2xhaW1pbmcgb25lIGV4aXN0cykgYmVmb3JlIGltcGxlbWVudGF0aW9uIHN0YXJ0cy5cbiAgICAgIHdvcms6XG4gICAgICAgIG1vZGU6IHdvcmtmbG93XG4gICAgICAgIHdvcmtmbG93OlxuICAgICAgICAgIG5hbWU6IG9wZW5zcGVjLXBsYW4tdmVyaWZ5XG4gICAgICAgICAgaW5wdXRzOlxuICAgICAgICAgICAgY2hhbmdlU2x1ZzogJ1xcJHt7IGRhdGEubGF0ZXN0KHNlbGYubmFtZSwgXCJhcnRpZmFjdC1wbGFuXCIpLnBheWxvYWQuY2hhbmdlU2x1ZyB9fSdcbiAgICAgICAgcmVzdWx0RXZpZGVuY2U6IHBsYW4tdmVyaWZ5LXJ1blxuICAgICAgdHJhbnNpdGlvbnM6XG4gICAgICAgIC0gbmFtZTogcGFzc1xuICAgICAgICAgIHRvOiBpbXBsZW1lbnRpbmdcbiAgICAgICAgICBnYXRlczpcbiAgICAgICAgICAgIC0gdHlwZTogd29ya2Zsb3ctc3VjY2VlZGVkXG4gICAgICAgICAgICAgIGNvbmZpZzogeyB3b3JrZmxvdzogb3BlbnNwZWMtcGxhbi12ZXJpZnkgfVxuICAgICAgICAtIG5hbWU6IGZhaWxcbiAgICAgICAgICB0bzogcGxhbm5pbmdcbiAgICAgICAgICBnYXRlczpcbiAgICAgICAgICAgIC0gdHlwZTogZXZpZGVuY2UtcmVjb3JkZWRcbiAgICAgICAgICAgICAgY29uZmlnOiB7IG5hbWU6IHBsYW4tdmVyaWZ5LXJ1biwgcmVxdWlyZUZpZWxkOiB7IHN0YXR1czogZmFpbGVkIH0gfVxuXG4gICAgLSBpZDogaW1wbGVtZW50aW5nXG4gICAgICBkZXNjcmlwdGlvbjogSW1wbGVtZW50IHRoZSBPcGVuU3BlYyBjaGFuZ2UncyB0YXNrcyBvbiBhIGJyYW5jaC5cbiAgICAgIHdvcms6XG4gICAgICAgIG1vZGU6IGludGVyYWN0aXZlXG4gICAgICAgIHN5c3RlbVByb21wdDogPi1cbiAgICAgICAgICBVc2UgdGhlIG9wZW5zcGVjLWFwcGx5LWNoYW5nZSBza2lsbCBhZ2FpbnN0IHRoZSBjaGFuZ2UgcmVjb3JkZWRcbiAgICAgICAgICBpbiB0aGUgcGxhbiBhcnRpZmFjdCdzIGNoYW5nZVNsdWcg4oCUIHdvcmsgdGhyb3VnaCBpdHMgdGFza3MubWRcbiAgICAgICAgICBvbmUgdGFzayBhdCBhIHRpbWUsIGNoZWNraW5nIGVhY2ggb2ZmIChcXGAtIFsgXVxcYCAtPiBcXGAtIFt4XVxcYCkgYXNcbiAgICAgICAgICB5b3UgY29tcGxldGUgaXQsIHVudGlsIGV2ZXJ5IHRhc2sgaXMgZG9uZS4gQ29tbWl0IHRoZSB3b3JrIG9uIGFcbiAgICAgICAgICBmZWF0dXJlIGJyYW5jaC4gUmVjb3JkIHRoZSBjaGFuZ2Utc3VtbWFyeSBhcnRpZmFjdCBhbmQgdGhlXG4gICAgICAgICAgY2hhbmdlLXJlcXVlc3QgZXZpZGVuY2UgKGJyYW5jaCBuYW1lIGFuZCBoZWFkIGNvbW1pdCBzaGEg4oCUIG5vXG4gICAgICAgICAgUFIgeWV0LCB0aGF0IGhhcHBlbnMgYWZ0ZXIgcHJlLVBSIHZlcmlmaWNhdGlvbiBwYXNzZXMpLlxuICAgICAgYXJ0aWZhY3RzOlxuICAgICAgICAtIG5hbWU6IGNoYW5nZS1zdW1tYXJ5XG4gICAgICAgICAgc2NoZW1hOlxuICAgICAgICAgICAgdHlwZTogb2JqZWN0XG4gICAgICAgICAgICByZXF1aXJlZDogW3N1bW1hcnksIGhlYWRTaGFdXG4gICAgICAgICAgICBwcm9wZXJ0aWVzOlxuICAgICAgICAgICAgICBzdW1tYXJ5OiB7IHR5cGU6IHN0cmluZywgbWluTGVuZ3RoOiAxIH1cbiAgICAgICAgICAgICAgaGVhZFNoYTogeyB0eXBlOiBzdHJpbmcsIG1pbkxlbmd0aDogNyB9XG4gICAgICBldmlkZW5jZTpcbiAgICAgICAgLSBuYW1lOiBjaGFuZ2UtcmVxdWVzdFxuICAgICAgICAgIHNjaGVtYTpcbiAgICAgICAgICAgIHR5cGU6IG9iamVjdFxuICAgICAgICAgICAgcmVxdWlyZWQ6IFticmFuY2gsIGhlYWRTaGFdXG4gICAgICAgICAgICBwcm9wZXJ0aWVzOlxuICAgICAgICAgICAgICBicmFuY2g6IHsgdHlwZTogc3RyaW5nLCBtaW5MZW5ndGg6IDEgfVxuICAgICAgICAgICAgICBoZWFkU2hhOiB7IHR5cGU6IHN0cmluZywgbWluTGVuZ3RoOiA3IH1cbiAgICAgICAgICAgICAgdXJsOiB7IHR5cGU6IHN0cmluZyB9XG4gICAgICB0cmFuc2l0aW9uczpcbiAgICAgICAgLSBuYW1lOiBzdWJtaXRcbiAgICAgICAgICB0bzogb3BlbnNwZWMtdGFza3MtdmVyaWZ5XG4gICAgICAgICAgZ2F0ZXM6XG4gICAgICAgICAgICAtIHR5cGU6IGFydGlmYWN0LWV4aXN0c1xuICAgICAgICAgICAgICBjb25maWc6IHsgYXJ0aWZhY3Q6IGNoYW5nZS1zdW1tYXJ5IH1cbiAgICAgICAgICAgIC0gdHlwZTogZXZpZGVuY2UtcmVjb3JkZWRcbiAgICAgICAgICAgICAgY29uZmlnOiB7IG5hbWU6IGNoYW5nZS1yZXF1ZXN0IH1cblxuICAgIC0gaWQ6IG9wZW5zcGVjLXRhc2tzLXZlcmlmeVxuICAgICAgZGVzY3JpcHRpb246ID4tXG4gICAgICAgIFZlcmlmeSBldmVyeSB0YXNrIGluIHRoZSBPcGVuU3BlYyBjaGFuZ2UgaXMgYWN0dWFsbHkgY2hlY2tlZCBvZmZcbiAgICAgICAgYmVmb3JlIHByZS1QUiB2ZXJpZmljYXRpb24gcnVucy5cbiAgICAgIHdvcms6XG4gICAgICAgIG1vZGU6IHdvcmtmbG93XG4gICAgICAgIHdvcmtmbG93OlxuICAgICAgICAgIG5hbWU6IG9wZW5zcGVjLXRhc2tzLXZlcmlmeVxuICAgICAgICAgIGlucHV0czpcbiAgICAgICAgICAgIGNoYW5nZVNsdWc6ICdcXCR7eyBkYXRhLmxhdGVzdChzZWxmLm5hbWUsIFwiYXJ0aWZhY3QtcGxhblwiKS5wYXlsb2FkLmNoYW5nZVNsdWcgfX0nXG4gICAgICAgIHJlc3VsdEV2aWRlbmNlOiB0YXNrcy12ZXJpZnktcnVuXG4gICAgICB0cmFuc2l0aW9uczpcbiAgICAgICAgLSBuYW1lOiBwYXNzXG4gICAgICAgICAgdG86IHByZS1wci12ZXJpZnlcbiAgICAgICAgICBnYXRlczpcbiAgICAgICAgICAgIC0gdHlwZTogd29ya2Zsb3ctc3VjY2VlZGVkXG4gICAgICAgICAgICAgIGNvbmZpZzogeyB3b3JrZmxvdzogb3BlbnNwZWMtdGFza3MtdmVyaWZ5IH1cbiAgICAgICAgLSBuYW1lOiBmYWlsXG4gICAgICAgICAgdG86IGltcGxlbWVudGluZ1xuICAgICAgICAgIGdhdGVzOlxuICAgICAgICAgICAgLSB0eXBlOiBldmlkZW5jZS1yZWNvcmRlZFxuICAgICAgICAgICAgICBjb25maWc6IHsgbmFtZTogdGFza3MtdmVyaWZ5LXJ1biwgcmVxdWlyZUZpZWxkOiB7IHN0YXR1czogZmFpbGVkIH0gfVxuXG4gICAgLSBpZDogcHJlLXByLXZlcmlmeVxuICAgICAgZGVzY3JpcHRpb246ID4tXG4gICAgICAgIFByZS1QUiB2ZXJpZmljYXRpb246IGJ1aWxkL3N5bnRoL3Rlc3QvbGludCBwbHVzIHRoZSBhcmNoaXRlY3R1cmVcbiAgICAgICAgcnVsZSByZWdpc3RyeSwgcnVuIGJlZm9yZSB0aGUgUFIgaXMgb3BlbmVkIHJhdGhlciB0aGFuIHJlbHlpbmcgb25cbiAgICAgICAgQ0kgYWZ0ZXIgdGhlIGZhY3QuXG4gICAgICB3b3JrOlxuICAgICAgICBtb2RlOiB3b3JrZmxvd1xuICAgICAgICB3b3JrZmxvdzpcbiAgICAgICAgICBuYW1lOiBwcmUtcHItdmVyaWZ5XG4gICAgICAgICAgaW5wdXRzOlxuICAgICAgICAgICAgY29tbWl0OiAnXFwke3sgZGF0YS5sYXRlc3Qoc2VsZi5uYW1lLCBcImV2aWRlbmNlLWNoYW5nZS1yZXF1ZXN0XCIpLnBheWxvYWQuaGVhZFNoYSB9fSdcbiAgICAgICAgICAgIGJyYW5jaDogJ1xcJHt7IGRhdGEubGF0ZXN0KHNlbGYubmFtZSwgXCJldmlkZW5jZS1jaGFuZ2UtcmVxdWVzdFwiKS5wYXlsb2FkLmJyYW5jaCB9fSdcbiAgICAgICAgcmVzdWx0RXZpZGVuY2U6IHZlcmlmeS1ydW5cbiAgICAgIHRyYW5zaXRpb25zOlxuICAgICAgICAtIG5hbWU6IHBhc3NcbiAgICAgICAgICB0bzogb3Blbi1wclxuICAgICAgICAgIGdhdGVzOlxuICAgICAgICAgICAgLSB0eXBlOiB3b3JrZmxvdy1zdWNjZWVkZWRcbiAgICAgICAgICAgICAgY29uZmlnOiB7IHdvcmtmbG93OiBwcmUtcHItdmVyaWZ5IH1cbiAgICAgICAgLSBuYW1lOiBmYWlsXG4gICAgICAgICAgdG86IGltcGxlbWVudGluZ1xuICAgICAgICAgIGdhdGVzOlxuICAgICAgICAgICAgLSB0eXBlOiBldmlkZW5jZS1yZWNvcmRlZFxuICAgICAgICAgICAgICBjb25maWc6IHsgbmFtZTogdmVyaWZ5LXJ1biwgcmVxdWlyZUZpZWxkOiB7IHN0YXR1czogZmFpbGVkIH0gfVxuXG4gICAgLSBpZDogb3Blbi1wclxuICAgICAgZGVzY3JpcHRpb246IE9wZW4gdGhlIHB1bGwgcmVxdWVzdCBub3cgdGhhdCBwcmUtUFIgdmVyaWZpY2F0aW9uIGhhcyBwYXNzZWQuXG4gICAgICB3b3JrOlxuICAgICAgICBtb2RlOiBpbnRlcmFjdGl2ZVxuICAgICAgICBzeXN0ZW1Qcm9tcHQ6ID4tXG4gICAgICAgICAgQmVmb3JlIG9wZW5pbmcgdGhlIFBSLCBmZXRjaCB0aGUgcmVhbCBwcmUtcHItdmVyaWZ5IGF0dGVzdGF0aW9uXG4gICAgICAgICAgd2l0aCBcXGBzd2FtcCBkYXRhIGdldCAtLXdvcmtmbG93IHByZS1wci12ZXJpZnlcbiAgICAgICAgICByZXBvcnQtc3dhbXAtdmVyaWZpY2F0aW9uLWF0dGVzdGF0aW9uIC0tanNvblxcYCBhbmQgaW5jbHVkZSBpdHNcbiAgICAgICAgICBcXGBjb250ZW50XFxgIHZlcmJhdGltIChhcyBhIG1hcmtkb3duIGJsb2NrLCBlLmcuIHVuZGVyIGEgXCJQcmUtUFJcbiAgICAgICAgICB2ZXJpZmljYXRpb25cIiBoZWFkaW5nKSBpbiB0aGUgUFIgYm9keSDigJQgbmV2ZXIganVzdCBhc3NlcnQgaW5cbiAgICAgICAgICBwcm9zZSB0aGF0IGNoZWNrcyBwYXNzZWQuIEFsc28gZmV0Y2ggdGhpcyBzYW1lIHJ1bidzIG1hbmlmZXN0IHN0ZXBcbiAgICAgICAgICBvdXRwdXQgKFxcYHN3YW1wIGRhdGEgcXVlcnkgJ21vZGVsTmFtZSA9PSBcIiR7c2hlbGxNYW5pZmVzdE1vZGVsfVwiICYmXG4gICAgICAgICAgbmFtZSA9PSBcInJlc3VsdFwiJyAtLXNlbGVjdCBhdHRyaWJ1dGVzLnN0ZG91dCAtLWpzb25cXGAsIHRha2UgdGhlXG4gICAgICAgICAgbGF0ZXN0KSBhbmQgYXBwZW5kIGl0IHRvIHRoZSBQUiBib2R5IGFzXG4gICAgICAgICAgXFxgPCEtLSBhdHRlc3RhdGlvbi1tYW5pZmVzdCB7XCJjb21taXRcIjogXCI8aGVhZFNoYT5cIiwgXCJzdGF0dXNcIjpcbiAgICAgICAgICBcInN1Y2NlZWRlZFwiLCAuLi50aGUgbWFuaWZlc3Qgc3RlcCdzIEpTT04sIHNwcmVhZCBpbi4uLn0gLS0+XFxgIOKAlCBhblxuICAgICAgICAgIEhUTUwgY29tbWVudCBzbyBpdCdzIGludmlzaWJsZSB3aGVuIHJlbmRlcmVkIGJ1dCBzdGlsbCBwcmVzZW50IGluXG4gICAgICAgICAgdGhlIHJhdyBib2R5IHRleHQgdmVyaWZ5LWF0dGVzdGF0aW9uLnltbCByZWFkcyB2aWEgdGhlIEdpdEh1YiBBUEkuXG4gICAgICAgICAgQWxzbyBmZXRjaCB0aGUgcGxhbiBhcnRpZmFjdCdzIGNoYW5nZVNsdWcgYW5kIGluY2x1ZGUgYW4gZXhwbGljaXRcbiAgICAgICAgICBcIk9wZW5TcGVjIGNoYW5nZVwiIGxpbmUgaW4gdGhlIFBSIGJvZHkgbmFtaW5nIGl0IGFuZCBpdHMgcGF0aFxuICAgICAgICAgIChcXGBvcGVuc3BlYy9jaGFuZ2VzLzxjaGFuZ2VTbHVnPi9cXGApIOKAlCBldmVyeSBQUiB0aGUgZmFjdG9yeSBvcGVuc1xuICAgICAgICAgIG11c3QgY2FsbCBvdXQgd2hpY2ggT3BlblNwZWMgY2hhbmdlKHMpIGRyb3ZlIHRoZSBzZXNzaW9uLCBub3RcbiAgICAgICAgICBqdXN0IGltcGx5IGl0LiBJZiBwbGFubmluZyBjeWNsZWQgdGhyb3VnaCBtb3JlIHRoYW4gb25lIGNoYW5nZVxuICAgICAgICAgIChhIHJld29yayBsb29wIHByb2R1Y2VkIGEgbmV3IGNoYW5nZVNsdWcpLCBsaXN0IGV2ZXJ5IGRpc3RpbmN0XG4gICAgICAgICAgY2hhbmdlU2x1ZyByZWNvcmRlZCBhY3Jvc3MgdGhlIHJ1bidzIHBsYW4gYXJ0aWZhY3QgdmVyc2lvbnMsIG5vdFxuICAgICAgICAgIG9ubHkgdGhlIGxhdGVzdC4gVGhlbiBjYWxsICR7cHJNb2RlbH0ncyBvcGVuUHIgbWV0aG9kXG4gICAgICAgICAgKHJlcG89JHtyZXBvfSwgaGVhZD08dGhlIGJyYW5jaCByZWNvcmRlZCBpblxuICAgICAgICAgIGNoYW5nZS1yZXF1ZXN0IGV2aWRlbmNlPiwgYmFzZT0ke2Jhc2VCcmFuY2h9LCBkcnlSdW49ZmFsc2UpLCB3aXRoXG4gICAgICAgICAgdGl0bGUvYm9keSByZWZlcmVuY2luZyB0aGUgaW50YWtlIHN0YWdlJ3MgaXNzdWUgbnVtYmVyLCB0aGVcbiAgICAgICAgICBPcGVuU3BlYyBjaGFuZ2UocyksIHRoZSBhdHRlc3RhdGlvbiBibG9jaywgYW5kIHRoZSBtYW5pZmVzdFxuICAgICAgICAgIGNvbW1lbnQsIHRoZW4gcmVjb3JkIHRoZSBwci1vcGVuZWQgZXZpZGVuY2Ugd2l0aCB0aGUgcmV0dXJuZWQgUFJcbiAgICAgICAgICB1cmwgYW5kIG51bWJlci5cbiAgICAgIGV2aWRlbmNlOlxuICAgICAgICAtIG5hbWU6IHByLW9wZW5lZFxuICAgICAgICAgIHNjaGVtYTpcbiAgICAgICAgICAgIHR5cGU6IG9iamVjdFxuICAgICAgICAgICAgcmVxdWlyZWQ6IFt1cmwsIG51bWJlcl1cbiAgICAgICAgICAgIHByb3BlcnRpZXM6XG4gICAgICAgICAgICAgIHVybDogeyB0eXBlOiBzdHJpbmcsIG1pbkxlbmd0aDogMSB9XG4gICAgICAgICAgICAgIG51bWJlcjogeyB0eXBlOiBpbnRlZ2VyIH1cbiAgICAgIHRyYW5zaXRpb25zOlxuICAgICAgICAtIG5hbWU6IHN1Ym1pdFxuICAgICAgICAgIHRvOiBkb25lXG4gICAgICAgICAgZ2F0ZXM6XG4gICAgICAgICAgICAtIHR5cGU6IGV2aWRlbmNlLXJlY29yZGVkXG4gICAgICAgICAgICAgIGNvbmZpZzogeyBuYW1lOiBwci1vcGVuZWQgfVxuXG4gICAgLSBpZDogZG9uZVxuICAgICAgdGVybWluYWw6IHRydWVcblxuICAgIC0gaWQ6IGFib3J0ZWRcbiAgICAgIHRlcm1pbmFsOiB0cnVlXG5cbiAgZ2xvYmFsVHJhbnNpdGlvbnM6XG4gICAgLSBuYW1lOiBhYm9ydFxuICAgICAgdG86IGFib3J0ZWRcbiAgICAgIGdhdGVzOlxuICAgICAgICAtIHR5cGU6IGh1bWFuLWFwcHJvdmFsXG4gICAgICAgICAgY29uZmlnOiB7IGlkOiBhYm9ydC1jb25maXJtYXRpb24gfVxubWV0aG9kczoge31cbmAsXG4gICAgfSk7XG4gIH1cbn1cbiJdfQ==
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { NodeProject } from 'projen/lib/javascript';
|
|
2
|
+
/**
|
|
3
|
+
* Deterministic gate: fails unless every task in the named OpenSpec change's
|
|
4
|
+
* tasks.md is checked off. Fully generic — no per-repo parameterization.
|
|
5
|
+
*/
|
|
6
|
+
export declare class SoftwareFactoryOpenSpecGates {
|
|
7
|
+
constructor(project: NodeProject);
|
|
8
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.SoftwareFactoryOpenSpecGates = void 0;
|
|
4
|
+
const projen_1 = require("projen");
|
|
5
|
+
/**
|
|
6
|
+
* Deterministic gate: fails unless every task in the named OpenSpec change's
|
|
7
|
+
* tasks.md is checked off. Fully generic — no per-repo parameterization.
|
|
8
|
+
*/
|
|
9
|
+
class SoftwareFactoryOpenSpecGates {
|
|
10
|
+
constructor(project) {
|
|
11
|
+
new projen_1.TextFile(project, 'openspec-gates/check-tasks-complete.ts', {
|
|
12
|
+
marker: true,
|
|
13
|
+
lines: [
|
|
14
|
+
'/**',
|
|
15
|
+
' * Gate: fails unless every task in the named OpenSpec change\'s tasks.md is',
|
|
16
|
+
' * checked off. `openspec validate`/`openspec status` don\'t check this (an',
|
|
17
|
+
' * in-progress change is a normal, valid state) — this is the deterministic',
|
|
18
|
+
' * check the pre-pr pipeline needs before letting implementation proceed.',
|
|
19
|
+
' */',
|
|
20
|
+
"import { execFileSync } from 'node:child_process';",
|
|
21
|
+
'',
|
|
22
|
+
'interface OpenSpecChangeSummary {',
|
|
23
|
+
' name: string;',
|
|
24
|
+
' completedTasks: number;',
|
|
25
|
+
' totalTasks: number;',
|
|
26
|
+
' status: string;',
|
|
27
|
+
'}',
|
|
28
|
+
'',
|
|
29
|
+
'function main(): number {',
|
|
30
|
+
' // pnpm\'s `-- <args>` forwarding (and projen\'s receiveArgs) can pass a',
|
|
31
|
+
' // literal "--" through as an argv token ahead of the real argument.',
|
|
32
|
+
" const slug = process.argv.slice(2).find((arg) => arg !== '--');",
|
|
33
|
+
' if (!slug) {',
|
|
34
|
+
" console.error('Usage: check-tasks-complete <change-slug>');",
|
|
35
|
+
' return 1;',
|
|
36
|
+
' }',
|
|
37
|
+
'',
|
|
38
|
+
" const raw = execFileSync('openspec', ['list', '--json'], { encoding: 'utf8' });",
|
|
39
|
+
' const { changes } = JSON.parse(raw) as { changes: OpenSpecChangeSummary[] };',
|
|
40
|
+
' const change = changes.find((c) => c.name === slug);',
|
|
41
|
+
'',
|
|
42
|
+
' if (!change) {',
|
|
43
|
+
' console.error(`OpenSpec change "${slug}" not found (openspec list --json)`);',
|
|
44
|
+
' return 1;',
|
|
45
|
+
' }',
|
|
46
|
+
" if (change.status !== 'complete') {",
|
|
47
|
+
' console.error(',
|
|
48
|
+
' `OpenSpec change "${slug}" has incomplete tasks: ${change.completedTasks}/${change.totalTasks}`,',
|
|
49
|
+
' );',
|
|
50
|
+
' return 1;',
|
|
51
|
+
' }',
|
|
52
|
+
'',
|
|
53
|
+
' console.log(`OpenSpec change "${slug}": all ${change.totalTasks} tasks complete`);',
|
|
54
|
+
' return 0;',
|
|
55
|
+
'}',
|
|
56
|
+
'',
|
|
57
|
+
'process.exit(main());',
|
|
58
|
+
],
|
|
59
|
+
});
|
|
60
|
+
new projen_1.JsonFile(project, 'openspec-gates/tsconfig.json', {
|
|
61
|
+
marker: false,
|
|
62
|
+
obj: {
|
|
63
|
+
extends: '../tsconfig.json',
|
|
64
|
+
compilerOptions: {
|
|
65
|
+
noEmit: true,
|
|
66
|
+
rootDir: '.',
|
|
67
|
+
},
|
|
68
|
+
include: ['*.ts'],
|
|
69
|
+
exclude: ['node_modules'],
|
|
70
|
+
},
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
exports.SoftwareFactoryOpenSpecGates = SoftwareFactoryOpenSpecGates;
|
|
75
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiU29mdHdhcmVGYWN0b3J5T3BlblNwZWNHYXRlcy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9Tb2Z0d2FyZUZhY3RvcnlPcGVuU3BlY0dhdGVzLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7OztBQUFBLG1DQUE0QztBQUc1Qzs7O0dBR0c7QUFDSCxNQUFhLDRCQUE0QjtJQUN2QyxZQUFZLE9BQW9CO1FBQzlCLElBQUksaUJBQVEsQ0FBQyxPQUFPLEVBQUUsd0NBQXdDLEVBQUU7WUFDOUQsTUFBTSxFQUFFLElBQUk7WUFDWixLQUFLLEVBQUU7Z0JBQ0wsS0FBSztnQkFDTCw4RUFBOEU7Z0JBQzlFLDZFQUE2RTtnQkFDN0UsNkVBQTZFO2dCQUM3RSwyRUFBMkU7Z0JBQzNFLEtBQUs7Z0JBQ0wsb0RBQW9EO2dCQUNwRCxFQUFFO2dCQUNGLG1DQUFtQztnQkFDbkMsaUJBQWlCO2dCQUNqQiwyQkFBMkI7Z0JBQzNCLHVCQUF1QjtnQkFDdkIsbUJBQW1CO2dCQUNuQixHQUFHO2dCQUNILEVBQUU7Z0JBQ0YsMkJBQTJCO2dCQUMzQiw0RUFBNEU7Z0JBQzVFLHdFQUF3RTtnQkFDeEUsbUVBQW1FO2dCQUNuRSxnQkFBZ0I7Z0JBQ2hCLGlFQUFpRTtnQkFDakUsZUFBZTtnQkFDZixLQUFLO2dCQUNMLEVBQUU7Z0JBQ0YsbUZBQW1GO2dCQUNuRixnRkFBZ0Y7Z0JBQ2hGLHdEQUF3RDtnQkFDeEQsRUFBRTtnQkFDRixrQkFBa0I7Z0JBQ2xCLGtGQUFrRjtnQkFDbEYsZUFBZTtnQkFDZixLQUFLO2dCQUNMLHVDQUF1QztnQkFDdkMsb0JBQW9CO2dCQUNwQix3R0FBd0c7Z0JBQ3hHLFFBQVE7Z0JBQ1IsZUFBZTtnQkFDZixLQUFLO2dCQUNMLEVBQUU7Z0JBQ0Ysc0ZBQXNGO2dCQUN0RixhQUFhO2dCQUNiLEdBQUc7Z0JBQ0gsRUFBRTtnQkFDRix1QkFBdUI7YUFDeEI7U0FDRixDQUFDLENBQUM7UUFFSCxJQUFJLGlCQUFRLENBQUMsT0FBTyxFQUFFLDhCQUE4QixFQUFFO1lBQ3BELE1BQU0sRUFBRSxLQUFLO1lBQ2IsR0FBRyxFQUFFO2dCQUNILE9BQU8sRUFBRSxrQkFBa0I7Z0JBQzNCLGVBQWUsRUFBRTtvQkFDZixNQUFNLEVBQUUsSUFBSTtvQkFDWixPQUFPLEVBQUUsR0FBRztpQkFDYjtnQkFDRCxPQUFPLEVBQUUsQ0FBQyxNQUFNLENBQUM7Z0JBQ2pCLE9BQU8sRUFBRSxDQUFDLGNBQWMsQ0FBQzthQUMxQjtTQUNGLENBQUMsQ0FBQztJQUNMLENBQUM7Q0FDRjtBQWpFRCxvRUFpRUMiLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQgeyBKc29uRmlsZSwgVGV4dEZpbGUgfSBmcm9tICdwcm9qZW4nO1xuaW1wb3J0IHsgTm9kZVByb2plY3QgfSBmcm9tICdwcm9qZW4vbGliL2phdmFzY3JpcHQnO1xuXG4vKipcbiAqIERldGVybWluaXN0aWMgZ2F0ZTogZmFpbHMgdW5sZXNzIGV2ZXJ5IHRhc2sgaW4gdGhlIG5hbWVkIE9wZW5TcGVjIGNoYW5nZSdzXG4gKiB0YXNrcy5tZCBpcyBjaGVja2VkIG9mZi4gRnVsbHkgZ2VuZXJpYyDigJQgbm8gcGVyLXJlcG8gcGFyYW1ldGVyaXphdGlvbi5cbiAqL1xuZXhwb3J0IGNsYXNzIFNvZnR3YXJlRmFjdG9yeU9wZW5TcGVjR2F0ZXMge1xuICBjb25zdHJ1Y3Rvcihwcm9qZWN0OiBOb2RlUHJvamVjdCkge1xuICAgIG5ldyBUZXh0RmlsZShwcm9qZWN0LCAnb3BlbnNwZWMtZ2F0ZXMvY2hlY2stdGFza3MtY29tcGxldGUudHMnLCB7XG4gICAgICBtYXJrZXI6IHRydWUsXG4gICAgICBsaW5lczogW1xuICAgICAgICAnLyoqJyxcbiAgICAgICAgJyAqIEdhdGU6IGZhaWxzIHVubGVzcyBldmVyeSB0YXNrIGluIHRoZSBuYW1lZCBPcGVuU3BlYyBjaGFuZ2VcXCdzIHRhc2tzLm1kIGlzJyxcbiAgICAgICAgJyAqIGNoZWNrZWQgb2ZmLiBgb3BlbnNwZWMgdmFsaWRhdGVgL2BvcGVuc3BlYyBzdGF0dXNgIGRvblxcJ3QgY2hlY2sgdGhpcyAoYW4nLFxuICAgICAgICAnICogaW4tcHJvZ3Jlc3MgY2hhbmdlIGlzIGEgbm9ybWFsLCB2YWxpZCBzdGF0ZSkg4oCUIHRoaXMgaXMgdGhlIGRldGVybWluaXN0aWMnLFxuICAgICAgICAnICogY2hlY2sgdGhlIHByZS1wciBwaXBlbGluZSBuZWVkcyBiZWZvcmUgbGV0dGluZyBpbXBsZW1lbnRhdGlvbiBwcm9jZWVkLicsXG4gICAgICAgICcgKi8nLFxuICAgICAgICBcImltcG9ydCB7IGV4ZWNGaWxlU3luYyB9IGZyb20gJ25vZGU6Y2hpbGRfcHJvY2Vzcyc7XCIsXG4gICAgICAgICcnLFxuICAgICAgICAnaW50ZXJmYWNlIE9wZW5TcGVjQ2hhbmdlU3VtbWFyeSB7JyxcbiAgICAgICAgJyAgbmFtZTogc3RyaW5nOycsXG4gICAgICAgICcgIGNvbXBsZXRlZFRhc2tzOiBudW1iZXI7JyxcbiAgICAgICAgJyAgdG90YWxUYXNrczogbnVtYmVyOycsXG4gICAgICAgICcgIHN0YXR1czogc3RyaW5nOycsXG4gICAgICAgICd9JyxcbiAgICAgICAgJycsXG4gICAgICAgICdmdW5jdGlvbiBtYWluKCk6IG51bWJlciB7JyxcbiAgICAgICAgJyAgLy8gcG5wbVxcJ3MgYC0tIDxhcmdzPmAgZm9yd2FyZGluZyAoYW5kIHByb2plblxcJ3MgcmVjZWl2ZUFyZ3MpIGNhbiBwYXNzIGEnLFxuICAgICAgICAnICAvLyBsaXRlcmFsIFwiLS1cIiB0aHJvdWdoIGFzIGFuIGFyZ3YgdG9rZW4gYWhlYWQgb2YgdGhlIHJlYWwgYXJndW1lbnQuJyxcbiAgICAgICAgXCIgIGNvbnN0IHNsdWcgPSBwcm9jZXNzLmFyZ3Yuc2xpY2UoMikuZmluZCgoYXJnKSA9PiBhcmcgIT09ICctLScpO1wiLFxuICAgICAgICAnICBpZiAoIXNsdWcpIHsnLFxuICAgICAgICBcIiAgICBjb25zb2xlLmVycm9yKCdVc2FnZTogY2hlY2stdGFza3MtY29tcGxldGUgPGNoYW5nZS1zbHVnPicpO1wiLFxuICAgICAgICAnICAgIHJldHVybiAxOycsXG4gICAgICAgICcgIH0nLFxuICAgICAgICAnJyxcbiAgICAgICAgXCIgIGNvbnN0IHJhdyA9IGV4ZWNGaWxlU3luYygnb3BlbnNwZWMnLCBbJ2xpc3QnLCAnLS1qc29uJ10sIHsgZW5jb2Rpbmc6ICd1dGY4JyB9KTtcIixcbiAgICAgICAgJyAgY29uc3QgeyBjaGFuZ2VzIH0gPSBKU09OLnBhcnNlKHJhdykgYXMgeyBjaGFuZ2VzOiBPcGVuU3BlY0NoYW5nZVN1bW1hcnlbXSB9OycsXG4gICAgICAgICcgIGNvbnN0IGNoYW5nZSA9IGNoYW5nZXMuZmluZCgoYykgPT4gYy5uYW1lID09PSBzbHVnKTsnLFxuICAgICAgICAnJyxcbiAgICAgICAgJyAgaWYgKCFjaGFuZ2UpIHsnLFxuICAgICAgICAnICAgIGNvbnNvbGUuZXJyb3IoYE9wZW5TcGVjIGNoYW5nZSBcIiR7c2x1Z31cIiBub3QgZm91bmQgKG9wZW5zcGVjIGxpc3QgLS1qc29uKWApOycsXG4gICAgICAgICcgICAgcmV0dXJuIDE7JyxcbiAgICAgICAgJyAgfScsXG4gICAgICAgIFwiICBpZiAoY2hhbmdlLnN0YXR1cyAhPT0gJ2NvbXBsZXRlJykge1wiLFxuICAgICAgICAnICAgIGNvbnNvbGUuZXJyb3IoJyxcbiAgICAgICAgJyAgICAgIGBPcGVuU3BlYyBjaGFuZ2UgXCIke3NsdWd9XCIgaGFzIGluY29tcGxldGUgdGFza3M6ICR7Y2hhbmdlLmNvbXBsZXRlZFRhc2tzfS8ke2NoYW5nZS50b3RhbFRhc2tzfWAsJyxcbiAgICAgICAgJyAgICApOycsXG4gICAgICAgICcgICAgcmV0dXJuIDE7JyxcbiAgICAgICAgJyAgfScsXG4gICAgICAgICcnLFxuICAgICAgICAnICBjb25zb2xlLmxvZyhgT3BlblNwZWMgY2hhbmdlIFwiJHtzbHVnfVwiOiBhbGwgJHtjaGFuZ2UudG90YWxUYXNrc30gdGFza3MgY29tcGxldGVgKTsnLFxuICAgICAgICAnICByZXR1cm4gMDsnLFxuICAgICAgICAnfScsXG4gICAgICAgICcnLFxuICAgICAgICAncHJvY2Vzcy5leGl0KG1haW4oKSk7JyxcbiAgICAgIF0sXG4gICAgfSk7XG5cbiAgICBuZXcgSnNvbkZpbGUocHJvamVjdCwgJ29wZW5zcGVjLWdhdGVzL3RzY29uZmlnLmpzb24nLCB7XG4gICAgICBtYXJrZXI6IGZhbHNlLFxuICAgICAgb2JqOiB7XG4gICAgICAgIGV4dGVuZHM6ICcuLi90c2NvbmZpZy5qc29uJyxcbiAgICAgICAgY29tcGlsZXJPcHRpb25zOiB7XG4gICAgICAgICAgbm9FbWl0OiB0cnVlLFxuICAgICAgICAgIHJvb3REaXI6ICcuJyxcbiAgICAgICAgfSxcbiAgICAgICAgaW5jbHVkZTogWycqLnRzJ10sXG4gICAgICAgIGV4Y2x1ZGU6IFsnbm9kZV9tb2R1bGVzJ10sXG4gICAgICB9LFxuICAgIH0pO1xuICB9XG59XG4iXX0=
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { NodeProject } from 'projen/lib/javascript';
|
|
2
|
+
import { WorkflowActionVersions } from './WorkflowActionVersions';
|
|
3
|
+
export interface SoftwareFactoryWorkflowsOptions {
|
|
4
|
+
/**
|
|
5
|
+
* The bare repo name, used to derive the `<repoName>-shell[-architecture|-manifest]`
|
|
6
|
+
* model names these workflows dispatch shell commands through.
|
|
7
|
+
*/
|
|
8
|
+
readonly repoName: string;
|
|
9
|
+
/**
|
|
10
|
+
* Overrides for the pinned action versions used by `verify-attestation.yml`.
|
|
11
|
+
*/
|
|
12
|
+
readonly actionVersions?: WorkflowActionVersions;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The 3 swamp workflows the factory's gate stages run (openspec-plan-verify,
|
|
16
|
+
* openspec-tasks-verify, pre-pr-verify), plus the GitHub Action that
|
|
17
|
+
* re-verifies a PR's embedded attestation at merge time.
|
|
18
|
+
*
|
|
19
|
+
* The swamp workflow YAMLs carry a swamp-assigned `id` that must stay stable
|
|
20
|
+
* across synths (swamp uses it as the workflow's identity), so — like the
|
|
21
|
+
* model instances in `SoftwareFactoryModels` — they're `SampleFile`s, written
|
|
22
|
+
* once and then owned by the team. `verify-attestation.yml` has no such
|
|
23
|
+
* identity constraint and is a plain GitHub Actions workflow, so it's built
|
|
24
|
+
* the same managed way `PrDeployWorkflow` is.
|
|
25
|
+
*/
|
|
26
|
+
export declare class SoftwareFactoryWorkflows {
|
|
27
|
+
constructor(project: NodeProject, options: SoftwareFactoryWorkflowsOptions);
|
|
28
|
+
}
|