fix(setup): require product evidence for FreeBuff discovery

Address review findings at their producing boundaries and preserve directly affected consumers. Source inspection and git diff --check completed. Tests and automated checks were not run at the user’s explicit request.
This commit is contained in:
morluto authored and 0xLLVEL committed 2026-10-14 11:34:39 +07:00
1 parent 783f0fe45e
commit 6092f6da7e
4 files changed
+48 -9

No files matched your search

+9 -3
View File
@@ -312,16 +312,22 @@ writing a registration ZCode cannot load.
## FreeBuff
For FreeBuff, setup writes `mcpServers.rea` into the user-scope
For FreeBuff, the proposed integration writes `mcpServers.rea` into the user-scope
`~/.agents/mcp.json` shared-agent file. Entries carry `command`, `args`, and
`env`; stdio is the default transport. FreeBuff discovers personal skills from
`env`; stdio is the default transport. The proposed personal skill location is
`~/.agents/skills`, so setup installs its skill copy there. Because the same
file doubles as ZCode's user-scope fallback (see above), a `freebuff`
registration also loads in ZCode until ZCode defines its own native server.
registration is also in the file ZCode uses until ZCode defines its own native server.
Setup and doctor detect FreeBuff from its own profile directory
(`~/.config/freebuff-desktop`), not from the shared `.agents` folder that
REA's skill installs create for every client.
The configuration write and personal skill locations are proposed integration
boundaries. FreeBuff MCP loading and skill discovery have not been verified
with an installed native FreeBuff client/version; filesystem tests cover REA's
editing, discovery, and ownership behavior only. Automatic applicability
requires the FreeBuff profile directory even when the shared MCP file exists.
FreeBuff is main-only and is not part of npm 6.4.0; `rea setup --client
freebuff` against `@latest` reports an unsupported client until a release
containing it is published.
+5
View File
@@ -1,5 +1,10 @@
# Client and agent verification
FreeBuff MCP loading and personal skill discovery are currently unverified with
an installed native client/version. Its temporary-filesystem coverage verifies
REA's configuration and applicability boundaries only; it does not establish
that FreeBuff consumes the written registration or skill.
Use these optional lanes to verify an installed client or evaluate an agent's
REA workflow. [Testing REA](testing.md) provides the normal development commands
and provider lanes. Each record below states its host, client version, model
+20 -6
View File
@@ -35,6 +35,8 @@ export interface SetupClient {
readonly msixPackage?: MsixPackageView;
/** A running client can rewrite its configuration from memory. */
readonly quitBeforeSetup?: boolean;
/** Product-specific applicability evidence when configuration storage is shared. */
readonly discoveryPaths?: readonly string[];
readonly format?:
| "json"
| "toml"
@@ -96,6 +98,7 @@ type ClientDefinition = ClientDefinitionBase &
interface ClientDefinitionBase {
readonly name: string;
readonly displayName: string;
readonly discoveryPaths?: (context: ClientPathContext) => readonly string[];
readonly format: NonNullable<SetupClient["format"]>;
readonly quitBeforeSetup?: boolean;
readonly zcodeFallbackPath?: (context: ClientPathContext) => string;
@@ -713,6 +716,9 @@ export const SUPPORTED_CLIENT_DEFINITIONS = [
configPath: ({ home }: ClientPathContext) =>
join(home, ".agents", "mcp.json"),
markerPath: freebuffProfileDirectory,
discoveryPaths: (context: ClientPathContext) => [
freebuffProfileDirectory(context),
],
format: "json",
},
{
@@ -730,14 +736,19 @@ export const SUPPORTED_CLIENT_DEFINITIONS = [
* directory override does not exist.
*/
export const clientEvidencePaths = (
client: Pick<SetupClient, "configPath" | "configPaths" | "markerPath">,
client: Pick<
SetupClient,
"configPath" | "configPaths" | "markerPath" | "discoveryPaths"
>,
): readonly string[] => [
...new Set(
[
client.configPath,
...(client.configPaths ?? []),
client.markerPath,
].filter((path): path is string => path !== undefined && path.length > 0),
(
client.discoveryPaths ?? [
client.configPath,
...(client.configPaths ?? []),
client.markerPath,
]
).filter((path): path is string => path !== undefined && path.length > 0),
),
];
@@ -805,6 +816,9 @@ export const supportedClients = (
...(definition.quitBeforeSetup === true
? { quitBeforeSetup: true }
: {}),
...(definition.discoveryPaths === undefined
? {}
: { discoveryPaths: definition.discoveryPaths(context) }),
format: definition.format,
};
} catch (cause: unknown) {
@@ -36,6 +36,7 @@ describe("FreeBuff configuration paths", () => {
displayName: "FreeBuff",
configPath: join("/home/a", ".agents", "mcp.json"),
markerPath: join("/home/a", ".config", "freebuff-desktop"),
discoveryPaths: [join("/home/a", ".config", "freebuff-desktop")],
format: "json",
});
expect(freebuffPath("/Users/a", "darwin")?.markerPath).toBe(
@@ -50,9 +51,21 @@ describe("FreeBuff configuration paths", () => {
const home = await createTestTempDirectory("rea-freebuff-shared-");
// REA's own skill installs create this directory for every client.
await mkdir(join(home, ".agents", "skills"), { recursive: true });
await writeFile(
join(home, ".agents", "mcp.json"),
'{"mcpServers":{"other":{"command":"other-server"}}}',
);
expect((await detectClients(home)).map(({ name }) => name)).not.toContain(
"freebuff",
);
expect(
(
await readClientRegistrationStatuses(home, undefined, {
platform: process.platform,
environment: {},
})
).map(({ client }) => client),
).not.toContain("freebuff");
await mkdir(join(home, ".config", "freebuff-desktop"), {
recursive: true,
@@ -85,6 +98,7 @@ describe("FreeBuff JSON registration", () => {
const client = freebuffPath(home, process.platform);
if (client === undefined) throw new Error("missing FreeBuff client");
await mkdir(dirname(client.configPath), { recursive: true });
await mkdir(client.markerPath!, { recursive: true });
return { home, client };
};