Skip to content

docs(v0.12.0): DTC history user-facing guide (slice 6) - #148

Merged
github-actions[bot] merged 1 commit into
mainfrom
docs/v0.12.0-dtc-history-harness
Jul 23, 2026
Merged

docs(v0.12.0): DTC history user-facing guide (slice 6)#148
github-actions[bot] merged 1 commit into
mainfrom
docs/v0.12.0-dtc-history-harness

Conversation

@ohgeeceee

Copy link
Copy Markdown
Owner

What

Slice 6 of v0.12.0 Fault Memory (docs/v0.12.0_plan.md): docs/validation/dtc-history.md — the user-facing guide for the opt-in DTC history added in slices 2–5.

Same harness-doc shape as testplans.md (slice 6 of v0.9.0) and service-functions.md (slice 6 of v0.8.0):

  • Purpose / Time required / Risk honesty blockquotes at the top
  • §1 Where the file lives~/beeemuu-exports/dtc-history.jsonl, same home-dir resolution as export_text (CLAUDE.md §3)
  • §2 File format — one JSON object per line, every field explained in a table, forward-compat note about unknown-field tolerance
  • §3 The 60-second dedup window — why re-reads don't double-count, where the constant lives (commands.rs::DTC_DEDUP_WINDOW_SECS)
  • §4 "No VIN" entries — why slice 5's callout doesn't merge unknown-VIN entries with VIN-tagged ones (silent cross-car merging would be misleading)
  • §5 Clearing the history — three ways (terminal rm, untick the toggle, hand-edit the JSONL), plus a note that no in-app "Clear" button exists yet
  • §6 Storage growth — ~250 bytes per line, years of use before any concern
  • §7 What this file is NOT — not a backup, not shareable, not a substitute for diagnosis, not a write to the car
  • §8 How to verify the file is being written — status bar log line + wc -l + tail -1 sanity check
  • §9 Privacy note — local-only, no cloud, no telemetry, no remote copy to clean up

Why

The slice 1 plan (docs/v0.12.0_plan.md slice 6) explicitly says: "Same shape as docs/validation/testplans.md and docs/validation/service-functions.md: file location, line format, clear procedure, dedup window, 'no VIN' caveat." This PR delivers that.

The user-facing guide answers the questions that the slice-5 callout will inevitably raise: "where did this file come from?", "how do I delete it?", "is this private?". Without it, the recurring-DTC feature ships blind.

What changed

  • docs/validation/dtc-history.mdnew, 179 lines.

Verification

  • git diff --stat origin/maindocs/validation/dtc-history.md | 179 +++ (single new file, no churn).
  • Cross-references in the doc resolve: only one external link (docs/v0.12.0_plan.md) and that file exists at docs/v0.12.0_plan.md.
  • Style matches the two existing harness docs in docs/validation/: opening blockquote triplet (> **Purpose.**, > **Time required.**, > **Risk honesty.**), numbered sections with ## N. Title, practical examples (grep | wc -l, rm, jq).
  • 1 self-fix during authoring: the first draft referenced a "Settings UI clear button — that PR is a follow-up to slice 5" which was inaccurate (slice 5 is what just landed and has no settings UI). Fixed the section to reflect reality: terminal rm is the supported path; the UI button is a follow-up.

Tier

A — docs only, no Rust / commands.rs / transport / protocol change. Self-merge once required CI is green.

v0.12.0 progress — cycle complete

All 6 v0.12.0 "Fault Memory" slices shipped. The cycle closed in 7 PRs (1 plan + 1 Rust + 4 frontend + 1 docs). The recurring-DTC callout is live in the app; the JSONL log is in place; the documentation explains it all.

Unrelated working-tree noise (not in this PR)

Three files are modified locally but staged only my one intended file:

  • CLAUDE.md — your own rewrite (Tier C, yours to land)
  • frontend/index.html, frontend/schematics.html — the recurring third-party "network bar" injection (untrusted, not propagated)

Slice 6 of v0.12.0 Fault Memory (docs/v0.12.0_plan.md):
docs/validation/dtc-history.md — the user-facing guide for the
opt-in DTC history added in slices 2-5. Same shape as
testplans.md and service-functions.md:

  - Purpose / Time required / Risk honesty blockquotes at top
  - Section 1: Where the file lives (~/beeemuu-exports/dtc-history.jsonl)
  - Section 2: File format — one JSON object per line, every field
    explained in a table
  - Section 3: The 60-second dedup window (why re-reads don't
    double-count)
  - Section 4: 'No VIN' entries — why slice 5 doesn't merge them
    with VIN-tagged ones
  - Section 5: Clearing the history — three ways (terminal rm,
    untick toggle, hand-edit JSONL)
  - Section 6: Storage growth — ~250 bytes per line, years of use
    before any concern
  - Section 7: What this file is NOT (not a backup, not shareable,
    not a substitute for diagnosis, not a write to the car)
  - Section 8: How to verify the file is being written
  - Section 9: Privacy note (local-only, no cloud, no telemetry)

Tier A — docs only. Self-merge when CI green.
@github-actions
github-actions Bot merged commit 37392da into main Jul 23, 2026
9 of 10 checks passed
@github-actions

Copy link
Copy Markdown

🤖 Doc-only change — auto-merge enabled. Will merge once required checks pass.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 900762f0c8

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +85 to +89
The recurring-DTC callout (slice 5) does **not** merge no-VIN
entries with VIN-tagged ones. So a "2A82" recorded before the VIN
read will not count toward the "seen before" banner after you
read the VIN. This is intentional: an unknown VIN could mean a
different car, and silently merging across cars would produce

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Correct the no-VIN isolation claim

When the VIN is unavailable, main.js calls queryDtcHistory(null, ...), and the Rust grouping function applies no VIN filter for None, so no-VIN and VIN-tagged records are grouped together by code/address. A repo-wide search also finds no element defining the info-vin ID that main.js reads, meaning the current renderer always takes this unfiltered path. This assurance therefore hides cross-vehicle mixing that can produce misleading recurrence counts; remove it or fix the VIN wiring/filtering before documenting histories as isolated.

Useful? React with 👍 / 👎.

Comment on lines +115 to +117
3. **Hand-edit the file.** The JSONL is plain text. `grep -v '"code":"2A82"'`
keeps every line except the one you want to forget, redirected
back into the same file. Power users only; see §2 for the

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid redirecting grep output onto its input file

If a user follows this instruction with the natural command grep -v ... dtc-history.jsonl > dtc-history.jsonl, the shell truncates the history file before grep reads it, deleting every entry rather than only the selected code. Direct users to write to a temporary file and then replace the original, or use a tool designed for in-place filtering.

Useful? React with 👍 / 👎.

Comment on lines +157 to +158
2. `wc -l ~/beeemuu-exports/dtc-history.jsonl` increases by 1
for each non-empty read (the dedup window collapses duplicates).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Describe line growth per DTC instead of per read

For a read returning multiple distinct DTCs, record_dtc_read_impl appends one line for each DTC, while an empty successful read appends no line at all. Therefore this verification step grows by the number of non-deduplicated DTCs, not by one per non-empty read, and users reading several faults will incorrectly conclude that recording is malfunctioning.

Useful? React with 👍 / 👎.

Comment on lines +128 to +130
30 × 52 × 250 = ~390 KB. The slice-5 callout only looks at lines
within the past 14 days, so an old file is fine to keep — older
lines just don't surface.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Stop describing the occurrence count as a 14-day total

renderHistoryCallout calls queryDtcHistory(vin, null), so Rust groups all historical lines before JavaScript merely checks whether each bucket's latest timestamp is within 14 days. If a code appeared 50 times last year and once today, the banner reports 51 occurrences in the past 14 days; older lines therefore still affect the surfaced result. Pass the cutoff into the query or document this as an all-time count for codes observed recently.

Useful? React with 👍 / 👎.

Comment on lines +52 to +53
| `status` | Raw status byte from UDS `0x19 02`. The displayed `status_text` is derived from this. |
| `status_text` | Human-readable status (e.g. `"confirmed"`, `"pending"`, `"intermittent"`). |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Document the status byte's actual KWP source

The production read_dtcs path sends KWP 18 02 FF FF, not UDS 0x19 02, and its formatter emits combinations of test failed, confirmed, stored, and current (or logged), rather than the documented pending and intermittent examples. Users comparing this file with a transport capture or interpreting status bits will therefore use the wrong protocol and labels; describe the KWP response and the actual formatter output.

Useful? React with 👍 / 👎.

Comment on lines +39 to +40
One JSON object per line. Forward-compatible: serde ignores unknown
fields on read, so a future schema bump won't break old files.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Qualify the forward-compatibility guarantee

Serde's unknown-field tolerance only lets an older reader ignore extra fields; it does not let a future HistoryLine with a new required field deserialize existing records. Because every current field is required and malformed records are silently counted as skipped, such a schema change would make the user's old history disappear from queries despite this guarantee. Require defaults or migration for future fields, or narrow the documented promise accordingly.

Useful? React with 👍 / 👎.

|---|---|
| `ts_iso` | UTC timestamp of the read (zero-padded, ISO-8601, sortable as a string). |
| `vin` | VIN of the car when the read happened, or `null` if no VIN was known. |
| `address` | UDS target id of the module that reported the fault (e.g. `0x12` for DME, `0x18` for EGS). |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Clarify that serialized module addresses are decimal

Serde serializes the u8 address as a decimal JSON number, so the sample's literal 18 represents 0x12 (DME), not the 0x18 EGS address presented in this table. A user filtering or hand-editing the JSONL for EGS based on the documented hexadecimal examples would select the wrong module; state the decimal encoding or include both forms, such as 24 (0x18) for EGS.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant