Spaces:
Build error
Build error
Commit History
-
Merge pull request #105 from abinaze/fix/r5-wire-cache-ttl
Unverified signatureWire get_graph_depth() into 5 internal investigator queriesfa85dbd -
Wire get_graph_depth() into 5 internal investigator queries (R-5, partial)
What: 5 hardcoded [*1..2] variable-length Cypher traversals -- 3 in ai/deep_investigator.py (_layer_3_patterns x2, _layer_4_timeline x1), 1 in ai/investigators/historical_investigator.py, 1 in api/routes/risk.py's ghost-company detection -- now interpolate int(get_graph_depth()) instead of a hardcoded 2, using the same f-string-into-Cypher-variable-length-path pattern ai/graph_analytics.py already established elsewhere in this codebase (Cypher does not accept *min..max as a query parameter -- this project's own BUG-1 fix already established that literal string interpolation is required here, not ). Why these 5 specifically, and why this is genuinely safe (unlike investigation_layers, deliberately NOT touched in this commit): every one of these 5 sites was hardcoded at exactly *1..2, which is also PROFILE['low']'s own graph_depth value. That means a low-tier/desktop install sees ZERO behavioral change from this commit -- it already was getting depth-2 traversal today, for everyone, reg…f064888 -
Merge pull request #104 from abinaze/fix/r5-wire-cache-ttl
Unverified signatureWire get_cache_ttl() into _STATS_TTL and _VERIFY_TTL (R-5, partial)f37a0af -
Wire get_cache_ttl() into _STATS_TTL and _VERIFY_TTL (R-5, partial)
What: two hardcoded cache-freshness constants -- api/main.py's _STATS_TTL (was 60.0) and api/dependencies.py's _VERIFY_TTL (was 30.0) -- now read config.model_selector.get_cache_ttl(), the same previously- dead function R-5 named. Pure caching-freshness/overhead trade-off in both cases: changes how OFTEN cached data is recomputed, never WHAT gets computed or returned. Why only 2 of R-5's 4 dead functions, not all: investigated all four rather than assuming R-5 was uniformly a design question or uniformly mechanical -- it's neither, it's two very different things wearing the same label. - get_batch_size(): confirmed genuinely dead. Grepped the whole codebase for any batching pattern (batch_size, BATCH_SIZE, chunked iteration) -- zero hits anywhere outside model_selector.py/ runtime_profile.py themselves. Nothing to wire it into; unchanged. - get_graph_depth() / get_investigation_layers(): found REAL existing call sites (hardcoded *1..2 Cypher patterns across deep_in…dd8fe80 -
Merge pull request #103 from abinaze/feat/d7-datasets-transparency-page
Unverified signatureAdd read-only Data Sources page (D7, scoped to transparency only)f4a0ef9 -
Add read-only Data Sources page (D7, scoped to transparency only)
What: a new /datasets page listing every official source this platform draws from -- name, official URL, what kind of data, live record count in the graph, and loaded/pending status -- with click-to-expand sample records per source. Reachable from the navbar between Forensics and About. Real discovery that reshaped this: api/routes/sources.py's /sources and /sources/{dataset} endpoints already existed, fully built and already registered in main.py, computing exactly this data (real per-source graph counts, loaded/pending status, sample records) -- just with no frontend ever calling them. So this commit is entirely frontend: two new Api client methods over the existing endpoints, two small CSS badge modifiers reusing existing color tokens (not a new palette), and one new Views.datasets page mirroring the Views.forensics page's exact structure (skeleton loaders, Api call, then/catch, sanitize() everywhere). Deliberately scoped to read-only, and this is worth being explicit about rather…5761f57
-
Merge pull request #102 from abinaze/feat/d8_provenance-badges
Unverified signatureExtend provenance badges to search results (D8)3019a12 -
Extend provenance badges to search results (D8)
What: search results now carry and display source_type, same as the profile page from the previous commit. Three pieces: - api/models.py: SearchResult gets a source_type field (Optional, same convention as risk_level). - api/routes/search.py: all 22 LABEL_QUERIES RETURN clauses, the fulltext-index query, and both result-construction code paths (fulltext success + label-scan fallback) now select/pass source_type through. Applied as one global, count-asserted substitution across the 22 near-identical query strings rather than 22 separate manual edits -- same reasoning as the desktop-mode chdir fix: many genuinely uniform call sites are better fixed once, centrally, when the change to each is identical. - frontend/js/components.js: ResultCard renders the badge by calling Components.ProvenanceBadge(...).outerHTML rather than duplicating its label map inline -- one source of truth for the 4 SOURCE_TYPES labels, and it inherits that component's textContent-based (not in…c91bada -
Merge pull request #101 from abinaze/feat/d8-provenance-badges
Unverified signatureAdd provenance badges to the entity profile view (D8)e8786a8 -
Add provenance badges to the entity profile view (D8)
What: a colored badge next to the entity-type badge on every profile page, showing graph/schema.py's SOURCE_TYPES value (OFFICIAL today; UNOFFICIAL/COMMUNITY/USER_CREATED are provenance for D7's future user-import paths). Mirrors the existing badge/risk-badge pattern exactly -- same component style (Components.ProvenanceBadge, container appended post-render), same CSS structure, reuses existing color tokens rather than inventing a new palette (OFFICIAL reuses badge--company's green, COMMUNITY reuses badge--contract's blue, USER_CREATED reuses risk-badge--MODERATE's amber, UNOFFICIAL reuses risk-badge--NONE's neutral gray). One commit, not several, because the two halves are inert alone: the backend field with no UI renders nothing, and the UI with no field has nothing to render. Real gap found and fixed, not assumed: source_type was already being read off every node (p = dict(pol['p'])) but silently dropped by the Politician and Company profile handlers' hand-curated overview dicts -…66bf6f4 -
Merge pull request #100 from abinaze/feat/d6-add-keyring-dependency
Unverified signatureAdd python -m config.credentials CLI (D6, ahead of D5's UI)7485c1c -
Add python -m config.credentials CLI (D6, ahead of D5's UI)
What: a thin argparse CLI appended to config/credentials.py -- 'list', 'get [--reveal]', 'set [VALUE]' (prompts hidden if omitted), 'delete' -- over the 6 managed keys. Values are masked to their last 4 chars by default. Calls the exact same get_credential()/set_credential() functions a future D5 setup-wizard UI would call, so nothing here needs to change when that UI exists. Why now rather than waiting for D5: this is the only way to test the 5 keys with no other write path (everything but ADMIN_SECRET) against a REAL OS keychain -- Windows Credential Manager specifically -- since no sandbox in this engagement has ever had one available to test against directly. Small, self-contained, and doesn't conflict with or duplicate D5's eventual work. Verified: python -m py_compile passes; ran 'python -m config.credentials list/get/set/delete' as a real subprocess (not just calling _cli() in-process) to confirm argparse's prog name and --help both render correctly. Behaviorally exercised eve…10d643d -
Merge pull request #99 from abinaze/feat/d6-add-keyring-dependency
Unverified signatureFeat/d6 add keyring dependency911812e -
Migrate admin-secret generation to try the OS keychain first (D6)
What: _generate_and_persist_admin_secret() in api/routes/admin.py now calls config.credentials.set_credential("ADMIN_SECRET", ...) first, falling back to config.settings.set_local_setting() (config.json) only if the keychain write fails or isn't available. _require_admin() needed zero changes -- it already reads via os.getenv("ADMIN_SECRET", ""), which transparently picks up whatever config.settings injected regardless of source. Subtlety specifically verified rather than assumed: set_local_setting() already injects its value into os.environ itself, but set_credential() (the keychain path) does not -- that's load_credentials_into_environ()'s job, which only runs once at process startup. Without an explicit os.environ["ADMIN_SECRET"] = new_secret on the keychain-success branch, a secret generated via the keychain would be correct on disk/in-keychain but invisible to the rest of that same running process until next restart. Added explicitly; confirmed by test. An existing install with …188acc4 -
Wire load_credentials_into_environ() into config/settings.py (D6)
What: after the existing local-config (config.json) injection loop, and before NEO4J_URI/NEO4J_USER/etc are read as module-level constants, call config.credentials.load_credentials_into_environ() -- but ONLY when BHARATGRAPH_DESKTOP_MODE=true. Why gated to desktop mode (a correction from the original design sketch, caught during this delivery's own verification, not assumed): a web deployment gets its credentials from GitHub Secrets/real env vars, never a local OS keychain, so there's no reason to import keyring there at all. Gating the whole block -- not just making load_credentials_into_environ() a no-op -- means keyring is never even imported in web mode, confirmed via a direct sys.modules check. Verified: python -m py_compile passes. Imported config.settings twice in the same isolated venv used for the previous commit: with BHARATGRAPH_DESKTOP_MODE unset, confirmed 'keyring' never appears in sys.modules and NEO4J_URI resolves exactly as before this change; with BHARATGRAPH_DESKTO…e205d9d -
Add config/credentials.py -- OS-keychain credential storage (D6)
What: new module with get_credential(key), set_credential(key, value), and load_credentials_into_environ(). Extends config.settings's existing 2-level precedence (env var > local config.json) with a third, lowest- precedence source: the OS keychain (Windows Credential Manager / macOS Keychain / Linux Secret Service), via the keyring package. A closed, explicit list of 6 managed keys (ADMIN_SECRET, NEO4J_URI, NEO4J_USER, NEO4J_PASSWORD, DATAGOV_API_KEY, OPENSANCTIONS_API_KEY) -- not an arbitrary-key store, since the OS keychain is a shared per-user resource. Not yet wired into anything (next two commits do that) -- this commit is the module on its own, reviewable in isolation. Verified: python -m py_compile passes. In an isolated venv with keyring installed: (1) with no real OS keychain backend available, confirmed get_credential/set_credential/load_credentials_into_environ all degrade to no-ops with no exceptions -- import and keyring.get_keyring() both succeed, and the failure only …89d90ae -
Add keyring==25.7.0 as the 24th pinned dependency (D6 prep)
Why: Phase D6 (API key manager / OS-keychain credential storage) needs the keyring package. Adding it to requirements.txt first, on its own, before config/credentials.py lands, so this dependency change can be reviewed and verified in isolation from the actual credential-storage logic. Verified: real (not --dry-run) install of keyring into a clean, isolated venv -- resolves to 25.7.0 (MIT license). Confirmed 'import keyring' and 'keyring.get_keyring()' both succeed even with no OS keychain backend available (this sandbox has none); confirmed the failure only surfaces at the first actual get_password()/set_password() call, raised as NoKeyringError specifically. This is the exact failure point config/credentials.py's error handling will be built around in the next step, so confirming it now (rather than assuming from memory) matters. Not done as a full-stack reinstall: keyring is small and functionally independent of the ML/PDF stack (spacy/torch/weasyprint etc.), so it was verified in…36b340f -
Merge pull request #98 from abinaze/fix/duplicate-dossier-output-dir
Unverified signatureFix duplicate get_dossier_output_dir() definition in config/settings.py2232d85 -
Fix duplicate get_dossier_output_dir() definition in config/settings.py
Root cause: an earlier delivery script was accidentally re-run after a 'git checkout -b' failed with branch-already-exists (bash does not stop on a non-&&-chained command failure). That script's edit anchor matched only the function's own unchanged body, not anything after it, so the second run found the same anchor again and inserted a second, verbatim copy of the function immediately after the first. Trigger condition: any second invocation of that specific delivery step against a repo where it had already succeeded once. Blast radius: definition-order only. Python's later def silently shadowed the earlier one; both bodies were byte-identical, so every call site kept calling the same function with the same behavior. No functional bug reached any user -- this is a code-hygiene / future- drift-risk fix. Verified (in sandbox before delivery): python -m py_compile succeeds; AST-parsed the module and confirmed exactly one top-level FunctionDef named get_dossier_output_dir (was 2, now 1…c43cb01
-
Merge pull request #97 from abinaze/chore/mechanical-cleanup-pass
Unverified signatureChore/mechanical cleanup passd3ec23d -
fix: loader counter-accuracy -- 20 methods counted attempts, not successes
Root cause: 20 of 22 load_* methods called self._run(query, params) without capturing its return value, then unconditionally incremented their success counter right after. _run() returns None on 3 distinct conditions: a genuine Cypher/Neo4j failure (caught by its own broad exception handler), the D-2 sample-data-rejection gate silently refusing to write a record, or dry_run mode. In all 3 cases, the counter still incremented as if the write had succeeded -- already directly observed in this project's own D-2 verification, where 'Politicians loaded: 1' logged even though the D-2 gate had correctly rejected the write underneath it. Only load_affidavits and load_wikidata_enrichments (2 of 22) already checked the return value correctly. Fix: all 20 now capture result = self._run(...) and only increment count when result is not None. Also moved the rels_created stat increment in load_politician_company_links inside the same guard, same reasoning. One deliberate trade-off, stated precisely…719c3d0 -
fix: pin all 23 direct dependencies to exact versions
Root cause: 0 of the (then-20, now 23 after the earlier transformers fix) direct dependencies used an exact (==) pin -- all were open-ended (>=). A real reproducibility risk specifically for a frozen desktop build (D2's PyInstaller packaging): whatever resolves at build time gets baked permanently into that one installer, with no way to reproduce the exact same build later. Verified, not guessed: did a real (not --dry-run) full install of the unpinned requirements.txt into a clean venv -- every package, including torch (pulled transitively by sentence-transformers), spacy, sentence-transformers, and weasyprint's native stack, actually installed successfully. Confirmed the real api.main app imports and boots correctly against that full environment. Ran the complete test suite against it: 116 passed, 0 failed -- the first time in this project's engagement history the suite has run with every dependency genuinely present rather than a lighter subset (prior verification passes deliberatel…15678cb -
fix: README version badge drift (F-8 residual)
Root cause: README's badge read v0.36.1; api/main.py's app.version (the single authoritative source after F-8's earlier fix consolidated GET / and GET /health to both read from it) reads 0.36.0. Checked PHASE_ROADMAP.md's own VERSION HISTORY table before picking a direction -- it stops at v0.33.0, then jumps straight to aspirational future versions (v0.40.0, v0.50.0, v1.0.0), all still marked 'planned'. No entry for v0.34/v0.35/v0.36 exists at all, and certainly no evidence of an actual v0.36.1 release. The README badge was drift, not a real, separately-released version this fix would be erasing evidence of. Fix: badge now matches app.version exactly.f1a2b52
-
Merge pull request #96 from abinaze/fix/source-type-stub-nodes
Unverified signaturefix: wire source_type into the Party/Ministry/Scheme stub-node helpers77c4516 -
fix: wire source_type into the Party/Ministry/Scheme stub-node helpers (P-1/D8)
Root cause: the previous commit wired source_type into all 20 primary load_* methods, but explicitly deferred 5 smaller stub-node creation sites: _create_party_and_link, _link_contract_to_ministry, _link_audit_to_scheme, and the inline Party/Company/Ministry ON CREATE SET stubs in load_electoral_bonds and load_tenders. Party/Ministry/Scheme ARE documented in NODE_SCHEMAS with the same source_type field and the same 'default OFFICIAL' language as every other node type -- this was a real, if smaller, gap, not a lower-priority skip. Fix: added source_type='OFFICIAL' to all 5 -- 3 plain SET clauses (re-applied every call, harmless since the value is always the same constant) and 2 ON CREATE SET clauses (correctly fires only the first time each stub node is created, consistent with how those clauses already handle source='electoral_bond'/'cppp'). Verified: invoked all 5 code paths for real (not just read the diff) by calling load_politicians/load_contracts/load_audit_reports/ load_elector…df73861 -
Merge pull request #95 from abinaze/feat/wire-source-type-into-loader
Unverified signaturefeat: wire source_type into graph/loader.py's actual write queries02be021 -
feat: wire source_type into graph/loader.py's actual write queries (P-1/D8)
Root cause: D1 added source_type to graph/schema.py's NODE_SCHEMAS and SETUP_QUERIES, but scoped that phase as schema-only groundwork -- no load_* method actually wrote the field. Every real node write across the 20 methods that create primary nodes from scraped records was missing it entirely. Scope decision, stated precisely: added source_type='OFFICIAL' to all 20 load_* methods that create primary nodes (load_politicians through load_datagov_documents). Explicitly did NOT touch: - load_wikidata_enrichments: uses MATCH, not MERGE, to enrich EXISTING Politician nodes -- stamping OFFICIAL here would misrepresent provenance for a node this method didn't create. - load_politician_company_links: its MERGE calls are inference- fallback stubs to anchor a DIRECTOR_OF relationship when no real Politician/Company node exists yet, not sourcing an authoritative record -- the relationship itself is already correctly labeled source='entity_resolution'. - The small st…19a0456
-
Merge pull request #94 from abinaze/fix/desktop-mode-chdir-writable-dir
Unverified signaturefix: chdir into app-data dir at desktop startup, fixing 30+ writes at onced3c07a5 -
fix: chdir into app-data dir at desktop startup, fixing 30+ writes at once (P-1)
Root cause: grepped every bare (non-__file__-anchored) CWD-relative path in the codebase. Found DATA_RAW_PATH/DATA_PROCESSED_PATH/ DATA_SAMPLES_PATH in config/settings.py were dead code exactly like LOGS_PATH was before its own fix -- zero real consumers reference them. Instead, 30+ real call sites hardcode their own copy of the same path directly: all 19 scrapers' data/samples/*.json sample- fallback output, processing/pipeline.py (data/processed + data/samples), ai/multi_investigator.py, ai/risk_scorer.py, ai/self_learning/schema_learner.py, processing/alias_graph.py, and graph/loader.py's own glob() read of pipeline output. Trigger condition: on a real Windows install, a double-clicked app's CWD is unpredictable, and the install directory itself is typically read-only for a non-admin user. Every one of these 30+ writes would fail outright under that condition -- not just lose regeneratable cache data, but error out entirely (scrapers couldn't run at all). Why this fix looks differ…45cee30 -
Merge pull request #93 from abinaze/fix/desktop-mode-dossier-output
Unverified signaturefix: desktop-mode-adapt dossier output; unify 3 hardcoded copies of its patha1ddfee
-
fix: desktop-mode-adapt dossier output; unify 3 hardcoded copies of its path (P-1)
Root cause: the dossier/export flow had this same base path hardcoded independently in THREE places, all of which need to agree for the export endpoint to work at all: 1. ai/dossier_generator.py's generate() computed its own <project_root>/data as a path-traversal allowlist (a pre-existing H-07/CodeQL security fix) and joined output_dir onto <project_root> directly. 2. api/routes/export.py passed the literal string "data/processed" as that function's output_dir argument. 3. api/routes/export.py ALSO independently recomputed <project_root>/data/processed as its OWN separate, independent path-traversal allowlist (a second H-07/CodeQL fix) to validate the path generate() handed back before serving the file. New finding while implementing this: (1) and (3) were ALREADY subtly inconsistent with each other before any desktop-mode change -- (1) allowed anything under <project_root>/data/ (e.g. data/samples), while (3) allowed only <project_root>/data/…7650d33 -
Merge pull request #92 from abinaze/fix/desktop-mode-data-paths
Unverified signatureFix/desktop mode data paths2b31b6b -
fix: centralize + desktop-mode-adapt the logs directory (P-1)
New finding while implementing this: config/settings.py's LOGS_PATH constant was dead code -- grepped every consumer, zero hits anywhere outside its own definition. The 3 real log-writing call sites each independently hardcoded or recomputed their own path instead: - scrapers/base_scraper.py: a bare CWD-relative "logs" string - api/middleware/audit_logger.py: __file__-relative, 3 dirname() calls up to project root - blockchain/audit_chain.py: __file__-relative, 2 dirname() calls up to project root (different file depth, same target) All 3 resolve to the identical directory TODAY only because Docker's fixed WORKDIR makes CWD == project root -- a coincidence, not a guarantee. A desktop build has no such guarantee: a double-clicked app's CWD is unpredictable, so the CWD-relative scraper logs and the __file__-relative audit logs could silently end up in two different, unrelated directories the moment this ships as a desktop app. Fix: LOGS_PATH is now desktop-mode-aware (same…6d12c24 -
fix: store case_memory.json in app-data dir in desktop mode (P-1)
Root cause: CASE_STORE_FILE was always computed relative to this file's own location (effectively the install directory), regardless of deployment mode. case_memory.json is the one file in the audit's Track 2 Sec 10.3 path-handling finding that holds real, accumulated, non-regeneratable user data (investigation history, learned patterns, recorded false positives) -- everything else in that finding (scraped data, logs) can be regenerated or safely lost. Trigger condition: any desktop-build update process that replaces or reinstalls the app's install directory (the normal shape of a desktop app update) would silently delete this file with zero warning to the user, since it lived inside that directory. Fix: in desktop mode, store it via the existing get_app_data_dir() helper (built in D1, previously used only for the admin-secret config file) instead. Web-deployment behavior is unchanged when BHARATGRAPH_DESKTOP_MODE is unset -- same exact path as before. Verified: confirmed the web-mo…af4b9c1
-
Merge pull request #91 from abinaze/fix/vendor-d3-locally
Unverified signaturefix(frontend): vendor D3.js locally instead of loading from CDN347ec79 -
fix: add .gitattributes to protect vendored D3 file's line endings
Root cause: the fix/vendor-d3-locally commit added frontend/js/vendor/d3.min.js with no .gitattributes anywhere in the repo. Confirmed live on a Windows checkout: git warned 'LF will be replaced by CRLF the next time Git touches it' -- meaning a future checkout/re-clone on a machine with core.autocrlf=true (Git for Windows' common default) would silently rewrite this file's line endings, and sha256sum run against that working copy would no longer match the hash documented in NOTICE.md, even though the actual git-stored blob (and what CI/Linux/macOS see) is untouched. Fix: -text on frontend/js/vendor/** tells Git to never apply line- ending conversion to vendored files, on any platform, on checkout or commit. Verified: reproduced the exact failure mode first (checked out with core.autocrlf=true simulated, confirmed hash mismatch was possible in principle), then added the .gitattributes rule and re-ran the same checkout -- file now has zero CR bytes and its sha256 matches f2094bbf6141b…2d97c20 -
fix(frontend): vendor D3.js locally instead of loading from CDN (N-2/F-14)
Root cause: frontend/index.html loaded D3 from cdnjs.cloudflare.com. window._d3Failed was set in the script's onerror handler but never read anywhere in the codebase (confirmed by grep across every frontend/*.js file) -- so on CDN failure, the Relationship Graph tab threw an unhandled ReferenceError instead of showing any kind of message. Trigger condition: whenever cdnjs.cloudflare.com is unreachable. Rare today for the hosted deployment, but becomes a routine condition (not an edge case) the moment any offline/local desktop build ships -- this is exactly the CDN dependency PHASE_ROADMAP.md's offline-first goal can't tolerate. Fix direction: vendored the library locally rather than wiring the existing _d3Failed flag into a real fallback message. A real offline-first / desktop build has no good 'recover from missing charting library' story worth building -- the relationship-graph feature would just be broken either way -- so removing the network dependency entirely is the correct fix…a18c616 -
Merge pull request #90 from abinaze/fix/wire-model-selector-hardcoded-models
Unverified signaturefix: wire 3 hardcoded AI models into the existing model_selector tiering80e3008 -
fix: wire 3 hardcoded AI models into the existing model_selector tiering (R-2/R-3/R-4)
Root cause: config/model_selector.py's get_model() (low/medium/high variants per task) has existed since Phase 31 but was called nowhere in the codebase -- confirmed via grep, zero call sites outside its own file and tests. Three consumers hardcoded a fixed model regardless of the machine's detected runtime_profile tier: - ai/nlp_extractor.py: always en_core_web_sm (get_model('ner')'s medium-tier value -- correct only by coincidence on medium tier) - ai/shadow_draft_detector.py: always all-MiniLM-L6-v2 (same coincidental medium-tier match) - processing/entity_resolver_v2.py: always paraphrase-multilingual-MiniLM-L12-v2 -- get_model('embeddings')'s HIGH-tier value, unconditionally, on every tier including 'low'. This is the most severe of the three: the 2GB-RAM 'low' tier -- the exact case runtime_profile.py exists to protect -- was loading the single heaviest embedding model available, every time. Trigger condition: always, independent of any misconfi…3066feb -
Merge pull request #89 from abinaze/fix/missing-transformers-indic-deps
Unverified signaturefix: add missing transformers/sentencepiece/indic-transliteration to …208b5da -
fix: add missing transformers/sentencepiece/indic-transliteration to requirements.txt
Root cause: ai/translator.py, ai/multilingual_ner.py, and ai/transliteration.py all genuinely import transformers/ indic_transliteration (wired in via api/routes/multilingual.py), but none of the three packages were declared in requirements.txt. Trigger condition: any fresh install (including a future PyInstaller- frozen desktop build). Not a crash -- all three call sites already guard with if self._model / if self._pipeline / except ImportError and fall back to template/pattern-based logic, so the failure mode is silent, permanent feature degradation: the real model-backed translation/NER/transliteration path never activates, with nothing surfaced to the user or logs beyond a debug-level warning. Blast radius: multilingual translation, multilingual NER, and transliteration features only. No other module imports these packages. Fix: added transformers>=4.41.0, sentencepiece>=0.1.99 (required by MarianTokenizer for the Helsinki-NLP/opus-mt-* model translator.py loads -- without it, t…d99d76d
-
Merge pull request #87 from abinaze/feat/d1-dataset-provenance-schema
Unverified signaturefeat(frontend): finish the Phase 35 entity-view tabs and forensics pa…210de4a -
Merge pull request #88 from abinaze/feat/d3-neo4j-sidecar
Unverified signaturefeat(D3): bundled Neo4j Community sidecar process manager4d9dd75 -
feat(D3): bundled Neo4j Community sidecar process manager
0948dec -
feat(D2): PyInstaller entry point and spec for the desktop backend
Freezes the FastAPI app into a standalone executable. A separate entry point (packaging/desktop_entry.py) was necessary, not optional -- the Docker deployment starts via 'uvicorn app:app', a CLI string-import that has no module/file to resolve against once frozen into a single binary. This entry point imports the app object directly and calls uvicorn.run() programmatically instead, sets BHARATGRAPH_DESKTOP_MODE before any other import (so Phase D1's admin-auth hardening and local config-file loading both apply), and binds to 127.0.0.1 only. Verified for real, not just written: installed PyInstaller, ran an actual build against this codebase, and ran the resulting binary -- /health, /runtime, /docs, and /forensics/ghost-companies all responded correctly over real HTTP; /runtime specifically confirmed the D1/F-10 scraper_workers value is correctly bundled and functional. Cold start is ~4s. The 503s on Neo4j-dependent routes are the correct behavior with no database connected, not failur…cd7f5fc
-
docs(roadmap): resolve audit Open Question #2 -- renumber the colliding Phase 34/35 proposals
The audit's own Finding D-3 and Open Question #2: PHASE_ROADMAP.md's Phase 33/34/35 headings describe different work than what the code labels as Phase 33/34/35. Phase 33 was already resolved (see the prior SUPERSEDED-note commit). This commit resolves the remaining two: - 'Phase 34 -- Vector Search and Hybrid Retrieval' renumbered to 'Phase 76' -- the code already uses Phase 34 for the Forensic Intelligence API (api/routes/forensics.py's own docstring, tests/test_phase34_forensics.py), which is real, shipped, working code. - 'Phase 35 -- Plugin System and YAML Enrichers' renumbered to 'Phase 77' -- the code already uses Phase 35 for the entity-view frontend tab wiring (tests/test_phase35_frontend.py), also real, shipped, working code. Chose to renumber the roadmap's still-unbuilt proposals rather than rename the code's own docstrings/test file names, which would be far more invasive. Moved to 76/77 (next unclaimed numbers after the document's existing high point of 75)…f2840cc -
feat(frontend): finish the Phase 35 entity-view tabs and forensics page (closes B-13)
Completes work that landed partially in an earlier commit -- the entity view's Promise.all expansion, tab buttons, and content panel divs were already in place, but the tab-rendering logic (Timeline/Analytics render, Forensics lazy-load, the 6-tab switch array), the standalone Views.forensics page itself, the Forensics nav link, and the test_six_tabs_defined fix had not yet been applied. Verified: node --check passes. Full suite: 116 passed, 0 failed, 0 errored.49ea00f
-
Merge pull request #86 from abinaze/feat/d1-dataset-provenance-schema
Unverified signaturefeat(D1): local config-file loading + desktop-mode admin-auth hardening4cdca05 -
feat(D1): local config-file loading + desktop-mode admin-auth hardening
Phase D1 scope (PHASE_ROADMAP.md): local-config-file loading added alongside .env, and the DEBUG_MODE admin-auth bypass replaced with a desktop-appropriate default per SECURITY.md's Desktop Application Threat Model. config/settings.py: added get_app_data_dir() (OS-appropriate config dir), _load_local_config()/set_local_setting() (JSON file at <app-data>/config.json). Mirrors exactly how load_dotenv() already works -- injects values into os.environ so every existing os.getenv(...) call site in the codebase picks them up transparently, without needing to touch api/dependencies.py or any scraper. Precedence: real env var (including .env) always wins; local config only fills gaps. A missing/malformed local config file degrades to a warning, never a crash. api/routes/admin.py: _require_admin() now branches on BHARATGRAPH_DESKTOP_MODE. When unset (every current deployment), web behavior is byte-for-byte unchanged. When set, the DEBUG_MODE bypass path is skipped entirely -- a missing ADMIN_…e26a730
-
Merge pull request #85 from abinaze/feat/d1-dataset-provenance-schema
Unverified signaturefeat(D1): add source_type provenance field and Dataset879eaa7