phase: 129_sync_chunk_progress
All green. Final report:
**Phase 129 final verification pass — 100% green** (previous work intact; this pass re-verified everything and fixed two pre-existing README inconsistencies)
- Verified on-spec: `LLMClient.embed` per-batch `on_progress` hook; importer `on_chunk_progress` plumbing (text/image/PDF branches; summary embed excluded); `SyncStatus.current_chunk/chunks_total` reset+clear contract; UI `.sync-label-chunk` pinned part with untruncated `title`/`#sync-result`
- `uv run pytest --cov=app -q`: exit 0, ~3041 passed, 0 failed, `app/` coverage **99%** (>90% gate)
- `uv run pytest tests/e2e/test_sync_chunk_progress.py -v --no-cov` (isolated, DB up): **2 passed** (endpoint chunk climb with true `chunk_document` total + UI `· chunk n/m` part)
- `uv run ruff check .`: all checks passed · `uv run pyright`: 0 errors, 0 warnings
- Completion criteria: all 5 ✅ (incl. no behavior change in completed phases — full suite green, CLI/chat no-op paths byte-identical)
- Docs: README.md fixed 2 pre-existing inconsistencies (upload section claimed "unpacks + scans immediately" — phase 90 made it unpack+register only, scan deferred to Sync; troubleshooting claimed "120s client timeout" — actual guard is the 300 s pre-token `TURN_TIMEOUT_MS`). Phase-129 label behavior already documented. AGENTS.md re-verified consistent — no changes
- Removed stray untracked `NOTES.md` (8-byte artifact); no live-infra changes; no commits — all changes left in working tree for the harness
- Next pending phase: `130_chat_answer_clusters`
This commit is contained in:
@@ -63,12 +63,39 @@ visible span carries main + count, the space is the CSS gap). Every
|
||||
other label state (idle "Sync sources", settle "Synced HH:MM", the
|
||||
202/409 click, the re-attach) goes through setSyncLabel(main) —
|
||||
count hidden.
|
||||
|
||||
Phase 129 (task 04) adds the label's THIRD pinned part — the chunk
|
||||
position: #sync-label ships a hidden .sync-label-chunk span after the
|
||||
count span (the pinned "· chunk n/m" — the in-flight file's
|
||||
embed-batch position, batch granularity: n the 1-based first chunk of
|
||||
the in-flight batch, m the file's total content chunks). The ONLY
|
||||
builder that reads the status's chunk fields is fmtSyncPhaseLabel's
|
||||
import fall-through — "· chunk n/m" renders while chunks_total > 0
|
||||
and hides in every other state (the prelude, the 202/409 click, the
|
||||
upload run — its status has no chunk fields, overview, summaries —
|
||||
its own (n/m) is its position, idle / settle). setSyncLabel(mainText,
|
||||
countText, chunkText) lands the part under the same present/absent
|
||||
contract as the count part, and the UNTRUNCATED full label — main +
|
||||
" " + count + " " + chunk — rides the button title + #sync-result
|
||||
(A4 — the in-flight import reads "Syncing… <file> (2/3) · chunk
|
||||
14/38"). The part keeps the pinned mono CSS (flex 0 0 auto, nowrap,
|
||||
--mono, the rule right after .sync-label-count). A node harness (the
|
||||
test-document-viewer pattern) behaviorally pins the rendered parts +
|
||||
the full title across every label state.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
#: The node harness needs node (the test-document-viewer pattern).
|
||||
HAVE_NODE = shutil.which("node") is not None
|
||||
|
||||
FRONTEND = Path(__file__).resolve().parents[2] / "frontend"
|
||||
SOURCES_JS = FRONTEND / "assets" / "sources.js"
|
||||
STYLES_CSS = FRONTEND / "assets" / "styles.css"
|
||||
@@ -219,21 +246,26 @@ def test_fmt_sync_label_returns_the_parts_contract() -> None:
|
||||
|
||||
|
||||
def test_set_sync_label_is_the_single_parts_writer() -> None:
|
||||
"""setSyncLabel(mainText, countText = "") — Phase 128 (task 01):
|
||||
the SINGLE point that writes #sync-label. The MAIN part gets
|
||||
mainText (textContent only — the house rule, no HTML strings);
|
||||
the COUNT part gets countText when present (textContent +
|
||||
hidden=false — the "(n/m)" position is visible, NEVER clipped) and
|
||||
is emptied + hidden=true when absent (the count span is hidden
|
||||
whenever there is no "(n/m)" to show). Then the UNTRUNCATED full
|
||||
label — mainText + (countText ? " " + countText : "") — rides the
|
||||
button title + #sync-result (the aria-live announcer) exactly as
|
||||
before (A4 — the screen reader hears the whole thing)."""
|
||||
"""setSyncLabel(mainText, countText = "", chunkText = "") — Phase
|
||||
128 (task 01) + Phase 129 (task 04): the SINGLE point that writes
|
||||
#sync-label. The MAIN part gets mainText (textContent only — the
|
||||
house rule, no HTML strings); the COUNT part gets countText when
|
||||
present (textContent + hidden=false — the "(n/m)" position is
|
||||
visible, NEVER clipped) and is emptied + hidden=true when absent;
|
||||
the CHUNK part (phase 129) gets chunkText under the SAME contract
|
||||
(present → the pinned "· chunk n/m" is visible, absent → emptied
|
||||
+ hidden=true — the part renders only while a file's embed batch
|
||||
is in flight). Then the UNTRUNCATED full label — mainText +
|
||||
(countText ? " " + countText : "") + (chunkText ? " " + chunkText
|
||||
: "") — rides the button title + #sync-result (the aria-live
|
||||
announcer) (A4 — the screen reader hears the whole "Syncing…
|
||||
<file> (2/3) · chunk 14/38")."""
|
||||
body = _fn(_js(), "setSyncLabel")
|
||||
assert 'function setSyncLabel(mainText, countText = "")' in body
|
||||
assert 'function setSyncLabel(mainText, countText = "", chunkText = "")' in body
|
||||
# the parts: textContent only (the house rule), null-guarded refs
|
||||
assert "syncLabelMain.textContent = mainText" in body
|
||||
assert "syncLabelCount.textContent = countText" in body
|
||||
assert "syncLabelChunk.textContent = chunkText" in body
|
||||
# present count → visible; absent count → hidden, emptied
|
||||
assert "syncLabelCount.hidden = false" in body
|
||||
assert 'syncLabelCount.textContent = ""' in body
|
||||
@@ -241,11 +273,22 @@ def test_set_sync_label_is_the_single_parts_writer() -> None:
|
||||
i_vis = body.find("syncLabelCount.hidden = false")
|
||||
i_hid = body.find("syncLabelCount.hidden = true")
|
||||
assert -1 < i_vis < i_hid, "the present-count branch precedes the absent one"
|
||||
# present chunk → visible; absent chunk → hidden, emptied (same contract)
|
||||
assert "syncLabelChunk.hidden = false" in body
|
||||
assert 'syncLabelChunk.textContent = ""' in body
|
||||
assert "syncLabelChunk.hidden = true" in body
|
||||
i_cvis = body.find("syncLabelChunk.hidden = false")
|
||||
i_chid = body.find("syncLabelChunk.hidden = true")
|
||||
assert -1 < i_cvis < i_chid, "the present-chunk branch precedes the absent one"
|
||||
# A4: the untruncated full label rides the title + the announcer —
|
||||
# main + " " + count (the join space), bare main when no count.
|
||||
# main + " " + count + " " + chunk (the join spaces = the CSS
|
||||
# gaps), bare main when both are absent.
|
||||
assert (
|
||||
'const full = countText ? `${mainText} ${countText}` : mainText'
|
||||
) in body, "the full label is main + ' ' + count (bare main when absent)"
|
||||
"const full =\n"
|
||||
" mainText +\n"
|
||||
" (countText ? ` ${countText}` : \"\") +\n"
|
||||
" (chunkText ? ` ${chunkText}` : \"\");"
|
||||
) in body, ("the full label is main + ' ' + count + ' ' + chunk (spaces = the CSS gaps)")
|
||||
i_full = body.find("const full")
|
||||
i_title = body.find("syncBtn.title = full")
|
||||
i_result = body.find("syncResult.textContent = full")
|
||||
@@ -281,8 +324,11 @@ def test_fmt_sync_phase_label_is_the_exact_d2_parts_contract() -> None:
|
||||
assert "current_summary" not in ov and "summaries_done" not in ov, (
|
||||
"the overview label carries no folder and no counts"
|
||||
)
|
||||
# "summaries": the exact template — optional folder, counts always.
|
||||
i_fall = body.find("return fmtSyncLabel")
|
||||
# "summaries": the exact template — optional folder, counts always,
|
||||
# NO chunk part (the summaries phase's own (n/m) is its position —
|
||||
# the status's chunk fields are cleared at this transition, so the
|
||||
# branch never reads them).
|
||||
i_fall = body.find("fmtSyncLabel(")
|
||||
assert i_sum < i_fall, "the summaries branch precedes the fall-through"
|
||||
sum_ = body[i_sum:i_fall]
|
||||
assert 'status.current_summary ? ` ${status.current_summary}` : ""' in sum_, (
|
||||
@@ -295,12 +341,40 @@ def test_fmt_sync_phase_label_is_the_exact_d2_parts_contract() -> None:
|
||||
"count: `(${status.summaries_done}/${status.summaries_total})`"
|
||||
) in sum_, "the count part is ALWAYS emitted in the summaries phase"
|
||||
assert "total > 0" not in sum_, ("no conditional — the hook always carries a total")
|
||||
assert 'chunk: ""' in ov, "the overview label carries no chunk part"
|
||||
assert 'chunk: ""' in sum_, (
|
||||
"the summaries label carries no chunk part (its (n/m) is its position)"
|
||||
)
|
||||
assert "chunks_total" not in ov, "the overview branch never reads the chunk fields"
|
||||
assert "chunks_total" not in sum_, "the summaries branch never reads the chunk fields"
|
||||
# anything else (null prelude, "import"): byte-identical to today's
|
||||
# sync label — same builder, same fields, same order.
|
||||
# sync label — same builder, same fields, same order — plus the
|
||||
# phase-129 chunk part: "· chunk n/m" ONLY while chunks_total > 0
|
||||
# (a file's embed batch is in flight — n the 1-based first chunk
|
||||
# of the batch, m the file's total content chunks, batch
|
||||
# granularity), "" otherwise (0/0 before the first batch — the
|
||||
# part hides). The fall-through is the ONLY branch that reads the
|
||||
# status's chunk fields.
|
||||
i_destr = body.find("const { main, count } = fmtSyncLabel(")
|
||||
assert -1 < i_destr < i_fall, "the fall-through destructures the phase-64 parts"
|
||||
fall = body[i_destr:]
|
||||
assert (
|
||||
'return fmtSyncLabel("sync", status.current_file, '
|
||||
"status.files_done, status.files_total);"
|
||||
) in body[i_fall:]
|
||||
'const { main, count } = fmtSyncLabel(\n'
|
||||
' "sync", status.current_file, status.files_done, status.files_total\n'
|
||||
" );"
|
||||
) in fall, "the phase-64 parts builder, unchanged"
|
||||
assert "status.chunks_total > 0" in fall, (
|
||||
"the chunk part renders only while a batch is in flight"
|
||||
)
|
||||
assert "`· chunk ${status.current_chunk}/${status.chunks_total}`" in fall
|
||||
i_guard = fall.find("status.chunks_total > 0")
|
||||
i_else = fall.find(": \"\";", i_guard)
|
||||
assert -1 < i_guard < i_else, "0/0 (before the first batch) → the empty chunk part"
|
||||
assert "return { main, count, chunk }" in fall
|
||||
# the bare builder (the upload job + the 202/409 click) returns NO
|
||||
# chunk part — those labels stay bare.
|
||||
bare = _fn(_js(), "fmtSyncLabel")
|
||||
assert "chunk" not in bare, "fmtSyncLabel stays chunk-free (upload + the click stay bare)"
|
||||
|
||||
|
||||
def test_enter_running_state_is_phase_aware_and_writes_the_parts() -> None:
|
||||
@@ -312,8 +386,10 @@ def test_enter_running_state_is_phase_aware_and_writes_the_parts() -> None:
|
||||
endpoint has no phase) and the 202/409 click keep the bare
|
||||
fmtSyncLabel parts, byte-identical. Phase 128 (task 01): the entry
|
||||
itself no longer writes the label — it hands the parts to the
|
||||
SINGLE writer (setSyncLabel(main, count)), which lands the main
|
||||
part, the pinned count part (hidden when absent), and the FULL
|
||||
SINGLE writer (setSyncLabel(main, count, chunk) — phase 129:
|
||||
the chunk part destructures with a "" default), which lands the
|
||||
main part, the pinned count part (hidden when absent), the pinned
|
||||
chunk part (phase 129 — hidden when absent), and the FULL
|
||||
untruncated label on the button title + #sync-result (A4 — set
|
||||
for EVERY running label; CSS ellipsizes the main part only)."""
|
||||
body = _fn(_js(), "enterSyncRunningState")
|
||||
@@ -332,8 +408,14 @@ def test_enter_running_state_is_phase_aware_and_writes_the_parts() -> None:
|
||||
i_fall = body.find("fmtSyncLabel(kind, currentFile, done, total)")
|
||||
assert 'kind === "sync" && status' in body
|
||||
assert -1 < i_phase < i_fall, "the phase label precedes the fall-through"
|
||||
# the parts are handed to the single writer (A4 lives in it).
|
||||
i_set = body.find("setSyncLabel(main, count)")
|
||||
# the parts are handed to the single writer (A4 lives in it) —
|
||||
# phase 129: the chunk part destructures with a "" default (the
|
||||
# bare builders — the upload job, the 202/409 click — return no
|
||||
# chunk part, so the part stays hidden there).
|
||||
assert "const { main, count, chunk = \"\" } =" in body, (
|
||||
"the chunk part destructures with the '' default (bare builders return none)"
|
||||
)
|
||||
i_set = body.find("setSyncLabel(main, count, chunk)")
|
||||
assert -1 < i_fall < i_set, "the parts are written after the split"
|
||||
assert body.count("setSyncLabel(") == 1, "one writer call per entry"
|
||||
assert "syncBtn.title =" not in body, ("the title write lives in setSyncLabel (A4)")
|
||||
@@ -369,6 +451,211 @@ def test_settle_and_idle_labels_go_through_the_parts_writer() -> None:
|
||||
assert "syncLabel.textContent" not in js, ("the parts writer is the single entry point")
|
||||
|
||||
|
||||
# =====================================================================
|
||||
# Phase 129 (task 04) — the label's third part: the chunk position
|
||||
# =====================================================================
|
||||
|
||||
|
||||
def test_sync_label_markup_ships_the_hidden_chunk_part() -> None:
|
||||
"""#sync-label (the shell's RAG view) ships the phase-129 THIRD
|
||||
part after the count span — the pinned "· chunk n/m" span
|
||||
(.sync-label-chunk), hidden + empty by default (the same ship
|
||||
contract as the count span: no chunk position at idle — the part
|
||||
renders only while a file's embed batch is in flight)."""
|
||||
html = _html()
|
||||
btn = html[html.find('id="sync-btn"') : html.find("</button>", html.find('id="sync-btn"'))]
|
||||
label = re.search(
|
||||
r'<span\b[^>]*id="sync-label"[^>]*>'
|
||||
r'<span class="sync-label-main">Sync sources</span>'
|
||||
r'<span class="sync-label-count" hidden></span>'
|
||||
r'<span class="sync-label-chunk" hidden></span>'
|
||||
r"</span>",
|
||||
btn,
|
||||
)
|
||||
assert label, (
|
||||
"the #sync-label parts markup (main + hidden count + hidden chunk) is missing"
|
||||
)
|
||||
|
||||
|
||||
def test_sync_label_chunk_css_is_the_pinned_part() -> None:
|
||||
""".sync-label-chunk (phase 129 task 04): the same pinned-part
|
||||
language as .sync-label-count — flex 0 0 auto (NEVER clipped),
|
||||
white-space nowrap, the --mono font (a position reads as data) —
|
||||
and it sits right AFTER the count rule (both mono parts read as
|
||||
data)."""
|
||||
css = _css()
|
||||
count_block = re.search(r"\.sync-label-count\s*\{", css)
|
||||
chunk_block = re.search(r"\.sync-label-chunk\s*\{([^}]*)\}", css)
|
||||
assert chunk_block, "styles.css must style .sync-label-chunk"
|
||||
for prop in ("flex: 0 0 auto", "white-space: nowrap", "font-family: var(--mono)"):
|
||||
assert prop in chunk_block.group(1), f".sync-label-chunk must carry {prop!r}"
|
||||
assert count_block and count_block.start() < chunk_block.start(), (
|
||||
"the chunk rule sits right after the count rule"
|
||||
)
|
||||
|
||||
|
||||
#: The node harness script (the test-document-viewer pattern): the
|
||||
#: REAL extracted label functions run against stub element refs, and
|
||||
#: every label state is snapshotted as a JSON row —
|
||||
#: [tag, title, announcer, main, count, countHidden, chunk, chunkHidden].
|
||||
#: No JS template literals in the script body (plain strings only —
|
||||
#: the script is assembled by concatenation, no placeholders).
|
||||
_CHUNK_NODE_HEAD = """
|
||||
const syncLabelMain = { textContent: "" };
|
||||
const syncLabelCount = { textContent: "", hidden: true };
|
||||
const syncLabelChunk = { textContent: "", hidden: true };
|
||||
const syncResult = { textContent: "" };
|
||||
const syncBtn = {
|
||||
disabled: false,
|
||||
title: "",
|
||||
attrs: {},
|
||||
setAttribute(k, v) { this.attrs[k] = v; },
|
||||
removeAttribute(k) { delete this.attrs[k]; },
|
||||
classList: { add() {}, remove() {} },
|
||||
};
|
||||
const syncIcon = { classList: { add() {}, remove() {} } };
|
||||
"""
|
||||
|
||||
_CHUNK_NODE_TAIL = """
|
||||
const show = (tag) => {
|
||||
console.log(JSON.stringify([
|
||||
tag, syncBtn.title, syncResult.textContent,
|
||||
syncLabelMain.textContent,
|
||||
syncLabelCount.textContent, syncLabelCount.hidden,
|
||||
syncLabelChunk.textContent, syncLabelChunk.hidden,
|
||||
]));
|
||||
};
|
||||
// 1. import, batch in flight (the task's fixture values, 14/38):
|
||||
enterSyncRunningState("sync", "src/a/b.md", 2, 3, {
|
||||
phase: "import", current_file: "src/a/b.md", files_done: 2, files_total: 3,
|
||||
current_chunk: 14, chunks_total: 38,
|
||||
});
|
||||
show("import-inflight");
|
||||
// 2. import before the first batch (0/0) — the chunk part hides:
|
||||
enterSyncRunningState("sync", "src/a/b.md", 2, 3, {
|
||||
phase: "import", current_file: "src/a/b.md", files_done: 2, files_total: 3,
|
||||
current_chunk: 0, chunks_total: 0,
|
||||
});
|
||||
show("import-prebatch");
|
||||
// 3. the prelude (null phase, no file yet):
|
||||
enterSyncRunningState("sync", null, 0, 0, {
|
||||
phase: null, current_file: null, files_done: 0, files_total: 0,
|
||||
current_chunk: 0, chunks_total: 0,
|
||||
});
|
||||
show("prelude");
|
||||
// 4. the 202/409 click (no status yet):
|
||||
enterSyncRunningState("sync", null, 0, 0);
|
||||
show("click");
|
||||
// 5. the upload run (no status — its endpoint has no chunk fields, D2):
|
||||
enterSyncRunningState("upload", null, 0, 0);
|
||||
show("upload");
|
||||
// 6. overview (the chunk fields are cleared at this transition):
|
||||
enterSyncRunningState("sync", null, 4, 4, {
|
||||
phase: "overview", current_file: null, files_done: 4, files_total: 4,
|
||||
current_chunk: 0, chunks_total: 0,
|
||||
});
|
||||
show("overview");
|
||||
// 7. summaries (the phase's own (n/m) is its position):
|
||||
enterSyncRunningState("sync", null, 4, 4, {
|
||||
phase: "summaries", current_summary: "src/alpha", files_done: 4, files_total: 4,
|
||||
summaries_done: 2, summaries_total: 3, current_chunk: 0, chunks_total: 0,
|
||||
});
|
||||
show("summaries");
|
||||
// 8. the idle settle goes through the writer with no chunk text:
|
||||
setSyncLabel("Sync sources");
|
||||
show("idle");
|
||||
"""
|
||||
|
||||
|
||||
def _run_node(script: str) -> str:
|
||||
proc = subprocess.run(["node", "-e", script], capture_output=True, text=True, timeout=60)
|
||||
assert proc.returncode == 0, f"node failed: {proc.stderr}"
|
||||
return proc.stdout
|
||||
|
||||
|
||||
@pytest.mark.skipif(not HAVE_NODE, reason="node not available")
|
||||
def test_chunk_part_renders_only_in_the_import_phase_under_node() -> None:
|
||||
"""Behavioral check (node) of the REAL label path — the extracted
|
||||
fmtSyncLabel / fmtSyncPhaseLabel / setSyncLabel /
|
||||
enterSyncRunningState run against stub element refs (the
|
||||
test-document-viewer pattern). The pinned "· chunk n/m" part (the
|
||||
task's fixture values):
|
||||
|
||||
* import, batch in flight (chunks_total 38 > 0): the chunk part
|
||||
shows "· chunk 14/38" and the UNTRUNCATED title + #sync-result
|
||||
read exactly "Syncing… src/a/b.md (2/3) · chunk 14/38" (A4);
|
||||
* import before the first batch (0/0): the chunk part hides and
|
||||
the label stays "Syncing… src/a/b.md (2/3)";
|
||||
* every OTHER state — prelude (null phase), the 202/409 click (no
|
||||
status), the upload run (no status, D2), overview, summaries (its
|
||||
own (n/m) is its position), the idle settle — leaves the chunk
|
||||
part hidden + empty.
|
||||
"""
|
||||
js = _js()
|
||||
fns = "\n".join(
|
||||
_fn(js, name)
|
||||
for name in ("fmtSyncLabel", "fmtSyncPhaseLabel", "setSyncLabel", "enterSyncRunningState")
|
||||
)
|
||||
script = _CHUNK_NODE_HEAD + fns + _CHUNK_NODE_TAIL
|
||||
rows = [json.loads(line) for line in _run_node(script).splitlines() if line]
|
||||
by_tag = {row[0]: row for row in rows}
|
||||
# row = [tag, title, announcer, main, count, countHidden, chunk, chunkHidden]
|
||||
|
||||
# 1. the in-flight import — the chunk part renders, the full
|
||||
# untruncated label rides the title + the announcer (A4).
|
||||
row = by_tag["import-inflight"]
|
||||
assert row[3] == "Syncing… src/a/b.md", row
|
||||
assert row[4] == "(2/3)" and row[5] is False, row
|
||||
assert row[6] == "· chunk 14/38" and row[7] is False, (
|
||||
"the chunk part shows the batch position (1-based first chunk / file total)"
|
||||
)
|
||||
assert row[1] == "Syncing… src/a/b.md (2/3) · chunk 14/38", row[1]
|
||||
assert row[2] == row[1], "the announcer hears the whole thing (A4)"
|
||||
|
||||
# 2. the import before the first batch (0/0) — the part hides, the
|
||||
# label is the byte-identical phase-64 string.
|
||||
row = by_tag["import-prebatch"]
|
||||
assert row[1] == "Syncing… src/a/b.md (2/3)", row[1]
|
||||
assert row[2] == row[1]
|
||||
assert row[6] == "" and row[7] is True, row
|
||||
|
||||
# 3. every other state — the chunk part is hidden + empty, and the
|
||||
# title is the byte-identical pre-phase-129 label for that state.
|
||||
for tag, expected_title in (
|
||||
("prelude", "Syncing…"),
|
||||
("click", "Syncing…"),
|
||||
("upload", "Importing"),
|
||||
("overview", "Writing KB overview…"),
|
||||
("summaries", "Summarizing folders… src/alpha (2/3)"),
|
||||
("idle", "Sync sources"),
|
||||
):
|
||||
row = by_tag[tag]
|
||||
assert row[1] == expected_title, (tag, row)
|
||||
assert row[2] == row[1], (tag, row)
|
||||
assert row[6] == "" and row[7] is True, f"{tag} must hide the chunk part"
|
||||
|
||||
|
||||
def test_every_other_state_passes_no_chunk_text() -> None:
|
||||
"""Source-level pin of the render-only-while-chunks_total>0
|
||||
contract: the import fall-through is the ONLY place in sources.js
|
||||
that reads the status's chunk fields (the summaries/overview
|
||||
branches and the bare builders never touch them), and every
|
||||
NON-running label emission passes no chunk text (the part hides):
|
||||
the settle/idle labels go through setSyncLabel(label) — the only
|
||||
three-arg call site is the running entry."""
|
||||
js = _js()
|
||||
body = _fn(js, "fmtSyncPhaseLabel")
|
||||
i_fall = body.find("fmtSyncLabel(")
|
||||
pre = body[:i_fall]
|
||||
assert "chunks_total" not in pre and "current_chunk" not in pre, (
|
||||
"the overview/summaries branches never read the chunk fields"
|
||||
)
|
||||
fall = body[i_fall:]
|
||||
assert "status.chunks_total" in fall and "status.current_chunk" in fall
|
||||
assert js.count("setSyncLabel(main, count, chunk)") == 1
|
||||
assert "setSyncLabel(label)" in js, "the settle/idle labels stay two-arg (no chunk)"
|
||||
|
||||
|
||||
# ---------- the two-job tick (startSyncPolling) ----------
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user