# Opalstack Concordance Editor Handoff

## Purpose

This document defines the intended Django-side workflow for the future admin-only concordance editor at `concordances.lutemusic.org`.

The Windows-side processing model stays unchanged:

- the web app edits `settings_new.json`
- the Windows machine imports that file
- local scripts rebuild and validate the derived concordance outputs
- only the Windows side promotes changes into live `settings.json`

The web side does not write `pieces.json`, `piece_data.json`, `setting_piece_map.json`, or `settings_with_pieces.json`.

## Current Implementation Stub

A Django app scaffold now exists at:

- `D:\website\design\lmo_code\static\concordance_editor`

It provides:

- file-backed draft state
- resume/discard logic
- stale-draft detection
- fallback search over settings rows
- the three real concordance edit operations
- export manifest generation
- lightweight JSON endpoints and a placeholder page

This is not the final UI. It is the backend contract and workflow skeleton.

## Core Rules

### Source of truth

- Live concordance membership still lives in `settings.json`.
- The web editor starts from the current live `settings.json`.
- The web editor writes draft changes into `settings_new.json` only.

### Derived files

These remain local rebuild products only:

- `pieces.json`
- `piece_data.json`
- `setting_piece_map.json`
- `settings_with_pieces.json`

Do not edit them directly on the web side.

### Singleton rule

`Make singleton` means:

- blank the selected setting's `concordances` field in `settings_new.json`

That is enough. The Windows rebuild step already knows how to turn blank concordances into generated singleton piece keys.

## Draft Files

The Django stub uses three files in its draft directory:

- `settings_new.json`
  - the only draft file that matters to the Windows importer
- `settings_new.manifest.json`
  - optional export sidecar for hash verification
- `settings_new.state.json`
  - web-only bookkeeping for resume/discard and stale-draft detection

`settings_new.state.json` is intentionally not part of the Windows workflow.

## Draft Lifecycle

### No draft exists

If no `settings_new.json` exists:

- copy the current live `settings.json` to `settings_new.json`
- record the live `settings.json` SHA-256 in `settings_new.state.json`

### Draft exists

If `settings_new.json` exists:

- `Resume previous editing` keeps the current draft
- `Discard previous changes` replaces the draft with a fresh copy of live `settings.json`

### Stale draft detection

The draft is stale if:

- `settings_new.state.json.base_settings_sha256`
- does not match the current live `settings.json` hash

If stale:

- resume should be blocked
- discard and reseed should be required

This avoids exporting a draft that `process_settings_new.py` would later reject as based on an older live state.

## Search Model

### Intended final behavior

- The concordance editor should reuse the main site search UI and search backend.
- The left-hand and right-hand pickers should both use that same search system.

### Current fallback stub

The Django scaffold includes a local token-based fallback search over:

- title
- subtitle
- composer
- original composer
- source
- document
- page

This is only a temporary local stand-in so the editor backend can be exercised before the main search is wired in.

## Setting Detail Payload

Each selected setting should expose enough data for the editor UI to show:

- title
- composer
- original composer
- source
- document
- page
- MIDI link
- PDF link
- concordance count
- concordance member list

The current backend already emits those fields in its JSON detail payload.

## Real Edit Operations

There are only three persistent edit operations on the server side.

### `merge_just_setting`

- change only the selected left-hand setting's `concordances`
- assign it to the right-hand piece

### `merge_setting_groups`

- change all settings currently in the left-hand piece
- assign them to the right-hand piece

### `make_singleton`

- blank only the selected left-hand setting's `concordances`

## UI-Only Actions

These are not persistent draft edits:

- `Different piece`
- `Cancel`

For a left-hand singleton, `Same piece` is just the UI label for `merge_just_setting`.

## Important Edge Case: Merging Into a Draft Singleton

After `make_singleton`, the selected setting has blank `concordances`.

That is correct for export, but it means the draft no longer has a stored piece key for that singleton. If a later edit targets that singleton from the right-hand side, the web app needs a usable piece key.

Current rule in the Django stub:

- blank singleton groups are treated as provisional singleton pieces in memory
- if another setting or setting group merges into that provisional singleton
- the web app generates a real manual piece key
- and writes that key into both sides of the merged group

This is the right behavior because the target is no longer a singleton after the merge.

## Export Behavior

### Export file

The primary export is:

- `settings_new.json`

This file should preserve the full settings rows and original ordering. Only concordance membership fields should change.

### Optional manifest

The optional sidecar export is:

- `settings_new.manifest.json`

Its job is:

- prove which live `settings.json` the export was based on
- prove the exported `settings_new.json` was transferred intact

### Required manifest fields

`process_settings_new.py` already accepts these names:

- `base_settings_sha256`
- `settings_new_sha256`

The current Django stub writes those exact field names.

### Additional manifest fields

The current stub also writes:

- `type`
- `version`
- `created_at`
- `created_by`
- `setting_count`
- `blank_concordance_count`
- `membership_change_count`
- `operation_count`

These extra fields are optional metadata and do not affect the Windows importer.

### No zip export

Do not zip the export by default.

The Windows importer expects:

- `settings_new.json`
- optional adjacent manifest JSON

Keeping both as plain files matches the existing local pipeline directly.

## Django App Wiring

The current design-project stub is wired at:

- route prefix: `/concordances/`

Implemented endpoints:

- `GET /concordances/`
- `GET /concordances/api/session/`
- `POST /concordances/api/session/start/`
- `GET /concordances/api/search/`
- `GET /concordances/api/setting/`
- `POST /concordances/api/action/`
- `POST /concordances/api/export/`
- `GET /concordances/download/settings_new.json`
- `GET /concordances/download/settings_new.manifest.json`

## Auth Assumption

The current stub allows:

- Django `is_staff`
- Django `is_superuser`
- or `accounts.CustomUser.user_type >= 2`

That matches the present "admin-only is fine" guidance.

## Non-Goals For This Stub

This pass does not attempt:

- final React UI
- true reuse of the production main-site search
- Opalstack deployment config
- end-to-end browser-to-Windows automation
- direct promotion of live concordance files from the web server

Those remain future work.

## Recommended Next Steps

1. Decide the real Opalstack draft directory and set Django settings accordingly.
2. Replace the fallback search endpoint with the main site search backend.
3. Build the actual left/right editor UI against the existing JSON endpoints.
4. Decide whether export should be one explicit button or always-available download links.
5. When Opalstack exists, verify that browser download behavior can place `settings_new.json` into the Windows inbox workflow with minimal friction.
