# Fossil paths

Quarry serves repositories that came from fossil, and imported content —
wiki pages, forum posts, check-in comments, embedded documents — is full
of fossil's own URLs. This is the map from those URLs onto quarry routes,
what is deliberately not mapped, and what it would take to map the rest.

Two seams do the work, both in `internal/api/register.go`:

- `FossilRoute(repo, path)` — the verbs that need no index lookup. Used
  by the check-in comment renderer and delegated to by the resolver.
- `resolver.ResolvePath(path)` — the same table plus the verbs that need
  the index (`/info/<hash>`, `/wiki?name=`). Used by the markup pipeline.

An empty answer from either means **quarry has no equivalent**, and the
caller unwraps the link to plain text. A reader is never handed a link
that 404s. That policy is the important one: everything below is about
which side of it a verb sits on.

## Mapped

| fossil | quarry |
| --- | --- |
| `/doc/<version>/<path>` | `/r/{repo}/doc/{version}/{path}` |
| `/info/<hash>`, `/artifact/<hash>`, `/whatis/<hash>` | `/r/{repo}/info/{prefix}` |
| `/timeline` | `/r/{repo}/timeline` |
| `/forum`, `/forumpost/<h>`, `/forumthread/<h>` | `/r/{repo}/forum` |
| `/technote`, `/event` | `/r/{repo}/technote` |
| `/tktview/<id>`, `/tktview?name=<id>` | `/r/{repo}/ticket/{id}` |
| `/wiki?name=<page>` | `/r/{repo}/wiki/{name}` |
| `/dir?ci=<v>&name=<d>` | `/r/{repo}/tree/{version}/{path}` |
| `/brlist` | `/r/{repo}/branch` |
| `/tarball/<v>/…`, `/zip/<v>/…` | `/r/{repo}/archive/{version}` |

The forum entries land on the **index**, not the thread, on purpose:
measured against a fossil clone, 303 of 304 cited forum hashes are not
present locally, so a deep link would 404.

`/tarball` and `/zip` map only when the link names a version. Quarry's
archive route has no "whatever the tip is" spelling, and guessing trunk
would hand a reader a different tree than the link meant.

## Relative links inside a document

Not a route question, but the same family of bug. Fossil serves a
document at `/doc/<version>/<path>`, so the URL's directory *is* the
document's directory and a relative link in it resolves for free. Quarry
renders the same file on the repository home page and under a tree
listing too, where the page's URL sits somewhere else entirely.

SQLite's README links `doc/compile-for-unix.md`. Rendered at `/r/sqlite`
a browser resolved that against `/r/` and asked for
`/r/doc/compile-for-unix.md` — a 404, while the document itself sat at
`/r/sqlite/doc/trunk/doc/compile-for-unix.md` the whole time.

`docResolver` now carries the version and directory the document was read
at, and `markup.rewriteDocRelative` re-bases every relative `href` and
`src` onto the doc route. A body that sits at no path — a wiki page, a
forum post, a ticket comment — has no sibling to resolve against, is not
a `DocResolver`, and keeps its targets literal.

## Gated: `/finfo`

Quarry **has** this page — `/r/{repo}/history/{version}/{path}` — and it
is the single commonest fossil verb in imported content (libfossil's wiki
alone carries 500 references). It is still dropped, for one reason:

`zit log -n 50 <version> <path>` takes **129 seconds** on the SQLite
mirror. The same page on quarry's own repository answers in 3.4s. `-n`
caps the entries *named*, not the nodes traversed — correct by design,
since capping traversal would make the answer depend on how much
unrelated history sat in between — so a path filter walks the whole
ancestry, and the per-check-in predicate is what costs.

The root cause is in zit, `src/cmd/history.zig`: the walk builds a
`logstore.PlaintextCache` and uses it for its own manifest reads
(`log.contentCached`), but `effectivePathContent` — called twice per
visited check-in, once for the node and once for its primary parent —
calls `repo.fileAt`, which takes neither that cache nor a `BaselineMemo`.
`repo.fileAtMemo` exists and takes both. Check-in manifests are routinely
stored as deltas against their neighbours, so each call re-resolves the
same base chain, and the cost grows with how much history sits behind the
node rather than with the node.

Two fixes, in order of size:

1. Thread the walk's `PlaintextCache` and a `BaselineMemo` through
   `effectivePathContent` to `fileAtMemo` — the memo that `#84` added for
   exactly this shape, which this one call site does not take.
2. Memoize `PathContent` by rid. Every check-in's content is computed
   twice: once as *here* when it is visited, once as *parent* when its
   child is. In a mostly-linear history that is exactly 2× the necessary
   work.

Pointing 500 imported links at a page that takes two minutes would be
worse than showing the label as text, so `/finfo` stays dropped until
that lands. `TestVerbsWithoutAQuarryPageStayDropped` asserts the gate and
is the place to revisit.

## Future work

None of these are mapped, and none should be until quarry has the page.
Recorded here so the decision is on the record rather than implied by
silence.

### `/uv/<name>` — unversioned files

The one with a clear path. Fossil's unversioned files are repository-level
blobs that live outside the check-in graph: release binaries, generated
downloads, anything that should not be versioned but should be served.

Quarry already has the machinery — the attachment store (RFC 0016), with
owner-supplied S3 behind it and the forge bucket as the default. An
attachment today is keyed to an *object* (a ticket, a wiki page, a forum
post); an unversioned file is the same thing keyed to the **repository**.
That is the whole gap: a repository-scoped attachment target, a listing
page, and `/uv/<name>` maps onto it.

Worth doing when someone mirrors a repository that actually uses them —
it is a real fossil feature, and the download links in that repository's
own documentation are what break without it.

### The rest

| fossil | what quarry would need |
| --- | --- |
| `/leaves` | a leaves query on the index; the branch page is close |
| `/vdiff?from=&to=` | diff between two arbitrary versions; today quarry diffs a check-in against its parent |
| `/stat`, `/timewarps` | repository statistics pages |
| `/help`, `/md_rules` | forge-level reference pages, not repository ones |

Each is a page first and a route second. Add the page, then add one row
to `FossilRoute`, then delete its line from this section.
