# ADR-023: parse_remote_url strips git insteadOf userinfo from HTTPS URLs ## Статус Accepted (2026-07-24) ## Контекст PR#53 (ADR-022) добавил `git config --global url."https://x-access-token:${GITHUB_TOKEN}@github.com/".insteadOf https://github.com/` в `setup-memory.sh` для non-interactive HTTPS auth в Docker. После этого `git remote get-url origin` возвращает rewritten URL с встроенным userinfo: ``` https://x-access-token:github_pat_...@github.com/slaid098/opencode-config.git ``` `parse_remote_url` (в `pipeline-status.py` и `spec-status.py`) использовал regex `https?://([^/]+)/([^/]+)/(.+?)(?:\.git)?$` — группа `[^/]+` жадно матчит всё между `://` и первым `/`, включая `user:password@` (символ `@` не исключён). В результате `host` = `x-access-token:github_pat_...@github.com` вместо `github.com`. Это ломало `get_memory_file_path()` → путь `repos/x-access-token:...@github.com/slaid098/opencode-config.md` (не существует) → `check_memory` возвращал NOT_DONE для каждого PR после PR#53. `get_repo_full_name()` случайно не ломался (возвращает `org/repo`, host отбрасывается), но фикс применён в обоих файлах для консистентности. CI на PR#53 не поймал regression — тесты `test_parse_remote_url_*` покрывали только plain URLs без userinfo. ## Решение Изменить HTTPS-regex в `parse_remote_url` (оба файла) на: ```python re.match(r"https?://(?:[^/@]*@)?([^/]+)/([^/]+)/(.+?)(?:\.git)?$", url) ``` `(?:[^/@]*@)?` — опциональная non-capturing группа, пропускающая `user:password@` userinfo перед host: - Для plain `https://github.com/...` группа не матчится (нет `@`) → host = `github.com`. - Для `https://user:token@github.com/...` группа матчит `user:token@` и отбрасывается → host = `github.com`. - `[^/@]` внутри userinfo не матчит ни `/` ни `@`, поэтому корректно останавливается на первом `@` (поддерживает `user:pass`, `user`, `token@` формы). 7 новых тестов: 4 в `test_pipeline_status.py` (parse + get_memory_file_path), 3 в `test_spec_status.py` (parse + get_repo_full_name). ## Альтернативы 1. **Использовать `git config --get remote.origin.url`** вместо `git remote get-url origin` — bypasses insteadOf, возвращает raw URL. Отклонено: coupling с git internals, ломается если кто-то задаст remote URL с token напрямую (не через insteadOf). 2. **Strip userinfo через `url.split("@")[-1]`** — хрупкий: ломается если path содержит `@` (legitimate в GitHub URLs для определённых refs/paths). 3. **Рефакторинг `parse_remote_url` в shared module** — оба файла дублируют функцию. Отклонено для этого PR: риск для ADR-010 cwd-aware `_resolve_repo_root` logic, выходит за рамки bugfix. 4. **Не чинить, отключить insteadOf** — отклонено: insteadOf нужен для non-interactive memory clone (PR#53/ADR-022), regression в pipeline_status — меньшее из зол. Regex fix (выбран) — минимальный, root-cause, без env coupling, покрывает оба caller'а.