diff --git a/README.md b/README.md
index 10c593a9..606cba43 100644
--- a/README.md
+++ b/README.md
@@ -1,294 +1,132 @@
# SpecFact CLI
-> **SpecFact is the validation and alignment layer for software delivery.**
-> It adds the missing validation layer that keeps backlog intent, specifications, tests, and code
-> from drifting apart across AI-assisted coding, brownfield systems, and governed delivery.
-> Use it to move fast without losing rigor.
-
-**No API keys required. Works offline. Zero vendor lock-in.**
-
-SpecFact CLI does **not** include built-in AI. It is a deterministic local CLI
-that can be paired with IDE slash-command prompts so your chosen AI copilot can
-invoke SpecFact as part of a command chain.
+> Review AI-assisted code against your own contracts.
+> Catch drift before it reaches PR or main.
[](https://pypi.org/project/specfact-cli/)
[](https://pypi.org/project/specfact-cli/)
[](LICENSE)
[](https://github.com/nold-ai/specfact-cli)
+**No API keys required. Works offline. Zero vendor lock-in.**
+
-**[specfact.com](https://specfact.com)** • **[specfact.io](https://specfact.io)** • **[specfact.dev](https://specfact.dev)** • **[Documentation](https://docs.specfact.io/)** • **[Support](mailto:hello@noldai.com)**
+**[Documentation](https://docs.specfact.io/)** • **[Modules](https://modules.specfact.io/)** • **[Support](mailto:hello@noldai.com)**
----
-
-## What is SpecFact?
-
-SpecFact is the validation and alignment layer for software delivery.
-
-It is a local CLI that helps you keep the intent behind a change aligned from
-backlog or idea through specifications, implementation, and checks. The “Swiss-knife CLI” metaphor
-fits because SpecFact gives you a set of focused tools for specific delivery problems, not a vague
-bag of features.
-
-In practice, SpecFact helps you:
-- add guardrails to AI-assisted and fast-moving greenfield work
-- reverse-engineer large brownfield codebases into trustworthy structured understanding
-- reduce the “I wanted X but got Y” drift between backlog, spec, and implementation
-- move from local rigor toward team and enterprise policy enforcement
-
-## Why does it exist?
-
-Modern delivery drifts in predictable ways:
-- AI-generated quick wins often lack the validation layer needed for mid- and long-term reliability
-- brownfield systems often have missing or drifted specs, so teams need to reverse-engineer reality
-- backlog intent gets reinterpreted into something else before it reaches code
-- teams working with different skill levels, opinions, and AI IDE setups need consistent review and
- policy enforcement
-
-SpecFact exists to reduce that drift. It helps teams understand what is really there, express what
-should happen more accurately, and validate that the result still matches the original intent.
-
-## Why should I use it?
-
-Use SpecFact if you want one of these outcomes:
-- ship AI-assisted changes faster without accepting fragile “looks fine to me” quality
-- understand a legacy or unfamiliar codebase before changing it
-- hand brownfield insight into OpenSpec, Spec-Kit, or other spec-first workflows
-- keep backlog expectations, specifications, and implementation from silently diverging
-- enforce shared rules consistently across developers and CI/CD
-
-## What do I get?
-
-With SpecFact, you get:
-- a deterministic local CLI instead of another opaque SaaS dependency
-- a validation layer around fast-moving implementation work
-- codebase analysis and sidecar flows for brownfield understanding
-- stronger backlog/spec/code alignment for real delivery workflows
-- a path from individual rigor to organization-level policy enforcement
-
-## How do I get started?
-
-### Start Here (about 2 minutes): scored code review — no `pip install`
-
-**Point SpecFact at your code.** From a **git repository** (any branch), run two commands:
+## Try it in 60 seconds
```bash
+# Zero-install, no API key, works offline
uvx specfact-cli init --profile solo-developer
uvx specfact-cli code review run --path . --scope full
```
-You should see a **Verdict** (PASS/FAIL), a **Score**, and categorized **findings** — the fastest way to see SpecFact on real code before you dive into backlog, specs, or CI.
+**Sample output:**
-- **Command 1** installs the `solo-developer` bundles (including `specfact-codebase` and `specfact-code-review`) into your user module store so `code review` and related commands are available on the next invocation.
-- **Command 2** runs the clean-code review on the repo at `.`. Use **`--scope full`** on the first run so review does not depend on having local git changes.
+```text
+SpecFact CLI - v0.45.1
-**Already installed the CLI?** Use the same flow with `specfact` instead of `uvx specfact-cli`:
+Running Ruff checks...
+Running Radon complexity checks...
+Running Semgrep rules...
+Running AST clean-code checks...
+Running basedpyright type checks...
+Running pylint checks...
+Running contract checks...
+Running targeted tests and coverage...
-```bash
-specfact init --profile solo-developer
-specfact code review run --path . --scope full
-```
-
-**Read the canonical walkthrough:** **[Documentation — Quickstart](https://docs.specfact.io/getting-started/quickstart/)** · **[Installation](https://docs.specfact.io/getting-started/installation/)** (uvx-first, then persistent install).
+Verdict: FAIL | CI exit: 1 | Score: 0 | Reward delta: -80
-### Install (persistent CLI for daily use)
+Findings:
+ - specfact_demo/enforcement.py:96 Cyclomatic complexity for run_enforcement is 16.
+ - specfact_demo/main.py:81 Avoid print() in source files; use structured logging instead.
+ - examples/buggy_math.py:4 Public function divide is missing @require/@ensure decorators.
-```bash
-pip install -U specfact-cli
+Evidence bundle: docs/_support/readme-first-contact/sample-output/
```
-You can still use **`uvx specfact-cli@latest ...`** anytime without installing; it always fetches the latest published CLI.
+⭐ **Star this repo if the output is useful.** Open an issue if you want the workflow adapted for your stack.
-### After the wow path: deeper workflows
-
-When you want analysis, snapshots, or sidecar validation on top of the review layer:
+**Already installed the CLI?** Use:
```bash
-# Analyze a codebase you care about
-specfact code import my-project --repo .
-
-# Snapshot the project state for follow-up workflows
-specfact project snapshot --bundle my-project
-
-# Validate external code without modifying the target repo
-specfact code validate sidecar init my-project /path/to/repo
-specfact code validate sidecar run my-project /path/to/repo
-```
-
-### AI IDE setup
-
-```bash
-specfact init ide
-specfact init ide --ide cursor
-specfact init ide --ide vscode
+specfact init --profile solo-developer
+specfact code review run --path . --scope full
```
-`specfact init ide` discovers prompt resources from installed workflow modules and exports them to
-your IDE. If module prompt payloads are not installed yet, the CLI uses packaged fallback resources.
-
-## Choose Your Path
-
-### Greenfield and AI-assisted delivery
-
-Use SpecFact as the validation layer around fast-moving implementation work.
-
-Start with:
-- `uvx specfact-cli init --profile solo-developer` then `uvx specfact-cli code review run --path . --scope full` (see **Start Here** above)
-- `specfact code validate sidecar init /path/to/repo`
-- `specfact code validate sidecar run /path/to/repo`
+The sample output comes from a pinned capture against `nold-ai/specfact-demo-repo`. Reproduce it with `docs/_support/readme-first-contact/capture-readme-output.sh`; capture metadata lives alongside the raw logs in `docs/_support/readme-first-contact/sample-output/`.
-### Brownfield and reverse engineering
+## What SpecFact does
-Use SpecFact to understand an existing system before you change it, then hand that understanding
-into spec-first tools such as OpenSpec or Spec-Kit.
+- **Reviews AI-assisted changes deterministically** — compare code against contracts, clean-code rules, and policy gates
+- **Extracts structure from existing code** — reverse-engineer brownfield repos before you change them
+- **Blocks drift before merge** — use the same checks locally, in pre-commit, and in CI
+- **Links backlog intent to code reality** — connect backlog, specs, validation, and implementation
+- **Stays local-first** — no cloud account, no vendor lock-in, no built-in model dependency
-Start with:
-- `specfact code import my-project --repo .`
-- `specfact project snapshot --bundle my-project`
-- `specfact code validate sidecar init my-project /path/to/repo`
-- `specfact code validate sidecar run my-project /path/to/repo`
-
-### Backlog to code alignment
-
-Use SpecFact when the problem is not only code quality, but drift between expectations and delivery.
-Backlog commands require a backlog-enabled profile or installed backlog bundle before the workflow
-commands are available.
-
-Start with:
-- `specfact init --profile backlog-team`
-- `specfact backlog ceremony standup ...`
-- `specfact backlog ceremony refinement ...`
-- `specfact backlog verify-readiness --bundle `
-
-### Team and policy enforcement
-
-Use SpecFact when multiple developers and AI IDEs need consistent checks and review behavior.
-
-Start with:
-- `specfact backlog verify-readiness --bundle `
-- `specfact govern ...`
-- CI validation flows that keep the same rules active outside local development
-
-## How do I get started if I want more?
-
-**Next steps**
-
-- **[Core CLI docs](docs/index.md)** for the core runtime, bootstrap, validation, and command topology
-- **[Reference: command topology](docs/reference/commands.md)** for grouped command surfaces
-- **[Canonical modules docs site](https://modules.specfact.io/)** for bundle-deep workflows
-
-## Documentation Topology
-
-`docs.specfact.io` is the canonical starting point for SpecFact.
-
-- Core CLI/runtime/platform documentation remains owned by `specfact-cli`.
-- Module-specific deep docs are canonically owned by `specfact-cli-modules`.
-- The live modules docs site is published at `https://modules.specfact.io/`.
-
-Use this repository's docs for the overall product story, runtime lifecycle, command topology,
-trust model, and getting-started flow. Use the modules docs site when you want deeper workflow,
-adapter, and module-authoring guidance.
-
-### Migration Note (Flat Commands Removed)
-
-As of `0.40.0`, flat root commands are removed. Use grouped commands:
+## What is SpecFact?
-- `specfact validate ...` -> `specfact code validate ...`
-- `specfact plan ...` -> removed; use `specfact project devops-flow` or `specfact project snapshot`
-- `specfact policy ...` -> removed; use `specfact backlog verify-readiness`
+SpecFact is a local CLI for catching backlog/spec/code drift before it becomes expensive. It gives solo developers, legacy maintainers, and teams a validation layer around AI-assisted delivery, brownfield reverse engineering, and contract-first reviews.
-### Backlog Bridge (60 seconds)
+It exists because delivery drifts in predictable ways:
-SpecFact's USP is closing the drift gap between **backlog -> specs -> code**.
-These commands require the backlog bundle to be installed first, for example via
-`specfact init --profile backlog-team` or `specfact init --install backlog`.
+- AI-assisted code lands faster than validation catches up
+- brownfield systems rarely have trustworthy up-to-date specs
+- backlog intent gets reinterpreted before it reaches code
+- teams need the same review rules across IDEs, CI, and pull requests
-```bash
-# 1) Initialize backlog config + field mapping
-specfact backlog init-config --force
-specfact backlog map-fields --provider ado --ado-org --ado-project ""
+## Add SpecFact to your workflow
-# 2) Run ceremony workflows on real backlog scope
-specfact backlog ceremony standup ado --ado-org --ado-project "" --state any --assignee any --limit 5
-specfact backlog ceremony refinement ado --ado-org --ado-project "" --id --preview
+**Pre-commit hook**
-# 3) Keep backlog + spec intent aligned (avoid silent drift)
-specfact backlog verify-readiness --bundle
+```yaml
+- repo: https://github.com/nold-ai/specfact-cli
+ rev: v0.45.1
+ hooks:
+ - id: specfact-smart-checks
```
-Compatibility note: `specfact backlog daily ...` and `specfact backlog refine ...` still exist, but the preferred entrypoints are `backlog ceremony standup` and `backlog ceremony refinement`.
-
-For GitHub, replace adapter/org/project with:
-`specfact backlog ceremony standup github --repo-owner --repo-name --state any --assignee any --limit 5`
+**GitHub Actions**
-**AI IDE quick start**
-
-```bash
-# In your IDE chat (Cursor, VS Code, Copilot, etc.)
-/specfact.01-import my-project --repo .
+```yaml
+- name: SpecFact Gate
+ run: uvx specfact-cli@latest govern enforce stage --preset minimal
```
----
+## How SpecFact is built
-## Who It Is For
+SpecFact uses the same discipline it asks you to trust:
-- **Vibe coders and new builders** who want to ship fast with guardrails and confidence.
-- **Legacy professionals** who want AI speed without lowering standards.
-- **DevOps and engineering leaders** who need evidence and repeatable workflows.
+1. Outside-in research on the workflow or drift problem
+2. Public **OpenSpec** proposal and spec deltas
+3. TDD evidence before implementation
+4. Dogfooding with `specfact code review`
+5. Format, type-check, contract-test, and docs quality gates
+6. PR review with evidence artifacts
+7. Release through the same reproducible CLI paths
----
+## For teams and organizations
-## The Missing Link (Coder + DevOps Bridge)
+SpecFact still scales beyond the solo-developer entry path:
-Most tools help **either** coders **or** agile teams. SpecFact does both:
+- **Backlog + ceremony workflows** for GitHub, Azure DevOps, Jira, and Linear
+- **DoR/DoD and policy enforcement** for teams that need repeatable gates
+- **Evidence-backed PR review** with the same checks used locally
+- **CI/CD adoption path** that keeps validation deterministic instead of model-driven
-- **Backlog sync that is actually strong**: round-trip sync + refinement with GitHub, Azure DevOps, Jira, Linear.
-- **Ceremony support teams can run**: standup, refinement, sprint planning, flow metrics (Scrum/Kanban/SAFe).
-- **Policy + validation**: DoR/DoD/flow checks plus contract enforcement for production-grade stability.
+Start with:
-Recommended command entrypoints:
- `specfact backlog ceremony standup ...`
- `specfact backlog ceremony refinement ...`
- `specfact backlog verify-readiness --bundle `
-- `specfact backlog analyze-deps --bundle `
-
-What the backlog readiness and ceremony commands do in practice:
-- Turns team agreements (DoR, DoD, flow checks) into executable checks against your real backlog data.
-- Shows exactly what is missing per item (for example missing acceptance criteria or definition of done).
-- Runs structured ceremony workflows directly from the CLI.
-
-Start with:
-- `specfact backlog ceremony standup --help`
-- `specfact backlog verify-readiness --bundle `
-- `specfact backlog refine --help`
-
----
-
-## Core CLI Features
-
-The `specfact-cli` repository owns the platform-level features that every workflow bundle depends on:
-
-- `specfact init` for first-run bootstrap and IDE setup.
-- `specfact module` for install/list/show/search/enable/disable/uninstall/upgrade lifecycle flows.
-- `specfact upgrade` for CLI upgrades.
-- Runtime contracts, registry bootstrapping, trust checks, logging, and shared orchestration.
-- The grouped command surface that mounts installed bundle families under `project`, `backlog`, `code`, `spec`, and `govern`.
-
-## Official Modules Integration
+- `specfact govern ...`
-Official workflow behavior now ships from `nold-ai/specfact-cli-modules`.
-The core CLI discovers those bundle packages, mounts their command groups, and enforces compatibility, trust, and lifecycle rules.
+## Module system
-Installed official bundles expose the current grouped surfaces:
-
-- `specfact project ...`
-- `specfact backlog ...`
-- `specfact code ...`
-- `specfact spec ...`
-- `specfact govern ...`
+Official workflow behavior ships from `nold-ai/specfact-cli-modules`. The core CLI owns bootstrap, runtime contracts, trust checks, logging, and the grouped command surface. Installed modules add families such as `project`, `backlog`, `code`, `spec`, and `govern`.
Install examples:
@@ -296,34 +134,33 @@ Install examples:
specfact module install nold-ai/specfact-project
specfact module install nold-ai/specfact-backlog
specfact module install nold-ai/specfact-codebase
-specfact module install nold-ai/specfact-spec
+specfact module install nold-ai/specfact-code-review
specfact module install nold-ai/specfact-govern
```
-If startup warns that bundled modules are missing or outdated, run:
+If startup warns that modules are missing or outdated, run:
```bash
specfact module init --scope project
specfact module init
```
-Use this repo's docs for the current CLI/runtime release branch and the overall process of how official modules plug into the core platform.
-Use `https://modules.specfact.io/` for the in-depth backlog, project, spec, govern, adapter, and module-authoring guides.
+## Documentation topology
----
+`docs.specfact.io` is the canonical starting point for SpecFact.
-## How It Works (High Level)
+- Core CLI/runtime/platform documentation remains owned by `specfact-cli`
+- Module-specific deep docs are canonically owned by `specfact-cli-modules`
+- The live modules docs site is published at `https://modules.specfact.io/`
-1. **Bootstrap**: use **uvx** or **pip**, then `init --profile` to install the bundles you need (for example `solo-developer` for a scored **code review** first).
-2. **Review or analyze**: run **`code review run`** on a repo, or import code and snapshot state for deeper workflows.
-3. **Sync**: connect backlog systems or sync external artifacts into project bundles when you are ready.
-4. **Validate**: run spec, governance, and sidecar validation flows before implementation or release.
-5. **Iterate safely**: use module-provided workflows while the core runtime keeps command mounting, trust, and lifecycle consistent.
+Use this repository's docs for the product story, runtime lifecycle, command topology, trust model, and getting-started flow. Use the modules docs site when you want deeper workflow, adapter, and module-authoring guidance.
-## Where SpecFact Fits
+## How do I get started if I want more?
-SpecFact complements your stack rather than replacing it.
+Next steps:
-- **Spec-Kit/OpenSpec** for authoring and change tracking
-- **Backlog tools** for planning and delivery
-- **CI/CD** for enforcement and regression prevention
+- **[Core CLI docs](docs/index.md)** for the core runtime, bootstrap, and validation flow
+- **[Installation guide](https://docs.specfact.io/getting-started/installation/)** for uvx-first and persistent CLI setup
+- **[Quickstart](https://docs.specfact.io/getting-started/quickstart/)** for the first repo review path
+- **[Reference: command topology](docs/reference/commands.md)** for grouped command surfaces
+- **[Canonical modules docs site](https://modules.specfact.io/)** for bundle-deep workflows
diff --git a/docs/_support/readme-first-contact/capture-readme-output.sh b/docs/_support/readme-first-contact/capture-readme-output.sh
new file mode 100755
index 00000000..90987874
--- /dev/null
+++ b/docs/_support/readme-first-contact/capture-readme-output.sh
@@ -0,0 +1,70 @@
+#!/usr/bin/env bash
+
+set -euo pipefail
+
+SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+REPO_ROOT="$(cd "$SCRIPT_DIR/../../.." && pwd)"
+
+CLI_VERSION="${CLI_VERSION:-0.45.1}"
+REPO_SLUG="${REPO_SLUG:-nold-ai/specfact-demo-repo}"
+CAPTURE_REF="${CAPTURE_REF:-${CAPTURE_COMMIT:-2b5ba8cd57d16c1a1f24463a297fdb28fbede123}}"
+WORK_DIR="${WORK_DIR:-/tmp/specfact-demo-repo}"
+CAPTURE_HOME="${CAPTURE_HOME:-/tmp/specfact-readme-capture-home}"
+OUTPUT_DIR="${OUTPUT_DIR:-$REPO_ROOT/docs/_support/readme-first-contact/sample-output}"
+RAW_OUTPUT_PATH="${RAW_OUTPUT_PATH:-$OUTPUT_DIR/review-output.txt}"
+SUMMARY_PATH="${SUMMARY_PATH:-$OUTPUT_DIR/capture-metadata.txt}"
+INIT_OUTPUT_PATH="${INIT_OUTPUT_PATH:-$OUTPUT_DIR/init-output.txt}"
+
+mkdir -p "$OUTPUT_DIR"
+rm -rf "$CAPTURE_HOME"
+mkdir -p "$CAPTURE_HOME"
+
+if [[ ! -d "$WORK_DIR/.git" ]]; then
+ rm -rf "$WORK_DIR"
+ gh repo clone "$REPO_SLUG" "$WORK_DIR"
+fi
+
+git -C "$WORK_DIR" fetch --all --tags --prune
+git -C "$WORK_DIR" checkout --force "$CAPTURE_REF"
+git -C "$WORK_DIR" reset --hard "$CAPTURE_REF"
+
+export HOME="$CAPTURE_HOME"
+
+uvx --from "specfact-cli==$CLI_VERSION" specfact init --profile solo-developer \
+ >"$INIT_OUTPUT_PATH" 2>&1
+
+pushd "$WORK_DIR" >/dev/null
+uvx \
+ --from "specfact-cli==$CLI_VERSION" \
+ --with ruff \
+ --with radon \
+ --with semgrep \
+ --with basedpyright \
+ --with pylint \
+ --with crosshair-tool \
+ specfact code review run --path . --scope full \
+ >"$RAW_OUTPUT_PATH" 2>&1
+REVIEW_EXIT_CODE=$?
+popd >/dev/null
+
+cat >"$SUMMARY_PATH" < │
+│ │ │ │ │ │ (Unknown | │
+│ │ │ │ │ │ None), (key: │
+│ │ │ │ │ │ Unknown, │
+│ │ │ │ │ │ default: │
+│ │ │ │ │ │ Unknown, /) │
+│ │ │ │ │ │ -> Unknown, │
+│ │ │ │ │ │ (key: │
+│ │ │ │ │ │ Unknown, │
+│ │ │ │ │ │ default: │
+│ │ │ │ │ │ _T@get, /) │
+│ │ │ │ │ │ -> (Unknown │
+│ │ │ │ │ │ | _T@get)]" │
+│ /tmp/specfa… │ 68 │ basedpyright │ reportExplic… │ warning │ Type `Any` │
+│ │ │ │ │ │ is not │
+│ │ │ │ │ │ allowed │
+│ /tmp/specfa… │ 69 │ basedpyright │ reportExplic… │ warning │ Type `Any` │
+│ │ │ │ │ │ is not │
+│ │ │ │ │ │ allowed │
+│ /tmp/specfa… │ 70 │ basedpyright │ reportExplic… │ warning │ Type `Any` │
+│ │ │ │ │ │ is not │
+│ │ │ │ │ │ allowed │
+│ /tmp/specfa… │ 75 │ basedpyright │ reportAny │ warning │ Argument │
+│ │ │ │ │ │ type is Any │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ corresponds │
+│ │ │ │ │ │ to parameter │
+│ │ │ │ │ │ "key" in │
+│ │ │ │ │ │ function │
+│ │ │ │ │ │ "setdefault" │
+│ /tmp/specfa… │ 75 │ basedpyright │ reportAny │ warning │ Argument │
+│ │ │ │ │ │ type is Any │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ corresponds │
+│ │ │ │ │ │ to parameter │
+│ │ │ │ │ │ "object" in │
+│ │ │ │ │ │ function │
+│ │ │ │ │ │ "append" │
+│ /tmp/specfa… │ 78 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "item" is │
+│ │ │ │ │ │ Any │
+│ /tmp/specfa… │ 79 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "story_id" │
+│ │ │ │ │ │ is Any │
+│ /tmp/specfa… │ 79 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "get" is Any │
+│ /tmp/specfa… │ 80 │ basedpyright │ reportAny │ warning │ Argument │
+│ │ │ │ │ │ type is Any │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ corresponds │
+│ │ │ │ │ │ to parameter │
+│ │ │ │ │ │ "key" in │
+│ │ │ │ │ │ function │
+│ │ │ │ │ │ "get" │
+│ /tmp/specfa… │ 83 │ basedpyright │ reportAny │ warning │ Argument │
+│ │ │ │ │ │ type is Any │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ corresponds │
+│ │ │ │ │ │ to parameter │
+│ │ │ │ │ │ "map" in │
+│ │ │ │ │ │ function │
+│ │ │ │ │ │ "__init__" │
+│ /tmp/specfa… │ 86 │ basedpyright │ reportUnknow… │ warning │ Type of │
+│ │ │ │ │ │ "append" is │
+│ │ │ │ │ │ partially │
+│ │ │ │ │ │ unknown │
+│ │ │ │ │ │ Type of │
+│ │ │ │ │ │ "append" is │
+│ │ │ │ │ │ "(object: │
+│ │ │ │ │ │ Unknown, /) │
+│ │ │ │ │ │ -> None" │
+│ /tmp/specfa… │ 89 │ basedpyright │ reportUnknow… │ warning │ Argument │
+│ │ │ │ │ │ type is │
+│ │ │ │ │ │ partially │
+│ │ │ │ │ │ unknown │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ corresponds │
+│ │ │ │ │ │ to parameter │
+│ │ │ │ │ │ "obj" in │
+│ │ │ │ │ │ function │
+│ │ │ │ │ │ "len" │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ type is │
+│ │ │ │ │ │ "list[Unknow │
+│ │ │ │ │ │ n]" │
+│ /tmp/specfa… │ 90 │ basedpyright │ reportUnknow… │ warning │ Argument │
+│ │ │ │ │ │ type is │
+│ │ │ │ │ │ partially │
+│ │ │ │ │ │ unknown │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ corresponds │
+│ │ │ │ │ │ to parameter │
+│ │ │ │ │ │ "obj" in │
+│ │ │ │ │ │ function │
+│ │ │ │ │ │ "len" │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ type is │
+│ │ │ │ │ │ "list[Unknow │
+│ │ │ │ │ │ n]" │
+│ /tmp/specfa… │ 92 │ basedpyright │ reportAny │ warning │ Argument │
+│ │ │ │ │ │ type is Any │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ corresponds │
+│ │ │ │ │ │ to parameter │
+│ │ │ │ │ │ "obj" in │
+│ │ │ │ │ │ function │
+│ │ │ │ │ │ "len" │
+│ /tmp/specfa… │ 92 │ basedpyright │ reportUnknow… │ warning │ Argument │
+│ │ │ │ │ │ type is │
+│ │ │ │ │ │ partially │
+│ │ │ │ │ │ unknown │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ corresponds │
+│ │ │ │ │ │ to parameter │
+│ │ │ │ │ │ "obj" in │
+│ │ │ │ │ │ function │
+│ │ │ │ │ │ "len" │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ type is │
+│ │ │ │ │ │ "list[Unknow │
+│ │ │ │ │ │ n]" │
+│ /tmp/specfa… │ 100 │ basedpyright │ reportExplic… │ warning │ Type `Any` │
+│ │ │ │ │ │ is not │
+│ │ │ │ │ │ allowed │
+│ /tmp/specfa… │ 106 │ basedpyright │ reportExplic… │ warning │ Type `Any` │
+│ │ │ │ │ │ is not │
+│ │ │ │ │ │ allowed │
+│ /tmp/specfa… │ 114 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "stories_dat │
+│ │ │ │ │ │ a" is Any │
+│ /tmp/specfa… │ 115 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "backlog_dat │
+│ │ │ │ │ │ a" is Any │
+│ /tmp/specfa… │ 116 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "policy_data │
+│ │ │ │ │ │ " is Any │
+│ /tmp/specfa… │ 117 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "stories" is │
+│ │ │ │ │ │ Any │
+│ /tmp/specfa… │ 117 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "get" is Any │
+│ /tmp/specfa… │ 128 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "required_ap │
+│ │ │ │ │ │ i_version" │
+│ │ │ │ │ │ is Any │
+│ /tmp/specfa… │ 128 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "get" is Any │
+│ /tmp/specfa… │ 129 │ basedpyright │ reportExplic… │ warning │ Type `Any` │
+│ │ │ │ │ │ is not │
+│ │ │ │ │ │ allowed │
+│ /tmp/specfa… │ 133 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "story" is │
+│ │ │ │ │ │ Any │
+│ /tmp/specfa… │ 133 │ basedpyright │ reportAny │ warning │ Argument │
+│ │ │ │ │ │ type is Any │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ corresponds │
+│ │ │ │ │ │ to parameter │
+│ │ │ │ │ │ "iterable" │
+│ │ │ │ │ │ in function │
+│ │ │ │ │ │ "__new__" │
+│ /tmp/specfa… │ 134 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "story_id" │
+│ │ │ │ │ │ is Any │
+│ /tmp/specfa… │ 136 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "get" is Any │
+│ /tmp/specfa… │ 148 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "contract_re │
+│ │ │ │ │ │ l" is Any │
+│ /tmp/specfa… │ 148 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "get" is Any │
+│ /tmp/specfa… │ 161 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "contract_pa │
+│ │ │ │ │ │ th" is Any │
+│ /tmp/specfa… │ 162 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "exists" is │
+│ │ │ │ │ │ Any │
+│ /tmp/specfa… │ 174 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "contract_pa │
+│ │ │ │ │ │ yload" is │
+│ │ │ │ │ │ Any │
+│ /tmp/specfa… │ 174 │ basedpyright │ reportAny │ warning │ Argument │
+│ │ │ │ │ │ type is Any │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ corresponds │
+│ │ │ │ │ │ to parameter │
+│ │ │ │ │ │ "path" in │
+│ │ │ │ │ │ function │
+│ │ │ │ │ │ "_read_json" │
+│ /tmp/specfa… │ 175 │ basedpyright │ reportAny │ warning │ Argument │
+│ │ │ │ │ │ type is Any │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ corresponds │
+│ │ │ │ │ │ to parameter │
+│ │ │ │ │ │ "contract" │
+│ │ │ │ │ │ in function │
+│ │ │ │ │ │ "_validate_c │
+│ │ │ │ │ │ ontract" │
+│ /tmp/specfa… │ 175 │ basedpyright │ reportAny │ warning │ Argument │
+│ │ │ │ │ │ type is Any │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ corresponds │
+│ │ │ │ │ │ to parameter │
+│ │ │ │ │ │ "required_ap │
+│ │ │ │ │ │ i_version" │
+│ │ │ │ │ │ in function │
+│ │ │ │ │ │ "_validate_c │
+│ │ │ │ │ │ ontract" │
+│ /tmp/specfa… │ 204 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "evidence_pa │
+│ │ │ │ │ │ yload" is │
+│ │ │ │ │ │ Any │
+│ /tmp/specfa… │ 205 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "get" is Any │
+│ /tmp/specfa… │ 223 │ basedpyright │ reportAny │ warning │ Argument │
+│ │ │ │ │ │ type is Any │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ corresponds │
+│ │ │ │ │ │ to parameter │
+│ │ │ │ │ │ "backlog_dat │
+│ │ │ │ │ │ a" in │
+│ │ │ │ │ │ function │
+│ │ │ │ │ │ "_sync_backl │
+│ │ │ │ │ │ og" │
+│ /tmp/specfa… │ 227 │ basedpyright │ reportAny │ warning │ Argument │
+│ │ │ │ │ │ type is Any │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ corresponds │
+│ │ │ │ │ │ to parameter │
+│ │ │ │ │ │ "obj" in │
+│ │ │ │ │ │ function │
+│ │ │ │ │ │ "len" │
+│ /tmp/specfa… │ 235 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "get" is Any │
+│ /tmp/specfa… │ 235 │ basedpyright │ reportAny │ warning │ Argument │
+│ │ │ │ │ │ type is Any │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ corresponds │
+│ │ │ │ │ │ to parameter │
+│ │ │ │ │ │ "x" in │
+│ │ │ │ │ │ function │
+│ │ │ │ │ │ "__new__" │
+│ /tmp/specfa… │ 297 │ basedpyright │ reportImplic… │ warning │ Implicit │
+│ │ │ │ │ │ string │
+│ │ │ │ │ │ concatenatio │
+│ │ │ │ │ │ n not │
+│ │ │ │ │ │ allowed │
+│ /tmp/specfa… │ 303 │ basedpyright │ reportUnused… │ warning │ Result of │
+│ │ │ │ │ │ call │
+│ │ │ │ │ │ expression │
+│ │ │ │ │ │ is of type │
+│ │ │ │ │ │ "int" and is │
+│ │ │ │ │ │ not used; │
+│ │ │ │ │ │ assign to │
+│ │ │ │ │ │ variable "_" │
+│ │ │ │ │ │ if this is │
+│ │ │ │ │ │ intentional │
+│ /tmp/specfa… │ 24 │ basedpyright │ reportUnanno… │ warning │ Type │
+│ │ │ │ │ │ annotation │
+│ │ │ │ │ │ for │
+│ │ │ │ │ │ attribute │
+│ │ │ │ │ │ `name` is │
+│ │ │ │ │ │ required │
+│ │ │ │ │ │ because this │
+│ │ │ │ │ │ class is not │
+│ │ │ │ │ │ decorated │
+│ │ │ │ │ │ with │
+│ │ │ │ │ │ `@final` │
+│ /tmp/specfa… │ 25 │ basedpyright │ reportUnanno… │ warning │ Type │
+│ │ │ │ │ │ annotation │
+│ │ │ │ │ │ for │
+│ │ │ │ │ │ attribute │
+│ │ │ │ │ │ `version` is │
+│ │ │ │ │ │ required │
+│ │ │ │ │ │ because this │
+│ │ │ │ │ │ class is not │
+│ │ │ │ │ │ decorated │
+│ │ │ │ │ │ with │
+│ │ │ │ │ │ `@final` │
+│ /tmp/specfa… │ 26 │ basedpyright │ reportUnanno… │ warning │ Type │
+│ │ │ │ │ │ annotation │
+│ │ │ │ │ │ for │
+│ │ │ │ │ │ attribute │
+│ │ │ │ │ │ `api_version │
+│ │ │ │ │ │ ` is │
+│ │ │ │ │ │ required │
+│ │ │ │ │ │ because this │
+│ │ │ │ │ │ class is not │
+│ │ │ │ │ │ decorated │
+│ │ │ │ │ │ with │
+│ │ │ │ │ │ `@final` │
+│ /tmp/specfa… │ 27 │ basedpyright │ reportUnanno… │ warning │ Type │
+│ │ │ │ │ │ annotation │
+│ │ │ │ │ │ for │
+│ │ │ │ │ │ attribute │
+│ │ │ │ │ │ `scope` is │
+│ │ │ │ │ │ required │
+│ │ │ │ │ │ because this │
+│ │ │ │ │ │ class is not │
+│ │ │ │ │ │ decorated │
+│ │ │ │ │ │ with │
+│ │ │ │ │ │ `@final` │
+│ /tmp/specfa… │ 28 │ basedpyright │ reportUnanno… │ warning │ Type │
+│ │ │ │ │ │ annotation │
+│ │ │ │ │ │ for │
+│ │ │ │ │ │ attribute │
+│ │ │ │ │ │ `invariants_ │
+│ │ │ │ │ │ touched` is │
+│ │ │ │ │ │ required │
+│ │ │ │ │ │ because this │
+│ │ │ │ │ │ class is not │
+│ │ │ │ │ │ decorated │
+│ │ │ │ │ │ with │
+│ │ │ │ │ │ `@final` │
+│ /tmp/specfa… │ 29 │ basedpyright │ reportUnanno… │ warning │ Type │
+│ │ │ │ │ │ annotation │
+│ │ │ │ │ │ for │
+│ │ │ │ │ │ attribute │
+│ │ │ │ │ │ `side_effect │
+│ │ │ │ │ │ s` is │
+│ │ │ │ │ │ required │
+│ │ │ │ │ │ because this │
+│ │ │ │ │ │ class is not │
+│ │ │ │ │ │ decorated │
+│ │ │ │ │ │ with │
+│ │ │ │ │ │ `@final` │
+│ /tmp/specfa… │ 30 │ basedpyright │ reportUnanno… │ warning │ Type │
+│ │ │ │ │ │ annotation │
+│ │ │ │ │ │ for │
+│ │ │ │ │ │ attribute │
+│ │ │ │ │ │ `attestation │
+│ │ │ │ │ │ _hash` is │
+│ │ │ │ │ │ required │
+│ │ │ │ │ │ because this │
+│ │ │ │ │ │ class is not │
+│ │ │ │ │ │ decorated │
+│ │ │ │ │ │ with │
+│ │ │ │ │ │ `@final` │
+│ /tmp/specfa… │ 32 │ basedpyright │ reportUnused… │ warning │ "context" is │
+│ │ │ │ │ │ not accessed │
+│ /tmp/specfa… │ 32 │ basedpyright │ reportExplic… │ warning │ Type `Any` │
+│ │ │ │ │ │ is not │
+│ │ │ │ │ │ allowed │
+│ /tmp/specfa… │ 35 │ basedpyright │ reportUnused… │ warning │ "context" is │
+│ │ │ │ │ │ not accessed │
+│ /tmp/specfa… │ 35 │ basedpyright │ reportExplic… │ warning │ Type `Any` │
+│ │ │ │ │ │ is not │
+│ │ │ │ │ │ allowed │
+│ /tmp/specfa… │ 38 │ basedpyright │ reportUnused… │ warning │ "context" is │
+│ │ │ │ │ │ not accessed │
+│ /tmp/specfa… │ 38 │ basedpyright │ reportExplic… │ warning │ Type `Any` │
+│ │ │ │ │ │ is not │
+│ │ │ │ │ │ allowed │
+│ /tmp/specfa… │ 41 │ basedpyright │ reportUnused… │ warning │ "context" is │
+│ │ │ │ │ │ not accessed │
+│ /tmp/specfa… │ 41 │ basedpyright │ reportExplic… │ warning │ Type `Any` │
+│ │ │ │ │ │ is not │
+│ │ │ │ │ │ allowed │
+│ /tmp/specfa… │ 44 │ basedpyright │ reportUnused… │ warning │ "context" is │
+│ │ │ │ │ │ not accessed │
+│ /tmp/specfa… │ 44 │ basedpyright │ reportExplic… │ warning │ Type `Any` │
+│ │ │ │ │ │ is not │
+│ │ │ │ │ │ allowed │
+│ /tmp/specfa… │ 47 │ basedpyright │ reportUnused… │ warning │ "context" is │
+│ │ │ │ │ │ not accessed │
+│ /tmp/specfa… │ 47 │ basedpyright │ reportExplic… │ warning │ Type `Any` │
+│ │ │ │ │ │ is not │
+│ │ │ │ │ │ allowed │
+│ /tmp/specfa… │ 10 │ basedpyright │ reportDeprec… │ warning │ This type is │
+│ │ │ │ │ │ deprecated │
+│ │ │ │ │ │ as of Python │
+│ │ │ │ │ │ 3.9; use │
+│ │ │ │ │ │ "collections │
+│ │ │ │ │ │ .abc.Iterabl │
+│ │ │ │ │ │ e" instead │
+│ /tmp/specfa… │ 15 │ basedpyright │ reportAny │ warning │ Return type │
+│ │ │ │ │ │ is Any │
+│ /tmp/specfa… │ 15 │ basedpyright │ reportExplic… │ warning │ Type `Any` │
+│ │ │ │ │ │ is not │
+│ │ │ │ │ │ allowed │
+│ /tmp/specfa… │ 17 │ basedpyright │ reportAny │ warning │ Return type │
+│ │ │ │ │ │ is Any │
+│ /tmp/specfa… │ 34 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "value" is │
+│ │ │ │ │ │ Any │
+│ /tmp/specfa… │ 35 │ basedpyright │ reportAny │ warning │ Argument │
+│ │ │ │ │ │ type is Any │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ corresponds │
+│ │ │ │ │ │ to parameter │
+│ │ │ │ │ │ "object" in │
+│ │ │ │ │ │ function │
+│ │ │ │ │ │ "isclass" │
+│ /tmp/specfa… │ 36 │ basedpyright │ reportUnknow… │ warning │ Type of │
+│ │ │ │ │ │ "append" is │
+│ │ │ │ │ │ partially │
+│ │ │ │ │ │ unknown │
+│ │ │ │ │ │ Type of │
+│ │ │ │ │ │ "append" is │
+│ │ │ │ │ │ "(object: │
+│ │ │ │ │ │ Unknown, /) │
+│ │ │ │ │ │ -> None" │
+│ /tmp/specfa… │ 41 │ basedpyright │ reportUnknow… │ warning │ Argument │
+│ │ │ │ │ │ type is │
+│ │ │ │ │ │ partially │
+│ │ │ │ │ │ unknown │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ corresponds │
+│ │ │ │ │ │ to parameter │
+│ │ │ │ │ │ "obj" in │
+│ │ │ │ │ │ function │
+│ │ │ │ │ │ "len" │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ type is │
+│ │ │ │ │ │ "list[Unknow │
+│ │ │ │ │ │ n]" │
+│ /tmp/specfa… │ 42 │ basedpyright │ reportUnknow… │ warning │ Type of │
+│ │ │ │ │ │ "sort" is │
+│ │ │ │ │ │ partially │
+│ │ │ │ │ │ unknown │
+│ │ │ │ │ │ Type of │
+│ │ │ │ │ │ "sort" is │
+│ │ │ │ │ │ "Overload[(* │
+│ │ │ │ │ │ , key: None │
+│ │ │ │ │ │ = None, │
+│ │ │ │ │ │ reverse: │
+│ │ │ │ │ │ bool = │
+│ │ │ │ │ │ False) -> │
+│ │ │ │ │ │ None, (*, │
+│ │ │ │ │ │ key: │
+│ │ │ │ │ │ (Unknown) -> │
+│ │ │ │ │ │ (SupportsDun │
+│ │ │ │ │ │ derLT[Any] | │
+│ │ │ │ │ │ SupportsDund │
+│ │ │ │ │ │ erGT[Any]), │
+│ │ │ │ │ │ reverse: │
+│ │ │ │ │ │ bool = │
+│ │ │ │ │ │ False) -> │
+│ │ │ │ │ │ None]" │
+│ /tmp/specfa… │ 42 │ basedpyright │ reportUnknow… │ warning │ Type of │
+│ │ │ │ │ │ parameter │
+│ │ │ │ │ │ "cls" is │
+│ │ │ │ │ │ unknown │
+│ /tmp/specfa… │ 42 │ basedpyright │ reportUnknow… │ warning │ Type of │
+│ │ │ │ │ │ "__name__" │
+│ │ │ │ │ │ is unknown │
+│ /tmp/specfa… │ 42 │ basedpyright │ reportUnknow… │ warning │ Return type │
+│ │ │ │ │ │ of lambda is │
+│ │ │ │ │ │ unknown │
+│ /tmp/specfa… │ 44 │ basedpyright │ reportUnknow… │ warning │ Return type │
+│ │ │ │ │ │ is unknown │
+│ /tmp/specfa… │ 51 │ basedpyright │ reportExplic… │ warning │ Type `Any` │
+│ │ │ │ │ │ is not │
+│ │ │ │ │ │ allowed │
+│ /tmp/specfa… │ 55 │ basedpyright │ reportUnused… │ warning │ Result of │
+│ │ │ │ │ │ call │
+│ │ │ │ │ │ expression │
+│ │ │ │ │ │ is of type │
+│ │ │ │ │ │ "object" and │
+│ │ │ │ │ │ is not used; │
+│ │ │ │ │ │ assign to │
+│ │ │ │ │ │ variable "_" │
+│ │ │ │ │ │ if this is │
+│ │ │ │ │ │ intentional │
+│ /tmp/specfa… │ 67 │ basedpyright │ reportImplic… │ warning │ Implicit │
+│ │ │ │ │ │ string │
+│ │ │ │ │ │ concatenatio │
+│ │ │ │ │ │ n not │
+│ │ │ │ │ │ allowed │
+│ /tmp/specfa… │ 70 │ basedpyright │ reportUnnece… │ warning │ Unnecessary │
+│ │ │ │ │ │ isinstance │
+│ │ │ │ │ │ call; "list" │
+│ │ │ │ │ │ is always an │
+│ │ │ │ │ │ instance of │
+│ │ │ │ │ │ "list[Unknow │
+│ │ │ │ │ │ n]" │
+│ /tmp/specfa… │ 72 │ basedpyright │ reportUnnece… │ warning │ Unnecessary │
+│ │ │ │ │ │ isinstance │
+│ │ │ │ │ │ call; │
+│ │ │ │ │ │ "list[Unknow │
+│ │ │ │ │ │ n]" is │
+│ │ │ │ │ │ always an │
+│ │ │ │ │ │ instance of │
+│ │ │ │ │ │ "list[Unknow │
+│ │ │ │ │ │ n]" │
+│ /tmp/specfa… │ 72 │ basedpyright │ reportUnknow… │ warning │ Type of │
+│ │ │ │ │ │ "invariants_ │
+│ │ │ │ │ │ touched" is │
+│ │ │ │ │ │ partially │
+│ │ │ │ │ │ unknown │
+│ │ │ │ │ │ Type of │
+│ │ │ │ │ │ "invariants_ │
+│ │ │ │ │ │ touched" is │
+│ │ │ │ │ │ "list[Unknow │
+│ │ │ │ │ │ n]" │
+│ /tmp/specfa… │ 74 │ basedpyright │ reportUnnece… │ warning │ Unnecessary │
+│ │ │ │ │ │ isinstance │
+│ │ │ │ │ │ call; │
+│ │ │ │ │ │ "list[Unknow │
+│ │ │ │ │ │ n]" is │
+│ │ │ │ │ │ always an │
+│ │ │ │ │ │ instance of │
+│ │ │ │ │ │ "list[Unknow │
+│ │ │ │ │ │ n]" │
+│ /tmp/specfa… │ 74 │ basedpyright │ reportUnknow… │ warning │ Type of │
+│ │ │ │ │ │ "side_effect │
+│ │ │ │ │ │ s" is │
+│ │ │ │ │ │ partially │
+│ │ │ │ │ │ unknown │
+│ │ │ │ │ │ Type of │
+│ │ │ │ │ │ "side_effect │
+│ │ │ │ │ │ s" is │
+│ │ │ │ │ │ "list[Unknow │
+│ │ │ │ │ │ n]" │
+│ /tmp/specfa… │ 77 │ basedpyright │ reportUnnece… │ warning │ Unnecessary │
+│ │ │ │ │ │ isinstance │
+│ │ │ │ │ │ call; "list" │
+│ │ │ │ │ │ is always an │
+│ │ │ │ │ │ instance of │
+│ │ │ │ │ │ "list[Unknow │
+│ │ │ │ │ │ n]" │
+│ /tmp/specfa… │ 88 │ basedpyright │ reportExplic… │ warning │ Type `Any` │
+│ │ │ │ │ │ is not │
+│ │ │ │ │ │ allowed │
+│ /tmp/specfa… │ 91 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "policy" is │
+│ │ │ │ │ │ Any │
+│ /tmp/specfa… │ 92 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "allowed_sco │
+│ │ │ │ │ │ pes" is Any │
+│ /tmp/specfa… │ 92 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "get" is Any │
+│ /tmp/specfa… │ 95 │ basedpyright │ reportAny │ warning │ Argument │
+│ │ │ │ │ │ type is Any │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ corresponds │
+│ │ │ │ │ │ to parameter │
+│ │ │ │ │ │ "allowed_sco │
+│ │ │ │ │ │ pes" in │
+│ │ │ │ │ │ function │
+│ │ │ │ │ │ "validate_pl │
+│ │ │ │ │ │ ugin_metadat │
+│ │ │ │ │ │ a" │
+│ /tmp/specfa… │ 97 │ basedpyright │ reportExplic… │ warning │ Type `Any` │
+│ │ │ │ │ │ is not │
+│ │ │ │ │ │ allowed │
+│ /tmp/specfa… │ 108 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "append" is │
+│ │ │ │ │ │ Any │
+│ /tmp/specfa… │ 113 │ basedpyright │ reportUnused… │ warning │ Result of │
+│ │ │ │ │ │ call │
+│ │ │ │ │ │ expression │
+│ │ │ │ │ │ is of type │
+│ │ │ │ │ │ "object" and │
+│ │ │ │ │ │ is not used; │
+│ │ │ │ │ │ assign to │
+│ │ │ │ │ │ variable "_" │
+│ │ │ │ │ │ if this is │
+│ │ │ │ │ │ intentional │
+│ /tmp/specfa… │ 133 │ basedpyright │ reportExplic… │ warning │ Type `Any` │
+│ │ │ │ │ │ is not │
+│ │ │ │ │ │ allowed │
+│ /tmp/specfa… │ 209 │ basedpyright │ reportUnused… │ warning │ Result of │
+│ │ │ │ │ │ call │
+│ │ │ │ │ │ expression │
+│ │ │ │ │ │ is of type │
+│ │ │ │ │ │ "int" and is │
+│ │ │ │ │ │ not used; │
+│ │ │ │ │ │ assign to │
+│ │ │ │ │ │ variable "_" │
+│ │ │ │ │ │ if this is │
+│ │ │ │ │ │ intentional │
+│ /tmp/specfa… │ 20 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "report" is │
+│ │ │ │ │ │ Any │
+│ /tmp/specfa… │ 24 │ basedpyright │ reportAny │ warning │ Argument │
+│ │ │ │ │ │ type is Any │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ corresponds │
+│ │ │ │ │ │ to parameter │
+│ │ │ │ │ │ "first" in │
+│ │ │ │ │ │ function │
+│ │ │ │ │ │ "assertAlmos │
+│ │ │ │ │ │ tEqual" │
+│ /tmp/specfa… │ 29 │ basedpyright │ reportAny │ warning │ Argument │
+│ │ │ │ │ │ type is Any │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ corresponds │
+│ │ │ │ │ │ to parameter │
+│ │ │ │ │ │ "first" in │
+│ │ │ │ │ │ function │
+│ │ │ │ │ │ "assertAlmos │
+│ │ │ │ │ │ tEqual" │
+│ /tmp/specfa… │ 39 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "backlog_syn │
+│ │ │ │ │ │ c" is Any │
+│ /tmp/specfa… │ 43 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "item" is │
+│ │ │ │ │ │ Any │
+│ /tmp/specfa… │ 44 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "label" is │
+│ │ │ │ │ │ Any │
+│ /tmp/specfa… │ 44 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "get" is Any │
+│ /tmp/specfa… │ 56 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "report" is │
+│ │ │ │ │ │ Any │
+│ /tmp/specfa… │ 60 │ basedpyright │ reportAny │ warning │ Argument │
+│ │ │ │ │ │ type is Any │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ corresponds │
+│ │ │ │ │ │ to parameter │
+│ │ │ │ │ │ "first" in │
+│ │ │ │ │ │ function │
+│ │ │ │ │ │ "assertAlmos │
+│ │ │ │ │ │ tEqual" │
+│ /tmp/specfa… │ 65 │ basedpyright │ reportAny │ warning │ Argument │
+│ │ │ │ │ │ type is Any │
+│ │ │ │ │ │ Argument │
+│ │ │ │ │ │ corresponds │
+│ │ │ │ │ │ to parameter │
+│ │ │ │ │ │ "first" in │
+│ │ │ │ │ │ function │
+│ │ │ │ │ │ "assertAlmos │
+│ │ │ │ │ │ tEqual" │
+│ /tmp/specfa… │ 72 │ basedpyright │ reportUnused… │ warning │ Result of │
+│ │ │ │ │ │ call │
+│ │ │ │ │ │ expression │
+│ │ │ │ │ │ is of type │
+│ │ │ │ │ │ "main" and │
+│ │ │ │ │ │ is not used; │
+│ │ │ │ │ │ assign to │
+│ │ │ │ │ │ variable "_" │
+│ │ │ │ │ │ if this is │
+│ │ │ │ │ │ intentional │
+│ /tmp/specfa… │ 23 │ basedpyright │ reportUnused… │ warning │ Result of │
+│ │ │ │ │ │ call │
+│ │ │ │ │ │ expression │
+│ │ │ │ │ │ is of type │
+│ │ │ │ │ │ "int" and is │
+│ │ │ │ │ │ not used; │
+│ │ │ │ │ │ assign to │
+│ │ │ │ │ │ variable "_" │
+│ │ │ │ │ │ if this is │
+│ │ │ │ │ │ intentional │
+│ /tmp/specfa… │ 45 │ basedpyright │ reportAny │ warning │ Type of │
+│ │ │ │ │ │ "error" is │
+│ │ │ │ │ │ Any │
+│ /tmp/specfa… │ 49 │ basedpyright │ reportUnused… │ warning │ Result of │
+│ │ │ │ │ │ call │
+│ │ │ │ │ │ expression │
+│ │ │ │ │ │ is of type │
+│ │ │ │ │ │ "main" and │
+│ │ │ │ │ │ is not used; │
+│ │ │ │ │ │ assign to │
+│ │ │ │ │ │ variable "_" │
+│ │ │ │ │ │ if this is │
+│ │ │ │ │ │ intentional │
+└──────────────┴──────┴──────────────┴───────────────┴──────────┴──────────────┘
+Verdict: FAIL | CI exit: 1 | Score: 0 | Reward delta: -80
+Review completed with 217 findings (2 blocking).
+
+✓ Finished: 2026-04-03 20:55:34 | Duration: 5.98s
diff --git a/docs/index.md b/docs/index.md
index d8d81fb9..fa4a1063 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -17,14 +17,19 @@ exempt_reason: ""
# SpecFact CLI Documentation
-**Point SpecFact at your code. Get a score and a list of what to fix.** No week-long setup — start with two commands, then add IDE prompts, backlog workflows, or CI gates when you want more.
+**Review AI-assisted code against your own contracts.**
+**Catch drift before it reaches PR or main.**
+
+Point SpecFact at your repo, get a scored review with file-level findings, then go deeper into
+backlog, specs, and CI when you need more control.
```bash
uvx specfact-cli init --profile solo-developer
uvx specfact-cli code review run --path . --scope full
```
-You should see a **Verdict** (PASS/FAIL), a **Score**, and a list of findings (for example dozens of categorized items on a real repo). That is the fastest way to see SpecFact on an existing project. [Read the full quickstart →](/getting-started/quickstart/)
+You should see a **Verdict**, a **Score**, and a list of findings on a real repo. That is the
+fastest way to see SpecFact on existing code. [Read the full quickstart →](/getting-started/quickstart/)
SpecFact does **not** include built-in AI. It pairs deterministic CLI commands with your chosen IDE and copilot so fast-moving work has a stronger validation and alignment layer around it.
diff --git a/openspec/CHANGE_ORDER.md b/openspec/CHANGE_ORDER.md
index c7e71951..2564bf55 100644
--- a/openspec/CHANGE_ORDER.md
+++ b/openspec/CHANGE_ORDER.md
@@ -127,6 +127,7 @@ The 2026-03-22 clean-code plan adds one new cross-repo change pair and re-sequen
| docs | 09 | docs-13-core-nav-search-theme-roles | [#458](https://github.com/nold-ai/specfact-cli/issues/458) | docs-05-core-site-ia-restructure; docs-07-core-handoff-conversion; docs-12-docs-validation-ci; modules-repo/docs-13-nav-search-theme-roles (design parity only, no content ownership coupling) |
| docs | 10 | docs-14-first-contact-story-and-onboarding (in progress) | [#466](https://github.com/nold-ai/specfact-cli/issues/466) | docs-05-core-site-ia-restructure ✅; docs-07-core-handoff-conversion ✅; docs-12-docs-validation-ci ✅; docs-13-core-nav-search-theme-roles ✅; Parent Feature: [#356](https://github.com/nold-ai/specfact-cli/issues/356) |
| docs | 11 | docs-new-user-onboarding | [#476](https://github.com/nold-ai/specfact-cli/issues/476) | Parent Feature: [#356](https://github.com/nold-ai/specfact-cli/issues/356); related [#466](https://github.com/nold-ai/specfact-cli/issues/466); vibe-coder uvx hero + CLI wow-path fixes |
+| docs | 12 | readme-star-conversion-01 | pending | blocked by `docs-new-user-onboarding` [#476](https://github.com/nold-ai/specfact-cli/issues/476); README-first proof and adoption restructuring |
### Docs refactoring plan addendum (2026-03-23)
diff --git a/openspec/changes/readme-star-conversion-01/.openspec.yaml b/openspec/changes/readme-star-conversion-01/.openspec.yaml
new file mode 100644
index 00000000..c430c5fa
--- /dev/null
+++ b/openspec/changes/readme-star-conversion-01/.openspec.yaml
@@ -0,0 +1,2 @@
+schema: spec-driven
+created: 2026-04-03
diff --git a/openspec/changes/readme-star-conversion-01/TDD_EVIDENCE.md b/openspec/changes/readme-star-conversion-01/TDD_EVIDENCE.md
new file mode 100644
index 00000000..183b1e39
--- /dev/null
+++ b/openspec/changes/readme-star-conversion-01/TDD_EVIDENCE.md
@@ -0,0 +1,74 @@
+# TDD evidence — readme-star-conversion-01
+
+## 2026-04-03 (pre-implementation failing run)
+
+### Timestamp
+
+- `2026-04-03 20:08:28 +0000`
+
+### Command run
+
+- `hatch run pytest tests/unit/docs/test_wow_entrypoint_contract.py tests/unit/docs/test_first_contact_story.py tests/unit/docs/test_release_docs_parity.py -q --no-cov`
+
+### Result
+
+- **Failed as expected** before README changes
+
+### Failure summary
+
+- README still opens with the older platform-first "validation and alignment layer" framing instead
+ of the new proof-first developer hook
+- README does not yet include the required sample output block, CTA, trust section, or reordered
+ top-level sections
+- `docs/_support/readme-first-contact/capture-readme-output.sh` and
+ `docs/_support/readme-first-contact/sample-output/` do not exist yet
+- `docs/index.md` has not yet been aligned to the new README hook / subhook wording
+
+### Evidence notes
+
+- Failing test output captured from the command above and used as the pre-implementation baseline
+- Post-implementation passing evidence will be appended after the README, docs, and evidence assets
+ are updated
+
+## 2026-04-03 (post-implementation passing run)
+
+### Timestamp
+
+- `2026-04-03 20:20:08 +0000`
+
+### Commands run
+
+- `hatch run pytest tests/unit/docs/test_wow_entrypoint_contract.py tests/unit/docs/test_first_contact_story.py tests/unit/docs/test_release_docs_parity.py -q --no-cov`
+- `hatch run yaml-lint`
+- `hatch run format`
+- `hatch run type-check`
+- `hatch run contract-test`
+- `hatch run specfact code review run --json --out .specfact/code-review.json`
+- `hatch run specfact code review run tests/unit/docs/test_wow_entrypoint_contract.py tests/unit/docs/test_first_contact_story.py tests/unit/docs/test_release_docs_parity.py --json --out .specfact/code-review-docs-tests.json`
+- `docs/_support/readme-first-contact/capture-readme-output.sh`
+
+### Result
+
+- **Passed** for the README/docs proof-first contract and supporting quality gates
+
+### Passing summary
+
+- `README.md` now leads with the proof-first hook, runnable quickstart, real sample-output block, CTA,
+ workflow snippet, trust section, team section, and deferred module/documentation detail
+- `docs/index.md` now shares the same first-contact hook and runnable quickstart ordering
+- Reproducible capture assets exist at `docs/_support/readme-first-contact/sample-output/` and are
+ generated by `docs/_support/readme-first-contact/capture-readme-output.sh`
+- The capture script now pins `nold-ai/specfact-demo-repo` to commit
+ `2b5ba8cd57d16c1a1f24463a297fdb28fbede123` so README evidence stays deterministic across reruns
+- Focused docs contract tests and focused SpecFact review checks for the changed docs test files pass
+
+### Environment notes
+
+- `hatch run smart-test` still fails in this environment for an unrelated repository baseline issue:
+ `tests/integration/test_command_package_runtime_validation.py` raises `IndexError: 2` during test
+ collection before executing this change's paths
+- `hatch run specfact code review run --json --out .specfact/code-review.json --scope full` writes the
+ required `.specfact/code-review.json` artifact but exits non-zero because the repository-wide review
+ surface already contains unrelated findings; a focused review of the changed docs test files passes and
+ was recorded above
+- `openspec validate readme-star-conversion-01 --strict` now passes after installing the OpenSpec CLI
diff --git a/openspec/changes/readme-star-conversion-01/proposal.md b/openspec/changes/readme-star-conversion-01/proposal.md
new file mode 100644
index 00000000..c62c7e07
--- /dev/null
+++ b/openspec/changes/readme-star-conversion-01/proposal.md
@@ -0,0 +1,93 @@
+# Change: readme-star-conversion-01
+
+## Why
+
+The repository README still opens with platform and governance language before showing a runnable
+command or a visible proof point. First-time visitors have to scroll past conceptual explanation
+before they see what SpecFact actually does on a real repository.
+
+That is a conversion problem for the audience most likely to star or try the project:
+
+- Python developers who want a concrete CLI outcome fast
+- solo engineers and vibe coders who evaluate tools by running one command
+- OSS visitors who decide in seconds whether a repo looks useful or too enterprise-heavy
+
+The current README already contains the uvx-first review path, but it appears only after multiple
+sections of framing. The README also lacks a real terminal output block that proves what the tool
+returns. A first-contact surface that does not show input -> output wastes the strongest available
+trust signal for an OSS CLI.
+
+This change restructures the README so the first screen answers four questions immediately:
+
+1. What does this do?
+2. What does the output look like?
+3. How do I try it right now?
+4. Why should I care?
+
+## What Changes
+
+- Rewrite the README top section around a proof-first hero:
+ - concrete one-line value proposition for developers
+ - badges directly below the title
+ - uvx quickstart within the first screen
+ - real sample output block directly after the quickstart
+ - visible CTA to star the repo if the output is useful
+- Add a reproducible capture script and checked-in evidence bundle for the README sample output
+- Reorder README sections so developer-first outcomes appear above enterprise and module-system
+ detail
+- Add a short "How SpecFact is built" section that turns the repo's OpenSpec + TDD workflow into a
+ trust signal
+- Add copy-pasteable pre-commit and GitHub Actions snippets near the top-half of the README
+- Keep team / enterprise content, module system detail, and documentation topology, but move them
+ below the proof-first onboarding flow
+
+## Capabilities
+
+### New Capabilities
+
+- `readme-first-contact`: The repository README acts as a proof-first OSS landing page that leads
+ with a concrete CLI outcome, a runnable command, and a visible output example
+- `readme-output-evidence`: The README sample output is backed by a reproducible capture script and
+ stored evidence files, not hand-written terminal copy
+
+### Modified Capabilities
+
+- `first-contact-story`: README messaging shifts from platform-internal framing toward developer
+ outcomes while preserving truthful claims and deeper product context below the fold
+- `entrypoint-onboarding`: The README must surface the runnable uvx entry path before architecture,
+ backlog, governance, or module-system sections
+
+## Impact
+
+**Files expected to change**
+
+- `README.md`
+- `docs/index.md` if needed to preserve first-contact parity with the README
+- `tests/unit/docs/test_wow_entrypoint_contract.py`
+- `tests/unit/docs/test_first_contact_story.py`
+- `docs/_support/readme-first-contact/capture-readme-output.sh`
+- `docs/_support/readme-first-contact/sample-output/`
+- `openspec/CHANGE_ORDER.md`
+
+**Behavior / documentation impact**
+
+- The repo's first screen becomes developer-first and proof-first
+- Enterprise and governance content is preserved but moved below the fold
+- The README gains a reproducible evidence path for the sample output block
+- Docs parity is maintained so README and docs landing do not drift on the first-contact story
+
+**Rollback**
+
+- Restore the previous README structure and delete the evidence bundle references if the new layout
+ proves less clear or creates maintenance burden
+
+
+---
+
+## Source Tracking
+
+
+- **GitHub Issue**: #481
+- **Issue URL**:
+- **Last Synced Status**: proposed
+- **Sanitized**: false
\ No newline at end of file
diff --git a/openspec/changes/readme-star-conversion-01/specs/readme-first-contact/spec.md b/openspec/changes/readme-star-conversion-01/specs/readme-first-contact/spec.md
new file mode 100644
index 00000000..48a051fe
--- /dev/null
+++ b/openspec/changes/readme-star-conversion-01/specs/readme-first-contact/spec.md
@@ -0,0 +1,65 @@
+## ADDED Requirements
+
+### Requirement: README hero leads with developer outcome and runnable proof
+
+The repository README SHALL lead with a developer-facing value proposition and a runnable quickstart
+before platform, module-system, or governance explanation. A first-time visitor must be able to
+understand what SpecFact does and how to try it without scrolling through architecture prose.
+
+#### Scenario: Visitor reads only the first screen of the README
+
+- **WHEN** a first-time visitor opens `README.md`
+- **THEN** the title SHALL be followed by a short, concrete value statement for developers
+- **AND** badges SHALL appear directly under the title / tagline block
+- **AND** the README SHALL show a uvx-based quickstart in the first screenful
+- **AND** the README SHALL include a visible CTA inviting the user to star the repo if the output is
+ useful
+
+#### Scenario: README defers enterprise framing
+
+- **WHEN** a user scans the README from the top
+- **THEN** enterprise, governance, module-marketplace, and architecture framing SHALL appear only
+ after the quickstart, sample output, and concrete feature summary
+- **AND** the README SHALL still preserve those deeper sections further down the page
+
+### Requirement: README includes real sample output with reproducible evidence
+
+The README SHALL include a sample output block derived from a real `specfact code review run`
+capture, not hand-written illustrative output. The proof block SHALL help the reader mentally
+simulate running the command.
+
+#### Scenario: Visitor evaluates whether the tool feels real
+
+- **WHEN** a visitor reads the quickstart section
+- **THEN** the README SHALL display a sample output block that includes a verdict, a score or status,
+ file-level findings, and an evidence bundle path
+- **AND** the output SHALL be sourced from a checked-in capture artifact under
+ `docs/_support/readme-first-contact/sample-output/`
+- **AND** the repo SHALL include a capture script that reproduces or refreshes the stored output
+
+### Requirement: README explains value before deep product detail
+
+The README SHALL include an explicit "what it does" summary for developers before it explains
+organizational workflows, module ownership, or documentation topology.
+
+#### Scenario: Visitor wants concrete reasons to care
+
+- **WHEN** the visitor reaches the section immediately after the quickstart and proof block
+- **THEN** the README SHALL summarize the product in concrete outcome bullets
+- **AND** those bullets SHALL focus on developer-visible outcomes such as review, validation, drift
+ detection, offline use, and backlog/code alignment
+- **AND** deeper sections for pipeline story, team use, module system, and documentation topology
+ SHALL appear later on the page
+
+### Requirement: README shows adoption path beyond the hero
+
+The README SHALL provide copy-pasteable adoption examples for local hooks and CI without forcing the
+reader into the full documentation set first.
+
+#### Scenario: Visitor wants to move from trial to workflow
+
+- **WHEN** a developer finds the quickstart useful
+- **THEN** the README SHALL include a short pre-commit or GitHub Actions snippet near the upper half
+ of the document
+- **AND** the README SHALL include a brief "How SpecFact is built" or equivalent trust section that
+ explains the repo's OpenSpec -> TDD -> quality-gate workflow in plain language
diff --git a/openspec/changes/readme-star-conversion-01/tasks.md b/openspec/changes/readme-star-conversion-01/tasks.md
new file mode 100644
index 00000000..535a3936
--- /dev/null
+++ b/openspec/changes/readme-star-conversion-01/tasks.md
@@ -0,0 +1,58 @@
+## 1. Branch and change setup
+
+- [x] 1.1 Work on managed cloud branch `cursor/readme-star-conversion-a583`
+- [ ] 1.2 Create worktree from `origin/dev` for `cursor/readme-star-conversion-a583`
+ - cloud-managed branch is already active in this session, but AGENTS.md requires the worktree step to be tracked explicitly
+- [ ] 1.3 Run `hatch env create` in the active worktree
+- [ ] 1.4 Run pre-flight checks in the active worktree:
+ - `hatch run smart-test-status`
+ - `hatch run contract-test-status`
+- [x] 1.5 Create OpenSpec change folder `openspec/changes/readme-star-conversion-01/`
+- [x] 1.6 Add proposal and README-focused spec delta
+- [x] 1.7 Validate change with OpenSpec CLI
+
+## 2. Tests first (strict TDD order)
+
+- [ ] 2.1 Update README contract tests to encode the new proof-first structure
+- [ ] 2.2 Run targeted docs tests and capture a failing result before editing `README.md`
+- [ ] 2.3 Record the failing command, timestamp, and failure summary in
+ `openspec/changes/readme-star-conversion-01/TDD_EVIDENCE.md`
+
+## 3. Evidence capture
+
+- [ ] 3.1 Add `docs/_support/readme-first-contact/capture-readme-output.sh`
+- [ ] 3.2 Capture raw `specfact code review run` output against `specfact-demo-repo`
+- [ ] 3.3 Store raw output and run metadata under `docs/_support/readme-first-contact/sample-output/`
+- [ ] 3.4 Pin the CLI version used by the capture and document rerun steps in the evidence folder
+
+## 4. README restructure
+
+- [ ] 4.1 Rewrite the README top screen around a concrete hook, badges, quickstart, sample output,
+ and CTA
+- [ ] 4.2 Add a concrete "What SpecFact does" summary near the top
+- [ ] 4.3 Add pre-commit and GitHub Actions snippets in the upper half of the README
+- [ ] 4.4 Add a short "How SpecFact is built" trust section
+- [ ] 4.5 Move team / enterprise, module-system, and documentation-topology sections below the fold
+- [ ] 4.6 Update `docs/index.md` only if needed to preserve first-contact parity with the README
+
+## 5. Verify and document
+
+- [ ] 5.1 Re-run the targeted docs tests and record passing evidence in
+ `openspec/changes/readme-star-conversion-01/TDD_EVIDENCE.md`
+- [ ] 5.2 Run required quality gates for the changed files:
+ - `hatch run format`
+ - `hatch run type-check`
+ - `hatch run lint`
+ - `hatch run yaml-lint`
+ - `hatch run contract-test`
+ - `hatch run smart-test`
+- [ ] 5.3 Refresh `.specfact/code-review.json` and resolve any findings
+- [ ] 5.4 Run `hatch run ./scripts/verify-modules-signature.py --require-signature`
+- [ ] 5.5 Update `openspec/CHANGE_ORDER.md` with this change entry
+
+## 6. Git and PR
+
+- [ ] 6.1 Commit the OpenSpec + README conversion change with a conventional commit message
+- [ ] 6.2 Push branch `cursor/readme-star-conversion-a583`
+- [ ] 6.3 Create or update the PR against `dev`
+- [ ] 6.4 After merge, remove/prune the worktree per AGENTS.md cleanup policy
diff --git a/tests/unit/docs/docs_test_constants.py b/tests/unit/docs/docs_test_constants.py
new file mode 100644
index 00000000..2fb7859f
--- /dev/null
+++ b/tests/unit/docs/docs_test_constants.py
@@ -0,0 +1,4 @@
+"""Shared constants for README/docs first-contact contract tests."""
+
+HOOK = "Review AI-assisted code against your own contracts."
+SUBHOOK = "Catch drift before it reaches PR or main."
diff --git a/tests/unit/docs/test_first_contact_story.py b/tests/unit/docs/test_first_contact_story.py
index 2dab1870..159a2d32 100644
--- a/tests/unit/docs/test_first_contact_story.py
+++ b/tests/unit/docs/test_first_contact_story.py
@@ -1,8 +1,6 @@
-"""Validate first-contact messaging across the core repo entry points.
+"""Validate the README-first first-contact story across entrypoint surfaces."""
-These tests ensure the README, docs landing page, and contributor guidance all
-present the same canonical product story and onboarding order.
-"""
+from __future__ import annotations
import re
from pathlib import Path
@@ -10,6 +8,8 @@
import pytest
+from tests.unit.docs.docs_test_constants import HOOK, SUBHOOK
+
REPO_ROOT = Path(__file__).resolve().parents[3]
README = REPO_ROOT / "README.md"
@@ -21,8 +21,6 @@
@pytest.fixture(scope="module", autouse=True)
def _require_entrypoint_files() -> None:
- """Skip the module if the docs entrypoint files are not present."""
-
if not REPO_ROOT.exists():
pytest.skip(f"Repository root missing: expected at {REPO_ROOT}", allow_module_level=True)
if not README.is_file():
@@ -34,173 +32,56 @@ def _require_entrypoint_files() -> None:
def _read(path: Path) -> str:
- """Return the UTF-8 text contents of a repository file.
-
- Args:
- path: Path to the file to read.
-
- Returns:
- File contents as a string.
- """
-
return path.read_text(encoding="utf-8")
-def _assert_question_order(content: str, questions: list[str], surface: str) -> None:
- """Assert that the first-contact questions appear in increasing order.
-
- Args:
- content: Text content to inspect.
- questions: Ordered question strings that must appear in sequence.
- surface: Human-readable name of the file or surface being inspected.
- """
-
- indices: list[int] = []
- for question in questions:
- index = content.find(question)
- assert index != -1, f"Missing question {question!r} in {surface}"
- indices.append(index)
-
- assert indices == sorted(indices), f"Questions are out of order in {surface}: {questions}"
-
-
def _assert_contains_url_host(content: str, host: str, surface: str) -> None:
- """Assert that a surface contains at least one absolute URL for the expected host.
-
- Args:
- content: Text content to inspect.
- host: Expected URL host name.
- surface: Human-readable name of the file or surface being inspected.
- """
-
found_hosts = {urlparse(match.group(0).rstrip(".,;:")).netloc for match in ABSOLUTE_URL_RE.finditer(content)}
assert host in found_hosts, f"Missing URL host {host!r} in {surface}; found hosts: {sorted(found_hosts)}"
-def _assert_contains_any_phrase(content: str, phrases: tuple[str, ...], message: str) -> None:
- """Assert that at least one of the candidate phrases appears in the content."""
+def test_readme_hero_uses_proof_first_story() -> None:
+ readme = _read(README)
+ try_it_idx = readme.lower().find("## try it in 60 seconds")
+ assert try_it_idx != -1
+ first_screen = readme[:try_it_idx].lower()
- lowered = content.lower()
- assert any(phrase in lowered for phrase in phrases), message
+ assert HOOK in readme
+ assert SUBHOOK in readme
+ assert "## Try it in 60 seconds" in readme
+ assert "validation and alignment layer" not in first_screen
+ assert "swiss knife" not in first_screen
-def test_readme_leads_with_validation_and_alignment_story() -> None:
+def test_readme_prioritizes_quickstart_before_deep_sections() -> None:
readme = _read(README)
- readme_lower = readme.lower()
- questions = [
- "What is SpecFact?",
- "Why does it exist?",
- "Why should I use it?",
- "What do I get?",
- "How do I get started?",
- ]
-
- assert "validation and alignment layer" in readme
- _assert_question_order(readme, questions, "README.md")
- _assert_contains_any_phrase(
- readme_lower,
- ("ai-assisted", "vibe-coded", "ai-generated"),
- "README.md must explain AI-assisted validation pressure in the why-story.",
- )
- _assert_contains_any_phrase(
- readme_lower,
- ("brownfield", "reverse-engineer", "reverse-engineered"),
- "README.md must explain the brownfield reverse-engineering pressure.",
- )
- _assert_contains_any_phrase(
- readme_lower,
- ("i wanted x but got y", "backlog/spec/code drift", "drift between backlog"),
- "README.md must explain backlog/spec/code drift as a reason SpecFact exists.",
- )
- _assert_contains_any_phrase(
- readme_lower,
- ("policy enforcement", "enterprise policy", "ci/cd", "shared rules"),
- "README.md must explain team and enterprise policy consistency pressure.",
- )
-
-
-def test_readme_prioritizes_fast_start_over_docs_topology() -> None:
- readme = _read(README)
-
- start_match = re.search(r"^#+\s*Start Here", readme, re.MULTILINE)
- topology_match = re.search(r"^#+\s*Documentation Topology", readme, re.MULTILINE)
- assert start_match is not None, "Missing Start Here heading in README.md"
- assert topology_match is not None, "Missing Documentation Topology heading in README.md"
- start_idx = start_match.start()
- topology_idx = topology_match.start()
- assert start_idx < topology_idx
+ try_it = readme.find("## Try it in 60 seconds")
+ teams = readme.find("## For teams and organizations")
+ modules = readme.find("## Module system")
+ topology = readme.find("## Documentation topology")
+ assert min(try_it, teams, modules, topology) != -1
+ assert try_it < teams < modules < topology
-def test_readme_routes_users_by_outcome() -> None:
+def test_readme_includes_trust_and_pipeline_sections() -> None:
readme = _read(README)
- readme_lower = readme.lower()
+ assert "## How SpecFact is built" in readme
+ assert "OpenSpec" in readme
+ assert "TDD" in readme
+ assert "quality gates" in readme.lower()
- assert "## Choose Your Path" in readme
- assert "Greenfield and AI-assisted delivery" in readme
- assert "Brownfield and reverse engineering" in readme
- assert "Backlog to code alignment" in readme
- _assert_contains_any_phrase(
- readme_lower,
- ("govern", "policy enforcement", "team and policy enforcement"),
- "README.md must route users toward team and enterprise policy enforcement outcomes.",
- )
-
-def test_docs_index_matches_first_contact_story() -> None:
+def test_docs_index_matches_proof_first_story() -> None:
docs_index = _read(DOCS_INDEX)
- docs_index_lower = docs_index.lower()
- questions = [
- "What is SpecFact?",
- "Why does it exist?",
- "Why should I use it?",
- "What do I get?",
- "How to get started",
- ]
-
- assert "validation and alignment layer" in docs_index
- _assert_question_order(docs_index, questions, "docs/index.md")
+ assert HOOK in docs_index
+ assert SUBHOOK in docs_index
+ assert "## What is SpecFact?" in docs_index
_assert_contains_url_host(docs_index, "modules.specfact.io", "docs/index.md")
- _assert_contains_any_phrase(
- docs_index_lower,
- ("brownfield", "legacy code", "existing systems"),
- "docs/index.md must describe the brownfield path.",
- )
- _assert_contains_any_phrase(
- docs_index_lower,
- ("spec-first", "openspec", "spec-kit"),
- "docs/index.md must explain the spec-first handoff for brownfield workflows.",
- )
- _assert_contains_any_phrase(
- docs_index_lower,
- ("default starting point", "start here before jumping", "start here before"),
- "docs/index.md must orient users that core docs are the default starting point.",
- )
- _assert_contains_any_phrase(
- docs_index_lower,
- ("ai-assisted", "vibe-coded", "validation layer"),
- "docs/index.md must explain AI-assisted validation pressure in the why-story.",
- )
- _assert_contains_any_phrase(
- docs_index_lower,
- ("i wanted x but got y", "backlog language", "backlog/spec/code"),
- "docs/index.md must explain backlog/spec/code drift as a reason SpecFact exists.",
- )
- _assert_contains_any_phrase(
- docs_index_lower,
- ("policy enforcement", "organizations need a path", "developers, ai ides, and ci/cd"),
- "docs/index.md must explain team and enterprise policy consistency pressure.",
- )
+ assert "default starting point" in docs_index.lower()
def test_contributing_guidance_mentions_entrypoint_story_hierarchy() -> None:
contributing = _read(CONTRIBUTING)
- questions = [
- "What is SpecFact?",
- "Why does it exist?",
- "Why should I use it?",
- "What do I get?",
- "How do I get started?",
- ]
-
assert "first-contact" in contributing
- _assert_question_order(contributing, questions, "CONTRIBUTING.md")
+ assert "What is SpecFact?" in contributing
+ assert "How do I get started?" in contributing
diff --git a/tests/unit/docs/test_release_docs_parity.py b/tests/unit/docs/test_release_docs_parity.py
index 698979dd..febbf7dc 100644
--- a/tests/unit/docs/test_release_docs_parity.py
+++ b/tests/unit/docs/test_release_docs_parity.py
@@ -348,10 +348,11 @@ def test_module_contracts_reference_external_bundle_boundary() -> None:
def test_readme_and_docs_index_define_core_and_modules_split() -> None:
readme = _repo_file("README.md").read_text(encoding="utf-8")
docs_index = _repo_file("docs/index.md").read_text(encoding="utf-8")
- assert "validation and alignment layer for software delivery" in readme
- assert "docs.specfact.io` is the canonical starting point for SpecFact" in readme
+ assert "Review AI-assisted code against your own contracts." in readme
+ assert "## Documentation topology" in readme
assert "Module-specific deep docs are canonically owned by `specfact-cli-modules`" in readme
_assert_mentions_modules_docs_site(readme)
+ assert "Review AI-assisted code against your own contracts." in docs_index
assert "canonical starting point for the core CLI story" in docs_index
assert "docs.specfact.io` is the default starting point" in docs_index
_assert_mentions_modules_docs_site(docs_index)
diff --git a/tests/unit/docs/test_wow_entrypoint_contract.py b/tests/unit/docs/test_wow_entrypoint_contract.py
index d4a9f026..307ccdc8 100644
--- a/tests/unit/docs/test_wow_entrypoint_contract.py
+++ b/tests/unit/docs/test_wow_entrypoint_contract.py
@@ -1,7 +1,4 @@
-"""Contract tests: README and docs landing must match the canonical uvx \"wow\" entry path.
-
-The wow path is the primary onboarding surface (init + code review with --scope full).
-"""
+"""Contract tests for the README proof-first onboarding surface."""
from __future__ import annotations
@@ -9,12 +6,15 @@
import pytest
+from tests.unit.docs.docs_test_constants import HOOK
+
REPO_ROOT = Path(__file__).resolve().parents[3]
README = REPO_ROOT / "README.md"
DOCS_INDEX = REPO_ROOT / "docs" / "index.md"
-
-# Canonical strings — keep in sync with docs/index.md hero and README "Start Here".
+CAPTURE_SCRIPT = REPO_ROOT / "docs" / "_support" / "readme-first-contact" / "capture-readme-output.sh"
+EVIDENCE_DIR = REPO_ROOT / "docs" / "_support" / "readme-first-contact" / "sample-output"
+CTA = "Star this repo if the output is useful."
UVX_INIT = "uvx specfact-cli init --profile solo-developer"
UVX_REVIEW = "uvx specfact-cli code review run --path . --scope full"
INSTALLED_INIT = "specfact init --profile solo-developer"
@@ -29,12 +29,12 @@ def _require_files() -> None:
pytest.skip(f"docs/index.md missing at {DOCS_INDEX}", allow_module_level=True)
-def _read(p: Path) -> str:
- return p.read_text(encoding="utf-8")
+def _read(path: Path) -> str:
+ return path.read_text(encoding="utf-8")
def test_readme_and_docs_index_include_identical_uvx_wow_commands() -> None:
- """Hero commands in README and docs/index.md must not drift."""
+ """README and docs/index must share the canonical uvx wow commands."""
readme = _read(README)
docs = _read(DOCS_INDEX)
for needle in (UVX_INIT, UVX_REVIEW):
@@ -42,36 +42,40 @@ def test_readme_and_docs_index_include_identical_uvx_wow_commands() -> None:
assert needle in docs, f"docs/index.md must contain {needle!r}"
-def test_readme_documents_pip_free_alternate_and_scope_full_rationale() -> None:
- """README explains --scope full and the installed-CLI equivalent."""
+def test_readme_documents_proof_block_cta_and_installed_equivalent() -> None:
+ """README keeps the hook, proof block, CTA, and installed CLI path together."""
readme = _read(README)
- assert "--scope full" in readme
+ assert HOOK in readme
+ assert "Sample output:" in readme
+ assert "Evidence bundle:" in readme
+ assert CTA in readme
assert INSTALLED_INIT in readme and INSTALLED_REVIEW in readme
- assert "Verdict" in readme and "Score" in readme and "findings" in readme.lower()
+ assert "Verdict:" in readme and "Findings:" in readme and "works offline" in readme.lower()
-def test_readme_wow_section_appears_before_choose_your_path() -> None:
- """Primary entry content must appear before outcome routing."""
+def test_readme_proof_first_sections_precede_deeper_sections() -> None:
+ """Proof and workflow sections must appear before org and module detail."""
readme = _read(README)
- wow = readme.find("uvx specfact-cli init --profile solo-developer")
- choose = readme.find("## Choose Your Path")
- assert wow != -1 and choose != -1
- assert wow < choose
+ try_it = readme.find("## Try it in 60 seconds")
+ what_it_does = readme.find("## What SpecFact does")
+ workflow = readme.find("## Add SpecFact to your workflow")
+ teams = readme.find("## For teams and organizations")
+ assert min(try_it, what_it_does, workflow, teams) != -1
+ assert try_it < what_it_does < workflow < teams
-def test_docs_index_wow_block_precedes_what_is_specfact() -> None:
- """Landing page leads with the runnable block before deep product copy."""
+def test_docs_index_shares_readme_hook_and_wow_block() -> None:
+ """Docs landing keeps the same first-contact identity and ordering."""
docs = _read(DOCS_INDEX)
- block = docs.find(UVX_INIT)
- heading = docs.find("## What is SpecFact?")
- assert block != -1 and heading != -1
- assert block < heading
-
-
-def test_readme_start_here_precedes_documentation_topology() -> None:
- """Fast-start remains above internal docs topology (existing contract)."""
- readme = _read(README)
- start = readme.find("### Start Here")
- topo = readme.find("## Documentation Topology")
- assert start != -1 and topo != -1
- assert start < topo
+ assert HOOK in docs
+ assert docs.find(UVX_INIT) != -1
+ assert docs.find(UVX_INIT) < docs.find("## What is SpecFact?")
+
+
+def test_readme_capture_script_and_evidence_folder_exist() -> None:
+ """README sample output must be backed by reproducible evidence artifacts."""
+ assert CAPTURE_SCRIPT.is_file(), "Missing docs/_support/readme-first-contact/capture-readme-output.sh"
+ assert EVIDENCE_DIR.is_dir(), "Missing docs/_support/readme-first-contact/sample-output/"
+ evidence_files = [path for path in EVIDENCE_DIR.iterdir() if path.is_file()]
+ assert evidence_files, "Expected at least one sample-output artifact file"
+ assert any(path.stat().st_size > 0 for path in evidence_files), "Expected a non-empty sample-output artifact"