fix(mindspace): release edited public pages and fix achievements dates
Memind CI / Test, build, and release guards (push) Successful in 3m37s
Memind CI / Test, build, and release guards (push) Successful in 3m37s
Finish now releases static HTML delivery contracts in a finally block so re-edited pages are not stuck at HTTP 409, and M成果 groups pages by Asia/Shanghai calendar dates to avoid duplicate day headings. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -13,6 +13,7 @@ import {
|
||||
import {
|
||||
markPageDeliveryContractReady,
|
||||
preparePageDeliveryContract,
|
||||
releaseMaterializedPageDeliveryContracts,
|
||||
} from '../mindspace-delivery-contract.mjs';
|
||||
import { maybeRepairH5HtmlAfterFinish } from '../mindspace-h5-html-finish-guard.mjs';
|
||||
import { maybeRepairPageDataAfterFinish } from '../mindspace-page-data-finish-guard.mjs';
|
||||
@@ -79,6 +80,8 @@ export function attachPortalSessionRoutes(
|
||||
maybeRepairPageDataAfterFinish,
|
||||
markPageDeliveryContractReadyFn =
|
||||
markPageDeliveryContractReady,
|
||||
releaseMaterializedPageDeliveryContractsFn =
|
||||
releaseMaterializedPageDeliveryContracts,
|
||||
finishDeliveryRetryDelaysMs = [250, 1_000],
|
||||
finishDeliveryRetryWaitFn = (delayMs) =>
|
||||
new Promise((resolve) =>
|
||||
@@ -477,6 +480,8 @@ export function attachPortalSessionRoutes(
|
||||
// workspace from DB-backed assets only.
|
||||
const finalizeAfterFinishOnce = async (sid, uid) => {
|
||||
beginSessionPageDelivery(sid);
|
||||
let releaseCandidatePaths = [];
|
||||
let allowPgRequiredRelease = false;
|
||||
try {
|
||||
const apiFetchFn = async (pathname, init) => {
|
||||
const target = await tkmindProxy.resolveTarget(sid);
|
||||
@@ -616,6 +621,8 @@ export function attachPortalSessionRoutes(
|
||||
...deliveryContractWrites.keys(),
|
||||
]),
|
||||
].sort();
|
||||
releaseCandidatePaths = publicHtmlRelativePaths;
|
||||
allowPgRequiredRelease = htmlReady && pageDataReady;
|
||||
const pgRequired = [
|
||||
...(Array.isArray(messages) ? messages : []),
|
||||
].some(
|
||||
@@ -660,11 +667,6 @@ export function attachPortalSessionRoutes(
|
||||
pgRequired,
|
||||
});
|
||||
}
|
||||
await markPageDeliveryContractReadyFn({
|
||||
pool: authPool,
|
||||
userId: uid,
|
||||
relativePath,
|
||||
}).catch(() => false);
|
||||
}
|
||||
}
|
||||
const memoryV2 = getMemoryV2();
|
||||
@@ -685,6 +687,20 @@ export function attachPortalSessionRoutes(
|
||||
});
|
||||
}
|
||||
} finally {
|
||||
if (releaseCandidatePaths.length > 0) {
|
||||
await releaseMaterializedPageDeliveryContractsFn({
|
||||
pool: authPool,
|
||||
userId: uid,
|
||||
relativePaths: releaseCandidatePaths,
|
||||
allowPgRequired: allowPgRequiredRelease,
|
||||
}).catch((error) => {
|
||||
logger.warn(
|
||||
`[MindSpace] delivery contract release failed for session ${sid}: ${
|
||||
error instanceof Error ? error.message : error
|
||||
}`,
|
||||
);
|
||||
});
|
||||
}
|
||||
endSessionPageDelivery(sid);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -112,7 +112,7 @@ function createDependencies(overrides = {}) {
|
||||
return { sessionId, hooks };
|
||||
},
|
||||
};
|
||||
return {
|
||||
const setup = {
|
||||
calls,
|
||||
proxy,
|
||||
dependencies: {
|
||||
@@ -203,6 +203,30 @@ function createDependencies(overrides = {}) {
|
||||
...overrides,
|
||||
},
|
||||
};
|
||||
if (
|
||||
!Object.prototype.hasOwnProperty.call(
|
||||
overrides,
|
||||
'releaseMaterializedPageDeliveryContractsFn',
|
||||
)
|
||||
) {
|
||||
setup.dependencies.releaseMaterializedPageDeliveryContractsFn =
|
||||
async (input) => {
|
||||
const released = [];
|
||||
for (const relativePath of input.relativePaths ?? []) {
|
||||
if (
|
||||
await setup.dependencies.markPageDeliveryContractReadyFn({
|
||||
pool: input.pool,
|
||||
userId: input.userId,
|
||||
relativePath,
|
||||
})
|
||||
) {
|
||||
released.push(relativePath);
|
||||
}
|
||||
}
|
||||
return released;
|
||||
};
|
||||
}
|
||||
return setup;
|
||||
}
|
||||
|
||||
test('session module preserves route inventory and order', () => {
|
||||
@@ -709,8 +733,8 @@ test('Finish hook preserves refresh, sync, delivery readiness, memory, and lock
|
||||
'prepare-page-data',
|
||||
'repair-page-data',
|
||||
'prepare-contract',
|
||||
'ready',
|
||||
'memory',
|
||||
'ready',
|
||||
],
|
||||
);
|
||||
assert.equal(
|
||||
@@ -957,5 +981,73 @@ test('Finish retries when a delivery guard is initially not ready', async () =>
|
||||
assert.equal(pageDataChecks, 2);
|
||||
assert.deepEqual(setup.calls.begin, ['session-1', 'session-1']);
|
||||
assert.deepEqual(setup.calls.end, ['session-1', 'session-1']);
|
||||
assert.deepEqual(readyPaths, ['public/survey.html']);
|
||||
assert.deepEqual(readyPaths, ['public/survey.html', 'public/survey.html']);
|
||||
});
|
||||
|
||||
test('Finish finally releases static HTML contracts when delivery guards fail', async () => {
|
||||
let hooks = null;
|
||||
const releasedPaths = [];
|
||||
const setup = createDependencies({
|
||||
finishDeliveryRetryDelaysMs: [],
|
||||
getAuthPool: () => ({ id: 'pool' }),
|
||||
getTkmindProxy: () => ({
|
||||
async resolveTarget(sessionId) {
|
||||
return `target:${sessionId}`;
|
||||
},
|
||||
async apiFetchTo() {
|
||||
return createUpstream({
|
||||
body: {
|
||||
id: 'session-1',
|
||||
conversation: [
|
||||
{
|
||||
id: 'user-1',
|
||||
role: 'user',
|
||||
content: 'update page',
|
||||
metadata: { userVisible: true },
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
},
|
||||
proxySessionEvents(_req, _res, _sessionId, receivedHooks) {
|
||||
hooks = receivedHooks;
|
||||
},
|
||||
}),
|
||||
getMindSpacePublicFinish: () => ({
|
||||
async syncAfterFinish() {
|
||||
return {
|
||||
publicHtmlRelativePaths: ['public/daily-news-0813.html'],
|
||||
docxSync: { missing: [] },
|
||||
};
|
||||
},
|
||||
async preparePageDataAfterFinish() {
|
||||
return {
|
||||
autoBind: { bound: [], skipped: [], errors: [] },
|
||||
evaluation: { structuralPageData: false, relevantFiles: [] },
|
||||
};
|
||||
},
|
||||
}),
|
||||
async maybeRepairH5HtmlAfterFinishFn() {
|
||||
return { skipped: 'limit' };
|
||||
},
|
||||
async releaseMaterializedPageDeliveryContractsFn(input) {
|
||||
releasedPaths.push(...(input.relativePaths ?? []));
|
||||
return input.relativePaths ?? [];
|
||||
},
|
||||
});
|
||||
const api = createRouterRecorder();
|
||||
attachPortalSessionRoutes(api, setup.dependencies);
|
||||
await api.routes.get('GET /sessions/:sessionId/events')(
|
||||
createRequest(),
|
||||
createResponseRecorder(),
|
||||
() => {},
|
||||
);
|
||||
|
||||
await assert.rejects(
|
||||
() => hooks.onAfterFinish('session-1', 'user-1'),
|
||||
/page delivery guards are not ready/,
|
||||
);
|
||||
|
||||
assert.deepEqual(releasedPaths, ['public/daily-news-0813.html']);
|
||||
assert.deepEqual(setup.calls.end, ['session-1']);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user