aboutsummaryrefslogtreecommitdiff
path: root/docs/CANONICAL.md
diff options
context:
space:
mode:
Diffstat (limited to 'docs/CANONICAL.md')
-rw-r--r--docs/CANONICAL.md182
1 files changed, 182 insertions, 0 deletions
diff --git a/docs/CANONICAL.md b/docs/CANONICAL.md
new file mode 100644
index 0000000..84c8f7e
--- /dev/null
+++ b/docs/CANONICAL.md
@@ -0,0 +1,182 @@
+# Canonical ShiftPlanner — measurement record
+
+Date: 2026-11-09
+Scope: five divergent copies of the same application found under `spaces/`.
+No source file was edited, moved, or deleted. All copies were read only, then
+copied into this product tree.
+
+## 1. Measured feature table
+
+Produced by one pass over each file with `wc`, `md5sum` and `grep -o | wc -l`.
+Pasted verbatim:
+
+```text
+FILE LINES BYTES MD5 OLLAMA AIWIRE DND EXPORT CDN VERSION
+spaces/mindtech/shiftplanner.html 2163 110972 5dc466b446ae3c19fffd9cd80f91461a 0 0 0 4 0 2.1
+spaces/mindtech/downloads/shiftplanner-v12-ai.html 2789 136212 37207b2227899603f90c42e59ff1875a 4 126 0 4 0 2.1
+spaces/mindtech/downloads/shiftplanner.html 1910 98435 a59dedc7608243985aad1d59b880bddd 0 0 0 4 0 2.0
+spaces/mindtech/shiftplanner_v1.3.html 1434 70415 daa2a71c94c2d0e5cb585857be5e4721 23 26 4 2 0 1.2
+spaces/agando/shiftplanner_v1.3.html 1627 81158 710875062b293ed78698d774696058ec 45 27 4 2 0 1.3
+```
+
+Column definitions (all counts are occurrences of the regex, not lines):
+
+| Column | Meaning |
+|---|---|
+| `LINES` / `BYTES` | `wc -l` / `wc -c` |
+| `MD5` | `md5sum` of the source file |
+| `OLLAMA` | `grep -o -i 'ollama'` — case-insensitive occurrences |
+| `AIWIRE` | `grep -o -i -E '\bai\b\|assistant'` — AI/assistant wiring tokens |
+| `DND` | `grep -o -i -E 'draggable\|dragstart'` — real drag-and-drop handlers |
+| `EXPORT` | `grep -o -E 'exportCSV\|toBlob\|download'` — export code paths |
+| `CDN` | `grep -o -E 'src="http'` — external `<script src="http…>` dependencies |
+| `VERSION` | the version literal the file itself declares (`version:'x.y'` in its save/export payload, or `version:'2.x'` in state) |
+
+Notes on measurement honesty:
+
+- `DND` deliberately excludes the bare word `drop`, because `drop` also matches
+ `dropdown` and `backdrop-filter`. Under the looser
+ `draggable|dragstart|drop` regex the three v2.x files score 1–2, but those
+ hits are CSS/UI strings only: `dragstart` is 0 and `draggable` is 0 in all
+ three v2.x files, so they have **no** drag-and-drop.
+- `CDN` is 0 for every file. A wider sweep for `src="http`, `href="http`,
+ `@import` and `fonts.googleapis` also returned nothing, so all five copies
+ are self-contained single files with no network dependency at load time.
+- `AIWIRE` 126 vs. the "~96" figure in the task context: 96 is reproducible as
+ the count of *lines* containing an AI token. The table reports occurrence
+ count, which is the more meaningful measure of wiring density.
+
+### Reconciling the "ollama" numbers
+
+The task context states the canonical copy has 0 `ollama` refs and the two
+v1.3 copies have 12 and 21. Measured result:
+
+| File | `grep -o 'ollama'` (case-sensitive occurrences) | `grep -c 'ollama'` (matching lines) | `grep -o -i 'ollama'` (any case) |
+|---|---|---|---|
+| canonical (v12-ai) | **0** | 0 | 4 |
+| mindtech v1.3 | 13 | **12** | 23 |
+| agando v1.3 | 25 | **21** | 45 |
+
+So the context figures are case-sensitive *line* counts. They are correct as
+stated, but they understate the canonical file's Ollama usage: the canonical
+contains four capitalised `Ollama` strings and calls the Ollama HTTP API
+directly. The claim "the canonical has no Ollama path" would be false. What is
+true is that the canonical's AI path is **Ollama-native and endpoint-driven**,
+while the v1.3 copies carry a **multi-provider** abstraction (Ollama + OpenAI +
+Anthropic) — a different architecture, not a strictly larger one. See §4.
+
+## 2. Decision
+
+**Canonical: `spaces/mindtech/downloads/shiftplanner-v12-ai.html`**
+→ published here as `src/index.html` (md5 `37207b2227899603f90c42e59ff1875a`,
+136,212 bytes, 2,789 lines).
+
+One-line reason: it is a strict functional superset — it contains every
+function present in both other v2.x copies (verified by set difference of
+`function` names: **zero** functions unique to either sibling) and adds the
+entire AI advisor panel on top, while remaining a single self-contained file
+with no CDN dependency.
+
+Evidence for the superset claim — the function-name set of each sibling minus
+the canonical's set is empty:
+
+```text
+=== spaces/mindtech/shiftplanner.html (functions NOT in canonical):
+
+=== spaces/mindtech/downloads/shiftplanner.html (functions NOT in canonical):
+
+```
+
+The two v1.3 copies, by contrast, are a **different, older codebase**
+(1,434 / 1,627 lines vs 2,789) with roughly 50 functions absent from the
+canonical. They have **no** shift templates (`saveTemplate`/`applyTemplate`
+count 0; their only "template" matches are `grid-template-columns` CSS), **no**
+day notes (`dayNote` count 0) and **no** CSV export (`exportCSV` count 0).
+They do keep automatic generation (`autoGeneratePlan`), fairness options
+(`Wochenende fair verteilen` / "distribute weekends fairly") and conflict
+handling (`detectAllConflicts`, `clearAllConflicts`), under a different
+codebase and with a **German-language UI**. They also add real drag-and-drop
+and a multi-provider AI chat. They are therefore not candidates for canonical,
+but they are worth archiving as feature donors.
+
+## 3. Archived copies
+
+| Archive file | Origin | Verdict |
+|---|---|---|
+| `archive/mindtech__shiftplanner.html` | `spaces/mindtech/shiftplanner.html` | **nothing found** — identical feature set to the canonical minus the AI panel; 0 functions and 0 features unique to it. |
+| `archive/mindtech__downloads__shiftplanner.html` | `spaces/mindtech/downloads/shiftplanner.html` | **nothing found** — the v2.0 predecessor; a strict subset of the canonical in both functions and UI strings. |
+| `archive/mindtech__shiftplanner_v1.3.html` | `spaces/mindtech/shiftplanner_v1.3.html` | **adds real drag-and-drop** and a 3-provider AI layer (see below). |
+| `archive/agando__shiftplanner_v1.3.html` | `spaces/agando/shiftplanner_v1.3.html` | **adds real drag-and-drop** plus the most complete Ollama integration of the five (proxy path, model refresh, timeouts, connection test). |
+
+What the two v1.3 copies have that the canonical does not, verified by grep:
+
+- **Drag-and-drop scheduling** — `draggable="true"`, `ondragstart`,
+ `ondragover`, `ondrop`, and handlers `onDragStart` / `onDrop` /
+ `onDragOver` / `onDragLeave` / `onDragEnd`. The canonical has **0** of these
+ (`draggable` 0, `dragstart` 0); it assigns workers via modals instead.
+- **Multi-provider AI** — both define `callLLM`, `sendChat`, `processChatMessage`,
+ `applyChatAction`, `chatAction`, `mockAgent`, `onProviderChange`; the
+ canonical's AI is single-provider (Ollama) and conversational only.
+ Endpoints observed:
+ - `http://localhost:11434` (Ollama)
+ - `https://api.openai.com/v1/chat/completions` + `https://api.openai.com/v1/models`
+ - `https://api.anthropic.com/v1/messages` (with `anthropic-version` and
+ `anthropic-dangerous-direct-browser-access` headers in the agando copy)
+- **Different localStorage schema** — key `schichtplan_v12` with
+ `loadState` / `saveState`, versus the canonical's key `shiftplanner_v2` with
+ `loadFromStorage` / `saveToStorage`. Any migration between the two needs an
+ explicit field mapping: the v1.3 payloads carry
+ `employees` / `plan` / `vacations` / `absences` / `customShifts` / `settings`,
+ the canonical carries `workers` / `shifts` / `assignments` / `customShifts` /
+ `templates` / `settings`.
+- **Extra functions unique to the agando copy** — `refreshOllamaModels`,
+ `testConnection`, `withTimeout`, `callProvider`, `providerName`,
+ `dateKeyOf`, `esc`.
+- **Export formats differ in the other direction** — both v1.3 copies export
+ **JSON only** (`exportCSV` count 0); the canonical exports **both JSON and
+ CSV**. This is a genuine canonical advantage.
+
+## 4. What the canonical's AI path actually calls
+
+The AI panel in `src/index.html` is wired to a locally running Ollama server.
+Exact strings, from grep:
+
+```text
+921: let aiEndpoint = 'http://localhost:11434';
+922: let aiModel = localStorage.getItem('sp_ai_model') || 'llama3.2';
+2478: fetch(aiEndpoint + '/api/tags', {
+2479: method: 'GET',
+2556: fetch(aiEndpoint + '/api/generate', {
+2486 (testAIConnection) models = data.models || []
+869: <input ... id="aiEndpointInput" value="http://localhost:11434" placeholder="http://localhost:11434">
+870: <div class="help-text">Ollama server URL. Leave default for local Ollama.</div>
+```
+
+- **Base URL:** `http://localhost:11434` (editable in Settings, persisted to
+ `localStorage['sp_ai_endpoint']`).
+- **Endpoints called:**
+ - `GET {endpoint}/api/tags` — connectivity check / model discovery.
+ - `POST {endpoint}/api/generate` — chat completion, body:
+ `{ model, prompt, stream: false, options: { temperature: 0.4, num_predict: 1024 } }`,
+ reply read from `data.response`.
+- **Model identifiers offered in the UI select:** `llama3.2` (default),
+ `mistral`, `llama3.1`, `phi3`, `gemma2`, `minimax-m3:cloud`.
+ The model choice persists to `localStorage['sp_ai_model']`.
+- **No cloud inference endpoint is contacted.** There is no `api.openai.com`,
+ `api.anthropic.com` or `generativelanguage` reference anywhere in the
+ canonical; the only network calls are to the user-configured Ollama base URL.
+- **Graceful degradation:** on failure the panel reports
+ `Not connected (start Ollama)` and the rest of the app is unaffected, so the
+ scheduler works fully with no server running.
+
+## 5. Provenance
+
+`src/index.html` is a byte-for-byte copy of the canonical source:
+
+```text
+37207b2227899603f90c42e59ff1875a products/shiftplanner/src/index.html
+37207b2227899603f90c42e59ff1875a spaces/mindtech/downloads/shiftplanner-v12-ai.html
+```
+
+All four archived files were verified md5-identical to their sources after
+copying. The originals under `spaces/` are unchanged.