Open Speckit Page
Product: Open Speckit
Domain: openspeckit.com
Document type: Functional specification (agent-ready, spec-driven development)
Version: 1.0
Status: Ready for implementation
Target stack: Angular (standalone, SSR) + Firebase (Auth, Firestore, Storage, Functions, Hosting)
0. How to use this document
This is the source specification for an AI coding agent. Implement in the order of the milestones in §16. Every functional requirement has an ID (FR-x.y) and acceptance criteria (AC-x.y.z). Do not invent features outside this document; if something is ambiguous, choose the simplest implementation that satisfies the acceptance criteria and record the decision in /docs/decisions.md.
Before coding, generate CONSTITUTION.md in the repository root from §2 and a technical plan from §12–§15. Do not begin implementation until the technical plan is approved.
1. Product vision
1.1 Problem
Software is increasingly written by AI coding agents. The quality of the resulting system depends almost entirely on the quality of the specification fed to the agent. Today those specifications live in private repositories, in chat histories, or nowhere at all. There is no common place to find, reuse, fork and improve high-quality specifications.
1.2 Solution
Open Speckit is an open repository of specifications for IT solutions — "GitHub for specifications of agent-coded software". Anyone can browse and download specifications for free. Registered users can publish, version, fork and improve them. All content is released under the MIT licence.
1.3 Core value propositions
| Audience | Value |
|---|---|
| Developer using an AI agent | Download a ready, structured specification and feed it directly to their agent |
| Author / architect | Publish a specification, gain reputation, receive forks and improvements |
| Team / consultancy | Standardise how specifications are written; reuse CONSTITUTION templates |
| Educator | Use published specifications as teaching material |
1.4 Product principles
- Free and open by default. Every public specification is MIT-licensed. No paywall, no ads on content pages.
- Anonymous read. No registration required to browse, read or download public specifications.
- Agent-first output. The download format is directly consumable by a coding agent without post-processing.
- Markdown only. No proprietary formats. What you see in the editor is what you download.
- Versioned truth. A published version is immutable. Work in progress never leaks into a published version.
1.5 Explicit non-goals (v1)
- No code hosting, no CI, no issue tracker.
- No in-app AI generation of specifications (planned post-v1, see §17).
- No paid plans, no billing.
- No real-time collaborative editing (single-writer with optimistic locking is sufficient).
- No comments/discussions (post-v1).
2. CONSTITUTION — cross-cutting requirements
These requirements apply to every module and every screen. They are non-negotiable and must be reflected in CONSTITUTION.md.
2.1 Internationalisation
- The UI supports Polish (pl) and English (en). English is the default and fallback.
- All UI strings live in translation files; no hardcoded user-facing text in templates or TypeScript.
- Language is resolved: URL param
?lang=→ user profile preference →navigator.language→en. Persisted in the user profile for signed-in users, inlocalStorageotherwise. - Specification content is not translated by the system. Each specification declares its own content language (
contentLanguage, ISO 639-1) which is used for filtering andhreflang. - Dates, numbers and relative times use the active locale.
2.2 Accessibility
- WCAG 2.2 level AA across all pages.
- Full keyboard operability; visible focus indicators; logical tab order; skip-to-content link.
- Correct landmark roles and a single
<h1>per page. - Colour contrast ≥ 4.5:1 for text, ≥ 3:1 for UI components and graphics.
- All images require alt text; screenshot upload requires an alt text field before saving.
- Live regions announce async results (save, publish, copy, download).
- Respect
prefers-reduced-motionandprefers-color-scheme. - No functionality depends on hover or pointer only.
2.3 Performance
- Public specification pages are server-side rendered and indexable.
- Lighthouse targets on public pages: Performance ≥ 90, Accessibility ≥ 95, Best Practices ≥ 95, SEO ≥ 95 (mobile).
- Largest Contentful Paint ≤ 2.5 s on a 4G connection.
- Editor and diff viewer are lazy-loaded route chunks.
- Initial JS payload of the landing page ≤ 200 kB gzipped.
2.4 Security & privacy
- Authentication only through Firebase Auth with Google provider.
- Authorisation enforced in Firestore Security Rules and Storage Rules, not only in the UI. The UI hides what the rules forbid; the rules are the boundary.
- Never trust client-supplied counters, ownership fields or timestamps — they are set by Cloud Functions or rules.
- Personal data stored: Google
uid, display name, avatar URL, e-mail, chosen handle, locale. Nothing else. - GDPR: account deletion removes the user profile and anonymises authorship on published public specifications (
Deleted user), or deletes them if the user requests removal of content. Data export available on request via contact form. - Cookies: only essential (auth session, language). No third-party analytics cookies. If analytics is enabled it must be cookieless and IP-anonymised.
- All Cloud Function endpoints validate input with a schema and enforce rate limits.
2.5 Code quality
- Angular standalone components, signals for local state,
OnPushchange detection everywhere. - Strict TypeScript (
strict: true,noUncheckedIndexedAccess: true), noanyin application code. - Feature-first folder structure (§12.2). Data access only through typed repository services — components never touch Firestore SDK directly.
- Unit tests for services, guards, pipes and diff logic. E2E tests for the critical paths in §15.
- Every route has a title and meta description set through Angular's
Title/Metaservices.
3. Glossary
| Term | Definition |
|---|---|
| Specification | The top-level content unit. Owned by a user, has a slug, metadata, articles and versions. |
| Article | One markdown document inside a specification. |
| README | Mandatory article of type README, the landing content of a specification, contains screenshots. |
| CONSTITUTION | Optional article of type CONSTITUTION holding general/cross-cutting requirements. |
| Structure mode | Whether articles are arranged as steps (implementation order) or modules (functional decomposition). |
| Draft (working copy) | The mutable current content of a specification, visible only to the owner and collaborators. |
| Version | An immutable snapshot of all articles, published with a semantic version number. |
| Visibility | public or private. |
| Stage | draft or production. Indicates maturity of the specification, independent of visibility. |
| Fork (duplicate) | A copy of a specification into another user's account, keeping attribution to the source. |
| Bundle | The downloadable ZIP archive of a specification version. |
| Handle | Unique, URL-safe user identifier, e.g. @mzmigrodzki. |
4. Roles and permissions
| Role | Description |
|---|---|
anonymous |
Any visitor. Not signed in. |
user |
Signed in via Google. |
owner |
The user who created a specification. |
collaborator |
A user granted edit rights on a specification by its owner. |
admin |
Platform administrator (flag on the user document, set manually). |
4.1 Permission matrix
| Action | anonymous | user | collaborator | owner | admin |
|---|---|---|---|---|---|
| Browse catalogue, search public specs | ✅ | ✅ | ✅ | ✅ | ✅ |
| Read public specification (any published version) | ✅ | ✅ | ✅ | ✅ | ✅ |
| Download public specification bundle | ✅ | ✅ | ✅ | ✅ | ✅ |
| Read private specification | ❌ | ❌ | ✅ | ✅ | ✅ |
| Create specification | ❌ | ✅ | ✅ | ✅ | ✅ |
| Edit articles of a specification | ❌ | ❌ | ✅ | ✅ | ✅ |
| Publish a new version | ❌ | ❌ | ✅ | ✅ | ✅ |
| Change visibility / stage | ❌ | ❌ | ❌ | ✅ | ✅ |
| Manage collaborators | ❌ | ❌ | ❌ | ✅ | ✅ |
| Fork a public specification | ❌ | ✅ | ✅ | ✅ | ✅ |
| Fork own private specification | ❌ | ❌ | ✅ | ✅ | ✅ |
| Delete specification | ❌ | ❌ | ❌ | ✅ | ✅ |
| Unpublish a version | ❌ | ❌ | ❌ | ✅ | ✅ |
| Moderate / hide any specification | ❌ | ❌ | ❌ | ❌ | ✅ |
Rule: A published public version can never be silently altered. An owner may unpublish it (removing it from public listings) but the version document itself is retained for integrity of forks.
5. Information architecture and routes
| Route | Page | Auth | Rendering |
|---|---|---|---|
/ |
Landing page | public | SSR + prerender |
/catalogue |
Catalogue & search | public | SSR |
/catalogue?q=&tags=&category=&lang=&sort= |
Filtered catalogue | public | SSR |
/s/:handle/:slug |
Specification overview (README of latest published version) | public/private | SSR |
/s/:handle/:slug/a/:articleSlug |
Article view | public/private | SSR |
/s/:handle/:slug/versions |
Version list | public/private | SSR |
/s/:handle/:slug/v/:version |
Specific version overview | public/private | SSR |
/s/:handle/:slug/v/:version/a/:articleSlug |
Article at a version | public/private | SSR |
/s/:handle/:slug/compare/:from...:to |
Version diff | public/private | CSR (lazy) |
/s/:handle/:slug/edit |
Editor shell (article list + editor) | collaborator+ | CSR (lazy) |
/s/:handle/:slug/edit/:articleSlug |
Editor for one article | collaborator+ | CSR (lazy) |
/s/:handle/:slug/settings |
Specification settings | owner+ | CSR (lazy) |
/s/:handle/:slug/stats |
Specification statistics | owner+ | CSR (lazy) |
/new |
Create specification wizard | user | CSR |
/u/:handle |
Public user profile with their public specs | public | SSR |
/dashboard |
My specifications | user | CSR |
/settings |
Account settings | user | CSR |
/about |
About / purpose & content | public | prerender |
/privacy |
Privacy policy | public | prerender |
/terms |
Terms of service | public | prerender |
/contact |
Contact form | public | prerender |
/licence |
MIT licence explanation | public | prerender |
/404 |
Not found | public | — |
Legacy/short links: /@:handle/:slug redirects (301) to /s/:handle/:slug.
6. FR-1 — Authentication and accounts
FR-1.1 Google sign-in
- Sign-in exclusively via Firebase Auth Google provider (
signInWithPopup, withsignInWithRedirectfallback on browsers blocking popups). - Sign-in entry points: header button, any gated action (create, edit, fork), and
/dashboardguard. - After sign-in the user returns to the page they attempted to reach (
returnUrlquery param, validated to be a same-origin relative path).
FR-1.2 First-time onboarding
On first sign-in the system creates a users/{uid} document and shows an onboarding dialog requiring:
- Handle — 3–30 chars,
^[a-z0-9](?:[a-z0-9-]{1,28}[a-z0-9])$, unique, case-insensitive, checked live against ahandles/{handle}reservation document. Pre-filled from the Google display name, slugified. - Display name — pre-filled, editable, 2–60 chars.
- Optional: bio (≤ 280 chars), website URL, preferred UI language.
- Acceptance of Terms and Privacy Policy (checkbox, required, timestamp stored).
Reserved handles that cannot be claimed: admin, api, about, catalogue, contact, dashboard, licence, license, new, privacy, s, settings, terms, u, www, support, help, openspeckit.
FR-1.3 Account settings
/settings allows editing display name, bio, website, UI language, avatar source (Google avatar or initials), and e-mail notification preferences. Handle can be changed once every 30 days; the old handle is kept as a redirect alias for 12 months.
FR-1.4 Account deletion
/settings → "Delete account" requires typing the handle to confirm and offers two options:
- Keep my published public specifications — they remain, authorship replaced by "Deleted user", ownership transferred to a system account.
- Delete everything — all specifications, versions, files and statistics are deleted; existing forks are unaffected but their
forkedFromreference is marked as deleted.
Deletion runs as a Cloud Function (deleteAccount), is idempotent and completes within 24 h.
Acceptance criteria
- AC-1.1 Anonymous visitor can read and download any public specification without ever seeing a sign-in prompt.
- AC-1.2 A handle cannot be duplicated even under concurrent claims (enforced by a transaction on
handles/{handle}). - AC-1.3 Attempting a gated action while signed out shows the sign-in dialog and, after success, completes the original action.
- AC-1.4 Rules deny writes to
users/{uid}whereuid != request.auth.uid.
7. FR-2 — Specifications
FR-2.1 Creation wizard (/new)
Three steps, all fields validated client- and rule-side:
- Basics — title (3–120 chars), slug (auto-generated from title, editable, unique per owner,
^[a-z0-9][a-z0-9-]{1,58}[a-z0-9]$), short description (≤ 300 chars, used in catalogue cards and meta description). - Classification — category (single, from fixed list), tags (1–10, free text, normalised to lowercase-kebab, max 30 chars each), content language, target stack (free tags, e.g.
angular,firebase,python), complexity (starter|standard|advanced). - Structure & access — structure mode (
steps|modules), visibility (privatedefault), stage (draftdefault), optional "start from template".
On completion the system creates the specification with a generated README article and, if a template was chosen, the template's articles.
Fixed category list: web-app, mobile-app, api-backend, data-analytics, ai-agent, automation, integration, devtool, game, e-commerce, internal-tool, other.
FR-2.2 Templates
The system ships with at least three built-in templates seeded as specifications owned by the system account and flagged isTemplate: true:
- Minimal — README + CONSTITUTION + 3 step articles.
- Web application (Angular + Firebase) — README, CONSTITUTION, and modules: Auth, Data model, UI, Deployment.
- AI agent / automation — README, CONSTITUTION, and steps: Goal, Tools, Prompts, Guardrails, Evaluation.
FR-2.3 Visibility and stage
visibility:private(only owner + collaborators + admin) orpublic(everyone).stage:draft(work in progress, shown with a badge, excluded from "production" catalogue filter by default) orproduction(author declares it ready to use).- Both are independently switchable from
/settings. Switching fromprivatetopublicrequires: a README with ≥ 200 characters of content, at least one non-README article, and explicit confirmation of MIT licensing (dialog with checkbox). - Making a specification
privateagain removes it from the catalogue and search index immediately; previously downloaded bundles are unaffected (state this in the confirmation dialog).
FR-2.4 Settings page
/s/:handle/:slug/settings sections:
- General — title, slug (with warning that old URLs will 301 for 12 months), description, category, tags, stack, complexity, content language.
- Access — visibility, stage, collaborators (add by handle or e-mail; role
editor; owner can remove; max 20). - Danger zone — transfer ownership (by handle, requires acceptance by the recipient), archive (read-only, still visible), delete (typed confirmation of the slug).
FR-2.5 Deletion
Deleting a specification soft-deletes it (deletedAt set, removed from all listings and rules deny reads) and hard-deletes after 30 days via a scheduled function. The owner can restore within that window from /dashboard → "Trash".
Acceptance criteria
- AC-2.1 Slug uniqueness is enforced per owner; a collision surfaces an inline error before submit and is rejected by rules.
- AC-2.2 A
privatespecification returns 404 (not 403) to unauthorised readers, both in SSR and in the client. - AC-2.3 Publishing checks in FR-2.3 are enforced by the
publishSpecificationCloud Function, not only in the UI. - AC-2.4 Changing a slug leaves working links at the previous URL for 12 months.
8. FR-3 — Articles and structure
FR-3.1 Article types
| Type | Cardinality | Purpose |
|---|---|---|
README |
exactly 1, mandatory, cannot be deleted | Overview, purpose, screenshots, how to use, credits |
CONSTITUTION |
0 or 1 | Cross-cutting/general requirements applying to the whole solution |
ARTICLE |
0..n | A step or a module, depending on structure mode |
README is always rendered first, CONSTITUTION always second, ARTICLEs follow in their explicit order.
FR-3.2 Article fields
title(3–120 chars)slug(unique within the specification, auto from title, editable)type(README|CONSTITUTION|ARTICLE)order(integer, gap-numbered by 10 for cheap reordering)summary(optional, ≤ 200 chars, shown in the table of contents and in the bundle index)body(markdown, max 200 000 characters)updatedAt,updatedBy,wordCount,readingTimeMinutes(derived, written by function/service)
FR-3.3 Structure management
- Left sidebar of the editor lists articles grouped by type, with drag-and-drop reordering (keyboard-accessible alternative required: "Move up"/"Move down" buttons and
Alt+↑/↓). - Structure mode (
stepsvsmodules) changes labels only: "Step 1, Step 2…" vs "Module: …". It does not change data. - Add article, rename, duplicate within specification, delete (with confirmation;
READMEcannot be deleted; deletingCONSTITUTIONwarns that references may break). - Maximum 100 articles per specification.
FR-3.4 Screenshots and assets
- Screenshots are uploaded from the editor into Firebase Storage at
specs/{specId}/assets/{assetId}.{ext}. - Allowed:
png,jpg,webp,gif,svg(SVG sanitised server-side); max 5 MB per file, max 50 files per specification, max 100 MB total. - Each upload requires alt text (WCAG). Optional caption.
- On upload the editor inserts
at the cursor. Relativeassets/paths are resolved to Storage download URLs at render time and are rewritten to real relative files in the bundle. - The README's first image is used as the specification cover in catalogue cards and the OG image; the owner can override the cover in settings.
Acceptance criteria
- AC-3.1 A specification always has exactly one README; the system prevents its deletion and its type change.
- AC-3.2 Reordering is fully operable by keyboard and announced to screen readers.
- AC-3.3 An image cannot be inserted without alt text.
- AC-3.4 Relative asset paths render correctly in the web view, in the diff view and in the downloaded bundle.
9. FR-4 — Markdown editor (wiki-style)
The editor follows the "Verbalu wiki" model: a single-pane markdown editing surface with a live preview, no WYSIWYG abstraction over the source.
FR-4.1 Layout
- Desktop: three columns — article tree (left, collapsible), markdown source (centre), live preview (right, toggleable, synchronised scroll). Split ratio persisted per user.
- Mobile/narrow: tabbed —
Edit/Preview/Structure. - Sticky toolbar; sticky save/status bar showing "Saved", "Saving…", "Unsaved changes", last saved time.
FR-4.2 Toolbar and shortcuts
Bold Ctrl/Cmd+B, Italic Ctrl/Cmd+I, Heading levels, Link Ctrl/Cmd+K, Inline code, Code block, Bullet list, Numbered list, Task list, Table insert (with size picker), Blockquote, Horizontal rule, Image upload, Internal link, Mermaid diagram, Front-matter block. Ctrl/Cmd+S saves. Ctrl/Cmd+/ opens the shortcut help dialog.
FR-4.3 Supported markdown
GitHub Flavored Markdown: headings, emphasis, lists, task lists, tables, fenced code with language hints and syntax highlighting, blockquotes, footnotes, autolinks, strikethrough, inline HTML sanitised (allow-list: formatting tags, <details>, <summary>, <kbd>, <sub>, <sup>; strip all scripts, iframes, event handlers and javascript: URLs).
Extensions:
- Mermaid fenced blocks (```mermaid) rendered as diagrams in preview, in the web view and exported as-is in the bundle. Failure to parse renders the source in a code block with a warning, never breaks the page.
- Internal links
[[article-slug]]or[[article-slug|Label]]resolving to another article in the same specification. Unresolved links render with a distinct "broken link" style and are listed in the structure health panel. - Callouts
> [!NOTE],> [!TIP],> [!IMPORTANT],> [!WARNING],> [!CAUTION]. - Front matter (optional YAML at the top of an article) with keys
title,summary,tags— parsed and used to override article metadata on save.
FR-4.4 Saving
- Autosave of the working copy 2 seconds after typing stops and on blur, on route change and on tab hide.
- Optimistic locking: each article carries
rev(integer). A save with a stalerevis rejected and the UI shows a conflict dialog offering "Keep mine", "Keep theirs", "Open side-by-side diff". - Local recovery: unsaved buffer is mirrored to IndexedDB; if the tab crashes, the editor offers recovery on next open.
- Saving never touches published versions.
FR-4.5 Editor helpers
- Live word count, character count and estimated reading time.
- Outline panel generated from headings, clickable.
- Structure health panel listing: broken internal links, images missing alt text, articles with empty bodies, headings skipping levels, README shorter than 200 chars.
- Full-text find & replace within the current article (
Ctrl/Cmd+F,Ctrl/Cmd+H). - "Paste as markdown": pasting HTML converts it to markdown.
- "Copy article as markdown" and "Copy whole specification as markdown" buttons.
Acceptance criteria
- AC-4.1 Typing in a 50 000-character article stays responsive (no visible input lag; preview debounced ≥ 150 ms).
- AC-4.2 No
<script>or event-handler attribute from markdown ever reaches the DOM (unit-tested with a sanitiser fixture set). - AC-4.3 A concurrent edit by a collaborator triggers the conflict dialog rather than silently overwriting.
- AC-4.4 The editor is fully usable with keyboard only, including image insertion and reordering.
10. FR-5 — Versioning, comparison and forking
FR-10.1 Model
- The working copy is the live set of
articles. It is mutable and private to owner/collaborators. - A version is an immutable snapshot:
versions/{versionId}with the complete article set embedded, plus metadata. - Versions use semantic versioning
MAJOR.MINOR.PATCH. On publish the UI proposes the next patch, minor and major and lets the user pick or type a value. Versions must be strictly increasing.
FR-10.2 Publishing
Publish dialog collects:
- Version number (validated, must be greater than the latest).
- Changelog entry (markdown, ≤ 5 000 chars, required, min 10 chars).
- Optional "This is a breaking change" flag (forces MAJOR).
publishSpecification Cloud Function then, in a single transaction:
- Validates permission, structure (README present and ≥ 200 chars, ≥ 1 non-README article for public specs), and version ordering.
- Copies all articles into the version snapshot with a content hash per article and an overall
contentHash. - Sets
latestVersion,publishedAt, incrementsversionCount, appends the changelog. - Updates the search index document and the daily stats bucket (
updatescounter).
FR-10.3 Version list
/s/:handle/:slug/versions shows a reverse-chronological list: version, date, author, changelog, article count, size, "Download", "View", "Compare with…". Owner sees additionally "Unpublish".
FR-10.4 Comparison
/s/:handle/:slug/compare/:from...:to where from/to are version numbers or the literal working (owner/collaborator only).
- Summary header: articles added / removed / renamed / modified / reordered, total lines added/removed.
- Per-article accordion showing a line-level unified or side-by-side diff (user-switchable, preference persisted), with word-level highlighting inside changed lines.
- Filter: "Only changed articles" (default on), "Show whitespace changes" (default off).
- Renames detected by slug match first, then by content similarity ≥ 60%.
- Deep-linkable to a specific article:
?a=article-slug. - Diff is computed client-side; the diff route is a lazy chunk.
FR-10.5 Fork / duplicate
Two operations, both available to any signed-in user on a readable specification:
- Duplicate (own specification) — creates a copy in the same account with slug
{slug}-copy, visibilityprivate, stagedraft, no version history, statistics reset to zero. - Fork (any readable specification) — creates a copy in the current user's account from a chosen version (default: latest published). Sets
forkedFrom: { specId, handle, slug, version }, visibilityprivateby default, stagedraft, statistics reset, and incrementsforkCounton the source.
Forked and duplicated specifications carry an attribution block automatically appended to the README:
---
Forked from [handle/slug](https://openspeckit.com/s/handle/slug) v1.2.0 — MIT licence.
The source specification page shows "Forks (n)" linking to a list of public forks.
FR-10.6 Restore
An owner can restore the working copy from any version ("Restore this version"), which overwrites the working articles after a typed confirmation. Restoring never deletes versions.
Acceptance criteria
- AC-5.1 A published version is byte-identical on every subsequent read; no write path can modify it.
- AC-5.2 Comparing a version against itself reports zero changes.
- AC-5.3 Comparing 100 articles totalling 500 kB renders in under 2 seconds on a mid-range laptop.
- AC-5.4 A fork's content equals the source version exactly, plus the attribution block.
- AC-5.5 Publishing is atomic: a failure mid-way leaves no partial version.
11. FR-6 — Download and export
FR-6.1 Download options
From the specification page, version page and catalogue card:
| Format | Contents |
|---|---|
| ZIP bundle (default) | Full folder structure, all articles as files, assets, LICENSE, speckit.json |
| Single markdown | All articles concatenated in order with --- separators and a generated table of contents |
| JSON | speckit.json with full content inline (for programmatic use) |
| Copy to clipboard | Single-markdown form, copied directly — the fastest path into an agent |
Anonymous users can use all four.
FR-6.2 Bundle layout
{handle}-{slug}-{version}/
├── README.md
├── CONSTITUTION.md # if present
├── LICENSE # MIT, with author and year
├── speckit.json # machine-readable manifest
├── articles/
│ ├── 01-discovery.md
│ ├── 02-data-model.md
│ └── 03-ui.md
└── assets/
├── screenshot-dashboard.png
└── diagram-flow.svg
- Article file names: zero-padded order + slug. In
modulesmode the numbering is still applied to keep deterministic ordering. - Every markdown file begins with YAML front matter:
---
title: Data model
type: ARTICLE
order: 20
summary: Firestore collections and their relationships
spec: openspeckit.com/s/mzmigrodzki/task-manager
version: 1.2.0
licence: MIT
---
FR-6.3 speckit.json manifest
{
"schemaVersion": "1.0",
"id": "spec_abc123",
"title": "AI Task Manager",
"slug": "ai-task-manager",
"description": "…",
"url": "https://openspeckit.com/s/mzmigrodzki/ai-task-manager",
"version": "1.2.0",
"publishedAt": "2026-08-14T10:00:00Z",
"licence": "MIT",
"author": { "handle": "mzmigrodzki", "displayName": "Marcin" },
"forkedFrom": null,
"structureMode": "steps",
"category": "web-app",
"tags": ["angular", "firebase", "kanban"],
"stack": ["angular", "firebase"],
"contentLanguage": "en",
"complexity": "standard",
"articles": [
{ "type": "README", "slug": "readme", "title": "Overview", "order": 0, "file": "README.md", "hash": "sha256:…" },
{ "type": "CONSTITUTION", "slug": "constitution", "title": "Constitution", "order": 1, "file": "CONSTITUTION.md", "hash": "sha256:…" },
{ "type": "ARTICLE", "slug": "data-model", "title": "Data model", "order": 20, "file": "articles/02-data-model.md", "hash": "sha256:…" }
],
"assets": [
{ "id": "screenshot-dashboard", "file": "assets/screenshot-dashboard.png", "alt": "Dashboard with three columns" }
],
"contentHash": "sha256:…"
}
FR-6.4 Generation and counting
- ZIP is generated client-side (JSZip) from the version snapshot for speed; assets are fetched from Storage via their download URLs.
- The download is registered by calling the
registerDownloadcallable function (or asendBeaconto an HTTPS function) with{ specId, versionId, format }. - If the client-side path fails, fall back to the
downloadBundleHTTPS function which streams a server-generated ZIP. - Deduplication: one counted download per
(specId, hashedClientId, day), wherehashedClientIdis a salted SHA-256 of IP + user agent, or theuidwhen signed in. The raw IP is never stored.
FR-6.5 Licence
- Every public specification is MIT. The licence text is generated with the author's display name and the year of first publication.
- The specification page shows a persistent "MIT licence" badge linking to
/licence. - Publishing publicly requires explicit acceptance (§FR-2.3), stored as
licenceAcceptedAtandlicenceAcceptedBy.
Acceptance criteria
- AC-6.1 An anonymous visitor downloads a bundle in ≤ 2 clicks from the specification page.
- AC-6.2 The bundle unzips into the exact structure of §11.2 and every article file has valid front matter.
- AC-6.3 Reloading the download page ten times in a minute increments
downloadCountby exactly 1. - AC-6.4
speckit.jsonvalidates against/schemas/speckit-1.0.schema.jsonshipped in the repo. - AC-6.5 Relative asset links inside markdown resolve to files present in the ZIP.
12. FR-7 — Catalogue and search
FR-7.1 Catalogue page
/catalogue lists public specifications as cards showing: cover image (or generated gradient with initials), title, author handle + avatar, short description (2 lines), category, up to 4 tags, stage badge (draft shown in amber), latest version, download count, updated date, "Download" quick action.
Layout: responsive grid (3 / 2 / 1 columns). 24 items per page, infinite scroll on client with an accessible "Load more" button as the primary control.
FR-7.2 Filters
- Text query — matches title, description, tags, author handle.
- Category — single select.
- Tags — multi-select, faceted with counts.
- Stack — multi-select.
- Content language — multi-select.
- Complexity — multi-select.
- Stage —
production only(default),include drafts. - Sort —
relevance(only with a query),most downloaded,recently updated,newest,most forked,title A–Z.
All filters are reflected in the URL query string so results are shareable and SSR-renderable.
FR-7.3 Search implementation
- Maintain a denormalised
searchIndex/{specId}document written by Cloud Functions on publish/settings change/visibility change, containing:titleLower,keywords(array of normalised tokens from title + description + tags + stack, deduplicated, max 100), plus all filter fields and sort counters. - Query strategy:
array-contains-anyonkeywords(max 10 tokens from the user query) + equality filters +orderByon the chosen sort field. Client-side re-ranking by token coverage and title-prefix match for relevance sort. - Empty query + filters uses plain equality + orderBy.
- Required composite indexes are declared in
firestore.indexes.json. - Debounce the query input at 300 ms; show skeleton cards while loading; show a clear empty state with suggested tags.
- The abstraction is a
SearchServiceinterface so an external engine (Typesense/Algolia) can replace it later without touching the UI.
FR-7.4 Discovery on the landing page
The landing page pulls from the same index: 6 most downloaded, 6 most recently updated, and the full tag cloud (top 30 tags).
Acceptance criteria
- AC-7.1 A private specification never appears in any catalogue or search result, including for its owner's anonymous session.
- AC-7.2 Filter state survives page reload and can be shared as a URL.
- AC-7.3 Search returns results in under 500 ms for an index of 1 000 specifications.
- AC-7.4 Empty results show actionable suggestions, never a bare "no results".
13. FR-8 — Statistics
FR-8.1 Counted events
| Event | Counter | Trigger | Dedup window |
|---|---|---|---|
| View | viewCount |
Specification overview or article page loaded | 30 min per client |
| Download | downloadCount |
Any of the four download formats (§11.1) | 24 h per client per spec |
| Update | updateCount |
A version is published | none |
| Fork | forkCount |
A fork is created | none |
| Copy | copyCount |
"Copy to clipboard" used | counted also as a download |
FR-8.2 Storage
- Aggregate counters live on the specification document (
statsmap), written only by Cloud Functions usingFieldValue.increment. - Daily buckets:
specs/{specId}/stats/{yyyy-MM-dd}with{ views, downloads, forks, updates }for time series. - Platform-wide totals in
platform/stats(specifications count, public count, users, total downloads), updated by a scheduled function every hour and shown on the landing page. - User-level totals on
users/{uid}.stats(total downloads across their public specs, spec count, fork count received).
FR-8.3 Owner statistics page
/s/:handle/:slug/stats (owner + collaborators + admin):
- KPI tiles: total views, total downloads, forks, versions published, last update.
- Line chart of views and downloads over the last 30 / 90 / 365 days (range selector).
- Table of downloads by format and by version.
- Simple, accessible charts: every chart has an equivalent data table toggle (WCAG).
FR-8.4 Public display
Specification pages and catalogue cards show download count, fork count and last-updated date. View counts are shown only to the owner.
Acceptance criteria
- AC-8.1 No client can write a counter directly (rules deny
statsfield writes from clients). - AC-8.2 Repeated page reloads within 30 minutes increment
viewCountby 1. - AC-8.3 The stats page renders correctly for a specification with zero events.
- AC-8.4 Every chart has a keyboard-accessible data-table equivalent.
14. FR-9 — Static and support pages
FR-9.1 Landing page (/)
Sections in order:
- Hero — headline ("Open specifications for software your agent will write"), one-sentence subhead, primary CTA "Browse the catalogue", secondary CTA "Publish a specification". Live platform counters (specs / downloads / authors).
- Why — three cards: Specifications are the new source code, Reuse instead of re-writing prompts, Free and MIT for everyone.
- What is inside a specification — visual of the structure: README → CONSTITUTION → steps/modules, with a short explanation of each.
- How it works — 4 steps: Browse → Download → Feed your agent → Fork and improve.
- Featured specifications — 6 cards from the index.
- For authors — versioning, diffs, forks, statistics; CTA to sign in with Google.
- Licence — MIT explained in two sentences, link to
/licence. - FAQ — 6 questions (Is it really free? Who owns what I publish? Can I keep a spec private? What format do I get? How is this different from GitHub? Do I need an account to download?).
- Final CTA + footer.
Footer on every page: About, Catalogue, Privacy, Terms, Contact, Licence, language switcher, "Made in Poland", GitHub link (if the platform repository is public).
FR-9.2 About (/about)
Purpose, what belongs on the platform and what does not, quality expectations for a good specification, content guidelines, moderation policy summary.
FR-9.3 Privacy (/privacy)
GDPR-compliant policy: controller identity and contact, data collected (Google profile: e-mail, name, avatar, uid; handle; content; technical logs), purposes and legal bases, Firebase/Google as processor and international transfers, retention periods, user rights (access, rectification, erasure, portability, objection), cookies (essential only), contact for data requests, last-updated date.
FR-9.4 Terms (/terms)
Free service "as is", account rules, content licensing: by publishing publicly the author irrevocably licenses the content under MIT, warranty that the author has the right to publish, prohibited content (illegal, infringing, malicious, spam, personal data of third parties), moderation and takedown procedure, DMCA-style complaint route, liability limitation, termination, governing law (Poland), changes to terms with notice.
FR-9.5 Contact (/contact)
Form: name, e-mail, subject (select: general, bug, content takedown, privacy request, partnership), message (≤ 3 000 chars), consent checkbox, invisible reCAPTCHA v3. Submits to submitContactForm Cloud Function which validates, rate-limits (3 per hour per IP hash), stores in contactMessages and sends an e-mail to the platform address. Success and error states announced in a live region. Also displays the direct e-mail address as a fallback.
FR-9.6 Licence page (/licence)
Plain-language explanation of MIT for specifications, the full MIT text, what it means for authors and for consumers, attribution expectations on forks.
Acceptance criteria
- AC-9.1 All static pages are prerendered and reachable from the footer of every page.
- AC-9.2 The contact form is unusable without consent and fails gracefully when reCAPTCHA is unavailable.
- AC-9.3 All static pages exist in both
planden.
15. Data model (Firestore)
15.1 Collections
users/{uid}
handles/{handle} # reservation, { uid, createdAt }
specs/{specId}
specs/{specId}/articles/{articleId} # working copy
specs/{specId}/versions/{versionId} # immutable snapshots
specs/{specId}/assets/{assetId}
specs/{specId}/stats/{yyyy-MM-dd}
searchIndex/{specId} # public, denormalised
platform/stats # single doc
contactMessages/{messageId} # server-write only
reports/{reportId} # abuse reports, server-write only
15.2 users/{uid}
interface UserDoc {
uid: string;
handle: string; // lowercase, unique
handleLower: string;
displayName: string;
email: string; // never exposed publicly
photoURL: string | null;
bio: string | null; // <= 280
website: string | null;
locale: 'pl' | 'en';
isAdmin: boolean; // set manually, never client-writable
termsAcceptedAt: Timestamp;
handleChangedAt: Timestamp | null;
stats: { specCount: number; publicSpecCount: number; totalDownloads: number; totalForks: number };
createdAt: Timestamp;
updatedAt: Timestamp;
deletedAt: Timestamp | null;
}
15.3 specs/{specId}
interface SpecDoc {
id: string;
ownerUid: string;
ownerHandle: string; // denormalised for URLs and rules
collaboratorUids: string[]; // max 20
title: string;
slug: string; // unique per owner
previousSlugs: { slug: string; until: Timestamp }[];
description: string; // <= 300
category: Category;
tags: string[]; // 1..10
stack: string[];
contentLanguage: string; // ISO 639-1
complexity: 'starter' | 'standard' | 'advanced';
structureMode: 'steps' | 'modules';
visibility: 'private' | 'public';
stage: 'draft' | 'production';
isTemplate: boolean;
coverAssetId: string | null;
latestVersion: string | null; // e.g. "1.2.0"
latestVersionId: string | null;
versionCount: number;
articleCount: number;
forkedFrom: { specId: string; handle: string; slug: string; version: string } | null;
licence: 'MIT';
licenceAcceptedAt: Timestamp | null;
stats: { views: number; downloads: number; forks: number; updates: number };
publishedAt: Timestamp | null;
createdAt: Timestamp;
updatedAt: Timestamp;
deletedAt: Timestamp | null;
hiddenByAdmin: boolean;
}
15.4 specs/{specId}/articles/{articleId}
interface ArticleDoc {
id: string;
specId: string;
type: 'README' | 'CONSTITUTION' | 'ARTICLE';
title: string;
slug: string;
summary: string | null;
body: string; // markdown, <= 200_000 chars
order: number; // gaps of 10
rev: number; // optimistic locking
wordCount: number;
updatedBy: string;
createdAt: Timestamp;
updatedAt: Timestamp;
}
15.5 specs/{specId}/versions/{versionId}
versionId = the version string with dots replaced by underscores, zero-padded for ordering (e.g. v0001_0002_0000).
interface VersionDoc {
id: string;
specId: string;
version: string; // "1.2.0"
major: number; minor: number; patch: number;
changelog: string; // markdown
breaking: boolean;
publishedBy: string;
publishedAt: Timestamp;
unpublishedAt: Timestamp | null;
articleCount: number;
totalChars: number;
contentHash: string; // sha256 of the ordered article hashes
articles: {
type: 'README' | 'CONSTITUTION' | 'ARTICLE';
title: string; slug: string; summary: string | null;
body: string; order: number; hash: string;
}[];
assets: { id: string; path: string; alt: string; caption: string | null }[];
snapshotMeta: { title: string; description: string; tags: string[]; stack: string[]; category: string; structureMode: string };
}
A version document may approach Firestore's 1 MiB limit for very large specifications. If
totalChars > 700_000, the publish function stores article bodies in Storage atspecs/{specId}/versions/{versionId}/{slug}.mdand leavesbody: nullwithbodyPathset. The version reader must transparently handle both shapes.
15.6 searchIndex/{specId}
interface SearchIndexDoc {
specId: string; ownerHandle: string; ownerDisplayName: string; ownerPhotoURL: string | null;
title: string; titleLower: string; slug: string; description: string;
keywords: string[]; // <= 100 normalised tokens
category: string; tags: string[]; stack: string[]; contentLanguage: string;
complexity: string; stage: string;
coverUrl: string | null;
latestVersion: string;
downloads: number; forks: number; views: number;
publishedAt: Timestamp; updatedAt: Timestamp;
}
Only public, non-deleted, non-hidden specifications have an index document. Deleting the index document is the mechanism for removing a spec from discovery.
16. Security rules (specification, not final code)
// Read
- users/{uid}: public fields readable by anyone; email readable only by self and admin.
(Implement by keeping email in a separate `usersPrivate/{uid}` doc.)
- searchIndex/*: readable by anyone.
- specs/{id}: readable if (visibility == 'public' && !deletedAt && !hiddenByAdmin)
|| ownerUid == auth.uid
|| auth.uid in collaboratorUids
|| isAdmin();
- specs/{id}/articles/*, versions/*, assets/*: same condition as the parent spec.
- specs/{id}/stats/*: owner, collaborators, admin only.
// Write
- users/{uid}: create/update only by self; `isAdmin`, `stats`, `handle` immutable from client
(handle changes go through the `changeHandle` function).
- handles/{handle}: create only via transaction from `claimHandle` function.
- specs/{id}: create by authenticated user with ownerUid == auth.uid, stats zeroed,
latestVersion null, visibility 'private', deletedAt null;
update by owner/collaborator but NEVER on fields:
ownerUid, stats, versionCount, latestVersion*, publishedAt, licenceAcceptedAt, hiddenByAdmin;
visibility/stage/collaboratorUids updatable by owner only;
delete: denied (soft delete via function only).
- articles/*: create/update/delete by owner/collaborator; enforce body size,
type README uniqueness (checked in function on create), rev increment by exactly 1.
- versions/*: client writes DENIED entirely (functions only).
- stats/*, searchIndex/*, platform/*, contactMessages/*, reports/*: client writes DENIED.
// Storage rules
- specs/{specId}/assets/{file}: read follows the spec's visibility (use a mirrored metadata check
or generate signed URLs from a function for private specs);
write only by owner/collaborator, contentType in the allow-list, size <= 5 MB.
Rules must be covered by tests using the Firebase emulator (@firebase/rules-unit-testing), at minimum: anonymous read of public, anonymous read denied on private, collaborator write allowed, non-collaborator write denied, counter tampering denied, version write denied.
17. Cloud Functions
| Function | Type | Purpose |
|---|---|---|
onUserCreate |
Auth trigger | Create users/{uid} skeleton, usersPrivate/{uid} |
claimHandle |
Callable | Transactional handle reservation + profile completion |
changeHandle |
Callable | Handle change with 30-day cooldown and alias creation |
createSpecification |
Callable | Create spec + README (+ template articles) atomically |
publishSpecification |
Callable | Validate, snapshot, bump version, update index and stats |
unpublishVersion |
Callable | Mark version unpublished, recompute latestVersion |
forkSpecification |
Callable | Copy version into caller's account, attribution, increment forkCount |
duplicateSpecification |
Callable | Copy working copy within the same account |
setVisibility |
Callable | Validate publish preconditions, write/delete searchIndex doc |
deleteSpecification |
Callable | Soft delete, remove index doc |
registerDownload |
Callable / HTTPS | Deduplicated download counting |
registerView |
HTTPS (beacon) | Deduplicated view counting |
downloadBundle |
HTTPS | Server-side ZIP fallback, streams the archive |
submitContactForm |
Callable | reCAPTCHA verify, rate limit, store, e-mail |
reportContent |
Callable | Abuse report intake |
deleteAccount |
Callable | GDPR deletion / anonymisation |
rollupStats |
Scheduled hourly | Platform totals, user totals |
purgeDeleted |
Scheduled daily | Hard-delete soft-deleted items older than 30 days |
sanitizeUpload |
Storage trigger | Validate MIME, strip SVG scripts, generate thumbnails and OG images |
All callables: authenticated where required, Zod-validated input, structured error codes (invalid-argument, permission-denied, failed-precondition, resource-exhausted), and per-uid rate limiting via a rateLimits/{uid} document.
18. Angular application design
18.1 Stack
- Angular 20+ standalone components, signals,
provideRouterwith lazyloadComponent/loadChildren. - Angular SSR (
@angular/ssr) with hydration; prerender static pages at build time. - AngularFire (
@angular/fire) for Auth, Firestore, Storage, Functions. - Markdown:
markdown-it+ plugins (footnote, task-lists, attrs, container for callouts) +Prism/Shikifor highlighting +DOMPurifyfor sanitisation +mermaid(lazy). - Diff:
diff(jsdiff) for line diff, custom word-level refinement. - ZIP:
jszip. - UI: Angular Material 3 with a custom theme (design tokens in SCSS), or a hand-rolled component set — pick one and stay consistent. Prefer Material for accessible primitives (dialog, menu, tabs, tooltip).
- i18n:
@ngx-translate/corewith JSON dictionaries in/public/i18n/{lang}.json. - State: signal-based services; no NgRx.
18.2 Folder structure
src/app/
├── core/
│ ├── auth/ (auth.service, auth.guard, owner.guard, collaborator.guard)
│ ├── data/ (spec.repository, article.repository, version.repository,
│ │ user.repository, search.repository, stats.repository)
│ ├── models/ (all interfaces from §15)
│ ├── i18n/ (translate setup, language.service)
│ ├── seo/ (seo.service — title, meta, canonical, OG, JSON-LD)
│ └── util/ (slugify, semver, hash, debounce, clipboard)
├── shared/
│ ├── ui/ (spec-card, tag-chip, stage-badge, empty-state, skeleton,
│ │ confirm-dialog, copy-button, avatar, licence-badge)
│ ├── markdown/ (markdown-renderer, markdown-editor, toolbar, mermaid-block,
│ │ internal-link.directive, sanitiser.config)
│ └── pipes/ (relative-time, number-compact, safe-html)
└── features/
├── landing/
├── catalogue/
├── spec-view/ (overview, article, versions, version-view)
├── spec-edit/ (editor shell, article-tree, article-editor, structure-health,
│ publish-dialog, conflict-dialog)
├── spec-compare/
├── spec-settings/
├── spec-stats/
├── spec-create/
├── dashboard/
├── profile/
├── account/
└── static/ (about, privacy, terms, contact, licence, not-found)
18.3 SEO
SeoServicesets title, description, canonical,hreflangalternates, Open Graph and Twitter cards per route.- JSON-LD on specification pages:
TechArticle/SoftwareSourceCodewithlicense: https://opensource.org/licenses/MIT,author,datePublished,version. - Dynamic
sitemap.xmlgenerated by a scheduled function listing all public specifications;robots.txtdisallowing/edit,/settings,/stats,/dashboard. - OG images generated per specification (cover screenshot or generated card with title + author).
18.4 Design direction
Clean, documentation-like, high legibility. Content column max 78ch. A monospace-accented visual identity (the product is about specifications, not marketing). Light and dark themes following prefers-color-scheme with a manual override. Generous whitespace, restrained accent colour, no decorative imagery on content pages.
19. Deployment
- Firebase Hosting with SSR (Cloud Run / Firebase App Hosting), custom domain
openspeckit.comwithwww→ apex redirect, forced HTTPS, HSTS. - Environments:
dev(emulators + aopenspeckit-devproject) andprod. - CI/CD (GitHub Actions): on PR — lint, unit tests, rules tests against emulators, build; on merge to
main— deploy hosting, functions, rules and indexes. - Firebase Emulator Suite for local development, with a seed script (
npm run seed) creating: 3 users, 3 templates, 12 public specifications with 2–4 versions each and realistic statistics. - Security headers: CSP (allowing only self, Firebase endpoints and Google auth domains),
X-Content-Type-Options,Referrer-Policy: strict-origin-when-cross-origin,Permissions-Policy. - Error tracking via a lightweight, cookieless solution; log Cloud Function errors to Cloud Logging with structured fields.
20. Milestones
| # | Milestone | Contents | Done when |
|---|---|---|---|
| M0 | Foundations | Repo, Angular + SSR skeleton, Firebase projects, emulators, CI, design tokens, i18n, layout shell, static pages | /, /about, /privacy, /terms, /contact live and prerendered |
| M1 | Identity | Google auth, onboarding, handles, profile, account settings, guards, rules + rules tests | AC-1.1 … AC-1.4 |
| M2 | Authoring core | Spec CRUD, articles, structure, markdown editor with preview, autosave, asset upload | AC-3.x, AC-4.x |
| M3 | Versions | Publish, version list, version view, restore, immutability rules | AC-5.1, AC-5.2, AC-5.5 |
| M4 | Public surface | Visibility, catalogue, search index, specification public pages, SEO, sitemap | AC-2.2, AC-7.x |
| M5 | Distribution | ZIP/markdown/JSON/clipboard export, speckit.json, licence flow, download counting |
AC-6.x |
| M6 | Compare & fork | Diff engine, compare UI, fork, duplicate, attribution, fork lists | AC-5.3, AC-5.4 |
| M7 | Insight | Statistics pipeline, owner stats page, platform counters on landing | AC-8.x |
| M8 | Hardening | Accessibility audit, Lighthouse pass, moderation tools, abuse reports, GDPR deletion, seed content, launch | §2 targets met, 20 seeded specifications published |
21. Definition of done (global)
A feature is done when:
- Its acceptance criteria pass.
- Firestore/Storage rules enforce its authorisation and have tests.
- It works keyboard-only and passes an axe scan with no serious/critical issues.
- Both
plandenstrings exist. - Loading, empty and error states are implemented — never a blank screen or an unhandled rejection.
- Public pages render server-side without client-only errors.
- No
console.logoranyin the shipped code.
22. Post-v1 backlog (do not implement now)
- AI-assisted specification drafting and review inside the editor.
- Comments and suggestions on articles; pull-request-style change proposals between forks.
- Stars/bookmarks and personal collections.
- Organisations/teams with shared ownership.
- Public REST API (
GET /api/v1/specs/:handle/:slug/:version) and a CLI (npx openspeckit pull handle/slug). - Quality score and badges (structure completeness, article depth, screenshot presence).
- Embeddable "Open in agent" buttons for external sites.
- Webhooks on new versions.
- Multi-language variants of a single specification.
Built with this spec
4 screenshotsScreenshots from people who took this specification and shipped something. Add yours.
Sign in to add a screenshot of what you built
- Featured build Catalogue cover (automatic)