Skip to content

docs(architecture): the map matches the code that ships - #389

Merged
gololdf1sh merged 9 commits into
mainfrom
docs/103-architecture-matches-the-code
Sep 7, 2026
Merged

gololdf1sh merged 9 commits into
mainfrom
docs/103-architecture-matches-the-code

Conversation

@gololdf1sh

Copy link
Copy Markdown
Collaborator

What

docs/architecture.md is the first thing a new contributor reads, and large parts of it described a
codebase that no longer ships. This brings the whole file — §0 through §10 — back in line with
extension/ as it is today: the module map, the message table, the storage tables, the API legs, the
chrome.debugger section and the rakes. All 52 file:NNN references are replaced by a file plus the
function, const, key or message name at that spot, and the two rotting line counts (background.js (485 lines), run-view.js (717, …)) are gone.

Method

Every sentence that states something about the code was checked against the code in this worktree
before it was allowed to stay: the file was opened, the line read, and only then was the sentence
kept, corrected or replaced. Where the issue and the code disagreed, the code won and the
disagreement is reported below. Nothing was rewritten from memory of how such extensions usually
work, and nothing was kept because it sounded right. Three mechanical sweeps close the gaps a
sentence-by-sentence pass leaves: every `path/file.ext` in the document was resolved against
the filesystem, every `name()` against extension/, and every `#element-id` against the
two HTML documents. The document's voice, structure and opinions are untouched — it is still a map
of the present, and no correction is phrased as a changelog.

Corrections

The chrome.debugger claim (§1.1, §7, rake 10)

  • Doc: "a temporary attach → shot → detach, and the only chrome.debugger user left"; §7 titled
    "one screenshot at a time, and nothing else". → There are two users.
    screenrec/session.js srecStartCast() attaches and runs Page.startScreencast, holding the
    session for the whole take (up to the five-minute cap). §7 is retitled and rewritten around the
    difference: how long each attach stands.
  • Doc: "the infobar blinks for the length of a screenshot. It no longer stands for a whole
    recording." → It stands for the whole of a cast recording, and that bar's own Cancel is a
    chrome.debugger.onDetach the worker reads as a Stop that keeps the file
    (screenrec/session.js, the onDetach listener).
  • Not in the issue, and load-bearing: a screenshot taken while a cast owns the tab shares the
    standing attach instead of taking a second one — shootViaDebugger() awaits
    srecCastOwnsReady(tabId) and neither attaches nor detaches when the answer is yes
    (background.js, screenrec/session.js).
  • Rake 10 said the screenshot was "its only victim". Both debugger users hit it, and both now take
    the same cure first: foreignFramesOut() / foreignFramesBack() (background.js) detach the
    offending chrome-extension:// iframes, retry once, and put each one back at its own parent and
    next sibling.

The update path (§1.3)

  • Doc: "there is no update path (api.js has no updateTest)". → updateTest(id, attrs) is at
    extension/api.js and is exported. It PATCHes /api/v2/{project}/tests/{id} with any subset of
    the create payload, and the editor sends exactly {title, description, priority} — deliberately
    never suite_id, which would MOVE the test. Two callers, both save() in editor/editor.js: the
    ?test=<uid>&edit mode (a real edit screen the doc did not mention at all, opened by a pencil in
    editor/view.js's header), and the retry after a create whose upload leg failed, aimed at
    savedId so a half-written Save cannot leave two tests behind.

The three screen-recording storage keys (§5.2)

  • Added screenRec, screenRecFile and screenRecTarget — and a fourth the issue did not name,
    screenRecReviewKey, written in the same chrome.storage.session.set call as screenRecFile
    (screenrec/session.js srecFinish()). It is the one-shot token by which a framed
    screenrec/review.html proves the extension framed it and the page under test did not.

uploadEvidenceLog() (§3.2, §3.4)

  • Doc: uploadEvidenceLog() in screens/evidence.js. → EvidenceUpload.log(record) in
    screens/evidence-upload.js, with the formatting beside it as EvidenceFormat.buildTxt() in
    screens/evidence-format.js. Its caller is core/write-status.js, not a screen.

Six more corrections the issue did not ask for, found by reading

  • §8: "Replay goes back through writeStatus, so the env meta keys are collected at replay time,
    not frozen at click time." This is backwards. writeEnvMeta takes opts.replay and uses
    opts.envMeta — the environment snapshotted at the click and parked with the queue entry
    (core/write-status.js, screens/offline-queue.js queueEnqueue()). Collecting at drain time is
    precisely what the code avoids: it would describe whatever tab happens to be open hours later.
  • §6: "Auth | the raw account General token as Bearer". v2 authenticates with a project key
    the session mints on demand (v2Token() → GET /projects/{slug} → attributes.api-key), held in
    memory per boot and never typed. Two behaviours hang off that and were missing: a 401 on a minted
    key drops it, re-mints and replays once, and a 403 is corroborated by an independent read before it
    is believed (request(), projectIsReadonly()).
  • §6 had no mention of the read-only lockout at all, though every screen entry gates on it
    (readonlyGate(), core/state.js; Gates.applyReadonlyBlock(), core/gates.js). Added as §6.0.
  • §3.2a: "the Failure / Meta / Steps disclosures" — there are four, with Artifacts
    between them (renderSummaryArtifacts(), screens/test-summary.js). And "not refreshed by the
    tester's own marking" is no longer true: TestSummary.refresh(record) patches status/message
    into the prefetched detail and repaints, at no request cost (refreshResultSummary()).
  • §9 rake 2: the runs-list chip order is all|passed|failed|running|scheduled|terminated
    (RUN_FILTERS, screens/runs-list.js), not all|running|passed|failed|… — and the order is
    load-bearing, because Fit.filterChips() hides the rightmost first.
  • §3.5 closed with a warning about a needsReinject field in a stepRec shape comment. Neither the
    field nor the comment exists anywhere in extension/. Removed.

The module map (§1)

  • §1.1: importScripts listed three files; it takes twelve. background.js owns six subjects, not
    three — the file overlay, the OPEN_RUN intent, the staged-shot sweep and the presence
    registration were all unmentioned.
  • §1.2: "The 35 <script> tags at the foot of index.html" — there are 73, plus shared/theme.js
    in the <head>.
  • §1.2's core/ table had 7 rows for 19 files. Added nav-model, toast, gates, fit, format,
    status-icons, suite-tree, dialog, write-status, session-restore, open-run-intent, and
    corrected core/views.js, which no longer owns TAB_OF_VIEW, the toast, the status lines, the
    degraded banner or the two self-measuring rows.
  • §1.2's screens/ list named 11 files for 27. Rewritten around the seam the split follows: where a
    screen grew a subject of its own, that subject took a file.
  • §1.4's shared/ table had 17 rows for 33 files plus the API client. Added markdown, hovercard,
    dropdown, roving, panel-link, handoff, shot-store, step-rec-core, dbg-errors,
    fullpage-trim, presence-match, webm-duration and the three annot-* files, each with the
    realm that actually loads it (grep -rl per file, not inference).
  • §1.5 gained the four injected surfaces it was missing — content/rec-bar.js,
    content/review-overlay.js, content/file-overlay.js, and the step recorder's five-file inject —
    and the note that those last two are why web_accessible_resources exists at all.
  • The ASCII diagram is redrawn (same Unicode box style) around the realms that exist, with the three
    single-job extension pages named under it: offscreen/recorder.html, screenrec/review.html,
    viewer/viewer.html.

§2.1 message table — eleven messages added: OPEN_RUN, OPEN_FILE_OVERLAY, STEPREC_PULL,
STEPREC_FLUSH and the ten SCREENREC_* entries. captureTab's reply is the six-field one the
worker sends (viewportOnly, framesMoved, trimmed, heightClipped, needsGrant), and
STEPREC_STATUS is the injected pill's poll — which doubles as the orphan check — not the editor's.

§4 permissions — the manifest block was three permissions short (tabCapture, offscreen,
contextMenus), and activeTab was listed among the absences that cost nothing. It costs the screen
recording its good capture route: chrome.tabCapture hands over a stream only where the extension
was invoked on that tab, which is why the debugger fallback exists and why the context-menu item and
the Alt+Shift+R command matter.

§4.1 — resolveSiteTab gained a bound target (siteTarget in storage.session, written by
rememberTab()) and an activate option; an ok answer may now come viaTarget.

§4.3 — the debugger failure path is no longer "reject, with one exception": it detaches foreign
frames, retries, and only then falls back to the viewport shot. The FULLPAGE_MAX_HEIGHT clip and
its heightClipped flag were undocumented.

§5 storage — every chrome.storage.*.set under extension/ was grepped across all realms. §5.1
gained handoffDeclinedAt, stepRecIndicatorPos and screenRecBarPos; §5.2 gained the four
screenRec* keys, commentDrafts, siteTarget, openRunIntent, fileOverlay and
handoffOpenedAt. Existing rows were corrected: polishSteps is written by editor/rec-session.js
(not editor.js), the session shape carries runInfoOpen, the offlineQueue entry carries
reason/envMeta/prevStatus, and editorDraft: has a test: form as well as suite:.

§9 / §10 — rake 1's temporal-dead-zone example moved files (run-lock.js reading
test-view.js's stepWriteChain, plus a second one in core/gates.js); rake 6 names
syncPanelBehavior(); rake 8 is rewritten around the fact that nothing calls sanitizeHtml
directly any more — Md.render() in shared/markdown.js is the one path. §10's table points at the
files a change now lands in, including the four seams a status write crosses.

The issue vs the code

  • "the three screen recording keys screenRec, screenRecFile and screenRecTarget" — there
    are four. screenRecReviewKey is written in the same call as screenRecFile and is what stops an
    arbitrary page from acting on a framed review. Added.
  • "extension/screenrec/session.js attaches and detaches the debugger and runs
    Page.startScreencast"
    — true, but only on one of two routes. srecStart() prefers
    chrome.tabCapture.getMediaStreamId() and falls through to the cast only when that throws, i.e.
    on a tab with no activeTab grant. The doc says so, because "the recorder holds a debugger
    session" without that qualifier would send a reader looking for an attach that a granted tab never
    makes.
  • "Line number references are stale. Seven spot checks all landed on unrelated lines." — 52 of
    52 were stale, not seven of seven; the file has moved on since the issue was written too. All are
    replaced by names.
  • "uploadEvidenceLog() is put in screens/evidence.js. It moved." — confirmed, and the caller
    moved as well: it is reached from core/write-status.js, so §3.2's diagram named the wrong file
    on both sides of that arrow.
  • "Two paths the doc names do not exist at all: routes/launch.js, core/site-resume.js" —
    neither is a defect, and both are left in place. core/site-resume.js appears only in §4.2's
    Gone | Was table, which exists precisely to record deleted machinery; a file listed there is
    supposed not to exist. routes/launch.js is a citation of the product's own Ember app, not of
    this repo — the sentence already said "the web's", and now says so unmistakably.
  • The earlier review's discrepancy table for this document —
    P1-10 (two debugger users) confirmed and fixed. P2-18's line-drift rows are all superseded: its
    own numbers (876-line background.js, 42 script tags, core/views.js:528-544) are themselves
    stale, so none was used as a target; the lines were re-derived from the code and replaced by names.
    Its onboarding row is already fixed in the doc, and its P1-7 egress finding is fixed in the
    code — ApiAssets.fetchAsset now defaults instanceOnly: true, so §0's "single egress, no
    exceptions" rule stands as written and was kept. P2-7 (envInfoOnFail runs on every status write,
    not only a failure) is a key-name complaint about the code, not a false claim in the doc: §5.1
    lists the key, §3.2's diagram already shows writeEnvMeta running on every write, and only the
    Console & network log key is gated on failed. Left alone.

Second pass

Two verifiers read all 273 listed claims against the code, one per half of the document, and each half end to end for sentences the list missed: 258 confirmed outright. Their findings are folded in as two commits — in §0–§3.3, twelve importScripts (the text said eleven), an orphaned half-sentence, the label fit's real home (Fit.actionLabels() in core/fit.js), eleven empty-state call sites of which two switch glyph, the confirm dialog's fourth caller (the offline queue), EVIDENCE_EVENTS answering {off}, the five core/ files with no /* global */ list, eight lock callers, the review page's three trim-* commands, and the second Icons block, which contradicted the first about BOXES and is now folded into it; in §3.4–§10, theme written by set(), settings-erase.js never writing settings, the hostHistory writers named, framesOut only on the cast route, an empty or already-parked take parking nothing, any active-instance erase leaving the handoff mark, a project switch keeping the minted v2 keys, the fourth capabilities.jwt writer (resetProjectScopedState()), the buffer's own names (evBuf.push(), isError), six stack lines rather than frames, seven tags between run-lock.js and test-view.js (not fourteen), the viewport shot's debugger fallback, DevTools blocking the full-page shot only, and runsSearch reset on the URL-intent paths too.

Five tracker numbers the writer had introduced (#14, #62, #155, #158, #160) were removed: those references in this repository's code point at a different tracker, and the document carries none it did not already have.

Left alone

  • Citations of the product's server and web app — Testrun#add_step!, Run#calculate_counters,
    TestrunSerializer, RunSerializer, Api::TemplatesController#index, Test#to_url,
    extra-run-actions.hbs, rungroup.rb, routes/launch.js. These cannot be verified from this
    worktree. Four of them are corroborated by comments in extension/ that say "verified live"; the
    rest are kept as written, with their attribution made explicit where a reader might mistake them
    for files in this repo.
  • Prose about design intent — why a token exists, why a control is the size it is, why a
    decision was taken. Where such a passage carried a checkable fact (a token name, a pixel value, a
    count) the fact was checked; the reasoning around it is the document's own voice and was not
    restyled.
  • #tc-polish-btn is the one element id in the document with no match in either HTML file. It is
    correct: editor/editor.js builds that button at runtime.

The issue also proposed a CI check that would validate doc references against the code. The
maintainer decided against it, so no test was written
and none is included here.

Verified after the last commit: node --test tests/*.test.mjs → 3544 tests, 0 fail, 2 todo;
git status clean.

Closes #103

🤖 Generated with Claude Code

gololdf1sh and others added 9 commits September 7, 2026 13:39
§1's map, its diagram and its four tables were written before the refactor
epic split the panel, the API client and the editor, and before the screen
recorder existed. Every file named here now exists, and every file that
matters to the map is placed: the worker's twelve importScripts, the eleven
new core/ seams, the screens that grew a subject of their own, the six
api/*.js files, the shared/ files the worker and the offscreen page load, and
the four injected surfaces (rec-bar, review-overlay, file-overlay and the
step recorder's rec-* helpers). §1.3 gains the editor's ?test=&edit mode and
loses the claim that there is no update path.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ding

§2.1 gains the eleven messages the table never had — OPEN_RUN,
OPEN_FILE_OVERLAY, STEPREC_PULL/FLUSH and the whole SCREENREC_* family — and
captureTab's reply is the four-field one the worker actually sends. §3.2's
writer is core/write-status.js, not the test view, and the log it uploads is
EvidenceUpload.log(). §3.2b names run-lock.js and the nine files that ask it.
A new §3.6 describes the screen recording, including the debugger session its
fallback route holds for a whole take. The needsReinject note goes: neither
the field nor the comment it warned about exists.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
§4's manifest block was missing tabCapture, offscreen and contextMenus, and
listed activeTab among the permissions whose absence costs nothing — it costs
the screen recording its good capture route. §4.1 gains the bound site target
and the `activate` knob. §4.3 gains the foreign-frame rescue that runs before
the viewport fallback, and the height clip. §5.1 and §5.2 now list every key
any realm writes: the four screenRec* ones, commentDrafts, siteTarget,
openRunIntent, fileOverlay, the two handoff marks and the two dragged-control
positions — each with a "Written by" that resolves to a function.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…akes

§6 said v2 authenticates with the raw General token; it authenticates with a
per-project key the session mints on demand, and the 401 remint and the 403
corroboration are how that key is kept honest. A new §6.0 covers the read-only
tri-state (#155) the panel gates every screen on. §7 is retitled: there are two
debugger sessions, and the screen recording's cast route holds one for a whole
take — a screenshot taken during it shares that attach. §8's claim that a
replay collects env meta at replay time was backwards: each entry carries the
snapshot taken at the click. Rakes 1, 2, 6, 8 and 10 name what is there now,
and §10 points at the files a change actually lands in.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ht files

The result-summary card has four disclosures, not three, and it IS repainted by
the tester's own marking — patched, not re-read. §3.5's context packet, masking,
naming, pill, outbox and polish now name the six files they live in rather than
step-recorder.js and editor.js. The empty-state count is measured.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
CONTEXT_WEB_TARGET, evWindowEntries and evAdoptTwin no longer exist under
those names; the erase paths are SettingsErase.disconnect()/forget(). Every
backticked identifier in the file now resolves against extension/, except the
two the §4.2 "Gone" table exists to record.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…arry

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Twelve importScripts, not eleven; an orphaned half-sentence; the label
fit lives in core/fit.js; eleven empty-state call sites, two of them
glyph-switching; the confirm dialog's fourth caller; EVIDENCE_EVENTS
answers {off}; five core files carry no global list; eight lock callers;
the review page's trim commands; and the second Icons block no longer
contradicts the first about BOXES.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
theme is written by set(); settings-erase never writes settings; the
hostHistory writers named; framesOut only on the cast route; an empty or
already-parked take parks nothing; any active-instance erase leaves the
handoff mark; a project switch keeps the minted keys; four jwt writers;
the buffer's own names; six stack lines; seven tags and seven screens;
the viewport shot's debugger fallback; DevTools blocks full-page only;
runsSearch's URL-intent resets.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@gololdf1sh
gololdf1sh merged commit 38d63f2 into main Sep 7, 2026
1 check passed
@gololdf1sh
gololdf1sh deleted the docs/103-architecture-matches-the-code branch September 7, 2026 10:39
@gololdf1sh gololdf1sh self-assigned this Sep 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Refresh architecture.md: debugger users, update path, storage keys, stale refs

1 participant