{
  "id": "cradle",
  "name": "Cradle",
  "version": "0.0.0-bootstrap",
  "maturity": "ui-ported-thin-data-spine",
  "category": "records",
  "spine": {
    "id": "records",
    "record": "table with typed fields and views"
  },
  "replaces": [
    { "name": "Airtable", "edition": null }
  ],
  "description": "A self-hosted launch-portfolio operating system for one business, focused on campaign readiness, risks, milestones, decisions, saved lenses, and visible automation.",
  "scope": "one business, one workspace, equal operators",
  "capabilities": [
    {
      "id": "owner-identity-landing",
      "clauses": ["HOME-001"]
    },
    {
      "id": "operator-workspace",
      "clauses": [
        "OPS-001", "OPS-002", "OPS-003", "OPS-004", "OPS-005", "OPS-006",
        "VIEW-001", "VIEW-002", "VIEW-003", "VIEW-004", "VIEW-005", "VIEW-006", "VIEW-007",
        "SET-001", "SET-002", "SET-003"
      ]
    },
    {
      "id": "campaign-portfolio",
      "clauses": [
        "CAMP-001", "CAMP-002", "CAMP-003", "CAMP-004", "CAMP-005", "CAMP-006", "CAMP-007",
        "CAMP-008", "CAMP-009", "CAMP-010", "CAMP-011", "CAMP-012", "CAMP-013", "CAMP-014"
      ]
    },
    {
      "id": "deliverables-and-milestones",
      "clauses": [
        "DELIV-001", "DELIV-002", "DELIV-003",
        "MILE-001", "MILE-002", "MILE-003", "MILE-004", "MILE-005", "MILE-006"
      ]
    },
    {
      "id": "risk-and-decision-operations",
      "clauses": [
        "RISK-001", "RISK-002", "RISK-003", "RISK-004", "RISK-005",
        "DEC-001", "DEC-002", "DEC-003", "DEC-004", "DEC-005", "DEC-006", "DEC-007", "DEC-008"
      ]
    },
    {
      "id": "saved-lenses",
      "clauses": ["LENS-001", "LENS-002", "LENS-003", "LENS-004", "LENS-005", "LENS-006", "LENS-007"]
    },
    {
      "id": "activity-and-comments",
      "clauses": ["ACT-001", "ACT-002", "ACT-003", "ACT-004"]
    },
    {
      "id": "visible-automation",
      "clauses": [
        "AUTO-001", "AUTO-002", "AUTO-003", "AUTO-004", "AUTO-005", "AUTO-006",
        "AUTO-007", "AUTO-008", "AUTO-009", "AUTO-010", "AUTO-011"
      ]
    },
    {
      "id": "notifications",
      "clauses": [
        "NOTIFY-001", "NOTIFY-002", "NOTIFY-003", "NOTIFY-004",
        "NOTIFY-005", "NOTIFY-006", "NOTIFY-007", "NOTIFY-008"
      ]
    },
    {
      "id": "read-only-ai-assistance",
      "clauses": ["AI-001", "AI-002", "AI-003", "AI-004", "AI-005", "AI-006"]
    },
    {
      "id": "csv-import-and-portability",
      "clauses": [
        "IMPORT-001", "IMPORT-002", "IMPORT-003", "IMPORT-004", "IMPORT-005",
        "DATA-001", "DATA-002", "DATA-003"
      ]
    }
  ],
  "nonGoals": [
    "multiple workspaces or tenants",
    "roles or granular permissions",
    "arbitrary tables, schema design, or user-defined formula and rollup fields",
    "general app building, scripting, or generic webhooks",
    "public or embeddable views",
    "bidirectional Airtable, spreadsheet, or SaaS sync",
    "automatic dependency rescheduling or critical-path optimisation",
    "resource or capacity planning",
    "general task management",
    "autonomous AI mutations or outbound communication",
    "notification channels other than in-app and email",
    "file attachments on records",
    "editorial content calendars and customer-feedback tracking",
    "native mobile applications"
  ],
  "externals": [
    {
      "id": "transactional-mail",
      "required": true,
      "why": "Magic-link sign-in and the operator notifications in NOTIFY-002 must reach email addresses outside the deployment.",
      "data": ["recipient email", "magic-link token or notification subject and body"],
      "adapters": ["mail.sender.v1"]
    },
    {
      "id": "ai-provider",
      "required": false,
      "why": "The campaign briefs, risk readouts, and lens proposals in AI-001 require a model; with AI off under SET-002 the rest of the product is unchanged.",
      "data": ["context allowed by ai.campaign-context.v1", "operator request"],
      "adapters": ["ai.provider.v1"]
    }
  ],
  "env": [
    {
      "name": "APP_ORIGIN",
      "required": true,
      "secret": false,
      "why": "Canonical HTTPS origin for authentication links and redirects."
    },
    {
      "name": "SESSION_SECRET",
      "required": true,
      "secret": true,
      "why": "Signs native sessions and authentication state."
    },
    {
      "name": "MAIL_FROM",
      "required": true,
      "secret": false,
      "why": "Verified sender for magic links and operator notifications."
    },
    {
      "name": "MAIL_PROVIDER_API_KEY",
      "required": true,
      "secret": true,
      "why": "Authenticates the default mail.sender.v1 adapter."
    },
    {
      "name": "AI_PROVIDER_API_KEY",
      "required": false,
      "requiredWhen": "AI is enabled under SET-002 and the default ai.provider.v1 adapter is used",
      "secret": true,
      "why": "Authenticates the configured AI provider."
    },
    {
      "name": "AI_MODEL",
      "required": false,
      "requiredWhen": "AI is enabled under SET-002 and the default ai.provider.v1 adapter is used",
      "secret": false,
      "why": "Selects the model recorded with AI usage."
    }
  ],
  "deploy": {
    "healthPath": "/health",
    "operatorPath": "/app",
    "apiPath": "/api",
    "publicPaths": ["/", "/health", "/signin", "/auth/capabilities", "/auth/local-sign-in", "/auth/request", "/auth/callback", "/assets/", "/favicon.ico"]
  },
  "customFields": ["campaign"],
  "extensionPoints": {
    "policies": [
      "operator.management.v1",
      "campaign.lifecycle.v1",
      "campaign.health.v1",
      "risk.escalation.v1",
      "decision.queue-order.v1",
      "decision.resolution.v1",
      "notification.delivery.v1",
      "ai.campaign-context.v1"
    ],
    "events": [
      "campaign.created.v1",
      "campaign.updated.v1",
      "campaign.archived.v1",
      "campaign.restored.v1",
      "campaign.deleted.v1",
      "deliverable.completed.v1",
      "deliverable.reopened.v1",
      "milestone.updated.v1",
      "risk.created.v1",
      "risk.updated.v1",
      "risk.escalated.v1",
      "risk.resolved.v1",
      "decision.requested.v1",
      "decision.assigned.v1",
      "decision.resolved.v1",
      "lens.saved.v1",
      "comment.created.v1",
      "import.committed.v1",
      "operator.removed.v1",
      "automation.run-failed.v1"
    ],
    "slots": [
      "navigation.after.v1",
      "dashboard.metrics.after.v1",
      "campaign.grid.toolbar.after.v1",
      "campaign.row.actions.v1",
      "campaign.detail.after.v1",
      "campaign.detail.sidebar.after.v1",
      "risk.card.actions.v1",
      "decision.detail.after.v1",
      "lens.builder.after.v1",
      "automation.card.after.v1",
      "settings.panel.after.v1"
    ],
    "contributions": [
      "app.routes.v1",
      "app.pages.v1",
      "app.navigation.v1",
      "settings.sections.v1",
      "jobs.commands.v1",
      "schedules.cron.v1",
      "queues.consumers.v1"
    ],
    "adapters": ["mail.sender.v1", "ai.provider.v1"]
  },
  "limits": {
    "status": "ui-ported-estimates-not-load-tested",
    "operators": 25,
    "campaigns": 50000,
    "campaignCustomFields": 50,
    "deliverables": 500000,
    "milestones": 250000,
    "risks": 250000,
    "decisions": 250000,
    "savedLenses": 200,
    "lensConditions": 20,
    "automations": 100,
    "bulkSelectionRecords": 500,
    "csvImportBytes": 10485760,
    "csvImportRows": 10000,
    "csvImportMimeTypes": ["text/csv", "application/vnd.ms-excel", "text/plain"],
    "requestBodyBytes": 1048576,
    "publicRequestsPerIpPerMinute": {
      "/": 120,
      "/health": 120,
      "/signin": 120,
      "/auth/request": 20,
      "/auth/callback": 20,
      "/assets/": 600,
      "/favicon.ico": 120
    }
  },
  "accessibility": {
    "level": "WCAG 2.2 AA",
    "status": "ui-ported-not-yet-audited"
  },
  "operatingCost": {
    "status": "estimated on 2026-08-31 from published Cloudflare pricing, not measured against a running deployment",
    "currency": "USD",
    "estimate": "$0 per month on Workers Free for the default deployment, excluding usage charged by the selected mail and AI providers",
    "assumptions": "Static assets, up to 100,000 Worker requests/day, 5 million D1 rows read/day, 100,000 D1 rows written/day, 5 GB total D1 storage, 10,000 Queue operations/day, five Cron Triggers, SQLite-backed Durable Objects (including alarms), and the R2 Standard free tier are available without a Workers Paid subscription. The declared record-count limits are capacity targets and do not by themselves require Workers Paid. Upgrade before production traffic or processing crosses a Workers Free hard cap—most likely 100,000 Worker requests/day, 5 million D1 rows read/day, 100,000 D1 rows written/day, 5 GB D1 storage, or 10,000 Queue operations/day. R2 usage beyond 10 GB-month, 1 million Class A operations, or 10 million Class B operations/month is billed separately. Adapter usage varies by provider.",
    "sources": [
      "https://developers.cloudflare.com/workers/platform/pricing/",
      "https://developers.cloudflare.com/workers/platform/limits/",
      "https://developers.cloudflare.com/d1/platform/pricing/",
      "https://developers.cloudflare.com/r2/pricing/",
      "https://developers.cloudflare.com/queues/platform/pricing/",
      "https://developers.cloudflare.com/durable-objects/platform/pricing/"
    ]
  }
}
