framework
Version:
The (AI) Framework: turnkey, zero-config AI orchestration that wraps a coding-agent CLI (Claude Code) as a black box and takes you from an idea to a running app. Vite for AI.
68 lines • 3.54 kB
JavaScript
import { join } from 'node:path';
import { DATA_BRANCH } from './branch-names.js';
import { THE_FRAMEWORK_DIR } from './framework-dir.js';
import { ARCHIVE_DIR, BRANCHES_DIR, EVENTS_FILE, META_FILE, nodeStoreFs } from './store/index.js';
import { FLAT_TODO_FILE, TICKETS_DIR } from './tickets.js';
/**
* The layout gate (#1575): a build refuses to run in a repo whose recorded bookkeeping layout
* differs from its own.
*
* The failure this closes was caught live (#1574): the cloud environment installs the framework
* from npm, the last published build predated the `sessions/` → `agents/` rename, and the web
* run's bookkeeping landed under the old name — a wrong-layout commit that main's guard test
* rejects, hours after the session could have said it will not work. Same philosophy as the
* extension gate (#1519): a skewed build half-works, so it is refused outright with both sides
* named, no degraded mode.
*
* The repo's half is a tracked marker file whose content is *derived from the build's own layout
* constants*, so a rename changes the derivation by itself and the lockstep test fails the rename
* PR until the checked-in marker is regenerated — nothing to remember to bump. A repo without the
* marker is ungated, the way an absent `expectedExtensionVersion` leaves the bridge ungated:
* install writes it, so every newly activated repo is gated from the start.
*/
/** The marker's name under `.the-framework/`. Tracked, so every worktree and clone carries it. */
export const LAYOUT_FILE = 'LAYOUT';
/** The marker path under `cwd`'s `.the-framework/`. */
export function layoutMarkerPath(cwd) {
return join(cwd, THE_FRAMEWORK_DIR, LAYOUT_FILE);
}
/**
* The layout this build writes, as one comparable string: every name a committed artifact's path
* hangs off. Pure data — a comment line would put message wording into the equality.
*/
export function layoutMarker() {
return [
`framework-dir: ${THE_FRAMEWORK_DIR}`,
`data-branch: ${DATA_BRANCH}`,
`archive-dir: ${ARCHIVE_DIR}`,
`branches-dir: ${BRANCHES_DIR}`,
`events-file: ${EVENTS_FILE}`,
`meta-file: ${META_FILE}`,
`tickets-dir: ${TICKETS_DIR}`,
`queue-file: ${FLAT_TODO_FILE}`,
'',
].join('\n');
}
/**
* Compare the repo's recorded layout against this build's. A missing marker passes (an unmarked
* repo is ungated); a present-but-different one refuses with both sides named and the fix for
* each direction — the stale-published-build case this exists for, and the older-repo case.
*/
export async function checkLayout(cwd, fs = nodeStoreFs()) {
const path = layoutMarkerPath(cwd);
if (!(await fs.exists(path)))
return { ok: true };
const recorded = await fs.read(path);
if (recorded === layoutMarker())
return { ok: true };
return {
ok: false,
error: `this framework build's bookkeeping layout does not match what the repo records in ` +
`${THE_FRAMEWORK_DIR}/${LAYOUT_FILE}, so running it here would commit files under names ` +
`the repo does not use (#1575).\n\nthis build writes:\n${layoutMarker()}\nthe repo records:\n${recorded}\n` +
`A published build that predates a repo-side rename does this: update ` +
`framework to a build matching the repo, or — if this build is the newer ` +
`side — rewrite the marker with its layout and land that as the rename's own commit.`,
};
}
//# sourceMappingURL=layout.js.map