fix(memory+spec): drop host segment from memory path + spec-status argv off-by-one #11

Closed
opened 2026-08-07 11:18:33 +03:00 by slaid098 · 0 comments
Owner

Контекст

Два блокирующих бага обнаружены при pipeline PR #10 (Forgejo-first adaptation, merged):

Баг 1 — Memory host mismatch (блокирует PR #10 MEMORY phase): после миграции remote с github.com на git.slaid098.dev (через git config url.insteadOf) pipeline-status оракул строит memory path из реального remote URL → repos/git.slaid098.dev/slaid098/opencode-config*.md. Но memory-syncer физически пишет в repos/github.com/slaid098/opencode-config-003.md (старый путь). Результат: MEMORY phase всегда ❌ "memory files не найдены" → pipeline не может COMPLETE. Блокирующий баг для всех будущих PR.

Баг 2 — spec-status.py:154 off-by-one (блокирует /spec pipeline): spec-status.py:153-156 dispatch gh issue view использует args[2] для номера issue, но args[2] содержит "view" (строка), не номер. Проверка args[2].lstrip("-").isdigit() всегда False → ветка никогда не выполняется. spec-status оракул в Forgejo-режиме не может читать issues по номеру. Pre-existing баг (найден reviewer'ом в PR #10, вне scope PR #10).

Задача

Фикс 1 — Memory path (убрать host сегмент):

  • Структура repos/{host}/{org}/{repo}.md → repos/{org}/{repo}.md (без host). Org slaid098 уникален, нет привязки к хостингу (Forgejo сегодня, GitLab завтра — те же файлы).
  • git -C ~/.local/share/opencode/opencode-memory mv repos/github.com/slaid098/ repos/slaid098/ — 22 файла переезжают (clean rename, git history сохранён)
  • pipeline-status.py:_resolve_memory_base — убрать {host} из пути: repos/{org}/ вместо repos/{host}/{org}/. parse_remote_url возвращает (host, org, repo) — использовать только org и repo, host игнорировать.
  • memory-syncer.md:45 — пример github.com/slaid098/... → slaid098/..., инструкция парсинга remote без host
  • memory/SKILL.md — проверить, обновить если упоминает {host}/{org}/{repo} format
  • Тесты tests/test_pipeline_status.py — обновить _resolve_memory_base тесты (без host), test_check_memory_done_rotated_file (путь без host), добавить test_memory_path_no_host_segment

Фикс 2 — spec-status.py:154 off-by-one:

  • spec-status.py:154 — args[2] → args[3] (номер issue, не "view")
  • spec-status.py:153 — len(args) > 2 → len(args) > 3 (гарантировать наличие args[3])
  • Тест test_spec_status_issue_view (в tests/test_spec_status.py если есть, иначе добавить) — проверить что dispatch gh issue view N доходит до Forgejo API

Контракты

  • Memory path: ~/.local/share/opencode/opencode-memory/repos/{org}/{repo}(-\d+)?.md (без host сегмента)
  • parse_remote_url (pipeline-status.py:385-402) — возвращает (host, org, repo), но _resolve_memory_base использует только (org, repo)
  • git mv в memory repo: git -C ~/.local/share/opencode/opencode-memory mv repos/github.com/slaid098/ repos/slaid098/
  • get_memory_files (pipeline-status.py:414-421) — glob {repo}(-\d+)?.md (без изменений)
  • spec-status dispatch argv: ["gh", "issue", "view", str(num), "--repo", repo] → args[1]="issue", args[2]="view", args[3]=str(num), args[4]="--repo", args[5]=repo. Проверка args[3].lstrip("-").isdigit() = True.
  • Memory repo commit: refactor: drop host segment from memory path (или fix(memory): drop host segment)

Инварианты

  • 22 memory файла не теряются (git mv = rename, git history сохранён)
  • Чистый разрыв со старым путём repos/{host}/{org}/ (не fallback, полная миграция — ты мигрируешь полностью на новый path)
  • pipeline-status оракул находит memory files по новому пути после миграции
  • spec-status dispatch gh issue view N доходит до Forgejo API после фикса
  • get_memory_files glob {repo}(-\d+)?.md поддерживает ротацию (не кейс #238)
  • Внешние контракты tools НЕ меняются

Граничные случаи

  • Репо с двумя remote (github.com + git.slaid098.dev) — parse_remote_url возвращает первый/настроенный, org одинаковый → работает
  • Memory files для других orgs (не slaid098) под repos/github.com/ — не трогаем, мигрируем только slaid098
  • После миграции старый путь repos/github.com/slaid098/ — пустой, rmdir если чисто (но оставить repos/github.com/ если есть другие orgs)
  • spec-status если --repo отсутствует в argv — args[3] всё равно номер issue, len(args) > 3 проверяет
  • parse_remote_url если host не определён (local repo без remote) — fallback на org/repo или ошибка

Влияние на связанные компоненты

  • pipeline-status.py:_resolve_memory_base (~L405) — основная правка (убрать host)
  • pipeline-status.py:get_memory_files (L414-421) — без изменений (glob)
  • pipeline-status.py:check_memory (L702-723) — без изменений
  • spec-status.py:153-156 — off-by-one фикc (2 строки)
  • memory-syncer.md:45 — пример пути, инструкция парсинга remote
  • memory/SKILL.md — проверить path format упоминания
  • tests/test_pipeline_status.py — обновить тесты path (убрать host из expected paths), test_check_memory_done_rotated_file, test_get_memory_files_excludes_non_rotation_suffix
  • tests/test_spec_status.py — добавить/обновить тест test_spec_status_issue_view_dispatch
  • PR #10 MEMORY phase — разблокируется после merge (pipeline-status найдёт files по новому пути)
  • /spec pipeline — spec-phase заработает после merge

Вне scope

  • Миграция других orgs (не slaid098) — не трогаем
  • insteadOf конфигурация git config — infra, не трогаем
  • Другие pre-existing баги в spec-status — только этот one off-by-one
  • PR #2 (skills), PR #3 (config), PR #4 (templates) — следующий после разблокировки
  • RAG индекс — перестраивается автоматически

Критерии приемки

  • git -C ~/.local/share/opencode/opencode-memory log --oneline -1 показывает commit про drop host segment
  • ls ~/.local/share/opencode/opencode-memory/repos/slaid098/ — 22 файла (или ~22, включая ротации)
  • ls ~/.local/share/opencode/opencode-memory/repos/github.com/slaid098/ — не существует или пусто
  • rg "_resolve_memory_base" .opencode/scripts/pipeline-status.py — path без {host} (только {org})
  • pipeline-status({pr_number: 10}) → MEMORY phase DONE (находит memory files по новому пути)
  • rg "args\[2\]" .opencode/scripts/spec-status.py — 0 совпадений в dispatch коде (заменено на args[3])
  • spec-status для существующего issue → корректный ответ (dispatch доходит до Forgejo API)
  • uv run pytest tests/test_pipeline_status.py tests/test_spec_status.py -x → all passed
  • PR #10 pipeline-status → Status: COMPLETE (all 6 phases DONE)
## Контекст Два блокирующих бага обнаружены при pipeline PR #10 (Forgejo-first adaptation, merged): **Баг 1 — Memory host mismatch (блокирует PR #10 MEMORY phase):** после миграции remote с `github.com` на `git.slaid098.dev` (через `git config url.insteadOf`) `pipeline-status` оракул строит memory path из реального remote URL → `repos/git.slaid098.dev/slaid098/opencode-config*.md`. Но memory-syncer физически пишет в `repos/github.com/slaid098/opencode-config-003.md` (старый путь). Результат: MEMORY phase всегда ❌ "memory files не найдены" → pipeline не может COMPLETE. Блокирующий баг для всех будущих PR. **Баг 2 — spec-status.py:154 off-by-one (блокирует /spec pipeline):** `spec-status.py:153-156` dispatch `gh issue view` использует `args[2]` для номера issue, но `args[2]` содержит `"view"` (строка), не номер. Проверка `args[2].lstrip("-").isdigit()` всегда `False` → ветка никогда не выполняется. spec-status оракул в Forgejo-режиме не может читать issues по номеру. Pre-existing баг (найден reviewer'ом в PR #10, вне scope PR #10). ## Задача **Фикс 1 — Memory path (убрать host сегмент):** - Структура `repos/{host}/{org}/{repo}.md` → `repos/{org}/{repo}.md` (без host). Org `slaid098` уникален, нет привязки к хостингу (Forgejo сегодня, GitLab завтра — те же файлы). - `git -C ~/.local/share/opencode/opencode-memory mv repos/github.com/slaid098/ repos/slaid098/` — 22 файла переезжают (clean rename, git history сохранён) - `pipeline-status.py:_resolve_memory_base` — убрать `{host}` из пути: `repos/{org}/` вместо `repos/{host}/{org}/`. `parse_remote_url` возвращает `(host, org, repo)` — использовать только `org` и `repo`, `host` игнорировать. - `memory-syncer.md:45` — пример `github.com/slaid098/...` → `slaid098/...`, инструкция парсинга remote без host - `memory/SKILL.md` — проверить, обновить если упоминает `{host}/{org}/{repo}` format - Тесты `tests/test_pipeline_status.py` — обновить `_resolve_memory_base` тесты (без host), `test_check_memory_done_rotated_file` (путь без host), добавить `test_memory_path_no_host_segment` **Фикс 2 — spec-status.py:154 off-by-one:** - `spec-status.py:154` — `args[2]` → `args[3]` (номер issue, не `"view"`) - `spec-status.py:153` — `len(args) > 2` → `len(args) > 3` (гарантировать наличие `args[3]`) - Тест `test_spec_status_issue_view` (в `tests/test_spec_status.py` если есть, иначе добавить) — проверить что dispatch `gh issue view N` доходит до Forgejo API ## Контракты - Memory path: `~/.local/share/opencode/opencode-memory/repos/{org}/{repo}(-\d+)?.md` (без host сегмента) - `parse_remote_url` (pipeline-status.py:385-402) — возвращает `(host, org, repo)`, но `_resolve_memory_base` использует только `(org, repo)` - `git mv` в memory repo: `git -C ~/.local/share/opencode/opencode-memory mv repos/github.com/slaid098/ repos/slaid098/` - `get_memory_files` (pipeline-status.py:414-421) — glob `{repo}(-\d+)?.md` (без изменений) - spec-status dispatch argv: `["gh", "issue", "view", str(num), "--repo", repo]` → `args[1]="issue"`, `args[2]="view"`, `args[3]=str(num)`, `args[4]="--repo"`, `args[5]=repo`. Проверка `args[3].lstrip("-").isdigit()` = True. - Memory repo commit: `refactor: drop host segment from memory path` (или `fix(memory): drop host segment`) ## Инварианты - 22 memory файла не теряются (`git mv` = rename, git history сохранён) - Чистый разрыв со старым путём `repos/{host}/{org}/` (не fallback, полная миграция — ты мигрируешь полностью на новый path) - `pipeline-status` оракул находит memory files по новому пути после миграции - `spec-status` dispatch `gh issue view N` доходит до Forgejo API после фикса - `get_memory_files` glob `{repo}(-\d+)?.md` поддерживает ротацию (не кейс #238) - Внешние контракты tools НЕ меняются ## Граничные случаи - Репо с двумя remote (github.com + git.slaid098.dev) — `parse_remote_url` возвращает первый/настроенный, `org` одинаковый → работает - Memory files для других orgs (не `slaid098`) под `repos/github.com/` — не трогаем, мигрируем только `slaid098` - После миграции старый путь `repos/github.com/slaid098/` — пустой, `rmdir` если чисто (но оставить `repos/github.com/` если есть другие orgs) - `spec-status` если `--repo` отсутствует в argv — `args[3]` всё равно номер issue, `len(args) > 3` проверяет - `parse_remote_url` если host не определён (local repo без remote) — fallback на `org/repo` или ошибка ## Влияние на связанные компоненты - `pipeline-status.py:_resolve_memory_base` (~L405) — основная правка (убрать host) - `pipeline-status.py:get_memory_files` (L414-421) — без изменений (glob) - `pipeline-status.py:check_memory` (L702-723) — без изменений - `spec-status.py:153-156` — off-by-one фикc (2 строки) - `memory-syncer.md:45` — пример пути, инструкция парсинга remote - `memory/SKILL.md` — проверить path format упоминания - `tests/test_pipeline_status.py` — обновить тесты path (убрать host из expected paths), `test_check_memory_done_rotated_file`, `test_get_memory_files_excludes_non_rotation_suffix` - `tests/test_spec_status.py` — добавить/обновить тест `test_spec_status_issue_view_dispatch` - PR #10 MEMORY phase — разблокируется после merge (pipeline-status найдёт files по новому пути) - `/spec` pipeline — spec-phase заработает после merge ## Вне scope - Миграция других orgs (не slaid098) — не трогаем - insteadOf конфигурация git config — infra, не трогаем - Другие pre-existing баги в spec-status — только этот one off-by-one - PR #2 (skills), PR #3 (config), PR #4 (templates) — следующий после разблокировки - RAG индекс — перестраивается автоматически ## Критерии приемки - [ ] `git -C ~/.local/share/opencode/opencode-memory log --oneline -1` показывает commit про drop host segment - [ ] `ls ~/.local/share/opencode/opencode-memory/repos/slaid098/` — 22 файла (или ~22, включая ротации) - [ ] `ls ~/.local/share/opencode/opencode-memory/repos/github.com/slaid098/` — не существует или пусто - [ ] `rg "_resolve_memory_base" .opencode/scripts/pipeline-status.py` — path без `{host}` (только `{org}`) - [ ] `pipeline-status({pr_number: 10})` → MEMORY phase DONE (находит memory files по новому пути) - [ ] `rg "args\[2\]" .opencode/scripts/spec-status.py` — 0 совпадений в dispatch коде (заменено на `args[3]`) - [ ] `spec-status` для существующего issue → корректный ответ (dispatch доходит до Forgejo API) - [ ] `uv run pytest tests/test_pipeline_status.py tests/test_spec_status.py -x` → all passed - [ ] PR #10 `pipeline-status` → Status: COMPLETE (all 6 phases DONE)
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
slaid098/opencode-config#11
No description provided.