---
phase: 01-scaffold
plan: "01"
type: execute
wave: 1
depends_on: []
files_modified:
  - Cargo.toml
  - Cargo.lock
  - src/lib.rs
  - rustfmt.toml
  - deny.toml
  - Justfile
  - CHANGELOG.md
  - .cargo-husky/hooks/pre-commit
autonomous: true
requirements:
  - INFRA-01
  - STYLE-01
  - STYLE-02

must_haves:
  truths:
    - "cargo build produces a cdylib artifact (libsemantic_views.so / semantic_views.dll / libsemantic_views.dylib)"
    - "cargo fmt --check passes with zero violations"
    - "cargo clippy -- -D warnings passes with zero violations"
    - "just setup completes without error on a clean checkout"
    - "just build, just test, just lint all succeed"
    - "CHANGELOG.md exists with an [Unreleased] section in Keep a Changelog format"
  artifacts:
    - path: "src/lib.rs"
      provides: "Extension entry point using duckdb_entrypoint_c_api! macro"
      contains: "duckdb_entrypoint_c_api"
    - path: "Cargo.toml"
      provides: "crate-type cdylib, workspace lints (pedantic deny), cargo-husky dev dep"
      contains: "cdylib"
    - path: "rustfmt.toml"
      provides: "rustfmt configuration with edition = 2021"
      contains: "edition"
    - path: "deny.toml"
      provides: "cargo-deny license and advisory config"
      contains: "[licenses]"
    - path: "Justfile"
      provides: "Developer commands: setup, build, build-release, test, lint, fmt, coverage, clean"
      contains: "setup"
    - path: ".cargo-husky/hooks/pre-commit"
      provides: "Pre-commit hook running cargo fmt --check and cargo clippy -- -D warnings"
      contains: "cargo fmt"
  key_links:
    - from: "src/lib.rs"
      to: "duckdb crate"
      via: "duckdb_entrypoint_c_api! macro"
      pattern: "duckdb_entrypoint_c_api"
    - from: "Cargo.toml"
      to: "workspace lints"
      via: "[workspace.lints.clippy] pedantic = deny"
      pattern: "pedantic"
    - from: "Justfile"
      to: "Makefile"
      via: "just setup calls make configure"
      pattern: "make configure"
---

<objective>
Create the Rust DuckDB extension scaffold — the Cargo project, entry point, code quality configuration, developer task runner, and pre-commit hooks. This plan produces the Rust foundation that the CI workflows in plan 02 will build and test.

Purpose: Without a correctly configured Rust project (cdylib crate type, workspace lints, proper entry point macro), the extension cannot be loaded by DuckDB and no subsequent plans can proceed.
Output: A buildable Rust extension scaffold with cargo-husky hooks, rustfmt/clippy enforced locally, just task runner, cargo-deny license config, and CHANGELOG.md.
</objective>

<execution_context>
@/Users/paul/.claude/get-shit-done/workflows/execute-plan.md
@/Users/paul/.claude/get-shit-done/templates/summary.md
</execution_context>

<context>
@.planning/PROJECT.md
@.planning/ROADMAP.md
@.planning/STATE.md
@.planning/phases/01-scaffold/01-RESEARCH.md
</context>

<tasks>

<task type="auto">
  <name>Task 1: Initialize Rust extension scaffold from extension-template-rs</name>
  <files>
    Cargo.toml
    src/lib.rs
    .cargo-husky/hooks/pre-commit
  </files>
  <action>
The project root already has LICENSE and README.md. Do NOT reinitialize git. Initialize the Rust extension scaffold by creating the required files directly (not by cloning — the project already exists as a git repo).

**Step 1: Verify the extension-template-rs template**

Fetch the template's Cargo.toml and src/lib.rs from https://github.com/duckdb/extension-template-rs to confirm exact crate names and versions in Cargo.lock before writing our Cargo.toml. Specifically verify:
- Whether `duckdb_loadable_macros` is a separate crate or re-exported from the `duckdb` crate
- The exact dependency list and versions

**Step 2: Create Cargo.toml**

Write `/Users/paul/Documents/Dev/Personal/duckdb-semantic-views/Cargo.toml` with:

```toml
[package]
name = "semantic_views"
version = "0.1.0"
edition = "2021"
description = "DuckDB extension providing semantic views — a declarative layer for dimensions, measures, and relationships"
license = "MIT"

[lib]
crate-type = ["cdylib"]

[dependencies]
duckdb = { version = "1.4.4", features = ["loadable-extension"] }
libduckdb-sys = "1.4.4"
# duckdb_loadable_macros: verify from template Cargo.lock whether this is a separate crate
# or re-exported from the duckdb crate. If separate, add it here.

[dev-dependencies.cargo-husky]
version = "1"
default-features = false
features = ["user-hooks"]

[profile.release]
lto = true
strip = true

[workspace.lints.clippy]
pedantic = "deny"
# Suppress known-noisy pedantic rules per RESEARCH.md
module_name_repetitions = "allow"
missing_errors_doc = "allow"
missing_panics_doc = "allow"

[lints]
workspace = true
```

NOTE: Do NOT add `#![deny(warnings)]` to lib.rs — per RESEARCH.md pitfall 4, this breaks on toolchain updates. Use `-D warnings` only in CI commands and clippy invocations.

**Step 3: Create src/lib.rs**

Write `/Users/paul/Documents/Dev/Personal/duckdb-semantic-views/src/lib.rs` as the minimal working entry point. Per RESEARCH.md Pattern 1, this is the only supported entry point pattern for Rust extensions using the C API:

```rust
use duckdb::{Connection, Result};
use duckdb_loadable_macros::duckdb_entrypoint_c_api;
use std::error::Error;

/// Extension entry point — called by DuckDB when the extension is loaded.
///
/// Phase 1: loads cleanly with no functions registered.
/// Phase 2+ will register scalar/table functions here.
#[duckdb_entrypoint_c_api()]
pub unsafe fn extension_entrypoint(con: Connection) -> Result<(), Box<dyn Error>> {
    let _ = con;
    Ok(())
}
```

If `duckdb_entrypoint_c_api` is re-exported from the `duckdb` crate (not a separate `duckdb_loadable_macros` crate), adjust the import accordingly after verifying from the template.

**Step 4: Create .cargo-husky/hooks/pre-commit**

Create directory `/Users/paul/Documents/Dev/Personal/duckdb-semantic-views/.cargo-husky/hooks/` and write the pre-commit hook:

```sh
#!/bin/sh
set -e
cargo fmt --check
cargo clippy -- -D warnings
```

Make it executable: `chmod +x /Users/paul/Documents/Dev/Personal/duckdb-semantic-views/.cargo-husky/hooks/pre-commit`

Per RESEARCH.md Pattern 7: cargo-husky installs hooks automatically on `cargo test`. The `user-hooks` feature reads from `.cargo-husky/hooks/`. Hook file must be executable or it will silently not run.

**Step 5: Run cargo to verify compilation**

```bash
cd /Users/paul/Documents/Dev/Personal/duckdb-semantic-views && cargo build 2>&1
```

If `duckdb_loadable_macros` import fails, check the template's exact structure and adjust imports. The `duckdb` crate v1.4.4 with `loadable-extension` feature may re-export the macro.
  </action>
  <verify>
    <automated>cd /Users/paul/Documents/Dev/Personal/duckdb-semantic-views && cargo build 2>&1 | tail -5</automated>
    <manual>Check that target/debug/ contains libsemantic_views.dylib (macOS) or libsemantic_views.so (Linux) — the cdylib artifact.</manual>
  </verify>
  <done>cargo build completes without errors. A cdylib artifact exists in target/debug/. src/lib.rs uses duckdb_entrypoint_c_api macro. Cargo.toml has crate-type = ["cdylib"] and workspace.lints.clippy pedantic = "deny".</done>
</task>

<task type="auto">
  <name>Task 2: Configure code quality tools (rustfmt, deny, Justfile, CHANGELOG)</name>
  <files>
    rustfmt.toml
    deny.toml
    Justfile
    CHANGELOG.md
  </files>
  <action>
**Step 1: Create rustfmt.toml**

Write `/Users/paul/Documents/Dev/Personal/duckdb-semantic-views/rustfmt.toml`:

```toml
edition = "2021"
max_width = 100
```

Per RESEARCH.md Pitfall 5: explicitly setting `edition = "2021"` prevents differences between `cargo fmt` (which infers edition) and standalone `rustfmt` (which defaults to 2015). Do NOT omit this field.

**Step 2: Create deny.toml**

Write `/Users/paul/Documents/Dev/Personal/duckdb-semantic-views/deny.toml`:

```toml
[licenses]
allow = [
    "MIT",
    "Apache-2.0",
    "Apache-2.0 WITH LLVM-exception",
    "BSD-2-Clause",
    "BSD-3-Clause",
    "ISC",
    "Unicode-DFS-2016",
    "OpenSSL",
    "Unicode-3.0",
    "Zlib",
]
confidence-threshold = 0.8

[advisories]
db-path = "~/.cargo/advisory-db"
db-urls = ["https://github.com/rustsec/advisory-db"]
version = 2
ignore = []

[bans]
multiple-versions = "warn"
wildcards = "allow"

[sources]
unknown-registry = "warn"
unknown-git = "warn"
```

Per RESEARCH.md Pitfall 6: the allowlist must be broad enough to cover DuckDB's transitive dependency tree. Run `cargo deny check licenses` after creation and add any missing licenses found. Common additions beyond the base list: Unicode-3.0, Zlib.

**Step 3: Create Justfile**

Write `/Users/paul/Documents/Dev/Personal/duckdb-semantic-views/Justfile`:

```makefile
# DuckDB Semantic Views — developer task runner
# Run `just` to see available commands

# Show available commands (default)
default:
    @just --list

# Set up complete local dev environment (one-time, new contributors)
# Downloads pinned DuckDB binary, installs dev tools, wires cargo-husky hooks
setup:
    @echo "Installing dev tools..."
    cargo install cargo-nextest --locked
    cargo install cargo-deny --locked
    cargo install cargo-llvm-cov --locked
    git submodule update --init --recursive
    make configure
    @echo "Running cargo test to install cargo-husky hooks..."
    cargo test
    @echo "Setup complete. Run 'just build' to build the extension."

# Build debug extension
build:
    make debug

# Build release extension
build-release:
    make release

# Run tests via SQLLogicTest (exercises actual LOAD mechanism)
test:
    make test_debug

# Run Rust unit tests only (does NOT test LOAD — use 'just test' for that)
test-rust:
    cargo nextest run

# Run all lints
lint:
    cargo fmt --check
    cargo clippy -- -D warnings
    cargo deny check

# Format code in place
fmt:
    cargo fmt

# Check code coverage (requires cargo-llvm-cov)
coverage:
    cargo llvm-cov nextest --fail-under-lines 80

# Clean build artifacts
clean:
    make clean
    cargo clean
```

Key notes on Justfile:
- `just setup` runs `cargo test` explicitly to trigger cargo-husky hook installation (per RESEARCH.md Pitfall 7 — hooks only install on `cargo test`, not `cargo build`)
- `just test` runs `make test_debug` (SQLLogicTest via DuckDB runner), NOT `cargo test` — this is the LOAD smoke test
- `just test-rust` is the separate `cargo nextest` target for pure Rust unit tests
- `git submodule update --init --recursive` ensures extension-ci-tools submodule is present (per RESEARCH.md Pitfall 1)

**Step 4: Create CHANGELOG.md**

Write `/Users/paul/Documents/Dev/Personal/duckdb-semantic-views/CHANGELOG.md` in Keep a Changelog format:

```markdown
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [Unreleased]

### Added
- Initial extension scaffold using `duckdb/extension-template-rs`
- Multi-platform CI build matrix (Linux x86_64/arm64, macOS x86_64/arm64, Windows x86_64)
- Scheduled DuckDB version monitor with automated PR creation
- Code quality gates: `rustfmt`, `clippy` (pedantic), `cargo-deny`, 80% coverage
- Developer task runner (`just`) with `just setup` one-command dev environment
- Pre-commit hooks via `cargo-husky` (rustfmt + clippy)

[Unreleased]: https://github.com/paul-rl/duckdb-semantic-views/compare/HEAD...HEAD
```

**Step 5: Verify code quality tools**

Run `cargo fmt --check` — should pass (no Rust files have been formatted yet since lib.rs was just created). If it fails, run `cargo fmt` first then verify again.

Run `cargo clippy -- -D warnings` — should pass with the minimal lib.rs. If there are warnings, fix them (e.g., unused import warnings on the `Connection` import in the `let _ = con;` pattern).
  </action>
  <verify>
    <automated>cd /Users/paul/Documents/Dev/Personal/duckdb-semantic-views && cargo fmt --check 2>&1 &amp;&amp; cargo clippy -- -D warnings 2>&1</automated>
    <manual>Check that CHANGELOG.md has an [Unreleased] section. Check deny.toml has [licenses] section. Check Justfile has a setup recipe.</manual>
  </verify>
  <done>cargo fmt --check exits 0. cargo clippy -- -D warnings exits 0. rustfmt.toml, deny.toml, Justfile, and CHANGELOG.md all exist at project root with correct content.</done>
</task>

</tasks>

<verification>
After both tasks complete, run the following to confirm the scaffold is correct:

1. `cd /Users/paul/Documents/Dev/Personal/duckdb-semantic-views && cargo build 2>&1` — exits 0, produces cdylib artifact
2. `cargo fmt --check 2>&1` — exits 0
3. `cargo clippy -- -D warnings 2>&1` — exits 0
4. Verify `Cargo.toml` contains `crate-type = ["cdylib"]`
5. Verify `Cargo.toml` contains `[workspace.lints.clippy]` with `pedantic = "deny"`
6. Verify `src/lib.rs` contains `duckdb_entrypoint_c_api`
7. Verify `.cargo-husky/hooks/pre-commit` is executable (`ls -la .cargo-husky/hooks/pre-commit`)
8. Verify `CHANGELOG.md` contains `[Unreleased]`
</verification>

<success_criteria>
- Extension builds as a cdylib (`cargo build` exits 0)
- `cargo fmt --check` exits 0 (code is correctly formatted)
- `cargo clippy -- -D warnings` exits 0 (zero lint violations)
- All 8 listed files exist with correct content
- `.cargo-husky/hooks/pre-commit` is executable
- `just` command is available and `just --list` shows all commands
</success_criteria>

<output>
After completion, create `.planning/phases/01-scaffold/01-01-SUMMARY.md` following the template at @/Users/paul/.claude/get-shit-done/templates/summary.md
</output>
