1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
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.
|