Engine gap: the 'automation.record.update' event carries changedFieldIds
(and now oldFields), but RecordUpdatedTrigger.accumulate() dropped both —
scripts had no way to see what changed or pre-update values, despite the
documented trigger output (trigger-types.md).
- record-updated/record-condition/record-created-or-updated triggers:
pass changedFieldIds + oldFields through to accumulate()
- batch-buffer TriggerEvent += oldFields; batch flush records[] entries
now carry changedFieldIds + oldFields per record
- v2 updateRecord/updateRecords: emit oldFields (pre-update snapshot)
- v1 controller updateRecord/updateRecords: snapshot before-fields via
getRecordsById, compute changedFieldIds (deep-equal diff, same
semantics as v2) instead of hardcoded [] — v1/v2 parity
- new spec record-updated.trigger.spec.ts (4 tests, passthrough +
watch-filter + table isolation); workflow suite 16 files / 99 tests
- trigger-types.md: document semantics + honest scope (REST API writes;
ShareDB grid edits do not fire this trigger)
Live-verified: PATCH 流转角色 → trigger_input.changedFieldIds=[fld...],
oldFields=[售前客服] → record=[技术]; script reads oldRole via
triggerInput; fieldIds watch-filter blocks non-watched fields.
Scripts can now branch on WHO triggered the workflow and their role in the
base, without an extra API call (the workflow token has no role-read scope).
New triggerInput fields (user-initiated triggers only: buttonClick /
recordCreated / recordUpdated / recordCondition / formSubmitted):
- _triggerUserRoles: string[] (enabled role display names in this base)
- _triggerUserIsAdmin: boolean (space owner / AM admin / AM-disabled)
Absent for scheduledTime / webhook (no user context).
Implementation:
- trigger-user-info.ts: resolveTriggerUserInfo(prisma, baseId, userId) —
standalone, unit-tested (8 tests). Mirrors the admin logic in
AuthorityMatrixPermissionService but returns only role names + bool.
- workflow-runner.service.ts: resolve once at first action node, merge into
enrichedInput (both execute() and test() paths).
- skill action-types.md: document the fields + script branching example.
- action add/update: new --dependencies <json> option
- parseDependencies accepts JSON, name@version shorthand, or bare names
- update is incremental; --dependencies replaces the whole array
- docs: deployment/systemd.md + skill SKILL/action-types/development
document the npm-deps feature and the service-PATH requirement
(spawn npm ENOENT without nvm bin dir on PATH)
Ran 4 end-to-end evals (skill-creator flow): all 23 assertions passed (100%).
The evals exposed 3 issues, now fixed:
1. CLI bug: getRecords/createRecord/updateRecord --target-table flag was
read as opts['target-table'] but commander stores it camelCase
(opts.targetTable). Added a pick() helper accepting both forms.
2. trigger-types.md: buttonClick output said record.fields.* implying all
fields; actually record.fields only contains the button field's count.
Documented the back-channel API fetch pattern for other fields.
3. automation-guide.md: documented that 'workflow test' uses mock input
with empty fields, so upstream outputs are absent in test mode — use
real fire for data-dependent validation.
Also: corrected button field options (workflow.id object, not workflowId).
Rename the skill directory and update all references (SKILL.md frontmatter
name, AGENTS.md, design doc, install.sh). Git tracks this as a rename so
history is preserved.