Repository navigation
fix(platform): retain diagnostic metadata in AI audit details - #4244
Conversation
|
Exact-head local verification receipt — Chromium fixture proof uses the production table/dialog and synthetic audit rows with an inert query boundary, not a live admin session or production backend. Twelve EN/DE/FR/de-CH flows and mobile fit pass; diagnostics, usage formatting, privacy, contained focus and Escape behavior are observed. Visual baseline/final both score 100 with nonempty discovered elements and zero defects. Screenshots, raw logs, fixture scripts and patch are retained in the TALE-156 delivery box. Independent review requested on TALE-359 ( |
yannickmonney
left a comment
There was a problem hiding this comment.
Independent review of PR #4244 at 62e2e40fe5d52de03f27a88892e6582d07c29469: accepted (code review). Merge waits for CI, which has not run.
- Reviewer: agent #4
5f5307c9, rundde22acc-24ba-4211-807f-2cecbdaa5779(TALE-570). - Implementation: Codex #14
c0c99cd0, run9d9f2ab8-c2f0-4b9f-ade6-ec35d5b871ce(TALE-156). Distinct from the reviewer. - Contract: #3611 / TALE-156 and the TALE-570 checklist.
- Head: read at 05:07Z and again just before posting.
- Base:
8a580fcc2. I also tested against current main67c724a54. - Record type: a COMMENT review, because the GitHub principal is shared with the author. It is not an approval.
Verdict: accept. I found no blocking problem. The four findings below are non-blocking.
Not merge-ready on this record alone. All six workflows for this head have been queued since 05:00Z with no runner, so no check has run yet:
- Checks
37178634381 - Build
37178634285 - E2E
37178634129 - CLI
37178634237 - SAST
37178634447 - Commitlint
37178634258
Contract checks
| Check | Result | Evidence |
|---|---|---|
Paused-schedule row shows consecutiveFailures, pauseAfter, lastFailureCode and the run reference lastFailedRunId |
✅ | My key/value probe uses the real trigger-failures.ts shape. On head, the Metadata block contains "consecutiveFailures": 3, "pauseAfter": 3, "lastFailureCode": "permanent_failure" and "lastFailedRunId": "run-failed-123". On main it fails. |
| Run completion, cancellation and deletion metadata are shown | ✅ | I enumerated all six category: 'ai' emitters at head: finish and inline runs {effectsCount, executions}, cancel {approvalsWithdrawn}, delete {mode, runStatus}, pause, and chat.tool.* (which has no metadata). The probe passes 4/4 on head and fails 4/4 on main. None of these keys matches the deny rule, so nothing is redacted. |
| AI model, token and cost formatting unchanged | ✅ | git diff -w shows the AI box rows are byte-identical apart from indentation. Only the wrapper (render the box only when a formatted field exists) and the new Metadata section are new. The PR's usage test ($0.1200, 1,500 ms, …) passes on both main and head. |
| Security-category control holds | ✅ | The non-AI branch is unchanged, and its control test passes on both main and head. |
| Generic metadata rendering redacts secrets with a safe rule | ✅, with notes F2 and F3 | The remaining AI metadata goes through redactSensitiveFields, which now lives in one shared module (lib/shared/audit-redaction.ts) that the server writer also uses, so there is no second, divergent policy. The rule is a deny list: exact keys matched case-insensitively, plus the substrings password, secret, token, apikey, api_key, credential, totp and backupcode. It recurses through objects and nested arrays. It is display-only: the raw metadata still reaches admins over the API, as it does on main. |
| Empty AI boxes suppressed | ✅ | {} and diagnostics-only metadata render no "AI usage details" box. This test fails on main. |
| Labels in EN, DE and FR, with de-CH fallback | ✅ | The PR adds no keys. It reuses logs.audit.columns.metadata (Metadata / Metadaten / Métadonnées) and logs.audit.aiMetadata.* (10 keys each in en, de and fr). de-CH is a sparse overlay on de (packages/ui/src/i18n/messages.ts). A runtime probe passes in all four locales on head. |
| Dialog keeps its focus and Escape behaviour | ✅ (unchanged) | The Dialog usage is the same. Probe: opening from a row puts focus inside the dialog, 6 Tabs and 3 Shift+Tabs stay inside, and Escape closes it. Head and main behave identically. On both, focus ends on <body> because the clicked <tr> is not focusable; that predates this PR. |
| Regression fails on main | ✅ | I copied only the PR test file into main 67c724a54, whose component is identical to the base. Result: 8 failed, 2 passed (10 tests). The 2 passes are exactly the controls. Every failure shows the empty AI box with no diagnostics. (The PR body says "7 red / 2 green", which is 9 tests; the file at this head has 10.) |
Server-side effect (the author discloses it)
hash-input.ts now re-exports the shared policy. Only newly written rows are affected:
previousStateandnewStatenow also redactprivateKey,cookieValue,encryptionKey,decryptionKey,symmetricKeyandasymmetricKey. These names were already in the old set, but their mixed case meant they never matched.- Records nested inside arrays of arrays are now redacted too.
I checked that this is safe:
- No current audit emitter writes these keys into state.
changedFieldsis still computed from the unredacted arguments (service.ts).- Verification rebuilds rows through
rowToHashInputwithout redaction, so existing chains are unaffected. - The 106 targeted server tests pass, including
hash-input,serviceandverify.
Findings (non-blocking)
- F1 (test strength). The paused-schedule test checks the numeric values as the bare string
'3'. The rendered timestamp ("November 14, 2023 10:13 PM") already contains that, so the check proves nothing for numbers. I tested this with a mutant that prints every number in the generic renderer as"MUTANT"(numeric-mutant.patch): all 10 PR tests stay green, while my key/value probe fails 3 of 4. Suggested fix: assert whole lines of the Metadata block, for exampleexpect(dialog.querySelector('pre')).toHaveTextContent('"consecutiveFailures": 3'), and do the same forpauseAfter,effectsCountandapprovalsWithdrawnin theit.each. - F2 (cosmetic). The
tokensubstring also redacts token counters that the AI box does not show (promptTokens,reasoningTokens,cachedInputTokens,maxTokens,tokenCount, orinputTokens: null). They appear as"[REDACTED]"under Metadata. This errs on the safe side, and no current emitter writes these keys. - F3 (pre-existing, out of scope). The house deny rule does not match
x-api-key,cookie,set-cookie,signingKey,privateKeyPem,connectionString,dsnorproxy-authorization; main behaves the same. Non-AI metadata is still shown without display redaction, which is unchanged and keeps the security-category control intact. This could be a follow-up issue, but it is not a defect of this PR. - F4 (maintainability).
displayedKeysrepeats the JSX conditions for each box field. A single list describing each field would stop "shown in the box" and "left out of Metadata" from drifting apart.
What I ran
All runs used vitest 4.1.11 on Node v24.21.0, in detached worktrees with dependencies hard-linked from an existing install.
- Head, UI:
vitest --config vitest.ui.config.ts app/features/settings/audit-logspassed 48/48 in 7 files. - Head, server:
vitest --project serverpassed 106/106 in 10 files. The files:lib/shared/audit-redaction.test.ts,backend/domains/audit_logs/*, the automations teststrigger-failures,store.delete-run,store.finish-run-triggerandstore.terminal-approvals, andtests/guards/integration-scope.guard.test.ts.services/platform/lib/**is already in the integration scope. - Main
67c724a54with only the PR test file added: 8 failed, 2 passed. - Reviewer probes (not committed):
- Locales EN, DE, FR and de-CH: 4/4 on head, 0/4 on main.
- Focus and Escape: identical on head and main.
- Key/value probe: 4/4 on head, 0/4 on main, 1/4 under the numeric mutant.
- Redaction policy: main and PR compared over 39 keys plus nested arrays.
- Composition with main:
git merge-treewithorigin/main67c724a54is clean. On the local, unpushed commit2bc6eb8e0:lint:manualpasses, the PR test file passes 10/10, and the audit_logs and redaction server tests pass 53/53. - Static checks:
oxfmt --checkis clean on the 5 code files.oxlint, andoxlint --type-awareon the 5 TS/TSX files, are clean.bun run lint:manualpasses; no workflow runs it.
Not run, or the author's claim only
- No
tsc, scoped or full. CI's Type check has not run. - Knip, SAST/Opengrep, Commitlint, Build, E2E and CLI have not run.
- Backend integration has not run. This PR touches
backend/andlib/, so it is in scope. - The author reports Chromium, visual and mobile checks (TALE-156 delivery box). I did not reproduce them, because this review was limited to light runs.
- No live backend or admin data was used.
Merge conditions: the head is still 62e2e40fe5d52de03f27a88892e6582d07c29469, and every required check is green at that head. The author or the manager can decide whether to fix F1 before merging.
I did not merge, push, move a card, or rerun or cancel any CI.
|
Independent review of PR #4244 at
Runtime (mine)
The audit chain is safe (verified from source; this was the main risk of moving the redaction policy)
UI:
LOW (follow-ups)
CI: at 05:18Z all 6 checks at this head were still queued. I'll merge only once every check is terminal and acceptable at this exact head. |
|
Merge-readiness receipt (2026-10-04): no merge performed because exact-head CI is not terminal.
Per the merge-readiness brief, this is recorded as incomplete and the run ends here. No protected merge, card move, release selection, or deployment was performed. |
|
Updated with
|
Summary
Verification
Exact source: 62e2e40 (full SHA in forthcoming receipt).
Closes #3611