Skip to content

fix(data): preserve numeric zero values through normalization - #631

Closed
seonghobae wants to merge 1 commit into
developfrom
fix/preserve-numeric-zero-609
Closed

fix(data): preserve numeric zero values through normalization#631
seonghobae wants to merge 1 commit into
developfrom
fix/preserve-numeric-zero-609

Conversation

@seonghobae

@seonghobae seonghobae commented Aug 29, 2026

Copy link
Copy Markdown
Contributor

Closes #609

Summary

  • preserve numeric 0 in sanitizeDraft, external-record normalization, and CSV export without changing missing-value or boolean fallback behavior
  • add a browser regression that serves numeric JSON zero values through bootstrap, render, and persistence

Verification

  • focused E2E: RED on protected develop, then green after fix
  • npm run test:unit
  • npm run test:api
  • npm run coverage
  • python3 -m pytest tests/config
  • npm run check:python-docstrings
  • npm run test:e2e: 76/77 passed; the single failure is the pre-existing protected-develop modulepreload expectation in the seeded-rows test, outside this diff
  • npm ci audit: 0 vulnerabilities

Devin Review

@coderabbitai

coderabbitai Bot commented Aug 29, 2026

Copy link
Copy Markdown

Warning

Review limit reached

Next included review available in 27 minutes.

View limit details

Limit details: You’ve used the included review currently available.

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Learn how review limits work.

Review configuration:

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: e115ac9c-2d5d-4644-bef7-61ea34546780

📥 Commits

Reviewing files that changed from the base of the PR and between 2c32887 and 1e922ea.

📒 Files selected for processing (2)
  • app.js
  • tests/e2e/scopeweave.spec.js

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

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

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Devin Review found 2 potential issues.

Devin Review

Comment thread app.js
Comment on lines +1817 to +1820
budget: task.budget === 0 ? 0 : task.budget || defaults.budget || '',
actualCost: task.actualCost === 0 ? 0 : task.actualCost || defaults.actualCost || '',
sprint: task.sprint || defaults.sprint || '',
storyPoints: task.storyPoints || defaults.storyPoints || ''
storyPoints: task.storyPoints === 0 ? 0 : task.storyPoints || defaults.storyPoints || ''

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🟡 Numeric zero fields appear empty

When imported fields contain numeric zeroes, renderEditorField displays them as blanks during editing. Users cannot distinguish preserved zeroes from missing values.

Prompt for agents
Numeric zero values introduced by createNormalizedExternalRecord remain numbers in state, but app.js renderEditorField initializes each input with value || '', so imported zero budget, actualCost, and storyPoints fields appear empty when edited. Initialize editor inputs without treating numeric zero as missing, while preserving the existing fallback for null, undefined, false, and empty strings as intended. Add regression coverage that opens each imported zero-valued task and verifies the corresponding editor input shows "0".
Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +93 to +124
test('preserves numeric zero values through seed normalization and persistence', async ({ page }) => {
await page.route('**/wbs.json', async (route) => route.fulfill({
status: 200,
contentType: 'application/json',
body: JSON.stringify([
{ __id: 'zero-budget', __depth: '3', phase: 'P5000', activity: 'Data', task: 'Zero budget', budget: 0, actualCost: 100, storyPoints: 1 },
{ __id: 'zero-cost', __depth: '3', phase: 'P5000', activity: 'Data', task: 'Zero cost', budget: 100, actualCost: 0, storyPoints: 1 },
{ __id: 'zero-points', __depth: '3', phase: 'P5000', activity: 'Data', task: 'Zero points', budget: 100, actualCost: 100, storyPoints: 0 },
{ __id: 'all-zero', __depth: '3', phase: 'P5000', activity: 'Data', task: 'All zero', budget: 0, actualCost: 0, storyPoints: 0 },
{ __id: 'missing-values', __depth: '3', phase: 'P5000', activity: 'Data', task: 'Missing values' }
])
}));
await page.evaluate(() => localStorage.clear());
await page.reload();

await expect(page.locator('tbody tr[data-task-id]')).toHaveCount(5);
await page.getByTestId('project-name-input').fill('Zero preservation round trip');
await expect.poll(() => page.evaluate(() => localStorage.getItem('scopeweave:planner-state:v1'))).not.toBeNull();
const savedTasks = await page.evaluate(() => JSON.parse(localStorage.getItem('scopeweave:planner-state:v1')).tasks);
const valuesById = Object.fromEntries(savedTasks.map((task) => [task.id, {
budget: task.budget,
actualCost: task.actualCost,
storyPoints: task.storyPoints
}]));
expect(valuesById).toEqual({
'zero-budget': { budget: 0, actualCost: 100, storyPoints: 1 },
'zero-cost': { budget: 100, actualCost: 0, storyPoints: 1 },
'zero-points': { budget: 100, actualCost: 100, storyPoints: 0 },
'all-zero': { budget: 0, actualCost: 0, storyPoints: 0 },
'missing-values': { budget: '', actualCost: '', storyPoints: '' }
});
});

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔍 Regression omits changed boundaries

The test stops after local persistence. It never opens the editor or exercises CSV export, so two modified zero-handling boundaries remain uncovered.

Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Copy link
Copy Markdown
Contributor Author

Closing this lane as superseded by #610 after exact comparison against the same protected base develop@2c328875e00e86537df3e965170be80532571cad.

#631 head 1e922ea312f2dd59c9605c98f6ac80b30d97dba9 is a two-file subset that does not complete issue #609's reachable contract. Current-head Devin findings confirm that imported numeric zero still renders blank in the editor and that the regression never exercises the changed editor/CSV boundaries. Its generic sanitizeDraft() value === 0 preservation is also field-agnostic, whereas #610 already went through the earlier review cycle that proved zero must be preserved only for budget, actualCost, and storyPoints so text fields such as owner keep existing falsy semantics.

#610 current head 4c965cb04a9ae5d970b7d61afcbb8caf32197d06 already covers editor rendering, draft sanitization, external normalization, local persistence, edit/save/reopen, CSV, and connected JSON sync with field-scoped ZERO_VALID_FIELDS; its currently enumerated review threads are resolved. #631 contains no unique production behavior or acceptance boundary that is not already owned more completely by #610, so carrying both branches would duplicate the same issue while retaining known defects.

@seonghobae seonghobae closed this Aug 29, 2026
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.

[Data Integrity] Preserve numeric zero values through work-item normalization

1 participant