@guilz-dev/sdlc-gh 0.1.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.
Files changed (176) hide show
  1. package/.github/CODEOWNERS +5 -0
  2. package/.github/ISSUE_TEMPLATE/bug_report.yml +68 -0
  3. package/.github/ISSUE_TEMPLATE/config.yml +1 -0
  4. package/.github/ISSUE_TEMPLATE/feature_request.yml +39 -0
  5. package/.github/ISSUE_TEMPLATE/support.yml +56 -0
  6. package/.github/ISSUE_TEMPLATE/task.yml +89 -0
  7. package/.github/agents/implementer.agent.md +17 -0
  8. package/.github/agents/reviewer.agent.md +18 -0
  9. package/.github/agents/triager.agent.md +13 -0
  10. package/.github/aw/actions-lock.json +9 -0
  11. package/.github/copilot-instructions.md +35 -0
  12. package/.github/hooks/hooks.json +12 -0
  13. package/.github/instructions/core.instructions.md +11 -0
  14. package/.github/instructions/profiles/go.instructions.md +10 -0
  15. package/.github/instructions/profiles/php.instructions.md +11 -0
  16. package/.github/instructions/profiles/python.instructions.md +11 -0
  17. package/.github/instructions/profiles/ruby.instructions.md +11 -0
  18. package/.github/instructions/profiles/typescript.instructions.md +11 -0
  19. package/.github/labels.yml +55 -0
  20. package/.github/pull_request_template.md +33 -0
  21. package/.github/ruleset.example.json +33 -0
  22. package/.github/ruleset.harness-eval.example.json +29 -0
  23. package/.github/skills/quality-loop/SKILL.md +23 -0
  24. package/.github/workflows/agent-retry-orchestrator.yml +161 -0
  25. package/.github/workflows/copilot-setup-steps.yml +64 -0
  26. package/.github/workflows/eval-ci.yml +169 -0
  27. package/.github/workflows/eval-drift.yml +75 -0
  28. package/.github/workflows/gh-aw-dogfood-ci.yml +73 -0
  29. package/.github/workflows/harness-ci.yml +244 -0
  30. package/.github/workflows/harness-sync.yml +28 -0
  31. package/.github/workflows/l1-readiness-check.yml +45 -0
  32. package/.github/workflows/labels-sync.yml +24 -0
  33. package/.github/workflows/nightly-harness-review.lock.yml +1643 -0
  34. package/.github/workflows/nightly-harness-review.md +87 -0
  35. package/.github/workflows/nightly-harness-review.yml +63 -0
  36. package/.github/workflows/npm-publish.yml +49 -0
  37. package/.github/workflows/pr-context-comment.yml +138 -0
  38. package/.github/workflows/product-ci-go.yml +33 -0
  39. package/.github/workflows/product-ci-php.yml +39 -0
  40. package/.github/workflows/product-ci-python.yml +34 -0
  41. package/.github/workflows/product-ci-ruby.yml +35 -0
  42. package/.github/workflows/product-ci-ts.yml +37 -0
  43. package/.github/workflows/task-issue-label-sync.yml +50 -0
  44. package/.github/workflows/weekly-redteam.lock.yml +1571 -0
  45. package/.github/workflows/weekly-redteam.md +76 -0
  46. package/.github/zizmor.yml +11 -0
  47. package/AGENTS.md +54 -0
  48. package/LICENSE +21 -0
  49. package/README.md +366 -0
  50. package/config/stacks.json +55 -0
  51. package/docs/adoption.md +126 -0
  52. package/docs/arch.md +535 -0
  53. package/docs/auth-boundaries.md +16 -0
  54. package/docs/coding-agent-l1.md +152 -0
  55. package/docs/exceptions/README.md +25 -0
  56. package/docs/exceptions/TEMPLATE.md +8 -0
  57. package/docs/failure-taxonomy.md +23 -0
  58. package/docs/gh-aw-dogfood.md +109 -0
  59. package/docs/kpi-baseline.md +9 -0
  60. package/docs/nightly-harness-review.md +94 -0
  61. package/docs/operations.md +108 -0
  62. package/docs/publishing.md +79 -0
  63. package/docs/revert-playbook.md +44 -0
  64. package/docs/shared-config.md +30 -0
  65. package/docs/telemetry-artifacts.md +78 -0
  66. package/docs/telemetry-schema.md +60 -0
  67. package/evals/.score-baseline.json +6 -0
  68. package/evals/e2e-bench/README.md +28 -0
  69. package/evals/e2e-bench/manifest.json +16 -0
  70. package/evals/e2e-bench/tasks/e2e-001.yml +10 -0
  71. package/evals/e2e-bench/tasks/e2e-002.yml +11 -0
  72. package/evals/e2e-bench/tasks/e2e-003.yml +10 -0
  73. package/evals/e2e-bench/tasks/e2e-004.yml +14 -0
  74. package/evals/e2e-bench/tasks/e2e-005.yml +11 -0
  75. package/evals/e2e-bench/tasks/e2e-006.yml +10 -0
  76. package/evals/e2e-bench/tasks/e2e-007.yml +10 -0
  77. package/evals/e2e-bench/tasks/e2e-008.yml +10 -0
  78. package/evals/e2e-bench/tasks/e2e-009.yml +10 -0
  79. package/evals/trajectories/rubric.md +12 -0
  80. package/evals/trajectories/test_harness_conventions.py +271 -0
  81. package/infra/README.md +49 -0
  82. package/infra/langfuse/docker-compose.yml +25 -0
  83. package/infra/otel/collector-config.yml +24 -0
  84. package/infra/samples/gh-aw-dogfood-report.json +44 -0
  85. package/infra/samples/harness-review-routing-plan.json +19 -0
  86. package/infra/samples/harness-review-summary.json +61 -0
  87. package/infra/samples/telemetry-artifact.json +29 -0
  88. package/infra/samples/telemetry-payload.json +19 -0
  89. package/package.json +85 -0
  90. package/prompts/triager-classify.prompt.yml +10 -0
  91. package/sample/go/add.go +5 -0
  92. package/sample/go/add_test.go +9 -0
  93. package/sample/go/go.mod +3 -0
  94. package/sample/php/composer.json +26 -0
  95. package/sample/php/composer.lock +1881 -0
  96. package/sample/php/phpunit.xml +8 -0
  97. package/sample/php/src/Add.php +13 -0
  98. package/sample/php/tests/AddTest.php +16 -0
  99. package/sample/python/requirements-dev.txt +2 -0
  100. package/sample/python/src/__init__.py +0 -0
  101. package/sample/python/src/greet.py +3 -0
  102. package/sample/python/tests/conftest.py +4 -0
  103. package/sample/python/tests/test_greet.py +5 -0
  104. package/sample/ruby/.rubocop.yml +10 -0
  105. package/sample/ruby/Gemfile +6 -0
  106. package/sample/ruby/Gemfile.lock +58 -0
  107. package/sample/ruby/lib/add.rb +9 -0
  108. package/sample/ruby/spec/add_spec.rb +11 -0
  109. package/sample/ts/biome.json +6 -0
  110. package/sample/ts/package-lock.json +1763 -0
  111. package/sample/ts/package.json +15 -0
  112. package/sample/ts/src/add.ts +3 -0
  113. package/sample/ts/tests/add.test.ts +8 -0
  114. package/sample/ts/tsconfig.json +12 -0
  115. package/scripts/aggregate-harness-review.mjs +48 -0
  116. package/scripts/bootstrap-harness.sh +411 -0
  117. package/scripts/check-diff-size.mjs +46 -0
  118. package/scripts/check-e2e-manifest.mjs +35 -0
  119. package/scripts/check-eval-score-drift.mjs +31 -0
  120. package/scripts/check-gh-aw-dogfood-scope.mjs +51 -0
  121. package/scripts/check-issue-spec.mjs +215 -0
  122. package/scripts/check-l1-readiness.mjs +82 -0
  123. package/scripts/check-open-pr-limit.mjs +34 -0
  124. package/scripts/doctor.mjs +177 -0
  125. package/scripts/emit-gh-aw-dogfood-report.mjs +112 -0
  126. package/scripts/emit-telemetry-artifact.mjs +99 -0
  127. package/scripts/fetch-telemetry-artifacts.mjs +176 -0
  128. package/scripts/harness-drift-report.mjs +99 -0
  129. package/scripts/lib/bootstrap-copy.mjs +123 -0
  130. package/scripts/lib/ccsd-contract.mjs +212 -0
  131. package/scripts/lib/diff-size.mjs +103 -0
  132. package/scripts/lib/doctor-local.mjs +179 -0
  133. package/scripts/lib/e2e-manifest.mjs +76 -0
  134. package/scripts/lib/gh-aw-dogfood.mjs +293 -0
  135. package/scripts/lib/github-config.mjs +94 -0
  136. package/scripts/lib/harness-ci-fragments.mjs +98 -0
  137. package/scripts/lib/harness-review-routing.mjs +244 -0
  138. package/scripts/lib/harness-review.mjs +388 -0
  139. package/scripts/lib/issue-form-label-sync.mjs +56 -0
  140. package/scripts/lib/l1-readiness.mjs +258 -0
  141. package/scripts/lib/merge-harness-package.mjs +36 -0
  142. package/scripts/lib/npm-package.mjs +129 -0
  143. package/scripts/lib/setup-wizard.mjs +224 -0
  144. package/scripts/lib/stacks.mjs +138 -0
  145. package/scripts/lib/telemetry-artifact.mjs +253 -0
  146. package/scripts/lib/template-root.mjs +39 -0
  147. package/scripts/merge-harness-package.mjs +14 -0
  148. package/scripts/route-harness-review.mjs +168 -0
  149. package/scripts/run-e2e-bench.mjs +216 -0
  150. package/scripts/sdlc-gh-cli.mjs +91 -0
  151. package/scripts/select-eval-jobs.mjs +41 -0
  152. package/scripts/setup-github.mjs +242 -0
  153. package/scripts/setup-github.sh +4 -0
  154. package/scripts/setup-wizard.mjs +426 -0
  155. package/scripts/test-bootstrap-guidance-scenarios.mjs +94 -0
  156. package/scripts/test-diff-size-scenarios.mjs +88 -0
  157. package/scripts/test-doctor-scenarios.mjs +70 -0
  158. package/scripts/test-e2e-manifest-scenarios.mjs +65 -0
  159. package/scripts/test-gh-aw-dogfood-scenarios.mjs +74 -0
  160. package/scripts/test-harness-review-routing-scenarios.mjs +130 -0
  161. package/scripts/test-harness-review-scenarios.mjs +92 -0
  162. package/scripts/test-hooks-scenarios.mjs +44 -0
  163. package/scripts/test-issue-form-label-sync-scenarios.mjs +48 -0
  164. package/scripts/test-issue-spec-scenarios.mjs +258 -0
  165. package/scripts/test-l1-readiness-scenarios.mjs +204 -0
  166. package/scripts/test-merge-harness-package-scenarios.mjs +53 -0
  167. package/scripts/test-npm-package-scenarios.mjs +31 -0
  168. package/scripts/test-sdlc-gh-cli-scenarios.mjs +54 -0
  169. package/scripts/test-setup-github-scenarios.mjs +103 -0
  170. package/scripts/test-setup-wizard-scenarios.mjs +114 -0
  171. package/scripts/test-telemetry-artifact-scenarios.mjs +69 -0
  172. package/scripts/trim-harness-ci.mjs +18 -0
  173. package/scripts/validate-gh-aw-compile.mjs +64 -0
  174. package/scripts/validate-harness.mjs +199 -0
  175. package/scripts/validate-telemetry.mjs +21 -0
  176. package/scripts/verify-bootstrap-stacks.sh +192 -0
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Emit a machine-readable telemetry artifact for inner-loop workflows.
4
+ * Workflows set TELEMETRY_SOURCE and context env vars, then upload telemetry-artifacts/.
5
+ */
6
+ import {
7
+ buildTelemetryArtifact,
8
+ diffStatsFromGit,
9
+ mapNameToWallFailureType,
10
+ missingRequiredFields,
11
+ wallFailureTypeFromJobResults,
12
+ writeTelemetryArtifact,
13
+ } from "./lib/telemetry-artifact.mjs";
14
+
15
+ function parseJobResults(raw) {
16
+ if (!raw) return {};
17
+ try {
18
+ return JSON.parse(raw);
19
+ } catch {
20
+ return {};
21
+ }
22
+ }
23
+
24
+ function main() {
25
+ const source = process.env.TELEMETRY_SOURCE;
26
+ if (!source) {
27
+ console.error("::error::TELEMETRY_SOURCE is required");
28
+ process.exit(1);
29
+ }
30
+
31
+ const prNumber = Number(process.env.PR_NUMBER || 0) || 0;
32
+ const skipWithoutPr = process.env.TELEMETRY_SKIP_WITHOUT_PR === "1";
33
+ if (skipWithoutPr && !prNumber) {
34
+ console.log("No PR context; skipping telemetry artifact");
35
+ return;
36
+ }
37
+
38
+ const overrides = {
39
+ source,
40
+ TELEMETRY_SOURCE: source,
41
+ GITHUB_REPOSITORY: process.env.GITHUB_REPOSITORY,
42
+ GITHUB_RUN_ID: process.env.GITHUB_RUN_ID,
43
+ GITHUB_RUN_ATTEMPT: process.env.GITHUB_RUN_ATTEMPT,
44
+ GITHUB_WORKFLOW: process.env.GITHUB_WORKFLOW,
45
+ GITHUB_EVENT_NAME: process.env.GITHUB_EVENT_NAME,
46
+ PR_NUMBER: prNumber,
47
+ PR_BODY: process.env.PR_BODY,
48
+ PR_LABELS: process.env.PR_LABELS,
49
+ agent_type: process.env.AGENT_TYPE,
50
+ execution_mode: process.env.EXECUTION_MODE,
51
+ model: process.env.MODEL,
52
+ task_class: process.env.TASK_CLASS,
53
+ autonomy_level: process.env.AUTONOMY_LEVEL,
54
+ tool_calls: process.env.TOOL_CALLS,
55
+ retry_count: process.env.RETRY_COUNT,
56
+ wall_failure_type: process.env.WALL_FAILURE_TYPE,
57
+ cost: process.env.COST,
58
+ elapsed_time: process.env.ELAPSED_TIME,
59
+ changed_files: process.env.CHANGED_FILES,
60
+ diff_loc: process.env.DIFF_LOC,
61
+ final_outcome: process.env.FINAL_OUTCOME,
62
+ review_outcome: process.env.REVIEW_OUTCOME,
63
+ };
64
+
65
+ if (!process.env.CHANGED_FILES && !process.env.DIFF_LOC && process.env.BASE_SHA) {
66
+ const stats = diffStatsFromGit(process.env.BASE_SHA);
67
+ overrides.changed_files = stats.changed_files;
68
+ overrides.diff_loc = stats.diff_loc;
69
+ }
70
+
71
+ if (!process.env.WALL_FAILURE_TYPE && process.env.JOB_RESULTS) {
72
+ const wallType = wallFailureTypeFromJobResults(parseJobResults(process.env.JOB_RESULTS));
73
+ if (wallType) overrides.wall_failure_type = wallType;
74
+ } else if (process.env.WALL_FAILURE_TYPE) {
75
+ const mapped = mapNameToWallFailureType(process.env.WALL_FAILURE_TYPE);
76
+ overrides.wall_failure_type = mapped || process.env.WALL_FAILURE_TYPE;
77
+ }
78
+
79
+ const artifact = buildTelemetryArtifact(overrides);
80
+ const missing = missingRequiredFields(artifact.payload);
81
+ if (missing.length) {
82
+ console.error(`::error::Telemetry payload missing fields: ${missing.join(", ")}`);
83
+ process.exit(1);
84
+ }
85
+
86
+ const strict = process.env.HARNESS_STRICT_TELEMETRY === "1";
87
+ if (strict && artifact.placeholders.length) {
88
+ console.error(
89
+ `::error::Strict telemetry: unresolved placeholders: ${artifact.placeholders.join(", ")}`,
90
+ );
91
+ process.exit(1);
92
+ }
93
+
94
+ const path = writeTelemetryArtifact(artifact);
95
+ console.log(`Wrote telemetry artifact: ${path}`);
96
+ console.log(`::notice::telemetry_placeholders=${artifact.placeholders.join(",") || "none"}`);
97
+ }
98
+
99
+ main();
@@ -0,0 +1,176 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Fetch inner-loop telemetry JSON artifacts from recent workflow runs.
4
+ * Output directory: telemetry-collected/ (default)
5
+ */
6
+ import { execSync } from "node:child_process";
7
+ import { mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
8
+ import { join } from "node:path";
9
+ import { pathToFileURL } from "node:url";
10
+
11
+ export const DEFAULT_COLLECT_DIR = "telemetry-collected";
12
+
13
+ export const TELEMETRY_WORKFLOWS = [
14
+ { workflow: "harness-ci.yml", artifactPrefix: "harness-telemetry-" },
15
+ { workflow: "eval-ci.yml", artifactPrefix: "eval-telemetry-" },
16
+ { workflow: "agent-retry-orchestrator.yml", artifactPrefix: "retry-telemetry-" },
17
+ { workflow: "pr-context-comment.yml", artifactPrefix: "pr-context-telemetry-" },
18
+ ];
19
+
20
+ function ghJson(cmd) {
21
+ const out = execSync(cmd, {
22
+ encoding: "utf8",
23
+ env: process.env,
24
+ stdio: ["pipe", "pipe", "pipe"],
25
+ });
26
+ return JSON.parse(out);
27
+ }
28
+
29
+ function runQuiet(cmd) {
30
+ try {
31
+ execSync(cmd, { encoding: "utf8", env: process.env, stdio: ["pipe", "pipe", "pipe"] });
32
+ return true;
33
+ } catch {
34
+ return false;
35
+ }
36
+ }
37
+
38
+ /**
39
+ * @param {string | Date} createdAt
40
+ * @param {number} windowHours
41
+ * @returns {boolean}
42
+ */
43
+ export function isWithinWindow(createdAt, windowHours) {
44
+ const created = new Date(createdAt).getTime();
45
+ const cutoff = Date.now() - windowHours * 60 * 60 * 1000;
46
+ return created >= cutoff;
47
+ }
48
+
49
+ /**
50
+ * @param {string} dir
51
+ * @returns {Record<string, unknown>[]}
52
+ */
53
+ export function loadTelemetryJsonFiles(dir) {
54
+ const records = [];
55
+ let rootStat;
56
+ try {
57
+ rootStat = statSync(dir);
58
+ } catch {
59
+ return records;
60
+ }
61
+ if (!rootStat.isDirectory()) return records;
62
+
63
+ const walk = (current) => {
64
+ for (const entry of readdirSync(current)) {
65
+ const path = join(current, entry);
66
+ const info = statSync(path);
67
+ if (info.isDirectory()) {
68
+ walk(path);
69
+ continue;
70
+ }
71
+ if (!entry.endsWith(".json")) continue;
72
+ try {
73
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
74
+ if (parsed?.payload) records.push(parsed);
75
+ } catch {
76
+ // skip invalid files
77
+ }
78
+ }
79
+ };
80
+ walk(dir);
81
+ return records;
82
+ }
83
+
84
+ /**
85
+ * @param {{ repo: string, outDir: string, windowHours: number }} options
86
+ * @returns {{ downloadedRuns: number, records: Record<string, unknown>[], manifestPath: string }}
87
+ */
88
+ export function fetchTelemetryArtifacts(options) {
89
+ const repo = options.repo;
90
+ const outDir = options.outDir ?? DEFAULT_COLLECT_DIR;
91
+ const windowHours = Number(options.windowHours ?? 24);
92
+ mkdirSync(outDir, { recursive: true });
93
+
94
+ const manifest = {
95
+ fetched_at: new Date().toISOString(),
96
+ repo,
97
+ window_hours: windowHours,
98
+ runs: [],
99
+ errors: [],
100
+ };
101
+
102
+ let downloadedRuns = 0;
103
+
104
+ for (const { workflow, artifactPrefix } of TELEMETRY_WORKFLOWS) {
105
+ let runs = [];
106
+ try {
107
+ runs = ghJson(
108
+ `gh run list --repo ${repo} --workflow ${workflow} --limit 100 --json databaseId,createdAt,conclusion`,
109
+ );
110
+ } catch (error) {
111
+ manifest.errors.push({ workflow, message: String(error.message || error) });
112
+ continue;
113
+ }
114
+
115
+ for (const run of runs) {
116
+ if (!isWithinWindow(run.createdAt, windowHours)) continue;
117
+ const runId = run.databaseId;
118
+ const artifactName = `${artifactPrefix}${runId}`;
119
+ const targetDir = join(outDir, `${workflow.replace(/\.yml$/, "")}-${runId}`);
120
+ mkdirSync(targetDir, { recursive: true });
121
+
122
+ const ok = runQuiet(
123
+ `gh run download ${runId} --repo ${repo} -n ${artifactName} -D "${targetDir}"`,
124
+ );
125
+ if (!ok) continue;
126
+
127
+ downloadedRuns += 1;
128
+ manifest.runs.push({
129
+ workflow,
130
+ run_id: runId,
131
+ artifact: artifactName,
132
+ conclusion: run.conclusion,
133
+ path: targetDir,
134
+ });
135
+ }
136
+ }
137
+
138
+ const manifestPath = join(outDir, "fetch-manifest.json");
139
+ writeFileSync(manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, "utf8");
140
+ const records = loadTelemetryJsonFiles(outDir);
141
+ return { downloadedRuns, records, manifestPath };
142
+ }
143
+
144
+ function main() {
145
+ const repo = process.env.GITHUB_REPOSITORY;
146
+ if (!repo) {
147
+ console.error("::error::GITHUB_REPOSITORY is required");
148
+ process.exit(1);
149
+ }
150
+ if (!process.env.GH_TOKEN && !process.env.GITHUB_TOKEN) {
151
+ console.error("::error::GH_TOKEN or GITHUB_TOKEN is required");
152
+ process.exit(1);
153
+ }
154
+
155
+ const outDir = process.env.TELEMETRY_COLLECT_DIR || DEFAULT_COLLECT_DIR;
156
+ const windowHours = Number(process.env.WINDOW_HOURS || 24);
157
+ const { downloadedRuns, records, manifestPath } = fetchTelemetryArtifacts({
158
+ repo,
159
+ outDir,
160
+ windowHours,
161
+ });
162
+
163
+ console.log(`Downloaded telemetry from ${downloadedRuns} workflow run(s)`);
164
+ console.log(`Loaded ${records.length} telemetry record(s) from ${outDir}`);
165
+ console.log(`Manifest: ${manifestPath}`);
166
+ if (records.length === 0) {
167
+ console.warn(
168
+ "::warning::No telemetry artifacts found in window — inner-loop emitters may not have run yet",
169
+ );
170
+ }
171
+ console.log(`::notice::telemetry_records=${records.length}`);
172
+ }
173
+
174
+ const isMain =
175
+ process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href;
176
+ if (isMain) main();
@@ -0,0 +1,99 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Manifest of harness paths for sync; optional --against <repo-path> for drift diff.
4
+ */
5
+ import { createHash } from "node:crypto";
6
+ import { appendFileSync, readFileSync, existsSync, readdirSync, statSync } from "node:fs";
7
+ import { join, relative, resolve } from "node:path";
8
+
9
+ const ROOT = process.cwd();
10
+ const againstArg = process.argv.indexOf("--against");
11
+ const AGAINST = againstArg >= 0 ? resolve(process.argv[againstArg + 1] || "") : null;
12
+
13
+ const PATHS = [
14
+ ".github/agents",
15
+ ".github/hooks",
16
+ ".github/instructions",
17
+ ".github/skills",
18
+ ".github/copilot-instructions.md",
19
+ ".github/labels.yml",
20
+ "scripts/validate-harness.mjs",
21
+ "scripts/check-diff-size.mjs",
22
+ "scripts/check-issue-spec.mjs",
23
+ "scripts/lib/ccsd-contract.mjs",
24
+ "docs/operations.md",
25
+ ];
26
+
27
+ function hashFile(p) {
28
+ return createHash("sha256").update(readFileSync(p)).digest("hex").slice(0, 12);
29
+ }
30
+
31
+ function walk(dir, files = []) {
32
+ if (!existsSync(dir)) return files;
33
+ const st = statSync(dir);
34
+ if (st.isFile()) {
35
+ files.push(dir);
36
+ return files;
37
+ }
38
+ for (const e of readdirSync(dir)) walk(join(dir, e), files);
39
+ return files;
40
+ }
41
+
42
+ function buildReport(root) {
43
+ const report = new Map();
44
+ for (const p of PATHS) {
45
+ const full = join(root, p);
46
+ if (!existsSync(full)) {
47
+ report.set(p, { status: "missing" });
48
+ continue;
49
+ }
50
+ for (const f of walk(full)) {
51
+ const rel = relative(root, f);
52
+ report.set(rel, { sha256_12: hashFile(f) });
53
+ }
54
+ }
55
+ return report;
56
+ }
57
+
58
+ const report = buildReport(ROOT);
59
+ const lines = ["## Harness drift manifest", "", "| Path | SHA (12) | Drift |", "|------|----------|-------|"];
60
+
61
+ let againstReport;
62
+ if (AGAINST) {
63
+ if (!existsSync(AGAINST)) {
64
+ console.error(`--against path not found: ${AGAINST}`);
65
+ process.exit(1);
66
+ }
67
+ againstReport = buildReport(AGAINST);
68
+ lines[0] = `## Harness drift vs \`${AGAINST}\``;
69
+ }
70
+
71
+ for (const [path, r] of [...report.entries()].sort((a, b) => a[0].localeCompare(b[0]))) {
72
+ let drift = "";
73
+ if (againstReport) {
74
+ const other = againstReport.get(path);
75
+ if (!other || other.status === "missing") drift = "⚠️ missing downstream";
76
+ else if (other.sha256_12 !== r.sha256_12) drift = "❌ differs";
77
+ else drift = "✅";
78
+ }
79
+ if (r.status) lines.push(`| ${path} | _${r.status}_ | ${drift} |`);
80
+ else lines.push(`| ${path} | \`${r.sha256_12}\` | ${drift} |`);
81
+ }
82
+
83
+ const text = lines.join("\n");
84
+ const out = process.env.GITHUB_STEP_SUMMARY;
85
+ if (out) {
86
+ appendFileSync(out, text + "\n");
87
+ } else {
88
+ console.log(text);
89
+ }
90
+
91
+ if (againstReport) {
92
+ const drifts = [...report.entries()].filter(([path, r]) => {
93
+ const other = againstReport.get(path);
94
+ return r.sha256_12 && (!other || other.sha256_12 !== r.sha256_12);
95
+ });
96
+ if (drifts.length > 0) {
97
+ console.warn(`::warning::${drifts.length} path(s) drift from --against repo`);
98
+ }
99
+ }
@@ -0,0 +1,123 @@
1
+ /**
2
+ * Canonical lists for bootstrap copy and validate-harness consistency checks.
3
+ * Keep in sync with scripts/bootstrap-harness.sh copy loops.
4
+ */
5
+
6
+ export const BOOTSTRAP_LIB_FILES = [
7
+ "bootstrap-copy.mjs",
8
+ "merge-harness-package.mjs",
9
+ "stacks.mjs",
10
+ "harness-ci-fragments.mjs",
11
+ "ccsd-contract.mjs",
12
+ "github-config.mjs",
13
+ "diff-size.mjs",
14
+ "e2e-manifest.mjs",
15
+ "doctor-local.mjs",
16
+ "l1-readiness.mjs",
17
+ "telemetry-artifact.mjs",
18
+ "harness-review.mjs",
19
+ "harness-review-routing.mjs",
20
+ "gh-aw-dogfood.mjs",
21
+ "setup-wizard.mjs",
22
+ "template-root.mjs",
23
+ "npm-package.mjs",
24
+ ];
25
+
26
+ export const BOOTSTRAP_SCRIPT_MJS = [
27
+ "validate-harness.mjs",
28
+ "check-diff-size.mjs",
29
+ "check-issue-spec.mjs",
30
+ "select-eval-jobs.mjs",
31
+ "check-e2e-manifest.mjs",
32
+ "validate-telemetry.mjs",
33
+ "emit-telemetry-artifact.mjs",
34
+ "fetch-telemetry-artifacts.mjs",
35
+ "aggregate-harness-review.mjs",
36
+ "route-harness-review.mjs",
37
+ "check-l1-readiness.mjs",
38
+ "check-gh-aw-dogfood-scope.mjs",
39
+ "validate-gh-aw-compile.mjs",
40
+ "emit-gh-aw-dogfood-report.mjs",
41
+ "check-open-pr-limit.mjs",
42
+ "merge-harness-package.mjs",
43
+ "test-hooks-scenarios.mjs",
44
+ "test-issue-spec-scenarios.mjs",
45
+ "test-diff-size-scenarios.mjs",
46
+ "test-e2e-manifest-scenarios.mjs",
47
+ "test-setup-github-scenarios.mjs",
48
+ "test-doctor-scenarios.mjs",
49
+ "test-telemetry-artifact-scenarios.mjs",
50
+ "test-harness-review-scenarios.mjs",
51
+ "test-harness-review-routing-scenarios.mjs",
52
+ "test-gh-aw-dogfood-scenarios.mjs",
53
+ "test-bootstrap-guidance-scenarios.mjs",
54
+ "test-merge-harness-package-scenarios.mjs",
55
+ "test-l1-readiness-scenarios.mjs",
56
+ "test-setup-wizard-scenarios.mjs",
57
+ "harness-drift-report.mjs",
58
+ "check-eval-score-drift.mjs",
59
+ "run-e2e-bench.mjs",
60
+ "doctor.mjs",
61
+ "setup-github.mjs",
62
+ "setup-wizard.mjs",
63
+ "sdlc-gh-cli.mjs",
64
+ "test-sdlc-gh-cli-scenarios.mjs",
65
+ "test-npm-package-scenarios.mjs",
66
+ "trim-harness-ci.mjs",
67
+ ];
68
+
69
+ export const BOOTSTRAP_SCRIPT_SH = [
70
+ "bootstrap-harness.sh",
71
+ "setup-github.sh",
72
+ "verify-bootstrap-stacks.sh",
73
+ ];
74
+
75
+ export const BOOTSTRAP_DOCS = [
76
+ "operations.md",
77
+ "adoption.md",
78
+ "auth-boundaries.md",
79
+ "failure-taxonomy.md",
80
+ "telemetry-schema.md",
81
+ "telemetry-artifacts.md",
82
+ "nightly-harness-review.md",
83
+ "gh-aw-dogfood.md",
84
+ "shared-config.md",
85
+ "coding-agent-l1.md",
86
+ "kpi-baseline.md",
87
+ "revert-playbook.md",
88
+ "arch.md",
89
+ ];
90
+
91
+ /** script entrypoint -> required lib/*.mjs basenames */
92
+ export const SCRIPT_LIB_IMPORTS = {
93
+ "check-diff-size.mjs": ["diff-size.mjs"],
94
+ "check-e2e-manifest.mjs": ["e2e-manifest.mjs"],
95
+ "doctor.mjs": ["doctor-local.mjs", "github-config.mjs"],
96
+ "setup-github.mjs": ["doctor-local.mjs", "github-config.mjs"],
97
+ "test-diff-size-scenarios.mjs": ["diff-size.mjs"],
98
+ "test-e2e-manifest-scenarios.mjs": ["e2e-manifest.mjs"],
99
+ "test-setup-github-scenarios.mjs": ["doctor-local.mjs", "github-config.mjs"],
100
+ "test-doctor-scenarios.mjs": ["doctor-local.mjs"],
101
+ "setup-wizard.mjs": ["setup-wizard.mjs", "stacks.mjs", "npm-package.mjs"],
102
+ "test-setup-wizard-scenarios.mjs": ["setup-wizard.mjs", "doctor-local.mjs"],
103
+ "sdlc-gh-cli.mjs": ["template-root.mjs"],
104
+ "test-sdlc-gh-cli-scenarios.mjs": ["template-root.mjs"],
105
+ "test-npm-package-scenarios.mjs": ["npm-package.mjs", "stacks.mjs"],
106
+ "validate-harness.mjs": ["npm-package.mjs", "stacks.mjs"],
107
+ "emit-telemetry-artifact.mjs": ["telemetry-artifact.mjs", "ccsd-contract.mjs", "diff-size.mjs"],
108
+ "validate-telemetry.mjs": ["telemetry-artifact.mjs"],
109
+ "test-telemetry-artifact-scenarios.mjs": ["telemetry-artifact.mjs", "harness-ci-fragments.mjs"],
110
+ "fetch-telemetry-artifacts.mjs": [],
111
+ "aggregate-harness-review.mjs": ["harness-review.mjs"],
112
+ "test-harness-review-scenarios.mjs": ["harness-review.mjs"],
113
+ "route-harness-review.mjs": ["harness-review-routing.mjs"],
114
+ "check-l1-readiness.mjs": ["l1-readiness.mjs", "doctor-local.mjs", "setup-wizard.mjs"],
115
+ "test-l1-readiness-scenarios.mjs": ["l1-readiness.mjs"],
116
+ "merge-harness-package.mjs": ["merge-harness-package.mjs"],
117
+ "test-merge-harness-package-scenarios.mjs": ["merge-harness-package.mjs"],
118
+ "test-harness-review-routing-scenarios.mjs": ["harness-review-routing.mjs"],
119
+ "check-gh-aw-dogfood-scope.mjs": ["gh-aw-dogfood.mjs"],
120
+ "validate-gh-aw-compile.mjs": ["gh-aw-dogfood.mjs"],
121
+ "emit-gh-aw-dogfood-report.mjs": ["gh-aw-dogfood.mjs"],
122
+ "test-gh-aw-dogfood-scenarios.mjs": ["gh-aw-dogfood.mjs"],
123
+ };