Annual Leave Promotion — Signature-Only Scope Correction Plan
Status: Planned on 2026-08-10 after Eron confirmed the final product rule: the hereby Sign document is an annual-leave-promotion consent form only. The employee completes the feature by signing; any annual-leave plan is prepared and submitted separately outside this feature, with no system-defined format or tracking.
Execution rule: This decision supersedes every earlier requirement or implementation that makes employee plan fields, a plan format, plan validation, or plan persistence part of first-notice completion. Patch 6 completion reliability remains required. Code changes still require Eron's separate approval.
1. Goal
Correct the implemented scope so the workflow matches the confirmed business outcome:
- DHub sends a Messenger notice and a Sign consent form.
- The employee reviews and signs the consent form.
- Verified Sign completion immediately completes/applies the first-notice workflow in DHub.
- DHub and Sign do not collect, validate, normalize, display, or store the employee's separately submitted annual-leave plan.
- Failure to submit a plan is not a DHub/Sign state or error condition.
This patch removes the employee-plan coupling added by the original plan and Patch 6 while preserving reliable completion, webhook Outbox delivery, tenant isolation, deadlines, reminders, expiry, and company-side second designation for unsigned cases.
2. Confirmed Product Decisions
In scope
- Consent-form template selection and ownership/fingerprint validation.
- One employee signer and required signature evidence.
- Messenger first notice, reminder, deadline, and Sign CTA.
- Sign completion atomicity, idempotent retry, completion webhook Outbox, DHub Inbox, and reconciliation.
- DHub
APPLIED / completedimmediately after authenticated completed signature validation. - Company-side second designation only for cases that did not complete the consent signature before the deadline.
Out of scope
- Employee plan fields in the Sign template.
- A system-provided plan form or format.
annual_leave.plan.*labels.- Plan date/minute parsing, total calculation, period/balance validation, or plan-item persistence from Sign.
- Tracking whether the employee separately submitted a plan.
- Blocking consent completion because a plan is missing.
- Mentioning plan submission as a condition for DHub completion.
The existing AnnualLeavePromotionPlanItem model may remain only for the company's second-designation schedule. It must not store employee plan data from Sign.
3. Impact on Existing QA and Patch 6
Contract 81 exposed two different observations:
- Still a Bug: the first completion request committed Sign terminal state but showed an unknown error, and no completion webhook reached DHub.
- No longer a Bug: every
annual_leave.plan.*value was empty. Under the confirmed requirement, no plan values should be required or interpreted.
Therefore:
- keep Patch 6's atomic completion, idempotent response, lifecycle-wide
notificationPolicy=none, and durable webhook Outbox; - remove Patch 6's
completionPolicy, plan-label validation, and empty-plan rejection; - remove DHub's plan-result validation and employee-plan persistence;
- treat a completed consent signature as sufficient for
APPLIED.
Contract 81 remains historical QA evidence and must not be rewritten or used to fake a webhook. A fresh contract is required to prove the corrected first-click response and reliable completion delivery.
4. Required Behavior
4.1 Sign template contract
Prepared local-QA upload artifacts:
output/pdf/annual-leave-promotion-consent-template.pdf
output/pdf/annual-leave-promotion-consent-template.coordinates.jsonUpload this as a new versioned template; do not replace or mutate the three-page template already used by contract 81. The PDF is a QA sample and its Korean labor/legal wording still requires review before production use.
The current Sign admin-import contract requires both admin and employee manifest entries. The prepared Manifest therefore includes Holder as the technical sender and employee as the signing participant. All seven components are assigned to employee; Holder has no field, signature, or seal component and is not a company-side approval step.
A compatible annual-leave-promotion template requires:
- the authenticated DHub corporation owns the template;
- the fingerprint/version matches the selected snapshot;
- the employee is the only participant assigned form/signature components; an importer-required Holder sender entry may exist without assigned components;
- required consent text and employee signature component are present;
- no company-side signature, seal, or approval component is required;
- no
annual_leave.plan.*labels or completion policy are required.
Generic fieldValues support remains available for immutable consent-form prefills such as employee name, period, remaining-balance display, notice date, and deadline. Do not remove the generic template-prefill capability used by other Sign integrations.
4.2 Sign completion
For a DHub-managed consent form:
- authenticate the signer/session and contract/request ownership;
- verify the required employee signature and terminal eligibility;
- atomically commit contract completion, external-request completion, terminal timestamp, and one completion webhook Outbox event;
- return success immediately after the local commit;
- return idempotent success to the same authenticated uncertain retry;
- deliver the same completion event through the durable Outbox worker;
- suppress Sign-native invitation/reminder/completion email, Kakao, and SMS for
notificationPolicy=none.
No plan field may participate in validation or terminal eligibility.
4.3 DHub completion
DHub accepts completion only after verifying:
- timestamped HMAC, replay window, event identity, and organization scope;
- request key, contract ID,
ESignRecord, attempt, promotion, and current-attempt consistency; - monotonic terminal status/version;
- authenticated S3 result is
completed; completedAtexists;- signer identity matches the employee snapshot/recipient reference.
Then one local transaction:
- updates generic e-sign truth to completed;
- updates the current attempt to completed;
- updates the promotion to
APPLIED / completed; - records
signedAt/completedAtand one append-only event/timeline entry; - does not create
AnnualLeavePromotionPlanItemrows; - does not create a leave request or mutate leave balance.
Webhook and reconciliation races must apply once.
4.4 Copy and status semantics
Replace plan-dependent product copy:
First notice title: 연차 사용촉진 안내
First notice action: 서명하기
Reminder title: 연차 사용촉진 동의서 서명 안내
Reminder condition: 동의서 서명 미완료
Second-designation fixed reason: 제출 기한 내 동의서 서명 미완료The first notice may state that signing does not create a leave request or deduct balance. It must not say that a plan must be completed inside Sign.
The separate employee plan is not referenced as a DHub completion condition. Any organization-specific off-system instruction is outside this feature.
5. DHub Changes
Remove employee-plan completion coupling
Likely files:
packages/api/src/services/annual-leave-promotion.service.ts
packages/api/src/services/annual-leave-promotion.service.test.ts
packages/api/src/services/hereby-contract.service.ts
packages/api/src/services/hereby-contract.service.test.ts
packages/core/src/schemas/annual-leave-promotion.schema.ts
packages/sdk/src/services/AnnualLeavePromotionService.ts- Remove
buildCompletionPolicy()and the S1completionPolicyrequest field. - Remove required plan-label compatibility checks.
- Remove
normalizeCompletedPlan()and all Sign-result plan validation branches. - Remove employee-plan
AnnualLeavePromotionPlanItemwrites during completion. - Remove raw plan
fieldValuesfrom completion events and review errors. - Apply on verified signature completion alone.
- Keep second-designation
planItemsinput and company schedule persistence unchanged.
Update message/admin copy
Likely files:
packages/api/src/services/annual-leave-promotion.service.ts
apps/admin/src/views/tabs/leave-management.html
apps/admin/src/app.ts- Update future first/reminder/second-designation copy to signature-only semantics.
- Do not rewrite already-published message snapshots.
- Do not require a data migration solely to change historical workflow titles.
- Preserve the company second-designation schedule editor; change only its eligibility/help/reason wording from missing signature+plan to missing consent signature.
Schema cleanup
- Remove completion-policy types from DHub request schemas/interfaces.
- Keep generic Sign
fieldValuesonly where consent prefill still uses it. - Keep the company second-designation plan schema/entity.
- Do not add compatibility aliases or accept both old and new completion-policy shapes.
6. Sign Changes
Likely files:
apps/contract-server/src/app/contract/dto/contract.external.dto.ts
apps/contract-server/src/app/contract/contract.service.ts
apps/contract-server/src/app/contract/dhub-completion-policy.ts
apps/contract-server/src/app/contract/dhub-completion-policy.spec.ts
apps/contract-server/src/app/contract/__tests__/contract.external.service.spec.ts
apps/contract-server/src/database/migrations/1787000000000-DHubCompletionReliability.ts
apps/contract-server/src/database/migrations/<next>-RemoveDHubCompletionPolicy.ts- Remove
ExternalCompletionPolicyDtoandcompletionPolicyfrom create/send. - Remove the DHub plan-policy validator and its tests.
- Remove same-request completion-policy drift checks.
- Remove the requirement that annual-leave labels imply a policy.
- Keep completion atomicity, external-request
completed, Outbox event, worker retry, idempotent response, and notification-policy snapshot. - Keep generic
fieldValuesprefill/result behavior only if another active API contract needs it; DHub annual-leave completion must not depend on it.
Migration 1787000000000-DHubCompletionReliability has already executed locally. Do not edit an executed migration to hide history. Add a forward migration that drops only the obsolete completion-policy column/index/constraint while preserving notification-policy and webhook-Outbox structures.
7. Contract 81 Handling
Do not directly mutate contract 81 during implementation.
Its facts remain useful:
- Sign terminal completion occurred.
- The first response was misleading.
- Completion webhook delivery failed.
- DHub entered review-required because of the now-obsolete plan rule.
Under the corrected product rule, empty plan fields are not invalid. However, contract 81 cannot prove the fixed first-click experience or new Outbox delivery because it is already terminal. Use a new QA contract for acceptance.
Any later decision to reconcile contract 81 into APPLIED is an explicit local-data operation and requires separate approval; it is not part of this code patch.
8. Regression Matrix
DHub
- Compatible consent template succeeds without any plan labels.
- S1 request contains no
completionPolicy. - Completed signature with empty/no plan fields becomes
APPLIED / completed. - Missing/mismatched signer, invalid tenant/request/contract, or missing
completedAtremains review-required. - Completion creates no employee plan rows.
- Company second designation still saves its own schedule for unsigned expired cases.
- First/reminder/second copy contains no Sign plan-completion requirement.
- No leave request or balance mutation.
Sign
- Create/send accepts a compatible consent template without completion policy.
- Same request key remains idempotent without policy drift logic.
- Valid signature commits contract/request/Outbox atomically.
- Outbox failure rolls back terminal state.
- Native notification/MQ failure cannot change a committed success response.
- Authenticated repeat returns idempotent success.
- Webhook worker retries the same immutable event.
notificationPolicy=nonesuppresses every Sign-native recipient notification.- No plan validator executes or blocks completion.
E2E
- Create one fresh consent-only template/contract for a designated QA employee.
- Verify one ticket, one contract, one waiting webhook Inbox event, and one Messenger notice.
- Open one signer session.
- Sign the consent form without entering a plan.
- Submit once and receive immediate success, not an unknown error.
- Verify one delivered completion Outbox event and one processed DHub Inbox event.
- Verify DHub
ESignRecord, attempt, and promotion reach completed/APPLIEDonce. - Verify no employee plan rows, no leave request/balance change, no duplicate contract/ticket/message, and no Sign-native email/Kakao/SMS.
9. Acceptance Criteria
- [ ] The Sign document is consent/signature only and requires no employee plan format or fields.
- [ ] Signing alone is sufficient for DHub
APPLIED / completedafter identity/status validation. - [ ] Patch 6 atomic completion, idempotent retry, and durable webhook delivery remain intact.
- [ ] DHub no longer rejects completion because plan fields are empty or absent.
- [ ] DHub stores no employee plan from Sign; company second designation remains separate.
- [ ] User-facing copy no longer says Sign plan completion is required.
- [ ] Executed migration history is preserved through a forward cleanup migration.
- [ ] Focused tests, package builds, repository gates, and fresh consent-only E2E pass.
10. Out of Scope
- Designing or tracking the employee's separately submitted plan.
- Providing a plan template, attachment flow, upload, approval, or reminder.
- Repairing contract
81data without separate approval. - Removing the company second-designation schedule.
- Adding compatibility code for the superseded completion-policy request.
- Commit, push, deployment, or rollout without separate authorization.