Refactor worksheet collaboration model boundary#29
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughRefactors spreadsheet model to stable-identity worksheets keyed by row/col IDs, reorganizes sheet package exports, adds frontend/backend worksheet axis and structure helpers, implements Yorkie worksheet shape migration CLI with tests, removes legacy frontend migration code, updates API and tests to use worksheet abstractions, and improves concurrency test coverage. Changes
Sequence Diagram(s)sequenceDiagram
participant CLI as Migration CLI
participant Prisma as Prisma/Postgres
participant Migrator as migrateYorkieWorksheetShape
participant Yorkie as Yorkie Server
CLI->>Prisma: Query document IDs (--document/--all/--limit)
Prisma-->>CLI: Return document list
CLI->>Yorkie: Attach document
Yorkie-->>CLI: Return root snapshot
CLI->>Migrator: Detect & migrate root shape
Migrator-->>CLI: { kind, changed, document }
alt changed
CLI->>Yorkie: Replace root with migrated document & sync
Yorkie-->>CLI: Ack / synced
CLI->>Yorkie: Detach
else unchanged
CLI-->>CLI: Skip update
end
CLI->>Prisma: Print migration summary
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Poem
✨ Finishing Touches
🧪 Generate unit tests (beta)
|
Verification: verify:selfResult: ✅ PASS in 97.6s
Verification: verify:integrationResult: ✅ PASS |
There was a problem hiding this comment.
Actionable comments posted: 8
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/sheet/src/index.ts (1)
139-177:⚠️ Potential issue | 🔴 CriticalRe-export type-only symbols with
export type.These symbols are imported as types but re-exported as runtime values in the single
export { ... }block (lines 136–255), causingSyntaxError: Export 'AggregateFunction' is not defined in moduleand similar errors for all type-only bindings.Split the export block: use
export type { Grid, Cell, CellStyle, ConditionalFormatOperator, ConditionalFormatRule, ConditionalFormatStyle, BorderPreset, TextAlign, VerticalAlign, NumberFormat, FilterOperator, FilterCondition, FilterState, HiddenState, AggregateFunction, PivotFieldSort, PivotField, PivotValueField, PivotFilterField, PivotTableDefinition, PivotRecord, GroupNode, PivotCellType, PivotCell, PivotResult, Ref, Sref, Range, MergeSpan, Direction, Axis, SelectionType, GridResolver, Store, LayoutRect, Theme, RangeStylePatch, LocaleFormatPreview, FormatValueOptions, InferInputOptions, InferredInput, InferredInputFormat, ChartType, SheetChart, WorksheetFilterState, Worksheet, TabType, SheetKind, TabMeta, SpreadsheetDocument }for type-only exports, and keep the remaining symbols inexport { ... }for runtime values.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/sheet/src/index.ts` around lines 139 - 177, The current single export block re-exports many bindings that are type-only, causing runtime export errors; split the block in packages/sheet/src/index.ts into two exports: an export type { ... } listing all type-only symbols (e.g. Grid, Cell, CellStyle, ConditionalFormatOperator, ConditionalFormatRule, ConditionalFormatStyle, BorderPreset, TextAlign, VerticalAlign, NumberFormat, FilterOperator, FilterCondition, FilterState, HiddenState, AggregateFunction, PivotFieldSort, PivotField, PivotValueField, PivotFilterField, PivotTableDefinition, PivotRecord, GroupNode, PivotCellType, PivotCell, PivotResult, Ref, Sref, Range, MergeSpan, Direction, Axis, SelectionType, GridResolver, Store, LayoutRect, Theme, RangeStylePatch, LocaleFormatPreview, FormatValueOptions, InferInputOptions, InferredInput, InferredInputFormat, ChartType, SheetChart, WorksheetFilterState, Worksheet, TabType, SheetKind, TabMeta, SpreadsheetDocument) and keep a separate runtime export { ... } for actual values (e.g. MemStore, ReadOnlyStore, findEdgeWithIndex, Grid, Cell classes/constructors or functions) so that only type-only imports use export type and runtime symbols remain in the normal export list.
🧹 Nitpick comments (18)
packages/backend/scripts/migrate-yorkie-worksheet-shape.d.ts (1)
1-1: Clarify the purpose of this empty declaration file.An empty
export {}in a.d.tsfile is unusual. If this is a CLI script that shouldn't be imported as a library, consider whether this declaration file is needed at all, or add a comment explaining its purpose (e.g., satisfying module resolution or preventing accidental imports).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/backend/scripts/migrate-yorkie-worksheet-shape.d.ts` at line 1, The file migrate-yorkie-worksheet-shape.d.ts currently only contains an empty "export {}" which is unclear; either delete this declaration file if it serves no purpose, or replace it with a short explanatory comment and a clearer intent (e.g., explain it exists to force module scope / prevent accidental imports or to satisfy TypeScript module resolution), or change it to a proper declaration (declare module '...' { /* types */ }) if it is intended to provide typings; update the file migrate-yorkie-worksheet-shape.d.ts accordingly and ensure the change matches the intended use of the CLI script or module.packages/sheet/src/view/spreadsheet.ts (1)
1-12: Make the core-types import type-only.Every symbol imported from
../model/core/typesis used exclusively in type positions (parameter types, return types, and type annotations), so converting to a type-only import accurately reflects the new core/worksheet boundary.♻️ Proposed fix
-import { +import type { Axis, BorderPreset, CellStyle, ConditionalFormatRule, FilterCondition, GridResolver, Range, Ref, SelectionType, } from '../model/core/types'; import { Sheet } from '../model/worksheet/sheet';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/sheet/src/view/spreadsheet.ts` around lines 1 - 12, The import from ../model/core/types should be changed to a type-only import because all symbols (Axis, BorderPreset, CellStyle, ConditionalFormatRule, FilterCondition, GridResolver, Range, Ref, SelectionType) are used only for typing; update the import line in spreadsheet.ts to use an "import type { ... } from '../model/core/types';" form so the core/worksheet boundary is preserved and runtime bundling won't include these types.packages/sheet/src/view/formulabar.ts (1)
2-2: Use a type-only import forSheet.
Sheetis only referenced in type positions (lines 12 and 80), so converting toimport typeremoves an unnecessary runtime dependency between the view and worksheet layers.♻️ Proposed fix
-import { Sheet } from '../model/worksheet/sheet'; +import type { Sheet } from '../model/worksheet/sheet';🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/sheet/src/view/formulabar.ts` at line 2, Replace the runtime import of Sheet with a type-only import: change "import { Sheet } from '../model/worksheet/sheet';" to "import type { Sheet } from '../model/worksheet/sheet';" in formulabar.ts, ensuring that all references to Sheet (e.g., the type annotations used in the form bar functions/variables) remain type-only and that no runtime usage of Sheet exists in the file so the compiler emits no runtime import.docs/tasks/active/20260314-yorkie-worksheet-shape-migration-lessons.md (1)
6-6: Hyphenate compound adjective for clarity.Consider changing “side-effect free” to “side-effect-free” in Line 6.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/tasks/active/20260314-yorkie-worksheet-shape-migration-lessons.md` at line 6, The phrase "side-effect free" should be hyphenated as the compound adjective "side-effect-free"; update the text in the document where the label sentence contains "side-effect free" so it reads "side-effect-free" to clarify the compound modifier.docs/tasks/active/20260313-crdt-structural-concurrency-retrospective-todo.md (1)
3-4: Use hyphenated compound modifiers for readability.Consider “stable-grid-based” and “visual-index-based” in these sections for cleaner technical prose.
Also applies to: 36-39
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/tasks/active/20260313-crdt-structural-concurrency-retrospective-todo.md` around lines 3 - 4, Replace non-hyphenated compound modifiers with hyphenated forms for clarity: change occurrences of "stable grid based" to "stable-grid-based" and "visual index based" to "visual-index-based" throughout the document (including the phrases in the current section and the lines referenced as 36-39); update any variants like "stable-grid based" or "visual-index based" to the fully hyphenated form to ensure consistent, readable technical prose.docs/tasks/active/20260313-backend-dev-start-failure-todo.md (1)
67-70: Make verification commands context-stable.Line 69 switches to a bare
tsc --build ...command, which is ambiguous outsidepackages/backend. Consider using the samepnpm --filter@wafflebase/backendexec ...pattern (or explicitly state the working directory) so the repro steps are deterministic.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/tasks/active/20260313-backend-dev-start-failure-todo.md` around lines 67 - 70, The second verification step uses a bare "tsc --build tsconfig.build.json --force --listEmittedFiles" which is ambiguous; update that line to either use the same pnpm wrapper (pnpm --filter `@wafflebase/backend` exec tsc --build tsconfig.build.json --force --listEmittedFiles) or explicitly state "run from packages/backend" so the commands are deterministic and context-stable in the repro steps.packages/frontend/tests/helpers/two-user-yorkie.ts (2)
63-86: Add exhaustiveness check for operation kinds.The switch statement handles known operation kinds but doesn't have a default case. If a new operation kind is added to
ConcurrencyOp, this function will silently do nothing rather than failing fast.♻️ Proposed fix to add exhaustiveness check
async function applyStoreOp(store: YorkieStore, op: ConcurrencyOp): Promise<void> { switch (op.kind) { case "set-data": { const ref = parseRef(op.ref); const cell = op.value.startsWith("=") ? { f: op.value } : { v: op.value }; await store.set(ref, cell); return; } case "insert-rows": await store.shiftCells("row", op.index, op.count ?? 1); return; case "delete-rows": await store.shiftCells("row", op.index, -(op.count ?? 1)); return; case "insert-columns": await store.shiftCells("column", op.index, op.count ?? 1); return; case "delete-columns": await store.shiftCells("column", op.index, -(op.count ?? 1)); return; + default: { + const _exhaustive: never = op; + throw new Error(`Unknown operation kind: ${(_exhaustive as ConcurrencyOp).kind}`); + } } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/frontend/tests/helpers/two-user-yorkie.ts` around lines 63 - 86, The switch in applyStoreOp handling ConcurrencyOp kinds is non-exhaustive; add a default branch that fails fast for unknown kinds (so new variants don't silently no-op). Update applyStoreOp to include a default case that asserts exhaustiveness (e.g., cast op to never or throw an Error) and surface the unexpected op.kind; reference applyStoreOp, ConcurrencyOp, parseRef, YorkieStore.set and YorkieStore.shiftCells to locate where to add the default/exhaustive check.
46-48: Consider improving type safety for YorkieStore instantiation.The
as nevercast bypasses type checking. This works but could mask type mismatches ifYorkieStoreconstructor signature changes.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/frontend/tests/helpers/two-user-yorkie.ts` around lines 46 - 48, The helper createStore currently silences type checking by casting doc to never; update it to preserve type-safety by making createStore generic and casting via unknown (or typing the parameter to the YorkieStore constructor type). For example, change function createStore(doc: T) to a generic signature (e.g., function createStore<T>(doc: T): YorkieStore and cast doc as unknown as ConstructorParamType) so the YorkieStore instantiation (new YorkieStore(...)) no longer uses "as never" and mismatched shapes are caught by the compiler; locate the createStore function and the YorkieStore constructor usage to apply this change.packages/frontend/src/app/spreadsheet/yorkie-worksheet-axis.ts (1)
72-93: Consider documenting the 1-based index convention.The function uses
Math.max(0, index - 1)to convert from 1-based to 0-based indices. This convention should be documented in the function signature or a comment to prevent confusion for callers.📝 Suggested documentation
+/** + * Insert new rows or columns into the worksheet. + * `@param` ws - The worksheet to modify + * `@param` axis - Whether to insert rows or columns + * `@param` index - 1-based position to insert at (1 = before first element) + * `@param` count - Number of rows/columns to insert + */ export function insertYorkieWorksheetAxis( ws: Worksheet, axis: Axis, index: number, count: number, ): void {🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/frontend/src/app/spreadsheet/yorkie-worksheet-axis.ts` around lines 72 - 93, Add clear documentation that the parameter index is 1-based and the function converts it to 0-based via Math.max(0, index - 1); update the function comment or JSDoc for insertYorkieWorksheetAxis to state the 1-based indexing convention (for both row and col axes) and note that ensureAxisLength and the splice call expect the converted 0-based index, so callers should pass 1-based positions (use index = 1 for the first position).packages/backend/scripts/copy-yorkie-documents.js (1)
104-126: Document mongosh dependency requirement.The
fetchExistingYorkieKeysfunction shells out tomongosh. If the tool isn't installed,execFileSyncwill throw with a potentially confusing error. Consider adding a pre-flight check or documenting this dependency in the usage text.💡 Proposed usage text update (apply to source .ts file)
function usage() { return `Usage: pnpm --filter `@wafflebase/backend` exec tsx scripts/copy-yorkie-documents.ts \\ --database-url <postgres-url> \\ --mongo-url <mongo-url> \\ --project-public-key <public-key> \\ --source-rpc-addr <source-rpc-addr> \\ --source-api-key <source-api-key> \\ --target-rpc-addr <target-rpc-addr> \\ - [--limit <count>] [--document <id> ...]`; + [--limit <count>] [--document <id> ...] + +Requires: mongosh (MongoDB Shell) to be installed and in PATH.`; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/backend/scripts/copy-yorkie-documents.js` around lines 104 - 126, The function fetchExistingYorkieKeys currently calls mongosh via execFileSync and will throw a confusing error if mongosh isn't installed; add a pre-flight check before invoking execFileSync (e.g., call execFileSync('mongosh', ['--version']) or spawnSync to verify availability and a zero exit code) and replace the raw exception with a clear, user-facing error that explains the missing mongosh dependency and points to installation instructions; update any usage/help text in the same file to document the mongosh requirement and refer to the new check so users see an actionable message instead of the low-level exception.packages/sheet/src/model/workbook/worksheet-grid.ts (1)
88-89: Usefor...ofto avoid linter warning about callback return values.The
forEachcallbacks implicitly return the result ofMap.set(), which triggers Biome'suseIterableCallbackReturnrule. While functionally correct, switching tofor...ofsilences the linter and is arguably more readable.♻️ Proposed refactor
- rowOrder.forEach((rowId, index) => rowIndex.set(rowId, index + 1)); - colOrder.forEach((colId, index) => colIndex.set(colId, index + 1)); + for (const [index, rowId] of rowOrder.entries()) { + rowIndex.set(rowId, index + 1); + } + for (const [index, colId] of colOrder.entries()) { + colIndex.set(colId, index + 1); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/sheet/src/model/workbook/worksheet-grid.ts` around lines 88 - 89, The linter flags useIterableCallbackReturn because rowOrder.forEach and colOrder.forEach callbacks return Map.set(); replace both forEach uses with explicit for...of loops to iterate arrays and call rowIndex.set(rowId, index + 1) and colIndex.set(colId, index + 1) inside the loop bodies (use an index counter or Array.prototype.entries() in the for...of) so that rowOrder, colOrder, rowIndex.set, and colIndex.set remain the same but no callback return value is produced.packages/backend/scripts/copy-yorkie-documents.ts (2)
370-373: Add error handling for individual document copy failures.If
copyDocumentthrows for one document, the entire loop aborts and remaining documents are not processed. Consider catching errors per-document to continue with others and report failures at the end.🛡️ Proposed error handling improvement
+ const failures: Array<{ key: string; error: unknown }> = []; for (const key of existingKeys) { - await copyDocument(sourceClient, targetClient, key); - console.log(`copied ${key}`); + try { + await copyDocument(sourceClient, targetClient, key); + console.log(`copied ${key}`); + } catch (error) { + console.error(`failed to copy ${key}:`, error instanceof Error ? error.message : error); + failures.push({ key, error }); + } } + + if (failures.length > 0) { + console.log(`\nFailed to copy ${failures.length} document(s).`); + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/backend/scripts/copy-yorkie-documents.ts` around lines 370 - 373, The loop over existingKeys currently awaits copyDocument(sourceClient, targetClient, key) directly so a thrown error aborts the whole process; wrap the per-key call in a try/catch inside the for loop (the loop that iterates existingKeys and calls copyDocument) to catch and log errors for that key (include the key and error message), push failed keys/errors into a local failures array, continue processing remaining keys, and after the loop report a summary (e.g., log or throw a single aggregated error) if failures is non-empty so callers can see which documents failed.
362-362: Consider using aSetfor O(1) membership checks.
existingKeysis an array, andArray.includes()is O(n) per call. Converting to aSetwould make the filter operation more efficient, especially with many documents.♻️ Proposed optimization
- const missingKeys = candidateKeys.filter((key) => !existingKeys.includes(key)); + const existingKeySet = new Set(existingKeys); + const missingKeys = candidateKeys.filter((key) => !existingKeySet.has(key));🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/backend/scripts/copy-yorkie-documents.ts` at line 362, The filter that builds missingKeys uses Array.includes on existingKeys which is O(n) per check; change to create a Set from existingKeys (e.g., const existingKeySet = new Set(existingKeys)) and then use existingKeySet.has(key) inside the candidateKeys.filter callback to get O(1) membership checks and improve performance (update the reference in the filter that currently uses existingKeys.includes).packages/sheet/src/model/workbook/worksheet-record.ts (1)
71-81: Silent fallback for malformed keys may hide bugs.When the separator is not found, returning
{ rowId: '', colId: '' }silently hides malformed input. Callers may not expect empty strings and could produce unexpected behavior. Consider whether throwing an error or returningnullwould be safer for debugging.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/sheet/src/model/workbook/worksheet-record.ts` around lines 71 - 81, The function parseWorksheetCellKey silently returns empty strings when WorksheetCellKeySeparator isn't found which can hide malformed input; update parseWorksheetCellKey to fail fast by throwing a descriptive Error (include the invalid key and the expected WorksheetCellKeySeparator) instead of returning {rowId: '', colId: ''}, and update any callers or types to handle the thrown error (or alternatively change the return type to nullable and return null consistently if you prefer non-throwing behavior); locate parseWorksheetCellKey and replace the fallback branch with a clear error throw (or null return) and ensure tests/call sites handle the new behavior.packages/backend/src/yorkie/worksheet-shape-migration.spec.ts (1)
252-258: Consider adding more unsupported shape test cases.The current test only covers a simple object with unrecognized keys. Consider adding edge cases like arrays, primitives, or nested structures that don't match any expected shape.
💡 Additional test cases to consider
it('throws for array roots', () => { expect(() => migrateYorkieWorksheetShape([])).toThrow( 'Unsupported Yorkie spreadsheet document shape', ); }); it('throws for primitive roots', () => { expect(() => migrateYorkieWorksheetShape('string' as never)).toThrow(); });🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/backend/src/yorkie/worksheet-shape-migration.spec.ts` around lines 252 - 258, Add extra negative test cases for migrateYorkieWorksheetShape to cover more unsupported shapes: include at least one test that passes an array root (e.g., []), one that passes a primitive root (e.g., 'string' cast as never), and optionally a nested unexpected structure, each asserting they throw 'Unsupported Yorkie spreadsheet document shape' (or at least throw); update the spec around the existing it('throws for unsupported shapes') block to add these new it(...) cases referencing migrateYorkieWorksheetShape so the function's validation covers arrays, primitives, and nested invalid inputs.packages/sheet/test/helpers/concurrency-driver.ts (2)
24-45: Add exhaustiveness check for operation kinds.The
switchstatement handles all current operation kinds but lacks adefaultcase. If new operation kinds are added toConcurrencyOp, this function would silently do nothing instead of failing fast.♻️ Add exhaustiveness check
export async function applyConcurrencyOp( sheet: Sheet, op: ConcurrencyOp, ): Promise<void> { switch (op.kind) { case 'set-data': await sheet.setData(parseRef(op.ref), op.value); return; case 'insert-rows': await sheet.insertRows(op.index, op.count ?? 1); return; case 'delete-rows': await sheet.deleteRows(op.index, op.count ?? 1); return; case 'insert-columns': await sheet.insertColumns(op.index, op.count ?? 1); return; case 'delete-columns': await sheet.deleteColumns(op.index, op.count ?? 1); return; + default: { + const _exhaustive: never = op; + throw new Error(`Unknown operation kind: ${(_exhaustive as ConcurrencyOp).kind}`); + } } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/sheet/test/helpers/concurrency-driver.ts` around lines 24 - 45, The switch in applyConcurrencyOp (function) covers current ConcurrencyOp kinds but lacks an exhaustive default; add an explicit exhaustiveness check so new ConcurrencyOp variants fail fast: either add a default branch that throws a descriptive Error (e.g., `throw new Error('Unhandled ConcurrencyOp kind: ' + (op as any).kind)`) or introduce/consume a utility like assertNever(value: never) and call it in the default case to ensure the compiler enforces exhaustiveness for ConcurrencyOp and surface unhandled kinds at runtime/compile time.
89-94: JSON.stringify comparison may have ordering issues.Using
JSON.stringifyfor snapshot comparison works but is sensitive to property ordering. IfConcurrencySnapshotproperties are added in different orders, semantically equal snapshots would fail comparison.💡 Consider deep equality check
For robustness, consider using a deep equality function or sorting keys before stringifying:
export function snapshotMatchesOneOf( actual: ConcurrencySnapshot, candidates: ConcurrencySnapshot[], ): boolean { const normalize = (obj: unknown) => JSON.stringify(obj, Object.keys(obj as object).sort()); return candidates.some((candidate) => normalize(candidate) === normalize(actual)); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/sheet/test/helpers/concurrency-driver.ts` around lines 89 - 94, The snapshotMatchesOneOf function currently compares snapshots with JSON.stringify which can fail if object key order differs; update snapshotMatchesOneOf (working with ConcurrencySnapshot) to perform a stable deep-equality comparison instead of raw stringify — either by normalizing objects with a deterministic JSON.stringify (use a normalize helper that sorts Object.keys(obj) in the replacer) before comparing, or call a reliable deepEqual (e.g. lodash/isEqual) to compare actual against each candidate; ensure the helper handles nested objects consistently and use that in the candidates.some check.packages/frontend/tests/app/spreadsheet/chart-utils.test.ts (1)
45-63: Use the shared document factory in this fixture helper.
buildDoc()hand-assembles the workbook envelope even though the canonicalcreateSpreadsheetDocument()helper now owns those defaults. Reusing that factory will keep this test data aligned with future document-schema changes instead of maintaining a parallel constructor here.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/frontend/tests/app/spreadsheet/chart-utils.test.ts` around lines 45 - 63, The buildDoc helper manually constructs the spreadsheet envelope; replace that with the shared factory createSpreadsheetDocument to keep test fixtures aligned with canonical defaults—create the worksheet via createWorksheet(), write cells with writeWorksheetCell(parseRef(...)), then call createSpreadsheetDocument to produce the full document envelope and attach the worksheet (or pass it into the factory if supported); update buildDoc to return the result of createSpreadsheetDocument (or the factory result after assigning tabs, tabOrder, and sheets) instead of hand-assembling the object so you avoid duplicating schema defaults.
🤖 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/tasks/active/20260314-local-hackerwins-access-todo.md`:
- Line 45: The verification query currently selects PII (email); update the SQL
in the documented command so it only returns non-PII fields used for
ownership/workspace verification (select id, username from "User" order by id
asc) — locate the command string containing `select id, username, email from
"User" order by id asc;` and remove `email` from the SELECT list, keeping
quoting and the rest of the psql invocation unchanged.
In `@docs/tasks/active/20260314-local-prod-restore-todo.md`:
- Around line 77-82: The docs embed concrete credentials in commands that call
the copy-yorkie-documents.ts script and DATABASE_URL; update the examples to use
environment variable placeholders instead of literals — replace the
--project-public-key and --source-api-key flags passed to
scripts/copy-yorkie-documents.ts with --project-public-key
"$YORKIE_PROJECT_PUBLIC_KEY" and --source-api-key "$SOURCE_YORKIE_API_KEY", and
replace hard-coded DB credentials in DATABASE_URL values with
postgresql://$DB_USER:$DB_PASSWORD@localhost:5432/<DB_NAME> (also keep
YORKIE_RPC_ADDR and any other addresses as variables where appropriate).
In `@packages/backend/scripts/copy-yorkie-documents.js`:
- Around line 164-166: Add a short inline comment above the target client
construction (the sdk_1.default.Client instantiation creating targetClient using
options.targetRpcAddr) stating that the target Yorkie server is assumed to not
require authentication and that no --target-api-key is parsed; note that if
target-server authentication is later required the script will need to accept
and pass an apiKey to the Client. Keep the comment concise and reference the
absence of apiKey handling so future maintainers know why no credential is
provided.
In `@packages/backend/scripts/migrate-yorkie-worksheet-shape.js`:
- Around line 126-137: The summary.byKind object is missing the 'current-flat'
key so summary.byKind['current-flat'] will be undefined; update the
initialization of the summary constant (the summary object with
processed/changed/unchanged and byKind) to include a "'current-flat': 0" entry
alongside current, 'initialized-empty', 'legacy-flat', and 'legacy-tabbed' so
counts for current-flat are tracked correctly.
- Around line 112-119: The compiled summarizeKinds function is missing the
`current-flat` entry compared to the TypeScript source; update the
implementation in summarizeKinds to include the template
`current-flat=${summary.byKind['current-flat']}` (referencing the summarizeKinds
function and the summary.byKind map) and then regenerate the compiled JS output
(rebuild/transpile the TypeScript) so the .js matches the .ts changes.
In `@packages/backend/src/yorkie/worksheet-shape-migration.ts`:
- Around line 302-314: isLegacyWorksheetShape currently only checks for 'sheet',
'rowHeights', 'colWidths', 'frozenRows', and 'frozenCols', so LegacyWorksheet
objects that only contain optional properties like 'merges' or 'charts' are
misclassified as unsupported; update isLegacyWorksheetShape to also detect
'merges' and 'charts' (keeping the isRecord check) so that objects matching the
LegacyWorksheet shape — which migrateLegacyWorksheet already handles — are
correctly recognized and passed to migration.
In `@packages/sheet/package.json`:
- Around line 7-16: The package exports map currently points "require" at the
UMD bundle file (./dist/wafflebase-sheet.js); change the exports entry for
"require" to reference the actual CommonJS artifact produced by the build
(./dist/wafflebase-sheet.cjs.js) so the package with "type": "module" exposes an
explicit CJS file for require consumers—update the "." exports object's
"require" property accordingly (search for the exports block and the "require"
key in package.json).
In `@packages/sheet/src/model/workbook/worksheet-document.ts`:
- Line 148: initialSpreadsheetDocument is a single shared mutable value (created
once via createSpreadsheetDocument()) and should not be exported as a mutable
seed; replace the exported constant with a factory that returns a fresh document
each call (e.g. change export const initialSpreadsheetDocument =
createSpreadsheetDocument(); to export function getInitialSpreadsheetDocument()
{ return createSpreadsheetDocument(); } or export const
getInitialSpreadsheetDocument = () => createSpreadsheetDocument();), or
alternatively export a deeply frozen template by calling Object.freeze on a deep
clone; update call sites to invoke getInitialSpreadsheetDocument() where they
previously referenced initialSpreadsheetDocument.
---
Outside diff comments:
In `@packages/sheet/src/index.ts`:
- Around line 139-177: The current single export block re-exports many bindings
that are type-only, causing runtime export errors; split the block in
packages/sheet/src/index.ts into two exports: an export type { ... } listing all
type-only symbols (e.g. Grid, Cell, CellStyle, ConditionalFormatOperator,
ConditionalFormatRule, ConditionalFormatStyle, BorderPreset, TextAlign,
VerticalAlign, NumberFormat, FilterOperator, FilterCondition, FilterState,
HiddenState, AggregateFunction, PivotFieldSort, PivotField, PivotValueField,
PivotFilterField, PivotTableDefinition, PivotRecord, GroupNode, PivotCellType,
PivotCell, PivotResult, Ref, Sref, Range, MergeSpan, Direction, Axis,
SelectionType, GridResolver, Store, LayoutRect, Theme, RangeStylePatch,
LocaleFormatPreview, FormatValueOptions, InferInputOptions, InferredInput,
InferredInputFormat, ChartType, SheetChart, WorksheetFilterState, Worksheet,
TabType, SheetKind, TabMeta, SpreadsheetDocument) and keep a separate runtime
export { ... } for actual values (e.g. MemStore, ReadOnlyStore,
findEdgeWithIndex, Grid, Cell classes/constructors or functions) so that only
type-only imports use export type and runtime symbols remain in the normal
export list.
---
Nitpick comments:
In `@docs/tasks/active/20260313-backend-dev-start-failure-todo.md`:
- Around line 67-70: The second verification step uses a bare "tsc --build
tsconfig.build.json --force --listEmittedFiles" which is ambiguous; update that
line to either use the same pnpm wrapper (pnpm --filter `@wafflebase/backend` exec
tsc --build tsconfig.build.json --force --listEmittedFiles) or explicitly state
"run from packages/backend" so the commands are deterministic and context-stable
in the repro steps.
In
`@docs/tasks/active/20260313-crdt-structural-concurrency-retrospective-todo.md`:
- Around line 3-4: Replace non-hyphenated compound modifiers with hyphenated
forms for clarity: change occurrences of "stable grid based" to
"stable-grid-based" and "visual index based" to "visual-index-based" throughout
the document (including the phrases in the current section and the lines
referenced as 36-39); update any variants like "stable-grid based" or
"visual-index based" to the fully hyphenated form to ensure consistent, readable
technical prose.
In `@docs/tasks/active/20260314-yorkie-worksheet-shape-migration-lessons.md`:
- Line 6: The phrase "side-effect free" should be hyphenated as the compound
adjective "side-effect-free"; update the text in the document where the label
sentence contains "side-effect free" so it reads "side-effect-free" to clarify
the compound modifier.
In `@packages/backend/scripts/copy-yorkie-documents.js`:
- Around line 104-126: The function fetchExistingYorkieKeys currently calls
mongosh via execFileSync and will throw a confusing error if mongosh isn't
installed; add a pre-flight check before invoking execFileSync (e.g., call
execFileSync('mongosh', ['--version']) or spawnSync to verify availability and a
zero exit code) and replace the raw exception with a clear, user-facing error
that explains the missing mongosh dependency and points to installation
instructions; update any usage/help text in the same file to document the
mongosh requirement and refer to the new check so users see an actionable
message instead of the low-level exception.
In `@packages/backend/scripts/copy-yorkie-documents.ts`:
- Around line 370-373: The loop over existingKeys currently awaits
copyDocument(sourceClient, targetClient, key) directly so a thrown error aborts
the whole process; wrap the per-key call in a try/catch inside the for loop (the
loop that iterates existingKeys and calls copyDocument) to catch and log errors
for that key (include the key and error message), push failed keys/errors into a
local failures array, continue processing remaining keys, and after the loop
report a summary (e.g., log or throw a single aggregated error) if failures is
non-empty so callers can see which documents failed.
- Line 362: The filter that builds missingKeys uses Array.includes on
existingKeys which is O(n) per check; change to create a Set from existingKeys
(e.g., const existingKeySet = new Set(existingKeys)) and then use
existingKeySet.has(key) inside the candidateKeys.filter callback to get O(1)
membership checks and improve performance (update the reference in the filter
that currently uses existingKeys.includes).
In `@packages/backend/scripts/migrate-yorkie-worksheet-shape.d.ts`:
- Line 1: The file migrate-yorkie-worksheet-shape.d.ts currently only contains
an empty "export {}" which is unclear; either delete this declaration file if it
serves no purpose, or replace it with a short explanatory comment and a clearer
intent (e.g., explain it exists to force module scope / prevent accidental
imports or to satisfy TypeScript module resolution), or change it to a proper
declaration (declare module '...' { /* types */ }) if it is intended to provide
typings; update the file migrate-yorkie-worksheet-shape.d.ts accordingly and
ensure the change matches the intended use of the CLI script or module.
In `@packages/backend/src/yorkie/worksheet-shape-migration.spec.ts`:
- Around line 252-258: Add extra negative test cases for
migrateYorkieWorksheetShape to cover more unsupported shapes: include at least
one test that passes an array root (e.g., []), one that passes a primitive root
(e.g., 'string' cast as never), and optionally a nested unexpected structure,
each asserting they throw 'Unsupported Yorkie spreadsheet document shape' (or at
least throw); update the spec around the existing it('throws for unsupported
shapes') block to add these new it(...) cases referencing
migrateYorkieWorksheetShape so the function's validation covers arrays,
primitives, and nested invalid inputs.
In `@packages/frontend/src/app/spreadsheet/yorkie-worksheet-axis.ts`:
- Around line 72-93: Add clear documentation that the parameter index is 1-based
and the function converts it to 0-based via Math.max(0, index - 1); update the
function comment or JSDoc for insertYorkieWorksheetAxis to state the 1-based
indexing convention (for both row and col axes) and note that ensureAxisLength
and the splice call expect the converted 0-based index, so callers should pass
1-based positions (use index = 1 for the first position).
In `@packages/frontend/tests/app/spreadsheet/chart-utils.test.ts`:
- Around line 45-63: The buildDoc helper manually constructs the spreadsheet
envelope; replace that with the shared factory createSpreadsheetDocument to keep
test fixtures aligned with canonical defaults—create the worksheet via
createWorksheet(), write cells with writeWorksheetCell(parseRef(...)), then call
createSpreadsheetDocument to produce the full document envelope and attach the
worksheet (or pass it into the factory if supported); update buildDoc to return
the result of createSpreadsheetDocument (or the factory result after assigning
tabs, tabOrder, and sheets) instead of hand-assembling the object so you avoid
duplicating schema defaults.
In `@packages/frontend/tests/helpers/two-user-yorkie.ts`:
- Around line 63-86: The switch in applyStoreOp handling ConcurrencyOp kinds is
non-exhaustive; add a default branch that fails fast for unknown kinds (so new
variants don't silently no-op). Update applyStoreOp to include a default case
that asserts exhaustiveness (e.g., cast op to never or throw an Error) and
surface the unexpected op.kind; reference applyStoreOp, ConcurrencyOp, parseRef,
YorkieStore.set and YorkieStore.shiftCells to locate where to add the
default/exhaustive check.
- Around line 46-48: The helper createStore currently silences type checking by
casting doc to never; update it to preserve type-safety by making createStore
generic and casting via unknown (or typing the parameter to the YorkieStore
constructor type). For example, change function createStore(doc: T) to a generic
signature (e.g., function createStore<T>(doc: T): YorkieStore and cast doc as
unknown as ConstructorParamType) so the YorkieStore instantiation (new
YorkieStore(...)) no longer uses "as never" and mismatched shapes are caught by
the compiler; locate the createStore function and the YorkieStore constructor
usage to apply this change.
In `@packages/sheet/src/model/workbook/worksheet-grid.ts`:
- Around line 88-89: The linter flags useIterableCallbackReturn because
rowOrder.forEach and colOrder.forEach callbacks return Map.set(); replace both
forEach uses with explicit for...of loops to iterate arrays and call
rowIndex.set(rowId, index + 1) and colIndex.set(colId, index + 1) inside the
loop bodies (use an index counter or Array.prototype.entries() in the for...of)
so that rowOrder, colOrder, rowIndex.set, and colIndex.set remain the same but
no callback return value is produced.
In `@packages/sheet/src/model/workbook/worksheet-record.ts`:
- Around line 71-81: The function parseWorksheetCellKey silently returns empty
strings when WorksheetCellKeySeparator isn't found which can hide malformed
input; update parseWorksheetCellKey to fail fast by throwing a descriptive Error
(include the invalid key and the expected WorksheetCellKeySeparator) instead of
returning {rowId: '', colId: ''}, and update any callers or types to handle the
thrown error (or alternatively change the return type to nullable and return
null consistently if you prefer non-throwing behavior); locate
parseWorksheetCellKey and replace the fallback branch with a clear error throw
(or null return) and ensure tests/call sites handle the new behavior.
In `@packages/sheet/src/view/formulabar.ts`:
- Line 2: Replace the runtime import of Sheet with a type-only import: change
"import { Sheet } from '../model/worksheet/sheet';" to "import type { Sheet }
from '../model/worksheet/sheet';" in formulabar.ts, ensuring that all references
to Sheet (e.g., the type annotations used in the form bar functions/variables)
remain type-only and that no runtime usage of Sheet exists in the file so the
compiler emits no runtime import.
In `@packages/sheet/src/view/spreadsheet.ts`:
- Around line 1-12: The import from ../model/core/types should be changed to a
type-only import because all symbols (Axis, BorderPreset, CellStyle,
ConditionalFormatRule, FilterCondition, GridResolver, Range, Ref, SelectionType)
are used only for typing; update the import line in spreadsheet.ts to use an
"import type { ... } from '../model/core/types';" form so the core/worksheet
boundary is preserved and runtime bundling won't include these types.
In `@packages/sheet/test/helpers/concurrency-driver.ts`:
- Around line 24-45: The switch in applyConcurrencyOp (function) covers current
ConcurrencyOp kinds but lacks an exhaustive default; add an explicit
exhaustiveness check so new ConcurrencyOp variants fail fast: either add a
default branch that throws a descriptive Error (e.g., `throw new
Error('Unhandled ConcurrencyOp kind: ' + (op as any).kind)`) or
introduce/consume a utility like assertNever(value: never) and call it in the
default case to ensure the compiler enforces exhaustiveness for ConcurrencyOp
and surface unhandled kinds at runtime/compile time.
- Around line 89-94: The snapshotMatchesOneOf function currently compares
snapshots with JSON.stringify which can fail if object key order differs; update
snapshotMatchesOneOf (working with ConcurrencySnapshot) to perform a stable
deep-equality comparison instead of raw stringify — either by normalizing
objects with a deterministic JSON.stringify (use a normalize helper that sorts
Object.keys(obj) in the replacer) before comparing, or call a reliable deepEqual
(e.g. lodash/isEqual) to compare actual against each candidate; ensure the
helper handles nested objects consistently and use that in the candidates.some
check.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: bed6be69-5b53-4bf5-b30b-30b511d39f0a
⛔ Files ignored due to path filters (3)
packages/backend/scripts/copy-yorkie-documents.js.mapis excluded by!**/*.mappackages/backend/scripts/migrate-yorkie-worksheet-shape.js.mapis excluded by!**/*.mappnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (135)
docs/design/README.mddocs/design/datasource.mddocs/design/frontend.mddocs/design/spreadsheet-collaboration.mddocs/tasks/README.mddocs/tasks/active/20260313-backend-dev-start-failure-lessons.mddocs/tasks/active/20260313-backend-dev-start-failure-todo.mddocs/tasks/active/20260313-crdt-structural-concurrency-analysis-lessons.mddocs/tasks/active/20260313-crdt-structural-concurrency-analysis-todo.mddocs/tasks/active/20260313-crdt-structural-concurrency-fix-lessons.mddocs/tasks/active/20260313-crdt-structural-concurrency-fix-todo.mddocs/tasks/active/20260313-crdt-structural-concurrency-retrospective-lessons.mddocs/tasks/active/20260313-crdt-structural-concurrency-retrospective-todo.mddocs/tasks/active/20260313-crdt-structural-concurrency-test-architecture-lessons.mddocs/tasks/active/20260313-crdt-structural-concurrency-test-architecture-todo.mddocs/tasks/active/20260314-collaboration-design-docs-lessons.mddocs/tasks/active/20260314-collaboration-design-docs-todo.mddocs/tasks/active/20260314-local-hackerwins-access-lessons.mddocs/tasks/active/20260314-local-hackerwins-access-todo.mddocs/tasks/active/20260314-local-prod-restore-lessons.mddocs/tasks/active/20260314-local-prod-restore-todo.mddocs/tasks/active/20260314-worksheet-model-boundary-lessons.mddocs/tasks/active/20260314-worksheet-model-boundary-todo.mddocs/tasks/active/20260314-yorkie-worksheet-shape-migration-lessons.mddocs/tasks/active/20260314-yorkie-worksheet-shape-migration-todo.mdpackage.jsonpackages/backend/package.jsonpackages/backend/scripts/copy-yorkie-documents.d.tspackages/backend/scripts/copy-yorkie-documents.jspackages/backend/scripts/copy-yorkie-documents.tspackages/backend/scripts/migrate-yorkie-worksheet-shape.d.tspackages/backend/scripts/migrate-yorkie-worksheet-shape.jspackages/backend/scripts/migrate-yorkie-worksheet-shape.tspackages/backend/src/api/v1/cells.controller.tspackages/backend/src/yorkie/worksheet-shape-migration.spec.tspackages/backend/src/yorkie/worksheet-shape-migration.tspackages/backend/src/yorkie/yorkie.types.tspackages/backend/tsconfig.build.jsonpackages/backend/tsconfig.jsonpackages/frontend/package.jsonpackages/frontend/src/app/documents/document-detail.tsxpackages/frontend/src/app/documents/migration.tspackages/frontend/src/app/spreadsheet/chart-utils.tspackages/frontend/src/app/spreadsheet/pivot/use-pivot-table.tspackages/frontend/src/app/spreadsheet/sheet-view.tsxpackages/frontend/src/app/spreadsheet/yorkie-store.tspackages/frontend/src/app/spreadsheet/yorkie-worksheet-axis.tspackages/frontend/src/app/spreadsheet/yorkie-worksheet-structure.tspackages/frontend/src/types/users.tspackages/frontend/src/types/worksheet.tspackages/frontend/tests/app/documents/migration.test.tspackages/frontend/tests/app/spreadsheet/chart-utils.test.tspackages/frontend/tests/app/spreadsheet/yorkie-concurrency-repro.test.tspackages/frontend/tests/app/spreadsheet/yorkie-concurrency.test.tspackages/frontend/tests/app/spreadsheet/yorkie-worksheet-structure.test.tspackages/frontend/tests/helpers/two-user-yorkie.tspackages/frontend/tests/resolve-hooks.mjspackages/sheet/package.jsonpackages/sheet/src/formula/arguments.tspackages/sheet/src/formula/formula.tspackages/sheet/src/formula/functions.tspackages/sheet/src/index.tspackages/sheet/src/model/core/coordinates.tspackages/sheet/src/model/core/locale.tspackages/sheet/src/model/core/types.tspackages/sheet/src/model/pivot/aggregate.tspackages/sheet/src/model/pivot/calculate.tspackages/sheet/src/model/pivot/group.tspackages/sheet/src/model/pivot/materialize.tspackages/sheet/src/model/pivot/parse.tspackages/sheet/src/model/workbook/worksheet-document.tspackages/sheet/src/model/workbook/worksheet-grid.tspackages/sheet/src/model/workbook/worksheet-record.tspackages/sheet/src/model/worksheet/calculator.tspackages/sheet/src/model/worksheet/conditional-format.tspackages/sheet/src/model/worksheet/dimensions.tspackages/sheet/src/model/worksheet/format.tspackages/sheet/src/model/worksheet/grids.tspackages/sheet/src/model/worksheet/input.tspackages/sheet/src/model/worksheet/merging.tspackages/sheet/src/model/worksheet/range-styles.tspackages/sheet/src/model/worksheet/sheet.tspackages/sheet/src/model/worksheet/shifting.tspackages/sheet/src/store/cell-index.tspackages/sheet/src/store/find-edge.tspackages/sheet/src/store/memory.tspackages/sheet/src/store/readonly.tspackages/sheet/src/store/store.tspackages/sheet/src/view/cellinput.tspackages/sheet/src/view/formulabar.tspackages/sheet/src/view/gridcanvas.tspackages/sheet/src/view/layout.tspackages/sheet/src/view/overlay.tspackages/sheet/src/view/spreadsheet.tspackages/sheet/src/view/worksheet.tspackages/sheet/test/formula/cross-sheet-ref.test.tspackages/sheet/test/formula/formula.test.tspackages/sheet/test/helpers/concurrency-case-table.tspackages/sheet/test/helpers/concurrency-driver.tspackages/sheet/test/model/conditional-format.test.tspackages/sheet/test/model/format.test.tspackages/sheet/test/model/grids.test.tspackages/sheet/test/model/input.test.tspackages/sheet/test/model/range-styles.test.tspackages/sheet/test/model/redirect-formula.test.tspackages/sheet/test/sheet/autofill.test.tspackages/sheet/test/sheet/calculation.test.tspackages/sheet/test/sheet/clipboard.test.tspackages/sheet/test/sheet/concurrency-matrix.test.tspackages/sheet/test/sheet/coordinates.test.tspackages/sheet/test/sheet/cross-sheet-calculation.test.tspackages/sheet/test/sheet/filter.test.tspackages/sheet/test/sheet/find.test.tspackages/sheet/test/sheet/formatting.test.tspackages/sheet/test/sheet/freeze-pane.test.tspackages/sheet/test/sheet/hidden.test.tspackages/sheet/test/sheet/input-inference.test.tspackages/sheet/test/sheet/manipulation.test.tspackages/sheet/test/sheet/merge.test.tspackages/sheet/test/sheet/moving.test.tspackages/sheet/test/sheet/pivot-aggregate.test.tspackages/sheet/test/sheet/pivot-calculate.test.tspackages/sheet/test/sheet/pivot-e2e.test.tspackages/sheet/test/sheet/pivot-group.test.tspackages/sheet/test/sheet/pivot-materialize.test.tspackages/sheet/test/sheet/pivot-parse.test.tspackages/sheet/test/sheet/pivot-protection.test.tspackages/sheet/test/sheet/sheet.test.tspackages/sheet/test/sheet/shifting.test.tspackages/sheet/test/store/find-edge.test.tspackages/sheet/test/store/store.test.tspackages/sheet/test/view/dimensions.test.tspackages/sheet/test/view/gridcanvas.test.tspackages/sheet/test/view/overlay.test.tsscripts/test/verify-entropy.test.mjs
💤 Files with no reviewable changes (2)
- packages/frontend/tests/app/documents/migration.test.ts
- packages/frontend/src/app/documents/migration.ts
|
|
||
| ### Verification | ||
|
|
||
| - `PGPASSWORD=wafflebase psql -h localhost -p 5432 -U wafflebase -d wafflebase_prod_restore -c "select id, username, email from \"User\" order by id asc;"` |
There was a problem hiding this comment.
Avoid unnecessary PII access in verification query.
Line 45 selects email, but the workflow only needs ownership/workspace verification. Please remove email from the query to minimize exposure when working with restored production-like data.
🔐 Proposed minimal-change fix
-- `PGPASSWORD=wafflebase psql -h localhost -p 5432 -U wafflebase -d wafflebase_prod_restore -c "select id, username, email from \"User\" order by id asc;"`
+- `PGPASSWORD=wafflebase psql -h localhost -p 5432 -U wafflebase -d wafflebase_prod_restore -c "select id, username from \"User\" order by id asc;"`📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| - `PGPASSWORD=wafflebase psql -h localhost -p 5432 -U wafflebase -d wafflebase_prod_restore -c "select id, username, email from \"User\" order by id asc;"` | |
| - `PGPASSWORD=wafflebase psql -h localhost -p 5432 -U wafflebase -d wafflebase_prod_restore -c "select id, username from \"User\" order by id asc;"` |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/tasks/active/20260314-local-hackerwins-access-todo.md` at line 45, The
verification query currently selects PII (email); update the SQL in the
documented command so it only returns non-PII fields used for
ownership/workspace verification (select id, username from "User" order by id
asc) — locate the command string containing `select id, username, email from
"User" order by id asc;` and remove `email` from the SELECT list, keeping
quoting and the rest of the psql invocation unchanged.
| - `pnpm --filter @wafflebase/backend exec tsx scripts/copy-yorkie-documents.ts --database-url postgresql://wafflebase:wafflebase@localhost:5432/wafflebase_prod_restore --mongo-url mongodb://127.0.0.1:37017/yorkie-meta --project-public-key fbuqYRxotajGGzb3kUD6aX --source-rpc-addr http://localhost:38080 --source-api-key fbuqYRxotajGGzb3kUD6aX --target-rpc-addr http://localhost:8080` | ||
| - `DATABASE_URL=postgresql://wafflebase:wafflebase@localhost:5432/wafflebase_prod_restore YORKIE_RPC_ADDR=http://localhost:8080 pnpm --filter @wafflebase/backend migrate:yorkie:worksheet-shape --document 4ca1ed51-1443-4dee-8575-f39035ad9eec` | ||
| - `pnpm --filter @wafflebase/backend test -- worksheet-shape-migration.spec.ts` | ||
| - `pnpm --filter @wafflebase/backend exec tsx scripts/copy-yorkie-documents.ts --database-url postgresql://wafflebase:wafflebase@localhost:5432/wafflebase_prod_restore --mongo-url mongodb://127.0.0.1:37017/yorkie-meta --project-public-key fbuqYRxotajGGzb3kUD6aX --source-rpc-addr http://localhost:38080 --source-api-key fbuqYRxotajGGzb3kUD6aX --target-rpc-addr http://localhost:8080 --document 3f54103b-9ba0-446b-a13b-96331d6a4199` | ||
| - `DATABASE_URL=postgresql://wafflebase:wafflebase@localhost:5432/wafflebase_prod_restore YORKIE_RPC_ADDR=http://localhost:8080 pnpm --filter @wafflebase/backend migrate:yorkie:worksheet-shape --document 3f54103b-9ba0-446b-a13b-96331d6a4199` | ||
| - `DATABASE_URL=postgresql://wafflebase:wafflebase@localhost:5432/wafflebase_prod_restore YORKIE_RPC_ADDR=http://localhost:8080 pnpm --filter @wafflebase/backend migrate:yorkie:worksheet-shape --all` |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
cat -n docs/tasks/active/20260314-local-prod-restore-todo.md | sed -n '70,90p'Repository: wafflebase/wafflebase
Length of output: 2010
🏁 Script executed:
# Check if these specific key values appear elsewhere in the codebase
rg "fbuqYRxotajGGzb3kUD6aX" --type md --type ts --type jsRepository: wafflebase/wafflebase
Length of output: 988
🏁 Script executed:
# Check the full context of the file to understand its purpose
head -20 docs/tasks/active/20260314-local-prod-restore-todo.mdRepository: wafflebase/wafflebase
Length of output: 701
🏁 Script executed:
# Search for these key values in test files and config
rg "fbuqYRxotajGGzb3kUD6aX|yorkie.*key|project.*public.*key" --type ts --type js -i | head -30Repository: wafflebase/wafflebase
Length of output: 3113
🏁 Script executed:
# Check if these look like dummy/test credentials by searching for similar patterns
rg "project-public-key|source-api-key" --type ts --type js -A 2 -B 2 | head -40Repository: wafflebase/wafflebase
Length of output: 2927
🏁 Script executed:
# Check for any .env.example or test configuration files
fd -e ".env.example" -e ".env.test" -e ".env.local"Repository: wafflebase/wafflebase
Length of output: 47
Use environment variables instead of embedding concrete credential values.
Lines 77 and 80 include credential parameters (--project-public-key fbuqYRxotajGGzb3kUD6aX, --source-api-key fbuqYRxotajGGzb3kUD6aX) and plaintext database credentials. Even though these appear to be test values, documentation should use environment variable references to maintain reusability and follow credential handling best practices.
Replace concrete values with environment variable references:
--project-public-key "$YORKIE_PROJECT_PUBLIC_KEY"--source-api-key "$SOURCE_YORKIE_API_KEY"postgresql://$DB_USER:$DB_PASSWORD@localhost:5432/...(for database URLs)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/tasks/active/20260314-local-prod-restore-todo.md` around lines 77 - 82,
The docs embed concrete credentials in commands that call the
copy-yorkie-documents.ts script and DATABASE_URL; update the examples to use
environment variable placeholders instead of literals — replace the
--project-public-key and --source-api-key flags passed to
scripts/copy-yorkie-documents.ts with --project-public-key
"$YORKIE_PROJECT_PUBLIC_KEY" and --source-api-key "$SOURCE_YORKIE_API_KEY", and
replace hard-coded DB credentials in DATABASE_URL values with
postgresql://$DB_USER:$DB_PASSWORD@localhost:5432/<DB_NAME> (also keep
YORKIE_RPC_ADDR and any other addresses as variables where appropriate).
| const targetClient = new sdk_1.default.Client({ | ||
| rpcAddr: options.targetRpcAddr, | ||
| }); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Check if there's a target-api-key argument handling in the source file
rg -n "target-api-key|targetApiKey" packages/backend/scripts/Repository: wafflebase/wafflebase
Length of output: 47
🏁 Script executed:
cat -n packages/backend/scripts/copy-yorkie-documents.js | head -200Repository: wafflebase/wafflebase
Length of output: 8327
Document the assumption that the target Yorkie server does not require authentication.
The target client is intentionally configured without an apiKey (no --target-api-key argument is parsed). This design assumes the target server does not require authentication. If this assumption changes or if target-server authentication becomes needed, the script will fail. Consider adding a comment in the code explaining this design decision.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/backend/scripts/copy-yorkie-documents.js` around lines 164 - 166,
Add a short inline comment above the target client construction (the
sdk_1.default.Client instantiation creating targetClient using
options.targetRpcAddr) stating that the target Yorkie server is assumed to not
require authentication and that no --target-api-key is parsed; note that if
target-server authentication is later required the script will need to accept
and pass an apiKey to the Client. Keep the comment concise and reference the
absence of apiKey handling so future maintainers know why no credential is
provided.
| const summary = { | ||
| processed: 0, | ||
| changed: 0, | ||
| unchanged: 0, | ||
| byKind: { | ||
| current: 0, | ||
| 'initialized-empty': 0, | ||
| 'legacy-flat': 0, | ||
| 'legacy-tabbed': 0, | ||
| }, | ||
| failures: [], | ||
| }; |
There was a problem hiding this comment.
Summary byKind initialization also missing current-flat.
The summary.byKind object initialization in the .js file is missing the 'current-flat': 0 entry that exists in the TypeScript source at line 347. This would cause summary.byKind['current-flat'] to be undefined, leading to incorrect summary output.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/backend/scripts/migrate-yorkie-worksheet-shape.js` around lines 126
- 137, The summary.byKind object is missing the 'current-flat' key so
summary.byKind['current-flat'] will be undefined; update the initialization of
the summary constant (the summary object with processed/changed/unchanged and
byKind) to include a "'current-flat': 0" entry alongside current,
'initialized-empty', 'legacy-flat', and 'legacy-tabbed' so counts for
current-flat are tracked correctly.
| "type": "module", | ||
| "main": "src/index.js", | ||
| "main": "dist/wafflebase-sheet.js", | ||
| "module": "dist/wafflebase-sheet.es.js", | ||
| "types": "dist/wafflebase-sheet.es.d.ts", | ||
| "exports": { | ||
| ".": { | ||
| "types": "./dist/wafflebase-sheet.es.d.ts", | ||
| "import": "./dist/wafflebase-sheet.es.js", | ||
| "require": "./dist/wafflebase-sheet.js", | ||
| "default": "./dist/wafflebase-sheet.es.js" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "1) Inspect sheet package build output naming/format config"
fd -a "vite.build.ts" packages/sheet | xargs -I{} sh -c 'echo "--- {}"; sed -n "1,220p" "{}"'
echo
echo "2) Inspect generated dist files (if present) to see cjs/esm artifact names"
fd -a "wafflebase-sheet*" packages/sheet/dist || true
echo
echo "3) Find CommonJS require callsites for `@wafflebase/sheet`"
rg -nP --type=ts --type=js --type=tsx --type=jsx -C2 'require\s*\(\s*["'\'']@wafflebase/sheet["'\'']\s*\)'
echo
echo "4) Inspect package.json export map currently in use"
sed -n '1,140p' packages/sheet/package.jsonRepository: wafflebase/wafflebase
Length of output: 856
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== 1) Full vite.build.ts for build config ==="
cat packages/sheet/vite.build.ts
echo
echo "=== 2) Full package.json exports section ==="
jq '.exports' packages/sheet/package.json
echo
echo "=== 3) Search for require calls (fixed syntax) ==="
rg 'require.*@wafflebase/sheet' -A 2 -B 2 --no-heading || echo "No require() calls found"
echo
echo "=== 4) Check package.json main/module/types fields ==="
jq '{main, module, types, type}' packages/sheet/package.json
echo
echo "=== 5) Check if vite produces cjs format ==="
grep -A 30 "build:" packages/sheet/vite.build.ts | grep -E "(lib|format|cjs)" || echo "No explicit cjs config found"Repository: wafflebase/wafflebase
Length of output: 1075
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Vite lib mode formats (checking Vite docs/defaults) ==="
# Check package.json dependencies to see Vite version
jq '.devDependencies | select(.vite)' packages/sheet/package.json
echo
echo "=== Check if there's a dist build or build logs ==="
find packages/sheet -name "*.cjs.js" -o -name "*.umd.js" 2>/dev/null | head -20 || echo "No dist artifacts found"
echo
echo "=== Check package.json main/module/types directly ==="
cat packages/sheet/package.json | head -20
echo
echo "=== Look for build scripts that show format intention ==="
jq '.scripts' packages/sheet/package.json || echo "No scripts section"
echo
echo "=== Verify: Does Vite lib mode include cjs by default? Check tsconfig or build docs ==="
ls -la packages/sheet/tsconfig* packages/sheet/vite* 2>/dev/null | grep -v node_modules || trueRepository: wafflebase/wafflebase
Length of output: 1776
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "=== Check for imports of `@wafflebase/sheet` across monorepo ==="
rg '@wafflebase/sheet' packages/ --type js --type ts --type json -l
echo
echo "=== Check if any of those imports use require() ==="
rg 'require.*@wafflebase/sheet|import.*@wafflebase/sheet' packages/ --type js --type ts -B 2 -A 2 | head -30
echo
echo "=== Verify Vite lib formats generated (check Vite 6 defaults) ==="
# Vite 6 lib mode defaults to building es, cjs, umd when name is set
echo "Vite 6.4.1 lib mode: When 'name' is set (wafflebase-sheet), default formats are: es, cjs, umd"
echo "The custom fileName maps them as shown in vite.build.ts"Repository: wafflebase/wafflebase
Length of output: 4335
exports.require should map to the CJS artifact, not UMD.
Line 15 maps "require" to ./dist/wafflebase-sheet.js (UMD format), but should point to ./dist/wafflebase-sheet.cjs.js (the actual CommonJS artifact from Vite's lib mode build). While UMD is technically compatible in both contexts, this violates idiomatic practice for "type": "module" packages. Update the exports map to use the explicit CJS file:
"require": "./dist/wafflebase-sheet.cjs.js"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@packages/sheet/package.json` around lines 7 - 16, The package exports map
currently points "require" at the UMD bundle file (./dist/wafflebase-sheet.js);
change the exports entry for "require" to reference the actual CommonJS artifact
produced by the build (./dist/wafflebase-sheet.cjs.js) so the package with
"type": "module" exposes an explicit CJS file for require consumers—update the
"." exports object's "require" property accordingly (search for the exports
block and the "require" key in package.json).
There was a problem hiding this comment.
🧹 Nitpick comments (2)
docs/tasks/active/20260314-ci-export-failure-todo.md (1)
1-4: Consider linking to the lessons learned document.To improve navigation between related documentation, consider adding a reference to the lessons learned file where this investigation's key takeaway is captured.
📝 Suggested enhancement with cross-reference
# CI Export Failure Investigation Investigate and fix the GitHub Actions failure caused by invalid runtime ESM exports from the `@wafflebase/sheet` barrel. + +> **Lesson captured:** [Barrel Files Must Separate Type-Only Exports From Runtime Exports](./20260314-ci-export-failure-lessons.md) ## Tasks🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/tasks/active/20260314-ci-export-failure-todo.md` around lines 1 - 4, Add a cross-reference from this task doc ("CI Export Failure Investigation" in 20260314-ci-export-failure-todo.md) to the lessons-learned document: insert a short sentence or markdown link after "Consider linking to the lessons learned document." that points to the canonical lessons file (e.g., "docs/lessons/ci-export-failure-lessons.md" or whatever the repository's lessons file is named), include link text like "See lessons learned" and ensure the link is relative and uses standard Markdown so readers can navigate between the task and the lessons-learned document.docs/tasks/active/20260314-ci-export-failure-lessons.md (1)
1-9: Consider adding a cross-reference to the investigation workflow.To improve discoverability, consider adding a reference to the related investigation workflow document at
docs/tasks/active/20260314-ci-export-failure-todo.md. This would help readers find the full context and verification steps.📝 Suggested enhancement with cross-reference
# CI Export Failure Investigation — Lessons + +> See also: [CI Export Failure Investigation workflow](./20260314-ci-export-failure-todo.md) ## Barrel Files Must Separate Type-Only Exports From Runtime Exports🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/tasks/active/20260314-ci-export-failure-lessons.md` around lines 1 - 9, Add a short cross-reference sentence and link to the related investigation workflow document near the top of this note (under the "Barrel Files Must Separate Type-Only Exports From Runtime Exports" heading) that points readers to the CI export failure investigation/workflow (the "todo" workflow) so they can follow the full investigation and verification steps; keep the wording concise (one or two sentences), include the document title as link text, and place it where readers scanning the lessons will notice it immediately.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@docs/tasks/active/20260314-ci-export-failure-lessons.md`:
- Around line 1-9: Add a short cross-reference sentence and link to the related
investigation workflow document near the top of this note (under the "Barrel
Files Must Separate Type-Only Exports From Runtime Exports" heading) that points
readers to the CI export failure investigation/workflow (the "todo" workflow) so
they can follow the full investigation and verification steps; keep the wording
concise (one or two sentences), include the document title as link text, and
place it where readers scanning the lessons will notice it immediately.
In `@docs/tasks/active/20260314-ci-export-failure-todo.md`:
- Around line 1-4: Add a cross-reference from this task doc ("CI Export Failure
Investigation" in 20260314-ci-export-failure-todo.md) to the lessons-learned
document: insert a short sentence or markdown link after "Consider linking to
the lessons learned document." that points to the canonical lessons file (e.g.,
"docs/lessons/ci-export-failure-lessons.md" or whatever the repository's lessons
file is named), include link text like "See lessons learned" and ensure the link
is relative and uses standard Markdown so readers can navigate between the task
and the lessons-learned document.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 52fe1417-c6fb-484d-879d-9ecb5b925515
📒 Files selected for processing (4)
docs/tasks/README.mddocs/tasks/active/20260314-ci-export-failure-lessons.mddocs/tasks/active/20260314-ci-export-failure-todo.mdpackages/sheet/src/index.ts
🚧 Files skipped from review as they are similar to previous changes (1)
- docs/tasks/README.md
There was a problem hiding this comment.
🧹 Nitpick comments (3)
packages/sheet/test/helpers/concurrency-driver.ts (1)
93-98: JSON.stringify comparison is acceptable but has a subtle ordering assumption.Since
ConcurrencySnapshot.cellsis aRecord<Sref, ...>,JSON.stringifywill serialize keys in insertion order. This works correctly as long as bothactualandcandidatesnapshots are built with the same key ordering (e.g., both using the samerefsarray viacaptureConcurrencySnapshot).If manually constructed candidate snapshots in test cases have different key ordering, comparison could fail unexpectedly. For test helpers this is likely fine, but for extra robustness you could normalize key order before comparison.
🔧 Optional: Normalize key order for comparison
export function snapshotMatchesOneOf( actual: ConcurrencySnapshot, candidates: ConcurrencySnapshot[], ): boolean { - return candidates.some((candidate) => JSON.stringify(candidate) === JSON.stringify(actual)); + const normalize = (snap: ConcurrencySnapshot): string => { + const sortedCells = Object.keys(snap.cells) + .sort() + .reduce((acc, key) => ({ ...acc, [key]: snap.cells[key] }), {}); + return JSON.stringify({ cells: sortedCells }); + }; + const actualStr = normalize(actual); + return candidates.some((candidate) => normalize(candidate) === actualStr); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/sheet/test/helpers/concurrency-driver.ts` around lines 93 - 98, The JSON.stringify comparison in snapshotMatchesOneOf can fail if Record keys are in different insertion orders; update snapshotMatchesOneOf (and/or add a small helper like normalizeSnapshot) to produce a deterministic representation by sorting the keys of ConcurrencySnapshot.cells (e.g., iterate Object.keys(snapshot.cells).sort() and build a new object with those ordered keys, and likewise normalize any nested objects/arrays if needed) and then compare JSON.stringify(normalizeSnapshot(candidate)) === JSON.stringify(normalizeSnapshot(actual)); this preserves existing behavior but removes the ordering sensitivity when comparing ConcurrencySnapshot instances.docs/design/spreadsheet-collaboration.md (1)
231-232: Minor grammar fix: hyphenate "top-level".Per static analysis, "top level" should be hyphenated when used as a compound modifier.
📝 Suggested fix
- - top level `tabs`, `tabOrder`, `sheets` + - top-level `tabs`, `tabOrder`, `sheets`🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/design/spreadsheet-collaboration.md` around lines 231 - 232, Change the phrase "top level `tabs`, `tabOrder`, `sheets`" to use a hyphenated compound modifier: "top-level `tabs`, `tabOrder`, `sheets`" so the document consistently hyphenates "top-level" when used as a compound adjective (the worksheet-local items `cells`, `rowOrder`, `colOrder`, `nextRowId`, `nextColId` remain unchanged).packages/sheet/src/index.ts (1)
127-141: Expose a callable alias for the default-document factory.
initialSpreadsheetDocumentnow reads like shared data, but this symbol now creates fresh state. Exporting acreateInitialSpreadsheetDocumentalias alongside the legacy name would make call sites much harder to misuse.♻️ Suggested export shape
import { DEFAULT_TAB_ID, DEFAULT_TAB_NAME, type ChartType, type SheetChart, type WorksheetFilterState, type Worksheet, type TabType, type SheetKind, type TabMeta, type SpreadsheetDocument, createWorksheet, createSpreadsheetDocument, - initialSpreadsheetDocument, + initialSpreadsheetDocument as createInitialSpreadsheetDocument, } from './model/workbook/worksheet-document'; ... DEFAULT_TAB_ID, DEFAULT_TAB_NAME, createWorksheet, createSpreadsheetDocument, - initialSpreadsheetDocument, + createInitialSpreadsheetDocument, + createInitialSpreadsheetDocument as initialSpreadsheetDocument, };As per coding guidelines, "Use descriptive variable and function names that clearly convey intent and purpose".
Also applies to: 212-216
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@packages/sheet/src/index.ts` around lines 127 - 141, Export a clearly named factory alias for the default-document creator: add and export createInitialSpreadsheetDocument as a callable alias that forwards to the existing initialSpreadsheetDocument factory so call sites use a verb-like name instead of a value-like name; update any other "initial..." exports in this module (the ones alongside createWorksheet and createSpreadsheetDocument) to provide matching createX aliases as well so factories are consistently named.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@docs/design/spreadsheet-collaboration.md`:
- Around line 231-232: Change the phrase "top level `tabs`, `tabOrder`,
`sheets`" to use a hyphenated compound modifier: "top-level `tabs`, `tabOrder`,
`sheets`" so the document consistently hyphenates "top-level" when used as a
compound adjective (the worksheet-local items `cells`, `rowOrder`, `colOrder`,
`nextRowId`, `nextColId` remain unchanged).
In `@packages/sheet/src/index.ts`:
- Around line 127-141: Export a clearly named factory alias for the
default-document creator: add and export createInitialSpreadsheetDocument as a
callable alias that forwards to the existing initialSpreadsheetDocument factory
so call sites use a verb-like name instead of a value-like name; update any
other "initial..." exports in this module (the ones alongside createWorksheet
and createSpreadsheetDocument) to provide matching createX aliases as well so
factories are consistently named.
In `@packages/sheet/test/helpers/concurrency-driver.ts`:
- Around line 93-98: The JSON.stringify comparison in snapshotMatchesOneOf can
fail if Record keys are in different insertion orders; update
snapshotMatchesOneOf (and/or add a small helper like normalizeSnapshot) to
produce a deterministic representation by sorting the keys of
ConcurrencySnapshot.cells (e.g., iterate Object.keys(snapshot.cells).sort() and
build a new object with those ordered keys, and likewise normalize any nested
objects/arrays if needed) and then compare
JSON.stringify(normalizeSnapshot(candidate)) ===
JSON.stringify(normalizeSnapshot(actual)); this preserves existing behavior but
removes the ordering sensitivity when comparing ConcurrencySnapshot instances.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: be16633e-5895-4851-8254-1c108145a256
📒 Files selected for processing (14)
docs/design/formula-and-calculator.mddocs/design/frontend.mddocs/design/sheet.mddocs/design/spreadsheet-collaboration.mdpackages/backend/package.jsonpackages/backend/src/yorkie/worksheet-shape-migration.tspackages/backend/test/jest-e2e.jsonpackages/frontend/src/app/documents/document-detail.tsxpackages/frontend/src/app/shared/shared-document.tsxpackages/frontend/src/app/spreadsheet/yorkie-worksheet-axis.tspackages/frontend/tests/helpers/two-user-yorkie.tspackages/sheet/src/index.tspackages/sheet/src/model/workbook/worksheet-document.tspackages/sheet/test/helpers/concurrency-driver.ts
🚧 Files skipped from review as they are similar to previous changes (3)
- packages/backend/package.json
- packages/frontend/tests/helpers/two-user-yorkie.ts
- packages/frontend/src/app/spreadsheet/yorkie-worksheet-axis.ts
Define a table-driven concurrency case set for spreadsheet structural edits and add a fast sheet-level serial-intent matrix. Add a real two-user Yorkie helper, a passing characterization slice, and an opt-in failing repro slice so known structure-vs-structure merge issues are executable without turning the default test lane red.
Move the worksheet/document schema behind a single shared boundary and reorganize the sheet package model into core, worksheet, workbook, and pivot layers. Replace the old coordinate-key and legacy worksheet paths with the canonical cells/rowOrder/colOrder shape, push Yorkie-specific axis and structure orchestration into frontend-local helpers, and share the worksheet types across frontend and backend. Add Yorkie worksheet-shape migration tooling and CLI, fix sheet barrel type exports, alias @wafflebase/sheet to source in Vite/Jest configs, and update doc references for moved model files. Co-Authored-By: Claude Opus 4.6 <[email protected]>
- Convert initialSpreadsheetDocument from shared const to factory function to prevent cross-caller mutation - Add all LegacyWorksheet optional properties to isLegacyWorksheetShape so objects with only merges, charts, filter, etc. are not misclassified - Add exhaustive switch defaults in applyStoreOp and applyConcurrencyOp - Reuse safeWorksheetRecordKeys, createWorksheetAxisId, and parseWorksheetCellKey from @wafflebase/sheet instead of maintaining duplicate implementations in the frontend Co-Authored-By: Claude Opus 4.6 <[email protected]>
7336bab to
fa574c6
Compare
Add same-cell, same-row/column-band, multi-count structural, and metadata+structure interaction cases to the concurrency matrix. Extend ConcurrencyOp with set-row-height and set-column-width variants, add dimension snapshot capture to both MemStore and Yorkie drivers, and classify new Yorkie cases into serial-intent and characterization filter lists. Co-Authored-By: Claude Opus 4.6 <[email protected]>
Rename spreadsheet-collaboration.md to collaboration.md — the project is a spreadsheet, so the prefix is redundant. Remove the deployment runbook and rehearsal log (operational, not design), drop the stale "Current Naming State" section, and update the case coverage list to reflect the 23-case matrix. Co-Authored-By: Claude Opus 4.6 <[email protected]>
1fb8d33 to
6779a71
Compare
The antlr4ts BitSet class uses util.inspect.custom as a computed property. Vite's browser polyfill for util doesn't include the custom symbol, causing a TypeError when the bundle loads in Node. Since the backend now imports @wafflebase/sheet at runtime (added in PR #29), this crash blocks the server from starting. Marking util as external lets Node.js provide its native implementation. The frontend imports sheet from source, not this bundle. Co-Authored-By: Claude Opus 4.6 <[email protected]>
PR #29 added runtime imports from @wafflebase/sheet in the backend, which surfaced two issues: 1. Docker build: sheet dist bundle must be built before the backend. Split into multi-stage build — compilation runs natively on the CI host while only prod dependency install runs under QEMU for the arm64 target. Build time dropped from 10+ min to ~1.5 min. 2. Sheet bundle: antlr4ts uses util.inspect.custom, which breaks when Vite polyfills Node's util for the browser. Externalize util in rollup and output .cjs for the CJS entry so Node always treats it as CommonJS regardless of the package "type": "module". Co-Authored-By: Claude Opus 4.6 <[email protected]>
Summary
cells/rowOrder/colOrderschema and split Yorkie-specific structure helpers out of the shared sheet packageTesting
pnpm verify:fastDATABASE_URL=postgresql://wafflebase:wafflebase@localhost:5432/wafflebase_prod_restore YORKIE_RPC_ADDR=http://localhost:8080 pnpm --filter @wafflebase/backend migrate:yorkie:worksheet-shape --all3f54103b-9ba0-446b-a13b-96331d6a4199migrated once aslegacy-tabbedand then re-ran ascurrentwithchanged=0Notes
delete row Nvsdelete row N/delete column Nvsdelete column Nhackerwinsworkspace to make browser validation easierSummary by CodeRabbit
New Features
Bug Fixes
Documentation
Tests
Chores