{
  "markdown": "# SpecQuill\n\n**Requirements as readable, structured Markdown — what you end up with is an\n[OKF bundle](repo-product/docs/specs/specs/okf.md).** A git-native requirements-engineering tool:\nrequirements, specs, regulations and data mappings live as plain markdown in\ngit; SpecQuill is the editing and review surface on top — traceability graph,\ntimed dependencies, a git-derived change history, rich editors, and an in-app\nbranch-based merge flow, every commit authored by the logged-in user.\n\nThe artifact SpecQuill produces is deliberately **not proprietary**: a\nworkspace is a conformant **Open Knowledge Format (v0.1) bundle** — typed\nfrontmatter, generated `index.md`/`log.md`, plain relative links — fully\nreadable by humans, agents and any OKF consumer straight from git, with or\nwithout SpecQuill running. Hand the whole bundle to an LLM as one zip via an\nunauthenticated [share link](repo-product/docs/specs/specs/share-links.md).\n\nOriginally implemented from the Claude Design project\n[`SpecQuill.dc.html`](design/SpecQuill.dc.html) (the static prototype it grew from lives in\n[`design/prototype/`](design/prototype/)).\n\n## Screenshots\n\nA tour of every surface lives in\n[`docs/screenshots/`](docs/screenshots/README.md) — editor, speccy, source\nalignment, impact graph, timed dependencies, change history, and more.\nRegenerate the gallery with `make shots` (isolated server + demo fixtures +\nmock LLM, so no keys are needed).\n\n[![Editor](docs/screenshots/editor.png)](docs/screenshots/README.md)\n\n## Architecture\n\n```\nserver/           Go single binary (specquill)\n  internal/gitx     the only git surface: bare clone + per-branch worktrees,\n                    status/commit (user = author & committer, service identity\n                    as Co-authored-by), structured diffs, merge-tree merges,\n                    env-token push/fetch\n  internal/auth     forge-PAT login (GitLab/GitHub personal access tokens,\n                    RAM-only token vault) + local argon2id fallback,\n                    opaque session cookies in the store\n  internal/store    embedded SQLite (modernc, cgo-free) at <data_dir>/specquill.db:\n                    users, sessions, per-repo grants, workspace claims —\n                    content never leaves git\n  internal/api      REST under /api + embedded SPA (embed.FS)\nweb/              React + Vite + TypeScript SPA\n  src/lib/model.ts  frontmatter/link parsing → workspace model (all client-side)\n  src/editors/      Milkdown WYSIWYG (mermaid click-to-edit node view,\n                    excalidraw embeds), CodeMirror 6 source mode,\n                    schema-driven PropertiesForm (yaml Document API),\n                    @excalidraw/excalidraw modal\nrepo/             demo \"trading-specs\" workspace (fixture source)\ndocs/             project docs: the feature screenshot gallery\n                  (docs/screenshots/, regenerated with `make shots`)\n```\n\nKey properties:\n\n- **The server never parses frontmatter** — it serves files + git operations; the model\n  (graph, dashboards) is computed in the browser from a `/snapshot` of the branch.\n- **The type model is configuration, not code.** Document families sit on a\n  WHY → WHAT → HOW → WHEN axis: drivers (regulation, product, technical) explain WHY\n  work exists, requirements say WHAT the product must do, specs say HOW it is\n  realized, work items say WHEN it lands. The whole type system — entities, drivers,\n  statuses, link types, ID schemes and the property schema — lives in ONE optional\n  file, `.specquill/config.yml`; every section it omits runs on the built-in\n  defaults, entities merge (override single fields, add families, or drop one with\n  `hidden: true`), and the Model view shows a **sample config** spelling out the full\n  default setup — importable in one click when no config exists. A stand-alone\n  `.specquill/schema.json` keeps working as the legacy property-schema form.\n- **Timed dependencies** ([REQ-026](repo-product/docs/specs/requirements/REQ-026.md)).\n  A document whose frontmatter carries a validity window — `starts`/`ends`, or\n  regulatory wording like `effective_from`, all configurable under `timed:` —\n  lands on a timeline as pending / active / expiring / expired, together with the\n  readiness of everything that links to it. A window that opens inside the horizon\n  while its dependents are still unfinished is flagged **at risk** on the Overview\n  and as a badge on the rail. No document *about* change is required: the dates\n  live on the documents themselves.\n- **Change history from git** ([REQ-027](repo-product/docs/specs/requirements/REQ-027.md)).\n  `/history` reads the workspace's commits (content-root scoped) and classifies every\n  touched path through the current config, so the feed reads \"3 requirements · 1 spec\"\n  rather than a file list. A selected commit is explained as a **semantic delta** —\n  frontmatter properties that moved, normative statements added, dropped or reworded,\n  sections that came and went — with the text diff one click away and, when an AI tier\n  is configured, a cached one-sentence summary generated from that delta. `/changes` is\n  the branch-scoped counterpart: uncommitted drafts, commits ahead of main, open MR.\n- **Protected main, personal workspaces.** The default branch is never edited directly:\n  the first edit transparently creates/switches to the user's `ws/<user>` branch\n  (server-claimed, fast-forwarded onto main when safe). Direct API writes to protected\n  branches 403. Drafts autosave to the branch worktree (debounced), survive branch\n  switches and navigation (localStorage recovery + unload keepalive), and an explicit\n  Commit turns them into history. Tree badges are real `git status`; merging\n  prompts to commit pending changes first.\n- **State lives in git; the database is bookkeeping.** Drafts are uncommitted\n  changes on a per-branch worktree, history is git commits — SQLite holds\n  identity, sessions, grants and workspace claims, never documents. Concurrent\n  saves of the same file are guarded by a `baseSha` precondition: the later\n  writer gets a 409 and a \"file changed — reload\" prompt instead of silently\n  clobbering.\n- **Two ways to land on main.** Local-auth deployments merge directly in-app: a\n  workspace branch lands on the protected default branch through a previewed merge\n  (diff + conflict check + dirty-worktree refusal); `git merge-tree` does the work\n  as a merge commit or squash. Forge-PAT deployments instead **propose**: the branch\n  is pushed with the user's own token and a merge request / pull request is opened\n  via the forge API (idempotent — re-proposing pushes onto the open MR); review and\n  the merge happen on the forge, and main comes back via fetch.\n- **Forge-PAT auth (`auth.forge`).** Users sign in with a personal access token from\n  the deployment's GitLab/GitHub; identity comes from the forge `/user` API and the\n  deployment role from the user's actual permission on the main project. The token\n  lives in the browser's localStorage and, per session, in a RAM-only server vault —\n  never in the database. Every user gets **fully independent server-side clones**\n  fetched with their own token, so nothing one token can reach ever leaks to another\n  user. Reference sources are defined in-repo (`.specquill/config.yml` `sources:`) —\n  listing one there grants nothing; the user's own forge permission is the gate.\n- **Honest git identity.** The logged-in user is both **author and committer** on every\n  commit and merge; the SpecQuill service identity is recorded as a `Co-authored-by:`\n  trailer instead.\n- **Byte-fidelity editing.** Untouched documents save byte-identical; frontmatter edits\n  go through the `yaml` Document API (comments/formatting preserved); WYSIWYG edits\n  normalize markdown to house style (covered by a golden round-trip suite).\n- **Rich WYSIWYG.** Slash-command menu (`/` inserts headings, lists, task lists,\n  quotes, tables, dividers, code/mermaid blocks, images, sketches), floating selection\n  toolbar (bold/italic/strike/code/link), link dialog (Ctrl+K, hover to preview/edit),\n  table editing controls (add/remove/align/drag rows & columns), a collapsible outline\n  panel with click-to-jump, markdown-aware clipboard, and inline formatting via\n  fixed toolbar, ⌘B/⌘I, or markdown syntax. **Images**: paste, drag-drop, or upload —\n  files land in `<docdir>/assets/` on the branch worktree (`POST /assets`, served raw\n  via `GET /raw/{path}`), embedded as doc-relative markdown. In edit mode internal\n  links follow on Ctrl/Cmd+click (plain click places the cursor).\n- **Sketches are PNGs.** New excalidraw sketches save as `*.excalidraw.png` — a real\n  PNG with the scene JSON embedded (excalidraw's export-embed-scene), so they render\n  natively anywhere git renders images (GitHub included) and stay fully editable in\n  the built-in sketch editor. Legacy `*.excalidraw` JSON files keep working.\n- **Sessions idle out after 10 minutes** without a request (sliding expiry server-side;\n  `session.ttl` in config). The cookie is a browser-session cookie — activity keeps you\n  signed in indefinitely.\n- **Responsive reading.** Under 900px the rail/tree/speccy collapse (tree becomes a\n  hamburger drawer, speccy an overlay) and documents read full-width.\n- **Read-only input repos** (e.g. a regulations repo) are fetched on an interval,\n  browsable in the tree (🔒), and refuse writes server-side.\n- **OKF bundles.** Workspaces conform to the\n  [Open Knowledge Format](repo-product/docs/specs/specs/okf.md) (v0.1): every document carries a\n  `type`, and opted-in bundles get `index.md` listings regenerated on every\n  commit — readable by any OKF consumer or agent straight from git. The\n  `log.md` change history is NOT materialized in the repo (git is the\n  history): it is generated on the fly and injected only when the OKF bundle\n  is exported through a share link. Untyped OKF body links show up as dashed\n  reference edges in the traceability graph.\n- **Workspace onboarding.** `specquill init <dir> [-types requirements,specs,changes,…]`\n  scaffolds a new workspace repo: folder skeleton per chosen document family\n  (requirements, specs, regulations, data-mappings, changes, work-items, decisions, glossary),\n  the combined `.specquill/config.yml` (model + property schema), starter documents, a server-config\n  stub — and the speccy's workspace-side brain: **authoring skills** under\n  `.specquill/skills/`, an **instructions** starter (`.specquill/instructions.md`,\n  with `speccy.instructions` in `config.yml` as the short inline form) and the\n  **project memory** convention (`.specquill/memory/`, one decision per file).\n  All of it is pinned into the system prompt, versioned in git, and reviewed\n  like any other change.\n- **Two model tiers.** `ai.model` is the main (thinking-class) tier for chat and\n  draft edits; `ai.quick_model` is a fast one-shot tier for small tasks. Commit\n  messages are auto-drafted from the uncommitted diff on the quick tier\n  (`POST /commit-message`) and prefill the commit dialog — editable, regenerable,\n  never overwriting what you typed. `<think>…</think>` reasoning tags are stripped.\n- **Speccy** (`ai:` config) talks to any **OpenAI-compatible** chat endpoint —\n  OpenAI, Gemini (`…/v1beta/openai`), Azure, Ollama — with the branch snapshot as\n  grounding (no index; the workspace is prompt-sized). Chat streams over SSE;\n  \"Draft edits & open as diff\" asks the model for surgical search/replace edits,\n  validates them (impacted files only, unique match), and applies them as\n  **uncommitted saves on a `speccy/<doc>` branch** — the human reviews via the\n  normal status → commit → merge flow. `scripts/mock-llm.py` is a keyless dev provider.\n- **Chat tools.** On a writable workspace branch the chat can act directly:\n  `read_file`/`list_files`/`search` (full files, listings and text search over\n  the workspace AND **every selected reference source** — `grounding: true`\n  only decides which sources are additionally excerpted into the prompt, so\n  large implementation repos stay explorable without prompt-stuffing),\n  `edit_file`/`create_file` (unique search/replace or new documents —\n  always **uncommitted drafts** on the current branch, never on protected ones;\n  frontmatter must still parse and `created:`/`updated:` are maintained\n  server-side), and `ask_user` (a clarifying question with option chips that\n  pauses the conversation). Tool descriptions carry the workspace's own\n  vocabulary — statuses, schema enums, family folders, ID patterns. Extra\n  authoring rules live in `.specquill/instructions.md` and/or\n  `speccy.instructions` in `.specquill/config.yml`, pinned into every prompt\n  next to the skills. Speccy interviews rather than assumes: undefined\n  behavior becomes pointed `ask_user` questions grounded in what the\n  referenced repositories already do, and durable answers are persisted as\n  **project memory** — one decision per file under `.specquill/memory/`\n  (merge-friendly by construction), pinned above the specs in every\n  conversation and reviewed/committed like any other workspace change.\n\n## Run (dev)\n\n```sh\nmake dev-fixture        # local bare origins under data/origin/ from repo/\n                        # (also drops the store so it can't outlive the fixtures)\nmake web server         # build SPA into the embed dir + build specquill\npython3 scripts/mock-llm.py &          # keyless speccy provider for dev\n./server/specquill -config specquill.dev.yml -dev\n# → http://localhost:8643  (dev flag auto-authenticates as auth.dev_user)\n```\n\nFrontend dev loop with HMR: `cd web && npm run dev` (Vite on 127.0.0.1:5643, proxying /api). A server started with `-dev` reverse-proxies the SPA routes on :8643 to vite while it runs — :8643 never serves a stale build in dev — and falls back to the embedded build when vite is down.\n\n## Run (production-ish)\n\n```sh\nmake build && ./server/specquill setup     # interactive wizard writes specquill.yml\n# (or: cp specquill.example.yml specquill.yml and edit — running the server\n#  without any config offers the wizard too)\n./server/specquill -config specquill.yml\n# forge-PAT mode needs no server-side credentials at all — users bring their own\n# tokens. Local-auth mode instead: export the token_env vars and add users with\n./server/specquill -config specquill.yml user add flo 'Flo' flo@example.com\n```\n\nRequirements: `git` ≥ 2.38 on the server (checked at startup). Exactly one `writable`\nrepo plus any number of `readonly` ones. The forge identity's `name`/`email` become\nthe git author on every commit.\n\n## Configuration — what lives where\n\nTwo auth modes, two splits. The rule of thumb: **credentials and identity follow the\nmode; content-shaped settings live in the repo.**\n\n**Forge-PAT mode (`auth.forge`, the v1 deployment)** — the server config is minimal\nand credential-free; access rides each user's own token:\n\n| lives in server YAML | lives in `.specquill/config.yml` (in the repo) | lives with the user |\n|---|---|---|\n| forge kind + base URL (`auth.forge`) | reference **source definitions** (`sources:` — name, https remote on an allowlisted host, branch) | the PAT (browser localStorage + RAM-only session vault) |\n| the workspace repo (`projects:` — remote, default branch, content root) | reference **selection** (`references:` — paths filter, `grounding:` = prompt excerpting; every selected source is chat-tool-explorable) | identity + git author (forge `/user`) |\n| optional: scopes / token-creation link overrides, `admin_emails`, `default_role` floor | taxonomy, entities, views, schema — and the speccy's brain: skills, `speccy.instructions`, `instructions.md`, `memory/` | deployment role (forge permission on the main project, refreshed each login) |\n| **no tokens, no source catalog** (a top-level `sources:` block is rejected) | | per-user clones under `data/…/repos/u<id>/` |\n\n**Local-auth mode (`auth.local`, the v2 developer setup)** — the server owns shared\ncredentials, so source definitions must stay server-side: the YAML carries the source\n**catalog** (git + url/openapi/confluence importers) with `token_env` env-var\ncredentials, and the in-repo config only **selects** cataloged sources by name\n(selection ∩ catalog — in-repo config can never mint access). In-app merges,\nboot clones and background sync loops exist only in this mode.\n\nThe authoritative version of this table is\n[`specs/forge-auth.md`](repo-product/docs/specs/specs/forge-auth.md); the\nauthorization reasoning is [`REQ-004`](repo-product/docs/specs/requirements/REQ-004.md)\nand [`REQ-024`](repo-product/docs/specs/requirements/REQ-024.md).\n\n### Example: forge-PAT deployment with reference sources\n\nA specs workspace grounded on regulatory texts, with the implementation repo\nthat is *built from* these specs selected as a read-only source — so the speccy\ncan check the code against the requirements (drift detection).\n\nServer YAML — minimal and credential-free; the top-level `sources:` block must\nstay empty in this mode:\n\n```yaml\nlisten: \":8080\"\ndata_dir: /var/lib/specquill\nbase_url: https://specquill.acme.com\n\nprojects:\n  - id: trading-specs\n    remote: https://gitlab.acme.com/trading/trading-specs.git\n    default_branch: main\n\nauth:\n  forge:\n    kind: gitlab\n    base_url: https://gitlab.acme.com\n    # in-repo source remotes may only name the forge/project hosts;\n    # extra hosts (e.g. a public mirror) must be listed here\n    allowed_source_hosts: [gitlab.esma-mirror.org]\n  admin_emails: [ops@acme.com]\n\nai:\n  enabled: true\n  base_url: https://api.openai.com/v1\n  model: gpt-4o\n  quick_model: gpt-4o-mini\n  api_key_env: SPECQUILL_AI_KEY\n```\n\n`.specquill/config.yml` in the workspace repo — sources are **defined** here\n(git repos, https only, no credentials; each user fetches them with their own\nPAT, so a definition never mints access) and **selected** under `references:`:\n\n```yaml\nversion: 2\nproject: trading-specs\ndefault_branch: main\n\nsources:\n  - name: regulations            # regulatory texts the requirements derive from\n    remote: https://gitlab.acme.com/compliance/regulations.git\n  - name: esma-rts               # public mirror — needs the allowed_source_hosts entry\n    remote: https://gitlab.esma-mirror.org/esma/rts-texts.git\n    default_branch: master\n  - name: trading-platform       # the implementation built from these specs\n    remote: https://gitlab.acme.com/trading/trading-platform.git\n\nreferences:\n  # small, load-bearing texts: pin into the speccy system prompt\n  - source: regulations\n    grounding: true\n  - source: esma-rts\n    grounding: true\n    paths: [rts22/]              # grounding-only prefix filter\n  # the implementation is too big to prompt-stuff: no grounding — the speccy\n  # still reads it on demand via its list_files/search/read_file tools\n  - source: trading-platform\n```\n\n`grounding: true` only decides what gets excerpted into the prompt; every\nselected source is fully explorable through the chat tools regardless.\n\n## Verify\n\n```sh\nmake test               # Go: gitx/auth/API suites · web: model, frontmatter, Milkdown round-trip\nmake e2e                # Playwright against a running dev server: edit → commit → merge\npython3 scripts/verify-write-path.py   # API-level write/commit/push/409 checks\npython3 scripts/mock-forge.py &        # mock GitLab for exercising forge-PAT auth\n```\n\n## Deploy\n\n`Dockerfile` builds the whole thing into one alpine+git image (pushed to\nghcr.io on every push to `main` and every tag); [`DEPLOY.md`](DEPLOY.md)\ndocuments self-hosting it — one binary or container, one YAML file, a\npersistent directory, a reverse proxy.\n\n## Notes & future work\n\n- Speccy grounding is whole-snapshot prompting — fine at workspace scale; a retrieval\n  index would be needed for large corpora or multi-repo grounding.\n- Read-only repos are browse-only inputs; federating them into the traceability model\n  (cross-repo `drives` links) is future work.\n- Conflicting PRs are blocked with the conflicted paths listed; materializing the\n  conflict into the source worktree for in-app resolution is future work.\n",
  "bytes": 20020,
  "sha": "75ece1a72aa5284eae1b566120be6e279452c93a485315a50c088d458eed17d8",
  "repo_slug": "gitu/specquill",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/okf_gitu_specquill_repo_index_md_f2caf327/readme"
}