Commit Graph
100 Commits
Author SHA1 Message Date
marcopan e2d1117614 fix: make workspace registry lock process-bound 2026-08-03 22:42:57 +02:00
marcopan 553bb41138 fix: harden workspace registry refresh and snapshots 2026-08-03 22:33:39 +02:00
marcopan 2087fbb0c9 feat: manage workspace Git checkout and snapshots 2026-08-03 22:21:10 +02:00
marcopan 5d7ebc5b01 fix: harden workspace runtime snapshots 2026-08-03 22:10:09 +02:00
marcopan 049f8675c6 feat: resolve workspace bindings into runtime configs 2026-08-03 21:49:57 +02:00
marcopan 5a654939a5 fix: harden workspace schema contracts 2026-08-03 21:40:58 +02:00
marcopan 92cb0545be feat: add canonical workspace schema 2026-08-03 21:31:07 +02:00
marcopan 6434c9c4e1 fix: reject reserved Git HEAD branch 2026-08-03 21:22:08 +02:00
marcopan cc951d8073 fix: harden workspace registry config validation 2026-08-03 21:19:29 +02:00
marcopan 6e1321f93c feat: configure Git workspace registry 2026-08-03 21:13:23 +02:00
marcopan 7ad0199f9b docs: plan Git workspace registry 2026-08-03 21:05:31 +02:00
marcopan a6e6bc6800 docs: require workspace installation manuals 2026-08-03 20:57:53 +02:00
marcopan fef7a7614e docs: design git-backed workspace registry 2026-08-03 20:41:57 +02:00
marcopan 18233d59af fix: refine session report and live activity layout 2026-07-24 00:21:36 +02:00
marcopan 6ca4275ff2 fix: improve memory markdown layout 2026-07-23 23:01:29 +02:00
marcopan 3c75edfd47 docs: explain early-stage disambiguation
Publish documentation / deploy (push) Failing after 4m23s
2026-07-23 19:55:17 +02:00
marcopan 0b11121bcc docs: include canonical harness skill text 2026-07-23 19:50:24 +02:00
marcopan 5d34447975 docs: document application skill contract 2026-07-23 19:44:07 +02:00
marcopan 17bca2b2cb docs: publish MkDocs site on gh-pages 2026-07-23 19:28:09 +02:00
marcopan 724c446087 fix: expand session summary prose with panel 2026-07-23 16:06:22 +02:00
marcopan 2e1bb621a0 fix: exclude schema tables from session memories 2026-07-23 15:54:27 +02:00
marcopan 9b5fca59e8 fix: expand final SQL to session panel width 2026-07-23 15:44:16 +02:00
marcopan 4108b99405 docs: record session summary deployment 2026-07-23 15:37:48 +02:00
marcopan 0db85e4e4e feat: redesign persisted session summaries 2026-07-23 15:35:31 +02:00
marcopan 12c7a976d7 docs: specify session summary redesign 2026-07-23 12:34:30 +02:00
marcopan 694b7dd21f fix: harden reviewer workflow and memory handling 2026-07-23 12:34:23 +02:00
marcopan 00761ae2ca fix: enforce one Pi runtime per user 2026-07-21 16:13:17 +02:00
marcopan a040c083cc fix: reap finalized runtimes before session start 2026-07-21 16:04:08 +02:00
marcopan 019f6b282e fix: release finalized Pi runtimes 2026-07-21 15:39:34 +02:00
marcopan 4c9424f1e3 feat: add button press feedback 2026-07-21 15:10:08 +02:00
marcopan 801f847ec4 fix: use Pi user auth and handle startup failures 2026-07-21 14:01:47 +02:00
marcopan 2ff63d371f feat: harden workflow gates and expose token usage 2026-07-21 12:14:26 +02:00
marcopan 8aa1676811 Expose PSD frontend locally 2026-07-20 20:27:37 +02:00
marcopan ad6057f0dd Fix PSD frontend network wiring 2026-07-20 20:26:13 +02:00
marcopan 32efd647c4 Merge session resume and container configuration fix 2026-07-20 20:23:13 +02:00
marcopan 0cf09777f2 Fix session resume and PSD container configuration 2026-07-20 20:22:47 +02:00
marcopan ec9b12dff4 fix(harness): recover schema table name typos 2026-07-20 17:53:24 +02:00
marcopanandClaude Opus 4.6 3b52c8c08c feat: DWH connectivity probe at startup — modal alert within 5s if unreachable
Backend: GET /health/dwh (unauthenticated) calls tht db ping with a 5s
timeout. Frontend: checkDwhHealth() races a 5s timer against the fetch;
on failure a non-dismissable Dialog with Retry appears immediately.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-20 13:59:11 +02:00
marcopanandClaude Fable 5 83942c0b5c feat(harness): F6/F7 close on their last approval — no echo phase gate
Where completeness is machine-detectable, the reviewer's last substantive
approval now closes the phase itself (same pattern as F3/F4/F8):

- F6: approving the LAST CTE of the plan (kind:"cte_result" with next_cte
  now empty) advances the phase; a non-final CTE keeps the phase open and
  names the next one.
- F7: kind:"sql" records sql_approved — which IS F7's only advance
  prerequisite — and advances immediately.

Two reviewer interactions per session removed, both pure echoes. The
summary phase gate remains only where completeness is a human judgment
(F1, F2 with recorded memories, F5). SKILL.md states the rule and the
five self-closing gates; L1 tests cover last/non-last CTE and sql close.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 02:23:45 +02:00
marcopanandClaude Fable 5 c4951e2aa5 chore: hygiene pass — ruff clean, docs storage-model truth, replay /me, failSession log
Audit findings 6.1-6.4 + the audit's remediation plan itself
(docs/superpowers/plans/2026-07-20-full-audit-remediation-plan.md).

- ruff: 34 → 0 (unused imports/f-strings auto-fixed; E702 semicolon lines
  split in test files; one unused local dropped). Suite still 819 green.
- CLAUDE.md + PROJECT_STATE.md no longer claim "no database / settings in
  settings.json": the harness selects filesystem OR PostgreSQL session
  storage (repository.py, server mode), and settings flow through harness
  preferences with the JSON file as fallback only.
- tools/replay: stub /me (SPA boot was parsing the SPA's own HTML as JSON)
  and /runtime/prewarm.
- failSession best-effort persistence now logs its failure server-side
  instead of vanishing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 01:55:41 +02:00
marcopanandClaude Fable 5 1ab0015460 fix(harness): state-integrity pass — reopen order, atomic decision batch, bash anti-bypass
Audit findings 5.1-5.3.

5.1 `phase reopen` now appends `phase_reopened` BEFORE the artifact
teardown: a crash between the two used to leave later-phase artifacts
deleted with the ledger still at the old phase (resume entered a phase
missing its artifacts). The inverse half-state — reopened with stale later
artifacts — is benign. Order locked by tests/test_phase_reopen_order.py.

5.2 New `tht decision add-batch --doc -`: N substantive decisions in ONE
atomic ledger write (meta types and cte_approved stay on `decision add`;
strictest min-phase enforced). reviewer_schema_linking now builds the
complete curation set and persists it with a single add-batch call — a
mid-loop failure can no longer leave the audit ledger half-written, and a
retry cannot duplicate the first K decisions.

5.3 The anti-bypass hook now also blocks BASH mutations of protected
state (`echo >> review_decisions.jsonl`, `sed -i` on the manifest,
`cat > tht-gate.js`, python open('w'), mv/rm/tee/…): FORBIDDEN only
covered tht subcommands and the write/edit hook only covered pi's own
tools. Read-only access (cat/grep/tail/ls) stays allowed.

Also: knownDecisionTypes is defensive — a workflow meta declaring NO
emits at all (older tht, minimal stubs) skips pre-validation instead of
rejecting every substantive type; with emits present, unknown types are
still rejected before the widget (new L1 test).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 01:51:38 +02:00
marcopanandClaude Fable 5 f772ef9dca fix(backend): robustness pass — spawn leak, timeouts, workspace fail-loud, 409 order, respond guard
Audit findings 4.1-4.6.

- spawnFor: a rejected configure/start no longer leaks a registered runtime
  with a live Pi child (identity-checked teardown + rethrow); every later
  start used to hit "session runtime already active".
- ThtRunner.run: default 60s timeout on every tht child (SIGKILL backstop),
  120s for DWH-touching calls (sql preview/export, search pack); a dropped
  VPN mid-call no longer wedges the HTTP request forever.
- configArg: a NAMED workspace whose yaml is missing now throws instead of
  silently falling back to the default config (operations were silently
  targeting the wrong workspace).
- resume: the finalized/archived 409 is evaluated BEFORE the alreadyActive
  fast-path — the manifest is the truth even with a lingering runtime.
- ollamaEnsure: exit-0 with non-JSON stdout is a failed check, not ok:true.
- SessionBridge.respond: only the response matching the pending descriptor
  is forwarded to Pi; stale/duplicate submissions return 409 instead of
  being sent with the current gate's RPC id.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 01:37:20 +02:00
marcopanandClaude Fable 5 2958b32fd5 fix(backend): generation-aware SSE event ids — stale cursors can no longer eat events
Audit finding 3.1 (high, 3/3 reviewer consensus). Event ids restart at 1
when the backend restarts; a browser auto-reconnect carrying the old
numeric Last-Event-ID was honored whenever the new process had already
emitted that many events, silently suppressing fresh events (same ids,
different content). The previous guard only caught cursor > lastId.

Wire ids are now "<generation>:<seq>" (generation = per-hub instance
token; seq = the existing per-session monotonic counter). The hub parses
raw header/query candidates itself: other-generation and legacy bare-
number cursors are stale → replay from the beginning; same-generation
cursors keep the newest-valid-wins behavior. EventSource treats ids as
opaque, so no frontend change.

Finding 3.2 (eviction) resolved by NOT evicting: close keeps the seq
counter on purpose (sessions reopen; monotonicity is what makes old
cursors detectable) — documented at the call site; buffers are emptied by
clear() and ring-bounded at 200.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 01:32:47 +02:00
marcopanandClaude Fable 5 3e072fe652 fix: realign workflow advance semantics and phase labels with workflow.yaml
Audit findings 2.1 + 2.2 (high).

2.1 WorkflowBar's static phase list was fiction from F2 on (F2 "Schema
linking" vs memoria, F4 "SQL plan" vs schema_linking, …): every live
session showed the wrong phase name. Both maps now mirror
harness/workflow.yaml (F1 chiarimento … F8 datamart).

2.2 forceAdvance (6ee5bda) let reviewer_decide advance:true bypass the
phase gate on ANY phase, contradicting SKILL.md's "auto-advance only
empty F2 / skipped F6". reviewer_decide is back on advanceIfReady (exit-6
no-op) and tells the model to close via reviewer_confirm; forceAdvance
stays only where selection IS the approval by design: reviewer_schema_linking
(F4) and the F8 promotion close path. SKILL.md now names the three
self-closing gates (F3 rewrite_question, F4 schema-linking advance:true,
F8 memory_promote) so gate and skill state one contract.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 01:28:18 +02:00
marcopanandClaude Fable 5 075883370e fix(harness): close the Pi-crash class — top-level try/catch on ALL gate tools
Audit finding 1.1 (high): reviewer_memory_promote, rewrite_question,
write_schema_linking, write_cte_sql and write_final_sql still ran execute
without a top-level catch — the same unhandled-rejection class that killed
Pi in reviewer_schema_linking (fixed in 87cb806 for the four reviewer_*
tools). All 9 registered tools now share the pattern: any uncaught throw
becomes a textResult the model can react to.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-20 01:26:27 +02:00
marcopanandClaude Fable 5 803b98de36 fix(frontend): center gate dialogs on the app area, not the browser viewport
Embedded in the Omics Portal the SPA sits to the right of the portal sidebar,
but dialogs used `fixed left-1/2` — centered on the whole browser window, so
gates rendered visibly off-center (overlapping the sidebar).

- AppShell publishes the app area's horizontal geometry (--app-area-left/width)
  as CSS vars on <html> via ResizeObserver + window resize.
- DialogContent centers on those vars (falls back to the viewport when the app
  is standalone, where the two coincide).
- ArtifactGateWidget: narrower — cap at 52rem instead of 70vw.
- tools/replay: resume now returns {id, alreadyActive} JSON — the post-f979ada
  frontend reads result.alreadyActive and a bare 204 broke replay resume.

Verified on the replay harness: with a simulated 300px portal sidebar the gate
centers on the app area (center 790 = 300 + 980/2); standalone unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-19 17:08:20 +02:00
marcopanandClaude Opus 4.6 87cb806dfc fix(harness): prevent Pi crash on unhandled throw in reviewer tool execute
The last live session crashed during reviewer_schema_linking: an uncaught
exception (likely from execFileSync in currentPhase/phaseMeta or from
cat.columns being undefined) rejected the async execute() Promise. Pi does
not catch rejected tool Promises — Node.js treats them as unhandled
rejections and kills the process.

Fix:
- Wrap all four reviewer tool execute bodies (select/decide/confirm/
  schema_linking) in a top-level try/catch → returns a textResult on any
  unexpected error instead of crashing Pi.
- reviewer_schema_linking: defensively re-parse `tables` if still a string
  (belt-and-suspenders over prepareArguments), guard cat.columns before .map().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-19 14:33:42 +02:00
marcopanandClaude Opus 4.6 6ee5bda7f0 feat: gate decision-type validation, force-advance, and frontend fixes
Gate (tht-gate.js):
- Pre-validate decision types against workflow.yaml before showing reviewer widget
- Reject decisions emitted by later phases (min-phase check)
- Copy top-level `kind` into artifact when model forgets it (prevents loop)
- Force-advance on reviewer_decide/schema_linking when advance:true — skip
  redundant reviewer_confirm gate

Backend:
- Emit agent_end on clean Pi exit (code 0 + bridge idle) instead of marking failed

Frontend:
- Strip <think> tags from transcript and activity panel
- Fix mermaid render with offscreen container + cleanup
- Graceful mermaid error: show source code instead of red error, fall back to table

Workflow:
- F2 now emits table_promoted and table_excluded (early schema linking decisions)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-07-19 14:14:44 +02:00
marcopanandClaude Opus 4.8 9fbca06f7c feat(harness): make tht schema columns glob/pattern-aware
At F4 (schema_linking) a model asking for a whole table family, e.g.
`fact_sost_impianto_*`, used to hit a bare "tabella non nel catalogo" and stall
without recovering. Now a pattern (containing * ? [) resolves to every matching
catalog table and returns their columns (JSON becomes an array of per-table
objects); exact names keep the original single-object contract. A non-glob miss
also suggests sibling tables sharing the leading segment, to aid recovery.

Verified live: `fact_sost_impianto_*` resolves the 20-table family on the psd
catalog. Harness suite green (811 passed).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 13:24:20 +02:00
marcopanandClaude Opus 4.8 ccfa3a18bf feat(frontend): pulse the stop dot while the harness is working
Give live feedback that something is happening the moment a session starts
processing: the red stop dot in the composer beats with a soft heartbeat + halo
while the harness is actively working (running — not waiting at a gate or
finalized), and settles the instant the model hands control back to the reviewer
or the session ends. Ring/scale only (no layout properties animated); honors
prefers-reduced-motion.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 12:33:25 +02:00
marcopanandClaude Opus 4.8 3453f3ae23 feat: pre-check DWH reachability before creating a session (local dev only)
New session now refuses to spawn a Pi runtime that would only die in bootstrap
retrieval when the DWH/vector host is unreachable (e.g. a dropped VPN). Before
`session new`, POST /sessions probes the DWH via `tht db ping`; if it is down it
returns 503 {code:"dwh_unreachable"} with a clear message and creates nothing.

- Gated behind the THT_DWH_PRECHECK flag (default off), enabled only by the local
  dev launcher (run-stack.sh) — containers/CI never pay the probe, and existing
  tests that don't set it are unaffected.
- ThtRunner.dbPing() runs `tht db ping` with a 10s timeout (run() gains an optional
  timeout that SIGKILLs a hung child).
- Frontend: apiFetch throws a typed ApiError (status + parsed payload); the new-
  session composer shows the specific alert on `dwh_unreachable` instead of the
  generic retry hint, keeping the question for retry.

Verified live on an isolated backend (precheck on + broken DWH host → 503
dwh_unreachable, no session created) and via unit tests (backend 228, frontend 308).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 12:08:47 +02:00
marcopanandClaude Opus 4.8 84da3b149b fix(backend): log the real bootstrap failure cause server-side
Session bootstrap swallowed configure/retrieval errors and surfaced only the
generic BOOTSTRAP_FAILURE_MESSAGE, so an operator could not tell why a session
"didn't start" — e.g. `tht search pack` failing because the DWH/vector host is
unresolvable behind a dropped VPN. Log the underlying error to the backend
console; the client-facing message stays generic.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-18 11:48:05 +02:00
marcopanandClaude Opus 4.8 f979ada5e7 feat(frontend): open a live session straight to its pending gate
Opening an in-progress session that has a live Pi runtime now reconnects to
its pending gate instead of the empty landing screen that read as "stopped".
Cold/completed sessions keep the read-only documents panel with its explicit
Resume, so a mere click never spawns a runtime. Backend GET /sessions now
reports a per-session `active` flag (live runtime bound) to drive this.

Also:
- "New session" now closes any open session detail panel (left box).
- The model-activity separator can be dragged to a full 50/50 split
  (was capped at 576px); central-min still guards narrow viewports.

Test fixes uncovered along the way:
- Node 25 ships an experimental global localStorage that shadows jsdom's and
  lacks clear(), failing every jsdom test at setup; install a spec-compliant
  in-memory Storage (feature-detected, inert on CI/LTS).
- Fix 4 pre-existing session-mgmt tests that used an ambiguous getByText for a
  session shown in both nav and header; target the nav item by test id.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 22:56:12 +02:00
marcopan 6274bf2da0 Merge branch 'feat/per-provider-model-credentials' 2026-07-17 19:31:06 +02:00
marcopanandClaude Opus 4.8 c5fd03ed84 feat(backend): let pi self-authenticate providers from its own auth store
The backend injects a single managed model key (THT_MODEL_API_KEY[_FILE]) as
the selected provider's env var, but that key belongs to one provider — so
selecting a second cloud provider (e.g. DeepSeek while the managed key is zai's)
forced the wrong key onto it and failed auth. This is why the model could not be
switched to DeepSeek.

When the selected provider is present in pi's own auth store
(~/.pi/agent/auth.json), skip injection and let pi resolve that provider's key
itself. Deployments without an auth store (containers) yield an empty set, so the
managed-key injection stays authoritative and fail-fast there. authProviders is
injectable into PiProcessManager for deterministic tests.

Verified live: GLM 5.2, DeepSeek V4 Flash, and aritmolab Qwen3.6 all operate through the ThothII model selector.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 18:26:18 +02:00
marcopanandClaude Opus 4.8 c252c95c5f fix(frontend): pin the question as a stable first line during processing
The central column was top-anchored with no fixed header, so the user's
question was never shown while the model worked and the activity/gate
content drifted upward. Render the active session's question as a sticky,
always-present first row of the central area so it stays evident and
anchored at the top of the form.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-17 16:30:18 +02:00
marcopan 7628eaa579 fix(docker): run real questions through trusted Pi gate 2026-07-12 19:20:10 +02:00
marcopan c446d40e1f docs: define local and server Docker deployment 2026-07-12 16:27:05 +02:00
marcopan 08f0029793 fix: finalize session after memory promotion 2026-07-12 14:26:48 +02:00
marcopan f67d2c97d8 fix(security): reject invalid optional bundle values 2026-07-12 11:53:15 +02:00
marcopan 449a333365 fix(security): validate bundle and clean runtime secrets 2026-07-12 11:52:02 +02:00
marcopan 385646d574 docs: complete Docker context settings 2026-07-12 11:50:12 +02:00
marcopan 10465917a5 test(compose): validate bundle deployment contract 2026-07-12 11:48:43 +02:00
marcopan 07967bf589 docs: document one-command Docker installation 2026-07-12 11:44:00 +02:00
marcopan 2ab91b7c0d docs(sdd): record secret scrub review fixes 2026-07-12 11:34:52 +02:00
marcopan 8518a73685 fix(security): scrub raw deployment secret values 2026-07-12 11:34:32 +02:00
marcopan d500563963 fix(security): scrub deployment secrets from Pi child 2026-07-12 11:33:13 +02:00
marcopan 70a19f290d feat(compose): use one secret bundle for local services 2026-07-12 11:30:43 +02:00
marcopan 32a2b71687 build(compose): make root startup the default 2026-07-12 11:14:36 +02:00
marcopan 3807c65a41 test(config): cover secret bundle inode races 2026-07-12 11:09:09 +02:00
marcopan 390cfd24b5 fix(config): accept raw environment secret lookup 2026-07-12 11:07:22 +02:00
marcopan 5fe74612fb feat(config): load one validated secret bundle 2026-07-12 11:06:19 +02:00
marcopan b539303002 docs: plan simplified Docker configuration implementation 2026-07-12 10:59:31 +02:00
marcopan a6b195b8ae docs: design simplified Docker configuration 2026-07-12 10:54:42 +02:00
marcopan 0b65135153 docs: explain loading deploy environment 2026-07-12 10:46:50 +02:00
marcopan 7f8346ff58 docs: keep installation configuration inside ThothII 2026-07-12 10:21:53 +02:00
marcopan e81a250b47 docs: define secret paths before file creation 2026-07-12 10:13:31 +02:00
marcopan 5e345c5567 docs: clarify host and container secret paths 2026-07-12 10:07:39 +02:00
marcopan 7e829a8414 docs: clarify Docker secrets and workspace configuration 2026-07-12 10:04:41 +02:00
marcopan b07f422bb3 docs: add Docker installation guide for four contexts 2026-07-12 09:49:09 +02:00
marcopan 2302286ea1 fix(backend): reject compound provider credentials 2026-07-12 08:10:47 +02:00
marcopan f064daef09 fix(backend): harden provider credential isolation 2026-07-12 08:05:51 +02:00
marcopan 32e73d66b5 docs(preprocess): restore local startup commands 2026-07-12 07:57:17 +02:00
marcopan 72bc50ab2e fix(preprocess): enforce local vector startup chain 2026-07-12 07:54:09 +02:00
marcopan e40a9d9a56 fix(backend): inject provider credentials from file 2026-07-12 07:53:22 +02:00
marcopan ee92ef45ab fix(vector): track packaged migrations in restore smoke 2026-07-12 07:49:58 +02:00
marcopan 09d50ac9bb fix(dwh): release snapshots before job startup 2026-07-12 07:34:56 +02:00
marcopan 4aae6c433d fix(dwh): bind snapshots to validated bytes 2026-07-12 07:31:46 +02:00
marcopan 2f6daaaea6 fix(dwh): anchor generation operations to lease fd 2026-07-12 07:26:10 +02:00
marcopan 24e61d5713 fix(preprocess): retain DWH root fd through lease 2026-07-12 07:12:28 +02:00
marcopan 4cf0adbdd2 fix(preprocess): initialize fresh DWH writers safely 2026-07-12 07:07:31 +02:00
marcopan 22e806a41b fix(preprocess): restrict DWH root claims to writers 2026-07-12 07:00:51 +02:00
marcopan c4dd6c8900 fix(preprocess): claim DWH roots atomically 2026-07-12 06:51:46 +02:00
marcopan 8110793f61 fix(evidence): make result summaries safe 2026-07-12 06:50:23 +02:00
marcopan b43075289e fix(preprocess): bind DWH artifacts to workspace 2026-07-12 06:46:06 +02:00
marcopan 0a2421c513 fix(evidence): bound reports and fail failed jobs 2026-07-12 06:45:19 +02:00
marcopan 03ee3ffda8 fix(evidence): validate active corpus artifacts 2026-07-12 06:35:27 +02:00
marcopan 5cc023f390 fix(evidence): harden unchanged job snapshot 2026-07-12 06:29:31 +02:00
marcopan 7d41c4cefc fix(preprocess): verify canonical generations and cleanup 2026-07-12 06:18:18 +02:00
marcopan 4a29086fe4 fix(preprocess): anchor DWH retention validation 2026-07-12 06:15:27 +02:00