Skip to content

feat(audit-trail): filter the per-record history by operation [PRD-1256] - #1910

Merged
bexchauveto merged 4 commits into
mainfrom
feature/prd-1256-agent-nodejs-operation-filter-on-the-audit-trail-route
Sep 30, 2026
Merged

bexchauveto merged 4 commits into
mainfrom
feature/prd-1256-agent-nodejs-operation-filter-on-the-audit-trail-route

Conversation

@bexchauveto

@bexchauveto bexchauveto commented Sep 21, 2026 •

Copy link
Copy Markdown
Member

fixes PRD-1256. Stacked on #1909 → #1908.

GET /forest/_audit-trail/{collection}/{recordId} accepts an operation query param.

The contract (Ruby copies this verbatim)

  • param name: operation (singular, like search; the store field is operations)
  • format: comma-separated — operation=create,delete — consistent with userIds and fields
  • set: closed — create, update, delete, action, action_failed
  • unknown value: 400, not dropped

That last one departs from userIds, which drops junk tokens. Deliberate: a silently ignored filter returns unfiltered rows into a list the caller believes is filtered — the exact failure the ticket warns about. userIds can afford to be lenient because a partial id list still filters; an ignored operation doesn't filter at all.

Empty or whitespace-only → no filter, same as fields.

Pushed into the query, not applied after

operations lands in the shared filters object, so it reaches listByRecord, countByRecord and listDistinctUsers. meta.count and meta.availableUsers therefore agree with the rows, and paging stays correct — filtering the fetched page would have desynced all three.

SQL side is one clause in buildHistoryWhereClause; the in-memory test store gained the same filter so the double stays honest.

Tests

6 route cases (single, comma-separated, count + author list, 400 on unknown, empty ignored) and a sql-store case covering listByRecord and countByRecord together. 1651 passed, lint clean.

🤖 Generated with Claude Code

Note

Add operation filter to audit-trail history and apply search/fields after permission withholding

  • The audit-history route accepts a comma-separated operations filter (create, update, delete, action, action_failed). Unknown values return a validation error; the filter is passed to the store for row, count, and author queries in audit-trail.ts and sql-store.ts.
  • For scoped records that are gone (deleted before or during the request), fields and search no longer match captured values. Instead, rows are read in batches of 500, permission withholding is applied, and matching runs in memory against served values, action, and user identity. Pagination, total count, and the first-page author list come from these served-value matches.
  • Behavioral Change: gone scoped-record histories with search/fields now return results based on post-withholding values, so search terms present only in withheld values produce no rows; see AuditTrailRoute.listServedMatches in audit-trail.ts.

Macroscope summarized d533447.

@linear-code

linear-code Bot commented Sep 21, 2026

Copy link
Copy Markdown

PRD-1256

@qltysh

qltysh Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

Qlty


Coverage Impact

This PR will not change total coverage.

Modified Files with Diff Coverage (2)

RatingFile% DiffUncovered Line #s
Coverage rating: A Coverage rating: A
packages/agent/src/routes/access/audit-trail.ts100.0%
Coverage rating: A Coverage rating: A
packages/agent/src/audit-trail/sql-store.ts100.0%
Total100.0%
🚦 See full report on Qlty Cloud »

🛟 Help
  • Diff Coverage: Coverage for added or modified lines of code (excludes deleted files). Learn more.

  • Total Coverage: Coverage for the whole repository, calculated as the sum of all File Coverage. Learn more.

  • File Coverage: Covered Lines divided by Covered Lines plus Missed Lines. (Excludes non-executable lines including blank lines and comments.)

    • Indirect Changes: Changes to File Coverage for files that were not modified in this PR. Learn more.

@bexchauveto
bexchauveto added this pull request to stack #1913 September 22, 2026 07:15
@bexchauveto
bexchauveto force-pushed the feature/prd-1256-agent-nodejs-operation-filter-on-the-audit-trail-route branch 2 times, most recently from f2a4f40 to 5192d43 Compare September 22, 2026 07:55
@qltysh

qltysh Bot commented Sep 22, 2026 •

Copy link
Copy Markdown

1 new issue

Tool Category Rule Count
qlty Structure Function with many returns (count = 4): handleHistory 1

@bexchauveto
bexchauveto force-pushed the feature/prd-1256-agent-nodejs-operation-filter-on-the-audit-trail-route branch 2 times, most recently from b1b6ca5 to 3f62633 Compare September 22, 2026 08:19
@bexchauveto
bexchauveto force-pushed the feature/prd-1256-agent-nodejs-operation-filter-on-the-audit-trail-route branch from 3f62633 to 4280bc9 Compare September 22, 2026 09:35
@bexchauveto

Copy link
Copy Markdown
Member Author

Rebased. #1908 is closed — its admin gate was scoped wrong and moved to #1911 (project-level timeline only). This PR is unchanged in substance; the inert permissionLevel: 'admin' that its new tests carried has been dropped, since the record route is no longer gated.

@bexchauveto
bexchauveto removed this pull request from stack #1913 September 22, 2026 12:05
@bexchauveto
bexchauveto added this pull request to stack #1915 September 22, 2026 12:06
@bexchauveto
bexchauveto force-pushed the feature/prd-1256-agent-nodejs-operation-filter-on-the-audit-trail-route branch from 4280bc9 to 8f7c4e3 Compare September 22, 2026 12:38
@bexchauveto
bexchauveto force-pushed the feature/prd-1256-agent-nodejs-operation-filter-on-the-audit-trail-route branch from 8f7c4e3 to b59c691 Compare September 23, 2026 09:14
@bexchauveto
bexchauveto force-pushed the feature/prd-1256-agent-nodejs-operation-filter-on-the-audit-trail-route branch from b59c691 to 1b88437 Compare September 23, 2026 09:36
@bexchauveto
bexchauveto force-pushed the feature/prd-1256-agent-nodejs-operation-filter-on-the-audit-trail-route branch 4 times, most recently from a2f6e57 to 665192b Compare September 23, 2026 13:20
@bexchauveto
bexchauveto force-pushed the feature/prd-1256-agent-nodejs-operation-filter-on-the-audit-trail-route branch 4 times, most recently from b523206 to cd2e1be Compare September 24, 2026 13:23
@bexchauveto
bexchauveto force-pushed the feature/prd-1256-agent-nodejs-operation-filter-on-the-audit-trail-route branch 4 times, most recently from 7cb26a6 to e325674 Compare September 24, 2026 15:16
Base automatically changed from feature/prd-1265-agent-nodejs-only-evaluate-the-audit-scope-against-a to main September 24, 2026 15:22
@bexchauveto
bexchauveto force-pushed the feature/prd-1256-agent-nodejs-operation-filter-on-the-audit-trail-route branch 6 times, most recently from db8e02e to 7d7c00d Compare September 25, 2026 12:42
Comment thread packages/agent/src/routes/access/audit-trail.ts Outdated
Comment thread packages/agent/src/routes/access/audit-trail.ts Outdated
@bexchauveto
bexchauveto force-pushed the feature/prd-1256-agent-nodejs-operation-filter-on-the-audit-trail-route branch from 250d416 to 744519f Compare September 28, 2026 14:50
@bexchauveto
bexchauveto force-pushed the feature/prd-1256-agent-nodejs-operation-filter-on-the-audit-trail-route branch 6 times, most recently from e243670 to 19f8e46 Compare September 30, 2026 15:13
bexchauveto and others added 4 commits September 30, 2026 17:23
`GET /forest/_audit-trail/{collection}/{recordId}` accepts an `operation`
query param: comma-separated, from the closed set the agent stores
(`create`, `update`, `delete`, `action`, `action_failed`).

An unrecognized value returns 400 rather than being dropped. A silently
ignored filter returns unfiltered rows into a list the caller believes is
filtered — the failure mode this param exists to avoid.

Pushed into the store query rather than applied to the fetched page, so
`meta.count` and `meta.availableUsers` agree with the rows and paging stays
correct.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…gone scoped record

Matched in SQL, `search` and `fields` test the values as captured, so the rows,
`meta.count` and `availableUsers` would reveal whether a withheld value holds the
term. For a record gone under a permission scope, the history is read without
those two filters and matched and paged in memory against the values served.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ord deleted mid-request

A record visible at the check but gone by the recheck had its rows, count and
authors matched in SQL on captured values; that answer is now discarded and
re-read against the served values. The served-value read runs in batches of 500,
keeping only the requested page and the authors in memory.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…kes a list

The parameter accepts a comma-separated set and is read into `operations`
everywhere behind the route, so the singular on the wire was the odd one out and
read as though one value were all it would take.

Nothing consumes it yet outside the front's own branch, so the rename costs no
compatibility.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@bexchauveto
bexchauveto force-pushed the feature/prd-1256-agent-nodejs-operation-filter-on-the-audit-trail-route branch from 19f8e46 to d533447 Compare September 30, 2026 15:23

@arnaud-moncel arnaud-moncel left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@bexchauveto
bexchauveto merged commit 92cd2f6 into main Sep 30, 2026
32 checks passed
@bexchauveto
bexchauveto deleted the feature/prd-1256-agent-nodejs-operation-filter-on-the-audit-trail-route branch September 30, 2026 15:34
forest-bot added a commit that referenced this pull request Sep 30, 2026
# @forestadmin/agent [1.104.0](https://github.com/ForestAdmin/agent-nodejs/compare/@forestadmin/agent@1.103.9...@forestadmin/agent@1.104.0) (2026-09-30)

### Features

* **audit-trail:** filter the per-record history by operation [PRD-1256] ([#1910](#1910)) ([92cd2f6](92cd2f6))
bexchauveto added a commit that referenced this pull request Sep 30, 2026
…ed-values

Keeps this branch's cursor-bounded scan over the offset-batched one
#1910 carried, and adds #1910's operations filter to the row filters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.

2 participants