Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions backend/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,11 @@ WIKI_ENABLED=True
# The team's bot MUST have a model configured (bind_model or custom config)
# If no model is configured, wiki generation will be disabled in the frontend
WIKI_DEFAULT_TEAM_NAME=wiki-team
# Team that runs knowledge-base code wikis (matches init_data/02-public-resources.yaml)
# Separate from WIKI_DEFAULT_TEAM_NAME: the two paths give their agents different
# instructions and different submission rules, so they cannot share one team.
# Its bot MUST have a model configured (bind_model or custom config)
WIKI_CODE_WIKI_TEAM_NAME=code-wiki-team
# Default agent type for wiki generation
WIKI_DEFAULT_AGENT_TYPE=ClaudeCode
# Default language for wiki documentation generation (en = English, zh = Chinese)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
"""add knowledge content origin and wiki generation kb link

Revision ID: bd9c871a93d2
Revises: b9c0d1e2f3a4

Adds the two things a code wiki needs to coexist with ordinary knowledge content:

- ``origin`` on documents and folders, marking whether a row is agent-generated or
user-owned. It defaults to ``user`` so that every existing row, and every row this
service does not create itself, is excluded from the generated-content projection.
Getting this backwards would let a regeneration delete content nobody can restore.
- ``kind_id`` on wiki generations, binding a version line to a knowledge base. The
versions previously hung off ``wiki_projects``, whose ``source_url`` is globally
unique; leaving them there would share one version line between knowledge bases
tracking the same repository. ``0`` marks rows that predate code wikis.

All three columns are NOT NULL with a server default, as required for existing rows to
backfill without a nullable intermediate state.
"""

from collections.abc import Sequence
from typing import Union

import sqlalchemy as sa

from alembic import op

# revision identifiers, used by Alembic.
revision: str = "bd9c871a93d2"
down_revision: Union[str, Sequence[str], None] = "b9c0d1e2f3a4"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None
Comment on lines +28 to +32

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# List all revision/down_revision declarations to detect missing parents or multiple heads.
fd -e py . backend/alembic/versions --exec rg -n '^revision(: str)? *=|^down_revision' {} \; -x true
python3 - <<'PY'
import ast, pathlib
revs, downs = {}, {}
for p in sorted(pathlib.Path("backend/alembic/versions").glob("*.py")):
    src = p.read_text()
    tree = ast.parse(src, filename=str(p))
    a = {}
    for node in tree.body:
        if isinstance(node, ast.Assign):
            for t in node.targets:
                if isinstance(t, ast.Name):
                    try:
                        a[t.id] = ast.literal_eval(node.value)
                    except Exception:
                        pass
        if isinstance(node, ast.AnnAssign) and isinstance(node.target, ast.Name):
            try:
                a[node.target.id] = ast.literal_eval(node.value)
            except Exception:
                pass
    if "revision" in a:
        revs[a["revision"]] = str(p)
        downs[a["revision"]] = a.get("down_revision")
parents = {d for d in downs.values() if d}
print("missing parents:", sorted(p for p in parents if p not in revs))
print("heads:", sorted(r for r in revs if r not in parents))
PY

Repository: wecode-ai/Wegent

Length of output: 2396


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "## Candidate migration files containing bd9c871a93d2 / b9c0d1e2f3a4"
rg -n "bd9c871a93d2|b9c0d1e2f3a4|revision = \"(c8d2e3f4a5b6|e6f7a8b9c013|e6f7a8b9c012|a2b3c4d5e6f7)\"" backend/alembic/versions || true

echo
echo "## Alembic env/config files"
git ls-files 'backend/alembic/**'
python3 - <<'PY'
import pathlib
for p in pathlib.Path("backend/alembic").rglob("*"):
    if p.is_file() and p.suffix in {".py", ".ini"}:
        if any(x in p.parts for x in {"versions", "env.py", "script.py.mako", "config.py"}):
            print(p)
PY

Repository: wecode-ai/Wegent

Length of output: 5842


Resolve the Alembic graph before applying this migration.

bd9c871a93d2 correctly points to b9c0d1e2f3a4, but the migration tree still has multiple heads (bd9c871a93d2, c8d2e3f4a5b6, e6f7a8b9c012, e6f7a8b9c013, and a2b3c4d5e6f7). Merge or relink the branches so upgrade head applies one linear sequence and the schema is not split across heads.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@backend/alembic/versions/20260731_bd9c871a93d2_add_knowledge_content_origin_and_wiki_.py`
around lines 28 - 32, Resolve the Alembic revision graph around revision
bd9c871a93d2 and the other listed heads by merging or relinking the migration
branches into one upgrade path. Update the affected revision identifiers and add
any required merge revision so upgrade head resolves to a single linear sequence
while preserving the existing schema changes.

Source: Coding guidelines



def upgrade() -> None:
"""Upgrade schema."""
op.add_column(
"knowledge_documents",
sa.Column(
"origin",
sa.String(length=20),
nullable=False,
server_default="user",
comment="Content ownership: 'generated' (agent-owned) or 'user'",
),
)
op.add_column(
"knowledge_folders",
sa.Column(
"origin",
sa.String(length=20),
nullable=False,
server_default="user",
comment="Content ownership: 'generated' (agent-owned) or 'user'",
),
)
op.add_column(
"wiki_generations",
sa.Column(
"kind_id",
sa.Integer(),
nullable=False,
server_default="0",
comment="Knowledge base this version line belongs to; 0 = legacy row",
),
)
op.create_index(
"ix_wiki_generations_kind_id", "wiki_generations", ["kind_id"], unique=False
)


def downgrade() -> None:
"""Downgrade schema."""
op.drop_index("ix_wiki_generations_kind_id", table_name="wiki_generations")
op.drop_column("wiki_generations", "kind_id")
op.drop_column("knowledge_folders", "origin")
op.drop_column("knowledge_documents", "origin")
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
"""link wiki projects to their code wiki

Revision ID: 2b5791acc5fa
Revises: bd9c871a93d2

``wiki_projects.source_url`` is already UNIQUE, which makes this table the only place
that can enforce "one repository, one code wiki" against two people creating at the
same moment. Recording the knowledge base here, rather than checking a JSON field on
the knowledge base itself, turns a check-then-insert into a database constraint.

``0`` marks a project row with no code wiki, which is every row that exists today.
"""

from collections.abc import Sequence
from typing import Union

import sqlalchemy as sa

from alembic import op

# revision identifiers, used by Alembic.
revision: str = "2b5791acc5fa"
down_revision: Union[str, Sequence[str], None] = "bd9c871a93d2"
branch_labels: Union[str, Sequence[str], None] = None
depends_on: Union[str, Sequence[str], None] = None


def upgrade() -> None:
"""Upgrade schema."""
op.add_column(
"wiki_projects",
sa.Column(
"kind_id",
sa.Integer(),
nullable=False,
server_default="0",
comment="Code wiki knowledge base built from this repository; 0 = none",
),
)
op.create_index(
"ix_wiki_projects_kind_id", "wiki_projects", ["kind_id"], unique=False
)


def downgrade() -> None:
"""Downgrade schema."""
op.drop_index("ix_wiki_projects_kind_id", table_name="wiki_projects")
op.drop_column("wiki_projects", "kind_id")
Loading
Loading