refactor: make production gate impact only
Memind CI / Test, build, and release guards (push) Has been cancelled

This commit is contained in:
john
2026-07-28 01:13:28 +08:00
parent 864197162d
commit 7a381db132
14 changed files with 195 additions and 98 deletions
+57 -37
View File
@@ -17,24 +17,44 @@ export const CORE_SCENARIO_IDS = Object.freeze([
'COMP-09',
]);
const FULL_GATE_PATHS = Object.freeze([
/^(?:package|npm-shrinkwrap).*\.json$/i,
/^(?:pnpm-lock\.yaml|yarn\.lock)$/i,
/^(?:server\.mjs|schema\.sql)$/i,
/^(?:migrations?|database)\//i,
/^release-gate\//i,
/^scripts\/(?:build-portal-runtime|release-|run-release-gate|verify-release-gate|verify-canary-)/i,
/^scripts\/(?:run-memind-portal-prod|run-memind-portal-candidate|goosed.*compose)/i,
/^docs\/(?:production-release-guardian|release-gate-automation|release-canary-103)\.md$/i,
/^(?:PRODUCTION_RELEASE_RULES|ENGINEERING_WORKFLOW_RULES)\.md$/i,
/^\.github\/workflows\//i,
/^server\/portal-(?:access-policy|auth-services-bootstrap|auth-session-helpers|session-coordinator|gateway-services-bootstrap|integration-services-bootstrap)/i,
/^src\/(?:api\/core|config)\.[cm]?[jt]sx?$/i,
/^(?:tkmind-proxy|session-stream|session-stream-store|deepseek-no-think-proxy)\.mjs$/i,
const CRITICAL_IMPACT_RULES = Object.freeze([
{
groups: ['AGENT', 'CFG', 'UI'],
pattern: /^(?:(?:package|npm-shrinkwrap).*\.json|pnpm-lock\.yaml|yarn\.lock)$/i,
},
{
groups: ['AGENT', 'AUTH', 'CFG', 'CHAT', 'DATA'],
pattern: /^(?:server\.mjs|schema\.sql|(?:migrations?|database)\/)/i,
},
{
groups: ['CFG', 'REL'],
pattern: /^(?:release-gate\/|scripts\/(?:build-portal-runtime|check-release-ready|release-|run-release-gate|verify-release-gate|verify-canary-))/i,
},
{
groups: ['AGENT', 'CFG', 'REL'],
pattern: /^scripts\/(?:run-memind-portal-prod|run-memind-portal-candidate|goosed.*compose)/i,
},
{
groups: ['CFG', 'REL'],
pattern: /^(?:docs\/(?:production-release-guardian|release-gate-automation|release-canary-103)\.md|(?:PRODUCTION_RELEASE_RULES|ENGINEERING_WORKFLOW_RULES)\.md|\.github\/workflows\/)/i,
},
{
groups: ['AGENT', 'AUTH', 'CFG', 'CHAT', 'WX'],
pattern: /^server\/portal-/i,
},
{
groups: ['AUTH', 'CFG', 'UI'],
pattern: /^src\/(?:api\/core|config)\.[cm]?[jt]sx?$/i,
},
{
groups: ['AGENT', 'CFG', 'CHAT'],
pattern: /^(?:tkmind-proxy|session-stream|session-stream-store|deepseek-no-think-proxy)\.mjs$/i,
},
]);
const NON_RUNTIME_PATHS = Object.freeze([
/^(?:AGENTS|README|CHANGELOG)\.md$/i,
/^\.runtime\//i,
/^docs\//i,
/^\.cursor\//i,
/^\.codex\//i,
@@ -57,11 +77,12 @@ const IMPACT_RULES = Object.freeze([
{ groups: ['IMGPG'], pattern: /(?:image|thumbnail|cover|imgproxy)/i },
{ groups: ['FILE'], pattern: /(?:file|attachment|upload|document|pdf|docx|csv)/i },
{ groups: ['MS'], pattern: /mindspace/i },
{ groups: ['PAGE'], pattern: /(?:public-page|published-page|publication|page-delivery|mindspace-public)/i },
{ groups: ['AGENT'], pattern: /(?:agent|goosed|worker|aider|mcp)/i },
{ groups: ['CHAT'], pattern: /(?:chat|conversation|message|sse|routing|intent)/i },
{ groups: ['PAGE'], pattern: /(?:public-(?:page|finish)|published-page|publication|page-delivery|mindspace-public)/i },
{ groups: ['AGENT'], pattern: /(?:agent|capabilit|goosed|worker|aider|mcp)/i },
{ groups: ['CHAT'], pattern: /(?:chat|conversation|message|reply|session|sse|routing|intent)/i },
{ groups: ['AUTH'], pattern: /(?:auth|access-policy|account|user-permission)/i },
{ groups: ['CFG'], pattern: /(?:config|provider|model-catalog|orchestrator|analytics|disclosure)/i },
{ groups: ['CFG'], pattern: /(?:^|\/)\.env(?:\.example)?$/i },
{ groups: ['UI'], pattern: /^(?:src\/|public\/)|\.(?:css|scss|tsx|vue)$/i },
]);
@@ -105,7 +126,7 @@ function closeGroupDependencies(initialGroups) {
export function selectImpactScenarios({
catalog,
changedPaths,
forceFullReasons = [],
blockingReasons = [],
}) {
const normalizedPaths = normalizePaths(changedPaths);
const catalogIds = new Set(catalog.map((scenario) => scenario.id));
@@ -114,18 +135,21 @@ export function selectImpactScenarios({
throw new Error(`Core release scenarios are missing from the catalog: ${missingCore.join(',')}`);
}
const fullGateReasons = [...forceFullReasons];
if (blockingReasons.length > 0) {
throw new Error(`Impact selection is blocked: ${blockingReasons.join(',')}`);
}
const directGroups = new Set();
const unmappedPaths = [];
for (const relativePath of normalizedPaths) {
if (matchesAny(FULL_GATE_PATHS, relativePath)) {
fullGateReasons.push(`critical_path:${relativePath}`);
continue;
let matched = false;
for (const rule of CRITICAL_IMPACT_RULES) {
if (!rule.pattern.test(relativePath)) continue;
matched = true;
for (const group of rule.groups) directGroups.add(group);
}
if (matchesAny(NON_RUNTIME_PATHS, relativePath)) continue;
let matched = false;
for (const rule of IMPACT_RULES) {
if (!rule.pattern.test(relativePath)) continue;
matched = true;
@@ -134,23 +158,19 @@ export function selectImpactScenarios({
if (matched) continue;
unmappedPaths.push(relativePath);
fullGateReasons.push(`unmapped_runtime_path:${relativePath}`);
}
if (unmappedPaths.length > 0) {
throw new Error(
`Impact selection has unmapped runtime paths: ${unmappedPaths.join(',')}`,
);
}
const impactGroups = closeGroupDependencies(directGroups);
const strategy = fullGateReasons.length > 0
? 'full'
: impactGroups.length > 0
? 'impact'
: 'core';
const strategy = impactGroups.length > 0 ? 'impact' : 'core';
const selected = new Set(CORE_SCENARIO_IDS);
if (strategy === 'full') {
for (const scenario of catalog) selected.add(scenario.id);
} else {
for (const scenario of catalog) {
if (impactGroups.includes(scenario.group)) selected.add(scenario.id);
}
for (const scenario of catalog) {
if (impactGroups.includes(scenario.group)) selected.add(scenario.id);
}
const selectedIds = catalog
@@ -158,14 +178,14 @@ export function selectImpactScenarios({
.filter((id) => selected.has(id));
return {
policy_version: 1,
policy_version: 2,
strategy,
catalog_total: catalog.length,
core_ids: [...CORE_SCENARIO_IDS],
impact_groups: impactGroups,
changed_paths: normalizedPaths,
unmapped_paths: unmappedPaths,
full_gate_reasons: [...new Set(fullGateReasons)].sort(),
full_gate_reasons: [],
selected_ids: selectedIds,
selected_total: selectedIds.length,
};