Skip to content
Open
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
72 changes: 72 additions & 0 deletions .claude/skills/commit-messages/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
---
name: commit-messages
description: MUST use before writing any git commit message in this repository. Covers the gitlint rules CI enforces (title/body length, blank line, trailing punctuation) and how to write a compliant message on the first try.
---

# Commit Messages

CI (`.github/workflows/CommitMessage.yml`) runs `gitlint` against every
commit on a PR and fails the build on any violation. `.gitlint` in the repo
root only exempts `Agent-Logs-Url:` and the Copilot Autofix co-author
trailer from the body-length rule -- every other line, in every commit,
is checked. There is no leniency for "just the summary" or "just this once."

## The rules that actually fire in practice

| Rule | Limit | Notes |
|---|---|---|
| Title length (T1) | 72 characters | Counts the whole subject line, including the `type:` prefix. |
| Body line length (B1) | 80 characters | Per line, not per paragraph. A heredoc does NOT auto-wrap -- you must break lines yourself. |
| Blank line after title (B4) | required | One empty line between the subject and the body. |
| Title trailing punctuation (T3) | none | No period, no colon, at the end of the subject line. |
| Trailing whitespace (T2, B2) | none | Watch for trailing spaces left by hand-wrapped lines. |
| Hard tabs (B3) | none | Use spaces in the body. |

## Writing a compliant message the first time

- Count the title before committing to it. "docs: remove doc-pointers and
provenance narration from PR #964" is 63 characters; adding a scope like
"and cleanup notes" on top of an already-full title is how T1 fails.
- Wrap body prose by hand at well under 80 characters per line -- a heredoc
passed to `git commit -F -`/`-m` reproduces exactly the line breaks you
typed, it does not reflow them. Aim for ~70 so a `Co-Authored-By:` trailer
or an indented list item added later doesn't push a line over.
- Prefer several short lines over one long one, and several short
paragraphs over one dense one -- a commit message is read in a `git log`
pane, not a text editor with wrapping.

## Verify before considering a commit done

Run the same check CI runs, scoped to the current branch:

```powershell
gitlint --ignore body-is-missing --commits main..HEAD
```

(Substitute the actual base branch if not `main`.) A clean run prints
nothing and exits 0. `Build/Agent/commit-messages.ps1` wraps this with the
same base-ref auto-detection CI uses, if you want the base resolved for
you instead of naming it.

## Fixing a violation after the fact

If a commit already landed non-compliant and hasn't been pushed to a shared
branch, reword it without a full interactive rebase:

```powershell
git rebase <target>^ --exec 'if [ "$(git rev-parse HEAD)" = "<target-full-sha>" ]; then git commit --amend -F <message-file>; fi'
```

Git's `--exec` always runs the quoted command through its own bundled `sh`,
even on Windows, so that inner string stays POSIX syntax regardless of
which shell you type this from -- only the outer PowerShell single-quotes
(passed through verbatim, unlike bash's backslash-escaped double-quotes)
change here.

This replays history non-interactively (no editor, no `-i` prompt) and
amends only the one commit whose SHA matches, at the point in the replay
where it is HEAD. Never do this on a branch that has already been pushed
and could have a PR or other work based on it -- check
`git rev-parse --abbrev-ref --symbolic-full-name @{u}` and
`git ls-remote --heads origin <branch>` first, and confirm with the user if
either shows the branch is shared.
239 changes: 100 additions & 139 deletions .claude/skills/fieldworks-code-commenting/SKILL.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion .claude/skills/powershell/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ name: powershell
description: >
PowerShell best practices for scripts used in FieldWorks (dev scripts & CI helpers).
Use when writing or modifying PowerShell scripts in scripts/ or Build/Agent/.
allowed-tools: "Read,Bash(pwsh:*)"
allowed-tools: "Read,PowerShell"
version: "1.0.0"
---

Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/pr-pitch/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: pr-pitch
description: Compose a PR body as a pitch that answers the unknowns a reviewer arrives with, with the branch's decisions, provenance, and paths-not-taken folded into collapsed accordions below it, while evicting those files from the repo. Use when writing or refreshing a PR description, when a branch carries working markdown that should not merge, or when pr-preflight reaches its PR step.
description: "NOT an entrypoint -- pr-preflight calls this for the write-up step; use pr-preflight for a fresh 'write/make/open a PR' request. Invoke this directly only to redo the write-up on a PR that already exists. Composes a PR body as a pitch that answers the unknowns a reviewer arrives with, with the branch's decisions, provenance, and paths-not-taken folded into collapsed accordions below it, while evicting those files from the repo."
argument-hint: "Optional PR number (defaults to the PR for the current branch)"
---

Expand Down
11 changes: 6 additions & 5 deletions .claude/skills/pr-preflight/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
name: pr-preflight
description: "Use when preparing a FieldWorks branch or pull request for review: pre-PR review, branch readiness, author interview, review summary generation, validation evidence, or PR description preparation."
description: "The required entrypoint whenever asked to write, make, open, create, update, or ship a PR for this repo -- do not post a PR body without running this first. Also use for pre-PR review, branch readiness, author interview, review summary generation, or validation evidence."
argument-hint: "Optional branch purpose or PR goal"
user-invocable: true
---
Expand Down Expand Up @@ -236,17 +236,18 @@ After writing the summary, tell the author:
>
> Please review it, make changes where appropriate, and run `/pr-preflight` again until you are ready to post the PR.
>
> If you do not want to make any changes and are ready for review, would you like me to commit any uncommitted changes, push, and post the PR? I will check whether one already exists for this branch and update it, or create a new one if not. The write-up runs through `pr-pitch`, which will also triage any research or working markdown on the branch into collapsed PR comments and out of the tree -- you approve that triage before anything is deleted."
> If you do not want to make any changes and are ready for review, would you like me to commit any uncommitted changes, push, and post the PR? I will check whether one already exists for this branch and update it, or create a new one if not. The write-up runs through `pr-pitch`, which will also triage any research or working markdown on the branch into collapsed sections in the PR body and out of the tree -- you approve that triage before anything is deleted."

Only create or update a PR after the author confirms.

## PR Description

This skill is the single entrypoint for making a PR, but it does not compose
the description itself. Once the author confirms readiness, invoke the
`pr-pitch` skill and let it own the write-up. It produces three artifacts
together: the PR body as a pitch, provenance in collapsed PR comments, and a
commit evicting the branch's research and working markdown from the tree.
`pr-pitch` skill and let it own the write-up. It produces two artifacts
together: the PR body (a pitch above the fold, provenance in collapsed
accordions below it) and a commit evicting the branch's research and working
markdown from the tree.

Hand `pr-pitch` the branch purpose, the findings, and `.review/summary.md`.

Expand Down
16 changes: 8 additions & 8 deletions .github/instructions/dotnet-upgrade.instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,11 +48,11 @@ To identify dependencies:
- Use the following approaches:
- **Visual Studio** → `Dependencies` in Solution Explorer.
- **dotnet CLI** → run:
```bash
```powershell
dotnet list <ProjectName>.csproj reference
```
- **Dependency Graph Generator**:
```bash
```powershell
dotnet msbuild <SolutionName>.sln /t:GenerateRestoreGraphFile /p:RestoreGraphOutputPath=graph.json
```
Inspect `graph.json` to see the dependency order.
Expand All @@ -79,16 +79,16 @@ For each project:
- `TargetFramework` → Change to the desired version (e.g., `net8.0`).
- `PackageReference` → Verify if each NuGet package supports the new framework.
- Run:
```bash
```powershell
dotnet list package --outdated
```
Update packages:
```bash
```powershell
dotnet add package <PackageName> --version <LatestVersion>
```

3. If `packages.config` is used (legacy), migrate to `PackageReference`:
```bash
```powershell
dotnet migrate <ProjectPath>
```

Expand Down Expand Up @@ -131,11 +131,11 @@ BlobServiceClient client = new BlobServiceClient(connectionString);
2. Update NuGet packages to versions compatible with the target framework.
3. After upgrading and restoring the latest DLLs, review code for any required changes.
4. Rebuild the project:
```bash
```powershell
dotnet build <ProjectName>.csproj
```
5. Run unit tests if any:
```bash
```powershell
dotnet test
```
6. Fix build or runtime issues before proceeding.
Expand Down Expand Up @@ -168,7 +168,7 @@ After all projects are upgraded:

## 7. Tools & Automation
- **.NET Upgrade Assistant**(Optional):
```bash
```powershell
dotnet tool install -g upgrade-assistant
upgrade-assistant upgrade <SolutionName>.sln```

Expand Down
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
git log --check --pretty=format:"---% h% s" origin/<base>..
git diff --check --cached
```
- [ ] Builds/tests pass locally (or I've run the CI-style build via Bash script or MSBuild).
- [ ] Builds/tests pass locally (or I've run the CI-style build via `build.ps1`/`test.ps1` or MSBuild).
- [ ] If this is core-developer AI-assisted work, I followed `Docs/workflows/ai-pr-workflow.md` and ran `pr-preflight` or the equivalent branch-readiness review before requesting review.
- [ ] For any `Src/**` folders touched, corresponding `AGENTS.md` files are updated or explicitly confirmed still accurate.

Expand Down
26 changes: 26 additions & 0 deletions .github/workflows/CI.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,32 @@ jobs:
- name: Checkout Files
uses: actions/checkout@v7
id: checkout
with:
# comment-hygiene.ps1 diffs against origin/<default-branch>; a
# shallow, single-branch checkout leaves that ref unresolvable
# and the gate fails every build with "bad revision".
fetch-depth: 0

# Real-runtime check: this repo is authored on PowerShell 7 but
# build.ps1/test.ps1 run under Windows PowerShell 5.1 below. A script
# can parse identically on both and still resolve differently at
# runtime (see comment-hygiene's ASCII-replacement map, which used to
# do exactly that). windows-2022 ships both engines, so run the
# comment-hygiene fixture suite under each rather than assuming one
# implies the other.
- name: Comment hygiene fixture tests (PowerShell 7)
id: comment-hygiene-tests-pwsh
shell: pwsh
run: |
Build/Agent/CommentHygiene.Tests.ps1
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }

- name: Comment hygiene fixture tests (Windows PowerShell 5.1)
id: comment-hygiene-tests-winps
shell: powershell
run: |
Build\Agent\CommentHygiene.Tests.ps1
if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE }

- name: Build with tests
id: build
Expand Down
29 changes: 18 additions & 11 deletions .github/workflows/CommitMessage.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,26 +13,33 @@ jobs:
with:
fetch-depth: 0
- name: Install dependencies
run: |
pip install --upgrade gitlint
shell: pwsh
run: pip install --upgrade gitlint
- name: Lint git commit messages
shell: bash
shell: pwsh
# run the linter and tee the output to a file, this will make the check fail but allow us to use the results in summary
run: gitlint --ignore body-is-missing --commits origin/$GITHUB_BASE_REF.. 2>&1 | tee check_results.log
run: |
# Pre-create the file: Tee-Object never creates (or even truncates) its target when the
# piped command emits zero objects, which is exactly what a clean gitlint run does.
New-Item -ItemType File -Path check_results.log -Force | Out-Null
gitlint --ignore body-is-missing --commits "origin/$env:GITHUB_BASE_REF.." 2>&1 | Tee-Object -FilePath check_results.log
exit $LASTEXITCODE
- name: Propegate Error Summary
if: always()
shell: bash
shell: pwsh
# put the output of the commit message linting into the summary for the job and in an environment variable
run: |
# Change the commit part of the log into a markdown link to the commit
commitsUrl="https:\/\/github.com\/${{ github.repository_owner }}\/${{ github.event.repository.name }}\/commit\/"
sed -i "s/Commit \([0-9a-f]\{7,40\}\)/[commit \1]($commitsUrl\1)/g" check_results.log
$commitsUrl = "https://github.com/${{ github.repository_owner }}/${{ github.event.repository.name }}/commit/"
$replacement = '[commit $1](' + $commitsUrl + '$1)'
$log = (Get-Content check_results.log -Raw) -replace 'Commit ([0-9a-f]{7,40})', $replacement
Set-Content -Path check_results.log -Value $log -NoNewline
# Put the results into the job summary
cat check_results.log >> "$GITHUB_STEP_SUMMARY"
Add-Content -Path $env:GITHUB_STEP_SUMMARY -Value $log
# Put the results into a multi-line environment variable to use in the next step
echo "check_results<<###LINT_DELIMITER###" >> "$GITHUB_ENV"
echo "$(cat check_results.log)" >> "$GITHUB_ENV"
echo "###LINT_DELIMITER###" >> "$GITHUB_ENV"
Add-Content -Path $env:GITHUB_ENV -Value 'check_results<<###LINT_DELIMITER###'
Add-Content -Path $env:GITHUB_ENV -Value $log
Add-Content -Path $env:GITHUB_ENV -Value '###LINT_DELIMITER###'
# add a comment on the PR if the commit message linting failed
- name: Comment on PR
if: failure()
Expand Down
4 changes: 2 additions & 2 deletions .github/workflows/check-whitespace.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,5 @@ jobs:
fetch-depth: 0

- name: Run whitespace check script
shell: bash
run: bash ./Build/Agent/check-whitespace.sh
shell: pwsh
run: ./Build/Agent/check-whitespace.ps1
32 changes: 32 additions & 0 deletions .github/workflows/stray-docs.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
name: stray-docs

on:
pull_request:
types: [opened, synchronize]

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
stray-docs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v7
with:
fetch-depth: 0

- name: Check for stray planning/spec docs
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$baseSha = "${{ github.event.pull_request.base.sha }}"
$added = git diff --name-only --diff-filter=A "$baseSha...HEAD" -- 'Docs/superpowers/plans/*.md' 'Docs/superpowers/specs/*.md'
if ($added) {
Write-Host "Stray working docs found in this PR's diff:"
$added | ForEach-Object { Write-Host $_ }
Write-Host ''
Write-Host "These are brainstorming/planning artifacts that should be evicted before merge (see the pr-pitch skill)."
exit 1
}
Write-Host 'No stray working docs found.'
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -217,3 +217,5 @@ DataTreeTimingBaselines.json
*[Ss]cratchPad*

Docs/migration/working/
Build/Agent/comment-hygiene-report.json
.review/
2 changes: 1 addition & 1 deletion .serena/memories/project_overview.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# FieldWorks Project Overview

- Purpose: FieldWorks (aka FLEx) is SIL International's Windows-focused linguistics and language data management suite. The repository hosts multiple desktop applications, shared libraries, an installer, tooling, and rich documentation.
- Tech stack: predominantly C#/.NET Framework 4.8 managed code, plus native C++/C++-CLI components, WiX installer assets, PowerShell/bash build scripts, and auxiliary Python tooling. Builds rely on MSBuild traversal (`FieldWorks.proj`).
- Tech stack: predominantly C#/.NET Framework 4.8 managed code, plus native C++/C++-CLI components, WiX installer assets, PowerShell build scripts, and auxiliary Python tooling. Builds rely on MSBuild traversal (`FieldWorks.proj`).
- Structure highlights: Src/ contains applications and libraries (with per-folder AGENTS.md docs). Build/ houses shared targets/scripts, FLExInstaller/ contains WiX artifacts, Include/ + Lib/ host native headers/libs, and Build/Agent scripts support worktree automation. Specs/ and Docs/ provide planning/reference material.
- Key guidelines: Follow `.github/instructions/*.instructions.md` (build, managed, native, installer, testing). Respect `.editorconfig`, update COPILOT metadata when touching folders, and keep documentation in sync with code.
- Tooling environment: development happens on Windows with Visual Studio 2022 workloads (Desktop .NET + C++), WiX 3.14.x.
6 changes: 6 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ Minimal, high-signal guidance for coding agents in this repository.
- Build with `.\build.ps1`.
- Test with `.\test.ps1`.
- Do not bypass repository scripts for normal build/test work.
- Commit messages must pass `gitlint` (CI: `.github/workflows/CommitMessage.yml`):
title <=72 characters, body lines <=80 characters, blank line between
them. A heredoc reproduces your line breaks exactly -- wrap body prose
by hand. See `.claude/skills/commit-messages/SKILL.md`.

## Critical constraints

Expand All @@ -16,6 +20,8 @@ Minimal, high-signal guidance for coding agents in this repository.
- Keep localization in `.resx`; do not hardcode translatable UI strings.
- Follow the code-comment standard in
`.claude/skills/fieldworks-code-commenting/SKILL.md`.
- Follow the commit-message rules in
`.claude/skills/commit-messages/SKILL.md` for every commit.

## Context model

Expand Down
Loading
Loading