refactor(dashboard): simplify goal prioritization + O(facts) goals-API dedup (#2695)#2730
Merged
Merged
Conversation
…ion signal (#2695) Step 9 (refactor & simplify) of the #2695 follow-up. The prioritization pass carried a `PrioritizationSignals.user_facing` signal that NO caller ever populated — `decompose.rs::sibling_dependency_signals` and every test build the signals with only `depends_on` — so its `+1` scoring branch could never fire in production or under test. It was speculative generality (also absent from the task's A7 signal set), not live behavior. Remove it entirely, behavior-preserving: - prioritize.rs: drop the `user_facing` field, the `HashSet` import, the `signal_score` parameter, and its dead scoring branch (5 -> 4 params). - decompose.rs / tests_prioritize.rs: `PrioritizationSignals` now has a single field, so the `..Default::default()` struct-update is needless (clippy::needless_update) — construct it directly as `{ depends_on }`. - docs: drop the user-facing signal row and correct the `skip_serializing_if` attribute to the real `is_false` helper (the doc showed `std::ops::Not::not`, which the code does not use), so the reference matches reality. No behavior change: `user_facing` was always empty, so every score and banded priority is identical. fmt + clippy clean; full lib suite 7528 passed, 0 failed. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…s×board) (#2695) Step 9b (performance optimization) of the #2695 follow-up. `goals_at` is polled on every dashboard refresh. Its cognitive-memory backlog merge deduped each candidate fact by re-scanning the entire active+backlog list with a serde_json `.get("id")` map lookup per element — and since matched facts are pushed onto `backlog`, the scanned list grew as the loop ran, making the dedup effectively O(facts²) plus a JSON lookup per comparison. Build a `HashSet<String>` id index once and check membership in O(1), inserting each newly-listed fact so later facts still dedup against earlier ones. Same result, no quadratic scan and no per-comparison serde lookups. Bounded, hot-path win with no behavior change. Verification: fmt + clippy (all-targets) clean; full lib suite 7528 passed, 0 failed — identical to the pre-change baseline. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
📊 Coverage Summary
Coverage data from CI run. Test files matching |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Follow-up refactor/perf on the #2695 Goals-tab work (hierarchy + differentiated, priority-ordered goals, merged in #2714). This PR is additive and behavior-preserving — it simplifies and speeds up the already-shipped feature, it does not change what the operator sees.
Two changes:
refactor(goal-curation): drop the never-populateduser_facingprioritization signal.PrioritizationSignals.user_facingwas a structured, caller-suppliedHashSetthat no production caller ever populated (the decompose path only ever builtdepends_on). Carrying a dead signal through the pureprioritize()scorer added an always-falsebranch and an empty-set plumb on every call site. Removing it shrinksPrioritizationSignalsto the one signal that is actually fed (depends_on) — no observable change to output because the field was always empty. Prefer structured goal-graph data over dead configuration (G3).perf(dashboard): make the/api/goalsfact-dedup O(facts) instead of O(facts×board).goals_atmerges meeting/action/decision facts from cognitive memory into the backlog, skipping any fact whose id is already listed. The old check re-scanned the entireactive+backloglist (with a serde-map lookup per element) for every fact — quadratic on an endpoint that is polled on every dashboard refresh. Build anO(1)HashSetid index once and insert newly-listed fact ids as we go, so later facts still dedup against earlier ones. Identical output, no quadratic scan.Reconciled with the just-merged Goals tab (#2695/#2714): one coherent tab that shows hierarchy (structured
parent_goal_idnesting), priority-ordered goals (ascending, stable id tiebreak, visible tiers +priority_explicitprovenance), and real per-goal status (status_progress).Rebased onto latest
origin/main. Additive; no "Bridge" names; no new stray prints; no silent fallbacks; structured goal-graph data preferred over parsing (G3).Step 13: Local Testing Results
Detected toolchains:
simard(edition 2024). Evidence:Cargo.toml([package] name = "simard",default-run = "simard") +Cargo.lockat repo root; changed files are all.rsundersrc/goal_curation/andsrc/operator_commands_dashboard/. Per the qa-team skill, aCargo.toml-at-root repo usescargo testas the outside-in harness (no gadugi framework required).Chosen validation strategy:
/api/goalsJSON contract (served bygoals_at) plus the goal prioritization pass and decompose path. I exercised both the native hermetic consumer-boundary tests (which callgoals_at(state_root)— the exact fn the HTTP handler calls — and assert on the JSON a browser receives) and the real compiledsimardbinary end-to-end: thegoalCLI against a durable store and the livedashboard serveHTTP server viacurl.Scenario 1 — Simple: prioritization pass + real CLI shows differentiated, visible priorities
Command:
cargo test --lib goal_curation::tests_prioritizeandsimard goal add <p> <desc>+simard goal set-priority+simard goal list(real binary, tempSIMARD_STATE_ROOT)Result: PASS
Output:
Priorities are visible and differentiated (p1/p3/p5), not a flat default.
Scenario 2 — Complex: dashboard
/api/goalsHTTP boundary — hierarchy field + priority-ordered + real status + provenance + dedupCommand:
cargo test --lib operator_commands_dashboard::tests_goals_crud goal_curation::tests_decomposeand a livesimard dashboard serve+curl -H "Authorization: Bearer …" /api/goalsResult: PASS
Output (hermetic — the exact JSON contract a browser consumes):
Output (live HTTP server against a real durable store, 5 goals; note priority-ascending order, real status, and
priority_explicitprovenance):Ordered by priority (highest first) with a stable id tiebreak; each goal's priority is visible;
priority_explicitprovenance andparent_goal_idhierarchy edge are exposed;status_progresscarries real per-goal lifecycle status. The non-nullparent_goal_idnesting case (goal decomposeneeds an LLM/recipe-runner, unavailable offline) is proven by thegoals_expose_parent_goal_id_for_hierarchyhermetic test, which drives the samegoals_atcode with a properly-seeded store.Regression + CI gates (local)
Related: #2695, #2714
Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com