Skip to content

fix(plugin-audit): declare sys_activity.type 'scheduled' and record its writer - #11522

Merged
os-sam merged 1 commit into
mainfrom
claude/issue-11424-sys-activity-scheduled
Aug 24, 2026
Merged

os-sam merged 1 commit into
mainfrom
claude/issue-11424-sys-activity-scheduled

Conversation

@claude

@claude claude Bot commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Part of #11424 — direction 1 only, and nothing else from that card.

What changed

file change
packages/plugins/plugin-audit/src/objects/sys-activity.object.ts scheduled added to the type select's declared options, placed next to completed — the value it alternates with at the writer
packages/plugins/plugin-audit/src/objects/sys-activity-type-vocabulary.test.ts the TYPES_WITH_WRITERS row the census pin demands for any newly declared value, plus two bounded corrections (below)
src/translations/{en,zh-CN,ja-JP,es-ES}.objects.generated.ts regenerated by the extractor; the three non-en leaves translated by hand (已安排 / 予定 / Programado)
.changeset/sys-activity-declares-scheduled.md patch on @objectstack/plugin-audit

Nothing accepts or rejects differently

Every field on sys_activity is readonly: true, and validateRecord skips
readonly fields on both branches — record-validator.ts L1023 (insert) and
L1033 (update) are the same if (def.system || def.readonly) continue; — so the
invalid_option check that enforces a select field's declared options never runs
for one. Read in the source, not taken on trust, and then measured:

With scheduled added to the enum and nothing else changed, the package's
full suite moved by exactly one assertion, and it was the census pin:

 FAIL  src/objects/sys-activity-type-vocabulary.test.ts > … > every declared type has a recorded disposition, and vice versa
 Test Files  1 failed | 18 passed (19)
      Tests  1 failed | 312 passed (313)
+   "scheduled",

activity-type-vocabulary-enforcement.test.ts — the behavioural half, which
inserts an undeclared value into this very column and asserts it lands, against a
control object that differs only by not being readonly — stayed green through
that run
. That is the mechanism above, observed from both sides: the behaviour
could not see this change, and only the literal inventory could.

The writer, verified rather than transcribed

The census file's own rule is that a TYPES_WITH_WRITERS row is a claim a writer
exists at the named location, so the row was read out of the producer at
objectstack-ai/hotcrm 5eee1bd (read-only clone), not copied from the card:

  • src/actions/global.actions.ts:349 — type: EVENT_STATUS === 'held' ? 'completed' : 'scheduled'
  • SCHEDULE_MEETING_SPEC declares eventStatus: 'planned' (L489-497), so the schedule_meeting action always takes the scheduled branch
  • registered on crm_lead / crm_contact / crm_account / crm_opportunity / crm_case via activityActionFor() (L545-567)

Two bounded corrections in the same file

Both are documentation in the file the card already required editing, both are
mechanical, and both are named here rather than left to be discovered:

  1. completed has a second writer. The same sweep found
    hotcrm src/actions/contact.actions.ts:165 inserting type: 'completed' from
    the send_email body. Its census row now says so. No assertion changes.
  2. Two stale sentences in the docblock. "its eleven-value type enum" loses
    the count (it would rot again); "its key set is set-equal to this enum today",
    about objectui's feed-kind map, is past-tensed to the [观察] A select field's option enum is unenforceable once the field is readonly — sys_audit_log.action declares a vocabulary nothing can check #8203 census and
    annotated with record:activity drops every sys_activity.type: "scheduled" row, and 10 of FeedItemType's 13 kinds have no producer at all objectui#5840 as the reason it moved —
    attributed to that card, explicitly not measured here, since this package
    still cannot import objectui. The set-equality pin itself is untouched from
    this side.

A third note records that the census's in-repo sweep has a second half it cannot
see: an app's server-side action reaches this column directly through
ctx.api.object('sys_activity').insert(...), which no grep of this repository
finds. That is recorded as a fact about where to look, and takes no position.

Measured at 980a966 (the commit at the head of this branch)

  • pnpm --filter @objectstack/plugin-audit test — 19 files, 314 passed (313 before; the census it.each grew by one case)
  • pnpm --filter @objectstack/plugin-audit typecheck — tsc --noEmit, clean
  • pnpm --filter '@objectstack/plugin-audit^...' build — dependency closure built first, so the suite ran against fresh dist
  • gate union, each exit code captured before any pipe, all exit=0: check:nul-bytes, check:empty-changeset, check:cross-package-test-inputs, check:published-files, check:slot-lookup, check:test-source-alias, check:type-source-resolution, check-ci-filter-parity, check-plugin-teardown-shape, docs-audit/check-affected-docs, check:query-options-erasure, check:type-check-coverage, check:engine-double-contract, check:where-matcher — the ratchet family re-run at this commit on a clean tree
  • ESLint, narrowed to the 7 changed files, --no-inline-config: 6 .ts files linted, 0 errors; the 7th is the changeset .md, which the config does not match

Not measured here

  • check:i18n — NOT MEASURED, its own verdict: check-i18n-bundles: PREREQUISITE NOT MET — the workspace CLI is not built. It runs the built CLI; building that closure locally is CI's run. What is known instead: the gate takes its flags from the config's own docstring (flagsFromDocstring), and the bundles here were regenerated by that same extractor with that same documented command through packages/cli/bin/run-dev.js (the source entry the repo's gates use precisely so they do not depend on packages/cli/dist), which reported ✓ Generated 4 file(s). The resulting diff is four single-line insertions, one per bundle, all inside the sys_activity.type options block. The untested delta is dist vs source entry into one command.
  • check:type-check-debt — needs the whole workspace built; not run. The substance for this diff is the package's own tsc --noEmit, which is green.
  • The repo-wide ESLint farm, per the narrowing above. This repo runs one eslint.config.mjs which never enables type-aware linting for any file (eslint.config.mjs:326-332, with its own recorded positive control), so this diff cannot move a verdict on a file it does not touch — but the population was not enumerated locally, so treat that as a narrowing CI closes, not a proof.

Still open

Direction 1 is the whole of this PR. The card records four directions, and the
vocabulary question behind them — closed platform vocabulary or author-extensible
one — is a maintainer call that nothing here decides in either direction.
#11424 remains open.


Generated by Claude Code

…ts writer

A shipped app action writes the value today — hotcrm's `schedule_meeting`
action, registered on all five CRM record types, inserts
`type: EVENT_STATUS === 'held' ? 'completed' : 'scheduled'` and its spec
declares `eventStatus: 'planned'`, so it always takes the `scheduled` branch.

Nothing accepts or rejects differently: every `sys_activity` field is
`readonly: true` and `validateRecord` skips readonly fields on both the insert
and the update branch, so the `invalid_option` check never runs for one. The
declaration now names a value the platform already stores, the writer census
records the writer, and the four generated locale bundles carry the label.

Part of #11424

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01APWX2AwT3a4xDcjPCe8bk4
@github-actions

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/plugin-audit, touching 2 documentable anchor(s).

3 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/kernel/runtime-services/audit-service.mdx (via sys_activity (symbol))
  • content/docs/plugins/packages.mdx (via sys_activity (symbol))
  • content/docs/ui/setup-app.mdx (via sys_activity (symbol))

⛔ 2 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v14.mdx (via sys_activity (symbol))
  • content/docs/releases/v16.mdx (via sys_activity (symbol))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • the SDK route bridge reached 45 of 222 client-bound route-ledger rows — the other 177 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run: node scripts/docs-audit/affected-docs.mjs --bridge-coverage

Coarse fallback — 7 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 daacc1071c69604976796f92d39448117b3b4990 → packageMentionDocs.

Which tree this was computed on

This run read content/docs from 89daae467a8fad0c671ff4469fba084c6b2c46ce — the merge of head 980a96661de02abf94be1f64a4cecb4b08abbabf into base daacc1071c69604976796f92d39448117b3b4990, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 89daae467a8fad0c671ff4469fba084c6b2c46ce && git checkout 89daae467a8fad0c671ff4469fba084c6b2c46ce
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin daacc1071c69604976796f92d39448117b3b4990 980a96661de02abf94be1f64a4cecb4b08abbabf && git checkout -B drift-repro daacc1071c69604976796f92d39448117b3b4990 && git merge --no-ff 980a96661de02abf94be1f64a4cecb4b08abbabf

node scripts/docs-audit/affected-docs.mjs --json daacc1071c69604976796f92d39448117b3b4990

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs daacc1071c69604976796f92d39448117b3b4990 → pass the list as
args.docs, on the commit named under Which tree this was computed on.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation size/s tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant