Files
agent/README.DB.STORY_PLAN.md
2026-02-27 21:28:09 +03:00

14 KiB
Raw Blame History

План доработки БД для хранения контекста Story и метаданных RAG

Цель

Зафиксировать проект миграции, который:

  • добавляет в таблицу чанков признаки артефакта (тип, источник, контекст),
  • вводит отдельный контур хранения инкремента по story_id,
  • не зависит от выбранного режима RAG (общий/сессионный/гибридный).

Границы

  • Документ описывает план и целевую схему.
  • Реализация SQL-миграций и backfill выполняется отдельным шагом после согласования.

1) Метаданные чанков (RAG-слой)

1.1. Что добавить

Для таблицы rag_chunks (или эквивалента таблицы чанков) добавить поля:

  • artifact_type (REQ|ARCH|API|DB|UI|CODE|OTHER)
  • path (нормализованный относительный путь файла)
  • section (заголовок/логический раздел документа)
  • doc_id (стабильный идентификатор документа)
  • doc_version (версия документа/ревизия)
  • owner (ответственная команда/человек)
  • system_component (система/подсистема/компонент)
  • last_modified (время последнего изменения источника)
  • staleness_score (0..1, в первую очередь для CODE)

1.2. Ограничения и индексы

  • CHECK для artifact_type и диапазона staleness_score.
  • Индексы:
    • (artifact_type)
    • (doc_id, doc_version)
    • (system_component)
    • (path)
    • GIN/BTREE по потребности для фильтрации в retrieval.

2) Контур Story (отдельно от чанков)

2.1. Таблица story_records

Карточка Story:

  • story_id (PK, строковый уникальный идентификатор)
  • project_id (идентификатор проекта/репозитория)
  • title
  • status (draft|in_progress|review|done|archived)
  • baseline_commit_sha (базовый снимок)
  • snapshot_id (опционально для session-RAG)
  • created_at, updated_at
  • created_by, updated_by

Индексы:

  • (project_id)
  • (status)
  • (updated_at)

2.2. Таблица story_artifacts

Связь Story с артефактами изменений:

  • id (PK)
  • story_id (FK -> story_records.story_id)
  • artifact_role (requirement|analysis|doc_change|test_model|note|decision|risk)
  • doc_id
  • doc_version
  • path
  • section
  • chunk_id (nullable; ссылка на chunk если стабильно поддерживается)
  • change_type (added|updated|removed|linked)
  • summary (краткое описание изменения)
  • source_ref (ссылка/внешний id)
  • created_at
  • created_by

Уникальность (черновик):

  • UNIQUE(story_id, artifact_role, COALESCE(doc_id,''), COALESCE(path,''), COALESCE(section,''), COALESCE(change_type,''))

Индексы:

  • (story_id, artifact_role)
  • (story_id, change_type)
  • (doc_id, doc_version)
  • (path)

2.3. Таблица story_links

Связи Story с внешними сущностями и Story-to-Story:

  • id (PK)
  • story_id (FK)
  • link_type (story|adr|ticket|pr|commit|doc|external)
  • target_ref (идентификатор/ссылка)
  • description
  • created_at

Индексы:

  • (story_id, link_type)
  • (target_ref)

3) Почему story_id не в чанках

  • Один чанк может относиться к нескольким Story.
  • Чанки нестабильны при переиндексации.
  • Разделение слоев упрощает поддержку и не привязывает модель к типу RAG.

Итог: связь Story и чанков/документов хранить в story_artifacts, а не в rag_chunks.

4) Целевая модель RAG: Hybrid-Lite

Выбранный вектор на текущем этапе: Session-first + Shared Cache + Story Ledger.

4.1. Принципы

  • Рабочий retrieval выполняется из сессионного индекса (видит незакоммиченные изменения).
  • Общий кэш чанков/эмбеддингов используется только для ускорения индексации.
  • Источник правды по инкременту Story находится в Story-таблицах, а не в RAG-индексе.

4.2. Что хранить дополнительно

  • rag_blob_cache: кэш файловых blob по repo_id + blob_sha.
  • rag_chunk_cache: кэш чанков/эмбеддингов, привязанный к blob_sha.
  • rag_session_chunk_map: привязка сессии к используемым chunk (чтобы retrieval был изолированным).
  • session_artifacts: временные артефакты сессии до появления story_id (late binding).

4.3. Алгоритм индексации (delta-only)

  1. На старте сессии сканировать рабочее дерево и считать blob_sha для файлов индексации.
  2. Для каждого файла:
    • cache hit: взять chunk/embedding из кэша и связать с текущей сессией.
    • cache miss: выполнить chunk+embed и записать результат в кэш.
  3. Для retrieval использовать rag_session_chunk_map как первичный источник.
  4. При необходимости делать fallback к cache-scoped данным по repo_id (опционально, под флагом).

4.4. Почему это подходит

  • Нет необходимости в сложном ACL общего RAG на уровне приложения.
  • Нет обязательной зависимости от ручного commit, индекс отражает локальные изменения.
  • Снижается время загрузки сессии за счет переиспользования эмбеддингов.
  • История Story не теряется и не зависит от режима RAG.

4.5. Late binding story_id (целевой процесс)

  1. Аналитик запускает работу только со ссылкой на документ (без story_id).
  2. Агент обрабатывает задачу в session-RAG и сохраняет все изменения в session_artifacts.
  3. Аналитик вручную делает commit и указывает story_id.
  4. Вебхук на commit:
    • извлекает story_id из commit metadata/message,
    • обновляет репозиторный RAG,
    • выполняет bind session -> story: переносит/привязывает session_artifacts к story_artifacts,
    • фиксирует связь story_id <-> commit_sha <-> changed_files.
  5. Исходный документ аналитики тоже попадает в контекст Story ретроспективно, даже если изначально был без story_id.

5) Черновик DDL (PostgreSQL)

-- 0. Enum-like checks можно заменить на справочники при необходимости

-- A) Session artifacts (временный слой до появления story_id)
CREATE TABLE IF NOT EXISTS session_artifacts (
    id BIGSERIAL PRIMARY KEY,
    session_id TEXT NOT NULL,
    project_id TEXT NOT NULL,
    artifact_role TEXT NOT NULL,
    source_ref TEXT,
    doc_id TEXT,
    doc_version TEXT,
    path TEXT,
    section TEXT,
    chunk_id TEXT,
    change_type TEXT,
    summary TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by TEXT,
    CONSTRAINT chk_session_artifact_role CHECK (artifact_role IN (
        'analysis','doc_change','note','decision','risk','test_model'
    )),
    CONSTRAINT chk_session_change_type CHECK (change_type IS NULL OR change_type IN (
        'added','updated','removed','linked'
    ))
);

CREATE INDEX IF NOT EXISTS idx_session_artifacts_session ON session_artifacts(session_id);
CREATE INDEX IF NOT EXISTS idx_session_artifacts_project ON session_artifacts(project_id);
CREATE INDEX IF NOT EXISTS idx_session_artifacts_role ON session_artifacts(artifact_role);

-- 1) Story records
CREATE TABLE IF NOT EXISTS story_records (
    story_id TEXT PRIMARY KEY,
    project_id TEXT NOT NULL,
    title TEXT,
    status TEXT NOT NULL DEFAULT 'draft',
    baseline_commit_sha TEXT,
    snapshot_id TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by TEXT,
    updated_by TEXT,
    CONSTRAINT chk_story_status CHECK (status IN (
        'draft','in_progress','review','done','archived'
    ))
);

CREATE INDEX IF NOT EXISTS idx_story_records_project ON story_records(project_id);
CREATE INDEX IF NOT EXISTS idx_story_records_status ON story_records(status);
CREATE INDEX IF NOT EXISTS idx_story_records_updated_at ON story_records(updated_at DESC);

-- 2) Story artifacts
CREATE TABLE IF NOT EXISTS story_artifacts (
    id BIGSERIAL PRIMARY KEY,
    story_id TEXT NOT NULL REFERENCES story_records(story_id) ON DELETE CASCADE,
    artifact_role TEXT NOT NULL,
    doc_id TEXT,
    doc_version TEXT,
    path TEXT,
    section TEXT,
    chunk_id TEXT,
    change_type TEXT,
    summary TEXT,
    source_ref TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    created_by TEXT,
    CONSTRAINT chk_story_artifact_role CHECK (artifact_role IN (
        'requirement','analysis','doc_change','test_model','note','decision','risk'
    )),
    CONSTRAINT chk_story_change_type CHECK (change_type IS NULL OR change_type IN (
        'added','updated','removed','linked'
    ))
);

CREATE INDEX IF NOT EXISTS idx_story_artifacts_story_role ON story_artifacts(story_id, artifact_role);
CREATE INDEX IF NOT EXISTS idx_story_artifacts_story_change ON story_artifacts(story_id, change_type);
CREATE INDEX IF NOT EXISTS idx_story_artifacts_doc ON story_artifacts(doc_id, doc_version);
CREATE INDEX IF NOT EXISTS idx_story_artifacts_path ON story_artifacts(path);

-- Вариант уникальности можно уточнить после согласования процессов

-- 3) Story links
CREATE TABLE IF NOT EXISTS story_links (
    id BIGSERIAL PRIMARY KEY,
    story_id TEXT NOT NULL REFERENCES story_records(story_id) ON DELETE CASCADE,
    link_type TEXT NOT NULL,
    target_ref TEXT NOT NULL,
    description TEXT,
    created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
    CONSTRAINT chk_story_link_type CHECK (link_type IN (
        'story','adr','ticket','pr','commit','doc','external'
    ))
);

CREATE INDEX IF NOT EXISTS idx_story_links_story_type ON story_links(story_id, link_type);
CREATE INDEX IF NOT EXISTS idx_story_links_target_ref ON story_links(target_ref);

6) План внедрения (после согласования)

  1. Подтвердить перечень полей и enum-значений.
  2. Подготовить SQL-миграцию Vxxx__story_context.sql.
  3. Обновить bootstrap/инициализацию схемы.
  4. Обновить репозитории для story_records/story_artifacts/story_links.
  5. Добавить таблицу и репозиторий session_artifacts (session-scoped артефакты без story_id).
  6. Добавить запись session-артефактов в оркестраторе во время работы аналитика.
  7. Добавить webhook-обработчик bind session -> story при появлении commit со story_id.
  8. Добавить API/сервисный метод get_story_context(story_id) для повторного входа в Story.
  9. Добавить тесты:
    • unit на репозитории,
    • интеграционные на happy-path записи/чтения,
    • регресс на отсутствие зависимости от типа RAG.
  10. Добавить миграцию для rag_blob_cache/rag_chunk_cache/rag_session_chunk_map.
  11. Внедрить delta-only индексацию для session-RAG с переиспользованием кэша.

7) Открытые вопросы

  • Нужен ли отдельный справочник для artifact_type, artifact_role, link_type.
  • Что считать doc_version: semver, дата, commit, hash файла.
  • Нужна ли soft-delete политика для Story.
  • Требуется ли аудит (кто/когда менял summary и связи).
  • Какой уровень обязательности chunk_id (опционален по умолчанию).
  • Нужна ли TTL/очистка для rag_blob_cache/rag_chunk_cache.
  • Делать ли fallback к репозиторному кэшу по умолчанию или только при explicit-флаге.
  • Как определять соответствие session_id и commit в webhook (1:1, последний активный, explicit token).
  • Как долго хранить session_artifacts до bind/cleanup.

8) Критерии готовности

  • По story_id можно восстановить инкремент без исходной сессии.
  • История изменений не теряется при переиндексации RAG.
  • Аналитик и тестировщик используют один story_id как общий ключ контекста.
  • Схема работает при любом выбранном режиме RAG.
  • Session-RAG поднимается быстрее за счет cache hit по неизмененным файлам.
  • Артефакты аналитика, созданные до появления story_id, корректно попадают в Story после commit/webhook bind.