Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions offline-collaboration-conflict-resolver/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Offline Collaboration Conflict Resolver

This module implements a focused sync layer for the SCIBASE real-time collaborative research editor bounty. It handles the tricky part that appears when researchers edit scientific manuscripts offline and later reconnect:
deterministic operation replay, section-lock conflict detection, suggestion/comment merge safety, audit reports, and restore-ready snapshots.

It is intentionally dependency-free so reviewers can run it with stock Node.js.

## What It Covers

- Client-side offline operation queues with actor and block metadata.
- Rebase of offline edits on top of server operations.
- Section lock checks so protected manuscript areas are not overwritten.
- Safe inline comment and suggestion merging.
- Idempotent operation replay so retried offline operations do not apply twice.
- Stale version and missing suggestion conflict reporting.
- Restore-ready snapshots with content hashes.
- Reviewer-facing audit reports with stable audit hashes and duplicate replay counts.

## Demo

```bash
npm run demo
```

The demo prints a sync report where an abstract edit is applied, a locked methods edit is blocked for manual review, and a review suggestion is safely accepted.

Demo artifacts: `docs/demo.gif` and `docs/demo.svg`.

## Verification

```bash
npm run check
npm test
npm run demo
```

## Files

- `src/conflict-resolver.js` - core queue, rebase, snapshot, and report logic.
- `test/conflict-resolver.test.js` - focused tests for rebase, locks, suggestions, duplicate replay, and snapshots.
- `scripts/demo.js` - CLI demo with sample scientific manuscript blocks.
- `docs/issue-12-requirement-map.md` - mapping from issue requirements to implementation evidence.

## AI-Assisted Disclosure

This contribution was produced with AI assistance and manually verified before submission.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 32 additions & 0 deletions offline-collaboration-conflict-resolver/docs/demo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Issue #12 Requirement Map

This module is a focused offline collaboration slice for the real-time collaborative research editor. It complements the broader editor modules by handling the state that accumulates when a researcher edits a manuscript while disconnected.

| Issue #12 requirement | Implementation evidence |
| --- | --- |
| Multi-user editing with live collaboration semantics | `queueOfflineOperation` records actor-scoped client edits, and `rebaseOfflineQueue` replays them on top of remote server operations. |
| Inline comments, suggestions, and change tracking | Offline comments, suggestions, and retried operation ids are merged idempotently; suggestion resolution is audited and blocked if the suggestion was already removed. |
| Locking / unlock modes for controlled sections | `detectConflict` blocks offline edits when a section has an active lock owned by another collaborator. |
| Continuous autosave with local caching | `createSnapshot` creates restore-ready snapshots with content hashes before or after sync. |
| Fine-grained version tracking | Each block carries a version, and stale offline edits are audited when the server version has advanced. |
| Restore previous versions or compare changes | The post-rebase snapshot contains a `restorePayload`, stable `contentHash`, and audit hash for comparison. |
| Integrated review workflow | Conflict reports identify manual-review blockers before risky edits overwrite locked sections or resolved suggestions. |

## Reviewer Notes

- The implementation is dependency-free and can be reviewed with stock Node.js.
- It is intentionally not a UI mock. The value is deterministic sync behavior that a real editor UI or API can call.
- The demo includes one applied stale edit, one locked-section conflict, and one accepted offline suggestion resolution.
13 changes: 13 additions & 0 deletions offline-collaboration-conflict-resolver/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "offline-collaboration-conflict-resolver",
"version": "1.0.0",
"description": "Offline edit rebase and conflict audit module for the SCIBASE real-time collaborative research editor bounty.",
"main": "src/conflict-resolver.js",
"type": "commonjs",
"scripts": {
"check": "node --check src/conflict-resolver.js && node --check scripts/demo.js && node --check test/conflict-resolver.test.js",
"demo": "node scripts/demo.js",
"test": "node test/conflict-resolver.test.js"
},
"license": "Apache-2.0"
}
89 changes: 89 additions & 0 deletions offline-collaboration-conflict-resolver/scripts/demo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
"use strict";

const {
queueOfflineOperation,
rebaseOfflineQueue,
buildConflictReport
} = require("../src/conflict-resolver");

const baseDocument = {
id: "manuscript-alpha",
baseRevision: "rev-17",
locks: [
{ sectionId: "methods", ownerId: "reviewer-2", status: "active" }
],
blocks: [
{
id: "abstract",
sectionId: "frontmatter",
type: "markdown",
content: "We introduce a catalyst screening workflow.",
citations: ["doi:10.1000/base"],
version: 2
},
{
id: "method-step",
sectionId: "methods",
type: "notebook",
content: "Run catalyst notebook",
version: 3
},
{
id: "discussion",
sectionId: "discussion",
type: "latex",
content: "The yield improves by 12%.",
suggestions: [{ id: "sug-1", status: "open", text: "Mention confidence interval." }],
version: 1
}
]
};

const serverOperations = [
{
id: "srv-1",
actorId: "editor-remote",
kind: "update-block",
blockId: "abstract",
content: "We introduce a reproducible catalyst screening workflow."
}
];

let offlineQueue = [];
offlineQueue = queueOfflineOperation(offlineQueue, {
id: "off-1",
actorId: "author-1",
blockId: "abstract",
expectedVersion: 2,
content: "We introduce a reproducible catalyst screening workflow for open labs.",
citations: ["doi:10.1000/open-labs"]
});
offlineQueue = queueOfflineOperation(offlineQueue, {
id: "off-2",
actorId: "author-1",
blockId: "method-step",
expectedVersion: 3,
content: "Run catalyst notebook with seeded environment capture."
});
offlineQueue = queueOfflineOperation(offlineQueue, {
id: "off-3",
actorId: "author-3",
blockId: "discussion",
kind: "resolve-suggestion",
suggestionId: "sug-1",
expectedVersion: 1
});

const result = rebaseOfflineQueue(baseDocument, serverOperations, offlineQueue);
const report = buildConflictReport(result);

console.log(JSON.stringify({
report,
applied: result.applied,
blocked: result.blocked,
snapshot: {
id: result.snapshot.id,
contentHash: result.snapshot.contentHash,
blockCount: result.snapshot.blockCount
}
}, null, 2));
Loading