Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
98 changes: 98 additions & 0 deletions .claude/rules/change-management.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Change Management Rule (ISO 27001)

All code and content changes must follow a traceable workflow that an auditor can verify: **issue →
branch → PR → merge → verify**.

## Standard Workflow

Every change that modifies behavior, configuration, content, or dependencies:

1. **Issue first.** Create a GitHub issue describing the change before starting work. If an issue
already exists, reference it.
2. **Branch.** Create a feature branch from `main` named `{type}/{short-description}` (e.g.,
`feat/engagement-model`, `fix/ja-em-dash-cleanup`).
3. **Work.** Make changes on the branch. Run `deno task preflight` before committing.
4. **PR.** Create a pull request linking to the issue with `Closes #N` or `Fixes #N` in the body. PR
body must include a Summary and Test Plan.
5. **Merge.** Merge with `gh pr merge --admin --merge --delete-branch` (org policy blocks
auto-merge; admin override is authorized for the repo owner).
6. **Post-merge verification.** After every merge to main:
- Check GitHub CI: `gh run list --limit 3`
- Check Cloudflare build logs (docs site deploys to CF Workers): verify the build succeeds and
deploy completes
- Check Dependabot:
`gh api repos/eSolia/marquis/dependabot/alerts --jq '[.[] | select(.state=="open")] | length'`
7. **Release.** Releases are created periodically (not per-change) via `gh release create v<x.y.z>`
with hand-written notes. Tag push triggers `publish.yml` to push to JSR.

## Branching correctly

Use `git switch -c <branch>` from main, NOT `git checkout origin/main -b <branch>` (the latter sets
the upstream to `origin/main`, which can cause `git push -u origin <branch>` to push directly to
main and bypass review).

```bash
git switch main && git pull --ff-only
git switch -c feat/whatever
# ...work...
git push -u origin HEAD
```

Pre-push sanity check on a new branch:

```bash
git branch -vv
# Current branch should NOT show [origin/main] or any [origin/something-else]
# upstream. An unset upstream (no brackets) is what you want.
```

## Writing PR and issue bodies

Always pass multi-line or markdown-rich bodies **by file**, never inline via a heredoc:

```bash
gh pr create --body-file <path>
gh pr edit N --body-file <path>
gh issue create --body-file <path>
git commit -F <path>
```

Heredocs cause backslash artifacts in rendered markdown.

## Conventional Commits

```
type(scope): description

Body explaining the change (if needed).

InfoSec: [security/quality/privacy consideration]
```

**Types:** `feat`, `fix`, `docs`, `style`, `refactor`, `test`, `chore`

**InfoSec line** — required for all changes. Examples:

- `InfoSec: input validation added for user-supplied query parameters`
- `InfoSec: no security impact — content-only change`
- `InfoSec: dependency update addresses CVE-2026-XXXX`

If a change has no security implications, state that explicitly.

## Rationale

This workflow produces the evidence chain that ISO 27001 (A.8.9, A.8.25, A.8.32) requires:

- **Change request** → GitHub issue
- **Authorization** → PR review and merge approval
- **Testing** → CI checks (lint, typecheck, test, security scan)
- **Implementation** → Commits on feature branch
- **Verification** → Post-merge CI confirmation

An auditor can trace any production change from PR → issue → commits → CI results.

---

_Originally synced from
[eSolia/devkit](https://github.com/eSolia/devkit)/.claude/shared-rules/change-management.md. This
repo is not a devkit sync consumer — edit locally as needed._
25 changes: 25 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
version: 2
updates:
- package-ecosystem: 'github-actions'
directory: '/'
schedule:
interval: 'weekly'
day: 'monday'
labels:
- 'dependencies'
- 'github-actions'
commit-message:
prefix: 'chore'
include: 'scope'

- package-ecosystem: 'npm'
directory: '/docs-site'
schedule:
interval: 'weekly'
day: 'monday'
labels:
- 'dependencies'
- 'docs-site'
commit-message:
prefix: 'chore'
include: 'scope'
41 changes: 41 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: CI

on:
push:
branches: [main]
pull_request:
branches: [main]

permissions:
contents: read

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}

jobs:
verify:
name: Verify (fmt / lint / check / test)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Deno
uses: denoland/setup-deno@v2
with:
deno-version: '2.x'

- name: Format check
run: deno fmt --check

- name: Lint
run: deno lint

- name: Type check
run: deno check mod.ts

- name: Test
run: deno task test

- name: Publish dry-run
run: deno publish --dry-run --allow-slow-types
58 changes: 58 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
name: Publish to JSR

on:
push:
tags:
- 'v*'
workflow_dispatch:

permissions:
contents: read
id-token: write

jobs:
test:
name: Verify before publish
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Setup Deno
uses: denoland/setup-deno@v2
with:
deno-version: '2.x'

- name: Format check
run: deno fmt --check

- name: Lint
run: deno lint

- name: Type check
run: deno check mod.ts

- name: Test
run: deno task test

publish:
name: Publish
needs: [test]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v')

permissions:
contents: read
id-token: write

steps:
- uses: actions/checkout@v4

- name: Setup Deno
uses: denoland/setup-deno@v2
with:
deno-version: '2.x'

- name: Publish to JSR
# --allow-slow-types: tracked as debt — see issue for follow-up to add
# explicit types to ui/icon-button.ts (13 symbols missing types).
run: deno publish --allow-slow-types
Loading
Loading