Bug: chat and file upload stubs non-functional; Deal/Task link selector gaps; dead code #10
Labels
No labels
prio_critical
prio_low
type_bug
type_contact
type_issue
type_lead
type_question
type_story
type_task
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
lhumina_code/hero_biz#10
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Critical — Broken features (stubs that are actively called)
Chat is entirely non-functional
save_chat_message(services/mod.rs:2271) returnsErr("Chat save not implemented").load_chat_messages(services/mod.rs:2263) always returnsOk(Vec::new()). Both are called from multiple handlers (lines 3010, 3068, 3174, 3220, 3504, 3549, 4456). The AI assistant chat UI exists and accepts input but nothing is ever stored or retrieved.File uploads are silently dropped
save_upload(services/mod.rs:1562) returnsOk("uploads/placeholder")and writes nothing. Called from handler line 3123. Uploads appear to succeed but the data is lost.High — Link selector / model gaps
Deal
instrument_sid: Stringandcontract_sid: Option<String>update_deal_linksignores bothget_link_selectordoes not offer instruments or contracts for dealsupdate_deal_linksand to the Deal branch inget_link_selectorTask
story_sid: Option<String>update_task_linksnever touches itget_link_selectordoes not offer stories for tasksupdate_task_linksand to the Task branch inget_link_selectorNote — Fields wiped on edit (tracked separately in #11)
Because saves DO reach OSIS, the hardcoded-
Nonefields in update handlers (contact, deal, task, project, milestone) cause real data loss on every edit. See issue #11 for the full list.Low — Dead code
save_to_space(services/mod.rs:1551) is a no-op stub but is never called — safe to delete.get_link_selector_dataservice method andLinkSelectorDatastruct are unreferenced after theget_link_selectorhandler rewrite — safe to delete.Low — Stale docs
instructions/coding_instructions.mdandinstructions/coding_instructions_ai.mddescribe the old heroscript/TOML pipeline — completely obsolete. Should be archived or removed.Suggested order
save_chat_message/load_chat_messagesvia a storage backendsave_uploadsave_to_space,get_link_selector_data,LinkSelectorData)Implementation Spec
Objective
Replace the no-op
save_to_spacestub with real SDK calls in everyupdate_*_linksfunction; wire the missinginstrument_sid,contract_sid(Deal) andstory_sid(Task) fields through their link selectors and save paths; resolve linked entities on five detail pages that currently show nothing; and remove dead code and stale documentation.Files to Modify
crates/hero_biz_ui/src/web/handlers/mod.rs—update_*_links,get_link_selector, detail handlerscrates/hero_biz_ui/src/web/templates/mod.rs— ContactDetail, DealDetail, TaskDetail, MilestoneDetail, ProjectDetail templatescrates/hero_biz_ui/src/services/mod.rs— addload_all_stories_for_space/load_story_for_space; removeget_link_selector_data+LinkSelectorDatainstructions/coding_instructions.md— replace stale heroscript/TOML contentinstructions/coding_instructions_ai.md— replace stale contentImplementation Plan
Step 1 — Replace
save_to_spacein all nineupdate_*_linksfunctionsFiles:
handlers/mod.rsEach function ends with
store.save_to_space(...)which is a no-op. Replace with the matchingstore.save_*(context, &entity).awaitcall. Mapping:update_deal_linksupdate_contact_linksupdate_contract_linksupdate_opportunity_linksupdate_project_linksupdate_task_linksupdate_milestone_linksupdate_instrument_linksupdate_transaction_linksError conversion:
.map_err(|e| anyhow::anyhow!(e.to_string()))on each.Dependencies: none.
Step 2 — Fix Deal link field gaps
Files:
handlers/mod.rsAdd to
update_deal_links:deal.instrument_sidfrom key"instruments",deal.contract_sidfrom key"contracts". Remove the incorrect comment "Deal doesn't have instrument_sid or contract_sid fields".Dependencies: none (parallel with Step 1).
Step 3 — Fix Task link field gap
Files:
handlers/mod.rsAdd to
update_task_links:task.story_sidfrom key"stories".Dependencies: none (parallel with Steps 1–2).
Step 4 — Add story service methods
Files:
services/mod.rsAdd
load_all_stories_for_space(context) -> Result<Vec<Story>>andload_story_for_space(sid, context) -> Result<Story>following the same pattern as milestones: callprojects_for(context).story_list()for SIDs, thenstory_get(sid)per item, map SDK type to localStorymodel.Dependencies: none (parallel with Steps 1–3).
Step 5 — Fix
get_link_selectorfor Deal and TaskFiles:
handlers/mod.rsDeal branch: add
opt_linkforinstrument_sidandcontract_sid; load instruments and contracts from store; add"instruments"and"contracts"tolinkable_types.Task branch: add
opt_linkforstory_sid; load stories from store (Step 4 must be done first); add"stories"tolinkable_types.Dependencies: Step 4.
Step 6 — Fix
contacts_detailhandlerFiles:
handlers/mod.rsLoad
contract,opportunity,dealfrom their SIDs on the contact. Pass toContactDetailTemplate.Dependencies: none (parallel with Step 5).
Step 7 — Fix
deals_detailhandlerFiles:
handlers/mod.rsLoad
opportunity,instrument,contractfrom their SIDs on the deal. Pass toDealDetailTemplate.Dependencies: none.
Step 8 — Fix
tasks_detailhandlerFiles:
handlers/mod.rsLoad
storyfromtask.story_sid. Pass toTaskDetailTemplate.Dependencies: Step 4.
Step 9 — Fix
milestones_detailhandlerFiles:
handlers/mod.rsLoad
owner(aPerson) frommilestone.owner_sid. Pass toMilestoneDetailTemplate.Dependencies: none.
Step 10 — Fix
projects_detailhandlerFiles:
handlers/mod.rsReplace
let owner: Option<Person> = None;with a real lookup usingproject.owner_sid. Pass toProjectDetailTemplate.Dependencies: none.
Step 11 — Update ContactDetailTemplate
Files:
templates/mod.rsAdd
contract: Option<Contract>,opportunity: Option<Opportunity>,deal: Option<Deal>to struct. Add three hyperlinked rows to the HTML render.Dependencies: Step 6.
Step 12 — Update DealDetailTemplate
Files:
templates/mod.rsAdd
opportunity: Option<Opportunity>,instrument: Option<Instrument>,contract: Option<Contract>. Add three hyperlinked rows.Dependencies: Step 7.
Step 13 — Update TaskDetailTemplate
Files:
templates/mod.rsAdd
story: Option<Story>. Add one hyperlinked row.Dependencies: Step 8.
Step 14 — Update MilestoneDetailTemplate
Files:
templates/mod.rsAdd
owner: Option<Person>. Add one hyperlinked row.Dependencies: Step 9.
Step 15 — Remove dead code
Files:
services/mod.rsDelete
get_link_selector_datamethod andLinkSelectorDatastruct. Verify no callers remain.Dependencies: none.
Step 16 — Replace stale instruction docs
Files:
instructions/coding_instructions.md,instructions/coding_instructions_ai.mdReplace heroscript/TOML-based content with accurate description of the current architecture: Axum web server backed by
hero_osis_sdk(BusinessClient, ProjectsClient, IdentityClient per context).Dependencies: none.
Acceptance Criteria
cargo buildpasses with no errorsLinkSelectorDataandget_link_selector_dataare goneinstructions/docs no longer reference heroscript or TOML databaseNotes
save_to_spaceitself is NOT deleted — it is still called from other paths (e.g. file uploads). Leave it with its TODO comment.ProjectDetailTemplatestruct already has anowner: Option<Person>field — only the handler and the comment need updating.update_*_links: check whetherHeroBizErrorimplementsstd::error::Error; use.map_err(anyhow::Error::from)if it does, otherwise.map_err(|e| anyhow::anyhow!(e.to_string())).Implementation Complete
All 16 steps from the spec have been implemented and
cargo build --releasepasses cleanly.Changes made
crates/hero_biz_ui/src/services/mod.rsload_all_stories,load_all_stories_for_space,load_story,load_story_for_spacefollowing the milestone patternget_link_selector_datamethod andLinkSelectorDatastructcrates/hero_biz_ui/src/web/handlers/mod.rssave_to_spacestub with realstore.save_*(context, &entity).awaitcalls in all nineupdate_*_linksfunctionsinstrument_sidandcontract_sidwiring inupdate_deal_links; removed incorrect commentstory_sidwiring inupdate_task_linksget_link_selectorDeal branch with instruments and contracts; Task branch with storiescontacts_detail: loads contract, opportunity, deal from their SIDsdeals_detail: loads opportunity, instrument, contract from their SIDstasks_detail: loads story fromtask.story_sidmilestones_detail: loads owner (Person) frommilestone.owner_sidprojects_detail: replacedlet owner: Option<Person> = Nonewith real lookupcrates/hero_biz_ui/src/web/templates/mod.rsContactDetailTemplate: addedcontract,opportunity,dealfields and hyperlinked display rowsDealDetailTemplate: addedopportunity,instrument,contractfields and hyperlinked display rowsTaskDetailTemplate: addedstoryfield and hyperlinked display rowMilestoneDetailTemplate: addedownerfield and hyperlinked display rowinstructions/coding_instructions.mdandinstructions/coding_instructions_ai.mdBuild
cargo build --release— no errors, no warningsLink integration audit: save_to_space stub, model/selector gaps, dead code, stale docsto Bug: chat and file upload stubs non-functional; Deal/Task link selector gaps; dead codeImplementation Spec: Fix Non-Functional Chat and File Upload Stubs
Objective
Replace three no-op stubs in
Storewith working disk-backed implementations, and delete one unreachable dead-code function. NoChatMessageorUploadschema exists in thehero_osis_sdkbusinessdomain. The correct approach — matching howFlowStoreinsrc/ai/flow.rsworks — is to use the existingdb_rootPathBuffor local file storage: JSON-lines for chat history, and a namespaced directory for uploaded files.Storage layout:
{db_root}/chat/{context_type}/{context_id}/messages.jsonl{db_root}/uploads/{context_type}/{context_id}/{uuid}_{filename}The
chat_filehandler already callsstate.store.db_root().join(&file_path)to serve files from disk, proving the intended design is local storage.Requirements
save_chat_messagemust persist theChatMessageto disk and return itssid.load_chat_messagesmust read all messages for the given(context_type, context_id)pair and return them in chronological order.clear_chat_messagesmust actually delete the messages file for the given context.save_uploadmust write the file bytes to disk under a collision-safe path and return the relative path string.save_to_space(dead code, never called) must be deleted.Cargo.toml— all required crates (uuid,chrono,serde_json,std::fs) are already present.Files to Modify
Single file:
crates/hero_biz_ui/src/services/mod.rsImplementation Plan
Step 1: Add imports
Add
use std::fs;anduse std::io::{BufRead, BufReader, Write};at the top ofservices/mod.rs.Step 2: Delete
save_to_spaceRemove the entire method (dead code, never called,
anyhow::Resultreturn type unlike the rest).Step 3: Implement
save_upload{db_root}/uploads/{context_type}/{context_id}/.,-; replace everything else with_){uuid}_{safe_name}uploads/{context_type}/{context_id}/{uuid}_{safe_name}Err(not silentOk) on IO failureStep 4: Implement
save_chat_messagesidwithuuid::Uuid::new_v4()if emptytimestampwithchrono::Utc::now()ISO-8601 if empty{db_root}/chat/{context_type}/{context_id}/messages.jsonlusingOpenOptions::append(true)sidStep 5: Implement
load_chat_messagesOk(Vec::new())ifmessages.jsonldoes not existChatMessagetracing::warn!) on malformed lines rather than returning an errorStep 6: Implement
clear_chat_messagesmessages.jsonlif it existsOk(())regardless (including when file does not exist)Acceptance Criteria
save_to_spaceis fully deletedsave_uploadwrites bytes to{db_root}/uploads/{context_type}/{context_id}/{uuid}_{filename}save_uploadreturnsErron IO failure (not silentOk)file_pathfromsave_uploadcan be served viaGET /api/chat/file/{file_path}save_chat_messageappends a JSON line to{db_root}/chat/{context_type}/{context_id}/messages.jsonlsave_chat_messageauto-populatessidandtimestampif emptyload_chat_messagesreturns messages in chronological orderload_chat_messagesreturnsOk(Vec::new())when no file exists yetload_chat_messagesskips malformed lines with a warning instead of erroringclear_chat_messagesdeletes the file if it exists,Ok(())either waycargo buildpasses with no errorsNotes
save_to_spaceitself usesanyhow::Result(not the crate'sResult) and isin_degree=0— safe to delete entirely.saveandsave<T>stubs at the top of that section are NOT part of this issue — do not touch them.ChatMessagestruct derivesClonesomessage.clone()is valid in the save implementation.context_sid(field onChatMessage) and thecontext_idURL parameter are the same value — usemsg.context_sidfor the directory path when saving, and thecontext_idparameter when loading.Build Results
Status: PASS
cargo build completed with no errors.
Implementation Complete
All remaining critical and low-priority items from issue #10 have been implemented.
Changes
crates/hero_biz_ui/src/services/mod.rsuse std::fs;anduse std::io::{BufRead, BufReader, Write};importssave_to_space(was never called)save_upload: now writes bytes to{db_root}/uploads/{context_type}/{context_id}/{uuid}_{filename}and returns the relative path; returnsErron IO failure instead of silently succeedingload_chat_messages: reads from{db_root}/chat/{context_type}/{context_id}/messages.jsonl; returnsOk(Vec::new())when no file exists yet; skips malformed lines with atracing::warn!instead of erroringsave_chat_message: auto-populatessid(UUID v4) andtimestamp(ISO-8601 UTC) if empty; appends a JSON line tomessages.jsonlviaOpenOptions::append(true)clear_chat_messages: deletesmessages.jsonlif it exists;Ok(())either wayStorage layout
The
chat_filehandler (GET /api/chat/file/*file_path) already resolves paths viadb_root().join(&file_path), so uploaded files are immediately servable after this change.Build
cargo build— no errors, no warnings.