diff --git a/apps/logicsrc-web/contract/logicsrc-web.contract.test.ts b/apps/logicsrc-web/contract/logicsrc-web.contract.test.ts index 0a55af0..849765f 100644 --- a/apps/logicsrc-web/contract/logicsrc-web.contract.test.ts +++ b/apps/logicsrc-web/contract/logicsrc-web.contract.test.ts @@ -173,18 +173,20 @@ describe("POST /api/hire-us/coinpay-checkout", () => { expect(createCall?.[1]?.headers).toMatchObject({ authorization: "Bearer cp_test_key" }); expect(createBody).toMatchObject({ business_id: "business-123", - amount_usd: 250, + amount_usd: 4000, payment_method: "both", currency: "usdc_pol", blockchain: "USDC_POL", - description: "LogicSRC Hire Us - $250/week", + description: "LogicSRC Hire Us - 10h @ $400/hour", success_url: "https://logicsrc.test/hire-us?payment=success", cancel_url: "https://logicsrc.test/hire-us?payment=cancelled", redirect_url: "https://logicsrc.test/hire-us?payment=coinpay", webhook_url: "https://logicsrc.test/api/webhooks/coinpay", metadata: { product: "logicsrc-hire-us", - interval: "week", + billing: "metered_hours", + hours: 10, + rate_usd_per_hour: 400, source: "logicsrc.com/hire-us", buyer_email: "buyer@example.com" } @@ -241,7 +243,71 @@ describe("POST /api/hire-us/coinpay-checkout", () => { const body = await response.json(); expect(response.status).toBe(201); - expect(body.payment.amount_usd).toBe(250); + expect(body.payment.amount_usd).toBe(4000); + }); + + it("prices the checkout from the approved hours", async () => { + process.env.COINPAY_API_KEY = "cp_test_key"; + process.env.COINPAY_API_URL = "https://coinpayportal.example"; + process.env.COINPAY_BUSINESS_ID = "business-123"; + process.env.COINPAY_ELIGIBILITY_MERCHANT_ID = "merchant-123"; + process.env.COINPAY_HIRE_US_BLOCKCHAIN = "USDC_POL"; + + const fetchMock = vi.spyOn(globalThis, "fetch").mockImplementation(async (input) => { + const url = typeof input === "string" ? input : input.toString(); + if (url.includes("/api/payments/merchant-eligibility")) { + return jsonResponse({ success: true, accepts_card: true, accepts_crypto: true, chains: ["USDC_POL"] }); + } + return jsonResponse({ success: true, payment: { id: "pay_123", amount_usd: 10100 } }, 201); + }); + + const response = await coinpayCheckout( + new NextRequest("http://localhost/api/hire-us/coinpay-checkout", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ hours: 25.25 }) + }) + ); + const body = await response.json(); + + const createCall = fetchMock.mock.calls.find(([input]) => + (typeof input === "string" ? input : input.toString()).includes("/api/payments/create") + ); + const createBody = JSON.parse((createCall?.[1]?.body as string) ?? "{}"); + + expect(response.status).toBe(201); + expect(createBody.amount_usd).toBe(10100); + expect(createBody.description).toBe("LogicSRC Hire Us - 25.25h @ $400/hour"); + expect(createBody.metadata).toMatchObject({ billing: "metered_hours", hours: 25.25, rate_usd_per_hour: 400 }); + expect(body.payment).toMatchObject({ amount_usd: 10100, hours: 25.25, rate_usd_per_hour: 400 }); + }); + + it("rejects hours below the minimum engagement or off the quarter-hour", async () => { + process.env.COINPAY_API_KEY = "cp_test_key"; + process.env.COINPAY_API_URL = "https://coinpayportal.example"; + process.env.COINPAY_BUSINESS_ID = "business-123"; + process.env.COINPAY_ELIGIBILITY_MERCHANT_ID = "merchant-123"; + + const fetchMock = vi.spyOn(globalThis, "fetch"); + + for (const hours of [9.75, 12.3, "many", -40]) { + const response = await coinpayCheckout( + new NextRequest("http://localhost/api/hire-us/coinpay-checkout", { + method: "POST", + headers: { "content-type": "application/json" }, + body: JSON.stringify({ hours }) + }) + ); + const body = await response.json(); + + expect(response.status).toBe(422); + expect(body).toEqual({ + success: false, + error: "Approved hours must be a quarter-hour increment of at least 10" + }); + } + + expect(fetchMock).not.toHaveBeenCalled(); }); it("does not create checkout when no payment rail is available", async () => { @@ -290,7 +356,13 @@ describe("POST /api/hire-us/project-request", () => { expect(response.status).toBe(202); expect(body).toMatchObject({ success: true, - request: { status: "pending_acceptance", amount_usd: 250, interval: "week", invoice: "created_after_acceptance" } + request: { + status: "pending_acceptance", + rate_usd_per_hour: 400, + billing: "metered_hours", + minimum_hours: 10, + invoice: "created_after_acceptance" + } }); expect(body.request.id).toMatch(/^hire_/); }); diff --git a/apps/logicsrc-web/e2e/logicsrc.spec.ts b/apps/logicsrc-web/e2e/logicsrc.spec.ts index b41e7db..04eb553 100644 --- a/apps/logicsrc-web/e2e/logicsrc.spec.ts +++ b/apps/logicsrc-web/e2e/logicsrc.spec.ts @@ -39,14 +39,17 @@ test.describe("LogicSRC PWA", () => { await page.goto("/hire-us"); await expect(page.getByRole("heading", { name: "Hire Us", exact: true })).toBeVisible(); - await expect(page.locator(".price-row strong", { hasText: "$250" })).toBeVisible(); - await expect(page.getByText("per week")).toBeVisible(); + await expect(page.locator(".price-row strong", { hasText: "$400" })).toBeVisible(); + await expect(page.getByText("per hour")).toBeVisible(); + await expect(page.getByText("Ten-hour minimum engagement")).toBeVisible(); await expect(page.getByText("open infrastructure and open specs for AI agent systems")).toBeVisible(); await expect(page.getByRole("button", { name: "Request review" })).toBeVisible(); await expect(page.getByPlaceholder("you@example.com")).toBeVisible(); await expect(page.getByPlaceholder("Describe the agent workflow")).toBeVisible(); await expect(page.getByText("without exposing merchant credentials to the browser")).toBeVisible(); await expect(page.getByText("COINPAY_PRODUCT=logicsrc-hire-us")).toBeVisible(); + await expect(page.getByText("COINPAY_RATE_USD_PER_HOUR=400")).toBeVisible(); + await expect(page.getByText("COINPAY_BILLING=metered_hours")).toBeVisible(); await expect(page.getByText("COINPAY_STATUS=pending_acceptance")).toBeVisible(); }); diff --git a/apps/logicsrc-web/src/app/[[...slug]]/page.tsx b/apps/logicsrc-web/src/app/[[...slug]]/page.tsx index 326b4d7..0101e96 100644 --- a/apps/logicsrc-web/src/app/[[...slug]]/page.tsx +++ b/apps/logicsrc-web/src/app/[[...slug]]/page.tsx @@ -8,8 +8,9 @@ import { HomeInteractivity } from "@/components/home-interactivity"; // scrolled to the matching section. We preserve those URLs (they are canonical // in sitemap.xml) by rendering the same page for each known route and 404ing // anything else. -// /about and /docs are now real routes (app/about, app/docs); the rest still -// render the homepage SPA scrolled to their section. +// /about, /docs, /pricing, and /terms are now real routes (app/about, app/docs, +// app/pricing, app/terms); the rest still render the homepage SPA scrolled to +// their section. const ROUTE_META: Record = { openspec: { title: "LogicSRC vs OpenSpec.dev · LogicSRC", @@ -21,9 +22,8 @@ const ROUTE_META: Record = { }, "hire-us": { title: "Hire Us · LogicSRC", - description: "Implementation help for LogicSRC, AgentSwarm, and Credential Sharing at $250/week for accepted work, paid via CoinPay.", + description: "Implementation help for LogicSRC, AgentSwarm, and Credential Sharing at $400/hour for accepted work, paid via CoinPay.", }, - terms: { title: "Terms · LogicSRC", description: "LogicSRC terms of use." }, privacy: { title: "Privacy · LogicSRC", description: "LogicSRC privacy notes." }, "agent-swarm": { title: "AgentSwarm · LogicSRC", diff --git a/apps/logicsrc-web/src/app/about/page.tsx b/apps/logicsrc-web/src/app/about/page.tsx index b610fa3..5c3ea77 100644 --- a/apps/logicsrc-web/src/app/about/page.tsx +++ b/apps/logicsrc-web/src/app/about/page.tsx @@ -84,7 +84,7 @@ export default function AboutPage(): ReactNode {

Work with us

- Profullstack implements LogicSRC-based systems at $250/week for + Profullstack implements LogicSRC-based systems at $400/hour for accepted work, paid via CoinPay. See Hire Us.

diff --git a/apps/logicsrc-web/src/app/api/hire-us/coinpay-checkout/route.ts b/apps/logicsrc-web/src/app/api/hire-us/coinpay-checkout/route.ts index e9dff88..b2607a1 100644 --- a/apps/logicsrc-web/src/app/api/hire-us/coinpay-checkout/route.ts +++ b/apps/logicsrc-web/src/app/api/hire-us/coinpay-checkout/route.ts @@ -4,8 +4,23 @@ import { choosePaymentRail, fetchMerchantEligibility, parseJson } from "@/lib/co export const dynamic = "force-dynamic"; -// POST /api/hire-us/coinpay-checkout — create a $250/week CoinPay checkout for -// the Hire Us plan, choosing card/crypto/both based on merchant eligibility. +// POST /api/hire-us/coinpay-checkout — create a CoinPay checkout for approved +// Hire Us hours at $400/hour, choosing card/crypto/both based on merchant +// eligibility. Billing is metered: the caller supplies the approved hours and the +// amount is derived from them, never a fixed recurring figure. +const RATE_USD_PER_HOUR = 400; +const MINIMUM_HOURS = 10; + +// Hours are quoted in quarter-hour increments; anything finer is a rounding +// artifact rather than a real billing unit. +function parseHours(value: unknown): number | null { + const hours = typeof value === "number" ? value : Number(value); + if (!Number.isFinite(hours) || hours < MINIMUM_HOURS) return null; + const quarters = Math.round(hours * 4); + if (Math.abs(hours * 4 - quarters) > 1e-9) return null; + return quarters / 4; +} + export async function POST(request: NextRequest) { const apiKey = process.env.COINPAY_API_KEY; const eligibilityApiKey = process.env.COINPAY_ELIGIBILITY_API_KEY || process.env.COINPAY_AGENT_API_KEY || apiKey; @@ -22,6 +37,19 @@ export async function POST(request: NextRequest) { try { const body = (await request.json().catch(() => ({}))) as Record; const buyerEmail = typeof body.email === "string" ? body.email.trim().slice(0, 160) : ""; + const hours = body.hours === undefined ? MINIMUM_HOURS : parseHours(body.hours); + + if (hours === null) { + return json( + { + success: false, + error: `Approved hours must be a quarter-hour increment of at least ${MINIMUM_HOURS}` + }, + 422 + ); + } + + const amountUsdDue = Math.round(hours * RATE_USD_PER_HOUR * 100) / 100; const eligibility = await fetchMerchantEligibility(apiUrl, eligibilityApiKey, eligibilityMerchantId); const paymentRail = choosePaymentRail(eligibility, blockchain); @@ -37,18 +65,20 @@ export async function POST(request: NextRequest) { }, body: JSON.stringify({ business_id: businessId, - amount_usd: 250, + amount_usd: amountUsdDue, payment_method: paymentRail.method, currency: paymentRail.currency, ...(paymentRail.blockchain ? { blockchain: paymentRail.blockchain } : {}), - description: "LogicSRC Hire Us - $250/week", + description: `LogicSRC Hire Us - ${hours}h @ $${RATE_USD_PER_HOUR}/hour`, success_url: `${publicUrl}/hire-us?payment=success`, cancel_url: `${publicUrl}/hire-us?payment=cancelled`, redirect_url: `${publicUrl}/hire-us?payment=coinpay`, webhook_url: `${publicUrl}/api/webhooks/coinpay`, metadata: { product: "logicsrc-hire-us", - interval: "week", + billing: "metered_hours", + hours, + rate_usd_per_hour: RATE_USD_PER_HOUR, source: "logicsrc.com/hire-us", ...(buyerEmail ? { buyer_email: buyerEmail } : {}) } @@ -70,13 +100,15 @@ export async function POST(request: NextRequest) { } const payment = (payload.payment as Record) || {}; - const amountUsd = Number(payment.amount_usd ?? payment.amount ?? 250); + const amountUsd = Number(payment.amount_usd ?? payment.amount ?? amountUsdDue); return json( { success: true, payment: { id: payment.id, - amount_usd: Number.isFinite(amountUsd) ? amountUsd : 250, + amount_usd: Number.isFinite(amountUsd) ? amountUsd : amountUsdDue, + hours, + rate_usd_per_hour: RATE_USD_PER_HOUR, payment_method: payment.stripe_checkout_url ? "card" : paymentRail.method, currency: payment.currency ?? payment.blockchain ?? paymentRail.blockchain ?? paymentRail.currency, crypto_amount: payment.amount_crypto ?? payment.crypto_amount ?? null, diff --git a/apps/logicsrc-web/src/app/api/hire-us/project-request/route.ts b/apps/logicsrc-web/src/app/api/hire-us/project-request/route.ts index 9d5c409..947af4d 100644 --- a/apps/logicsrc-web/src/app/api/hire-us/project-request/route.ts +++ b/apps/logicsrc-web/src/app/api/hire-us/project-request/route.ts @@ -3,8 +3,12 @@ import { json } from "@/lib/http"; export const dynamic = "force-dynamic"; -// POST /api/hire-us/project-request — accept a Hire Us project request before a -// recurring CoinPay invoice is created (invoice is created after acceptance). +// POST /api/hire-us/project-request — accept a Hire Us project request before any +// CoinPay invoice is created. Hire Us bills metered hours at $400/hour, so there is +// no amount until we accept the project and hours are approved. +const RATE_USD_PER_HOUR = 400; +const MINIMUM_HOURS = 10; + export async function POST(request: NextRequest) { try { const body = (await request.json().catch(() => ({}))) as Record; @@ -20,7 +24,7 @@ export async function POST(request: NextRequest) { id: requestId, contact, project_length: project.length, - plan: "250/week", + plan: "400/hour", invoice: "pending_acceptance" }); @@ -30,8 +34,9 @@ export async function POST(request: NextRequest) { request: { id: requestId, status: "pending_acceptance", - amount_usd: 250, - interval: "week", + rate_usd_per_hour: RATE_USD_PER_HOUR, + billing: "metered_hours", + minimum_hours: MINIMUM_HOURS, invoice: "created_after_acceptance" } }, diff --git a/apps/logicsrc-web/src/app/llms.txt/route.ts b/apps/logicsrc-web/src/app/llms.txt/route.ts index d923479..f583c90 100644 --- a/apps/logicsrc-web/src/app/llms.txt/route.ts +++ b/apps/logicsrc-web/src/app/llms.txt/route.ts @@ -24,7 +24,7 @@ export function GET(): Response { ## Company & legal - [About](${SITE_URL}/about): What LogicSRC is and who maintains it (Profullstack, Inc.). -- [Hire Us](${SITE_URL}/hire-us): Implementation help at $250/week for accepted LogicSRC work. +- [Hire Us](${SITE_URL}/hire-us): Implementation help at $400/hour for accepted LogicSRC work. - [Terms](${SITE_URL}/terms) - [Privacy](${SITE_URL}/privacy) `; diff --git a/apps/logicsrc-web/src/app/pricing/page.tsx b/apps/logicsrc-web/src/app/pricing/page.tsx index 71d5f4a..70e1888 100644 --- a/apps/logicsrc-web/src/app/pricing/page.tsx +++ b/apps/logicsrc-web/src/app/pricing/page.tsx @@ -5,7 +5,7 @@ import { SiteShell } from "@/components/site-shell"; export const metadata: Metadata = { title: "Pricing · LogicSRC", description: - "LogicSRC the open specification, schemas, SDKs, and CLI are free and open source. Implementation help is $250/week for accepted work, paid via CoinPay.", + "LogicSRC the open specification, schemas, SDKs, and CLI are free and open source. Implementation help is $400/hour for accepted work, paid via CoinPay.", alternates: { canonical: "/pricing" }, }; @@ -16,7 +16,7 @@ const FAQ: Array<{ q: string; a: string }> = [ }, { q: "How does pricing work?", - a: "The standard is free. If you want Profullstack to build a LogicSRC-based system for you, implementation work is billed at $250/week for accepted work, paid via a CoinPay recurring invoice.", + a: "The standard is free. If you want Profullstack to build a LogicSRC-based system for you, implementation work is billed at $400/hour against actual hours worked, invoiced through CoinPay after you approve them. The minimum engagement is 10 hours.", }, { q: "Who is LogicSRC for?", @@ -24,7 +24,7 @@ const FAQ: Array<{ q: string; a: string }> = [ }, { q: "How do I pay or get started?", - a: "Read the docs and adopt the schemas for free, or submit a project through the Hire Us form. Accepted projects are invoiced weekly via CoinPay.", + a: "Read the docs and adopt the schemas for free, or submit a project through the Hire Us form. Accepted projects are invoiced for approved hours via CoinPay.", }, ]; @@ -61,9 +61,11 @@ export default function PricingPage(): ReactNode { CLI, TUI, and reference plugins are open source.
  • - Implementation — $250/week. Profullstack builds - LogicSRC-based systems for accepted projects, billed weekly via - CoinPay. See Hire Us. + Implementation — $400/hour. Profullstack builds + LogicSRC-based systems for accepted projects, billed against actual + hours worked and invoiced via CoinPay once you approve them. + Ten-hour minimum engagement. See Hire Us and{" "} + Terms.
  • diff --git a/apps/logicsrc-web/src/app/skill.md/route.ts b/apps/logicsrc-web/src/app/skill.md/route.ts index af58207..ad8e261 100644 --- a/apps/logicsrc-web/src/app/skill.md/route.ts +++ b/apps/logicsrc-web/src/app/skill.md/route.ts @@ -25,7 +25,7 @@ Base URL: ${SITE_URL} - Read the LogicSRC coordination schemas and conventions. - Compare LogicSRC with OpenSpec.dev. - Request paid implementation help via the Hire Us flow (${SITE_URL}/hire-us), - billed at $250/week for accepted work and paid through CoinPay. + billed at $400/hour for accepted work and paid through CoinPay. ## Notes diff --git a/apps/logicsrc-web/src/app/terms/page.tsx b/apps/logicsrc-web/src/app/terms/page.tsx new file mode 100644 index 0000000..1000721 --- /dev/null +++ b/apps/logicsrc-web/src/app/terms/page.tsx @@ -0,0 +1,128 @@ +import type { ReactNode } from "react"; +import type { Metadata } from "next"; +import { SiteShell } from "@/components/site-shell"; + +export const metadata: Metadata = { + title: "Terms · LogicSRC", + description: + "Terms of engagement for LogicSRC: the specification and tooling are open source and free; Profullstack implementation work is billed at $400/hour against approved hours, with a 10-hour minimum.", + alternates: { canonical: "/terms" }, +}; + +export default function TermsPage(): ReactNode { + return ( + +
    +
    +

    Terms

    +

    + Two separate things live on this site: an open standard anyone may + use, and paid implementation work from Profullstack, Inc. These terms + cover both. +

    +
    + +
    +

    The standard is free

    +

    + The LogicSRC specification, JSON schemas, SDKs, CLI, TUI, and + reference plugins are open source under the project license. There is + no license fee, no per-seat charge, and no obligation to hire us in + order to implement the standard. Nothing on this page restricts your + use of the spec. +

    + +

    Rate

    +

    + Profullstack implementation work is billed at{" "} + $400 per hour. One rate applies to all + implementation work — specs, CLIs, SDKs, MCP resources, APIs, PWAs, + and provider-neutral plugin surfaces. There are no tiers, role-based + rates, or volume discounts. +

    + +

    What is billable

    +

    + Billable hours are hours spent on your project: design, spec work, + implementation, review, debugging, integration, and deployment, plus + meetings and written communication about the work. Time is recorded + in quarter-hour increments. +

    +

    + The following are not billed: the initial scoping conversation, + preparing your invoice, and time spent fixing defects in work we have + already delivered and you have already paid for. +

    + +

    How you are invoiced

    +

    + Billing is metered against actual hours worked, not a subscription. + After we accept a project, we send you a record of hours worked. Once + you approve those hours, we issue a CoinPay invoice for exactly that + amount at $400/hour. You are never charged for hours you have not + seen and approved. +

    +

    + Invoices are payable on receipt via CoinPay, by card or by supported + cryptocurrency. Work may pause on invoices unpaid after 14 days. +

    + +

    Minimum engagement

    +

    + The minimum engagement is 10 hours. Engagements + smaller than this do not cover the cost of scoping and context, so we + will decline them rather than quote them. +

    + +

    Cancellation

    +

    + Either side may end an engagement at any time with one week's + written notice. You pay for hours already worked and approved, + including hours worked during the notice period; nothing further is + owed. We do not bill a cancellation fee and we do not hold unused + committed hours. Work product produced by hours you have paid for is + yours to keep. +

    + +

    Existing engagements

    +

    + Clients engaged under a prior pricing model keep their existing terms + until both sides agree in writing to move to the hourly rate. This + page does not reprice work already underway. +

    + +

    Estimates

    +

    + Any estimate of total hours is an estimate, not a fixed-price quote. + If the work looks likely to exceed an estimate we will tell you + before the additional hours are worked, so you can rescope or stop. +

    + +

    Acceptable use

    +

    + We build auditable, portable systems in the open. We decline work + intended to deceive users, evade legal obligations, or produce + systems that cannot be inspected by the people they affect. +

    + +

    Reference implementations

    +

    + Reference implementations published under the LogicSRC project exist + to prove the specification is usable. They are provided as-is, + without warranty, and are not a hosted product or a support + commitment. Paid engagements are governed by the terms above, not by + the license of any reference implementation. +

    + +

    Questions

    +

    + Submit a project through the Hire Us form, or + see Pricing for a summary. These terms may + change; the version on this page at the time your engagement starts + is the one that applies to it. +

    +
    +
    +
    + ); +} diff --git a/apps/logicsrc-web/src/components/home-interactivity.tsx b/apps/logicsrc-web/src/components/home-interactivity.tsx index 05487f4..f4c6874 100644 --- a/apps/logicsrc-web/src/components/home-interactivity.tsx +++ b/apps/logicsrc-web/src/components/home-interactivity.tsx @@ -45,7 +45,7 @@ export function HomeInteractivity(): null { } result.replaceChildren( - buildParagraph("Request received. If it is a fit, we will send a $250/week recurring CoinPay invoice.") + buildParagraph("Request received. If it is a fit, we will scope the work and invoice approved hours at $400/hour via CoinPay.") ); } catch (error) { result.replaceChildren( diff --git a/apps/logicsrc-web/src/lib/page-markup.ts b/apps/logicsrc-web/src/lib/page-markup.ts index b2536e2..b29f188 100644 --- a/apps/logicsrc-web/src/lib/page-markup.ts +++ b/apps/logicsrc-web/src/lib/page-markup.ts @@ -69,9 +69,9 @@ const pages = [ { id: "blog", title: "Blog", detail: "Project notes for LogicSRC, AgentSwarm, AgentByte, OpenSpec workflows, and reference implementations." }, { id: "openspec", title: "OpenSpec", detail: "Comparison and compatibility notes for OpenSpec.dev-style repo-local specs, proposals, tasks, and deltas." }, { id: "credential-sharing", title: "Credential Sharing", detail: "Open replacement architecture for portable secret sync across .env, Doppler, Railway variables, GitHub Secrets, and future providers." }, - { id: "hire-us", title: "Hire Us", detail: "$250/week LogicSRC work on open infrastructure, specs, AI agent workflows, and reference implementations paid through CoinPay after project acceptance." }, + { id: "hire-us", title: "Hire Us", detail: "$400/hour LogicSRC work on open infrastructure, specs, AI agent workflows, and reference implementations paid through CoinPay after project acceptance." }, { id: "about", title: "About", detail: "LogicSRC is the Profullstack open specification project for human and AI agent coordination." }, - { id: "terms", title: "Terms", detail: "Draft terms will cover acceptable use, reference implementation boundaries, and hosted-product responsibilities." }, + { id: "terms", title: "Terms", detail: "Terms of engagement: the $400/hour rate, what is billable, how approved hours are invoiced, the 10-hour minimum, cancellation, acceptable use, and reference implementation boundaries." }, { id: "privacy", title: "Privacy", detail: "Draft privacy notes will cover telemetry, audit events, identity data, and hosted-product data boundaries." } ]; @@ -358,7 +358,7 @@ logicsrc credentials rollback --run <runId>

    Hire Us

    -

    $250/week for accepted LogicSRC work using open infrastructure and open specs for AI agent systems.

    +

    $400/hour for accepted LogicSRC work using open infrastructure and open specs for AI agent systems.

    @@ -366,9 +366,10 @@ logicsrc credentials rollback --run <runId>

    Open-spec AI agent implementation help

    Hire us to turn agent ideas into portable LogicSRC specs, CLIs, SDKs, MCP resources, PWAs, APIs, and provider-neutral plugin workflows. We prioritize auditable contracts, repo-local artifacts, and integrations that can move between model providers and infrastructure.

    - $250 - per week + $400 + per hour
    +

    Billed against actual hours worked, invoiced after you approve them. Ten-hour minimum engagement; cancel any time with one week’s notice and pay only for hours already approved. See Terms.

    -

    CoinPay recurring invoice

    -

    After we accept the project, we create a recurring CoinPay invoice for the weekly plan without exposing merchant credentials to the browser.

    +

    CoinPay hourly invoice

    +

    After we accept the project, we invoice approved hours at $400/hour through CoinPay without exposing merchant credentials to the browser.

    COINPAY_ORG=profullstack
     COINPAY_PRODUCT=logicsrc-hire-us
    -COINPAY_AMOUNT_USD=250
    -COINPAY_INTERVAL=week
    +COINPAY_RATE_USD_PER_HOUR=400
    +COINPAY_BILLING=metered_hours
    +COINPAY_MINIMUM_HOURS=10
     COINPAY_STATUS=pending_acceptance
    diff --git a/apps/logicsrc-web/src/styles.css b/apps/logicsrc-web/src/styles.css index 4352367..6067b7a 100644 --- a/apps/logicsrc-web/src/styles.css +++ b/apps/logicsrc-web/src/styles.css @@ -484,6 +484,17 @@ pre { font-weight: 800; } +.price-terms { + color: #b5beb2; + font-size: 0.85rem; + line-height: 1.5; + margin: -0.5rem 0 0; +} + +.price-terms a { + color: inherit; +} + .cta-row { display: flex; flex-wrap: wrap; diff --git a/packages/logicsrc-mcp/src/standards.test.ts b/packages/logicsrc-mcp/src/standards.test.ts index a80eeaa..dc50ef5 100644 --- a/packages/logicsrc-mcp/src/standards.test.ts +++ b/packages/logicsrc-mcp/src/standards.test.ts @@ -202,7 +202,8 @@ describe("MCP: OpenPRD", () => { it("reports the next free id and the allowed lifecycle moves", async () => { const client = await connect(); - expect(toolText(await client.callTool({ name: "prd_next_id", arguments: {} }))).toBe("0002"); + // Asserted against the live prd/ directory, so this advances with every PRD added. + expect(toolText(await client.callTool({ name: "prd_next_id", arguments: {} }))).toBe("0003"); const moves = await client.callTool({ name: "prd_next_statuses", arguments: { ref: "0001" } }); const payload = JSON.parse(toolText(moves)) as { status: string; allowedNext: string[] }; diff --git a/prd/0002-hourly-hire-us-rate.md b/prd/0002-hourly-hire-us-rate.md new file mode 100644 index 0000000..1bc3b61 --- /dev/null +++ b/prd/0002-hourly-hire-us-rate.md @@ -0,0 +1,146 @@ +--- +openprd: "0.2" +id: "0002" +title: Move Hire Us pricing from a weekly retainer to an hourly rate +status: Accepted +authors: + - anthony@profullstack.com +created: 2026-07-28 +updated: 2026-07-28 +repo: https://github.com/profullstack/logicsrc +implementation: apps/logicsrc-web +tags: [pricing, site, billing] +--- + +## Problem + +The Hire Us surface on logicsrc.com prices standards work at $250/week, paid through a +recurring CoinPay invoice after project acceptance. That number reads as a token retainer +rather than a rate for senior open-spec implementation work — schemas, CLIs, SDKs, MCP +resources, and provider-neutral plugin surfaces. It anchors every inbound conversation at a +price that cannot cover the work, and it selects for clients who are shopping on price rather +than on the standard. + +The replacement is $400/hour. This is not a bump to an existing hourly number; it is a change +of pricing *model*, which touches page copy, the payment configuration, and the invoicing +mechanics that currently assume a fixed weekly amount. + +## Goals + +- Every public price on logicsrc.com states one rate, in one unit, with no stale $250/week + copy left behind on any surface. +- Inbound Hire Us inquiries arrive already anchored to a senior rate, so the pricing + conversation is about scope rather than about the number. +- Billing can actually execute the new model: an accepted project produces a correct invoice + without manual repair. +- The change is reversible and auditable — the reasoning survives in the repo, not in a Slack + thread. + +## Non-Goals + +- Repricing the standard itself. LogicSRC schemas, specs, and reference implementations stay + open and free; this covers implementation services only. +- Building time tracking. Hourly billing needs hours captured, but that is an operational + process for now, not a product to build. +- Replacing CoinPay or adding a second payment provider. +- Publishing a rate card with tiers, discounts, or role-based pricing. One rate, one line. +- Migrating anyone currently engaged at $250/week. Handled case by case, not by this PRD. + +## Users + +- **Prospective clients** evaluating whether to hire Profullstack for LogicSRC work — mostly + founders and engineering leads arriving from the spec pages, who read the price before they + read anything else. +- **Profullstack**, as the party quoting, invoicing, and collecting. +- **Existing clients** on the weekly plan, who must not be silently repriced. + +## Decisions + +Two questions blocked this PRD at drafting time. Both were resolved before implementation: + +- **The rate is $400/hour, confirmed deliberately.** Against $250/week this is roughly a 64x + change at a 40-hour week. The magnitude is the point: the weekly figure was a token retainer, + not a rate, and the new number is intended to filter out engagements too small to scope. +- **Billing is metered against actual hours, not a committed weekly block.** This matches how + the code already works — `/api/payments/create` creates a single one-shot payment, never a + recurring subscription, so the previous "recurring invoice" copy documented a mechanic that + did not exist. Metered billing also avoids the committed-block failure mode, which is a + weekly rate wearing an hourly label. A **10-hour minimum engagement** replaces the week as + the unit of commitment. + +## Requirements + +- R1 [P0] The Hire Us section heading states the new rate and unit, replacing "$250/week for + accepted LogicSRC work". +- R2 [P0] The price display block shows $400 with the unit "per hour". +- R3 [P0] The `/hire-us` entry in the Top-Level Pages list is updated; it repeated the weekly + figure independently of the section above it. +- R4 [P0] The `/hire-us` page body and its meta description are updated. +- R5 [P0] The CoinPay configuration block reflects the new model. `COINPAY_AMOUNT_USD=250` and + `COINPAY_INTERVAL=week` are both wrong under hourly pricing and are replaced by a rate, a + billing mode, and a minimum rather than renumbered. +- R6 [P0] A repo-wide search for `250`, `per week`, and `/week` returns no remaining pricing + references before the change is considered done. +- R7 [P1] The site states what an hour is billed against — metered actual hours, invoiced after + the client approves them — so the invoice mechanic is legible before a client asks. +- R8 [P1] Minimum engagement and cancellation terms are stated, since removing the weekly + cadence also removes the implicit unit of commitment. +- R9 [P2] Terms of engagement are documented at `/terms` rather than only in marketing copy. + +Surfaces the original draft did not list, but which carried the weekly price and were therefore +in scope for R6: `/pricing` (metadata, two FAQ answers, and the rate bullet), `/about`, +`llms.txt`, `skill.md`, the Hire Us form's success message, and both the contract and e2e tests. + +## UX Notes + +The price appears in several places across the front page, `/pricing`, `/about`, and the +machine-readable surfaces; they are separate strings and will drift if edited one at a time. +Treat the set as one change. + +The CoinPay block is rendered as example configuration, so it reads as documentation of how +billing actually works. A weekly interval next to an hourly rate is worse than a stale price — +it looks like the system does not do what the copy says. + +The stated rate sits next to what it buys. The existing four capability cards (workflow specs, +reference implementations, integration hardening, open infrastructure) already do that work and +are unchanged. + +## Implementation Notes + +- `src/lib/page-markup.ts` — section heading, price row, a new `.price-terms` line carrying the + minimum and cancellation summary, the CoinPay config block, and the Top-Level Pages entries + for both Hire Us and Terms. +- `src/app/api/hire-us/coinpay-checkout/route.ts` — the amount is now derived as + `hours × $400` rather than hardcoded. Hours are validated as quarter-hour increments at or + above the 10-hour minimum, defaulting to the minimum when omitted; invalid hours return 422 + before any call to CoinPay. Payment metadata carries `billing`, `hours`, and + `rate_usd_per_hour` in place of `interval`. +- `src/app/api/hire-us/project-request/route.ts` — returns a rate, billing mode, and minimum + instead of a fixed `amount_usd`/`interval` pair, since no amount exists until hours are + approved. +- `src/app/terms/page.tsx` — new real route, replacing the `/terms` stub that rendered the + homepage SPA. The stub's entry was removed from the catch-all's `ROUTE_META`. + +## Success Metrics + +- Zero occurrences of the weekly price across the site and repo after the change, verified by + grep rather than by reading. +- First accepted project under the new model invoices correctly on the first attempt, with no + manual adjustment to the CoinPay invoice. +- Inbound inquiries that reach a scoping conversation do not open by disputing the rate. +- No existing engagement is repriced without explicit agreement. + +## Risks & Open Questions + +- **Hours are not currently tracked.** Metered billing won, and there is no capture mechanism. + `/terms` now defines what is billable (project work, meetings, and written communication in + quarter-hour increments; not scoping calls, invoicing, or warranty fixes), so the definition + is settled even though the tooling is not. Capturing hours is an operational process until it + is worth building. +- **Client mix will change.** An hourly rate at this level filters out the small experimental + engagements the weekly price attracted. This is the intent, recorded here as a decision + rather than left to be discovered. +- **Existing weekly clients** keep their terms until both sides agree in writing to move, per + `/terms`. Whether any active weekly engagements exist is still unknown. +- **Author attribution** is assumed from the CoinPay org configuration and should be corrected + if wrong. diff --git a/prd/README.md b/prd/README.md index cff3687..4b20305 100644 --- a/prd/README.md +++ b/prd/README.md @@ -12,3 +12,4 @@ Status lives in each file's front-matter and is the source of truth: | ID | Title | Status | Tags | | --- | --- | --- | --- | | [0001](./0001-add-logicsrc-openontology-spec.md) | Add the LogicSRC OpenOntology specification | Draft | openontology, ontology, knowledge-graph, agents, mcp, schemas | +| [0002](./0002-hourly-hire-us-rate.md) | Move Hire Us pricing from a weekly retainer to an hourly rate | Accepted | pricing, site, billing |