From ced7ab7a8057f4de28795d555efa2f318905f5e7 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Wed, 18 Feb 2026 07:34:56 +0100 Subject: [PATCH 01/10] Initial skill migration with CI and gated release workflow --- .github/workflows/skills-ci.yml | 29 ++++ .github/workflows/skills-release.yml | 161 ++++++++++++++++++ .release-policy.yml | 1 + LICENSE | 21 +++ README.md | 75 ++++++++ scripts/build_release_notes.py | 57 +++++++ scripts/compute_next_version.py | 68 ++++++++ scripts/validate_skills.py | 102 +++++++++++ .../gecode-brancher-implementation/SKILL.md | 38 +++++ .../agents/openai.yaml | 4 + skills/gecode-general-knowledge/SKILL.md | 39 +++++ .../agents/openai.yaml | 4 + skills/gecode-memory-handling/SKILL.md | 37 ++++ .../gecode-memory-handling/agents/openai.yaml | 4 + skills/gecode-modeling/SKILL.md | 45 +++++ skills/gecode-modeling/agents/openai.yaml | 4 + .../gecode-propagator-implementation/SKILL.md | 40 +++++ .../agents/openai.yaml | 4 + .../SKILL.md | 38 +++++ .../agents/openai.yaml | 4 + skills/gecode-search-engines/SKILL.md | 50 ++++++ .../gecode-search-engines/agents/openai.yaml | 4 + 22 files changed, 829 insertions(+) create mode 100644 .github/workflows/skills-ci.yml create mode 100644 .github/workflows/skills-release.yml create mode 100644 .release-policy.yml create mode 100644 LICENSE create mode 100755 scripts/build_release_notes.py create mode 100755 scripts/compute_next_version.py create mode 100755 scripts/validate_skills.py create mode 100644 skills/gecode-brancher-implementation/SKILL.md create mode 100644 skills/gecode-brancher-implementation/agents/openai.yaml create mode 100644 skills/gecode-general-knowledge/SKILL.md create mode 100644 skills/gecode-general-knowledge/agents/openai.yaml create mode 100644 skills/gecode-memory-handling/SKILL.md create mode 100644 skills/gecode-memory-handling/agents/openai.yaml create mode 100644 skills/gecode-modeling/SKILL.md create mode 100644 skills/gecode-modeling/agents/openai.yaml create mode 100644 skills/gecode-propagator-implementation/SKILL.md create mode 100644 skills/gecode-propagator-implementation/agents/openai.yaml create mode 100644 skills/gecode-search-engine-implementation/SKILL.md create mode 100644 skills/gecode-search-engine-implementation/agents/openai.yaml create mode 100644 skills/gecode-search-engines/SKILL.md create mode 100644 skills/gecode-search-engines/agents/openai.yaml diff --git a/.github/workflows/skills-ci.yml b/.github/workflows/skills-ci.yml new file mode 100644 index 0000000..52cd197 --- /dev/null +++ b/.github/workflows/skills-ci.yml @@ -0,0 +1,29 @@ +name: skills-ci + +on: + push: + paths: + - 'skills/**' + - 'scripts/**' + - '.github/workflows/skills-*.yml' + pull_request: + paths: + - 'skills/**' + - 'scripts/**' + - '.github/workflows/skills-*.yml' + +jobs: + validate: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + - uses: actions/setup-node@v4 + with: + node-version: '20' + - name: Validate skills + run: python scripts/validate_skills.py + - name: Smoke discovery + run: npx --yes skills add . --list diff --git a/.github/workflows/skills-release.yml b/.github/workflows/skills-release.yml new file mode 100644 index 0000000..f0e61dd --- /dev/null +++ b/.github/workflows/skills-release.yml @@ -0,0 +1,161 @@ +name: skills-release + +on: + push: + branches: [main] + paths: + - 'skills/**' + - 'scripts/**' + - '.release-policy.yml' + - '.github/workflows/skills-release.yml' + workflow_dispatch: + inputs: + version_override: + description: 'Optional explicit version tag (vX.Y.Z)' + required: false + type: string + bump_type: + description: 'Semver bump type when no version_override is set' + required: false + default: 'patch' + type: choice + options: + - patch + - minor + - major + - auto + +permissions: + contents: write + +jobs: + release: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Validate skills + run: python scripts/validate_skills.py + + - name: Read release policy + id: policy + run: | + python - << 'PY' + from pathlib import Path + txt = Path('.release-policy.yml').read_text(encoding='utf-8') + auto = 'auto_release: true' in txt + with open(Path.cwd() / '.policy_out', 'w', encoding='utf-8') as f: + f.write(f"auto_release={'true' if auto else 'false'}\n") + PY + cat .policy_out >> "$GITHUB_OUTPUT" + + - name: Decide whether release is allowed + id: gate + run: | + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + echo "should_release=true" >> "$GITHUB_OUTPUT" + elif [ "${{ steps.policy.outputs.auto_release }}" = "true" ]; then + echo "should_release=true" >> "$GITHUB_OUTPUT" + else + echo "should_release=false" >> "$GITHUB_OUTPUT" + fi + + - name: Stop (policy gate) + if: steps.gate.outputs.should_release != 'true' + run: echo "Release policy disabled for push events; exiting successfully." + + - name: Determine bump from PR labels + if: steps.gate.outputs.should_release == 'true' && github.event_name == 'push' + id: prlabels + uses: actions/github-script@v7 + with: + script: | + const {owner, repo} = context.repo; + let bump = 'patch'; + try { + const prs = await github.rest.repos.listPullRequestsAssociatedWithCommit({ + owner, + repo, + commit_sha: context.sha, + }); + if (prs.data && prs.data.length > 0) { + const labels = (prs.data[0].labels || []).map(l => l.name); + if (labels.includes('release:major')) bump = 'major'; + else if (labels.includes('release:minor')) bump = 'minor'; + } + } catch (e) { + core.warning(`Could not infer PR labels: ${e.message}`); + } + core.setOutput('bump', bump); + + - name: Compute version + if: steps.gate.outputs.should_release == 'true' + id: version + env: + INPUT_BUMP: ${{ github.event.inputs.bump_type }} + INPUT_VERSION: ${{ github.event.inputs.version_override }} + PUSH_BUMP: ${{ steps.prlabels.outputs.bump }} + run: | + if [ -n "${INPUT_VERSION:-}" ]; then + VERSION="$INPUT_VERSION" + else + if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then + BUMP="${INPUT_BUMP:-patch}" + else + BUMP="${PUSH_BUMP:-patch}" + fi + VERSION=$(python scripts/compute_next_version.py --bump "$BUMP") + fi + echo "version=$VERSION" >> "$GITHUB_OUTPUT" + + - name: Check tag does not already exist + if: steps.gate.outputs.should_release == 'true' + run: | + if git rev-parse "${{ steps.version.outputs.version }}" >/dev/null 2>&1; then + echo "Tag ${{ steps.version.outputs.version }} already exists" >&2 + exit 1 + fi + + - name: Determine release range + if: steps.gate.outputs.should_release == 'true' + id: range + run: | + PREV=$(git tag --list 'v*' --sort=-v:refname | head -n 1 || true) + if [ -n "$PREV" ]; then + FROM="$PREV" + else + FROM=$(git rev-list --max-parents=0 HEAD) + fi + echo "from_ref=$FROM" >> "$GITHUB_OUTPUT" + echo "to_ref=${GITHUB_SHA}" >> "$GITHUB_OUTPUT" + + - name: Build release notes + if: steps.gate.outputs.should_release == 'true' + run: | + python scripts/build_release_notes.py \ + --from-ref "${{ steps.range.outputs.from_ref }}" \ + --to-ref "${{ steps.range.outputs.to_ref }}" \ + --version "${{ steps.version.outputs.version }}" \ + --repo "${{ github.repository }}" \ + --output RELEASE_NOTES.md + + - name: Create and push tag + if: steps.gate.outputs.should_release == 'true' + run: | + git config user.name "github-actions[bot]" + git config user.email "41898282+github-actions[bot]@users.noreply.github.com" + git tag "${{ steps.version.outputs.version }}" "${GITHUB_SHA}" + git push origin "${{ steps.version.outputs.version }}" + + - name: Create GitHub release + if: steps.gate.outputs.should_release == 'true' + uses: softprops/action-gh-release@v2 + with: + tag_name: ${{ steps.version.outputs.version }} + name: ${{ steps.version.outputs.version }} + body_path: RELEASE_NOTES.md diff --git a/.release-policy.yml b/.release-policy.yml new file mode 100644 index 0000000..574cc56 --- /dev/null +++ b/.release-policy.yml @@ -0,0 +1 @@ +auto_release: false diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..95ee507 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Gecode + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md index 8b13789..221a33d 100644 --- a/README.md +++ b/README.md @@ -1 +1,76 @@ +# Gecode Skills +Canonical skill repository for Gecode-focused AI agent skills. + +Install with: + +```bash +npx skills add Gecode/gecode-skills +``` + +List available skills: + +```bash +npx skills add Gecode/gecode-skills --list +``` + +Install a single skill: + +```bash +npx skills add Gecode/gecode-skills --skill gecode-modeling +``` + +## Available Skills + +- `gecode-general-knowledge` +- `gecode-modeling` +- `gecode-propagator-implementation` +- `gecode-brancher-implementation` +- `gecode-memory-handling` +- `gecode-search-engines` +- `gecode-search-engine-implementation` + +## Contributing + +### Skill structure + +Each skill must be under: + +- `skills//SKILL.md` + +Optional metadata for UIs can be added at: + +- `skills//agents/openai.yaml` + +### Required frontmatter + +Each `SKILL.md` must include YAML frontmatter with: + +- `name` +- `description` + +The `name` must match the directory name (``). + +### Release bump labels + +Auto-release determines semver bump from PR labels: + +- `release:major` -> major bump +- `release:minor` -> minor bump +- no label -> patch bump + +## Release policy + +Releases are controlled by `.release-policy.yml`. + +- Initially: `auto_release: false` +- This means pushes to `main` do **not** auto-release. +- Manual release via workflow dispatch is enabled. + +To enable auto-release later, set: + +```yaml +auto_release: true +``` + +and merge that change with maintainer review. diff --git a/scripts/build_release_notes.py b/scripts/build_release_notes.py new file mode 100755 index 0000000..91c1d86 --- /dev/null +++ b/scripts/build_release_notes.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import subprocess +from pathlib import Path + + +def changed_skills(diff_range: str) -> list[str]: + out = subprocess.check_output(["git", "diff", "--name-only", diff_range], text=True) + names: set[str] = set() + for line in out.splitlines(): + parts = line.split("/") + if len(parts) >= 3 and parts[0] == "skills" and parts[1].startswith("gecode-"): + names.add(parts[1]) + return sorted(names) + + +def main() -> int: + ap = argparse.ArgumentParser() + ap.add_argument("--from-ref", required=True) + ap.add_argument("--to-ref", required=True) + ap.add_argument("--version", required=True) + ap.add_argument("--repo", required=True, help="owner/repo") + ap.add_argument("--output", required=True) + args = ap.parse_args() + + diff_range = f"{args.from_ref}..{args.to_ref}" + skills = changed_skills(diff_range) + + lines = [] + lines.append(f"# {args.version}") + lines.append("") + if skills: + lines.append("## Changed skills") + lines.append("") + for s in skills: + lines.append(f"- `{s}`") + lines.append("") + else: + lines.append("No skill directory changes detected in this release range.") + lines.append("") + + lines.append("## Install") + lines.append("") + lines.append(f"```bash\nnpx skills add {args.repo}\n```") + lines.append("") + lines.append("List available skills:") + lines.append("") + lines.append(f"```bash\nnpx skills add {args.repo} --list\n```") + + Path(args.output).write_text("\n".join(lines) + "\n", encoding="utf-8") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/compute_next_version.py b/scripts/compute_next_version.py new file mode 100755 index 0000000..0e02962 --- /dev/null +++ b/scripts/compute_next_version.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import argparse +import re +import subprocess +import sys + +TAG_RE = re.compile(r"^v(\d+)\.(\d+)\.(\d+)$") + + +def latest_tag() -> tuple[int, int, int]: + out = subprocess.check_output( + ["git", "tag", "--list", "v*", "--sort=-v:refname"], text=True + ).strip() + if not out: + return (0, 0, 0) + first = out.splitlines()[0].strip() + m = TAG_RE.match(first) + if not m: + return (0, 0, 0) + return tuple(int(m.group(i)) for i in (1, 2, 3)) + + +def bump_from_labels(labels: list[str]) -> str: + s = set(labels) + if "release:major" in s: + return "major" + if "release:minor" in s: + return "minor" + return "patch" + + +def main() -> int: + ap = argparse.ArgumentParser() + ap.add_argument("--bump", choices=["major", "minor", "patch", "auto"], default="auto") + ap.add_argument("--labels", default="") + ap.add_argument("--current", default="") + args = ap.parse_args() + + if args.current: + m = TAG_RE.match(args.current.strip()) + if not m: + print("ERROR: --current must be in form vX.Y.Z", file=sys.stderr) + return 1 + cur = (int(m.group(1)), int(m.group(2)), int(m.group(3))) + else: + cur = latest_tag() + + bump = args.bump + if bump == "auto": + labels = [x.strip() for x in args.labels.split(",") if x.strip()] + bump = bump_from_labels(labels) + + major, minor, patch = cur + if bump == "major": + nxt = (major + 1, 0, 0) + elif bump == "minor": + nxt = (major, minor + 1, 0) + else: + nxt = (major, minor, patch + 1) + + print(f"v{nxt[0]}.{nxt[1]}.{nxt[2]}") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/scripts/validate_skills.py b/scripts/validate_skills.py new file mode 100755 index 0000000..a6e833d --- /dev/null +++ b/scripts/validate_skills.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python3 +from __future__ import annotations + +import re +import sys +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parents[1] +SKILLS_DIR = REPO_ROOT / "skills" + + +def parse_frontmatter(skill_md: Path) -> dict[str, str]: + text = skill_md.read_text(encoding="utf-8") + lines = text.splitlines() + if not lines or lines[0].strip() != "---": + raise ValueError("missing opening frontmatter delimiter '---'") + + end = None + for i in range(1, len(lines)): + if lines[i].strip() == "---": + end = i + break + if end is None: + raise ValueError("missing closing frontmatter delimiter '---'") + + fm = {} + kv_re = re.compile(r"^([A-Za-z0-9_-]+):\s*(.*)$") + for line in lines[1:end]: + if not line.strip() or line.lstrip().startswith("#"): + continue + m = kv_re.match(line) + if not m: + continue + key, raw_val = m.group(1), m.group(2).strip() + if raw_val.startswith('"') and raw_val.endswith('"') and len(raw_val) >= 2: + raw_val = raw_val[1:-1] + if raw_val.startswith("'") and raw_val.endswith("'") and len(raw_val) >= 2: + raw_val = raw_val[1:-1] + fm[key] = raw_val + return fm + + +def main() -> int: + if not SKILLS_DIR.exists(): + print(f"ERROR: skills directory not found: {SKILLS_DIR}") + return 1 + + errors: list[str] = [] + seen_names: dict[str, Path] = {} + + skill_dirs = sorted( + p for p in SKILLS_DIR.iterdir() if p.is_dir() and p.name.startswith("gecode-") + ) + if not skill_dirs: + errors.append("no gecode-* skill directories found under skills/") + + for skill_dir in skill_dirs: + skill_md = skill_dir / "SKILL.md" + if not skill_md.exists(): + errors.append(f"{skill_dir}: missing SKILL.md") + continue + + try: + fm = parse_frontmatter(skill_md) + except ValueError as e: + errors.append(f"{skill_md}: {e}") + continue + + for req in ("name", "description"): + if req not in fm or not fm[req].strip(): + errors.append(f"{skill_md}: missing required frontmatter field '{req}'") + + name = fm.get("name", "") + if name and name != skill_dir.name: + errors.append( + f"{skill_md}: frontmatter name '{name}' does not match directory '{skill_dir.name}'" + ) + + if name: + if name in seen_names: + errors.append( + f"duplicate skill name '{name}' in {skill_md} and {seen_names[name]}" + ) + else: + seen_names[name] = skill_md + + agents_dir = skill_dir / "agents" + if agents_dir.exists() and not (agents_dir / "openai.yaml").exists(): + errors.append(f"{skill_dir}: agents/ exists but agents/openai.yaml is missing") + + if errors: + print("Skill validation failed:") + for e in errors: + print(f"- {e}") + return 1 + + print(f"Validated {len(skill_dirs)} skills successfully.") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/skills/gecode-brancher-implementation/SKILL.md b/skills/gecode-brancher-implementation/SKILL.md new file mode 100644 index 0000000..973c9e1 --- /dev/null +++ b/skills/gecode-brancher-implementation/SKILL.md @@ -0,0 +1,38 @@ +--- +name: gecode-brancher-implementation +description: "Implement custom Gecode branchers and choice mechanics: status/choice/commit, archiving, recomputation compatibility, no-good literal support, and brancher view reuse. Use when predefined `branch(...)` is insufficient." +--- + +# Gecode Brancher Implementation + +## Core +- Brancher is actor implementing branching behavior. +- Implement `status`, `choice(Space&)`, `choice(const Space&,Archive&)`, `commit`, `print`, `copy`, `dispose`. +- Choice stores only space-independent commit data. +- `commit` must work with recomputed/cloned spaces using only choice payload. +- Choices must be archive-compatible and deterministic. +- Branchers execute in queue order of posting. +- Optional `ngl()` adds no-good support. +- Brancher `status()==false` does not imply immediate disposal; commits for earlier choices must remain valid. + +## Key Patterns +- Track first candidate index (`start`) to avoid rescanning. +- Keep choice payload minimal (`pos`, `val`, alt count), archive/unarchive deterministically. +- Use binary alternatives (`eq` vs `nq`) unless assignment brancher (single alt). +- Implement NGL class with `status`, `prune`, `subscribe`, `cancel`, `reschedule`. +- For complementary last alternatives, `ngl()` can return `NULL` when semantically valid. +- Reuse branchers through views (notably minus view for max-style variants). +- Encode problem heuristic explicitly (for example Warnsdorff, best-fit slack). +- Mix assignment-style one-alt choices with pruning alternatives when justified. +- Design second alternatives to embed symmetry breaking when safe. +- Pair brancher with branch print callbacks for explainability/debugging. + +## Pitfalls +- Storing views/pointers to space state inside choice objects. +- Disposing brancher too early when `status()` becomes false. +- Depending on mutable brancher state not encoded in choice for `commit()`. +- Using choices after invalidation by a later `choice()` call on the same space. +- Not skipping assigned views, causing repeated same choice/infinite tree. +- Violating recomputation invariants and commit order assumptions. +- Using generic variable-value branching when structure-aware heuristic is required. +- Forgetting that brancher disposal is not automatic when external resources exist. diff --git a/skills/gecode-brancher-implementation/agents/openai.yaml b/skills/gecode-brancher-implementation/agents/openai.yaml new file mode 100644 index 0000000..6c115f3 --- /dev/null +++ b/skills/gecode-brancher-implementation/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Gecode Brancher Implementation" + short_description: "Implement custom Gecode branchers and NGLs" + default_prompt: "Provide concise implementation guidance for Gecode branchers, choices, commits, and NGL support." diff --git a/skills/gecode-general-knowledge/SKILL.md b/skills/gecode-general-knowledge/SKILL.md new file mode 100644 index 0000000..c08c598 --- /dev/null +++ b/skills/gecode-general-knowledge/SKILL.md @@ -0,0 +1,39 @@ +--- +name: gecode-general-knowledge +description: "Core Gecode architecture and runtime model: spaces, propagators, branchers, status/choice/clone/commit lifecycle, cloning/recomputation semantics, groups/tracing. Use when explaining solver behavior, search flow, or high-level debugging/design decisions." +--- + +# Gecode General Knowledge + +## Core +- Space is home for variables, propagators, branchers, optimization order. +- Propagation is explicit: call `status()`. +- Search primitives: `status()`, `choice()`, `clone()`, `commit()`, `constrain()`. +- Space status: `SS_FAILED`, `SS_SOLVED`, `SS_BRANCH`. +- Choice is space-independent descriptor; alternatives indexed `0..n-1`. +- Choice compatibility is clone-based: a choice is compatible with its source space and clones. +- `choice()` invalidates previous choices for later `commit()` on that space. +- Clone only stable, non-failed spaces. +- Branchers run in posting order. +- Recomputation can be nondeterministic with weakly monotonic propagation, while remaining sound/complete. + +## Key Patterns +- Model as `class M : public Space`. +- Implement copy constructor + virtual `copy()`. +- In space cloning, clone variable arrays via `x.update(home, s.x)`; do not use a variable-array copy constructor. +- After `status()==SS_BRANCH`, compute `choice()` immediately. +- Seed search engine with model, then delete seed model. +- Treat solution as space closure over member variables. +- Use groups/tracing for observability, selective control. +- Iterate model quality in loops: baseline -> improve propagation -> improve branching -> tune search. +- Measure with nodes/time/restarts, not runtime only. +- Treat symmetry handling as first-class design work, not post-processing. + +## Pitfalls +- Assuming posting runs full propagation. +- Calling `Space` copy constructor directly instead of `clone()`. +- Using stale choices after invalidating via later `choice()` usage. +- Forgetting ownership/deletion of choices and returned solution spaces. +- Cloning unstable/failed spaces. +- Assuming parallel search preserves sequential solution order or runtime profile. +- Assuming one modeling pass is enough; most case studies need staged refinement. diff --git a/skills/gecode-general-knowledge/agents/openai.yaml b/skills/gecode-general-knowledge/agents/openai.yaml new file mode 100644 index 0000000..942c9bc --- /dev/null +++ b/skills/gecode-general-knowledge/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Gecode General Knowledge" + short_description: "Core Gecode architecture and workflow guide" + default_prompt: "Explain core Gecode concepts, spaces, propagation, and search operations concisely." diff --git a/skills/gecode-memory-handling/SKILL.md b/skills/gecode-memory-handling/SKILL.md new file mode 100644 index 0000000..a183f31 --- /dev/null +++ b/skills/gecode-memory-handling/SKILL.md @@ -0,0 +1,37 @@ +--- +name: gecode-memory-handling +description: "Manage Gecode memory areas and actor state: space/region/heap/freelists, lazy vs eager state allocation, shared/local handles, and disposal obligations (`AP_DISPOSE`). Use when implementing memory-sensitive propagators/branchers." +--- + +# Gecode Memory Handling + +## Core +- Memory areas: space, region, heap, space freelists. +- `alloc/realloc/free` follow C++ object lifecycle semantics. +- Space memory auto-reclaimed on space deletion; good for stable-size actor data. +- Region is temporary arena; implicit free on region destruction. +- Heap is for frequently resized/dynamic structures. +- Search memory profile favors pristine clones; allocation timing matters. +- Shared handles: cross-space/thread shared heap object with refcount. +- Local handles: per-space shared object, copied on cloning. + +## Key Patterns +- Allocate fixed actor members in home space. +- Allocate resize-heavy buffers on heap; free in `dispose()`. +- Build heavy internal state lazily on first propagation when possible. +- Choose eager vs lazy vs hybrid allocation based on clone-footprint and hit rate. +- Use regions for short-lived temporary iterators/buffers. +- Explicitly `Region::free()` at clear control-flow boundaries to maximize reuse. +- Use `SharedHandle` for immutable/global lookup data. +- Use `LocalHandle` for shared per-space mutable state. +- Use `IntSharedArray`/shared arrays for read-only large data reused across clones. +- For brancher choices using heap buffers, pair allocation/free and register `AP_DISPOSE`. +- Use `Region` for per-choice scratch arrays to avoid heap churn. + +## Pitfalls +- Frequent resize in space memory causing fragmentation. +- Forgetting `home.notice(..., AP_DISPOSE)` for external/heap resources. +- Forgetting matching `home.ignore(..., AP_DISPOSE)` in dispose path. +- Assuming same alignment guarantees across space vs heap/region. +- Leaking ownership assumptions across cloning boundaries. +- Allocating per-choice temporary arrays on heap in hot paths. diff --git a/skills/gecode-memory-handling/agents/openai.yaml b/skills/gecode-memory-handling/agents/openai.yaml new file mode 100644 index 0000000..6366222 --- /dev/null +++ b/skills/gecode-memory-handling/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Gecode Memory Handling" + short_description: "Manage Gecode memory and shared/local state" + default_prompt: "Summarize concise best practices for Gecode memory areas, lifecycle, and handle-based sharing." diff --git a/skills/gecode-modeling/SKILL.md b/skills/gecode-modeling/SKILL.md new file mode 100644 index 0000000..134f6d3 --- /dev/null +++ b/skills/gecode-modeling/SKILL.md @@ -0,0 +1,45 @@ +--- +name: gecode-modeling +description: "Model CP problems in Gecode with Int/Bool/Set/Float variables, constraints, MiniModel expressions, branchings, and search configuration. Use when building or refining Gecode models and solver setup." +--- + +# Gecode Modeling + +## Core +- Define model as `Space` subclass. +- Create typed variable arrays with tight domains early. +- Post constraints via post functions (`rel`, `linear`, `distinct`, set/float variants). +- Post branching via `branch(...)`; variable strategy + value strategy define tree shape. +- Use search engines (`DFS`, `BAB`, restart/portfolio variants) per objective. +- MiniModel adds expression syntax via `expr(...)`, `rel(...)`, matrix/channel helpers. +- Reified modeling supports full and half reification; decomposition semantics matter. + +## Key Patterns +- Prefer global constraints over manual decompositions when available. +- Keep branchings explicit and problem-specific for scale. +- Use multiple branchers intentionally; creation order matters. +- Tune search options: recomputation distance, restarts, no-goods, stop objects. +- Use tracing/Gist/CPProfiler for model diagnostics. +- Add implied constraints aggressively when semantics unchanged but propagation improves. +- Break symmetry structurally: order constraints, fixed anchors, `precede`, monotone bins. +- Use LDSB only with supported branching/value configurations; validate symmetry assumptions. +- Match propagation level to complexity (`IPL_DOM` only where payoff > cost). +- Replace weak decompositions with stronger globals (`count` GCC, `binpacking`, `circuit`, `extensional`). +- Cache reusable heavy artifacts (tuple sets, shared arrays) keyed by shape/parameters. +- For arrays requiring non-shared vars, call `unshare(...)` once and reuse the result. +- Use branch filters/print functions for targeted branching and observability. +- When executing code between branchers, remember propagation is still explicit on recomputation paths. +- For optimization models, branch on cost-driving vars first, tie with objective structure. + +## Pitfalls +- Weak domains at model start causing huge trees. +- Forgetting to update all variable members in cloning constructor. +- Assuming MiniModel nonlinear expressions stay monolithic; many decompose. +- Assuming reified non-functional decompositions imply `b=false`; they can fail instead. +- Treating Boolean vars as subclass of integer vars. +- Ignoring exceptions from invalid arguments/overflow. +- Using domain propagation for `linear` indiscriminately (can be exponential). +- Recomputing identical tuple sets/shared maps per post. +- Repeated implicit-style unsharing patterns that create unnecessary vars/propagators. +- Combining LDSB with unrelated static symmetry breaking without safety analysis. +- Leaving major value/variable symmetries unbroken. diff --git a/skills/gecode-modeling/agents/openai.yaml b/skills/gecode-modeling/agents/openai.yaml new file mode 100644 index 0000000..dd3bb29 --- /dev/null +++ b/skills/gecode-modeling/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Gecode Modeling" + short_description: "Model Gecode CP problems and search setup" + default_prompt: "Guide concise Gecode modeling choices, constraints, branchings, and search configuration." diff --git a/skills/gecode-propagator-implementation/SKILL.md b/skills/gecode-propagator-implementation/SKILL.md new file mode 100644 index 0000000..b6fe4ed --- /dev/null +++ b/skills/gecode-propagator-implementation/SKILL.md @@ -0,0 +1,40 @@ +--- +name: gecode-propagator-implementation +description: "Implement and optimize custom Gecode propagators: posting, propagate/reschedule lifecycle, propagation conditions, domain iterators, advisors, reification, and rewriting. Use when adding new constraints or improving propagation performance/safety." +--- + +# Gecode Propagator Implementation + +## Core +- Propagator computes on views, not model variables. +- Implement post function + actor lifecycle (`copy`, `dispose`, `cost`, `reschedule`, `propagate`). +- Use `Home` for posting context; use fail/check macros. +- Return honest `ExecStatus`: `ES_FAILED`, `ES_SUBSUMED`, `ES_FIX`, `ES_NOFIX`. +- Respect obligations: correctness, checking, contracting, monotonicity (or waived), subscription completeness, update completeness. +- Respect implementation obligations: subsumption complete, cloning conservative, subscription correct. +- Use patterns (`Unary/Binary/Ternary/Nary`, mixed variants) to reduce boilerplate. + +## Key Patterns +- Do cheap pruning in `post()`; skip posting when trivially subsumed/failed. +- Select minimal propagation conditions (`*_VAL`, `*_BND`, `*_DOM`). +- Prefer iterator-based domain ops (`inter_r`, `narrow_r`, `minus_r`) for domain propagation. +- Use fixpoint reasoning deliberately: return `ES_FIX` only when justified. +- Use `ModEventDelta` and staging to combine cheap and expensive propagation phases. +- Use advisors for incremental change localization. +- For advisors, maintain council lifecycle and ensure rescheduling/subscription completeness. +- Rewrite propagators (`GECODE_REWRITE`) when state simplifies. +- Use reified/rewriting patterns to remove reification overhead once control literals decide. +- Template propagators on view types for reuse. +- If decomposition is propagation-weak, prefer dedicated propagator or extensional surrogate. +- Treat expensive support data as cacheable object, not per-post recomputation. + +## Pitfalls +- Modifying a view while iterating its domain iterator. +- Returning `ES_FIX` when not actually at fixpoint. +- Returning `ES_NOFIX` when propagation is idempotent and could reach fixpoint inside the same `propagate()` call (causes avoidable re-scheduling). +- Missing view updates/subscription cancellation during cloning/disposal. +- Using external resources without `AP_DISPOSE` notice/ignore discipline. +- Continuing execution after subsuming/disposing actor. +- Failing to check modification-event failure after view updates. +- Breaking subscription completeness when using advisors/dynamic subscriptions. +- Expecting two weak propagators (`distinct` + `linear`) to match joint reasoning of one stronger constraint. diff --git a/skills/gecode-propagator-implementation/agents/openai.yaml b/skills/gecode-propagator-implementation/agents/openai.yaml new file mode 100644 index 0000000..e975e10 --- /dev/null +++ b/skills/gecode-propagator-implementation/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Gecode Propagator Implementation" + short_description: "Implement custom Gecode propagators safely" + default_prompt: "Provide concise implementation guidance for Gecode propagators, obligations, and optimization." diff --git a/skills/gecode-search-engine-implementation/SKILL.md b/skills/gecode-search-engine-implementation/SKILL.md new file mode 100644 index 0000000..27fc33a --- /dev/null +++ b/skills/gecode-search-engine-implementation/SKILL.md @@ -0,0 +1,38 @@ +--- +name: gecode-search-engine-implementation +description: "Implement custom Gecode search engines: status/choice/clone/commit orchestration, recomputation strategies (full/hybrid/adaptive), last-alternative optimization, branch-and-bound integration, and invariants for choice compatibility and completeness." +--- + +# Gecode Search Engine Implementation + +## Core +- Implement engines against the `Space` interface (`status`, `choice`, `clone`, `commit`). +- Maintain explicit ownership for all `Space*` and `Choice*` objects. +- Respect compatibility invariants: choices are valid only for clone-related spaces. +- Treat `choice()` as invalidating earlier choices on that space. +- Handle all `SpaceStatus` cases: `SS_FAILED`, `SS_SOLVED`, `SS_BRANCH`. + +## Key Patterns +- Keep a clear split between exploration mode and recomputation mode. +- Use edge/path state to store choices, alternatives, and optional clones. +- For recomputation, replay commits from nearest stored clone (or root clone). +- Apply LAO (last-alternative optimization) to avoid unnecessary stored choices/commits. +- Use hybrid recomputation with commit distance to cap recomputation cost. +- Use adaptive recomputation to add clones where repeated failures indicate benefit. +- Integrate branch-and-bound by constraining future spaces against best solution. +- Keep restart/meta-engine hooks explicit (`master`, `slave`) when required. +- Wire statistics and stop-object checks consistently. + +## Invariants +- Recomputed spaces must follow the same decision path as stored edge choices. +- Commit order must match original choice generation order. +- If recomputation fails due to nondeterminism/weak monotonicity effects, recover path state safely and continue search. +- Cloning/copying must never mutate model state outside allowed operations. + +## Pitfalls +- Reusing stale choices after another `choice()` call. +- Mixing incompatible choices/spaces and triggering `SpaceNoBrancher`. +- Forgetting to delete choices and returned solution spaces. +- Assuming deterministic node order under parallel execution. +- Overusing no-goods depth without accounting for memory and LAO tradeoffs. +- Reporting completeness when stop/cutoff/meta policy makes the run incomplete. diff --git a/skills/gecode-search-engine-implementation/agents/openai.yaml b/skills/gecode-search-engine-implementation/agents/openai.yaml new file mode 100644 index 0000000..0b1085b --- /dev/null +++ b/skills/gecode-search-engine-implementation/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Gecode Search Engine Implementation" + short_description: "Implement custom Gecode search engines safely" + default_prompt: "Guide implementation of a custom Gecode search engine, including recomputation, invariants, ownership, and branch-and-bound integration." diff --git a/skills/gecode-search-engines/SKILL.md b/skills/gecode-search-engines/SKILL.md new file mode 100644 index 0000000..8277987 --- /dev/null +++ b/skills/gecode-search-engines/SKILL.md @@ -0,0 +1,50 @@ +--- +name: gecode-search-engines +description: "Use existing Gecode search engines and meta-engines effectively: DFS/BAB/LDS, restart and portfolio setup, no-goods, parallel behavior, and completeness tradeoffs. Use when selecting/tuning search, not implementing new engines." +--- + +# Gecode Search Engines + +## Core +- This skill is for using built-in engines, not implementing custom engines. +- Base engines: `DFS`, `BAB`, `LDS`. +- Meta engines: `RBS` (restart-based) and `PBS` (portfolio-based). +- Key options live in `Search::Options` (`threads`, `c_d`, `a_d`, `clone`, `stop`, `cutoff`, `nogoods_limit`, `assets`, `slice`, `tracer`). +- For optimization, use `BAB`-style search with a valid model-side objective/constrain setup. + +## Engine Selection +- Use `DFS` for baseline complete search/enumeration. +- Use `BAB` for best-solution search. +- Use `LDS` when the branching heuristic is strong and you want discrepancy-ordered exploration. +- Use `RBS` to improve robustness via cutoffs, restart policies, and optional no-goods. +- Use `PBS` to improve robustness via asset diversification (heuristics/models/engines/options). + +## Restart, Portfolio, and Incomplete Search Interactions +- `RBS` requires a cutoff generator in options. +- In restart search, `master()` decides restart behavior and can post no-goods; `slave()` configures each restart run. +- In restart search, `slave()` return value matters: + `true` means complete slave search; `false` means intentionally incomplete search (for example LNS neighborhoods). +- In portfolio search, `slave()` return value has no meaning. +- `PBS` uses one engine type per asset; mixed portfolios use `SEBs(...)` to combine `dfs/lds/bab/rbs/pbs` with per-asset options. +- Do not mix best-solution and non-best assets in one SEB portfolio (`Search::MixedBest`). +- For restart-based best-solution assets in portfolios, use `RBS`. + +## No-Good Nuances +- Restart no-goods are only available from `DFS`/`BAB`, not `LDS`. +- Enable with `nogoods_limit > 0`; depth is a memory/benefit tradeoff. +- Larger no-good depth limits reduce LAO effectiveness near root and can significantly increase memory. +- Not all branchers support no-goods; float branchers and execution branchers do not. +- Parallel search usually yields fewer extractable no-goods. + +## Parallel and Portfolio Semantics +- Parallel search is intentionally nondeterministic (solution order, node counts, runtime). +- `assets` and `threads` are allocated conservatively in portfolios. +- Sequential portfolios use failure slices (`slice`) per asset (round-robin). +- With `threads > assets`, extra threads can be used inside asset engines. + +## Pitfalls +- Expecting deterministic behavior from restart/portfolio/parallel runs. +- Treating restart/portfolio as complete when stop conditions, cutoffs, or `slave()==false` make runs incomplete. +- Forgetting to diversify assets and then expecting portfolio gains. +- Assuming no-goods are always available/effective regardless of brancher mix and parallelism. +- Forgetting that `master()`/`slave()` policy choices directly change search completeness and restart behavior. diff --git a/skills/gecode-search-engines/agents/openai.yaml b/skills/gecode-search-engines/agents/openai.yaml new file mode 100644 index 0000000..40c8de5 --- /dev/null +++ b/skills/gecode-search-engines/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Gecode Search Engines" + short_description: "Use and tune existing Gecode search engines" + default_prompt: "Recommend and configure Gecode DFS/BAB/LDS/RBS/PBS usage, including restart/portfolio/no-good and completeness tradeoffs." From 062cd39e0115ed386f8d9c3e0538380514f0ce82 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Thu, 19 Feb 2026 20:13:55 +0100 Subject: [PATCH 02/10] Fix release policy parsing and semver release baselines --- .github/workflows/skills-release.yml | 9 ++++++--- scripts/compute_next_version.py | 10 +++++----- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/.github/workflows/skills-release.yml b/.github/workflows/skills-release.yml index f0e61dd..e2ee36a 100644 --- a/.github/workflows/skills-release.yml +++ b/.github/workflows/skills-release.yml @@ -46,9 +46,12 @@ jobs: id: policy run: | python - << 'PY' + import re from pathlib import Path + txt = Path('.release-policy.yml').read_text(encoding='utf-8') - auto = 'auto_release: true' in txt + m = re.search(r"(?im)^\s*auto_release\s*:\s*(true|false)\s*(?:#.*)?$", txt) + auto = bool(m and m.group(1).lower() == 'true') with open(Path.cwd() / '.policy_out', 'w', encoding='utf-8') as f: f.write(f"auto_release={'true' if auto else 'false'}\n") PY @@ -125,11 +128,11 @@ jobs: if: steps.gate.outputs.should_release == 'true' id: range run: | - PREV=$(git tag --list 'v*' --sort=-v:refname | head -n 1 || true) + PREV=$(git tag --list 'v*' --sort=-v:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -n 1 || true) if [ -n "$PREV" ]; then FROM="$PREV" else - FROM=$(git rev-list --max-parents=0 HEAD) + FROM=$(git hash-object -t tree /dev/null) fi echo "from_ref=$FROM" >> "$GITHUB_OUTPUT" echo "to_ref=${GITHUB_SHA}" >> "$GITHUB_OUTPUT" diff --git a/scripts/compute_next_version.py b/scripts/compute_next_version.py index 0e02962..95648bb 100755 --- a/scripts/compute_next_version.py +++ b/scripts/compute_next_version.py @@ -15,11 +15,11 @@ def latest_tag() -> tuple[int, int, int]: ).strip() if not out: return (0, 0, 0) - first = out.splitlines()[0].strip() - m = TAG_RE.match(first) - if not m: - return (0, 0, 0) - return tuple(int(m.group(i)) for i in (1, 2, 3)) + for tag in out.splitlines(): + m = TAG_RE.match(tag.strip()) + if m: + return tuple(int(m.group(i)) for i in (1, 2, 3)) + return (0, 0, 0) def bump_from_labels(labels: list[str]) -> str: From 45a628d0dca9aded89683df39d60065bee86eb2b Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Mon, 23 Feb 2026 20:38:43 +0100 Subject: [PATCH 03/10] Add progressive disclosure references to general knowledge skill --- skills/gecode-general-knowledge/SKILL.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/skills/gecode-general-knowledge/SKILL.md b/skills/gecode-general-knowledge/SKILL.md index c08c598..81e4bc2 100644 --- a/skills/gecode-general-knowledge/SKILL.md +++ b/skills/gecode-general-knowledge/SKILL.md @@ -5,6 +5,14 @@ description: "Core Gecode architecture and runtime model: spaces, propagators, b # Gecode General Knowledge +## Related Skills +- Use `gecode-modeling` for modeling choices, globals, branchings, symmetry handling, and search setup. +- Use `gecode-propagator-implementation` for custom propagator design, posting, propagation lifecycle, and optimization. +- Use `gecode-brancher-implementation` for custom branchers, choice/commit mechanics, and no-good literal support. +- Use `gecode-memory-handling` for space/region/heap allocation strategy and actor state ownership/disposal rules. +- Use `gecode-search-engines` for selecting and tuning built-in engines (`DFS`/`BAB`/`LDS`/restart/portfolio). +- Use `gecode-search-engine-implementation` for implementing custom search engines and recomputation strategies. + ## Core - Space is home for variables, propagators, branchers, optimization order. - Propagation is explicit: call `status()`. From a4b38a7b2043b690265a6918165f644608288bb4 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Mon, 23 Feb 2026 20:39:41 +0100 Subject: [PATCH 04/10] Add cmake consumption skill --- README.md | 1 + skills/gecode-cmake-consumption/SKILL.md | 47 +++++++++++++++++++ .../agents/openai.yaml | 4 ++ skills/gecode-general-knowledge/SKILL.md | 1 + 4 files changed, 53 insertions(+) create mode 100644 skills/gecode-cmake-consumption/SKILL.md create mode 100644 skills/gecode-cmake-consumption/agents/openai.yaml diff --git a/README.md b/README.md index 221a33d..fa6425f 100644 --- a/README.md +++ b/README.md @@ -23,6 +23,7 @@ npx skills add Gecode/gecode-skills --skill gecode-modeling ## Available Skills - `gecode-general-knowledge` +- `gecode-cmake-consumption` - `gecode-modeling` - `gecode-propagator-implementation` - `gecode-brancher-implementation` diff --git a/skills/gecode-cmake-consumption/SKILL.md b/skills/gecode-cmake-consumption/SKILL.md new file mode 100644 index 0000000..091e5d2 --- /dev/null +++ b/skills/gecode-cmake-consumption/SKILL.md @@ -0,0 +1,47 @@ +--- +name: gecode-cmake-consumption +description: "Consume Gecode from CMake using exported package config (`find_package(Gecode CONFIG)`), aggregate/component targets, version checks, and source-fetch fallback patterns. Use when integrating Gecode into downstream CMake projects or migrating custom `FindGecode.cmake` logic. Assume Gecode 6.3.0 or newer." +--- + +# Gecode CMake Consumption + +## Core +- Prefer config-package consumption: `find_package(Gecode CONFIG REQUIRED)`. +- Link downstream targets to `Gecode::gecode` unless explicit component granularity is required. +- Require `Gecode_VERSION >= 6.3.0`; use package version checks rather than parsing `gecode/support/config.hpp`. +- Hint package location with `Gecode_ROOT` or `CMAKE_PREFIX_PATH`. +- Keep `cmake_minimum_required(VERSION 3.21)` or newer for parity with Gecode's build. + +## Canonical Patterns +- Minimal integration: + ```cmake + find_package(Gecode CONFIG REQUIRED) + target_link_libraries(app PRIVATE Gecode::gecode) + ``` +- Component-specific integration: + ```cmake + find_package(Gecode CONFIG REQUIRED COMPONENTS driver) + target_link_libraries(app PRIVATE Gecode::gecodedriver) + ``` +- Version guard: + ```cmake + if(SOME_STRICT_OPTION AND Gecode_VERSION VERSION_LESS "6.3.0") + message(FATAL_ERROR "Gecode >= 6.3.0 required, found ${Gecode_VERSION}") + endif() + ``` + +## Dependency Resolution Workflow +- Try installed package first via `find_package(Gecode CONFIG QUIET)` when optional discovery is desired. +- If not found and project policy allows vendoring, use `FetchContent` to obtain Gecode source and call `FetchContent_MakeAvailable(...)`. +- Set Gecode cache options before `FetchContent_MakeAvailable(...)` to limit dependency surface (for example disable `GECODE_ENABLE_GIST`, examples, tests, or optional modules). +- Require `TARGET Gecode::gecode` after resolution; fail fast with actionable error text if absent. +- Mark Gecode target as `SYSTEM` in strict-warning projects to isolate third-party headers from local warning policy. +- For source-pinned fallback builds, prefer stable release tags or commits over long-lived feature branches. +- For migration away from custom discovery modules, remove manual imported-target composition and library probing once package config is the baseline path. +- Keep strict-version toggles if desired, but enforce them against `Gecode_VERSION`. + +## Pitfalls +- Calling `find_package(Gecode REQUIRED)` without `CONFIG` and accidentally resolving old/module-mode shims. +- Assuming optional components exist without checking enabled modules in the installed build. +- Mixing custom imported-target composition with package-provided targets in the same code path. +- Parsing headers for version when package metadata already provides `Gecode_VERSION`. diff --git a/skills/gecode-cmake-consumption/agents/openai.yaml b/skills/gecode-cmake-consumption/agents/openai.yaml new file mode 100644 index 0000000..d2d57aa --- /dev/null +++ b/skills/gecode-cmake-consumption/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Gecode CMake Consumption" + short_description: "Integrate Gecode via CMake package config" + default_prompt: "Guide downstream CMake integration with Gecode package exports, version checks (>= 6.3.0), and migration from custom FindGecode logic." diff --git a/skills/gecode-general-knowledge/SKILL.md b/skills/gecode-general-knowledge/SKILL.md index 81e4bc2..c6e5195 100644 --- a/skills/gecode-general-knowledge/SKILL.md +++ b/skills/gecode-general-knowledge/SKILL.md @@ -6,6 +6,7 @@ description: "Core Gecode architecture and runtime model: spaces, propagators, b # Gecode General Knowledge ## Related Skills +- Use `gecode-cmake-consumption` for downstream CMake integration, package consumption, and migration from custom `FindGecode` logic. - Use `gecode-modeling` for modeling choices, globals, branchings, symmetry handling, and search setup. - Use `gecode-propagator-implementation` for custom propagator design, posting, propagation lifecycle, and optimization. - Use `gecode-brancher-implementation` for custom branchers, choice/commit mechanics, and no-good literal support. From 9f1a5f8ec9d7af341d479aa9b672bea73a1e3918 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Mon, 9 Mar 2026 07:53:08 +0100 Subject: [PATCH 05/10] feat(skills): consolidate Gecode guidance into umbrella skill --- .../gecode-brancher-implementation/SKILL.md | 38 -------------- .../agents/openai.yaml | 4 -- skills/gecode-cmake-consumption/SKILL.md | 47 ----------------- .../agents/openai.yaml | 4 -- skills/gecode-general-knowledge/SKILL.md | 48 ------------------ .../agents/openai.yaml | 4 -- skills/gecode-memory-handling/SKILL.md | 37 -------------- .../gecode-memory-handling/agents/openai.yaml | 4 -- skills/gecode-modeling/SKILL.md | 45 ----------------- skills/gecode-modeling/agents/openai.yaml | 4 -- .../gecode-propagator-implementation/SKILL.md | 40 --------------- .../agents/openai.yaml | 4 -- .../SKILL.md | 38 -------------- .../agents/openai.yaml | 4 -- skills/gecode-search-engines/SKILL.md | 50 ------------------- .../gecode-search-engines/agents/openai.yaml | 4 -- skills/gecode/SKILL.md | 35 +++++++++++++ skills/gecode/agents/openai.yaml | 4 ++ .../references/brancher-implementation.md | 33 ++++++++++++ skills/gecode/references/cmake-consumption.md | 39 +++++++++++++++ skills/gecode/references/general-knowledge.md | 34 +++++++++++++ skills/gecode/references/memory-handling.md | 32 ++++++++++++ skills/gecode/references/modeling.md | 40 +++++++++++++++ .../references/propagator-implementation.md | 35 +++++++++++++ .../search-engine-implementation.md | 33 ++++++++++++ skills/gecode/references/search-engines.md | 38 ++++++++++++++ 26 files changed, 323 insertions(+), 375 deletions(-) delete mode 100644 skills/gecode-brancher-implementation/SKILL.md delete mode 100644 skills/gecode-brancher-implementation/agents/openai.yaml delete mode 100644 skills/gecode-cmake-consumption/SKILL.md delete mode 100644 skills/gecode-cmake-consumption/agents/openai.yaml delete mode 100644 skills/gecode-general-knowledge/SKILL.md delete mode 100644 skills/gecode-general-knowledge/agents/openai.yaml delete mode 100644 skills/gecode-memory-handling/SKILL.md delete mode 100644 skills/gecode-memory-handling/agents/openai.yaml delete mode 100644 skills/gecode-modeling/SKILL.md delete mode 100644 skills/gecode-modeling/agents/openai.yaml delete mode 100644 skills/gecode-propagator-implementation/SKILL.md delete mode 100644 skills/gecode-propagator-implementation/agents/openai.yaml delete mode 100644 skills/gecode-search-engine-implementation/SKILL.md delete mode 100644 skills/gecode-search-engine-implementation/agents/openai.yaml delete mode 100644 skills/gecode-search-engines/SKILL.md delete mode 100644 skills/gecode-search-engines/agents/openai.yaml create mode 100644 skills/gecode/SKILL.md create mode 100644 skills/gecode/agents/openai.yaml create mode 100644 skills/gecode/references/brancher-implementation.md create mode 100644 skills/gecode/references/cmake-consumption.md create mode 100644 skills/gecode/references/general-knowledge.md create mode 100644 skills/gecode/references/memory-handling.md create mode 100644 skills/gecode/references/modeling.md create mode 100644 skills/gecode/references/propagator-implementation.md create mode 100644 skills/gecode/references/search-engine-implementation.md create mode 100644 skills/gecode/references/search-engines.md diff --git a/skills/gecode-brancher-implementation/SKILL.md b/skills/gecode-brancher-implementation/SKILL.md deleted file mode 100644 index 973c9e1..0000000 --- a/skills/gecode-brancher-implementation/SKILL.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: gecode-brancher-implementation -description: "Implement custom Gecode branchers and choice mechanics: status/choice/commit, archiving, recomputation compatibility, no-good literal support, and brancher view reuse. Use when predefined `branch(...)` is insufficient." ---- - -# Gecode Brancher Implementation - -## Core -- Brancher is actor implementing branching behavior. -- Implement `status`, `choice(Space&)`, `choice(const Space&,Archive&)`, `commit`, `print`, `copy`, `dispose`. -- Choice stores only space-independent commit data. -- `commit` must work with recomputed/cloned spaces using only choice payload. -- Choices must be archive-compatible and deterministic. -- Branchers execute in queue order of posting. -- Optional `ngl()` adds no-good support. -- Brancher `status()==false` does not imply immediate disposal; commits for earlier choices must remain valid. - -## Key Patterns -- Track first candidate index (`start`) to avoid rescanning. -- Keep choice payload minimal (`pos`, `val`, alt count), archive/unarchive deterministically. -- Use binary alternatives (`eq` vs `nq`) unless assignment brancher (single alt). -- Implement NGL class with `status`, `prune`, `subscribe`, `cancel`, `reschedule`. -- For complementary last alternatives, `ngl()` can return `NULL` when semantically valid. -- Reuse branchers through views (notably minus view for max-style variants). -- Encode problem heuristic explicitly (for example Warnsdorff, best-fit slack). -- Mix assignment-style one-alt choices with pruning alternatives when justified. -- Design second alternatives to embed symmetry breaking when safe. -- Pair brancher with branch print callbacks for explainability/debugging. - -## Pitfalls -- Storing views/pointers to space state inside choice objects. -- Disposing brancher too early when `status()` becomes false. -- Depending on mutable brancher state not encoded in choice for `commit()`. -- Using choices after invalidation by a later `choice()` call on the same space. -- Not skipping assigned views, causing repeated same choice/infinite tree. -- Violating recomputation invariants and commit order assumptions. -- Using generic variable-value branching when structure-aware heuristic is required. -- Forgetting that brancher disposal is not automatic when external resources exist. diff --git a/skills/gecode-brancher-implementation/agents/openai.yaml b/skills/gecode-brancher-implementation/agents/openai.yaml deleted file mode 100644 index 6c115f3..0000000 --- a/skills/gecode-brancher-implementation/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Gecode Brancher Implementation" - short_description: "Implement custom Gecode branchers and NGLs" - default_prompt: "Provide concise implementation guidance for Gecode branchers, choices, commits, and NGL support." diff --git a/skills/gecode-cmake-consumption/SKILL.md b/skills/gecode-cmake-consumption/SKILL.md deleted file mode 100644 index 091e5d2..0000000 --- a/skills/gecode-cmake-consumption/SKILL.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -name: gecode-cmake-consumption -description: "Consume Gecode from CMake using exported package config (`find_package(Gecode CONFIG)`), aggregate/component targets, version checks, and source-fetch fallback patterns. Use when integrating Gecode into downstream CMake projects or migrating custom `FindGecode.cmake` logic. Assume Gecode 6.3.0 or newer." ---- - -# Gecode CMake Consumption - -## Core -- Prefer config-package consumption: `find_package(Gecode CONFIG REQUIRED)`. -- Link downstream targets to `Gecode::gecode` unless explicit component granularity is required. -- Require `Gecode_VERSION >= 6.3.0`; use package version checks rather than parsing `gecode/support/config.hpp`. -- Hint package location with `Gecode_ROOT` or `CMAKE_PREFIX_PATH`. -- Keep `cmake_minimum_required(VERSION 3.21)` or newer for parity with Gecode's build. - -## Canonical Patterns -- Minimal integration: - ```cmake - find_package(Gecode CONFIG REQUIRED) - target_link_libraries(app PRIVATE Gecode::gecode) - ``` -- Component-specific integration: - ```cmake - find_package(Gecode CONFIG REQUIRED COMPONENTS driver) - target_link_libraries(app PRIVATE Gecode::gecodedriver) - ``` -- Version guard: - ```cmake - if(SOME_STRICT_OPTION AND Gecode_VERSION VERSION_LESS "6.3.0") - message(FATAL_ERROR "Gecode >= 6.3.0 required, found ${Gecode_VERSION}") - endif() - ``` - -## Dependency Resolution Workflow -- Try installed package first via `find_package(Gecode CONFIG QUIET)` when optional discovery is desired. -- If not found and project policy allows vendoring, use `FetchContent` to obtain Gecode source and call `FetchContent_MakeAvailable(...)`. -- Set Gecode cache options before `FetchContent_MakeAvailable(...)` to limit dependency surface (for example disable `GECODE_ENABLE_GIST`, examples, tests, or optional modules). -- Require `TARGET Gecode::gecode` after resolution; fail fast with actionable error text if absent. -- Mark Gecode target as `SYSTEM` in strict-warning projects to isolate third-party headers from local warning policy. -- For source-pinned fallback builds, prefer stable release tags or commits over long-lived feature branches. -- For migration away from custom discovery modules, remove manual imported-target composition and library probing once package config is the baseline path. -- Keep strict-version toggles if desired, but enforce them against `Gecode_VERSION`. - -## Pitfalls -- Calling `find_package(Gecode REQUIRED)` without `CONFIG` and accidentally resolving old/module-mode shims. -- Assuming optional components exist without checking enabled modules in the installed build. -- Mixing custom imported-target composition with package-provided targets in the same code path. -- Parsing headers for version when package metadata already provides `Gecode_VERSION`. diff --git a/skills/gecode-cmake-consumption/agents/openai.yaml b/skills/gecode-cmake-consumption/agents/openai.yaml deleted file mode 100644 index d2d57aa..0000000 --- a/skills/gecode-cmake-consumption/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Gecode CMake Consumption" - short_description: "Integrate Gecode via CMake package config" - default_prompt: "Guide downstream CMake integration with Gecode package exports, version checks (>= 6.3.0), and migration from custom FindGecode logic." diff --git a/skills/gecode-general-knowledge/SKILL.md b/skills/gecode-general-knowledge/SKILL.md deleted file mode 100644 index c6e5195..0000000 --- a/skills/gecode-general-knowledge/SKILL.md +++ /dev/null @@ -1,48 +0,0 @@ ---- -name: gecode-general-knowledge -description: "Core Gecode architecture and runtime model: spaces, propagators, branchers, status/choice/clone/commit lifecycle, cloning/recomputation semantics, groups/tracing. Use when explaining solver behavior, search flow, or high-level debugging/design decisions." ---- - -# Gecode General Knowledge - -## Related Skills -- Use `gecode-cmake-consumption` for downstream CMake integration, package consumption, and migration from custom `FindGecode` logic. -- Use `gecode-modeling` for modeling choices, globals, branchings, symmetry handling, and search setup. -- Use `gecode-propagator-implementation` for custom propagator design, posting, propagation lifecycle, and optimization. -- Use `gecode-brancher-implementation` for custom branchers, choice/commit mechanics, and no-good literal support. -- Use `gecode-memory-handling` for space/region/heap allocation strategy and actor state ownership/disposal rules. -- Use `gecode-search-engines` for selecting and tuning built-in engines (`DFS`/`BAB`/`LDS`/restart/portfolio). -- Use `gecode-search-engine-implementation` for implementing custom search engines and recomputation strategies. - -## Core -- Space is home for variables, propagators, branchers, optimization order. -- Propagation is explicit: call `status()`. -- Search primitives: `status()`, `choice()`, `clone()`, `commit()`, `constrain()`. -- Space status: `SS_FAILED`, `SS_SOLVED`, `SS_BRANCH`. -- Choice is space-independent descriptor; alternatives indexed `0..n-1`. -- Choice compatibility is clone-based: a choice is compatible with its source space and clones. -- `choice()` invalidates previous choices for later `commit()` on that space. -- Clone only stable, non-failed spaces. -- Branchers run in posting order. -- Recomputation can be nondeterministic with weakly monotonic propagation, while remaining sound/complete. - -## Key Patterns -- Model as `class M : public Space`. -- Implement copy constructor + virtual `copy()`. -- In space cloning, clone variable arrays via `x.update(home, s.x)`; do not use a variable-array copy constructor. -- After `status()==SS_BRANCH`, compute `choice()` immediately. -- Seed search engine with model, then delete seed model. -- Treat solution as space closure over member variables. -- Use groups/tracing for observability, selective control. -- Iterate model quality in loops: baseline -> improve propagation -> improve branching -> tune search. -- Measure with nodes/time/restarts, not runtime only. -- Treat symmetry handling as first-class design work, not post-processing. - -## Pitfalls -- Assuming posting runs full propagation. -- Calling `Space` copy constructor directly instead of `clone()`. -- Using stale choices after invalidating via later `choice()` usage. -- Forgetting ownership/deletion of choices and returned solution spaces. -- Cloning unstable/failed spaces. -- Assuming parallel search preserves sequential solution order or runtime profile. -- Assuming one modeling pass is enough; most case studies need staged refinement. diff --git a/skills/gecode-general-knowledge/agents/openai.yaml b/skills/gecode-general-knowledge/agents/openai.yaml deleted file mode 100644 index 942c9bc..0000000 --- a/skills/gecode-general-knowledge/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Gecode General Knowledge" - short_description: "Core Gecode architecture and workflow guide" - default_prompt: "Explain core Gecode concepts, spaces, propagation, and search operations concisely." diff --git a/skills/gecode-memory-handling/SKILL.md b/skills/gecode-memory-handling/SKILL.md deleted file mode 100644 index a183f31..0000000 --- a/skills/gecode-memory-handling/SKILL.md +++ /dev/null @@ -1,37 +0,0 @@ ---- -name: gecode-memory-handling -description: "Manage Gecode memory areas and actor state: space/region/heap/freelists, lazy vs eager state allocation, shared/local handles, and disposal obligations (`AP_DISPOSE`). Use when implementing memory-sensitive propagators/branchers." ---- - -# Gecode Memory Handling - -## Core -- Memory areas: space, region, heap, space freelists. -- `alloc/realloc/free` follow C++ object lifecycle semantics. -- Space memory auto-reclaimed on space deletion; good for stable-size actor data. -- Region is temporary arena; implicit free on region destruction. -- Heap is for frequently resized/dynamic structures. -- Search memory profile favors pristine clones; allocation timing matters. -- Shared handles: cross-space/thread shared heap object with refcount. -- Local handles: per-space shared object, copied on cloning. - -## Key Patterns -- Allocate fixed actor members in home space. -- Allocate resize-heavy buffers on heap; free in `dispose()`. -- Build heavy internal state lazily on first propagation when possible. -- Choose eager vs lazy vs hybrid allocation based on clone-footprint and hit rate. -- Use regions for short-lived temporary iterators/buffers. -- Explicitly `Region::free()` at clear control-flow boundaries to maximize reuse. -- Use `SharedHandle` for immutable/global lookup data. -- Use `LocalHandle` for shared per-space mutable state. -- Use `IntSharedArray`/shared arrays for read-only large data reused across clones. -- For brancher choices using heap buffers, pair allocation/free and register `AP_DISPOSE`. -- Use `Region` for per-choice scratch arrays to avoid heap churn. - -## Pitfalls -- Frequent resize in space memory causing fragmentation. -- Forgetting `home.notice(..., AP_DISPOSE)` for external/heap resources. -- Forgetting matching `home.ignore(..., AP_DISPOSE)` in dispose path. -- Assuming same alignment guarantees across space vs heap/region. -- Leaking ownership assumptions across cloning boundaries. -- Allocating per-choice temporary arrays on heap in hot paths. diff --git a/skills/gecode-memory-handling/agents/openai.yaml b/skills/gecode-memory-handling/agents/openai.yaml deleted file mode 100644 index 6366222..0000000 --- a/skills/gecode-memory-handling/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Gecode Memory Handling" - short_description: "Manage Gecode memory and shared/local state" - default_prompt: "Summarize concise best practices for Gecode memory areas, lifecycle, and handle-based sharing." diff --git a/skills/gecode-modeling/SKILL.md b/skills/gecode-modeling/SKILL.md deleted file mode 100644 index 134f6d3..0000000 --- a/skills/gecode-modeling/SKILL.md +++ /dev/null @@ -1,45 +0,0 @@ ---- -name: gecode-modeling -description: "Model CP problems in Gecode with Int/Bool/Set/Float variables, constraints, MiniModel expressions, branchings, and search configuration. Use when building or refining Gecode models and solver setup." ---- - -# Gecode Modeling - -## Core -- Define model as `Space` subclass. -- Create typed variable arrays with tight domains early. -- Post constraints via post functions (`rel`, `linear`, `distinct`, set/float variants). -- Post branching via `branch(...)`; variable strategy + value strategy define tree shape. -- Use search engines (`DFS`, `BAB`, restart/portfolio variants) per objective. -- MiniModel adds expression syntax via `expr(...)`, `rel(...)`, matrix/channel helpers. -- Reified modeling supports full and half reification; decomposition semantics matter. - -## Key Patterns -- Prefer global constraints over manual decompositions when available. -- Keep branchings explicit and problem-specific for scale. -- Use multiple branchers intentionally; creation order matters. -- Tune search options: recomputation distance, restarts, no-goods, stop objects. -- Use tracing/Gist/CPProfiler for model diagnostics. -- Add implied constraints aggressively when semantics unchanged but propagation improves. -- Break symmetry structurally: order constraints, fixed anchors, `precede`, monotone bins. -- Use LDSB only with supported branching/value configurations; validate symmetry assumptions. -- Match propagation level to complexity (`IPL_DOM` only where payoff > cost). -- Replace weak decompositions with stronger globals (`count` GCC, `binpacking`, `circuit`, `extensional`). -- Cache reusable heavy artifacts (tuple sets, shared arrays) keyed by shape/parameters. -- For arrays requiring non-shared vars, call `unshare(...)` once and reuse the result. -- Use branch filters/print functions for targeted branching and observability. -- When executing code between branchers, remember propagation is still explicit on recomputation paths. -- For optimization models, branch on cost-driving vars first, tie with objective structure. - -## Pitfalls -- Weak domains at model start causing huge trees. -- Forgetting to update all variable members in cloning constructor. -- Assuming MiniModel nonlinear expressions stay monolithic; many decompose. -- Assuming reified non-functional decompositions imply `b=false`; they can fail instead. -- Treating Boolean vars as subclass of integer vars. -- Ignoring exceptions from invalid arguments/overflow. -- Using domain propagation for `linear` indiscriminately (can be exponential). -- Recomputing identical tuple sets/shared maps per post. -- Repeated implicit-style unsharing patterns that create unnecessary vars/propagators. -- Combining LDSB with unrelated static symmetry breaking without safety analysis. -- Leaving major value/variable symmetries unbroken. diff --git a/skills/gecode-modeling/agents/openai.yaml b/skills/gecode-modeling/agents/openai.yaml deleted file mode 100644 index dd3bb29..0000000 --- a/skills/gecode-modeling/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Gecode Modeling" - short_description: "Model Gecode CP problems and search setup" - default_prompt: "Guide concise Gecode modeling choices, constraints, branchings, and search configuration." diff --git a/skills/gecode-propagator-implementation/SKILL.md b/skills/gecode-propagator-implementation/SKILL.md deleted file mode 100644 index b6fe4ed..0000000 --- a/skills/gecode-propagator-implementation/SKILL.md +++ /dev/null @@ -1,40 +0,0 @@ ---- -name: gecode-propagator-implementation -description: "Implement and optimize custom Gecode propagators: posting, propagate/reschedule lifecycle, propagation conditions, domain iterators, advisors, reification, and rewriting. Use when adding new constraints or improving propagation performance/safety." ---- - -# Gecode Propagator Implementation - -## Core -- Propagator computes on views, not model variables. -- Implement post function + actor lifecycle (`copy`, `dispose`, `cost`, `reschedule`, `propagate`). -- Use `Home` for posting context; use fail/check macros. -- Return honest `ExecStatus`: `ES_FAILED`, `ES_SUBSUMED`, `ES_FIX`, `ES_NOFIX`. -- Respect obligations: correctness, checking, contracting, monotonicity (or waived), subscription completeness, update completeness. -- Respect implementation obligations: subsumption complete, cloning conservative, subscription correct. -- Use patterns (`Unary/Binary/Ternary/Nary`, mixed variants) to reduce boilerplate. - -## Key Patterns -- Do cheap pruning in `post()`; skip posting when trivially subsumed/failed. -- Select minimal propagation conditions (`*_VAL`, `*_BND`, `*_DOM`). -- Prefer iterator-based domain ops (`inter_r`, `narrow_r`, `minus_r`) for domain propagation. -- Use fixpoint reasoning deliberately: return `ES_FIX` only when justified. -- Use `ModEventDelta` and staging to combine cheap and expensive propagation phases. -- Use advisors for incremental change localization. -- For advisors, maintain council lifecycle and ensure rescheduling/subscription completeness. -- Rewrite propagators (`GECODE_REWRITE`) when state simplifies. -- Use reified/rewriting patterns to remove reification overhead once control literals decide. -- Template propagators on view types for reuse. -- If decomposition is propagation-weak, prefer dedicated propagator or extensional surrogate. -- Treat expensive support data as cacheable object, not per-post recomputation. - -## Pitfalls -- Modifying a view while iterating its domain iterator. -- Returning `ES_FIX` when not actually at fixpoint. -- Returning `ES_NOFIX` when propagation is idempotent and could reach fixpoint inside the same `propagate()` call (causes avoidable re-scheduling). -- Missing view updates/subscription cancellation during cloning/disposal. -- Using external resources without `AP_DISPOSE` notice/ignore discipline. -- Continuing execution after subsuming/disposing actor. -- Failing to check modification-event failure after view updates. -- Breaking subscription completeness when using advisors/dynamic subscriptions. -- Expecting two weak propagators (`distinct` + `linear`) to match joint reasoning of one stronger constraint. diff --git a/skills/gecode-propagator-implementation/agents/openai.yaml b/skills/gecode-propagator-implementation/agents/openai.yaml deleted file mode 100644 index e975e10..0000000 --- a/skills/gecode-propagator-implementation/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Gecode Propagator Implementation" - short_description: "Implement custom Gecode propagators safely" - default_prompt: "Provide concise implementation guidance for Gecode propagators, obligations, and optimization." diff --git a/skills/gecode-search-engine-implementation/SKILL.md b/skills/gecode-search-engine-implementation/SKILL.md deleted file mode 100644 index 27fc33a..0000000 --- a/skills/gecode-search-engine-implementation/SKILL.md +++ /dev/null @@ -1,38 +0,0 @@ ---- -name: gecode-search-engine-implementation -description: "Implement custom Gecode search engines: status/choice/clone/commit orchestration, recomputation strategies (full/hybrid/adaptive), last-alternative optimization, branch-and-bound integration, and invariants for choice compatibility and completeness." ---- - -# Gecode Search Engine Implementation - -## Core -- Implement engines against the `Space` interface (`status`, `choice`, `clone`, `commit`). -- Maintain explicit ownership for all `Space*` and `Choice*` objects. -- Respect compatibility invariants: choices are valid only for clone-related spaces. -- Treat `choice()` as invalidating earlier choices on that space. -- Handle all `SpaceStatus` cases: `SS_FAILED`, `SS_SOLVED`, `SS_BRANCH`. - -## Key Patterns -- Keep a clear split between exploration mode and recomputation mode. -- Use edge/path state to store choices, alternatives, and optional clones. -- For recomputation, replay commits from nearest stored clone (or root clone). -- Apply LAO (last-alternative optimization) to avoid unnecessary stored choices/commits. -- Use hybrid recomputation with commit distance to cap recomputation cost. -- Use adaptive recomputation to add clones where repeated failures indicate benefit. -- Integrate branch-and-bound by constraining future spaces against best solution. -- Keep restart/meta-engine hooks explicit (`master`, `slave`) when required. -- Wire statistics and stop-object checks consistently. - -## Invariants -- Recomputed spaces must follow the same decision path as stored edge choices. -- Commit order must match original choice generation order. -- If recomputation fails due to nondeterminism/weak monotonicity effects, recover path state safely and continue search. -- Cloning/copying must never mutate model state outside allowed operations. - -## Pitfalls -- Reusing stale choices after another `choice()` call. -- Mixing incompatible choices/spaces and triggering `SpaceNoBrancher`. -- Forgetting to delete choices and returned solution spaces. -- Assuming deterministic node order under parallel execution. -- Overusing no-goods depth without accounting for memory and LAO tradeoffs. -- Reporting completeness when stop/cutoff/meta policy makes the run incomplete. diff --git a/skills/gecode-search-engine-implementation/agents/openai.yaml b/skills/gecode-search-engine-implementation/agents/openai.yaml deleted file mode 100644 index 0b1085b..0000000 --- a/skills/gecode-search-engine-implementation/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Gecode Search Engine Implementation" - short_description: "Implement custom Gecode search engines safely" - default_prompt: "Guide implementation of a custom Gecode search engine, including recomputation, invariants, ownership, and branch-and-bound integration." diff --git a/skills/gecode-search-engines/SKILL.md b/skills/gecode-search-engines/SKILL.md deleted file mode 100644 index 8277987..0000000 --- a/skills/gecode-search-engines/SKILL.md +++ /dev/null @@ -1,50 +0,0 @@ ---- -name: gecode-search-engines -description: "Use existing Gecode search engines and meta-engines effectively: DFS/BAB/LDS, restart and portfolio setup, no-goods, parallel behavior, and completeness tradeoffs. Use when selecting/tuning search, not implementing new engines." ---- - -# Gecode Search Engines - -## Core -- This skill is for using built-in engines, not implementing custom engines. -- Base engines: `DFS`, `BAB`, `LDS`. -- Meta engines: `RBS` (restart-based) and `PBS` (portfolio-based). -- Key options live in `Search::Options` (`threads`, `c_d`, `a_d`, `clone`, `stop`, `cutoff`, `nogoods_limit`, `assets`, `slice`, `tracer`). -- For optimization, use `BAB`-style search with a valid model-side objective/constrain setup. - -## Engine Selection -- Use `DFS` for baseline complete search/enumeration. -- Use `BAB` for best-solution search. -- Use `LDS` when the branching heuristic is strong and you want discrepancy-ordered exploration. -- Use `RBS` to improve robustness via cutoffs, restart policies, and optional no-goods. -- Use `PBS` to improve robustness via asset diversification (heuristics/models/engines/options). - -## Restart, Portfolio, and Incomplete Search Interactions -- `RBS` requires a cutoff generator in options. -- In restart search, `master()` decides restart behavior and can post no-goods; `slave()` configures each restart run. -- In restart search, `slave()` return value matters: - `true` means complete slave search; `false` means intentionally incomplete search (for example LNS neighborhoods). -- In portfolio search, `slave()` return value has no meaning. -- `PBS` uses one engine type per asset; mixed portfolios use `SEBs(...)` to combine `dfs/lds/bab/rbs/pbs` with per-asset options. -- Do not mix best-solution and non-best assets in one SEB portfolio (`Search::MixedBest`). -- For restart-based best-solution assets in portfolios, use `RBS`. - -## No-Good Nuances -- Restart no-goods are only available from `DFS`/`BAB`, not `LDS`. -- Enable with `nogoods_limit > 0`; depth is a memory/benefit tradeoff. -- Larger no-good depth limits reduce LAO effectiveness near root and can significantly increase memory. -- Not all branchers support no-goods; float branchers and execution branchers do not. -- Parallel search usually yields fewer extractable no-goods. - -## Parallel and Portfolio Semantics -- Parallel search is intentionally nondeterministic (solution order, node counts, runtime). -- `assets` and `threads` are allocated conservatively in portfolios. -- Sequential portfolios use failure slices (`slice`) per asset (round-robin). -- With `threads > assets`, extra threads can be used inside asset engines. - -## Pitfalls -- Expecting deterministic behavior from restart/portfolio/parallel runs. -- Treating restart/portfolio as complete when stop conditions, cutoffs, or `slave()==false` make runs incomplete. -- Forgetting to diversify assets and then expecting portfolio gains. -- Assuming no-goods are always available/effective regardless of brancher mix and parallelism. -- Forgetting that `master()`/`slave()` policy choices directly change search completeness and restart behavior. diff --git a/skills/gecode-search-engines/agents/openai.yaml b/skills/gecode-search-engines/agents/openai.yaml deleted file mode 100644 index 40c8de5..0000000 --- a/skills/gecode-search-engines/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Gecode Search Engines" - short_description: "Use and tune existing Gecode search engines" - default_prompt: "Recommend and configure Gecode DFS/BAB/LDS/RBS/PBS usage, including restart/portfolio/no-good and completeness tradeoffs." diff --git a/skills/gecode/SKILL.md b/skills/gecode/SKILL.md new file mode 100644 index 0000000..df0fbed --- /dev/null +++ b/skills/gecode/SKILL.md @@ -0,0 +1,35 @@ +--- +name: gecode +description: "Gecode architecture, modeling, propagators, branchers, memory management, search engine usage and implementation, recomputation/cloning behavior, and downstream CMake consumption. Use for any materially Gecode-specific task: building or refining models, implementing custom constraints or branchers, tuning DFS/BAB/RBS/PBS/LDS search, debugging solver behavior, reasoning about space memory/lifecycle semantics, or integrating Gecode into CMake projects." +--- + +# Gecode + +Use this skill as the entry point for any Gecode-specific task. Keep the body lean: route to the relevant reference file, load only what the task needs, and avoid pulling unrelated topic docs into context. + +## Routing +- Start with `references/general-knowledge.md` when the task is broad, diagnostic, or about solver/runtime semantics. +- Read `references/modeling.md` for model structure, variables, constraints, branching setup, and built-in search configuration. +- Read `references/propagator-implementation.md` for custom propagator design, posting, propagation lifecycle, advisors, and rewriting. +- Read `references/brancher-implementation.md` for custom branchers, choices, commits, archiving, and NGL support. +- Read `references/memory-handling.md` for space/region/heap allocation, handles, clone footprint, and disposal obligations. +- Read `references/search-engines.md` for using and tuning built-in engines such as `DFS`, `BAB`, `LDS`, restart, and portfolio search. +- Read `references/search-engine-implementation.md` for custom engine orchestration, recomputation strategy, LAO, and completeness invariants. +- Read `references/cmake-consumption.md` for `find_package(Gecode CONFIG)`, target usage, version checks, and vendored fallback patterns. + +## Operating Rules +- Read only the reference file or files needed for the current task. +- Combine references only when the task genuinely crosses boundaries, such as a custom propagator with nontrivial memory strategy or a search-engine bug tied to choice compatibility. +- Prefer the narrowest useful reference set first, then expand if the user asks for adjacent concerns. +- Keep answers Gecode-specific. If the request is generic CMake, generic C++ memory, or generic CP theory without a real Gecode angle, do not over-apply this skill. +- When a task spans modeling and runtime behavior, anchor the explanation in `references/general-knowledge.md` and then pull in the specialized topic doc. + +## Reference Index +- `references/general-knowledge.md`: spaces, propagation/search lifecycle, cloning, recomputation, choices, and debugging mental model. +- `references/modeling.md`: variable selection, globals, reification, symmetry, branching, and search setup in ordinary models. +- `references/propagator-implementation.md`: actor lifecycle, `ExecStatus`, propagation conditions, iterators, advisors, and rewrite patterns. +- `references/brancher-implementation.md`: `status`, `choice`, `commit`, archive compatibility, NGLs, and heuristic encoding. +- `references/memory-handling.md`: memory areas, lazy vs eager allocation, shared/local handles, and `AP_DISPOSE` discipline. +- `references/search-engines.md`: engine selection, restart/portfolio tradeoffs, no-goods, parallel semantics, and completeness caveats. +- `references/search-engine-implementation.md`: custom engine state, replay/recomputation, ownership, branch-and-bound integration, and invariants. +- `references/cmake-consumption.md`: package-config integration, exported targets, component selection, and fetch fallback. diff --git a/skills/gecode/agents/openai.yaml b/skills/gecode/agents/openai.yaml new file mode 100644 index 0000000..c1973fe --- /dev/null +++ b/skills/gecode/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "Gecode" + short_description: "Route any Gecode task to the right internal reference" + default_prompt: "Handle this Gecode-specific task by reading only the relevant internal reference files, then provide concise, technically accurate guidance." diff --git a/skills/gecode/references/brancher-implementation.md b/skills/gecode/references/brancher-implementation.md new file mode 100644 index 0000000..cf38a25 --- /dev/null +++ b/skills/gecode/references/brancher-implementation.md @@ -0,0 +1,33 @@ +# Gecode Brancher Implementation + +## Core +- Brancher is the actor that implements branching behavior. +- Implement `status`, `choice(Space&)`, `choice(const Space&, Archive&)`, `commit`, `print`, `copy`, and `dispose`. +- Choice stores only space-independent commit data. +- `commit` must work with recomputed or cloned spaces using only the choice payload. +- Choices must be archive-compatible and deterministic. +- Branchers execute in queue order of posting. +- Optional `ngl()` adds no-good support. +- `status()==false` does not imply immediate disposal; commits for earlier choices must remain valid. + +## Key Patterns +- Track the first candidate index such as `start` to avoid rescanning. +- Keep the choice payload minimal, for example `pos`, `val`, and alternative count, and archive it deterministically. +- Use binary alternatives such as `eq` versus `nq` unless an assignment brancher genuinely needs a single alternative. +- Implement an NGL class with `status`, `prune`, `subscribe`, `cancel`, and `reschedule`. +- For complementary last alternatives, `ngl()` can return `NULL` when that is semantically valid. +- Reuse branchers through views, notably minus views for max-style variants. +- Encode the problem heuristic explicitly, such as Warnsdorff or best-fit slack. +- Mix assignment-style one-alternative choices with pruning alternatives only when the heuristic justifies it. +- Design second alternatives to embed symmetry breaking when safe. +- Pair the brancher with branch print callbacks for explainability and debugging. + +## Pitfalls +- Storing views or pointers to space state inside choice objects. +- Disposing the brancher too early when `status()` becomes false. +- Depending on mutable brancher state not encoded in the choice for `commit()`. +- Using choices after invalidation by a later `choice()` call on the same space. +- Not skipping assigned views, causing repeated same choice or an infinite tree. +- Violating recomputation invariants and commit-order assumptions. +- Using generic variable-value branching when a structure-aware heuristic is required. +- Forgetting that brancher disposal is not automatic when external resources exist. diff --git a/skills/gecode/references/cmake-consumption.md b/skills/gecode/references/cmake-consumption.md new file mode 100644 index 0000000..6e59642 --- /dev/null +++ b/skills/gecode/references/cmake-consumption.md @@ -0,0 +1,39 @@ +# Gecode CMake Consumption + +## Core +- Prefer config-package consumption: `find_package(Gecode CONFIG REQUIRED)`. +- Link downstream targets to `Gecode::gecode` unless explicit component granularity is required. +- Require `Gecode_VERSION >= 6.3.0` and use package version checks rather than parsing `gecode/support/config.hpp`. +- Hint package location with `Gecode_ROOT` or `CMAKE_PREFIX_PATH`. +- Keep `cmake_minimum_required(VERSION 3.21)` or newer for parity with Gecode's build. + +## Key Patterns +- Minimal integration: + ```cmake + find_package(Gecode CONFIG REQUIRED) + target_link_libraries(app PRIVATE Gecode::gecode) + ``` +- Component-specific integration: + ```cmake + find_package(Gecode CONFIG REQUIRED COMPONENTS driver) + target_link_libraries(app PRIVATE Gecode::gecodedriver) + ``` +- Version guard: + ```cmake + if(SOME_STRICT_OPTION AND Gecode_VERSION VERSION_LESS "6.3.0") + message(FATAL_ERROR "Gecode >= 6.3.0 required, found ${Gecode_VERSION}") + endif() + ``` +- Try installed package first with `find_package(Gecode CONFIG QUIET)` when discovery is optional. +- If the package is absent and project policy allows vendoring, use `FetchContent` and set Gecode cache options before `FetchContent_MakeAvailable(...)`. +- Require `TARGET Gecode::gecode` after resolution and fail fast with actionable error text if it is missing. +- Mark Gecode targets as `SYSTEM` in strict-warning projects to isolate third-party headers from local warning policy. +- For pinned source fallbacks, prefer stable release tags or commits over long-lived feature branches. +- When migrating away from custom discovery modules, remove manual imported-target composition and library probing once package config is the baseline. +- Keep strict version toggles if desired, but enforce them against `Gecode_VERSION`. + +## Pitfalls +- Calling `find_package(Gecode REQUIRED)` without `CONFIG` and accidentally resolving old module-mode shims. +- Assuming optional components exist without checking enabled modules in the installed build. +- Mixing custom imported-target composition with package-provided targets in the same code path. +- Parsing headers for version when package metadata already provides `Gecode_VERSION`. diff --git a/skills/gecode/references/general-knowledge.md b/skills/gecode/references/general-knowledge.md new file mode 100644 index 0000000..441ddb8 --- /dev/null +++ b/skills/gecode/references/general-knowledge.md @@ -0,0 +1,34 @@ +# Gecode General Knowledge + +## Core +- Space is home for variables, propagators, branchers, and optimization order. +- Propagation is explicit: call `status()`. +- Search primitives are `status()`, `choice()`, `clone()`, `commit()`, and `constrain()`. +- Space status values are `SS_FAILED`, `SS_SOLVED`, and `SS_BRANCH`. +- Choice is a space-independent descriptor; alternatives are indexed `0..n-1`. +- Choice compatibility is clone-based: a choice is valid for its source space and clones. +- `choice()` invalidates previous choices for later `commit()` on that space. +- Clone only stable, non-failed spaces. +- Branchers run in posting order. +- Recomputation can be nondeterministic with weakly monotonic propagation while remaining sound and complete. + +## Key Patterns +- Model as `class M : public Space`. +- Implement copy constructor and virtual `copy()`. +- In space cloning, clone variable arrays via `x.update(home, s.x)`; do not use a variable-array copy constructor. +- After `status()==SS_BRANCH`, compute `choice()` immediately. +- Seed the search engine with the model, then delete the seed model. +- Treat a solution as a space closure over member variables. +- Use groups and tracing for observability and selective control. +- Improve models in loops: baseline, stronger propagation, better branching, then tuned search. +- Measure with nodes, time, and restarts, not runtime alone. +- Treat symmetry handling as a first-class design concern, not post-processing. + +## Pitfalls +- Assuming posting performs full propagation. +- Calling the `Space` copy constructor directly instead of `clone()`. +- Using stale choices after invalidating them with a later `choice()` call. +- Forgetting ownership and deletion of choices and returned solution spaces. +- Cloning unstable or failed spaces. +- Assuming parallel search preserves sequential solution order or runtime profile. +- Assuming one modeling pass is enough; most nontrivial case studies need staged refinement. diff --git a/skills/gecode/references/memory-handling.md b/skills/gecode/references/memory-handling.md new file mode 100644 index 0000000..fd8b91a --- /dev/null +++ b/skills/gecode/references/memory-handling.md @@ -0,0 +1,32 @@ +# Gecode Memory Handling + +## Core +- Memory areas are space, region, heap, and space freelists. +- `alloc`, `realloc`, and `free` follow C++ object lifecycle semantics. +- Space memory is reclaimed automatically on space deletion and fits stable-size actor data. +- Region is a temporary arena with implicit free on region destruction. +- Heap is for frequently resized or dynamic structures. +- Search memory profile favors pristine clones, so allocation timing matters. +- Shared handles point to cross-space or cross-thread shared heap objects with reference counting. +- Local handles point to per-space shared objects copied on cloning. + +## Key Patterns +- Allocate fixed actor members in home space. +- Allocate resize-heavy buffers on the heap and free them in `dispose()`. +- Build heavy internal state lazily on first propagation when possible. +- Choose eager, lazy, or hybrid allocation based on clone footprint and expected hit rate. +- Use regions for short-lived iterators and temporary buffers. +- Call `Region::free()` at clear control-flow boundaries to maximize reuse. +- Use `SharedHandle` for immutable or global lookup data. +- Use `LocalHandle` for shared per-space mutable state. +- Use `IntSharedArray` or related shared arrays for read-only large data reused across clones. +- For brancher choices using heap buffers, pair allocation and free, and register `AP_DISPOSE`. +- Use `Region` for per-choice scratch arrays to avoid heap churn. + +## Pitfalls +- Frequent resize in space memory causing fragmentation. +- Forgetting `home.notice(..., AP_DISPOSE)` for external or heap resources. +- Forgetting the matching `home.ignore(..., AP_DISPOSE)` in the dispose path. +- Assuming identical alignment guarantees across space, heap, and region memory. +- Leaking ownership assumptions across cloning boundaries. +- Allocating per-choice temporary arrays on the heap in hot paths. diff --git a/skills/gecode/references/modeling.md b/skills/gecode/references/modeling.md new file mode 100644 index 0000000..c95cb17 --- /dev/null +++ b/skills/gecode/references/modeling.md @@ -0,0 +1,40 @@ +# Gecode Modeling + +## Core +- Define the model as a `Space` subclass. +- Create typed variable arrays with tight domains early. +- Post constraints via post functions such as `rel`, `linear`, `distinct`, and the set/float variants. +- Post branching via `branch(...)`; variable and value strategy together define the tree shape. +- Use search engines such as `DFS`, `BAB`, or restart/portfolio variants according to the objective. +- MiniModel adds expression syntax via `expr(...)`, `rel(...)`, and matrix/channel helpers. +- Reified modeling supports full and half reification; decomposition semantics matter. + +## Key Patterns +- Prefer global constraints over manual decompositions when available. +- Keep branchings explicit and problem-specific for scale. +- Use multiple branchers intentionally; creation order matters. +- Tune search options such as recomputation distance, restarts, no-goods, and stop objects. +- Use tracing, Gist, or CPProfiler for diagnostics. +- Add implied constraints when semantics stay unchanged but propagation improves. +- Break symmetry structurally with order constraints, fixed anchors, `precede`, or monotone bins. +- Use LDSB only with supported branching and value configurations, and validate symmetry assumptions. +- Match propagation level to complexity; use `IPL_DOM` only where the payoff exceeds the cost. +- Replace weak decompositions with stronger globals such as `count`, `binpacking`, `circuit`, or `extensional`. +- Cache reusable heavy artifacts such as tuple sets or shared arrays keyed by shape and parameters. +- For arrays requiring non-shared variables, call `unshare(...)` once and reuse the result. +- Use branch filters and print functions for targeted branching and observability. +- When executing code between branchers, remember propagation is still explicit on recomputation paths. +- For optimization models, branch on cost-driving variables first and tie-break with objective structure. + +## Pitfalls +- Weak domains at model start causing huge trees. +- Forgetting to update every variable member in the cloning constructor. +- Assuming MiniModel nonlinear expressions stay monolithic; many decompose. +- Assuming reified non-functional decompositions imply `b=false`; they can fail instead. +- Treating Boolean variables as a subclass of integer variables. +- Ignoring exceptions from invalid arguments or overflow. +- Using domain propagation for `linear` indiscriminately when it can be exponential. +- Recomputing identical tuple sets or shared maps per post. +- Repeated implicit unsharing patterns that create unnecessary variables and propagators. +- Combining LDSB with unrelated static symmetry breaking without safety analysis. +- Leaving major value or variable symmetries unbroken. diff --git a/skills/gecode/references/propagator-implementation.md b/skills/gecode/references/propagator-implementation.md new file mode 100644 index 0000000..c6afa08 --- /dev/null +++ b/skills/gecode/references/propagator-implementation.md @@ -0,0 +1,35 @@ +# Gecode Propagator Implementation + +## Core +- Propagators compute on views, not model variables. +- Implement a post function and the actor lifecycle: `copy`, `dispose`, `cost`, `reschedule`, and `propagate`. +- Use `Home` for posting context and use fail/check macros. +- Return honest `ExecStatus`: `ES_FAILED`, `ES_SUBSUMED`, `ES_FIX`, or `ES_NOFIX`. +- Respect obligations around correctness, checking, contracting, monotonicity or waived monotonicity, subscription completeness, and update completeness. +- Respect implementation obligations: subsumption complete, cloning conservative, and subscription correct. +- Use standard patterns such as `Unary`, `Binary`, `Ternary`, `Nary`, or mixed variants to reduce boilerplate. + +## Key Patterns +- Do cheap pruning in `post()` and skip posting when already subsumed or failed. +- Select the weakest sound propagation condition: `*_VAL`, `*_BND`, or `*_DOM`. +- Prefer iterator-based domain operations such as `inter_r`, `narrow_r`, or `minus_r` for domain propagation. +- Use fixpoint reasoning deliberately; return `ES_FIX` only when justified. +- Use `ModEventDelta` and staging to combine cheap and expensive propagation phases. +- Use advisors for incremental change localization. +- Maintain council lifecycle correctly when advisors are present, including rescheduling and subscription completeness. +- Rewrite propagators with `GECODE_REWRITE` when state simplifies enough to switch representation. +- Use reified and rewriting patterns to remove reification overhead once control literals decide the mode. +- Template propagators on view types for reuse. +- If decomposition is propagation-weak, prefer a dedicated propagator or an extensional surrogate. +- Treat expensive support data as a cacheable object rather than recomputing it per post. + +## Pitfalls +- Modifying a view while iterating its domain iterator. +- Returning `ES_FIX` when the propagator is not actually at fixpoint. +- Returning `ES_NOFIX` when the propagator is idempotent and could finish inside the same `propagate()` call. +- Missing view updates or subscription cancellation during cloning or disposal. +- Using external resources without `AP_DISPOSE` notice/ignore discipline. +- Continuing execution after subsuming or disposing the actor. +- Failing to check modification-event failure after view updates. +- Breaking subscription completeness when using advisors or dynamic subscriptions. +- Expecting two weak propagators such as `distinct` plus `linear` to match the joint reasoning of one stronger constraint. diff --git a/skills/gecode/references/search-engine-implementation.md b/skills/gecode/references/search-engine-implementation.md new file mode 100644 index 0000000..235dc06 --- /dev/null +++ b/skills/gecode/references/search-engine-implementation.md @@ -0,0 +1,33 @@ +# Gecode Search Engine Implementation + +## Core +- Implement engines against the `Space` interface: `status`, `choice`, `clone`, and `commit`. +- Maintain explicit ownership for all `Space*` and `Choice*` objects. +- Respect compatibility invariants: choices are valid only for clone-related spaces. +- Treat `choice()` as invalidating earlier choices on that space. +- Handle all `SpaceStatus` cases: `SS_FAILED`, `SS_SOLVED`, and `SS_BRANCH`. + +## Key Patterns +- Keep a clear split between exploration mode and recomputation mode. +- Use edge or path state to store choices, alternatives, and optional clones. +- For recomputation, replay commits from the nearest stored clone or the root clone. +- Apply LAO, the last-alternative optimization, to avoid unnecessary stored choices and commits. +- Use hybrid recomputation with a commit distance to cap replay cost. +- Use adaptive recomputation to add clones where repeated failures show that recomputation is too expensive. +- Integrate branch-and-bound by constraining future spaces against the current best solution. +- Keep restart and meta-engine hooks explicit, such as `master` and `slave`, when required. +- Wire statistics and stop-object checks consistently. + +## Pitfalls +- Reusing stale choices after another `choice()` call. +- Mixing incompatible choices and spaces and triggering `SpaceNoBrancher`. +- Forgetting to delete choices and returned solution spaces. +- Assuming deterministic node order under parallel execution. +- Overusing no-good depth without accounting for memory and LAO tradeoffs. +- Reporting completeness when stop, cutoff, or meta-engine policy makes the run incomplete. + +## Invariants +- Recomputed spaces must follow the same decision path as the stored edge choices. +- Commit order must match original choice generation order. +- If recomputation fails due to nondeterminism or weak monotonicity effects, recover path state safely and continue search. +- Cloning and copying must never mutate model state outside the allowed operations. diff --git a/skills/gecode/references/search-engines.md b/skills/gecode/references/search-engines.md new file mode 100644 index 0000000..65bf86b --- /dev/null +++ b/skills/gecode/references/search-engines.md @@ -0,0 +1,38 @@ +# Gecode Search Engines + +## Core +- This reference is for using built-in engines, not implementing custom engines. +- Base engines are `DFS`, `BAB`, and `LDS`. +- Meta engines are `RBS` for restart-based search and `PBS` for portfolio-based search. +- Key options live in `Search::Options`, including `threads`, `c_d`, `a_d`, `clone`, `stop`, `cutoff`, `nogoods_limit`, `assets`, `slice`, and `tracer`. +- For optimization, use `BAB`-style search with a valid model-side objective and constrain setup. + +## Key Patterns +- Use `DFS` for baseline complete search and enumeration. +- Use `BAB` for best-solution search. +- Use `LDS` when the branching heuristic is strong and discrepancy-ordered exploration is desirable. +- Use `RBS` to improve robustness through cutoffs, restart policies, and optional no-goods. +- Use `PBS` to improve robustness through asset diversification across heuristics, models, engines, or options. +- In restart search, let `master()` decide restart behavior and optional no-good posting. +- In restart search, let `slave()` signal completeness intentionally: `true` for complete slave search, `false` for deliberate incompleteness such as LNS neighborhoods. +- In portfolio search, remember `slave()` return value has no meaning. +- For restart-based best-solution assets in portfolios, use `RBS`. +- Diversify assets intentionally; identical assets rarely justify portfolio overhead. + +## Pitfalls +- Expecting deterministic behavior from restart, portfolio, or parallel runs. +- Treating restart or portfolio search as complete when stop conditions, cutoffs, or `slave()==false` make the run incomplete. +- Forgetting to diversify assets and then expecting portfolio gains. +- Assuming no-goods are always available or effective regardless of brancher mix and parallelism. +- Forgetting that `master()` and `slave()` policy choices directly change search completeness and restart behavior. + +## No-Good and Parallel Nuances +- Restart no-goods are available from `DFS` and `BAB`, not `LDS`. +- Enable no-goods with `nogoods_limit > 0`; depth is a memory-versus-benefit tradeoff. +- Larger no-good depth limits reduce LAO effectiveness near the root and can raise memory use significantly. +- Not all branchers support no-goods; float branchers and execution branchers do not. +- Parallel search usually yields fewer extractable no-goods. +- Parallel search is intentionally nondeterministic in solution order, node counts, and runtime. +- In portfolios, `assets` and `threads` are allocated conservatively. +- Sequential portfolios use failure slices via `slice` for round-robin asset scheduling. +- With `threads > assets`, extra threads can be used inside asset engines. From 16d9ab2667f8a63c03297dd360b70e37422b2346 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Mon, 9 Mar 2026 07:53:13 +0100 Subject: [PATCH 06/10] chore(repo): update docs and validation for umbrella skill --- README.md | 29 ++++++++++--------- evals/gecode-trigger-evals.json | 50 +++++++++++++++++++++++++++++++++ scripts/validate_skills.py | 17 +++++++---- 3 files changed, 77 insertions(+), 19 deletions(-) create mode 100644 evals/gecode-trigger-evals.json diff --git a/README.md b/README.md index fa6425f..70d6616 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Gecode Skills -Canonical skill repository for Gecode-focused AI agent skills. +Canonical skill repository for the umbrella Gecode AI agent skill. Install with: @@ -17,25 +17,28 @@ npx skills add Gecode/gecode-skills --list Install a single skill: ```bash -npx skills add Gecode/gecode-skills --skill gecode-modeling +npx skills add Gecode/gecode-skills --skill gecode ``` -## Available Skills +## Available Skill -- `gecode-general-knowledge` -- `gecode-cmake-consumption` -- `gecode-modeling` -- `gecode-propagator-implementation` -- `gecode-brancher-implementation` -- `gecode-memory-handling` -- `gecode-search-engines` -- `gecode-search-engine-implementation` +- `gecode` + +The skill routes internally to focused reference documents for: +- Gecode architecture and runtime semantics +- modeling and search setup +- custom propagators +- custom branchers +- memory management +- built-in search engines +- custom search engine implementation +- downstream CMake consumption ## Contributing ### Skill structure -Each skill must be under: +The skill must be under: - `skills//SKILL.md` @@ -45,7 +48,7 @@ Optional metadata for UIs can be added at: ### Required frontmatter -Each `SKILL.md` must include YAML frontmatter with: +`SKILL.md` must include YAML frontmatter with: - `name` - `description` diff --git a/evals/gecode-trigger-evals.json b/evals/gecode-trigger-evals.json new file mode 100644 index 0000000..b483bd5 --- /dev/null +++ b/evals/gecode-trigger-evals.json @@ -0,0 +1,50 @@ +[ + { + "query": "I'm implementing a cumulative-style constraint in Gecode and the decomposition is too weak. I think I need a custom propagator with better domain filtering. Can you sketch the post/propagate structure and what propagation conditions I should subscribe to?", + "should_trigger": true + }, + { + "query": "I need a custom Gecode brancher for a packing model where the second alternative should encode a symmetry-breaking exclusion. How should I structure the choice payload and commit logic so recomputation stays safe?", + "should_trigger": true + }, + { + "query": "My Gecode model solves, but BAB is crawling. Help me decide whether to stay on BAB, switch to restart-based search, or diversify with PBS. I also want to understand the completeness tradeoffs.", + "should_trigger": true + }, + { + "query": "Can you help debug a Gecode issue where a stored choice seems invalid after recomputation? The model uses custom branchers and I suspect I'm breaking clone or commit invariants.", + "should_trigger": true + }, + { + "query": "I am packaging a C++ solver that depends on Gecode 6.3+ and I want the downstream CMake project to use find_package(Gecode CONFIG) with a FetchContent fallback when it's not installed.", + "should_trigger": true + }, + { + "query": "Please model this scheduling problem in Gecode with strong global constraints, sensible symmetry breaking, and a branching strategy that focuses on the cost-driving variables first.", + "should_trigger": true + }, + { + "query": "I need to clean up a generic CMakeLists.txt for a small SDL app. There is no Gecode involved, I just want better target_link_libraries usage and install rules.", + "should_trigger": false + }, + { + "query": "How would you model a nurse rostering problem in constraint programming? I'm open to any solver or even OR-Tools, I mainly want high-level CP advice.", + "should_trigger": false + }, + { + "query": "I'm debugging a C++ memory leak around std::shared_ptr and custom allocators. This is ordinary application code, not a solver, and I'm not using Gecode.", + "should_trigger": false + }, + { + "query": "Can you explain branch and bound versus depth-first search in general terms? I don't need implementation details for any particular library.", + "should_trigger": false + }, + { + "query": "I have an old FindFoo.cmake module and want to migrate to package config usage. The library is our in-house SDK, not Gecode.", + "should_trigger": false + }, + { + "query": "What are good heuristics for graph coloring in CP, and how do I think about symmetry? Solver-agnostic advice is fine.", + "should_trigger": false + } +] diff --git a/scripts/validate_skills.py b/scripts/validate_skills.py index a6e833d..04126db 100755 --- a/scripts/validate_skills.py +++ b/scripts/validate_skills.py @@ -7,6 +7,7 @@ REPO_ROOT = Path(__file__).resolve().parents[1] SKILLS_DIR = REPO_ROOT / "skills" +EXPECTED_SKILLS = {"gecode"} def parse_frontmatter(skill_md: Path) -> dict[str, str]: @@ -48,11 +49,15 @@ def main() -> int: errors: list[str] = [] seen_names: dict[str, Path] = {} - skill_dirs = sorted( - p for p in SKILLS_DIR.iterdir() if p.is_dir() and p.name.startswith("gecode-") - ) - if not skill_dirs: - errors.append("no gecode-* skill directories found under skills/") + skill_dirs = sorted(p for p in SKILLS_DIR.iterdir() if p.is_dir()) + actual_names = {p.name for p in skill_dirs} + missing = sorted(EXPECTED_SKILLS - actual_names) + unexpected = sorted(actual_names - EXPECTED_SKILLS) + + if missing: + errors.append(f"missing expected skills: {', '.join(missing)}") + if unexpected: + errors.append(f"unexpected skill directories: {', '.join(unexpected)}") for skill_dir in skill_dirs: skill_md = skill_dir / "SKILL.md" @@ -94,7 +99,7 @@ def main() -> int: print(f"- {e}") return 1 - print(f"Validated {len(skill_dirs)} skills successfully.") + print(f"Validated {len(skill_dirs)} skill successfully.") return 0 From 84a13c3e978760cb93946653c8347d1140cc89ca Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Mon, 9 Mar 2026 16:38:12 +0100 Subject: [PATCH 07/10] Move most general knowledge into main skill --- skills/gecode/SKILL.md | 37 +++++++++++++++++-- skills/gecode/references/general-knowledge.md | 27 +------------- 2 files changed, 35 insertions(+), 29 deletions(-) diff --git a/skills/gecode/SKILL.md b/skills/gecode/SKILL.md index df0fbed..2310765 100644 --- a/skills/gecode/SKILL.md +++ b/skills/gecode/SKILL.md @@ -5,10 +5,38 @@ description: "Gecode architecture, modeling, propagators, branchers, memory mana # Gecode -Use this skill as the entry point for any Gecode-specific task. Keep the body lean: route to the relevant reference file, load only what the task needs, and avoid pulling unrelated topic docs into context. +Use this skill as the entry point for any Gecode-specific task. Carry the universal Gecode runtime model in mind for every response, then load only the additional reference files needed for the task. + +## Always-On Mental Model +- Space is the home for variables, propagators, branchers, and optimization order. +- Propagation is explicit: call `status()`. +- Search primitives are `status()`, `choice()`, `clone()`, `commit()`, and `constrain()`. +- Space status values are `SS_FAILED`, `SS_SOLVED`, and `SS_BRANCH`. +- Choice is a space-independent descriptor; alternatives are indexed `0..n-1`. +- Choice compatibility is clone-based: a choice is valid for its source space and clones. +- `choice()` invalidates previous choices for later `commit()` on that space. +- Clone only stable, non-failed spaces. +- Branchers run in posting order. +- Recomputation can be nondeterministic with weakly monotonic propagation while remaining sound and complete. +- Model as `class M : public Space`, implement copy constructor and virtual `copy()`, and update variable arrays with `x.update(home, s.x)` during cloning. +- After `status()==SS_BRANCH`, compute `choice()` immediately. +- Treat returned solutions as owned `Space` objects and delete seed models, choices, and solution spaces explicitly. +- Do not assume posting performs full propagation. +- Do not call the `Space` copy constructor directly instead of `clone()`. +- Do not reuse stale choices after another `choice()` call. + +## Default Gecode Heuristics +- Tighten variable domains as early as possible. +- Prefer global constraints over weak manual decompositions. +- Keep branching explicit and problem-specific rather than relying on generic defaults. +- Treat symmetry handling as first-class design work. +- Use `DFS` as the baseline complete search engine. +- Use `BAB` for optimization unless there is a concrete reason to move to restart or portfolio search. +- Treat restart, portfolio, and parallel search behavior as intentionally nondeterministic. +- Remember that clone footprint matters when designing actor state and cached data. +- Use explicit disposal discipline for external or heap-backed resources. ## Routing -- Start with `references/general-knowledge.md` when the task is broad, diagnostic, or about solver/runtime semantics. - Read `references/modeling.md` for model structure, variables, constraints, branching setup, and built-in search configuration. - Read `references/propagator-implementation.md` for custom propagator design, posting, propagation lifecycle, advisors, and rewriting. - Read `references/brancher-implementation.md` for custom branchers, choices, commits, archiving, and NGL support. @@ -16,16 +44,17 @@ Use this skill as the entry point for any Gecode-specific task. Keep the body le - Read `references/search-engines.md` for using and tuning built-in engines such as `DFS`, `BAB`, `LDS`, restart, and portfolio search. - Read `references/search-engine-implementation.md` for custom engine orchestration, recomputation strategy, LAO, and completeness invariants. - Read `references/cmake-consumption.md` for `find_package(Gecode CONFIG)`, target usage, version checks, and vendored fallback patterns. +- Read `references/general-knowledge.md` only for broad conceptual explanations, tracing/observability guidance, or staged model-improvement workflow discussion that goes beyond the always-on mental model. ## Operating Rules - Read only the reference file or files needed for the current task. - Combine references only when the task genuinely crosses boundaries, such as a custom propagator with nontrivial memory strategy or a search-engine bug tied to choice compatibility. - Prefer the narrowest useful reference set first, then expand if the user asks for adjacent concerns. - Keep answers Gecode-specific. If the request is generic CMake, generic C++ memory, or generic CP theory without a real Gecode angle, do not over-apply this skill. -- When a task spans modeling and runtime behavior, anchor the explanation in `references/general-knowledge.md` and then pull in the specialized topic doc. +- Use this `SKILL.md` alone for broad explanations, initial modeling guidance, and many runtime/debugging answers before reaching for extra references. ## Reference Index -- `references/general-knowledge.md`: spaces, propagation/search lifecycle, cloning, recomputation, choices, and debugging mental model. +- `references/general-knowledge.md`: advanced observability, staged improvement workflow, and broad conceptual framing beyond the always-on core. - `references/modeling.md`: variable selection, globals, reification, symmetry, branching, and search setup in ordinary models. - `references/propagator-implementation.md`: actor lifecycle, `ExecStatus`, propagation conditions, iterators, advisors, and rewrite patterns. - `references/brancher-implementation.md`: `status`, `choice`, `commit`, archive compatibility, NGLs, and heuristic encoding. diff --git a/skills/gecode/references/general-knowledge.md b/skills/gecode/references/general-knowledge.md index 441ddb8..f414afc 100644 --- a/skills/gecode/references/general-knowledge.md +++ b/skills/gecode/references/general-knowledge.md @@ -1,34 +1,11 @@ # Gecode General Knowledge -## Core -- Space is home for variables, propagators, branchers, and optimization order. -- Propagation is explicit: call `status()`. -- Search primitives are `status()`, `choice()`, `clone()`, `commit()`, and `constrain()`. -- Space status values are `SS_FAILED`, `SS_SOLVED`, and `SS_BRANCH`. -- Choice is a space-independent descriptor; alternatives are indexed `0..n-1`. -- Choice compatibility is clone-based: a choice is valid for its source space and clones. -- `choice()` invalidates previous choices for later `commit()` on that space. -- Clone only stable, non-failed spaces. -- Branchers run in posting order. -- Recomputation can be nondeterministic with weakly monotonic propagation while remaining sound and complete. - -## Key Patterns -- Model as `class M : public Space`. -- Implement copy constructor and virtual `copy()`. -- In space cloning, clone variable arrays via `x.update(home, s.x)`; do not use a variable-array copy constructor. -- After `status()==SS_BRANCH`, compute `choice()` immediately. -- Seed the search engine with the model, then delete the seed model. -- Treat a solution as a space closure over member variables. +## Advanced Framing - Use groups and tracing for observability and selective control. - Improve models in loops: baseline, stronger propagation, better branching, then tuned search. - Measure with nodes, time, and restarts, not runtime alone. - Treat symmetry handling as a first-class design concern, not post-processing. -## Pitfalls -- Assuming posting performs full propagation. -- Calling the `Space` copy constructor directly instead of `clone()`. -- Using stale choices after invalidating them with a later `choice()` call. -- Forgetting ownership and deletion of choices and returned solution spaces. -- Cloning unstable or failed spaces. +## Conceptual Pitfalls - Assuming parallel search preserves sequential solution order or runtime profile. - Assuming one modeling pass is enough; most nontrivial case studies need staged refinement. From e7db20bc6a498cf21a0388ba6da5c943b7d63be7 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Mon, 9 Mar 2026 16:45:44 +0100 Subject: [PATCH 08/10] feat(skill): expand Gecode debugging and modeling coverage --- evals/gecode-trigger-evals.json | 24 +++++++++++ skills/gecode/SKILL.md | 10 ++++- .../gecode/references/debugging-workflow.md | 41 +++++++++++++++++++ skills/gecode/references/modeling-cookbook.md | 36 ++++++++++++++++ skills/gecode/references/modeling.md | 5 +++ .../gecode/references/scheduling-patterns.md | 22 ++++++++++ .../references/set-and-float-modeling.md | 23 +++++++++++ 7 files changed, 160 insertions(+), 1 deletion(-) create mode 100644 skills/gecode/references/debugging-workflow.md create mode 100644 skills/gecode/references/modeling-cookbook.md create mode 100644 skills/gecode/references/scheduling-patterns.md create mode 100644 skills/gecode/references/set-and-float-modeling.md diff --git a/evals/gecode-trigger-evals.json b/evals/gecode-trigger-evals.json index b483bd5..664b4ad 100644 --- a/evals/gecode-trigger-evals.json +++ b/evals/gecode-trigger-evals.json @@ -23,6 +23,22 @@ "query": "Please model this scheduling problem in Gecode with strong global constraints, sensible symmetry breaking, and a branching strategy that focuses on the cost-driving variables first.", "should_trigger": true }, + { + "query": "My Gecode search tree exploded after I added a few side constraints. I need a debugging workflow for deciding whether the problem is weak propagation, bad branching, or missing symmetry breaking.", + "should_trigger": true + }, + { + "query": "I want a cookbook-style answer for modeling a Gecode optimization problem with channeling, a couple of implied constraints, and a clean brancher order. Think recipe, not theory.", + "should_trigger": true + }, + { + "query": "How should I model this warehouse assignment problem in Gecode if each customer can be assigned to a set of depots and I care about subset and cardinality structure? I think set vars might fit better than raw ints.", + "should_trigger": true + }, + { + "query": "I'm using Gecode with a mix of discrete choices and continuous tolerances. What should I watch out for when float vars are part of the model, especially compared with ordinary integer branching intuition?", + "should_trigger": true + }, { "query": "I need to clean up a generic CMakeLists.txt for a small SDL app. There is no Gecode involved, I just want better target_link_libraries usage and install rules.", "should_trigger": false @@ -46,5 +62,13 @@ { "query": "What are good heuristics for graph coloring in CP, and how do I think about symmetry? Solver-agnostic advice is fine.", "should_trigger": false + }, + { + "query": "My game engine is slow and I want advice on profiling frame spikes with Tracy and Instruments. This has nothing to do with constraint programming.", + "should_trigger": false + }, + { + "query": "I need help modeling a production schedule, but I'm doing it in CP-SAT and mostly want generic scheduling ideas, not Gecode-specific guidance.", + "should_trigger": false } ] diff --git a/skills/gecode/SKILL.md b/skills/gecode/SKILL.md index 2310765..a792f20 100644 --- a/skills/gecode/SKILL.md +++ b/skills/gecode/SKILL.md @@ -1,6 +1,6 @@ --- name: gecode -description: "Gecode architecture, modeling, propagators, branchers, memory management, search engine usage and implementation, recomputation/cloning behavior, and downstream CMake consumption. Use for any materially Gecode-specific task: building or refining models, implementing custom constraints or branchers, tuning DFS/BAB/RBS/PBS/LDS search, debugging solver behavior, reasoning about space memory/lifecycle semantics, or integrating Gecode into CMake projects." +description: "Gecode architecture, modeling, cookbook-style modeling patterns, set/float/scheduling guidance, propagators, branchers, memory management, search engine usage and implementation, recomputation/cloning behavior, debugging/performance diagnosis, and downstream CMake consumption. Use for any materially Gecode-specific task: building or refining Gecode models, implementing custom constraints or branchers, tuning DFS/BAB/RBS/PBS/LDS search, diagnosing weak propagation or search pathologies, reasoning about set/float/resource-style models, or integrating Gecode into CMake projects." --- # Gecode @@ -38,6 +38,10 @@ Use this skill as the entry point for any Gecode-specific task. Carry the univer ## Routing - Read `references/modeling.md` for model structure, variables, constraints, branching setup, and built-in search configuration. +- Read `references/modeling-cookbook.md` when the user needs concrete recipe-style guidance for channeling, symmetry, branching, optimization setup, or choosing globals versus decompositions. +- Read `references/debugging-workflow.md` for weak propagation, exploding search trees, stale choices, recomputation bugs, memory growth, or tracing/profiling workflow. +- Read `references/set-and-float-modeling.md` for set-variable modeling, float-specific caveats, or mixed-domain modeling. +- Read `references/scheduling-patterns.md` for cumulative/resource-style models, sequencing/order constraints, and scheduling-oriented branching or symmetry choices. - Read `references/propagator-implementation.md` for custom propagator design, posting, propagation lifecycle, advisors, and rewriting. - Read `references/brancher-implementation.md` for custom branchers, choices, commits, archiving, and NGL support. - Read `references/memory-handling.md` for space/region/heap allocation, handles, clone footprint, and disposal obligations. @@ -56,6 +60,10 @@ Use this skill as the entry point for any Gecode-specific task. Carry the univer ## Reference Index - `references/general-knowledge.md`: advanced observability, staged improvement workflow, and broad conceptual framing beyond the always-on core. - `references/modeling.md`: variable selection, globals, reification, symmetry, branching, and search setup in ordinary models. +- `references/modeling-cookbook.md`: concrete modeling recipes for globals, channeling, symmetry, branching, optimization, and “propagation versus search” decisions. +- `references/debugging-workflow.md`: symptom-driven diagnosis for weak models, stale choices, recomputation issues, performance pathologies, and observability tooling. +- `references/set-and-float-modeling.md`: set-variable patterns, float-specific caveats, and mixed-domain modeling reminders. +- `references/scheduling-patterns.md`: scheduling/resource modeling patterns, sequencing constraints, and search guidance for schedule-like problems. - `references/propagator-implementation.md`: actor lifecycle, `ExecStatus`, propagation conditions, iterators, advisors, and rewrite patterns. - `references/brancher-implementation.md`: `status`, `choice`, `commit`, archive compatibility, NGLs, and heuristic encoding. - `references/memory-handling.md`: memory areas, lazy vs eager allocation, shared/local handles, and `AP_DISPOSE` discipline. diff --git a/skills/gecode/references/debugging-workflow.md b/skills/gecode/references/debugging-workflow.md new file mode 100644 index 0000000..b3b3055 --- /dev/null +++ b/skills/gecode/references/debugging-workflow.md @@ -0,0 +1,41 @@ +# Gecode Debugging Workflow + +## Symptom: Search Tree Explodes +- First ask whether the model is weak or the branching is weak; the fix is often different. +- Check initial domains, missing implied constraints, and whether a stronger global can replace a weak decomposition. +- Compare nodes and failures before and after modeling changes; do not trust runtime alone. +- If propagation looks adequate but the tree is still huge, inspect branching order, value choice, and symmetry breaking. +- Use `DFS` as a baseline before judging restart or portfolio search. + +## Symptom: Branching or Recomputation Looks Wrong +- Suspect stale choices if behavior changes after another `choice()` call on the same space. +- Recheck choice compatibility: choices must be valid only for the originating space and its clones. +- Confirm `commit()` uses only archived, space-independent choice payload. +- If a bug appears only after deeper search, compare direct-clone behavior versus replayed recomputation behavior. +- When a custom brancher is involved, inspect queue order, assigned-view skipping, and brancher disposal timing. + +## Symptom: Propagation Is Wrong or Too Weak +- Separate correctness bugs from strength issues. Wrong answers or unexplained failure usually mean correctness; huge trees with valid answers usually mean weak propagation. +- Re-check subscriptions, propagation conditions, and whether the propagator actually reaches fixpoint when it should. +- Replace weak decompositions with stronger globals or a dedicated propagator when two ordinary constraints do not reason jointly enough. +- Use advisor-based localization only when the extra complexity is justified by change locality. +- If reification is involved, verify the exact semantics of the chosen decomposition rather than assuming the control literal forces a benign fallback. + +## Symptom: Memory or Clone Footprint Is Too Large +- Inspect what state is copied into every clone and move resize-heavy data away from space memory when appropriate. +- Prefer lazy construction for heavy internal state when many branches will never need it. +- Check external-resource ownership and `AP_DISPOSE` discipline before assuming the issue is search alone. +- Watch for per-choice heap allocations in hot paths, especially inside custom branchers. +- If recomputation is cheaper than cloning the current state, revisit search options and state layout together. + +## Observability Tools +- Use groups and tracing to narrow which parts of the model or search are active at the wrong time. +- Use Gist or CPProfiler when you need to see search-tree shape, branching behavior, and failure concentration rather than just counters. +- Measure nodes, failures, restarts, and memory-related symptoms alongside runtime. +- Improve in loops: baseline, strengthen propagation, improve branching, then tune search and memory strategy. + +## Escalation Paths +- For custom propagation mechanics, continue into `propagator-implementation.md`. +- For stale-choice, commit, or NGL issues, continue into `brancher-implementation.md`. +- For memory ownership and clone-footprint problems, continue into `memory-handling.md`. +- For restart, no-good, and completeness behavior, continue into `search-engines.md` or `search-engine-implementation.md` depending on whether you are using or implementing engines. diff --git a/skills/gecode/references/modeling-cookbook.md b/skills/gecode/references/modeling-cookbook.md new file mode 100644 index 0000000..e56f69c --- /dev/null +++ b/skills/gecode/references/modeling-cookbook.md @@ -0,0 +1,36 @@ +# Gecode Modeling Cookbook + +## Strong Initial Domains +- Start with the tightest semantically correct domains you can justify; domain width is often the first-order search cost. +- Introduce helper variables only when they buy propagation, symmetry handling, or cleaner branching. +- Reuse shared arrays and tuple sets when structure repeats across many constraints. + +## Globals Versus Decompositions +- Prefer globals such as `distinct`, `count`, `binpacking`, `circuit`, or `extensional` when they capture the intended structure directly. +- Decompose only when there is no suitable global or when the decomposition is easier to maintain and the propagation loss is acceptable. +- When a decomposition is kept for simplicity, add implied constraints that recover some lost strength. + +## Channeling and Reification +- Use channeling when two views of the same decision space support different strong constraints or different branchers. +- Keep reification semantics explicit: full reification, half reification, and decomposed reification behave differently under failure. +- If Boolean control logic starts dominating the model, verify that the introduced structure is paying for its propagation cost. + +## Symmetry Templates +- Break symmetry structurally before trying heuristic tricks. +- Use anchors, ordering constraints, `precede`, or canonical placement rules to collapse equivalent solutions early. +- Re-check symmetry assumptions when mixing LDSB with static symmetry breaking or custom branchers. + +## Branching Playbooks +- Put branchers in intentional order; creation order matters. +- For optimization models, branch first on variables that drive the objective or major feasibility bottlenecks. +- When a generic brancher underperforms, ask whether the issue is value choice, variable choice, or missing model structure before jumping to a custom brancher. + +## Optimization Setup +- Use `BAB` as the default exact optimization engine. +- If best-solution search stalls, improve bounds and cost-driving propagation before assuming a different engine will rescue the model. +- Tune restart or portfolio search only after you understand the baseline `DFS` or `BAB` behavior. + +## Propagation Versus Search +- If failures come late and trees are wide, strengthen propagation first. +- If failures are early but node count is still high, the model may be fine and the branching may be poor. +- When a model already has strong globals and tight domains, branching quality often dominates the next gain. diff --git a/skills/gecode/references/modeling.md b/skills/gecode/references/modeling.md index c95cb17..146a6cd 100644 --- a/skills/gecode/references/modeling.md +++ b/skills/gecode/references/modeling.md @@ -1,5 +1,10 @@ # Gecode Modeling +## Related References +- Read `modeling-cookbook.md` for recipe-style modeling patterns and concrete construction templates. +- Read `set-and-float-modeling.md` when the task depends on set vars, float vars, or mixed-domain behavior. +- Read `scheduling-patterns.md` for cumulative/resource-style models and sequencing-heavy schedules. + ## Core - Define the model as a `Space` subclass. - Create typed variable arrays with tight domains early. diff --git a/skills/gecode/references/scheduling-patterns.md b/skills/gecode/references/scheduling-patterns.md new file mode 100644 index 0000000..fa1c797 --- /dev/null +++ b/skills/gecode/references/scheduling-patterns.md @@ -0,0 +1,22 @@ +# Gecode Scheduling Patterns + +## Resource-Style Models +- Start with the tightest time windows, durations, and resource bounds you can justify. +- Prefer cumulative or other structure-aware scheduling constraints over hand-built overlap decompositions when the resource semantics are standard. +- Add obvious implied constraints such as precedence, release/deadline tightening, or resource-balance bounds when they strengthen propagation without changing semantics. + +## Sequencing and Ordering +- Use explicit sequencing or order constraints when the model’s main difficulty is relative position rather than raw resource usage. +- Anchor interchangeable tasks or machines when symmetry would otherwise multiply equivalent schedules. +- When setup or transition structure matters, make it explicit in the model rather than burying it inside a weak objective. + +## Branching Guidance +- Branch first on bottleneck tasks, scarce resources, or tasks that most affect the objective. +- Prefer branchers that expose schedule structure over generic variable-value selection when the schedule has obvious critical-path or packing bottlenecks. +- If a schedule model has wide windows and weak propagation, strengthen the model before inventing a custom search strategy. + +## Common Failure Modes +- Weak overlap decompositions that fail to propagate resource pressure. +- Symmetric machines or interchangeable tasks creating huge equivalent subtrees. +- Objective-driven search without enough feasibility structure, causing the engine to explore many nearly identical schedules. +- Diagnosing schedule problems only by runtime rather than failures, nodes, and where the tree actually branches. diff --git a/skills/gecode/references/set-and-float-modeling.md b/skills/gecode/references/set-and-float-modeling.md new file mode 100644 index 0000000..99ba1a6 --- /dev/null +++ b/skills/gecode/references/set-and-float-modeling.md @@ -0,0 +1,23 @@ +# Gecode Set and Float Modeling + +## Set Variables +- Model set variables when membership, subset, cardinality, or partition structure is the natural shape of the problem. +- Keep lower and upper bounds on sets tight; loose envelope sets behave like wide integer domains and weaken propagation. +- Use cardinality constraints aggressively when the set size matters; they often provide the missing structure for branching and propagation. +- Channel set decisions to integer or Boolean views only when another constraint family becomes substantially stronger through that representation. + +## Float Variables +- Treat float models differently from integer models: propagation is approximate, and branching intuition from integer domains often does not transfer directly. +- Use float variables when the problem is genuinely continuous or mixed continuous/discrete, not just because integer scaling feels inconvenient. +- Be careful with no-good assumptions and search behavior: not all branchers or search features available for integer models carry over the same way for float-heavy models. +- Inspect tolerances, bounds, and objective semantics before concluding that a float model is “wrong.” + +## Mixed-Domain Reminders +- Keep the reason for each domain type explicit; mixed-domain models become hard to debug when variables exist only as translation artifacts. +- Channel across domains only when it enables stronger pruning, clearer objectives, or better branching. +- Re-check clone footprint and cache layout when mixed-domain support data becomes large. + +## When Integer Intuition Fails +- Do not assume set or float domains shrink in the same way that integer intervals do. +- Do not assume the strongest-looking branching is best; mixed-domain models often need problem-structure-aware branching more than aggressive generic branching. +- When propagation quality is hard to judge, compare behavior on a tiny instance with tracing or profiling before scaling up. From e1ddbb3b348526ccf1311c8eb8d6ba69e2123fd0 Mon Sep 17 00:00:00 2001 From: Mikael Zayenz Lagerkvist Date: Mon, 20 Jul 2026 08:56:05 +0100 Subject: [PATCH 09/10] chore(repo): harden v1 release candidate --- .github/workflows/skills-ci.yml | 8 ++ .github/workflows/skills-release.yml | 17 +++- .gitignore | 12 +++ README.md | 25 +++++ evals/gecode-trigger-evals.json | 2 +- scripts/build_release_notes.py | 8 +- scripts/compute_next_version.py | 25 ++++- scripts/validate_skills.py | 85 ++++++++++++++++- .../references/brancher-implementation.md | 2 +- skills/gecode/references/cmake-consumption.md | 9 +- skills/gecode/references/memory-handling.md | 5 + skills/gecode/references/modeling.md | 1 + .../references/propagator-implementation.md | 2 +- .../search-engine-implementation.md | 1 + skills/gecode/references/search-engines.md | 14 ++- tests/test_repository.py | 91 +++++++++++++++++++ 16 files changed, 289 insertions(+), 18 deletions(-) create mode 100644 .gitignore create mode 100644 tests/test_repository.py diff --git a/.github/workflows/skills-ci.yml b/.github/workflows/skills-ci.yml index 52cd197..9ba6564 100644 --- a/.github/workflows/skills-ci.yml +++ b/.github/workflows/skills-ci.yml @@ -5,11 +5,17 @@ on: paths: - 'skills/**' - 'scripts/**' + - 'tests/**' + - 'evals/**' + - '.release-policy.yml' - '.github/workflows/skills-*.yml' pull_request: paths: - 'skills/**' - 'scripts/**' + - 'tests/**' + - 'evals/**' + - '.release-policy.yml' - '.github/workflows/skills-*.yml' jobs: @@ -25,5 +31,7 @@ jobs: node-version: '20' - name: Validate skills run: python scripts/validate_skills.py + - name: Run repository tests + run: python -m unittest discover -s tests -v - name: Smoke discovery run: npx --yes skills add . --list diff --git a/.github/workflows/skills-release.yml b/.github/workflows/skills-release.yml index e2ee36a..3d8e53c 100644 --- a/.github/workflows/skills-release.yml +++ b/.github/workflows/skills-release.yml @@ -28,6 +28,10 @@ on: permissions: contents: write +concurrency: + group: skills-release + cancel-in-progress: false + jobs: release: runs-on: ubuntu-latest @@ -38,10 +42,19 @@ jobs: - uses: actions/setup-python@v5 with: python-version: '3.11' + - uses: actions/setup-node@v4 + with: + node-version: '20' - name: Validate skills run: python scripts/validate_skills.py + - name: Run repository tests + run: python -m unittest discover -s tests -v + + - name: Smoke discovery + run: npx --yes skills add . --list + - name: Read release policy id: policy run: | @@ -105,7 +118,7 @@ jobs: PUSH_BUMP: ${{ steps.prlabels.outputs.bump }} run: | if [ -n "${INPUT_VERSION:-}" ]; then - VERSION="$INPUT_VERSION" + VERSION=$(python scripts/compute_next_version.py --validate "$INPUT_VERSION") else if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then BUMP="${INPUT_BUMP:-patch}" @@ -119,7 +132,7 @@ jobs: - name: Check tag does not already exist if: steps.gate.outputs.should_release == 'true' run: | - if git rev-parse "${{ steps.version.outputs.version }}" >/dev/null 2>&1; then + if git show-ref --verify --quiet "refs/tags/${{ steps.version.outputs.version }}"; then echo "Tag ${{ steps.version.outputs.version }} already exists" >&2 exit 1 fi diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6cddb49 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +# Local skill-installer outputs +.agents/ +.claude/ +.junie/ +.kiro/ +.windsurf/ +skills-lock.json + +# Python test/cache outputs +__pycache__/ +*.py[cod] +.pytest_cache/ diff --git a/README.md b/README.md index 70d6616..4bedc61 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ Canonical skill repository for the umbrella Gecode AI agent skill. +[Gecode](https://www.gecode.org/) 6.4.0 is the current knowledge and compatibility baseline. Repository releases use an independent semantic version because the skill can evolve between Gecode releases. + Install with: ```bash @@ -34,8 +36,31 @@ The skill routes internally to focused reference documents for: - custom search engine implementation - downstream CMake consumption +## Versioning and distribution + +- GitHub releases are immutable snapshots of this repository, starting with `v1.0.0`. +- The standard `npx skills add Gecode/gecode-skills` command installs from the public repository's default branch. +- A new Gecode release normally causes a minor skill release when it adds or changes substantial guidance; corrections and refinements are patch releases. +- Major releases are reserved for incompatible skill structure or behavior changes. +- skills.sh discovers the public skill automatically after an installation through the `skills` CLI; there is no separate package upload. + ## Contributing +### Verification + +Run the structural validator and repository regression tests locally: + +```bash +python scripts/validate_skills.py +python -m unittest discover -s tests -v +``` + +The CI smoke test also checks that the skill is discoverable by the skills CLI: + +```bash +npx --yes skills add . --list +``` + ### Skill structure The skill must be under: diff --git a/evals/gecode-trigger-evals.json b/evals/gecode-trigger-evals.json index 664b4ad..0fa02da 100644 --- a/evals/gecode-trigger-evals.json +++ b/evals/gecode-trigger-evals.json @@ -16,7 +16,7 @@ "should_trigger": true }, { - "query": "I am packaging a C++ solver that depends on Gecode 6.3+ and I want the downstream CMake project to use find_package(Gecode CONFIG) with a FetchContent fallback when it's not installed.", + "query": "I am packaging a C++ solver that targets current Gecode main (6.4+) and I want the downstream CMake project to use find_package(Gecode CONFIG) with a FetchContent fallback when it's not installed.", "should_trigger": true }, { diff --git a/scripts/build_release_notes.py b/scripts/build_release_notes.py index 91c1d86..a9be7d4 100755 --- a/scripts/build_release_notes.py +++ b/scripts/build_release_notes.py @@ -6,12 +6,14 @@ from pathlib import Path -def changed_skills(diff_range: str) -> list[str]: - out = subprocess.check_output(["git", "diff", "--name-only", diff_range], text=True) +def changed_skills(diff_range: str, cwd: Path | None = None) -> list[str]: + out = subprocess.check_output( + ["git", "diff", "--name-only", diff_range], text=True, cwd=cwd + ) names: set[str] = set() for line in out.splitlines(): parts = line.split("/") - if len(parts) >= 3 and parts[0] == "skills" and parts[1].startswith("gecode-"): + if len(parts) >= 3 and parts[0] == "skills" and parts[1]: names.add(parts[1]) return sorted(names) diff --git a/scripts/compute_next_version.py b/scripts/compute_next_version.py index 95648bb..f72f52a 100755 --- a/scripts/compute_next_version.py +++ b/scripts/compute_next_version.py @@ -9,6 +9,13 @@ TAG_RE = re.compile(r"^v(\d+)\.(\d+)\.(\d+)$") +def validate_version_tag(value: str) -> str: + value = value.strip() + if not TAG_RE.fullmatch(value): + raise ValueError("version must be in form vX.Y.Z") + return value + + def latest_tag() -> tuple[int, int, int]: out = subprocess.check_output( ["git", "tag", "--list", "v*", "--sort=-v:refname"], text=True @@ -36,13 +43,25 @@ def main() -> int: ap.add_argument("--bump", choices=["major", "minor", "patch", "auto"], default="auto") ap.add_argument("--labels", default="") ap.add_argument("--current", default="") + ap.add_argument("--validate", default="", metavar="VERSION") args = ap.parse_args() + if args.validate: + try: + print(validate_version_tag(args.validate)) + except ValueError as error: + print(f"ERROR: {error}", file=sys.stderr) + return 1 + return 0 + if args.current: - m = TAG_RE.match(args.current.strip()) - if not m: - print("ERROR: --current must be in form vX.Y.Z", file=sys.stderr) + try: + current = validate_version_tag(args.current) + except ValueError as error: + print(f"ERROR: --current {error}", file=sys.stderr) return 1 + m = TAG_RE.fullmatch(current) + assert m is not None cur = (int(m.group(1)), int(m.group(2)), int(m.group(3))) else: cur = latest_tag() diff --git a/scripts/validate_skills.py b/scripts/validate_skills.py index 04126db..0abcd2c 100755 --- a/scripts/validate_skills.py +++ b/scripts/validate_skills.py @@ -1,13 +1,18 @@ #!/usr/bin/env python3 from __future__ import annotations +import json import re import sys from pathlib import Path REPO_ROOT = Path(__file__).resolve().parents[1] SKILLS_DIR = REPO_ROOT / "skills" +EVALS_DIR = REPO_ROOT / "evals" EXPECTED_SKILLS = {"gecode"} +REFERENCE_RE = re.compile(r"`references/([^`]+)`") +FRONTMATTER_FIELDS = {"name", "description"} +INTERFACE_FIELDS = {"display_name", "short_description", "default_prompt"} def parse_frontmatter(skill_md: Path) -> dict[str, str]: @@ -41,6 +46,46 @@ def parse_frontmatter(skill_md: Path) -> dict[str, str]: return fm +def validate_trigger_evals(skill_name: str, errors: list[str]) -> None: + eval_path = EVALS_DIR / f"{skill_name}-trigger-evals.json" + if not eval_path.is_file(): + errors.append(f"{skill_name}: missing trigger evals: {eval_path}") + return + + try: + cases = json.loads(eval_path.read_text(encoding="utf-8")) + except (json.JSONDecodeError, OSError) as error: + errors.append(f"{eval_path}: invalid JSON: {error}") + return + + if not isinstance(cases, list) or not cases: + errors.append(f"{eval_path}: expected a non-empty JSON array") + return + + seen_queries: set[str] = set() + outcomes: set[bool] = set() + for index, case in enumerate(cases): + location = f"{eval_path}[{index}]" + if not isinstance(case, dict): + errors.append(f"{location}: expected an object") + continue + query = case.get("query") + should_trigger = case.get("should_trigger") + if not isinstance(query, str) or not query.strip(): + errors.append(f"{location}: query must be a non-empty string") + elif query in seen_queries: + errors.append(f"{location}: duplicate query") + else: + seen_queries.add(query) + if not isinstance(should_trigger, bool): + errors.append(f"{location}: should_trigger must be a boolean") + else: + outcomes.add(should_trigger) + + if outcomes != {False, True}: + errors.append(f"{eval_path}: include both triggering and non-triggering cases") + + def main() -> int: if not SKILLS_DIR.exists(): print(f"ERROR: skills directory not found: {SKILLS_DIR}") @@ -75,6 +120,33 @@ def main() -> int: if req not in fm or not fm[req].strip(): errors.append(f"{skill_md}: missing required frontmatter field '{req}'") + unexpected_fields = sorted(set(fm) - FRONTMATTER_FIELDS) + if unexpected_fields: + errors.append( + f"{skill_md}: unsupported frontmatter fields: {', '.join(unexpected_fields)}" + ) + + referenced_files = set( + REFERENCE_RE.findall(skill_md.read_text(encoding="utf-8")) + ) + for reference in sorted(referenced_files): + reference_path = skill_dir / "references" / reference + if not reference_path.is_file(): + errors.append(f"{skill_md}: referenced file does not exist: {reference_path}") + + references_dir = skill_dir / "references" + actual_references = ( + {path.name for path in references_dir.glob("*.md")} + if references_dir.is_dir() + else set() + ) + orphaned_references = sorted(actual_references - referenced_files) + if orphaned_references: + errors.append( + f"{skill_md}: references not routed from SKILL.md: " + f"{', '.join(orphaned_references)}" + ) + name = fm.get("name", "") if name and name != skill_dir.name: errors.append( @@ -90,8 +162,17 @@ def main() -> int: seen_names[name] = skill_md agents_dir = skill_dir / "agents" - if agents_dir.exists() and not (agents_dir / "openai.yaml").exists(): - errors.append(f"{skill_dir}: agents/ exists but agents/openai.yaml is missing") + if agents_dir.exists(): + openai_yaml = agents_dir / "openai.yaml" + if not openai_yaml.is_file(): + errors.append(f"{skill_dir}: agents/ exists but agents/openai.yaml is missing") + else: + metadata = openai_yaml.read_text(encoding="utf-8") + for field in sorted(INTERFACE_FIELDS): + if not re.search(rf"(?m)^\s{{2}}{field}:\s*\S", metadata): + errors.append(f"{openai_yaml}: missing interface field '{field}'") + + validate_trigger_evals(skill_dir.name, errors) if errors: print("Skill validation failed:") diff --git a/skills/gecode/references/brancher-implementation.md b/skills/gecode/references/brancher-implementation.md index cf38a25..73309f3 100644 --- a/skills/gecode/references/brancher-implementation.md +++ b/skills/gecode/references/brancher-implementation.md @@ -14,7 +14,7 @@ - Track the first candidate index such as `start` to avoid rescanning. - Keep the choice payload minimal, for example `pos`, `val`, and alternative count, and archive it deterministically. - Use binary alternatives such as `eq` versus `nq` unless an assignment brancher genuinely needs a single alternative. -- Implement an NGL class with `status`, `prune`, `subscribe`, `cancel`, and `reschedule`. +- Implement an NGL class with `status`, `prune`, `subscribe`, `cancel`, `reschedule`, and `copy`; add `notice` and disposal handling when the literal owns resources. - For complementary last alternatives, `ngl()` can return `NULL` when that is semantically valid. - Reuse branchers through views, notably minus views for max-style variants. - Encode the problem heuristic explicitly, such as Warnsdorff or best-fit slack. diff --git a/skills/gecode/references/cmake-consumption.md b/skills/gecode/references/cmake-consumption.md index 6e59642..4a494e2 100644 --- a/skills/gecode/references/cmake-consumption.md +++ b/skills/gecode/references/cmake-consumption.md @@ -3,9 +3,10 @@ ## Core - Prefer config-package consumption: `find_package(Gecode CONFIG REQUIRED)`. - Link downstream targets to `Gecode::gecode` unless explicit component granularity is required. -- Require `Gecode_VERSION >= 6.3.0` and use package version checks rather than parsing `gecode/support/config.hpp`. +- Current Gecode `main` is building version `6.4.0`; when targeting that line, require `Gecode_VERSION >= 6.4.0` and use package version checks rather than parsing `gecode/support/config.hpp`. If supporting older releases too, document `6.3.0` as the minimum compatibility policy separately. - Hint package location with `Gecode_ROOT` or `CMAKE_PREFIX_PATH`. - Keep `cmake_minimum_required(VERSION 3.21)` or newer for parity with Gecode's build. +- Use a C++17-capable compiler; current main exports that requirement through the Gecode targets. ## Key Patterns - Minimal integration: @@ -20,12 +21,12 @@ ``` - Version guard: ```cmake - if(SOME_STRICT_OPTION AND Gecode_VERSION VERSION_LESS "6.3.0") - message(FATAL_ERROR "Gecode >= 6.3.0 required, found ${Gecode_VERSION}") + if(SOME_STRICT_OPTION AND Gecode_VERSION VERSION_LESS "6.4.0") + message(FATAL_ERROR "Gecode >= 6.4.0 required, found ${Gecode_VERSION}") endif() ``` - Try installed package first with `find_package(Gecode CONFIG QUIET)` when discovery is optional. -- If the package is absent and project policy allows vendoring, use `FetchContent` and set Gecode cache options before `FetchContent_MakeAvailable(...)`. +- If the package is absent and project policy allows vendoring, use `FetchContent` and set Gecode cache options before `FetchContent_MakeAvailable(...)`. For a subproject, explicitly set `GECODE_INSTALL=OFF`, `GECODE_ENABLE_EXAMPLES=OFF`, and `BUILD_TESTING=OFF`; disable `GECODE_ENABLE_QT`, `GECODE_ENABLE_GIST`, or `GECODE_ENABLE_FLATZINC` when their dependencies are outside project scope. - Require `TARGET Gecode::gecode` after resolution and fail fast with actionable error text if it is missing. - Mark Gecode targets as `SYSTEM` in strict-warning projects to isolate third-party headers from local warning policy. - For pinned source fallbacks, prefer stable release tags or commits over long-lived feature branches. diff --git a/skills/gecode/references/memory-handling.md b/skills/gecode/references/memory-handling.md index fd8b91a..d156c8d 100644 --- a/skills/gecode/references/memory-handling.md +++ b/skills/gecode/references/memory-handling.md @@ -23,6 +23,11 @@ - For brancher choices using heap buffers, pair allocation and free, and register `AP_DISPOSE`. - Use `Region` for per-choice scratch arrays to avoid heap churn. +## Exception Safety and Fault Injection +- Treat allocation, actor-copy, `AP_DISPOSE` registration, and partial-clone failures as normal exception paths. +- Make custom `copy()` and `dispose()` logic leave both the source and partially constructed clone recoverable when an allocation throws. +- Current Gecode main provides the CMake-only `GECODE_ENABLE_FAULT_INJECTION` option and an isolated single-threaded `check-fault` suite; use it to exercise ownership and clone-recovery paths rather than relying only on successful allocations. + ## Pitfalls - Frequent resize in space memory causing fragmentation. - Forgetting `home.notice(..., AP_DISPOSE)` for external or heap resources. diff --git a/skills/gecode/references/modeling.md b/skills/gecode/references/modeling.md index 146a6cd..9ba7b55 100644 --- a/skills/gecode/references/modeling.md +++ b/skills/gecode/references/modeling.md @@ -30,6 +30,7 @@ - Use branch filters and print functions for targeted branching and observability. - When executing code between branchers, remember propagation is still explicit on recomputation paths. - For optimization models, branch on cost-driving variables first and tie-break with objective structure. +- Current main optionally exposes counting-based search branching through `cbsbranch(...)` when built with `GECODE_ENABLE_CBS`; treat it as an optional, model-dependent alternative to generic branching. ## Pitfalls - Weak domains at model start causing huge trees. diff --git a/skills/gecode/references/propagator-implementation.md b/skills/gecode/references/propagator-implementation.md index c6afa08..7c7a8f9 100644 --- a/skills/gecode/references/propagator-implementation.md +++ b/skills/gecode/references/propagator-implementation.md @@ -4,7 +4,7 @@ - Propagators compute on views, not model variables. - Implement a post function and the actor lifecycle: `copy`, `dispose`, `cost`, `reschedule`, and `propagate`. - Use `Home` for posting context and use fail/check macros. -- Return honest `ExecStatus`: `ES_FAILED`, `ES_SUBSUMED`, `ES_FIX`, or `ES_NOFIX`. +- Return honest `ExecStatus`: `ES_FAILED`, `ES_FIX`, or `ES_NOFIX`; when a propagator is subsumed, return `home.ES_SUBSUMED(*this)` (or `home.ES_SUBSUMED_DISPOSED(...)` when the disposal size is part of the return). Never use the internal `ES_SUBSUMED_` enum value directly. - Respect obligations around correctness, checking, contracting, monotonicity or waived monotonicity, subscription completeness, and update completeness. - Respect implementation obligations: subsumption complete, cloning conservative, and subscription correct. - Use standard patterns such as `Unary`, `Binary`, `Ternary`, `Nary`, or mixed variants to reduce boilerplate. diff --git a/skills/gecode/references/search-engine-implementation.md b/skills/gecode/references/search-engine-implementation.md index 235dc06..7a5a55c 100644 --- a/skills/gecode/references/search-engine-implementation.md +++ b/skills/gecode/references/search-engine-implementation.md @@ -17,6 +17,7 @@ - Integrate branch-and-bound by constraining future spaces against the current best solution. - Keep restart and meta-engine hooks explicit, such as `master` and `slave`, when required. - Wire statistics and stop-object checks consistently. +- In current main, stop objects and no-good state are copyable and parallel search uses atomic stop coordination plus completion handshakes; do not share mutable stop state non-atomically or destroy PBS workers before their completion signal. ## Pitfalls - Reusing stale choices after another `choice()` call. diff --git a/skills/gecode/references/search-engines.md b/skills/gecode/references/search-engines.md index 65bf86b..a27bce8 100644 --- a/skills/gecode/references/search-engines.md +++ b/skills/gecode/references/search-engines.md @@ -16,7 +16,19 @@ - In restart search, let `master()` decide restart behavior and optional no-good posting. - In restart search, let `slave()` signal completeness intentionally: `true` for complete slave search, `false` for deliberate incompleteness such as LNS neighborhoods. - In portfolio search, remember `slave()` return value has no meaning. -- For restart-based best-solution assets in portfolios, use `RBS`. +- For restart-based best-solution assets in portfolios, add the restart builder to `SEBs`; `RBS` names the engine type, while `rbs(options)` creates the portfolio asset builder. For example: + ```cpp + Search::Options asset_options; + asset_options.cutoff = Search::Cutoff::constant(1000000); + SEBs assets(3); + assets[0] = bab