Skip to content

Clean up docs and update for Phase 2#2

Merged
arberx merged 6 commits intoax/separate-monitor-repofrom
arberx/phase-2-doc-cleanup
Mar 9, 2026
Merged

Clean up docs and update for Phase 2#2
arberx merged 6 commits intoax/separate-monitor-repofrom
arberx/phase-2-doc-cleanup

Conversation

@arberx
Copy link
Member

@arberx arberx commented Mar 9, 2026

Summary

  • Remove stale Phase 1 Docker-era files (compose.yaml, .env.example, docs/self-hosting.md)
  • Add canonical Phase 2 design doc (docs/phase-2-design.md) — moved from .context/plans/
  • Update all agent docs (CLAUDE.md, AGENTS.md), README.md, CONTRIBUTING.md, and all docs/ files to reflect Phase 2 architecture: single-process local server, SQLite, CLI, config-as-code, publishable npm package
  • Update all package-level READMEs (packages/db, packages/contracts, packages/provider-gemini, apps/api, apps/worker, apps/web)
  • Mark docs/site-audit.md as deferred to Phase 3

Stacked on

PR #1 (ax/separate-monitor-repomain)

Test plan

  • pnpm run typecheck passes
  • pnpm run test passes (all 30 tests)
  • pnpm run lint passes
  • No code changes — documentation only

🤖 Generated with Claude Code

arberx and others added 2 commits March 9, 2026 12:56
Remove stale Docker/Postgres-era files (compose.yaml, .env.example,
self-hosting.md) and superseded plan drafts. Add canonical Phase 2
design doc. Update all agent docs (CLAUDE.md, AGENTS.md), README,
CONTRIBUTING, architecture, product plan, testing guide, workspace
packaging, and all package-level READMEs to reflect Phase 2 reality:
single-process local server, SQLite, CLI, config-as-code, publishable
npm package.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Database (packages/db):
- Drizzle ORM schema with 8 tables: projects, keywords, competitors,
  runs, query_snapshots, audit_log, api_keys, usage_counters
- SQLite client factory with WAL mode and foreign keys
- Auto-migration runner (idempotent CREATE IF NOT EXISTS)
- 9 tests covering CRUD, cascade deletes, unique constraints

Contracts (packages/contracts):
- canonry.yaml config-schema with Zod validation (apiVersion/kind/
  metadata/spec structure, slug validation for project names)
- Structured error codes with AppError class and factory helpers
- Extended ProjectDto with labels, configSource, configRevision
- Extended RunDto with trigger, startedAt, finishedAt, error
- New schemas: citationState, computedTransition, querySnapshot,
  auditLogEntry
- 12 tests covering all new schemas and error handling

Also: remove stale Docker scripts from root package.json, approve
better-sqlite3 native builds, fix web mock data for extended DTOs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
arberx and others added 4 commits March 9, 2026 14:43
Phase 2b — packages/api-routes/:
- Fastify route plugins: auth, projects, keywords, competitors, runs,
  apply (config-as-code), history/timeline/snapshots
- API key auth via SHA-256 hash lookup in api_keys table
- Audit log entries on all config mutations
- Usage counter increments on runs
- Computed citation transitions (lost/emerging) from consecutive snapshots
- 8 tests covering CRUD, run trigger, duplicate guard, audit log

Phase 2c — provider execution + job runner:
- In-process JobRunner in packages/canonry/src/job-runner.ts
- Executes visibility queries via provider-gemini for each keyword
- Determines citation state (cited/not-cited) by checking if canonical
  domain appears in cited domains
- Persists query_snapshots, updates run status, increments usage

Phase 2d — packages/canonry/ (publishable npm package):
- CLI entry point with parseArgs routing for all commands
- Unified Fastify server mounting api-routes + @fastify/static for SPA
- Config reader/writer for ~/.canonry/config.yaml
- HTTP API client for CLI → server communication
- Commands: init, serve, project CRUD, keyword/competitor management,
  run trigger, status, history, apply, export
- canonry init: creates ~/.canonry/, generates cnry_* API key, SQLite DB
- 4 tests covering config, server creation, API flow, health endpoint

Also: cascade delete on query_snapshots.keyword_id (user fix).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… parity

- runs.ts: add orderBy(asc) to GET /projects/:name/runs so status/export
  commands reliably get the latest run via runs[runs.length - 1]
- runs.ts: wrap activeRun check + insert in a db.transaction to eliminate
  the race condition that could create duplicate concurrent runs
- history.ts: replace full-table query + JS filter with inArray WHERE clause
  in both /snapshots and /timeline endpoints
- history.ts + runs.ts: wrap all JSON.parse calls in tryParseJson helper so
  a single malformed DB value no longer poisons the entire response
- config.ts: write ~/.canonry/config.yaml with mode 0o600 to prevent
  apiKey and geminiApiKey from being world-readable
- commands/evidence.ts: implement canonry evidence using the timeline API
  to display keyword-level citation states (was silently calling showStatus)
- cli.ts: wire evidence case to new showEvidence command
- api.ts: add fetchExport for GET /projects/:name/export
- App.tsx: add Export (Download) button to ProjectPage header for surface
  parity with CLI and API export surfaces

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- api.ts: new typed SPA client for all API operations
- build-dashboard.ts: transforms live API responses into DashboardVm tree
- App.tsx: load real data on mount, reactive setup wizard with live API calls,
  delete confirmation, optional readinessSummary/technicalFindings sections
- view-models.ts: make readinessSummary and technicalFindings optional
- mock-data.ts: update mocks for new required DTO fields
- styles.css: gauge layout fixes, setup-wizard styles
- vite.config.ts: simplify dev proxy to single canonry serve instance
- server.ts: serve bundled SPA assets, inject API key, /health endpoint
- build-web.ts: script to build SPA and copy assets into package
- button.tsx: add destructive variant
- CLAUDE.md / AGENTS.md: phase 2 documentation updates
- docs/phase-2-design.md: architecture refinements

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Phase 2a: Database schema and contracts foundation
@arberx arberx merged commit b3e4628 into ax/separate-monitor-repo Mar 9, 2026
@arberx arberx deleted the arberx/phase-2-doc-cleanup branch March 10, 2026 04:17
arberx added a commit that referenced this pull request Mar 13, 2026
- Replace dense EvidenceTable with EvidencePhraseCards grid
  - One card per keyword phrase (2-col responsive grid)
  - Color-accented left border by citation state (emerald/rose/zinc)
  - Citation status badge + trend arrow + streak label at a glance
  - Timeline dot history per phrase across all providers
  - Per-provider clickable pills (Gemini ✓ / OpenAI ✗) that open the drawer

- Redesign EvidenceDrawer for human readability
  - Position hero: 'Cited #2 of 4 domains' or 'Not in this answer'
  - AI answer with your domain/name highlighted in emerald
  - Citation leaderboard: ordered ranked list (You / Competitor / Other)
  - Action items: technical signals as styled list (✓ why cited / ⚠ what to fix)
  - Grounding sources and evidence URLs as clickable links

- Add highlightTermsInText() utility for domain highlighting
- Add ~170 lines of new CSS in styles.css following existing @apply pattern
arberx added a commit that referenced this pull request Mar 13, 2026
- Replace dense EvidenceTable with EvidencePhraseCards grid
  - One card per keyword phrase (2-col responsive grid)
  - Color-accented left border by citation state (emerald/rose/zinc)
  - Citation status badge + trend arrow + streak label at a glance
  - Timeline dot history per phrase across all providers
  - Per-provider clickable pills (Gemini ✓ / OpenAI ✗) that open the drawer

- Redesign EvidenceDrawer for human readability
  - Position hero: 'Cited #2 of 4 domains' or 'Not in this answer'
  - AI answer with your domain/name highlighted in emerald
  - Citation leaderboard: ordered ranked list (You / Competitor / Other)
  - Action items: technical signals as styled list (✓ why cited / ⚠ what to fix)
  - Grounding sources and evidence URLs as clickable links

- Add highlightTermsInText() utility for domain highlighting
- Add ~170 lines of new CSS in styles.css following existing @apply pattern
arberx added a commit that referenced this pull request Mar 13, 2026
* Redesign evidence tracking: phrase cards + improved drawer

- Replace dense EvidenceTable with EvidencePhraseCards grid
  - One card per keyword phrase (2-col responsive grid)
  - Color-accented left border by citation state (emerald/rose/zinc)
  - Citation status badge + trend arrow + streak label at a glance
  - Timeline dot history per phrase across all providers
  - Per-provider clickable pills (Gemini ✓ / OpenAI ✗) that open the drawer

- Redesign EvidenceDrawer for human readability
  - Position hero: 'Cited #2 of 4 domains' or 'Not in this answer'
  - AI answer with your domain/name highlighted in emerald
  - Citation leaderboard: ordered ranked list (You / Competitor / Other)
  - Action items: technical signals as styled list (✓ why cited / ⚠ what to fix)
  - Grounding sources and evidence URLs as clickable links

- Add highlightTermsInText() utility for domain highlighting
- Add ~170 lines of new CSS in styles.css following existing @apply pattern

* Fix evidence redesign: pending cards, highlight markdown, uncited copy

- Filter pending keywords from provider buttons; show dedicated
  "Awaiting first run" text instead of blank provider CTAs
- Add pending case to drawer heroCopy so pending evidence doesn't
  render as "Not in this answer" with empty provider meta
- Parse **bold** markdown before splitting on highlight terms so
  bolded domain/project names render correctly instead of leaking
  literal ** markers
- Change uncited hero text from "competitor(s) cited" to "domain(s)
  cited" since citedDomains includes non-competitor sources

Bump version to 1.4.4.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Replace evidence sidebar drawer with centered detail modal

- Switch from side-panel Sheet to a centered Radix Dialog modal
  (~1100px wide, 90vh tall) for much better content readability
- Two-column layout: left side has status hero + AI answer + action
  items, right side has citation leaderboard + grounding sources
- Add run history timeline at the top with clickable dots per run,
  showing citation state over time with back-to-latest navigation
- Improve markdown rendering: headings render as styled text with
  proper hierarchy, horizontal rules as <hr>, emoji prefixes stripped,
  paragraphs break on single empty lines
- Fade-mask on collapsed answers instead of hard line-clamp cutoff

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Wire run history timeline to fetch full snapshot data per run

The run history dots in the evidence modal were visual-only — clicking
them didn't update the displayed content because RunHistoryPoint only
stored citationState + createdAt.

- Add runId to RunHistoryPoint (already available from timeline API)
- On historical dot click, fetch GET /runs/:id to get full snapshot
- Find matching keyword+provider snapshot and display its answer text,
  cited domains, competitor overlap, and grounding sources
- Cache fetched run details so re-clicking dots is instant
- Show loading state while fetching, graceful fallback on error
- All display data (hero, answer, leaderboard, sources) switches
  between latest evidence and fetched historical snapshot via a
  shared EvidenceDisplayData interface

The DB already persists answerText, citedDomains, competitorOverlap,
groundingSources, and rawResponse per querySnapshot — this just
surfaces that data in the UI when browsing historical runs.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(web): guard stale async race, restore evidence URLs, improve a11y

P1: Guard selectHistoricalRun against out-of-order async completions
using a ref-based request counter. Stale fetch results are discarded
so rapid dot clicks never show mismatched content.

P2: Restore evidenceUrls to EvidenceDisplayData and render them in the
modal sidebar. These were dropped when moving from the drawer to the
modal, removing the direct path from citation items to source pages.

P2: Add aria-label, aria-pressed, focus-visible ring, and aria-hidden
on decorative dot spans for run history buttons so screen-reader and
keyboard users get the same context as pointer users.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claw (AINYC Agent) <agent@ainyc.ai>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
arberx added a commit that referenced this pull request Mar 16, 2026
Wrap JSON.parse(toolCall.function.arguments) in try-catch to prevent crashes
when LLMs return malformed JSON. On parse error, persist the error as a tool
result and continue the agent loop instead of crashing.

Addresses review comment #2 (Bug)
arberx added a commit that referenced this pull request Mar 16, 2026
Wrap JSON.parse(toolCall.function.arguments) in try-catch to prevent crashes
when LLMs return malformed JSON. On parse error, persist the error as a tool
result and continue the agent loop instead of crashing.

Addresses review comment #2 (Bug)
arberx added a commit that referenced this pull request Mar 16, 2026
Wrap JSON.parse(toolCall.function.arguments) in try-catch to prevent crashes
when LLMs return malformed JSON. On parse error, persist the error as a tool
result and continue the agent loop instead of crashing.

Addresses review comment #2 (Bug)
arberx added a commit that referenced this pull request Mar 17, 2026
Wrap JSON.parse(toolCall.function.arguments) in try-catch to prevent crashes
when LLMs return malformed JSON. On parse error, persist the error as a tool
result and continue the agent loop instead of crashing.

Addresses review comment #2 (Bug)
arberx added a commit that referenced this pull request Mar 17, 2026
Wrap JSON.parse(toolCall.function.arguments) in try-catch to prevent crashes
when LLMs return malformed JSON. On parse error, persist the error as a tool
result and continue the agent loop instead of crashing.

Addresses review comment #2 (Bug)
arberx added a commit that referenced this pull request Mar 17, 2026
Wrap JSON.parse(toolCall.function.arguments) in try-catch to prevent crashes
when LLMs return malformed JSON. On parse error, persist the error as a tool
result and continue the agent loop instead of crashing.

Addresses review comment #2 (Bug)
arberx added a commit that referenced this pull request Mar 18, 2026
Issue #1 — trailing-slash mismatch on root route:
  Register both '/canonry/' and '/canonry' (bare) variants so
  either URL shape returns the SPA without a 404.

Issue #2 — SPA fallback over-broad scope:
  When basePath is set, only serve index.html for paths that
  start with basePath; return JSON 404 for everything else so
  co-hosted apps on the same origin are not hijacked.

Issue #3 — bare '/' base path causing duplicate-route error:
  Normalised base path equal to '/' is now treated as undefined
  (no base path), keeping the fastify-static prefix at '/' and
  avoiding a duplicate route registration error.

Issue #4 — routePrefix without leading slash silently mis-routes:
  Validate opts.routePrefix at startup and throw a descriptive
  error if it does not start with '/'.

Issue #5 — copy-pasted health handler body:
  Extract to a shared healthHandler const; both /health and
  ${basePath}health now reference the same function.
arberx added a commit that referenced this pull request Mar 18, 2026
* fix: base-path-aware API route prefix and SPA fallback

When `--base-path /canonry/` is set, three things were broken:

1. API routes were registered at /api/v1 regardless of base path,
   so requests to /canonry/api/v1/* had no matching route.
2. setNotFoundHandler checked request.url.startsWith('/api/') —
   correct without base path, but missed /canonry/api/v1/* URLs,
   causing the SPA catch-all to return 200 HTML for all API routes.
3. @fastify/static and root route handler were prefix-unaware,
   so static assets and the root index.html were not served under
   the configured base path.

Fix:
- Add `routePrefix` option to ApiRoutesOptions (named to avoid
  collision with Fastify's reserved `prefix` register option).
- Compute basePath once before apiRoutes registration and use it
  to set routePrefix = `${basePath}api/v1` or '/api/v1' default.
- Register @fastify/static and root handler at `basePath ?? '/'`.
- Update setNotFoundHandler guard to check both '/api/' and
  '${basePath}api/' so it works with and without --base-path.
- Register /health at both '/health' and '${basePath}health'.

Closes #113

* fix: address review feedback on base-path routing

Issue #1 — trailing-slash mismatch on root route:
  Register both '/canonry/' and '/canonry' (bare) variants so
  either URL shape returns the SPA without a 404.

Issue #2 — SPA fallback over-broad scope:
  When basePath is set, only serve index.html for paths that
  start with basePath; return JSON 404 for everything else so
  co-hosted apps on the same origin are not hijacked.

Issue #3 — bare '/' base path causing duplicate-route error:
  Normalised base path equal to '/' is now treated as undefined
  (no base path), keeping the fastify-static prefix at '/' and
  avoiding a duplicate route registration error.

Issue #4 — routePrefix without leading slash silently mis-routes:
  Validate opts.routePrefix at startup and throw a descriptive
  error if it does not start with '/'.

Issue #5 — copy-pasted health handler body:
  Extract to a shared healthHandler const; both /health and
  ${basePath}health now reference the same function.

---------

Co-authored-by: Claw (AINYC Agent) <agent@ainyc.ai>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant