v2 foundation: shared contracts, app profile schema, wiring stubs

- projects: app profile columns + one app per org; project_external_git,
  deploy_targets, org_model_grants tables (migration a0f0e1d2c3b4)
- ports + fakes: RepoWorkspace, DeliveryHooks, DeployTarget, Memory,
  ModelGrants, WorkforceBootstrap; AppProfile + AppProfileRepository
- per-workstream wiring (container_org/engine/delivery), config blocks,
  empty routers, delivery worker, MCP/delivery service shells, web stubs
- admin nav derives from ADMIN_ITEMS; plan docs updated for ext4 worktrees

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
mjalmoudy94
2026-10-13 23:21:17 +03:30
co-authored by Claude Sonnet 5.5
parent 3a68b0fa07
commit 0cd9194b64
58 changed files with 1576 additions and 26 deletions
+100
View File
@@ -221,6 +221,8 @@ class ProjectRow(Base):
__tablename__ = "projects"
__table_args__ = (
UniqueConstraint("organization_id", "slug", name="uq_project_org_slug"),
# v2 rule: one organization has exactly one application.
UniqueConstraint("organization_id", name="uq_project_one_per_org"),
# The project list is always scoped to one organization and ordered by
# creation, so one composite index serves both the filter and the sort.
Index("ix_project_org_created", "organization_id", "created_at"),
@@ -237,6 +239,94 @@ class ProjectRow(Base):
ForeignKey("users.id"), nullable=True, index=True
)
default_branch: Mapped[str] = mapped_column(String(80), default="main", nullable=False)
# --- v2 application profile (filled by the organization wizard) ---
app_type: Mapped[str | None] = mapped_column(String(32), nullable=True)
language: Mapped[str | None] = mapped_column(String(32), nullable=True)
framework: Mapped[str | None] = mapped_column(String(48), nullable=True)
runtime_version: Mapped[str | None] = mapped_column(String(32), nullable=True)
package_manager: Mapped[str | None] = mapped_column(String(32), nullable=True)
setup_command: Mapped[str | None] = mapped_column(Text, nullable=True)
build_command: Mapped[str | None] = mapped_column(Text, nullable=True)
test_command: Mapped[str | None] = mapped_column(Text, nullable=True)
start_command: Mapped[str | None] = mapped_column(Text, nullable=True)
app_port: Mapped[int | None] = mapped_column(Integer, nullable=True)
healthcheck_path: Mapped[str] = mapped_column(String(255), default="/", nullable=False)
git_source: Mapped[str] = mapped_column(String(16), default="platform", nullable=False)
external_repo_url: Mapped[str | None] = mapped_column(String(512), nullable=True)
wizard_completed_at: Mapped[datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True
)
created_at: Mapped[datetime] = _created()
class ProjectExternalGitRow(Base):
"""Credentials for a user-supplied git remote. Secrets are encrypted at rest."""
__tablename__ = "project_external_git"
project_id: Mapped[str] = mapped_column(
ForeignKey("projects.id", ondelete="CASCADE"), primary_key=True
)
auth_type: Mapped[str] = mapped_column(String(16), default="none", nullable=False)
username: Mapped[str | None] = mapped_column(String(160), nullable=True)
secret_encrypted: Mapped[str | None] = mapped_column(Text, nullable=True)
known_hosts: Mapped[str | None] = mapped_column(Text, nullable=True)
updated_at: Mapped[datetime] = mapped_column(
DateTime(timezone=True), default=utcnow, onupdate=utcnow, nullable=False
)
class DeployTargetRow(Base):
"""A production deploy target (ssh / sftp / ftp / ftps). Secrets are encrypted."""
__tablename__ = "deploy_targets"
__table_args__ = (Index("ix_deploy_target_project", "project_id"),)
id: Mapped[str] = _pk()
organization_id: Mapped[str] = mapped_column(
ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False, index=True
)
project_id: Mapped[str] = mapped_column(
ForeignKey("projects.id", ondelete="CASCADE"), nullable=False
)
kind: Mapped[str] = mapped_column(String(8), nullable=False)
host: Mapped[str] = mapped_column(String(255), nullable=False)
port: Mapped[int] = mapped_column(Integer, nullable=False)
username: Mapped[str] = mapped_column(String(160), nullable=False)
auth_type: Mapped[str] = mapped_column(String(16), default="password", nullable=False)
secret_encrypted: Mapped[str | None] = mapped_column(Text, nullable=True)
remote_path: Mapped[str] = mapped_column(String(512), nullable=False)
post_deploy_command: Mapped[str | None] = mapped_column(Text, nullable=True)
host_fingerprint: Mapped[str | None] = mapped_column(String(255), nullable=True)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
last_tested_at: Mapped[datetime | None] = mapped_column(
DateTime(timezone=True), nullable=True
)
last_test_ok: Mapped[bool | None] = mapped_column(Boolean, nullable=True)
last_test_message: Mapped[str | None] = mapped_column(String(500), nullable=True)
created_at: Mapped[datetime] = _created()
class OrgModelGrantRow(Base):
"""A model the platform owner lets one organization's employees use."""
__tablename__ = "org_model_grants"
__table_args__ = (
UniqueConstraint("organization_id", "ai_model_id", name="uq_org_model_grant"),
)
id: Mapped[str] = _pk()
organization_id: Mapped[str] = mapped_column(
ForeignKey("organizations.id", ondelete="CASCADE"), nullable=False, index=True
)
ai_model_id: Mapped[str] = mapped_column(
ForeignKey("ai_models.id", ondelete="CASCADE"), nullable=False
)
daily_credit_cap: Mapped[float | None] = mapped_column(Float, nullable=True)
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
created_by_user_id: Mapped[str | None] = mapped_column(
ForeignKey("users.id"), nullable=True
)
created_at: Mapped[datetime] = _created()
@@ -926,3 +1016,13 @@ class QualityMetricRow(Base):
value: Mapped[float] = mapped_column(Float, default=0.0, nullable=False)
dimensions_json: Mapped[dict] = mapped_column(JSON, default=dict, nullable=False)
created_at: Mapped[datetime] = _created()
# ---------------------------------------------------------------------------
# v2 tables owned by one workstream each (kept in separate modules so the three
# parallel agents never edit the same file). Imported last so every table is
# registered on ``Base.metadata`` before ``create_all`` / alembic autogenerate.
# ---------------------------------------------------------------------------
from app.infrastructure.db import models_delivery as _models_delivery # noqa: E402,F401
from app.infrastructure.db import models_engine as _models_engine # noqa: E402,F401
from app.infrastructure.db import models_org as _models_org # noqa: E402,F401