engineering
Curation Without Global Rejection
Naseeg keeps a project's public identity separate from a Fund's round-specific decision.

In many funding systems, a rejection can become a global label. A creator applies to one opportunity, receives a negative decision, and the product model quietly treats that decision as a judgment on the project itself.
Naseeg takes a narrower view.
A project is not a submission
Projects are persistent content with their own publication state. A Fund submission is a separate record that links a project, a Fund, and a specific round. The submission can move from submitted to accepted to curated, or it can be rejected. That decision belongs to the Fund and the round; it does not rewrite the project's global status.
One project may appear in several Funds during the same round and in new Funds in later rounds. A Fund rejection therefore means only what it actually says: this Fund did not choose to curate this submission at this time.
Why the model matters
This separation preserves dignity for creators and clarity for Fund Accounts. It avoids accidental global consequences, supports multi-Fund participation, and makes authorization easier to reason about. Fund owners review only submissions to their Funds; administrators have an explicit global override.
Good domain modeling is often an act of restraint. It prevents a local decision from becoming a broader claim than the system has earned.
The current record that carries the decision
Naseeg's current workflow is round-first. An administrator opens a round; Fund Accounts create Funds; creators publish reusable projects and submit them to one or more Funds; Fund owners make their local decisions; supporters purchase Artifacts; and the round is closed and finalized by an administrator. The important technical point is that a project's public identity does not need to change when one Fund chooses not to curate it.
The round-specific decision lives in fundSubmissions. The support-eligibility helper reads the project status, current round status, and the statuses of submissions in that round:
export function isProjectSupportEligible(input: {
roundStatus?: string | null;
projectStatus: string;
submissionStatuses: string[];
}) {
return Boolean(input.roundStatus) &&
normalizedRoundStatus(input.roundStatus!) === "open" &&
normalizedProjectStatus(input.projectStatus) === "published" &&
input.submissionStatuses.includes("curated");
}
This is a small function with a large consequence. The system does not ask “has this project ever been rejected?” It asks a narrower question: is this published project curated in an open round? That keeps eligibility tied to the actual decision that matters.
01 / reusable
Project
A published project and its Artifacts can persist across rounds.
02 / local
Fund submission
A Fund records its own round-specific review and curation state.
03 / eligible
Support + match
Only curated projects in an open round can receive counted support.
Fund review is authorized locally
Naseeg's curation module first requires the fund role, then requires an open round. A review contains the project, round, curator, optional score and note, a local status, and an optional on-chain transaction reference. Existing reviews from the same curator for the project are patched instead of duplicated.
// Simplified from convex/curation.ts.
const { user } = await requireRole(ctx, "fund");
const openRound = await requireOpenRound(ctx);
const values = {
projectId: args.projectId,
roundId: openRound._id,
curatorUserId: user._id,
score: args.score,
note: args.note,
status: args.status,
onchainTxHash: args.onchainTxHash,
updatedAt: now(),
};
if (review) await ctx.db.patch(review._id, values);
else await ctx.db.insert("curationReviews", { ...values, createdAt: now() });
The scope is the key. This is not a global moderation mutation on projects. A Fund Account is not implicitly granted a universal ability to change the standing of every creator. It records a decision within the round it owns; administrator override remains explicit elsewhere in the system.
Support eligibility is checked at checkout preparation
Naseeg does not rely only on what a page displayed when the supporter opened it. When checkout is prepared, the backend loads the project and Artifact, finds the open round, reads the relevant submission records, and applies the eligibility helper before creating a contribution.
const openRound = await findOpenRound(ctx);
const submissions = await ctx.db
.query("fundSubmissions")
.withIndex("by_project", (q) => q.eq("projectId", project._id))
.take(100);
const eligible = isProjectSupportEligible({
roundStatus: openRound?.status,
projectStatus: project.status,
submissionStatuses: submissions
.filter((item) => item.roundId === openRound?._id)
.map((item) => item.status),
});
if (!eligible) fail("INVALID_STATE", { resource: "curated project" });
That backend check matters even if the interface is correct. A project can be unpublished, an Artifact can cease to be live, a round can close, or its last eligible submission can change after the browser loaded the page. The current state decides whether the contribution record is created.
Matching is calculated per Fund, not as a global score
The same local model carries into allocations. recalculateFund reads submissions for one Fund, filters to curated, derives direct sales for each project in that Fund's round, and calls the funding calculation with that Fund's pool, match multiple, curated-project count, and explicit cap. It then writes match and prize allocation rows for that Fund and round.
const eligible = submissions.filter((item) => item.status === "curated");
const allocation = calculateFunding({
totalPoolCents: fund.totalPoolCents,
matchMultipleBps: fund.matchMultipleBps,
curatedProjectCount: eligible.length,
directSalesCents,
explicitProjectCapCents: fund.explicitProjectCapCents,
});
One paid Artifact sale can be attributed to the open round and independently unlock matching in every Fund that curated the project in that round. That is a deliberate product rule, not an accidental side effect of a global leaderboard.
Why this is better than a universal acceptance state
A universal “accepted” flag is attractive because it is simple to query. It is also easy to misuse. It makes a Fund's editorial choice look like a statement about the creator, prevents later context from changing the outcome, and makes multi-Fund participation awkward. Naseeg keeps the reusable project and its round-scoped opportunity separate so that each record says only what it can honestly support.
Naseeg is still Coming Soon. These details describe the current product implementation and intended operating model; they are not a claim that every round, payment flow, or optional on-chain signal is already publicly available.