Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions .changeset/20751-services-strings-stage4-state-the-decision.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
---
'@objectstack/service-automation': patch
'@objectstack/plugin-audit': patch
---

Automation refusals, prescriptions, log lines and run-object field help, and the activity type help, no longer cite tracker numbers; each one states the decision behind it in words

Clause-②: no

Some strings these two packages show to flow authors, operators and administrators pointed at an issue-tracker number for the reason behind them. The number goes; where the sentence did not already say what was decided, it now does.

- `@objectstack/service-automation`: the refusal for a `fieldValues` write map says a runtime alias for it was rejected by design, so the node keeps one strict `fields` key; the refusal for a screen field's `visibleIf` says a predicate under any other key is never read, so the field always shows, and a `required` field meant to stay hidden then blocks the screen from ever being submitted; the undeclared-config-key refusal says the built-in node types were reconciled so that every key their executors read is declared; the unknown-function error in a flow value expression says such a name is refused rather than evaluated to null, which would write the field as undefined; the inert-connector warning says entries without a `provider` are catalog descriptors, while an entry that names a `provider` is a connector instance that provider's installed executor materializes; the `sys_automation_run` field help says the paused node's type decides who may continue a run (an approval pause only through its owning service), that rows written before run history recorded its trigger were not backfilled, and that a finished run's bounded step log keeps its per-node detail across a restart; three bridge debug lines say what each bridge provides. The bulk-intent guidance, the degraded-connector dispatch error and retry lines, the user-less `runAs` warning and refusal, the unclaimed-branch warning, the script-function and node-config refusals and the `sys_flow_dispatch` description drop their citations.
- `@objectstack/plugin-audit`: the `sys_activity` `type` help, whose English text all four shipped locale bundles carry, says the vocabulary is open by decision, not a gap awaiting enforcement.

Text only: no status, error code, field, route or control flow moves. A client or log filter that matches the old text (for example a tracker-number suffix) needs the new spelling.
Original file line number Diff line number Diff line change
Expand Up @@ -117,8 +117,8 @@ export const SysActivity = ObjectSchema.create({
+ 'vocabulary, not a closed enum: metadata authors may contribute their own values '
+ '(sanctioned channel: `activityMilestones[].type`, ADR-0052 §5b.2), and an '
+ 'undeclared value is stored verbatim rather than rejected. Consumers must render '
+ 'an unknown value instead of assuming this list is exhaustive (maintainer ruling '
+ '2026-08-24, #11507).',
+ 'an unknown value instead of assuming this list is exhaustive: the vocabulary is '
+ 'open by decision, not a gap awaiting enforcement.',
required: true,
readonly: true,
searchable: true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export const enObjects: NonNullable<TranslationData['objects']> = {
},
type: {
label: "Type",
help: "Activity kind. The declared options are the platform BUILT-IN set of an open vocabulary, not a closed enum: metadata authors may contribute their own values (sanctioned channel: `activityMilestones[].type`, ADR-0052 §5b.2), and an undeclared value is stored verbatim rather than rejected. Consumers must render an unknown value instead of assuming this list is exhaustive (maintainer ruling 2026-08-24, #11507).",
help: "Activity kind. The declared options are the platform BUILT-IN set of an open vocabulary, not a closed enum: metadata authors may contribute their own values (sanctioned channel: `activityMilestones[].type`, ADR-0052 §5b.2), and an undeclared value is stored verbatim rather than rejected. Consumers must render an unknown value instead of assuming this list is exhaustive: the vocabulary is open by decision, not a gap awaiting enforcement.",
options: {
created: "created",
updated: "updated",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export const esESObjects: NonNullable<TranslationData['objects']> = {
},
type: {
label: "Tipo",
help: "Activity kind. The declared options are the platform BUILT-IN set of an open vocabulary, not a closed enum: metadata authors may contribute their own values (sanctioned channel: `activityMilestones[].type`, ADR-0052 §5b.2), and an undeclared value is stored verbatim rather than rejected. Consumers must render an unknown value instead of assuming this list is exhaustive (maintainer ruling 2026-08-24, #11507).",
help: "Activity kind. The declared options are the platform BUILT-IN set of an open vocabulary, not a closed enum: metadata authors may contribute their own values (sanctioned channel: `activityMilestones[].type`, ADR-0052 §5b.2), and an undeclared value is stored verbatim rather than rejected. Consumers must render an unknown value instead of assuming this list is exhaustive: the vocabulary is open by decision, not a gap awaiting enforcement.",
options: {
created: "Creado",
updated: "Actualizado",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

export const esESGeneratedSourceHashes: Readonly<Record<string, string>> = {
"objects.sys_activity.fields.actor_id.label": "b155813f8a7f06e3",
"objects.sys_activity.fields.type.help": "bc8b10c89aacd494",
"objects.sys_activity.fields.type.help": "5d8df777db464241",
"objects.sys_activity.fields.url.label": "2203b0b9f72534f7",
"objects.sys_audit_log.fields.actor.label": "b155813f8a7f06e3",
};
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export const jaJPObjects: NonNullable<TranslationData['objects']> = {
},
type: {
label: "タイプ",
help: "Activity kind. The declared options are the platform BUILT-IN set of an open vocabulary, not a closed enum: metadata authors may contribute their own values (sanctioned channel: `activityMilestones[].type`, ADR-0052 §5b.2), and an undeclared value is stored verbatim rather than rejected. Consumers must render an unknown value instead of assuming this list is exhaustive (maintainer ruling 2026-08-24, #11507).",
help: "Activity kind. The declared options are the platform BUILT-IN set of an open vocabulary, not a closed enum: metadata authors may contribute their own values (sanctioned channel: `activityMilestones[].type`, ADR-0052 §5b.2), and an undeclared value is stored verbatim rather than rejected. Consumers must render an unknown value instead of assuming this list is exhaustive: the vocabulary is open by decision, not a gap awaiting enforcement.",
options: {
created: "作成",
updated: "更新",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
*/

export const jaJPGeneratedSourceHashes: Readonly<Record<string, string>> = {
"objects.sys_activity.fields.type.help": "bc8b10c89aacd494",
"objects.sys_activity.fields.type.help": "5d8df777db464241",
"objects.sys_activity.fields.url.label": "2203b0b9f72534f7",
};
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ export const zhCNObjects: NonNullable<TranslationData['objects']> = {
},
type: {
label: "类型",
help: "Activity kind. The declared options are the platform BUILT-IN set of an open vocabulary, not a closed enum: metadata authors may contribute their own values (sanctioned channel: `activityMilestones[].type`, ADR-0052 §5b.2), and an undeclared value is stored verbatim rather than rejected. Consumers must render an unknown value instead of assuming this list is exhaustive (maintainer ruling 2026-08-24, #11507).",
help: "Activity kind. The declared options are the platform BUILT-IN set of an open vocabulary, not a closed enum: metadata authors may contribute their own values (sanctioned channel: `activityMilestones[].type`, ADR-0052 §5b.2), and an undeclared value is stored verbatim rather than rejected. Consumers must render an unknown value instead of assuming this list is exhaustive: the vocabulary is open by decision, not a gap awaiting enforcement.",
options: {
created: "已创建",
updated: "已更新",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,6 @@
*/

export const zhCNGeneratedSourceHashes: Readonly<Record<string, string>> = {
"objects.sys_activity.fields.type.help": "bc8b10c89aacd494",
"objects.sys_activity.fields.type.help": "5d8df777db464241",
"objects.sys_activity.fields.url.label": "2203b0b9f72534f7",
};
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ describe('unknown node config keys are rejected (#4277)', () => {
// …and this particular key has a documented incident, so it also carries
// its tombstone (the UNKNOWN_KEY_GUIDANCE pattern).
expect(msg).toContain('visibleWhen');
expect(msg).toContain('#3528');
expect(msg).toContain('blocks the screen from ever being submitted');
// The flow is NOT registered.
await expect(engine.getFlow('f')).resolves.toBeNull();
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export function registerConnectorNodes(engine: AutomationEngine, ctx: PluginCont
success: false,
error:
`connector_action '${node.id}': connector '${cfg.connectorId}' is degraded — ${degraded}. `
+ `Dispatch is unavailable until its upstream recovers; the platform retries automatically (#3017).`,
+ `Dispatch is unavailable until its upstream recovers; the platform retries automatically.`,
};
}
return {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ describe('decision branch routing (#4414)', () => {
// branch and every out-edge label.
expect(warnings.some((w) =>
w.msg.includes('no out-edge carries that label')
&& w.msg.includes('#4414')
&& w.msg.includes('The branch selection is IGNORED')
&& w.meta?.branchLabel === 'Yes — already converted'
&& (w.meta?.outEdges as Array<{ label: string | null }>)
.map((e) => e.label).includes('Yes')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export function parseNodeConfig<T>(
`${nodeType} '${nodeId}': config does not satisfy the ${nodeType} contract — ${issues || 'invalid config'}. ` +
`The config is metadata, so re-running changes nothing; fix the node in the flow definition. ` +
`The declared contract is the node type's configSchema (the Studio form) and the ` +
`${nodeType} config Zod in @objectstack/spec/automation (#4277).`,
`${nodeType} config Zod in @objectstack/spec/automation.`,
),
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -333,7 +333,7 @@ export function registerScreenNodes(engine: AutomationEngine, ctx: PluginContext
success: false,
error:
`script node '${node.id}': no function named '${target}' is registered. ` +
`Register it via \`defineStack({ functions: { '${target}': fn } })\`, or fix the name (#1870).`,
`Register it via \`defineStack({ functions: { '${target}': fn } })\`, or fix the name.`,
};
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ function unknownFunctionError(name: string, expr: string): FlowExpressionFunctio
`flow value expression: unknown function '${name}' in '${expr}'. ` +
`Value expressions support round, floor, ceil, abs, min, max (1:1 with the CEL stdlib) ` +
`and the whole-token date macros NOW() / TODAY().${hint} ` +
`(Before #11060 this name was silently rewritten to null and the field was written undefined.)`,
`An unknown function is refused here rather than evaluated to null, which would write the field as undefined.`,
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ describe('#5636 — the degrade announcement is ONE record, cause in meta', () =
expect(record.msg).toContain("'gh_mcp'");
expect(record.msg).toContain("provider 'fake'");
expect(record.msg).toContain('instance registered degraded (no actions)');
expect(record.msg).toContain('attempt 1 (#3017)');
expect(record.msg).toContain('retrying with backoff, attempt 1.');
// Not a validation rejection → `error`, not `issues`; and the full text
// survives, newlines escaped by the logger's JSON.stringify.
expect(record.issues).toBeUndefined();
Expand Down Expand Up @@ -271,7 +271,7 @@ describe('#5636 — a husk-registration rejection reports its issues, on one lin
};
expect(record.msg).not.toContain('\n');
expect(record.msg).toContain("'gh_mcp'");
expect(record.msg).toContain('#3017');
expect(record.msg).toContain('stays absent from the connector registry until a retry succeeds');
// The facts a reader came for.
const issues = record.issues;
expect(Array.isArray(issues)).toBe(true);
Expand Down Expand Up @@ -373,7 +373,7 @@ describe('#5636 — what the interpolated rendering cost, measured', () => {
expect(before.length - beforeKept.length, 'lines the buffer drops').toBeGreaterThan(1);

const after = await captureStream('stdout', async () => {
log.warn(`[Automation] could not register degraded husk for 'gh_mcp' (#3017).`, {
log.warn(`[Automation] could not register degraded husk for 'gh_mcp'.`, {
issues: [{ code: 'invalid_value', path: 'type', message: 'Invalid option' }],
});
});
Expand All @@ -393,7 +393,7 @@ describe('#5636 — what the interpolated rendering cost, measured', () => {
const before = await captureStream('stderr', async () => {
log.error(
`[Automation] connector instance 'gh_mcp' (provider 'fake') upstream unavailable — ` +
`instance registered degraded (no actions); retrying with backoff, attempt 1 (#3017): ${MULTILINE_UPSTREAM}`,
`instance registered degraded (no actions); retrying with backoff, attempt 1: ${MULTILINE_UPSTREAM}`,
);
});
expect(before).toHaveLength(3);
Expand All @@ -404,7 +404,7 @@ describe('#5636 — what the interpolated rendering cost, measured', () => {
const after = await captureStream('stderr', async () => {
log.error(
`[Automation] connector instance 'gh_mcp' (provider 'fake') upstream unavailable — ` +
`instance registered degraded (no actions); retrying with backoff, attempt 1 (#3017).`,
`instance registered degraded (no actions); retrying with backoff, attempt 1.`,
undefined,
{ error: MULTILINE_UPSTREAM },
);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ describe('kernel:ready audit (wiring)', () => {
const warnings = auditWarnings(warnSpy);
expect(warnings).toHaveLength(1);
expect(warnings[0]).toContain('crm_billing');
expect(warnings[0]).toContain('#2612');
expect(warnings[0]).toContain('catalog descriptors (descriptor-only contract)');
await kernel.shutdown();
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ describe('#5660 — the degrade REGISTRATION is one record, cause in meta', () =
// Self-sufficient without the reason: what the state costs and what
// happens next, so the message alone is a usable record.
expect(record.msg).toContain('no actions and no handlers');
expect(record.msg).toContain('#3017');
expect(record.msg).toContain('the materializer retries with backoff');
// …and none of the foreign text is in it.
expect(record.msg).not.toContain(CAUSE_LINE_FACT);
expect(record.msg).not.toContain(HINT_LINE_FACT);
Expand Down Expand Up @@ -404,7 +404,7 @@ describe('#5660 — what the interpolated rendering cost, measured', () => {
log.warn(
'Connector registered DEGRADED: gh_mcp (origin: declarative) — no actions and no handlers ' +
'until its upstream is reachable; a connector_action dispatching to it fails with the stored ' +
'reason, and the materializer retries with backoff (#3017).',
'reason, and the materializer retries with backoff.',
{ degradedReason: MULTILINE_UPSTREAM, error: MULTILINE_UPSTREAM },
);
});
Expand Down
Loading
Loading