Skip to content

feat: implement atomic archiving operations with transaction - #139

Merged
johlju merged 5 commits into
viscalyx:mainfrom
johlju:f/a5-april
May 1, 2026
Merged

feat: implement atomic archiving operations with transaction#139
johlju merged 5 commits into
viscalyx:mainfrom
johlju:f/a5-april

Conversation

@johlju

@johlju johlju commented May 1, 2026

Copy link
Copy Markdown
Member

Description

Screenshots (if applicable)

Related Issues

Type of Change

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Breaking change (fix or feature that would cause existing functionality to not work as expected)
  • Documentation update
  • Refactoring (no functional changes)
  • Performance improvement (improves performance without changing functionality)
  • Dependency update (updating libraries or tools)

Testing

  • npm run check passes locally
  • All existing tests still pass
  • Manual testing completed
  • UI tested on desktop and mobile (if applicable)

Checklist

  • Documentation updated as needed

Checklist

  • Code follows the project style guidelines (Biome)
  • Tests added/updated as needed
  • Self-review of code completed
  • Comments added for complex logic
  • No hardcoded strings (use translations if i18n is added)

This change is Reviewable

@coderabbitai

coderabbitai Bot commented May 1, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@johlju has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 42 minutes before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: fd8cd466-fdd9-4b41-950a-e23ac355a2a0

📥 Commits

Reviewing files that changed from the base of the PR and between 5c6e0e1 and e4f6251.

📒 Files selected for processing (1)
  • .github/skills/run-spec-audit/references/scrutiny-areas.md

Walkthrough

Adds UI support for an "Archiving Review" label when a version's archive_initiated_at is set, threads that timestamp through list/version payloads, centralizes status-label resolution, converts archiving DAL operations to SERIALIZABLE transactions with locking, and adds tests and docs for archiving concurrency semantics.

Changes

Cohort / File(s) Summary
UI Status Label Rendering
app/[locale]/requirements/[id]/requirement-detail-client.tsx, components/RequirementsTable.tsx, components/StatusStepper.tsx, components/VersionHistory.tsx
Forwarded isArchiving to StatusStepper; replaced inline locale branching with centralized resolveStatusLabel; StatusStepper added isArchiving prop and stepLabel override for the Review step.
Status Label Resolution
lib/requirements/status-label.ts
New module exporting resolveStatusLabel plus types; returns archiving-specific translation when status === Review and archiveInitiatedAt is set, otherwise returns locale-specific status name or .
Version Payload Data Extension
lib/requirements/list-view.ts, lib/dal/requirement-packages.ts, lib/requirements/service.ts
Added optional archiveInitiatedAt to version payloads and mappers so list/service/DAL include the timestamp (initialized to null where appropriate).
Database Archiving Operations
lib/dal/requirements.ts
Converted initiateArchiving, approveArchiving, cancelArchiving to run under SERIALIZABLE transactions, added WITH (UPDLOCK, HOLDLOCK) reads, OUTPUT INSERTED.id verification, and WHERE guards to ensure single-version targeting and detect conflicts.
Translations
messages/en.json, messages/sv.json
Added requirement.statusLabel.Arkiveringsgranskning translations ("Archiving Review" / "Arkiveringsgranskning").
Documentation
docs/database-schema.md, docs/lifecycle-workflow.md
Documented archive_initiated_at UI derivation and formalized archiving workflow semantics, transaction isolation, locking, and UI-only label override behavior.
Tests & Quality Specs
tests/quality/QUALITY.md, tests/quality/functional.test.ts, tests/unit/requirements-dal.test.ts, tests/unit/requirements-table.test.tsx, tests/unit/status-label.test.ts, tests/unit/status-stepper.test.tsx
Added Scenario 12 quality spec and functional/unit tests covering concurrency, SERIALIZABLE + locking assertions, label resolution, StatusStepper behavior, and RequirementsTable rendering with archiveInitiatedAt.
Repo Metadata
.github/skills/.../scrutiny-areas.md
Added scrutiny checklist entry for Scenario 12 requiring SERIALIZABLE + UPDLOCK/HOLDLOCK and conditional update verification.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Client/UI
    participant Service as Service Layer
    participant DAL as Archiving DAL
    participant DB as Database

    rect rgba(0,128,0,0.5)
    Client->>Service: User triggers initiateArchiving(requirementId, versionId)
    end

    rect rgba(0,0,128,0.5)
    Service->>DAL: initiateArchiving(requirementId, versionId)
    DAL->>DB: BEGIN TRANSACTION (SERIALIZABLE)
    DAL->>DB: SELECT published version WITH (UPDLOCK,HOLDLOCK)
    DB-->>DAL: published version row
    DAL->>DB: CHECK for newer draft/review WITH (UPDLOCK,HOLDLOCK)
    DB-->>DAL: no blockers
    DAL->>DB: UPDATE version SET archive_initiated_at = NOW() OUTPUT INSERTED.id WHERE archive_initiated_at IS NULL
    DB-->>DAL: updated id OR 0 rows
    DAL->>DB: COMMIT
    DB-->>DAL: commit result
    DAL-->>Service: success / conflict
    Service-->>Client: response (ok | conflict)
    end
Loading
sequenceDiagram
    participant Admin as Admin/UI
    participant Service as Service Layer
    participant DAL as Archiving DAL
    participant DB as Database

    Admin->>Service: approveArchiving(requirementId, versionId)
    Service->>DAL: approveArchiving(...)
    DAL->>DB: BEGIN TRANSACTION (SERIALIZABLE)
    DAL->>DB: SELECT version WITH (UPDLOCK,HOLDLOCK) WHERE archive_initiated_at IS NOT NULL AND status = REVIEW
    DB-->>DAL: targeted row or none
    alt targeted row found
        DAL->>DB: UPDATE version SET status = ARCHIVED OUTPUT INSERTED.id WHERE ... AND archive_initiated_at IS NOT NULL
        DAL->>DB: UPDATE requirements SET is_archived = 1 WHERE requirement_id = ...
    else none
        DAL-->>Service: conflict
    end
    DAL->>DB: COMMIT
    DAL-->>Service: success / conflict
    Service-->>Admin: response
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is entirely a template with no implementation-specific content, missing all required sections like actual description, related issues, testing confirmation, and completion of checklist items. Fill in the description section with details about the archiving transaction implementation, link related issues, confirm testing completion, and check applicable checklist items.
Docstring Coverage ⚠️ Warning Docstring coverage is 7.69% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: implementing atomic archiving operations with transaction support, which is the core focus across database, service, and component changes.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
Review rate limit: 0/1 reviews remaining, refill in 42 minutes.

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov

codecov Bot commented May 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 57.66%. Comparing base (0b61858) to head (e4f6251).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #139      +/-   ##
==========================================
+ Coverage   57.43%   57.66%   +0.22%     
==========================================
  Files         288      289       +1     
  Lines       17455    17472      +17     
  Branches     6670     6562     -108     
==========================================
+ Hits        10026    10075      +49     
+ Misses       7317     7287      -30     
+ Partials      112      110       -2     
Files with missing lines Coverage Δ
[...e]/requirements/[id]/requirement-detail-client.tsx](https://app.codecov.io/gh/viscalyx/Kravhantering/pull/139?src=pr&el=tree&filepath=app%2F%5Blocale%5D%2Frequirements%2F%5Bid%5D%2Frequirement-detail-client.tsx&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=viscalyx#diff-YXBwL1tsb2NhbGVdL3JlcXVpcmVtZW50cy9baWRdL3JlcXVpcmVtZW50LWRldGFpbC1jbGllbnQudHN4) 70.56% <ø> (ø)
components/RequirementsTable.tsx 74.78% <100.00%> (+0.12%) ⬆️
components/StatusStepper.tsx 100.00% <100.00%> (ø)
components/VersionHistory.tsx 93.70% <100.00%> (+0.78%) ⬆️
lib/dal/requirement-packages.ts 79.78% <ø> (ø)
lib/dal/requirements.ts 38.25% <100.00%> (+6.00%) ⬆️
lib/requirements/list-view.ts 88.88% <ø> (ø)
lib/requirements/service.ts 53.64% <ø> (ø)
lib/requirements/status-label.ts 100.00% <100.00%> (ø)
🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@johlju

johlju commented May 1, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 1, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Nitpick comments (1)
tests/unit/requirements-table.test.tsx (1)

635-671: ⚡ Quick win

Add one English-locale assertion for the new label path.

Since this PR also adds the English copy (Archiving Review), include a locale="en" case to catch locale-specific regressions in status-label rendering.

Proposed test addition
+  it('renders the archiving review label in English when archiveInitiatedAt is set', () => {
+    const rows = [
+      makeRow({
+        version: {
+          ...makeRow().version,
+          status: 2,
+          statusColor: '#f59e0b',
+          statusNameEn: 'Review',
+          statusNameSv: 'Granskning',
+          archiveInitiatedAt: '2026-04-01T12:00:00.000Z',
+        },
+      }),
+    ]
+    render(<RequirementsTable locale="en" rows={rows} />)
+
+    expect(screen.getByText('Archiving Review')).toBeTruthy()
+    expect(screen.queryByText('Review')).toBeNull()
+  })
As per coding guidelines: "When changing visible UI elements, labels, roles, or layout surfaces, update the relevant unit and integration tests."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/requirements-table.test.tsx` around lines 635 - 671, Add an
English-locale assertion for the new "Archiving Review" label in the
RequirementsTable tests: in tests/unit/requirements-table.test.tsx add a case
similar to the existing Swedish tests but render <RequirementsTable locale="en"
rows={rowsWithArchiveInitiatedAt}> and assert screen.getByText('Archiving
Review') is present and screen.queryByText('Review') is null; also add the
converse case where archiveInitiatedAt is null to assert 'Review' is present and
'Archiving Review' is null, referencing the existing test helper makeRow and the
RequirementsTable component to locate where to add the new assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/lifecycle-workflow.md`:
- Around line 98-101: Update the sentence to say that the status badge reflects
the per-version "resolved" status derived from the effective status logic, not
the raw DB column; explicitly mention that the requirements list label is
computed by EFFECTIVE_STATUS_SQL in lib/dal/requirements.ts (which consolidates
requirement_versions.requirement_status_id and
requirement_versions.archive_initiated_at) so readers know the list view uses
the effective/resolved status rather than the raw
requirement_versions.requirement_status_id column.

In `@lib/dal/requirement-packages.ts`:
- Line 1552: The code is hardcoding archiveInitiatedAt: null for library package
items which hides archive-review status; instead read the linked requirement
version's archive_initiated_at and assign it to archiveInitiatedAt
(converting/normalizing to the same casing/format used elsewhere) when building
the package item (replace the literal null in the package construction logic
that sets archiveInitiatedAt with the linked requirement version's
archive_initiated_at value, falling back to null if absent).

In `@lib/dal/requirements.ts`:
- Around line 948-966: The SELECTs using `TOP (1)` (e.g., the query populating
publishedRows via tx.query against requirement_versions) can hide multiple
matching rows; update the logic to detect ambiguous targets by running a COUNT
or selecting all matching ids (instead of TOP (1)) and if more than one row is
returned throw a conflictError('Ambiguous archive target') before proceeding;
apply the same change to the mirrored approve/cancel queries that rely on TOP
(1), or alternatively enforce and validate a unique filtered index on
requirement_versions (unique on requirement_id + requirement_status_id +
archive_initiated_at filter) and add a runtime check that ensures exactly one
row is returned when querying `requirement_versions` in functions handling
archive/approve/cancel.

In `@tests/quality/functional.test.ts`:
- Around line 848-851: The current assertions on the filtered "fulfilled"
results (variable fulfilled derived from results.filter(r => r.status ===
'fulfilled')) allow two successes which contradicts the test contract; change
the assertions to require exactly one success by replacing the two checks with a
single strict check like expect(fulfilled.length).toBe(1) so the test fails if
both operations succeed.
- Around line 769-799: The two test case titles for Scenario 12 must be
verbatim-equal to the Scenario 12 heading in QUALITY.md; update the it(...)
description strings that wrap the concurrent initiateArchiving (the test calling
initiateArchiving twice) and the concurrent approveArchiving (the test starting
at the next it) so their title text exactly matches the Scenario 12 heading in
QUALITY.md (including punctuation and spacing) to satisfy the repo’s
scenario-name contract used by vitest -t; leave the test bodies (calls to
initiateArchiving, approveArchiving, assertions against getVersionHistory,
STATUS_REVIEW, etc.) unchanged.

In `@tests/quality/QUALITY.md`:
- Around line 352-383: Add a new scrutiny area entry for "Scenario 12" to
.github/skills/run-spec-audit/references/scrutiny-areas.md so the three-file
sync is complete: include the same requirement tag string `[Req: formal —
docs/lifecycle-workflow.md "Two-Step Archiving"]` and the same verify command
shown in QUALITY.md (`npm exec -- vitest run tests/quality/functional.test.ts -t
"Scenario 12"`) and ensure the entry's title/description matches the Scenario 12
wording used in QUALITY.md and the test name in
tests/quality/functional.test.ts.

---

Nitpick comments:
In `@tests/unit/requirements-table.test.tsx`:
- Around line 635-671: Add an English-locale assertion for the new "Archiving
Review" label in the RequirementsTable tests: in
tests/unit/requirements-table.test.tsx add a case similar to the existing
Swedish tests but render <RequirementsTable locale="en"
rows={rowsWithArchiveInitiatedAt}> and assert screen.getByText('Archiving
Review') is present and screen.queryByText('Review') is null; also add the
converse case where archiveInitiatedAt is null to assert 'Review' is present and
'Archiving Review' is null, referencing the existing test helper makeRow and the
RequirementsTable component to locate where to add the new assertions.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: c650f543-bc3a-4fcf-b2ac-59d54a03454d

📥 Commits

Reviewing files that changed from the base of the PR and between 0b61858 and cf4100a.

📒 Files selected for processing (19)
  • app/[locale]/requirements/[id]/requirement-detail-client.tsx
  • components/RequirementsTable.tsx
  • components/StatusStepper.tsx
  • components/VersionHistory.tsx
  • docs/database-schema.md
  • docs/lifecycle-workflow.md
  • lib/dal/requirement-packages.ts
  • lib/dal/requirements.ts
  • lib/requirements/list-view.ts
  • lib/requirements/service.ts
  • lib/requirements/status-label.ts
  • messages/en.json
  • messages/sv.json
  • tests/quality/QUALITY.md
  • tests/quality/functional.test.ts
  • tests/unit/requirements-dal.test.ts
  • tests/unit/requirements-table.test.tsx
  • tests/unit/status-label.test.ts
  • tests/unit/status-stepper.test.tsx

Comment thread docs/lifecycle-workflow.md Outdated
Comment thread lib/dal/requirement-packages.ts
Comment thread lib/dal/requirements.ts
Comment thread tests/quality/functional.test.ts
Comment thread tests/quality/functional.test.ts
Comment thread tests/quality/QUALITY.md
@johlju

johlju commented May 1, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented May 1, 2026

Copy link
Copy Markdown
Contributor
✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.github/skills/run-spec-audit/references/scrutiny-areas.md:
- Around line 164-179: The markdown has duplicate "## 14" for "Reference Data
Behavioral Contracts" and mis-numbered "AI Generation Contracts"; rename the
heading "Reference Data Behavioral Contracts" from "## 14" to "## 15" and change
"AI Generation Contracts" from "## 15" to "## 16" (and scan the rest of the file
to increment any subsequent section numbers to maintain sequential ordering) so
section numbering is unique and sequential.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 05f5f060-06d9-44b3-a7aa-bbe41412c7b4

📥 Commits

Reviewing files that changed from the base of the PR and between cf4100a and 5c6e0e1.

📒 Files selected for processing (3)
  • .github/skills/run-spec-audit/references/scrutiny-areas.md
  • docs/lifecycle-workflow.md
  • tests/quality/functional.test.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • docs/lifecycle-workflow.md

Comment thread .github/skills/run-spec-audit/references/scrutiny-areas.md
@johlju
johlju merged commit 145c7fd into viscalyx:main May 1, 2026
9 checks passed
@johlju
johlju deleted the f/a5-april branch May 1, 2026 20:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant