diff options
Diffstat (limited to 'docs/CANONICAL.md')
| -rw-r--r-- | docs/CANONICAL.md | 182 |
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. |