phase: 135_git_source_branch
All verification complete. No edits were needed by this pass — the phase work and docs were already consistent and green. **Phase 135 final verification pass — report** - Verified all 5 tasks' work present & correct: migration 0024 (reversible), `GitSource.branch` + 4 schema shapes, `validate_branch_name` (full check order per design), `clone_or_pull(…, branch)` (HEAD guard first, `--branch` clone, fetch+ff merge), both call sites pass `row.branch`, UI (add-form field, editor field, row tag), `file://` URL transport. - Completion criteria: (1) branch-mirrored through Sync + CLI — **pass** (E2E + integration); (2) `branch=None` byte-identical — **pass** (`test_branch_none_argv_is_byte_identical_pre_phase_sequence` green); (3) fixed 422s / local-row 422 / tri-state — **pass**; (4) mismatch fails loudly with remove-and-re-add — **pass** (E2E); (5) coverage 99% >90%, E2E isolated green — **pass**; (6) no regressions — **pass**. - Tests: `uv run pytest --cov=app --cov-report=term-missing` → 3223 passed, app/ 99%; `uv run pytest tests/e2e/test_git_source_branch.py -v --no-cov` → 4 passed (16.4 s); `uv run ruff check .` clean; `uv run pyright` → 0 errors, 0 warnings. - Documentation: README.md reviewed in full — phase-135 branch section accurate; the phase's README diff also correctly fixed a pre-existing stale line (shallow `--depth 1` clone vs full-history, phase 107). Routes, env defaults, feature lists verified against code. AGENTS.md — all 10 rules + quick-reference verified (referenced tests, PLAN.md, phases dirs, commands all accurate). **Verified consistent — no changes needed.** - No live-infrastructure changes (dev DB already running and healthy; no live steps in this phase). - Next pending phase: `.agents/phases/todo/136_force_push_reset`.
This commit is contained in:
@@ -174,6 +174,7 @@ def _out_kwargs() -> dict:
|
||||
"added_at": None,
|
||||
"ignore_paths": [],
|
||||
"include_hidden": False,
|
||||
"branch": None,
|
||||
}
|
||||
|
||||
|
||||
@@ -186,6 +187,7 @@ def _row_kwargs() -> dict:
|
||||
"added_at": None,
|
||||
"ignore_paths": [],
|
||||
"include_hidden": False,
|
||||
"branch": None,
|
||||
}
|
||||
|
||||
|
||||
@@ -464,8 +466,19 @@ STYLES_CSS = FRONTEND / "assets" / "styles.css"
|
||||
|
||||
ADD_TOKEN_PLACEHOLDER = "ghp_… or another PAT"
|
||||
EDIT_TOKEN_PLACEHOLDER = "leave blank to keep the current token"
|
||||
ADD_BODY_EXPR = "(url, token) => ({ url, ...(token ? { token } : {}) })"
|
||||
PATCH_BODY_EXPR = "JSON.stringify({ ignore_paths: lines, ...(token ? { token } : {}) })"
|
||||
#: Phase 135 (task 04) widened the body to the branch pin — the token
|
||||
#: key keeps its conditional shape (the contract under test) and the
|
||||
#: branch key (git rows always — "" = the tri-state's clear leg) lands
|
||||
#: first (test_git_source_branch_ui.py pins the branch contract).
|
||||
ADD_BODY_EXPR = (
|
||||
"(url, token, branch) => ({ url, ...(branch ? { branch } : {}), "
|
||||
"...(token ? { token } : {}) })"
|
||||
)
|
||||
#: The phase-121 paragraph of the module docstring records the body AS
|
||||
#: IT WAS in phase 121 (a historical record — the phase-135 paragraph
|
||||
#: records the widened expression, pinned by test_git_source_branch_ui.py).
|
||||
ADD_BODY_EXPR_PHASE_121 = "(url, token) => ({ url, ...(token ? { token } : {}) })"
|
||||
PATCH_BODY_EXPR = "...(token ? { token } : {})"
|
||||
|
||||
|
||||
def _read(path: Path) -> str:
|
||||
@@ -526,20 +539,22 @@ def test_add_form_token_field_is_masked_and_optional() -> None:
|
||||
|
||||
|
||||
def test_add_form_submit_omits_a_blank_token() -> None:
|
||||
"""The submit body is `(url, token) => ({ url, ...(token ?
|
||||
{ token } : {}) })` — a BLANK token omits the key (None = no
|
||||
credential); wireAddForm reads the masked field and passes it to
|
||||
the body callback, and 201 clears BOTH inputs (the credential is
|
||||
stored — write-only)."""
|
||||
"""The submit body is `(url, token, branch) => ({ url, ...(branch ?
|
||||
{ branch } : {}), ...(token ? { token } : {}) })` — a BLANK token
|
||||
omits the key (None = no credential; phase 135 added the branch
|
||||
key, which also omits when blank); wireAddForm reads the masked
|
||||
field and passes it to the body callback, and 201 clears ALL THREE
|
||||
credential-free inputs (the credential is stored — write-only)."""
|
||||
js = _js_text()
|
||||
assert f"body: {ADD_BODY_EXPR}" in js, "the submit body omits the key when blank"
|
||||
wire = _fn(js, "wireAddForm")
|
||||
read_i = wire.find('tokenInput ? tokenInput.value.trim() : ""')
|
||||
call_i = wire.find("opts.body(value, token)", read_i)
|
||||
call_i = wire.find("opts.body(value, token, branch)", read_i)
|
||||
assert -1 < read_i < call_i, "the token is read from the field and passed to the body"
|
||||
clear_url_i = wire.find('input.value = ""')
|
||||
clear_tok_i = wire.find("tokenInput.value = \"\"", clear_url_i)
|
||||
assert -1 < clear_url_i < clear_tok_i, "201: both inputs clear"
|
||||
clear_br_i = wire.find("branchInput.value = \"\"", clear_tok_i)
|
||||
assert -1 < clear_url_i < clear_tok_i < clear_br_i, "201: all three inputs clear"
|
||||
|
||||
|
||||
def test_editor_token_field_is_masked_and_blanks_to_keep() -> None:
|
||||
@@ -570,15 +585,23 @@ def test_editor_token_field_is_masked_and_blanks_to_keep() -> None:
|
||||
|
||||
def test_editor_patch_omits_a_blank_token() -> None:
|
||||
"""saveIgnorePaths: the token is read AFTER the line parse and the
|
||||
PATCH body is `{ ignore_paths: lines, ...(token ? { token } : {}) }`
|
||||
— a BLANK token omits the key (the task-01 tri-state: absent = no
|
||||
change, the row's stored credential is kept)."""
|
||||
PATCH body keeps the token as the CONDITIONAL key
|
||||
(`...(token ? { token } : {})` — a BLANK token omits the key, the
|
||||
task-01 tri-state: absent = no change, the row's stored credential
|
||||
is kept). Phase 135 (task 04) widens the body with the branch key
|
||||
(`...(isGitRow ? { branch } : {})` — ALWAYS for git rows: an empty
|
||||
save is the deliberate clear leg, unlike the token's blank = keep)
|
||||
landing before the token key — the token's semantics are untouched
|
||||
(the branch contract is pinned by test_git_source_branch_ui.py)."""
|
||||
js = _js_text()
|
||||
save = _fn(js, "saveIgnorePaths")
|
||||
drop_i = save.find(".filter(Boolean)")
|
||||
read_i = save.find('ignoreTokenInput ? ignoreTokenInput.value.trim() : ""', drop_i)
|
||||
body_i = save.find(PATCH_BODY_EXPR, read_i)
|
||||
assert -1 < drop_i < read_i < body_i, ("blank token → key omitted (tri-state no-change)")
|
||||
branch_i = save.find("...(isGitRow ? { branch } : {})", read_i)
|
||||
body_i = save.find(PATCH_BODY_EXPR, branch_i)
|
||||
assert -1 < drop_i < read_i < branch_i < body_i, (
|
||||
"blank token → key omitted (tri-state no-change; the branch key lands first)"
|
||||
)
|
||||
|
||||
|
||||
def test_display_sites_render_the_server_bare_url_only() -> None:
|
||||
@@ -609,7 +632,7 @@ def test_module_docstring_carries_the_phase_121_token_contract() -> None:
|
||||
"#git-source-token",
|
||||
"type=password",
|
||||
"optional — private repos",
|
||||
ADD_BODY_EXPR,
|
||||
ADD_BODY_EXPR_PHASE_121,
|
||||
"leave blank to keep the current token",
|
||||
"sanitize_url",
|
||||
):
|
||||
@@ -625,7 +648,9 @@ def test_token_field_css_rules_are_house_styled() -> None:
|
||||
width at the 44px floor; the mobile media query groups it in the
|
||||
same min-width:0 rule as the URL input."""
|
||||
css = _read(STYLES_CSS)
|
||||
grouped = css.find("#git-source-url,\n#git-source-token {")
|
||||
# Phase 135 (task 04): the branch input joined the group — the
|
||||
# token input still shares the URL input's box.
|
||||
grouped = css.find("#git-source-url,\n#git-source-branch,\n#git-source-token {")
|
||||
assert grouped != -1, "#git-source-token shares #git-source-url's box"
|
||||
rule = css[grouped : css.find("}", grouped)]
|
||||
assert "min-height: 44px" in rule and "var(--mono)" in rule
|
||||
@@ -637,5 +662,5 @@ def test_token_field_css_rules_are_house_styled() -> None:
|
||||
assert tok != -1, "the editor's token field rule"
|
||||
tok_rule = css[tok : css.find("}", tok)]
|
||||
assert "width: 100%" in tok_rule and "min-height: 44px" in tok_rule
|
||||
mobile = css.find("#git-source-url,\n #git-source-token,")
|
||||
mobile = css.find("#git-source-url,\n #git-source-branch,\n #git-source-token,")
|
||||
assert mobile != -1, "the mobile squeeze groups the token input"
|
||||
|
||||
Reference in New Issue
Block a user