A3 Fix List

Team Relay · CIS*3250 A3 report · due tonight, Wed Oct 28, 11:59 PM · updated Oct 28, 9:30 PM (checked against the 9:26 PM PDF)

0need the section's owner
0Gurmehar is fixing tonight
0fixed and checked in the PDF

How to read this. The number on each person's button is how many items they still have to do. Items marked Owner must do are new analysis, new evidence, or your own Agent Appendix, so only you can write them. Gurmehar applied the corrections (wrong line numbers, typos, sentences the code contradicts) and, in some sections, one to five new sentences; each is quoted word for word under your name in "Handled by Gurmehar" and tagged Fixed. You don't need to do anything for those, but check you're happy with the wording. The report's Limitations of AI Assistance paragraph declares them. Priority: Critical loses a mark or is an integrity issue, Important a grader checking the code would catch, Polish if there's time. Every item was checked against the code (Meridian e95dc51, jq jq-1.7.1) and recorded runs of Meridian. Ticks save in your own browser only.

Decide together first

These affect several sections at once.

  1. How MRD-166 gets fixed, and when. Valerie says bind parameters are impossible with the sqlite3 command line, Sonia says MRD-166 must wait for MRD-77, and Gurmehar's tests expect the old routes to answer 400 like the newer ones. The code supports one answer: fix MRD-166 first with the checks the newer handlers already use (Validate.requireStation and QueryBuilder.whereEq, as SummaryHandler.java:60-61 does), and do MRD-77 later. Suggested order: baseline, MRD-166, MRD-247, MRD-249, MRD-248, MRD-77. (Sonia's order of work now matches.)
  2. One location per ticket. Jenna's locations are corrected; Sonia's section should cite the same lines.
  3. Where MRD-249's station data moves to. Suggested: a configuration file read through Env.java. It stays inside our subsystem and matches tests T13 and T24.
  4. Part 2's main claim. "Agent-era Meridian code has no warning flags" is contradicted by our own Part 1: Dispatcher.java:30-34, written in the same 2026 commit (a0f1aef) as the cache bug, says it is "knowingly a second way of doing what Router already does". Suggested wording: the 2026 code flags its deliberate choices, but not its defects.
New evidence everyone can use (recorded Oct 28 on a fresh default run): MRD-166 is exploitable, not just a crash. /api/forecast?station=X'OR'1'='1 returns 200 with all six stations' forecasts; /api/series?station=X'OR'1'='1&year=2025 returns all 1,044 daily rows for every station; the newer /api/summary rejects the same input with 400 not a station id.

Whoever assembles the doc

Title page, headings, final paragraph · Gurmehar

Nothing left for you: everything below is fixed or being fixed.
Handled by Gurmehar (4)

Title page date

Now "October 28, 2026".

Limitations of AI Assistance finished and made team-wide

The cut-off sentence is finished and "ChatGPT" becomes "the AI tools each of us declared above". It now ends with the team-wide declaration of the final review:

“A final review of the whole report with Claude Code (Claude Opus 5.5) checked every section against the code and the recorded runs, and Gurmehar applied the corrections and short additions it proposed across the sections. It also drafted the jq evidence for Signature 1 and the matching sentences in its comparison.”

MRD-77 label

Now "Related ticket: MRD-77".

Heading and figure numbering

"Part 2; Signature 3" becomes "Part 2, Signature 3"; Jason's figure becomes Figure 3.

Back to top

Valerie

Part 1, Genuine pattern (QueryBuilder)

Strong section: the pattern is named precisely, the 13 call sites in 8 files and the f7c4b94 history check out, and both diagrams are readable.

Your Agent Appendix entry is empty

Page 28 shows "Used ... :" and five blank headings. The brief says undeclared AI use is an academic-integrity violation, and only you can say how you used AI. Name the tool and model, and fill all five headings: at least one thing the agent got right (e.g. the fluent Builder reading, checked at QueryBuilder.java:22, :31, :112) and one thing it got wrong or partly wrong, and how you caught it. If you used no AI for a step, say so.

Figure 2 leaves out StationHandler

It also holds a Db directly and writes fixed SQL (no user input). Optional diagram update.

Figure 1 table split across pages

The mapping table starts on page 2 and continues on page 3. Keep it with its figure (layout only).

Handled by Gurmehar (4)

"Only class that quotes user input" made precise

Now "It is the only class that escapes user input for SQL". Other classes wrap user values in quotes, just not for SQL.

"This project" becomes "the Java services"

The Python analytics and reporting layers do use the sqlite3 library; only the Java services shell out.

Step range

"then whereEq and the other steps at :49-72" becomes "then from, whereEq and the other steps at :39-91".

Quoting gap includes where-method column names

The column name in each where method (:49-72) is also added raw.

Back to top

Jason

Part 1, Degenerate pattern (parallel routing)

The evidence is accurate and well located. Two brief requirements are not met yet, and the figure can't be read in the PDF.

The figure is unreadable at page size

Labels are about 4 pt because the two panels sit side by side. Stack panel (a) above panel (b), each at full page width, or give the figure its own page. Check it in the exported PDF.

Appendix item 4 reads as if true facts were the AI's errors

Reword as: "the agent's first diagram left out the duplicated route metadata, which is the core of the pattern". Item 2 says you checked the SVG labels were readable; the PDF figure isn't, so check the PDF after re-rendering.

Handled by Gurmehar (6)

Pattern and anti-pattern named

The brief asks for the degenerate pattern to be "named precisely". Heading and opening of "Patterns and Evidence":

Was: “Parallel Routing and Duplicate Route Metadata”
Now: “Degenerate Front Controller: Parallel Routing and Duplicated Route Metadata”

Added: “Pattern: Front Controller with a route registry. Router (Router.java:52-68) registers each path passed to add() with the shared Cors and RequestLog filters and records it for the /api index, and Dispatcher (Dispatcher.java:38-153) is an annotation-driven front controller for /api/v2. Degenerate form: the registry is split across three registration styles (ApiServer.java:41-44, 60-68, 75-79), and the route metadata has become duplicated knowledge, a DRY violation: a new v2 route needs both an @Endpoint summary and a hand-written router.note() (ApiServer.java:80-82), and the two already disagree for export.”

Keep-or-remove verdict added

At the start of "Recommendation":

Added: “Judgement: keep the Router and the Dispatcher, since both serve live traffic (Router.java:52-57, Dispatcher.java:132-146), and remove the duplicated metadata: delete the three hand-written router.note() lines (ApiServer.java:80-82) and build the /api index's v2 entries from Dispatcher.routes() (Dispatcher.java:108-117).”

Strongest evidence added: the two live indexes disagree

Added: “The divergence is already visible to clients: the Dispatcher serves its own index at GET /api/v2, built from the annotations (Dispatcher.java:126-130, 166-180), so GET /api lists /api/v2/export as "CSV or JSON export of a table" while GET /api/v2 lists it as "CSV or JSON export of a table (fmt)".”

Two small wording points

Was: “(fmt),"”
Now: “(fmt)",”

Was: “The migration of legacy routes should occur with caution and preserve the behaviour that is exposed externally by Meridian.”
Now: “The migration of legacy routes should occur with caution: it should keep their paths and JSON bodies, but moving them under the Router changes their OPTIONS answers (the Cors filter answers 204) and adds them to /api/metrics, and giving them the newer handlers' GET-only check would also turn their POST answers into 405, so those changes must be declared (Part 3, Safe change, tests T11, T12 and T17).”

Citations corrected

Dispatcher.of() cited at :62-72 (it calls register()), Router.routes() at :65-68, Router setup at ApiServer.java:60.

Name typo, ticket reference, figure number

"Jaoson" becomes "Jason"; adds "This pattern is tracked as MRD-248 (ISSUES.md:28), and its change is planned in Part 3, section 3.5."; your figure becomes Figure 3.

Back to top

Abinav

Part 2, Decay signatures 1 and 2 — the biggest single fix in the report

Appendix: the tool name is blank

Your entry still reads "Used :". Add the tool and model you used. Only you can write this.

Handled by Gurmehar (9)

Signature 1's jq example replaced (Abinav agreed)

The parse_slice example repeated Yuktha's Signature 3 and wasn't drift. Limitations declares this drafted text.

Was: “In src/jv_aux.c:30-37, the parse_slice() implementation contains an XXX comment stating that the branch should be unreachable because callers should not call the function when the value is neither an array nor a string. However, the implementation still handles this unexpected state by freeing the relevant values and returning an error stating that only arrays and strings can be sliced.”
Now: “The jq manual says "The limit function extracts up to n outputs from exp" (docs/content/manual/manual.yml:2995). The code, src/builtin.jq:149-152, ends "else exp end", so a negative n returns every output, not "up to n". Git history: the sentence and the negative-n branch were written together in 5a863bf (2014-07-06, Nicolas Williams, "Add foreach and limit"); the code was then revised eight times, five in 2014-2015 (starting the next day with 824f7df), twice in 2018 (e113c67, 7fd9e86) and once in 2019 (130152a), and up to jq-1.7.1 the manual sentence was never updated.”

Was: “Source: jq 1.7.1 - src/jv_aux.c, lines 25-38”
Now: “Source: jq 1.7.1 - docs/content/manual/manual.yml:2995, src/builtin.jq:149-152”

Was: “In jq, the comment describes a state expected not to occur, while the implementation still handles that state defensively.”
Now: “In jq, the limit sentence is an omission by a human author: incomplete from its first commit and left in place through eight later edits of the code, although the negative-n branch was deliberate from the start.”

Was: “The Meridian examples are identified in the assignment issue, while the jq example may describe a valid invariant rather than an actual contradiction.”
Now: “The ForecastCache Javadoc, by contrast, was written in the 2026 layer (a0f1aef, 2026-07-08) and confidently describes an eviction the code never performs, so it was wrong from the day it was written.”

Was: “Git history, blame, and inspection of the relevant implementation are needed to investigate how these cases developed.”
Now: “Git history shows how each case developed: slowly, by omission, in jq; wrong from the start in Meridian's agent-era Javadoc.”

Signature 1's ForecastCache sentence restored

The sentence lost between the 9:10 and 9:18 PM versions is back after "(services/build.sh:2-3).", word for word: "Additionally, services/src/ca/meridian/db/ForecastCache.java:88-96 is identified as an example where the Javadoc describes behaviour that the code cannot perform. This provides a second, code-level candidate for documentation-code drift."

jq evidence added (by Abinav)

Both signatures now have jq evidence in place of the template instructions.

Comparison paragraphs rewritten (by Abinav)

The instruction text is gone from both Comparison paragraphs.

build.sh misquote removed

"It also states to trust the run.sh and Makefile" is gone; the sentence now cites services/build.sh:2-3. That advice was the assignment brief's, not build.sh's.

README line numbers

Now "The documentation (README.md:11-12, :15, :19-21) mentions using Spring Boot, PostgreSQL, and Gradle."

Signature 2 code files

The Files line now names the code behind MRD-252 and MRD-253: ui-next/src/api/forecastClient.ts vs dashboard/js/views/compare.js, forecast.js and util/fmt.js; ui-next/src/util/quality.ts vs dashboard/js/views/quality.js and trends.js.

jv_get line range

Now jv_aux.c:87-94, which includes the second NaN check at :93.

"ISSUSE.md" typo

Now "Files: ISSUES.md:32-33".

Back to top

Yuktha

Part 2, Signature 3, Argument, Conclusion

Well written, and the core Meridian evidence (the two key() overloads) is correct. The wrong jq claims are being corrected for you; the additions below are yours.

Appendix: "No factual errors were caught"

The parse_slice and util.c claims below were wrong and are being corrected, so this line no longer holds. Replace it with a real catch, e.g. "the assistant presented the key mismatch as the whole bug; cross-checking with Part 3 showed evict runs as a separate program and cannot reach the server's cache."

Handled by Gurmehar (10)

Argument covers Signatures 1 and 2

Added: “Signatures 1 and 2 show a related contrast: jq's incomplete limit documentation and the redundant NaN check in jv_get are human gaps at the edges left in place, while Meridian's ForecastCache Javadoc describes an eviction that never happens and its two console copies show users different numbers (MRD-252).”

Second cause in Meridian added

Added: “There is a second gap the ticket does not mention: the evict command runs as its own separate program with its own ForecastCache (AdminCli.java:40-42, :136), while the running server holds a different one (ApiServer.java:74), so even matching keys would not clear the server's cache.”

The two key() overloads are already next to each other

Was: “Catching the disconnect requires placing both private key() overloads next to each other, a step most developers wouldn't think to take without prior suspicion.”
Now: “The two private key() overloads sit back to back, separated only by a blank line and the second one's two-line comment (ForecastCache.java:131-139), each under a comment that justifies its own rule: "keyed by the station alone" (:129-130) and "the key is built from both" (:135-136). The contradiction is written in the source, but each half reads as a reasoned decision, so nothing prompts a reader to reconcile them.”

Git history added

Was: “The gap likely emerged when one side was drafted around a specific concept ("key by station and year") that was never wired up to the rest of the implementation.”
Now: “git blame shows invalidate(), both key() overloads and their comments arrived together in one commit, a0f1aef (2026-07-08, the 2026 layer), so the incoherence was there from the start rather than drifting in; the evict command that relies on it arrived 13 days later in b34d9c1 (2026-07-21).”

"No warning flags" made consistent with Part 1

Was: “In Meridian, there are no warning flags.”
Now: “In Meridian, the 2026 code does flag its deliberate choices (the Dispatcher's Javadoc, Dispatcher.java:30-34, from the same commit a0f1aef, calls it "knowingly a second way of doing what Router already does"), but nothing in the code flags this defect.”

Was: “neither check ensured that the store and drop routines actually agreed on the underlying contract.”
Now: “nothing checks that the store and drop routines agree on the underlying contract: services/ has no tests at all.”

jq parse_slice: the contract is enforced

Now: "a defensive fallback; every caller checks the type first (jv_aux.c:109, 118, 182-184, 280), so the branch is unreachable", and the argument sentence becomes "In jq, parse_slice keeps a branch its own comment says can never run (jv_aux.c:31-40)."

jq util.c is not a broken path

Now: "a TODO admits that stream read errors bypass the error callback, and the code falls back to printing the error directly (lines 263-265)".

Conclusion says what the maintainer must do

Added: “For jq's maintainer, the markers and git history are a usable to-do list; for Meridian's maintainer, the cache's own Javadoc and the evict command's output cannot be trusted (ForecastCache.java:88-93, AdminCli.java:141-142), so behaviour has to be pinned by tests before anything is changed, as Part 3 does.”

"Leaving no trace"

Now: "its only trace is an entry count that does not drop, which nobody reads as an error."

"Dead Operation" name removed

It isn't an established pattern name.

Back to top

Jenna

Part 3, Changes and their reach

Appendix: "The Agent did not get anything wrong"

Contradicted by the mitigation claim and the missing SeriesHandler (both being corrected), and no ticket or file is named. Replace item 4 with a real catch (e.g. "the agent left out the SeriesHandler half of MRD-166") and give one concrete correct trace in item 3. Name the model you used.

Handled by Gurmehar (6)

MRD-166 is not "mitigated"

Now: "this is not mitigated today: the newer handlers avoid it with Validate.requireStation and QueryBuilder quoting (SummaryHandler.java:45, :60), and the fix adopts these in the two old handlers."

Each change is now stated

One clause per ticket saying what the change is:

Was: “For MRD-166:”
Now: “For MRD-166 (send the station through Validate.requireStation and QueryBuilder quoting, as the newer handlers already do):”

Was: “For MRD-247:”
Now: “For MRD-247 (store and evict under the same station key, and give the evict command a path into the running server's cache):”

Was: “For MRD-248:”
Now: “For MRD-248 (register every route through one mechanism and build the /api index from it):”

Was: “For MRD-249:”
Now: “For MRD-249 (move the station catalogue out of the Java source into a configuration file read through Env.java):”

Was: “For MRD-77:”
Now: “For MRD-77 (replace the sqlite3 command-line calls in Db.java with an in-process SQLite driver):”

Every module and interface each change reaches

The brief says "all". One sentence per ticket:

Added: “It also reaches the route registration (ApiServer.java:42-43), the error path (ApiServer.java:97-106), the forecast and forecast_daily tables, and both consoles (dashboard/js/api.js:61-62, ui-next/src/api/endpoints.ts:33,42) through GET /api/forecast?station= and GET /api/series?station=&year=.”

Added: “It also reaches ForecastV2.java:94-95, ApiServer.java:74-76, the cache timeout setting meridian.v2.cache.ttl (Env.java:32, :57-58) and the admin command evict <station> <year>; because the admin tool is a separate program, it cannot reach the running server's cache.”

Added: “It also reaches the Cors and RequestLog filters, /api/metrics (the hand-wired routes are not counted today) and the /api route index (RootHandler).”

Added: “Moving it to configuration reaches Env.java and StationCache; no in-repo client calls /api/v2/stations.”

Added: “It also reaches DbClient.java:50-74, whose retry depends on the error text "exited 5", every old route that passes sqlite3's JSON through unchanged (/api/stations, /api/forecast and /api/series; the float formatting on the last two, such as "yield_t":5.95099999999999962, will change), and services/build.sh and run.sh:25, which must supply a driver and put it on the classpath.”

Locations corrected

  • MRD-166: ForecastHandler.java:41-54 (parseQuery at :25-37) and SeriesHandler.java:22-42.
  • MRD-247: ForecastCache.java:94-96 and :129-139, plus AdminCli.java:135-143.
  • MRD-248: ApiServer.java:41-84, Router.java:46-63, Dispatcher.java:62-164.
  • MRD-77: Db.java:29-56 and :58-75; tsstore.c marked as the ingest team's half.

Unrendered link syntax removed

"[Router.java] ...#L12-L48 and [Dispatcher.java](" becomes plain file:line.

"Github Co-pilot"

Now "GitHub Copilot" in your appendix heading.

Back to top

Sonia

Part 3, Risks, order of work, dependencies

Your baseline facts all check out (1,044 frames, ten wire-suspect, ports, the BRANTFRD values, 13 routes), and your separate-cache-instance point in 3.4 is correct and important.

Overlap with the Safe change section (optional)

3.1, the per-ticket "Safe-Change Plan" and "Rollback Plan" blocks, and 3.8 repeat the separate Safe change section and disagree with it in places (run_at handling, database backups). Consider replacing each with one line pointing to the Safe change tests. "3.9 Conclusion" sits in the middle of Part 3.

Appendix: six vs seven endpoints

3.1 lists seven endpoints but your appendix says six. Only you know which you ran; make them agree.

Handled by Gurmehar (11)

Order of work changed: MRD-166 first, MRD-77 last

MRD-166 doesn't need MRD-77 (team decision 1). 3.2, 3.7 and 3.9 now read:

Was: “To fix this, we would replace the current string concatenation with parameterized queries.”
Now: “To fix this, we would stop pasting the raw station into the SQL: first by validating and quoting it as the newer handlers do, and later with bound parameters once MRD-77 provides them.”

Was: “This means we would need to update or extend the database interface before fully fixing the unsafe query construction.”
Now: “A full bound-parameter fix would need a new database interface, but the immediate fix reuses the validation and quoting the newer handlers already use (SummaryHandler.java:60-61), so it does not wait for MRD-77.”

Was: “If necessary, we would first introduce a smaller, tested way to bind SQL parameters and fix the vulnerable handlers.”
Now: “The codebase already has a smaller way to do this, which the newer handlers use today: they validate the station and quote it through QueryBuilder (SummaryHandler.java:60-61, QueryBuilder.java:99-110; the behaviour is recorded in Safe change T8), so we fix MRD-166 with it first.”

Was: “Step 1: Prepare safer database access related to MRD-77”
Now: “Step 1: Fix unsafe SQL queries in MRD-166”

Was: “This would help provide parameter-binding support for MRD-166.”
Now: “We would send the station through Validate.requireStation and QueryBuilder quoting, as SummaryHandler.java:60-61 already does, so the security fix does not wait for any database change.”

Was: “Step 2: Fix unsafe SQL queries in MRD-166”
Now: “Step 2: Fix cache invalidation in MRD-247”

Was: “Once a safe database interface is available, we would use it to fix the vulnerable queries.”
Now: “This would require a way for the administrative command to reach the live server's cache.”

Was: “Step 3: Fix cache invalidation in MRD-247”
Now: “Step 3: Move station metadata out of Java source code for MRD-249”

Was: “This would require a way for the administrative command to reach the live server's cache.”
Now: “We would first make sure the new metadata source is working correctly and that station queries remain compatible.”

Was: “Step 4: Move station metadata out of Java source code for MRD-249”
Now: “Step 4: Make routing more consistent for MRD-248”

Was: “We would first make sure the new metadata source is working correctly and that station queries remain compatible.”
Now: “Routing affects many endpoints, so we would leave this broader change until the other endpoint behaviours are stable.”

Was: “Step 5: Make routing more consistent for MRD-248”
Now: “Step 5: Replace the external SQLite process for MRD-77”

Was: “Routing affects many endpoints, so we would leave this broader change until the other endpoint behaviours are stable.”
Now: “This is the largest change and crosses into the ingest team's subsystem (ISSUES.md:7), so it goes last, once the characterization tests are in place.”

Risks made concrete

Each risk now names what breaks and today's recorded value:

Was: “We would test station values containing quotation marks and other SQL-special characters to make sure they cannot change the meaning of the query.”
Now: “This already happens today: station=X'OR'1'='1 returns all six stations' forecasts on /api/forecast and all 1,044 daily rows on /api/series (Safe change T5); after the fix both must answer 400 not a station id.”

Added: “For example, /api/forecast passes sqlite3's JSON through (Db.java:30), so today BRANTFRD's yield prints as 5.84100000000000019; a driver would print it differently.”

Was: “We would test these situations to make sure the API can still access the database safely.”
Now: “We would hold an exclusive lock on a copy of the database, as Safe change T27 does: today both the hand-wired /api/forecast and the Router's /api/summary answer 500 sqlite3 exited 5 (the second after two retries) and recover once the lock is released.”

Was: “We would test database failures and make sure they are handled properly.”
Now: “We would point the API at a database with no tables, as Safe change T26 does: today health answers 200 and every data route answers 500 sqlite3 exited 1.”

Added: “Safe change T22 records that today the summary still says runs 1 after evict.”

3.7 complete dependency list added

The brief says "all". Added as one sentence:

Added: “Other dependencies: MRD-166 changes the two hand-wired handlers (ApiServer.java:42-43) that MRD-248 would move under the Router, so MRD-166 lands first; MRD-249 and MRD-248 share the v2 Dispatcher path (StationsV2.java:51, ApiServer.java:75-79), so MRD-249 lands before MRD-248; MRD-77 is filed under ingest (ISSUES.md:7), changes DbClient's retry, which can recognise a lock only by the text "exited 5" because Db reports nothing but "sqlite3 exited N" (DbClient.java:67-74, Db.java:51), and needs a driver on the classpath (run.sh:25); and MRD-247's in-server eviction needs a path from the admin command into the running server, such as an authenticated admin operation, which services does not have today.”

3.5 no longer promises "responses the same"

Was: “while keeping the existing endpoint paths and responses the same.”
Now: “while keeping the existing endpoint paths and JSON bodies the same. Moving the hand-wired routes under the Router would change their OPTIONS answer (200 today, 204 after) and add them to /api/metrics, and giving them the GET check the newer handlers already make (SummaryHandler.java:41-43) would change their POST answer from 200 to 405, so those differences are declared in the Safe change tests T11, T12 and T17.”

MRD-247 and MRD-249 don't depend on database changes

"Would require reliable database reads" removed; "Once the shared database interfaces..." becomes "neither depends on database changes (ForecastCache.java:107-116, StationCache.java:61-69)".

Ingest doesn't write during the default run

Now: "On the default run, ingest and the forecast finish before the API starts (run.sh:19-26), but in production the ingestion process may write ... while the Java API reads from it."

"V2 portal" isn't in the code

Now "API users (no in-repo client calls /api/v2)", and Risk 3 says "API users".

The "Limitations of AI Assistance" paragraph

Finished and made team-wide (see the assembler section).

MRD-166 test 6 uses a raw quote

Was: “Entering quotation marks or other SQL-special characters in the station parameter.”
Now: “Sending a raw (unencoded) quote and the injection X'OR'1'='1 in the station parameter, which today return a 500 and every station's data respectively.”

3.8 list restored

"First, ... Second, ... Third, ..." with the missing space after "tests." fixed.

DbClient range

DbClient.java:57-68 becomes :50-74.

Back to top

Gurmehar

Part 3, Safe change

Nothing left for you: everything below is fixed or being fixed.
Handled by Gurmehar (9)

Fields that differ between runs

"Five fields" corrected to four things, and "clock and counter fields" to "clock fields".

T5 and Table 1 now show the working injection

A balanced injection returns every station's data; an unmatched quote gives 500.

T27 retry timing

120 ms then 240 ms, on the two measured routes.

T17

Only the four routes that check the method answer POST with 405.

"Never writes" wording

Now "the service's own code issues no writes", noting sqlite3 opens the store read-write.

Pins-today list

T20 added.

Lost formatting

T28 setup spelled out; Table 2 header and stations row fixed.

Small wording

"43 source files"; "the 13 recorded paths".

T17 after-change cell and order note

T17 now says OPTIONS answers 204 and POST becomes 405 only with the newer handlers' GET check; the completeness note now says keep T26 until MRD-77 lands.

Back to top