Skip to content

fibengine-next: repo structure, Optuna cleanup (docs/artifacts), machine labeling#7

Merged
JohnCCarter merged 24 commits into
mainfrom
feature/fibengine-next
May 30, 2026
Merged

fibengine-next: repo structure, Optuna cleanup (docs/artifacts), machine labeling#7
JohnCCarter merged 24 commits into
mainfrom
feature/fibengine-next

Conversation

@JohnCCarter
Copy link
Copy Markdown
Owner

@JohnCCarter JohnCCarter commented May 29, 2026

Summary

  • Organize repo (policy, tracks, archive, label paths, CI/pre-commit, test layout).
  • Remove label-driven Optuna tuning (conflicts with labels-as-reference philosophy).
  • Sync with main (worklist, premortem mitigations, stability gate, coverage).
  • Add machine-labeling (source=human|machine): candidates for review only; excluded from recall/agreement and 20–30 target.

Test plan

  • uv run pytest — 85 passed, ~76% coverage
  • uv run ruff check src tests
  • Review PR diff vs main before merge

Summary by Sourcery

Inför explicita källor för mänskliga vs. maskinella etiketter, lägg till arbetsflöde för maskinmärkning och ta bort Optuna-baserade tuning‑artefakter till förmån för principstyrda varianter.

New Features:

  • Lägg till fältet source på swing‑etiketter för att skilja mänsklig ground truth från maskin‑genererade kandidater.
  • Introducera labeling.autolabel‑CLI för att generera preliminära maskinetiketter för angivna symbol/tidsfönster‑galler.
  • Utöka worklist‑rapporteringen så att maskinkandidater visas för granskning tillsammans med kvarvarande omärkta kombinationer.

Enhancements:

  • Säkerställ att utvärdering (pivot_recall) och experimentens överensstämmelsemetrik endast använder mänskliga etiketter som ground truth och uttryckligen exkluderar maskin‑genererade etiketter.
  • Uppdatera logiken för etikettlistning och täckning så att endast mänskliga etiketter räknas mot märkningsmålen medan maskinetiketter spåras separat för granskning.
  • Förfina repo‑policy, spår, backtest‑plan och varianter‑dokumentation för att ta bort Optuna/tuning‑flöden och betona principmotiverade konfigurationsvarianter.

Documentation:

  • Dokumentera semantiken kring maskinmärkning, inklusive source‑värden och integritetsregler, i README och etikettdata‑dokumentation.
  • Revidera backtest‑ och spår‑dokumentation för att ta bort Optuna‑fasen och förtydliga att promotion‑gating baseras på stabilitet och recall snarare än etikettoptimerade vikter.
  • Förtydliga rollen för config/variants som principstyrda profiler och ta bort referenser till Optuna‑artefakter och tuning‑kataloger.

Tests:

  • Lägg till tester för hantering av maskinetiketter i etikettlagring, worklist‑täckning samt experiment- och pivot‑recall‑utvärderingsvägar.
  • Lägg till tester för den nya autolabel‑modulen, inklusive konvertering från swing till etikett och skydd mot överskrivning av mänskliga etiketter.

Chores:

  • Arkivera och dokumentera borttagningen av Optuna‑relaterad kod, varianter och resultatfiler som en del av repo‑städningen.
Original summary in English

Summary by Sourcery

Introduce explicit human vs machine label sources, add machine labeling workflow, and remove Optuna-based tuning artifacts in favor of principle-driven variants.

New Features:

  • Add source field on swing labels to distinguish human ground truth from machine-generated candidates.
  • Introduce labeling.autolabel CLI to generate provisional machine labels for specified symbol/timeframe grids.
  • Extend worklist reporting to surface machine candidates for review alongside remaining unlabeled combinations.

Enhancements:

  • Ensure evaluation (pivot_recall) and experiment agreement metrics use only human labels as ground truth, explicitly excluding machine-generated labels.
  • Update label listing and coverage logic so only human labels count toward labeling targets while machine labels are tracked separately for review.
  • Refine repository policy, tracks, backtest plan, and variants docs to remove Optuna/tuning flows and emphasize principle-motivated configuration variants.

Documentation:

  • Document machine labeling semantics, including source values and integrity rules, in README and label data docs.
  • Revise backtest and track documentation to drop Optuna phase and clarify promotion gating based on stability and recall rather than label-optimized weights.
  • Clarify the role of config/variants as principle-driven profiles and remove references to Optuna artifacts and tuning directories.

Tests:

  • Add tests for machine-label handling in label storage, worklist coverage, experiment and pivot recall evaluation paths.
  • Add tests for the new autolabel module, including swing-to-label conversion and protection against overwriting human labels.

Chores:

  • Archive and document the removal of Optuna-related code, variants, and result files as part of repo cleanup.

JohnCCarter and others added 22 commits May 28, 2026 09:29
Move modules under tests/{core,backtest,data,...} and remove duplicate root test files.

Co-authored-by: Cursor <cursoragent@cursor.com>
Ruff and pytest run on commit (pytest skippable during partial staging) and on GitHub Actions.

Co-authored-by: Cursor <cursoragent@cursor.com>
Move backtest roadmap to docs/ and document Research/Validate/Promotion tracks.

Co-authored-by: Cursor <cursoragent@cursor.com>
Move jsonl ledgers to experiments/results/, label batches under batches/, and legacy assets to archive/.

Co-authored-by: Cursor <cursoragent@cursor.com>
Add hierarchical label store under data/labels/binance/ and remove flat legacy JSON path.

Co-authored-by: Cursor <cursoragent@cursor.com>
Consolidate config, models, scoring, and related helpers under src/fibengine/core/ and update imports.

Co-authored-by: Cursor <cursoragent@cursor.com>
Hierarchical label paths, multi-timeframe queue, and batch manifest workflow.

Co-authored-by: Cursor <cursoragent@cursor.com>
Tune scoring weights against labels; store results under experiments/results/ and config/variants/.

Co-authored-by: Cursor <cursoragent@cursor.com>
Switch to fractal pivots and retune scoring weights from manual review.

Co-authored-by: Cursor <cursoragent@cursor.com>
Document quality gate links, ignore tmp/, and disable Cursor Pyright for this workspace.

Co-authored-by: Cursor <cursoragent@cursor.com>
Optuna optimized weights toward agreement with manual label drawings,
which conflicts with the project principle that labels are reference,
not an objective. Remove the tuning package, optuna dependency, active
variants, and result ledgers; update docs and tracks accordingly.

Experiment-run history in leaderboard.jsonl is kept as an audit trail.

Co-authored-by: Cursor <cursoragent@cursor.com>
…gate)

Co-authored-by: Cursor <cursoragent@cursor.com>
… facit

Add an opt-in machine-labeling flow that lowers the cost of filling label
gaps without corrupting the golden set:

- SwingLabel.source ('human' default / 'machine'); list_labels(source=...)
  filter. Existing labels stay human.
- Integrity guards: pivot_recall and experiment evaluate human labels only
  (machine excluded, so the engine is never scored against its own output);
  worklist counts human toward the 20-30 target and lists machine candidates
  separately for review.
- labeling.autolabel builds a candidate from the engine's selection, tags it
  source=machine, and never overwrites a human label.
- Promotion path: review a candidate in labeling.tool and save -> becomes
  source=human.
- Docs (README, data/labels) + reflection. 85 tests green, lint clean.
Copilot AI review requested due to automatic review settings May 29, 2026 06:40
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented May 29, 2026

Granskarens guide

Introducera maskin-genererade swing-etiketter taggade med ett nytt source-fält, samtidigt som du säkerställer att de utesluts från utvärderingsmetrik och måltäckt, samt ta bort all Optuna-baserad autotuning-kod/-dokumentation till förmån för principstyrda, manuella konfigurationsvarianter och en förtydligad repo-policy/track-struktur.

Flödesschema för maskinmärkning och utvärdering enbart med mänskliga etiketter

flowchart TD
  Start([Start])

  subgraph Machine_label_generation
    AutolabelMain["Run fibengine.labeling.autolabel.main"]
    RunAutolabel["run_autolabel(settings, exchange, symbols, timeframes, overwrite)"]
    AutolabelOne["autolabel_one(settings, exchange, symbol, timeframe, overwrite)"]
    FindLabel["find_label(exchange, symbol, timeframe)"]
    HasHuman{existing.source == human?}
    BuildLabel["label_from_swing(swing, exchange, symbol, timeframe)\nsource = machine"]
    SaveLabel["save_label(label)"]
  end

  subgraph Evaluation_and_worklist
    PivotRecall["run_pivot_recall(settings)"]
    Experiment["run_experiment(settings)"]
    Worklist["coverage_report(exchange, symbols, timeframes, target)"]
    ListHuman["list_labels(source = human)"]
    ListMachine["list_labels(source = machine)"]
  end

  Start --> AutolabelMain --> RunAutolabel --> AutolabelOne --> FindLabel --> HasHuman
  HasHuman -- Yes --> AutolabelOne
  HasHuman -- No --> BuildLabel --> SaveLabel --> RunAutolabel

  Start --> PivotRecall --> ListHuman
  Start --> Experiment --> ListHuman

  Start --> Worklist
  Worklist --> ListHuman
  Worklist --> ListMachine
Loading

Ändringar på filnivå

Ändring Detaljer Filer
Lägg till explicit spårning av etiketternas källa och maskinetikett-semantik i märkning, utvärdering och experimentflöden.
  • Utöka SwingLabel med ett source-fält som standard är human och säkerställ att JSON round-tripping bevarar det.
  • Uppdatera list_labels så att etiketter valfritt kan filtreras på source och anpassa befintliga tester till den nya signaturen.
  • Justera täckningsberäkningen i labeling worklist så att endast mänskliga etiketter räknas mot mål, exponera maskinetiketter som granskingskandidater och uppdatera formaterade rapporter i enlighet med detta.
  • Dokumentera semantiken för etikettfältet source och beteendet för maskinetiketter i data- och README-dokumentation.
src/fibengine/labeling/store.py
tests/labeling/test_label_store.py
src/fibengine/labeling/worklist.py
tests/labeling/test_worklist.py
data/labels/README.md
README.md
Introducera en maskinmärkningspipeline som genererar kandidatetiketter från motorn samtidigt som mänsklig ground truth skyddas.
  • Skapa modulen labeling.autolabel för att generera SwingLabels med source="machine" från engine-valda swings, med skydd mot att skriva över mänskliga etiketter.
  • Implementera ett CLI-gränssnitt för autolabeling med val av symbol/tidsupplösning och kontroller för overwrite.
  • Lägg till tester som validerar etikettgenerering från swings och garanterar att mänskliga etiketter aldrig skrivs över av autolabeling.
src/fibengine/labeling/autolabel.py
tests/labeling/test_autolabel.py
Säkerställ att utvärderings- och experimentmetrik endast använder mänskliga etiketter som ground truth och loggar uteslutna maskinetiketter explicit.
  • Uppdatera pivot recall-runnern så att den endast hämtar mänskliga etiketter, loggar antalet överhoppade maskinetiketter och lägger till ett test som säkerställer att maskinetiketter utesluts från recall.
  • Ändra experiment-runnern så att överensstämmelse endast mäts mot mänskliga etiketter och logga uteslutningar av maskinetiketter; justera tester för att hantera den nya list_labels-API:n och det nya beteendet.
src/fibengine/evaluation/pivot_recall.py
tests/evaluation/test_pivot_recall.py
src/fibengine/experiment.py
tests/test_experiment.py
Ta bort Optuna/autotuning från kodbasen och anpassa repo-dokumentation och policys till en principstyrd filosofi med manuella varianter.
  • Ta bort alla referenser till Optuna/tuning-körningar från planer, policys, experimentdokumentation och resultatscheman.
  • Positionera om config/variants som principstyrda forskningsprofiler i stället för autotunade varianter, och uppdatera README och INDEX i enlighet med detta.
  • Förtydliga ansvar och promotion-gates för olika tracks för att förbjuda optimering mot etikettöverensstämmelse och betona stabilitet + recall-validering.
  • Registrera premortem-reflektioner som dokumenterar borttagningen av Optuna och motiveringen till att maskinmärkning endast används som kandidater.
  • Uppdatera arkivindexet för att dokumentera artefakter relaterade till borttagningen av Optuna.
docs/FIB_BACKTEST_PLAN.md
README.md
config/variants/README.md
config/variants/INDEX.md
REPO_POLICY.md
docs/TRACKS.md
experiments/README.md
experiments/results/README.md
experiments/results/leaderboard.jsonl
archive/INDEX.md
premortem/reflections/2026-05-28-remove-optuna.md
premortem/reflections/2026-05-29-machine-labeling.md

Tips och kommandon

Interagera med Sourcery

  • Trigga en ny granskning: Kommentera @sourcery-ai review på pull requesten.
  • Fortsätt diskussioner: Svara direkt på Sourcerys granskningskommentarer.
  • Generera ett GitHub-issue från en granskningskommentar: Be Sourcery skapa ett
    issue från en granskningskommentar genom att svara på den. Du kan också svara på en
    granskningskommentar med @sourcery-ai issue för att skapa ett issue från den.
  • Generera en pull request-titel: Skriv @sourcery-ai var som helst i
    pull request-titeln för att generera en titel när som helst. Du kan också kommentera
    @sourcery-ai title på pull requesten för att (åter)generera titeln när som helst.
  • Generera en pull request-sammanfattning: Skriv @sourcery-ai summary var som helst i
    pull request-beskrivningen för att generera en PR-sammanfattning när som helst exakt där du
    vill ha den. Du kan också kommentera @sourcery-ai summary på pull requesten för att
    (åter)generera sammanfattningen när som helst.
  • Generera granskarens guide: Kommentera @sourcery-ai guide på pull
    requesten för att (åter)generera granskarens guide när som helst.
  • Lös alla Sourcery-kommentarer: Kommentera @sourcery-ai resolve på pull
    requesten för att lösa alla Sourcery-kommentarer. Användbart om du redan
    hanterat alla kommentarer och inte vill se dem längre.
  • Avsluta alla Sourcery-granskningar: Kommentera @sourcery-ai dismiss på pull
    requesten för att avfärda alla befintliga Sourcery-granskningar. Särskilt användbart om du
    vill börja om med en ny granskning – glöm inte att kommentera
    @sourcery-ai review för att trigga en ny granskning!

Anpassa din upplevelse

Gå till din dashboard för att:

  • Aktivera eller inaktivera granskningsfunktioner såsom Sourcery-genererad pull request-
    sammanfattning, granskarens guide och andra.
  • Ändra granskningsspråk.
  • Lägga till, ta bort eller redigera anpassade granskningsinstruktioner.
  • Justera andra granskningsinställningar.

Få hjälp

Original review guide in English

Reviewer's Guide

Introduce machine-generated swing labels tagged with a new source field while ensuring they are excluded from evaluation metrics and target coverage, and remove all Optuna-based auto-tuning code/docs in favor of principled, manual config variants and a clarified repo policy/track structure.

Flow diagram for machine labeling and human-only evaluation

flowchart TD
  Start([Start])

  subgraph Machine_label_generation
    AutolabelMain["Run fibengine.labeling.autolabel.main"]
    RunAutolabel["run_autolabel(settings, exchange, symbols, timeframes, overwrite)"]
    AutolabelOne["autolabel_one(settings, exchange, symbol, timeframe, overwrite)"]
    FindLabel["find_label(exchange, symbol, timeframe)"]
    HasHuman{existing.source == human?}
    BuildLabel["label_from_swing(swing, exchange, symbol, timeframe)\nsource = machine"]
    SaveLabel["save_label(label)"]
  end

  subgraph Evaluation_and_worklist
    PivotRecall["run_pivot_recall(settings)"]
    Experiment["run_experiment(settings)"]
    Worklist["coverage_report(exchange, symbols, timeframes, target)"]
    ListHuman["list_labels(source = human)"]
    ListMachine["list_labels(source = machine)"]
  end

  Start --> AutolabelMain --> RunAutolabel --> AutolabelOne --> FindLabel --> HasHuman
  HasHuman -- Yes --> AutolabelOne
  HasHuman -- No --> BuildLabel --> SaveLabel --> RunAutolabel

  Start --> PivotRecall --> ListHuman
  Start --> Experiment --> ListHuman

  Start --> Worklist
  Worklist --> ListHuman
  Worklist --> ListMachine
Loading

File-Level Changes

Change Details Files
Add explicit label source tracking and machine-label semantics across labeling, evaluation, and experiment flows.
  • Extend SwingLabel with a source field defaulting to human and ensure JSON round-tripping preserves it.
  • Update list_labels to optionally filter labels by source and adapt existing tests to use the new signature.
  • Adjust labeling worklist coverage to count only human labels toward targets, surface machine labels as review candidates, and update formatted reports accordingly.
  • Document label source semantics and machine-label behavior in data and README documentation.
src/fibengine/labeling/store.py
tests/labeling/test_label_store.py
src/fibengine/labeling/worklist.py
tests/labeling/test_worklist.py
data/labels/README.md
README.md
Introduce a machine-labeling pipeline that generates candidate labels from the engine while protecting human ground truth.
  • Create labeling.autolabel module to generate source="machine" SwingLabels from engine-selected swings with safeguards against overwriting human labels.
  • Implement CLI interface for autolabeling with symbol/timeframe selection and overwrite controls.
  • Add tests that validate label creation from swings and guarantee human labels are never overwritten by autolabeling.
src/fibengine/labeling/autolabel.py
tests/labeling/test_autolabel.py
Ensure evaluation and experiment metrics only use human labels as ground truth and explicitly log excluded machine labels.
  • Update pivot recall runner to fetch only human labels, log the number of skipped machine labels, and add a test ensuring machine labels are excluded from recall.
  • Change experiment runner to measure agreement only against human labels and log machine-label exclusions; adjust tests to accommodate the new list_labels API and behavior.
src/fibengine/evaluation/pivot_recall.py
tests/evaluation/test_pivot_recall.py
src/fibengine/experiment.py
tests/test_experiment.py
Remove Optuna/auto-tuning from the codebase and align repo docs and policies with a principled, manual-variants philosophy.
  • Delete all references to Optuna/tuning runs from plans, policies, experiments docs, and results schemas.
  • Reposition config/variants as principled Research profiles rather than auto-tuned variants, updating README and INDEX accordingly.
  • Clarify track responsibilities and promotion gates to prohibit optimization against label agreement and emphasize stability + recall validation.
  • Record premortem reflections documenting Optuna removal and the rationale for machine labeling as candidates only.
  • Update archive index to document Optuna removal artifacts.
docs/FIB_BACKTEST_PLAN.md
README.md
config/variants/README.md
config/variants/INDEX.md
REPO_POLICY.md
docs/TRACKS.md
experiments/README.md
experiments/results/README.md
experiments/results/leaderboard.jsonl
archive/INDEX.md
premortem/reflections/2026-05-28-remove-optuna.md
premortem/reflections/2026-05-29-machine-labeling.md

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a18c79fd3e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

return {"symbol": symbol, "timeframe": timeframe, "status": "no_swing"}

label = label_from_swing(swing, exchange, symbol, timeframe)
path = save_label(label)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep machine candidates in the label-tool review queue

When this writes a source="machine" file to the normal label path, labeling.tool still treats that combination as already labeled because its queue/title logic only checks find_label(...) is not None. After running autolabel over the default matrix, pressing n in the tool will skip these unreviewed candidates (and the count shows them as labeled), so the intended review-and-promote workflow can silently leave machine outputs unreviewed unless the user goes through worklist commands one by one.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR restructures labeling/evaluation around explicit human vs. machine label sources and removes Optuna-driven tuning references in favor of principle-motivated variants.

Changes:

  • Adds source="human"|"machine" label handling, machine autolabel generation, and worklist review reporting.
  • Updates experiment and pivot recall evaluation to use only human labels as ground truth.
  • Removes/updates Optuna-related policy, docs, plans, and archived-result references.

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
src/fibengine/labeling/store.py Adds label source field and source filtering.
src/fibengine/labeling/autolabel.py Adds machine-label generation CLI/workflow.
src/fibengine/labeling/worklist.py Separates human coverage from machine candidates needing review.
src/fibengine/experiment.py Excludes machine labels from agreement evaluation.
src/fibengine/evaluation/pivot_recall.py Excludes machine labels from recall ground truth.
tests/... Adds/updates tests for machine-label behavior and evaluation exclusion.
README.md, REPO_POLICY.md, docs/*, config/variants/*, experiments/*, archive/INDEX.md, premortem/reflections/*, data/labels/README.md Updates documentation and policy around Optuna removal and machine-label semantics.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

created_at: str = ""
# "human" = manuellt facit (golden set). "machine" = maskingenererad kandidat
# (provisorisk, EXKLUDERAS från recall/agreement — får aldrig bli domare).
source: str = "human"
Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey - jag har hittat 3 problem och lämnat lite övergripande feedback:

  • I ställen som coverage_report, run_pivot_recall och run_experiment anropar du list_labels flera gånger med olika source-värden, vilket läser in alla labels på nytt varje gång. Överväg att läsa in en gång och partitionera i minnet för att undvika upprepad disk-I/O och parsing.
  • source-värdena ("human" / "machine") används nu i flera moduler (store, worklist, autolabel, evaluation, experiment). Att införa delade konstanter eller ett enum för dessa skulle minska risken för felskrivningar och hålla beteendet konsekvent.
Prompt för AI-agenter
Please address the comments from this code review:

## Overall Comments
- I ställen som `coverage_report`, `run_pivot_recall` och `run_experiment` anropar du `list_labels` flera gånger med olika `source`-värden, vilket läser in alla labels på nytt varje gång. Överväg att läsa in en gång och partitionera i minnet för att undvika upprepad disk-I/O och parsing.
- `source`-värdena (`"human"` / `"machine"`) används nu i flera moduler (store, worklist, autolabel, evaluation, experiment). Att införa delade konstanter eller ett enum för dessa skulle minska risken för felskrivningar och hålla beteendet konsekvent.

## Individual Comments

### Comment 1
<location path="src/fibengine/evaluation/pivot_recall.py" line_range="123-125" />
<code_context>
-    labels = list_labels()
+    # Endast mänskligt facit får vara ground truth. Maskin-labels är kandidater
+    # och EXKLUDERAS — annars mäter vi motorn mot sig själv (cirkulärt).
+    labels = list_labels(source="human")
+    n_machine = len(list_labels(source="machine"))
+    if n_machine:
+        log.info("Hoppar över {} maskin-labels (ej ground truth för recall)", n_machine)

</code_context>
<issue_to_address>
**suggestion (performance):** Att ladda labels två gånger träffar label-storen två gånger; överväg att partitionera en enda laddning för bättre effektivitet.

Detta triggar nu två fullständiga katalogskanningar/JSON-laddningar (`source="human"` och `source="machine"`). När mängden labels växer kan den extra I/O:n bli märkbar. Överväg att ladda en gång (t.ex. `all_labels = list_labels()`) och sedan partitionera till human vs. machine i minnet för att behålla samma loggning men undvika upprepad I/O.
</issue_to_address>

### Comment 2
<location path="src/fibengine/experiment.py" line_range="130-132" />
<code_context>
-    labels = list_labels()
+    # Endast mänskligt facit får vara ground truth. Maskin-labels är kandidater
+    # och EXKLUDERAS — annars mäter vi motorn mot sig själv (cirkulärt).
+    labels = list_labels(source="human")
+    n_machine = len(list_labels(source="machine"))
+    if n_machine:
+        log.info("Hoppar över {} maskin-labels (ej ground truth för recall)", n_machine)

</code_context>
<issue_to_address>
**suggestion (performance):** Samma dubbel-laddningsmönster som i `pivot_recall`; du kan återanvända ett enda anrop till `list_labels()` och partitionera i minnet.

För att undvika upprepad I/O och hålla detta konsekvent med `pivot_recall`, överväg att anropa `list_labels()` en gång och dela upp resultatet i human- respektive machine-labels i minnet, särskilt eftersom experiment kan köras ofta mot en växande label-mängd.

Suggested implementation:

```python
    # Agreement mäts bara mot mänskligt facit; maskin-labels exkluderas (kandidater,
    # inte domare) så vi inte råkar mäta motorn mot sin egen output.
    all_labels = list_labels()
    labels = [lbl for lbl in all_labels if getattr(lbl, "source", None) == "human"]
    n_machine = sum(1 for lbl in all_labels if getattr(lbl, "source", None) == "machine")
    if n_machine:
        log.info("Hoppar över {} maskin-labels i agreement (ej ground truth)", n_machine)
    results: list[dict] = []
    if not labels:
        log.warning("Inga labels i data/labels/ — kör enbart prediktion på konfig-symbolen.")

```

Om `list_labels()` redan returnerar separata strukturer eller tupler (t.ex. `(human_labels, machine_labels)`), eller om label-objekten exponerar källan under ett annat attribut eller nyckel än `.source`, justera `getattr(lbl, "source", None)`-kontrollerna därefter för att matcha det faktiska API:et (till exempel `lbl["source"]` eller `lbl.meta.source`). Det viktiga är: behåll ett enda anrop till `list_labels()` och härled `labels` (human) och `n_machine` (antal machine) från det i minnet.
</issue_to_address>

### Comment 3
<location path="src/fibengine/labeling/autolabel.py" line_range="84-95" />
<code_context>
+        for timeframe in timeframes:
+            try:
+                result = autolabel_one(settings, exchange, symbol, timeframe, overwrite)
+            except Exception as exc:  # noqa: BLE001 - rapportera per kombination.
+                result = {
+                    "symbol": symbol,
+                    "timeframe": timeframe,
</code_context>
<issue_to_address>
**suggestion:** Den breda exception-hanteringen tappar stacktracen; överväg att logga undantaget med stack trace för enklare felsökning.

Att fånga `Exception` här är rimligt för att kunna fortsätta bearbetningen per (symbol, timeframe), men att bara spara `str(exc)` i `result` gör djupare problem (t.ex. felaktig konfiguration/data) svåra att debugga. Överväg att anropa `log.exception("Autolabel failed for %s %s", symbol, timeframe)` i `except`-blocket så att du behåller fulla stacktraces i loggarna samtidigt som du fortfarande returnerar det strukturerade `result`.

```suggestion
        for timeframe in timeframes:
            try:
                result = autolabel_one(settings, exchange, symbol, timeframe, overwrite)
            except Exception as exc:  # noqa: BLE001 - rapportera per kombination.
                log.exception("Autolabel failed for %s %s", symbol, timeframe)
                result = {
                    "symbol": symbol,
                    "timeframe": timeframe,
                    "status": "error",
                    "error": str(exc),
                }
            log.info("{} {} -> {}", symbol, timeframe, result["status"])
            results.append(result)
```
</issue_to_address>

Sourcery är gratis för open source – om du gillar våra reviews får du gärna dela dem ✨
Hjälp mig bli mer användbar! Klicka gärna 👍 eller 👎 på varje kommentar så använder jag feedbacken för att förbättra dina reviews.
Original comment in English

Hey - I've found 3 issues, and left some high level feedback:

  • In places like coverage_report, run_pivot_recall, and run_experiment you call list_labels multiple times with different source values, which re-reads all labels each time; consider loading once and partitioning in memory to avoid repeated disk IO and parsing.
  • The source values ("human" / "machine") are now used across multiple modules (store, worklist, autolabel, evaluation, experiment); introducing shared constants or an enum for these would reduce the risk of typos and keep behavior consistent.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In places like `coverage_report`, `run_pivot_recall`, and `run_experiment` you call `list_labels` multiple times with different `source` values, which re-reads all labels each time; consider loading once and partitioning in memory to avoid repeated disk IO and parsing.
- The `source` values (`"human"` / `"machine"`) are now used across multiple modules (store, worklist, autolabel, evaluation, experiment); introducing shared constants or an enum for these would reduce the risk of typos and keep behavior consistent.

## Individual Comments

### Comment 1
<location path="src/fibengine/evaluation/pivot_recall.py" line_range="123-125" />
<code_context>
-    labels = list_labels()
+    # Endast mänskligt facit får vara ground truth. Maskin-labels är kandidater
+    # och EXKLUDERAS — annars mäter vi motorn mot sig själv (cirkulärt).
+    labels = list_labels(source="human")
+    n_machine = len(list_labels(source="machine"))
+    if n_machine:
+        log.info("Hoppar över {} maskin-labels (ej ground truth för recall)", n_machine)

</code_context>
<issue_to_address>
**suggestion (performance):** Loading labels twice hits the label store twice; consider partitioning a single load for better efficiency.

This now triggers two full directory scans/JSON loads (`source="human"` and `source="machine"`). As the label set grows this extra I/O can add up. Consider loading once (e.g. `all_labels = list_labels()`) and then partitioning into human vs machine in memory to keep the same logging while avoiding repeated I/O.
</issue_to_address>

### Comment 2
<location path="src/fibengine/experiment.py" line_range="130-132" />
<code_context>
-    labels = list_labels()
+    # Endast mänskligt facit får vara ground truth. Maskin-labels är kandidater
+    # och EXKLUDERAS — annars mäter vi motorn mot sig själv (cirkulärt).
+    labels = list_labels(source="human")
+    n_machine = len(list_labels(source="machine"))
+    if n_machine:
+        log.info("Hoppar över {} maskin-labels (ej ground truth för recall)", n_machine)

</code_context>
<issue_to_address>
**suggestion (performance):** Same double-load pattern as in `pivot_recall`; you can reuse a single `list_labels()` call and partition in memory.

To avoid repeated I/O and keep this consistent with `pivot_recall`, consider calling `list_labels()` once and splitting the result into human vs. machine labels in memory, especially since experiments may run often over an increasing label set.

Suggested implementation:

```python
    # Agreement mäts bara mot mänskligt facit; maskin-labels exkluderas (kandidater,
    # inte domare) så vi inte råkar mäta motorn mot sin egen output.
    all_labels = list_labels()
    labels = [lbl for lbl in all_labels if getattr(lbl, "source", None) == "human"]
    n_machine = sum(1 for lbl in all_labels if getattr(lbl, "source", None) == "machine")
    if n_machine:
        log.info("Hoppar över {} maskin-labels i agreement (ej ground truth)", n_machine)
    results: list[dict] = []
    if not labels:
        log.warning("Inga labels i data/labels/ — kör enbart prediktion på konfig-symbolen.")

```

If `list_labels()` already returns separate structures or tuples (e.g. `(human_labels, machine_labels)`), or if the label objects expose the source under a different attribute or key than `.source`, adjust the `getattr(lbl, "source", None)` checks accordingly to match the actual API (for example `lbl["source"]` or `lbl.meta.source`). The important part is: keep a single `list_labels()` call and derive `labels` (human) and `n_machine` (machine count) from that in memory.
</issue_to_address>

### Comment 3
<location path="src/fibengine/labeling/autolabel.py" line_range="84-95" />
<code_context>
+        for timeframe in timeframes:
+            try:
+                result = autolabel_one(settings, exchange, symbol, timeframe, overwrite)
+            except Exception as exc:  # noqa: BLE001 - rapportera per kombination.
+                result = {
+                    "symbol": symbol,
+                    "timeframe": timeframe,
</code_context>
<issue_to_address>
**suggestion:** The broad exception handling loses the traceback; consider logging the exception with stack trace for easier debugging.

Catching `Exception` here is reasonable to keep processing per (symbol, timeframe), but only storing `str(exc)` in `result` makes deeper issues (e.g. bad config/data) hard to debug. Consider calling `log.exception("Autolabel failed for %s %s", symbol, timeframe)` in the `except` block so you retain full tracebacks in logs while still returning the structured `result`.

```suggestion
        for timeframe in timeframes:
            try:
                result = autolabel_one(settings, exchange, symbol, timeframe, overwrite)
            except Exception as exc:  # noqa: BLE001 - rapportera per kombination.
                log.exception("Autolabel failed for %s %s", symbol, timeframe)
                result = {
                    "symbol": symbol,
                    "timeframe": timeframe,
                    "status": "error",
                    "error": str(exc),
                }
            log.info("{} {} -> {}", symbol, timeframe, result["status"])
            results.append(result)
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +123 to +125
labels = list_labels(source="human")
n_machine = len(list_labels(source="machine"))
if n_machine:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (performance): Att ladda labels två gånger träffar label-storen två gånger; överväg att partitionera en enda laddning för bättre effektivitet.

Detta triggar nu två fullständiga katalogskanningar/JSON-laddningar (source="human" och source="machine"). När mängden labels växer kan den extra I/O:n bli märkbar. Överväg att ladda en gång (t.ex. all_labels = list_labels()) och sedan partitionera till human vs. machine i minnet för att behålla samma loggning men undvika upprepad I/O.

Original comment in English

suggestion (performance): Loading labels twice hits the label store twice; consider partitioning a single load for better efficiency.

This now triggers two full directory scans/JSON loads (source="human" and source="machine"). As the label set grows this extra I/O can add up. Consider loading once (e.g. all_labels = list_labels()) and then partitioning into human vs machine in memory to keep the same logging while avoiding repeated I/O.

Comment on lines +130 to +132
labels = list_labels(source="human")
n_machine = len(list_labels(source="machine"))
if n_machine:
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (performance): Samma dubbel-laddningsmönster som i pivot_recall; du kan återanvända ett enda anrop till list_labels() och partitionera i minnet.

För att undvika upprepad I/O och hålla detta konsekvent med pivot_recall, överväg att anropa list_labels() en gång och dela upp resultatet i human- respektive machine-labels i minnet, särskilt eftersom experiment kan köras ofta mot en växande label-mängd.

Suggested implementation:

    # Agreement mäts bara mot mänskligt facit; maskin-labels exkluderas (kandidater,
    # inte domare) så vi inte råkar mäta motorn mot sin egen output.
    all_labels = list_labels()
    labels = [lbl for lbl in all_labels if getattr(lbl, "source", None) == "human"]
    n_machine = sum(1 for lbl in all_labels if getattr(lbl, "source", None) == "machine")
    if n_machine:
        log.info("Hoppar över {} maskin-labels i agreement (ej ground truth)", n_machine)
    results: list[dict] = []
    if not labels:
        log.warning("Inga labels i data/labels/ — kör enbart prediktion på konfig-symbolen.")

Om list_labels() redan returnerar separata strukturer eller tupler (t.ex. (human_labels, machine_labels)), eller om label-objekten exponerar källan under ett annat attribut eller nyckel än .source, justera getattr(lbl, "source", None)-kontrollerna därefter för att matcha det faktiska API:et (till exempel lbl["source"] eller lbl.meta.source). Det viktiga är: behåll ett enda anrop till list_labels() och härled labels (human) och n_machine (antal machine) från det i minnet.

Original comment in English

suggestion (performance): Same double-load pattern as in pivot_recall; you can reuse a single list_labels() call and partition in memory.

To avoid repeated I/O and keep this consistent with pivot_recall, consider calling list_labels() once and splitting the result into human vs. machine labels in memory, especially since experiments may run often over an increasing label set.

Suggested implementation:

    # Agreement mäts bara mot mänskligt facit; maskin-labels exkluderas (kandidater,
    # inte domare) så vi inte råkar mäta motorn mot sin egen output.
    all_labels = list_labels()
    labels = [lbl for lbl in all_labels if getattr(lbl, "source", None) == "human"]
    n_machine = sum(1 for lbl in all_labels if getattr(lbl, "source", None) == "machine")
    if n_machine:
        log.info("Hoppar över {} maskin-labels i agreement (ej ground truth)", n_machine)
    results: list[dict] = []
    if not labels:
        log.warning("Inga labels i data/labels/ — kör enbart prediktion på konfig-symbolen.")

If list_labels() already returns separate structures or tuples (e.g. (human_labels, machine_labels)), or if the label objects expose the source under a different attribute or key than .source, adjust the getattr(lbl, "source", None) checks accordingly to match the actual API (for example lbl["source"] or lbl.meta.source). The important part is: keep a single list_labels() call and derive labels (human) and n_machine (machine count) from that in memory.

Comment on lines +84 to +95
for timeframe in timeframes:
try:
result = autolabel_one(settings, exchange, symbol, timeframe, overwrite)
except Exception as exc: # noqa: BLE001 - rapportera per kombination.
result = {
"symbol": symbol,
"timeframe": timeframe,
"status": "error",
"error": str(exc),
}
log.info("{} {} -> {}", symbol, timeframe, result["status"])
results.append(result)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Den breda exception-hanteringen tappar stacktracen; överväg att logga undantaget med stack trace för enklare felsökning.

Att fånga Exception här är rimligt för att kunna fortsätta bearbetningen per (symbol, timeframe), men att bara spara str(exc) i result gör djupare problem (t.ex. felaktig konfiguration/data) svåra att debugga. Överväg att anropa log.exception("Autolabel failed for %s %s", symbol, timeframe) i except-blocket så att du behåller fulla stacktraces i loggarna samtidigt som du fortfarande returnerar det strukturerade result.

Suggested change
for timeframe in timeframes:
try:
result = autolabel_one(settings, exchange, symbol, timeframe, overwrite)
except Exception as exc: # noqa: BLE001 - rapportera per kombination.
result = {
"symbol": symbol,
"timeframe": timeframe,
"status": "error",
"error": str(exc),
}
log.info("{} {} -> {}", symbol, timeframe, result["status"])
results.append(result)
for timeframe in timeframes:
try:
result = autolabel_one(settings, exchange, symbol, timeframe, overwrite)
except Exception as exc: # noqa: BLE001 - rapportera per kombination.
log.exception("Autolabel failed for %s %s", symbol, timeframe)
result = {
"symbol": symbol,
"timeframe": timeframe,
"status": "error",
"error": str(exc),
}
log.info("{} {} -> {}", symbol, timeframe, result["status"])
results.append(result)
Original comment in English

suggestion: The broad exception handling loses the traceback; consider logging the exception with stack trace for easier debugging.

Catching Exception here is reasonable to keep processing per (symbol, timeframe), but only storing str(exc) in result makes deeper issues (e.g. bad config/data) hard to debug. Consider calling log.exception("Autolabel failed for %s %s", symbol, timeframe) in the except block so you retain full tracebacks in logs while still returning the structured result.

Suggested change
for timeframe in timeframes:
try:
result = autolabel_one(settings, exchange, symbol, timeframe, overwrite)
except Exception as exc: # noqa: BLE001 - rapportera per kombination.
result = {
"symbol": symbol,
"timeframe": timeframe,
"status": "error",
"error": str(exc),
}
log.info("{} {} -> {}", symbol, timeframe, result["status"])
results.append(result)
for timeframe in timeframes:
try:
result = autolabel_one(settings, exchange, symbol, timeframe, overwrite)
except Exception as exc: # noqa: BLE001 - rapportera per kombination.
log.exception("Autolabel failed for %s %s", symbol, timeframe)
result = {
"symbol": symbol,
"timeframe": timeframe,
"status": "error",
"error": str(exc),
}
log.info("{} {} -> {}", symbol, timeframe, result["status"])
results.append(result)

@JohnCCarter
Copy link
Copy Markdown
Owner Author

@claude check this PR if the reviews are legitimate and if so, implement them if.

- TRACKS.md: restore the Validate stability_gate criteria (drift as a
  first-class metric + explicit out-of-window recall reporting) that the
  feature branch dropped; the gate code and README still describe them.
- archive/INDEX.md + README: the Optuna artifacts under archive/ were not
  actually deleted, so correct the index note (code removed from src; the
  artifacts are retained as history) and restore the README archive pointer.

Docs only; no code/behavior change. 85 tests green, lint clean.
Copy link
Copy Markdown
Owner Author

Granskning + doc-fixar (commit 97a93a9)

Granskade PR:n. Kärnkoden (maskin-labeling) är solid och väl testad (85 gröna, ~76% coverage); inga buggar. Fixade två dokument-/konsistensregressioner direkt på branchen:

  1. docs/TRACKS.md — Validate-kriterierna för stability_gate (drift som förstklassig metric + explicit out-of-window-recall) hade tappats, trots att koden (stability.py, config.gate_*) och README fortfarande beskriver dem. Återställde dem.
  2. archive/INDEX.md + README.md — Optuna-artefakterna under archive/ raderas faktiskt inte i PR:n, men INDEX påstod "Raderat" och README tappade arkiv-pekaren. Korrigerade noten (kod borttagen ur drift; artefakter behålls som historik) och återställde pekaren. (Alternativ: faktiskt radera arkivfilerna — säg till om du hellre vill det.)

Endast docs, ingen kod-/beteendeändring.

Kvarvarande review-noter (ej åtgärdade — din bedömning)

  1. experiments/results/leaderboard.jsonl får 4 nya rader, 3 med principvidriga negativa Optuna-vikter (t.ex. scale_confluence: -1.749) — i samma PR som tar bort Optuna. Avsiktligt audit-spår eller bör utelämnas?
  2. premortem/reflections/2026-05-28-remove-optuna.md saknar Scope + Observationer (REPO_POLICY §11.4) och överlappar 2026-05-28-optuna-rollback.md.
  3. Titeln "Optuna removal" är något missvisande — src/fibengine/tuning/ och optuna-dependencyn fanns inte längre på main; detta är doc-städning + arkiv-konsistens.

Generated by Claude Code

- leaderboard.jsonl: drop the 4 rows this branch appended (3 of them are
  label-optimized Optuna trials with off-principle negative weights). This
  PR should not enshrine label-optimized run records; revert to main's ledger.
- remove-optuna reflection: rewrite to satisfy REPO_POLICY 11.4 (add Scope +
  Observationer), correct it (tuning code/dep were already gone), note the
  archived artifacts are retained, and cross-link the optuna-rollback note.

Finding 5 (PR title accuracy) handled via the PR title. Docs/data only.
@JohnCCarter JohnCCarter changed the title fibengine-next: repo structure, Optuna removal, machine labeling fibengine-next: repo structure, Optuna cleanup (docs/artifacts), machine labeling May 30, 2026
Copy link
Copy Markdown
Owner Author

Review-noter 3–5 åtgärdade (commit 73f42ec)

  • 3 (leaderboard): tog bort de 4 rader branchen lade till i experiments/results/leaderboard.jsonl (3 var label-optimerade Optuna-trials med principvidriga negativa vikter). PR:n bör inte tillföra label-optimerade körposter — ledgern är nu = main.
  • 4 (reflektion): skrev om 2026-05-28-remove-optuna.md så den uppfyller REPO_POLICY §11.4 (lade till Scope + Observationer), korrigerade den (tuning-kod/dependency var redan borttagna), klargjorde att arkiv-artefakterna behålls, och korslänkade 2026-05-28-optuna-rollback.md.
  • 5 (titel): uppdaterade PR-titeln till "Optuna cleanup (docs/artifacts)" eftersom src/fibengine/tuning/ och optuna-dependencyn redan var borta på main.

Docs/data only, ingen kodändring. 85 tester gröna, lint rent.


Generated by Claude Code

@JohnCCarter JohnCCarter merged commit 95ba291 into main May 30, 2026
3 checks passed
@JohnCCarter JohnCCarter deleted the feature/fibengine-next branch May 30, 2026 15:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants