diff --git a/src/features/pilots/pilot-page.test.tsx b/src/features/pilots/pilot-page.test.tsx
index edd6ee2..f159c00 100644
--- a/src/features/pilots/pilot-page.test.tsx
+++ b/src/features/pilots/pilot-page.test.tsx
@@ -88,7 +88,9 @@ describe("PilotPage", () => {
name: /close, revise, or proceed/i,
});
expect(within(decisions).getByText(/^continue$/i)).toBeInTheDocument();
- expect(decisions).toHaveTextContent(/5 qualified test participants/i);
+ expect(decisions).toHaveTextContent(
+ /5 reviewers ready for an authorized test/i,
+ );
expect(decisions).toHaveTextContent(/3 suitable authorized projects/i);
expect(within(decisions).getByText(/^revise$/i)).toBeInTheDocument();
expect(within(decisions).getByText(/^archive$/i)).toBeInTheDocument();
diff --git a/src/features/pilots/pilot-page.tsx b/src/features/pilots/pilot-page.tsx
index 943b597..c7261d2 100644
--- a/src/features/pilots/pilot-page.tsx
+++ b/src/features/pilots/pilot-page.tsx
@@ -141,12 +141,12 @@ export function PilotPage() {
{
expect(within(dashboard).getByText("9")).toBeInTheDocument();
expect(within(dashboard).getByText("5")).toBeInTheDocument();
expect(within(dashboard).getByText("3")).toBeInTheDocument();
+ expect(within(dashboard).getByText(/ready reviewers/i)).toBeInTheDocument();
+ expect(dashboard).not.toHaveTextContent(/participant responses/i);
expect(within(dashboard).getByText("2 active")).toBeInTheDocument();
expect(within(dashboard).getByText("1 accepted")).toBeInTheDocument();
expect(within(dashboard).getByText("2 spaces remain")).toBeInTheDocument();
diff --git a/src/features/pilots/pilot-readiness-panel.tsx b/src/features/pilots/pilot-readiness-panel.tsx
index 818eff3..8bc9f51 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 authorized test run.",
+ "Ready-reviewer and suitable-project evidence support preparing the published authorized test run.",
},
revise: {
label: "Revise before advancing",
@@ -101,7 +101,7 @@ export function PilotReadinessPanel({ pilotId }: { pilotId: string }) {
/>
= evidence.continue_participant_threshold
+ and evidence.project_response_count >= evidence.continue_project_threshold
+ then 'continue'
+ when timezone('utc', now()) >= evidence.created_at + make_interval(days => evidence.evidence_window_days)
+ and evidence.meaningful_signal_count <= evidence.archive_signal_ceiling
+ then 'archive'
+ when timezone('utc', now()) >= evidence.created_at + make_interval(days => evidence.evidence_window_days)
+ then 'revise'
+ else 'pending'
+ end as recommendation
+ from evidence;
+$$;
+
+comment on function public.get_pilot_readiness_summary(uuid) is
+ 'Returns creator/operator-only aggregate readiness: distinct current ready reviewers, explicitly scoped historical project evidence, private application totals, and a threshold preview without member identities.';
diff --git a/supabase/tests/database/idea-pilots.test.sql b/supabase/tests/database/idea-pilots.test.sql
index d98ede0..4a65d64 100644
--- a/supabase/tests/database/idea-pilots.test.sql
+++ b/supabase/tests/database/idea-pilots.test.sql
@@ -589,6 +589,13 @@ values
('00000000-0000-4000-8000-000000000401', '00000000-0000-4000-8000-000000000411', '55555555-5555-4555-8555-555555555555'),
('00000000-0000-4000-8000-000000000401', '00000000-0000-4000-8000-000000000415', '66666666-6666-4666-8666-666666666666');
+insert into public.idea_validation_responses (question_id, option_id, profile_id)
+values (
+ '00000000-0000-4000-8000-000000000601',
+ '00000000-0000-4000-8000-000000000611',
+ '55555555-5555-4555-8555-555555555555'
+);
+
set local role authenticated;
set local "request.jwt.claim.sub" = '55555555-5555-4555-8555-555555555555';
set local "request.jwt.claims" = '{"sub":"55555555-5555-4555-8555-555555555555","role":"authenticated","app_metadata":{}}';
@@ -613,8 +620,8 @@ select is(
)::text
from public.get_pilot_readiness_summary('00000000-0000-4000-8000-000000000501')
),
- row(2, 2, 1, 1, 1, 2, 'pending')::text,
- 'the creator receives aggregate progress without applicant identities'
+ row(2, 1, 1, 1, 1, 2, 'pending')::text,
+ 'the creator receives current ready-reviewer and historical project aggregates without double-counting one member'
);
set local "request.jwt.claim.sub" = '77777777-7777-4777-8777-777777777777';
@@ -655,6 +662,11 @@ select is(
);
reset role;
+insert into public.idea_validation_responses (question_id, option_id, profile_id)
+values
+ ('00000000-0000-4000-8000-000000000601', '00000000-0000-4000-8000-000000000611', '66666666-6666-4666-8666-666666666666'),
+ ('00000000-0000-4000-8000-000000000601', '00000000-0000-4000-8000-000000000611', '77777777-7777-4777-8777-777777777777');
+
update public.idea_pilots
set continue_participant_threshold = 3, continue_project_threshold = 2
where id = '00000000-0000-4000-8000-000000000501';
diff --git a/supabase/verification/authorized-bounty-upgrade.test.sql b/supabase/verification/authorized-bounty-upgrade.test.sql
index d7070e5..8038fed 100644
--- a/supabase/verification/authorized-bounty-upgrade.test.sql
+++ b/supabase/verification/authorized-bounty-upgrade.test.sql
@@ -59,6 +59,19 @@ update public.idea_validation_options
set value = 'ready-in-range'
where id = '00000000-0000-4000-8000-000000000611';
+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-000000000101'
+ ),
+ (
+ '00000000-0000-4000-8000-000000000601',
+ '00000000-0000-4000-8000-000000000611',
+ '00000000-0000-4000-8000-000000000101'
+ );
+
create temporary table bounty_upgrade_fixture_before as
select
ideas.updated_at,
@@ -70,6 +83,8 @@ where ideas.id = '88888888-8888-4888-8888-888888888811'
\ir ../migrations/20260811193000_recast_as_authorized_bounty_network.sql
\ir ../migrations/20260811193000_recast_as_authorized_bounty_network.sql
+\ir ../migrations/20260813203034_fix_pilot_readiness_generation_counts.sql
+\ir ../migrations/20260813203034_fix_pilot_readiness_generation_counts.sql
do $$
begin
@@ -152,6 +167,19 @@ begin
) <> 0 then
raise exception 'unauthenticated validation summary unexpectedly returned rows';
end if;
+
+ perform set_config(
+ 'request.jwt.claim.sub',
+ '00000000-0000-4000-8000-000000000101',
+ true
+ );
+
+ if (
+ select row(participant_response_count, project_response_count)::text
+ from public.get_pilot_readiness_summary('00000000-0000-4000-8000-000000000501')
+ ) is distinct from row(1::bigint, 1::bigint)::text then
+ raise exception 'authorized bounty migration mixed historical and active pilot-readiness responses';
+ end if;
end
$$;