diff --git a/.changeset/20513-objectql-runtime-strings-state-the-decision.md b/.changeset/20513-objectql-runtime-strings-state-the-decision.md new file mode 100644 index 00000000000..dc22d334c56 --- /dev/null +++ b/.changeset/20513-objectql-runtime-strings-state-the-decision.md @@ -0,0 +1,35 @@ +--- +'@objectstack/objectql': patch +--- + +objectql refusals, log lines and metadata text no longer cite tracker numbers; each states the reason in words + +Clause-②: no + +Many messages the query engine shows to authors, administrators and operators ended with an +issue-tracker number where the reason belonged. The number goes, and where the sentence did not +already say what was decided, it now does: + +- Refusals: the bulk update and bulk delete row-scoping refusals now name the seed they are missing + (the AST seeded before the middleware chain, which RLS and sharing compose their row-scoping onto, + so a bulk write reaches only the rows the caller may edit); the hook-target rebind refusal says + why `delete()` stopped honouring a rebind (a handler that silently redirects which row gets + deleted is a trap) and names the `dispatchUnscopedMultiWrite` registration the whole-operation + dispatch goes to, on update and delete alike. The unknown-option, filter-array, + credential-aggregation, HAVING-operator, empty-hook-target, strict read-only and system-write + organization refusals lose only the citation, because their sentences already said it. +- Metadata text: the lifecycle `retention_overrides` setting description and the search companion + field description lose their citation. +- Log lines: the non-atomic cascade warning says a single-datasource cascade is now one + transaction; the system-ledger transaction line calls the ledger the one class carved out of the + cross-datasource write refusal; the dangling-reference audit summary says findings are reported, + never rewritten, because a system-context write is exempt from the write-time reference check; + the legacy `apiMethods` warning says the authorable values are the six primitives only, every + other operation being derived from them or retired; the two unevaluable-rule warnings say such a + rule fails closed and is never skipped. The ADR-0104 value-shape gate lines, the delegated + protocol-assembly line and the read-only and runtime-owned strip warnings lose only the citation. + +The `findOne` no-predicate refusal keeps its citation for now: `@objectstack/metadata-core` +carries a byte-identical copy that this package's tests compare against, and both move together. + +Text only: no error code, field name, status or behaviour changes. diff --git a/packages/objectql/src/engine-dropped-fields-primary-key.test.ts b/packages/objectql/src/engine-dropped-fields-primary-key.test.ts index bdcd06249c9..4b20e146424 100644 --- a/packages/objectql/src/engine-dropped-fields-primary-key.test.ts +++ b/packages/objectql/src/engine-dropped-fields-primary-key.test.ts @@ -315,7 +315,7 @@ describe('#6437 — the refusal message is composed from `drops`, not from the c `API-boundary caller, isSystem included. A value DERIVED by a beforeUpdate hook is ` + `not a caller write and is never stripped — that is the sanctioned write path for a ` + `conditionally-locked derived field). To let the strip happen and merely observe it, drop ` + - `strictReadonlyWrites and pass options.onFieldsDropped instead (#3407).`, + `strictReadonlyWrites and pass options.onFieldsDropped instead.`, ); }); diff --git a/packages/objectql/src/engine.ts b/packages/objectql/src/engine.ts index 851cca296b6..036856ead49 100644 --- a/packages/objectql/src/engine.ts +++ b/packages/objectql/src/engine.ts @@ -770,7 +770,7 @@ function rejectUnknownEngineOptions( throw new Error( `${operation}('${object}') does not recognise option${unknown.length > 1 ? 's' : ''} ` + `${details.join('; ')}. The engine executes none of ${unknown.length > 1 ? 'them' : 'it'}, ` + - `so the call would succeed with the option silently ignored (#4371). ` + + `so the call would succeed with the option silently ignored. ` + `Legal keys for ${operation}: ${[...legal].sort().join(', ')}.`, ); } @@ -1071,7 +1071,7 @@ function lowerWhereFilterArray( `${JSON.stringify(where)}. A filter array is a comparison [field, operator, value], ` + `a logical node ["and"|"or", ...conditions], or a list of those — it is INPUT-ONLY ` + `sugar (spec 'FilterArray'), lowered to a FilterCondition here before any driver sees ` + - `it (#5158). This value cannot be lowered, and an unapplied filter would have returned ` + + `it. This value cannot be lowered, and an unapplied filter would have returned ` + `the UNFILTERED result set. Recognised operators: ` + `${[...VALID_AST_OPERATORS].sort().join(', ')}. Infix joins ([condA, "or", condB]) are ` + `NOT one of the shapes — write the prefix form ["or", condA, condB].`, @@ -1094,7 +1094,7 @@ function lowerWhereFilterArray( throw new Error( `${operation}('${object}'): filter array ${JSON.stringify(where)} passed isFilterAST() ` + `but parseFilterAST() lowered it to nothing. Refusing rather than running the query ` + - `unfiltered (#5158).`, + `unfiltered.`, ); } // [#5869] Door 2's half of the same check USED to be a second @@ -9616,7 +9616,7 @@ export class ObjectQL implements IObjectQLEngine { FILE_REFERENCES_MIGRATION_ID, '[value-shape] this deployment has verified the file-as-reference migration — ' + 'media value shapes are enforced and released field files may be collected ' + - '(ADR-0104 / #3617)', + '(ADR-0104)', ); } @@ -9664,7 +9664,7 @@ export class ObjectQL implements IObjectQLEngine { 'valueShapesMigrationVerified', VALUE_SHAPES_MIGRATION_ID, '[value-shape] this deployment has verified the value-shape scan — reference and ' + - 'structured-JSON value shapes are enforced (ADR-0104 / #3438)', + 'structured-JSON value shapes are enforced (ADR-0104)', ); } @@ -9967,7 +9967,7 @@ export class ObjectQL implements IObjectQLEngine { 'no byte is deleted on evidence this deployment has contradicted. Fix the data and run ' + '`os migrate ' + (migrationId === FILE_REFERENCES_MIGRATION_ID ? 'files-to-references' : 'value-shapes') + - ' --apply` to clear it (ADR-0104 / #4797).', + ' --apply` to clear it (ADR-0104).', ); }) .catch((err: any) => { @@ -9978,7 +9978,7 @@ export class ObjectQL implements IObjectQLEngine { `[value-shape] could not record the observed deviation for '${migrationId}' ` + `(${err?.message ?? err}) — the ledger still authorises irreversible collection while ` + 'this deployment holds a value its own contract rejects; run the migration to ' + - 're-derive the gate (#4797)', + 're-derive the gate', ); }); } @@ -10069,7 +10069,7 @@ export class ObjectQL implements IObjectQLEngine { `(${tally?.first.object}.${tally?.first.field}: ${tally?.first.detail}). ` + 'The gate is closed again — fix the data, then run `os migrate ' + (migrationId === FILE_REFERENCES_MIGRATION_ID ? 'files-to-references' : 'value-shapes') + - ' --apply` to re-earn it (ADR-0104 / #4769).', + ' --apply` to re-earn it (ADR-0104).', ); }) .catch((err: any) => { @@ -10078,7 +10078,7 @@ export class ObjectQL implements IObjectQLEngine { this.logger.warn( `[value-shape] could not revoke the creation attestation for '${migrationId}' ` + `(${err?.message ?? err}) — the ledger still claims this deployment is verified ` + - 'while its data contradicts that; run the migration to re-derive it (#4769)', + 'while its data contradicts that; run the migration to re-derive it', ); }); } @@ -10154,7 +10154,7 @@ export class ObjectQL implements IObjectQLEngine { '[value-shape] media values are checked but NOT enforced here, and released files are ' + 'never collected — this deployment has not verified its file migration. Run ' + '`os migrate files-to-references` (dry run) to see what it would do, then `--apply` ' + - 'to close the gate (ADR-0104 / #3617).', + 'to close the gate (ADR-0104).', ); } if (covered && !(await this.readMigrationFlagVerified(VALUE_SHAPES_MIGRATION_ID)).verified) { @@ -10162,7 +10162,7 @@ export class ObjectQL implements IObjectQLEngine { '[value-shape] reference and structured-JSON values are checked but NOT enforced here — ' + 'this deployment has not verified its value-shape scan. Run `os migrate value-shapes` ' + '(dry run) to see what it would report, then `--apply` to close the gate ' + - '(ADR-0104 / #3438).', + '(ADR-0104).', ); } } catch { @@ -14017,7 +14017,9 @@ export class ObjectQL implements IObjectQLEngine { if (!ast) { throw new Error( `[Security] Refusing bulk update on '${object}': row-scoping AST was not seeded ` + - `(the predicate branch was reached without the #2982 seed).`, + `(the predicate branch was reached without the AST seeded before the middleware ` + + `chain — the one RLS and sharing compose their row-scoping onto, so that a bulk ` + + `write reaches only the rows this caller may edit).`, ); } // [#9974] The unscoped-multi shape check, BEFORE the matched-row @@ -15199,7 +15201,8 @@ export class ObjectQL implements IObjectQLEngine { `Cascade delete of '${object}' cannot run as one unit of work: the cascade reaches an object routed ` + `to a datasource other than the default one ('${this.defaultDriver ?? ''}'), and a transaction ` + "covers one driver's connection only (ADR-0119 D1 — no two-phase commit). The cascade therefore runs " + - 'UNWRAPPED, exactly as it did before #7413: if a later dependent refuses the delete, the rows already ' + + 'UNWRAPPED, as every cascade did before a single-datasource cascade was made one transaction: if a ' + + 'later dependent refuses the delete, the rows already ' + 'removed stay removed while the call rejects. Route the cascading objects to one datasource to get the ' + 'atomic path. Reported once per object per engine instance.', { object, defaultDatasource: this.defaultDriver ?? undefined }, @@ -16337,7 +16340,9 @@ export class ObjectQL implements IObjectQLEngine { if (!ast) { throw new Error( `[Security] Refusing bulk delete on '${object}': row-scoping AST was not seeded ` + - `(the predicate branch was reached without the #2982 seed).`, + `(the predicate branch was reached without the AST seeded before the middleware ` + + `chain — the one RLS and sharing compose their row-scoping onto, so that a bulk ` + + `write reaches only the rows this caller may edit).`, ); } // [#9719] The unscoped-multi shape check, BEFORE the matched-row read: @@ -16658,7 +16663,7 @@ export class ObjectQL implements IObjectQLEngine { + 'secret/password fields are masked on read and `internal: true` fields are omitted ' + 'outright, so the value never leaves the engine on the generic data path; aggregating ' + 'them (group-by, min/max, array_agg, …) would surface it. ' - + 'Refusing (fail-closed) — see ADR-0100 / #3171 / #7922.', + + 'Refusing (fail-closed) — see ADR-0100.', ); } } @@ -17422,7 +17427,8 @@ export class ObjectQL implements IObjectQLEngine { this.logger.debug( `${operation} of '${objectName}' inside transaction() is routed to datasource '${target}' while the ` + `transaction is open on '${scope.datasource}' — executing it OUTSIDE the transaction, on its own ` + - 'connection (ADR-0057 §3.6 system ledger, carved out by #5351). It commits independently and will ' + + 'connection (ADR-0057 §3.6 system ledger — the one class carved out of the cross-datasource write ' + + 'refusal). It commits independently and will ' + 'SURVIVE a rollback of this transaction: an audit/telemetry/event row may describe a write that was ' + 'undone. That is the decided direction of error for an append-only ledger — an extra reconcilable ' + 'row beats a missing row for a write that did commit. Said once per transaction per datasource.', diff --git a/packages/objectql/src/having-filter.ts b/packages/objectql/src/having-filter.ts index 5dbf9111136..edd1d6b59b0 100644 --- a/packages/objectql/src/having-filter.ts +++ b/packages/objectql/src/having-filter.ts @@ -325,7 +325,7 @@ function unknownOperator( return invalidFilterError( `Unsupported operator '${op}' in \`${clause.root}\`. ${clause.semantics} and supports: ${supported}. ` + `An unknown operator is refused rather than ignored — ignoring it would silently ` - + `return unfiltered aggregates (#4286, ADR-0078).`, + + `return unfiltered aggregates (ADR-0078).`, ); } diff --git a/packages/objectql/src/hook-binder.ts b/packages/objectql/src/hook-binder.ts index 6f72007cea3..fbe6d57253f 100644 --- a/packages/objectql/src/hook-binder.ts +++ b/packages/objectql/src/hook-binder.ts @@ -205,7 +205,7 @@ export function bindHooksToEngine( result.skipped += 1; const reason = 'hook target names no object — an empty `object` is refused rather than widened to ' - + "the wildcard '*' (#4001). Name the object(s), or write `object: '*'` if firing on " + + "the wildcard '*'. Name the object(s), or write `object: '*'` if firing on " + 'every object is the intent.'; result.errors.push({ hook: hook.name, reason }); if (opts.strict) { diff --git a/packages/objectql/src/hook-target-rebind-errors.ts b/packages/objectql/src/hook-target-rebind-errors.ts index 280b6ccac23..890dcb26127 100644 --- a/packages/objectql/src/hook-target-rebind-errors.ts +++ b/packages/objectql/src/hook-target-rebind-errors.ts @@ -167,18 +167,20 @@ function buildMessage(info: { ? cleared ? ` The capability this used to have is RETIRED: clearing 'input.id' in a '${event}' handler ` + `converted a by-id write into a PREDICATE write over the caller's 'where'. Since ADR-0058 ` + - `Addendum II (#5574 / #5846) the dispatch ladder is resolved BEFORE the before phase — the ` + + `Addendum II the dispatch ladder is resolved BEFORE the before phase — the ` + `predicate path has to read its matched rows first, to build one context per row — so there ` + `is no ladder left to re-enter.` : ` The capability this used to have is RETIRED: rebinding 'input.id' in a '${event}' handler ` + `moved the write to another row. The engine now resolves the target BEFORE the before phase ` + `and computes the whole write against it — the pre-image, the 'readonlyWhen' locks, the ` + `validation rules — so a by-id target is immutable once a handler runs, on BOTH verbs. ` + - `'delete()' honoured a rebind until #6752 by re-resolving the new target; that is retired ` + - `too, so one rule now covers both.` + `'delete()' used to honour a rebind by re-resolving the new target; that is retired ` + + `too, because a handler that silently redirects which row gets deleted is a trap — so ` + + `one rule now covers both.` : path === 'unscoped-multi' ? ` This is the whole-operation dispatch an UNSCOPED predicate write delivers to a declared ` + - `shape guard (#9719, both write verbs since #9974): its 'id' is present-but-undefined ON ` + + `shape guard (one registered with 'dispatchUnscopedMultiWrite', on update and delete ` + + `alike): its 'id' is present-but-undefined ON ` + `PURPOSE — there is no target row — and the dispatch ladder was resolved before any handler ` + `ran, so binding 'input.id' here retargets nothing. It is refused rather than ignored, ` + `because a silent no-op is the failure this contract exists to abolish.` diff --git a/packages/objectql/src/integrity/dangling-reference-audit.test.ts b/packages/objectql/src/integrity/dangling-reference-audit.test.ts index 261af66b224..92911b1033e 100644 --- a/packages/objectql/src/integrity/dangling-reference-audit.test.ts +++ b/packages/objectql/src/integrity/dangling-reference-audit.test.ts @@ -374,7 +374,7 @@ describe('[#4551] dangling stored references are reported, never rewritten', () }); await auditDanglingReferences(dirty); expect(dirty.warnings).toHaveLength(1); - expect(dirty.warnings[0][0]).toContain('#4551'); + expect(dirty.warnings[0][0]).toContain('reported, never rewritten'); expect((dirty.warnings[0][1] as any).references).toEqual([ 'sys_position_permission_set#ppr_1.permission_set_id → sys_permission_set#ps_gone', ]); @@ -495,7 +495,7 @@ describe('[#4747] a run that was called off is not a finding about the data', () expect(out.unreadableObjects).toEqual([]); // The real finding is still reported, and the summary line carries the // incompleteness so the log cannot read as a finished run either. - const summary = port.warnings.find((w) => w[0].includes('#4551')); + const summary = port.warnings.find((w) => w[0].includes('reported, never rewritten')); expect(summary).toBeDefined(); expect((summary![1] as any).aborted).toBe(true); }); @@ -712,7 +712,7 @@ describe('[#4743] provenance references are audited, in their OWN bucket', () => const out = await auditDanglingReferences(port); - const summary = port.warnings.find((w) => w[0].includes('#4551')); + const summary = port.warnings.find((w) => w[0].includes('reported, never rewritten')); expect(summary).toBeDefined(); const meta = summary![1] as Record; expect(meta.dangling).toBe(1); @@ -956,7 +956,7 @@ describe('[#5718] objects a finite budget never reached are named, not dropped', }); await auditDanglingReferences(loud, { maxRows: 1 }); - const summary = loud.warnings.find((w) => w[0].includes('#4551')); + const summary = loud.warnings.find((w) => w[0].includes('reported, never rewritten')); expect(summary).toBeDefined(); const meta = summary![1] as Record; // Itemised, not merely counted: object-scale, and a reader who has to act diff --git a/packages/objectql/src/integrity/dangling-reference-audit.ts b/packages/objectql/src/integrity/dangling-reference-audit.ts index e6595284a59..deab1c943dc 100644 --- a/packages/objectql/src/integrity/dangling-reference-audit.ts +++ b/packages/objectql/src/integrity/dangling-reference-audit.ts @@ -733,7 +733,7 @@ export async function auditDanglingReferences( // They ride along whenever the line fires for a real finding; the full // report always carries them for a caller that came looking. if (report.dangling.length || report.undetermined || report.unreadableObjects.length) { - port.warn?.('[integrity] stored references that resolve to nothing (#4551)', { + port.warn?.('[integrity] stored references that resolve to nothing — reported, never rewritten: a system-context write is exempt from the write-time reference check, so this audit is where such a reference surfaces', { scanned: report.scanned, dangling: report.dangling.length, undetermined: report.undetermined, diff --git a/packages/objectql/src/lifecycle/lifecycle-settings.ts b/packages/objectql/src/lifecycle/lifecycle-settings.ts index d70335b7c70..7e0142ed772 100644 --- a/packages/objectql/src/lifecycle/lifecycle-settings.ts +++ b/packages/objectql/src/lifecycle/lifecycle-settings.ts @@ -46,7 +46,7 @@ export const lifecycleSettingsManifest = { 'Per-object window overrides: { "": { "maxAge": "1y", "expireAfter": "30d" } }. ' + 'Duration literals: h/d/w/y. Tenant-scoped — a regulated tenant sets years while dev keeps days (ADR-0057 §3.2). ' + 'An override BELOW a retention floor a consumer registered (e.g. the job queue\'s dedup window) is rejected at ' + - 'sweep time and logged at error — the declared window keeps running (#5195).', + 'sweep time and logged at error — the declared window keeps running.', }, { type: 'json', diff --git a/packages/objectql/src/plugin.ts b/packages/objectql/src/plugin.ts index 98afc50a9ca..6530671021e 100644 --- a/packages/objectql/src/plugin.ts +++ b/packages/objectql/src/plugin.ts @@ -495,7 +495,7 @@ export class ObjectQLPlugin implements Plugin { }); this.subscribeMetadataRebind(ctx, protocolShim); } else { - ctx.logger.info('registerProtocol=false — protocol assembly delegated to MetadataProtocolPlugin (ADR-0076 Step 2, #2462)'); + ctx.logger.info('registerProtocol=false — protocol assembly delegated to MetadataProtocolPlugin (ADR-0076 Step 2)'); } // ADR-0057: the platform-owned LifecycleService. Registered from the diff --git a/packages/objectql/src/readonly-strict-errors.ts b/packages/objectql/src/readonly-strict-errors.ts index b897b0a19cd..c02452d2cab 100644 --- a/packages/objectql/src/readonly-strict-errors.ts +++ b/packages/objectql/src/readonly-strict-errors.ts @@ -123,7 +123,7 @@ function buildRefusalMessage( const head = `${operation === 'insert' ? 'Insert' : 'Update'} on '${object}' was REFUSED: `; const tail = `To let the strip happen and merely observe it, drop ` + - `strictReadonlyWrites and pass options.onFieldsDropped instead (#3407).`; + `strictReadonlyWrites and pass options.onFieldsDropped instead.`; // Empty `drops` cannot happen on either throw site, but `every` on it is // vacuously true, which lands on the historical wording — the safe default. @@ -138,7 +138,7 @@ function buildRefusalMessage( (operation === 'insert' ? `{ context: { isSystem: true } } — or, for a data migration reinstating legacy ` + `values for a runtime-owned field (a record number), the historical-import ` + - `context { context: { preserveAudit: true } } (#3493). ` + `context { context: { preserveAudit: true } }. ` : `{ context: { isSystem: true } } (this exempts statically 'readonly' fields, but NOT ` + `fields locked by a TRUE 'readonlyWhen' predicate — those stay locked for every ` + `API-boundary caller, isSystem included. A value DERIVED by a beforeUpdate hook is ` + diff --git a/packages/objectql/src/registry.test.ts b/packages/objectql/src/registry.test.ts index 989f0d8c399..cd024dcc672 100644 --- a/packages/objectql/src/registry.test.ts +++ b/packages/objectql/src/registry.test.ts @@ -1198,7 +1198,7 @@ describe('warnStrippedLegacyApiMethods (#3543)', () => { expect(warn.mock.calls[0][0]).toContain('import'); expect(warn.mock.calls[0][0]).toContain('export'); expect(warn.mock.calls[0][0]).toContain('IGNORED'); - expect(warn.mock.calls[0][0]).toContain('#3543'); + expect(warn.mock.calls[0][0]).toContain('derived from them or retired'); // primitives remain, so no deny-all escalation expect(warn.mock.calls[0][0]).not.toContain('deny-all'); }); diff --git a/packages/objectql/src/registry.ts b/packages/objectql/src/registry.ts index b0af60d72dc..9ea529d0526 100644 --- a/packages/objectql/src/registry.ts +++ b/packages/objectql/src/registry.ts @@ -1069,8 +1069,9 @@ export function warnStrippedLegacyApiMethods( const warn = opts?.warn ?? ((msg: string) => console.warn(msg)); warn( `[Registry] Object "${name}" declares retired legacy apiMethods value(s) ` + - `[${legacy.join(', ')}] in enable.apiMethods — since the enum shrink ` + - `(#3543) these are IGNORED: the effective API surface derives from the ` + + `[${legacy.join(', ')}] in enable.apiMethods — the authorable values are now the six ` + + `primitives only, because every other operation is derived from them or retired, so these ` + + `are IGNORED: the effective API surface derives from the ` + `six primitives (get/list/create/update/delete/bulk) alone ` + `(['create','update'] ⇒ upsert/import; ['list'] ⇒ aggregate/search/export; ` + `['get'] + trackHistory ⇒ history).` + diff --git a/packages/objectql/src/search-companion.ts b/packages/objectql/src/search-companion.ts index 57b358a8d07..8d39ac91b3a 100644 --- a/packages/objectql/src/search-companion.ts +++ b/packages/objectql/src/search-companion.ts @@ -323,7 +323,7 @@ export function provisionSearchCompanion(schema: searchable: false, description: `Search-normalized forms of the display/name field (normalizers: ${SEARCH_COMPANION_NORMALIZERS.join(', ')}) — ` + - 'e.g. full pinyin + initials for CJK names. Maintained by plugin-pinyin-search; never hand-edited. See #2486.', + 'e.g. full pinyin + initials for CJK names. Maintained by plugin-pinyin-search; never hand-edited.', }, }, }; diff --git a/packages/objectql/src/tenancy/system-write-organization.ts b/packages/objectql/src/tenancy/system-write-organization.ts index 799beb2c296..f20365d5b7f 100644 --- a/packages/objectql/src/tenancy/system-write-organization.ts +++ b/packages/objectql/src/tenancy/system-write-organization.ts @@ -302,7 +302,7 @@ function buildRefusalMessage( `${DEFAULT_TENANT_FIELD} = NULL, which the autonumber counter and the partitioned unique index ` + `(COALESCE(${DEFAULT_TENANT_FIELD}, '${GLOBAL_TENANT}'), ) both collapse to the ` + `'${GLOBAL_TENANT}' pseudo-tenant — a second counter that cannot see the organization's own, so a ` + - `field declared unique silently gets the same value twice (#8844). Nothing was written. Fix it by ` + + `field declared unique silently gets the same value twice. Nothing was written. Fix it by ` + `carrying the organization the way a session write does: pass it on the execution context ` + `({ context: { isSystem: true, tenantId: '' } }), or set ${DEFAULT_TENANT_FIELD} ` + `on the record itself. If rows of '${object}' are genuinely platform-global and belong to no ` + diff --git a/packages/objectql/src/validation/rule-validator.ts b/packages/objectql/src/validation/rule-validator.ts index 90cec7aea79..7af815c4d4b 100644 --- a/packages/objectql/src/validation/rule-validator.ts +++ b/packages/objectql/src/validation/rule-validator.ts @@ -2564,8 +2564,8 @@ function preserveAuditRemedySentence(options?: StripWarningOptions): string { if (options?.preserveAuditApplies !== true) return ''; return ( ` A historical import restoring this record's own earlier values does NOT need that blanket ` + - `exemption: pass the narrower historical-import context { context: { preserveAudit: true } } ` + - `(#3493), which reinstates THIS field while the rest of the strip stays in force.` + `exemption: pass the narrower historical-import context { context: { preserveAudit: true } }, ` + + `which reinstates THIS field while the rest of the strip stays in force.` ); } @@ -2578,9 +2578,9 @@ function preserveAuditRemedySentence(options?: StripWarningOptions): string { function observeInsteadSentence(options?: StripWarningOptions): string { return options?.strict === true ? ` To let the strip happen and merely observe it instead of refusing the write, drop ` + - `options.strictReadonlyWrites and pass options.onFieldsDropped (#3407).` + `options.strictReadonlyWrites and pass options.onFieldsDropped.` : ` To detect drops programmatically instead of reading ` + - `this log, pass options.onFieldsDropped (#3407).`; + `this log, pass options.onFieldsDropped.`; } /** @@ -2616,10 +2616,10 @@ export function runtimeOwnedStripWarning( ? `DROPPED and the write is being REFUSED ENTIRELY — the runtime issues this value from its ` + `sequence, and this write passed options.strictReadonlyWrites, so NOTHING is written: not ` + `this column, and not the fields that would have survived the strip. The call throws ` + - `ERR_READONLY_FIELD_REJECTED rather than returning success (#5126).` + `ERR_READONLY_FIELD_REJECTED rather than returning success.` : `DROPPED and the write is being COMMITTED WITHOUT IT — the runtime issues this value from its ` + `sequence, so the call returns success while the column holds the generated number, not the one ` + - `sent (#5503).`; + `sent.`; return ( `Field '${field}'${on} is a runtime-owned '${type}' field: the caller-supplied value was ` + consequence + @@ -2627,8 +2627,8 @@ export function runtimeOwnedStripWarning( `declare itself trusted by passing { context: { isSystem: true } }` + (audit ? `; a data import reinstating ` + - `legacy record numbers uses the historical-import context ({ context: { preserveAudit: true } }, ` + - `#3493), which reinstates THIS field while the rest of the strip stays in force. ` + + `legacy record numbers uses the historical-import context ({ context: { preserveAudit: true } }), ` + + `which reinstates THIS field while the rest of the strip stays in force. ` + `A beforeInsert/beforeUpdate hook does NOT need either — hook-written keys are not ` + `caller-supplied.` : `. A beforeInsert/beforeUpdate hook does NOT need it — hook-written keys are not ` + @@ -2680,14 +2680,14 @@ export function readonlyStripWarning( ? `the caller-supplied value was DROPPED and the ${noun} is being REFUSED ENTIRELY — this ` + `write passed options.strictReadonlyWrites, so NOTHING is written: not this column, and ` + `not the fields that would have survived the strip. The call throws ` + - `ERR_READONLY_FIELD_REJECTED rather than returning success (#5126).` + `ERR_READONLY_FIELD_REJECTED rather than returning success.` : insert ? `the caller-supplied value was DROPPED and the create ` + `is being COMMITTED WITHOUT IT — the call returns success while this column takes its ` + `declared defaultValue instead of the value you sent.` : `the caller-supplied value was DROPPED and the update ` + `is being COMMITTED WITHOUT IT — the call returns success while this column keeps its stored ` + - `value (#2948).`; + `value.`; return ( `Field '${field}'${on} is read-only: ` + consequence + @@ -4065,7 +4065,7 @@ function checkPredicate( // Still logged — the operator needs the fault in the log even though the // caller now gets it in the response. Note the verb: rejected, not skipped. logger?.warn?.( - `Validation rule '${rule.name}' predicate failed to evaluate (${result.error.kind}: ${result.error.message}) — write rejected (#4649)`, + `Validation rule '${rule.name}' predicate failed to evaluate (${result.error.kind}: ${result.error.message}) — write rejected: a rule that cannot be evaluated fails closed, it is never skipped`, ); const unevaluable = unevaluableRuleError(rule.name, field, result.error, 'predicate'); // [#20006] Same verdict; on a delete's reference cleanup, a text that names it. @@ -4229,7 +4229,7 @@ function checkConditional( if (!result.ok) { ctx.logger?.warn?.( - `Validation rule '${rule.name}' when-predicate failed to evaluate (${result.error.kind}: ${result.error.message}) — write rejected (#4649)`, + `Validation rule '${rule.name}' when-predicate failed to evaluate (${result.error.kind}: ${result.error.message}) — write rejected: a rule that cannot be evaluated fails closed, it is never skipped`, ); return unevaluableRuleError(rule.name, '_record', result.error, 'when-predicate'); } diff --git a/scripts/doc-authoring-prose-id.baseline.json b/scripts/doc-authoring-prose-id.baseline.json index 12311965e9d..da4c5c61053 100644 --- a/scripts/doc-authoring-prose-id.baseline.json +++ b/scripts/doc-authoring-prose-id.baseline.json @@ -313,61 +313,7 @@ "#4246": 1 }, "packages/objectql/src/engine.ts": { - "#2982": 2, - "#3171": 1, - "#3438": 2, - "#3617": 2, - "#4371": 1, - "#4419": 1, - "#4769": 2, - "#4797": 2, - "#5158": 2, - "#5351": 1, - "#7413": 1, - "#7922": 1 - }, - "packages/objectql/src/having-filter.ts": { - "#4286": 1 - }, - "packages/objectql/src/hook-binder.ts": { - "#4001": 1 - }, - "packages/objectql/src/hook-target-rebind-errors.ts": { - "#5574": 1, - "#5846": 1, - "#6752": 1, - "#9719": 1, - "#9974": 1 - }, - "packages/objectql/src/integrity/dangling-reference-audit.ts": { - "#4551": 1 - }, - "packages/objectql/src/lifecycle/lifecycle-settings.ts": { - "#5195": 1 - }, - "packages/objectql/src/plugin.ts": { - "#2462": 1 - }, - "packages/objectql/src/readonly-strict-errors.ts": { - "#3407": 1, - "#3493": 1 - }, - "packages/objectql/src/registry.ts": { - "#3543": 1 - }, - "packages/objectql/src/search-companion.ts": { - "#2486": 1 - }, - "packages/objectql/src/tenancy/system-write-organization.ts": { - "#8844": 1 - }, - "packages/objectql/src/validation/rule-validator.ts": { - "#2948": 1, - "#3407": 2, - "#3493": 2, - "#4649": 2, - "#5126": 2, - "#5503": 1 + "#4419": 1 }, "packages/platform-objects/src/apps/translations/en.objects.generated.ts": { "#7987": 3,