Appearance
Delivery log
Per-request delivery observations, kept to build an evidence base about the development process itself rather than about the code. Requested by the product owner on 2026-07-28 after several small changes took over an hour: the decision was to change nothing yet, run the existing workflow under observation, and let trends rather than single incidents drive any process change (QRS-241).
Why this is a separate page from the tracker
tracker.md records defects, debt and decisions as permanent QRS-### ids. These are observations about how a request was delivered — a different kind of record with a different lifetime. Mixing them would mean a search for open bugs returned retrospectives, and it would inflate the id space with rows nothing ever cross-references. Entries here link out to QRS-### rows where a specific defect or decision came out of the request.
The two halves, and why both are needed
Measured columns are machine-collected. Narrative columns are self-reported. That split is deliberate and it is the main thing that makes this log worth keeping: the party being measured is also the one writing the notes, so the narrative alone would be an opinion accumulating over time. The numbers are checkable — wall-clock from timings, gate runs from how many times a suite was actually invoked, rework from commits/edits that existed only to fix something self-inflicted.
Where the two disagree, trust the numbers.
Review trigger
After 10 entries, or 2026-08-31, whichever comes first — then a written analysis, and only then a process proposal. A stated trigger rather than "once we have enough data", because this repo has a measured history of deferred reconciliation never happening: see QRS-180 (the tracker ran without ids for months) and the design-drift ledger's own note that the completion rate for "we'll reconcile this later" here is approximately zero.
Measures
| # | Date | Request | Items | Wall-clock | Gate runs | Rework cycles | Prose (words) |
|---|---|---|---|---|---|---|---|
| 1 | 2026-07-28 | iOS composer + Android alerts + 3 UI/UX items + CI docs gate | 6 | ~4h | e2e ×4, unit ×12 | 6 | ~7,400 |
| 2 | 2026-07-29 | Reminders never notify at the scheduled time: diagnose, plan P3, fix | 3 | ~4h | e2e ×2 (neither completed, see below), unit ×9, prebuild+APK ×1 | 4 | ~5,400 |
| 3 | 2026-07-29 | Per-package type-check (QRS-015) + local e2e split (QRS-245) | 2 | ~1h | type-check ×4, unit ×2, e2e:quick ×1 | 2 | ~1,900 |
| 4 | 2026-07-29 | Refactor to a clean Sonar scan before any new feature | 141 findings across 43 files | ~5h | lint ×9, type-check ×5, unit ×5, format ×2, e2e:quick ×1, web export ×1, static gates ×2, local Sonar ×2 (both failed, see below) | 7 | ~9,800 |
| 5 | 2026-07-30 | Finish the remaining Sonar findings + clean baseline before planning auth | 17 findings across 9 files | ~2h | lint ×4, type-check ×6, unit ×2, format ×3, static gates ×8, e2e:quick ×1, web export ×1, measured eslint ×4, APK ×1 | 2 | ~6,100 |
| 6 | 2026-07-30 | Triage the first real Sonar CE scan to zero (77 → 0 findings) | 77 findings across ~37 files | ~3.5h | deno check ×4, deno lint ×3, test:ef ×4, lint ×2, type-check ×5, format ×2, static gates ×2, e2e:quick ×1, web export ×1, CI sonar runs ×3 | 4 | ~9,600 |
| 7 | 2026-07-31 | Explain + fix GitHub Actions quota burn, then a proactive pre-P3 codebase audit, then promote pending migrations to Prod | 3 | ~3h | check:readmes ×3, check:parity ×3, format ×3, gh read-only ×6, Supabase MCP advisors/migrations ×5, supabase CLI ×4 | 5 (4 permission-denial retries + 1 real repair still outstanding) | ~4,200 |
| 8 | 2026-07-31 | P3 — Google (PKCE) + Sign in with Apple across web/Android/iOS | 1 | not measured (session compacted before this entry was written) | type-check ×9 workspaces, typed lint ×1, unit ×1 (527), e2e:quick ×1 (99), web:export ×2 (1st failed, see QRS-270) | 3 (QRS-269 fix, QRS-270 fix, Google-SDK install-then-uninstall reversal) | ~550 |
| 9 | 2026-07-31 | Confirm GCP/Supabase naming, then a full Google OAuth + Apple sign-in setup guide + tracker task for missing production assets | 3 | not measured | check:readmes ×0 (docs-only, no code gate applies) | 0 | ~2,100 |
| 10 | 2026-08-02 | Design + build an enterprise Release Management System (HLD, LLD, gate, 26.0.1 folder), after 4 rounds of adversarial review | 1 | not measured | release validator ×6 (37 tests), derive ×3 (19), fn-config ×1 (11), env-drift ×1 (14), check:release ×5 (incl. 3 deliberate failure paths), lint/format/type-check ×1 each | 4 (bad test fixtures ×3, fabricated QRS-289-orig id, ci.yml paths-ignore, buildSlotFor boundary) | ~9,000 |
Observations
10 · 2026-08-02 · QRS-288, QRS-293, QRS-294, QRS-289 (amended), QRS-284
Went well. The owner rejected the plan four times, and every rejection found something real that I had missed: a solo-shaped governance model that could not express concurrency; the store-rejection case that broke the single-status assumption; build-number divergence that falsified a conclusion already shipped in QRS-289; and a request for HLD/LLD that exposed having designed no notifications at all. The final design is materially better than the first, and none of the improvements came from me re-reading my own work.
Cost. Four plan revisions before a line of code. That is the right trade here — schema changes would have migrated live release records — but it is the largest planning-to-implementation ratio in this log by a wide margin.
Avoidable. Four self-inflicted errors, all caught by my own gates or tests rather than by review: (1) I asserted iOS had never been built from its absence in the repo, when /ios and /android are gitignored expo prebuild outputs — absence of evidence from a location where evidence cannot live. The owner corrected it. (2) I planned the gate as a step in ci.yml, whose paths-ignore excludes both paths it validates — it would have been a green no-op, the QRS-013 pattern I cite constantly. Caught while wiring, not while planning. (3) Three test failures from lazy fixtures (in_development requires scope) that also muddied what each test isolated. (4) I invented a QRS-289-orig id, breaking the never-fabricate-ids rule in the same session I wrote a gate to enforce id integrity.
Improve. The iOS error has a general form worth naming: before concluding "X was never done", establish that the repo is a place where evidence of X could exist. A .gitignore check would have taken ten seconds. Second: when a plan says "add a step to workflow Y", read Y's triggers in the same breath — I read its steps carefully and its filters not at all.
Not done, and stated plainly. 26.0.1 scope is empty pending the owner's brainstorm, so the Aug 15 date has no commitment behind it (risk R2 in the release record). Phase-2 gate rules are deferred. The stale-language sweep updated current-state claims but deliberately left historical tracker rows describing past iOS-PWA incidents intact — rewriting those would falsify the record.
9 · 2026-07-31 · QRS-271
Went well. The naming question answered itself from evidence already in memory (qr-setu-dev/qr-setu-prod mirrored across both systems) rather than a fresh judgment call, and reading the actual redirect-target code (socialAuth.web.ts/.native.ts) before writing the guide caught that the exact native redirect value is a transcription, not a confirmed one — flagged in the doc instead of stated as fact.
Cost. None beyond the writing itself — no code changed, so no gate reran.
Avoidable. N/A this entry, but worth naming a gap in the log's own machine: Wall-clock is documented as machine-collected ("Measures columns are machine-collected"), and neither this entry nor entry 8 had a real timestamp source available to write one truthfully — so both say "not measured" rather than a plausible-looking guess. A fabricated number here would be exactly the failure mode CLAUDE.md's verification standard exists to prevent, just applied to this log instead of to code.
Improve. If wall-clock matters to the eventual review (2026-08-31 trigger), it needs an actual timestamp source wired into whatever writes this log — self-estimation from turn count is not a measurement, and six prior entries reporting confident-looking hour figures without saying how they were derived is itself worth re-checking before the review trusts them.
8 · 2026-07-31 · QRS-269, QRS-270
Went well. The D3 native-Google-SDK decision was reversed on fresh evidence (the installed package's own source has zero nonce references) rather than on the plan's original stated rationale, and the replacement (PKCE) needed zero new dependencies — a rare case where the more-secure option was also the cheaper one.
Cost. Two real, previously-shipped defects surfaced only because the full gate suite was run to actual completion rather than assumed green: AuthScreen's OTP field prefilled with the stub's fixed code (QRS-269, hidden by five tests that depended on the bug) and startAuthBootstrap() throwing at module scope and breaking expo export -p web (QRS-270, hidden by a cancelled CI run in the prior session).
Avoidable. Not within this request — both defects predate P3 (P2's own commit), so the cost here is the cost of finally running the check that had been skipped, not new rework this session introduced.
Improve. Both catches validate running web:export and the full jest suite to completion before reporting a feature done, rather than trusting an earlier local pass — worth stating as a thing that worked, not just logging the defects it found.
7 · 2026-07-31 · QRS-263, QRS-264, QRS-265, QRS-266, QRS-267
Went well. The CI-quota diagnosis was grounded in the four workflow files and the actual run history rather than in a guess about "duplicate runs": the real cause was three workflows missing a concurrency group, so superseded pushes ran to completion. The proactive audit that followed found four things nobody had asked about, two of them real — a security gate that had been red on a false positive for two days, and Prod sitting 10 migrations behind Dev.
Cost. Three assumption-driven errors, all mine, all in QRS-267. The expensive one: using Supabase's MCP apply_migration against Prod without first checking how it registers versions. It stamps its own timestamp, so four migrations applied correctly while leaving four orphan rows in schema_migrations and four repo files reading as unapplied — real drift in a production database, requiring a migration repair that is still outstanding. Separately, a classifier outage was misread as a permanent denial, which abandoned a working CLI path and escalated to requesting a production DB password that was never needed.
Avoidable. All three. The migration drift needed one read-only migration list after the FIRST apply, before the other three — a five-second check against a fifteen-minute repair. The outage needed one retry and one reading of the error text, which was textually distinct (temporarily unavailable) from the denial it was mistaken for. The CI re-run needed the question "why were these cancelled?" asked before the answer was assumed to be benign.
Improve. The owner escalated this into a non-negotiable standard, now the FIRST entry under "Non-negotiable engineering standards" in CLAUDE.md and part of the Definition of Done: verified, never assumed, with six sub-rules each naming the incident that produced it. Worth noting what the evidence base says across seven entries: "acting before reading" is now the named root cause in entries 2, 3, 4 and 7. That is four of seven, and it is no longer a per-session lapse — it is the pattern this log exists to surface, which is the argument for the standard being a gate rather than an intention.
6 · 2026-07-30 · QRS-260
Went well. Triaging by evidence rather than by rule name caught the one place applying Sonar's own suggestion would have been a regression: typescript:S7741 wanted process !== undefined instead of typeof process !== 'undefined', and platform-globals.d.ts's own comment already documents why that throws in a plain browser bundle. Read the file the rule pointed at before trusting the rule.
Also went well. HoursTab's array-index key turned out to be a real bug, not a lint nit — its own "Remove" button deletes from the middle of the list, which is exactly the shape that misattributes TextField input state to the wrong row after a deletion. Found only because the finding said why it mattered (fixed-length decorative rows elsewhere did not have the same "remove from the middle" operation and were correctly left as suppressions instead).
Cost. Two research detours, both worth their time. (1) Proving the validate-user-input email regex was ReDoS-safe took two rounds of timing harnesses against different attack-string shapes before the growth curve was actually linear in V8 — the first attempt didn't trigger the ambiguity at all, and assuming "no blowup in try #1" would have been the wrong lesson. (2) The gate-scenario test hung with no output for several minutes: an in-process stub server on the default host, fetched via localhost, left a keep-alive socket open that server.close() waited on forever. Rewritten as a child-process stub bound to an explicit 127.0.0.1 with connection: close.
Avoidable. The initial size estimate for the "mechanical tools/ cleanup" was wrong by roughly 5× — "~50 findings" turned out to be 10 in tools/ plus 43 more spread across apps/, packages/ and supabase/functions/ that the first grep-based skim never surfaced. Recounting from the full 77-row table before starting, rather than after noticing the estimate felt too small, would have sized the work correctly the first time.
Improve. sonar-project.properties accumulated two duplicate top-level sonar.issue.ignore.multicriteria= keys mid-edit (a properties file silently keeps only the last one) — caught by re-reading the file rather than by any gate, since nothing lints .properties syntax here. Worth a standing habit: after any multi-step edit to a config format with no linter of its own, read the whole file back before moving on.
Closing update, same day. The estimate of "~13 findings left" was itself off — the real number, confirmed by a second CI run, was 3: two S6571 (Promise<any | null>, the | null redundant since any subsumes it) and one S7776 (RETRYABLE_STATUSES as an array instead of a Set). Fixed, re-verified locally (deno check + 108/108 test:ef), pushed, and CI's third run in this pass confirmed bugs 0 · vulnerabilities 0 · code_smells 0 · security_hotspots 0 — sonar-baseline.json ratcheted to all-zero. Separately, PR #27 (the only thing triggering this branch's CI, since ci.yml's push: trigger only watches develop/uat/main) closed itself unmerged mid-session with no comment and no visible cause — reopened after confirming with the user, which restored the CI trigger. Worth a standing watch: on a feature branch, a CI run's continued existence depends on the PR staying open, and nothing surfaces that dependency breaking until a push produces no run.
5 · 2026-07-30 · QRS-247 (closed), QRS-258, QRS-259
Went well. The backlog was re-measured before being worked rather than read off the config note — and the note was wrong again, claiming 8 cognitive-complexity findings where there were 9. The ninth was this programme's own sonar-baseline-check.cjs, pushed over the threshold by the liveness check added to it the previous day. That is entry 4's lesson applied rather than merely recorded, and it is the first time in five entries that a stale number was caught before it was reported. Sequencing held up too: pure logic first (recurrence.ts 38 → clean, with 117 unit tests as the spec and 117/117 green after each step), screens last.
The technique worth reusing. The five screens were fixed by extracting a per-screen SHELL component and turning the ladder into top-level guard returns, which leaves the rendered element tree unchanged — same wrappers, same testIDs, same contentContainerStyle. That property is what made touching seven files inside the QRS-203/206/207 blast radius defensible in a single pass; threading each screen's dozen values out as props would have been the same refactor without it.
Cost. Two self-inflicted detours. --rule cannot inject a plugin rule under flat config, and the replacement config then hit Cannot redefine plugin "sonarjs" even importing the identical object — about ten minutes to land a working measurement harness. And resolveSaveState was typed ProfileRecord | undefined when the query actually returns ProfileRecord | null | undefined; the inline data ? … it replaced had accepted both by truthiness, so the extraction exposed a detail the old spelling hid.
Avoidable. The second one. Extracting an expression into a typed signature means reading what the source actually admitted, not what it looked like it admitted — data ? … says nothing about whether the falsy case is null, undefined or both. The compiler caught it in seconds, which is the system working; the point is it was knowable by reading the hook's return type first.
Improve. One finding in nine was in the gate's own code, added by the previous day's fix to that same gate. Gate scripts are app code for lint purposes and drift exactly like it — measure the tooling in the same pass as the product rather than assuming the thing doing the checking is exempt.
Not done, and stated plainly: the Sonar baseline is still un-bootstrapped, so the scan is not green yet — it is clean at layer 1 (both rules ON at zero, typed eslint . exit 0) with layer 2's first real numbers still pending a CI run. Entry 4 ended by refusing to call an amber scan green; the same applies here, one step further along. Two CI defects had to be fixed just to reach a measurement (QRS-257, then QRS-258 — a token step failing ~36% of runs on a random password vs a character-class policy).
4 · 2026-07-29 · QRS-247, QRS-251..253
The one thing worth carrying out of this entry: the backlog I reported last time was wrong, and no gate could have told me. Entry 3 shipped with "73 findings" recorded in the config as the measured truth. The real number was 158. eslint-plugin-sonarjs silently skips every rule that needs type information — no warning, no error, the rules just never run — and ~15 of them were already error in the recommended set the repo spreads, including the one rule the owner's screenshot was dominated by. So a green npm run lint sat on top of 92 prefer-read-only-props findings and a real always-true comparison in the reminders write path. This is the same defect class as QRS-246 itself — a standard that is configured but not executable — one layer further down. The corrective is generic and belongs in how I work, not just in this repo: a count is not a measurement until the thing producing it has been shown to be capable of seeing what it is counting.
Went well. Measuring before and after every batch, which caught three of my own errors that would otherwise have shipped: (1) the first EMAIL_RE "fix" was 1445 ms vs the original's 1388 ms — no improvement at all, because I had diagnosed the wrong ambiguity, and only a timing harness exposed it (QRS-253); (2) rewriting the CSS-var parser to avoid backtracking silently dropped 3 of 66 tokens from a parity gate, because theme.css groups variables under /* comment */ headers and my anchored regex rejected the chunk after a header — a hole in a gate is worse than a slow gate, and I had written that sentence in a comment one edit before I violated it; (3) eslint-disable-next-line with a wrapped reason silently targets the comment continuation rather than the code. All three were found by re-running the measurement, never by reasoning.
Also went well. The type-aware discovery turned a style pass into a defect pass. The different-types-comparison finding in service.stub.ts was a genuine type-system lie, and critically the rule's own suggested fix would have introduced a data-loss bug — deleting the "always true" filter would let an explicitly-undefined PATCH field wipe a stored value. Taking a static-analysis suggestion literally is not the same as resolving it.
Cost. ~5h, and the two expensive line items were both avoidable in hindsight. (1) The local SonarQube attempt: ~35 min for nothing. It booted, then the scanner container died and took Docker Desktop with it (QRS-252). The 16 GB box already cannot finish the Playwright matrix (QRS-245); I had that precedent and still tried. (2) A 92-site codemod run against a stale report. I had edited 10 files after measuring, so the dry run reported one skip whose line numbers had shifted — the skip was the only reason I noticed. Had it silently "succeeded" it would have rewritten the wrong ranges. The rule now is: re-measure immediately before applying a position-driven codemod, every time.
A near-miss worth naming. I ran git checkout -- on a file to undo a bad edit, and the permission classifier blocked it. That would have destroyed an unrelated, verified fix earlier in the same file. The habit was wrong, not the tooling: with many edits in flight, reverting a whole file is never the narrow operation it feels like. Fix forward.
Avoidable. Three items, in descending order of cost.
- Reporting 73 as measured, in entry 3. The number went into the config as a load-bearing comment and into a tracker row as fact. Nothing about it was checked. This is the entry's headline and the reason the other two are worth listing at all.
- The local Sonar attempt, against existing evidence about this machine's limits.
- Two batch-edit scripts referencing hoisted variables I had not yet declared (
greetKey,groupHeading,bullets), plus one that converted an arrow-expression.mapto a block without closing it. Caught bytype-checkin seconds, so cheap — but it is the same write-then-verify-later pattern that produced the corrupted test names in entry 2, which means it is a habit and not an incident.
What I would change. Nothing about the process gates: they held. One thing about my own reporting — stop treating a tool's output as a measurement of the world. Before recording any count as a baseline, establish what the tool can and cannot see, and write that limitation next to the number. The sonar layer's header now does exactly this, and it is the durable output of this session alongside the 141 fixes.
Not done, and stated plainly: 17 findings remain across two rules (no-nested-conditional 8, cognitive-complexity 8). They are one piece of work — the async-screen ladder in 5 screens, plus recurrence.ts at complexity 38 — and they are broad JSX churn in exactly the files behind QRS-203/206/207, so they need the native pass rather than a tail-end edit. The scan is not green. Calling it green would be the one outcome worse than leaving it amber.
1 · 2026-07-28 · QRS-233..240
Went well. Root-causing found real defects rather than symptoms: four independent causes for the Android notification silence, and two WCAG/touch-target failures the Playwright gate had been reporting unread. Design-first was followed for both new src/ui primitives. Two bugs I introduced were caught by tests I had just written, which is the system working.
Cost. The e2e loop is 8–13 min (export + Playwright) and ran 4 times — ~40 min, of which ~30 was waste: the parity probe is compile-time inlined, so the flag alone does nothing against a warm Metro cache and needs --clear. Unit gates were not a factor: the full suite is 74s measured (static gates 6.6s, lint 15s, type-check 12s, tests 40s).
Avoidable. All 30 min of the e2e waste. Six rework cycles from self-inflicted errors — notably config-plugin ordering, which was already documented in the README being edited at the time. And reporting npm run e2e | tail -8 as "506 passed" when 29 tests had failed above the cut: truncating a gate's output turns red into green while leaving a number behind that looks like evidence.
Improve. Read folder conventions before editing them. Batch edits, then run gates once, instead of edit → full gate → repeat. Read the summary line or the JSON, never a tail. Prose was ~5,200 words of code comment + ~2,200 of tracker rows for 988 lines of code; CountBadge.tsx is 48% comment.
Caveat against over-reading this entry. The request was six items including two multi-cause native bugs — it was not a small change, so it is a poor baseline for "small changes take an hour". Entry 2 onward should record scope honestly so the trend is not built on this one.
2 · 2026-07-29 · QRS-237, QRS-242, QRS-243, QRS-244
Went well. Diagnosis was evidence-led rather than plausible: the exact-alarm branch was read out of ExpoSchedulingDelegate.kt, the missing permission confirmed against the merged manifest of a real APK, and the Play policy checked against the live policy page instead of memory. That last one caught a factual error before it reached code (I had the default-grant boundary at API 33; it is 34). Reading RemindersScreen before building also withdrew one of my own five reported causes — the contextual permission prompt already existed. The cause-A regression test is the shape worth repeating: it renders something that is deliberately not the Reminders screen, because the previous suite could only ever mount the one configuration in which the bug does not occur.
Cost. Four rework cycles, all mine and all found by gates rather than by thought: useRootNavigationStatethrows rather than returning undefined without a navigation container, which made the provider unmountable in tests and forced a mid-flight split into ReminderTapRouter; the react-compiler lint correctly rejected setState in an effect, forcing the focus target to be re-done as a render-time derivation; a self-referential mock object defeated TS inference; and a blanket search-and-replace to add jest's mock prefix silently corrupted three test names and the router mock's shape.
Avoidable. The blanket rename, entirely: it was a scripted edit across a file I had just written, and re-reading the result was the only reason it did not ship. useRootNavigationState throwing is discoverable from its source in under a minute and I designed around an assumption instead. Both are the same root habit as entry 1's config-plugin ordering: acting before reading.
Improve. The two forced redesigns (tap router split, derived focus) both produced better code than the plan, which is worth noting honestly: the gates were not obstacles here, they were the review. But they were paid for at the end rather than the start, so the lesson is to check the lint rules and the hook contracts that govern a design before committing to its shape, not after.
The web gate did not run, and that is recorded rather than glossed. Two Playwright attempts were killed by MEMORY, not by failures: the first stopped silently at test 138 of 712 with exit 1 and no error text, and the owner killed the second manually at ~100% RAM. check:disk also reports C: at 11.2 GB against a 15 GB floor, and clean:dev finds nothing reclaimable, so the pressure is in files the sweep must not touch. Two lessons. First, a killed run looks almost exactly like a passing one in truncated output: all ok, no not ok, and the summary simply never printed. Only the exit code and the test count gave it away, which is the same class of trap as entry 1's | tail -8. Second, the local box cannot run a release build and the Playwright suite in the same session, so web verification for this change moves to CI, which starts from a clean runner (QRS-245). Claiming green here would have been the worse outcome by a distance.
Note on scope. Recorded as 3 items (plan P3, create the tracker task, then implement), but item 3 expanded from "cause C" to four causes plus two newly found defects once the code was read. That is value rather than creep, and it is why Items counts what was asked for.
3 · 2026-07-29 · QRS-015, QRS-245
Went well. Both estimates were "~30m" and both were wrong, but informatively: closing the type-check hole immediately surfaced two real defects that were unreachable before — packages/data's seven stub sleep helpers passed a promise resolve straight to setTimeout (an arity mismatch), and hexFromHslTriplet would emit #NANNANNAN as a browser theme-colour for a malformed triplet. That is the strongest possible argument that the gate was worth closing. The design call that mattered was refusing @types/node: it would have made the errors vanish by deleting the platform-agnostic guardrail, so a narrow platform-globals.d.ts went in instead. For QRS-245, e2e:quick is a real gate rather than a token one (99 deterministic assertions at the tightest viewport, 2.4 min) because it keeps the layer CLAUDE.md calls primary.
Cost. A grep pattern of @qrsetu/[a-z]+@ silently failed to match i18n (it contains digits), so a block of i18n's type errors was attributed to packages/domain and I spent a few minutes investigating a regression in a package that was passing. Caught by noticing the missing npm error workspace line, not by the filter.
Avoidable. Both items. The grep, by testing a filter against the actual workspace names before trusting its attribution — the same "acting before reading" habit as entries 1 and 2, now three for three. And a ?? 0 I added to satisfy an error the compiler had not actually raised: dead defensive code in a function whose whole point is correctness, removed once I checked which line the error was really on.
Improve. The /init request surfaced something structural: CLAUDE.md drifts silently. Its test counts were stale (477 vs 509), its type-check description was about to become wrong, and the delivery-log practice the owner instituted was absent entirely — so a future session would simply not have done it. Whenever root scripts or gate behaviour change, the Commands and gate-layer sections need re-reading in the same pass, not later.
How to add an entry
- Append a row to Measures and a section to Observations, same number.
- Keep each narrative field to 2–3 sentences. Detail belongs in the
QRS-###row it links to. - Record
Itemsas the number of distinct deliverables asked for, not the number of things found — otherwise a request that uncovered five defects looks like scope creep instead of value. - Do not soften the Avoidable field. It is the only column that can produce a process change, and an honest one is the entire point of the exercise.
Living doc: append per request. A gap in the sequence means the evidence base has a hole in it.