feat(docs): save chat answers as docs — edit screen, commit + push to the .env docs branch
This commit is contained in:
@@ -31,6 +31,16 @@ Data model — see ``.agent/PLAN.md`` §Data Model:
|
||||
generation of the knowledge base is current,
|
||||
bumped exactly once per KB-changing sync so saved
|
||||
chats can be marked stale (phase 53).
|
||||
* ``doc_drafts`` — server-side drafts of chat answers saved as
|
||||
documentation: one row per "Save as doc" action
|
||||
(the long answer body lives here, never in a URL),
|
||||
keyed by an unguessable ``uuid4`` ``token`` (the
|
||||
edit screen's URL credential — the share-token
|
||||
trust model, phase 51); ``status`` moves
|
||||
``draft`` → ``pushed`` (``branch`` +
|
||||
``commit_sha`` recorded) when the push endpoint
|
||||
commits + pushes the file to the
|
||||
``BOR_DOCS_REPO`` branch (phase 59).
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
@@ -205,6 +215,62 @@ class GitSource(Base):
|
||||
added_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
|
||||
|
||||
class DocDraft(Base):
|
||||
"""One server-side draft of a chat answer saved as documentation
|
||||
(phase 59, task 01).
|
||||
|
||||
A long answer body must live on the **server**, never in a URL: the
|
||||
"Save as doc" action POSTs the answer's raw markdown to
|
||||
``POST /api/doc-drafts`` (task 02), which stores it here and hands
|
||||
back an unguessable 128-bit ``uuid4`` ``token`` — the edit
|
||||
screen's URL credential (``/doc-edit.html?draft=<token>``, the
|
||||
share-token trust model, phase 51). ``status`` stays ``draft``
|
||||
until the push endpoint (task 04) commits + pushes the file to the
|
||||
``BOR_DOCS_REPO`` branch — then it is ``pushed``, with ``branch``
|
||||
and ``commit_sha`` recorded (the UI's branch + sha feedback; D3:
|
||||
no PR tooling — the owner opens the PR themselves).
|
||||
"""
|
||||
|
||||
__tablename__ = "doc_drafts"
|
||||
|
||||
id: Mapped[uuid.UUID] = mapped_column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
|
||||
#: The URL credential (``/doc-edit.html?draft=<token>``): an
|
||||
#: unguessable 128-bit ``uuid4`` — never the row id, never
|
||||
#: sequential/guessable. Unique NOT NULL: unlike the NULLable
|
||||
#: ``saved_chats.share_token`` there is no "un-drafted" state, so
|
||||
#: NULLs never occur (always set on create).
|
||||
token: Mapped[uuid.UUID] = mapped_column(
|
||||
UUID(as_uuid=True), unique=True, nullable=False, default=uuid.uuid4
|
||||
)
|
||||
#: The document's title. Defaults client-side to the last user
|
||||
#: question (whitespace-collapsed, ≤120 chars — the chat auto-title
|
||||
#: convention, phase 50); the edit screen changes anything.
|
||||
title: Mapped[str] = mapped_column(Text)
|
||||
#: The in-repo file path (default ``docs/<slug>.md``). Guard-railled
|
||||
#: by the API layer (task 02 — repo-relative, no ``..``); the
|
||||
#: column itself is plain TEXT (the ``documents.path`` precedent).
|
||||
path: Mapped[str] = mapped_column(Text)
|
||||
#: The markdown body — the answer's raw text (never HTML — the
|
||||
#: ``bor.chat.v1`` record's ``text``), edited on the edit screen.
|
||||
body: Mapped[str] = mapped_column(Text)
|
||||
#: "draft" until the push endpoint commits + pushes the file, then
|
||||
#: "pushed" — the domain is enforced by the API layer (the
|
||||
#: ``git_sources.kind`` phase-38 precedent: plain TEXT + server
|
||||
#: default, no CHECK constraint).
|
||||
status: Mapped[str] = mapped_column(Text, default="draft", server_default="'draft'")
|
||||
#: Set on push (task 04): the branch the commit landed on (the
|
||||
#: ``BOR_DOCS_BRANCH`` name); NULL while still a draft.
|
||||
branch: Mapped[str | None] = mapped_column(Text)
|
||||
#: ... and the pushed branch's new HEAD sha (must equal
|
||||
#: ``git rev-parse <branch>`` in the repo); NULL while still a
|
||||
#: draft.
|
||||
commit_sha: Mapped[str | None] = mapped_column(Text)
|
||||
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), server_default=func.now())
|
||||
updated_at: Mapped[datetime] = mapped_column(
|
||||
DateTime(timezone=True), server_default=func.now(), onupdate=func.now()
|
||||
)
|
||||
|
||||
|
||||
class SavedChat(Base):
|
||||
"""One owner-saved chat conversation (phase 50).
|
||||
|
||||
|
||||
Reference in New Issue
Block a user