fix(mindspace): restore publish guards and safe 103 goosed canary routing
Finish-sync guards now accept SEO/GEO disk enrichment instead of requiring byte-identical HTML, and fragment writes no longer duplicate doctype. Users canary mode keeps the full native pool while routing allowlisted identities to :18015 without overriding memory settings. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -99,6 +99,26 @@ const BLOCKED_HOSTS = new Set([
|
||||
export const DEFAULT_GOOSE_STABLE_TARGET = 'https://127.0.0.1:18006';
|
||||
export const DEFAULT_GOOSE_V149_TARGET = 'https://127.0.0.1:18049';
|
||||
|
||||
export function parseConfiguredPoolTargets(env = process.env) {
|
||||
const csvTargets = String(env.TKMIND_API_TARGETS ?? '')
|
||||
.split(',')
|
||||
.map((value) => value.trim())
|
||||
.filter(Boolean);
|
||||
const legacyTargets = [
|
||||
env.TKMIND_API_TARGET ?? DEFAULT_GOOSE_STABLE_TARGET,
|
||||
env.TKMIND_API_TARGET_1,
|
||||
].filter(Boolean);
|
||||
const seen = new Set();
|
||||
const targets = [];
|
||||
for (const raw of [...csvTargets, ...legacyTargets]) {
|
||||
const normalized = assertLoopbackGooseTarget(raw, 'pool goosed target');
|
||||
if (seen.has(normalized)) continue;
|
||||
seen.add(normalized);
|
||||
targets.push(normalized);
|
||||
}
|
||||
return targets;
|
||||
}
|
||||
|
||||
function csvSet(value) {
|
||||
return new Set(
|
||||
String(value ?? '')
|
||||
@@ -159,10 +179,12 @@ export function resolveGooseApiTargetsFromEnv(env = process.env) {
|
||||
const mode = parseGooseCanaryMode(env);
|
||||
if (mode === 'off') return null;
|
||||
|
||||
const stable = assertLoopbackGooseTarget(
|
||||
env.TKMIND_API_TARGET_STABLE ?? DEFAULT_GOOSE_STABLE_TARGET,
|
||||
'TKMIND_API_TARGET_STABLE',
|
||||
);
|
||||
const poolTargets = parseConfiguredPoolTargets(env);
|
||||
const stable = poolTargets[0]
|
||||
?? assertLoopbackGooseTarget(
|
||||
env.TKMIND_API_TARGET_STABLE ?? DEFAULT_GOOSE_STABLE_TARGET,
|
||||
'TKMIND_API_TARGET_STABLE',
|
||||
);
|
||||
const v149 = assertLoopbackGooseTarget(
|
||||
env.TKMIND_API_TARGET_V149 ?? DEFAULT_GOOSE_V149_TARGET,
|
||||
'TKMIND_API_TARGET_V149',
|
||||
@@ -186,12 +208,14 @@ export function resolveGooseApiTargetsFromEnv(env = process.env) {
|
||||
);
|
||||
}
|
||||
|
||||
const targets = [...new Set([...poolTargets, v149])];
|
||||
|
||||
return Object.freeze({
|
||||
mode,
|
||||
stable,
|
||||
v149,
|
||||
primary: stable,
|
||||
targets: [stable, v149],
|
||||
targets,
|
||||
policy,
|
||||
});
|
||||
}
|
||||
@@ -201,8 +225,11 @@ export function resolveGooseTargetForIdentity(identity, config) {
|
||||
return config?.stable ?? DEFAULT_GOOSE_STABLE_TARGET;
|
||||
}
|
||||
if (config.mode === 'all') return config.v149;
|
||||
const targetKey = resolveCanaryTarget(identity, config.policy);
|
||||
return targetKey === 'candidate' ? config.v149 : config.stable;
|
||||
if (config.mode === 'users') {
|
||||
const targetKey = resolveCanaryTarget(identity, config.policy);
|
||||
return targetKey === 'candidate' ? config.v149 : null;
|
||||
}
|
||||
return config.stable;
|
||||
}
|
||||
|
||||
export function describeGooseCanaryConfig(env = process.env) {
|
||||
|
||||
@@ -25,29 +25,34 @@ test('resolveGooseApiTargetsFromEnv all mode pins Portal to v149', () => {
|
||||
assert.deepEqual(config.targets, ['https://127.0.0.1:18049']);
|
||||
});
|
||||
|
||||
test('resolveGooseApiTargetsFromEnv users mode exposes dual targets', () => {
|
||||
test('resolveGooseApiTargetsFromEnv users mode keeps full pool and adds v149', () => {
|
||||
const config = resolveGooseApiTargetsFromEnv({
|
||||
TKMIND_GOOSE_CANARY: 'users',
|
||||
TKMIND_GOOSE_CANARY_USER_IDS: 'user-1',
|
||||
TKMIND_API_TARGETS: 'https://127.0.0.1:18006,https://127.0.0.1:18007,https://127.0.0.1:18008',
|
||||
TKMIND_API_TARGET_V149: 'https://127.0.0.1:18015',
|
||||
});
|
||||
assert.equal(config.mode, 'users');
|
||||
assert.equal(config.primary, 'https://127.0.0.1:18006');
|
||||
assert.deepEqual(config.targets, ['https://127.0.0.1:18006', 'https://127.0.0.1:18049']);
|
||||
assert.deepEqual(config.targets, [
|
||||
'https://127.0.0.1:18006',
|
||||
'https://127.0.0.1:18007',
|
||||
'https://127.0.0.1:18008',
|
||||
'https://127.0.0.1:18015',
|
||||
]);
|
||||
});
|
||||
|
||||
test('resolveGooseTargetForIdentity routes canary users to v149', () => {
|
||||
test('resolveGooseTargetForIdentity routes canary users to v149 only', () => {
|
||||
const config = resolveGooseApiTargetsFromEnv({
|
||||
TKMIND_GOOSE_CANARY: 'users',
|
||||
TKMIND_GOOSE_CANARY_USER_IDS: 'user-abc',
|
||||
TKMIND_API_TARGET_V149: 'https://127.0.0.1:18015',
|
||||
});
|
||||
assert.equal(
|
||||
resolveGooseTargetForIdentity({ id: 'user-abc' }, config),
|
||||
'https://127.0.0.1:18049',
|
||||
);
|
||||
assert.equal(
|
||||
resolveGooseTargetForIdentity({ id: 'other-user' }, config),
|
||||
'https://127.0.0.1:18006',
|
||||
'https://127.0.0.1:18015',
|
||||
);
|
||||
assert.equal(resolveGooseTargetForIdentity({ id: 'other-user' }, config), null);
|
||||
});
|
||||
|
||||
test('assertLoopbackGooseTarget refuses production hosts', () => {
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
#!/usr/bin/env bash
|
||||
# Toggle Goose v1.49 canary routing on 103 stable Portal (.env).
|
||||
# users mode keeps the full native pool in TKMIND_API_TARGETS and only routes
|
||||
# allowlisted identities to TKMIND_API_TARGET_V149 (:18015 by default).
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "$0")/.." && pwd)"
|
||||
ENV_FILE="${GOOSE_V149_CANARY_ENV_FILE:-/Users/john/Project/Memind/.env}"
|
||||
MARKER_BEGIN='# GOOSE_V149_CANARY_BEGIN'
|
||||
MARKER_END='# GOOSE_V149_CANARY_END'
|
||||
STABLE_TARGET="${TKMIND_API_TARGET_STABLE:-https://127.0.0.1:18006}"
|
||||
V149_TARGET="${TKMIND_API_TARGET_V149:-https://127.0.0.1:18015}"
|
||||
PORTAL_LABEL="${MEMIND_PORTAL_LABEL:-cn.tkmind.memind-portal}"
|
||||
GUI="gui/$(id -u)"
|
||||
@@ -16,12 +17,16 @@ usage() {
|
||||
Usage: bash scripts/switch-goose-v149-canary-103.sh <command>
|
||||
|
||||
Commands:
|
||||
on [all|users] Route Portal to v1.49 canary (${V149_TARGET})
|
||||
on [all|users] Route allowlisted users (or everyone in all mode) to v1.49 canary (${V149_TARGET})
|
||||
off|rollback Remove canary block from ${ENV_FILE}
|
||||
status Show active block
|
||||
|
||||
users mode example (唐 only):
|
||||
TKMIND_GOOSE_CANARY_WECHAT_USER_IDS=wx_ul610et8 bash scripts/switch-goose-v149-canary-103.sh on users
|
||||
users mode example (john only):
|
||||
TKMIND_GOOSE_CANARY_USERNAMES=john bash scripts/switch-goose-v149-canary-103.sh on users
|
||||
|
||||
Notes:
|
||||
- Does not rewrite TKMIND_API_TARGETS or memory settings.
|
||||
- all mode is intended for local loopback canary only; avoid on the 9-instance 103 pool.
|
||||
EOF
|
||||
}
|
||||
|
||||
@@ -45,22 +50,14 @@ write_canary_block() {
|
||||
{
|
||||
echo "${MARKER_BEGIN}"
|
||||
echo "TKMIND_GOOSE_CANARY=${mode}"
|
||||
echo "TKMIND_API_TARGET_STABLE=${STABLE_TARGET}"
|
||||
echo "TKMIND_API_TARGET_V149=${V149_TARGET}"
|
||||
if [[ "${mode}" == "all" ]]; then
|
||||
echo "TKMIND_API_TARGET=${V149_TARGET}"
|
||||
elif [[ "${mode}" == "users" ]]; then
|
||||
echo "TKMIND_API_TARGET=${STABLE_TARGET}"
|
||||
echo "TKMIND_API_TARGETS=${STABLE_TARGET},${V149_TARGET}"
|
||||
[[ -n "${TKMIND_GOOSE_CANARY_USER_IDS:-}" ]] && echo "TKMIND_GOOSE_CANARY_USER_IDS=${TKMIND_GOOSE_CANARY_USER_IDS}"
|
||||
[[ -n "${TKMIND_GOOSE_CANARY_USERNAMES:-}" ]] && echo "TKMIND_GOOSE_CANARY_USERNAMES=${TKMIND_GOOSE_CANARY_USERNAMES}"
|
||||
[[ -n "${TKMIND_GOOSE_CANARY_WECHAT_USER_IDS:-}" ]] && echo "TKMIND_GOOSE_CANARY_WECHAT_USER_IDS=${TKMIND_GOOSE_CANARY_WECHAT_USER_IDS}"
|
||||
fi
|
||||
echo "MEMORY_BACKEND=legacy"
|
||||
echo "MEMORY_VECTOR_ENABLED=0"
|
||||
echo "MEMORY_AGENT_INJECTION_MODE=off"
|
||||
echo "MEMORY_LIFECYCLE_ENABLED=0"
|
||||
echo "MEMORY_CANDIDATE_ENABLED=0"
|
||||
echo "${MARKER_END}"
|
||||
} >> "${ENV_FILE}"
|
||||
}
|
||||
@@ -79,6 +76,9 @@ case "${cmd}" in
|
||||
echo "Missing ${ENV_FILE}" >&2
|
||||
exit 1
|
||||
fi
|
||||
if [[ "${mode}" == "all" ]]; then
|
||||
echo "[goose-v149-canary-103] WARN: all mode replaces the entire pool with v149; use users mode on 103 production" >&2
|
||||
fi
|
||||
if [[ "${mode}" == "users" && -z "${TKMIND_GOOSE_CANARY_USER_IDS:-}${TKMIND_GOOSE_CANARY_USERNAMES:-}${TKMIND_GOOSE_CANARY_WECHAT_USER_IDS:-}" ]]; then
|
||||
echo "users mode requires identity allowlist env vars" >&2
|
||||
exit 2
|
||||
|
||||
@@ -12,6 +12,10 @@ const root = path.resolve(new URL('..', import.meta.url).pathname);
|
||||
const runtimeServer = path.join(root, '.runtime', 'portal', 'server.mjs');
|
||||
const currentUser = { id: 'a6fb1e97-2b0f-447b-b138-4561d8e5c53e', username: 'john' };
|
||||
|
||||
const RAW_HTML =
|
||||
'<!doctype html><html lang="zh-CN"><head><title>Guarded release</title></head>'
|
||||
+ '<body><h1>Guarded release</h1></body></html>';
|
||||
|
||||
const toolRequestOnlyMessages = [
|
||||
{
|
||||
role: 'assistant',
|
||||
@@ -23,7 +27,7 @@ const toolRequestOnlyMessages = [
|
||||
name: 'write',
|
||||
arguments: {
|
||||
path: 'public/guarded-release.html',
|
||||
content: '<!doctype html><title>Guarded release</title>',
|
||||
content: RAW_HTML,
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -49,10 +53,11 @@ try {
|
||||
publishDir,
|
||||
});
|
||||
assert.deepEqual(result.materialized, ['public/guarded-release.html']);
|
||||
assert.equal(
|
||||
fs.readFileSync(path.join(publishDir, 'public/guarded-release.html'), 'utf8'),
|
||||
'<!doctype html><title>Guarded release</title>',
|
||||
);
|
||||
const written = fs.readFileSync(path.join(publishDir, 'public/guarded-release.html'), 'utf8');
|
||||
assert.ok(written.includes('<title>Guarded release</title>'));
|
||||
assert.ok(written.includes('<h1>Guarded release</h1>'));
|
||||
assert.ok(/data-mindspace-seo-outline=|name="mindspace-geo"/i.test(written));
|
||||
assert.doesNotMatch(written, /<body>\s*<!doctype html>/i);
|
||||
} finally {
|
||||
fs.rmSync(publishDir, { recursive: true, force: true });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user