sinapse-ai 9.4.0 → 9.5.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/.claude/CLAUDE.md +10 -4
- package/.claude/hooks/enforce-architecture-first.py +197 -197
- package/.claude/hooks/enforce-git-push-authority.sh +25 -4
- package/.claude/hooks/mind-clone-governance.py +193 -193
- package/.claude/hooks/read-protection.py +152 -152
- package/.claude/hooks/sql-governance.py +183 -183
- package/.claude/hooks/verify-packages.cjs +83 -0
- package/.claude/hooks/write-path-validation.py +195 -195
- package/.claude/rules/hook-governance.md +1 -0
- package/.claude/rules/mandatory-delegation.md +24 -0
- package/.claude/rules/project-intelligence.md +63 -0
- package/.claude/rules/response-format.md +4 -0
- package/.claude/rules/safe-collaboration.md +4 -2
- package/.claude/rules/security-data-protection.md +18 -0
- package/.claude/rules/squad-awareness.md +93 -67
- package/.claude/rules/token-economy.md +148 -0
- package/.codex/agents/analyst.md +90 -0
- package/.codex/agents/architect.md +78 -0
- package/.codex/agents/data-engineer.md +38 -0
- package/.codex/agents/developer.md +97 -0
- package/.codex/agents/devops.md +121 -0
- package/.codex/agents/product-lead.md +27 -0
- package/.codex/agents/project-lead.md +28 -0
- package/.codex/agents/quality-gate.md +89 -0
- package/.codex/agents/sprint-lead.md +28 -0
- package/.codex/agents/squad-creator.md +58 -0
- package/.codex/agents/ux-design-expert.md +28 -0
- package/.sinapse-ai/core/code-intel/registry-syncer.js +56 -3
- package/.sinapse-ai/core/doctor/checks/agent-memory.js +5 -1
- package/.sinapse-ai/core/doctor/checks/claude-md.js +4 -1
- package/.sinapse-ai/core/doctor/checks/code-intel.js +5 -1
- package/.sinapse-ai/core/doctor/checks/commands-count.js +4 -1
- package/.sinapse-ai/core/doctor/checks/constitution-consistency.js +4 -1
- package/.sinapse-ai/core/doctor/checks/core-config.js +4 -1
- package/.sinapse-ai/core/doctor/checks/entity-registry.js +6 -1
- package/.sinapse-ai/core/doctor/checks/git-hooks.js +5 -1
- package/.sinapse-ai/core/doctor/checks/graph-dashboard.js +4 -1
- package/.sinapse-ai/core/doctor/checks/hooks-claude-count.js +5 -1
- package/.sinapse-ai/core/doctor/checks/ide-sync.js +4 -1
- package/.sinapse-ai/core/doctor/checks/node-version.js +4 -1
- package/.sinapse-ai/core/doctor/checks/npm-packages.js +4 -1
- package/.sinapse-ai/core/doctor/checks/rules-files.js +4 -1
- package/.sinapse-ai/core/doctor/checks/settings-json.js +4 -1
- package/.sinapse-ai/core/doctor/checks/skills-count.js +4 -1
- package/.sinapse-ai/core/doctor/index.js +157 -50
- package/.sinapse-ai/core/ids/registry-updater.js +6 -1
- package/.sinapse-ai/core/logger/index.js +319 -0
- package/.sinapse-ai/core/orchestration/terminal-spawner.js +2 -2
- package/.sinapse-ai/core/telemetry/index.js +247 -0
- package/.sinapse-ai/data/entity-registry.yaml +1384 -944
- package/.sinapse-ai/development/agents/architect.md +5 -0
- package/.sinapse-ai/development/agents/data-engineer.md +38 -0
- package/.sinapse-ai/development/agents/developer.md +28 -0
- package/.sinapse-ai/development/agents/devops.md +4 -0
- package/.sinapse-ai/development/agents/product-lead.md +27 -0
- package/.sinapse-ai/development/agents/project-lead.md +28 -0
- package/.sinapse-ai/development/agents/quality-gate.md +4 -0
- package/.sinapse-ai/development/agents/sprint-lead/MEMORY.md +8 -0
- package/.sinapse-ai/development/agents/sprint-lead.md +28 -0
- package/.sinapse-ai/development/agents/squad-creator.md +58 -0
- package/.sinapse-ai/development/agents/ux-design-expert.md +28 -0
- package/.sinapse-ai/development/knowledge-base/agent-communication-protocol.md +127 -0
- package/.sinapse-ai/development/knowledge-base/database-scaling-patterns.md +374 -0
- package/.sinapse-ai/development/knowledge-base/environment-deployment-patterns.md +353 -0
- package/.sinapse-ai/development/knowledge-base/gotchas-patterns.md +224 -0
- package/.sinapse-ai/development/knowledge-base/infrastructure-decision-framework.md +221 -0
- package/.sinapse-ai/development/knowledge-base/security-pre-deploy-checklist.md +410 -0
- package/.sinapse-ai/development/knowledge-base/software-architecture-patterns.md +299 -0
- package/.sinapse-ai/development/knowledge-base/token-economy-guide.md +198 -0
- package/.sinapse-ai/development/scripts/populate-entity-registry.js +5 -1
- package/.sinapse-ai/development/skills/captcha-handler.md +82 -0
- package/.sinapse-ai/development/skills/chrome-brain.md +81 -0
- package/.sinapse-ai/development/skills/deploy-readiness.md +93 -0
- package/.sinapse-ai/development/skills/model-router.md +92 -0
- package/.sinapse-ai/development/skills/sinapse-methodology.md +175 -0
- package/.sinapse-ai/development/skills/story-fast-track.md +71 -0
- package/.sinapse-ai/development/tasks/dev-develop-story.md +10 -0
- package/.sinapse-ai/development/tasks/environment-promotion-pipeline.md +582 -0
- package/.sinapse-ai/development/tasks/generate-agent-handoff.md +223 -0
- package/.sinapse-ai/development/tasks/infrastructure-assessment.md +432 -0
- package/.sinapse-ai/development/tasks/load-testing-setup.md +611 -0
- package/.sinapse-ai/development/tasks/observability-blueprint.md +562 -0
- package/.sinapse-ai/development/templates/legal/breach-notification-tmpl.md +113 -0
- package/.sinapse-ai/development/templates/legal/privacy-policy-tmpl.md +93 -0
- package/.sinapse-ai/development/templates/legal/terms-of-service-tmpl.md +85 -0
- package/.sinapse-ai/development/templates/service-template/README.md.hbs +159 -159
- package/.sinapse-ai/development/templates/service-template/__tests__/index.test.ts.hbs +238 -238
- package/.sinapse-ai/development/templates/service-template/client.ts.hbs +404 -404
- package/.sinapse-ai/development/templates/service-template/errors.ts.hbs +183 -183
- package/.sinapse-ai/development/templates/service-template/index.ts.hbs +121 -121
- package/.sinapse-ai/development/templates/service-template/package.json.hbs +88 -88
- package/.sinapse-ai/development/templates/service-template/types.ts.hbs +146 -146
- package/.sinapse-ai/development/templates/squad-template/LICENSE +22 -22
- package/.sinapse-ai/development/workflows/story-development-cycle.yaml +40 -1
- package/.sinapse-ai/hooks/ids-post-commit.js +22 -0
- package/.sinapse-ai/infrastructure/contracts/compatibility/README.md +42 -0
- package/.sinapse-ai/infrastructure/contracts/compatibility/sinapse-current.yaml +35 -0
- package/.sinapse-ai/infrastructure/scripts/llm-routing/templates/claude-free-tracked.cmd +127 -127
- package/.sinapse-ai/infrastructure/scripts/llm-routing/templates/deepseek-proxy.cmd +71 -71
- package/.sinapse-ai/infrastructure/scripts/llm-routing/templates/deepseek-usage.cmd +51 -51
- package/.sinapse-ai/infrastructure/scripts/pr-review-ai.js +16 -13
- package/.sinapse-ai/infrastructure/scripts/setup-project-infra.js +128 -0
- package/.sinapse-ai/infrastructure/scripts/test-discovery.js +8 -3
- package/.sinapse-ai/infrastructure/scripts/validate-manifest-parity.js +380 -0
- package/.sinapse-ai/infrastructure/scripts/validate-parity.js +76 -25
- package/.sinapse-ai/infrastructure/templates/coderabbit.yaml.template +280 -280
- package/.sinapse-ai/infrastructure/templates/config/env.example +16 -0
- package/.sinapse-ai/infrastructure/templates/config/gitignore-additions.tmpl +59 -0
- package/.sinapse-ai/infrastructure/templates/github/CODEOWNERS.template +12 -0
- package/.sinapse-ai/infrastructure/templates/github/PULL_REQUEST_TEMPLATE.md +29 -0
- package/.sinapse-ai/infrastructure/templates/github/ci-template.yml +77 -0
- package/.sinapse-ai/infrastructure/templates/github/issue-templates/bug_report.md +34 -0
- package/.sinapse-ai/infrastructure/templates/github/issue-templates/feature_request.md +19 -0
- package/.sinapse-ai/infrastructure/templates/github-workflows/ci.yml.template +170 -170
- package/.sinapse-ai/infrastructure/templates/github-workflows/pr-automation.yml.template +331 -331
- package/.sinapse-ai/infrastructure/templates/github-workflows/release.yml.template +197 -197
- package/.sinapse-ai/infrastructure/templates/gitignore/gitignore-brownfield-merge.tmpl +19 -19
- package/.sinapse-ai/infrastructure/templates/gitignore/gitignore-node.tmpl +86 -86
- package/.sinapse-ai/infrastructure/templates/gitignore/gitignore-python.tmpl +146 -146
- package/.sinapse-ai/infrastructure/templates/gitignore/gitignore-sinapse-base.tmpl +64 -64
- package/.sinapse-ai/infrastructure/templates/sinapse-sync.yaml.template +183 -183
- package/.sinapse-ai/install-manifest.yaml +275 -140
- package/.sinapse-ai/local-config.yaml.template +65 -65
- package/.sinapse-ai/monitor/hooks/lib/__init__.py +2 -2
- package/.sinapse-ai/monitor/hooks/lib/enrich.py +59 -59
- package/.sinapse-ai/monitor/hooks/lib/send_event.py +48 -48
- package/.sinapse-ai/monitor/hooks/notification.py +30 -30
- package/.sinapse-ai/monitor/hooks/post_tool_use.py +46 -46
- package/.sinapse-ai/monitor/hooks/pre_compact.py +30 -30
- package/.sinapse-ai/monitor/hooks/pre_tool_use.py +41 -41
- package/.sinapse-ai/monitor/hooks/stop.py +30 -30
- package/.sinapse-ai/monitor/hooks/subagent_stop.py +30 -30
- package/.sinapse-ai/monitor/hooks/user_prompt_submit.py +39 -39
- package/.sinapse-ai/product/templates/adr.hbs +126 -126
- package/.sinapse-ai/product/templates/dbdr.hbs +242 -242
- package/.sinapse-ai/product/templates/epic.hbs +213 -213
- package/.sinapse-ai/product/templates/pmdr.hbs +187 -187
- package/.sinapse-ai/product/templates/prd-v2.0.hbs +217 -217
- package/.sinapse-ai/product/templates/prd.hbs +202 -202
- package/.sinapse-ai/product/templates/story-tmpl.yaml +59 -0
- package/.sinapse-ai/product/templates/story.hbs +264 -264
- package/.sinapse-ai/product/templates/task.hbs +171 -171
- package/.sinapse-ai/product/templates/tmpl-comment-on-examples.sql +159 -159
- package/.sinapse-ai/product/templates/tmpl-migration-script.sql +92 -92
- package/.sinapse-ai/product/templates/tmpl-rls-granular-policies.sql +105 -105
- package/.sinapse-ai/product/templates/tmpl-rls-kiss-policy.sql +11 -11
- package/.sinapse-ai/product/templates/tmpl-rls-roles.sql +136 -136
- package/.sinapse-ai/product/templates/tmpl-rls-simple.sql +78 -78
- package/.sinapse-ai/product/templates/tmpl-rls-tenant.sql +153 -153
- package/.sinapse-ai/product/templates/tmpl-rollback-script.sql +78 -78
- package/.sinapse-ai/product/templates/tmpl-seed-data.sql +141 -141
- package/.sinapse-ai/product/templates/tmpl-smoke-test.sql +17 -17
- package/.sinapse-ai/product/templates/tmpl-staging-copy-merge.sql +140 -140
- package/.sinapse-ai/product/templates/tmpl-stored-proc.sql +141 -141
- package/.sinapse-ai/product/templates/tmpl-trigger.sql +153 -153
- package/.sinapse-ai/product/templates/tmpl-view-materialized.sql +134 -134
- package/.sinapse-ai/product/templates/tmpl-view.sql +178 -178
- package/.sinapse-ai/scripts/diagnostics/health-dashboard/package-lock.json +427 -355
- package/LICENSE +34 -34
- package/README.en.md +167 -20
- package/README.md +190 -22
- package/bin/cli.js +510 -196
- package/bin/postinstall.js +564 -0
- package/bin/sinapse-cli +283 -283
- package/bin/sinapse-graph.js +9 -0
- package/bin/sinapse-init.js +36 -4
- package/bin/sinapse-minimal.js +20 -9
- package/bin/sinapse.js +202 -122
- package/bin/utils/deprecation-warning.js +46 -0
- package/bin/utils/pre-push-safety.js +14 -0
- package/docs/TELEMETRY.md +131 -0
- package/docs/chrome-brain-upgrade-plan.md +624 -0
- package/docs/framework/orqx-plan.md +1 -1
- package/docs/installation/chrome-brain.md +17 -7
- package/docs/mega-upgrade-orchestration-plan.md +71 -0
- package/docs/pt/contributing.md +20 -0
- package/docs/research-synthesis-for-upgrade.md +511 -0
- package/docs/security-audit-report.md +306 -0
- package/package.json +20 -8
- package/packages/installer/src/config/configure-environment.js +19 -44
- package/packages/installer/src/detection/detect-project-type.js +181 -63
- package/packages/installer/src/installer/manifest-signature.js +32 -17
- package/packages/installer/src/wizard/i18n.js +12 -0
- package/packages/installer/src/wizard/ide-config-generator.js +8 -39
- package/packages/installer/src/wizard/index.js +119 -14
- package/packages/installer/src/wizard/questions.js +2 -3
- package/packages/installer/tests/integration/environment-configuration.test.js +7 -5
- package/packages/installer/tests/unit/detection/detect-project-type.test.js +138 -1
- package/packages/installer/tests/unit/doctor/doctor-orchestrator.test.js +3 -3
- package/packages/sinapse-install/bin/edmcp.js +0 -0
- package/packages/sinapse-install/bin/sinapse-install.js +0 -0
- package/packages/sinapse-pro-cli/bin/sinapse-pro.js +0 -0
- package/scripts/check-markdown-links.py +353 -353
- package/scripts/coverage-report-summary.js +169 -0
- package/scripts/generate-install-manifest.js +6 -2
- package/scripts/release-readiness.js +169 -0
- package/scripts/test-install-matrix-local.sh +153 -0
- package/scripts/validate-install-docs.js +394 -0
- package/scripts/validate-no-external-refs.js +376 -0
- package/scripts/validate-squad-orqx.js +302 -0
- package/scripts/validate-story-meta.js +263 -0
- package/squads/claude-code-mastery/CHANGELOG.md +1 -1
- package/squads/claude-code-mastery/README.md +2 -2
- package/squads/claude-code-mastery/squad.yaml +1 -1
- package/squads/squad-artdir/README.md +90 -0
- package/squads/squad-artdir/agents/accessibility-guardian.md +184 -0
- package/squads/squad-artdir/agents/artdir-orqx.md +145 -0
- package/squads/squad-artdir/agents/color-psychologist.md +166 -0
- package/squads/squad-artdir/agents/cro-persuasion.md +161 -0
- package/squads/squad-artdir/agents/design-system-architect.md +100 -0
- package/squads/squad-artdir/agents/ia-architect.md +169 -0
- package/squads/squad-artdir/agents/interaction-designer.md +162 -0
- package/squads/squad-artdir/agents/layout-engineer.md +163 -0
- package/squads/squad-artdir/agents/motion-architect.md +185 -0
- package/squads/squad-artdir/agents/platform-aesthetic-director.md +84 -0
- package/squads/squad-artdir/agents/premium-packaging-strategist.md +107 -0
- package/squads/squad-artdir/agents/product-surface-director.md +86 -0
- package/squads/squad-artdir/agents/type-systemist.md +138 -0
- package/squads/squad-artdir/agents/visual-strategist.md +127 -0
- package/squads/squad-artdir/checklists/seven-pillars-validation-checklist.md +172 -0
- package/squads/squad-artdir/knowledge-base/case-nyo-ia-reference.md +289 -0
- package/squads/squad-artdir/knowledge-base/deliverables-templates.md +457 -0
- package/squads/squad-artdir/knowledge-base/motion-technique-catalog.md +247 -0
- package/squads/squad-artdir/knowledge-base/premium-packaging-principles.md +133 -0
- package/squads/squad-artdir/knowledge-base/psychological-toolkit.md +229 -0
- package/squads/squad-artdir/knowledge-base/saas-art-direction-canon.md +242 -0
- package/squads/squad-artdir/knowledge-base/seven-pillars-framework.md +289 -0
- package/squads/squad-artdir/knowledge-base/ten-pillars-framework.md +221 -0
- package/squads/squad-artdir/package.json +20 -0
- package/squads/squad-artdir/squad.yaml +271 -0
- package/squads/squad-artdir/tasks/audit-conversion.md +97 -0
- package/squads/squad-artdir/tasks/audit-drift-multi-surface.md +55 -0
- package/squads/squad-artdir/tasks/consult-saas-canon.md +54 -0
- package/squads/squad-artdir/tasks/create-art-direction-brief.md +110 -0
- package/squads/squad-artdir/tasks/create-premium-packaging-brief.md +61 -0
- package/squads/squad-artdir/tasks/create-wireflow.md +84 -0
- package/squads/squad-artdir/tasks/design-color-system.md +81 -0
- package/squads/squad-artdir/tasks/design-product-surface.md +60 -0
- package/squads/squad-artdir/tasks/design-token-system.md +58 -0
- package/squads/squad-artdir/tasks/diagnose-visual-language.md +92 -0
- package/squads/squad-artdir/tasks/first-5-minutes-choreography.md +65 -0
- package/squads/squad-artdir/tasks/specify-motion-system.md +84 -0
- package/squads/squad-artdir/tasks/validate-against-pillars.md +143 -0
- package/squads/squad-artdir/templates/art-direction-brief-template.md +215 -0
- package/squads/squad-artdir/workflows/conversion-audit-cycle.yaml +78 -0
- package/squads/squad-artdir/workflows/full-art-direction-cycle.yaml +98 -0
- package/squads/squad-artdir/workflows/saas-platform-art-direction-cycle.yaml +174 -0
- package/squads/squad-brand/knowledge-base/ai-visual-generation-canon.md +234 -0
- package/squads/squad-brand/squad.yaml +20 -6
- package/squads/squad-claude/knowledge-base/context-window-optimization.md +1 -1
- package/squads/squad-claude/knowledge-base/swarm-orchestration-patterns.md +2 -2
- package/squads/squad-content/knowledge-base/ai-native-content-loop.md +220 -0
- package/squads/squad-content/knowledge-base/signal-intelligence-v2.md +234 -0
- package/squads/squad-content/knowledge-base/task-ownership-map.md +235 -0
- package/squads/squad-content/squad.yaml +187 -27
- package/squads/squad-copy/knowledge-base/ai-copy-human-loop-canon.md +235 -0
- package/squads/squad-copy/squad.yaml +19 -4
- package/squads/squad-design/knowledge-base/cross-surface-token-canon.md +209 -0
- package/squads/squad-design/squad.yaml +19 -4
- package/.sinapse-ai/core/registry/service-registry.json +0 -6346
- package/.sinapse-ai/data/registry-update-log.jsonl +0 -1323
- package/.sinapse-ai/manifests/agents.csv +0 -29
- package/.sinapse-ai/manifests/tasks.csv +0 -204
- package/.sinapse-ai/manifests/workers.csv +0 -196
- package/squads/squad-growth/tasks/calculate-sample-size.md +0 -121
- package/squads/squad-paidmedia/tasks/calculate-sample-size.md +0 -57
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* coverage-report-summary — Story 10.25
|
|
4
|
+
*
|
|
5
|
+
* Reads coverage/coverage-summary.json (Jest `json-summary` reporter)
|
|
6
|
+
* and prints a Current vs Floor comparison table to stdout. When run
|
|
7
|
+
* inside GitHub Actions (GITHUB_STEP_SUMMARY env var set), also writes
|
|
8
|
+
* a Markdown table to the job summary file so reviewers see coverage
|
|
9
|
+
* in the PR Checks tab.
|
|
10
|
+
*
|
|
11
|
+
* Always exits 0 — coverage gating is enforced by Jest's
|
|
12
|
+
* coverageThreshold in jest.config.js, not by this script.
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* node scripts/coverage-report-summary.js
|
|
16
|
+
* node scripts/coverage-report-summary.js --json (JSON output)
|
|
17
|
+
*
|
|
18
|
+
* Reads ratchet floors from jest.config.js coverageThreshold.global.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
'use strict';
|
|
22
|
+
|
|
23
|
+
const fs = require('fs');
|
|
24
|
+
const path = require('path');
|
|
25
|
+
|
|
26
|
+
const PROJECT_ROOT = path.resolve(__dirname, '..');
|
|
27
|
+
const SUMMARY_PATH = path.join(PROJECT_ROOT, 'coverage', 'coverage-summary.json');
|
|
28
|
+
const JEST_CONFIG_PATH = path.join(PROJECT_ROOT, 'jest.config.js');
|
|
29
|
+
|
|
30
|
+
function readCoverageSummary(summaryPath = SUMMARY_PATH) {
|
|
31
|
+
if (!fs.existsSync(summaryPath)) {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
try {
|
|
35
|
+
const raw = fs.readFileSync(summaryPath, 'utf8');
|
|
36
|
+
return JSON.parse(raw);
|
|
37
|
+
} catch {
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function readRatchetFloors(jestConfigPath = JEST_CONFIG_PATH) {
|
|
43
|
+
// We don't require() jest.config.js because Jest itself loads it with
|
|
44
|
+
// its own resolver. Instead we parse it as text and extract the global
|
|
45
|
+
// floors. This avoids running jest at script load time.
|
|
46
|
+
if (!fs.existsSync(jestConfigPath)) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
const raw = fs.readFileSync(jestConfigPath, 'utf8');
|
|
50
|
+
const globalBlockMatch = raw.match(/global:\s*\{([^}]+)\}/);
|
|
51
|
+
if (!globalBlockMatch) return null;
|
|
52
|
+
const block = globalBlockMatch[1];
|
|
53
|
+
const floors = {};
|
|
54
|
+
for (const key of ['statements', 'branches', 'lines', 'functions']) {
|
|
55
|
+
const re = new RegExp(`${key}\\s*:\\s*(\\d+)`);
|
|
56
|
+
const m = block.match(re);
|
|
57
|
+
if (m) floors[key] = Number(m[1]);
|
|
58
|
+
}
|
|
59
|
+
return floors;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function buildRows(summary, floors) {
|
|
63
|
+
if (!summary || !summary.total || !floors) return [];
|
|
64
|
+
const total = summary.total;
|
|
65
|
+
const metrics = ['statements', 'branches', 'lines', 'functions'];
|
|
66
|
+
return metrics.map((m) => {
|
|
67
|
+
const actual = total[m] && typeof total[m].pct === 'number' ? total[m].pct : null;
|
|
68
|
+
const floor = typeof floors[m] === 'number' ? floors[m] : null;
|
|
69
|
+
let status = '?';
|
|
70
|
+
if (actual !== null && floor !== null) {
|
|
71
|
+
status = actual >= floor ? 'PASS' : 'FAIL';
|
|
72
|
+
}
|
|
73
|
+
return { metric: m, floor, actual, status };
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function formatAsciiTable(rows) {
|
|
78
|
+
const header = ['Metric', 'Floor', 'Actual', 'Status'];
|
|
79
|
+
const widths = header.map((h) => h.length);
|
|
80
|
+
for (const r of rows) {
|
|
81
|
+
widths[0] = Math.max(widths[0], r.metric.length);
|
|
82
|
+
widths[1] = Math.max(widths[1], String(r.floor).length + 1); // +1 for %
|
|
83
|
+
widths[2] = Math.max(widths[2], String(r.actual).length + 1);
|
|
84
|
+
widths[3] = Math.max(widths[3], r.status.length);
|
|
85
|
+
}
|
|
86
|
+
const sep = '+' + widths.map((w) => '-'.repeat(w + 2)).join('+') + '+';
|
|
87
|
+
const fmt = (cells) =>
|
|
88
|
+
'| '
|
|
89
|
+
+ cells.map((c, i) => String(c).padEnd(widths[i])).join(' | ')
|
|
90
|
+
+ ' |';
|
|
91
|
+
const lines = [sep, fmt(header), sep];
|
|
92
|
+
for (const r of rows) {
|
|
93
|
+
lines.push(
|
|
94
|
+
fmt([
|
|
95
|
+
r.metric,
|
|
96
|
+
r.floor === null ? '?' : `${r.floor}%`,
|
|
97
|
+
r.actual === null ? '?' : `${r.actual}%`,
|
|
98
|
+
r.status,
|
|
99
|
+
]),
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
lines.push(sep);
|
|
103
|
+
return lines.join('\n');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function formatMarkdownTable(rows) {
|
|
107
|
+
const lines = [];
|
|
108
|
+
lines.push('### Coverage Report (Story 10.19 ratchet)');
|
|
109
|
+
lines.push('');
|
|
110
|
+
lines.push('| Metric | Floor | Actual | Status |');
|
|
111
|
+
lines.push('|--------|-------|--------|--------|');
|
|
112
|
+
for (const r of rows) {
|
|
113
|
+
const floorStr = r.floor === null ? '?' : `${r.floor}%`;
|
|
114
|
+
const actualStr = r.actual === null ? '?' : `${r.actual}%`;
|
|
115
|
+
const statusEmoji = r.status === 'PASS' ? '✅' : r.status === 'FAIL' ? '❌' : '❓';
|
|
116
|
+
lines.push(`| ${r.metric} | ${floorStr} | ${actualStr} | ${statusEmoji} |`);
|
|
117
|
+
}
|
|
118
|
+
return lines.join('\n');
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function appendToGithubSummary(markdown) {
|
|
122
|
+
const target = process.env.GITHUB_STEP_SUMMARY;
|
|
123
|
+
if (!target) return false;
|
|
124
|
+
try {
|
|
125
|
+
fs.appendFileSync(target, markdown + '\n\n', 'utf8');
|
|
126
|
+
return true;
|
|
127
|
+
} catch {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function main(argv = process.argv.slice(2)) {
|
|
133
|
+
const args = new Set(argv);
|
|
134
|
+
const summary = readCoverageSummary();
|
|
135
|
+
if (!summary) {
|
|
136
|
+
console.log('=== coverage-report-summary ===');
|
|
137
|
+
console.log('No coverage/coverage-summary.json found — run `npm run test:coverage` first.');
|
|
138
|
+
return 0;
|
|
139
|
+
}
|
|
140
|
+
const floors = readRatchetFloors();
|
|
141
|
+
if (!floors) {
|
|
142
|
+
console.log('=== coverage-report-summary ===');
|
|
143
|
+
console.log('Could not parse jest.config.js coverageThreshold.global — skipping comparison.');
|
|
144
|
+
return 0;
|
|
145
|
+
}
|
|
146
|
+
const rows = buildRows(summary, floors);
|
|
147
|
+
if (args.has('--json')) {
|
|
148
|
+
console.log(JSON.stringify({ rows, floors }, null, 2));
|
|
149
|
+
return 0;
|
|
150
|
+
}
|
|
151
|
+
console.log('=== Coverage Report (Story 10.19 ratchet) ===');
|
|
152
|
+
console.log(formatAsciiTable(rows));
|
|
153
|
+
appendToGithubSummary(formatMarkdownTable(rows));
|
|
154
|
+
return 0;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (require.main === module) {
|
|
158
|
+
process.exitCode = main();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
module.exports = {
|
|
162
|
+
readCoverageSummary,
|
|
163
|
+
readRatchetFloors,
|
|
164
|
+
buildRows,
|
|
165
|
+
formatAsciiTable,
|
|
166
|
+
formatMarkdownTable,
|
|
167
|
+
appendToGithubSummary,
|
|
168
|
+
main,
|
|
169
|
+
};
|
|
@@ -274,9 +274,13 @@ async function generateManifest() {
|
|
|
274
274
|
fileEntries.sort((a, b) => a.path.localeCompare(b.path));
|
|
275
275
|
|
|
276
276
|
// Build manifest object
|
|
277
|
+
// Story 10.27 — `generated_at` removed from the manifest body to keep
|
|
278
|
+
// the file deterministic across regenerations. Without removal, every
|
|
279
|
+
// post-commit hook regen produced a new timestamp -> new file content
|
|
280
|
+
// -> recurring "M install-manifest.yaml" churn in git status. Consumers
|
|
281
|
+
// (post-install-validator) treat the field as optional metadata.
|
|
277
282
|
const manifest = {
|
|
278
283
|
version: version,
|
|
279
|
-
generated_at: new Date().toISOString(),
|
|
280
284
|
generator: 'scripts/generate-install-manifest.js',
|
|
281
285
|
file_count: fileEntries.length,
|
|
282
286
|
files: fileEntries,
|
|
@@ -318,7 +322,7 @@ async function writeManifest(manifest) {
|
|
|
318
322
|
console.log(`\nManifest written to: ${manifestPath}`);
|
|
319
323
|
console.log(` Version: ${manifest.version}`);
|
|
320
324
|
console.log(` Files: ${manifest.file_count}`);
|
|
321
|
-
console.log(` Generated: ${
|
|
325
|
+
console.log(` Generated: ${new Date().toISOString()}`);
|
|
322
326
|
|
|
323
327
|
// Also compute and display manifest hash for integrity verification
|
|
324
328
|
const manifestHash = hashString(yamlContent);
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* release-readiness — Story 10.29
|
|
4
|
+
*
|
|
5
|
+
* Aggregates every validator and quality gate built throughout Epic 10.0
|
|
6
|
+
* into one release-readiness report. Run before opening a release PR or
|
|
7
|
+
* tagging v10.0.0 to confirm the framework is shippable.
|
|
8
|
+
*
|
|
9
|
+
* Validators executed (in order):
|
|
10
|
+
* 1. lint — npm run lint
|
|
11
|
+
* 2. typecheck — npm run typecheck
|
|
12
|
+
* 3. agents — npm run validate:agents
|
|
13
|
+
* 4. squad-orqx — npm run validate:squad-orqx (Story 10.28)
|
|
14
|
+
* 5. story-meta — npm run validate:story-meta (Story 10.19)
|
|
15
|
+
* 6. no-external-refs — npm run validate:no-external-refs (Story 10.17)
|
|
16
|
+
* 7. parity — npm run validate:parity (Story 10.18)
|
|
17
|
+
* 8. manifest — npm run validate:manifest
|
|
18
|
+
* 9. install-docs — npm run validate:docs
|
|
19
|
+
*
|
|
20
|
+
* Each gate runs in its own subprocess. Failures are reported but do NOT
|
|
21
|
+
* short-circuit — the script always runs every gate so the operator sees
|
|
22
|
+
* the full picture in one pass.
|
|
23
|
+
*
|
|
24
|
+
* Exit codes:
|
|
25
|
+
* 0 every gate passed
|
|
26
|
+
* 1 one or more gates failed (per-gate detail in the summary table)
|
|
27
|
+
*
|
|
28
|
+
* Usage:
|
|
29
|
+
* node scripts/release-readiness.js
|
|
30
|
+
* node scripts/release-readiness.js --json
|
|
31
|
+
* node scripts/release-readiness.js --quiet
|
|
32
|
+
*/
|
|
33
|
+
|
|
34
|
+
'use strict';
|
|
35
|
+
|
|
36
|
+
const path = require('path');
|
|
37
|
+
const { spawnSync } = require('child_process');
|
|
38
|
+
|
|
39
|
+
const PROJECT_ROOT = path.resolve(__dirname, '..');
|
|
40
|
+
|
|
41
|
+
const GATES = [
|
|
42
|
+
{ id: 'lint', name: 'ESLint', cmd: ['npm', 'run', 'lint', '--silent'] },
|
|
43
|
+
{ id: 'typecheck', name: 'TypeScript', cmd: ['npm', 'run', 'typecheck', '--silent'] },
|
|
44
|
+
{ id: 'agents', name: 'Core agents', cmd: ['npm', 'run', 'validate:agents', '--silent'] },
|
|
45
|
+
{ id: 'squad-orqx', name: 'Squad orqx (10.28)', cmd: ['npm', 'run', 'validate:squad-orqx', '--silent'] },
|
|
46
|
+
{ id: 'story-meta', name: 'Story meta (10.19)', cmd: ['npm', 'run', 'validate:story-meta', '--silent'] },
|
|
47
|
+
{ id: 'no-external-refs', name: 'External refs (10.17)', cmd: ['npm', 'run', 'validate:no-external-refs', '--silent'] },
|
|
48
|
+
{ id: 'parity', name: 'Cross-IDE parity (10.18)', cmd: ['npm', 'run', 'validate:parity', '--silent'] },
|
|
49
|
+
{ id: 'manifest', name: 'Install manifest', cmd: ['npm', 'run', 'validate:manifest', '--silent'] },
|
|
50
|
+
{ id: 'install-docs', name: 'Install docs', cmd: ['npm', 'run', 'validate:docs', '--silent'] },
|
|
51
|
+
];
|
|
52
|
+
|
|
53
|
+
function parseArgs(argv = process.argv.slice(2)) {
|
|
54
|
+
const args = new Set(argv);
|
|
55
|
+
return {
|
|
56
|
+
json: args.has('--json'),
|
|
57
|
+
quiet: args.has('--quiet') || args.has('-q'),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function runGate(gate) {
|
|
62
|
+
const start = Date.now();
|
|
63
|
+
const result = spawnSync(gate.cmd[0], gate.cmd.slice(1), {
|
|
64
|
+
cwd: PROJECT_ROOT,
|
|
65
|
+
encoding: 'utf8',
|
|
66
|
+
shell: process.platform === 'win32',
|
|
67
|
+
});
|
|
68
|
+
const elapsed = Date.now() - start;
|
|
69
|
+
return {
|
|
70
|
+
id: gate.id,
|
|
71
|
+
name: gate.name,
|
|
72
|
+
status: result.status === 0 ? 'PASS' : 'FAIL',
|
|
73
|
+
exitCode: result.status,
|
|
74
|
+
elapsedMs: elapsed,
|
|
75
|
+
stderr: result.stderr ? result.stderr.split('\n').slice(-5).join('\n') : '',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function runAllGates(gates = GATES) {
|
|
80
|
+
return gates.map((g) => runGate(g));
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function summarize(results) {
|
|
84
|
+
return {
|
|
85
|
+
total: results.length,
|
|
86
|
+
pass: results.filter((r) => r.status === 'PASS').length,
|
|
87
|
+
fail: results.filter((r) => r.status === 'FAIL').length,
|
|
88
|
+
elapsedMs: results.reduce((sum, r) => sum + r.elapsedMs, 0),
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function formatTable(results) {
|
|
93
|
+
const lines = [];
|
|
94
|
+
const widths = [22, 8, 8, 6];
|
|
95
|
+
const sep = '+' + widths.map((w) => '-'.repeat(w + 2)).join('+') + '+';
|
|
96
|
+
lines.push(sep);
|
|
97
|
+
lines.push(
|
|
98
|
+
'| '
|
|
99
|
+
+ 'Gate'.padEnd(widths[0])
|
|
100
|
+
+ ' | '
|
|
101
|
+
+ 'Status'.padEnd(widths[1])
|
|
102
|
+
+ ' | '
|
|
103
|
+
+ 'Time'.padEnd(widths[2])
|
|
104
|
+
+ ' | '
|
|
105
|
+
+ 'Exit'.padEnd(widths[3])
|
|
106
|
+
+ ' |',
|
|
107
|
+
);
|
|
108
|
+
lines.push(sep);
|
|
109
|
+
for (const r of results) {
|
|
110
|
+
lines.push(
|
|
111
|
+
'| '
|
|
112
|
+
+ r.name.padEnd(widths[0])
|
|
113
|
+
+ ' | '
|
|
114
|
+
+ r.status.padEnd(widths[1])
|
|
115
|
+
+ ' | '
|
|
116
|
+
+ `${r.elapsedMs}ms`.padEnd(widths[2])
|
|
117
|
+
+ ' | '
|
|
118
|
+
+ String(r.exitCode).padEnd(widths[3])
|
|
119
|
+
+ ' |',
|
|
120
|
+
);
|
|
121
|
+
}
|
|
122
|
+
lines.push(sep);
|
|
123
|
+
return lines.join('\n');
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function main(argv = process.argv.slice(2)) {
|
|
127
|
+
const args = parseArgs(argv);
|
|
128
|
+
if (!args.quiet && !args.json) {
|
|
129
|
+
console.log('=== release-readiness ===');
|
|
130
|
+
console.log('Running all release gates (this can take a few minutes)...');
|
|
131
|
+
console.log('');
|
|
132
|
+
}
|
|
133
|
+
const results = runAllGates();
|
|
134
|
+
const summary = summarize(results);
|
|
135
|
+
|
|
136
|
+
if (args.json) {
|
|
137
|
+
console.log(JSON.stringify({ results, summary }, null, 2));
|
|
138
|
+
} else if (!args.quiet) {
|
|
139
|
+
console.log(formatTable(results));
|
|
140
|
+
console.log('');
|
|
141
|
+
console.log(
|
|
142
|
+
`Summary: ${summary.total} gates — ${summary.pass} pass, ${summary.fail} fail (${summary.elapsedMs}ms total)`,
|
|
143
|
+
);
|
|
144
|
+
if (summary.fail > 0) {
|
|
145
|
+
console.log('');
|
|
146
|
+
console.log('Failed gates (last 5 lines of stderr):');
|
|
147
|
+
for (const r of results.filter((x) => x.status === 'FAIL')) {
|
|
148
|
+
console.log(`\n--- ${r.name} ---`);
|
|
149
|
+
console.log(r.stderr || '(no stderr)');
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return summary.fail > 0 ? 1 : 0;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
if (require.main === module) {
|
|
158
|
+
process.exitCode = main();
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
module.exports = {
|
|
162
|
+
parseArgs,
|
|
163
|
+
GATES,
|
|
164
|
+
runGate,
|
|
165
|
+
runAllGates,
|
|
166
|
+
summarize,
|
|
167
|
+
formatTable,
|
|
168
|
+
main,
|
|
169
|
+
};
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Story A.5 - Local install matrix helper
|
|
3
|
+
#
|
|
4
|
+
# Best-effort local validation of the install matrix. You can only fully
|
|
5
|
+
# validate the OS you are currently running on — the other 2 OSes are
|
|
6
|
+
# covered by the GitHub Actions workflow `.github/workflows/install-matrix.yml`
|
|
7
|
+
# on release PRs.
|
|
8
|
+
#
|
|
9
|
+
# Usage:
|
|
10
|
+
# bash scripts/test-install-matrix-local.sh # runs all PMs x methods available locally
|
|
11
|
+
# bash scripts/test-install-matrix-local.sh --pm npm # limit to one PM
|
|
12
|
+
# bash scripts/test-install-matrix-local.sh --method global
|
|
13
|
+
# bash scripts/test-install-matrix-local.sh --only npm:global,pnpm:local
|
|
14
|
+
#
|
|
15
|
+
# What it does:
|
|
16
|
+
# 1. Packs the current repo with `npm pack` into a tarball
|
|
17
|
+
# 2. For each selected (pm, method) combo, creates a scratch dir and runs
|
|
18
|
+
# the SAME combo runner used by CI: .github/workflows/install-matrix/run-combo.sh
|
|
19
|
+
# 3. Prints a summary table of local results
|
|
20
|
+
#
|
|
21
|
+
# What it does NOT do:
|
|
22
|
+
# - Run on OSes other than the current one
|
|
23
|
+
# - Replace the CI workflow (the gate for rc -> latest promotion)
|
|
24
|
+
|
|
25
|
+
set -u
|
|
26
|
+
|
|
27
|
+
REPO_ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
|
28
|
+
cd "$REPO_ROOT"
|
|
29
|
+
|
|
30
|
+
# Defaults
|
|
31
|
+
FILTER_PM=""
|
|
32
|
+
FILTER_METHOD=""
|
|
33
|
+
ONLY=""
|
|
34
|
+
|
|
35
|
+
while [ $# -gt 0 ]; do
|
|
36
|
+
case "$1" in
|
|
37
|
+
--pm) FILTER_PM="$2"; shift 2 ;;
|
|
38
|
+
--method) FILTER_METHOD="$2"; shift 2 ;;
|
|
39
|
+
--only) ONLY="$2"; shift 2 ;;
|
|
40
|
+
-h|--help)
|
|
41
|
+
sed -n '2,30p' "$0"
|
|
42
|
+
exit 0
|
|
43
|
+
;;
|
|
44
|
+
*)
|
|
45
|
+
echo "Unknown arg: $1" >&2
|
|
46
|
+
exit 2
|
|
47
|
+
;;
|
|
48
|
+
esac
|
|
49
|
+
done
|
|
50
|
+
|
|
51
|
+
# Detect OS (for informational purposes only)
|
|
52
|
+
OS_KIND="unknown"
|
|
53
|
+
case "$(uname -s 2>/dev/null || echo Windows)" in
|
|
54
|
+
Linux*) OS_KIND="linux" ;;
|
|
55
|
+
Darwin*) OS_KIND="macos" ;;
|
|
56
|
+
MINGW*|CYGWIN*|MSYS*|Windows*) OS_KIND="windows" ;;
|
|
57
|
+
esac
|
|
58
|
+
|
|
59
|
+
echo "=== Local install matrix helper ==="
|
|
60
|
+
echo "OS: $OS_KIND"
|
|
61
|
+
echo "Repo: $REPO_ROOT"
|
|
62
|
+
echo "Node: $(node --version 2>/dev/null || echo 'NOT FOUND')"
|
|
63
|
+
echo "npm: $(npm --version 2>/dev/null || echo 'NOT FOUND')"
|
|
64
|
+
echo "pnpm: $(pnpm --version 2>/dev/null || echo 'NOT INSTALLED (skipped)')"
|
|
65
|
+
echo "yarn: $(yarn --version 2>/dev/null || echo 'NOT INSTALLED (skipped)')"
|
|
66
|
+
echo
|
|
67
|
+
|
|
68
|
+
# Build tarball once
|
|
69
|
+
echo "[1/3] Packing candidate tarball..."
|
|
70
|
+
TARBALL_REL=$(npm pack --silent 2>/dev/null | tail -n1)
|
|
71
|
+
if [ -z "$TARBALL_REL" ] || [ ! -f "$TARBALL_REL" ]; then
|
|
72
|
+
echo "ERROR: failed to pack candidate" >&2
|
|
73
|
+
exit 1
|
|
74
|
+
fi
|
|
75
|
+
TARBALL="$REPO_ROOT/$TARBALL_REL"
|
|
76
|
+
echo " tarball: $TARBALL"
|
|
77
|
+
echo
|
|
78
|
+
|
|
79
|
+
# Enumerate combos
|
|
80
|
+
ALL_PMS="npm pnpm yarn"
|
|
81
|
+
ALL_METHODS="global npx local"
|
|
82
|
+
RESULTS=()
|
|
83
|
+
|
|
84
|
+
should_run() {
|
|
85
|
+
local pm="$1" method="$2"
|
|
86
|
+
if [ -n "$FILTER_PM" ] && [ "$pm" != "$FILTER_PM" ]; then return 1; fi
|
|
87
|
+
if [ -n "$FILTER_METHOD" ] && [ "$method" != "$FILTER_METHOD" ]; then return 1; fi
|
|
88
|
+
if [ -n "$ONLY" ]; then
|
|
89
|
+
case ",$ONLY," in
|
|
90
|
+
*,"$pm:$method",*) return 0 ;;
|
|
91
|
+
*) return 1 ;;
|
|
92
|
+
esac
|
|
93
|
+
fi
|
|
94
|
+
return 0
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
has_pm() {
|
|
98
|
+
command -v "$1" >/dev/null 2>&1
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
echo "[2/3] Running combos..."
|
|
102
|
+
TMP_BASE="$(mktemp -d 2>/dev/null || mktemp -d -t install-matrix)"
|
|
103
|
+
trap 'rm -rf "$TMP_BASE"' EXIT
|
|
104
|
+
|
|
105
|
+
for pm in $ALL_PMS; do
|
|
106
|
+
if ! has_pm "$pm"; then
|
|
107
|
+
echo " skip $pm/* — $pm not installed locally"
|
|
108
|
+
continue
|
|
109
|
+
fi
|
|
110
|
+
for method in $ALL_METHODS; do
|
|
111
|
+
if ! should_run "$pm" "$method"; then continue; fi
|
|
112
|
+
echo " -> $pm / $method"
|
|
113
|
+
SM_PM="$pm" \
|
|
114
|
+
SM_METHOD="$method" \
|
|
115
|
+
SM_TARBALL="$TARBALL" \
|
|
116
|
+
SM_TEST_DIR="$TMP_BASE/$pm-$method" \
|
|
117
|
+
bash "$REPO_ROOT/.github/workflows/install-matrix/run-combo.sh"
|
|
118
|
+
code=$?
|
|
119
|
+
if [ $code -eq 0 ]; then
|
|
120
|
+
RESULTS+=("PASS|$pm|$method")
|
|
121
|
+
else
|
|
122
|
+
RESULTS+=("FAIL|$pm|$method")
|
|
123
|
+
fi
|
|
124
|
+
echo
|
|
125
|
+
done
|
|
126
|
+
done
|
|
127
|
+
|
|
128
|
+
echo "[3/3] Local matrix summary ($OS_KIND):"
|
|
129
|
+
printf " %-6s %-6s %-8s %s\n" "STATUS" "PM" "METHOD" ""
|
|
130
|
+
printf " %-6s %-6s %-8s %s\n" "------" "----" "------" ""
|
|
131
|
+
PASS_COUNT=0
|
|
132
|
+
FAIL_COUNT=0
|
|
133
|
+
for row in "${RESULTS[@]:-}"; do
|
|
134
|
+
IFS='|' read -r status pm method <<<"$row"
|
|
135
|
+
printf " %-6s %-6s %-8s\n" "$status" "$pm" "$method"
|
|
136
|
+
case "$status" in
|
|
137
|
+
PASS) PASS_COUNT=$((PASS_COUNT+1)) ;;
|
|
138
|
+
FAIL) FAIL_COUNT=$((FAIL_COUNT+1)) ;;
|
|
139
|
+
esac
|
|
140
|
+
done
|
|
141
|
+
echo
|
|
142
|
+
echo " PASS: $PASS_COUNT FAIL: $FAIL_COUNT"
|
|
143
|
+
echo
|
|
144
|
+
echo "NOTE: Local runs validate only $OS_KIND. The full 27-combo gate runs in CI"
|
|
145
|
+
echo " via .github/workflows/install-matrix.yml on release PRs."
|
|
146
|
+
|
|
147
|
+
# Cleanup tarball
|
|
148
|
+
rm -f "$TARBALL" 2>/dev/null || true
|
|
149
|
+
|
|
150
|
+
if [ $FAIL_COUNT -gt 0 ]; then
|
|
151
|
+
exit 1
|
|
152
|
+
fi
|
|
153
|
+
exit 0
|