Update-Prozedur in CLAUDE.md auf volume-aware docker run umgestellt
(-v dienstplan-data:/data, -e DATA_DIR=/data) und Dockerfile-Snippet
auf den neuen Express/SQLite-Server aktualisiert. Hinweis ergaenzt,
dass DB und Backups auf dem Volume liegen und die Domain hinter Caddy
Basic-Auth steht.
Zusaetzlich .dockerignore ergaenzt: node_modules aus dem Host-Kontext
wurde sonst per COPY . . ins Image kopiert und ueberschrieb das im
Image gebaute better-sqlite3-Binary (invalid ELF header beim Start).
Ohne diesen Fix startet der Container nicht.
V1/V2/V3 duplicated the empty-result shape, the paid-shares/bonus math
and the Friday-priority deduction loop. Pull them into ineligibleResult,
payResult and deductInOrder; each variant keeps only its distinct
threshold, eligibility test and deduction slots.
paidShares is now computed uniformly as max(0, classified - deduction);
never-deducted slots carry deduction 0, so the result is byte-identical
(verified against a 13122-case golden master over the full input grid).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Each variant emitted isWinner:false and the calculator set winner.isWinner
=true, but the UI marks the winner via variantId===winnerId, never the
flag. Only one test read it. Removed from the 6 variant returns, the empty
result, and the calculator.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
No UI reads them - app.js computes its own labels via isQualifyingDay/
getDayTypeLabel when rendering the duty list. Only the test-suite asserted
them; those asserts are removed too. buildDutyDetails goes with them.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
groupEntriesByResolvedEmployee and resolveImports carried an identical
resolvedNames -> ignore/new/assign branch. One helper returning
{resolved, isNew}; callers keep their own null/newEmployee handling.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The "loop employees -> getVacationMode -> calculateAllEmployees" block
was copy-pasted in calculateBonuses, generateEmailReport, exportCSV and
exportBonusReport. Collapse to one storage helper. Behaviour identical:
calculateAllEmployees only reads keys present in employeeDuties.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
new Date() and the object spread never throw; the isNaN guard already
drops invalid dates via the .filter(). The inner catch could never fire.
Outer boundary try/catch is kept.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Zero callers across app, calculator, image-import and tests. Holiday
lookup goes through isHoliday/getHolidayName only.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- Same-day duty overwrite: addDuty is keyed per employee+date, so a
split Saturday duty between two employees creates two independent
0.5 entries, not a conflict. Not a bug.
- Holiday cutoff 2030: not a current priority, dropped per user call.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
The eligible-over-ineligible tie-break in calculateMonthlyBonus was a
deliberate fix (commit c733c3e) covered by an explicit test
(test-suite.js:191-203). Flagging it as an open bug in TODO.md was a
misreading of calculator.js against the design spec's simplified
pseudocode; the spec's own test plan (section 12.5) and the actual
test suite confirm the current behavior is correct.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Tie-breaker in calculateMonthlyBonus deviates from spec section 5,
same-day duties overwrite instead of summing, and holiday data only
covers 2025-2030.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Remove broken webpack.yml (no package.json/webpack config exists)
- Drop dead RATE_NORMAL/RATE_WEEKEND fields on BonusCalculator; share the
real rate constants across all three variants in variants.js
- Simplify getEmptyResult() to build allResults via .map instead of
three manual spreads
- Replace 5 duplicated German month-name arrays and 2 weekday-name
arrays in app.js with a shared monthName()/WEEKDAY_NAMES helper;
drop the now-redundant getMonthName() method
- Reuse existing .modal/.modal-backdrop/.modal-content CSS classes in
generateEmailReport() instead of hand-rolled inline styles
- Delegate ImageImporter.classify() to the shared classify() in
variants.js instead of a duplicated copy
- Unify ad hoc YYYY-MM-DD formatting in app.js to use the existing
HolidayProvider.formatDate() helper
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Multi-tenant deploy policies, container/Quadlet hardening rules, and
ADR/SOP templates don't fit a single-maintainer static-site app with
one Dockerfile and no container orchestration.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adopt the org engineering standard (its-consulting/standards @ v1.0.0).
Adds baseline governance/CI/policy files rendered from the standard's
templates and pins .standards-version=1.0.0. Vendored OPA/Rego policies
under .standards/policies/ so CI runs the gate locally (no cross-repo dep).
Placeholders ({{ORG}}/{{REPO}}/{{OWNER_HANDLE}}/{{MAINTAINER_EMAIL}}) filled in.
Existing files that differ were left untouched by the adopter.
Automated rollout. Files created: 16.
The bild-import and bonus-varianten plans were step-by-step build guides
consumed during implementation. Both features are merged and live.
Design specs in docs/specs/ stay for reference.
Conflicts resolved:
- sw.js: bumped CACHE_NAME to dienstplan-pro-v4 (was v3 + v2). Both
variants.js and image-import.js are in ASSETS.
- storage.js: kept STORAGE_KEY_DUTIES + STORAGE_KEY_VACATION (Feature B)
alongside STORAGE_KEY_OPENROUTER_KEY/MODEL + DEFAULT_MODEL (Feature A).
- styles.css: appended Feature B variants/vacation/date-stepper rules
after Feature A modal/key rules; both blocks coexist.
`let app` at top level does not create a window property, so the
DOMContentLoaded handler in image-import.js found window.app undefined
and never instantiated window.imageImporter. Clicking the Bild-Import
button then showed the 'Bild-Import nicht verfuegbar' toast.
Set window.app explicitly after construction.
Winner-picker used strict > with lowest-variantId tie-break, which made
V1 (ineligible) win over V3 (eligible) when all variants produced bonus=0
on Sa+So-only duties. Now ties prefer the eligible variant, keeping the
lowest-variantId rule as a sub-tie-breaker.