Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
bd505cb
feat(infra): initialize monorepo structure, uv workspace, Next.js, CI
fray-cloud Mar 18, 2026
cab06eb
feat(infra): add Docker Compose dev/prod environment with Makefile
fray-cloud Mar 18, 2026
d283ce1
feat(shared): add common domain building blocks
fray-cloud Mar 18, 2026
90e659a
feat(shared): add CQRS framework with Command/Query Bus
fray-cloud Mar 18, 2026
42128fd
feat(shared): add Event Sourcing framework with PostgreSQL store
fray-cloud Mar 18, 2026
0e07a32
feat(shared): add Kafka messaging with JSON serialization
fray-cloud Mar 18, 2026
5e0848f
feat(shared): add Custom Fields, Tags, and domain model infrastructure
fray-cloud Mar 18, 2026
5d93277
feat(shared): add API common module with middleware and utilities
fray-cloud Mar 18, 2026
26120ce
feat(tenant): add Tenant domain model with value objects and events
fray-cloud Mar 18, 2026
c8ee95a
feat(tenant): add CQRS application layer with commands and queries
fray-cloud Mar 18, 2026
7aab567
feat(tenant): add infrastructure with DB provisioning and Alembic
fray-cloud Mar 18, 2026
5173c52
feat(tenant): add REST API with FastAPI router and dependency injection
fray-cloud Mar 18, 2026
62b206c
chore: update uv.lock for tenant service dependencies
fray-cloud Mar 18, 2026
cf5912d
feat(auth): implement Auth Service with JWT RS256, RBAC, and API tokens
fray-cloud Mar 19, 2026
7573926
feat(gateway): implement Nginx API Gateway with JWT validation, CORS,…
fray-cloud Mar 19, 2026
06ff0da
feat(event): implement Event Service with central Event Store and Cha…
fray-cloud Mar 19, 2026
429b424
feat(ipam): implement IPAM domain model with AggregateRoot event sour…
fray-cloud Mar 19, 2026
b2bec9b
feat(ipam): add new aggregates, custom fields/tags support, and share…
fray-cloud Mar 19, 2026
1e24b7b
feat(ipam): add CQRS commands and command handlers with Event Sourcing
fray-cloud Mar 19, 2026
b6b744a
feat(ipam): add CQRS queries, query handlers, and DTOs
fray-cloud Mar 19, 2026
7e5781b
feat(ipam): add Bulk operations, infrastructure, and REST API interface
fray-cloud Mar 19, 2026
6f6615b
chore: CLAUDE settings update
fray-cloud Mar 21, 2026
0bdc6b5
feat(shared/ipam): add snapshot-aware load_aggregate() and auto-snaps…
fray-cloud Mar 21, 2026
b092629
feat(ipam): add async Event Projector for Kafka-based read model proj…
fray-cloud Mar 21, 2026
759a6de
feat(ipam): add Redis cache layer for prefix utilization
fray-cloud Mar 21, 2026
6d22bbc
feat(ipam): add cross-service consumer skeleton for Phase 2
fray-cloud Mar 21, 2026
a601b90
feat(ipam): add RouteTarget, VLANGroup, Service domain aggregates
fray-cloud Mar 21, 2026
60f3e18
feat(ipam): add import/export route targets to VRF aggregate
fray-cloud Mar 21, 2026
20314ae
feat(ipam): add IPRange utilization domain service and query handler
fray-cloud Mar 21, 2026
c5db0da
feat(ipam): add application + infrastructure layers for RouteTarget, …
fray-cloud Mar 21, 2026
8e4ca4e
feat(ipam): add REST routers for RouteTarget, VLANGroup, Service + IP…
fray-cloud Mar 21, 2026
288816f
feat(ipam): add bulk update and bulk delete endpoints for all 11 aggr…
fray-cloud Mar 21, 2026
4192305
feat(ipam): add Strawberry GraphQL API with 11 types and nested queries
fray-cloud Mar 21, 2026
a7b0c9d
feat(ipam): add OpenAPI customization with tag descriptions
fray-cloud Mar 21, 2026
bf3b948
feat(webhook): add domain models, infrastructure foundation, and data…
fray-cloud Mar 21, 2026
4c24551
feat(webhook): add CQRS application layer, repositories, and webhook …
fray-cloud Mar 21, 2026
9d5bc7a
feat(webhook): add delivery service, retry manager, dispatcher, and K…
fray-cloud Mar 21, 2026
afb154e
feat(webhook): add REST API router and FastAPI lifespan integration
fray-cloud Mar 21, 2026
aa0e90e
feat(webhook): add comprehensive tests for domain and infrastructure
fray-cloud Mar 21, 2026
c9813e4
chore: fix import ordering and update uv.lock
fray-cloud Mar 21, 2026
1a532e8
feat(ipam): add search, filtering, and saved filters (#14)
fray-cloud Mar 22, 2026
d8ce7fb
feat(event): add journal entries for per-object notes (#15)
fray-cloud Mar 22, 2026
eb0e3cd
feat(ipam): add data import/export with Jinja2 templates (#16)
fray-cloud Mar 22, 2026
83b62d1
feat(frontend): restructure as monorepo with shared package (#17)
fray-cloud Mar 22, 2026
00db5d0
feat(frontend): add shadcn/ui, layouts, auth pages, and dashboard (#17)
fray-cloud Mar 22, 2026
884847b
feat(frontend): add IPAM CRUD pages for all entities (#17)
fray-cloud Mar 22, 2026
4b30f47
feat(frontend): add search, changelog, journal, and dashboard (#17)
fray-cloud Mar 22, 2026
9916e8b
test: add comprehensive integration tests across all services (#18)
fray-cloud Mar 22, 2026
de8871d
fix: update dev environment for monorepo frontend and missing deps
fray-cloud Mar 22, 2026
7aad137
fix: consolidate docker-compose and fix dev environment (#17)
fray-cloud Mar 22, 2026
44871c2
feat: add dev environment init system and setup wizard
fray-cloud Mar 22, 2026
0eafc33
fix: fix dev-init migration system
fray-cloud Mar 22, 2026
30aa944
fix: fix API base URLs for dev environment
fray-cloud Mar 22, 2026
c02b10a
fix: fix dev-init migration system
fray-cloud Mar 22, 2026
9b26c39
fix: add tenant selector to login page
fray-cloud Mar 22, 2026
02b9d60
refactor: route all API calls through Nginx gateway
fray-cloud Mar 22, 2026
f77abe0
fix: use relative API paths for same-origin requests via Nginx
fray-cloud Mar 22, 2026
a4bb51f
fix: fix Nginx routing and IPAM connection pool
fray-cloud Mar 22, 2026
c54e569
fix: fix IPAM prefix CRUD session management
fray-cloud Mar 22, 2026
1e2a53b
fix: fix all IPAM CRUD — session management and nullable fields
fray-cloud Mar 22, 2026
0b620a5
Merge feat/infra into dev — P1 Foundation + IPAM complete
fray-cloud Mar 22, 2026
6843ca4
fix: fix CI lint errors
fray-cloud Mar 22, 2026
cee72e8
fix: fix all Python import ordering (ruff I001)
fray-cloud Mar 22, 2026
482ba8a
fix: fix CI pipeline — tests, frontend lint, workflow
fray-cloud Mar 22, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
94 changes: 94 additions & 0 deletions .agents/skills/design-an-interface/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
---
name: design-an-interface
description: Generate multiple radically different interface designs for a module using parallel sub-agents. Use when user wants to design an API, explore interface options, compare module shapes, or mentions "design it twice".
---

# Design an Interface

Based on "Design It Twice" from "A Philosophy of Software Design": your first idea is unlikely to be the best. Generate multiple radically different designs, then compare.

## Workflow

### 1. Gather Requirements

Before designing, understand:

- [ ] What problem does this module solve?
- [ ] Who are the callers? (other modules, external users, tests)
- [ ] What are the key operations?
- [ ] Any constraints? (performance, compatibility, existing patterns)
- [ ] What should be hidden inside vs exposed?

Ask: "What does this module need to do? Who will use it?"

### 2. Generate Designs (Parallel Sub-Agents)

Spawn 3+ sub-agents simultaneously using Task tool. Each must produce a **radically different** approach.

```
Prompt template for each sub-agent:

Design an interface for: [module description]

Requirements: [gathered requirements]

Constraints for this design: [assign a different constraint to each agent]
- Agent 1: "Minimize method count - aim for 1-3 methods max"
- Agent 2: "Maximize flexibility - support many use cases"
- Agent 3: "Optimize for the most common case"
- Agent 4: "Take inspiration from [specific paradigm/library]"

Output format:
1. Interface signature (types/methods)
2. Usage example (how caller uses it)
3. What this design hides internally
4. Trade-offs of this approach
```

### 3. Present Designs

Show each design with:

1. **Interface signature** - types, methods, params
2. **Usage examples** - how callers actually use it in practice
3. **What it hides** - complexity kept internal

Present designs sequentially so user can absorb each approach before comparison.

### 4. Compare Designs

After showing all designs, compare them on:

- **Interface simplicity**: fewer methods, simpler params
- **General-purpose vs specialized**: flexibility vs focus
- **Implementation efficiency**: does shape allow efficient internals?
- **Depth**: small interface hiding significant complexity (good) vs large interface with thin implementation (bad)
- **Ease of correct use** vs **ease of misuse**

Discuss trade-offs in prose, not tables. Highlight where designs diverge most.

### 5. Synthesize

Often the best design combines insights from multiple options. Ask:

- "Which design best fits your primary use case?"
- "Any elements from other designs worth incorporating?"

## Evaluation Criteria

From "A Philosophy of Software Design":

**Interface simplicity**: Fewer methods, simpler params = easier to learn and use correctly.

**General-purpose**: Can handle future use cases without changes. But beware over-generalization.

**Implementation efficiency**: Does interface shape allow efficient implementation? Or force awkward internals?

**Depth**: Small interface hiding significant complexity = deep module (good). Large interface with thin implementation = shallow module (avoid).

## Anti-Patterns

- Don't let sub-agents produce similar designs - enforce radical difference
- Don't skip comparison - the value is in contrast
- Don't implement - this is purely about interface shape
- Don't evaluate based on implementation effort
14 changes: 14 additions & 0 deletions .agents/skills/edit-article/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
name: edit-article
description: Edit and improve articles by restructuring sections, improving clarity, and tightening prose. Use when user wants to edit, revise, or improve an article draft.
---

1. First, divide the article into sections based on its headings. Think about the main points you want to make during those sections.

Consider that information is a directed acyclic graph, and that pieces of information can depend on other pieces of information. Make sure that the order of the sections and their contents respects these dependencies.

Confirm the sections with the user.

2. For each section:

2a. Rewrite the section to improve clarity, coherence, and flow. Use maximum 240 characters per paragraph.
95 changes: 95 additions & 0 deletions .agents/skills/git-guardrails-claude-code/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
name: git-guardrails-claude-code
description: Set up Claude Code hooks to block dangerous git commands (push, reset --hard, clean, branch -D, etc.) before they execute. Use when user wants to prevent destructive git operations, add git safety hooks, or block git push/reset in Claude Code.
---

# Setup Git Guardrails

Sets up a PreToolUse hook that intercepts and blocks dangerous git commands before Claude executes them.

## What Gets Blocked

- `git push` (all variants including `--force`)
- `git reset --hard`
- `git clean -f` / `git clean -fd`
- `git branch -D`
- `git checkout .` / `git restore .`

When blocked, Claude sees a message telling it that it does not have authority to access these commands.

## Steps

### 1. Ask scope

Ask the user: install for **this project only** (`.claude/settings.json`) or **all projects** (`~/.claude/settings.json`)?

### 2. Copy the hook script

The bundled script is at: [scripts/block-dangerous-git.sh](scripts/block-dangerous-git.sh)

Copy it to the target location based on scope:

- **Project**: `.claude/hooks/block-dangerous-git.sh`
- **Global**: `~/.claude/hooks/block-dangerous-git.sh`

Make it executable with `chmod +x`.

### 3. Add hook to settings

Add to the appropriate settings file:

**Project** (`.claude/settings.json`):

```json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/block-dangerous-git.sh"
}
]
}
]
}
}
```

**Global** (`~/.claude/settings.json`):

```json
{
"hooks": {
"PreToolUse": [
{
"matcher": "Bash",
"hooks": [
{
"type": "command",
"command": "~/.claude/hooks/block-dangerous-git.sh"
}
]
}
]
}
}
```

If the settings file already exists, merge the hook into existing `hooks.PreToolUse` array — don't overwrite other settings.

### 4. Ask about customization

Ask if user wants to add or remove any patterns from the blocked list. Edit the copied script accordingly.

### 5. Verify

Run a quick test:

```bash
echo '{"tool_input":{"command":"git push origin main"}}' | <path-to-script>
```

Should exit with code 2 and print a BLOCKED message to stderr.
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
#!/bin/bash

INPUT=$(cat)
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command')

DANGEROUS_PATTERNS=(
"git push"
"git reset --hard"
"git clean -fd"
"git clean -f"
"git branch -D"
"git checkout \."
"git restore \."
"push --force"
"reset --hard"
)

for pattern in "${DANGEROUS_PATTERNS[@]}"; do
if echo "$COMMAND" | grep -qE "$pattern"; then
echo "BLOCKED: '$COMMAND' matches dangerous pattern '$pattern'. The user has prevented you from doing this." >&2
exit 2
fi
done

exit 0
2 changes: 1 addition & 1 deletion .agents/skills/grill-me/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@ name: grill-me
description: Interview the user relentlessly about a plan or design until reaching shared understanding, resolving each branch of the decision tree. Use when user wants to stress-test a plan, get grilled on their design, or mentions "grill me".
---

Interview me relentlessly about every aspect of this plan until we reach a shared understanding. Walk down each branch of the design tree, resolving dependencies between decisions one-by-one.
Interview me relentlessly about every aspect of this plan until we reach a shared understanding. Walk down each branch of the design tree, resolving dependencies between decisions one-by-one. For each question, provide your recommended answer.

If a question can be answered by exploring the codebase, explore the codebase instead.
118 changes: 118 additions & 0 deletions .agents/skills/migrate-to-shoehorn/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
---
name: migrate-to-shoehorn
description: Migrate test files from `as` type assertions to @total-typescript/shoehorn. Use when user mentions shoehorn, wants to replace `as` in tests, or needs partial test data.
---

# Migrate to Shoehorn

## Why shoehorn?

`shoehorn` lets you pass partial data in tests while keeping TypeScript happy. It replaces `as` assertions with type-safe alternatives.

**Test code only.** Never use shoehorn in production code.

Problems with `as` in tests:

- Trained not to use it
- Must manually specify target type
- Double-as (`as unknown as Type`) for intentionally wrong data

## Install

```bash
npm i @total-typescript/shoehorn
```

## Migration patterns

### Large objects with few needed properties

Before:

```ts
type Request = {
body: { id: string };
headers: Record<string, string>;
cookies: Record<string, string>;
// ...20 more properties
};

it("gets user by id", () => {
// Only care about body.id but must fake entire Request
getUser({
body: { id: "123" },
headers: {},
cookies: {},
// ...fake all 20 properties
});
});
```

After:

```ts
import { fromPartial } from "@total-typescript/shoehorn";

it("gets user by id", () => {
getUser(
fromPartial({
body: { id: "123" },
}),
);
});
```

### `as Type` → `fromPartial()`

Before:

```ts
getUser({ body: { id: "123" } } as Request);
```

After:

```ts
import { fromPartial } from "@total-typescript/shoehorn";

getUser(fromPartial({ body: { id: "123" } }));
```

### `as unknown as Type` → `fromAny()`

Before:

```ts
getUser({ body: { id: 123 } } as unknown as Request); // wrong type on purpose
```

After:

```ts
import { fromAny } from "@total-typescript/shoehorn";

getUser(fromAny({ body: { id: 123 } }));
```

## When to use each

| Function | Use case |
| --------------- | -------------------------------------------------- |
| `fromPartial()` | Pass partial data that still type-checks |
| `fromAny()` | Pass intentionally wrong data (keeps autocomplete) |
| `fromExact()` | Force full object (swap with fromPartial later) |

## Workflow

1. **Gather requirements** - ask user:
- What test files have `as` assertions causing problems?
- Are they dealing with large objects where only some properties matter?
- Do they need to pass intentionally wrong data for error testing?

2. **Install and migrate**:
- [ ] Install: `npm i @total-typescript/shoehorn`
- [ ] Find test files with `as` assertions: `grep -r " as [A-Z]" --include="*.test.ts" --include="*.spec.ts"`
- [ ] Replace `as Type` with `fromPartial()`
- [ ] Replace `as unknown as Type` with `fromAny()`
- [ ] Add imports from `@total-typescript/shoehorn`
- [ ] Run type check to verify
Loading
Loading