From 418792d1ec9c5f83c5d477b66cebb152c35d3382 Mon Sep 17 00:00:00 2001 From: vasily Date: Fri, 30 Oct 2026 12:40:16 +0800 Subject: [PATCH] feat: refresh shared skills catalog --- .gitignore | 2 + README.md | 164 +++-- bin/sync-skills | 103 +++ bin/t3-deliver | 37 +- bin/validate-skills | 125 ++++ docs/installation.md | 151 +++++ docs/maintenance.md | 111 +++ protocols/agents-template.md | 65 ++ protocols/telemetry.md | 73 ++ protocols/workflow.md | 45 ++ skills/api-design/SKILL.md | 116 +--- skills/app-security/SKILL.md | 120 +--- .../references/app-hardening-playbook.md | 184 +---- .../references/review-checklist.md | 42 +- skills/backprop/SKILL.md | 89 +-- skills/benchmark-refactor/SKILL.md | 49 ++ .../references/madr-template.md | 45 ++ skills/build/SKILL.md | 92 +-- skills/cad-3d-print/SKILL.md | 46 ++ skills/caveman/SKILL.md | 107 +-- skills/check/SKILL.md | 105 +-- skills/database-design/SKILL.md | 106 +-- skills/debugging-workflow/SKILL.md | 86 +-- skills/deepen/SKILL.md | 86 +-- skills/deliver-file/SKILL.md | 7 +- skills/emergency-procedures/SKILL.md | 96 +-- skills/fuck-slop/SKILL.md | 99 +-- skills/fuck-slop/references/tells.md | 6 +- skills/fuck-slop/references/voices.md | 2 +- skills/git-workflow/SKILL.md | 92 +-- skills/git-workflow/references/pr-template.md | 35 + skills/git-workflow/references/worktrees.md | 60 ++ skills/grill/SKILL.md | 91 +-- skills/interface-kit/SKILL.md | 637 +----------------- .../references/accessibility-checklist.md | 28 +- .../references/animation-playbook.md | 5 +- .../references/design-recipes.md | 506 ++++++++++++++ .../references/review-checklist.md | 4 +- skills/junior-to-senior/SKILL.md | 123 +--- .../references/research-playbook.md | 6 +- skills/kicad-pcb-design/SKILL.md | 70 ++ skills/review/SKILL.md | 102 +-- skills/skill-architect/SKILL.md | 107 +-- .../references/sample-skill.md | 56 +- skills/spec/SKILL.md | 104 +-- skills/spec/references/format.md | 13 +- sync-skills.sh | 66 +- tests/test_tools.py | 180 +++++ tests/workflow-cases.md | 19 + 49 files changed, 2273 insertions(+), 2390 deletions(-) create mode 100644 .gitignore create mode 100755 bin/sync-skills create mode 100755 bin/validate-skills create mode 100644 docs/installation.md create mode 100644 docs/maintenance.md create mode 100644 protocols/agents-template.md create mode 100644 protocols/telemetry.md create mode 100644 protocols/workflow.md create mode 100644 skills/benchmark-refactor/SKILL.md create mode 100644 skills/benchmark-refactor/references/madr-template.md create mode 100644 skills/cad-3d-print/SKILL.md create mode 100644 skills/git-workflow/references/pr-template.md create mode 100644 skills/git-workflow/references/worktrees.md create mode 100644 skills/interface-kit/references/design-recipes.md create mode 100644 skills/kicad-pcb-design/SKILL.md create mode 100644 tests/test_tools.py create mode 100644 tests/workflow-cases.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7a60b85 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +__pycache__/ +*.pyc diff --git a/README.md b/README.md index bfe900f..b94986d 100644 --- a/README.md +++ b/README.md @@ -1,85 +1,111 @@ # global-skills -Personal skill repository shared by every coding agent on this host -(Antigravity/agy, Codex, OpenCode, Claude Code). Kept separate from JEV, -which remains its own project with its own skills. +Reusable workflows for coding agents: planning, debugging, API and database design, +security, UI, writing, performance benchmarking, and optional spec-driven development. +Skills help an agent complete the requested work with clear steps and appropriate verification. -## Sources +This repository is the canonical source for 20 shared skills on this host. +Installed skills link back to the source, so edits take effect without copying +files into each agent's directory. The workflows favor direct execution: use +existing context, preserve user work, and check only what the change needs. -Agents see skills from two sources: +## Install -| Source | Owner | Path | -|---|---|---| -| This repo | here | `~/Projects/work/global-skills/skills/` | -| JEV skills | JEV project | `~/Projects/work/JEV/.agents/skills/` | +Requires Bash, Python 3, and a filesystem supporting symbolic links. The supplied +installer targets Linux-style home directories and uses Python's standard library. -## Targets - -`sync-skills.sh` maintains symlinks into each agent's skills directory: - -- `~/.agents/skills` (OpenCode) -- `~/.codex/skills` (Codex) -- `~/.claude/skills` (Claude Code) - -Antigravity reads both sources directly via `~/.gemini/config/skills.json`. - -## Skill Catalog - -All skills adhere to the 5-phase Skill Architect anatomy (`Phase 0: Setup` through `Phase 4: Output Execution & Verification`) with System One balance invariants. - -### Planning & Review -- [`junior-to-senior`](skills/junior-to-senior/SKILL.md) — Adversarial staff-engineer review upgrading agent plans using live codebase inspection and SOTA research. -- [`debugging-workflow`](skills/debugging-workflow/SKILL.md) — Systematic root-cause isolation, minimal reproduction, and regression testing. -- [`emergency-procedures`](skills/emergency-procedures/SKILL.md) — Recovery protocols for broken agent loops, git tangle, and context drift. - -### Writing & UI Craft -- [`fuck-slop`](skills/fuck-slop/SKILL.md) — Multi-genre de-slop pass eliminating statistical AI writing fingerprints. -- [`interface-kit`](skills/interface-kit/SKILL.md) — Authoritative implementation guide for UI craft, spring animations, and WCAG AA accessibility. - -### Spec-Driven Development (SDD) Suite -- [`grill`](skills/grill/SKILL.md) — Calibrated plan interrogation before speccing. -- [`spec`](skills/spec/SKILL.md) — Sole mutator of `SPEC.md` managing goals, invariants, tasks, and bugs. -- [`caveman`](skills/caveman/SKILL.md) — Token-compression dialect (~75% reduction) for dense invariant specification. -- [`review`](skills/review/SKILL.md) — Adversarial pre-implementation spec refutation and gatekeeper. -- [`build`](skills/build/SKILL.md) — Single-threaded plan-to-execute loop with automated test oracles. -- [`check`](skills/check/SKILL.md) — Read-only drift detector verifying code against active invariants. -- [`backprop`](skills/backprop/SKILL.md) — Bug-to-spec feedback loop binding regression tests. -- [`deepen`](skills/deepen/SKILL.md) — Module design pass pulling complexity down and shrinking interfaces. - -### Architecture & Security -- [`api-design`](skills/api-design/SKILL.md) — REST membranes, contract-first schemas, and boundary clamping. -- [`database-design`](skills/database-design/SKILL.md) — SQLite WAL persistence, repository abstraction, and keyset pagination. -- [`app-security`](skills/app-security/SKILL.md) — Four-Pillar upload defense, outbound SSRF guards, and secret masking. -- [`git-workflow`](skills/git-workflow/SKILL.md) — Atomic commit grouping, selective staging, and conventional commits. - -### Meta & Infrastructure -- [`deliver-file`](skills/deliver-file/SKILL.md) — Push files and directories to the user's workstation over Tailscale SSH via `t3-deliver`. -- [`skill-architect`](skills/skill-architect/SKILL.md) — Standardized authoring, auditing, and maintenance of global agent skills. - -## Usage +From a checkout: ```sh -./sync-skills.sh # after adding/updating/removing a skill +cd /path/to/global-skills +./sync-skills.sh --dry-run +./sync-skills.sh +./sync-skills.sh --check ``` -Idempotent: re-links only what changed, never touches unrelated entries -(omarchy system skills etc.), warns on name collisions between the two -sources (this repo wins). +The default targets are: -## Adding a skill +| Target | Skill directory | +| --- | --- | +| OpenCode / shared agents | `~/.agents/skills` | +| Codex | `~/.codex/skills` | +| Claude Code | `~/.claude/skills` | -1. Create `skills//SKILL.md` following the [sample-skill blueprint](skills/skill-architect/references/sample-skill.md). -2. Run `./sync-skills.sh`. -3. Commit. +Sync also includes JEV skills when `~/Projects/work/JEV/.agents/skills` exists. +It preserves unrelated files and symlinks, removes stale managed links, and rejects +source-name collisions. Antigravity uses separate source-path configuration. -## t3-deliver +See [installation and usage](docs/installation.md) for first-time setup, installing +one skill, custom targets, Antigravity configuration, updates, and troubleshooting. -`bin/t3-deliver` is symlinked to `~/.local/bin/t3-deliver`. It prints a -ready-to-paste `scp` command (Tailscale SSH) so the user can pull any file -from this host to their Windows machine. The `deliver-file` skill teaches -every agent to use it. +## Use -## T3 integration +Ask for the outcome in normal language. An agent can select a skill from its +name and description, or you can explicitly name the workflow: -Open this folder as a project in T3 Code to get a top-bar Action running -`sync-skills.sh` (Actions are project-scoped commands). +- “Use debugging-workflow to diagnose this failing request.” +- “Use interface-kit to improve this form using the existing components.” +- “Use junior-to-senior to review this implementation plan.” +- “Use benchmark-refactor to profile this module and evaluate optimizations.” +- “Use spec to write a specification, then implement task T1 with build.” + +The spec suite applies when you choose that workflow. Ordinary bug fixes and +feature edits do not require a spec. Requests to review or report remain read-only. +Discovery and activation depend on the agent; this repository installs Markdown +instructions and links, without adding runtime hooks or model calls. + +For projects with substantial architecture or design history, keep a concise, +project-owned context index pointing to the key code paths and design documents. +Load it when the task needs that history; avoid generating directory-by-directory +documentation unless maintainers will keep it accurate. + +## Catalog + +| Work | Skills | +| --- | --- | +| Plan and review | [grill](skills/grill/SKILL.md), [junior-to-senior](skills/junior-to-senior/SKILL.md), [review](skills/review/SKILL.md) | +| Adopted spec workflow | [spec](skills/spec/SKILL.md), [build](skills/build/SKILL.md), [check](skills/check/SKILL.md), [backprop](skills/backprop/SKILL.md), [caveman](skills/caveman/SKILL.md) | +| Fix and simplify | [debugging-workflow](skills/debugging-workflow/SKILL.md), [deepen](skills/deepen/SKILL.md), [emergency-procedures](skills/emergency-procedures/SKILL.md) | +| Architecture | [api-design](skills/api-design/SKILL.md), [database-design](skills/database-design/SKILL.md), [app-security](skills/app-security/SKILL.md), [benchmark-refactor](skills/benchmark-refactor/SKILL.md) | +| UI and writing | [interface-kit](skills/interface-kit/SKILL.md), [fuck-slop](skills/fuck-slop/SKILL.md) | +| Maintenance | [git-workflow](skills/git-workflow/SKILL.md), [deliver-file](skills/deliver-file/SKILL.md), [skill-architect](skills/skill-architect/SKILL.md) | + +JEV supplies `context-architect`, `lint-architect`, `typesafe-ai`, `doc-architect`, +and `system-one-balance-protocol`. Its `anti-slop-editor` is a compatibility alias +to `fuck-slop`. + +## Maintain + +Content edits propagate through existing links immediately. Sync after adding, +removing, or moving a skill. Running agents may need a new session to refresh their +cached skill list or instructions. + +```sh +python3 bin/validate-skills # metadata, local links, duplicate names; read-only +./sync-skills.sh --check # installed-link drift/conflicts; read-only +``` + +Validation is for catalog maintenance, not a preflight for everyday coding. +See [authoring and maintenance](docs/maintenance.md) for metadata, ownership, +verification conventions, and tool regression checks. + +## Workflow conventions + +[Shared workflow and verification](protocols/workflow.md) defines proportional +checks and preservation of user work. User and repository instructions take +precedence. Telemetry and pull requests follow [the telemetry protocol](protocols/telemetry.md). +Repository governance rules and boundary tiers adhere to [the agents template](protocols/agents-template.md). +References load on demand; there is no required phase anatomy, per-task commit, +separate review invocation, or blanket full-suite run. + +Skills declare `scope: global`, `project`, or `alias`. Scope documents applicability; +it does not change a harness router by itself. JEV-dependent skills state their +project requirements. Some optional references assume a sibling JEV checkout. + +## Host-specific file delivery + +[deliver-file](skills/deliver-file/SKILL.md) uses `bin/t3-deliver` to print a command +for the user's Windows machine to pull files over Tailscale SSH. Its host address +is configured for this machine; adapt it before using delivery on another host. +The helper assumes `realpath` locally and OpenSSH scp with SFTP support remotely. +It prints a command and does not start a transfer. diff --git a/bin/sync-skills b/bin/sync-skills new file mode 100755 index 0000000..e7d6ec4 --- /dev/null +++ b/bin/sync-skills @@ -0,0 +1,103 @@ +#!/usr/bin/env python3 +"""Synchronize skill symlinks without taking ownership of unrelated entries.""" +import argparse +from pathlib import Path +import os +import re +import sys + + +def lexical(path): + return Path(os.path.abspath(path)) + + +def link_target(path): + return lexical(path.parent / os.readlink(path)) + + +def inside(path, root): + return path != root and root in path.parents + + +def main(argv=None): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--dry-run', action='store_true', help='show changes without writing') + parser.add_argument('--check', action='store_true', help='exit 1 if links differ; do not write') + parser.add_argument('--global-dir', type=Path, default=Path(__file__).resolve().parents[1] / 'skills') + parser.add_argument('--jev-dir', type=Path, default=Path.home() / 'Projects/work/JEV/.agents/skills') + parser.add_argument('--target', action='append', type=Path, help='override target directories; repeatable') + args = parser.parse_args(argv) + sources = [lexical(args.global_dir), lexical(args.jev_dir)] + targets = args.target if args.target is not None else [Path.home() / p for p in ('.agents/skills', '.codex/skills', '.claude/skills')] + targets = [lexical(p) for p in targets] + # Reject destinations inside catalogs (or containing them) before any mutation. + for target in targets: + resolved_target = target.resolve() + if any(resolved_target == source.resolve() or + inside(resolved_target, source.resolve()) or + inside(source.resolve(), resolved_target) for source in sources): + parser.error(f'target overlaps a source catalog: {target}') + if not sources[0].is_dir(): + parser.error(f'global skill source unavailable: {sources[0]}') + available = [p for p in sources if p.is_dir()] + expected = {} + errors = [] + for root in available: + for child in sorted(root.iterdir()): + if not child.is_dir(): + continue + if not re.fullmatch(r'[a-z0-9]+(?:-[a-z0-9]+)*', child.name) or not (child / 'SKILL.md').is_file(): + errors.append(f'invalid skill directory: {child}') + continue + if child.name in expected: + errors.append(f'colliding skill name: {child.name} ({expected[child.name]}, {child})') + else: + expected[child.name] = child + if errors: + print('\n'.join(errors), file=sys.stderr) + return 2 + if len(available) != len(sources): + print(f'WARN: JEV source unavailable; preserving its existing links: {sources[1]}', file=sys.stderr) + changes = 0 + conflicts = 0 + readonly = args.check or args.dry_run + for target in targets: + if target.exists() and not target.is_dir(): + print(f'SKIP: target is not a directory: {target}', file=sys.stderr) + conflicts += 1 + continue + if not readonly: + target.mkdir(parents=True, exist_ok=True) + # Only prune links to sources that were successfully inventoried. + for dest in sorted(target.iterdir()) if target.is_dir() else []: + if dest.is_symlink() and any(inside(link_target(dest), root) for root in available) and dest.name not in expected: + print(f'remove stale: {dest}') + changes += 1 + if not readonly: + dest.unlink() + for name, src in sorted(expected.items()): + dest = target / name + if dest.is_symlink(): + current = link_target(dest) + if current == src: + continue + if not any(inside(current, root) for root in available): + print(f'SKIP: unrelated symlink: {dest}', file=sys.stderr) + conflicts += 1 + continue + elif dest.exists(): + print(f'SKIP: unrelated entry: {dest}', file=sys.stderr) + conflicts += 1 + continue + print(f'link: {dest} -> {src}') + changes += 1 + if not readonly: + if dest.is_symlink(): + dest.unlink() + dest.symlink_to(src, target_is_directory=True) + print(f'{"check" if args.check else "dry-run" if args.dry_run else "sync"}: {changes} changes, {conflicts} conflicts') + return 1 if conflicts or (args.check and changes) else 0 + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/bin/t3-deliver b/bin/t3-deliver index 97c99e8..d9a93d0 100755 --- a/bin/t3-deliver +++ b/bin/t3-deliver @@ -1,32 +1,25 @@ #!/usr/bin/env bash -# Print a ready-to-paste scp command for pulling a file from this host -# to the user's Windows machine over Tailscale SSH. +# Print a command for the user's Windows machine to pull a path over Tailscale SSH. set -euo pipefail - -HOST="vasily@msi.tail0b014a.ts.net" - +DELIVERY_HOST="vasily@msi.tail0b014a.ts.net" if [[ $# -ne 1 ]]; then echo "usage: t3-deliver " >&2 exit 1 fi - -path="$1" -if [[ ! -e "$path" ]]; then - echo "t3-deliver: no such path: $path" >&2 +if [[ ! -e "$1" ]]; then + echo "t3-deliver: no such path: $1" >&2 exit 1 fi - -if [[ "$path" != /* ]]; then - path="$(realpath "$path")" -else - path="$(realpath -m "$path")" +DELIVERY_PATH="$(realpath -- "$1")" +# Reject characters that cannot be shared safely between cmd and PowerShell. +# OpenSSH SFTP mode handles the quoted remote path without a remote shell. +if [[ "$DELIVERY_PATH" == *[\"\$\`\%\!]* || "$DELIVERY_PATH" == *$'\n'* || "$DELIVERY_PATH" == *$'\r'* ]]; then + echo 't3-deliver: rename or archive this path with a simple name before delivery' >&2 + exit 1 fi - -flags="" -if [[ -d "$path" ]]; then - flags="-r " +DELIVERY_FLAGS="" +if [[ -d "$DELIVERY_PATH" ]]; then + DELIVERY_FLAGS="-r " fi - -echo "Paste in cmd or PowerShell on your Windows machine:" -echo -echo "${flags}scp ${HOST}:\"${path}\" ." +printf 'Paste in cmd or PowerShell on your Windows machine (OpenSSH scp with SFTP support):\n\n' +printf 'scp %s"%s:%s" .\n' "$DELIVERY_FLAGS" "$DELIVERY_HOST" "$DELIVERY_PATH" diff --git a/bin/validate-skills b/bin/validate-skills new file mode 100755 index 0000000..23f8e40 --- /dev/null +++ b/bin/validate-skills @@ -0,0 +1,125 @@ +#!/usr/bin/env python3 +"""Check catalog metadata, local links, and duplicate names without mutating files.""" +import argparse +import json +from pathlib import Path +import re +import sys + + +def metadata(text): + """Read the catalog's scalar and block-string YAML frontmatter subset.""" + match = re.match(r'\A---\r?\n(.*?)\r?\n---(?:\r?\n|$)', text, re.S) + if not match: + raise ValueError('missing frontmatter') + result = {} + field = None + for line in match[1].splitlines(): + if not line.strip() or line.lstrip().startswith('#'): + continue + if line[0].isspace(): + if field is None: + raise ValueError('unexpected indentation in frontmatter') + result[field] += ' ' + line.strip() + continue + item = re.fullmatch(r'([a-z][a-z0-9_-]*):(?:\s+(.*))?', line) + if not item: + raise ValueError('invalid frontmatter field') + key, value = item.groups() + if key in result: + raise ValueError(f'duplicate field: {key}') + value = (value or '').strip() + field = key if value in ('>', '|', '>-', '|-') else None + if field: + result[key] = '' + else: + if value.startswith('"'): + try: + result[key] = json.loads(value) + except json.JSONDecodeError as exc: + raise ValueError(f'invalid quoted field: {key}') from exc + elif value.startswith("'"): + if len(value) < 2 or not value.endswith("'"): + raise ValueError(f'invalid quoted field: {key}') + result[key] = value[1:-1].replace("''", "'") + else: + if re.search(r':\s', value): + raise ValueError(f'invalid plain scalar: {key}; use a block string') + result[key] = value.split(' #', 1)[0] + return {k: v.strip() for k, v in result.items()} + + +def local_links(path): + fenced = False + for number, line in enumerate(path.read_text().splitlines(), 1): + if line.lstrip().startswith(('```', '~~~')): + fenced = not fenced + continue + if fenced: + continue + for raw in re.findall(r'\]\(([^)]+)\)', line): + target = raw.strip().split('#', 1)[0] + if not target or re.match(r'[a-zA-Z][a-zA-Z0-9+.-]*:', target): + continue + target = target.strip('<>') + resolved = path.parent / target + if not resolved.exists(): + yield f'{path}:{number}: missing link target {target}' + + +def main(argv=None): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument('--global-dir', type=Path, default=Path(__file__).resolve().parents[1] / 'skills') + parser.add_argument('--jev-dir', type=Path, default=Path.home() / 'Projects/work/JEV/.agents/skills') + args = parser.parse_args(argv) + errors = [] + names = {} + count = 0 + if not args.global_dir.is_dir(): + parser.error(f'global catalog unavailable: {args.global_dir}') + roots = [args.global_dir] + if args.jev_dir.is_dir(): + roots.append(args.jev_dir) + else: + print(f'WARN: JEV catalog unavailable: {args.jev_dir}', file=sys.stderr) + for root in roots: + for directory in sorted(root.iterdir()): + if not directory.is_dir(): + continue + path = directory / 'SKILL.md' + if not path.is_file(): + errors.append(f'{directory}: missing SKILL.md') + continue + count += 1 + try: + fields = metadata(path.read_text()) + name = fields.get('name', '') + if name != directory.name or not re.fullmatch(r'[a-z0-9]+(?:-[a-z0-9]+)*', name): + errors.append(f'{path}: invalid or mismatched name') + if not fields.get('description'): + errors.append(f'{path}: empty description') + if fields.get('scope') not in ('global', 'project', 'alias'): + errors.append(f'{path}: scope must be global, project, or alias') + if name in names: + errors.append(f'{path}: name collides with {names[name]}') + names[name] = path + except ValueError as exc: + errors.append(f'{path}: {exc}') + for path in sorted(root.rglob('*.md')): + errors.extend(local_links(path)) + # Global protocols are part of this catalog; JEV protocols belong to its source. + for folder in [args.global_dir.parent / 'protocols', args.jev_dir.parent / 'protocols']: + if folder.is_dir(): + for path in sorted(folder.glob('*.md')): + errors.extend(local_links(path)) + readme = args.global_dir.parent / 'README.md' + if readme.is_file(): + errors.extend(local_links(readme)) + for error in errors: + print(error, file=sys.stderr) + print(f'catalog: {count} skills, {len(errors)} errors') + return bool(errors) + + +if __name__ == '__main__': + sys.exit(main()) diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..c665c90 --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,151 @@ +# Installation and usage + +## Get the source + +Use an existing checkout or clone the repository into a stable location. Replace +`` with the URL of your copy; this checkout has no configured Git +remote from which to derive one. + +```sh +git clone ~/Projects/work/global-skills +cd ~/Projects/work/global-skills +``` + +Requirements: Bash, Python 3, and symbolic-link support. No Python packages or Node +packages are needed for syncing. Keep the checkout after installing: installed +links point into it. + +The commands here target a Linux host. Native Windows installation and junction +management are not implemented by this script. + +## Install the catalog + +```sh +./sync-skills.sh --dry-run +./sync-skills.sh +./sync-skills.sh --check +``` + +The first command previews changes, the second applies them, and the third checks +the result without writing. Default destinations are `~/.agents/skills`, +`~/.codex/skills`, and `~/.claude/skills`. + +Sync includes every valid skill directory from this checkout and, when available, +`~/Projects/work/JEV/.agents/skills`. A source skill must have a kebab-case directory +name and a `SKILL.md` file. Duplicate names across sources stop sync before writes. +Existing unrelated destinations are preserved and reported as conflicts. + +If JEV is absent, sync warns and installs the shared catalog. Some skill references +and the `anti-slop-editor` alias depend on JEV or the expected sibling-checkout +layout. The optional catalog validator reports unresolved local references; it is +not required to install the shared skills. + +## Install into selected agent directories + +`--target` replaces the default destinations. Repeat it to select several: + +```sh +./sync-skills.sh --target "$HOME/.codex/skills" +./sync-skills.sh --target "$HOME/.codex/skills" --target "$HOME/.claude/skills" +``` + +Both commands install the available source catalogs, not a selected subset of +skills. Targets cannot overlap a source catalog. + +For a different JEV location, use the same override for syncing and checking: + +```sh +./sync-skills.sh --jev-dir /path/to/JEV/.agents/skills --target "$HOME/.codex/skills" +./sync-skills.sh --check --jev-dir /path/to/JEV/.agents/skills --target "$HOME/.codex/skills" +``` + +`--global-dir` overrides the shared source directory. Moving sources may require +manual removal of old links: the script recognizes ownership only under the source +roots supplied for that invocation. + +## Install one shared skill + +For a small selection, create links directly instead of running catalog sync. +From the repository root: + +```sh +mkdir -p "$HOME/.codex/skills" +ln -s "$PWD/skills/interface-kit" "$HOME/.codex/skills/interface-kit" +``` + +Repeat with the desired skill name and agent directory. Link the whole skill +directory so its references remain available. Keep the checkout and its protocols; +copying just `SKILL.md` can break relative references. + +`ln -s` fails if that destination already exists. Inspect an existing entry before +replacing it. Catalog sync later adds all available skills to that target; it is +not a subset installer. + +## Antigravity + +The sync script does not edit Antigravity configuration. On this host, Antigravity +reads source directories listed in `~/.gemini/config/skills.json`: + +```json +{ + "entries": [ + { "path": "/absolute/path/to/global-skills/skills" }, + { "path": "/absolute/path/to/JEV/.agents/skills" } + ] +} +``` + +Use your actual absolute paths and omit JEV if unavailable. Merge entries with +existing configuration rather than overwriting unrelated sources. This is the +configuration used by the current host integration; another Antigravity version +or setup may discover skills differently. + +## Use and update + +Ask for work normally or explicitly name a skill. For example: + +```text +Use app-security to check authorization on these routes. Give me a report first. +Use deepen to simplify this module while preserving its public behavior. +Use fuck-slop to edit this draft without changing its claims. +``` + +The agent decides applicability from its available skill catalog. Naming a skill +does not authorize unrelated edits, publishing, or other external actions. + +Edit the canonical source to update a skill. Existing links expose that content +immediately; start a new agent session if the running session cached older +instructions. Sync after changes to skill names or directory locations. + +For Git updates, follow your checkout's branch/remote convention. Inspect local +changes before pulling; this project does not impose a remote or auto-update policy. + +## Remove an installation + +Remove an individual symlink from the agent directory after inspecting it: + +```sh +ls -l "$HOME/.codex/skills/interface-kit" +``` + +If it points to this checkout, remove that link. Removing an installed link leaves +the source intact; the next full sync recreates it. To retire a skill from the +catalog, remove its source directory and sync while the source root is still +available. Missing JEV sources cause their old links to be preserved, not removed. + +## Troubleshooting + +| Result | Meaning and next step | +| --- | --- | +| `check: 0 changes, 0 conflicts` | Installed links match the available catalogs. | +| Check exits `1` with changes | Preview with `--dry-run`, then sync to apply the changes. | +| `SKIP: unrelated entry` or `unrelated symlink` | Inspect that destination; sync preserves it. Choose another target or deliberately resolve the collision. | +| Colliding skill name, exit `2` | Rename or consolidate the source skills; there is no silent precedence rule. | +| JEV unavailable warning | Shared skills can install; JEV links are preserved until its source is available. | +| Validator reports missing local links | Restore the referenced file/checkouts or correct the source link. Sync checks installation, not reference integrity. | +| Skill missing in a running agent | Check the source and installed link, then refresh that agent's session/catalog. | + +`--dry-run` and `--check` make no changes. Dry-run exits `0` for a conflict-free +preview even when changes are needed. Check exits `1` for drift or conflicts. +Sync also exits `1` for destination conflicts; unaffected destinations may already +have been updated. Invalid source catalogs/arguments exit `2` before syncing. diff --git a/docs/maintenance.md b/docs/maintenance.md new file mode 100644 index 0000000..4e42c31 --- /dev/null +++ b/docs/maintenance.md @@ -0,0 +1,111 @@ +# Authoring and maintenance + +Shared workflows live in `skills//`; JEV runtime-specific skills remain in +JEV. Installed directories link to those sources. Maintain one procedure per +workflow and use small aliases when an old name needs to remain callable. + +## Add or edit a skill + +Use [skill-architect](../skills/skill-architect/SKILL.md) for catalog work. A skill +needs concrete activation criteria, useful steps, and appropriate completion +evidence. There is no required phase structure or length quota. + +Example `SKILL.md` metadata: + +```yaml +--- +name: example-skill +description: > + Perform a specific workflow when the user requests its concrete outcome. +scope: global +--- +``` + +Names use lowercase kebab-case and match the containing directory. Scope values: + +| Scope | Meaning | +| --- | --- | +| `global` | Reusable workflow; preserve the target repository's conventions. | +| `project` | Requires a named project's tooling or layout; explain that dependency. | +| `alias` | Explicit compatibility entry point delegating to another skill. | + +Scope is descriptive metadata; a harness must implement its own routing policy. +Write optional recipes under `references/` and link them where they help a decision. +Keep relative links valid from the source file. Use the +[template](../skills/skill-architect/references/sample-skill.md) as an example. + +For catalog changes, validate once from the repository root: + +```sh +python3 bin/validate-skills +``` + +After adding, removing, or moving a skill, preview and sync: + +```sh +./sync-skills.sh --dry-run +./sync-skills.sh +``` + +Content-only edits already propagate through existing links. Commit according to +the user's request and repository/session conventions; these tools do not commit. + +## Workflow rules + +Follow [shared workflow and verification](../protocols/workflow.md): use existing +context, preserve user work, and choose checks that can detect a real failure in +the changed behavior. Avoid mandatory cross-skill handoffs, repeated confirmation, +full suites for prose edits, or automatic spec creation for ordinary bug fixes. + +In an adopted spec workflow, `spec` owns architecture and task definitions, `build` +owns task statuses, and `backprop` owns bug records and bug-related invariants. +See [the spec format](../skills/spec/references/format.md). A review-only request +produces findings without edits or commits. + +## What validation covers + +`bin/validate-skills` checks metadata, matching names, scope, duplicate source +names, and local Markdown targets outside fenced examples. It uses Python's +standard library and supports this catalog's scalar/block-string YAML subset. +It does not verify web links, Markdown anchors, arbitrary YAML schemas, or whether +an agent will activate or execute a skill correctly. + +Both tools accept `--global-dir` and `--jev-dir`. Sync additionally accepts +repeatable `--target` arguments. Use source overrides consistently when checking +a custom installation. Link ownership is inferred from those source roots; it is +not recorded in a separate manifest. + +## Verify tool changes + +When sync, validation, or delivery behavior changes, run the relevant regression +checks from the repository root: + +```sh +python3 -m unittest discover -s tests -v +``` + +Tests use temporary source catalogs and targets; they do not change installed +agent directories. Coverage includes link ownership, stale cleanup, read-only +modes, collisions, missing sources, and delivery command generation. + +[Workflow evaluation cases](../tests/workflow-cases.md) are optional fixtures for +trigger or policy changes. A behavioral evaluation requires observed agent actions; +passing script tests does not prove routing quality. Use real failures to guide +small corrections rather than adding more mandatory checks. + +## Delivery helper + +`bin/t3-deliver` prints a quoted `scp` command using this host's Tailscale address. +It requires Bash and `realpath`; the receiving Windows machine needs OpenSSH scp +with SFTP support and access to the host. It starts no server or transfer. + +To expose it on PATH, link the helper from the repository root: + +```sh +mkdir -p "$HOME/.local/bin" +ln -s "$PWD/bin/t3-deliver" "$HOME/.local/bin/t3-deliver" +``` + +Inspect an existing destination before replacing it. `sync-skills.sh` installs +skill directories, not this executable link. On another host, adapt the address +in `bin/t3-deliver` and the delivery skill's host-specific instructions together. diff --git a/protocols/agents-template.md b/protocols/agents-template.md new file mode 100644 index 0000000..541a543 --- /dev/null +++ b/protocols/agents-template.md @@ -0,0 +1,65 @@ +# Autonomous Agent Repository Rules Template + +This protocol defines the standard structure for a repository's root `AGENTS.md` +(and symlinked `CLAUDE.md`). It establishes concise behavioral baselines, +deterministic boundary tiers, and progressive disclosure pointers. + +## Design imperatives + +- **Length limit**: Keep the root instruction file concise ($\le 100$ lines target, + 300 lines maximum). Long rule files dilute model attention and are ignored. +- **Progressive disclosure**: Keep the root file an index of essential build/test + commands and pointers. Direct deep subsystem knowledge or tutorials into + specialized documents under `docs/` or `.agents/`. +- **Deterministic tooling**: Never delegate code formatting, whitespace, or import + sorting to LLM prompt prose. Formatting and linting belong exclusively to + deterministic CLI tools (Ruff, Biome, Prettier) run via hooks or CI. + +## Three-tier operational boundaries + +Root repository instructions must establish three explicit operational tiers: + +### 1. ALWAYS (Autonomous Execution) +Actions the agent must execute autonomously without asking for confirmation: +- Run targeted local unit and regression tests after making code changes. +- Execute deterministic linters and formatters before staging files. +- Update relevant documentation and architectural records when public interfaces change. +- Follow existing repository patterns, conventions, and module structure. + +### 2. ASK FIRST (Human Confirmation Required) +Boundary actions requiring explicit confirmation before invocation: +- Modifying database schemas or executing destructive data migrations. +- Introducing new third-party dependencies, packages, or external services. +- Altering production deployment configurations, Dockerfiles, or CI workflows. +- Deleting existing tracked source files or removing deprecated public APIs. + +### 3. NEVER (Non-Negotiable Invariants) +Hard negative constraints that must never be violated under any circumstances: +- Never commit plain-text credentials, tokens, or environment files (`.env`). +- Never force-push (`git push --force` or `-f`) to any remote tracking branch. +- Never execute destructive git resets (`git reset --hard` or `git clean -f`) on uncommitted work. +- Never commit directly to primary branches (`main` or `master`). +- Never suppress compiler warnings or type errors with blanket ignores (`# noqa`, `@ts-ignore`). + +## Canonical AGENTS.md template + +```markdown +# [Project Name] Agent Rules + +## Stack & Commands +- Language & Runtime: [e.g. Python 3.12 / Node 20 / Rust 1.80] +- Package Manager: [e.g. uv / pnpm / cargo] +- Fast Check: [e.g. pytest tests/unit/ / pnpm test / cargo test] +- Linter & Formatter: [e.g. ruff check --fix && ruff format / pnpm biome check --apply] +- Type Checker: [e.g. pyright / pnpm tsc --noEmit] + +## Three-Tier Boundaries +- **ALWAYS**: Run relevant checks before declaring completion. Run deterministic formatters. +- **ASK FIRST**: Adding dependencies, altering database schemas, or changing CI/deploy configs. +- **NEVER**: Direct commits to main, force pushes, hard resets, or committing secrets/.env. + +## Workflow & References +- Feature work occurs in isolated git worktrees or dedicated branches. +- Follow WHAT-WHY-HOW-LEARNINGS telemetry format for commits and pull requests. +- Subsystem details: see docs/architecture.md and docs/gotchas/. +``` diff --git a/protocols/telemetry.md b/protocols/telemetry.md new file mode 100644 index 0000000..4772de2 --- /dev/null +++ b/protocols/telemetry.md @@ -0,0 +1,73 @@ +# Standardized Agent Telemetry Protocol + +This protocol defines the **WHAT-WHY-HOW-LEARNINGS** communication standard for +autonomous agents. It eliminates unstructured conversational filler and ensures +every completion report and pull request provides verified empirical evidence and +captures reusable operational memory. + +## Governing principle + +**"The model proposes, but the engine measures."** +An agent must never claim completion based on internal heuristics or theoretical +assumptions. Verification requires concrete CLI execution evidence with explicit +exit codes and metric outputs. + +## Telemetry structure + +When reporting task completion or generating pull request bodies, format the +output using these four distinct sections: + +### 1. WHAT (System Modifications) +Itemize physical repository deltas. Do not use speculative language. Mirror the +factual `git status` / `git diff` state: +- **Modified Components**: Relative paths of modified files. +- **Added Assets**: Relative paths of newly created modules, tests, or configurations. +- **Removed Assets**: Deprecated files, removed functions, or deleted packages. + +### 2. WHY (Context & Architectural Intent) +Articulate the underlying rationale and business justification: +- **Problem Statement**: Explicit description of the bug, requirement, or performance bottleneck. +- **Requirement Traceability**: Reference to specification issue, plan bead, or task ticket. +- **Design Justification**: Why this approach was selected over alternative implementations. + +### 3. HOW (Technical Implementation & Verification Evidence) +Detail the mechanical execution and empirical proof of correctness: +- **Implementation Details**: Summary of algorithmic changes, API updates, or data structures. +- **Deterministic Verification Evidence**: + - Exact command(s) executed (e.g. `pytest tests/unit/test_auth.py -v`). + - Terminal exit code (must be 0). + - Test runner output metrics (e.g. `38 passed, 0 failed, 100% pass rate in 1.4s`). +- **Static Analysis & Type Checks**: + - Linter/type checker commands and output (e.g. `ruff check src/` -> clean). + +### 4. LEARNINGS & ARCHITECTURAL GOTCHAS +Capture operational traps, latent technical debt, and environment quirks: +- **Operational Traps**: Version sensitivities, platform discrepancies, race conditions. +- **Uncovered Debt**: Missing mocks, tight couplings, or obsolete patterns uncovered. +- **Repository Gotchas**: When a new trap or quirk is discovered, persist it to + `docs/gotchas/.md` so subsequent sessions load it into context. +- **Upstream Dependencies**: Follow-up tasks or recommendations. + +## Markdown template for PRs and task reports + +```markdown +### 1. WHAT (System Modifications) +- **Modified**: `src/auth/session.py` +- **Added**: `tests/unit/test_session_refresh.py` +- **Removed**: None + +### 2. WHY (Context & Architectural Intent) +- **Problem**: Concurrent session refresh requests cause race conditions and invalid tokens. +- **Traceability**: Closes #142 (Task T-3). +- **Design Justification**: Implemented in-process locking with Redis token leases. + +### 3. HOW (Technical Implementation & Verification Evidence) +- **Implementation**: Wrapped refresh logic in distributed mutex with 5s expiry. +- **Verification Commands Executed**: + - `pytest tests/unit/test_session_refresh.py` (Exit Code: 0, 12 passed in 0.8s) + - `ruff check src/auth/` (Exit Code: 0, clean) + +### 4. LEARNINGS & ARCHITECTURAL GOTCHAS +- **Operational Trap**: Redis cluster failover resets key TTL in mock environment. +- **Indexed Gotcha**: Saved to `docs/gotchas/redis-mock-cluster-ttl.md`. +``` diff --git a/protocols/workflow.md b/protocols/workflow.md new file mode 100644 index 0000000..a12142f --- /dev/null +++ b/protocols/workflow.md @@ -0,0 +1,45 @@ +# Shared workflow + +Skills simplify an authorized task. User instructions and repository conventions +set the scope; a skill supplies defaults where those leave a gap. + +- Use the existing conversation, files, and project state before asking questions. + Ask only when a missing answer changes the outcome and cannot be inferred safely. +- A report or audit stays read-only. An implementation request authorizes the + necessary reversible edits and checks. Continue authorized work without asking + the user to approve each step. +- Follow the repository's operational boundaries ([agents-template.md](agents-template.md)): + distinguish autonomous actions (ALWAYS), user-approved boundaries (ASK FIRST), + and hard invariants (NEVER). +- Do not create a spec, force a review, run an editing skill, or start a refactor + merely because another skill mentions it. Use another workflow only when its + prerequisites and purpose fit the current task. +- Preserve unrelated changes. Stage and commit only within the authorized scope, + following repository and session instructions. A skill does not require commits. +- Keep references optional: read the relevant section when needed. Do not load + every protocol or checklist on each task. +- Report completion and pull requests using the [telemetry protocol](telemetry.md) + (WHAT-WHY-HOW-LEARNINGS). + +## Verification + +**"The model proposes, but the engine measures."** +Choose checks that can detect a real failure in the changed behavior. + +1. Run the smallest relevant check first: syntax, a focused test, type check, + rendered interaction, or direct output inspection. +2. Broaden to integration or the full suite when shared behavior, security, + migrations, or unresolved failures justify it, or project instructions require it. +3. Do not add tests that mirror implementation or cover only a reversible prose + or cosmetic edit. Add regression coverage for meaningful behavioral bugs when + practical; a reproduction or manual check can be the evidence when automation + is unsuitable. +4. Distinguish new failures from pre-existing failures and missing dependencies. + Report what passed and what remains unverified; do not claim unavailable checks + passed or rewrite unrelated code to make the entire repository green. +5. Once relevant checks pass, stop repeating them unless something changed. +6. When reporting verification in task handoffs or PRs, document the exact terminal + command, exit code, and pass counts under the HOW section of telemetry. + +Complete means the requested behavior is implemented and supported by empirical +evidence. A successful command exit alone does not prove the user-facing outcome. diff --git a/skills/api-design/SKILL.md b/skills/api-design/SKILL.md index 5ba8f1a..0c3aa1d 100644 --- a/skills/api-design/SKILL.md +++ b/skills/api-design/SKILL.md @@ -1,104 +1,26 @@ --- name: api-design description: > - Designs RESTful APIs, typed schema membranes, request/response validation contracts, and API documentation. - Use when creating or modifying API routes, defining request/response schemas, designing error responses, - or standardizing API contracts. -concerns: [security, system-one-balance] + Design or modify API routes, validated request/response schemas, error contracts, and API documentation within the existing project stack. +scope: global --- -# API Design & Membrane Architecture +# API design -Governs API design and boundary membranes, ensuring typed validation, input clamping, predictable HTTP semantics, and consistent structured error contracts. +1. Inspect the existing transport, schema library, auth, and error conventions. + Keep domain logic independent of HTTP/framework request objects where practical. +2. Define inputs, outputs, status codes, authorization, and meaningful payload limits. + Use runtime validation at untrusted boundaries; TypeScript interfaces alone do + not validate requests. Derive limits from product needs and resource budgets. +3. Implement the route using existing services and data access patterns. Reuse + existing dependency injection; do not introduce layers or repositories by default. +4. Preserve HTTP semantics and the project's error shape. Use Problem Details + when adopting that format; otherwise document the existing structured contract. + Serialize timestamps consistently with explicit timezone information. +5. Verify changed contracts and failure behavior under + [shared verification](../../protocols/workflow.md#verification). ---- - -## Phase 0: Setup and Protocols - -1. **Membrane Principles**: - - **Boundary Isolation**: Route handlers serve as the transport membrane. Core business services and data access layers remain transport-agnostic (never import web framework request objects or HTTP status codes into domain logic). - - **Contract-First Validation**: Define typed schemas (e.g. Pydantic, Zod, TypeScript interfaces) before implementing endpoint logic. - - **Dependency Injection**: Inject database sessions, repositories, and authentication state into route handlers; avoid importing global singletons directly. - ---- - -## Phase 1: Ingest and Check - -1. **Identify Endpoint Requirements**: - - Resource name, target path, HTTP method (`GET`, `POST`, `PUT`, `PATCH`, `DELETE`). - - Expected input payload, query parameters, route path variables, and headers. - - Response model, status codes (200, 201, 204), and error failure modes. -2. **Input Boundary Clamping**: - - Enforce explicit upper bounds on all strings (e.g. `min_length=1`, `max_length=200` for names; `max_length=50000` for content). - - Enforce pagination limits: `1 <= limit <= 50` (or `100` max). - - Constrain array inputs: clamp batch endpoints to maximum batch sizes. - ---- - -## Phase 2: Processing (Core Logic) - -### Step 1: Define Typed Contracts -```python -# 1. Request & Response Schemas -class ResourceCreateRequest(BaseModel): - name: str = Field(..., min_length=1, max_length=200) - description: str | None = Field(None, max_length=2000) - -class ResourceResponse(BaseModel): - id: str - name: str - description: str | None = None - created_at: datetime -``` - -### Step 2: Implement Route Handler -```python -# 2. Endpoint Implementation -@router.post("/resources", response_model=ResourceResponse, status_code=201) -async def create_resource( - payload: ResourceCreateRequest, - repo: ResourceRepository = Depends(get_resource_repo), -): - entity = await repo.create(payload.model_dump()) - return ResourceResponse.model_validate(entity) -``` - -### Step 3: Enforce HTTP & Error Semantics -- **HTTP Methods**: - - `POST`: Resource creation / command execution (201 Created or 200 OK). - - `GET`: Idempotent retrieval (200 OK). - - `PUT`: Full resource replacement (200 OK). - - `PATCH`: Partial state update (200 OK). - - `DELETE`: Resource removal (204 No Content). -- **Structured Error Responses**: - - Return standardized error objects (e.g. RFC 7807 problem details or structured error payloads): - ```json - { - "error": { - "code": "RESOURCE_NOT_FOUND", - "message": "Resource with id 'xyz' was not found", - "details": {} - } - } - ``` - - Standard status codes: 400 (Validation), 401 (Unauthenticated), 403 (Unauthorized), 404 (Not Found), 409 (Conflict). -- **Timezone Invariant**: Always serialize timestamps in UTC using ISO-8601 (`YYYY-MM-DDTHH:MM:SSZ`). - ---- - -## Phase 3: The System One Balance Pass - -- **Augment, Don't Handcuff**: - - Do NOT over-complicate simple internal endpoints with bloated enterprise middleware. - - Keep route handlers thin: route logic should unpack requests, call services/repos, and map to response models. - - Fail open on safe internal queries; enforce rigorous clamping at external boundary layers. - ---- - -## Phase 4: Output Execution & Verification - -1. Verify: - - Route docstrings populate OpenAPI / Swagger schemas accurately. - - All response models are strongly typed without bare `dict` or `Any` escapes. - - Input clamping fields prevent memory exhaustion and DoS. -2. Deliver the schema definition, route implementation, and corresponding test case. +Read [security guidance](../app-security/SKILL.md) for affected untrusted boundaries, +not as a mandatory second audit. Validation and authorization failures reject the +request. A dependency fallback requires an explicit behavior contract; never infer +"fail open" merely because an endpoint is internal. diff --git a/skills/app-security/SKILL.md b/skills/app-security/SKILL.md index 2f3b22b..8153cd2 100644 --- a/skills/app-security/SKILL.md +++ b/skills/app-security/SKILL.md @@ -1,98 +1,38 @@ --- name: app-security description: > - Full-stack application and API security hardening: Four-Pillar upload defense, - SSRF prevention, input boundary clamping, IDOR elimination, and secret masking. - Use when creating or auditing API routes, handling uploads, making outbound requests, - or preparing pre-release security verification. -concerns: [security] + Harden affected application boundaries when implementing or auditing authorization, uploads, user-influenced outbound requests, or sensitive data handling. +scope: global --- -# Application Security Hardening +# Application security -Enforces defensive boundaries across the API membrane, eliminating exploit vectors, path traversals, SSRF risks, IDOR vulnerabilities, and payload exhaustion. +Inspect the affected boundary and existing controls. Apply the protections relevant +to the task; a small fix does not require a full application audit. -```mermaid -flowchart LR - A["Inbound Request"] --> B["1. Membrane Clamping
(Schema & Bound Validation)"] - B --> C["2. Four-Pillar Uploads
(Size, Ext, Magic, Path)"] - C --> D["3. Outbound SSRF Guard
(Loopback/Metadata Block)"] - D --> E["4. Secret Masking
(Zero Token Sediment)"] -``` +1. Validate untrusted payloads with runtime schemas and resource bounds derived + from product requirements. Reject invalid data; do not silently truncate credentials. +2. Derive identity from verified server authentication. Enforce tenant/resource + permissions on private reads and writes, preferably within the data operation. + Cover unauthenticated access, cross-tenant access, and relevant races. +3. For uploads, enforce a product-specific extension/type allowlist, stream byte + limits, and appropriate content validation. Magic headers alone do not prove + safety. Use generated storage names and an isolated non-executable root; prevent + traversal and symlink escapes. Serve potentially active content with appropriate + isolation and headers. Bound archive expansion or decoding work when applicable. +4. For user-influenced outbound destinations, allowlist known services where possible. + Parse URLs, restrict schemes, resolve and validate all IPv4/IPv6 addresses, and + prevent DNS changes from bypassing connection checks. Disable redirects or validate + each destination using the same policy. Block private, loopback, link-local, + unspecified, and metadata targets unless a specific trusted internal service is + intentionally allowed. Apply time, response-size, and network egress limits. +5. Redact credentials at logging/telemetry boundaries and keep public errors free + of secrets and internal traces. Apply relevant session, CSRF, rate-limiting, and + browser content controls using existing framework facilities. +6. Verify the changed boundary with meaningful negative cases under + [shared verification](../../protocols/workflow.md#verification). Report actual + protections and gaps; do not claim the whole app is secure from a checklist. ---- - -## Phase 0: Setup and Protocols - -1. Review reference playbooks: - - [references/app-hardening-playbook.md](references/app-hardening-playbook.md) for concrete TypeScript/Python code patterns and boundary clamps. - - [references/review-checklist.md](references/review-checklist.md) for pre-release OWASP verification checks. -2. Invariants: - - Every input payload must be typed and bounded. - - Every state mutation must verify session identity and tenant ownership. - - Outbound HTTP fetches must never touch private/loopback networks. - ---- - -## Phase 1: Ingest and Check - -1. **Surface Identification**: - - Inbound API routes (REST, GraphQL, RPC handlers). - - File/document ingestion and attachment endpoints. - - Outbound network calls (webhooks, URL unfurlers, external data scrapers). - - Data persistence operations and logging sinks. -2. **Access Control Check**: - - Confirm identity is derived from verified server sessions/tokens, never trusted client parameters (`userId`, `orgId`). - - Confirm entity queries enforce ownership: verify record exists AND belongs to the authenticated caller before mutating. - ---- - -## Phase 2: Processing (Core Logic) - -### Step 1: Input Boundaries & Membrane Clamping -- Never accept arbitrary, unvalidated request dictionaries. Model all payloads using strict schemas (e.g. Pydantic, Zod). -- Enforce hard string limits: - - Text prompts / message content: Max 50,000 characters. - - Titles / Names: Max 100–200 characters. - - Pagination limits: Clamped to `1 <= limit <= 50`. - - Batch arrays: Clamped to max 100–200 items. -- Sanitize entity and path identifiers against path traversal tokens (`../`, `..\\`). - -### Step 2: Four-Pillar Upload Defense -When handling file or media uploads: -1. **Size Bounds**: Reject payloads exceeding size thresholds (e.g., 100MB for docs; 5MB for images). -2. **Blocked Extensions**: Reject executable and script formats (`.exe`, `.bat`, `.sh`, `.svg`, `.html`, `.php`). -3. **Magic Byte Inspection**: Inspect file headers to detect disguised binaries (PE `MZ`, ELF, Mach-O). -4. **Path Traversal Defense**: - - Sanitize original filenames (strip path separators and non-printable characters). - - Write files strictly to designated, isolated upload roots with unique IDs (e.g. UUID filenames). - -### Step 3: Outbound SSRF Guard -Before executing outbound HTTP requests (scraping, webhooks): -- Validate target URLs against an allowlist or safety filter. -- Block loopback addresses (`127.0.0.1`, `localhost`, `::1`), RFC 1918 private subnets (`10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`), and cloud metadata services (`169.254.169.254`). -- Restrict allowed protocols strictly to `http` and `https`. - -### Step 4: Secret Masking & Error Shielding -- Scrub Authorization tokens (`Bearer ***`), provider API keys, and sensitive credentials from logs, database traces, and telemetry. -- In public HTTP error responses, return clean structured error objects with opaque error codes; never leak internal filesystem paths, environment variables, or stack traces. - ---- - -## Phase 3: The System One Balance Pass - -- **Augment, Don't Handcuff**: - - Do NOT break local development workflows: allow local mock endpoints and fixture testing in non-production test environments. - - Keep security checks non-intrusive during prototyping; enforce strictly at boundaries and pre-release gates. - - Avoid noisy confirmation warnings for routine safe operations. - ---- - -## Phase 4: Output Execution & Verification - -1. Run verification checks from [references/review-checklist.md](references/review-checklist.md). -2. Provide a security verification summary detailing: - - Bound clamps applied to route schemas. - - Upload filters active (size, extension, magic bytes, safe path). - - SSRF protection status on outbound calls. - - Secret scrubbing confirmation. +Use [the playbook](references/app-hardening-playbook.md) for implementation choices +and [the checklist](references/review-checklist.md) for an explicitly requested audit. +Local fixtures need deliberate configuration, not an automatic security bypass. diff --git a/skills/app-security/references/app-hardening-playbook.md b/skills/app-security/references/app-hardening-playbook.md index 0aebad8..2c6e788 100644 --- a/skills/app-security/references/app-hardening-playbook.md +++ b/skills/app-security/references/app-hardening-playbook.md @@ -1,160 +1,50 @@ -# Full-Stack Application Security Hardening Playbook +# Boundary hardening choices -This high-level guide provides a reusable, standardized protocol for auditing and securing full-stack applications (Next.js, Node.js, Express, etc.). +Read the relevant section when implementing that boundary. Values and tools come +from the product's threat model, stack, and resource budgets, not this document. ---- +## Identity and authorization -## Phase 1: Endpoint & Access Control (Preventing IDOR) +Authenticate on the server. Bind private reads and writes to the caller's allowed +resources and tenant. Prefer an ownership predicate in the data operation; a +separate read-check-write sequence can race. Verify cookie-authenticated mutations +have appropriate CSRF/origin protections and session expiry behavior. -### 1. The Multi-Tenant Ownership Rule -Every state-changing API request (`POST`, `PUT`, `PATCH`, `DELETE`) and private resource read (`GET`) must establish identity via the server session—never by trusting client parameters. +## Payloads and credentials -#### Never Trust Client User IDs: -```typescript -// ❌ Vulnerable (attacker can pass any userId in body) -const { title, userId } = await req.json(); -await db.resource.create({ data: { title, userId } }); +Validate runtime shapes and bound request bytes, field lengths, array counts, and +expensive processing. Reject invalid credentials without silent truncation. Hash +passwords with the framework's supported algorithm and settings; bcrypt's 72-byte +input limitation is bytes, not characters. Use the selected algorithm's documented +limits and preserve long-password support when choosing the design. -// ✅ Secure (always bind to verified session) -const session = await getServerSession(authOptions); -if (!session?.user?.id) return unauthorized(); -await db.resource.create({ data: { title, userId: session.user.id } }); -``` +## Uploads -#### Verify Resource Ownership Before Mutation: -```typescript -// Verify target record exists AND belongs to the caller -const item = await db.resource.findUnique({ where: { id } }); -if (!item) return notFound(); -if (item.userId !== session.user.id) return forbidden(); -``` +Choose an explicit extension/content allowlist and byte cap per supported format. +Stop streaming at the cap. Inspect content with a suitable parser; signatures alone +cannot validate the whole file. Generate storage IDs; keep the storage root isolated +and non-executable, prevent traversal/symlink escapes, and bound archive expansion. +Serve active content in an appropriate isolated origin or as an attachment. Apply +malware scanning or re-encoding when the threat model warrants it. -#### Lock Down Sensitive Data Endpoints: -* Strip internal data (emails, password hashes, roles, permissions) from public query endpoints. -* Never allow query flags like `?includePrivate=true` unless the caller has verified ownership or administrative authorization for that account. +## User-influenced outbound requests ---- +Prefer service allowlists. Resolve all IPv4/IPv6 addresses and validate the actual +connection destination, including DNS changes. Disable redirects or repeat checks +for each hop. Bound time, bytes, and egress. Trusted internal service calls use their +own allowlist rather than a blanket private-network ban. +See [OWASP SSRF guidance](https://cheatsheetseries.owasp.org/cheatsheets/Server_Side_Request_Forgery_Prevention_Cheat_Sheet.html). -## Phase 2: Input Boundaries & Payload Sanitization +## Browser and abuse controls -### 1. Enforce Hard Limits on Every Field -Unbounded input strings can cause database exhaustion, buffer overruns, or CPU denial-of-service (e.g., Bcrypt hashing huge strings). +Use CSP, framing rules, safe content types, CSRF protection, and rate limiting for +the relevant flow. Configure limits by actor, cost, and deployment topology. Apply +HSTS only to intended HTTPS domains; subdomains/preload require deliberate coverage. +Do not disable required browser capabilities through a blanket Permissions-Policy. -| Field Type | Recommended Hard Bound | Rationale | -| :--- | :--- | :--- | -| **Passwords** | Min 8 chars, Max 72–100 chars | Bcrypt truncates or burns excessive CPU beyond 72 bytes | -| **Emails** | Max 254 chars | Standard RFC 5321 length; prevents regex DoS | -| **Titles / Names** | Max 100–200 chars | Prevents UI overflow and DB memory bloat | -| **Descriptions / Content** | Max 2,000–50,000 chars | Prevents payload bombing | -| **Pagination limit** | Clamped to `1 <= limit <= 50` | Prevents querying the entire table at once | +## Verification -### 2. JSON & Batch Array Clamping -When accepting array batches (e.g., bulk updating nodes or items), enforce an explicit upper limit (e.g., max 200–500 items per request). - ---- - -## Phase 3: Safe File & Document Upload Architecture - -If an application accepts images, PDFs, or documents, implement the **Four-Pillar Upload Defense**: - -```mermaid -flowchart LR - A["File Upload"] --> B["1. Size Check"] - B --> C["2. Extension & MIME Allowlist"] - C --> D["3. Magic Bytes Inspection"] - D --> E["4. Filename Sanitization & Storage"] -``` - -### 1. Size Caps: -* Check file size before buffering: images (max 2–5MB), documents (max 10MB). -* Terminate stream early if incoming chunks exceed the configured byte threshold. - -### 2. Strict MIME & Extension Allowlists: -* **Allowed:** `image/jpeg` (`.jpg`, `.jpeg`), `image/png` (`.png`), `image/webp` (`.webp`), `application/pdf` (`.pdf`), `application/json` (`.json`), `text/markdown` (`.md`). -* **Strictly Blocked:** `.exe`, `.bat`, `.sh`, `.php`, `.js`, `.html`, `.svg` (SVGs can embed `