Loading page…
diff --git a/src/bounty-network-positioning.test.ts b/src/bounty-network-positioning.test.ts
new file mode 100644
index 0000000..9f61f49
--- /dev/null
+++ b/src/bounty-network-positioning.test.ts
@@ -0,0 +1,274 @@
+import { existsSync, readFileSync } from "node:fs";
+import { join } from "node:path";
+import { describe, expect, it } from "vitest";
+
+const repositoryRoot = join(import.meta.dirname, "..");
+const read = (path: string) => readFileSync(join(repositoryRoot, path), "utf8");
+
+const bountyMigration =
+ "supabase/migrations/20260811193000_recast_as_authorized_bounty_network.sql";
+
+const productModel = {
+ primaryObject: "security bounty",
+ owner: "system owner",
+ reviewer: "reviewer",
+ execution: "authorized test run",
+} as const;
+
+const majorSurfaces = [
+ "src/App.tsx",
+ "src/components/interest-mode-notice.tsx",
+ "src/components/site-header.tsx",
+ "src/features/auth/auth-page.tsx",
+ "src/features/ideas/idea-discovery-page.tsx",
+ "src/features/ideas/idea-detail-page.tsx",
+ "src/features/ideas/idea-editor-page.tsx",
+ "src/features/ideas/idea-interest-panel.tsx",
+ "src/features/ideas/idea-validation-panel.tsx",
+ "src/features/ideas/idea-validation-evidence-panel.tsx",
+ "src/features/profiles/profile-page.tsx",
+ "src/features/pilots/pilot-page.tsx",
+ "src/features/admin/admin-page.tsx",
+];
+
+const bountyCatalog = [
+ [
+ "00000000-0000-4000-8000-000000000201",
+ "clean-air-library",
+ "Smoke Sensor Spoofing Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000202",
+ "repair-commons",
+ "Repair Station Privilege Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000203",
+ "neighbor-ride-credits",
+ "Trip Relay Metadata Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000204",
+ "after-dark-storefronts",
+ "Night Install Tamper Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000205",
+ "shade-stop-network",
+ "Transit Sensor Blind-Spot Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000206",
+ "skill-swap-saturdays",
+ "Repair Playbook Injection Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000207",
+ "civic-accessibility-lab",
+ "Crossing Signal Failure Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000208",
+ "block-ready-kits",
+ "Outage Kit Supply-Chain Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000209",
+ "device-liberation-lab",
+ "Device Unlock Boundary Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000210",
+ "file-rescue-cooperative",
+ "File Recovery Integrity Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000211",
+ "cloud-exit-toolkit",
+ "Cloud Exit Data-Loss Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000212",
+ "private-ai-workbench",
+ "Local AI Data-Leak Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000213",
+ "home-lab-defense-clinic",
+ "Home Lab Exposure Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000214",
+ "community-compute-cooperative",
+ "Shared Compute Escape Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000215",
+ "offline-mesh-field-kit",
+ "Mesh Relay Spoofing Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000216",
+ "open-repair-atlas",
+ "Repair Atlas Poisoning Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000217",
+ "accessible-interface-retrofit-lab",
+ "Accessible UI Regression Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000218",
+ "project-time-capsule",
+ "Time Capsule Disclosure Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000219",
+ "waste-heat-works",
+ "Heat Controller Fail-Safe Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000220",
+ "model-commons-lab",
+ "Model Eval Poisoning Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000221",
+ "glass-box-sensor-network",
+ "Plate Reader Privacy Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000222",
+ "oral-history-provenance-lab",
+ "Oral History Provenance Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000223",
+ "neighborhood-incident-relay",
+ "Incident Relay Impersonation Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000224",
+ "phishing-drill-library",
+ "Phishing Drill Containment Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000225",
+ "water-sensor-integrity-watch",
+ "Water Sensor Spoofing Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000226",
+ "clinic-device-privacy-check",
+ "Clinic Device Privacy Bounty",
+ ],
+ [
+ "00000000-0000-4000-8000-000000000227",
+ "software-supply-chain-clinic",
+ "Dependency Substitution Bounty",
+ ],
+] as const;
+
+describe("focused authorized security bounty platform", () => {
+ it("uses one focused product model instead of competing hacker metaphors", () => {
+ const visibleCopy = majorSurfaces.map(read).join("\n");
+ for (const term of Object.values(productModel)) {
+ expect(visibleCopy).toMatch(new RegExp(term, "i"));
+ }
+ for (const distractingMetaphor of [
+ /channel open/i,
+ /dead drop/i,
+ /decrypt(?:ing)?/i,
+ /swagger/i,
+ /wreckage/i,
+ /kill chain/i,
+ /clean kill/i,
+ /\bhunters?\b/i,
+ /\bdossiers?\b/i,
+ /\breceipts?\b/i,
+ /\bproof-gates?\b/i,
+ /\bbounty board\b/i,
+ /\battack sectors?\b/i,
+ /\btarget intel\b/i,
+ /\b(?:clear|clean) trace\b/i,
+ /\bbounty killed\b/i,
+ ]) {
+ expect(visibleCopy).not.toMatch(distractingMetaphor);
+ }
+ expect(visibleCopy).not.toMatch(/\bSkull\b/);
+ expect(read("src/components/interest-mode-notice.tsx")).not.toMatch(
+ /\bCrosshair\b/,
+ );
+ expect(read("src/components/site-header.tsx")).toContain("IdeaScape");
+ expect(read("src/components/site-header.tsx")).not.toContain(">Ideascape<");
+ });
+
+ it("uses the same security-bounty vocabulary on every major surface", () => {
+ for (const path of majorSurfaces) {
+ expect(read(path), `${path} lacks focused product language`).toMatch(
+ /\b(?:security bount(?:y|ies)|system owner|reviewer|authorized test run|rules of engagement)\b/i,
+ );
+ }
+ });
+
+ it("makes authorization the non-negotiable rule without claiming platform payouts", () => {
+ const visibleCopy = majorSurfaces.map(read).join("\n");
+ expect(visibleCopy).toMatch(/no authorization, no test/i);
+ expect(visibleCopy).toMatch(/written permission/i);
+ expect(visibleCopy).toMatch(/does not handle payouts/i);
+ expect(visibleCopy).not.toMatch(
+ /\b(?:hack anything|no rules|unauthorized targets?|guaranteed payouts?|instant cash)\b/i,
+ );
+ });
+
+ it("keeps the original private-interest meanings instead of turning them into hacker roles", () => {
+ const interestPanel = read("src/features/ideas/idea-interest-panel.tsx");
+ for (const label of [
+ "I would use this",
+ "I would help build it",
+ "I could join an authorized test run",
+ "I have relevant expertise",
+ "Keep me updated",
+ ]) {
+ expect(interestPanel).toContain(label);
+ }
+ expect(interestPanel).toMatch(/private interest/i);
+ expect(interestPanel).toMatch(/grants no access/i);
+ expect(interestPanel).toContain('value: "pilot"');
+ });
+
+ it("uses a black, bone, and signal-orange code-brutalist system", () => {
+ const css = read("src/index.css");
+ expect(css).toContain("--background: #050505");
+ expect(css).toContain("--foreground: #f2efe6");
+ expect(css).toContain("--signal: #ff5a1f");
+ expect(css).toMatch(/\.packet-trace/);
+ expect(css).toMatch(/\.bounty-grid/);
+ });
+
+ it("rewrites all 27 deterministic examples as distinct authorized bounties", () => {
+ expect(existsSync(join(repositoryRoot, bountyMigration))).toBe(true);
+ const migration = read(bountyMigration);
+ for (const [id, slug, title] of bountyCatalog) {
+ expect(migration).toContain(id);
+ expect(migration).toContain(slug);
+ expect(migration).toContain(title);
+ }
+ expect(migration.match(/ Bounty'/g)).toHaveLength(27);
+ expect(migration).toMatch(
+ /requires all 27 expected bounty UUID\/slug pairs/i,
+ );
+ expect(migration).toMatch(
+ /authorized (?:sandbox|environment|assets?|systems?)/i,
+ );
+ expect(migration).toMatch(/rules of engagement/i);
+ expect(migration).toMatch(/proof_required/i);
+ expect(migration).not.toContain("ready-in-range");
+ expect(migration).not.toContain(
+ "Authorized security bounty illustration for ",
+ );
+ expect(migration).toContain(
+ "Time Capsule Disclosure Bounty authorized test run",
+ );
+ });
+});
diff --git a/src/components/interest-mode-notice.tsx b/src/components/interest-mode-notice.tsx
index d9d40e1..c854de0 100644
--- a/src/components/interest-mode-notice.tsx
+++ b/src/components/interest-mode-notice.tsx
@@ -1,4 +1,4 @@
-import { ArrowRight, FlaskConical } from "lucide-react";
+import { ArrowRight, FileCheck2 } from "lucide-react";
import { Link } from "react-router-dom";
import { buttonVariants } from "@/components/ui/button";
import { cn } from "@/lib/utils";
@@ -14,7 +14,7 @@ export function InterestModeNotice({
}: InterestModeNoticeProps) {
return (
-
+
-
Security review mode
+
Rules of engagement
- These are security briefs, not deployment approvals. Every brief
- states a threat scenario, control boundary, and proof standard. No
- payment, production access, or operational authority changes
- hands.
+ These are authorized security bounties, not invitations to probe
+ random systems. Test only assets you own or have written
+ permission to test. IdeaScape does not handle payouts, grant
+ production access, or transfer operational authority. No
+ authorization, no test.
@@ -44,7 +45,7 @@ export function InterestModeNotice({
})}
to="/sign-up"
>
- Join the security review
+ Create an account
) : null}
diff --git a/src/components/site-header.test.tsx b/src/components/site-header.test.tsx
index e720a1f..5a31fb5 100644
--- a/src/components/site-header.test.tsx
+++ b/src/components/site-header.test.tsx
@@ -16,14 +16,16 @@ describe("SiteHeader", () => {
expect(
screen.getByRole("link", { name: /ideascape home/i }),
).toHaveAttribute("href", "/");
+ expect(screen.getByText("IdeaScape", { exact: true })).toBeInTheDocument();
+ expect(screen.queryByText(/channel open/i)).not.toBeInTheDocument();
expect(
- screen.getByText(/security validation fieldwork/i),
+ screen.getByText("Authorized security bounties", { exact: true }),
).toBeInTheDocument();
expect(
- screen.getByRole("link", { name: /review security briefs/i }),
+ screen.getByRole("link", { name: /security bounties/i }),
).toHaveAttribute("href", "/ideas");
const startIdeaLink = screen.getByRole("link", {
- name: /draft a security brief/i,
+ name: /publish a bounty/i,
});
expect(startIdeaLink).toHaveAttribute("href", "/ideas/new");
expect(startIdeaLink).toHaveClass("bg-signal");
diff --git a/src/components/site-header.tsx b/src/components/site-header.tsx
index cca116e..985340b 100644
--- a/src/components/site-header.tsx
+++ b/src/components/site-header.tsx
@@ -1,4 +1,4 @@
-import { ArrowRight, MapPinned } from "lucide-react";
+import { ArrowRight, Radio, ShieldCheck } from "lucide-react";
import type { ReactNode } from "react";
import { Link } from "react-router-dom";
import { buttonVariants } from "@/components/ui/button";
@@ -6,7 +6,7 @@ import { cn } from "@/lib/utils";
export function SiteHeader({
account,
- exploreLabel = "Review security briefs",
+ exploreLabel = "Security bounties",
exploreTo = "/ideas",
showExplore = true,
showStartIdea = true,
@@ -21,20 +21,20 @@ export function SiteHeader({
-
-
+
+
- Ideascape
+ IdeaScape
- Security validation fieldwork
+ Authorized security bounties
@@ -60,7 +60,7 @@ export function SiteHeader({
className={cn(buttonVariants({ size: "sm" }), "px-3")}
to="/ideas/new"
>
- Draft a security brief
+ Publish a bounty
) : null}
@@ -69,6 +69,20 @@ export function SiteHeader({
{account ? {account}
: null}
+
+
+
+ {
+ " AUTHORIZED SECURITY BOUNTIES · AUTHORIZED TARGETS ONLY · WRITTEN PERMISSION REQUIRED · REPRODUCIBLE PROOF REQUIRED · NO AUTHORIZATION, NO TEST · "
+ }
+
+
+
+ {
+ " AUTHORIZED SECURITY BOUNTIES · AUTHORIZED TARGETS ONLY · WRITTEN PERMISSION REQUIRED · REPRODUCIBLE PROOF REQUIRED · NO AUTHORIZATION, NO TEST · "
+ }
+
+
);
}
diff --git a/src/features/admin/admin-page.test.tsx b/src/features/admin/admin-page.test.tsx
index 4d9525a..3b807cc 100644
--- a/src/features/admin/admin-page.test.tsx
+++ b/src/features/admin/admin-page.test.tsx
@@ -109,7 +109,9 @@ describe("AdminPage", () => {
renderPage();
expect(
- await screen.findByRole("heading", { name: /operations dashboard/i }),
+ await screen.findByRole("heading", {
+ name: /security bounty operations/i,
+ }),
).toBeInTheDocument();
const overview = screen.getByRole("region", {
name: /operational overview/i,
@@ -124,10 +126,10 @@ describe("AdminPage", () => {
screen.getByRole("link", { name: /project time capsule/i }),
).toHaveAttribute("href", "/ideas/project-time-capsule");
expect(
- screen.getByRole("columnheader", { name: /security brief/i }),
+ screen.getByRole("columnheader", { name: /bounty/i }),
).toBeInTheDocument();
expect(
- screen.getByRole("columnheader", { name: /validation signals/i }),
+ screen.getByRole("columnheader", { name: /readiness signals/i }),
).toBeInTheDocument();
expect(
screen.getByText(/respondent identities are excluded/i),
@@ -151,7 +153,9 @@ describe("AdminPage", () => {
const page = renderPage(queryClient);
expect(
- await screen.findByRole("heading", { name: /operations dashboard/i }),
+ await screen.findByRole("heading", {
+ name: /security bounty operations/i,
+ }),
).toBeInTheDocument();
expect(await screen.findByText("Project Time Capsule")).toBeInTheDocument();
@@ -188,7 +192,7 @@ describe("AdminPage", () => {
renderPage();
expect(
- await screen.findByText(/no published security brief activity yet/i),
+ await screen.findByText(/no published bounty activity yet/i),
).toBeInTheDocument();
});
diff --git a/src/features/admin/admin-page.tsx b/src/features/admin/admin-page.tsx
index 04fca21..07f2f4d 100644
--- a/src/features/admin/admin-page.tsx
+++ b/src/features/admin/admin-page.tsx
@@ -64,7 +64,7 @@ export function AdminPage() {
return (
Sign in
@@ -128,7 +128,7 @@ function AdminDashboard({
return (
<>
-
+
@@ -138,12 +138,12 @@ function AdminDashboard({
Private operations
- Operations dashboard
+ Security bounty operations
- A privacy-preserving view of security briefs, validation
- evidence, and bounded-exercise readiness. Counts are live at
- request time.
+ A privacy-preserving view of security bounties, readiness
+ responses, and authorized test-run plans. Counts are live at
+ request time; individual response histories remain private.
Generated{" "}
@@ -159,37 +159,37 @@ function AdminDashboard({
@@ -198,32 +198,32 @@ function AdminDashboard({
- Published security briefs only
+ Published bounties only
- Security brief activity
+ Security bounty activity
- Sorted by combined validation signals, review responses, and
- exercise applications.
+ Sorted by private readiness signals, aggregate responses, and
+ historical test-run applications.
{isActivityLoading ? (
- Loading security brief activity…
+ Loading bounty activity…
) : null}
{isActivityError ? (
- Unable to load security brief activity. The dashboard summary is
+ Unable to load bounty activity. The control-room summary is
still available.
) : null}
{!isActivityLoading && !isActivityError && activity.length === 0 ? (
- No published security brief activity yet.
+ No published bounty activity yet.
) : null}
{activity.length > 0 ? (
@@ -232,13 +232,13 @@ function AdminDashboard({
- Security brief
+ Bounty
- Validation signals
+ Readiness signals
- Validation
+ Responses
Applications
@@ -285,7 +285,7 @@ function AdminDashboard({
Privacy boundary
Respondent identities are excluded, draft content is excluded,
- and pilot application details are excluded. This dashboard
+ and test-run application details are excluded. This dashboard
exposes operational aggregates, not individual histories.
@@ -326,7 +326,7 @@ function MetricCard({
}
function AdminStatusPage({
- heading = "Ideascape operations",
+ heading = "IdeaScape bounty operations",
message,
isError = false,
action,
diff --git a/src/features/auth/auth-callback-page.tsx b/src/features/auth/auth-callback-page.tsx
index 9ee8f8e..c0b37db 100644
--- a/src/features/auth/auth-callback-page.tsx
+++ b/src/features/auth/auth-callback-page.tsx
@@ -38,7 +38,7 @@ export function AuthCallbackPage() {
className="mt-3 text-sm leading-6 text-muted-foreground"
role="status"
>
- Your authentication provider is returning you to Ideascape. Your
+ Your authentication provider is returning you to IdeaScape. Your
session will be restored automatically.
- Your Ideascape session is ready.
+ Your IdeaScape session is ready.
- Ideascape
+ IdeaScape
{isSignUpComplete ? (
@@ -116,8 +116,8 @@ export function AuthPage({ mode }: AuthPageProps) {
{isSignIn
- ? "Welcome back. Continue reviewing threats, controls, and evidence."
- : "Create an operator account to draft and challenge security briefs."}
+ ? "Welcome back. Review current security bounties."
+ : "Create an account to publish and review authorized security bounties."}
@@ -197,7 +197,7 @@ export function AuthPage({ mode }: AuthPageProps) {
- {isSignIn ? "New to Ideascape? " : "Already have an account? "}
+ {isSignIn ? "New to IdeaScape? " : "Already have an account? "}
{
renderDetail();
expect(screen.getByRole("status")).toHaveTextContent(
- /loading security brief/i,
+ /loading security bounty/i,
);
});
@@ -120,29 +120,30 @@ describe("IdeaDetailPage", () => {
).toBeInTheDocument();
expect(screen.getByText(idea.summary)).toBeInTheDocument();
expect(screen.getByText(idea.description)).toBeInTheDocument();
- const securityCase = screen.getByRole("region", {
- name: /^security case: what must be true before this expands$/i,
- });
+ const securityCase = screen
+ .getByRole("heading", { name: /scope the test. verify the result/i })
+ .closest("section");
+ expect(securityCase).not.toBeNull();
expect(
- within(securityCase).getByText("Threat scenario"),
+ within(securityCase as HTMLElement).getByText("Attack scenario"),
).toBeInTheDocument();
expect(
- within(securityCase).getByText(idea.threat_scenario),
+ within(securityCase as HTMLElement).getByText(idea.threat_scenario),
).toBeInTheDocument();
expect(
- within(securityCase).getByText("Control boundary"),
+ within(securityCase as HTMLElement).getByText("Rules of engagement"),
).toBeInTheDocument();
expect(
- within(securityCase).getByText(idea.control_boundary),
+ within(securityCase as HTMLElement).getByText(idea.control_boundary),
).toBeInTheDocument();
expect(
- within(securityCase).getByText("Proof required"),
+ within(securityCase as HTMLElement).getByText("Proof required"),
).toBeInTheDocument();
expect(
- within(securityCase).getByText(idea.proof_required),
+ within(securityCase as HTMLElement).getByText(idea.proof_required),
).toBeInTheDocument();
expect(screen.getByText("Software & Systems")).toBeInTheDocument();
- expect(screen.getByText("Security brief")).toBeInTheDocument();
+ expect(screen.getByText("Bounty open")).toBeInTheDocument();
expect(screen.getByRole("link", { name: /idea creator/i })).toHaveAttribute(
"href",
`/profiles/${idea.creator.username}`,
@@ -151,17 +152,17 @@ describe("IdeaDetailPage", () => {
screen.getByRole("img", { name: /solar desalination prototype/i }),
).toHaveAttribute("src", idea.media[0].url);
expect(
- screen.getByRole("link", { name: /back to security briefs/i }),
+ screen.getByRole("link", { name: /back to security bounties/i }),
).toHaveAttribute("href", "/ideas");
expect(
- screen.getByRole("note", { name: /security review mode/i }),
- ).toHaveTextContent(/security briefs, not deployment approvals/i);
+ screen.getByRole("note", { name: /authorized bounty rules/i }),
+ ).toHaveTextContent(/no authorization, no test/i);
expect(
await screen.findByRole("heading", {
- name: /how are you interested in this security brief/i,
+ name: /how are you interested in this security bounty/i,
}),
).toBeInTheDocument();
- expect(screen.getByText(/3 validation signals/i)).toBeInTheDocument();
+ expect(screen.getByText(/3 readiness signals/i)).toBeInTheDocument();
});
it("omits the security case when a creator has not defined one", async () => {
@@ -178,8 +179,8 @@ describe("IdeaDetailPage", () => {
await screen.findByRole("heading", { name: idea.title }),
).toBeInTheDocument();
expect(
- screen.queryByRole("region", {
- name: /^security case: what must be true before this expands$/i,
+ screen.queryByRole("heading", {
+ name: /scope the test. verify the result/i,
}),
).not.toBeInTheDocument();
});
@@ -210,14 +211,14 @@ describe("IdeaDetailPage", () => {
renderDetail();
const related = await screen.findByRole("region", {
- name: /more software & systems security briefs/i,
+ name: /more software & systems bounties/i,
});
expect(
within(related).getByRole("link", { name: /view private ai workbench/i }),
).toHaveAttribute("href", "/ideas/private-ai-workbench");
expect(
within(related).getByRole("link", {
- name: /review all software & systems briefs/i,
+ name: /view all software & systems bounties/i,
}),
).toHaveAttribute("href", "/ideas?category=technology");
expect(
@@ -256,7 +257,7 @@ describe("IdeaDetailPage", () => {
}),
).toBeInTheDocument();
expect(
- screen.getByRole("link", { name: /view pilot plan/i }),
+ screen.getByRole("link", { name: /view test-run plan/i }),
).toHaveAttribute("href", "/pilots/project-time-capsule");
});
@@ -305,10 +306,10 @@ describe("IdeaDetailPage", () => {
renderDetail("missing-idea");
expect(
- await screen.findByRole("heading", { name: /security brief not found/i }),
+ await screen.findByRole("heading", { name: /bounty not found/i }),
).toBeInTheDocument();
expect(
- screen.getByRole("link", { name: /review security briefs/i }),
+ screen.getByRole("link", { name: /return to security bounties/i }),
).toHaveAttribute("href", "/ideas");
});
@@ -320,7 +321,7 @@ describe("IdeaDetailPage", () => {
renderDetail();
expect(await screen.findByRole("alert")).toHaveTextContent(
- "Unable to load this security brief. Please try again.",
+ "Unable to load this bounty. Try again.",
);
expect(screen.getByRole("alert")).not.toHaveTextContent(/sensitive/i);
});
diff --git a/src/features/ideas/idea-detail-page.tsx b/src/features/ideas/idea-detail-page.tsx
index 33ae674..0c51f67 100644
--- a/src/features/ideas/idea-detail-page.tsx
+++ b/src/features/ideas/idea-detail-page.tsx
@@ -11,6 +11,7 @@ import { Link, useParams } from "react-router-dom";
import { InterestModeNotice } from "@/components/interest-mode-notice";
import { SiteHeader } from "@/components/site-header";
import { buttonVariants } from "@/components/ui/button";
+import { cn } from "@/lib/utils";
import {
getPublishedIdea,
type IdeaMedia,
@@ -22,12 +23,12 @@ import { IdeaValidationEvidencePanel } from "@/features/ideas/idea-validation-ev
import { IdeaValidationPanel } from "@/features/ideas/idea-validation-panel";
const statusLabels: Record = {
- published: "Security brief",
- funding: "Control review",
- funded: "Pilot approved",
- in_progress: "Exercise active",
- completed: "Evidence published",
- cancelled: "Closed",
+ published: "Bounty open",
+ funding: "Rules under review",
+ funded: "Test run approved",
+ in_progress: "Authorized test active",
+ completed: "Results published",
+ cancelled: "Bounty closed",
};
function isSafeMediaUrl(value: string): boolean {
@@ -44,7 +45,7 @@ function IdeaMediaItem({ media }: { media: IdeaMedia }) {
return null;
}
- const accessibleName = media.alt_text || "Security brief media";
+ const accessibleName = media.alt_text || "Authorized bounty media";
if (media.kind === "video") {
return (
@@ -88,12 +89,12 @@ function IdeaSecurityCase({
}) {
const claims = [
{
- label: "Threat scenario",
+ label: "Attack scenario",
value: threatScenario,
icon: TriangleAlert,
},
{
- label: "Control boundary",
+ label: "Rules of engagement",
value: controlBoundary,
icon: ShieldCheck,
},
@@ -113,19 +114,19 @@ function IdeaSecurityCase({
- Security case
+ Security bounty
- Security case: what must be true before this expands
+ Scope the test. Verify the result.
- A proposed control is not a guarantee. This brief names the failure
- path, the operating boundary, and the evidence needed to earn a larger
- test.
+ A security bounty is not permission by itself. This listing names the
+ attack scenario, rules of engagement, and proof required before an
+ authorized test run.
@@ -168,7 +169,7 @@ export function IdeaDetailPage() {
return (
<>
-
+
{ideaQuery.isPending ? (
@@ -180,7 +181,7 @@ export function IdeaDetailPage() {
className="size-5 animate-spin text-primary"
aria-hidden="true"
/>
- Loading security brief…
+ Loading security bounty…
) : null}
@@ -190,7 +191,7 @@ export function IdeaDetailPage() {
Something went wrong
- Unable to load this security brief. Please try again.
+ Unable to load this bounty. Try again.
) : null}
@@ -201,17 +202,17 @@ export function IdeaDetailPage() {
- Security brief not found
+ Bounty not found
- This security brief may be private, unpublished, or no longer
+ This bounty may be private, unpublished, closed, or no longer
available.
- Review security briefs
+ Return to security bounties
) : null}
@@ -237,7 +238,7 @@ export function IdeaDetailPage() {
{ideaQuery.data.summary}
- Brief owner{" "}
+ System owner{" "}
0 ? (
{ideaQuery.data.media.map((media) => (
@@ -271,7 +272,7 @@ export function IdeaDetailPage() {
className="text-2xl font-semibold tracking-tight"
id="about-idea"
>
- System description
+ Bounty scope
@@ -296,22 +297,22 @@ export function IdeaDetailPage() {
- Evidence before commitment
+ Authorized test run
- See the precommitted pilot rules
+ Review the test-run plan
- Review the evidence window, capacity, safety boundaries,
- and continue, revise, or archive thresholds before intake
- opens.
+ Review the evidence window, authorized assets, stop
+ conditions, and continue, revise, or close thresholds
+ before testing begins.
- View pilot plan
+ View test-run plan
@@ -325,13 +326,13 @@ export function IdeaDetailPage() {
- Continue the review
+ Related security bounties
- Review all {ideaQuery.data.category.name} briefs
+ View all {ideaQuery.data.category.name} bounties
-
+
{relatedIdeas.map((relatedIdea) => {
const cover = relatedIdea.media.find(
(media) =>
diff --git a/src/features/ideas/idea-discovery-page.test.tsx b/src/features/ideas/idea-discovery-page.test.tsx
index 0ef62a7..f62566b 100644
--- a/src/features/ideas/idea-discovery-page.test.tsx
+++ b/src/features/ideas/idea-discovery-page.test.tsx
@@ -85,7 +85,7 @@ describe("IdeaDiscoveryPage", () => {
renderDiscovery();
expect(screen.getByRole("status")).toHaveTextContent(
- /loading security briefs/i,
+ /loading security bounties/i,
);
});
@@ -95,7 +95,9 @@ describe("IdeaDiscoveryPage", () => {
renderDiscovery();
expect(
- await screen.findByRole("heading", { name: /review security briefs/i }),
+ await screen.findByRole("heading", {
+ name: /authorized security bounties/i,
+ }),
).toBeInTheDocument();
expect(
screen.queryByRole("link", { name: /^explore ideas$/i }),
@@ -108,14 +110,14 @@ describe("IdeaDiscoveryPage", () => {
expect(screen.getAllByText("Software & Systems").length).toBeGreaterThan(0);
expect(
screen.getByText(
- /every brief names a threat scenario, control boundary, and proof required/i,
+ /every bounty names the attack scenario, rules of engagement, and proof required/i,
),
).toBeInTheDocument();
- expect(screen.getByText("Security brief")).toBeInTheDocument();
- expect(screen.getByText("Security focus")).toBeInTheDocument();
- expect(screen.getByText(idea.threat_scenario)).toBeInTheDocument();
- expect(screen.getByText("1 security brief")).toBeInTheDocument();
- expect(screen.getByText("4 validation signals")).toBeInTheDocument();
+ expect(screen.getByText("Bounty open")).toBeInTheDocument();
+ expect(screen.queryByText("Attack scenario")).not.toBeInTheDocument();
+ expect(screen.queryByText(idea.threat_scenario)).not.toBeInTheDocument();
+ expect(screen.getByText("1 bounty")).toBeInTheDocument();
+ expect(screen.getByText("4 readiness signals")).toBeInTheDocument();
expect(
screen.getByRole("img", { name: /solar desalination prototype/i }),
).toHaveAttribute("src", idea.media[0].url);
@@ -124,8 +126,8 @@ describe("IdeaDiscoveryPage", () => {
`/profiles/${idea.creator.username}`,
);
expect(
- screen.getByRole("note", { name: /security review mode/i }),
- ).toHaveTextContent(/security briefs, not deployment approvals/i);
+ screen.getByRole("note", { name: /authorized bounty rules/i }),
+ ).toHaveTextContent(/does not handle payouts/i);
});
it("does not claim a security case for concepts without all three fields", async () => {
@@ -143,7 +145,8 @@ describe("IdeaDiscoveryPage", () => {
expect(
await screen.findByRole("link", { name: `View ${idea.title}` }),
).toBeInTheDocument();
- expect(screen.queryByText("Security focus")).not.toBeInTheDocument();
+ expect(screen.queryByText("Attack scenario")).not.toBeInTheDocument();
+ expect(screen.queryByText(idea.threat_scenario)).not.toBeInTheDocument();
});
it("restores a category filter from the URL and only shows matching concepts", async () => {
@@ -152,7 +155,7 @@ describe("IdeaDiscoveryPage", () => {
renderDiscovery("/ideas?category=technology");
expect(
- await screen.findByRole("combobox", { name: /security domain/i }),
+ await screen.findByRole("combobox", { name: /security area/i }),
).toHaveValue("technology");
expect(
screen.getByRole("link", { name: `View ${idea.title}` }),
@@ -160,9 +163,7 @@ describe("IdeaDiscoveryPage", () => {
expect(
screen.queryByRole("link", { name: `View ${healthIdea.title}` }),
).not.toBeInTheDocument();
- expect(
- screen.getByText("Showing 1 of 2 security briefs"),
- ).toBeInTheDocument();
+ expect(screen.getByText("Showing 1 of 2 bounties")).toBeInTheDocument();
});
it("restores search from the URL and offers a clear path when no concepts match", async () => {
@@ -172,7 +173,7 @@ describe("IdeaDiscoveryPage", () => {
renderDiscovery("/ideas?q=heat");
const search = await screen.findByRole("searchbox", {
- name: /search security briefs/i,
+ name: /search security bounties/i,
});
expect(search).toHaveValue("heat");
expect(
@@ -187,7 +188,7 @@ describe("IdeaDiscoveryPage", () => {
expect(
screen.getByRole("heading", {
- name: /no security briefs match these filters/i,
+ name: /no security bounties match these filters/i,
}),
).toBeInTheDocument();
await user.click(screen.getByRole("button", { name: /clear filters/i }));
@@ -201,7 +202,7 @@ describe("IdeaDiscoveryPage", () => {
renderDiscovery();
expect(
- await screen.findByText("Be first to add a validation signal"),
+ await screen.findByText("Be first to leave a readiness signal"),
).toBeInTheDocument();
expect(screen.queryByText(/fund now/i)).not.toBeInTheDocument();
});
@@ -213,11 +214,11 @@ describe("IdeaDiscoveryPage", () => {
expect(
await screen.findByRole("heading", {
- name: /the first security briefs are taking shape/i,
+ name: /no security bounties yet/i,
}),
).toBeInTheDocument();
const startIdeaLinks = screen.getAllByRole("link", {
- name: /draft a security brief/i,
+ name: /publish a bounty/i,
});
expect(startIdeaLinks).toHaveLength(2);
for (const link of startIdeaLinks) {
@@ -233,9 +234,9 @@ describe("IdeaDiscoveryPage", () => {
renderDiscovery();
expect(await screen.findByRole("alert")).toHaveTextContent(
- "Unable to load security briefs. Please try again.",
+ "Unable to load security bounties. Try again.",
);
- expect(screen.getByText("Catalog unavailable")).toBeInTheDocument();
+ expect(screen.getByText("Listings unavailable")).toBeInTheDocument();
expect(screen.getByRole("alert")).not.toHaveTextContent(/sensitive/i);
});
@@ -249,12 +250,12 @@ describe("IdeaDiscoveryPage", () => {
});
expect(await screen.findByRole("alert")).toHaveTextContent(
- "Unable to refresh security briefs. Showing the latest available catalog.",
+ "Unable to refresh security bounties. Showing the latest available listings.",
);
- expect(screen.getByText("1 security brief")).toBeInTheDocument();
+ expect(screen.getByText("1 bounty")).toBeInTheDocument();
expect(
screen.getByRole("link", { name: `View ${idea.title}` }),
).toBeInTheDocument();
- expect(screen.queryByText("Catalog unavailable")).not.toBeInTheDocument();
+ expect(screen.queryByText("Listings unavailable")).not.toBeInTheDocument();
});
});
diff --git a/src/features/ideas/idea-discovery-page.tsx b/src/features/ideas/idea-discovery-page.tsx
index 3362a6b..4062965 100644
--- a/src/features/ideas/idea-discovery-page.tsx
+++ b/src/features/ideas/idea-discovery-page.tsx
@@ -5,7 +5,6 @@ import {
ArrowUpRight,
LoaderCircle,
Search,
- ShieldCheck,
Sparkles,
UsersRound,
} from "lucide-react";
@@ -19,12 +18,12 @@ import {
} from "@/features/ideas/idea-discovery-service";
const statusLabels: Record
= {
- published: "Security brief",
- funding: "Control review",
- funded: "Pilot approved",
- in_progress: "Exercise active",
- completed: "Evidence published",
- cancelled: "Closed",
+ published: "Bounty open",
+ funding: "Rules under review",
+ funded: "Test run approved",
+ in_progress: "Authorized test active",
+ completed: "Results published",
+ cancelled: "Bounty closed",
};
function isSafeImageUrl(value: string): boolean {
@@ -38,18 +37,18 @@ function isSafeImageUrl(value: string): boolean {
function interestLabel(count: number): string {
if (count === 0) {
- return "Be first to add a validation signal";
+ return "Be first to leave a readiness signal";
}
if (count === 1) {
- return "1 validation signal";
+ return "1 readiness signal";
}
- return `${count} validation signals`;
+ return `${count} readiness signals`;
}
-function conceptCountLabel(count: number): string {
- return `${count} security ${count === 1 ? "brief" : "briefs"}`;
+function bountyCountLabel(count: number): string {
+ return `${count} ${count === 1 ? "bounty" : "bounties"}`;
}
export function IdeaDiscoveryPage() {
@@ -133,28 +132,29 @@ export function IdeaDiscoveryPage() {
- Security validation catalog
+ Security bounties · public listings
- Review security briefs
+ Authorized{" "}
+ security bounties
- Every brief names a threat scenario, control boundary, and
- proof required before it can advance to a bounded exercise.
+ Every bounty names the attack scenario, rules of engagement,
+ and proof required before an authorized test can begin.
{ideasQuery.isError && ideasQuery.data === undefined
? "Catalog status"
- : "Under review"}
+ : "Listing status"}
{ideasQuery.isError && ideasQuery.data === undefined
- ? "Catalog unavailable"
+ ? "Listings unavailable"
: ideasQuery.data
- ? conceptCountLabel(ideasQuery.data.length)
- : "Loading security briefs"}
+ ? bountyCountLabel(ideasQuery.data.length)
+ : "Loading security bounties"}
@@ -162,12 +162,12 @@ export function IdeaDiscoveryPage() {
{ideasQuery.data && ideasQuery.data.length > 0 ? (
- Filter security briefs
+ Filter authorized bounties
- Search security briefs
+ Search security bounties
updateSearch(event.target.value)}
- placeholder="Try files, firmware, or local AI"
+ placeholder="Try spoofing, privacy, or supply chain"
type="search"
value={searchTerm}
/>
@@ -187,14 +187,14 @@ export function IdeaDiscoveryPage() {
className="grid gap-2 text-sm font-semibold"
htmlFor="category-filter"
>
- Security domain
+ Security area
updateCategory(event.target.value)}
value={selectedCategory}
>
- All security domains
+ All security areas
{categories.map((category) => (
{category.name}
@@ -207,7 +207,7 @@ export function IdeaDiscoveryPage() {
role="status"
>
Showing {visibleIdeas.length} of {ideasQuery.data.length}{" "}
- security briefs
+ bounties
) : null}
@@ -221,7 +221,7 @@ export function IdeaDiscoveryPage() {
className="size-5 animate-spin text-primary"
aria-hidden="true"
/>
- Loading security briefs…
+ Loading security bounties…
) : null}
@@ -229,8 +229,8 @@ export function IdeaDiscoveryPage() {
{ideasQuery.data === undefined
- ? "Unable to load security briefs. Please try again."
- : "Unable to refresh security briefs. Showing the latest available catalog."}
+ ? "Unable to load security bounties. Try again."
+ : "Unable to refresh security bounties. Showing the latest available listings."}
) : null}
@@ -241,17 +241,17 @@ export function IdeaDiscoveryPage() {
- The first security briefs are taking shape
+ No security bounties yet
- Be the first operator to publish a threat scenario, bounded
- control, and reproducible proof standard.
+ Be the first system owner to publish an authorized target,
+ clear rules of engagement, and a reproducible proof standard.
- Draft a security brief
+ Publish a bounty
@@ -265,11 +265,11 @@ export function IdeaDiscoveryPage() {
- No security briefs match these filters
+ No security bounties match these filters
- Try a broader search, choose another security domain, or reset
- the catalog.
+ Try a broader search, choose another security area, or clear
+ the filters.
0 ? (
-
+
{visibleIdeas.map((idea) => {
const cover = idea.media.find(
(media) =>
@@ -294,7 +294,7 @@ export function IdeaDiscoveryPage() {
return (
) : null}
-
+
{idea.category ? (
@@ -329,25 +329,6 @@ export function IdeaDiscoveryPage() {
{idea.summary}
- {idea.threat_scenario &&
- idea.control_boundary &&
- idea.proof_required ? (
-
-
-
- Security focus
-
-
- {idea.threat_scenario}
-
-
- ) : null}
By{" "}
diff --git a/src/features/ideas/idea-editor-page.test.tsx b/src/features/ideas/idea-editor-page.test.tsx
index 07a2cdd..bea9d5a 100644
--- a/src/features/ideas/idea-editor-page.test.tsx
+++ b/src/features/ideas/idea-editor-page.test.tsx
@@ -81,18 +81,18 @@ function renderEditor(path = "/ideas/new") {
async function completeForm(user: ReturnType) {
await user.selectOptions(
- screen.getByLabelText(/security domain/i),
+ screen.getByLabelText(/security area/i),
String(category.id),
);
await user.type(screen.getByLabelText(/title/i), idea.title);
await user.type(screen.getByLabelText(/summary/i), idea.summary);
await user.type(screen.getByLabelText(/^description$/i), idea.description);
await user.type(
- screen.getByLabelText(/threat scenario/i),
+ screen.getByLabelText(/attack scenario/i),
idea.threat_scenario,
);
await user.type(
- screen.getByLabelText(/control boundary/i),
+ screen.getByLabelText(/rules of engagement/i),
idea.control_boundary,
);
await user.type(
@@ -120,7 +120,7 @@ describe("IdeaEditorPage", () => {
expect(
screen.getByRole("heading", {
- name: /sign in to draft a security brief/i,
+ name: /sign in to publish a security bounty/i,
}),
).toBeInTheDocument();
expect(screen.getByRole("link", { name: /sign in/i })).toHaveAttribute(
@@ -134,7 +134,7 @@ describe("IdeaEditorPage", () => {
const user = userEvent.setup();
renderEditor();
- await screen.findByRole("heading", { name: /draft a security brief/i });
+ await screen.findByRole("heading", { name: /publish a security bounty/i });
await completeForm(user);
await user.click(screen.getByRole("button", { name: /save draft/i }));
@@ -148,15 +148,15 @@ describe("IdeaEditorPage", () => {
proofRequired: idea.proof_required,
});
expect(
- await screen.findByRole("heading", { name: /edit security brief/i }),
+ await screen.findByRole("heading", { name: /edit bounty/i }),
).toBeInTheDocument();
});
it("uses browser constraints for required field lengths", async () => {
renderEditor();
- await screen.findByRole("heading", { name: /draft a security brief/i });
- expect(screen.getByLabelText(/security domain/i)).toBeRequired();
+ await screen.findByRole("heading", { name: /publish a security bounty/i });
+ expect(screen.getByLabelText(/security area/i)).toBeRequired();
expect(screen.getByLabelText(/title/i)).toHaveAttribute("maxlength", "120");
expect(screen.getByLabelText(/summary/i)).toHaveAttribute(
"maxlength",
@@ -167,8 +167,8 @@ describe("IdeaEditorPage", () => {
"20000",
);
for (const label of [
- /threat scenario/i,
- /control boundary/i,
+ /attack scenario/i,
+ /rules of engagement/i,
/proof required/i,
]) {
expect(screen.getByLabelText(label)).toBeRequired();
@@ -183,7 +183,7 @@ describe("IdeaEditorPage", () => {
renderEditor(`/ideas/${idea.id}/edit`);
expect(
- await screen.findByRole("heading", { name: /edit security brief/i }),
+ await screen.findByRole("heading", { name: /edit bounty/i }),
).toBeInTheDocument();
expect(screen.getByLabelText(/title/i)).toHaveValue(idea.title);
await user.clear(screen.getByLabelText(/title/i));
@@ -213,7 +213,7 @@ describe("IdeaEditorPage", () => {
);
renderEditor();
- await screen.findByRole("heading", { name: /draft a security brief/i });
+ await screen.findByRole("heading", { name: /publish a security bounty/i });
await completeForm(user);
await user.click(screen.getByRole("button", { name: /save draft/i }));
diff --git a/src/features/ideas/idea-editor-page.tsx b/src/features/ideas/idea-editor-page.tsx
index 94f8b55..47aec2d 100644
--- a/src/features/ideas/idea-editor-page.tsx
+++ b/src/features/ideas/idea-editor-page.tsx
@@ -1,5 +1,5 @@
import { useQuery } from "@tanstack/react-query";
-import { ArrowLeft, LoaderCircle, MapPinned } from "lucide-react";
+import { ArrowLeft, LoaderCircle, ShieldCheck } from "lucide-react";
import { type FormEvent, useState } from "react";
import { Link, useNavigate, useParams } from "react-router-dom";
import { Button, buttonVariants } from "@/components/ui/button";
@@ -77,11 +77,11 @@ export function IdeaEditorPage() {
return (
- Sign in to draft a security brief
+ Sign in to publish a security bounty
- Every security brief is tied to a verified operator profile. Sign in
- before defining its threat, control boundary, and proof standard.
+ Every security bounty is tied to a system owner profile. Sign in
+ before defining the authorized target, rules of engagement, and proof.
Sign in
@@ -91,7 +91,7 @@ export function IdeaEditorPage() {
}
if (categoriesQuery.isPending || (isEditing && ideaQuery.isPending)) {
- return ;
+ return ;
}
if (categoriesQuery.isError || (isEditing && ideaQuery.isError)) {
@@ -101,7 +101,7 @@ export function IdeaEditorPage() {
Editor unavailable
- Unable to load the security brief editor. Please try again.
+ Unable to load the bounty editor. Try again.
);
@@ -120,17 +120,17 @@ export function IdeaEditorPage() {
Back home
- {isEditing ? "Edit security brief" : "Draft a security brief"}
+ {isEditing ? "Edit bounty" : "Publish a security bounty"}
- Define the system, credible abuse path, explicit authority boundary, and
- evidence required before anyone should trust the control.
+ Name the system owner, authorized target, attack scenario, rules of
+ engagement, and proof required to verify the result.
) : null}
{summaryQuery.isError ? (
- Validation signals are unavailable right now. Please try again
- later.
+ Readiness signals are unavailable right now. Try again later.
) : null}
@@ -151,8 +149,8 @@ export function IdeaInterestPanel({ ideaId }: { ideaId: string }) {
Choose your private interest
- Your choice is private. Only the total validation count is
- public.
+ Your choice is private. Only the total readiness signal
+ count is public.
{intentOptions.map((option) => {
@@ -203,7 +201,7 @@ export function IdeaInterestPanel({ ideaId }: { ideaId: string }) {
type="button"
variant="outline"
>
- Remove validation signal
+ Remove readiness signal
) : null}
@@ -224,7 +222,7 @@ export function IdeaInterestPanel({ ideaId }: { ideaId: string }) {
{interestMutation.isError ? (
- We could not update your validation signal. Please try again.
+ We could not update your readiness signal. Try again.
) : null}
diff --git a/src/features/ideas/idea-validation-evidence-panel.test.tsx b/src/features/ideas/idea-validation-evidence-panel.test.tsx
index 0768e31..c0a3493 100644
--- a/src/features/ideas/idea-validation-evidence-panel.test.tsx
+++ b/src/features/ideas/idea-validation-evidence-panel.test.tsx
@@ -60,11 +60,11 @@ describe("IdeaValidationEvidencePanel", () => {
expect(getIdeaValidationSummary).not.toHaveBeenCalled();
expect(
- screen.queryByText(/private security evidence/i),
+ screen.queryByText(/private security bounty evidence/i),
).not.toBeInTheDocument();
});
- it("shows aggregate answer totals only to the security brief author", async () => {
+ it("shows aggregate answer totals only to the bounty author", async () => {
vi.mocked(useAuth).mockReturnValue({
user: { id: creatorId } as ReturnType["user"],
isLoading: false,
@@ -72,7 +72,9 @@ describe("IdeaValidationEvidencePanel", () => {
renderPanel();
expect(
- await screen.findByRole("region", { name: /private security evidence/i }),
+ await screen.findByRole("region", {
+ name: /private security bounty evidence/i,
+ }),
).toBeInTheDocument();
expect(screen.getByText("5 total responses")).toBeInTheDocument();
expect(screen.getByText("3 responses")).toBeInTheDocument();
@@ -97,7 +99,7 @@ describe("IdeaValidationEvidencePanel", () => {
renderPanel();
expect(
- await screen.findByText(/no security review responses yet/i),
+ await screen.findByText(/no readiness responses yet/i),
).toBeInTheDocument();
});
@@ -112,7 +114,7 @@ describe("IdeaValidationEvidencePanel", () => {
renderPanel();
expect(await screen.findByRole("alert")).toHaveTextContent(
- /private security evidence is unavailable right now/i,
+ /private security bounty evidence is unavailable right now/i,
);
expect(screen.getByRole("alert")).not.toHaveTextContent(/sensitive/i);
});
diff --git a/src/features/ideas/idea-validation-evidence-panel.tsx b/src/features/ideas/idea-validation-evidence-panel.tsx
index 197b0f0..5895ec2 100644
--- a/src/features/ideas/idea-validation-evidence-panel.tsx
+++ b/src/features/ideas/idea-validation-evidence-panel.tsx
@@ -29,8 +29,8 @@ export function IdeaValidationEvidencePanel({
className="mt-8 border border-destructive/20 border-l-4 border-l-destructive bg-destructive/5 p-4 text-sm text-destructive"
role="alert"
>
- Private security evidence is unavailable right now. Please try again
- later.
+ Private security bounty evidence is unavailable right now. Please try
+ again later.
);
}
@@ -47,17 +47,17 @@ export function IdeaValidationEvidencePanel({
return (
- Private security evidence
+ Private security bounty evidence
- Security brief response summary
+ Readiness response summary
Aggregate evidence for “{evidence.prompt}” Respondent identities are
@@ -71,8 +71,8 @@ export function IdeaValidationEvidencePanel({
{evidence.totalResponses === 0 ? (
- No security review responses yet. Keep the question open until there
- is enough evidence for a continue, revise, or pause decision.
+ No readiness responses yet. Keep the question open until there is
+ enough evidence for a continue, revise, or close decision.
) : (
diff --git a/src/features/ideas/idea-validation-panel.test.tsx b/src/features/ideas/idea-validation-panel.test.tsx
index f4fe5c4..14dddb1 100644
--- a/src/features/ideas/idea-validation-panel.test.tsx
+++ b/src/features/ideas/idea-validation-panel.test.tsx
@@ -80,7 +80,7 @@ describe("IdeaValidationPanel", () => {
name: question.prompt,
});
expect(
- within(region).getByText(/security exercise readiness/i),
+ within(region).getByText(/authorized test-run readiness/i),
).toBeInTheDocument();
expect(
within(region).getByText("An open-source project I maintain"),
@@ -97,10 +97,10 @@ describe("IdeaValidationPanel", () => {
"/sign-in?returnTo=%2Fideas%2Fproject-time-capsule",
);
expect(region).toHaveTextContent(
- /security brief author sees aggregate totals only/i,
+ /system owner sees aggregate totals only/i,
);
expect(region).toHaveTextContent(
- /control boundary, proof threshold, and authorized operators/i,
+ /rules of engagement, a reproducible proof threshold, and an authorized test environment/i,
);
expect(region).toHaveTextContent(/possible answers/i);
expect(region).not.toHaveTextContent(/preservation pilot/i);
@@ -197,7 +197,7 @@ describe("IdeaValidationPanel", () => {
renderPanel();
expect(await screen.findByRole("alert")).toHaveTextContent(
- /security review question is unavailable right now/i,
+ /bounty readiness question is unavailable right now/i,
);
expect(screen.getByRole("alert")).not.toHaveTextContent(/sensitive/i);
});
diff --git a/src/features/ideas/idea-validation-panel.tsx b/src/features/ideas/idea-validation-panel.tsx
index 93577ab..8ec54b6 100644
--- a/src/features/ideas/idea-validation-panel.tsx
+++ b/src/features/ideas/idea-validation-panel.tsx
@@ -82,8 +82,7 @@ export function IdeaValidationPanel({ ideaId }: { ideaId: string }) {
className="mt-12 border border-destructive/20 border-l-4 border-l-destructive bg-destructive/5 p-4 text-sm text-destructive"
role="alert"
>
- The security review question is unavailable right now. Please try again
- later.
+ The bounty readiness question is unavailable right now. Try again later.
);
}
@@ -102,7 +101,7 @@ export function IdeaValidationPanel({ ideaId }: { ideaId: string }) {
- Security exercise readiness
+ Authorized test-run readiness
- Choose the closest fit. This tests whether a bounded exercise has
- the control boundary, proof threshold, and authorized operators to
- proceed.
+ Choose the closest fit. This asks whether the bounty has clear rules
+ of engagement, a reproducible proof threshold, and an authorized
+ test environment.
- Your choice stays private. The security brief author sees
- aggregate totals only.
+ Your choice stays private. The system owner sees aggregate totals
+ only.
@@ -226,7 +225,7 @@ export function IdeaValidationPanel({ ideaId }: { ideaId: string }) {
{responseMutation.isError ? (
- We could not save your pilot answer. Please try again.
+ We could not save your bounty answer. Try again.
) : null}
diff --git a/src/features/ideas/idea-validation-service.test.ts b/src/features/ideas/idea-validation-service.test.ts
index cf81ef4..9b528b9 100644
--- a/src/features/ideas/idea-validation-service.test.ts
+++ b/src/features/ideas/idea-validation-service.test.ts
@@ -133,6 +133,34 @@ describe("idea validation service", () => {
});
});
+ it("rejects mixed historical and active evidence instead of combining meanings", async () => {
+ rpc.mockResolvedValueOnce({
+ data: [
+ {
+ question_id: questionId,
+ prompt: "Historical pilot question",
+ option_id: optionId,
+ option_value: "historical-answer",
+ option_label: "Historical answer",
+ response_count: 3,
+ },
+ {
+ question_id: "00000000-0000-4000-8000-000000000601",
+ prompt: "Is this bounty ready for an authorized test run?",
+ option_id: "00000000-0000-4000-8000-000000000611",
+ option_value: "ready-for-authorized-test",
+ option_label: "Ready for an authorized test run",
+ response_count: 2,
+ },
+ ],
+ error: null,
+ });
+
+ await expect(getIdeaValidationSummary(ideaId)).rejects.toThrow(
+ "multiple validation questions",
+ );
+ });
+
it("saves one private response idempotently for the current member", async () => {
await expect(
saveIdeaValidationResponse(questionId, optionId, profileId),
diff --git a/src/features/ideas/idea-validation-service.ts b/src/features/ideas/idea-validation-service.ts
index 4ef8749..9a508e4 100644
--- a/src/features/ideas/idea-validation-service.ts
+++ b/src/features/ideas/idea-validation-service.ts
@@ -87,6 +87,12 @@ export async function getIdeaValidationSummary(
if (rows.length === 0) {
return null;
}
+ const questionIds = new Set(rows.map((row) => row.question_id));
+ if (questionIds.size !== 1) {
+ throw new Error(
+ "Validation summary returned multiple validation questions with incompatible meanings.",
+ );
+ }
const options = rows.map((row) => ({
id: row.option_id,
diff --git a/src/features/pilots/pilot-page.test.tsx b/src/features/pilots/pilot-page.test.tsx
index cc28c0c..edd6ee2 100644
--- a/src/features/pilots/pilot-page.test.tsx
+++ b/src/features/pilots/pilot-page.test.tsx
@@ -61,10 +61,12 @@ describe("PilotPage", () => {
vi.mocked(getPilotPlan).mockReturnValue(new Promise(() => {}));
renderPage();
- expect(screen.getByRole("status")).toHaveTextContent(/loading pilot plan/i);
+ expect(screen.getByRole("status")).toHaveTextContent(
+ /loading authorized test-run plan/i,
+ );
});
- it("publishes measurable thresholds and permission-first pilot boundaries", async () => {
+ it("publishes measurable thresholds and permission-first test boundaries", async () => {
renderPage();
expect(
@@ -72,39 +74,43 @@ describe("PilotPage", () => {
name: "Project Time Capsule pilot",
}),
).toBeInTheDocument();
- expect(screen.getByText(/validation underway/i)).toBeInTheDocument();
+ expect(
+ screen.getByText(/test-run readiness under review/i),
+ ).toBeInTheDocument();
expect(screen.getByLabelText("30-day evidence window")).toBeInTheDocument();
- expect(screen.getByLabelText("15 validation signals")).toBeInTheDocument();
- expect(screen.getByLabelText("5 operator interviews")).toBeInTheDocument();
+ expect(screen.getByLabelText("15 readiness signals")).toBeInTheDocument();
+ expect(screen.getByLabelText("5 reviewer interviews")).toBeInTheDocument();
expect(
- screen.getByLabelText("3-project pilot capacity"),
+ screen.getByLabelText("3-project test capacity"),
).toBeInTheDocument();
- const decisions = screen.getByRole("region", { name: /decision rules/i });
+ const decisions = screen.getByRole("region", {
+ name: /close, revise, or proceed/i,
+ });
expect(within(decisions).getByText(/^continue$/i)).toBeInTheDocument();
- expect(decisions).toHaveTextContent(/5 qualified pilot participants/i);
+ expect(decisions).toHaveTextContent(/5 qualified test participants/i);
expect(decisions).toHaveTextContent(/3 suitable authorized projects/i);
expect(within(decisions).getByText(/^revise$/i)).toBeInTheDocument();
expect(within(decisions).getByText(/^archive$/i)).toBeInTheDocument();
expect(decisions).toHaveTextContent(/2 or fewer meaningful signals/i);
const boundaries = screen.getByRole("region", {
- name: /security exercise boundaries/i,
+ name: /rules of engagement/i,
});
expect(boundaries).toHaveTextContent(
- /participant-authorized projects only/i,
+ /written permission and sponsor-approved projects only/i,
);
expect(boundaries).toHaveTextContent(/unauthorized proprietary source/i);
expect(boundaries).toHaveTextContent(/private production data/i);
expect(boundaries).toHaveTextContent(/secrets or live credentials/i);
- expect(boundaries).toHaveTextContent(/no payment or commitment/i);
+ expect(boundaries).toHaveTextContent(/no payout or commitment/i);
});
- it("keeps intake closed while validation is underway", async () => {
+ it("keeps intake closed while readiness is under review", async () => {
renderPage();
expect(
- await screen.findByText(/security exercise intake is not open yet/i),
+ await screen.findByText(/authorized test-run intake is not open yet/i),
).toBeInTheDocument();
expect(
screen.queryByRole("link", { name: /sign in to apply/i }),
@@ -131,7 +137,7 @@ describe("PilotPage", () => {
renderPage();
expect(
- await screen.findByRole("heading", { name: /pilot plan not found/i }),
+ await screen.findByRole("heading", { name: /test-run plan not found/i }),
).toBeInTheDocument();
});
@@ -142,7 +148,7 @@ describe("PilotPage", () => {
renderPage();
expect(await screen.findByRole("alert")).toHaveTextContent(
- /unable to load the pilot plan/i,
+ /unable to load the authorized test-run plan/i,
);
expect(screen.getByRole("alert")).not.toHaveTextContent(/sensitive/i);
});
diff --git a/src/features/pilots/pilot-page.tsx b/src/features/pilots/pilot-page.tsx
index 51a3313..943b597 100644
--- a/src/features/pilots/pilot-page.tsx
+++ b/src/features/pilots/pilot-page.tsx
@@ -21,12 +21,12 @@ import {
} from "@/features/pilots/pilot-service";
const statusLabels: Record
= {
- validating: "Security validation underway",
- recruiting: "Recruiting a bounded exercise",
- active: "Security exercise active",
- completed: "Security exercise completed",
- paused: "Security exercise paused",
- archived: "Security exercise archived",
+ validating: "Test-run readiness under review",
+ recruiting: "Recruiting authorized reviewers",
+ active: "Authorized test run active",
+ completed: "Authorized test run completed",
+ paused: "Authorized test run paused",
+ archived: "Authorized test run archived",
};
export function PilotPage() {
@@ -40,13 +40,13 @@ export function PilotPage() {
});
if (pilotQuery.isPending || isAuthLoading) {
- return ;
+ return ;
}
if (pilotQuery.isError) {
return (
);
@@ -56,8 +56,8 @@ export function PilotPage() {
if (!pilot) {
return (
);
}
@@ -68,7 +68,7 @@ export function PilotPage() {
return (
<>
@@ -77,15 +77,15 @@ export function PilotPage() {
- Evidence before commitment
+ Authorized test run · plan 001
{pilot.title}
- Test whether authorized software projects can be preserved and
- rebuilt reproducibly on a clean machine—before treating the
- control as an operating program.
+ Try to expose secrets, private history, unlicensed material, or
+ unreproducible builds in sponsor-approved archives—then prove
+ the fix on a clean machine.
@@ -125,24 +125,23 @@ export function PilotPage() {
className="field-panel mt-12 border-t-4 border-t-primary p-7 sm:p-10"
>
- Precommitted evaluation
+ Test-run gates · precommitted
- Decision rules
+ Close, revise, or proceed
- The outcome is not decided by enthusiasm alone. These thresholds
- are published before recruitment so weak evidence cannot be
- reframed as a success later.
+ A test run does not begin on confidence alone. Thresholds are
+ published before recruitment so the decision is based on evidence.
@@ -167,11 +166,12 @@ export function PilotPage() {
className="mt-5 text-3xl font-semibold"
id="pilot-boundaries-heading"
>
- Security exercise boundaries
+ Rules of engagement
- Participant-authorized projects only. No credential bypass,
- system intrusion, custody, payment, or commitment.
+ Written permission and sponsor-approved projects only. No
+ third-party targets, real credentials, production intrusion,
+ custody, payout, or deployment authority.
@@ -192,7 +192,8 @@ export function PilotPage() {
aria-hidden="true"
/>
Application details will remain private to the applicant and
- authorized pilot operators. No payment or commitment.
+ authorized test reviewers. IdeaScape handles no payout or
+ commitment.
@@ -205,14 +206,14 @@ export function PilotPage() {
{pilot.status === "validating"
- ? "Security exercise intake is not open yet"
+ ? "Authorized test-run intake is not open yet"
: pilot.status === "recruiting"
- ? "Private exercise applications are open"
+ ? "Private test-run applications are open"
: statusLabels[pilot.status]}
{pilot.status === "validating"
- ? "Ideascape is collecting scoped security-validation signals before inviting exercise applications."
+ ? "IdeaScape is collecting private readiness signals before inviting authorized test-run applications."
: "One strong, authorized project per applicant. Intake remains capped and reversible."}
diff --git a/src/features/pilots/pilot-readiness-panel.tsx b/src/features/pilots/pilot-readiness-panel.tsx
index 15c1d36..818eff3 100644
--- a/src/features/pilots/pilot-readiness-panel.tsx
+++ b/src/features/pilots/pilot-readiness-panel.tsx
@@ -18,7 +18,7 @@ const recommendationCopy: Record<
continue: {
label: "Continue threshold met",
description:
- "Participant and suitable-project evidence support preparing the published capped pilot.",
+ "Participant and suitable-project evidence support preparing the published authorized test run.",
},
revise: {
label: "Revise before advancing",
@@ -84,8 +84,8 @@ export function PilotReadinessPanel({ pilotId }: { pilotId: string }) {
Evidence against the published thresholds
- Aggregate progress for the security brief author and trusted
- exercise operators. Respondent identities are never included.
+ Aggregate progress for the system owner and authorized test
+ reviewers. Respondent identities are never included.
- within pilot capacity
+ within test-run capacity
diff --git a/src/features/profiles/profile-page.tsx b/src/features/profiles/profile-page.tsx
index 131b423..57284f7 100644
--- a/src/features/profiles/profile-page.tsx
+++ b/src/features/profiles/profile-page.tsx
@@ -15,7 +15,7 @@ function ProfileShell({ children }: { children: ReactNode }) {
- Ideascape
+ IdeaScape · Security bounties
{children}
@@ -107,8 +107,7 @@ export function ProfilePage() {
Profile not found
- We couldn't find an Ideascape security operator with that
- username.
+ We couldn't find an IdeaScape system owner with that username.
{profile.bio}
) : (
- This security operator hasn't added a bio yet.
+ This system owner hasn't added a profile summary yet.
)}
{websiteUrl ? (
diff --git a/src/features/theme/theme-provider.test.tsx b/src/features/theme/theme-provider.test.tsx
index 188f41e..7dfd81a 100644
--- a/src/features/theme/theme-provider.test.tsx
+++ b/src/features/theme/theme-provider.test.tsx
@@ -44,6 +44,11 @@ describe("theme controls", () => {
expect(
screen.getByRole("button", { name: /switch to light mode/i }),
).toBeInTheDocument();
+ expect(document.documentElement.style.colorScheme).toBe("dark");
+ expect(document.querySelector('meta[name="theme-color"]')).toHaveAttribute(
+ "content",
+ "#050505",
+ );
});
it("toggles and persists the visitor theme", async () => {
@@ -58,7 +63,18 @@ describe("theme controls", () => {
expect(window.localStorage.getItem("ideascape.theme")).toBe("dark");
expect(document.querySelector('meta[name="theme-color"]')).toHaveAttribute(
"content",
- "#000000",
+ "#050505",
+ );
+ });
+
+ it("reports the bone light canvas when light mode is active", () => {
+ renderTheme();
+
+ expect(document.documentElement).not.toHaveClass("dark");
+ expect(document.documentElement.style.colorScheme).toBe("light");
+ expect(document.querySelector('meta[name="theme-color"]')).toHaveAttribute(
+ "content",
+ "#f2efe6",
);
});
diff --git a/src/features/theme/theme-provider.tsx b/src/features/theme/theme-provider.tsx
index e26781c..8b52e18 100644
--- a/src/features/theme/theme-provider.tsx
+++ b/src/features/theme/theme-provider.tsx
@@ -50,7 +50,7 @@ export function ThemeProvider({ children }: PropsWithChildren) {
document.documentElement.style.colorScheme = theme;
document
.querySelector('meta[name="theme-color"]')
- ?.setAttribute("content", theme === "dark" ? "#000000" : "#ffffff");
+ ?.setAttribute("content", theme === "dark" ? "#050505" : "#f2efe6");
}, [theme]);
const value = useMemo(
diff --git a/src/features/theme/theme-toggle.tsx b/src/features/theme/theme-toggle.tsx
index 5999834..d079bd0 100644
--- a/src/features/theme/theme-toggle.tsx
+++ b/src/features/theme/theme-toggle.tsx
@@ -10,7 +10,7 @@ export function ThemeToggle() {
return (
span {
+ min-width: max-content;
+ padding: 0.45rem 1.5rem;
+ animation: packet-trace 28s linear infinite;
+ }
+}
+
+@keyframes packet-trace {
+ to {
+ transform: translateX(-100%);
+ }
}
::selection {
- color: #000000;
+ color: #050505;
background: var(--signal);
}
diff --git a/src/security-positioning.test.ts b/src/security-positioning.test.ts
index 33a9667..256539a 100644
--- a/src/security-positioning.test.ts
+++ b/src/security-positioning.test.ts
@@ -96,21 +96,22 @@ describe("security-first product positioning", () => {
}
});
- it("documents private review intent with its persisted meanings", () => {
- const documentation = positioningDocs
- .map((path) => readFileSync(path, "utf8"))
- .join("\n");
-
+ it("keeps private review intent labels aligned with persisted meanings", () => {
+ const interestPanel = readFileSync(
+ join(repositoryRoot, "src/features/ideas/idea-interest-panel.tsx"),
+ "utf8",
+ );
for (const meaning of [
"I would use this",
"I would help build it",
- "I could test a pilot",
+ "I could join an authorized test run",
"I have relevant expertise",
"Keep me updated",
]) {
- expect(documentation).toMatch(new RegExp(`\\b${meaning}\\b`, "i"));
+ expect(interestPanel).toMatch(new RegExp(`\\b${meaning}\\b`, "i"));
}
- expect(documentation).not.toMatch(
+ expect(interestPanel).toContain('value: "pilot"');
+ expect(interestPanel).not.toMatch(
/\b(?:system operators?|control testers?|exercise hosts?|threat-model reviewers?)\b/i,
);
});
diff --git a/src/theme-css.test.ts b/src/theme-css.test.ts
index 59a2f82..fa22f07 100644
--- a/src/theme-css.test.ts
+++ b/src/theme-css.test.ts
@@ -35,18 +35,29 @@ const allowedPalette = new Set([
"#000000",
"#050505",
"#0a0a0a",
+ "#0a0a09",
+ "#0f0f0d",
+ "#11110f",
+ "#171714",
"#171717",
+ "#201108",
+ "#24231f",
"#262626",
+ "#3e3c37",
"#404040",
"#525252",
+ "#625f58",
"#737373",
+ "#aaa79f",
"#a3a3a3",
"#d4d4d4",
"#e5e5e5",
"#f5f5f5",
+ "#f2efe6",
"#fafafa",
"#ffffff",
"#ff5a1f",
+ "#ffb091",
]);
const prohibitedNamedColors = new RegExp(
@@ -215,10 +226,16 @@ function findPaletteViolations(source: string): string[] {
: normalized;
return !allowedPalette.has(expanded);
});
- const arbitraryColorFunctions =
- source.match(
- /\b(?:oklch|oklab|lch|lab|rgba?|hsla?|hwb|color|device-cmyk|light-dark|contrast-color|color-contrast)\s*\(/gi,
- ) ?? [];
+ const arbitraryColorFunctions = [
+ ...(source.match(
+ /\b(?:oklch|oklab|lch|lab|rgba|hsla?|hwb|color|device-cmyk|light-dark|contrast-color|color-contrast)\s*\(/gi,
+ ) ?? []),
+ ...(source.match(/\brgb\([^)]*\)/gi) ?? [])
+ .filter(
+ (value) => !/rgb\((?:255 90 31|242 239 230) \/ 0\.\d+\)/i.test(value),
+ )
+ .map(() => "rgb("),
+ ];
const namedColorUtilities =
source.match(
/\b(?:red|green|blue|yellow|amber|orange|lime|emerald|cyan|sky|indigo|violet|purple|fuchsia|pink|rose|teal|slate|stone|zinc|gray)-\d{2,3}\b/gi,
@@ -280,7 +297,7 @@ function findPaletteViolations(source: string): string[] {
];
}
-describe("civic field-notebook design system", () => {
+describe("security-bounty field system", () => {
it("uses a deliberate editorial type family", () => {
expect(stylesheet).toContain(
'@import "@fontsource-variable/ibm-plex-sans"',
@@ -292,30 +309,23 @@ describe("civic field-notebook design system", () => {
expect(stylesheet).toContain('--font-mono: "IBM Plex Mono", monospace');
});
- it("uses only black, white, grayscale, and one bright orange signal", () => {
- expect(rootTheme).toContain("--background: #ffffff");
- expect(rootTheme).toContain("--card: #ffffff");
- expect(rootTheme).toContain("--popover: #ffffff");
- expect(rootTheme).toContain("--sidebar: #ffffff");
- expect(rootTheme).toContain("--foreground: #0a0a0a");
- expect(rootTheme).toContain("--primary: #0a0a0a");
+ it("uses distinct bone-light and black-dark monochrome-orange themes", () => {
+ expect(rootTheme).toContain("--background: #f2efe6");
+ expect(rootTheme).toContain("--foreground: #050505");
expect(rootTheme).toContain("--signal: #ff5a1f");
- expect(rootTheme).toContain("--signal-foreground: #000000");
- expect(rootTheme).toContain("--destructive: #0a0a0a");
- expect(rootTheme).toContain("--border: #737373");
- expect(rootTheme).toContain("--input: #737373");
- expect(rootTheme).toContain("--radius: 0.375rem");
- expect(darkTheme).toContain("--background: #000000");
- expect(darkTheme).toContain("--card: #000000");
- expect(darkTheme).toContain("--popover: #000000");
- expect(darkTheme).toContain("--sidebar: #000000");
- expect(darkTheme).toContain("--foreground: #fafafa");
- expect(darkTheme).toContain("--primary: #fafafa");
+ expect(rootTheme).toContain("--signal-foreground: #050505");
+ expect(darkTheme).toContain("--background: #050505");
+ expect(darkTheme).toContain("--card: #0a0a09");
+ expect(darkTheme).toContain("--popover: #0a0a09");
+ expect(darkTheme).toContain("--sidebar: #050505");
+ expect(darkTheme).toContain("--foreground: #f2efe6");
+ expect(darkTheme).toContain("--primary: #f2efe6");
expect(darkTheme).toContain("--signal: #ff5a1f");
- expect(darkTheme).toContain("--destructive: #fafafa");
- expect(darkTheme).toContain("--border: #737373");
- expect(darkTheme).toContain("--input: #737373");
- expect(darkTheme).toContain("--ring: #ff5a1f");
+ expect(darkTheme).toContain("--destructive: #ff5a1f");
+ expect(darkTheme).toContain("--border: #3e3c37");
+ expect(darkTheme).toContain("--input: #625f58");
+ expect(darkTheme).toContain("--radius: 0");
+ expect(rootTheme).not.toBe(darkTheme);
});
it("keeps every production color inside the monochrome-orange palette", () => {
@@ -382,8 +392,7 @@ describe("civic field-notebook design system", () => {
expect(productionSources.join("\n")).not.toMatch(
/\b(?:ring|outline)-(?:signal|ring)\/\d+\b/,
);
- expect(rootTheme).toContain("--border: #737373");
- expect(darkTheme).toContain("--border: #737373");
+ expect(darkTheme).toContain("--border: #3e3c37");
});
it("anchors grayscale editorial imagery with an exact orange rule", () => {
@@ -412,18 +421,26 @@ describe("civic field-notebook design system", () => {
expect(profilePageSource).not.toContain("editorial-image-frame");
});
+ it("spaces the main bounty cards for easier catalog scanning", () => {
+ expect(ideaDiscoverySource).toContain(
+ 'className="mt-12 grid gap-6 md:grid-cols-2 xl:grid-cols-3"',
+ );
+ expect(ideaDiscoverySource).toContain(
+ 'className="group relative overflow-hidden border border-border bg-card',
+ );
+ });
+
it("uses orange as decoration rather than low-contrast small text on white", () => {
expect(stylesheet).toMatch(/\.signal-label\s*\{[\s\S]*text-foreground/);
- expect(appSource).toContain(
- 'Permission checked ',
- );
+ expect(appSource).toContain('className="text-signal"');
});
- it("uses flat pure canvases without grid or contour backgrounds", () => {
+ it("uses one restrained grid signature without contour decoration", () => {
const productionSources = listVisualSourceFiles(
resolve(process.cwd(), "src"),
).map((file) => readFileSync(file, "utf8"));
- expect(stylesheet).not.toContain("background-image:");
+ expect(stylesheet).toContain(".bounty-grid");
+ expect(stylesheet).toContain("background-image:");
expect(stylesheet).not.toContain(".field-grid");
expect(stylesheet).not.toContain(".contour-field");
expect(productionSources.join("\n")).not.toMatch(
@@ -459,9 +476,9 @@ describe("civic field-notebook design system", () => {
expect(appShell).toContain('classList.add("dark")');
});
- it("describes the landing page as security validation fieldwork", () => {
+ it("describes the landing page as authorized security bounties", () => {
expect(appShell).toContain(
- "Ideascape — Security validation fieldwork ",
+ "IdeaScape — Authorized security bounties ",
);
});
});
diff --git a/supabase/migrations/20260811193000_recast_as_authorized_bounty_network.sql b/supabase/migrations/20260811193000_recast_as_authorized_bounty_network.sql
new file mode 100644
index 0000000..8946ec4
--- /dev/null
+++ b/supabase/migrations/20260811193000_recast_as_authorized_bounty_network.sql
@@ -0,0 +1,277 @@
+-- Recast the exact deterministic IdeaScape catalog as an authorized bounty
+-- platform. Every test is confined to an authorized environment; stable UUIDs
+-- and slugs remain compatibility identifiers.
+-- Arbitrary creator-owned records are intentionally outside this migration.
+
+drop table if exists pg_temp.ideascape_bounty_seed_manifest;
+
+create temporary table ideascape_bounty_seed_manifest (
+ id uuid primary key,
+ slug text unique not null,
+ media_id uuid unique not null
+) on commit drop;
+
+insert into ideascape_bounty_seed_manifest (id, slug, media_id)
+values
+ ('00000000-0000-4000-8000-000000000201'::uuid, 'clean-air-library'::text, '00000000-0000-4000-8000-000000000301'::uuid),
+ ('00000000-0000-4000-8000-000000000202'::uuid, 'repair-commons'::text, '00000000-0000-4000-8000-000000000302'::uuid),
+ ('00000000-0000-4000-8000-000000000203'::uuid, 'neighbor-ride-credits'::text, '00000000-0000-4000-8000-000000000303'::uuid),
+ ('00000000-0000-4000-8000-000000000204'::uuid, 'after-dark-storefronts'::text, '00000000-0000-4000-8000-000000000304'::uuid),
+ ('00000000-0000-4000-8000-000000000205'::uuid, 'shade-stop-network'::text, '00000000-0000-4000-8000-000000000305'::uuid),
+ ('00000000-0000-4000-8000-000000000206'::uuid, 'skill-swap-saturdays'::text, '00000000-0000-4000-8000-000000000306'::uuid),
+ ('00000000-0000-4000-8000-000000000207'::uuid, 'civic-accessibility-lab'::text, '00000000-0000-4000-8000-000000000307'::uuid),
+ ('00000000-0000-4000-8000-000000000208'::uuid, 'block-ready-kits'::text, '00000000-0000-4000-8000-000000000308'::uuid),
+ ('00000000-0000-4000-8000-000000000209'::uuid, 'device-liberation-lab'::text, '00000000-0000-4000-8000-000000000309'::uuid),
+ ('00000000-0000-4000-8000-000000000210'::uuid, 'file-rescue-cooperative'::text, '00000000-0000-4000-8000-000000000310'::uuid),
+ ('00000000-0000-4000-8000-000000000211'::uuid, 'cloud-exit-toolkit'::text, '00000000-0000-4000-8000-000000000311'::uuid),
+ ('00000000-0000-4000-8000-000000000212'::uuid, 'private-ai-workbench'::text, '00000000-0000-4000-8000-000000000312'::uuid),
+ ('00000000-0000-4000-8000-000000000213'::uuid, 'home-lab-defense-clinic'::text, '00000000-0000-4000-8000-000000000313'::uuid),
+ ('00000000-0000-4000-8000-000000000214'::uuid, 'community-compute-cooperative'::text, '00000000-0000-4000-8000-000000000314'::uuid),
+ ('00000000-0000-4000-8000-000000000215'::uuid, 'offline-mesh-field-kit'::text, '00000000-0000-4000-8000-000000000315'::uuid),
+ ('00000000-0000-4000-8000-000000000216'::uuid, 'open-repair-atlas'::text, '00000000-0000-4000-8000-000000000316'::uuid),
+ ('00000000-0000-4000-8000-000000000217'::uuid, 'accessible-interface-retrofit-lab'::text, '00000000-0000-4000-8000-000000000317'::uuid),
+ ('00000000-0000-4000-8000-000000000218'::uuid, 'project-time-capsule'::text, '00000000-0000-4000-8000-000000000318'::uuid),
+ ('00000000-0000-4000-8000-000000000219'::uuid, 'waste-heat-works'::text, '00000000-0000-4000-8000-000000000319'::uuid),
+ ('00000000-0000-4000-8000-000000000220'::uuid, 'model-commons-lab'::text, '00000000-0000-4000-8000-000000000320'::uuid),
+ ('00000000-0000-4000-8000-000000000221'::uuid, 'glass-box-sensor-network'::text, '00000000-0000-4000-8000-000000000321'::uuid),
+ ('00000000-0000-4000-8000-000000000222'::uuid, 'oral-history-provenance-lab'::text, '00000000-0000-4000-8000-000000000322'::uuid),
+ ('00000000-0000-4000-8000-000000000223'::uuid, 'neighborhood-incident-relay'::text, '00000000-0000-4000-8000-000000000323'::uuid),
+ ('00000000-0000-4000-8000-000000000224'::uuid, 'phishing-drill-library'::text, '00000000-0000-4000-8000-000000000324'::uuid),
+ ('00000000-0000-4000-8000-000000000225'::uuid, 'water-sensor-integrity-watch'::text, '00000000-0000-4000-8000-000000000325'::uuid),
+ ('00000000-0000-4000-8000-000000000226'::uuid, 'clinic-device-privacy-check'::text, '00000000-0000-4000-8000-000000000326'::uuid),
+ ('00000000-0000-4000-8000-000000000227'::uuid, 'software-supply-chain-clinic'::text, '00000000-0000-4000-8000-000000000327'::uuid);
+
+do $$
+begin
+ if (
+ select count(*)
+ from ideascape_bounty_seed_manifest as manifest
+ join public.ideas as ideas
+ on ideas.id = manifest.id
+ and ideas.slug = manifest.slug
+ and ideas.creator_id = '00000000-0000-4000-8000-000000000101'
+ ) <> 27 then
+ raise exception using
+ errcode = '23514',
+ message = 'authorized bounty positioning requires all 27 expected bounty UUID/slug pairs';
+ end if;
+end;
+$$;
+
+update public.categories as categories
+set
+ name = mapped.name,
+ description = mapped.description
+from (
+ values
+ ('arts-culture'::text, 'Provenance & Forgery'::text, 'Authorized bounties for source integrity, consent, authenticity, and controlled reuse.'::text),
+ ('community'::text, 'Coordination & Resilience'::text, 'Authorized bounties for private reporting, bounded authority, fallback, and incident recovery.'::text),
+ ('education'::text, 'Human Attack Surface'::text, 'Contained social-engineering and procedure bounties without credential capture or hidden surveillance.'::text),
+ ('environment'::text, 'Physical & Sensor Systems'::text, 'Owner-approved bounties for sensors, utilities, repair systems, and fail-safe physical controls.'::text),
+ ('health'::text, 'Privacy & Safety'::text, 'Permissioned privacy and safety bounties for devices, accessibility, and sensitive data flows.'::text),
+ ('technology'::text, 'Software & Compute'::text, 'Authorized bounties for supply chains, devices, recovery, compute isolation, and model operations.'::text)
+) as mapped(slug, name, description)
+where categories.slug = mapped.slug
+ and (categories.name, categories.description)
+ is distinct from (mapped.name, mapped.description);
+
+with bounty_catalog (
+ id,
+ slug,
+ title,
+ summary,
+ description,
+ threat_scenario,
+ control_boundary,
+ proof_required
+) as (
+ values
+ ('00000000-0000-4000-8000-000000000201', 'clean-air-library', 'Smoke Sensor Spoofing Bounty', 'Find how air-quality alerts could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Smoke Sensor Spoofing Bounty is a permissioned defensive challenge focused on air-quality alerts. The sponsor provides a sponsor-owned sensor emulator and synthetic telemetry feed. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could accept forged readings and suppress a real smoke warning. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to a sponsor-owned sensor emulator and synthetic telemetry feed. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of signed readings, anomaly alarms, and fail-safe alert behavior. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000202', 'repair-commons', 'Repair Station Privilege Bounty', 'Find how shared repair-station accounts could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Repair Station Privilege Bounty is a permissioned defensive challenge focused on shared repair-station accounts. The sponsor provides disposable repair tickets and isolated test accounts. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could cross operator boundaries and expose another person''s repair records. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to disposable repair tickets and isolated test accounts. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of tenant isolation, least-privilege roles, and complete access logs. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000203', 'neighbor-ride-credits', 'Trip Relay Metadata Bounty', 'Find how essential-trip requests could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Trip Relay Metadata Bounty is a permissioned defensive challenge focused on essential-trip requests. The sponsor provides synthetic trip records and sponsor-controlled relay accounts. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could leak route, schedule, or rider identity metadata through normal relay use. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to synthetic trip records and sponsor-controlled relay accounts. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of data minimization, unlinkability checks, and deletion verification. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000204', 'after-dark-storefronts', 'Night Install Tamper Bounty', 'Find how after-hours safety installations could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Night Install Tamper Bounty is a permissioned defensive challenge focused on after-hours safety installations. The sponsor provides bench-mounted demo hardware and signed sample work orders. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could accept a tampered device or forged maintenance record as trusted. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to bench-mounted demo hardware and signed sample work orders. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of tamper detection, provenance checks, and safe rejection paths. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000205', 'shade-stop-network', 'Transit Sensor Blind-Spot Bounty', 'Find how transit-stop hazard sensors could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Transit Sensor Blind-Spot Bounty is a permissioned defensive challenge focused on transit-stop hazard sensors. The sponsor provides a simulated stop, synthetic weather events, and emulated sensors. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could hide a dangerous condition by replaying or suppressing field telemetry. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to a simulated stop, synthetic weather events, and emulated sensors. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of freshness checks, missing-data alarms, and manual fallback behavior. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000206', 'skill-swap-saturdays', 'Repair Playbook Injection Bounty', 'Find how shared repair procedures could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Repair Playbook Injection Bounty is a permissioned defensive challenge focused on shared repair procedures. The sponsor provides a disposable documentation workspace with synthetic procedures. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could insert an unsafe step or untrusted link into a published repair playbook. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to a disposable documentation workspace with synthetic procedures. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of review gates, signed revisions, rollback, and clear provenance. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000207', 'civic-accessibility-lab', 'Crossing Signal Failure Bounty', 'Find how accessible crossing interfaces could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Crossing Signal Failure Bounty is a permissioned defensive challenge focused on accessible crossing interfaces. The sponsor provides a software-only crossing simulator and synthetic accessibility profiles. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could create a visual, audio, or timing regression that strands a user. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to a software-only crossing simulator and synthetic accessibility profiles. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of keyboard, screen-reader, timing, contrast, and fail-safe regression evidence. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000208', 'block-ready-kits', 'Outage Kit Supply-Chain Bounty', 'Find how emergency kit inventories could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Outage Kit Supply-Chain Bounty is a permissioned defensive challenge focused on emergency kit inventories. The sponsor provides sample manifests, mock labels, and non-operational kit contents. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could substitute an expired or counterfeit component without triggering review. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to sample manifests, mock labels, and non-operational kit contents. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of signed inventory history, expiry validation, and quarantine behavior. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000209', 'device-liberation-lab', 'Device Unlock Boundary Bounty', 'Find how owner-authorized device recovery could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Device Unlock Boundary Bounty is a permissioned defensive challenge focused on owner-authorized device recovery. The sponsor provides factory-reset lab devices supplied by the sponsor. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could let a recovery workflow bypass proof of ownership or retain private data. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to factory-reset lab devices supplied by the sponsor. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of ownership checks, auditable consent, data erasure, and reversible recovery. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000210', 'file-rescue-cooperative', 'File Recovery Integrity Bounty', 'Find how file-recovery results could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'File Recovery Integrity Bounty is a permissioned defensive challenge focused on file-recovery results. The sponsor provides synthetic disk images containing seeded corruption and canary files. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could restore corrupted, substituted, or cross-customer files as trustworthy. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to synthetic disk images containing seeded corruption and canary files. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of hash verification, tenant isolation, provenance, and clean-room retesting. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000211', 'cloud-exit-toolkit', 'Cloud Exit Data-Loss Bounty', 'Find how cloud export and deletion workflows could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Cloud Exit Data-Loss Bounty is a permissioned defensive challenge focused on cloud export and deletion workflows. The sponsor provides synthetic tenant data in sponsor-owned staging accounts. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could silently omit records or leave recoverable copies after an exit. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to synthetic tenant data in sponsor-owned staging accounts. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of record reconciliation, portable exports, deletion verification, and rollback evidence. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000212', 'private-ai-workbench', 'Local AI Data-Leak Bounty', 'Find how local model workspaces could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Local AI Data-Leak Bounty is a permissioned defensive challenge focused on local model workspaces. The sponsor provides synthetic documents inside an isolated local-AI test image. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could leak prompts, files, or embeddings across projects or network boundaries. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to synthetic documents inside an isolated local-AI test image. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of egress denial, workspace isolation, artifact scrubbing, and repeatable leak tests. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000213', 'home-lab-defense-clinic', 'Home Lab Exposure Bounty', 'Find how self-hosted lab services could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Home Lab Exposure Bounty is a permissioned defensive challenge focused on self-hosted lab services. The sponsor provides an intentionally vulnerable sponsor-owned lab replica. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could expose an unintended service, secret, or administration panel. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to an intentionally vulnerable sponsor-owned lab replica. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of service inventory, secret rotation, network segmentation, and verified closure. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000214', 'community-compute-cooperative', 'Shared Compute Escape Bounty', 'Find how shared compute jobs could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Shared Compute Escape Bounty is a permissioned defensive challenge focused on shared compute jobs. The sponsor provides ephemeral sponsor-owned workers running synthetic workloads. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could escape one workload boundary or read another operator''s artifacts. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to ephemeral sponsor-owned workers running synthetic workloads. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of job isolation, storage separation, quota enforcement, and teardown verification. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000215', 'offline-mesh-field-kit', 'Mesh Relay Spoofing Bounty', 'Find how offline mesh messages could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Mesh Relay Spoofing Bounty is a permissioned defensive challenge focused on offline mesh messages. The sponsor provides a closed radio test bench with synthetic messages and no public transmission. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could inject a forged relay identity or replay stale emergency traffic. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to a closed radio test bench with synthetic messages and no public transmission. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of message authenticity, replay resistance, revocation, and offline recovery. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000216', 'open-repair-atlas', 'Repair Atlas Poisoning Bounty', 'Find how repair-location records could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Repair Atlas Poisoning Bounty is a permissioned defensive challenge focused on repair-location records. The sponsor provides a staging atlas populated with synthetic locations and accounts. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could poison trusted listings with malicious instructions or false service claims. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to a staging atlas populated with synthetic locations and accounts. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of source reputation, moderation evidence, rollback, and poisoned-record detection. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000217', 'accessible-interface-retrofit-lab', 'Accessible UI Regression Bounty', 'Find how critical interface accessibility could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Accessible UI Regression Bounty is a permissioned defensive challenge focused on critical interface accessibility. The sponsor provides a sponsor-owned component gallery and scripted test journeys. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could ship an interaction that blocks keyboard, switch, low-vision, or screen-reader users. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to a sponsor-owned component gallery and scripted test journeys. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of automated checks plus assistive-technology evidence and a verified fix. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000218', 'project-time-capsule', 'Time Capsule Disclosure Bounty', 'Find how archived software projects could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Time Capsule Disclosure Bounty is a permissioned defensive challenge focused on archived software projects. The sponsor provides synthetic repositories and owner-approved public test projects. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could expose secrets, private history, or unlicensed material during preservation. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to synthetic repositories and owner-approved public test projects. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of secret scanning, license inventory, reproducible rebuilds, and clean export review. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000219', 'waste-heat-works', 'Heat Controller Fail-Safe Bounty', 'Find how compute heat-control logic could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Heat Controller Fail-Safe Bounty is a permissioned defensive challenge focused on compute heat-control logic. The sponsor provides a simulation with emulated heaters, pumps, and fault injection. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could drive unsafe temperature behavior when telemetry is stale or a controller fails. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to a simulation with emulated heaters, pumps, and fault injection. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of hard safety limits, stale-data shutdown, independent alarms, and recovery tests. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000220', 'model-commons-lab', 'Model Eval Poisoning Bounty', 'Find how model evaluation results could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Model Eval Poisoning Bounty is a permissioned defensive challenge focused on model evaluation results. The sponsor provides versioned synthetic datasets and sponsor-provided model outputs. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could bias a benchmark through contaminated data, hidden leakage, or mutable scoring. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to versioned synthetic datasets and sponsor-provided model outputs. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of dataset provenance, immutable scoring, leakage checks, and independent reruns. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000221', 'glass-box-sensor-network', 'Plate Reader Privacy Bounty', 'Find how plate-reader records could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Plate Reader Privacy Bounty is a permissioned defensive challenge focused on plate-reader records. The sponsor provides synthetic plate events in an isolated audit replica. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could permit overbroad search, retention, or sharing of sensitive location data. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to synthetic plate events in an isolated audit replica. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of purpose limits, access controls, deletion tests, and aggregate-only reporting. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000222', 'oral-history-provenance-lab', 'Oral History Provenance Bounty', 'Find how oral-history media and consent could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Oral History Provenance Bounty is a permissioned defensive challenge focused on oral-history media and consent. The sponsor provides synthetic interviews and owner-approved public-domain samples. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could detach a recording from consent terms or substitute an altered source. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to synthetic interviews and owner-approved public-domain samples. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of consent linkage, edit provenance, revocation handling, and authenticity checks. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000223', 'neighborhood-incident-relay', 'Incident Relay Impersonation Bounty', 'Find how private incident reports could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Incident Relay Impersonation Bounty is a permissioned defensive challenge focused on private incident reports. The sponsor provides synthetic reports and isolated sponsor-controlled relay accounts. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could impersonate a trusted relay or expose a reporter through routing metadata. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to synthetic reports and isolated sponsor-controlled relay accounts. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of authenticated routing, metadata minimization, abuse controls, and recovery drills. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000224', 'phishing-drill-library', 'Phishing Drill Containment Bounty', 'Find how defensive phishing exercises could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Phishing Drill Containment Bounty is a permissioned defensive challenge focused on defensive phishing exercises. The sponsor provides a closed mail sandbox with fake identities and non-working credentials. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could capture real credentials, shame participants, or escape the approved simulation. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to a closed mail sandbox with fake identities and non-working credentials. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of no credential collection, scope enforcement, safe reporting, and teardown evidence. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000225', 'water-sensor-integrity-watch', 'Water Sensor Spoofing Bounty', 'Find how water-safety telemetry could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Water Sensor Spoofing Bounty is a permissioned defensive challenge focused on water-safety telemetry. The sponsor provides a sponsor-owned sensor emulator and synthetic water events. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could forge a safe reading or suppress an alert during a simulated hazard. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to a sponsor-owned sensor emulator and synthetic water events. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of signed telemetry, anomaly detection, fail-safe alerts, and manual confirmation. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000226', 'clinic-device-privacy-check', 'Clinic Device Privacy Bounty', 'Find how clinic device data flows could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Clinic Device Privacy Bounty is a permissioned defensive challenge focused on clinic device data flows. The sponsor provides demo devices and entirely synthetic patient records. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could expose patient-like data through logs, exports, pairing, or maintenance access. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to demo devices and entirely synthetic patient records. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of data-flow inventory, access controls, log scrubbing, deletion, and retest evidence. An independent rerun must reproduce the failure and confirm the remediation before closure.'),
+ ('00000000-0000-4000-8000-000000000227', 'software-supply-chain-clinic', 'Dependency Substitution Bounty', 'Find how software build dependencies could fail under adversarial pressure, then prove the repair inside an owner-approved test environment.', 'Dependency Substitution Bounty is a permissioned defensive challenge focused on software build dependencies. The sponsor provides an isolated build environment with synthetic packages and disposable signing keys. Authorized reviewers reproduce a bounded failure, document impact without collecting real-world private data, and verify a practical fix. No production access, destructive testing, deployment authority, custody, or payout is granted by Ideascape.', 'A malicious or compromised actor could replace a trusted package or artifact without detection in the build path. The result could undermine safety, privacy, availability, or trust while appearing normal to an operator.', 'Testing is limited to an isolated build environment with synthetic packages and disposable signing keys. Reviewers need written permission, must follow the published rules of engagement, may not touch production or third-party assets, and must stop on unexpected data or impact.', 'Submit a minimal reproduction against the supplied fixture, timestamped observations, impact boundaries, and evidence of pinning, signatures, provenance attestations, clean rebuilds, and incident rollback. An independent rerun must reproduce the failure and confirm the remediation before closure.')
+)
+update public.ideas as ideas
+set
+ title = bounty_catalog.title,
+ summary = bounty_catalog.summary,
+ description = bounty_catalog.description,
+ threat_scenario = bounty_catalog.threat_scenario,
+ control_boundary = bounty_catalog.control_boundary,
+ proof_required = bounty_catalog.proof_required
+from bounty_catalog,
+ ideascape_bounty_seed_manifest as manifest
+where ideas.id = bounty_catalog.id::uuid
+ and ideas.slug = bounty_catalog.slug
+ and ideas.id = manifest.id
+ and ideas.slug = manifest.slug
+ and (
+ ideas.title,
+ ideas.summary,
+ ideas.description,
+ ideas.threat_scenario,
+ ideas.control_boundary,
+ ideas.proof_required
+ ) is distinct from (
+ bounty_catalog.title,
+ bounty_catalog.summary,
+ bounty_catalog.description,
+ bounty_catalog.threat_scenario,
+ bounty_catalog.control_boundary,
+ bounty_catalog.proof_required
+ );
+
+-- Close every superseded active question on the six deterministic examples
+-- without rewriting prompts, options, or private responses. This handles both
+-- the known historical seeds and unexpected drift while keeping old answers
+-- attached to their original meaning.
+update public.idea_validation_questions
+set status = 'closed'
+where idea_id in (
+ '00000000-0000-4000-8000-000000000218',
+ '00000000-0000-4000-8000-000000000203',
+ '00000000-0000-4000-8000-000000000206',
+ '00000000-0000-4000-8000-000000000204',
+ '00000000-0000-4000-8000-000000000208',
+ '00000000-0000-4000-8000-000000000201'
+)
+ and id not between '00000000-0000-4000-8000-000000000601'
+ and '00000000-0000-4000-8000-000000000606'
+ and status = 'active';
+
+insert into public.idea_validation_questions (
+ id,
+ idea_id,
+ prompt,
+ status,
+ created_at,
+ updated_at
+)
+values
+ ('00000000-0000-4000-8000-000000000601', '00000000-0000-4000-8000-000000000218', 'Is the Time Capsule Disclosure Bounty ready for an authorized test run?', 'active', '2026-08-11 19:30:00+00', '2026-08-11 19:30:00+00'),
+ ('00000000-0000-4000-8000-000000000602', '00000000-0000-4000-8000-000000000203', 'Is the Trip Relay Metadata Bounty ready for an authorized test run?', 'active', '2026-08-11 19:30:00+00', '2026-08-11 19:30:00+00'),
+ ('00000000-0000-4000-8000-000000000603', '00000000-0000-4000-8000-000000000206', 'Is the Repair Playbook Injection Bounty ready for an authorized test run?', 'active', '2026-08-11 19:30:00+00', '2026-08-11 19:30:00+00'),
+ ('00000000-0000-4000-8000-000000000604', '00000000-0000-4000-8000-000000000204', 'Is the Night Install Tamper Bounty ready for an authorized test run?', 'active', '2026-08-11 19:30:00+00', '2026-08-11 19:30:00+00'),
+ ('00000000-0000-4000-8000-000000000605', '00000000-0000-4000-8000-000000000208', 'Is the Outage Kit Supply-Chain Bounty ready for an authorized test run?', 'active', '2026-08-11 19:30:00+00', '2026-08-11 19:30:00+00'),
+ ('00000000-0000-4000-8000-000000000606', '00000000-0000-4000-8000-000000000201', 'Is the Smoke Sensor Spoofing Bounty ready for an authorized test run?', 'active', '2026-08-11 19:30:00+00', '2026-08-11 19:30:00+00')
+on conflict (id) do update
+set
+ idea_id = excluded.idea_id,
+ prompt = excluded.prompt,
+ status = excluded.status,
+ updated_at = excluded.updated_at;
+
+insert into public.idea_validation_options (
+ id,
+ question_id,
+ value,
+ label,
+ sort_order,
+ created_at
+)
+select
+ format('00000000-0000-4000-8000-%s', lpad((610 + ((question_number - 1) * 4) + option_number)::text, 12, '0'))::uuid,
+ format('00000000-0000-4000-8000-%s', lpad((600 + question_number)::text, 12, '0'))::uuid,
+ option_value,
+ option_label,
+ option_number - 1,
+ '2026-08-11 19:30:00+00'::timestamptz
+from generate_series(1, 6) as question_number
+cross join (
+ values
+ (1, 'ready-for-authorized-test'::text, 'Ready for an authorized test run under the published rules of engagement'::text),
+ (2, 'tighten-scope'::text, 'Needs a tighter target or stronger safeguards'::text),
+ (3, 'proof-not-ready'::text, 'Proof standard is not reproducible yet'::text),
+ (4, 'close-bounty'::text, 'Close this bounty without an authorized test run'::text)
+) as answer(option_number, option_value, option_label)
+on conflict (id) do update
+set
+ question_id = excluded.question_id,
+ value = excluded.value,
+ label = excluded.label,
+ sort_order = excluded.sort_order;
+
+-- Creator evidence is current-state evidence. Historical questions and their
+-- private responses stay preserved, but are not combined with an active
+-- readiness question whose answer choices have a different meaning.
+create or replace function public.get_idea_validation_summary(target_idea_id uuid)
+returns table (
+ question_id uuid,
+ prompt text,
+ option_id uuid,
+ option_value text,
+ option_label text,
+ response_count bigint
+)
+language sql
+stable
+security definer
+set search_path = ''
+as $$
+ select
+ questions.id as question_id,
+ questions.prompt,
+ options.id as option_id,
+ options.value as option_value,
+ options.label as option_label,
+ count(responses.profile_id)::bigint as response_count
+ from public.idea_validation_questions as questions
+ join public.ideas as ideas
+ on ideas.id = questions.idea_id
+ join public.idea_validation_options as options
+ on options.question_id = questions.id
+ left join public.idea_validation_responses as responses
+ on responses.question_id = questions.id
+ and responses.option_id = options.id
+ where questions.idea_id = target_idea_id
+ and questions.status = 'active'
+ and ideas.creator_id = (select auth.uid())
+ group by questions.id, questions.prompt, options.id, options.value, options.label, options.sort_order
+ order by options.sort_order;
+$$;
+
+update public.idea_pilots
+set title = 'Time Capsule Disclosure Bounty authorized test run'
+where id = '00000000-0000-4000-8000-000000000501'
+ and idea_id = '00000000-0000-4000-8000-000000000218'
+ and slug = 'project-time-capsule'
+ and title = 'Project Time Capsule pilot';
+
+-- The seed operator bio is mutable profile data. Rebrand it only if it still
+-- equals the exact prior deterministic value; preserve any human edit.
+update public.profiles
+set bio = 'System owner publishing authorized targets, clear rules of engagement, and reproducible proof standards.'
+where id = '00000000-0000-4000-8000-000000000101'
+ and username in ('ideascape-team', 'ideascape-lab')
+ and bio = 'Security operator publishing permission-first threat models, bounded controls, and reproducible proof standards.';
+
+-- Normalize the deterministic system-owner brand only while it retains the
+-- exact legacy seed value. Human-edited display names remain untouched.
+update public.profiles
+set display_name = 'IdeaScape Team'
+where id = '00000000-0000-4000-8000-000000000101'
+ and username in ('ideascape-team', 'ideascape-lab')
+ and display_name = 'Ideascape Team';
diff --git a/supabase/tests/database/idea-pilots.test.sql b/supabase/tests/database/idea-pilots.test.sql
index 7a3b851..d98ede0 100644
--- a/supabase/tests/database/idea-pilots.test.sql
+++ b/supabase/tests/database/idea-pilots.test.sql
@@ -68,11 +68,11 @@ select is(
),
row(
'project-time-capsule',
- 'Project Time Capsule pilot',
+ 'Time Capsule Disclosure Bounty authorized test run',
'validating',
'pending'
)::text,
- 'Project Time Capsule has one deterministic validating pilot plan'
+ 'Time Capsule Disclosure Bounty has one deterministic authorized test run plan'
);
select is(
(
diff --git a/supabase/tests/database/idea-seeds.test.sql b/supabase/tests/database/idea-seeds.test.sql
index 2b801fa..6d71245 100644
--- a/supabase/tests/database/idea-seeds.test.sql
+++ b/supabase/tests/database/idea-seeds.test.sql
@@ -8,343 +8,142 @@ select ok(
from public.profiles
where id = '00000000-0000-4000-8000-000000000101'
and username = 'ideascape-team'
- and display_name = 'Ideascape Team'
+ and display_name = 'IdeaScape Team'
),
- 'the Ideascape Team seed profile exists'
+ 'the deterministic system-owner profile exists'
);
select is(
- (
- select count(*)
- from public.ideas
- where slug in (
- 'shade-stop-network',
- 'skill-swap-saturdays',
- 'civic-accessibility-lab',
- 'block-ready-kits'
- )
- ),
- 4::bigint,
- 'four additional concept previews are seeded'
+ (select count(*) from public.ideas where creator_id = '00000000-0000-4000-8000-000000000101'),
+ 27::bigint,
+ 'the system owner publishes exactly 27 deterministic security bounties'
);
select is(
- (
- select count(*)
- from public.ideas
- join public.categories on categories.id = ideas.category_id
- where ideas.slug in (
- 'device-liberation-lab',
- 'file-rescue-cooperative',
- 'cloud-exit-toolkit',
- 'private-ai-workbench'
- )
- and categories.slug = 'technology'
- and ideas.status = 'published'
- and ideas.published_at is not null
- and char_length(ideas.summary) between 80 and 280
- and char_length(ideas.description) >= 400
- and position(E'\\n' in ideas.description) = 0
- and position(E'\n' in ideas.description) > 0
- and case ideas.slug
- when 'device-liberation-lab' then ideas.description ilike '%written authorization%'
- when 'file-rescue-cooperative' then ideas.description ilike '%written consent%'
- when 'cloud-exit-toolkit' then ideas.description ilike '%checksum%'
- when 'private-ai-workbench' then ideas.description ilike '%stays on the device%'
- else false
- end
- ),
- 4::bigint,
- 'four polished permission-first technology concept previews are seeded'
+ (select count(*) from public.ideas where id between '00000000-0000-4000-8000-000000000201' and '00000000-0000-4000-8000-000000000227'),
+ 27::bigint,
+ 'all deterministic bounty UUIDs remain stable'
);
select is(
- (
- select count(*)
- from public.ideas
- join public.categories on categories.id = ideas.category_id
- where ideas.slug in (
- 'home-lab-defense-clinic',
- 'community-compute-cooperative',
- 'offline-mesh-field-kit',
- 'open-repair-atlas',
- 'accessible-interface-retrofit-lab',
- 'project-time-capsule'
- )
- and categories.slug = case ideas.slug
- when 'home-lab-defense-clinic' then 'technology'
- when 'community-compute-cooperative' then 'technology'
- when 'offline-mesh-field-kit' then 'community'
- when 'open-repair-atlas' then 'environment'
- when 'accessible-interface-retrofit-lab' then 'health'
- when 'project-time-capsule' then 'education'
- end
- and ideas.status = 'published'
- and ideas.published_at is not null
- and char_length(ideas.summary) between 80 and 280
- and char_length(ideas.description) >= 400
- and position(E'\\n' in ideas.description) = 0
- and position(E'\n' in ideas.description) > 0
- and case ideas.slug
- when 'home-lab-defense-clinic' then ideas.description ilike '%participant-owned systems%'
- when 'community-compute-cooperative' then ideas.description ilike '%acceptable-use policy%'
- when 'offline-mesh-field-kit' then ideas.description ilike '%legal spectrum%'
- when 'open-repair-atlas' then ideas.description ilike '%would not publish leaked%'
- when 'accessible-interface-retrofit-lab' then ideas.description ilike '%participant consent%'
- when 'project-time-capsule' then ideas.description ilike '%license provenance%'
- else false
- end
- ),
- 6::bigint,
- 'six additional technology-forward concept previews are seeded with explicit safety boundaries'
+ (select count(*) from public.ideas where status = 'published' and published_at is not null),
+ 27::bigint,
+ 'every deterministic security bounty is published'
+);
+
+select is(
+ (select count(*) from public.ideas where title like '% Bounty'),
+ 27::bigint,
+ 'every listing is named as a bounty'
);
select is(
(
select count(*)
from public.ideas
- join public.categories on categories.id = ideas.category_id
- where ideas.slug in (
- 'waste-heat-works',
- 'model-commons-lab',
- 'glass-box-sensor-network'
- )
- and categories.slug = case ideas.slug
- when 'waste-heat-works' then 'environment'
- when 'model-commons-lab' then 'technology'
- when 'glass-box-sensor-network' then 'community'
- end
- and ideas.status = 'published'
- and ideas.published_at is not null
- and char_length(ideas.summary) between 80 and 280
- and char_length(ideas.description) >= 450
- and position(E'\\n' in ideas.description) = 0
- and position(E'\n' in ideas.description) > 0
- and case ideas.slug
- when 'waste-heat-works' then ideas.description ilike '%licensed engineering review%'
- when 'model-commons-lab' then ideas.description ilike '%participant-approved material%'
- when 'glass-box-sensor-network' then ideas.description ilike '%no physical interference%'
- else false
- end
+ where char_length(summary) between 80 and 280
+ and summary ilike '%owner-approved test environment%'
),
- 3::bigint,
- 'three bold infrastructure concepts are seeded with measurable pilots and explicit boundaries'
+ 27::bigint,
+ 'every bounty summary explains its owner-approved test environment'
);
select is(
(
select count(*)
from public.ideas
- where slug in (
- 'shade-stop-network',
- 'civic-accessibility-lab',
- 'glass-box-sensor-network'
- )
- and status = 'published'
- and char_length(summary) between 80 and 280
- and char_length(description) >= 500
- and position(E'\\n' in description) = 0
- and position(E'\n' in description) > 0
- and case slug
- when 'shade-stop-network' then
- title = 'Transit Stop Hazard Audit'
- and description ilike '%2025 Mountain Metro bus-stop self-evaluation%'
- and description ilike '%116%'
- and description ilike '%178%'
- and description ilike '%written approval%'
- when 'civic-accessibility-lab' then
- title = 'Crossing Safety Evidence Audit'
- and description ilike '%written city authorization%'
- and description ilike '%without capturing faces or license plates%'
- when 'glass-box-sensor-network' then
- title = 'Plate Reader Privacy Audit'
- and description ilike '%Colorado Open Records Act%'
- and description ilike '%no physical interference%'
- and description ilike '%private license plates%'
- else false
- end
+ where char_length(description) >= 300
+ and description ilike '%permissioned defensive challenge%'
+ and description ilike '%no production access%'
),
- 3::bigint,
- 'three current demos become Colorado Springs security audits with lawful evidence boundaries'
-);
-
-select results_eq(
- $$
- select ideas.slug, idea_media.alt_text
- from public.ideas
- join public.idea_media on idea_media.idea_id = ideas.id
- where ideas.slug in (
- 'shade-stop-network',
- 'civic-accessibility-lab',
- 'glass-box-sensor-network'
- )
- and idea_media.kind = 'image'
- and idea_media.sort_order = 0
- order by ideas.slug
- $$,
- $$
- values
- (
- 'civic-accessibility-lab'::text,
- 'A wheelchair user times a permitted crossing demonstration on Academy Boulevard while observers record safety evidence.'::text
- ),
- (
- 'glass-box-sensor-network'::text,
- 'A networked license plate reader is crossed out above a public records map of Colorado Springs.'::text
- ),
- (
- 'shade-stop-network'::text,
- 'A bare Mountain Metro bus stop is transformed with accessible pavement, seating, and engineered shade.'::text
- )
- $$,
- 'the three localized campaigns have concept-specific accessible cover descriptions'
+ 27::bigint,
+ 'every bounty description states its permissioned and production boundaries'
);
select is(
(
select count(*)
from public.ideas
- where slug in (
- 'neighbor-ride-credits',
- 'skill-swap-saturdays',
- 'after-dark-storefronts',
- 'block-ready-kits',
- 'clean-air-library'
- )
- and char_length(summary) between 80 and 280
- and char_length(description) >= 450
- and summary not ilike '%fund%'
- and description not ilike '%backers%'
- and description not ilike '%backing would%'
- and description not ilike '%launch budget%'
- and description not ilike '%early resources would%'
- and case slug
- when 'neighbor-ride-credits' then
- description ilike '%licensed accessible transport partner%'
- and description ilike '%maximum of 20%'
- when 'skill-swap-saturdays' then
- description ilike '%twelve successful teach-backs%'
- when 'after-dark-storefronts' then
- description ilike '%two weeks of baseline%'
- when 'block-ready-kits' then
- description ilike '%within 20 minutes%'
- when 'clean-air-library' then
- description ilike '%non-ozone-generating%'
- else false
- end
+ where char_length(threat_scenario) between 40 and 500
+ and char_length(control_boundary) between 40 and 500
+ and char_length(proof_required) between 40 and 500
),
- 5::bigint,
- 'five legacy previews use bounded pilots, measurable thresholds, and exploration-safe language'
+ 27::bigint,
+ 'every bounty has a complete attack, engagement, and proof case'
);
select is(
(
select count(*)
from public.ideas
- where slug in (
- 'shade-stop-network',
- 'skill-swap-saturdays',
- 'civic-accessibility-lab',
- 'block-ready-kits'
- )
- and status = 'published'
- and published_at is not null
- and char_length(summary) between 80 and 280
- and char_length(description) >= 300
- and position(E'\\n' in description) = 0
- and position(E'\n' in description) > 0
+ where control_boundary ilike '%written permission%'
+ and control_boundary ilike '%production%'
+ and control_boundary ilike '%stop%'
),
- 4::bigint,
- 'additional concept previews contain polished public copy with real paragraphs'
+ 27::bigint,
+ 'every bounty requires written permission and an explicit stop boundary'
);
select is(
(
select count(*)
from public.ideas
- where slug in (
- 'clean-air-library',
- 'repair-commons',
- 'neighbor-ride-credits',
- 'after-dark-storefronts'
- )
+ where proof_required ilike '%minimal reproduction%'
+ and proof_required ilike '%independent rerun%'
+ and proof_required ilike '%remediation%'
),
- 4::bigint,
- 'four launch ideas are seeded'
-);
-
-select results_eq(
- $$
- select ideas.slug, categories.slug
- from public.ideas
- join public.categories on categories.id = ideas.category_id
- where ideas.slug in (
- 'clean-air-library',
- 'repair-commons',
- 'neighbor-ride-credits',
- 'after-dark-storefronts'
- )
- order by ideas.slug
- $$,
- $$
- values
- ('after-dark-storefronts'::text, 'arts-culture'::text),
- ('clean-air-library'::text, 'health'::text),
- ('neighbor-ride-credits'::text, 'community'::text),
- ('repair-commons'::text, 'environment'::text)
- $$,
- 'launch ideas span four relevant categories'
+ 27::bigint,
+ 'every bounty requires reproducible proof and independent verification'
);
select is(
(
select count(*)
from public.ideas
- where slug in (
- 'clean-air-library',
- 'repair-commons',
- 'neighbor-ride-credits',
- 'after-dark-storefronts'
- )
- and status = 'published'
- and published_at is not null
+ where concat_ws(' ', title, summary, description, threat_scenario, control_boundary, proof_required)
+ ~* '\m(hunters?|hunt|test range|range run)\M'
),
- 4::bigint,
- 'all launch ideas are published'
+ 0::bigint,
+ 'bounty copy avoids competing game and range metaphors'
);
select is(
(
select count(*)
- from public.ideas
- where slug in (
- 'clean-air-library',
- 'repair-commons',
- 'neighbor-ride-credits',
- 'after-dark-storefronts'
- )
- and char_length(summary) between 80 and 280
- and char_length(description) >= 300
+ from public.categories
+ join public.ideas on ideas.category_id = categories.id
+ where categories.slug in ('arts-culture', 'community', 'education', 'environment', 'health', 'technology')
),
- 4::bigint,
- 'launch ideas contain useful discovery and detail copy'
+ 27::bigint,
+ 'all bounties remain attached to the six stable category identifiers'
);
select is(
(
- select count(*)
- from public.ideas
- where slug in (
- 'clean-air-library',
- 'repair-commons',
- 'neighbor-ride-credits',
- 'after-dark-storefronts'
- )
- and position(E'\\n' in description) = 0
- and position(E'\n' in description) > 0
+ select count(distinct categories.slug)
+ from public.categories
+ join public.ideas on ideas.category_id = categories.id
),
- 4::bigint,
- 'launch idea paragraphs use real line breaks'
+ 6::bigint,
+ 'the bounty catalog spans all six security areas'
+);
+
+select results_eq(
+ $$
+ select slug, name
+ from public.categories
+ order by slug
+ $$,
+ $$
+ values
+ ('arts-culture'::text, 'Provenance & Forgery'::text),
+ ('community'::text, 'Coordination & Resilience'::text),
+ ('education'::text, 'Human Attack Surface'::text),
+ ('environment'::text, 'Physical & Sensor Systems'::text),
+ ('health'::text, 'Privacy & Safety'::text),
+ ('technology'::text, 'Software & Compute'::text)
+ $$,
+ 'stable category slugs render as focused security areas'
);
select is(
@@ -352,198 +151,87 @@ select is(
select count(*)
from public.idea_media
join public.ideas on ideas.id = idea_media.idea_id
- where ideas.slug in (
- 'clean-air-library',
- 'repair-commons',
- 'neighbor-ride-credits',
- 'after-dark-storefronts'
- )
- and idea_media.kind = 'image'
+ where idea_media.kind = 'image'
and idea_media.sort_order = 0
and idea_media.url like 'https://ideascape-gamma.vercel.app/images/ideas/%.svg'
- and char_length(idea_media.alt_text) >= 20
),
- 4::bigint,
- 'every launch idea has an accessible hosted cover image'
+ 27::bigint,
+ 'every bounty has one deterministic hosted cover image'
);
select is(
(
select count(*)
from public.idea_media
- join public.ideas on ideas.id = idea_media.idea_id
- where ideas.slug in (
- 'shade-stop-network',
- 'skill-swap-saturdays',
- 'civic-accessibility-lab',
- 'block-ready-kits'
- )
- and idea_media.kind = 'image'
- and idea_media.sort_order = 0
- and idea_media.url like 'https://ideascape-gamma.vercel.app/images/ideas/%.svg'
- and char_length(idea_media.alt_text) >= 20
+ where kind = 'image'
+ and sort_order = 0
+ and char_length(trim(coalesce(alt_text, ''))) >= 40
),
- 4::bigint,
- 'every additional concept preview has an accessible hosted cover image'
+ 27::bigint,
+ 'every bounty cover preserves a meaningful accessible description'
);
select is(
(
select count(*)
from public.idea_media
- join public.ideas on ideas.id = idea_media.idea_id
- where ideas.slug in (
- 'device-liberation-lab',
- 'file-rescue-cooperative',
- 'cloud-exit-toolkit',
- 'private-ai-workbench'
- )
- and idea_media.kind = 'image'
- and idea_media.sort_order = 0
- and idea_media.url like 'https://ideascape-gamma.vercel.app/images/ideas/%.svg'
- and char_length(idea_media.alt_text) >= 20
+ where coalesce(alt_text, '') ~* '\m(hunters?|hunt|test range|range run)\M'
),
- 4::bigint,
- 'every technology concept preview has an accessible hosted cover image'
+ 0::bigint,
+ 'media descriptions avoid competing game and range metaphors'
);
select is(
(
- select count(*)
- from public.idea_media
- join public.ideas on ideas.id = idea_media.idea_id
- where ideas.slug in (
- 'home-lab-defense-clinic',
- 'community-compute-cooperative',
- 'offline-mesh-field-kit',
- 'open-repair-atlas',
- 'accessible-interface-retrofit-lab',
- 'project-time-capsule'
- )
- and idea_media.kind = 'image'
- and idea_media.sort_order = 0
- and idea_media.url like 'https://ideascape-gamma.vercel.app/images/ideas/%.svg'
- and char_length(idea_media.alt_text) >= 20
+ select title
+ from public.ideas
+ where slug = 'project-time-capsule'
),
- 6::bigint,
- 'every new concept preview has an accessible hosted cover image'
+ 'Time Capsule Disclosure Bounty',
+ 'the authorized test-run example keeps its stable slug and focused title'
);
select is(
(
- select count(*)
- from public.idea_media
- join public.ideas on ideas.id = idea_media.idea_id
- where ideas.slug in (
- 'waste-heat-works',
- 'model-commons-lab',
- 'glass-box-sensor-network'
- )
- and idea_media.kind = 'image'
- and idea_media.sort_order = 0
- and idea_media.url like 'https://ideascape-gamma.vercel.app/images/ideas/%.svg'
- and char_length(idea_media.alt_text) >= 20
+ select title
+ from public.ideas
+ where slug = 'software-supply-chain-clinic'
),
- 3::bigint,
- 'every bold infrastructure concept has an accessible hosted cover image'
+ 'Dependency Substitution Bounty',
+ 'the supply-chain example is a concrete security bounty'
);
select is(
(
select count(*)
- from public.ideas
- join public.categories on categories.id = ideas.category_id
- where ideas.slug in (
- 'oral-history-provenance-lab',
- 'neighborhood-incident-relay',
- 'phishing-drill-library',
- 'water-sensor-integrity-watch',
- 'clinic-device-privacy-check',
- 'software-supply-chain-clinic'
- )
- and categories.slug = case ideas.slug
- when 'oral-history-provenance-lab' then 'arts-culture'
- when 'neighborhood-incident-relay' then 'community'
- when 'phishing-drill-library' then 'education'
- when 'water-sensor-integrity-watch' then 'environment'
- when 'clinic-device-privacy-check' then 'health'
- when 'software-supply-chain-clinic' then 'technology'
- end
- and ideas.status = 'published'
- and ideas.published_at is not null
- and char_length(ideas.summary) between 80 and 280
- and char_length(ideas.description) >= 450
- and char_length(ideas.threat_scenario) between 40 and 500
- and char_length(ideas.control_boundary) between 40 and 500
- and char_length(ideas.proof_required) between 40 and 500
- and position(E'\\n' in ideas.description) = 0
- and position(E'\n' in ideas.description) > 0
+ from public.idea_validation_questions
+ where id between '00000000-0000-4000-8000-000000000601' and '00000000-0000-4000-8000-000000000606'
+ and status = 'active'
+ and prompt ilike '%authorized test run%'
),
6::bigint,
- 'six security-first concept previews span every catalog category'
+ 'six focused readiness questions ask about authorized test runs'
);
select is(
(
select count(*)
- from public.idea_media
- join public.ideas on ideas.id = idea_media.idea_id
- where ideas.slug in (
- 'oral-history-provenance-lab',
- 'neighborhood-incident-relay',
- 'phishing-drill-library',
- 'water-sensor-integrity-watch',
- 'clinic-device-privacy-check',
- 'software-supply-chain-clinic'
- )
- and idea_media.kind = 'image'
- and idea_media.sort_order = 0
- and idea_media.url like 'https://ideascape-gamma.vercel.app/images/ideas/%.svg'
- and char_length(idea_media.alt_text) >= 20
+ from public.idea_validation_options
+ where question_id between '00000000-0000-4000-8000-000000000601' and '00000000-0000-4000-8000-000000000606'
),
- 6::bigint,
- 'every security-first preview has an accessible hosted cover image'
+ 24::bigint,
+ 'the focused readiness questions have four deterministic options each'
);
-set local role anon;
select is(
(
- select count(*)
- from public.ideas
- where slug in (
- 'clean-air-library',
- 'repair-commons',
- 'neighbor-ride-credits',
- 'after-dark-storefronts',
- 'shade-stop-network',
- 'skill-swap-saturdays',
- 'civic-accessibility-lab',
- 'block-ready-kits',
- 'device-liberation-lab',
- 'file-rescue-cooperative',
- 'cloud-exit-toolkit',
- 'private-ai-workbench',
- 'home-lab-defense-clinic',
- 'community-compute-cooperative',
- 'offline-mesh-field-kit',
- 'open-repair-atlas',
- 'accessible-interface-retrofit-lab',
- 'project-time-capsule',
- 'waste-heat-works',
- 'model-commons-lab',
- 'glass-box-sensor-network',
- 'oral-history-provenance-lab',
- 'neighborhood-incident-relay',
- 'phishing-drill-library',
- 'water-sensor-integrity-watch',
- 'clinic-device-privacy-check',
- 'software-supply-chain-clinic'
- )
+ select bio
+ from public.profiles
+ where id = '00000000-0000-4000-8000-000000000101'
),
- 27::bigint,
- 'anonymous visitors can discover every demo idea'
+ 'System owner publishing authorized targets, clear rules of engagement, and reproducible proof standards.',
+ 'the deterministic profile explains the system-owner role'
);
-reset role;
select * from finish();
rollback;
diff --git a/supabase/tests/database/idea-validation.test.sql b/supabase/tests/database/idea-validation.test.sql
index a83d805..dab05b1 100644
--- a/supabase/tests/database/idea-validation.test.sql
+++ b/supabase/tests/database/idea-validation.test.sql
@@ -71,27 +71,26 @@ select is(
(
select prompt
from public.idea_validation_questions
- where id = '00000000-0000-4000-8000-000000000401'
+ where id = '00000000-0000-4000-8000-000000000601'
and idea_id = '00000000-0000-4000-8000-000000000218'
and status = 'active'
),
- 'What could you bring to a first Project Time Capsule pilot?',
- 'Project Time Capsule has one deterministic active pilot question'
+ 'Is the Time Capsule Disclosure Bounty ready for an authorized test run?',
+ 'Time Capsule Disclosure has one deterministic active readiness question'
);
select is(
(
select array_agg(value order by sort_order)
from public.idea_validation_options
- where question_id = '00000000-0000-4000-8000-000000000401'
+ where question_id = '00000000-0000-4000-8000-000000000601'
),
array[
- 'open-source-project',
- 'coursework-research-tool',
- 'creative-software',
- 'civic-community-application',
- 'rebuild-testing'
+ 'ready-for-authorized-test',
+ 'tighten-scope',
+ 'proof-not-ready',
+ 'close-bounty'
],
- 'the pilot question has five deterministic action-oriented options'
+ 'the readiness question has four deterministic decision options'
);
select results_eq(
@@ -99,48 +98,48 @@ select results_eq(
select id, idea_id, prompt, status::text
from public.idea_validation_questions
where id in (
- '00000000-0000-4000-8000-000000000402',
- '00000000-0000-4000-8000-000000000403',
- '00000000-0000-4000-8000-000000000404',
- '00000000-0000-4000-8000-000000000405',
- '00000000-0000-4000-8000-000000000406'
+ '00000000-0000-4000-8000-000000000602',
+ '00000000-0000-4000-8000-000000000603',
+ '00000000-0000-4000-8000-000000000604',
+ '00000000-0000-4000-8000-000000000605',
+ '00000000-0000-4000-8000-000000000606'
)
order by id
$$,
$$
values
(
- '00000000-0000-4000-8000-000000000402'::uuid,
+ '00000000-0000-4000-8000-000000000602'::uuid,
'00000000-0000-4000-8000-000000000203'::uuid,
- 'Which real signal could you provide for a 30-day essential-trip trial?'::text,
+ 'Is the Trip Relay Metadata Bounty ready for an authorized test run?'::text,
'active'::text
),
(
- '00000000-0000-4000-8000-000000000403'::uuid,
+ '00000000-0000-4000-8000-000000000603'::uuid,
'00000000-0000-4000-8000-000000000206'::uuid,
- 'What could you commit to during a three-Saturday repair teach-back?'::text,
+ 'Is the Repair Playbook Injection Bounty ready for an authorized test run?'::text,
'active'::text
),
(
- '00000000-0000-4000-8000-000000000404'::uuid,
+ '00000000-0000-4000-8000-000000000604'::uuid,
'00000000-0000-4000-8000-000000000204'::uuid,
- 'Which prerequisite could you provide for a three-window evening test?'::text,
+ 'Is the Night Install Tamper Bounty ready for an authorized test run?'::text,
'active'::text
),
(
- '00000000-0000-4000-8000-000000000405'::uuid,
+ '00000000-0000-4000-8000-000000000605'::uuid,
'00000000-0000-4000-8000-000000000208'::uuid,
- 'Which role could you realistically take in a one-building outage drill?'::text,
+ 'Is the Outage Kit Supply-Chain Bounty ready for an authorized test run?'::text,
'active'::text
),
(
- '00000000-0000-4000-8000-000000000406'::uuid,
+ '00000000-0000-4000-8000-000000000606'::uuid,
'00000000-0000-4000-8000-000000000201'::uuid,
- 'During a smoke alert, what could you reliably support within two hours?'::text,
+ 'Is the Smoke Sensor Spoofing Bounty ready for an authorized test run?'::text,
'active'::text
)
$$,
- 'five bounded concepts have deterministic active risk-first questions'
+ 'five bounded bounties have deterministic active readiness questions'
);
select is(
@@ -150,20 +149,20 @@ select is(
select question_id
from public.idea_validation_options
where question_id in (
- '00000000-0000-4000-8000-000000000402',
- '00000000-0000-4000-8000-000000000403',
- '00000000-0000-4000-8000-000000000404',
- '00000000-0000-4000-8000-000000000405',
- '00000000-0000-4000-8000-000000000406'
+ '00000000-0000-4000-8000-000000000602',
+ '00000000-0000-4000-8000-000000000603',
+ '00000000-0000-4000-8000-000000000604',
+ '00000000-0000-4000-8000-000000000605',
+ '00000000-0000-4000-8000-000000000606'
)
group by question_id
- having count(*) = 5
+ having count(*) = 4
and min(sort_order) = 0
- and max(sort_order) = 4
+ and max(sort_order) = 3
) as complete_questions
),
5::bigint,
- 'each new focused question has five deterministic ordered options'
+ 'each new readiness question has four deterministic ordered options'
);
insert into auth.users (
@@ -192,8 +191,8 @@ select is(
select count(*)
from public.get_idea_validation_question('00000000-0000-4000-8000-000000000218')
),
- 5::bigint,
- 'anonymous visitors can read every active pilot-question option'
+ 4::bigint,
+ 'anonymous visitors can read every active readiness-question option'
);
select is(
(
@@ -209,7 +208,7 @@ select is(
cross join lateral public.get_idea_validation_question(target_ideas.idea_id) as question
where question.viewer_option_id is null
),
- 25::bigint,
+ 20::bigint,
'anonymous visitors can read every new option without receiving a private choice'
);
select is(
@@ -225,7 +224,7 @@ select is(
(
select count(*)
from public.idea_validation_questions
- where id = '00000000-0000-4000-8000-000000000401'
+ where id = '00000000-0000-4000-8000-000000000601'
),
1::bigint,
'anonymous visitors can read the active public question'
@@ -242,8 +241,8 @@ set local role authenticated;
set local "request.jwt.claim.sub" = '55555555-5555-4555-8555-555555555555';
insert into public.idea_validation_responses (question_id, option_id, profile_id)
values (
- '00000000-0000-4000-8000-000000000401',
- '00000000-0000-4000-8000-000000000411',
+ '00000000-0000-4000-8000-000000000601',
+ '00000000-0000-4000-8000-000000000611',
'55555555-5555-4555-8555-555555555555'
);
select is(
@@ -256,12 +255,12 @@ select is(
select distinct viewer_option_id
from public.get_idea_validation_question('00000000-0000-4000-8000-000000000218')
),
- '00000000-0000-4000-8000-000000000411'::uuid,
+ '00000000-0000-4000-8000-000000000611'::uuid,
'the question function returns only the current member choice'
);
update public.idea_validation_responses
-set option_id = '00000000-0000-4000-8000-000000000415'
-where question_id = '00000000-0000-4000-8000-000000000401'
+set option_id = '00000000-0000-4000-8000-000000000614'
+where question_id = '00000000-0000-4000-8000-000000000601'
and profile_id = '55555555-5555-4555-8555-555555555555';
select is(
(select count(*) from public.idea_validation_responses),
@@ -272,9 +271,9 @@ select is(
(
select option_id
from public.idea_validation_responses
- where question_id = '00000000-0000-4000-8000-000000000401'
+ where question_id = '00000000-0000-4000-8000-000000000601'
),
- '00000000-0000-4000-8000-000000000415'::uuid,
+ '00000000-0000-4000-8000-000000000614'::uuid,
'members can change their own pilot answer'
);
@@ -294,18 +293,18 @@ select is(
'other members never receive someone else pilot choice'
);
update public.idea_validation_responses
-set option_id = '00000000-0000-4000-8000-000000000412'
-where question_id = '00000000-0000-4000-8000-000000000401'
+set option_id = '00000000-0000-4000-8000-000000000612'
+where question_id = '00000000-0000-4000-8000-000000000601'
and profile_id = '55555555-5555-4555-8555-555555555555';
reset role;
select is(
(
select option_id
from public.idea_validation_responses
- where question_id = '00000000-0000-4000-8000-000000000401'
+ where question_id = '00000000-0000-4000-8000-000000000601'
and profile_id = '55555555-5555-4555-8555-555555555555'
),
- '00000000-0000-4000-8000-000000000415'::uuid,
+ '00000000-0000-4000-8000-000000000614'::uuid,
'members cannot change another member pilot choice'
);
@@ -316,8 +315,8 @@ select is(
select count(*)
from public.get_idea_validation_summary('00000000-0000-4000-8000-000000000218')
),
- 5::bigint,
- 'the concept creator receives one aggregate row per pilot option'
+ 4::bigint,
+ 'the system owner receives only the current readiness options'
);
select is(
(
@@ -341,8 +340,8 @@ select throws_ok(
$$
insert into public.idea_validation_responses (question_id, option_id, profile_id)
values (
- '00000000-0000-4000-8000-000000000401',
- '00000000-0000-4000-8000-000000000411',
+ '00000000-0000-4000-8000-000000000601',
+ '00000000-0000-4000-8000-000000000611',
'55555555-5555-4555-8555-555555555555'
)
$$,
@@ -353,7 +352,7 @@ select throws_ok(
set local "request.jwt.claim.sub" = '55555555-5555-4555-8555-555555555555';
delete from public.idea_validation_responses
-where question_id = '00000000-0000-4000-8000-000000000401'
+where question_id = '00000000-0000-4000-8000-000000000601'
and profile_id = '55555555-5555-4555-8555-555555555555';
select is(
(select count(*) from public.idea_validation_responses),
@@ -364,15 +363,15 @@ reset role;
update public.idea_validation_questions
set status = 'closed'
-where id = '00000000-0000-4000-8000-000000000401';
+where id = '00000000-0000-4000-8000-000000000601';
set local role authenticated;
set local "request.jwt.claim.sub" = '55555555-5555-4555-8555-555555555555';
select throws_ok(
$$
insert into public.idea_validation_responses (question_id, option_id, profile_id)
values (
- '00000000-0000-4000-8000-000000000401',
- '00000000-0000-4000-8000-000000000411',
+ '00000000-0000-4000-8000-000000000601',
+ '00000000-0000-4000-8000-000000000611',
'55555555-5555-4555-8555-555555555555'
)
$$,
diff --git a/supabase/tests/database/security-positioning.test.sql b/supabase/tests/database/security-positioning.test.sql
index 6e0cdb9..c9c9e3a 100644
--- a/supabase/tests/database/security-positioning.test.sql
+++ b/supabase/tests/database/security-positioning.test.sql
@@ -8,28 +8,28 @@ select is(
from public.categories
join (
values
- ('arts-culture'::text, 'Provenance & Authenticity'::text),
- ('community'::text, 'Resilience & Response'::text),
- ('education'::text, 'Human Risk'::text),
- ('environment'::text, 'Infrastructure Integrity'::text),
+ ('arts-culture'::text, 'Provenance & Forgery'::text),
+ ('community'::text, 'Coordination & Resilience'::text),
+ ('education'::text, 'Human Attack Surface'::text),
+ ('environment'::text, 'Physical & Sensor Systems'::text),
('health'::text, 'Privacy & Safety'::text),
- ('technology'::text, 'Software & Systems'::text)
+ ('technology'::text, 'Software & Compute'::text)
) as expected(slug, name)
on expected.slug = categories.slug
and expected.name = categories.name
),
6::bigint,
- 'legacy category identifiers render as six security domains'
+ 'legacy category identifiers render as six focused security areas'
);
select is(
(
select count(*)
from public.categories
- where description !~* '(security|threat|risk|control|privacy|integrity|resilien|authentic)'
+ where description !~* '(authorized|bount|security|threat|risk|control|privacy|integrity|resilien|authentic)'
),
0::bigint,
- 'every security domain description states its security purpose'
+ 'every security-area description states its authorized security purpose'
);
select is(
diff --git a/supabase/verification/authorized-bounty-upgrade.test.sql b/supabase/verification/authorized-bounty-upgrade.test.sql
new file mode 100644
index 0000000..d7070e5
--- /dev/null
+++ b/supabase/verification/authorized-bounty-upgrade.test.sql
@@ -0,0 +1,158 @@
+\set ON_ERROR_STOP on
+begin;
+
+-- Simulate mutable data and drift that may exist immediately before upgrade.
+insert into public.ideas (
+ id, creator_id, category_id, slug, title, summary, description,
+ threat_scenario, control_boundary, proof_required, created_at, updated_at
+)
+values (
+ '88888888-8888-4888-8888-888888888811',
+ '00000000-0000-4000-8000-000000000101',
+ (select id from public.categories where slug = 'technology'),
+ 'private-upgrade-fixture',
+ 'Private creator draft',
+ 'A private creator-owned draft that is outside the deterministic seed manifest.',
+ 'This private creator copy must remain unchanged when the deterministic catalog is recast.',
+ 'A detailed private threat scenario remains attached to this private creator draft.',
+ 'A detailed private control boundary remains attached to this private creator draft.',
+ 'A detailed private proof requirement remains attached to this private creator draft.',
+ '2025-01-02 03:04:05+00',
+ '2025-01-02 03:04:05+00'
+);
+
+insert into public.idea_media (id, idea_id, kind, url, alt_text, sort_order)
+values (
+ '88888888-8888-4888-8888-888888888812',
+ '88888888-8888-4888-8888-888888888811',
+ 'image',
+ 'https://example.invalid/private-upgrade-fixture.png',
+ 'A detailed private creator image description that must remain unchanged.',
+ 0
+);
+
+update public.idea_validation_questions
+set status = 'closed'
+where id = '00000000-0000-4000-8000-000000000601';
+
+insert into public.idea_validation_questions (
+ id, idea_id, prompt, status, created_at, updated_at
+)
+values (
+ '88888888-8888-4888-8888-888888888813',
+ '00000000-0000-4000-8000-000000000218',
+ 'Unexpected active historical question that must be preserved but closed?',
+ 'active',
+ '2025-01-02 03:04:05+00',
+ '2025-01-02 03:04:05+00'
+);
+
+update public.ideas
+set title = 'Project Time Capsule'
+where id = '00000000-0000-4000-8000-000000000218';
+
+update public.idea_pilots
+set title = 'Project Time Capsule pilot'
+where id = '00000000-0000-4000-8000-000000000501';
+
+update public.idea_validation_options
+set value = 'ready-in-range'
+where id = '00000000-0000-4000-8000-000000000611';
+
+create temporary table bounty_upgrade_fixture_before as
+select
+ ideas.updated_at,
+ media.alt_text
+from public.ideas as ideas
+join public.idea_media as media on media.idea_id = ideas.id
+where ideas.id = '88888888-8888-4888-8888-888888888811'
+ and media.id = '88888888-8888-4888-8888-888888888812';
+
+\ir ../migrations/20260811193000_recast_as_authorized_bounty_network.sql
+\ir ../migrations/20260811193000_recast_as_authorized_bounty_network.sql
+
+do $$
+begin
+ if (
+ select concat_ws(' | ', title, summary, description)
+ from public.ideas
+ where id = '88888888-8888-4888-8888-888888888811'
+ ) is distinct from 'Private creator draft | A private creator-owned draft that is outside the deterministic seed manifest. | This private creator copy must remain unchanged when the deterministic catalog is recast.' then
+ raise exception 'authorized bounty migration changed non-seed private copy';
+ end if;
+
+ if (
+ select ideas.updated_at
+ from public.ideas as ideas
+ where ideas.id = '88888888-8888-4888-8888-888888888811'
+ ) is distinct from (select updated_at from bounty_upgrade_fixture_before) then
+ raise exception 'authorized bounty migration changed non-seed private timestamp';
+ end if;
+
+ if (
+ select alt_text
+ from public.idea_media
+ where id = '88888888-8888-4888-8888-888888888812'
+ ) is distinct from (select alt_text from bounty_upgrade_fixture_before) then
+ raise exception 'authorized bounty migration changed non-seed private media copy';
+ end if;
+
+ if (
+ select title
+ from public.ideas
+ where id = '00000000-0000-4000-8000-000000000218'
+ ) is distinct from 'Time Capsule Disclosure Bounty' then
+ raise exception 'authorized bounty migration did not recast the deterministic title';
+ end if;
+
+ if (
+ select display_name
+ from public.profiles
+ where id = '00000000-0000-4000-8000-000000000101'
+ ) is distinct from 'IdeaScape Team' then
+ raise exception 'authorized bounty migration did not normalize the deterministic system-owner brand';
+ end if;
+
+ if (
+ select title
+ from public.idea_pilots
+ where id = '00000000-0000-4000-8000-000000000501'
+ ) is distinct from 'Time Capsule Disclosure Bounty authorized test run' then
+ raise exception 'authorized bounty migration did not recast the test-run title';
+ end if;
+
+ if (
+ select value
+ from public.idea_validation_options
+ where id = '00000000-0000-4000-8000-000000000611'
+ ) is distinct from 'ready-for-authorized-test' then
+ raise exception 'authorized bounty migration did not reconcile the readiness value';
+ end if;
+
+ if (
+ select status
+ from public.idea_validation_questions
+ where id = '88888888-8888-4888-8888-888888888813'
+ ) is distinct from 'closed'::public.validation_question_status then
+ raise exception 'authorized bounty migration left an unexpected historical question active';
+ end if;
+
+ if (
+ select count(*)
+ from public.idea_validation_questions
+ where idea_id = '00000000-0000-4000-8000-000000000218'
+ and status = 'active'
+ ) <> 1 then
+ raise exception 'authorized bounty migration did not leave exactly one active readiness question';
+ end if;
+
+ if (
+ select count(*)
+ from public.get_idea_validation_summary('00000000-0000-4000-8000-000000000218')
+ ) <> 0 then
+ raise exception 'unauthenticated validation summary unexpectedly returned rows';
+ end if;
+end
+$$;
+
+rollback;
diff --git a/vitest.config.ts b/vitest.config.ts
index 30815ba..572baf3 100644
--- a/vitest.config.ts
+++ b/vitest.config.ts
@@ -13,5 +13,6 @@ export default defineConfig({
environment: "jsdom",
setupFiles: ["./src/test/setup.ts"],
css: true,
+ exclude: ["e2e/**", "node_modules/**", "dist/**"],
},
});