Skip to content

Implementation Plan: core-hr-transaction-bundle

Summary

Configures five new named transactions against Nova's existing transaction framework (TransactionContextRunnerService + Decision Table routing in nova-corehcm-sn-app, orchestrated by nova-corehcm-agent's decision node) — Personal Data Updates, Job Data Changes, Promotion, Demotion, and Salary Changes. Bonus and Incentive Awards (originally a sixth) is deferred out of this build pass — decided, not just flagged: it has no existing home in nova-compensation-sn-app's schema, and rather than pulling that app's team in as a stakeholder now, it's skipped (see spec.md's Out of Scope). None of the five is a new service or a new architectural pattern; each is a new intent + entity schema + Decision Table + context-script config on top of what already runs department transfers and manager changes today.

Salary Changes crosses a domain boundary the spec didn't originally account for. This is resolved by reading nova-compensation-sn-app's actual source directly (see schema/compensation-schema.md): it calls WorkerCompensationService.upsertCompensation() — a cross-scope-public script include already live in that app, upserting x_novaw_comp_worker_compensation by (worker, effective_date). No new REST endpoint, table, or service is needed.

Promotion and Demotion are primarily nova-corehcm-sn-app transactions (job level, title, and Job Assignment are entirely Core HR-owned), but both also trigger a compensation-band change — that write goes through the same WorkerCompensationService.upsertCompensation() cross-scope call as Salary Changes, not a separate mechanism. Three transactions touch nova-compensation-sn-app, not one.

Spec is status: draft with several genuine open items (document/attachment reference, job-description storage location, multi-jurisdiction tax, Bonus/Incentive Award schema) — none of these are stated by the spec owner as blocking, so this plan proceeds to ready-for-exploration. They're carried into Open Questions below and should be settled during schema-changes.md drafting, before the schema-first gate is signed.

test-plan.md now exists alongside this plan — 21 Given/When/Then scenarios expanding spec.md's Acceptance Criteria per transaction, plus a Cross-cutting group covering the compensation-write role check, the RCA cross-scope-privilege check, and the missing hrbp test account, all sourced from this plan's own Risk Flags. Several scenarios are explicitly marked blocked on the same Open Questions listed below — Nova Developer should treat it as the acceptance-level source of truth, not re-derive scenarios from scratch.

End-to-End Flow

A Direct Manager proposes a Salary Change for a report → it's validated, routed for approval, and on approval the change lands on both the Core HR job/worker record and the compensation record, with the worker notified.

sequenceDiagram
    participant Mgr as Direct Manager
    participant SA as nova-superagent-agent
    participant CA as nova-corehcm-agent
    participant SN as nova-corehcm-sn-app
    participant HRBP as HR Business Partner
    participant Comp as nova-compensation-sn-app
    participant Worker as Worker

    Mgr->>SA: submit salary change request
    SA->>CA: dispatch HR transaction task
    CA->>SN: validate inputs against Core HR schema
    SN-->>CA: validation passed
    CA->>SN: evaluate Decision Table for salary change
    SN-->>CA: requires approval, route to HRBP
    CA->>SN: create task and approval record
    SN->>HRBP: approval task assigned
    HRBP->>SN: approve with stated reason
    SN->>SN: run ordered context scripts for salary change
    SN->>Comp: update worker compensation record
    SN-->>CA: transaction complete via Kafka event
    CA->>Worker: notify approved change and effective date

Design Decisions

  • Configuration, not new services. All five transactions reuse the existing TransactionContextRunnerService / Decision Table pattern already live for department transfers and manager changes — this Feature adds five new named transaction configs, not new infrastructure.
  • Deterministic vs. approval-gated is set per transaction, not uniformly. Personal Data Updates' address/contact path is the only fully deterministic (no-approval) path among the five; every other path routes through at least one human approval, per each transaction's own policy (see spec.md Scope).
  • Compensation writes cross a scope boundary via an existing public script include — and three transactions need it, not one. Salary Changes', Promotion's, and Demotion's execution scripts all call new x_novaw_comp.WorkerCompensationService().upsertCompensation({worker_sys_id, effective_date, base_salary}) directly — confirmed by reading nova-compensation-sn-app's source (WorkerCompensationServiceWrapper.js, accessibleFrom: 'public'). This is the same public-wrapper/package_private-impl pattern nova-corehcm-transaction-creation's skill documents for WorkerService/JobService, just cross-scope. Promotion and Demotion stay primarily nova-corehcm-sn-app transactions (job level/title change) — the compensation write is one step inside their execution script, not a separate transaction. Role gotcha (applies to all three): the table's write ACL grants x_novaw_comp.hr_ops and x_novaw_comp.integration_writer (create also integration_creator) — notably not the generic x_novaw_comp.agent role used elsewhere in the platform. Confirm which role the execution script's session runs as; if it's a generic agent role, the write will fail ACL, not just silently no-op.
  • New transaction-framework skill now exists and is auto-loaded. nova-corehcm-transaction-creation (installed this session, .github-private/claude/skills/) documents the transaction_configuration → context script → execution script anatomy, the ctx/ctxJson contract, CoreHRConstants table lookup, and the IIFE return-value pattern — nova-developer-sn.md's Phase 1b now reads it automatically whenever the target repo uses this framework. All six transaction configs (plus the cross-scope compensation calls above) should follow its boilerplate rather than being built ad hoc from generic Business Rule/Script Include patterns.
  • Bonus and Incentive Awards is deferred, not built this pass — decided. x_novaw_comp_worker_compensation has sign_on_bonus (one-time) but nothing resembling a target/actual incentive award record anywhere in that scope's actual schema; building it would mean a new table and service method in nova-compensation-sn-app's own scope. Rather than pulling that app's team in now, this transaction is out of scope for this Feature (see spec.md). No schema-gate item for it in this pass.
  • Job Data Changes' storage mechanism is resolved. It writes a new effective-dated Job Assignment entry carrying the updated description/skill tags, with the prior entry closed via job_end_date set to the same effective date — kept for historical record, not overwritten. This is the same insert-new/close-old pattern nova-corehcm-sn-app's update_sor_node flow already uses for department transfers and manager changes (per its own flow.md), not a new mechanism and not a Job Profile edit. No new field needed on either table.
  • Post-dated transactions use the platform's Business Event / Event Queue pattern — not in the transaction-creation skill, confirmed via KB. All six transactions carry a future effective date (required for Promotion/Demotion/tax withholding; optional elsewhere), and every one of their source policy docs already has a "Stakeholders by Phase" section (Confirmed/Reminder/Pre-Execution/Post-Execution/Follow-Up) — these are literally x_novaw_platform_event_phase_configuration.phase values, not prose. The real mechanism (nova-platform-sn-app's skills.md/kafka.md): agent calls build_eventsprocess_events, immediate phase fires now, later phases queue against their effective_date, an hourly BusinessEventOrchestrator job picks up due events via the NovaEventQueueProcessor extension point (CoreHCMEventQueueProcessor already implements it), and the Kafka payload's agent_process_id resumes the LangGraph checkpoint — the same rehydration mechanism already used for approval resume, just triggered by a date arriving instead of a click. nova-corehcm-transaction-creation says nothing about this (grepped, zero mentions) — it's a second, separate pattern each of the six transactions also needs configured (a business_event_configuration + per-phase event_phase_configuration records), on top of its transaction_configuration. Work is configuration within nova-corehcm-sn-app (extension point already implemented there) — no nova-platform-sn-app code changes expected.
  • Test coverage is nova-corehcm-agent's job, not a separate repo — detailed pattern belongs in a skill, not here. Each of the five transactions needs: a ServiceNow-side eval/integration test, a chat/conversational-experience test, and unit tests for the agent code changes. nova-corehcm-agent already has an established convention for the first two (tests/EVALS.md, tests/API_integration_tests/INTEGRATION_TESTS.md) — Nova Developer should follow it rather than reinvent test structure; if that convention isn't already captured as a skill, one should be, the same way nova-corehcm-transaction-creation now captures the SN-script pattern.
  • nova-test-ci is held, not implemented this pass — decided. Its E2E expansion (per Services Affected) is real, needed work, but this Feature does not build it now. test-plan.md's 21 scenarios are what will drive that expansion in a future pass — removed from deploy_order/exploration_status below accordingly.

Services Affected

Service Role
nova-corehcm-agent Extends intent taxonomy and decision routing for the 5 new transaction types; dispatches to nova-corehcm-sn-app for validation, decisioning, and SOR update
nova-corehcm-sn-app New Decision Table configs and ordered context scripts per transaction; new business_event_configuration + per-phase event_phase_configuration records per transaction (Confirmed/Reminder/Pre-Execution/Post-Execution/Follow-Up — CoreHCMEventQueueProcessor extension point already implemented, no new plumbing); schema deltas (reason code, tax jurisdiction); approval task/notification wiring. Job Data Changes needs no new field — resolved to reuse the existing effective-dated Job Assignment insert-new/close-old pattern.
nova-compensation-sn-app Receives the WorkerCompensationService.upsertCompensation() write from Salary Changes, Promotion, and Demotion. (Bonus and Incentive Awards' new-table need is moot — deferred, not built this pass.)
nova-compensation-agent Reflects updated compensation data in existing paycheck/benefits queries once written — read-side only, no new intents expected
nova-frontend-litjs New self-service widget(s) for Worker-facing personal-data fields and a Manager/HRBP approval-queue view, fed by data injected from the ServiceNow host per this repo's consumer-owned data flow pattern

nova-test-ci is held for this pass — its E2E coverage (corehcm.spec.ts is currently a generic container-health smoke test only) genuinely needs expanding per nova-architecture.md's own constraint, but that work is deferred to a future pass driven by test-plan.md's 21 scenarios, not built now. Removed from deploy_order below.

Sibling Conflicts / Dependencies

No active sibling Feature Set/Feature found in-progress or ready-for-review in this repo. Two soft dependencies carried from spec.md: - worker-document-management (separate Feature Set, not yet scoped) — resolved as a hard dependency, not an open item: Personal Data Updates' dependent/tax-withholding paths block on it for all document/attachment handling; this Feature builds none of that capability itself. - A future Cost Center Change / Org Restructuring / Bonus and Incentive Awards Feature (same Feature Set) shares the same transaction-framework pattern; no data dependency.

Risk Flags

  • TENANT DATA — every new table/field (reason code, tax jurisdiction, incentive award) must carry domain/domain_path per schema/core-hr-schema.md's Domain Separation note; verify each new table follows this before the schema gate.
  • ACCESS CONTROLx_novaw_comp_worker_compensation's write ACL does not include x_novaw_comp.agent. Nova Developer's Phase 1 must confirm the execution script's runtime role includes hr_ops or integration_writer before assuming the cross-scope WorkerCompensationService call will succeed — applies to Salary Changes, Promotion, and Demotion alike.
  • RCA (Restricted Caller Access) NOT YET GRANTEDnova-corehcm-sn-app has two existing sys_scope_privilege records, both granting global scope access — zero target x_novaw_comp. accessibleFrom: 'public' on WorkerCompensationService only makes the target eligible for cross-scope calls — the caller still needs its own outbound RCA record with status: allowed. Without one, ServiceNow auto-creates the record as requested (blocked) the first time Salary Changes/Promotion/Demotion actually runs in a live instance, not at build or deploy time — this would silently fail in testing/production until an admin manually approves it. Required as part of this Feature's nova-corehcm-sn-app changes: add a sys_scope_privilege_<uuid>.xml record to that repo's update/ directory (pattern from its own existing sys_scope_privilege_0528b550c3a5b290eb7cf3ddd40131de.xml — confirmed by Phase 1 exploration; the .now.ts fluent-SDK pattern this risk originally cited does not apply to this repo, which uses raw XML update sets) — operation: execute, target_name: WorkerCompensationService, target_scope: <x_novaw_comp scope sys_id, resolve from the live instance>, target_type: sys_script_include, status: allowed — pre-declared, not left to auto-request.
  • NO ISOLABLE HRBP TEST ACCOUNT — per tests/API_integration_tests/INTEGRATION_TESTS.md §3.2: every account holding x_novaw_corehcm.hrbp on the test instance also holds full admin, so HRBP-gated behavior can't be tested in isolation today. Promotion, Demotion, and Salary Changes all route through HRBP approval — three of five transactions in this pass. A dedicated hrbp-only test account is needed before this Feature's integration tests can actually verify that gate, not just admin behavior standing in for it.

Schema Gate

proposed — no schema-changes.md has been drafted yet for this Feature Set. Phase 1 exploration narrowed what's actually needed, confirmed against the live dictionary XML: x_novaw_corehcm_job_assignment.reason already exists with promotion/demotion choice values present — the schema-gate item here is just adding Job Data Changes' own choice-list values (role refresh / skills update / job architecture alignment), not a new field. job_assignment.description also already exists (string, max 500) — no delta needed for Job Data Changes at all. tax_jurisdiction is confirmed not present anywhere in the person/worker schema — a real, unresolved gate item, but scoped only to Personal Data Updates' tax-withholding path; address/contact/name are unaffected. Draft feature-sets/critical-core-hr-transactions/schema-changes.md covering the reason choice-list addition, the tax-jurisdiction decision, and the new business_event_configuration/event_phase_configuration records per transaction — the gate must be signed before SN table/field creation in nova-corehcm-sn-app. No schema delta is needed in nova-compensation-sn-app this pass (Bonus and Incentive Awards deferred). The new sys_scope_privilege (RCA) record in nova-corehcm-sn-app is a security-relevant config change, not a table — call it out explicitly at gate review even though it isn't a schema delta itself.

Checklist Applicability

From github-private/.github/CHECKLIST/feature.md:

  • [ ] Unit tests written and passing — applicable (5 repos this pass — nova-test-ci held); scenarios sourced from test-plan.md
  • [ ] AI evals (unit level) — applicable (nova-corehcm-agent gains 5 new intents; utterance-classification coverage via tests/testsets/, per Design Decisions)
  • [ ] AI evals end-to-end — applicable
  • [ ] Feature flags for WIP merged to main — applicable if rolled out incrementally per transaction
  • [ ] Backward compatibility confirmed — applicable (SN schema changes in 2 scoped apps)
  • [ ] Rollback plan documented — applicable (schema changes are hard to roll back per Risk Flags)
  • [ ] Bug bash completed — applicable
  • [ ] Accessible via /nova workspace interface — applicable if the conversational path is the primary entry
  • [ ] Portal widgets ready and tested — applicable (nova-frontend-litjs new widgets)
  • [ ] Portal widgets function outside /nova — applicable, needs explicit test if a standalone portal surface is also supported
  • [ ] Storybook entries complete — applicable (nova-frontend-litjs)
  • [ ] Transactions reviewed and categorized to a pricing tier — applicable, PM/CPO sign-off
  • [ ] MAU user counting for new SN actions — applicable
  • [ ] Nova User/Admin documentation — applicable
  • [ ] Nova Implementation Guide updated — applicable (2 scoped apps + new agent intents)
  • [ ] github-private engineering info updated — applicable

Open Questions

  • Does Worker need multi-jurisdiction tax-withholding support in v1? (from spec.md)
  • Confirm the Salary Changes/Promotion/Demotion execution scripts' runtime role actually has hr_ops or integration_writer on x_novaw_comp_worker_compensation (see Risk Flags) — WorkerCompensationService.upsertCompensation() itself is resolved.
  • nova-test-ci is deliberately held this pass (decided) — when a future pass picks it up, does nova-developer-orchestrator's repo-classification table need a fourth specialized-developer path for it (confirmed today it has none — grepped, zero mentions), or is it handled manually outside the automated pipeline?
  • Should a second skill (mirroring nova-corehcm-transaction-creation) document the Business Event / Event Queue phase-configuration pattern before Nova Developer's Phase 1 starts, given it's fully KB-documented but has no skill today the way the transaction-execution layer now does?

For Nova Developer

service_repo: nova-corehcm-agent
base_branch: dev
suggested_branch: feature/316-core-hr-transaction-bundle
deploy_order:
  - nova-corehcm-sn-app
  - nova-compensation-sn-app
  - nova-corehcm-agent
  - nova-compensation-agent
  - nova-frontend-litjs
  # nova-test-ci intentionally omitted — held this pass, per decision; a future pass
  # adds it back, driven by test-plan.md's 21 scenarios
sn_app_changes_required: true
sn_app_repo: nova-corehcm-sn-app
new_env_vars: []
infra_changes_before_code: none
spec_path: feature-sets/critical-core-hr-transactions/core-hr-transaction-bundle/spec.md
exploration_status:
  nova-corehcm-agent: done
  nova-corehcm-sn-app: done
  nova-compensation-sn-app: done
  nova-compensation-agent: done
  nova-frontend-litjs: done

Files to Create

nova-corehcm-sn-app (raw XML update sets under 3befabcfc38df25044b1bdac0501316d/update/):

  • x_novaw_platform_transaction_configuration_<uuid>.xml — names: personal data update, job data change, promotion, demotion, salary change. Pattern: x_novaw_platform_transaction_configuration_0c9db1a58328cf1067746f60ceaad379.xml ("Additional job assignment").
  • x_novaw_platform_context_script_<uuid>.xmluser_information (order 1) for all 5 transactions, plus all_profiles (order 2) for promotion and demotion. Pattern: x_novaw_platform_context_script_7e8aba6ac337b210fc4eba2ed4013178.xml (note: source is active: false — new records must be active: true) and "change of job assignment"'s all_profiles script.
  • x_novaw_platform_execution_script_<uuid>.xml — see Implementation Steps for per-transaction service calls. Patterns: ..._41cc467183ac0b90cec35fd6feaad30a.xml (update preferred name), ..._80bf5b3cc3808710fc4eba2ed4013180.xml (change of job assignment), ..._f986ec9083a0471067746f60ceaad36c.xml (contractor to FTE — multi-step reference for promotion/demotion).
  • x_novaw_platform_business_event_configuration_<uuid>.xml — one per transaction. Pattern: ..._115aa30ec3b50b1078485f73e40131cb.xml ("Remove contact").
  • 25× x_novaw_platform_event_phase_configuration_<uuid>.xml — 5 phases (confirmed, reminder, pre_execution, post_execution, follow_up) × 5 transactions. Pattern: ..._055aa30ec3b50b1078485f73e40131a7.xml.
  • sys_scope_privilege_<uuid>.xmlhard blocker if omitted. operation: execute, source_scope: 3befabcfc38df25044b1bdac0501316d, target_name: WorkerCompensationService, target_scope: <x_novaw_comp scope sys_id — resolve from live instance, do not hardcode>, target_type: sys_script_include, status: allowed. Pattern: sys_scope_privilege_0528b550c3a5b290eb7cf3ddd40131de.xml (existing RCA record in this same repo — NOT the nova-compensation-sn-app fluent .now.ts pattern, which doesn't apply here).

nova-corehcm-agent:

  • tests/testsets/personal_data_updates.yml, job_data_changes.yml, promotion.yml, demotion.yml, salary_changes.yml — pattern: tests/testsets/change_of_department.yml (full standard scenario matrix) / change_of_job_assignment.yml for job data changes specifically. Salary Changes' happy path uses mock_scenario: "waiting_for_approval" as primary (no isolable hrbp test account — see Risk Flags).

nova-frontend-litjs:

  • src/hr-transactions/personal-data-widget.ts + -styles.ts — pattern: src/profile-details/add-contact-modal.ts + employee-details-widget.ts.
  • src/hr-transactions/hr-approval-queue.ts + -styles.ts — pattern: src/superagent-tasks/superagent-tasks.ts.
  • src/hr-transactions/types.tsHrTransactionTask, PersonalDataPayload, HrTransactionStatus.
  • src/hr-transactions/index.ts — barrel export.
  • src/hr-transactions/__tests__/personal-data-widget.test.ts, hr-approval-queue.test.ts.
  • config/widget-build-config/vite.config.hr-transactions.ts.
  • src/stories/hr-transactions.stories.ts — flagged by explorer as missing from the original high-level plan; needed for the Checklist's Storybook item.

nova-compensation-sn-app, nova-compensation-agent: none — confirmed zero changes needed (see Implementation Steps).

Files to Modify

  • nova-corehcm-agent: tests/testsets/intent_detection.yml (5 new phrasing groups), tests/EVALS.md (5 new testset rows).
  • nova-frontend-litjs: src/index.ts (barrel export), package.json build script (append new vite build invocation).

Implementation Steps

  1. Transaction configuration records (nova-corehcm-sn-app) — create the 5 transaction_configuration XMLs first; every other record references their sys_ids.
  2. Context scriptsuser_information (order 1) for all 5; all_profiles (order 2) additionally for promotion/demotion (job-profile validation before the level change).
  3. Execution scripts — per-transaction service calls:
  4. Personal data update → WorkerService.updatePersonalInfo(userInput, fields). Address/contact/name paths fully buildable now; tax-withholding path blocked on the tax_jurisdiction schema gap (see Open Questions).
  5. Job data change → DepartmentService.changeDepartment() — insert-new/close-old on Job Assignment is already built into this call; description field already exists (string, max 500), no schema delta needed.
  6. Promotion / Demotion → DepartmentService.changeDepartment() then new x_novaw_comp.WorkerCompensationService().upsertCompensation({worker_sys_id, effective_date, base_salary}). Set the existing job_assignment.reason field explicitly ("promotion"/"demotion" values already present in its choice list).
  7. Salary change → upsertCompensation() only, no Core HR–side record change.
  8. Business event + event phase configuration records — one business_event_configuration per transaction, 5 event_phase_configuration children each, audience_script via ApprovalService.getDownstreamApproverSysIds().
  9. Cross-scope privilege (RCA) record — create before any live/test execution of Promotion, Demotion, or Salary Change; without it the first execution auto-creates a requested (blocked) privilege instead of failing loudly.
  10. nova-corehcm-agent test artifacts — 5 new testset YAMLs + intent_detection.yml/EVALS.md updates. No graph/node/routing code changes — confirmed the intent taxonomy is entirely SN-config-driven. Coordinate transaction name strings exactly between the SN config names above (personal data update, job data change, promotion, demotion, salary change) and the testset YAMLs' expected_intent fields — a mismatch here would silently break intent detection.
  11. nova-frontend-litjs widgets — build both as pure data-receivers (SN host injects props, component only emits events); no direct API calls from inside either widget.

Tests

Unit Tests

  • No new unit tests required in nova-corehcm-agent or nova-compensation-agent — no routing/node code changed; existing suites (tests/unit/test_routing.py, etc.) already cover the unchanged code paths that the 5 new transactions flow through generically.
  • nova-frontend-litjs: Jest + @open-wc/testing fixtures for both new widgets (empty state, data render, event emission on submit/approve/reject) — pattern from time-off-page.test.ts.

Eval Cases

  • nova-corehcm-agent: 5 new tests/testsets/*.yml files (standard scenario matrix per transaction) + intent_detection.yml phrasing cases — see Files to Create above.
  • No eval files needed in nova-compensation-agent, nova-compensation-sn-app, or nova-frontend-litjs.