Skip to content
17 changes: 17 additions & 0 deletions .specify/integrations/speckit.manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"integration": "speckit",
"version": "0.12.18",
"installed_at": "2026-07-17T15:07:12.597929+00:00",
"files": {
".specify/scripts/bash/common.sh": "6ff86bf39f6b4684b0f80927dc7a1dadec26b4671988a3fe4d6c2523cbd3aa22",
".specify/scripts/bash/setup-plan.sh": "4469b22960f43c07c33dca00de6dedb252145e9a9ce8fbb0e63be82e02b082ab",
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated
".specify/scripts/bash/setup-tasks.sh": "1d4bcebe93f3e4e778964978cfe9bfb67ee94e7dc688f2ff3be224f647f61f1f",
".specify/scripts/bash/check-prerequisites.sh": "ac3e96258a05d029d048076393a03aadff5c7c3a55a26d0a9f5c17886a1c659d",
".specify/scripts/bash/create-new-feature.sh": "dd531f9ba47c9ce9975b597947377be9542b7236681d6dc033513c4e3cfc50f2",
".specify/templates/constitution-template.md": "ce7549540fa45543cca797a150201d868e64495fdff39dc38246fb17bd4024b3",
".specify/templates/checklist-template.md": "0ad704b60af2df817aee1c0a2ecc0e0304b271d2de34047df1c891735967033e",
".specify/templates/tasks-template.md": "c731575d8099b3f871861186fbd1a592b51b2ba57fb99e1a0dab439ff6d5608f",
".specify/templates/spec-template.md": "3945437fc35cd30a5b2bf7beea680337c3516826d3efa5a6b92c4a7eca1ba28e",
".specify/templates/plan-template.md": "5ef0e4c97b36e9f91372dc6eb8e5a7e515af8958cf1a9286e43a1ebd9bd48540"
Comment on lines +5 to +15

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in c3803cd — refreshed the four changed SHA-256 entries in speckit.manifest.json; final template edits have hashes refreshed too (4a69844).

}
}
108 changes: 108 additions & 0 deletions .specify/memory/constitution.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# guiltty Constitution

These are the principles that govern every spec, plan, and task in this
repo. They supersede convenience. Amendments require an ADR under
`specs/decisions/` (added alongside this file by issue #39's task
breakdown -- not yet a live link here since task merge order isn't
guaranteed).

## Core Principles

### I. Backend-Agnostic Core

`guiltty-core` stays scoped to the absolute-coordinate drawing surface
(`Canvas`, `Shape`, text, the `Backend` trait) and never contains
backend-specific code; backend concerns live only in backend crates
(`guiltty-kitty` today). Crates built on `guiltty-core`'s public API
(`guiltty-sprite`, and `guiltty-turtle` once it exists) follow the same
rule one level up: they never reach into another crate's private state,
only its public API. Adding a new backend crate, or changing the workspace
crate boundaries this describes, is an **ask-first** change.

### II. Recoverable Errors Never Panic

Public API returns `Result<T, guiltty_core::Error>` for recoverable
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
Outdated
conditions (a missing/malformed image file, a failed terminal write, a
stale sprite footprint) rather than panicking. Panics are reserved for
programmer-error invariants only (e.g. an out-of-bounds internal index) --
never a condition a caller could legitimately hit and need to recover
from.

### III. Ask First On New Dependencies

Adding any new external dependency (especially anything requiring C/FFI)
is an **ask-first** change, same as a new backend crate or workspace
boundary change (Principle I). Removing one usually isn't.

### IV. Test-First For Behavioral Changes

Every behavioral change lands with tests that actually assert the new
behavior -- not coverage-padding. `guiltty-core`/`guiltty-sprite` unit
tests assert pixel-buffer/state correctness with no terminal required;
`guiltty-kitty` protocol tests assert byte-level escape-sequence encoding;
actual rendered output stays a manual/visual check for now (see
`docs/spec-kitty-e2e.md` for the planned automated tier). CI enforces a
90% line-coverage floor (`docs/spec-ci.md`) -- a PR that drops below it
fails, but clearing the floor is a side effect of real tests, never the
goal itself.

### V. Pre-1.0 Breaking Changes Are Cheap, Not Silent

Every crate in this workspace is at `0.0.0`. Breaking a public API
pre-1.0 is acceptable and sometimes the right call (see the
`guiltty-sprite` extraction's precedent) -- but it must be called out
explicitly in the PR description as a breaking change, never shipped as
if it were routine.

### VI. Docs That Contradict Code Are Bugs

A stale "not yet implemented" note, a broken doc link, a crate list
missing a crate that now exists -- these are bugs, not polish, and they
only get more misleading the longer they're left. Fix doc staleness
encountered while touching the affected area in the same PR, not a
follow-up (`docs/spec.md`'s crate list still omits `guiltty-sprite` and
describes sprites as living in `guiltty-core` -- a known staleness this
bootstrap does not fix, since issue #39 is pure scaffolding and moves no
existing `docs/*.md` content).

## Development Constraints

- **Rust**, latest stable toolchain, 2021 edition, no nightly-only
features. Toolchain pinned via [`mise.toml`](../../mise.toml).
- **Structure:** a Cargo workspace, not a single crate -- see Principle I.
- **Color/coordinates:** RGBA8 throughout; pixel-addressable, origin
top-left.
- Full tech-stack rationale (why `kittage` over hand-rolled encoding, why
`notcurses`/GPU acceleration are out of scope for now, etc.) lives in
[`docs/spec.md`](../../docs/spec.md), not restated here.

## Workflow

Unlike iklo's spec-kit-driven `/speckit.*` gates, this repo's day-to-day
shipping process predates `.specify/` and stays as-is: the
`pull-request-process`/`map-issue-to-tasks`/`fix-mapped-issue` skills --
worktrees (never the shared checkout), a dedicated git/PR identity,
issue → `tasks/issue-N-*.md` task breakdown → one task per PR → bots/the
maintainer review and merge, never self-merged. `.specify/`'s templates
and scripts are bootstrapped (issue #39) for future spec-kit-format work
under `specs/NNN-slug/`, alongside this process, not replacing it.

**Bugs and feature work** are GitHub Issues on
[rsenna/guiltty](https://github.com/rsenna/guiltty/issues). Promote one
into a `specs/NNN-slug/spec.md` when it grows into real design work worth
the spec-kit gates; smaller decisions can stay as a `docs/design/*.md`
write-up (this repo's existing convention, e.g.
[`docs/design/viewport-regions-zoom-scroll.md`](../../docs/design/viewport-regions-zoom-scroll.md))
or an ADR under `specs/decisions/`.

## Governance

- These principles supersede all other practices in the repo.
- Amendments require an ADR (context, alternatives rejected,
consequences) under `specs/decisions/`.
- Every PR/review verifies compliance.
- Day-to-day agent operating guidance lives in `AGENTS.md` (added
alongside this file by issue #39's task breakdown -- not yet a live
link here since task merge order isn't guaranteed).

**Version**: 1.0.0 | **Ratified**: 2026-08-02 | **Last Amended**: 2026-08-02
201 changes: 201 additions & 0 deletions .specify/scripts/bash/check-prerequisites.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,201 @@
#!/usr/bin/env bash

# Consolidated prerequisite checking script
#
# This script provides unified prerequisite checking for Spec-Driven Development workflow.
# It replaces the functionality previously spread across multiple scripts.
#
# Usage: ./check-prerequisites.sh [OPTIONS]
#
# OPTIONS:
# --json Output in JSON format
# --require-tasks Require tasks.md to exist (for implementation phase)
# --include-tasks Include tasks.md in AVAILABLE_DOCS list
# --paths-only Only output path variables (no validation)
# --help, -h Show help message
#
# OUTPUTS:
# JSON mode: {"FEATURE_DIR":"...", "AVAILABLE_DOCS":["..."]}
# Text mode: FEATURE_DIR:... \n AVAILABLE_DOCS: \n ✓/✗ file.md
# Paths only: REPO_ROOT: ... \n BRANCH: ... \n FEATURE_DIR: ... etc.

set -e

# Parse command line arguments
JSON_MODE=false
REQUIRE_TASKS=false
INCLUDE_TASKS=false
PATHS_ONLY=false

for arg in "$@"; do
case "$arg" in
--json)
JSON_MODE=true
;;
--require-tasks)
REQUIRE_TASKS=true
;;
--include-tasks)
INCLUDE_TASKS=true
;;
--paths-only)
PATHS_ONLY=true
;;
--help|-h)
cat << 'EOF'
Usage: check-prerequisites.sh [OPTIONS]

Consolidated prerequisite checking for Spec-Driven Development workflow.

OPTIONS:
--json Output in JSON format
--require-tasks Require tasks.md to exist (for implementation phase)
--include-tasks Include tasks.md in AVAILABLE_DOCS list
--paths-only Only output path variables (no prerequisite validation)
--help, -h Show this help message

EXAMPLES:
# Check task prerequisites (plan.md required)
./check-prerequisites.sh --json

# Check implementation prerequisites (plan.md + tasks.md required)
./check-prerequisites.sh --json --require-tasks --include-tasks

# Get feature paths only (no validation)
./check-prerequisites.sh --paths-only

EOF
exit 0
;;
*)
echo "ERROR: Unknown option '$arg'. Use --help for usage information." >&2
exit 1
;;
esac
done

# Source common functions
SCRIPT_DIR="$(CDPATH="" cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/common.sh"

# Get feature paths.
# In --paths-only mode this is pure resolution, so pass --no-persist to opt out
# of the feature.json write side effect (issue #3025).
if $PATHS_ONLY; then
_paths_output=$(get_feature_paths --no-persist) || { echo "ERROR: Failed to resolve feature paths" >&2; exit 1; }
else
_paths_output=$(get_feature_paths) || { echo "ERROR: Failed to resolve feature paths" >&2; exit 1; }
fi
eval "$_paths_output"
unset _paths_output

# If paths-only mode, output paths and exit (no validation)
if $PATHS_ONLY; then
if $JSON_MODE; then
# Minimal JSON paths payload (no validation performed)
if has_jq; then
jq -cn \
--arg repo_root "$REPO_ROOT" \
--arg branch "$CURRENT_BRANCH" \
--arg feature_dir "$FEATURE_DIR" \
--arg feature_spec "$FEATURE_SPEC" \
--arg impl_plan "$IMPL_PLAN" \
--arg tasks "$TASKS" \
'{REPO_ROOT:$repo_root,BRANCH:$branch,FEATURE_DIR:$feature_dir,FEATURE_SPEC:$feature_spec,IMPL_PLAN:$impl_plan,TASKS:$tasks}'
else
printf '{"REPO_ROOT":"%s","BRANCH":"%s","FEATURE_DIR":"%s","FEATURE_SPEC":"%s","IMPL_PLAN":"%s","TASKS":"%s"}\n' \
"$(json_escape "$REPO_ROOT")" "$(json_escape "$CURRENT_BRANCH")" "$(json_escape "$FEATURE_DIR")" "$(json_escape "$FEATURE_SPEC")" "$(json_escape "$IMPL_PLAN")" "$(json_escape "$TASKS")"
fi
else
echo "REPO_ROOT: $REPO_ROOT"
echo "BRANCH: $CURRENT_BRANCH"
echo "FEATURE_DIR: $FEATURE_DIR"
echo "FEATURE_SPEC: $FEATURE_SPEC"
echo "IMPL_PLAN: $IMPL_PLAN"
echo "TASKS: $TASKS"
fi
exit 0
fi

# Validate required directories and files
if [[ ! -d "$FEATURE_DIR" ]]; then
echo "ERROR: Feature directory not found: $FEATURE_DIR" >&2
echo "Run /speckit.specify first to create the feature structure." >&2
exit 1
fi

if [[ ! -f "$FEATURE_SPEC" ]]; then
echo "ERROR: spec.md not found in $FEATURE_DIR" >&2
echo "Run /speckit.specify first to create the feature spec." >&2
exit 1
fi

if [[ ! -f "$IMPL_PLAN" ]]; then
Comment thread
cubic-dev-ai[bot] marked this conversation as resolved.
echo "ERROR: plan.md not found in $FEATURE_DIR" >&2
echo "Run /speckit.plan first to create the implementation plan." >&2
exit 1
fi

# Check for tasks.md if required
if $REQUIRE_TASKS && [[ ! -f "$TASKS" ]]; then
echo "ERROR: tasks.md not found in $FEATURE_DIR" >&2
echo "Run /speckit.tasks first to create the task list." >&2
exit 1
fi

# Build list of available documents
docs=()

# Always check these optional docs
[[ -f "$RESEARCH" ]] && docs+=("research.md")
[[ -f "$DATA_MODEL" ]] && docs+=("data-model.md")

# Check contracts directory (only if it exists and has files)
if [[ -d "$CONTRACTS_DIR" ]] && [[ -n "$(ls -A "$CONTRACTS_DIR" 2>/dev/null)" ]]; then
docs+=("contracts/")
fi

[[ -f "$QUICKSTART" ]] && docs+=("quickstart.md")

# Include tasks.md if requested and it exists
if $INCLUDE_TASKS && [[ -f "$TASKS" ]]; then
docs+=("tasks.md")
fi

# Output results
if $JSON_MODE; then
# Build JSON array of documents
if has_jq; then
if [[ ${#docs[@]} -eq 0 ]]; then
json_docs="[]"
else
json_docs=$(printf '%s\n' "${docs[@]}" | jq -R . | jq -s .)
fi
jq -cn \
--arg feature_dir "$FEATURE_DIR" \
--argjson docs "$json_docs" \
'{FEATURE_DIR:$feature_dir,AVAILABLE_DOCS:$docs}'
else
if [[ ${#docs[@]} -eq 0 ]]; then
json_docs="[]"
else
json_docs=$(for d in "${docs[@]}"; do printf '"%s",' "$(json_escape "$d")"; done)
json_docs="[${json_docs%,}]"
fi
printf '{"FEATURE_DIR":"%s","AVAILABLE_DOCS":%s}\n' "$(json_escape "$FEATURE_DIR")" "$json_docs"
fi
else
# Text output
echo "FEATURE_DIR:$FEATURE_DIR"
echo "AVAILABLE_DOCS:"

# Show status of each potential document
check_file "$RESEARCH" "research.md"
check_file "$DATA_MODEL" "data-model.md"
check_dir "$CONTRACTS_DIR" "contracts/"
check_file "$QUICKSTART" "quickstart.md"

if $INCLUDE_TASKS; then
check_file "$TASKS" "tasks.md"
fi
fi
Loading