Compare commits
2 Commits
b01c63f6a8
...
10988d98df
| Author | SHA1 | Date | |
|---|---|---|---|
| 10988d98df | |||
| e72036e48d |
@@ -2,7 +2,7 @@
|
||||
|
||||
Read and send mail for an Arizona Computer Guru mailbox via Microsoft Graph, using the shared **Claude-MSP-Access** app. Defaults to the mailbox of the user running it (from `identity.json`).
|
||||
|
||||
> **[BLOCKED 2026-06-14]** The `Claude-MSP-Access` app (`fabb3421`) was **DELETED** from the azcomputerguru.com tenant, so every token request returns **AADSTS700016** and this command cannot read or send until a replacement mail-capable app is provisioned. Decision (2026-06-15): the replacement is the **Exchange Operator** suite tier (`exchange-op`, `b43e7342-5b4b-492f-890f-bb5a4f7f40e9`) once `Mail.Send` (+ optionally `Mail.ReadWrite`/`Contacts`) is added to its manifest and consented — Mail.Send's real use is IR victim-notification during mailbox takeovers, so it lives in the suite. NOT yet provisioned. If a token fails with `AADSTS700016`, this is why — do not retry; surface this note. When provisioned, repoint `client_id` (API Configuration + the `py` helper) to `b43e7342...` and the vault path to `computerguru-exchange-operator.sops.yaml`. See `errorlog.md` and `remediation-tool/references/gotchas.md`.
|
||||
> **Mail path (working — repointed 2026-06-17).** `/mailbox` uses the dedicated single-tenant **ComputerGuru Mailbox** app (`1873b1b0-3377-485c-a848-bae9b2f8f1f5`; vault `msp-tools/computerguru-mailbox.sops.yaml`; Mail.ReadWrite + Mail.Send + Contacts.ReadWrite; azcomputerguru.com only). Tokens come from the suite tool: `bash .claude/skills/remediation-tool/scripts/get-token.sh azcomputerguru.com mailbox` (cert-preferred, secret fallback, 55-min cache). This **replaces the deleted `fabb3421`** (Claude-MSP-Access), removed from the tenant 2026-06-14 — it returns **AADSTS700016**; do NOT reintroduce it. The mailbox app's service principal is **disabled when idle**: on a token 401 "account is disabled", enable the SP, then retry.
|
||||
|
||||
## Usage
|
||||
|
||||
@@ -21,15 +21,15 @@ Read and send mail for an Arizona Computer Guru mailbox via Microsoft Graph, usi
|
||||
|
||||
Microsoft Graph access to ACG's own mailboxes (azcomputerguru.com tenant). Reading is unrestricted; **sending and replying are always gated** behind a full draft preview + explicit confirmation. Sends go out *as* the mailbox owner (your `From:`), saved to your Sent Items.
|
||||
|
||||
**Scope boundary:** this is for ACG's OWN mailboxes. For reading a CLIENT tenant's mailboxes (breach checks, rule audits), use `/remediation-tool` — same Graph app (`fabb3421`), different purpose.
|
||||
**Scope boundary:** this is for ACG's OWN mailboxes (the dedicated mailbox app, azcomputerguru.com only). For reading a CLIENT tenant's mailboxes (breach checks, rule audits), use `/remediation-tool` — the tiered Security Investigator / Exchange Operator apps, different apps and purpose.
|
||||
|
||||
## API Configuration
|
||||
|
||||
- **App:** Claude-MSP-Access (Graph API), `client_id = fabb3421-8b34-484b-bc17-e46de9703418` (application permissions incl. Mail.ReadWrite + Mail.Send, tenant-wide).
|
||||
- **App:** ComputerGuru Mailbox (dedicated single-tenant Graph app), `client_id = 1873b1b0-3377-485c-a848-bae9b2f8f1f5` (Mail.ReadWrite + Mail.Send + Contacts.ReadWrite, azcomputerguru.com only). Replaces the deleted `fabb3421`.
|
||||
- **Tenant:** `azcomputerguru.com`
|
||||
- **Secret:** SOPS vault `msp-tools/claude-msp-access-graph-api.sops.yaml` -> `credentials.credential`. Read with command substitution; never hardcode, never print it.
|
||||
- **Token:** acquire via the suite tool — `bash .claude/skills/remediation-tool/scripts/get-token.sh azcomputerguru.com mailbox` (cert-preferred, secret fallback, 55-min cache in `/tmp/remediation-tool/<tenant>/mailbox.jwt`). Do NOT roll your own `client_credentials` here. Credential vault entry: `msp-tools/computerguru-mailbox.sops.yaml`.
|
||||
- **SP idle-toggle:** the mailbox app's service principal is disabled when idle. On a token 401 "account is disabled", enable the SP, then retry.
|
||||
- **Mailbox (default):** resolved from `identity.json` `.user` -> `mike` = `mike@azcomputerguru.com`, `howard` = `howard@azcomputerguru.com`. Override with `--as <email>`.
|
||||
- **Token:** `client_credentials`, scope `https://graph.microsoft.com/.default`. Cache to `$REPO_ROOT/.claude/tmp/mailbox-token.json` (gitignored), ~55 min TTL.
|
||||
- **Graph base:** `https://graph.microsoft.com/v1.0`
|
||||
|
||||
## Hard Rules (sending email is irreversible — no exceptions)
|
||||
@@ -72,29 +72,18 @@ sys.stdout.reconfigure(encoding='utf-8', errors='replace')
|
||||
MAILBOX = sys.argv[1]
|
||||
REPO = subprocess.run(["bash","-lc","jq -r .claudetools_root \"$HOME/.claude/identity.json\" 2>/dev/null || git rev-parse --show-toplevel"],
|
||||
capture_output=True, text=True).stdout.strip() or r"D:\claudetools"
|
||||
CLIENT_ID = "fabb3421-8b34-484b-bc17-e46de9703418"
|
||||
TENANT = "azcomputerguru.com"
|
||||
GRAPH = "https://graph.microsoft.com/v1.0"
|
||||
CACHE = os.path.join(REPO, ".claude", "tmp", "mailbox-token.json")
|
||||
|
||||
def secret():
|
||||
r = subprocess.run(["bash", os.path.join(REPO,".claude","scripts","vault.sh"),
|
||||
"get-field","msp-tools/claude-msp-access-graph-api.sops.yaml","credentials.credential"],
|
||||
capture_output=True, text=True)
|
||||
return r.stdout.strip()
|
||||
|
||||
def token():
|
||||
try:
|
||||
c = json.load(open(CACHE))
|
||||
if c.get("exp",0) - time.time() > 120: return c["tok"]
|
||||
except Exception: pass
|
||||
data = urllib.parse.urlencode({"client_id":CLIENT_ID,"client_secret":secret(),
|
||||
"scope":"https://graph.microsoft.com/.default","grant_type":"client_credentials"}).encode()
|
||||
t = json.loads(urllib.request.urlopen(urllib.request.Request(
|
||||
f"https://login.microsoftonline.com/{TENANT}/oauth2/v2.0/token", data=data), timeout=20).read())
|
||||
os.makedirs(os.path.dirname(CACHE), exist_ok=True)
|
||||
json.dump({"tok":t["access_token"],"exp":time.time()+int(t.get("expires_in",3600))}, open(CACHE,"w"))
|
||||
return t["access_token"]
|
||||
# Delegate to the suite token tool: dedicated mailbox app (1873b1b0) via vault
|
||||
# msp-tools/computerguru-mailbox.sops.yaml. Cert-preferred, secret fallback, 55-min cache.
|
||||
# Do NOT reintroduce the deleted fabb3421 / inline client_credentials here.
|
||||
gt = os.path.join(REPO, ".claude", "skills", "remediation-tool", "scripts", "get-token.sh")
|
||||
r = subprocess.run(["bash", gt, "azcomputerguru.com", "mailbox"], capture_output=True, text=True)
|
||||
tok = (r.stdout.strip().splitlines() or [""])[-1].strip()
|
||||
if not tok:
|
||||
raise SystemExit("[ERROR] mailbox token failed (SP disabled? enable it, then retry). stderr: " + r.stderr[-400:])
|
||||
return tok
|
||||
|
||||
def graph(method, path, body=None):
|
||||
h = {"Authorization": f"Bearer {token()}", "Content-Type":"application/json"}
|
||||
|
||||
@@ -8,7 +8,9 @@ When the user says "365 remediation tool" or "remediation tool", they mean ACG's
|
||||
|
||||
**App suite (current — tiered):** Security Investigator `bfbc12a4` (Graph read + EXO read), Exchange Operator `b43e7342` (EXO write), User Manager `64fac46b` (user/license/MFA/pw write), Tenant Admin `709e6eed` (high-priv directory), Defender Add-on `dbf8ad1a` (MDE-licensed tenants ONLY). Secrets in `msp-tools/computerguru-*.sops.yaml`. Client-credentials auth; tenant ID via OpenID discovery (or the `*.onmicrosoft.com` domain when the primary domain isn't verified). Use the lowest tier needed. Each app is consented per-tenant (URLs in `references/gotchas.md`); privileged ops also need directory roles assigned to the SP in that tenant (`onboard-tenant.sh`).
|
||||
|
||||
**DEPRECATED — do NOT consent to customer tenants:** `fabb3421` ("AI Remediation" / "Claude-MSP-Access", secret `msp-tools/claude-msp-access-graph-api.sops.yaml`). ~159 perms incl. Defender ATP, so admin consent **breaks with AADSTS650052 on any tenant lacking an MDE license**. It still works where already consented (e.g. ACG's own tenant — the `/mailbox` skill reads our own mailboxes with it), but new onboarding MUST use the tiered suite. (Corrected 2026-05-27 during Quantum onboarding — nearly consented the deprecated app to a no-MDE tenant.)
|
||||
**DELETED — gone, do not reference:** `fabb3421` ("AI Remediation" / "Claude-MSP-Access", secret `msp-tools/claude-msp-access-graph-api.sops.yaml`). Removed from the azcomputerguru.com tenant **2026-06-14**; every token request now returns **AADSTS700016**. It previously had ~159 perms incl. Defender ATP (admin consent broke with AADSTS650052 on no-MDE tenants). Any skill still pointing at it is broken — repoint to the suite. (Original deprecation: 2026-05-27 Quantum onboarding.)
|
||||
|
||||
**ACG OWN-mailbox reads/sends (`/mailbox`) — dedicated app `1873b1b0-3377-485c-a848-bae9b2f8f1f5`** ("ComputerGuru Mailbox", vault `msp-tools/computerguru-mailbox.sops.yaml`, Mail.ReadWrite + Mail.Send + Contacts.ReadWrite, azcomputerguru.com single-tenant). Token via `get-token.sh azcomputerguru.com mailbox` (a tier in get-token.sh; cert-preferred). This is what REPLACED fabb3421 for `/mailbox`. Its SP is **disabled when idle** → a token 401 "account is disabled" means enable the SP first. (`/mailbox` command doc repointed to it 2026-06-17 — it had been left on the dead fabb3421.)
|
||||
|
||||
**Why (original):** user clarified "remediation tool" != CIPP after a wrong CIPP navigation. **How to apply:** prefer the `/remediation-tool` skill — it wraps tenant resolution, token caching, breach check, sweep, gated remediation, and consent/onboarding URLs (`references/gotchas.md`, `graph-endpoints.md`, `checklist.md`).
|
||||
|
||||
|
||||
@@ -17,6 +17,8 @@ Categories (the `[type]` tag): _(none)_ = skill/command execution failure ·
|
||||
|
||||
<!-- Append entries below this line -->
|
||||
|
||||
2026-06-17 | GURU-5070 | mailbox/365-mail | [correction] claimed in a prior session that /mailbox skill + memories were repointed off the deleted fabb3421 to the 365-mail suite, but mailbox.md still hardwired fabb3421 (token 401 AADSTS700016). Correct app is the dedicated ComputerGuru Mailbox app 1873b1b0 via get-token.sh 'mailbox' tier (cert auth); repointed mailbox.md + feedback_365_remediation_tool.md 2026-06-17. Lesson: verify the edit actually landed before reporting it done.
|
||||
|
||||
2026-06-17 | Howard-Home | wiki-compile/coord | [friction] skill doc Phase 6 shows 'lock release claudetools wiki/<type>/<slug>' but coord.py takes 'lock release <id>'; wasted a round-trip. Capture the lock id from claim output and release by id. [ctx: ref=wiki-compile-skill]
|
||||
|
||||
2026-06-17 | Howard-Home | unifi/controller-write | [friction] UniFi OS controller PUT (rest/device port_overrides) returned 403 without CSRF. Fix: login with -D headers, read 'x-updated-csrf-token' (or decode csrfToken from TOKEN cookie JWT), send as X-CSRF-Token on PUT/POST/DELETE
|
||||
|
||||
134
projects/dataforth-dos/DATASHEET-FIX-SPEC-2026-06-17.md
Normal file
134
projects/dataforth-dos/DATASHEET-FIX-SPEC-2026-06-17.md
Normal file
@@ -0,0 +1,134 @@
|
||||
# Dataforth Test-Datasheet Pipeline — Fix Spec (hardened)
|
||||
|
||||
**Date:** 2026-06-17 · **Host:** AD2 (`C:\Shares\testdatadb`, Node + PostgreSQL 18) · **Status:** SPEC for review — implementation driven on AD2
|
||||
**Inputs:** AD2 diagnosis (`DATASHEET-RTD-BUG-DIAGNOSIS`, `PARSING-FIDELITY-VERDICT`, `MISSING-UNITS-REPORT`, `CONFLICT-RULE-FIX-PROPOSAL`) + independent multi-AI review (Grok adversarial + Gemini). Owner direction on retest handling (Mike, 2026-06-17).
|
||||
|
||||
All defects are in the **regeneration/ingestion** pipeline that replaced the cryptolocker-destroyed original parser/publisher. Source test data is intact (DB matches staged originals across 11,239 records, 0 parse faults).
|
||||
|
||||
---
|
||||
|
||||
## 0. Ground truth (verified live, 2026-06-17)
|
||||
- `test_records`: 473,780 rows = 473,780 distinct `serial_number` → **exactly one row per serial**.
|
||||
- Unique constraints: `uq_test_records_sn` UNIQUE(serial_number) [operative]; redundant UNIQUE(log_type,model_number,serial_number,test_date,test_station).
|
||||
- Columns incl. `raw_data` (verbatim .DAT), `overall_result`, `api_uploaded_at`, `forweb_exported_at`, `datasheet_exported_at`, `work_order`.
|
||||
- **Deployed `database/import.js` uses `ON CONFLICT (serial_number)`** with `WHERE overall_result='FAIL' OR (EXCLUDED PASS AND EXCLUDED.test_date > test_records.test_date)`.
|
||||
- **WARNING — repo drift:** the repo copy `…/implementation/database/import.js` is STALE (shows a 5-tuple `ON CONFLICT`). **Edit the DEPLOYED file; reconcile the repo copy after.** Verify every file's deployed content before changing it.
|
||||
|
||||
## 0a. CROSS-CUTTING — re-publication discipline (MANDATORY for any fix that changes cert text)
|
||||
Every fix below that alters rendered output must be published deliberately, not by blanket cache-clear:
|
||||
1. **Diff before re-push.** For each candidate serial, render OLD vs NEW and only act where output actually changes. Do not clear `api_uploaded_at`/`forweb_exported_at` for unchanged renders.
|
||||
2. **Re-POST semantics.** Hoffman bulk API is idempotent — returns `Unchanged` when content matches, overwrites when it differs (per diagnosis §6). Confirm this holds before bulk re-push; watch for dedup/version behavior.
|
||||
3. **Targeted, staged rollout.** Re-publish in bounded batches (start with the Phytec 102), confirm counts, then widen. Log every batch.
|
||||
4. **Rollback.** Keep the prior rendered text (or the prior template commit) for every re-published serial so a bad batch can be reverted.
|
||||
5. **Audit framing.** A cert's text changing after initial publication is a bug correction — record it (ticket #32441 + an internal change log of affected serial ranges) so it's defensible in an audit.
|
||||
|
||||
---
|
||||
|
||||
## Fix 1 — Defect A: RTD input labeled resistance, not temperature (the audit finding)
|
||||
**File:** `templates/datasheet-exact.js` · **Scope:** ~24,000 certs (8B35, DSCA34, SCM5B34/35) · **Status:** fix written, needs scope/ground-truth proof.
|
||||
|
||||
**Root cause:** `getSensorNum()` returns 7 for RTD sentypes (`s.includes('RTD')`); two branches on `sensorNum===7` emit `' Rin (ohms)'` header + unsigned value. Dataforth RTD certs report the input as Temperature (deg C); raw_data stimulus is already deg C.
|
||||
|
||||
**Approach (AD2 diff):** fold `sensorNum===7` into the temperature branch (3–6) for header (`' Temp. (C)'`) and value (`formatSigned`). Leave the `i===13` ohm/ohm Lead-R override intact.
|
||||
|
||||
**Hardening (multi-AI):**
|
||||
- "15 renders changed / 0 non-RTD" proves a branch moved, **not** correctness. Before deploy: (a) **byte-compare** the fixed render against the staged original `.TXT` for a real RTD sample (8B35 incl. SN 179553-13, DSCA34, SCM5B34/35) — require exact match; (b) **confirm RTD-detection coverage**: count RTD-family rows in the DB (`model_number LIKE '%34%'/'%35%'` etc.) and confirm the regenerator's `s.includes('RTD')` actually classifies all of them as 7 (only 15 changing across 184 renders may mean the sample was RTD-thin, or some RTD sentypes aren't matched).
|
||||
|
||||
**Risk:** LOW-MODERATE (localized; main risk is under-detecting which modules are RTD). **Deploy:** after byte-match + coverage check; then targeted re-push (Phytec 102 first).
|
||||
|
||||
---
|
||||
|
||||
## Fix 2 — Defect B: DSCA Final-Test table wrong / dropped lines
|
||||
**File:** `templates/datasheet-exact.js` (`DATA_LINES['DSCA']`, `buildTSpecs()` DSCA branch, accuracy-block titles) · **Scope:** up to ~78,000 DSCA certs · **Status:** NEEDS DESIGN — highest structural risk.
|
||||
|
||||
**Root cause:** a single hardcoded `DATA_LINES['DSCA']` + single DSCA `buildTSpecs` branch; real DSCA modules have **per-subtype** Final-Test layouts → wrong names, garbage specs (`< 0 mA`, `+/- 0 %`), rows misaligned, lines dropped (e.g. Output Noise on DSCA38-05). Accuracy block also uses 5B/8B titles (`Vout (V)` / `====`) instead of DSCA's (`Output (V|mA)` / `----`).
|
||||
|
||||
**Approach (multi-AI consensus — REVISED from AD2's DSCFIN.DAT idea):**
|
||||
- **Do NOT reverse-engineer `DSCFIN.DAT`** (legacy DOS config; QB writer has hardcoded overrides outside the config → guaranteed edge-case drift).
|
||||
- **Derive per-subtype templates from the staged original `.TXT`** (the actual correct customer certs are ground truth): group staged DSCA `.TXT` by subtype, extract each subtype's Final-Test parameter name/unit/spec list and accuracy titles directly.
|
||||
- **Key subtype selection** on `model_number` (prefix) + `SENTYPE` / output-signal type. Build an explicit subtype→layout map.
|
||||
- Fix the DSCA accuracy block titles/separators (`Output (V|mA)`, `----`).
|
||||
|
||||
**Validation (hard gate):** generate ALL DSCA certs and **byte-for-byte diff vs the staged originals** across every subtype; zero-delta required. Any subtype with no staged original → flag, do not guess.
|
||||
|
||||
**Risk:** HIGH (largest population + wrong numeric labels/limits). The longer pole; do after 1/3/4. **Deploy:** only after zero-delta validation per subtype; re-publish in batches with diff-gating.
|
||||
|
||||
---
|
||||
|
||||
## Fix 3 — Retest handling: latest test supersedes (OWNER DIRECTION + hardening)
|
||||
**File:** deployed `database/import.js` (`ON CONFLICT (serial_number)` WHERE clause) · **Scope:** ~311 stuck units now + all future retests · **Status:** design owner-set, implementation hardened.
|
||||
|
||||
**Owner rule (Mike):** one row per serial; a new test on the SAME unit (same model / "everything else checks out") **supersedes anything prior — latest test wins**. A reused serial on a DIFFERENT product is NOT the same unit — recognize the collision, don't blindly overwrite.
|
||||
|
||||
**Why the current rule fails:** strictly-greater date + date-only granularity → same-day reruns can't replace (~311 stuck on a non-final run).
|
||||
|
||||
**Approach (hardened — both AIs refute pure scan-order as the recency signal):**
|
||||
- Conflict on `serial_number`. Update when the incoming row is the SAME unit and is genuinely newer:
|
||||
- `EXCLUDED.model_number = test_records.model_number` (same unit) **AND** `EXCLUDED.raw_data IS DISTINCT FROM test_records.raw_data` (real change; avoids re-push churn) **AND** incoming is at least as new.
|
||||
- **Recency must not rely on import scan order alone.** Use `EXCLUDED.test_date >= test_records.test_date`, and break same-date ties with a **monotonic signal captured at parse time** — source `.DAT` mtime or an ingest sequence number (add a column, e.g. `ingest_seq` / `source_mtime`). Last-by-(date, tiebreaker) wins.
|
||||
- **Collision handling (different `model_number`, same serial):** do NOT overwrite. Route to a `test_records_quarantine` table (or a flagged status) + alert. These are the reused generic serials (`1-1`, `1-2`) — genuinely different units.
|
||||
- Keep the `FAIL → PASS` override.
|
||||
|
||||
**Validation:** ingest the owner's **4-retest sample IN REVERSE chronological order**; final DB state MUST be the mathematically newest run. Re-run `tools/validate-parsing.js`; same-day violations → ~0. Confirm the 311 settle on the latest run.
|
||||
|
||||
**Risk:** HIGH if scan-order is trusted (a future bulk re-import could overwrite newer with older across the whole DB). MITIGATED by the date+tiebreaker rule. **Deploy:** after the reverse-order sample passes; then re-import + diff-gated re-push of the 311.
|
||||
|
||||
---
|
||||
|
||||
## Fix 4 — Importer drops letter-prefixed encoded serials
|
||||
**File:** `parsers/multiline.js` (serial/date regex) · **Scope:** ~9,510 records / 840 serials / 141 models · **Status:** needs design (PK-boundary mutation).
|
||||
|
||||
**Root cause:** `line.match(/^"(\d+-\d+[A-Za-z]?)","(\d{2}-\d{2}-\d{4})"$/)` — `\d+-\d+` requires leading digits, so DOS 8.3-encoded serials (`10243-1` → `A243-1`; first two digits → letter, `prefix = charCodeAt(0)-55`) never match → whole record silently dropped.
|
||||
|
||||
**Approach (hardened — keep the literal, both AIs):**
|
||||
- Widen regex to allow an optional leading letter: `/^"([A-Za-z]?\d+-\d+[A-Za-z]?)","(\d{2}-\d{2}-\d{4})"$/`.
|
||||
- **Store BOTH:** add `raw_serial_number` (the literal file bytes, e.g. `A243-1`) and keep `serial_number` = decoded numeric (`10243-1`). UNIQUE stays on `serial_number`. Preserves a perfect audit trail.
|
||||
- Decode only when the captured serial matches `^[A-Za-z]\d`.
|
||||
|
||||
**Pre-flight (MANDATORY before any import):** run the parser over the ~9,510 dropped records read-only → emit CSV `raw_serial, decoded_serial, model_number`. **Search decoded serials for collisions against the existing 473,780 rows.** For each collision decide policy (a decoded `A243-1` colliding with a genuine `10243-1` of a different model = a real conflict — quarantine, don't merge two physical units). Only proceed once collisions are enumerated and a policy set.
|
||||
|
||||
**Risk:** MODERATE (transforms the uniqueness key + customer lookup id). **Deploy:** after the collision CSV is clean/resolved; the re-import re-exercises the upsert path → run under the Fix-3 rule, diff-gated re-push.
|
||||
|
||||
---
|
||||
|
||||
## Fix 5 — Backfill 379 cryptolocker-era units from staged originals
|
||||
**Scope:** 379 units (Oct 2025–Jan 2026, 3 stations), no surviving `.DAT`; staged `.TXT` exist · **Status:** operational.
|
||||
|
||||
**Key fact:** the staged `.TXT` were produced by the ORIGINAL (pre-crypto) renderer → already correct (no Defect A/B).
|
||||
|
||||
**Approach (both AIs — publish directly, don't round-trip):**
|
||||
- Add `legacy_cert_text` column. Insert the 379 with `raw_data = NULL`, `legacy_cert_text` = the staged `.TXT` content.
|
||||
- Publisher serves `legacy_cert_text` when `raw_data IS NULL` (bypasses regeneration).
|
||||
- Do NOT reverse `.TXT`→raw_data→re-render (two translation-loss points; guarantees drift).
|
||||
|
||||
**Validation:** cross-reference the 379 serials against the ERP / work-order system to confirm they are valid shipped units before exposing via the API. Spot-check rendered vs staged text.
|
||||
|
||||
**Consistency note:** these rows are permanently "original-renderer" output, divergent from what the fixed template would emit. Acceptable (and safer) given no raw source; document the class.
|
||||
|
||||
**Risk:** LOW (bounded 379, immutable text) — but zero tolerance for wrong text (no raw fallback). **Deploy:** after ERP cross-check.
|
||||
|
||||
---
|
||||
|
||||
## Risk ranking (combined) & recommended order
|
||||
| Rank | Fix | Why |
|
||||
|---|---|---|
|
||||
| 1 (tie) | **Fix 3** retest | scan-order recency could corrupt DB-wide on any future re-import (Gemini #1) |
|
||||
| 1 (tie) | **Fix 2** DSCA | largest scope + wrong numeric labels/limits; design-heavy (Grok #1) |
|
||||
| 3 | **Fix 4** serials | mutates the uniqueness/lookup key; merge risk |
|
||||
| 4 | **Fix 1** RTD | localized; risk is under-scoping RTD detection |
|
||||
| 5 | **Fix 5** backfill | small, immutable, but no raw fallback |
|
||||
|
||||
**Suggested execution order (lowest-risk customer win first, hardest last):**
|
||||
1. **Fix 1 (RTD label)** — after byte-match + scope check → clears the audit + corrects the Phytec 102 (deploy tonight candidate).
|
||||
2. Re-publish Phytec 102 (diff-gated).
|
||||
3. **Fix 4 (serial decode)** — after clean collision CSV.
|
||||
4. **Fix 3 (retest rule)** — after reverse-order sample passes.
|
||||
5. **Fix 2 (DSCA rebuild)** — after per-subtype zero-delta validation.
|
||||
6. **Fix 5 (backfill 379)** — after ERP cross-check.
|
||||
|
||||
## Open items needing an owner decision
|
||||
- **Fix 3:** add a real tie-breaker column (`ingest_seq`/`source_mtime`) vs accept `test_date >=` + scan-order? (recommend the column.)
|
||||
- **Fix 3/4 collisions:** quarantine table + alert vs reject-and-log? (recommend quarantine table.)
|
||||
- **Fix 4:** add `raw_serial_number` column (recommended) — schema change.
|
||||
- **Fix 5:** add `legacy_cert_text` column + publisher branch (recommended) — schema + publisher change.
|
||||
- **Tonight:** scope = Fix 1 + Phytec re-push only (smallest safe win); the rest staged after.
|
||||
Reference in New Issue
Block a user