Architecture Decision Document

Marketplace
Architecture

The definitive reference for how the Delivery Hub marketplace works, why it is built on the existing DH platform, and what needs to be decided before implementation.

Recommended Path:
Build Architecture B first (add fields + controller, 1-2 weeks), then evolve to Architecture C (multi-client hub routing, proven pattern already running with MF). Architecture A (portal-only database) is a rewrite that buys nothing.
1

Agenda and Priorities

What this document covers, in order of importance

1

Architecture Decision

Where does bounty logic live? Portal database, Salesforce fields, or hub model?

2

Data Model

What fields get added to which objects? How many are new vs already exist?

3

The Controller

What DeliveryBountyController looks like -- every method, every trigger, every side effect

4

How the Pieces Connect

DH orgs -> Nimba sandbox -> Dev Hub -> cloudnimbusllc.com -- the sync chain that already runs

5

Edge Cases

NTE overruns, multi-dev conflicts, auto-promote, team claims, AI agents, recruiter placements

6

Glen's Feedback Summary

Every piece of feedback with its architectural implication and decision status

7

Open Questions

Remaining decisions that need answers before implementation begins

8

Recommended Next Steps

Prioritized action items with time estimates

2

How Delivery Hub IS the Marketplace

Glen said "I don't think it is" -- this section makes the case

The Core Insight

A "marketplace" is just a pattern: someone posts work, someone claims it, someone delivers it, someone pays. Delivery Hub already does all of this. The "marketplace" is not a new system -- it is making the existing system visible to a wider audience. cloudnimbusllc.com is the storefront window; Salesforce (via Delivery Hub) is the store.

Three Architecture Options

Architecture B: DH + Portal UIStart Here

Developer

Browser

cloudnimbusllc.com

Thin UI Layer (same as today)

/api/delivery-hub proxy

Salesforce (DH Package)

Single source of truth

Existing proxy at /api/delivery-hub/[...path] already handles all API calls. Add ~20 fields to 3 existing objects + 1 new Apex controller. Portal just adds new pages.

Pros

  • - Single source of truth (Salesforce)
  • - Existing sync engine works
  • - Existing hour logging works
  • - Existing invoicing works
  • - Minimal new code (~20 fields, 1 class)
  • - 1-2 weeks to build

Cons

  • - Every schema change needs a package version + CI
  • - Salesforce API limits at scale
  • - Single-org until sync engine routes multi-client

Recommendation

Architecture C is what Glen described and what already works. MF is already doing this: MF Prod syncs to Nimba syncs to Dev Hub syncs to the portal. The marketplace is just opening up the pattern. Build B first (add fields + controller to DH package), then evolve to C (multi-client hub routing as more clients onboard). Architecture A is a rewrite that buys nothing.

Full Comparison

ConcernA: Portal DBB: DH FieldsC: Hub Model
Data model workBuild from scratch~20 fields on 3 objects~20 fields + hub routing config
Source of truthTwo (portal + SF)One (Salesforce)One per org, aggregated at hub
Sync complexityHigh (bidirectional portal <> SF)None (proxy passthrough)Existing (already works, proven)
Portal changesFull backend app (DB, ORM, API)New pages + API route callsNew pages + API route calls
Salesforce changesNone20 fields + 1 controller20 fields + 1 controller + hub config
Works without SFYesNoNo (portal could have lite mode later)
Multi-client supportManual per-client DB configVia existing sync engineVia sync engine (proven with MF)
Time to build4-6 weeks1-2 weeks2-3 weeks
Scalability ceilingHigh (own DB, no SF limits)SF API limits (fine for years)SF API limits per org (distributed)
Existing infrastructure reuseNone -- everything rebuilt100% -- all existing features work100% -- all existing features work
3

The Exact Data Model Changes

~20 new fields on 3 existing objects. Zero new objects.

Total new fields: ~24 across 3 objects. WorkItem__c gets 11 bounty fields. WorkRequest__c gets 7 claim/bid fields. NetworkEntity__c gets 6 developer profile fields. No new custom objects required -- existing objects already model the full bounty lifecycle.

New Fields Required

WorkItem__c

The Bounty
Field API NameTypePurpose
IsBountyBool__cCheckboxFlags this work item as a public bounty
BountyAmountCurrency__cCurrencyFixed-price bounty amount
BountyStatusPk__cPicklistBounty lifecycle: Draft, Published, Claimed, In Review, Completed, Cancelled
BountyDeadlineDate__cDateSubmission deadline for the bounty
BountyDifficultyPk__cPicklistSkill level required: Beginner, Intermediate, Advanced, Expert
BountySkillsTxt__cLongTextAreaComma-separated required skills for matching
BountyRepoUrlTxt__cURLGitHub repository link for the work
BountyPublicTokenTxt__cText(255)Public access token for unauthenticated viewing
BountyVisibilityPk__cPicklistWho can see this bounty: Internal, Approved Vendors, Public
BountyMaxClaimsNumber__cNumberHow many developers can claim simultaneously
BountyNteCeilingCurrency__cCurrencyMaximum NTE any single claim can propose

WorkRequest__c

The Claim / Bid
Field API NameTypePurpose
NteHoursNumber__cNumberDeveloper's proposed NTE hours
NteAmountCurrency__cCurrencyDeveloper's proposed NTE dollar amount
ProposalTxt__cLongTextAreaDeveloper's technical approach and proposal narrative
SubmissionUrlTxt__cURLPR link when work is submitted for review
SubmissionNoteTxt__cLongTextAreaSubmission notes, test coverage summary, known issues
NteConsumedPct__cFormula(Percent)Logged hours / NTE hours -- for budget guard alerts
ComplianceScoreNumber__cNumberNTE compliance metric that feeds into developer reputation

NetworkEntity__c

Developer / Vendor Profile
Field API NameTypePurpose
DeveloperBioTxt__cLongTextAreaPublic-facing developer or team bio
DeveloperSkillsTxt__cLongTextAreaComma-separated skill tags for matching
CompletedBountiesNumber__cNumberRollup count of completed bounties
AverageRatingNumber__cNumber(2,2)Average rating from client reviews
IsPublicProfileBool__cCheckboxOpt-in to the public developer directory
InsuranceStatusPk__cPicklistLiability insurance status: Verified, Unverified, Not Required

Existing Fields That Already Cover Bounty Needs

These fields are already in the DH package and do not need to be created. The marketplace uses them as-is.

ObjectFieldTypeMarketplace Purpose
WorkItem__cAcceptanceCriteriaTxt__cHTML LongTextArea (32K)Bounty acceptance criteria -- already exists
WorkItem__cEstimatedHoursNumber__cNumberBaseline estimate -- already exists
WorkItem__cBillableRateCurrency__cCurrencyHourly rate -- already exists
WorkItem__cTags__cLongTextAreaSkills tagging -- already exists
WorkRequest__cStatusPk__cPicklistClaim lifecycle (includes "Open for Bids") -- already exists
WorkRequest__cWorkProofUrl__cURLPR submission link -- already exists
WorkRequest__cQuotedHoursNumber__cNumberDeveloper estimate -- already exists
WorkRequest__cHourlyRateCurrency__cCurrencyAgreed rate -- already exists
NetworkEntity__cGithubUsernameTxt__cTextGitHub identity -- already exists
NetworkEntity__cEntityTypePk__cPicklistClient / Vendor / Both -- already exists
WorkLog__c(entire object)Custom ObjectFull hour logging with approval workflow -- already exists
DeliveryDocument__c(entire object)Custom ObjectInvoices, client agreements, contractor agreements -- already exists
DeliveryTransaction__c(entire object)Custom ObjectPayment records and financial tracking -- already exists
4

The Controller: DeliveryBountyController

One Apex class. 12 methods. Every read, write, and side effect documented.

@RestResource(urlMapping='/bounties/*')
global with sharing class DeliveryBountyController {

  // Public API (portal REST proxy)
  @HttpGet
  global static List<BountyView> getPublicBounties(BountyFilter filters)

  // Developer actions
  @AuraEnabled
  public static WorkRequest__c claimBounty(Id workItemId, String proposal, Decimal nteHours, Decimal nteAmount)
  public static WorkRequest__c submitWork(Id workRequestId, String prUrl, String notes)
  public static DashboardView getDeveloperDashboard(String email)
  public static NetworkEntity__c registerDeveloper(...)

  // Org / Admin actions
  public static WorkItem__c promoteToBounty(...)
  public static WorkRequest__c reviewClaim(...)
  public static WorkRequest__c acceptSubmission(...)
  public static WorkRequest__c requestRevision(...)

  // Budget guards + scoring
  public static NteStatusView getNteStatus(Id workRequestId)
  public static void scoreSubmission(...)
}

Public API (called by portal via REST proxy)

getPublicBounties

getPublicBounties(filters: BountyFilter) => List<BountyView>

Returns all published bounties visible to the caller. Filters by difficulty, skills, org, status. Respects BountyVisibilityPk__c settings.

Reads

WorkItem__c (WHERE IsBountyBool__c = true AND BountyStatusPk__c = 'Published')

Writes

None

Triggers

None

getBountyByToken

getBountyByToken(token: String) => BountyDetailView

Fetches a single bounty by its public access token. Used for unauthenticated sharing links. Returns full detail including acceptance criteria, existing claims count, and deadline.

Reads

WorkItem__c, WorkRequest__c (count)

Writes

None

Triggers

None

Developer Actions

claimBounty

claimBounty(workItemId, proposal, nteHours, nteAmount) => WorkRequest__c

Creates a new WorkRequest (claim) against a published bounty. Validates max claims not exceeded, deadline not passed, and NTE is within ceiling.

Reads

WorkItem__c (bounty details + claim count)

Writes

WorkRequest__c (new record)

Triggers

Email notification to org admin

submitWork

submitWork(workRequestId, prUrl, notes) => WorkRequest__c

Developer submits their completed work for review. Updates claim status to 'Submitted'. Captures PR URL and submission notes.

Reads

WorkRequest__c

Writes

WorkRequest__c (status update)

Triggers

Email notification to reviewer, Ghost Recorder activity log

getDeveloperDashboard

getDeveloperDashboard(email: String) => DashboardView

Returns all active claims, submitted work, completed bounties, NTE consumption stats, and reputation score for a developer.

Reads

NetworkEntity__c, WorkRequest__c, WorkLog__c, WorkItem__c

Writes

None

Triggers

None

registerDeveloper

registerDeveloper(name, email, github, bio, skills) => NetworkEntity__c

Creates a new NetworkEntity with EntityType=Vendor and a PortalAccess record. Sets up the developer profile for the marketplace directory.

Reads

NetworkEntity__c (duplicate check)

Writes

NetworkEntity__c, PortalAccess__c

Triggers

Welcome email, profile setup notification

Org / Admin Actions

promoteToBounty

promoteToBounty(workItemId, amount, difficulty, skills, deadline, visibility, maxClaims) => WorkItem__c

Promotes an existing internal work item to a public bounty. Sets all bounty fields, generates a public access token, and changes BountyStatusPk__c to Published.

Reads

WorkItem__c

Writes

WorkItem__c (bounty fields populated)

Triggers

Sync engine propagates to hub, bounty board refreshes

reviewClaim

reviewClaim(workRequestId, decision, notes) => WorkRequest__c

Admin approves or rejects a developer's claim. If approved, generates contractor agreement via Document Engine. If rejected, sends feedback to developer.

Reads

WorkRequest__c, WorkItem__c

Writes

WorkRequest__c (status update)

Triggers

If approved: Document Engine creates contractor agreement. Email to developer.

acceptSubmission

acceptSubmission(workRequestId) => WorkRequest__c

Marks submitted work as accepted. Triggers Document Engine to generate invoice with all logged hours as line items. Moves bounty to Completed if no other active claims.

Reads

WorkRequest__c, WorkLog__c (all logged hours), WorkItem__c

Writes

WorkRequest__c (Accepted), WorkItem__c (BountyStatusPk__c update)

Triggers

Document Engine: contractor agreement finalization + invoice generation. Email with PDF CC to glen@cloudnimbusllc.com.

requestRevision

requestRevision(workRequestId, feedback) => WorkRequest__c

Sends work back to developer with specific revision feedback. Status changes to 'Revision Requested'. Developer can re-submit.

Reads

WorkRequest__c

Writes

WorkRequest__c (status + feedback)

Triggers

Email to developer with revision details

Budget Guards & Scoring

getNteStatus

getNteStatus(workRequestId) => NteStatusView

Returns current NTE consumption: total hours logged, percentage consumed, threshold alerts (70%, 90%, 100%). Used by both developer dashboard and admin review.

Reads

WorkRequest__c, WorkLog__c (SUM of hours)

Writes

None

Triggers

If >= 70%: warning flag. If >= 90%: alert. If >= 100%: soft stop notification.

scoreSubmission

scoreSubmission(workRequestId, rating, qualityNotes) => void

Admin rates a completed submission. Updates developer's average rating on NetworkEntity__c. Factors in NTE compliance (did they stay within budget?).

Reads

WorkRequest__c, NetworkEntity__c

Writes

NetworkEntity__c (AverageRatingNumber__c, CompletedBountiesNumber__c)

Triggers

Reputation update visible on developer profile

4

How the Pieces Connect

The sync chain that is already running in production

Live Sync Chain (Running Today)

MF Production

Client's DH installation

37 WorkItems, 59 WorkLogs

push sync

Nimba Sandbox

Hub / aggregator

Receives from all clients

push sync

Dev Hub

Central hub org

Aggregated view

/api/delivery-hub proxy

cloudnimbusllc.com

Portal + Marketplace UI (thin layer)

/bounties, /portal, /platform

What Exists Today

  • - V3 sync engine with echo suppression and dedup
  • - Push flow via Sync_Item__c queue + SyncItemProcessor
  • - Pull flow via DeliveryHubPoller + @HttpGet /changes
  • - GlobalSourceIdTxt__c for cross-org identity
  • - Multi-tenant isolation on vendor side
  • - /api/delivery-hub/[...path] proxy in Next.js

What Gets Added for Marketplace

  • - Bounty fields on WorkItem__c (sync automatically)
  • - DeliveryBountyController REST endpoints
  • - Portal pages: /bounties (board), /bounties/[id] (detail), /developer/dashboard
  • - Developer registration flow
  • - NTE consumption API for budget guards
  • - Scoring hooks for reputation system

The portal stays thin. cloudnimbusllc.com does not need a database, ORM, or its own data model. Every API call passes through the existing proxy to Salesforce. The portal renders data and handles auth -- that is it. This is how the portal works today for the client dashboard, activity feed, and hour logging. Bounties follow the same pattern.

5

Edge Cases and How They Are Handled

Every scenario that could go wrong, with the planned resolution

1

Developer Exceeds NTE

Warning at 70%, alert at 90%, soft stop at 100%. Logged in compliance score. Client decides whether to pay overrun. Developer's NTE compliance score is affected -- it's a soft guideline, not a hard block, but it factors into reputation.

2

Multiple Devs, One Delivers

Delivered dev gets paid via Document Engine (contractor agreement + invoice). Non-delivered dev's claim moves to Cancelled. Client only pays for accepted work. Both claims are tracked for transparency.

3

Client Rejects All Submissions

Bounty goes back to Published. Devs who submitted get documented hours (billable per the lifecycle agreement -- all phases are billable). Client can re-scope and re-post. Rejection feedback is recorded.

4

Internal Employee vs External Dev

Both claims tracked equally via WorkRequest__c. Manager sees both proposals side by side. Can accept one or both. Internal employee's familiarity with the repo is a natural advantage -- and that is fine.

5

Auto-Promote Internal to Marketplace

Configurable trigger: deadline proximity, NTE consumption rate, manual override. Different businesses want different things -- some want aggressive auto-promote, some want manual control only. Creates a Published bounty from the existing internal WorkItem.

6

Developer Claims as a Team

NetworkEntity with EntityType=Vendor, multiple PortalAccess records. Team submits one WorkRequest with aggregated NTE. Individual members log hours via WorkLog. Platform aggregates for invoicing.

7

Scratch Org for Developer

CCI spins up scratch org, installs DH package, syncs WorkItems from hub sandbox. Dev works in scratch org, pushes to branch, opens PR. CI validates. This is the Salesforce-specific enhancement -- not required for the core lifecycle.

8

Recruiter Places a Developer

Recruiter is a NetworkEntity (type=Vendor). Their developer is a PortalAccess member. Recruiter takes a configurable cut (markup on their entity). Platform rake applies on top. Both are tracked in the rate chain.

9

AI Agent Claims a Bounty

NetworkEntity with a special type flag (AI Agent). Automated PR submission via GitHub API. CI validates test coverage and PMD. Human review still required for acceptance -- AI can attempt, but a human decides whether to pay.

10

Manual Post-Deployment Steps

AcceptanceCriteria includes checklist items marked as 'manual.' Reviewer checks these off separately from CI results. Both must pass for acceptance. Documented in Ghost Recorder audit trail.

6

Glen's Feedback Summary

Every piece of feedback, its architectural implication, and decision status

Open

"Open to any revenue model"

Implication: Platform rake is configurable per-org, per-project, or per-hour. No need to hardcode a percentage. Start with whatever is easiest and adjust.

Decided

"Glen bills At Large at $90/hr. At Large bills MF at ~$125-135/hr."

Implication: The rate chain already exists in real life. The platform needs to model it: client rate, vendor rate, platform rake. Each hop takes a cut.

Needs Discussion

"I don't think the marketplace IS Delivery Hub -- I thought this was portal-only"

Implication: This is the key point this document addresses. See Section 2. The marketplace IS Delivery Hub because DH already does everything a marketplace needs: post work, claim it, deliver it, review it, pay. The portal is a window, not a separate system.

Needs Discussion

"Wants to understand the architecture before committing to implementation"

Implication: This document IS the understanding artifact. No code will be written until Glen reviews this page and makes the A vs B vs C decision. Reading this page should be sufficient to decide.

7

Open Questions Needing Answers

Remaining decisions before implementation. Glen's prior answers and follow-ups.

1

Rate chain rake -- what percentage, and when?

Glen's answer: "Open to any model."

Follow-up: Should we start with a simple 10% platform fee and adjust? Or no fee while it is just Glen + Untangleit? The system supports any percentage -- this is a business decision, not a technical one.

2

First bounty -- which MF WorkItem?

Glen's answer: "MF work, whatever is in WIP."

Follow-up: Want me to pull the current MF WorkItem list from Nimba and propose one that is well-scoped for Untangleit? Should be something with clear acceptance criteria and a reasonable NTE.

3

Untangleit onboarding -- how does Edson currently receive work?

No answer yet. Need to understand: Email? Jira? Just calls? What is the minimum viable improvement the marketplace provides over his current workflow?

4

Hub sandbox -- is Nimba permanent or do we need a dedicated marketplace hub?

No answer yet. Nimba Sandbox is the current hub for MF. As more clients onboard, does it stay as the central aggregator, or do we need a dedicated "marketplace hub" org? (Architecture C implication.)

5

Recruiter model -- how do recruiters find you today?

No answer yet. Word of mouth? LinkedIn? Do we need a recruiter-facing page on cloudnimbusllc.com? Or is the developer directory sufficient for recruiters to browse?

!

THE KEY QUESTION: Architecture A, B, or C?

This is the decision that unblocks everything else. The recommendation is B first (1-2 weeks, minimal risk), then evolve to C (multi-client routing, proven pattern). But Glen needs to review this page and decide.

A: Portal DB

4-6 weeks, full rewrite

B: DH Fields

1-2 weeks, start here

C: Hub Model

2-3 weeks, evolve to

8

Recommended Next Steps

Prioritized action items -- what to do and when

1

THIS WEEK: Launch March Invoice

UrgentEst: Same day

Document Engine is already built. Enter hours for At Large (March 2026), select period, generate PDF, email with CC to glen@cloudnimbusllc.com. Independent of marketplace architecture decision.

2

Send David the /platform Walkthrough Link

This WeekEst: Same day

Share this architecture page + the platform overview page with David. Get his reaction to the A vs B vs C decision. His input matters because he has built similar systems.

3

Review This Architecture Page

This WeekEst: 1 hour

Glen reads this page end-to-end. Makes the A vs B vs C decision. Resolves the open questions. This unblocks everything below.

4

If Architecture B/C: Add Bounty Fields to DH Package

NextEst: Half day

~24 new fields on WorkItem__c, WorkRequest__c, NetworkEntity__c. DeliveryBountyController with 12 methods. Half a day of Apex work. Package version bump + CI.

5

Wire Bounty Board to Real Salesforce Data

NextEst: 1-2 days

Replace the mock bounty data on /bounties with real API calls through the /api/delivery-hub proxy. Connect to DeliveryBountyController.getPublicBounties().

6

Assign First MF WorkItem as Bounty to Untangleit

Week 2Est: 1 day

Pick a well-scoped MF WorkItem. Use promoteToBounty() to publish it. Edson (Untangleit) claims it via the portal. Real test of the full flow: claim, develop, log hours, submit, review, accept, invoice.

7

Build Portal Hour Logging for Developers

Week 2Est: 2-3 days

So Untangleit can log time through cloudnimbusllc.com against all lifecycle phases. Uses existing WorkLog__c + proxy pattern. Developer-facing UI for creating, editing, and submitting hours.

8

Generate Contractor Agreement + Invoice via Document Engine

Week 3Est: 1 day

Full dogfood loop: Untangleit completes bounty -> Document Engine generates contractor agreement + invoice -> PDF emailed to all parties -> payment via Melio. This closes the financial loop.

Timeline Summary

Steps 1-3 can happen this week (independent of architecture decision). Steps 4-5 take 1-2 weeks and require the architecture decision. Steps 6-8 are the real-world test with Untangleit and complete the first full marketplace loop. Total: 3-4 weeks from decision to first completed bounty.