Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
48c31db
docs: update spec
Pertempto Oct 25, 2025
cee0006
docs: revise AGENTS.md
Pertempto Oct 25, 2025
0a2ae4d
docs: revise start-pr
Pertempto Oct 25, 2025
be4befd
docs: revisions
Pertempto Oct 25, 2025
fa5f0ce
docs(18): add OpenAPI spec and validation contract test
Pertempto Oct 25, 2025
00db4ae
test(18): locate specs file more robustly in contract test
Pertempto Oct 25, 2025
6050bd8
chore: add changelog entry for PR #67
Pertempto Oct 25, 2025
9e9b19b
chore: clean up
Pertempto Oct 25, 2025
54c75db
docs: revise do-pr
Pertempto Oct 25, 2025
4ea12af
feat: validate openapi spec
Pertempto Oct 25, 2025
0738dc5
feat: validate openapi spec
Pertempto Oct 25, 2025
314e585
docs: revise
Pertempto Oct 25, 2025
5fda1b3
feat: verify open API spec
Pertempto Oct 25, 2025
3c0341a
fix: revise
Pertempto Oct 25, 2025
7aeab08
fix: jobs
Pertempto Oct 25, 2025
37dd7a1
fix: revert incorrect changes
Pertempto Oct 25, 2025
93e141d
fix: revert incorrect changes
Pertempto Oct 25, 2025
4a30f23
fix: security specifiers
Pertempto Oct 25, 2025
42f314d
docs(spec): document setup token format and enforce length/pattern
Pertempto Oct 25, 2025
53a7790
docs(spec): document API key format and add pattern constraints
Pertempto Oct 25, 2025
c1cd929
test(contract): ensure server is started, wait for readiness, and rel…
Pertempto Oct 25, 2025
2005dbc
test(contract): ensure server process group is killed after tests com…
Pertempto Oct 25, 2025
4c02791
test(contract): simplify verify_openapi script; keep robust cleanup a…
Pertempto Oct 25, 2025
4151ea0
test(contract): remove setsid usage; run server in background and cle…
Pertempto Oct 25, 2025
3158310
docs(spec): add /api/v1 servers base URL to openapi spec
Pertempto Oct 25, 2025
06e134d
docs(spec): prefix all paths with /api/v1 instead of using servers entry
Pertempto Oct 25, 2025
4e8d767
docs(spec): require non-empty user for GenerateTokenRequest (minLengt…
Pertempto Oct 25, 2025
2fea2ed
fix: clean up
Pertempto Oct 25, 2025
782ee81
fix: revisions
Pertempto Oct 25, 2025
b34a5b3
feat: got spec to pass
Pertempto Oct 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,34 @@ on:
branches: [ main ]

jobs:
validate-openapi:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '18'

- name: Run OpenAPI lint
run: bash tests/contract/validate_openapi.sh

verify-openapi:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install uv
uses: astral-sh/setup-uv@v7
with:
enable-cache: true

- name: Verify OpenAPI spec
run: bash tests/contract/verify_openapi.sh

test:
runs-on: ubuntu-latest

Expand Down Expand Up @@ -41,3 +69,4 @@ jobs:

- name: Run performance tests
run: go test ./tests/performance

3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,6 @@ src/src

# Data directory
data/

# Schemathesis
.hypothesis/
6 changes: 5 additions & 1 deletion .opencode/command/do-pr.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ agent: build

IMPORTANT: This command must run the full non-interactive flow for creating a PR. That means it MUST run the test suite(s), commit any changes, push the branch, create the GitHub pull request, update `CHANGELOG.md` with the PR number, and push the changelog — all without asking the user for additional input.

If the user has NOT previously run the `/start-pr` command, prompt them for the issue number to work on.
The user gave the input: "$ARGUMENTS"

Use the user input as the issue number.

If the user input is empty or invalid, use the previously entered issue number from `/start-pr` (but if `/start-pr` was not previously ran, prompt the user for the issue number).

Required behavior (non-interactive flow)

Expand Down
8 changes: 4 additions & 4 deletions .opencode/command/start-pr.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ If the user input is empty or invalid, prompt the user for the issue number.
Required behavior and confirmation flow

1. Read the spec for the given issue in `specs/` and determine the next incomplete section from the Task List.
2. Branch creation rules:
- Create a new branch only when the current branch name does **not** already match the desired `{issue-number}-{section-name}` for the section.
- If the current branch already matches the section, do not create or switch branches.
- If the user explicitly requests to stay on the current branch, do not create a branch.
2. Branch creation rules (agents MUST NOT ask the user about branch behavior):
- Compute branch as `{issue-number}-{slug(section-header)}` where `slug()` lowercases the header, replaces any non‑alphanumeric sequence with `-`, collapses duplicate `-`, and trims leading/trailing `-`.
- If current branch equals OR is very similar to the computed name, do nothing; otherwise create and switch with `git checkout -b "<branch>"`.
- If creating/switching would overwrite uncommitted work, warn and request confirmation.
3. Research the codebase to gather information about the change.
4. Ask the user clarifying questions.
- Clearly number the questions.
Expand Down
7 changes: 7 additions & 0 deletions .redocly.lint-ignore.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# This file instructs Redocly's linter to ignore the rules contained for specific parts of your API.
# See https://redocly.com/docs/cli/ for more information.
specs/openapi.yaml:
no-empty-servers:
- '#/openapi'
operation-4xx-response:
- '#/paths/~1health/get/responses'
31 changes: 9 additions & 22 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -110,24 +110,8 @@ See `specs/7-data-persistence.md` for a well-structured specification that:

### Shell quoting when using backticks

- **Problem:** Unescaped backticks in bash commands are interpreted as command substitution, causing the shell to execute the content between backticks instead of passing it as literal text (this can break `gh` calls or insert unintended output).
- **Rule:** When passing text that contains backticks to shell commands, avoid unescaped backticks. Prefer one of these safe patterns:
- Single-quoted argument (simple cases):
- `gh pr edit 56 --body 'Tables: `users`, `events`'`
- Escape backticks inside double quotes:
- `gh pr edit 56 --body "Tables: \`users\`, \`events\`"`
- HEREDOC with single-quoted delimiter (recommended for multi-line bodies or complex content):
- `gh pr edit 56 --body "$(cat <<'EOF'\nTables: `users`, `events`\nEOF\n)"`

- **Examples:**
- Bad: `gh pr edit 56 --body "Tables: `users`, `events`"` (backticks executed by shell)
- Good (HEREDOC): `gh pr edit 56 --body "$(cat <<'EOF'\nTables: `users`, `events`\nEOF\n)"`
- Good (single quotes): `gh pr edit 56 --body 'Tables: `users`, `events`'`
- Good (escaped): `gh pr edit 56 --body "Tables: \`users\`, \`events\`"`

- **Recommendation:** Prefer the HEREDOC pattern when generating multi-line PR bodies that include code formatting or backticks. It avoids shell expansion and is easy to read and maintain.

- Commit messages follow conventional format: `feat:`, `refactor:`, `chore:`, `fix:`, etc.
- Unescaped backticks in bash commands are interpreted as command substitution, causing the shell to execute the content between backticks instead of passing it as literal text (this can break `gh` calls or insert unintended output).
- Prefer the HEREDOC pattern when generating multi-line PR bodies that include code formatting or backticks. It avoids shell expansion and is easy to read and maintain.

### Grep / Ripgrep Patterns

Expand All @@ -137,15 +121,18 @@ See `specs/7-data-persistence.md` for a well-structured specification that:
- Use fixed-string mode for literals: `rg -F 'AddUser('` or `rg --fixed-strings 'AddUser('
- Escape regex metacharacters: `rg 'AddUser\('` (escape `(` with `\` in single-quoted shell strings)
- Search the identifier only (no parens): `rg 'AddUser'`
- Prefer single quotes around patterns to avoid shell interpolation: `rg 'GetUserById\('`
- When using the assistant `functions.grep` tool, pass a syntactically valid regex (escape metacharacters) or a simple identifier-only pattern.
- Prefer single quotes around patterns to avoid shell interpolation: `rg 'GetUserById\('
- When using the `grep` tool, pass a syntactically valid regex (escape metacharacters) or a simple identifier-only pattern.
- **Examples:**
- Bad: `rg "AddUser("` → causes ripgrep regex parse error (unclosed group)
- Good (escape): `rg 'AddUser\('`
- Good (fixed-string): `rg -F 'AddUser('
- Good (fixed-string): `rg -F 'AddUser('`
- Good (identifier only): `rg 'AddUser'`

- **Recommendation:** When programmatically constructing search patterns, either validate the regex before use or default to fixed-string searches. If you are unsure whether a pattern contains regex metacharacters, use `-F` to avoid surprises.
- **Directory scope rule:** When running `rg`, `grep`, or other repository search tools, you MUST specify a relative subdirectory or a specific file path (for example `src/` or `tests/unit/`). You MUST NOT specify a full absolute filesystem path. This prevents searches from scanning unwanted or large directories such as `.git/`, `node_modules/`, or the user's home directory which can produce noisy, slow, or sensitive results.
- **Usage notes:**
- Good: `rg 'AddUser' src/` or `rg -F 'TODO' tests/unit/` or using the assistant `functions.grep` with `path: 'src/'`.
- Bad: `rg 'AddUser' /home/user/repos/kwila/simple-sync` or calling `functions.grep` with `path: '/home/user/...'` (do not use absolute paths).

### PR Title & Description Rules
- **Always inspect the full diff for the branch before creating a PR.** Use Git to view changes against the base branch and confirm the final, combined diff that will become the PR.
Expand Down
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Release History

## [0.4.0] - 2025-10-25
- [#67](https://github.com/kwila-cloud/simple-sync/pull/67): Add OpenAPI spec and validation contract test
- [#62](https://github.com/kwila-cloud/simple-sync/pull/62): Add storage documentation
- [#61](https://github.com/kwila-cloud/simple-sync/pull/61): Add performance and concurrency tests for SQLite storage
- [#60](https://github.com/kwila-cloud/simple-sync/pull/60): Implement SQLite setup token and API key storage
Expand Down
45 changes: 27 additions & 18 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,37 +3,46 @@ module simple-sync
go 1.25

require (
github.com/gin-gonic/gin v1.9.1
github.com/gin-gonic/gin v1.11.0
github.com/google/uuid v1.6.0
github.com/stretchr/testify v1.8.4
golang.org/x/crypto v0.42.0
github.com/mattn/go-sqlite3 v1.14.15
github.com/stretchr/testify v1.11.1
golang.org/x/crypto v0.43.0
)

require (
github.com/bytedance/sonic v1.9.1 // indirect
github.com/bytedance/sonic v1.14.0 // indirect
github.com/bytedance/sonic/loader v0.3.0 // indirect
github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 // indirect
github.com/cloudwego/base64x v0.1.6 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/gabriel-vasile/mimetype v1.4.2 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/gabriel-vasile/mimetype v1.4.8 // indirect
github.com/gin-contrib/sse v1.1.0 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.14.0 // indirect
github.com/go-playground/validator/v10 v10.27.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
github.com/goccy/go-yaml v1.18.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/klauspost/cpuid/v2 v2.2.4 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/mattn/go-sqlite3 v1.14.15 // indirect
github.com/klauspost/cpuid/v2 v2.3.0 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/pelletier/go-toml/v2 v2.0.8 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/quic-go/qpack v0.5.1 // indirect
github.com/quic-go/quic-go v0.54.0 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.2.11 // indirect
golang.org/x/arch v0.3.0 // indirect
golang.org/x/net v0.43.0 // indirect
golang.org/x/sys v0.36.0 // indirect
golang.org/x/text v0.29.0 // indirect
google.golang.org/protobuf v1.30.0 // indirect
github.com/ugorji/go/codec v1.3.0 // indirect
go.uber.org/mock v0.5.0 // indirect
golang.org/x/arch v0.20.0 // indirect
golang.org/x/mod v0.28.0 // indirect
golang.org/x/net v0.45.0 // indirect
golang.org/x/sync v0.17.0 // indirect
golang.org/x/sys v0.37.0 // indirect
golang.org/x/text v0.30.0 // indirect
golang.org/x/tools v0.37.0 // indirect
google.golang.org/protobuf v1.36.9 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
Loading
Loading