diff --git a/README.md b/README.md
index 6b2d8350..c08ec990 100644
--- a/README.md
+++ b/README.md
@@ -10,7 +10,7 @@ a research project on traceability link recovery and documentation consistency a
---
-Built with [Astro](https://astro.build/): eight Zod-validated content collections, a hand-written
+Built with [Astro](https://astro.build/): nine Zod-validated content collections, a hand-written
design, no UI framework, no CSS framework, no theme, dark only. Previously ran on al-folio (Jekyll).
## Develop
@@ -32,10 +32,10 @@ Node 22 or newer (`.nvmrc`).
```
src/
consts.ts SITE (url, lang, repo, email) and SECTIONS, the accent areas
- content.config.ts the eight collections and their schemas
+ content.config.ts the nine collections and their schemas
loaders/bibtex.ts papers.bib → a typed `publications` collection
- data/ papers.bib, venues.yml, authors.yml, people.yml
- content/ approaches/ (8), conferences/ (16), pages/: every word the
+ data/ papers.bib, venues.yml, authors.yml, people.yml, datasets.yml
+ content/ approaches/ (9), conferences/ (16), pages/: every word the
templates print, one Markdown file per page, and standalone/:
pages that are nothing but text
pages/ routes; see the URL table below
@@ -50,7 +50,7 @@ verification/ committed SHA-256 baseline for the published assets
## Content model
-Six collections of content, plus `pages`, the site's own words, and `standalone`, the pages that
+Seven collections of content, plus `pages`, the site's own words, and `standalone`, the pages that
are nothing but text. Every cross-reference is an Astro
`reference()`, so a bad slug, an unknown venue or
a BibTeX key pointing at a page that does not exist **stops the build**. Under Jekyll each of those
@@ -60,28 +60,47 @@ was a silent Liquid lookup that rendered blank.
| -------------- | -------------------------- | ----------------------------------------------- |
| `publications` | `src/data/papers.bib` | parsed at build time; 16 entries |
| `conferences` | `src/content/conferences/` | one page per paper, plus 5 redirect stubs |
-| `approaches` | `src/content/approaches/` | the 8 approaches, ordered by `importance` |
+| `approaches` | `src/content/approaches/` | the 9 approaches, by group, then `importance` |
+| `datasets` | `src/data/datasets.yml` | the datasets strip on `/approaches/` |
| `people` | `src/data/people.yml` | the entry key **is** the `/people/#anchor` |
| `authors` | `src/data/authors.yml` | every author who appears anywhere; name + ORCID |
| `venues` | `src/data/venues.yml` | badge colours, keyed by the BibTeX `abbr` |
+Datasets are not approaches, so `datasets` entries have no page of their own: each links out to the
+dataset and to the paper that introduces it.
+
The BibTeX loader also reads ARDoCo's own `html = {/c/}` field, which links a paper to its
page, and fails the build if that page is missing.
+## Approaches
+
+Each approach is one `.md` in `src/content/approaches/`; the body is the page text. Front matter:
+
+- `title`, `description`, `importance`: `importance` is only a sort key, so renumber freely.
+- `group`: `tlr`, `consistency` or `tools`, required. It decides the section on `/approaches/`.
+- `artifacts`: the pairs of artifact the approach links, e.g. `[['SAD', 'SAM']]`; a generic approach lists several pairs.
+- `figure`: `{ src, alt, plate, thumb? }`. `thumb` is an optional different image for the overview card.
+- `gallery`: extra figures after the body, `[{ src, alt, caption?, plate? }]`. Use these, not Markdown images:
+ the audit requires width and height on every `img`.
+- `repositories`: `[{ name, url }]`.
+- `publications`: BibTeX keys of papers with no conference page of their own, e.g. a journal extension
+ whose page is only a redirect stub: `[fuchss_whos_2026]`. They join the papers found through
+ conference pages' `approaches:`.
+
## Page text — `src/content/pages/`
The `.astro` files hold layout and code. Every word a reader sees that is not computed — a page's
`` and description, its lede, the labels on its chips and buttons, the nav and footer rows,
the home page's introduction — is in `src/content/pages/`, one Markdown file per page:
-| File | Holds |
-| --------------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
-| `site/site.md` | the site `title`, the `tagline` and the default `description` |
-| `site/nav.md` | each section's nav item |
-| `site/footer.md` | the copyright holder and link row; the body is the affiliation |
-| `site/publication.md` | words shared by a publication entry and a conference page, and `linkLabels` |
-| `home.md`, `home/affiliation.md`, `home/poster.md`, `home/links.md` | the home page: buttons and labels; the body is the introduction; one block per fragment |
-| `404.md`, `approaches.md`, `conferences.md`, `people.md`, `publications.md` | one page each; the body, where there is one, is the lede |
+| File | Holds |
+| ------------------------------------------------------------------- | --------------------------------------------------------------------------------------- |
+| `site/site.md` | the site `title`, the `tagline` and the default `description` |
+| `site/nav.md` | each section's nav item |
+| `site/footer.md` | the copyright holder and link row; the body is the affiliation |
+| `site/publication.md` | words shared by a publication entry and a conference page, and `linkLabels` |
+| `home.md`, `home/affiliation.md`, `home/poster.md`, `home/links.md` | the home page: buttons and labels; the body is the introduction; one block per fragment |
+| `404.md`, `approaches.md`, `people.md`, `publications.md` | one page each; the body, where there is one, is the lede |
**Front matter holds short plain strings, the body holds anything with a link or emphasis in it.**
`labels` are short words, `intros` the sentence under a section heading, `plurals` a
@@ -115,7 +134,8 @@ Two shapes, both inherited from the Jekyll site and both still served:
| `/` | `src/pages/index.astro` |
| `/approaches/` | `src/pages/approaches/index.astro` |
| `/approaches//` | `src/pages/approaches/[slug]/` |
-| `/conferences/` | `src/pages/conferences/index.astro` |
+| `/approaches/tv/` | redirect stub to `/approaches/tools/` |
+| `/conferences/` | redirect stub to `/publications/` |
| **`/c/`** | `src/pages/c/[slug].astro` → `.html` |
| `/publications/` | `src/pages/publications/index.astro` |
| `/people/` | `src/pages/people/index.astro` |
@@ -133,6 +153,12 @@ is the _file_ path, so canonical links and sitemap entries go through `canonical
(`se24`, `se25`, `se26-exarch`, `se26-lissa`, `taas26`) redirect to the page that superseded them and
stay that way.
+**`/conferences/`** was published, so it stays as a redirect stub to `/publications/`: that page links every
+`/c/` page, so the list was a duplicate.
+
+**`/approaches/tv/`** was the page for ARDoCo-TV alone, which grew into the tools page, so it stays as a
+redirect stub to `/approaches/tools/`.
+
**`public/assets/**`** — served byte-for-byte at URLs that have been live for years. Never rename,
move, or run them through an optimiser. Pinned by SHA-256 in `verification/asset-sha256.txt`; if you
genuinely add or replace one, rerun `node scripts/generate-asset-baseline.mjs`.
@@ -145,7 +171,8 @@ makes them easy to lose.
**`public/CNAME`** — the deploy replaces the `gh-pages` branch wholesale, so if this file stops being
emitted, `ardoco.de` stops resolving.
-`npm run verify` asserts all four, and the deploy workflow runs it before publishing. It also checks
+`npm run verify` asserts the redirect stubs (the five `/c/` ones, `/conferences/` and
+`/approaches/tv/`), the asset hashes, the two comments and `CNAME`, and the deploy workflow runs it before publishing. It also checks
WCAG contrast, that every internal link and `#fragment` resolves, that images carry intrinsic
dimensions, that no text runs into a link, that no email address appears in the served bytes, and
that `www.youtube.com` (the two screencasts) is still the only third-party origin.
@@ -157,10 +184,10 @@ that `www.youtube.com` (the two screencasts) is still the only third-party origi
| Add a publication | append to `src/data/papers.bib`; `html = {/c/}` links it to its page |
| Add a venue badge | add the abbreviation to `src/data/venues.yml` — an unknown `abbr` fails the build |
| Add a paper page | new `.md` in `src/content/conferences/`; the filename is the `/c/` URL |
-| Add an approach | new `.md` in `src/content/approaches/`; `importance` sets its place in the list |
+| Add an approach | new `.md` in `src/content/approaches/`; see "Approaches" above |
| Link a paper to approaches | `approaches: [lissa, exarch]` in its front matter — bad slugs fail the build |
| Add a person | new entry in `src/data/people.yml`; the key becomes the `/people/#anchor` |
-| Reorder the nav | `navOrder` on a conference entry; it drives the nav and the front-page list alike |
+| Reorder the home list | `navOrder` on a conference entry; it orders the home-page list and the feed |
| Add an image | drop it in `public/assets/img/`, then rerun `scripts/generate-asset-baseline.mjs` |
| Change a page's words | its file in `src/content/pages/`; the nav and footer rows are in `site/` |
| Check bib against Crossref | `python3 scripts/update_bib.py` (stdlib only; `--write` applies the safe fields) |
diff --git a/public/assets/img/approaches/ase26-landscape.svg b/public/assets/img/approaches/ase26-landscape.svg
new file mode 100644
index 00000000..bade42e5
--- /dev/null
+++ b/public/assets/img/approaches/ase26-landscape.svg
@@ -0,0 +1,427 @@
+
+
diff --git a/public/assets/img/approaches/ase26-traceview.png b/public/assets/img/approaches/ase26-traceview.png
new file mode 100644
index 00000000..d2b82eba
Binary files /dev/null and b/public/assets/img/approaches/ase26-traceview.png differ
diff --git a/public/assets/img/approaches/ase26-traceviz.png b/public/assets/img/approaches/ase26-traceviz.png
new file mode 100644
index 00000000..3614c2c5
Binary files /dev/null and b/public/assets/img/approaches/ase26-traceviz.png differ
diff --git a/public/assets/img/approaches/ecsa21-swattr.svg b/public/assets/img/approaches/ecsa21-swattr.svg
index 22483a5b..73c883b2 100644
--- a/public/assets/img/approaches/ecsa21-swattr.svg
+++ b/public/assets/img/approaches/ecsa21-swattr.svg
@@ -1,4 +1,4 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/public/assets/img/approaches/icse24-arcotl.svg b/public/assets/img/approaches/icse24-arcotl.svg
new file mode 100644
index 00000000..e677a846
--- /dev/null
+++ b/public/assets/img/approaches/icse24-arcotl.svg
@@ -0,0 +1,781 @@
+
+
diff --git a/public/assets/img/approaches/icse24-ardocode.svg b/public/assets/img/approaches/icse24-ardocode.svg
index 937ceaf3..ef2cd8b1 100644
--- a/public/assets/img/approaches/icse24-ardocode.svg
+++ b/public/assets/img/approaches/icse24-ardocode.svg
@@ -1,4 +1,4 @@
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/public/assets/img/approaches/taas26-artemis.svg b/public/assets/img/approaches/taas26-artemis.svg
new file mode 100644
index 00000000..70d006f1
--- /dev/null
+++ b/public/assets/img/approaches/taas26-artemis.svg
@@ -0,0 +1,361 @@
+
+
diff --git a/public/assets/img/people/fuchss_dominik.jpg b/public/assets/img/people/fuchss_dominik.jpg
index a7bd9e72..1e289d49 100644
Binary files a/public/assets/img/people/fuchss_dominik.jpg and b/public/assets/img/people/fuchss_dominik.jpg differ
diff --git a/scripts/audit-features.mjs b/scripts/audit-features.mjs
index 632abf62..71e52e9d 100644
--- a/scripts/audit-features.mjs
+++ b/scripts/audit-features.mjs
@@ -69,6 +69,18 @@ const PUB_COUNT = (readFileSync(join(SRC, 'data/papers.bib'), 'utf8').match(/^@/
`conferences: all ${stubs.length} legacy redirect stubs refresh and are noindex`,
);
+ // The two folded-away pages: published URLs that now only point onward.
+ for (const [file, to] of [
+ ['conferences/index.html', '/publications/'],
+ ['approaches/tv/index.html', '/approaches/tools/'],
+ ]) {
+ const html = has(file) ? read(file) : '';
+ check(
+ html.includes(` {
const fm = frontmatter(join(SRC, 'content/conferences', f));
if (!/^publication:/m.test(fm)) return false;
diff --git a/src/components/ApproachMap.astro b/src/components/ApproachMap.astro
new file mode 100644
index 00000000..3a3313ef
--- /dev/null
+++ b/src/components/ApproachMap.astro
@@ -0,0 +1,425 @@
+---
+import { getCollection } from 'astro:content';
+import { ARTIFACTS, type Artifact } from '../consts';
+import { approachPath } from '../lib/urls';
+import { getPage } from '../lib/pages';
+
+/*
+ * The approach map at the top of /approaches/: the four kinds of artifact
+ * ARDoCo connects, and on each connection the approaches that recover it.
+ *
+ * Hand-drawn inline SVG rather than an image, for two reasons: every label is
+ * a real link to its approach, reachable by keyboard and read by a screen
+ * reader; and every colour is a token from tokens.css, so the map follows
+ * the section accent like the rest of the page.
+ *
+ * Coordinates are in the viewBox (840 × 470); the SVG scales to its column.
+ * Below 48rem that would shrink the labels to a few pixels, so there the SVG
+ * is hidden and the same edges are drawn as a list of rows instead —
+ * "SAD ↔ SAM: SWATTR, ArTEMiS, …". Both are built from EDGES below, and
+ * display: none takes the hidden one out of the tab order and the
+ * accessibility tree, so no link is announced twice.
+ *
+ * The SVG is named by aria-label rather than , which browsers show
+ * as a hover tooltip. Its carries a fixed id (map-desc), so the
+ * component is meant to appear once per page. It sits on /approaches/ and on
+ * the home page, but never twice on either.
+ */
+
+type Node = 'req' | 'sad' | 'sam' | 'code';
+
+/* The words: the node names in src/content/pages/approaches.md. */
+const page = await getPage('approaches');
+const NODE_KEY = { req: 'Requirements', sad: 'Sad', sam: 'Sam', code: 'Code' } as const;
+const name = (n: Node) => page.label(`node${NODE_KEY[n]}`);
+const sub = (n: Node) => page.label(`node${NODE_KEY[n]}Sub`);
+
+/* Box centres. Documentation and model share the top row, requirements and
+ code the bottom one, so the two trace-link families read left to right. */
+const W = 180;
+const H = 64;
+const NODES: Record = {
+ sad: { cx: 140, cy: 100 },
+ sam: { cx: 700, cy: 100 },
+ req: { cx: 140, cy: 360 },
+ code: { cx: 700, cy: 360 },
+};
+
+/*
+ * The lines, drawn under the boxes. `dashed` is consistency analysis rather
+ * than link recovery; `transitive` is TransArC's route SAD → SAM → Code: it
+ * leaves SAD's top, crosses above everything, runs down behind the SAM box and
+ * on into Code, so it reads as one path through the model rather than a
+ * shortcut past it.
+ */
+const LINES: { d: string; kind?: 'dashed' | 'transitive' }[] = [
+ { d: 'M230 84 H610' }, // SAD — SAM
+ { d: 'M230 120 H610', kind: 'dashed' }, // SAD — SAM, inconsistencies
+ { d: 'M660 132 V328' }, // SAM — Code
+ { d: 'M230 132 L610 328' }, // SAD — Code
+ { d: 'M230 360 H610' }, // Requirements — Code
+ { d: 'M110 392 C95 455 185 455 170 392' }, // Requirements — Requirements
+ { d: 'M140 68 V48 Q140 28 160 28 H730 Q750 28 750 48 V328', kind: 'transitive' },
+];
+
+/*
+ * The labels on the lines, one per approach and connection, keyed by approach
+ * id; x and y are the label's centre, on its line. An approach appears once
+ * per connection it covers (LiSSA twice). `via` marks a transitive
+ * connection, which the list below the breakpoint spells out.
+ */
+const EDGES: { id: string; from: Node; to: Node; via?: Node; x: number; y: number }[] = [
+ { id: 'swattr', from: 'sad', to: 'sam', x: 350, y: 84 },
+ { id: 'artemis', from: 'sad', to: 'sam', x: 490, y: 84 },
+ { id: 'inconsistency-detection', from: 'sad', to: 'sam', x: 420, y: 120 },
+ { id: 'arcotl', from: 'sam', to: 'code', x: 660, y: 230 },
+ { id: 'ardocode', from: 'sad', to: 'code', x: 367, y: 203 },
+ { id: 'exarch', from: 'sad', to: 'code', x: 473, y: 258 },
+ { id: 'transarc', from: 'sad', via: 'sam', to: 'code', x: 420, y: 28 },
+ { id: 'lissa', from: 'req', to: 'code', x: 420, y: 360 },
+ { id: 'lissa', from: 'req', to: 'req', x: 140, y: 432 },
+];
+
+/* A hand-written id that names no approach would render a dead link; fail the
+ build instead, naming it. */
+const approaches = new Map((await getCollection('approaches')).map((a) => [a.id, a]));
+for (const { id } of EDGES) {
+ if (!approaches.has(id)) {
+ throw new Error(
+ `ApproachMap: unknown approach "${id}" (no src/content/approaches/${id}.md) in EDGES`,
+ );
+ }
+}
+const title = (id: string) => approaches.get(id)!.data.title;
+
+/* The edges are drawn by hand and the `artifacts` front matter is written by
+ hand, so they can drift apart. Every edge must be a pair the approach lists
+ (either order; a transitive edge counts as its two ends). The reverse is
+ NOT required: the map deliberately shows LiSSA only for its requirements
+ pairs, although its `artifacts` also list SAD pairs. */
+const ARTIFACT_OF: Record = {
+ req: ARTIFACTS[0],
+ sad: ARTIFACTS[1],
+ sam: ARTIFACTS[2],
+ code: ARTIFACTS[3],
+};
+for (const e of EDGES) {
+ const [a, b] = [ARTIFACT_OF[e.from], ARTIFACT_OF[e.to]];
+ const listed = approaches
+ .get(e.id)!
+ .data.artifacts.some(([x, y]) => (x === a && y === b) || (x === b && y === a));
+ if (!listed) {
+ throw new Error(
+ `ApproachMap: ${e.id} is drawn linking ${a} and ${b}, but its \`artifacts\` ` +
+ `in src/content/approaches/${e.id}.md does not list that pair`,
+ );
+ }
+}
+
+/* What a connection is called, in words: "SAD with SAM", or "SAD to Code via
+ SAM" for a transitive one. Used for each label's accessible name and the
+ SVG's description, where a glyph would be read out as "left right arrow". */
+const pairWords = (e: (typeof EDGES)[number]) =>
+ e.via
+ ? `${name(e.from)} to ${name(e.to)} via ${name(e.via)}`
+ : `${name(e.from)} with ${name(e.to)}`;
+
+/* The same connection for the narrow-screen rows, with the glyph shown and the
+ words for a screen reader (see the template). The row key is the words,
+ which are unique per connection. */
+const glyph = (e: (typeof EDGES)[number]) => (e.via ? '→' : '↔');
+const parts = (e: (typeof EDGES)[number]) =>
+ (e.via ? [e.from, e.via, e.to] : [e.from, e.to]).map(name);
+
+/* Label size, estimated: the labels are set in IBM Plex Mono, whose every
+ glyph is 0.6 em wide, so the text width is known without measuring it.
+ FONT must equal the font-size of `.label text` in the style block below. */
+const FONT = 14;
+const pillWidth = (text: string) => text.length * FONT * 0.6 + 24;
+const PILL_H = 26;
+
+/* The boxes an approach touches, over all its edges (LiSSA: requirements and
+ code; TransArC: SAD, SAM and code). Hovering or focusing any of its labels
+ lights them all up; see `.graph:has(…)` in the style block. */
+const touches = new Map>();
+for (const e of EDGES) {
+ const set = touches.get(e.id) ?? new Set();
+ for (const n of [e.from, e.to, e.via]) if (n) set.add(n);
+ touches.set(e.id, set);
+}
+
+const labels = EDGES.map((e) => ({
+ ...e,
+ text: title(e.id),
+ href: approachPath(e.id),
+ aria: `${title(e.id)}: links ${pairWords(e)}`,
+ w: pillWidth(title(e.id)),
+ on: [...touches.get(e.id)!].map((n) => `on-${n}`),
+}));
+
+/* The same edges as rows, one per connection, in EDGES order. */
+const rows = new Map<
+ string,
+ { parts: string[]; glyph: string; items: { id: string; text: string; href: string }[] }
+>();
+for (const l of labels) {
+ const key = pairWords(l);
+ const row = rows.get(key) ?? { parts: parts(l), glyph: glyph(l), items: [] };
+ if (!row.items.some((r) => r.id === l.id)) {
+ row.items.push({ id: l.id, text: l.text, href: l.href });
+ }
+ rows.set(key, row);
+}
+
+/* The SVG's name and description; the description is the list in prose, for
+ a reader who gets the SVG but cannot see it. */
+const desc = [...rows].map(([key, row]) => `${key}: ${row.items.map((r) => r.text).join(', ')}.`);
+---
+
+
+ {/* role="group", not role="img": img makes everything inside it
+ presentational, and the labels inside are links that must stay
+ reachable. */}
+
+
+
+
+
+
diff --git a/src/components/Artifacts.astro b/src/components/Artifacts.astro
new file mode 100644
index 00000000..ce9ca921
--- /dev/null
+++ b/src/components/Artifacts.astro
@@ -0,0 +1,60 @@
+---
+/**
+ * The pairs of artifact an approach links — `artifacts` in its front matter,
+ * e.g. SAD ↔ SAM — as a row of tags. Used on the approach's card on
+ * /approaches/ and under the lede on its own page. Most approaches link one
+ * pair; a generic one like LiSSA lists several.
+ */
+interface Props {
+ pairs: [string, string][];
+}
+const { pairs } = Astro.props;
+---
+
+
+ {pairs.map(([from, to]) => (
+
+ {from}{' '}
+ {/* The arrow says "linked with". A glyph, so it lives here rather than
+ in the content; a screen reader gets the words instead. The explicit
+ spaces keep the words from running into the tags in the accessible
+ text ("SADlinked withSAM"): Astro strips the whitespace between
+ elements, so none would survive otherwise. */}
+
+ ↔
+ {' '}
+ linked with{to}
+
+ ))}
+
+
+
diff --git a/src/components/Figure.astro b/src/components/Figure.astro
index c63a000b..5e67b4aa 100644
--- a/src/components/Figure.astro
+++ b/src/components/Figure.astro
@@ -5,15 +5,18 @@ interface Props {
src: string;
alt: string;
plate?: boolean;
+ /** Plain text under the image. The approach figure has none; a gallery image may. */
+ caption?: string;
}
-const { src, alt, plate = true } = Astro.props;
+const { src, alt, plate = true, caption } = Astro.props;
const { width, height } = intrinsic(src);
/*
* How wide the figure is allowed to get.
*
- * The approach diagrams are small and wide (icsa25-transarc.svg is 337x110).
+ * The approach diagrams are small and wide (icsa25-transarc.svg is 337x110;
+ * despite its name it depicts ExArch).
* Stretched across the full 1120px column they dominate the page and dwarf the
* text they illustrate, so vectors are capped at roughly the prose measure.
*
@@ -26,6 +29,7 @@ const maxWidth = isVector ? 'var(--measure)' : `${width}px`;
+ {caption && {caption}}
diff --git a/src/components/Nav.astro b/src/components/Nav.astro
index 48465f51..21b99761 100644
--- a/src/components/Nav.astro
+++ b/src/components/Nav.astro
@@ -67,7 +67,7 @@ const { title } = await siteText();
color: var(--teal);
}
- /* No hamburger: five short items wrap onto a second line on narrow screens,
+ /* No hamburger: four short items move onto a second line on narrow screens,
which is less machinery and one fewer thing that can trap focus. */
ul {
display: flex;
@@ -86,6 +86,23 @@ const { title } = await siteText();
border-bottom: 1px solid transparent;
}
+ /* Phones: the brand gets its own row and the links share the full width of
+ the second, spread edge to edge rather than hanging off the right, so the
+ sticky bar stays two rows high down to 320px. */
+ @media (max-width: 32rem) {
+ .inner {
+ gap: 0.25rem;
+ padding-block: 0.6rem 0.2rem;
+ }
+ ul {
+ flex: 1 0 100%;
+ flex-wrap: nowrap;
+ justify-content: space-between;
+ gap: 0.75rem;
+ margin: 0;
+ }
+ }
+
/* Each nav item previews the accent of the section it leads to, so the
colour change on arrival is continuous rather than a surprise. */
ul a[data-section='home']:hover,
@@ -98,11 +115,6 @@ const { title } = await siteText();
color: var(--azure);
border-color: var(--azure-line);
}
- ul a[data-section='conferences']:hover,
- ul a[data-section='conferences'][aria-current='page'] {
- color: var(--violet);
- border-color: var(--violet-line);
- }
ul a[data-section='publications']:hover,
ul a[data-section='publications'][aria-current='page'] {
color: var(--green);
diff --git a/src/components/pub/VenueBadge.astro b/src/components/pub/VenueBadge.astro
index c1e08360..f0c9137d 100644
--- a/src/components/pub/VenueBadge.astro
+++ b/src/components/pub/VenueBadge.astro
@@ -1,9 +1,10 @@
---
import { getEntry } from 'astro:content';
import type { DataEntryMap } from 'astro:content';
+import type { VenueRef } from '../../lib/publications';
interface Props {
- abbr: { collection: 'venues'; id: string } | undefined;
+ abbr: VenueRef | undefined;
}
const { abbr } = Astro.props;
diff --git a/src/consts.ts b/src/consts.ts
index a22dad06..2d18cac8 100644
--- a/src/consts.ts
+++ b/src/consts.ts
@@ -21,5 +21,24 @@ export const SITE = {
* A list rather than a bare union so the nav rows in
* src/content/pages/site/nav.md are validated against it.
*/
-export const SECTIONS = ['home', 'approaches', 'conferences', 'publications', 'people'] as const;
+export const SECTIONS = ['home', 'approaches', 'publications', 'people'] as const;
export type Section = (typeof SECTIONS)[number];
+
+/**
+ * The four kinds of artifact an approach can link, as the short tags the site
+ * shows: `SAD` is architecture documentation, `SAM` an architecture model. A
+ * list rather than a bare union so `artifacts` in the approaches schema can
+ * validate against it.
+ */
+export const ARTIFACTS = ['Requirements', 'SAD', 'SAM', 'Code'] as const;
+export type Artifact = (typeof ARTIFACTS)[number];
+
+/**
+ * The groups /approaches/ is divided into, in the order the page shows them:
+ * traceability link recovery, consistency analysis, then the tools built on
+ * both. Each approach names its group in front matter (`group`); its
+ * `importance` orders it within that group. A list rather than a bare union so
+ * the schema can validate against it and the page can iterate it.
+ */
+export const APPROACH_GROUPS = ['tlr', 'consistency', 'tools'] as const;
+export type ApproachGroup = (typeof APPROACH_GROUPS)[number];
diff --git a/src/content.config.ts b/src/content.config.ts
index 37332553..e46f5719 100644
--- a/src/content.config.ts
+++ b/src/content.config.ts
@@ -2,7 +2,7 @@ import { defineCollection, reference } from 'astro:content';
import { z } from 'zod';
import { file, glob } from 'astro/loaders';
import { bibtexLoader } from './loaders/bibtex';
-import { SECTIONS } from './consts';
+import { APPROACH_GROUPS, ARTIFACTS, SECTIONS } from './consts';
/** Conference brand colours for the publication badges, keyed by BibTeX `abbr`. */
const venues = defineCollection({
@@ -51,8 +51,8 @@ const publications = defineCollection({
authors: z.array(z.object({ first: z.string(), last: z.string() })).min(1),
year: z.number().int(),
month: z.number().int().min(1).max(12).optional(),
- // An abbr with no venues.yml entry fails the build. Five entries carry no
- // abbr at all, which is fine — they render without a badge.
+ // An abbr with no venues.yml entry fails the build. An entry without an
+ // abbr renders without a badge.
abbr: reference('venues').optional(),
booktitle: z.string().optional(),
journal: z.string().optional(),
@@ -75,29 +75,101 @@ const publications = defineCollection({
}),
});
-const figure = z.object({
+/**
+ * An image under public/, as every figure and gallery picture is given.
+ *
+ * The approach diagrams are dark-on-transparent SVGs drawn for a white page.
+ * On a near-black background they need a light plate behind them. Only
+ * screenshots, which bring their own background, turn it off.
+ *
+ * The draw.io exports (ecsa21-swattr.svg, icse24-ardocode.svg) carry
+ * `color-scheme: light dark` and light-dark() fills, so on this dark site
+ * they drew black boxes onto the white plate. Their root style is pinned
+ * to `color-scheme: light`; a fresh export needs the same edit.
+ */
+const image = z.object({
src: z.string(),
alt: z.string(),
- /**
- * The approach diagrams are dark-on-transparent SVGs drawn for a white page.
- * On a near-black background they need a light plate behind them — except
- * the two that were authored without one.
- */
plate: z.boolean().default(true),
});
+const figure = image
+ .extend({
+ /**
+ * A different image for the approach's card on /approaches/, where the
+ * figure itself would not read at thumbnail size — a screenshot of a tool
+ * says more there than its architecture diagram. The card shows
+ * `thumb ?? src`, on no plate (a thumb is a screenshot with a background
+ * of its own). Only approaches use it; a conference page ignores it.
+ */
+ thumb: z.string().optional(),
+ })
+ .strict();
+
+/**
+ * One extra image on an approach page, after its text. Front matter rather
+ * than a Markdown image in the body: Markdown cannot carry width and height,
+ * and scripts/audit-site.mjs fails any without them.
+ */
+const galleryImage = image
+ .extend({
+ /** Shown under the image; plain text. */
+ caption: z.string().optional(),
+ })
+ .strict();
+
const approaches = defineCollection({
loader: glob({ base: 'src/content/approaches', pattern: '**/*.md' }),
schema: z.object({
title: z.string(),
description: z.string(),
- /** Sort key for /approaches/, roughly the order the work was published. */
+ /** The section of /approaches/ the card sits in; see APPROACH_GROUPS. */
+ group: z.enum(APPROACH_GROUPS),
+ /** Order within the group, lowest first: roughly the order the work was published. */
importance: z.number().int(),
+ /**
+ * The pairs of artifact the approach links, each as two short tags: `SAD`
+ * (architecture documentation), `SAM` (architecture model), `Code`,
+ * `Requirements`. A list of pairs rather than one list of tags, because a
+ * generic approach like LiSSA links several pairs, and a flat list would
+ * read as one chain (Requirements ↔ SAD ↔ Code ↔ …). Shown as tags on the
+ * card and under the page's lede.
+ */
+ artifacts: z.array(z.tuple([z.enum(ARTIFACTS), z.enum(ARTIFACTS)])).default([]),
repositories: z.array(z.object({ name: z.string(), url: z.url() })).default([]),
+ /**
+ * Papers about the approach that have no conference page of their own, as
+ * references to BibTeX entries (the id is the BibTeX key). Most papers
+ * reach an approach the other way round, through a conference page's
+ * `approaches:`; this is for the ones that only exist as a redirect stub,
+ * like a journal extension, whose stub would otherwise attach the
+ * approach to the paper it redirects to. Merged with the conference
+ * pages by relatedPublications() in lib/approaches.ts.
+ */
+ publications: z.array(reference('publications')).default([]),
figure: figure.optional(),
+ gallery: z.array(galleryImage).default([]),
}),
});
+/**
+ * Datasets ARDoCo publishes, shown as a strip at the foot of /approaches/.
+ * They have no pages of their own: each links out to the dataset and to the
+ * paper that introduces it. File order is page order.
+ */
+const datasets = defineCollection({
+ loader: file('src/data/datasets.yml'),
+ schema: z
+ .object({
+ name: z.string(),
+ description: z.string(),
+ url: z.url(),
+ /** The paper that introduces it, by BibTeX key; a stale key fails the build. */
+ publication: reference('publications').optional(),
+ })
+ .strict(),
+});
+
/**
* A map of label -> target, e.g. links.paper.ieee. Labels are resolved by
* `linkLabels` in src/content/pages/site/publication.md. Targets are absolute
@@ -132,10 +204,9 @@ const conferences = defineCollection({
inPress: z.boolean().default(false),
/**
- * Navbar label and ordering, previously the `children:` array in
- * _pages/conferences.md. That one list drove both the dropdown and the
- * front-page publication order, so it had to be edited in lockstep with
- * this directory; now each entry carries its own position.
+ * Label and ordering of the home page's publication list (and the feed),
+ * previously the `children:` array in _pages/conferences.md, which also
+ * drove a navbar dropdown. Each entry now carries its own position.
*/
navLabel: z.string().optional(),
navOrder: z.number().int().optional(),
@@ -249,6 +320,7 @@ export const collections = {
people,
publications,
approaches,
+ datasets,
conferences,
pages,
standalone,
diff --git a/src/content/approaches/arcotl.md b/src/content/approaches/arcotl.md
index d2c0510a..524b7667 100644
--- a/src/content/approaches/arcotl.md
+++ b/src/content/approaches/arcotl.md
@@ -2,13 +2,15 @@
title: ArCoTL
description: ArCoTL – TLR between Software Architecture Models and Code.
importance: 2
+group: tlr
+artifacts: [['SAM', 'Code']]
figure:
- src: /assets/img/approaches/icse24-transarc.svg
- alt: ArCoTL Overview
+ src: /assets/img/approaches/icse24-arcotl.svg
+ alt: ArCoTL heuristic computation graph
plate: true
repositories:
- name: Implementation
- url: https://github.com/ardoco/tlr
+ url: https://github.com/ardoco/ardoco
- name: Replication Package (ICSE 2024)
url: https://github.com/ardoco/Replication-Package-ICSE24_Recovering-Trace-Links-Between-Software-Documentation-And-Code
---
@@ -16,7 +18,7 @@ repositories:
ArCoTL (Architecture–Code Trace Links) focuses on linking a given architecture model (SAM) to the source code.
It assumes you have a formal model of the system's components and interfaces, and wants to find the corresponding code.
ArCoTL transforms both the architecture model and the code into intermediate representations (e.g. simplified graphs) and then applies various heuristics to match elements.
-These heuristics include standalone rules and dependent rules (which consider relationships) plus filters to refine the links.
+These heuristics are organized as a computation graph: standalone heuristics, dependent heuristics (which build on the results of others), aggregators that combine their results, and filters that refine the links.
-- How it works: Starting from a SAM and the codebase, ArCoTL builds simplified model and code representations. It then uses text similarity, naming conventions, and dependency heuristics to propose links between each model component and code artifact.
+- How it works: Starting from a SAM and the codebase, ArCoTL builds simplified model and code representations. Standalone heuristics look at the component name, package, interface name, methods, and path. Dependent heuristics use inheritance, ambiguous packages, common words, component relations, and interface provision. Aggregators (best, max, first) combine these results, and filters refine them to propose links between each model component and code artifact.
- Effectiveness: ArCoTL turned out to be very effective on its own. In experiments, the model-to-code step (ArCoTL) achieved an average F1 of ~0.98.
diff --git a/src/content/approaches/ardocode.md b/src/content/approaches/ardocode.md
index 8847a2c3..c61feddd 100644
--- a/src/content/approaches/ardocode.md
+++ b/src/content/approaches/ardocode.md
@@ -1,14 +1,16 @@
---
title: ArDoCode
description: ArDoCode – TLR between Software Architecture Documentation and Code.
-importance: 5
+importance: 4
+group: tlr
+artifacts: [['SAD', 'Code']]
figure:
src: /assets/img/approaches/icse24-ardocode.svg
- alt: ArCoTL Overview
- plate: false
+ alt: ArDoCode Overview
+ plate: true
repositories:
- name: Implementation
- url: https://github.com/ardoco/tlr
+ url: https://github.com/ardoco/ardoco
- name: Replication Package (ICSE 2024)
url: https://github.com/ardoco/Replication-Package-ICSE24_Recovering-Trace-Links-Between-Software-Documentation-And-Code
---
@@ -18,4 +20,4 @@ Instead of first building a formal model, ArDoCode directly matches architecture
In practice, it extracts key terms from the documentation and tries to align them with names in the code (e.g. class or module names) as if the code were the model.
- Key idea: Apply the SWATTR approach without an explicit SAM by interpreting the codebase as a model. For example, if the doc mentions a component "WebUI" and there is a WebUI package in code, ArDoCode will link them.
-- Effectiveness: Because it skips the formal modeling step, ArDoCode is easier to apply but less precise. In evaluations, ArDoCode achieved a weighted F1 of only ~0.62, substantially lower than the full TransArC method. It serves mainly as a baseline and demonstrates that without structured models, the TLR performance drops.
+- Effectiveness: Because it skips the formal modeling step, ArDoCode is easier to apply but less precise. In evaluations, ArDoCode achieved a weighted average F1 of only ~0.62 (as reported in the ICSA 2025 paper), compared to 0.87 for the full TransArC method. It serves mainly as a baseline and demonstrates that without structured models, the TLR performance drops.
diff --git a/src/content/approaches/artemis.md b/src/content/approaches/artemis.md
new file mode 100644
index 00000000..7532ac59
--- /dev/null
+++ b/src/content/approaches/artemis.md
@@ -0,0 +1,28 @@
+---
+title: ArTEMiS
+description: ArTEMiS – LLM-based Architecture Entity Recognition for TLR between Software Architecture Documentation and Models.
+importance: 6
+group: tlr
+artifacts: [['SAD', 'SAM']]
+figure:
+ src: /assets/img/approaches/taas26-artemis.svg
+ alt: ArTEMiS Overview
+ plate: true
+publications: [fuchss_whos_2026]
+repositories:
+ - name: Implementation
+ url: https://github.com/ardoco/ardoco
+ - name: Named Architecture Entity Recognition library
+ url: https://github.com/ardoco/named-architecture-entity-recognition
+ - name: Replication Package (TAAS)
+ url: https://github.com/ardoco/Replication-Package-TAAS25_LLM-assisted-Software-Traceability-with-Architecture-Entity-Recognition
+---
+
+ArTEMiS (Architecture Traceability with Entity Matching via Semantic inference) links software architecture documentation (SAD) to software architecture models (SAM) using an LLM.
+A specialized, LLM-based named entity recognition finds the architecture entities mentioned in the SAD, including coreferences and alternative names, and matches them to the elements of the SAM.
+It replaces the text stages of [SWATTR](/approaches/swattr/) through the NER connection generator and is built on the Named Architecture Entity Recognition (NAER) library.
+ArTEMiS was introduced in the TAAS journal extension of the [ExArch](/approaches/exarch/) paper, "Who's Who? LLM-assisted Software Traceability with Architecture Entity Recognition".
+
+- How it works: A first prompt asks the LLM to name the architecturally relevant components in the SAD, with their alternative names and the lines that mention them. The names of the SAM elements are given as positive examples. A second prompt converts this output to JSON. The identified entities are then matched to the SAM elements by string similarity and embedding similarity.
+- Hybrids: ArTEMiS can also stand in for SWATTR in larger pipelines that link the SAD to code. ArTEMiS in ExArch uses an LLM-generated SAM, and ArTEMiS in TransArC uses a manual SAM.
+- Results: With GPT-5, ArTEMiS achieved an average F1 of 0.81 for SAD-SAM links, slightly above SWATTR (0.80), and a higher average recall (0.85 vs. 0.77). Integrated into TransArC, it raised the average F1 for SAD-code links from 0.80 to 0.85 and the average recall from 0.77 to 0.87. TransArC with ExArch (GPT-4o) and ArTEMiS (GPT-5) reached a weighted average F1 of 0.87, the best of the approaches that need no manual SAM (ArDoCode: 0.62).
diff --git a/src/content/approaches/exarch.md b/src/content/approaches/exarch.md
index 99d3c03f..7866e670 100644
--- a/src/content/approaches/exarch.md
+++ b/src/content/approaches/exarch.md
@@ -1,23 +1,27 @@
---
title: ExArch
description: ExArch – LLM-based Architecture Component Name Extraction for TLR between Software Architecture Documentation and Code.
-importance: 4
+importance: 5
+group: tlr
+artifacts: [['SAD', 'Code']]
figure:
src: /assets/img/approaches/icsa25-transarc.svg
alt: ExArch Overview
plate: true
+publications: [fuchss_whos_2026]
repositories:
- name: Implementation
- url: https://github.com/ardoco/tlr
+ url: https://github.com/ardoco/ardoco
- name: Replication Package (ICSA 2025)
url: https://github.com/ardoco/Replication-Package-ICSA25_Enabling-Arch-Traceability-by-LLM-based-Arch-Component-Name-Extraction
---
-ExArch extends the TransArC idea by using an LLM to generate a simple architecture model (SAM).
-In this approach, instead of requiring a hand-made SAM, a large language model (such as GPT-4) is prompted to extract or invent the main component names from the SAD (and optionally from code).
-These names serve as a minimal architecture model (i.e. a list of components).
+ExArch extends the TransArC idea by using an LLM to generate a simple SAM.
+In this approach, instead of requiring a hand-made SAM, a large language model (GPT-4o) is prompted to extract the main component names from the SAD and the source code.
+These names serve as a minimal architecture model, i.e. a simple SAM that is just a list of components.
Then, as in TransArC, these LLM-derived components are matched to code.
The goal is to bridge the SAD–code gap without manual modeling.
-- How it works: Given the software architecture text and the codebase, the system asks the LLM to list likely component names. That list of names forms a "Simple Software Architecture Model" (SSAM). Finally, code elements with matching names or descriptions are linked to the documentation. This pipeline avoids needing an explicit UML model.
+- How it works: The code goes through a feature extraction step, and both the software architecture text and the extracted code features feed the prompting strategies, which ask the LLM to list likely component names. That list of names forms the simple SAM. Finally, code elements with matching names or descriptions are linked to the documentation. This pipeline avoids needing an explicit UML model.
- Effectiveness: ExArch achieved very competitive results. Using GPT-4o, it obtained a weighted F1 of about 0.86, nearly as good as the original TransArC with a hand-made model (F1 0.87). It also substantially outperformed the ArDoCode baseline (which scored ~0.62). This shows that LLMs can automatically infer the key architectural components.
+- Extension: The TAAS journal extension, "Who's Who? LLM-assisted Software Traceability with Architecture Entity Recognition", adds [ArTEMiS](/approaches/artemis/).
diff --git a/src/content/approaches/inconsistency-detection.md b/src/content/approaches/inconsistency-detection.md
index fceb724b..1ab51bba 100644
--- a/src/content/approaches/inconsistency-detection.md
+++ b/src/content/approaches/inconsistency-detection.md
@@ -2,13 +2,15 @@
title: Inconsistency Detection
description: Documentation-Model-Inconsistency-Analysis pipeline.
importance: 8
+group: consistency
+artifacts: [['SAD', 'SAM']]
figure:
src: /assets/img/approaches/icsa23-inconsistency.svg
alt: Approach Overview
plate: true
repositories:
- name: Implementation
- url: https://github.com/ardoco/inconsistency-detection
+ url: https://github.com/ardoco/ardoco
- name: Replication Package (ICSA 2023)
url: https://github.com/ardoco/DetectingInconsistenciesInSoftwareArchitectureDocumentationUsingTraceabilityLinkRecovery
---
@@ -24,7 +26,7 @@ It identifies two kinds of issues:
(a) **Model Entity Absent from Text (MEAT)**: components or interfaces that appear in the model but are never described in the documentation;
(b) **Text Entity Absent from Model (TEAM)**: elements mentioned in the text that do not exist in the model.
-The method runs a TLR procedure (namely SWATTR) and then flags any model element with no corresponding text link (a MEAT) or any sentence that refers to a non-modeled item (a TEAM).
+The method runs a TLR procedure (an extended version of SWATTR that adds a filter and recommended instances) and then flags any model element with no corresponding text link (a MEAT) or any sentence that refers to a non-modeled item (a TEAM).
- Detection strategy: Use the TLR results as a bridge. After linking as many sentences to model elements as possible, any "orphan" model nodes or text mentions indicate a consistency gap. For example, if the model has a "Cache" component with no sentence linked, that is a MEAT; if the doc talks about "Common" but the model lacks it, that is a TEAM.
-- Results: The approach achieved an excellent F1 (0.81) for the underlying trace recovery. For inconsistency detection, it attained ~93% accuracy in identifying MEAT and ~75% for TEAM, significantly better than naive baselines. These results suggest that using trace links is a promising way to find documentation-model mismatches.
+- Results: The extended SWATTR achieved an excellent F1 (0.81) for the underlying trace recovery. For inconsistency detection, it attained ~93% accuracy in identifying MEAT and ~75% accuracy for TEAM (reported as UME and MME in the ICSA 2023 paper), significantly better than competing baselines. These results suggest that using trace links is a promising way to find documentation-model mismatches.
diff --git a/src/content/approaches/lissa.md b/src/content/approaches/lissa.md
index 7ce6dace..9e30fcfe 100644
--- a/src/content/approaches/lissa.md
+++ b/src/content/approaches/lissa.md
@@ -1,14 +1,25 @@
---
title: LiSSA
description: LiSSA – LLM/RAG-based TLR.
-importance: 6
+importance: 7
+group: tlr
+artifacts:
+ - ['Requirements', 'Code']
+ - ['Requirements', 'Requirements']
+ - ['SAD', 'Code']
+ - ['SAD', 'SAM']
figure:
src: /assets/img/approaches/icse25-lissa.svg
alt: LiSSA Overview
plate: true
+gallery:
+ - src: /assets/img/approaches/aire25-aire.svg
+ alt: AIRE 2025 Overview
+ caption: 'Beyond Retrieval: A Study of Using LLM Ensembles for Candidate Filtering in Requirements Traceability (AIRE 2025)'
+ plate: true
repositories:
- name: Implementation
- url: https://github.com/ardoco/lissa-ratlr
+ url: https://github.com/ardoco/lissa
- name: Replication Package (ICSE 2025)
url: https://github.com/ardoco/Replication-Package-ICSE25_LiSSA-Toward-Generic-Traceability-Link-Recovery-through-RAG
- name: Replication Package (REFSQ 2025)
@@ -22,5 +33,7 @@ The key idea is to use a Large Language Model (LLM) together with information re
For a given source artifact (e.g. a requirement or a sentence in documentation), LiSSA first uses IR techniques to retrieve a small set of potentially relevant target artifacts (code files, model elements, etc.).
It then queries the LLM with the retrieved context to generate or suggest the most likely trace link.
-- Scope: LiSSA was tested on multiple tasks including requirements→code, documentation→code, and architecture-docs→models. The same RAG process is applied in each case, making it a one-size-fits-many solution.
-- Effectiveness: In experiments, LiSSA significantly outperformed state-of-the-art tools on the code-centric tasks. For example, it showed much higher accuracy when linking requirements to code than prior methods.
+- Scope: LiSSA was tested on multiple tasks including requirements→code, documentation→code, and architecture-docs→models. The same RAG process is applied in each case.
+- Effectiveness: LiSSA is most effective on requirements-related tasks. For requirements→code it significantly outperformed the state-of-the-art approaches. For documentation→code it achieved better F1-scores than the state of the art on smaller projects, but underperformed on larger ones. For architecture documentation→models it did not outperform the state of the art. Further research is needed before RAG-based approaches are applicable in practice.
+- REFSQ 2025: Building on that strength, LiSSA was applied to requirements-to-requirements TLR via RAG, evaluated on six benchmark datasets. Chain-of-thought prompting can be beneficial, and open-source models perform comparably to proprietary ones.
+- AIRE 2025: An ensemble of small LLMs (chaining, majority voting) replaces IR for candidate filtering in requirements TLR. It reduces the candidate space with high recall, but lags in precision.
diff --git a/src/content/approaches/swattr.md b/src/content/approaches/swattr.md
index 39fa35bb..0118888e 100644
--- a/src/content/approaches/swattr.md
+++ b/src/content/approaches/swattr.md
@@ -2,13 +2,15 @@
title: SWATTR
description: SWATTR – TLR between Software Architecture Documentation and Software Architecture Models.
importance: 1
+group: tlr
+artifacts: [['SAD', 'SAM']]
figure:
src: /assets/img/approaches/ecsa21-swattr.svg
alt: SWATTR Overview
- plate: false
+ plate: true
repositories:
- name: Implementation
- url: https://github.com/ardoco/tlr
+ url: https://github.com/ardoco/ardoco
- name: Replication Package (ECSA 2021)
url: https://github.com/ardoco/SWATTR
---
@@ -20,4 +22,4 @@ Next, it uses NLP and heuristics to identify architecture elements (like compone
Finally, it connects these identified text elements to model elements to form trace links.
- Pipeline stages: The framework is extendable, meaning you can plug in different strategies at each step. For example, one agent might use term matching to find components in sentences, while another uses more advanced similarity measures. All results are aggregated to produce the final links.
-- Results: SWATTR was evaluated on three case studies and achieved a weighted average F1-score of about 0.72 for trace recovery. This was a strong performance (outperforming simple baselines by ~0.24 F1) and demonstrated the benefit of the multi-stage approach.
+- Results: SWATTR was evaluated on three case studies and achieved a weighted average F1-score of about 0.72 for trace recovery. This was a strong performance (outperforming simple baselines by at least 0.24 F1, and by 0.31 for the weighted average) and demonstrated the benefit of the multi-stage approach.
diff --git a/src/content/approaches/tools.md b/src/content/approaches/tools.md
new file mode 100644
index 00000000..d85d6465
--- /dev/null
+++ b/src/content/approaches/tools.md
@@ -0,0 +1,39 @@
+---
+title: ARDoCo Tools
+description: The ARDoCo tool landscape – the REST API, TraceView, and TraceViz make ARDoCo's TLR approaches accessible.
+importance: 9
+group: tools
+figure:
+ src: /assets/img/approaches/ase26-landscape.svg
+ alt: ARDoCo tool landscape
+ plate: true
+ thumb: /assets/img/approaches/ase26-traceview.png
+gallery:
+ - src: /assets/img/approaches/ase26-traceview.png
+ alt: TraceView screenshot
+ caption: TraceView in the browser
+ plate: false
+ - src: /assets/img/approaches/ase26-traceviz.png
+ alt: TraceViz screenshot
+ caption: TraceViz in VS Code
+ plate: false
+repositories:
+ - name: REST API
+ url: https://github.com/ardoco/REST
+ - name: TraceView
+ url: https://github.com/ardoco/traceview-v2
+ - name: TraceViz
+ url: https://github.com/ardoco/traceviz
+ - name: TraceView v1 (Legacy)
+ url: https://github.com/ardoco/traceview-v1
+ - name: Replication Package (ASE 2026)
+ url: https://github.com/ardoco/Replication-Package-ASE26_Ardoco-Tool-Landscape
+---
+
+The ARDoCo tool landscape makes the traceability link recovery approaches publicly usable.
+The REST API exposes them over HTTP, and two frontends, TraceView in the browser and TraceViz in VS Code, show the recovered links.
+It is described in our ASE 2026 tools paper.
+
+- REST API: A Spring Boot service at [rest.ardoco.de](https://rest.ardoco.de) that runs SWATTR, ArDoCode, ArCoTL and TransArC via HTTP endpoints, with asynchronous execution and caching.
+- TraceView: A browser-based frontend at [tv.ardoco.de](https://tv.ardoco.de) with a guided wizard for uploading artifacts and choosing a pipeline. It shows the links between SAD, SAM and code side by side, together with the TEAM and MEAT inconsistencies.
+- TraceViz: A VS Code extension that marks SAD lines with gutter markers and lets you jump from a sentence to the linked code files and back. It gets its SAD-to-code links from the REST API or LiSSA, or imports them from a CSV file.
diff --git a/src/content/approaches/transarc.md b/src/content/approaches/transarc.md
index 0366eb7c..fc6e67a5 100644
--- a/src/content/approaches/transarc.md
+++ b/src/content/approaches/transarc.md
@@ -2,17 +2,17 @@
title: TransArC
description: TransArC – TLR between Software Architecture Documentation, Models, and Code.
importance: 3
+group: tlr
+artifacts: [['SAD', 'Code']]
figure:
src: /assets/img/approaches/icse24-transarc.svg
alt: TransArC Overview
plate: true
repositories:
- name: Implementation
- url: https://github.com/ardoco/tlr
+ url: https://github.com/ardoco/ardoco
- name: Replication Package (ICSE 2024)
url: https://github.com/ardoco/Replication-Package-ICSE24_Recovering-Trace-Links-Between-Software-Documentation-And-Code
- - name: Replication Package (ICSA 2025)
- url: https://github.com/ardoco/Replication-Package-ICSA25_Enabling-Arch-Traceability-by-LLM-based-Arch-Component-Name-Extraction
---
TransArC is a transitive trace link recovery approach that connects architecture documents to code via an intermediate architecture model.
@@ -21,4 +21,4 @@ In other words, TransArC builds a bridge: document ⟶ model ⟶ code.
This two-step strategy helps bridge the semantic gap between informal text and code.
- How it works: TransArC combines the two link sets of trace links, namely SWATTR and ArCoTL, to produce trace links transitively from documentation to code.
-- Results: In experiments on five systems, TransArC achieved a high average F1 score (~0.82) for recovering documentation-to-code links, significantly outperforming baseline methods. This shows that combining the two specialized steps yields much more accurate links than simpler approaches.
+- Results: In experiments on five systems, TransArC achieved a high average F1 score (~0.82, averaged over the five systems, as reported at ICSE 2024) for recovering documentation-to-code links, significantly outperforming baseline methods. This shows that combining the two specialized steps yields much more accurate links than simpler approaches.
diff --git a/src/content/approaches/tv.md b/src/content/approaches/tv.md
deleted file mode 100644
index 62da5051..00000000
--- a/src/content/approaches/tv.md
+++ /dev/null
@@ -1,14 +0,0 @@
----
-title: ARDoCo-TV
-description: 'Trace View: a viewer for trace links.'
-importance: 9
-repositories:
- - name: Implementation (v2)
- url: https://github.com/ardoco/traceview-v2
- - name: Implementation (v1 - Legacy)
- url: https://github.com/ardoco/traceview-v1
----
-
-ARDoCo-TV is a tool for visualizing trace links between software artifacts, supporting the analysis and understanding of traceability in software projects.
-
-See our [ARDoCo TV](https://tv.ardoco.de) for more information.
diff --git a/src/content/conferences/ase26-tools.md b/src/content/conferences/ase26-tools.md
index aa577950..bfde260e 100644
--- a/src/content/conferences/ase26-tools.md
+++ b/src/content/conferences/ase26-tools.md
@@ -8,6 +8,8 @@ authors:
- tobias_hey
- julian_winter
- kevin_feichtinger
+approaches:
+ - tools
year: 2026
featured: true
navLabel: ArDoCo Tools @ASE 2026
@@ -15,6 +17,10 @@ navOrder: 1
pubShortName: ASE 2026
conferenceName: 41st IEEE/ACM International Conference on Automated Software Engineering (ASE 2026) — Tools and Datasets Track
conferenceUrl: https://conf.researchr.org/home/ase-2026
+figure:
+ src: /assets/img/approaches/ase26-landscape.svg
+ alt: ARDoCo tool landscape
+ plate: true
links:
paper:
acm: https://doi.org/10.1145/3832783.3834621
diff --git a/src/content/conferences/ecsa21.md b/src/content/conferences/ecsa21.md
index a4342e98..866e8754 100644
--- a/src/content/conferences/ecsa21.md
+++ b/src/content/conferences/ecsa21.md
@@ -20,7 +20,7 @@ conferenceUrl: https://conf.researchr.org/home/ecsa-2021
figure:
src: /assets/img/approaches/ecsa21-swattr.svg
alt: SWATTR Overview
- plate: false
+ plate: true
links:
paper:
springer: https://doi.org/10.1007/978-3-030-86044-8_7
diff --git a/src/content/conferences/icsa25.md b/src/content/conferences/icsa25.md
index edc11b65..cbd552eb 100644
--- a/src/content/conferences/icsa25.md
+++ b/src/content/conferences/icsa25.md
@@ -34,7 +34,7 @@ links:
kitopen: https://publikationen.bibliothek.kit.edu/1000179830
replication:
zenodo: https://doi.org/10.5281/zenodo.14506935
- repo: https://github.com/ardoco/ReplicationPackage-EnablingArchitectureTraceabilitybyLLM-basedArchitectureComponentNameExtraction
+ repo: https://github.com/ardoco/Replication-Package-ICSA25_Enabling-Arch-Traceability-by-LLM-based-Arch-Component-Name-Extraction
slides:
pptx: /assets/pdf/presentation_icsa25.pptx
pdf: /assets/pdf/presentation_icsa25.pdf
diff --git a/src/content/conferences/icse25.md b/src/content/conferences/icse25.md
index 0ef8fe01..6c13f6b0 100644
--- a/src/content/conferences/icse25.md
+++ b/src/content/conferences/icse25.md
@@ -32,7 +32,7 @@ links:
kitopen: https://publikationen.bibliothek.kit.edu/1000179816
replication:
zenodo: https://doi.org/10.5281/zenodo.14714706
- repo: https://github.com/ardoco/ReplicationPackage-ICSE25_LiSSA-Toward-Generic-Traceability-Link-Recovery-through-RAG/tree/main
+ repo: https://github.com/ardoco/Replication-Package-ICSE25_LiSSA-Toward-Generic-Traceability-Link-Recovery-through-RAG/tree/main
slides:
pptx: /assets/pdf/presentation_icse25.pptx
pdf: /assets/pdf/presentation_icse25.pdf
diff --git a/src/content/conferences/refsq25.md b/src/content/conferences/refsq25.md
index 2e9e955d..078b77ea 100644
--- a/src/content/conferences/refsq25.md
+++ b/src/content/conferences/refsq25.md
@@ -25,7 +25,7 @@ links:
springer: https://doi.org/10.1007/978-3-031-88531-0_27
replication:
zenodo: https://doi.org/10.5281/zenodo.14779457
- repo: https://github.com/ardoco/ReplicationPackage-REFSQ25_Requirements-TLR-via-RAG
+ repo: https://github.com/ardoco/Replication-Package-REFSQ25_Requirements-TLR-via-RAG
---
## Abstract
diff --git a/src/content/pages/404.md b/src/content/pages/404.md
index 67bdf153..720c649e 100644
--- a/src/content/pages/404.md
+++ b/src/content/pages/404.md
@@ -7,6 +7,5 @@ lede: 'The address you followed does not exist here. It may have been a paper pa
links:
- '[Home](/)'
- '[Approaches](/approaches/)'
- - '[Conferences](/conferences/)'
- '[Publications](/publications/)'
---
diff --git a/src/content/pages/approaches.md b/src/content/pages/approaches.md
index ea15563e..4bf54200 100644
--- a/src/content/pages/approaches.md
+++ b/src/content/pages/approaches.md
@@ -1,10 +1,32 @@
---
-# /approaches/. Each approach's own words are its file in
-# src/content/approaches/.
+# /approaches/: the approach map, one section per group of approaches, then the
+# datasets. Each approach's own words are its file in src/content/approaches/,
+# each dataset's its entry in src/data/datasets.yml. The section headings stay
+# in src/pages/approaches/index.astro.
title: 'approaches'
-description: 'The approaches developed in ARDoCo: traceability link recovery and inconsistency detection between architecture documentation, models and code.'
-lede: 'Each approach targets a different pair of artifacts — documentation, architecture models, requirements, source code — and most of them compose. They are listed roughly in the order they were published.'
+description: 'The approaches developed in ARDoCo: traceability link recovery and inconsistency detection between requirements, architecture documentation, models and code, the tools built on them, and the datasets they are evaluated on.'
+lede: 'Each approach connects a different pair of artifacts — requirements, architecture documentation, architecture models, source code — and many of them compose. The map shows which approach links what; below, they are grouped by what they are for.'
+# Under each section's heading, keyed by section.
+intros:
+ map: 'The four kinds of artifact ARDoCo connects, and on each connection the approaches built mainly for it. Generic approaches such as LiSSA cover more pairs; the tags on each card list them all. Each label leads to its approach.'
+ tlr: 'Recovering trace links: which sentence, model element or requirement corresponds to which part of the system.'
+ consistency: 'Using trace links to find where documentation and models disagree.'
+ tools: 'Tools that put trace links in front of developers.'
+ datasets: 'The benchmarks the approaches are evaluated on, published for anyone to reuse.'
labels:
# the link on each card, before its arrow
more: 'Learn more'
+ # a dataset's two links: to the data, and to the paper that introduces it
+ dataset: 'Dataset'
+ paper: 'Paper'
+ # The map's four nodes: the short name, as in the `artifacts` tags on the
+ # cards, and the line under it.
+ nodeRequirements: 'Requirements'
+ nodeRequirementsSub: 'in natural language'
+ nodeSad: 'SAD'
+ nodeSadSub: 'architecture documentation'
+ nodeSam: 'SAM'
+ nodeSamSub: 'architecture model'
+ nodeCode: 'Code'
+ nodeCodeSub: 'source code'
---
diff --git a/src/content/pages/conferences.md b/src/content/pages/conferences.md
deleted file mode 100644
index c9be7319..00000000
--- a/src/content/pages/conferences.md
+++ /dev/null
@@ -1,7 +0,0 @@
----
-# /conferences/, the list of paper pages. Each page's own words are its file in
-# src/content/conferences/, the shared ones site/publication.md.
-title: 'conferences'
-description: 'Paper pages for ARDoCo publications: abstracts, slides, replication packages and the approaches each one builds on.'
-lede: 'One page per publication, with the abstract, the approaches it builds on, and links to the paper, slides and replication package.'
----
diff --git a/src/content/pages/home.md b/src/content/pages/home.md
index 3ad8614a..e7ebba8a 100644
--- a/src/content/pages/home.md
+++ b/src/content/pages/home.md
@@ -10,6 +10,8 @@ labels:
approaches: 'Explore approaches'
publications: 'Publications'
github: 'GitHub'
+ # under the approach map, linking the full /approaches/ page
+ allApproaches: 'All approaches, tools and datasets'
# before the mailing list, the last row under "Important links"
contact: 'Contact us at'
# Under a section's heading, and after its list.
diff --git a/src/content/pages/people.md b/src/content/pages/people.md
index 09fc493d..ecea602e 100644
--- a/src/content/pages/people.md
+++ b/src/content/pages/people.md
@@ -3,7 +3,7 @@
# lede.
title: 'people'
description: 'The researchers behind ARDoCo at the MCSE group, KASTEL, Karlsruhe Institute of Technology.'
-# The chips on a person's card; the ORCID one is followed by the id.
+# The ORCID mark after a person's name, and the chip linking their KIT page.
labels:
orcid: 'ORCID'
kitUrl: 'Website at KIT'
diff --git a/src/content/pages/publications.md b/src/content/pages/publications.md
index ce661b5c..c33000bc 100644
--- a/src/content/pages/publications.md
+++ b/src/content/pages/publications.md
@@ -9,4 +9,4 @@ labels:
noResults: 'No publications match that filter.'
---
-Entries with a **Details** link have a page of their own with the abstract, slides and replication package.
+Entries with a **Details** link have a paper page of their own with the abstract, slides and replication package.
diff --git a/src/content/pages/site/nav.md b/src/content/pages/site/nav.md
index 342b614e..8b16aac4 100644
--- a/src/content/pages/site/nav.md
+++ b/src/content/pages/site/nav.md
@@ -4,7 +4,6 @@
sections:
home: '[about](/)'
approaches: '[approaches](/approaches/)'
- conferences: '[conferences](/conferences/)'
publications: '[publications](/publications/)'
people: '[people](/people/)'
---
diff --git a/src/content/pages/site/publication.md b/src/content/pages/site/publication.md
index 75f95837..8c685260 100644
--- a/src/content/pages/site/publication.md
+++ b/src/content/pages/site/publication.md
@@ -1,6 +1,6 @@
---
-# Words shared by a publication entry (/publications/), the conference list
-# (/conferences/) and a conference page (/c/).
+# Words shared by a publication entry (/publications/, via PubEntry) and a
+# conference page (/c/). The home page does not load this file.
labels:
# the chips on an entry; `details` is followed by an arrow
details: 'Details'
@@ -8,8 +8,6 @@ labels:
# the entry's `url`, shown when it has no DOI
link: 'Link'
bibtex: 'BibTeX'
- # the status in the conference list
- toAppear: 'to appear'
# before the `conferenceName` on a conference page
publishedAt: 'Published at'
toBePublishedAt: 'To be published at'
diff --git a/src/data/datasets.yml b/src/data/datasets.yml
new file mode 100644
index 00000000..23dff5ad
--- /dev/null
+++ b/src/data/datasets.yml
@@ -0,0 +1,18 @@
+# Datasets ARDoCo publishes, shown in the strip at the foot of /approaches/.
+#
+# The key is the entry's id; nothing links to it, so it may change. File order
+# is page order. `publication` is the introducing paper's BibTeX key in
+# papers.bib (build-checked): the entry links to that paper's /c/ page when it
+# has one, otherwise to its DOI.
+
+benchmark:
+ name: Benchmark
+ description: Gold-standard trace links between software architecture documentation (SAD), architecture models (SAM) and code for MediaStore, TeaStore, TEAMMATES, BigBlueButton and JabRef.
+ url: https://github.com/ArDoCo/Benchmark
+ publication: fuchss_establishing_2023
+
+everest:
+ name: EVerest
+ description: Security requirements, documentation, architecture and code of the EVerest EV-charging stack, with labels and the trace links between them.
+ url: https://doi.org/10.5281/zenodo.21157836
+ publication: corallo_everest_2026
diff --git a/src/data/papers.bib b/src/data/papers.bib
index 18bc2fa6..be6f6029 100644
--- a/src/data/papers.bib
+++ b/src/data/papers.bib
@@ -73,6 +73,7 @@ @inproceedings{keim_recovering_2024
}
@inproceedings{keim_detecting_2024,
+ abbr = {SE},
title = {{Detecting} {Inconsistencies} in {Software} {Architecture} {Documentation} {Using} {Traceability} {Link} {Recovery}},
author = {Keim, Jan and Corallo, Sophie and Fuch{\ss}, Dominik and Koziolek, Anne},
address = {Bonn},
@@ -91,6 +92,7 @@ @inproceedings{keim_detecting_2024
}
@inproceedings{keim_recovering_2025,
+ abbr = {SE},
title = {{Recovering} {Trace} {Links} {Between} {Software} {Documentation} {And} {Code}},
author = {Keim, Jan and Corallo, Sophie and Fuch{\ss}, Dominik and Hey, Tobias and Telge, Tobias and Koziolek, Anne},
address = {Bonn},
@@ -148,6 +150,7 @@ @inproceedings{fuchss_lissa_2025
@inproceedings{hey_requirements_2025,
title = {{Requirements} {Traceability} {Link} {Recovery} via {Retrieval}-{Augmented} {Generation}},
author = {Hey, Tobias and Fuch{\ss}, Dominik and Keim, Jan and Koziolek, Anne},
+ abbr = {REFSQ},
address = {Cham},
booktitle = {Lecture Notes in Computer Science},
doi = {10.1007/978-3-031-88531-0\_27},
@@ -165,6 +168,7 @@ @inproceedings{hey_requirements_2025
@inproceedings{fuchss_beyond_2025,
title = {{Beyond} {Retrieval}: {A} {Study} of {Using} {LLM} {Ensembles} for {Candidate} {Filtering} in {Requirements} {Traceability}},
author = {Fuch{\ss}, Dominik and Schwedt, Stefan and Keim, Jan and Hey, Tobias},
+ abbr = {AIRE},
booktitle = {2025 IEEE 33rd International Requirements Engineering Conference Workshops (REW)},
doi = {10.1109/rew66121.2025.00006},
html = {/c/aire25},
@@ -229,6 +233,7 @@ @inproceedings{liu_architecture_2026
@article{fuchss_whos_2026,
author = {Fuch{\ss}, Dominik and Liu, Haoyu and Corallo, Sophie and Hey, Tobias and Keim, Jan and von Geisau, Johannes and Koziolek, Anne},
title = {Who's Who? LLM-assisted Software Traceability with Architecture Entity Recognition},
+ abbr = {TAAS},
year = {2026},
publisher = {Association for Computing Machinery},
address = {New York, NY, USA},
diff --git a/src/data/venues.yml b/src/data/venues.yml
index 31ed6a83..572ef879 100644
--- a/src/data/venues.yml
+++ b/src/data/venues.yml
@@ -26,5 +26,22 @@ ASE:
# badges rendered grey and unlabelled on the old site.
SE:
name: Software Engineering (GI)
- url: https://fachgruppe-se.gi.de/
+ url: https://fb-swt.gi.de/
color: '#5b5bd6'
+
+# Colours are chosen apart from the five above (blue, amber, magenta, teal,
+# violet) and light enough for the badge's dark-site background.
+TAAS:
+ name: ACM Transactions on Autonomous and Adaptive Systems
+ url: https://dl.acm.org/journal/taas
+ color: '#e4572e'
+
+REFSQ:
+ name: 'International Working Conference on Requirements Engineering: Foundation for Software Quality'
+ url: https://refsq.org/
+ color: '#7cb518'
+
+AIRE:
+ name: International Workshop on Artificial Intelligence and Requirements Engineering
+ url: https://aire-ws.github.io/aire25/
+ color: '#e86a92'
diff --git a/src/lib/approaches.ts b/src/lib/approaches.ts
new file mode 100644
index 00000000..b2c205a7
--- /dev/null
+++ b/src/lib/approaches.ts
@@ -0,0 +1,111 @@
+import { getCollection, getEntry, type CollectionEntry } from 'astro:content';
+import { APPROACH_GROUPS } from '../consts';
+import { conferencePath } from './urls';
+import { doiOrUrl, type VenueRef } from './publications';
+
+type Approach = CollectionEntry<'approaches'>;
+
+/**
+ * Page order: by group in APPROACH_GROUPS order, then by `importance` within
+ * the group. `importance` used to be one global sequence; since the groups
+ * came in it only means something inside a group, so a bare sort on it would
+ * interleave them. The title breaks a tie, so two equal numbers do not
+ * reshuffle between builds.
+ */
+export function ordered(all: Approach[]): Approach[] {
+ return [...all].sort(
+ (a, b) =>
+ APPROACH_GROUPS.indexOf(a.data.group) - APPROACH_GROUPS.indexOf(b.data.group) ||
+ a.data.importance - b.data.importance ||
+ a.data.title.localeCompare(b.data.title),
+ );
+}
+
+/** What both approach pages render for one related paper. */
+export interface RelatedPublication {
+ title: string;
+ /** Site path for a conference page, absolute URL for a paper elsewhere. */
+ href: string;
+ /** The short label line under the title. */
+ venue: string | undefined;
+ /** For the venue badge; absent when the BibTeX entry has no `abbr`. */
+ abbr: VenueRef | undefined;
+ year: number;
+ month: number;
+}
+
+/**
+ * Every paper that belongs to this approach, newest first: the conference
+ * pages that name it, plus the papers the approach lists itself in
+ * `publications`.
+ *
+ * The Jekyll version compared `conf.approaches` against `page.title` as
+ * strings, so a renamed approach quietly emptied this list. The collection
+ * stores references, so the join is by id and the build fails instead.
+ *
+ * Redirect stubs are left out of the conference side: they stand in for a
+ * page that superseded them, which is listed in its own right. That is also
+ * why direct publications exist — a paper whose only page is such a stub
+ * (the TAAS journal extension) cannot be reached through `approaches:` on a
+ * conference page without showing the paper it redirects to.
+ *
+ * A paper reachable both ways is listed once, as its conference page: that
+ * page carries the abstract and links, which a bare DOI does not.
+ */
+export async function relatedPublications(approach: Approach): Promise {
+ const conferences = (await getCollection('conferences'))
+ .filter((c) => c.data.redirectTo === undefined)
+ .filter((c) => c.data.approaches.some((ref) => ref.id === approach.id));
+
+ const found = new Map();
+ const withoutPub: RelatedPublication[] = [];
+
+ for (const c of conferences) {
+ const pub = c.data.publication ? await getEntry(c.data.publication) : undefined;
+ const item: RelatedPublication = {
+ title: c.data.title,
+ href: conferencePath(c.id),
+ venue: c.data.pubShortName ?? c.data.conferenceName,
+ abbr: pub?.data.abbr,
+ year: pub?.data.year ?? c.data.year ?? 0,
+ month: pub?.data.month ?? 0,
+ };
+ if (pub) found.set(pub.id, item);
+ else withoutPub.push(item);
+ }
+
+ for (const ref of approach.data.publications) {
+ if (found.has(ref.id)) continue;
+ const pub = await getEntry(ref);
+ // The entry's `html` slug is no use here: for these papers it names a
+ // redirect stub, which would bounce to a different paper.
+ const href = doiOrUrl(pub);
+ if (!href)
+ throw new Error(`approaches/${approach.id}: publication ${pub.id} has no DOI or url`);
+ const where = pub.data.abbr?.id ?? pub.data.journal ?? pub.data.booktitle;
+ found.set(pub.id, {
+ title: pub.data.title,
+ href,
+ venue: where ? `${where} ${pub.data.year}` : String(pub.data.year),
+ abbr: pub.data.abbr,
+ year: pub.data.year,
+ month: pub.data.month ?? 0,
+ });
+ }
+
+ return [...found.values(), ...withoutPub].sort(
+ (a, b) => b.year - a.year || b.month - a.month || a.title.localeCompare(b.title),
+ );
+}
+
+/**
+ * One badge per venue, newest paper first: two papers at the same conference
+ * would otherwise print its badge twice on the approach's card.
+ */
+export async function relatedVenues(approach: Approach): Promise {
+ const venues = new Map();
+ for (const { abbr } of await relatedPublications(approach)) {
+ if (abbr && !venues.has(abbr.id)) venues.set(abbr.id, abbr);
+ }
+ return [...venues.values()];
+}
diff --git a/src/lib/conferences.ts b/src/lib/conferences.ts
index 8d3f1cba..a4d16c9f 100644
--- a/src/lib/conferences.ts
+++ b/src/lib/conferences.ts
@@ -6,10 +6,10 @@ type Conference = CollectionEntry<'conferences'>;
const isPage = (c: Conference): boolean => c.data.redirectTo === undefined;
/**
- * Navbar and front-page order.
+ * Home-page and feed order.
*
* `navOrder` came from the `children:` array in _pages/conferences.md, which
- * was the single list driving both. Entries without one (the redirect stubs)
+ * once drove a navbar dropdown as well. Entries without one (the redirect stubs)
* are not listed anywhere.
*/
function byNavOrder(a: Conference, b: Conference): number {
diff --git a/src/lib/publications.ts b/src/lib/publications.ts
index 12f7e149..c36c3777 100644
--- a/src/lib/publications.ts
+++ b/src/lib/publications.ts
@@ -1,7 +1,11 @@
import type { CollectionEntry } from 'astro:content';
+import { conferencePath } from './urls';
type Pub = CollectionEntry<'publications'>;
+/** A reference to a venues.yml entry, as `abbr` on a publication. */
+export type VenueRef = NonNullable;
+
/**
* Newest first, fully deterministic.
*
@@ -32,3 +36,18 @@ export function byYear(pubs: Pub[]): { year: number; pubs: Pub[] }[] {
.sort((a, b) => b[0] - a[0])
.map(([year, list]) => ({ year, pubs: list.sort(byNewest) }));
}
+
+/** The paper's DOI as a link, else its `url`; undefined when it has neither. */
+export function doiOrUrl(pub: Pub): string | undefined {
+ return pub.data.doi ? `https://doi.org/${pub.data.doi}` : pub.data.url;
+}
+
+/**
+ * Where a link "to the paper" goes: its own page under /c/ when it has one —
+ * that page carries the abstract, slides and every other link — otherwise
+ * straight to the DOI, otherwise to its `url`. Undefined when there is none.
+ */
+export function paperHref(pub: Pub): string | undefined {
+ if (pub.data.infoSlug) return conferencePath(pub.data.infoSlug);
+ return doiOrUrl(pub);
+}
diff --git a/src/lib/redirect.ts b/src/lib/redirect.ts
index 759fa98b..9718b9da 100644
--- a/src/lib/redirect.ts
+++ b/src/lib/redirect.ts
@@ -10,11 +10,12 @@ const escapeHtml = (s: string): string =>
* A bare meta-refresh page.
*
* Used for the five /c/ URLs that outlived the papers they pointed at
- * (se24, se25, se26-exarch, se26-lissa, taas26). Returned as a Response from
- * the route rather than rendered as markup, because rendering it through the
- * page component would make Astro inject that page's stylesheet into the
- * stub's — several KB of CSS on a page whose whole job is to be left
- * immediately.
+ * (se24, se25, se26-exarch, se26-lissa, taas26), for /approaches/tv/, which
+ * became /approaches/tools/, and for /conferences/, folded into
+ * /publications/. Returned as a Response from the route rather than rendered
+ * as markup, because rendering it through the page component would make Astro
+ * inject that page's stylesheet into the stub's — several KB of CSS on
+ * a page whose whole job is to be left immediately.
*
* Lives in a .ts module rather than inline in the .astro frontmatter: raw
* closing tags inside a template literal confuse the frontmatter parser
diff --git a/src/pages/approaches/[slug]/index.astro b/src/pages/approaches/[slug]/index.astro
index 1f9ae402..e8d05769 100644
--- a/src/pages/approaches/[slug]/index.astro
+++ b/src/pages/approaches/[slug]/index.astro
@@ -1,10 +1,11 @@
---
-import { getCollection, getEntry, render } from 'astro:content';
+import { getCollection, render } from 'astro:content';
import Base from '../../../layouts/Base.astro';
import Prose from '../../../components/Prose.astro';
import Figure from '../../../components/Figure.astro';
+import Artifacts from '../../../components/Artifacts.astro';
import VenueBadge from '../../../components/pub/VenueBadge.astro';
-import { conferencePath } from '../../../lib/urls';
+import { relatedPublications } from '../../../lib/approaches';
export async function getStaticPaths() {
const all = await getCollection('approaches');
@@ -15,24 +16,9 @@ const { entry } = Astro.props;
const d = entry.data;
const { Content } = await render(entry);
-/*
- * Reverse index: every conference page that names this approach.
- *
- * The Jekyll version compared `conf.approaches` against `page.title` as
- * strings, so a renamed approach quietly emptied this list. The collection
- * stores references, so the join is by id and the build fails instead.
- */
-const conferences = (await getCollection('conferences'))
- .filter((c) => c.data.redirectTo === undefined)
- .filter((c) => c.data.approaches.some((ref) => ref.id === entry.id))
- .sort((a, b) => (b.data.year ?? 0) - (a.data.year ?? 0));
-
-const related = await Promise.all(
- conferences.map(async (c) => ({
- conference: c,
- pub: c.data.publication ? await getEntry(c.data.publication) : undefined,
- })),
-);
+/* Every conference page that names this approach, plus its own
+ `publications` — see relatedPublications(). */
+const related = await relatedPublications(entry);
---
@@ -43,6 +29,7 @@ const related = await Promise.all(
{d.title}
{d.description}
+ {d.artifacts.length > 0 && }
{d.figure && }
@@ -51,6 +38,16 @@ const related = await Promise.all(
+ {/* Extra figures from front matter (`gallery`), after the text they
+ illustrate; see the schema for why they are not Markdown images. */}
+ {d.gallery.length > 0 && (
+
+ {d.gallery.map((g) => (
+
+ ))}
+
+ )}
+
{d.repositories.length > 0 && (
Code
@@ -70,12 +67,14 @@ const related = await Promise.all(
+
+ )}
diff --git a/src/pages/approaches/tv/index.astro b/src/pages/approaches/tv/index.astro
new file mode 100644
index 00000000..55264c5d
--- /dev/null
+++ b/src/pages/approaches/tv/index.astro
@@ -0,0 +1,14 @@
+---
+import { getEntry } from 'astro:content';
+import { approachPath } from '../../../lib/urls';
+import { redirectResponse } from '../../../lib/redirect';
+
+/*
+ * /approaches/tv/ was the page for ARDoCo-TV alone. It grew into the page for
+ * all the tools (REST API, TraceView, TraceViz) and moved to /approaches/tools/;
+ * the old URL stays alive as a stub, like the legacy /c/ pages.
+ */
+const tools = await getEntry('approaches', 'tools');
+if (!tools) throw new Error('approaches/tv: src/content/approaches/tools.md is gone');
+return redirectResponse(approachPath('tools'), tools.data.title);
+---
diff --git a/src/pages/c/[slug].astro b/src/pages/c/[slug].astro
index 940d1a8d..a22d52d4 100644
--- a/src/pages/c/[slug].astro
+++ b/src/pages/c/[slug].astro
@@ -62,7 +62,7 @@ const linkRows = sections
.filter((s) => s.items.length > 0);
---
-
+
{pub && (
@@ -79,7 +79,7 @@ const linkRows = sections
Both sides of the separator need an explicit space: Astro strips the
newline between an element and the next. */}
diff --git a/src/pages/conferences/index.astro b/src/pages/conferences/index.astro
index cf5c7321..ac177249 100644
--- a/src/pages/conferences/index.astro
+++ b/src/pages/conferences/index.astro
@@ -1,126 +1,13 @@
---
-import { getCollection, getEntry } from 'astro:content';
-import Base from '../../layouts/Base.astro';
-import VenueBadge from '../../components/pub/VenueBadge.astro';
-import { listed } from '../../lib/conferences';
-import { conferencePath } from '../../lib/urls';
+import { redirectResponse } from '../../lib/redirect';
import { getPage, need } from '../../lib/pages';
/*
- * This page is new. The old site had no conferences index: the eleven paper
- * pages were reachable only from a navbar dropdown, and the page that defined
- * that dropdown had no permalink, so Jekyll published it at the accidental
- * address /_pages/conferences/ (which now redirects here).
+ * /conferences/ listed the paper pages, but /publications/ already links each
+ * one (title and "Details" chip) and "conferences" was a misnomer for a list
+ * that includes journal papers. The list is folded into /publications/; this
+ * stub keeps the URL, which has been published since the Astro migration.
*/
-const all = listed(await getCollection('conferences'));
-const entries = await Promise.all(
- all.map(async (c) => ({
- c,
- pub: c.data.publication ? await getEntry(c.data.publication) : undefined,
- })),
-);
-
-/* The words: src/content/pages/conferences.md, and site/publication.md for a row. */
-const page = await getPage('conferences');
-const words = await getPage('site/publication');
+const page = await getPage('publications');
+return redirectResponse('/publications/', need(page.data.title, 'publications.title'));
---
-
-
-
+ {/* The same map as at the top of /approaches/: what connects to what is
+ the quickest way in. It leaves out the tools and the datasets, so the
+ link below names them. */}
+
+