---
pr: 55
title: fix parse_remote_url breaking with git insteadOf userinfo
---

# PR 55: fix parse_remote_url breaking with git insteadOf userinfo

## Что сделано
- `.opencode/scripts/pipeline-status.py:171` — в `parse_remote_url` HTTPS-regex изменён с `r"https?://([^/]+)/([^/]+)/(.+?)(?:\.git)?$"` на `r"https?://(?:[^/@]*@)?([^/]+)/([^/]+)/(.+?)(?:\.git)?$"`. Опциональная non-capturing группа `(?:[^/@]*@)?` пропускает `user:password@` userinfo перед host, не ломая plain-URL случай (группа опциональна).
- `.opencode/scripts/spec-status.py:129` — тот же regex fix (функция `parse_remote_url` дублирована в spec-status.py, используется в Phase 8 `gh issue view --repo`).
- `tests/test_pipeline_status.py` — 4 новых теста:
  - `test_parse_remote_url_https_with_userinfo` — URL `https://x-access-token:github_pat_TOKEN@github.com/slaid098/opencode-config.git` → `("github.com", "slaid098", "opencode-config")`.
  - `test_parse_remote_url_https_without_userinfo` — plain URL работает (regression guard).
  - `test_parse_remote_url_https_userinfo_no_git_suffix` — userinfo + нет `.git` suffix.
  - `test_get_memory_file_path_with_userinfo` — `get_memory_file_path()` строит `repos/github.com/slaid098/opencode-config.md` (без `x-access-token`/`github_pat_TOKEN` в пути).
- `tests/test_spec_status.py` — 3 новых теста:
  - `test_parse_remote_url_https_with_userinfo` — same URL → correct tuple.
  - `test_parse_remote_url_https_without_userinfo` — regression guard.
  - `test_get_repo_full_name_with_userinfo` — `get_repo_full_name()` → `slaid098/opencode-config` (не `x-access-token:...@github.com/...`).
- ADR-023 + этот handoff.

## Почему
PR#53 (ADR-022) добавил `git config --global url.insteadOf` в `setup-memory.sh` для non-interactive HTTPS auth при клонировании memory repo в Docker. После этого `git remote get-url origin` возвращает rewritten URL с встроенным userinfo: `https://x-access-token:TOKEN@github.com/slaid098/opencode-config.git`. Старый regex `[^/]+` жадно матчит `x-access-token:TOKEN@github.com` как host (символ `@` не входит в исключения `[^/]`), поэтому:
- `get_memory_file_path()` строил путь `repos/x-access-token:TOKEN@github.com/slaid098/opencode-config.md` (не существует) → `check_memory` в pipeline-status возвращал ❌ "memory file не существует" для ВСЕХ будущих PR.
- `get_repo_full_name()` в spec-status случайно работал (возвращает `org/repo`, host отбрасывается), но фикс всё равно применён для консистентности и на случай future callers.

Это латентная регрессия — влияет на каждый pipeline_status вызов после PR#53, пока insteadOf активен. Тесты `test_parse_remote_url_*` покрывали только plain URLs, поэтому CI на PR#53 не поймал regression.

Fix выбрал минимальный — regex-only, без env coupling (вариант "использовать `git config --get remote.origin.url` вместо `git remote get-url`" ломается если кто-то задаст remote URL с token напрямую; вариант `url.split('@')[-1]` хрупкий при `@` в path).

## Pending
— (нет)

## Watch out
- **`(?:[^/@]*@)?` опциональна** — для plain `https://github.com/...` группа не матчится (нет `@`), host = `github.com`. Для `https://user:token@github.com/...` группа матчит `user:token@` и отбрасывается (non-capturing), host = `github.com`. `[^/@]` внутри userinfo не матчит ни `/` ни `@`, поэтому корректно останавливается на первом `@`.
- **Дублирование `parse_remote_url`** — функция идентична в `pipeline-status.py` и `spec-status.py`. Fix применён в обоих. Рефакторинг в shared module выходит за рамки этого PR (риск для ADR-010 cwd-aware logic).
- **ADR number = 023** (sequential, следующий после 022), НЕ PR number.
- Существующие 362 теста не сломаны — полный suite: 369 passed (362 + 7 новых).
- **PR number в filename** — issue #54 → PR #55. Handoff/ADR первично scaffold'нуты как `pr-54-*` (по issue number), после `gh pr create` переименованы в `pr-55-*` (по PR number, по конвенции репо).