Commit Graph
100 Commits
Author SHA1 Message Date
sgeboers 1dbf8da3a2 docs: move active plan to docs/plans/ 2026-04-04 18:47:01 +02:00
sgeboers f376300804 refactor: delete stale files and consolidate .mindmodel structure
Deleted stale root-level Python files:
- main.py (unused 'Hello world' script)
- verify.py (unused table info script)
- scraper.py (unused MotionScraper class)
- scheduler.py (unused DataUpdateScheduler class)

Deleted duplicate .mindmodel root YAML files (subdirectory versions are more comprehensive):
- anti-patterns.yaml, architecture.yaml, conventions.yaml
- dependencies.yaml, domain.yaml, domain-glossary.yaml
- stack.yaml, tech-stack.yaml, workflows.yaml

Added comprehensive .mindmodel subdirectories:
- constraints/ (naming, db-schema, error-handling, types, etc.)
- patterns/ (api, architecture, database, python, streamlit, etc.)
- examples/ (code examples for each pattern)
- anti-patterns/, architecture/, conventions/, dependencies/, domain/, stack/

Updated ARCHITECTURE.md to reflect current codebase:
- Removed references to non-existent files
- Added missing files (explorer.py, explorer_helpers.py, pipeline/)
- Added directory structure documentation
- Updated tech stack to include scipy, sklearn, umap

Updated .gitignore:
- Added patterns for generated analysis files
- Added .worktrees/ pattern (was already in gitignore but dir was deleted)

Removed empty .worktrees/ directory
2026-04-04 18:46:53 +02:00
sgeboers 0308d20f12 docs: add AGENTS.md with docs/solutions reference and SVD label best practice
- Add AGENTS.md with documented solutions reference
- Include SVD label convention (right-wing parties on right side)
- Document SVD insight: labels reflect voting patterns, not semantics
- Fix SQL verification example to use Python approach
2026-04-04 18:36:05 +02:00
sgeboers 92c3c0ee01 fix: update Components 2, 4, 5, 6 SVD labels based on voting pattern analysis 2026-04-04 18:29:48 +02:00
sgeboers f7fc908b58 fix: update Component 1 label to coalition-opposition reality
The component captures voting unity of the right-wing coalition vs left
opposition, NOT semantic content like 'defense' or 'EU integration'.
Motions about elderly care (Dobbe) appear because the left votes for them
while the right coalition votes against - this is coalition-opposition
polarization, not policy domain.
2026-04-04 18:25:19 +02:00
sgeboers bfe37c6806 fix: align report generation with JSON output for positive/negative separation
Bug: report_per_component used scored[:args.report_top_n] which took
top N by score (all positive for components with only positive scores).
JSON correctly separated positive and negative poles.

Fix: Use same positive/negative separation logic for report as JSON.
2026-04-04 18:19:31 +02:00
sgeboers e77f0ec9e3 fix: update SVD_THEMES labels to match actual motion content
- Component 1: EU-integratie → Defensie-uitgaven en NAVO-commitment
- Component 2: Flip False (was True), clarify populistisch vs institutioneel
- Component 4: Pragmatisch centrisme → Gematigde middenpartijen
- Component 5: Christelijk-sociaal → Gemeenschapszin en sociale zekerheid
- Component 6: Add migratie-integratie to label
- Component 8: Complete rewrite - was COMPLETELY WRONG (vaccinatie, onderwijs)
- Component 9: Decentraal bestuur → Pragmatische probleemoplossing
- Component 10: Institutioneel toezicht → Kritisch op overheidsbemoeienis
2026-04-04 18:09:46 +02:00
sgeboers 33edb334c4 feat: implement exclusive SVD motion assignment with label review report
- Each motion now assigned to exactly one component (highest absolute score)
- Added --exclusive flag (default: True) for backward compatibility
- Added markdown report generation with motion details for label review
- Added --report-top-n for report size (default: 20 per component)
- Updated JSON output with 'exclusive' flag for transparency
2026-04-04 17:55:28 +02:00
sgeboers ee8ffea6e2 fix: add health check wait to ansible deploy
Wait for Streamlit to be ready before finishing deployment to prevent 502 errors
2026-04-02 22:46:01 +02:00
sgeboers d8bee43c15 feat: add voting discipline analysis paragraph under political compass
- Add Dutch paragraph explaining Rice index and party discipline patterns
- Analysis covers high discipline parties (PVV, SGP) vs lower discipline parties
- Explains what discipline reveals about party dynamics
2026-04-02 22:03:57 +02:00
sgeboers f5f0c8d6b1 feat: add year selector for SVD components 3-10
- Add _load_mp_vectors_by_party_for_window() to load SVD vectors for specific windows
- Add load_party_axis_scores_for_window() cached function
- Add year selector UI for components 3-10 similar to components 1-2
- Uses get_uniform_dim_windows() to get available windows
2026-04-02 21:49:12 +02:00
sgeboers 5f7126f53f docs: add voting discipline analysis
Explain Rice index methodology and what it reveals about Dutch political parties
2026-04-02 21:47:08 +02:00
sgeboers abd3281044 refactor: remove Stemgedrag cohesie section and fallback axis message
- Remove voting discipline (cohesie) section from Political Compass tab
- Remove 'empirisch stempatroon zonder duidelijke ideologische richting' fallback message from axis classifier
- Clean up unused fallback template from _INTERPRETATION_TEMPLATES
2026-04-02 21:46:24 +02:00
sgeboers a5e95c33d7 refactor: use scatter plot format for SVD components 3-10
- Changed _render_party_axis_chart_1d from horizontal bar chart to scatter plot
- Same format as components 1-2: markers on horizontal line with axis arrows- Axis labels now show correct direction with arrows (← left | right →)
- Ensures consistent visualization across all SVD components
2026-04-02 21:39:09 +02:00
sgeboers fa019d8a9c test: add test for auto-flip computation for all components 2026-04-02 21:21:58 +02:00
sgeboers ed2b4c1fae test: add tests for 1D party position chart 2026-04-02 21:19:23 +02:00
sgeboers 95183fec5b test: update tests for unified SVD label system (Task 7) 2026-04-02 21:08:50 +02:00
sgeboers ba24ad4fe6 feat: auto-compute flip directions for all SVD components (Task 6) 2026-04-02 21:07:46 +02:00
sgeboers bda803089a feat: add 1D party position charts for SVD components 3-10 (Task 5) 2026-04-02 21:07:11 +02:00
sgeboers 5b3cf23d36 refactor: use svd_labels for fallback labels in explorer and axis_classifier (Task 4) 2026-04-02 21:05:30 +02:00
sgeboers 36b58ad50d refactor: use svd_labels module for fallback labels in axis_classifier (Task 3) 2026-04-02 21:02:53 +02:00
sgeboers 5b1be26050 refactor: move SVD_THEMES to module level for import (Task 2) 2026-04-02 21:02:03 +02:00
sgeboers a1c3e92fab docs: add SVD label unification implementation plan 2026-04-02 20:45:44 +02:00
sgeboers bed776f295 docs: add SVD label unification design spec 2026-04-02 20:40:56 +02:00
sgeboers c9c59dd166 feat(diagnostics): enhance trajectory diagnostic script with real data mode 2026-04-01 01:59:41 +02:00
sgeboers 7e202e15be test(trajectory): fix test quality issues 2026-04-01 01:57:59 +02:00
sgeboers 8bc43b67fd test(trajectory): add tests for plot rendering with edge cases 2026-04-01 01:56:04 +02:00
sgeboers 31e1dd4371 fix(trajectory): correct import for diagnose_trajectories 2026-04-01 01:51:14 +02:00
sgeboers 5cd031777c fix(trajectory): improve fallback handling and diagnostics when trace_count is 0 2026-04-01 01:49:14 +02:00
sgeboers 8e67b89a1d fix(trajectory): fix division by zero and None handling in name normalization 2026-04-01 01:46:13 +02:00
sgeboers 0b79709847 fix(trajectory): normalize MP names to improve party_map matching 2026-04-01 01:43:39 +02:00
sgeboers 26bdb4c61c refactor(trajectory): fix code quality issues in centroid diagnostics 2026-04-01 01:40:40 +02:00
sgeboers 7d93753530 fix(trajectory): add diagnostics to compute_party_centroids for NaN detection 2026-04-01 01:35:57 +02:00
sgeboers 385a25853c diagnose(trajectory): add diagnostics to identify why trace_count is 0 2026-04-01 01:31:49 +02:00
sgeboers 24796f97d3 test: add trajectory pipeline integration test 2026-03-31 15:08:50 +02:00
sgeboers 69208e0bf6 fix: skip second trace loop when helper succeeds to avoid duplicate traces 2026-03-31 15:04:45 +02:00
sgeboers 5d1328f824 chore: add TRAJ DEBUG print checkpoints to build_trajectories_tab 2026-03-31 14:55:08 +02:00
sgeboers 1a83f0f319 docs: add trajectory plots debugging plan 2026-03-31 02:12:49 +02:00
sgeboers 9f98dbae60 Add debug st.info before st.plotly_chart to diagnose invisible chart 2026-03-31 01:49:38 +02:00
sgeboers 72d1c20340 Show error and diagnostics when st.plotly_chart fails instead of silent pass
Previously the st.plotly_chart call was wrapped in 'except Exception: pass'
which silently swallowed all rendering errors. The user would see no chart
and no error message.

Now:
- Exception message is shown via st.error()
- Diagnostics JSON is shown when debug is enabled (EXPLORER_DEBUG_TRAJECTORIES=1
  or UI checkbox), even when trace_count > 0

This reveals the actual root cause when the chart fails to render.
2026-03-31 01:38:30 +02:00
sgeboers baee50f3a5 feat(explorer): extend diagnostic inspector to surface mp samples/counts
chore(explorer): add get_debug_trajectories_enabled helper

feat(explorer): instrument trajectories with debug diagnostics and un-silence helper exceptions
2026-03-31 00:28:14 +02:00
sgeboers 0f2db0a9be chore(explorer): add get_debug_trajectories_enabled helper 2026-03-31 00:22:26 +02:00
sgeboers 525cd157c0 docs: add diagnose-no-plot-trajectories design (2026-03-30) 2026-03-30 23:57:18 +02:00
sgeboers ce1fc86bcb docs(design): add fix-missing-trajectories design 2026-03-30 22:24:05 +02:00
sgeboers c059d5d955 Fix compass orientation and simplify CI display
- Lock x_label/y_label to Links-Rechts / Progressief-Conservatief after
  classify_axes; Procrustes sign-fixing in compute_2d_axes already ensures
  the correct orientation so the heuristic _should_swap_axes call is removed
- Remove visual error bars from party axis chart; 95% CI is now shown in
  hover text (party: score, N=n, 95%-BI: [low, high]) to keep the 1D
  scatter clean
- Remove show_ci checkbox and parameter — CI is always accessible on hover
- Update tests to match new hover format and absence of error_x
2026-03-30 18:31:39 +02:00
sgeboers b7129b3755 Extract _load_mp_vectors_by_party helper and fix cache key
- Extract shared helper that both load_party_axis_scores and
  load_party_mp_vectors delegate to, eliminating ~40 lines of
  duplicated DB query + vector parsing code
- Remove dead code in load_party_axis_scores that queried mp_metadata
  twice (first without ORDER BY, then again with ORDER BY, overwriting)
- Fix _cached_bootstrap_cis parameter: remove _ prefix so Streamlit
  actually hashes the input dict instead of caching with no key
2026-03-29 23:41:15 +02:00
sgeboers 3938eecc53 Add bootstrap CIs to party axis chart with error bars and diamond markers
- Add load_party_mp_vectors() to return raw per-MP SVD vectors by party
- Extract _build_party_axis_figure() as pure function for testability
- Modify _render_party_axis_chart to accept bootstrap_data and delegate
  to the new builder
- When bootstrap_data present: show error_x bars, diamond markers for
  N=1 parties, and N=count in hover text
- Wire up bootstrap computation in build_svd_components_tab via cached
  _cached_bootstrap_cis wrapper
- Add 6 tests covering figure construction, bootstrap rendering, flip
  behavior, and importability
2026-03-29 23:35:53 +02:00
sgeboers 88110b0aaa Fix update_existing_motions: single write connection and module-level duckdb import
Use one DuckDB write connection for the entire update loop instead of
opening/closing per row, wrapped in try/finally for proper cleanup.
Move 'import duckdb' to module level with other imports.
2026-03-29 23:28:40 +02:00
sgeboers be8887f6f8 Add --skip-details, --update-existing flags to download_past_year.py with tests
Enable backfilling body_text for existing motions that lack it (2016-2018 data).
New extract_besluit_id() and update_existing_motions() helpers support the
--update-existing mode, while --no-skip-details enables detail fetching during
normal downloads. Includes 7 tests covering URL parsing, DB update flow, and
argparse wiring.
2026-03-29 23:25:04 +02:00
sgeboers 72a8dd2721 Fix RNG re-seeding per party and vectorize bootstrap loop
Move rng initialization before the party loop so each party gets a
unique segment of the random stream instead of identical sequences.
Replace Python bootstrap loop with vectorized numpy indexing.
2026-03-29 23:17:33 +02:00
sgeboers cd8aeec997 Add compute_party_bootstrap_cis() to political_axis.py with tests
Pure numpy function that computes bootstrap confidence intervals for
party centroid vectors. Handles N>=2 (bootstrap), N=1 (degenerate CI),
and N=0 (excluded) cases. Uses np.random.default_rng for reproducibility.
2026-03-29 23:13:30 +02:00
sgeboers ef96edf478 Remove stale ad-hoc JSON analysis files
party_svd_scores.json was missing NSC and axis_analysis_data.json was
unused by the app. Both were one-off analysis artifacts.
2026-03-29 23:09:37 +02:00
sgeboers 10c9b78d16 Add .worktrees/ to .gitignore 2026-03-29 23:08:34 +02:00
sgeboers ff4ce0f9b2 Add design spec for bootstrap CIs and data enrichment 2026-03-29 22:57:36 +02:00
sgeboers db9a61094b Fix SVD_THEMES after self-review: PC2 label, indicatief markers, accuracy
- PC2: rename 'maatschappelijke verantwoordelijkheid' to 'institutioneel
  progressivisme' (less normatively loaded), rewrite explanation with actual
  party scores (CU=-59, SGP=-25, VVD=-15 — all strongly negative, not
  'near the middle'), update pole descriptions
- PC3: remove speculative motivation claim about PVV, state factual
  observation that PVV/SP/PvdD/GL-PvdA vote alike despite opposing PC1
- PC7-PC10: add '(indicatief)' to labels — these axes explain <4% EVR
  and may be below noise level
- PC7: add explicit fragility warning in explanation
- PC8: clarify DENK/SP negative scores mean active opposition voting,
  not lack of focus; note Volt N=1 unreliability
- Scree plot: soften claim that later axes are 'meaningful'
2026-03-29 21:55:36 +02:00
sgeboers a92315701f docs: improve SVD axis analysis with corrected methodology, party sizes, and review fixes
Major corrections:
- Fix PC2 factual error: CU/CDA/SGP/D66 are strongly negative (-13 to -58), not near zero
- Correct methodology: party scores use single-window SVD, not Procrustes pipeline
- Correct centering: global (after stacking), not per-window
- Fix Groep Markuszower misclassification on PC4 (positive, not negative pool)
- Fix D66/PC4-PC5 cross-reference error
- Fix PC8/DENK interpretation (negative = voting against, not absence of focus)

Additions:
- Party sizes (N=) for all 17 parties across all axes
- Party size reliability table (D66=26 to Volt=1)
- All 5 flip values documented (PC3,4,7,9,10), not just PC3
- Vector-space mismatch table (single-window scores vs Procrustes EVR)
- Cautionary '(indicatief label)' on PC7-PC10
- New follow-up steps: bootstrap CIs, dimensionality testing, varimax, external validation
- Softened causal claims (kabinetscrisis correlation, PVV motivations)
- Less normatively loaded PC2 label
2026-03-29 21:48:35 +02:00
sgeboers fd63585fe5 docs: detailed SVD axis analysis with method, findings, doubts and conclusions 2026-03-29 21:30:21 +02:00
sgeboers f96e804b67 update: refresh SVD axis labels based on current parliament motions (2025-2026)
- Re-ran generate_svd_json.py for current_parliament window (100 rows, 10 components)
- Computed party centroid scores per axis from 150 matched MPs
- Updated all 10 SVD_THEMES entries with accurate labels, Dutch explanations
  and correct positive/negative pole party attributions
- Key findings: PC1=rechts-links, PC2=populistisch nationalisme vs mainstream,
  PC3=verzorgingsstaat vs bezuinigingen, PC6=klimaat & energie,
  PC8=Europese defensie-integratie
- Added axis_analysis_data.json and party_svd_scores.json as analysis artifacts
2026-03-29 21:24:14 +02:00
sgeboers fc16664c5e fix: open DuckDB read_only in trajectory helpers to avoid lock conflict with Streamlit
Both _load_window_ids and _load_mp_vectors_for_window only read from the DB.
Opening without read_only=True caused an IOException when Streamlit already held
a read-only lock, silently returning an empty scree plot.
2026-03-29 21:10:33 +02:00
sgeboers 98b2583efd fix: scree plot now shows true EVR from Procrustes-aligned multi-window SVD
Previously load_scree_data computed L2-norms per dimension on current_parliament
vectors only, giving ~11% for PC1. This was inconsistent with the compass which
uses all windows + Procrustes alignment and gets PC1=24.1%.

Added compute_svd_spectrum() helper to political_axis.py that reuses the same
alignment pipeline. load_scree_data now delegates to it. _render_scree_plot
no longer re-normalizes (inputs are already EVR percentages). Hover label
updated to 'verklaarde variantie'.
2026-03-29 21:06:51 +02:00
sgeboers e0f17e8b83 Revert "fix: use annual-only windows for SVD to restore EVR (~20% PC1)"
This reverts commit ffd8b191ef.
2026-03-29 20:58:49 +02:00
sgeboers ffd8b191ef fix: use annual-only windows for SVD to restore EVR (~20% PC1)
Quarterly windows (29 of 41 total) diluted PC1 explained variance ratio
from ~20% down to ~14.6%. The fix splits the vector collection loop into:
- pca_vecs: annual windows only (re.match r'^\d{4}$') -> M_pca used for SVD
- all_vecs: every window -> M used for projections onto derived axes

Centering for SVD and global_mean for projection both now use M_pca.mean(axis=0)
so axes are consistent. Falls back to all windows if no annual windows exist.
2026-03-29 20:12:24 +02:00
sgeboers 2cca1000ca refactor: move _render_axis_motions to module level 2026-03-29 19:43:13 +02:00
sgeboers ab9b91e4a8 fix: close duckdb connections safely, swap x/y_axis vectors, fix EVR caption after axis swap 2026-03-29 19:42:22 +02:00
sgeboers ea3c68ece9 refactor: extract _render_axis_motions helper, use literal emoji in expander 2026-03-29 19:37:26 +02:00
sgeboers 37300f2c4e feat: add motion expander to compass tab — shows top motions per axis 2026-03-29 19:34:45 +02:00
sgeboers 9d219d63ee test: add neither-axis-LR edge case + document swap pass-through 2026-03-29 19:33:09 +02:00
sgeboers 74b3f10d07 feat: add axis swap — left-right goes on horizontal axis when detected 2026-03-29 19:29:32 +02:00
sgeboers 95c5ab9302 fix: generate interpretation string when motion path wins without ideology 2026-03-29 19:26:43 +02:00
sgeboers 1ff280e0e3 feat: restructure classify_axes — motion projection as primary label source 2026-03-29 19:20:56 +02:00
sgeboers 62daad321e fix: add outer exception handling to motion helpers in axis_classifier 2026-03-29 19:18:33 +02:00
sgeboers 96224be6ee feat: add motion-loading helpers to axis_classifier 2026-03-29 19:16:57 +02:00
sgeboers 1e52a8a8cc fix: deterministic tie handling and regex matching in _classify_from_titles 2026-03-29 19:15:31 +02:00
sgeboers 71e4b68926 fix: correct docstring for _classify_from_titles return value 2026-03-29 15:00:08 +02:00
sgeboers f8d9af7d9d feat: add _classify_from_titles keyword classifier to axis_classifier 2026-03-29 14:57:19 +02:00
sgeboers 6c4dd81723 feat: expose global_mean in compute_2d_axes axes dict 2026-03-29 14:50:05 +02:00
sgeboers 93a2287c04 docs: add motion-driven axis labeling implementation plan 2026-03-29 14:36:28 +02:00
sgeboers 9dcf6201bb Add design spec for motion-driven axis labeling
Replaces static ideology CSV as primary axis classification signal with
per-year motion projection + Dutch keyword classifier. Adds axis-swap
logic so left-right is conventionally on X when present. Adds Option C
UI expander showing top motions per axis pole.
2026-03-29 14:19:54 +02:00
sgeboers 392fd3afce fix: add per-window X-axis orientation correction
The global PCA X-axis flip uses centroids averaged across all windows,
which can leave individual windows with left/right inverted (e.g. PvdA
appearing right of VVD in 2020). Mirror the existing per-window Y-axis
correction to also check and flip X values per window.
2026-03-29 01:15:21 +01:00
sgeboers 34c08a40fa feat: use dynamic axis labels in compass and trajectories UI
Replace hardcoded 'Links-Rechts' / 'Progressief-Conservatief' axis labels
with values from classify_axes(). Add per-year interpretation caption when
axis quality score is below the 0.65 correlation threshold.
2026-03-29 01:04:54 +01:00
sgeboers 5ec1f7af75 feat: add axis classifier with party ideology reference data
classify_axes() correlates per-party PCA positions against party_ideologies.csv
to assign honest dynamic labels (Links-Rechts, Coalitie-Oppositie, etc.)
instead of always assuming the first PCA axis is left-right.
2026-03-29 01:00:55 +01:00
sgeboers 23849c9cb6 docs: add axis classification implementation plan 2026-03-29 00:58:53 +01:00
sgeboers 6b811364c5 docs: add deployment note to axis classification spec
CSV files committed to git, baked into Docker image — no rsync needed.
2026-03-29 00:53:54 +01:00
sgeboers bb5f2961d1 docs: fix two spec ambiguities in axis classification design
- Clarify CSV path derivation from db_path (same data/ directory)
- Handle current_parliament window exclusion from modal label voting
2026-03-29 00:52:11 +01:00
sgeboers bed911b92c docs: add axis classification design spec
Add design for honest PCA axis labeling — validates each compass axis
against a party ideology reference CSV and labels dynamically (Links–Rechts,
Coalitie–Oppositie, or fallback) instead of hardcoding Left–Right always.
2026-03-29 00:51:34 +01:00
sgeboers 50f8a06c6d fix: connection leak, Rice index excludes absences, per-party motion count guard 2026-03-28 23:50:09 +01:00
sgeboers bcf9407957 feat: add voting discipline section below political compass 2026-03-28 23:45:38 +01:00
sgeboers ab99b7de18 fix: replace sideways Y-axis arrows with proper top/bottom annotations 2026-03-28 23:41:01 +01:00
sgeboers aac8a89118 fix: add missing party justifications in SVD_THEMES axes 3 and 5 explanations 2026-03-28 23:39:24 +01:00
sgeboers b6c2a9bacf fix: update SVD_THEMES axes 3-5 descriptions to reflect stable multi-year patterns 2026-03-28 23:37:10 +01:00
sgeboers 6914b2284a Add implementation plan for compass UI improvements 2026-03-28 23:33:03 +01:00
sgeboers c5b39ced5f Add design doc for compass UI improvements (axes 3-5, Y-axis arrows, discipline section) 2026-03-28 23:30:32 +01:00
sgeboers 064cd059d4 fix: per-window Y-axis correction for political compass
The global orientation check using party centroids averaged across all
windows was insufficient — individual windows (notably 2023) could still
have conservative parties above progressive ones on the Y-axis.

Added a per-window flip in compute_2d_axes (PCA branch) that checks
prog_avg_y vs cons_avg_y for each window independently and negates all
Y values in that window when cons > prog. Flipped window IDs are stored
in axis_def['y_flipped_windows'] for diagnostics.

Moved the canonical party set definitions outside the orientation try-
block so they are always in scope for the per-window correction.

Added test_per_window_y_orientation to cover the case where one window
is globally fine but locally inverted.
2026-03-28 22:45:40 +01:00
sgeboers 6329d6a256 UI improvements + add axis orientation test
- Rename app to 'Motief: de stematlas' in Home.py
- Remove PCA variance caption from compass tab
- Hardcode db_path and window_size; remove sidebar inputs
- Change trajectories default to [CDA, D66, VVD]
- Move quiz to pages/1_Stemwijzer.py; wrap in st.form
- Remove quiz tab from main explorer
- Add pytest dev dep + fix test fixtures (_load_mp_vectors_for_window)
- Add test_pca_axis_orientation with proper PCA variance dominance
2026-03-28 22:27:39 +01:00
sgeboers 72fbe0008e fix(ansible): add headless and CORS flags for reverse proxy 2026-03-28 22:01:04 +01:00
sgeboers b50ee650de fix(ansible): create data directory on server before rsync 2026-03-28 21:39:33 +01:00
sgeboers cbab8f080d fix(ansible): use rsync with checksum for motions.db sync 2026-03-28 21:09:17 +01:00
sgeboers 0bd1c08cb2 fix(ansible): ignore pkill errors, add motions.db sync task 2026-03-28 21:04:49 +01:00
sgeboers 22067fd162 fix(ansible): use full path for uv binary in shell tasks 2026-03-28 21:02:01 +01:00
sgeboers de6ed29bf7 fix(ansible): use port 222 for Gitea SSH, write SSH config on server 2026-03-28 20:46:57 +01:00