fix(pi): close final ownership and native UX gaps

This commit is contained in:
SaladDay
2026-08-03 11:49:56 +00:00
parent 469d6e0f59
commit afd58e31d8
15 changed files with 364 additions and 48 deletions
@@ -113,6 +113,29 @@ describe("PiNativePromptResources", () => {
);
});
it("does not offer prompt-template names that Pi cannot invoke portably", async () => {
renderResources();
const slug = screen.getByPlaceholderText("pi.prompts.templateSlug");
const create = screen.getByRole("button", {
name: "pi.prompts.createTemplate",
});
for (const invalid of ["release notes", "bad:name", "CON"]) {
fireEvent.change(slug, { target: { value: invalid } });
expect(create).toBeDisabled();
expect(
screen.getByText("pi.prompts.templateSlugInvalid"),
).toBeInTheDocument();
}
fireEvent.change(slug, { target: { value: "release.v2" } });
expect(create).toBeEnabled();
expect(
screen.queryByText("pi.prompts.templateSlugInvalid"),
).not.toBeInTheDocument();
});
it("requires confirmation before creating the dangerous SYSTEM override", async () => {
renderResources();
@@ -122,9 +145,7 @@ describe("PiNativePromptResources", () => {
fireEvent.change(instructionEditors[0], {
target: { value: "replace the system prompt" },
});
fireEvent.click(
screen.getAllByRole("button", { name: "common.save" })[0],
);
fireEvent.click(screen.getAllByRole("button", { name: "common.save" })[0]);
expect(promptsApi.replacePiPromptFile).not.toHaveBeenCalled();
expect(
+33
View File
@@ -0,0 +1,33 @@
import { describe, expect, it } from "vitest";
import { isValidPiPromptTemplateSlug } from "@/lib/piPromptSlug";
describe("isValidPiPromptTemplateSlug", () => {
it("accepts callable Unicode and dotted slugs", () => {
expect(isValidPiPromptTemplateSlug("review-pr")).toBe(true);
expect(isValidPiPromptTemplateSlug("release.v2")).toBe(true);
expect(isValidPiPromptTemplateSlug("评审")).toBe(true);
expect(isValidPiPromptTemplateSlug("SYSTEM")).toBe(true);
});
it("rejects whitespace, portable filename hazards, and Windows device names", () => {
for (const slug of [
"release notes",
"tab\tname",
"bad:name",
"bad*name",
"CON",
"con.anything",
"LPT9",
"nul.json",
]) {
expect(isValidPiPromptTemplateSlug(slug), slug).toBe(false);
}
});
it("measures the contract limit in UTF-8 bytes", () => {
expect(isValidPiPromptTemplateSlug("a".repeat(128))).toBe(true);
expect(isValidPiPromptTemplateSlug("a".repeat(129))).toBe(false);
expect(isValidPiPromptTemplateSlug("评".repeat(42))).toBe(true);
expect(isValidPiPromptTemplateSlug("评".repeat(43))).toBe(false);
});
});