Skip to content

docs(plugin-audit): the published README stops documenting an API, a row shape and an action vocabulary that do not exist (#9517) - #9531

Merged
os-project-manager merged 1 commit into
mainfrom
claude/issue-9517-plugin-audit-readme
Aug 18, 2026
Merged

docs(plugin-audit): the published README stops documenting an API, a row shape and an action vocabulary that do not exist (#9517)#9531
os-project-manager merged 1 commit into
mainfrom
claude/issue-9517-plugin-audit-readme

Conversation

@os-project-manager

Copy link
Copy Markdown
Collaborator

Part of #9517

⚠️Part of, not Fixes, deliberately — scope ③ of the ruling is only half-deliverable today. See What is left at the bottom; the card should stay open.

packages/plugins/plugin-audit/README.md is in the package's published files array with private unset, so it is what the npm package page renders. It documented an API surface with no implementation anywhere in this repo, under a banner claiming SOC 2 / HIPAA / GDPR readiness.

Implemented under the maintainer ruling on #9517 (2026-08-18): ⛔ do not migrate to the enterprise repo — delete what is false, rewrite what is real, annotate what is enterprise-dependent.

Verified before rewriting — and the drift was wider than the card's ledger

Every claim was re-measured against origin/main rather than taken from the card. All of it held, and the sweep found more of the same class:

ClaimMeasurement
getFailedActions, logAdminAction, logDataAccess absent✅ zero hits repo-wide over packages/**/*.ts
9 further auditService.* methodsgetRecordHistory, getUserActivity, searchLogs, getRecordSnapshot, generateReport, archiveLogs, purgeLogs, logDataDeletion, logDataExportalso zero hits — 12 documented methods, 0 implementations
Row shape userName / userEmail / errorMessage not on the object✅ confirmed, plus timestamp, recordName, changes, sessionId, status
Action values outside the enum'insert', 'auth:login', 'security:password_reset', 'workflow:approval', 'user_role_change' — the shipped enum is create/update/delete/login/logout/config_change/import
PluginAudit does not exist✅ appears only inside the README itself; the export is AuditPlugin
.configure({...}) does not existgit grep "static configure" over all TS returns nothing — no class in this repo has one
IAuditService not in @objectstack/spec/contracts✅ zero hits under packages/spec/**
Object name wrongaudit_log documented; the object is sys_audit_log
6 /api/v1/audit/* REST routes✅ no such routes; the object declares apiMethods: ['get', 'list']
Rest of the published surface repeats the claims?nofiles is ['dist','README.md','CHANGELOG.md']; the shipped CHANGELOG.md has 0 hits for auditService / SOC 2 / HIPAA. README.md was the only offender

What the rewrite does

① The compliance paragraph is deleted — not softened, qualified or relocated. The three external SOC 2 / GDPR / HIPAA "See Also" links existed only to prop up that framing and are gone with it.

② Everything fabricated is removed — all 12 methods, the PluginAudit.configure entry point, the whole config object (trackObjects, trackFields, retentionDays, autoArchive, excludeUsers, trackSystemEvents), the IAuditService contract block, the fake row shape, the out-of-enum actions, the REST namespace and the dashboard snippets built on the fake columns. ⛔ Nothing was implemented to match the docs.

③ The replacement documents only what the code can be pointed at:

  • the real exports and the real registration (kernel.use(new AuditPlugin()) — it takes no configuration);
  • the real sys_audit_log columns;
  • the action enum as a table pairing each value with its writer, so any row of it can be checked: create/update/deleteinstallAuditWriters, login/logoutcreateAuthEventAuditSink, config_changeservice-settings, importplugin-auth's admin import;
  • the audit service slot's actual surface — one method, recordAuthEvent, over the closed union 'login' | 'logout';
  • credential masking on old_value / new_value;
  • the coverage model — all objects minus an exclusion list, not per-object opt-in. Subtraction is load-bearing: the object universe is open, so an allow list would freeze at boot and silently stop auditing everything registered afterwards.

Three gaps are now stated rather than left for a reader to mistake for coverage:

  • reads/views are not on the ledger — no writer emits a read action;
  • failed operations are not on the ledger — there is no success/failure column and the writers fire only on after* events, so the README's getFailedActions "security monitoring" had no mechanism behind it even in principle;
  • ip_address / user_agent are auth-event-only — the record-level writer never stamps them, so a null client fingerprint on a CRUD row does not mean the request had none.

④ Two dependency boundaries are annotated with their degraded behaviour, following the access-recipes.mdx pattern named in the ruling:

  • hierarchy-relative permission scopes need @objectstack/security-enterprise and fail closed to own without it — so a grant meant to let managers read their reports' audit rows shows them only their own on an open build;
  • lifecycle.archive needs a registered archive datasource and fails closed to retention without one — nothing is ever deleted and the table grows.

⚠️ On the enterprise half, one measured correction to the card's framing: platform-capabilities.ts declares audit: { package: '@objectstack/plugin-audit', edition: 'open' }the audit capability itself is open-edition, so there was no enterprise-gated audit feature to annotate. The two boundaries above are the real ones.

Changeset: owed, and why

A patch changeset is included even though this is docs-only. The README only reaches npm on a version bump. Without a release the package page keeps rendering the false compliance claim indefinitely — which is the entire point of the card. A docs-only fix to a published artifact that never publishes is not a fix.

Verification

All gates run at 07316babd, which is HEAD and the branch tip. Derived from git merge-base (072b55ad8) per #9320, not origin/main..HEAD:

node scripts/pm/dispatch-gates.mjs .changeset/... packages/plugins/plugin-audit/README.md
⇒ 8 path-derived gates + 1 convention-triggered (i18n)
check:changeset-gate-self-tests OK (440 assertions across the 3 self-tests)
check:objectui-changeset OK
check:test-source-alias OK — 72 packages scanned
check:type-source-resolution OK — 76 packages scanned
check-adr-0087-registration OK — no declared-breaking changeset
check-changeset-no-major OK
check-empty-changeset OK — 1 declaring changeset added
check-affected-docs OK — 220 self-test cases
check:i18n OK — 9 packages, all bundles in sync
check:nul-bytes OK — 6142 files, no raw control bytes
plugin-audit test 16 files / 242 tests passed
plugin-audit typecheck clean

check:i18n refused to judge the unbuilt tree first (PREREQUISITE NOT MET — the workspace CLI is not built) and was re-run only after turbo run build --filter=@objectstack/cli — 55 tasks successful. Reported green means green, not skipped.

No reverse verification is claimed: there is no assertion to ablate. This diff changes two Markdown files and no executable code — the test and typecheck runs above are a no-regression control, not evidence about the README's content. The content's evidence is the measurement table above, every row of which is a git grep anyone can rerun.

What is left

⚠️Scope ③ of the ruling is only half-delivered, and this is why the PR says Part of.

The ruling says to wait for PR #9515 to merge and then describe what is on main. #9515 has not merged — measured at dispatch and again here: state: open, draft: true, merged: false, head 23b2f70b6. Verified independently of the PR metadata: packages/plugins/plugin-audit/src/read-audit.ts does not exist on main, installReadAuditWriter is not exported, and read is not in the shipped action enum.

⇒ The read action, the per-object install-time opt-in, the off-the-request-path batched writes and the record-detail discriminator are not describable from main today, and documenting them from a draft PR's body is precisely what the ruling forbids. So this PR delivers ①, ②, ④ and the part of ③ that today's main supports.

⛔ I did not block the whole card on that: ①/② are the urgent half by the maintainer's own grading, they are independently landable, and the false compliance claim is live on npm right now. Blocking a live correction behind an unmerged draft is the wrong trade.

Follow-up owed once #9515 lands (that PR is not addressed here and remains open): the action-enum table in this README gains a read row with its writer, and the "reads and views are not on the ledger" bullet must be replaced by the real opt-in story. The table shape was chosen to make that a one-row edit.


Generated by Claude Code

…row shape and an action vocabulary that do not exist (#9517)
The README is in the package's published `files` array with `private` unset, so
it is what the npm package page renders. It documented twelve `auditService.*`
methods that exist nowhere in the repo, a `PluginAudit.configure({...})` entry
point that does not exist, an `IAuditService` spec contract that was never
declared, a row shape `sys_audit_log` does not have, action values outside its
enum, and six `/api/v1/audit/*` routes — all under a banner claiming SOC 2 /
HIPAA / GDPR readiness.
The compliance paragraph is deleted rather than softened or relocated: it is a
company-level claim needing an accountable owner, and does not belong in a
package README.
The replacement documents only what the code can be pointed at — the real
exports, the real columns, the seven-value action enum with the writer for each
value, the credential masking, and the subtract-an-exclusion-list coverage model.
It also states three gaps the old README obscured (reads, failed operations, and
the auth-events-only client fingerprint), and names two dependency boundaries
with their degraded behaviour per the access-recipes.mdx pattern.
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

Nothing in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)), so this run has no opinion about the docs.

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/plugins/plugin-audit/README.md) — pages documenting those are invisible to this run

Coarse fallback — 4 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json origin/mainpackageMentionDocs.

@github-actionsgithub-actionsBot added documentation Improvements or additions to documentation tooling labels Aug 18, 2026
@os-project-manager
os-project-manager marked this pull request as ready for review August 18, 2026 07:40
@os-project-manager
os-project-manager added this pull request to the merge queueAug 18, 2026
Merged via the queue into main with commit 53fc099Aug 18, 2026
26 checks passed
@os-project-manager
os-project-manager deleted the claude/issue-9517-plugin-audit-readme branch August 18, 2026 08:01
os-project-manager pushed a commit that referenced this pull request Aug 18, 2026
…dit surface that shipped (#9517)
PR #9531 corrected this README against the shipped surface while record-view
auditing was still an unmerged draft, so it correctly refused to describe it.
That work has since landed (#8992 via PR #9515), which made two of the page's
statements false: "reads and views are not on the ledger", and that the plugin
takes no configuration.
The page now documents the surface that exists, each point measured against the
source rather than against a description of it: the `read` action and its writer
in the action table, the `record_views` list view, the record-detail
discriminator (one materialized record plus a primary-key pin, `$or`/`$not`
refused) that keeps list and search reads out of scope, the batched
off-request-path writes with the view-instant `created_at` and the two loud
once-only failure postures, and the two declared boundaries — a system-elevated
read and a read with no principal both write no row.
The opt-in is documented as what it is: an INSTALL-TIME list on the plugin
constructor, explicitly not an `enable.auditReads` object-metadata key. That
spelling was ruled against on #8992 for the reason this card exists — a
declarable key can be set on an object in a deployment that never installs the
plugin, producing metadata that reads as audited and writes nothing.
Two things the page now says that only reading the code shows: `maxBufferedEvents`
is a writer knob the plugin does not forward, and the shipped `record_views` view
carries an `ip_address` column that is always empty on a `read` row because no
read-path writer stamps it.
Record-view auditing adds no enterprise dependency — this package's declared
edition is `open` — so nothing new is annotated under that pattern; the two
existing annotations are unchanged.
A changeset is owed because the README ships in the package's `files` array: a
docs-only correction with no version bump never reaches the npm package page.
Co-authored-by: Claude <noreply@anthropic.com>
os-steve added a commit that referenced this pull request Aug 18, 2026
…nter, in a form published readers can follow (#9589)
PR #9531 dropped this README's "See Also" pointer to the runtime-services audit
page because the page documented the settings sink (`record()` / `'set' | 'reset'`)
as if it were the `audit` slot. PR #9587 rewrote the page around the real slot, so
the omission's reason has stopped holding.
Restored because the page carries three things this README deliberately does not:
the slot's own failure posture (`recordAuthEvent` never throws; error once per
process then debug; row lost, nothing retries; silent no-op with no engine or no
`userId`), the event's field-by-field shape, and the settings-sink disambiguation
with its `TypeError` consequence. The README's own slot section states the
interface and the closed action union and stops there.
The restored line is not the removed line. That one was labelled "Audit Logging
Best Practices" — a guide the page has never been — and spelled
`/content/docs/.../audit-service.mdx`, which resolves for neither audience a
published README has: on npm and GitHub a root-relative href resolves against
npmjs.com / github.com, not the docs site. The replacement uses the absolute
`https://docs.objectstack.ai/docs/...` form that create-objectstack's published
READMEs already use.
The one pre-existing site-root-relative docs link in the same file is converted to
the same absolute form; its page and heading anchor both exist, only the spelling
was unfollowable off the docs site.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XqDQYVU5smx29ts9pAErja
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentationImprovements or additions to documentationsize/ltooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@os-project-manager@claude