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
6 changes: 3 additions & 3 deletions .agents/skills/quantmind-dev/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ Development workflow for contributing to the QuantMind codebase.

## Start Here

1. Use the repository [`contexts/README.md`](../../../contexts/README.md) to
1. Use the repository `contexts/README.md` to
select the development context for this contribution.
2. Read the repository root `AGENTS.md` or `CLAUDE.md` for the stable
architecture constraints and the module map.
3. Read `docs/README.md` when the task adds, changes, or uses a public
operation or public-network source.
4. When creating, updating, or triaging an issue or pull request, use the
canonical [repository label guidance](../../../contexts/dev/labels.md).
canonical repository label guidance in `contexts/dev/labels.md`.
5. Before writing or editing any GitHub body, follow the canonical
[GitHub writing style](../../../contexts/dev/github-writing.md), including
GitHub writing style in `contexts/dev/github-writing.md`, including
its no-hard-wrap rule.
6. Pick exactly one workflow reference below; do not load the others.

Expand Down
5 changes: 5 additions & 0 deletions .agents/skills/quantmind-dev/references/commit.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ English, [Conventional Commits](https://www.conventionalcommits.org/):
`chore(verify): ...`, `docs(news): ...`). Omit the scope only for
repo-wide changes (`feat: flows + magic apex layer`).
- **summary**: imperative, lower-case start, no trailing period.
- **body** (optional): explain what changed and why. Follow the same
no-hard-wrap prose rule as GitHub bodies (see the GitHub writing style in
`contexts/dev/github-writing.md`, a repo-root-relative path) — keep each
paragraph or list item on one physical line, with no fixed-width wrapping —
so a squash-merge keeps a clean description on GitHub.

Examples from history:

Expand Down
2 changes: 1 addition & 1 deletion .agents/skills/quantmind-dev/references/pull-request.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ batch input`, `docs(readme): update quick start`.
Written in English (external audiences read it: contributors, search
indexers, future maintainers). Follow `.github/PULL_REQUEST_TEMPLATE.md`
and the canonical
[GitHub writing style](../../../../contexts/dev/github-writing.md). Do not
GitHub writing style in `contexts/dev/github-writing.md`. Do not
hard-wrap body prose at 80 columns or another fixed width. Make sure the body
covers:

Expand Down
6 changes: 3 additions & 3 deletions .claude/skills/quantmind-dev/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,16 +9,16 @@ Development workflow for contributing to the QuantMind codebase.

## Start Here

1. Use the repository [`contexts/README.md`](../../../contexts/README.md) to
1. Use the repository `contexts/README.md` to
select the development context for this contribution.
2. Read the repository root `AGENTS.md` or `CLAUDE.md` for the stable
architecture constraints and the module map.
3. Read `docs/README.md` when the task adds, changes, or uses a public
operation or public-network source.
4. When creating, updating, or triaging an issue or pull request, use the
canonical [repository label guidance](../../../contexts/dev/labels.md).
canonical repository label guidance in `contexts/dev/labels.md`.
5. Before writing or editing any GitHub body, follow the canonical
[GitHub writing style](../../../contexts/dev/github-writing.md), including
GitHub writing style in `contexts/dev/github-writing.md`, including
its no-hard-wrap rule.
6. Pick exactly one workflow reference below; do not load the others.

Expand Down
5 changes: 5 additions & 0 deletions .claude/skills/quantmind-dev/references/commit.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ English, [Conventional Commits](https://www.conventionalcommits.org/):
`chore(verify): ...`, `docs(news): ...`). Omit the scope only for
repo-wide changes (`feat: flows + magic apex layer`).
- **summary**: imperative, lower-case start, no trailing period.
- **body** (optional): explain what changed and why. Follow the same
no-hard-wrap prose rule as GitHub bodies (see the GitHub writing style in
`contexts/dev/github-writing.md`, a repo-root-relative path) — keep each
paragraph or list item on one physical line, with no fixed-width wrapping —
so a squash-merge keeps a clean description on GitHub.

Examples from history:

Expand Down
2 changes: 1 addition & 1 deletion .claude/skills/quantmind-dev/references/pull-request.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ batch input`, `docs(readme): update quick start`.
Written in English (external audiences read it: contributors, search
indexers, future maintainers). Follow `.github/PULL_REQUEST_TEMPLATE.md`
and the canonical
[GitHub writing style](../../../../contexts/dev/github-writing.md). Do not
GitHub writing style in `contexts/dev/github-writing.md`. Do not
hard-wrap body prose at 80 columns or another fixed width. Make sure the body
covers:

Expand Down
26 changes: 26 additions & 0 deletions .github/workflows/pr-title.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
name: PR title

# The commit-msg git hook only fires for contributors who ran
# `pre-commit install`, so it cannot bind external contributors. This job is
# the server-side floor: it lints the PR title (which becomes the squash-merge
# commit subject) with the same rule as the commit-msg hook, reusing
# scripts/hooks/commit_msg_check.py so the convention has a single source.

on:
pull_request:
types: [opened, edited, reopened]

permissions:
contents: read

jobs:
lint-pr-title:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Lint PR title as an English Conventional Commit
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: |
printf '%s\n' "$PR_TITLE" > "$RUNNER_TEMP/pr_title.txt"
python3 scripts/hooks/commit_msg_check.py "$RUNNER_TEMP/pr_title.txt"
1 change: 1 addition & 0 deletions contexts/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ This directory is the public repository context system for coding agents and mai

## Where Information Lives

- **`contexts/` vs `docs/`**: `contexts/` is the internal navigation and design memory for coding agents and maintainers — design decisions in `contexts/design/`, contributor rules in `contexts/dev/`, usage routing in `contexts/usage/`. `docs/` is the user-facing surface for people consuming QuantMind as a library — the public component catalog `docs/README.md` and usage guides such as `docs/library.md`. A fact that helps an agent build the repo lives in `contexts/`; a fact that helps someone use the library lives in `docs/`. Neither keeps a second copy of the other.
- Keep each kind of information in one place so agents do not have to compare competing versions.
- `contexts/design/` records accepted design decisions and planned behavior.
- `contexts/dev/` and `contexts/usage/` route readers to existing rules and examples instead of copying them.
Expand Down
3 changes: 2 additions & 1 deletion contexts/dev/harness-engineering.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,10 +77,11 @@ Each rule is enforced at the layer that fits it, and hooks are kept to mechanica
| Contexts page structure (Quick Summary / Contents / anchors / index links) | `tests/test_contexts.py` inside `verify.sh` | CI + local |
| English Conventional Commit subject | `scripts/hooks/commit_msg_check.py` | `commit-msg` git hook (tool-agnostic: humans, Claude, Codex) |
| No `--no-verify` bypass of the hooks above | `scripts/hooks/pre_tool_use_no_bypass.py` | Agent PreToolUse hook |
| English Conventional Commit PR title | `.github/workflows/pr-title.yml` (reuses `commit_msg_check.py`) | CI check |

The anti-bypass hook is the one guarantee that cannot live in a git hook: `--no-verify` is by definition the flag that turns git hooks off, and CI only notices after the fact. A PreToolUse `deny` stops an agent from skipping the checks at the source. It is a mechanical check only: the command is tokenized with `shlex` and split into simple commands, so it denies only when `--no-verify` is a real argument token of a real `git` invocation — a command that merely mentions the flag inside a quoted string (an `echo`, a `grep`, a commit message) keeps it inside one token and passes. The residual gap is shell indirection (`$VAR` / `eval`), which a mechanical guard does not resolve. Commit-*message* format, by contrast, is a git hook rather than an agent hook, so it also catches a human committing from a terminal.

Planned next: enforce the commit convention for external contributors at the CI floor (a PR-title lint), since local git hooks only fire for contributors who ran `pre-commit install`.
The PR-title lint (`.github/workflows/pr-title.yml`) is the server-side floor for the commit convention. Local git hooks only fire for contributors who ran `pre-commit install`, so the PR title — which becomes the squash-merge commit subject — is linted in CI with the same `commit_msg_check.py` rule (single source). Making that workflow a *required* status check is a one-time repository-settings toggle, separate from committing the workflow here.

## References

Expand Down
94 changes: 46 additions & 48 deletions tests/test_contexts.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,50 +101,48 @@ def test_context_index_targets_exist(self) -> None:

def test_required_files_link_to_context_entry_point(self) -> None:
repo_root = Path(__file__).resolve().parents[1]
required_links = {
"AGENTS.md": "contexts/README.md",
"CLAUDE.md": "contexts/README.md",
".agents/skills/quantmind-dev/SKILL.md": "../../../contexts/README.md",
".claude/skills/quantmind-dev/SKILL.md": "../../../contexts/README.md",
}

for source_path, target in required_links.items():
source = repo_root / source_path
entry_point = "contexts/README.md"
# Root-level guides link to the entry point; nested skill files
# reference the same repo-root path in backticks. Both contain the
# path string, so one substring check is form-agnostic.
sources = (
"AGENTS.md",
"CLAUDE.md",
".agents/skills/quantmind-dev/SKILL.md",
".claude/skills/quantmind-dev/SKILL.md",
)
self.assertTrue(
(repo_root / entry_point).is_file(),
f"missing context entry point: {entry_point}",
)
for source_path in sources:
with self.subTest(source=source_path):
self.assertIn(
f"]({target})",
entry_point,
_guide_text(repo_root, source_path),
f"{source_path} must link to contexts/README.md",
)
self.assertTrue(
(source.parent / target).resolve().is_file(),
f"broken context entry link from {source_path}: {target}",
f"{source_path} must reference {entry_point}",
)

def test_label_guide_has_required_routes(self) -> None:
repo_root = Path(__file__).resolve().parents[1]
required_links = {
"contexts/dev/README.md": "labels.md",
".agents/skills/quantmind-dev/SKILL.md": (
"../../../contexts/dev/labels.md"
),
".claude/skills/quantmind-dev/SKILL.md": (
"../../../contexts/dev/labels.md"
),
self.assertTrue(
(repo_root / "contexts/dev/labels.md").is_file(),
"missing canonical label guide",
)
# The dev index links a sibling page; nested skill files use a
# repo-root backtick reference.
references = {
"contexts/dev/README.md": "](labels.md)",
".agents/skills/quantmind-dev/SKILL.md": "`contexts/dev/labels.md`",
".claude/skills/quantmind-dev/SKILL.md": "`contexts/dev/labels.md`",
}

for source_path, target in required_links.items():
source = repo_root / source_path
for source_path, expected in references.items():
with self.subTest(source=source_path):
self.assertIn(
f"]({target})",
expected,
_guide_text(repo_root, source_path),
f"{source_path} must route to the canonical label guide",
)
self.assertTrue(
(source.parent / target).resolve().is_file(),
f"broken label guide link from {source_path}: {target}",
)

def test_label_guide_has_complete_taxonomy(self) -> None:
repo_root = Path(__file__).resolve().parents[1]
Expand Down Expand Up @@ -186,37 +184,37 @@ def test_label_guide_has_complete_taxonomy(self) -> None:

def test_github_writing_guide_has_required_routes(self) -> None:
repo_root = Path(__file__).resolve().parents[1]
required_links = {
"contexts/dev/README.md": "github-writing.md",
"contexts/dev/labels.md": "github-writing.md",
"AGENTS.md": "contexts/dev/github-writing.md",
"CLAUDE.md": "contexts/dev/github-writing.md",
self.assertTrue(
(repo_root / "contexts/dev/github-writing.md").is_file(),
"missing GitHub writing guide",
)
# Sibling contexts pages link the short path; root guides link the
# repo-root path; nested skill files use a repo-root backtick reference.
references = {
"contexts/dev/README.md": "](github-writing.md)",
"contexts/dev/labels.md": "](github-writing.md)",
"AGENTS.md": "](contexts/dev/github-writing.md)",
"CLAUDE.md": "](contexts/dev/github-writing.md)",
".agents/skills/quantmind-dev/SKILL.md": (
"../../../contexts/dev/github-writing.md"
"`contexts/dev/github-writing.md`"
),
".claude/skills/quantmind-dev/SKILL.md": (
"../../../contexts/dev/github-writing.md"
"`contexts/dev/github-writing.md`"
),
".agents/skills/quantmind-dev/references/pull-request.md": (
"../../../../contexts/dev/github-writing.md"
"`contexts/dev/github-writing.md`"
),
".claude/skills/quantmind-dev/references/pull-request.md": (
"../../../../contexts/dev/github-writing.md"
"`contexts/dev/github-writing.md`"
),
}

for source_path, target in required_links.items():
source = repo_root / source_path
for source_path, expected in references.items():
with self.subTest(source=source_path):
self.assertIn(
f"]({target})",
expected,
_guide_text(repo_root, source_path),
f"{source_path} must route to the GitHub writing guide",
)
self.assertTrue(
(source.parent / target).resolve().is_file(),
f"broken GitHub writing guide link: {target}",
)

marker = "<!-- github-prose-style:"
templates = (
Expand Down
Loading