Skip to content

Commit ac53a42

Browse files
asifuddin01claude
andcommitted
One job title, in one place
Four files stated who the author is, independently, and they disagreed. Two called him a deep learning researcher and two an AI/ML engineer; of the two that agreed on the noun, one listed three fields and the other five. A reader who landed on the front page, clicked through to the Summa and then looked at the page source got three answers, and the structured data offered search engines both titles with no order to them. Researcher leads now, because that is the strength — including in the structured data, where the order is not cosmetic. AI/ML engineer stays as a second title there: it is true, it is searched for, and it is not visible text claiming to be the headline. The fix is one editable source rather than one merged sentence. The two fields do different jobs — the front page wants a stance under a very large name, the Summa is the title page and can carry the fields — so they keep their lengths and now share their noun. `getRole()` reads the frontispiece entry, which is where it can be edited in /admin; ROLE in consts is the fallback when that entry is missing, and no longer a third opinion. The Vitae had a fourth copy that listed the fewest fields of any of them; it reads the same source. Bioinformatics and gene sequence analysis stays in the list, at the author's word that it is a strength — it was in one variant and missing from two. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
1 parent 9847cf4 commit ac53a42

8 files changed

Lines changed: 48 additions & 16 deletions

File tree

src/components/home/SummaHead.astro

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
import ArtPlate from '../art/ArtPlate.astro';
33
import { getEntry } from 'astro:content';
44
import { getHomePlates } from '../../lib/collections';
5-
import { AUTHOR, LOCATION } from '../../consts';
5+
import { AUTHOR, LOCATION, ROLE } from '../../consts';
66
77
const fp = await getEntry('site', 'frontispiece');
8-
const role = fp?.data.role ?? '';
8+
const role = fp?.data.role?.trim() || ROLE;
99
const epigraph = fp?.data.epigraph ?? '';
1010
const attribution = fp?.data.attribution ?? 'the author';
1111
const location = fp?.data.location ?? LOCATION;

src/components/layout/BaseHead.astro

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
import { ogPathFor } from '../../lib/og';
3-
import { SITE, AUTHOR, ROLE } from '../../consts';
4-
import { getContact } from '../../lib/collections';
3+
import { SITE, AUTHOR } from '../../consts';
4+
import { getContact, getRole } from '../../lib/collections';
55
66
interface Props {
77
title: string;
@@ -28,6 +28,7 @@ const { title, description, image, type = 'website', noindex } = Astro.props;
2828
const ogPath = image ?? ogPathFor(Astro.url.pathname);
2929
3030
const { github, linkedin } = await getContact();
31+
const role = await getRole();
3132
// Astro's pathname keeps a trailing slash; the links and the served URL do
3233
// not. A canonical that names a different address than the one linked is a
3334
// canonical pointing at a redirect.
@@ -45,8 +46,10 @@ const personLd = {
4546
* him only as a researcher after the frontispiece, the meta tags and the
4647
* title had all moved.
4748
*/
48-
jobTitle: ['AI/ML engineer', 'Deep learning researcher'],
49-
description: ROLE,
49+
/* Both are true and both are searched for, but the order is not cosmetic:
50+
the first is what he leads with, and research is the strength. */
51+
jobTitle: ['Deep learning researcher', 'AI/ML engineer'],
52+
description: role,
5053
url: SITE,
5154
sameAs: [github, linkedin],
5255
address: { '@type': 'PostalAddress', addressLocality: 'Dhaka', addressCountry: 'BD' },

src/consts.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,19 @@
22
export const SITE = 'https://asifuddin.com';
33

44
export const AUTHOR = 'Md. Asif Uddin';
5+
/**
6+
* The fallback self-description.
7+
*
8+
* The one that is actually shown comes from the frontispiece entry, which is
9+
* editable in /admin; this is what every page falls back to if that entry is
10+
* missing. It used to be a third independent copy, and the three disagreed:
11+
* two called him a researcher and one an engineer, and the two that agreed on
12+
* the noun listed different fields. A reader who moved between the front page,
13+
* the Summa and the page source got three answers.
14+
*/
515
export const ROLE =
6-
'Deep learning researcher — medical imaging, vision-language models, causal inference';
16+
'Deep learning researcher — vision, medical imaging, vision-language models, ' +
17+
'causal inference, bioinformatics and gene sequence analysis';
718
export const LOCATION = 'Dhaka, Bangladesh';
819
export const EMAIL = 'md.asif.uddin@g.bracu.ac.bd';
920
export const GITHUB = 'https://github.com/asifuddin01';

src/content/projects/localscholar.mdx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,7 @@ system: true
44
title: "LocalScholar — a private, laptop-scale research assistant"
55
summary: "Built because two scoping reviews needed more PDFs than any hosted chatbot would take. Keeps a permanent local library, answers questions across all of it at once, and grounds every claim in a page you can open. Hybrid BM25 and dense retrieval fused by reciprocal rank, then a cross-encoder reranker — an ablation over 30 hand-written questions takes Recall@5 from 0.833 to 0.933 and MRR from 0.672 to 0.840. Refusal is measured too: four questions the library provably cannot answer, four correct refusals. Citations are computed by matching extracted values back against the retrieved passages rather than reported by the model, which took structured extraction from 6/15 fields to 12/15 while making every remaining value more trustworthy. Runs on an 8 GB M1 with no API key and nothing leaving the machine."
66
cvSummary: "Local-first RAG research assistant (FastAPI, React, ONNX, Ollama). Hybrid BM25 + dense retrieval with RRF fusion and cross-encoder reranking; measured ablation to Recall@5 0.933, MRR 0.840; verified refusal on unanswerable questions; citations computed from passage overlap rather than self-reported. {localscholar.tests} tests, runs offline on 8 GB."
7+
link: "https://asifuddin01.github.io/LocalScholar/"
8+
linkLabel: "See a recorded session"
79
repo: "https://github.com/asifuddin01/LocalScholar"
810
---

src/content/site/frontispiece.mdx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
title: Frontispiece
3-
role: Deep Learning ResearcherVision, Medical Imaging, Vision-Language Models, Causal Inference, Bioinformatics & Gene Sequence Analysis
4-
tagline: AI and machine learning engineer — I build systems that train, run and can be checked.
3+
role: Deep learning researchervision, medical imaging, vision-language models, causal inference, bioinformatics and gene sequence analysis
4+
tagline: Deep learning researcher — I build systems that train, run and can be checked.
55
epigraph: Correlation is cheap. Only an intervention costs anything.
66
attribution: the author
77
location: Dhaka, Bangladesh

src/lib/collections.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { getCollection, getEntry, type CollectionEntry } from 'astro:content';
2-
import { EMAIL, GITHUB, LINKEDIN, LOCATION } from '../consts';
2+
import { EMAIL, GITHUB, LINKEDIN, LOCATION, ROLE } from '../consts';
33
import {
44
APPARATUS_PARTS, TIER_RULES, COVERAGE_RULES, STRANDS,
55
propositionId, type MathTier, type Strand,
@@ -505,6 +505,20 @@ export async function getAxioms(): Promise<CollectionEntry<'axioms'>[]> {
505505
* Contact details. Editable in /admin under Site text, with the values in
506506
* consts.ts as a fallback so nothing breaks if the entry is removed.
507507
*/
508+
/**
509+
* How the site describes its author, from the one place that is editable.
510+
*
511+
* Four files stated this independently and they disagreed — on the job title
512+
* in two of them, and on the list of fields in the two that agreed on the
513+
* title. Reading it makes the frontispiece entry the source, so it can be
514+
* changed in /admin and every page follows, which is the same reason the
515+
* contact block below is read rather than repeated.
516+
*/
517+
export async function getRole(): Promise<string> {
518+
const e = await getEntry('site', 'frontispiece');
519+
return e?.data.role?.trim() || ROLE;
520+
}
521+
508522
export async function getContact(): Promise<{
509523
email: string;
510524
github: string;

src/pages/index.astro

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ const stack = (await getCollection('instrumentarium'))
4747
const works = (await getCollection('works')).sort((a, b) => a.data.plate - b.data.plate);
4848
---
4949
<BaseLayout
50-
title={`${AUTHOR} — AI/ML engineer`}
50+
title={`${AUTHOR} — deep learning researcher`}
5151
description={tagline}
5252
folio="I"
5353
>

src/pages/vitae.astro

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
---
2+
import { getRole } from '../lib/collections';
23
import { fillFacts } from '../lib/facts.mjs';
34
import BaseLayout from '../layouts/BaseLayout.astro';
45
import LesionRule from '../components/ornament/LesionRule.astro';
@@ -15,21 +16,22 @@ const skills = (await getCollection('instrumentarium')).sort((a, b) => a.data.or
1516
const cvMeta = (await getEntry('site', 'cv'))?.data;
1617
const { email, github, linkedin, location } = await getContact();
1718
19+
20+
const role = await getRole();
1821
---
1922
<BaseLayout
2023
title={`Vitae — ${AUTHOR}`}
21-
description={`Curriculum vitae of ${AUTHOR}, deep learning researcher in medical imaging, vision-language models and causal inference.`}
24+
description={`Curriculum vitae of ${AUTHOR} — ${role}.`}
2225
folio="V"
2326
>
2427
<div class="codex cv">
2528
<header class="cv-head">
2629
<div class="cv-headline">
2730
<div>
2831
<h1 class="cv-name" data-press>{AUTHOR}</h1>
29-
<p class="cv-role" data-reveal>
30-
Deep learning researcher — medical imaging, vision-language models,
31-
causal inference
32-
</p>
32+
{/* Read, not restated. This was a fourth hand-written copy of the
33+
same sentence, and it listed fewer fields than the other three. */}
34+
<p class="cv-role" data-reveal>{role}</p>
3335
</div>
3436
<div class="cv-actions">
3537
<a class="cv-btn" href="/vitae/cv">Get the PDF</a>

0 commit comments

Comments
 (0)