Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions PROJECT_STATUS.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ maintenance-first support.

## Timeline

- **2026-07-31:** Kept the public sitemap HTML-only and added source-level
sitemap/catalog/Markdown parity coverage.
- **2026-07-31:** Added locally verified Open Graph/Twitter image metadata and
SoftwareApplication structured data to the public landing layout; production
deployment remains separate.
Expand All @@ -36,6 +38,7 @@ maintenance-first support.
AI-assisted reading.
- Account-backed and device-local capture paths.
- Owned editorial product changelog at `/changelog`.
- HTML-only public sitemap with cataloged Markdown mirrors for agent discovery.

## Work queue

Expand Down
2 changes: 1 addition & 1 deletion docs/product/surfaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ Served from `public/` and the `agent-edge.mjs` handler in `src/worker.ts`
| `index.md` | `/index.md` | Product brief in Markdown (no JS) |
| `api/ai` | `/api/ai` | JSON catalog of public surfaces |
| `robots.txt` | `/robots.txt` | Allows all + lists agent surfaces |
| `sitemap.xml` | `/sitemap.xml` | Public URL inventory |
| `sitemap.xml` | `/sitemap.xml` | Canonical public HTML page inventory |
| IndexNow key | `/fa7259e2e0d942f1a1267b344a75a143.txt` | Bing/Yandex URL submission key |

The agent-edge payload is generated into `src/agent-edge.mjs` by the fleet
Expand Down
2 changes: 0 additions & 2 deletions public/sitemap.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url><loc>https://read.significanthobbies.com/</loc></url>
<url><loc>https://read.significanthobbies.com/index.md</loc></url>
<url><loc>https://read.significanthobbies.com/llms.txt</loc></url>
<url><loc>https://read.significanthobbies.com/login</loc></url>
</urlset>
58 changes: 58 additions & 0 deletions src/agent-surfaces.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { existsSync, readFileSync } from 'node:fs';
import { resolve } from 'node:path';
import { describe, expect, it } from 'vitest';

const publicPath = (name: string) => resolve(process.cwd(), 'public', name);
const readPublicFile = (name: string) => readFileSync(publicPath(name), 'utf8');

const sitemap = readPublicFile('sitemap.xml');
const catalog = JSON.parse(readPublicFile('api-ai.json')) as {
url: string;
llms: string;
llmsFull: string;
sitemap: string;
robots: string;
surfaces: Array<{ id: string; url: string; md: string }>;
};

const sitemapUrls = [...sitemap.matchAll(/<loc>([^<]+)<\/loc>/g)].map((match) => match[1]);

describe('public agent surface parity', () => {
it('keeps the sitemap limited to cataloged HTML pages', () => {
expect(sitemapUrls.sort()).toEqual(catalog.surfaces.map((surface) => surface.url).sort());

for (const url of sitemapUrls) {
const pathname = new URL(url).pathname;
expect(pathname).not.toMatch(/\.(?:md|txt|json|xml)$/);
expect(pathname).not.toBe('/api/ai');
}
});

it('keeps one checked-in Markdown mirror for every cataloged HTML page', () => {
for (const surface of catalog.surfaces) {
const markdownUrl = new URL(surface.md);
expect(markdownUrl.origin).toBe(catalog.url);
expect(markdownUrl.pathname).toMatch(/\.md$/);
expect(existsSync(publicPath(markdownUrl.pathname.slice(1)))).toBe(true);
}
});

it('advertises machine resources outside the HTML sitemap', () => {
const discoverySources = [
readPublicFile('robots.txt'),
readPublicFile('llms.txt'),
readPublicFile('api-ai.json'),
].join('\n');

for (const resource of [
'/llms.txt',
'/llms-full.txt',
'/index.md',
'/api/ai',
'/robots.txt',
'/sitemap.xml',
]) {
expect(discoverySources).toContain(resource);
}
});
});