Method · Changelog · 5 Sept

Every change, in the words it was made with

This is the repository’s own history, newest first, each entry the message its change was committed with and nothing added after the fact. The messages say what changed, why, and what they found wrong on the way, including in earlier entries. A figure quoted in one is the figure on the day it was written. A day to a page.

746 changes over 22 days since Monday 10 August 2026 · as held in the repository at 10 Sept 2026, 09:03 pm AWST, when this build was deployed. The defects that reached the database have their own register on the data-quality page.

Saturday 5 September 2026 · 68 changes

The re-issue record names the build that stands

10:39 pm AWST · d54c43d

A comparison across a coverage step says so beside its numbers

10:37 pm AWST · f106e2c

When cars watched at close moved a tenth or more between the windows, the comparison paragraph and the movement line say that exits and cuts move with coverage before they move with the market, and point to the cuts-per-100-cars rate. A cover tile no longer prints a change under a median stands in and says where it came from.

Record the second re-issue of the archive, on instruction

10:36 pm AWST · c5a511f

A report has a share card, the terminal's vitals move week on week, the quarterly has a clock

09:55 pm AWST · be8b1f9

/reports/<id>/opengraph-image: the period, the brief's first sentence and three figures with their change against the period before, the report's own numbers, which do not date. The home terminal's Median days to sell and Repriced this week tiles carry the 7 days to yesterday against the 7 before, per recorded day if the roll-up missed one. The quarterly generator, present since 0277, is scheduled: first issue 2026-10-01.

Record the reports work in the status table

09:52 pm AWST · da7fd86

The web report draws what the PDF draws

09:49 pm AWST · 29c5fc0

Two screen renderers join the chart kit, twins of the paper ones: period bars (one bar per window, hollow where not fully recorded, a tick before the series began) and the slopegraph. The report page's trend, its weeks within a month, and the state median ask against the period before are now kit charts with a spec, a name and a caveat on the chart, and the running total against the prior window's draws with the cumulative line the kit already had.

The comparison carries days to sell for both windows

09:43 pm AWST · 1b53ba8

Days to sell is the number the product is for, and the report's own turn figure had nothing to stand against. market_report_movement now carries cars, dated cars and the median days advertised at exit for this window and the one before, on the report's own rule; the comparison table has the row, the brief's last sentence names the prior median, the cover tile carries the change, and the web view prints days as days.

A month reads week by week, the share text leads with the reading, the terminal carries the movement

09:39 pm AWST · 182e001

A monthly or quarterly report sums its own daily series by UTC week and draws the weeks as bars, first and last weeks partial and said so; the share paragraph opens with the brief's first sentence where the payload carries a comparison; the home terminal's week-so-far panel ends with the per-recorded-day movement line. A comparison the windows cannot bear now states its one reason once, not once per row, and a sum over a billion prints as one.

A report reads against the period before it, and the archive is two archives

04:48 pm AWST · 7bcf0b6

PDF should read like the large advisory houses' market reads: good graphs, high-level data, what it means, the week-on-week and month-on-month difference.

market_report_movement gives every report its prior window --- the previous calendar month, or the same number of days before --- nationally, by state, by fuel and for the leading models, the last eight windows as a trend, and the prior window day by day. Every issue from today carries it; the four issued before read it live from the same append-only roll-ups. The national median used ask is written daily from today and never backfilled from a different measure.

One module, report-movement.ts, computes the reading for both surfaces: a window that recorded nothing is not compared and the report says why; flows are compared per recorded day when the windows differ and every column head or sentence says so; up to six takeaways with the count or basis inside each.

The PDF: a brief, a change under every headline figure, the comparison table, trend bars hollow where a window was not fully recorded, the running total against the prior window's, a state slopegraph of median ask, exhibit titles that state their message, and a "what it means" line where the figures bear one. The web view carries the same brief and comparison. /reports is a monthly archive and a weekly one, each row with its exits change and a PDF link; /reports/weekly and /reports/monthly carry the whole of each. Quarterly admitted ahead of October.

Five reasons a dealer's car stayed live after it had gone

04:48 pm AWST · 717efd9

The two freshness warnings decomposed. 8,344 of the 9,830 week-stale rows were marketplace rows inside their designed 14-day lapse, so check 6 now counts dealer rows and says so. The dealer remainder was five faults:

- the JSON-LD stock-page rule read `inline?.length ?? listingUrls.length` and an empty inline array made every stock page count zero other cars, so it had never fired on a jsonld site (`||` now); - iMotor sitemaps list /latest-news/<slug>/<id>/ beside the cars and the generic four-digit rule fetched them as cars and counted them as parser failures, refusing the soft-404 path; Extractor.isListingUrl is now authoritative for a platform that states its URL shape; - the server-rendered iMotor flavour fetches no detail page, so the health gate read 0/0 every run; twenty probes that read a car off its own page are now the measurement when the detail loop had none; - cars24 marks a sale only in a disabled button reading SOLD, a fourth sold-marker kind; - Sydney BMW moved host and its old sitemap child answers 500; Evergreen Auto answers every stock page with an empty 200 (source rows updated, Evergreen withdrawn with the reason).

First run of the fixed rule on Maddington Isuzu UTE: 45 dead cars served the stock page, 45 recorded as no longer listed, never as sold. DQ 55.

A coverage row says which kind of pass its counters came from

04:00 pm AWST · 44997cf

Every crawl run now records whether it was the two-hourly index pass or the twice-daily deep pass, on insert and for good, where before the kind lived in a note held only while the run was open. The coverage page's Found cell says which one its counters came from: a count of 21 from an index pass is one page of a yard, not a yard of 21 cars, which the coverage brief found misleading on 683 of 1,046 rows.

Seven functions added this week are closed to the browser roles

03:55 pm AWST · 35f5222

The integrity suite caught what the migrations missed: six new SQL functions and one recreated one were executable by anon and authenticated, the Postgres default a new signature starts from. Nothing in the browser calls them and the site reads through the service role, but the quota check writes a row per call, so the window --- under a day --- is on the register as entry 54. Every migration that creates a function ends with the revoke, and Operations now says so.

The value page's state medians are the subject's own year

03:50 pm AWST · 66ef075

Whole-model medians by state beside a 2019 car measured mix, not price: a state full of 2024 stock read as dear. Where eight or more cars of the subject's year price in at least two states, the rows are that year alone and say so; otherwise the whole model, and the copy says why.

Operations notes the auction house

03:42 pm AWST · 67782c0

A blank state fills in on a later read, like a blank suburb does

03:42 pm AWST · b904d91

The blanks-only back-fill covered dealer name, suburb, postcode and condition and not state, so six lots first read before the extractor knew where to look for one would have stayed stateless however many times they were read again. Blanks only, as before: a state once recorded is never overwritten, and only one of the eight is ever written.

A Perth lot knows it is in Perth

03:38 pm AWST · 241fca7

Six of the first nineteen lots had no state: their pickup field read "6 Spartan street Jandakot" and nothing more. The auction site's own address carries the state and postcode, so it is the second place the extractor looks, and a state named without a postcode still counts.

Slattery's lots are discovered newest first, and from the category page too

03:32 pm AWST · ce36556

The asset sitemaps list every lot the site has ever held, and a first pass spent forty detail fetches on closed ones. Discovery now orders lots by id descending, so the open lots lead the never-seen queue, and also reads the eighteen lot summaries the motor-vehicles category page server-renders. The category's other twenty-one pages paint in client-side and no search parameter pages them, so the sitemaps stay the full-coverage path.

The review table records the closed-lot rule

03:29 pm AWST · 6386ad7

A closed lot is the seller's own word that the car is gone

03:24 pm AWST · a7c4472

An auction lot whose closing time has passed, or that carries a sale price, is not offered for sale any more, and the page says so in its own columns rather than in a retitled heading. The extractor contract gains an optional soldMarker() the crawl consults before its generic reader, and Slattery's reports the close date, the sale price where one is stated and the bid count as a status marker. It is the only way a Slattery lot can ever leave the live corpus, because the asset sitemap keeps listing closed lots forever. An open lot (a 2019 Hilux closing 16 September) joins the fixtures; an odometer of zero is not a reading.

An extractor for Slattery Auctions, and why the other two are not built

03:19 pm AWST · d0bbd2a

Slattery's lot pages serialise the whole lot into the RSC payload --- make, model, year, odometer, VIN, registration, transmission, fuel, body, colour, the pickup site, and the auction's own figures --- and its sitemap index lists every lot. The extractor reads both. An auction lot is a car publicly offered for sale, so it is a listing; it is not an asking price, so a lot carries a price only where the seller named a buy-now figure and every bid travels in raw. Only motor-vehicle lots are cars; a forklift on the same page shape extracts to null. Proved on three captured fixtures in CI.

easyauto123 and Suttons are not built. easyauto123's grid is fetched by its own script through a Next.js server action keyed by an id inside the bundle; no public URL serves the stock and the sitemap its robots.txt names answers 404. Suttons paints its cards from a POST carrying an Elasticsearch query, and seven honest shapes of that request answered 500. Replaying an app's private RPC is not a public read path, so both stay out of scope, with the findings on their notes.

The blocked marketplaces are asked again every Monday, and coverage says what they said

02:39 pm AWST · b231f54

The coverage brief found the blocked-marketplace evidence three weeks old with no stated cadence. A weekly workflow now asks each one again exactly as the crawler asks --- robots first, the honest user agent, one polite request --- and writes the answer beside the survey note on /coverage. It never enables a source: a site that has started answering is recorded as a decision for a person. The first run found eight still blocking and seven answering at the base URL, which for the brochure sites and the robots-disallowed locators changes nothing.

State rows show their mix, views state their unit, and a yard sees its peers

02:33 pm AWST · f0474c9

The model brief's three remaining asks. The views figure says what it is --- the dealer site's own counter over its own listing age, per used car --- and shows the new and demo figures the board already held. Each state's used-ask median carries the median build year and odometer of the cars behind it, so a cheaper state reads as older, higher-km stock when that is what it is, rather than as a price gap.

And the dealer brief's first ask: the aged-stock block reads the yard's 90-day share against every yard with 20 or more dated used cars --- the median yard, the middle half, the market as a whole --- and says which quarter this yard sits in.

The sample says why its valuation_id is null

02:27 pm AWST · f7e71c1

The API page shows a real answer, and every answer names its limit

02:24 pm AWST · 9a4a7c0

The API brief asked for a real sample, units and basis in the response, and rate-limit headers. The response body lives in one module now, read by the route and by the page, which renders an actual answer for the request it prints --- computed as an internal check, stored and counted nowhere, cached an hour, two of the comparables shown and the rest counted. The body names its units and the range's basis. Every answer and every 429 carries RateLimit-Limit, -Remaining, -Reset and -Policy; the quota function returns the limit beside what is left.

A valuation with insufficient confidence withholds its figure

02:19 pm AWST · d17699b

A yellow headline with an "insufficient confidence" pill under it was still the figure a reader took away. At that label the page prints "withheld" where the figure was, states the confidence and its three parts, and gives the cohort's spread as evidence rather than as the car's value. The working below is unchanged; the buy price was already withheld at this label.

The buyer's most actionable fact reaches the home terminal

02:16 pm AWST · 4ffa427

The home brief ranked discounting against time on market eighth and found it only on /market: the share of live used cars discounted at least once, by how long they have been advertised, with n on every row. It sits under the most-traded table now, with the caveat the market page carries --- a discount says the car has been sitting, not that discounting fails to move it --- and the fuller table linked.

The yard's days to sell is a headline figure, and the turn heading is a sentence

02:12 pm AWST · 685834d

The dealer brief ranked the turn median third and found it undated in a four-number heading. It sits beside the three stock medians now with its own scope --- proved sales with a published age, the whole watch --- and the section it came from opens with one figure and one sentence that adds up: proved sold, gone without proof, still for sale elsewhere.

The listing states the seller's own figures beside ours

02:09 pm AWST · cfbe5f2

Listing age by the seller's counter, the was-price they display beside the ask, and their view counter, where the page carries them: the listing brief's negotiating facts, held in the raw capture and shown nowhere. Labelled as their claims, with the observation log beneath as ours.

The other listings' price basis reads as words

02:06 pm AWST · bdb7a6a

A listing shows the same car's other pages, and the depth panel matches the year

02:03 pm AWST · c104f06

The listing brief's highest-value addition: one VIN across seventeen pages carried a $1,000 spread and a buyer on any one of them could not know. Every listing page now shows the same vehicle's other listings, live or gone, with yard, state, status and ask, the live spread stated, and whether identity is the VIN or a fingerprint.

The value page's depth panel quoted the newest ANCAP rating and every model year's Green Vehicle Guide variants regardless of the car's year --- the brief's "misleads twice". It picks the rating whose manufactured window holds the build year and says so, or says plainly that the nearest rating is not the car's own; the Guide variants are the car's own model year where the Guide holds it, else the nearest year, named.

The segment table counts cars too, and coverage states the proof-of-sale mix

01:53 pm AWST · 9c58d9e

The by-segment table is rebuilt the way the make table was: cars, one per vehicle; used cars; the median used ask at one price each; the seller's own days advertised on the live used stock; cars proved sold in the last 28 days and their median days to sell from eight up; and the share each row owes to the Green Vehicle Guide, kept because the classification is the caveat. The fuel and ANCAP averages left with the old board: figures joined from public datasets that changed no decision.

/coverage now states the corpus-wide mix of how cars left --- the seller's own SOLD text, a dead page, or without proof --- which the audit's section 2 called the product's strongest fact and only /method carried.

The RBA strip goes, and the make table counts cars on the seller's clocks

01:48 pm AWST · 96bc9d8

Taj's second pass. The cash-rate strip leaves /reports with its fetch: the page itself said it claimed nothing about cars. The by-make table is rebuilt on a function that counts what the rest of the site counts --- cars, one per vehicle however many sites carry it; used cars; the median used ask at one price per car; days advertised from the seller's own listing age on the live used stock; cars proved sold in the last 28 days and their median days to sell, printed from eight up. Makes under 20 cars are left out, which is where the thirteen rows of taxonomy junk lived. The old board counted listings, blended every condition, took days from our own window and delistings from advertisements.

One pixel of overflow is still a scrollbar

07:53 am AWST · c7e124a

A 375px phone has 279px of panel: the day-by-day charts fit inside it

07:51 am AWST · d8b3e27

The day-by-day charts fit a phone without scrolling

07:48 am AWST · 593d0f7

A fortnight of bars and a step line do not need 480 or 560 pixels; at those floors the right third of each chart, and its last date label, sat off the edge of a phone behind a scroll. The home and model panels' charts fit a 375px screen now.

A level series draws on its own range, and says so

07:44 am AWST · a8eeccc

The model page's median-ask step drew a $44,000 level flat along the top of an axis from zero, with its tick labels clipped to ",000" --- the same fault the PDF's series line had on its first render. The web step line takes a baseline now: "zero" for a rate or a count, where the floor is the floor, and "range" for a level, where the axis runs over the level's own range with its floor printed so it is never a silent zoom. The label gutter is wide enough for a dollar figure. Proved on markup.

The review table records the model fortnight and the heat table's wrap

07:43 am AWST · ed05065

The model page draws its own fortnight

07:41 am AWST · fc31ec0

Everything on a model page was a cross-section of now. The daily model rollup has held one row per model per day since 11 August, and the page now draws it: cars proved out and cars cut by day as bars, and the median used ask held across each day as a step, over the fourteen days to the latest recorded day. A day the roll-up did not record is named, ticked on the baseline, and never drawn as a zero; a model too thin for the rollup renders nothing here rather than a chart of noise.

Two more paragraphs at half their length

07:38 am AWST · 5d78b23

The repricing panel's two-paragraph disclaimer becomes one, and the yard's note on body words loses its second explanation. Nothing the figures rest on is gone: the corpus growth figures, the fixed-set rule, the wagon-against-SUV count.

The heat table's sentence wraps to the panel, and a state not stated says so

07:35 am AWST · d51ca0a

Inside the scroll region the table's caption took the table's width and clipped on a phone; it is a paragraph above the region now, with the same sentence kept as the table's caption for a screen reader. The report's turn matrix labels an unknown state "not stated" as the site does elsewhere, and its headline carries the dates as words.

Grand Prix resolves from the box: the fallback's first pass found 21 listings

07:34 am AWST · d4987ac

The first review, recorded against what was done with it

07:33 am AWST · e0334ab

The week panel's specs are built whole, not spread from a partial

07:29 am AWST · 23520aa

5b61be1 went to main with type errors --- a spread over the spec's discriminated union, and a series narrowed in one scope and read in another --- and the deploy gate refused it, which is what it is for. Each chart's spec is now written out in full and the series row type is named from the payload.

The home terminal draws the week it is in

07:28 am AWST · 5b61be1

The daily series has existed since 0277 and the home page never drew it: the vitals said what the week added up to and nothing showed its shape. A panel under the vitals carries two charts from the kit --- cars that left the market and cars cut, by UTC day of the running week --- read from the same query the weekly report runs, so the terminal and the report cannot disagree about the week. A day the roll-up did not record is named in the caption and drawn as an absence, never a zero. The days- to-sell tile's caption is shorter on a phone.

Eleven more paragraphs at two thirds of their length

07:25 am AWST · 2319892

The dealer page, the home page, coverage and the movers: every paragraph over sixty words rewritten shorter with its dates, counts and caveats intact. What went was the second sentence that explained the first.

The market page's seven longest paragraphs say the same things in fewer words

07:22 am AWST · 42cb15c

Taj's review: too many words. The seven paragraphs over sixty words on /market are rewritten at roughly two thirds of their length, every date, count, threshold and caveat kept --- the 10% withholding rule, the not-stated rows, the one-decimal shares, the six-hourly board against the daily capture --- and the sentences that explained a sentence removed.

A section's heading is bound to its first child, and the table rows no longer overprint

07:18 am AWST · 305ca69

The presence hint on a heading left it stranded a third time, and a presence hint wrapped around the day-by-day table made its unbreakable head block overprint its own rows at a page foot. Hints are gone: a section keeps its heading and first child in one unbreakable block, and the table keeps its head with its first two rows the same way.

A table head travels with its first rows, a heading with its exhibit

07:15 am AWST · ce4159d

Read back: the day-by-day table's head still stranded at a page foot and a section heading sat alone above the break, because a presence hint on a single node does not bind it to what follows. The head and the first two rows are one unbreakable block now, a heading asks for the height of an exhibit beneath it, the state "UNKNOWN" prints as "not stated" as it does on the site, and the coverage paragraph starts with a capital.

The report is one flow under a running masthead, not four pages with holes

07:12 am AWST · 4d7e1d0

The redesigned report broke its body into hard pages, so a table head stranded at the foot of one page over nothing and two pages ran mostly empty. The body is one flow now, paginated by the layout under a fixed masthead and footer; a table's head stays with its first rows; the day-by-day table travels with its note; and the models chart has room for a model's name, which was clipping to "subishi Outlander".

The models table was 522pt wide in a 503pt column, and the deploy refused it

07:08 am AWST · 7dad127

The previous commit went to main with its PDF test failing --- the chain that built it was gated on the type check and not on the test --- and the deploy gate did its job and refused to stamp. The table's widths are trimmed to 484pt; the assertion that caught it stays.

The report carries what the tables held, and looks like Spotlot's

07:07 am AWST · 1115f11

Taj's review: the first PDFs were thin, unbranded and chartless. Three sections the daily tables already held and the report never carried join the payload (additive: the previous series function keeps its body under a new name and the public name wraps it): the twelve models with most proved exits over the period, each with its live stock, median used ask, days advertised and repricing; electric share of stated-powertrain cars by state at the period's start and close; and days to sell by state and segment over the period's proved exits, the printed-number heat table. The four reports issued before this keep their shape, as issued.

The document opens on the site's own ground: a black cover carrying the period's six figures in the brand yellow, so it is recognisable face-down on a desk; the pages inside are paper. They carry the cars-live line, the exits and cuts bars side by side, the models as bars and as a table, the electric-share table, the turn distribution and the heat table, each titled as a sentence with its unit, scope and source. The web report view carries the same three new sections.

The four boxes are four again, the door says sign up, the phone gets a menu, and Method loses half its words

06:59 am AWST · 93ec4f1

Taj's review, first pass. The home vitals had lost their fourth box to the cut list and read as three and a hole on a phone; Valuations made is back in it, said his way. The sign-in page's way in for someone without an account was a footnote reading "create one"; it is a full-width door under the form now, "Sign up for free", with the dealer-access route in the line beneath. On a phone the seven links, the pulse and the dealer door wrapped to three rows above the fold and pushed the product below it; they fold behind one Menu control, a details element that needs no script, and the pulse stays visible because it is the site's one-line claim. Method said everything twice: 2,333 words become 1,138, every definition and every live figure kept.

And the crawl box's resolver: DATA-QUALITY 52 named the cause as ENOTFOUND for one dealer's zone on the box while public resolvers and every laptop answer. The fetcher asks 1.1.1.1 and 8.8.8.8 only when the system resolver says a name does not exist, runs the same SSRF guard on whatever comes back, and says so in the log once per host. Any other failure is returned as it came.

Where the vocabulary sweep stands, stated rather than left open

04:29 am AWST · 78cfd18

The status row said the rest of section 9's surfaces remained. What remains is twenty-four stat-sized figures across eleven files that carry their qualifiers as prose rather than through the Figure component, and the lint passes on every one. That is a consistency refactor and not an honesty gap, and the row now says so instead of implying work the site's claims do not need.

A car's name wraps on a phone rather than losing its end to an ellipsis

04:27 am AWST · 6fe9de2

The two remaining lines of the accessibility checklist, checked on the live market page: of 39 targets, 36 are under 24px in one dimension and none has another target within 24px, which is the spacing exception the rule allows; and under the text-spacing override nothing overflows and nothing visible clips except the car names in the movers' flex rows, which truncated with an ellipsis. They wrap below the sm breakpoint now and truncate only where a row has the width to carry them.

The briefs' cut lists, taken

04:24 am AWST · de432f8

Each surface's brief ranked what it showed by whether a reader acts on it and named what to cut. Taken today: home loses Most watched (one platform's counter over n of 10 to 27, with a caption apologising for the average), Marked down hardest (dealers' own strike-throughs, mostly from before we were watching) and the car-by-car cuts tape, and its valuations count leaves the headline tiles for the coverage sentence where a usage figure belongs. The model page loses the by-model-year table, which the ladder answers with quartiles. The market page loses the live-cars-by- state bars, which duplicated the movement table's first column with a 21-car discrepancy and a caption explaining it, and the half-row of navigation prose beside them; one line does both jobs. Coverage loses the Delisted column, one pass's tally on what is usually an index probe. The movers' colour table becomes the sentence it was always working toward. The dealer page keeps its make numbers and drops the bars, moves the mix below the lot, and folds the two photo figures into the build note as a sentence. The listing's raw-field count moves from the headline to the log's footnote. The report's tiles lead with cuts against rises, the valuations tile goes, listings-monitored lives in the corpus paragraph where it already sat with its date, and the dollar total sits under the cars it belongs to, with its basis.

Not taken, and why, in docs/UPLIFT.md: the RBA strip, the two-prices panel, and the by-make table, which wants rebuilding rather than cutting. Also carried: the PDF's basis sentence no longer starts a sentence in lower case.

The first issued pages, read back: a false sentence and an axis from zero

04:15 am AWST · 3913209

Reading the four PDFs the route had just issued found two things wrong. A report from before the daily series existed said "0 of 7 days recorded" on its cover and "every day due in this period recorded" on the next page --- a count of nothing printed as a count, and a claim about days it never held. The cover now says the issue predates the series and carries none, and the day-by-day section says only that the series is withheld and why. The coverage paragraph also ran two sentences together.

The series line ran its axis from zero, so a level of 150,000 cars that moved by 6,000 across the week drew as a flat line along the top of an empty plot --- the one thing the chart was for, hidden by the one rule that is usually right. A level series takes its own range now, with the gridlines carrying their values at the left, because a floor that is not zero is honest only when it is printed.

The four archive rows are the route's own smoke issuance from minutes before, in no reader's hands; docs/OPERATIONS.md records that they were deleted and re-issued from this build, and that this is the one time the archive has been rewritten.

The market report as a document, issued once and kept as issued

04:10 am AWST · ce4befa

The reporting engine's missing third: a document model, the report drawn on it, and an issuer. lib/pdf-doc.tsx holds the idioms the statement had grown locally --- masthead, cover figures, numbered sections that keep their heading with the start of their body, exhibits titled as a sentence with unit and scope beneath and the source under, tables whose rows never split and whose widths are asserted against the text column at build, a running footer carrying the issue date and build sha, a contents list, and a Withheld line that prints the reason where a figure would have been. The first render of the day-by-day table was 516pt wide in a 503pt column, which is the assertion doing the job it was written for.

lib/report-pdf.tsx draws the weekly and the monthly on that model from the same payload the web page renders, so the two cannot disagree: a day that recorded nothing is a tick on the baseline and a name in the prose, never a line across it; sold is split by proof; every count says listings or cars; what the period cannot support is withheld with its reason.

/reports/[id]/pdf is the issuer. The first request for an issued report's PDF renders it, hashes it, stores it in a private bucket and writes the market_report_archive row; every request after streams the stored bytes with the sha in a header, whatever the code now says, and two writers racing for the same first issue are settled by the row's primary key. The running week renders live under a PRELIMINARY chip and is never stored.

And the engine runs under plain Node. The kit "could not be exercised outside a bundler" because react-pdf's text layout requires one subpath its dependency exports for import only; scripts/pdf-resolve-hook.cjs maps that family of specifiers to the files they name and nothing else, and scripts/test-report-pdf.ts renders a weekly, a monthly, a report issued before the series existed and the preliminary from a fixture, in CI and before every deploy.

The tag goes on the gated view too, which is the one the market shows

03:59 am AWST · 9f6d0ff

d0b6a37 put the evidence tag under the three panels' drawn headings and not their gated ones, and the market cohort stands below the first rung on all three, so the site showed no tag at all. Six headings now carry see what rung it stands at and what it is waiting for.

Every figure on the terminal reads beneath its rung

03:52 am AWST · d0b6a37

The market terminal's three week-against-week panels carry an evidence panel defines, then the measure the next rung needs and what the cohort has. It is the rung frame's sentence without the frame, because a table of figures is not a chart and a dashed border around it said nothing; and it is driven by the ladder's own evaluation, not a second list of thresholds the page keeps for itself. A cohort the ladder has not evaluated says so rather than wearing rung 0 as if it had been measured.

The deploy runs the checks CI runs, before it stamps

03:50 am AWST · 5ea3d47

a9af92c named the gap: the figure lint, the contrast and chart lints and the kit's tests ran in CI and nowhere on the path to production, so a deploy could put on the site what CI would fail a minute later --- and did. `npm run check` is the one list, CI and the deploy script both run it, and the deploy refuses before the stamp rather than after the upload.

A time, a sha and a day's count are not statistics

03:49 am AWST · a9af92c

check-figures failed the changelog page in CI: three .figure spans with no qualifier --- a commit's time of day, its sha, and the count beside each day in the index. The first two are not figures at all and now use tabular numerals without the class; the count gains its word, "7 changes", which is what a reader needed anyway. The build had passed locally because the lint runs in CI and the deploy script, not in `npm run build`, which is a gap this commit does not close.

A public changelog: the repository's history, a day to a page

03:46 am AWST · 5fa7d39

/changelog shows every commit as it was written, newest first, the message unedited but for the machine trailers, snapshotted into the build at deploy time the way the sha and the data-quality register already travel. The committed module is empty; a build made without the repository behind it says so rather than showing nothing.

A curated changelog is a second account of the work, and a second account drifts. The messages in this repository were written as prose for exactly this reason: each says what changed and why, and names what it found wrong, including in earlier entries. The page is a day to a page because a week of this repository rendered to 1.8 MB of HTML; the index lists the days by ISO week in Perth time with the count on each. The week and trailer rules are proved on planted input in CI.

The two renamed tables the previous commit described

03:37 am AWST · b80bb90

1f5973f's message said the market's segment table and the folded tail of its make table had stopped wearing another table's name. They had not: the script carrying those two edits failed on a quoting error before it ran, and the chain went on to commit and deploy the eleven captions that had already landed. This commit is the two it described: the segment table is announced as live listings by segment, the tail as the remaining brands, each with its caption and scroll region, and the market page's eight tables all carry one now.

Every table on the site says what it holds, and two more stop wearing another's name

03:30 am AWST · 1f5973f

The remaining thirteen tables gain a caption stating unit, n, date and basis, and a scroll region a keyboard can reach: the market's eight, the movers' two, the ticker's comparables and model years, the group's locations, and the report's day-by-day series and its states. The sweep found two more tables announced under a different table's name --- the market's segment table as "same car, two prices" and the folded tail of its make table as "demo against new" --- which is what a name typed by hand rather than read from the visible heading was always going to do.

The last chart drawn by hand, and six tables that could not say what they held

03:23 am AWST · c63b978

The rates strip is the kit's step line now: a monthly series held across the month it describes and never interpolated, a hit target per month with its own readout, the axis topped by the first round number above the maximum so the top of the plot is a figure a reader can check. No chart on the site is drawn outside the kit.

Six tables gain a caption stating unit, n, date and basis, and a scrolling region a keyboard can reach with a focus ring of its own: the valuation's comparables, a listing's observation log, coverage's sources, the yard's branches, the statement's models moved, and standing stock by age. The statement's table had carried the name of a different table entirely --- "your cars by how long each has been advertised" over a list of models moved --- and takes its own heading now.

Inside a yellow panel, everything focusable outlines in ink. The rule had covered the scroll region only, and the checklist's open defect --- a yellow ring on a yellow ground, 1.00:1 --- stood for every link and button in those panels until now.

The value page's two charts draw from the kit

03:18 am AWST · f3e028d

The comparables scatter and the age ladder, the last two forms drawn by hand, now come from the kit's geometry and renderers. The scatter gained what a valuation needs of it: the subject car enters the domain as an extra value, so one sitting beyond its comparables' odometers is placed rather than clipped; the trend is drawn dashed, because a line read off the cloud is not a series and should not look like one; and the crosshair and subject mark are an overlay in the plot's own units, so they can never drift from the marks they annotate. The ladder takes a frame, so the value panel's tighter pitch and gutters are the page's choice rather than a second copy of the drawing.

Two kinds join the spec: a ladder describes itself as a ladder and a range bar as a range bar, where the ticker's had called themselves bucket bars and a point series. Every check on the page's markup is a real one this went, and a string is always true.

The model ticker's four charts draw from the kit

03:09 am AWST · 1e8dab2

The cloud, the book, the vintage ladder and the range bar: the forms the model page is built around, now computed once in the kit's geometry and drawn by its renderer. What each keeps, by test: every mark on the cloud carries its readout, identity, odometer and sync percentage so the interaction layer --- readout, keyboard, the one admitted brush, the range marker --- reads them off the marks exactly as before; the fit runs across the odometers observed and is clamped to the plot box; ticks come from a scale function and never repeat on a narrow cohort; the book picks out the shelf below each boundary and says so in the readout; the ladder's axis spans the drawn marks only, so a tick-only year's quartiles cannot stretch it, and every such year still has a hit target; and the range bar keeps the inset the synced marker is placed in. The sections around them keep their own headlines and copy, so the frame draws bare and the spec speaks only to a screen reader.

Grand Prix's failure has a name: the box cannot resolve the host

03:05 am AWST · b9ce92f

The first cause the crawler recorded is getaddrinfo ENOTFOUND for www.grandprixautogroup.com.au. Public resolvers and a laptop resolve it and the site answers; the box's resolver does not. A box-side DNS fault on one zone's CNAME chain, recorded in DATA-QUALITY 52 and the source's notes; the fix belongs on the box.