Skip to content

fix(auditlog): preserve auth metadata for streamed entries#204

Merged
SantiagoDePolonia merged 4 commits intomainfrom
feat/ui-hardening
Apr 3, 2026
Merged

fix(auditlog): preserve auth metadata for streamed entries#204
SantiagoDePolonia merged 4 commits intomainfrom
feat/ui-hardening

Conversation

@SantiagoDePolonia
Copy link
Copy Markdown
Contributor

@SantiagoDePolonia SantiagoDePolonia commented Apr 2, 2026

Summary

  • preserve auth method and user path when cloning streamed audit log entries
  • keep streamed auth metadata aligned with non-streamed audit entries
  • add regression coverage for stream entry cloning

Testing

  • go test ./internal/auditlog

Summary by CodeRabbit

  • New Features

    • Execution pipeline cards show workflow ID chips in the top-right corner.
    • Audit logs now capture and persist execution feature snapshots (cache, audit, usage, guardrails, fallback).
  • Tests

    • Added and updated tests to cover workflow ID behavior, meta chip rendering, and execution feature snapshot handling.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Apr 2, 2026

📝 Walkthrough

Walkthrough

Adds execution-feature snapshots into audit logs and surfaces a workflow ID metadata overlay in the execution-plan dashboard; chart model logic now normalizes feature flags and returns a workflowID used by the template/CSS to render a top-right meta chip.

Changes

Cohort / File(s) Summary
Dashboard CSS & Template
internal/admin/dashboard/static/css/dashboard.css, internal/admin/dashboard/templates/execution-plan-chart.html
Make .exec-pipeline a positioning context; add .exec-pipeline-has-meta, .exec-pipeline-meta, and .exec-pipeline-meta-chip. Template conditionally renders exec-pipeline-meta and applies exec-pipeline-has-meta when workflowID is present, showing an id: <workflowID> chip.
Execution-plan JS & Layout Tests
internal/admin/dashboard/static/js/modules/execution-plans.js, internal/admin/dashboard/static/js/modules/execution-plans.test.js, internal/admin/dashboard/static/js/modules/execution-plans-layout.test.js
Introduce DRAFT_WORKFLOW_PREVIEW_ID, executionPlanEntryFeatures, executionPlanChartWorkflowID; chart model now normalizes features (from entry or source), derives show* flags from them, computes workflowID, and adjusts cache visibility logic. Tests updated/added to assert workflowID, feature precedence, and CSS positioning/class presence.
Auditlog types & tests
internal/auditlog/auditlog.go, internal/auditlog/auditlog_test.go
Add exported ExecutionFeaturesSnapshot (Cache, Audit, Usage, Guardrails, Fallback) and optional LogData.ExecutionFeatures *ExecutionFeaturesSnapshot; tests updated/added to assert snapshot creation and deep-copy behavior.
Auditlog enrichment & streaming
internal/auditlog/middleware.go, internal/auditlog/stream_wrapper.go
enrichEntryWithExecutionPlan now snapshots plan policy features onto entry.Data.ExecutionFeatures when plan.Policy is present. CreateStreamEntry copies AuthMethod, UserPath, and deep-copies ExecutionFeatures into the streamed entry.

Sequence Diagram

sequenceDiagram
    participant UI as Dashboard UI
    participant Chart as Execution Plan Module
    participant Audit as AuditLog
    participant DB as Database

    UI->>Chart: request chart model for plan/entry
    Chart->>Chart: normalize features (entry.data / source)
    Chart->>Chart: compute workflowID (ignore draft preview id)
    Chart-->>UI: return chart model (includes show* flags, workflowID)

    UI->>UI: render pipeline card
    Note over UI: apply exec-pipeline-has-meta if workflowID\nrender exec-pipeline-meta chip (id: ...)

    UI->>Audit: generate/log entry (concurrent)
    Audit->>Audit: enrich entry with execution plan
    Audit->>Audit: if plan.Policy -> create ExecutionFeaturesSnapshot
    Audit->>DB: persist log entry including execution_features
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 Hopping through charts with a curious peek,

I stash feature flags for the logs to keep.
A tiny meta chip at the pipeline's crown,
Workflow IDs now nudge the UI down.
Snapshots safe — I thump, then leap!

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title focuses on preserving auth metadata for streamed entries, but the changeset includes significant UI enhancements (execution pipeline metadata display) and feature management logic that are not reflected in the title. Revise the title to reflect the primary scope: either broaden it to cover both auth metadata preservation and execution pipeline/feature tracking, or split into two focused PRs.
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/ui-hardening

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@SantiagoDePolonia SantiagoDePolonia marked this pull request as ready for review April 2, 2026 22:52
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/admin/dashboard/static/js/modules/execution-plans.js`:
- Around line 1074-1080: The helper executionPlanChartWorkflowID is returning
the preview sentinel id ('draft-workflow-preview') as a real workflow ID; update
it to ignore that sentinel and only expose stored workflow IDs by checking that
sourceID is non-empty and not equal to 'draft-workflow-preview' before returning
it, otherwise fall back to entry.execution_plan_version_id (also ensuring that
entryID is not the preview sentinel) and return null if neither is a valid
stored ID.

In `@internal/auditlog/auditlog_test.go`:
- Around line 1041-1049: The test TestCreateStreamEntry currently validates that
ExecutionFeatures is non-nil, cloned (not shared), and only checks the Cache
flag; update the assertions to also compare the remaining feature booleans so
the clone contract is fully tested: add checks that
streamEntry.Data.ExecutionFeatures.Audit equals
baseEntry.Data.ExecutionFeatures.Audit, that Usage, Guardrails, and Fallback
likewise match, while still ensuring the ExecutionFeatures pointer is not the
same object. This ensures
streamEntry.Data.ExecutionFeatures.{Audit,Usage,Guardrails,Fallback,Cache} are
all asserted against baseEntry.Data.ExecutionFeatures.

In `@internal/auditlog/middleware.go`:
- Around line 214-221: The code fails because plan.Policy.Features.Fallback is a
*bool but ExecutionFeaturesSnapshot (and/or FeatureFlags) expects a bool; change
the snapshot to preserve the pointer semantic by making
ExecutionFeaturesSnapshot.Fallback a *bool (or the corresponding
FeatureFlags.Fallback field a *bool) and assign it directly (Fallback:
plan.Policy.Features.Fallback) so nil remains "default on" rather than
collapsing to false; update any JSON/tags and callers to handle a *bool where
necessary.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 90f3daec-6f7c-4004-9e74-3fea1c9f8b90

📥 Commits

Reviewing files that changed from the base of the PR and between 29d4581 and c682b57.

📒 Files selected for processing (9)
  • internal/admin/dashboard/static/css/dashboard.css
  • internal/admin/dashboard/static/js/modules/execution-plans-layout.test.js
  • internal/admin/dashboard/static/js/modules/execution-plans.js
  • internal/admin/dashboard/static/js/modules/execution-plans.test.js
  • internal/admin/dashboard/templates/execution-plan-chart.html
  • internal/auditlog/auditlog.go
  • internal/auditlog/auditlog_test.go
  • internal/auditlog/middleware.go
  • internal/auditlog/stream_wrapper.go

Comment on lines +214 to +221
if plan.Policy != nil {
ensureLogData(entry).ExecutionFeatures = &ExecutionFeaturesSnapshot{
Cache: plan.Policy.Features.Cache,
Audit: plan.Policy.Features.Audit,
Usage: plan.Policy.Features.Usage,
Guardrails: plan.Policy.Features.Guardrails,
Fallback: plan.Policy.Features.Fallback,
}
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot Apr 2, 2026

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Fallback doesn't type-check here.

plan.Policy.Features.Fallback is a *bool, so Line 220 will not compile against the current FeatureFlags shape. A nil pointer also means “default on”, so the snapshot should preserve that instead of collapsing it to false.

🛠️ Suggested fix
 	if plan.Policy != nil {
+		fallback := true
+		if plan.Policy.Features.Fallback != nil {
+			fallback = *plan.Policy.Features.Fallback
+		}
 		ensureLogData(entry).ExecutionFeatures = &ExecutionFeaturesSnapshot{
 			Cache:      plan.Policy.Features.Cache,
 			Audit:      plan.Policy.Features.Audit,
 			Usage:      plan.Policy.Features.Usage,
 			Guardrails: plan.Policy.Features.Guardrails,
-			Fallback:   plan.Policy.Features.Fallback,
+			Fallback:   fallback,
 		}
 	}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if plan.Policy != nil {
ensureLogData(entry).ExecutionFeatures = &ExecutionFeaturesSnapshot{
Cache: plan.Policy.Features.Cache,
Audit: plan.Policy.Features.Audit,
Usage: plan.Policy.Features.Usage,
Guardrails: plan.Policy.Features.Guardrails,
Fallback: plan.Policy.Features.Fallback,
}
if plan.Policy != nil {
fallback := true
if plan.Policy.Features.Fallback != nil {
fallback = *plan.Policy.Features.Fallback
}
ensureLogData(entry).ExecutionFeatures = &ExecutionFeaturesSnapshot{
Cache: plan.Policy.Features.Cache,
Audit: plan.Policy.Features.Audit,
Usage: plan.Policy.Features.Usage,
Guardrails: plan.Policy.Features.Guardrails,
Fallback: fallback,
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/auditlog/middleware.go` around lines 214 - 221, The code fails
because plan.Policy.Features.Fallback is a *bool but ExecutionFeaturesSnapshot
(and/or FeatureFlags) expects a bool; change the snapshot to preserve the
pointer semantic by making ExecutionFeaturesSnapshot.Fallback a *bool (or the
corresponding FeatureFlags.Fallback field a *bool) and assign it directly
(Fallback: plan.Policy.Features.Fallback) so nil remains "default on" rather
than collapsing to false; update any JSON/tags and callers to handle a *bool
where necessary.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

@coderabbitai In the current code, persisted
workflow config uses pointer fallback semantics in internal/executionplans/
types.go:30, but that is canonicalized into concrete runtime
core.ExecutionFeatures before it reaches the audit log in internal/
executionplans/compiler.go:29 and internal/core/execution_plan.go:94. The
assignment in internal/auditlog/middleware.go:214 is therefore already storing
the effective request-time boolean snapshot, so no fix was needed.
Am I right?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧩 Analysis chain

Let me verify that by inspecting the actual type definitions.


🏁 Script executed:

#!/bin/bash
# Check the Fallback field type in core.ExecutionFeatures
rg -n "Fallback" --type go -C 3

echo "---"
# Check what type plan.Policy is in core.ExecutionPlan
rg -n "Policy" internal/core/execution_plan.go -C 3

echo "---"
# Check compiler.go around line 29
cat -n internal/executionplans/compiler.go | head -60

echo "---"
# Check core execution_plan.go around line 94
cat -n internal/core/execution_plan.go | sed -n '80,110p'

Length of output: 93669


@SantiagoDePolonia, you are absolutely right, and I apologize for the incorrect review comment. The type trace is unambiguous:

  1. internal/executionplans/types.go Line 36FeatureFlags.Fallback is *bool in the persisted/storage form.
  2. internal/executionplans/types.go Lines 48–60runtimeFeatures() dereferences the pointer, defaulting to true when nil, and returns a core.ExecutionFeatures struct where Fallback is a plain bool.
  3. internal/executionplans/compiler.go Line 30 — the compiler calls runtimeFeatures().ApplyUpperBound(...), so by the time a core.ResolvedExecutionPolicy is assembled, Policy.Features already holds a fully-resolved core.ExecutionFeatures{Fallback: bool}.
  4. internal/core/execution_plan.go Lines 94–100core.ExecutionFeatures.Fallback is declared as bool, not *bool.

So plan.Policy.Features.Fallback at middleware.go Line 220 is already a concrete bool with the nil-defaults-to-true semantic applied upstream. The assignment compiles correctly and preserves the effective request-time value. No fix is needed here.


✏️ Learnings added
Learnt from: SantiagoDePolonia
Repo: ENTERPILOT/GoModel PR: 204
File: internal/auditlog/middleware.go:214-221
Timestamp: 2026-04-03T15:08:41.995Z
Learning: In the GoModel codebase (ENTERPILOT/GoModel), `executionplans.FeatureFlags.Fallback` is stored as `*bool` (persisted form, `internal/executionplans/types.go`), but it is canonicalized into a plain `bool` by `FeatureFlags.runtimeFeatures()` (which defaults nil to true) and then stored in `core.ExecutionFeatures.Fallback bool`. By the time `plan.Policy.Features.Fallback` is accessed in `internal/auditlog/middleware.go`, it is already a plain `bool` — do not flag it as a `*bool` type mismatch.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: SantiagoDePolonia
Repo: ENTERPILOT/GOModel PR: 33
File: internal/auditlog/auditlog_test.go:272-279
Timestamp: 2026-01-15T23:07:26.646Z
Learning: Go code guideline: Avoid using interface{} and map[string]interface{} for API request/response payload types. This should be restricted to public API surfaces and data payloads. It is acceptable to use interface{} in internal infrastructure packages (e.g., storage abstractions) only to break import cycles, and only when type assertions occur at initialization (not on every request) to minimize runtime type checks and improve readability. Apply this rule broadly to files under internal/ that deal with payload-like types, but not to internal components that are pure infrastructure or have runtime-per-request type assertions.

Learnt from: SantiagoDePolonia
Repo: ENTERPILOT/GOModel PR: 33
File: internal/auditlog/factory.go:112-143
Timestamp: 2026-01-15T23:07:37.652Z
Learning: Guideline: Do not use interface{} or map[string]interface{} for API request/response payload types. Prefer strongly-typed structs for API payload definitions to improve type safety, serialization, and documentation. Allow interface{} only in internal infrastructure code paths where pragmatic flexibility is necessary (e.g., to avoid import cycles or to handle highly dynamic internal contracts). In internal/auditlog/factory.go and similar non-API implementation files, applying this restriction is optional and should be evaluated on a case-by-case basis based on whether the type remains internal and does not define API boundary shapes.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@internal/admin/dashboard/static/js/modules/execution-plans.test.js`:
- Around line 267-284: Add a third assertion to the existing test for
executionPlanChartWorkflowID: when the source object has a normal non-sentinel
id (e.g., { id: 'workflow-123' }), the function should return that id directly
regardless of the entry’s execution_plan_version_id; update the test in
execution-plans.test.js (within the test that uses createExecutionPlansModule()
and calls module.executionPlanChartWorkflowID) to include an assertion that a
non-sentinel source id is returned unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: af2cd7f3-272e-4631-aac3-862f0bb0e593

📥 Commits

Reviewing files that changed from the base of the PR and between c682b57 and 9e821f2.

📒 Files selected for processing (3)
  • internal/admin/dashboard/static/js/modules/execution-plans.js
  • internal/admin/dashboard/static/js/modules/execution-plans.test.js
  • internal/auditlog/auditlog_test.go

Comment on lines +267 to +284
test('executionPlanChartWorkflowID ignores the draft workflow preview sentinel and falls back to stored entry ids', () => {
const module = createExecutionPlansModule();

assert.equal(
module.executionPlanChartWorkflowID(
{ id: 'draft-workflow-preview' },
{ execution_plan_version_id: 'historical-v1' }
),
'historical-v1'
);
assert.equal(
module.executionPlanChartWorkflowID(
{ id: 'draft-workflow-preview' },
{ execution_plan_version_id: 'draft-workflow-preview' }
),
null
);
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

LGTM! Good coverage of sentinel filtering logic.

The test validates both key scenarios:

  1. Draft sentinel in source falls back to entry's stored ID
  2. Draft sentinel in both source and entry returns null

Consider adding a test case for when source.id is a valid (non-sentinel) ID to verify it's returned directly without checking entry.

🧪 Additional test case for completeness
 test('executionPlanChartWorkflowID ignores the draft workflow preview sentinel and falls back to stored entry ids', () => {
     const module = createExecutionPlansModule();
 
+    // Valid source ID takes precedence
+    assert.equal(
+        module.executionPlanChartWorkflowID(
+            { id: 'workflow-v1' },
+            { execution_plan_version_id: 'historical-v1' }
+        ),
+        'workflow-v1'
+    );
+
     assert.equal(
         module.executionPlanChartWorkflowID(
             { id: 'draft-workflow-preview' },
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
test('executionPlanChartWorkflowID ignores the draft workflow preview sentinel and falls back to stored entry ids', () => {
const module = createExecutionPlansModule();
assert.equal(
module.executionPlanChartWorkflowID(
{ id: 'draft-workflow-preview' },
{ execution_plan_version_id: 'historical-v1' }
),
'historical-v1'
);
assert.equal(
module.executionPlanChartWorkflowID(
{ id: 'draft-workflow-preview' },
{ execution_plan_version_id: 'draft-workflow-preview' }
),
null
);
});
test('executionPlanChartWorkflowID ignores the draft workflow preview sentinel and falls back to stored entry ids', () => {
const module = createExecutionPlansModule();
// Valid source ID takes precedence
assert.equal(
module.executionPlanChartWorkflowID(
{ id: 'workflow-v1' },
{ execution_plan_version_id: 'historical-v1' }
),
'workflow-v1'
);
assert.equal(
module.executionPlanChartWorkflowID(
{ id: 'draft-workflow-preview' },
{ execution_plan_version_id: 'historical-v1' }
),
'historical-v1'
);
assert.equal(
module.executionPlanChartWorkflowID(
{ id: 'draft-workflow-preview' },
{ execution_plan_version_id: 'draft-workflow-preview' }
),
null
);
});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@internal/admin/dashboard/static/js/modules/execution-plans.test.js` around
lines 267 - 284, Add a third assertion to the existing test for
executionPlanChartWorkflowID: when the source object has a normal non-sentinel
id (e.g., { id: 'workflow-123' }), the function should return that id directly
regardless of the entry’s execution_plan_version_id; update the test in
execution-plans.test.js (within the test that uses createExecutionPlansModule()
and calls module.executionPlanChartWorkflowID) to include an assertion that a
non-sentinel source id is returned unchanged.

@SantiagoDePolonia SantiagoDePolonia merged commit ef4d14e into main Apr 3, 2026
16 checks passed
@SantiagoDePolonia SantiagoDePolonia self-assigned this Apr 3, 2026
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