Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
faecdd6
Merge branch 'main' into development
cemililik Oct 19, 2026
2059b59
Merge pull request #1026 from HodeTech/sync/main-into-development-2
cemililik Oct 19, 2026
760f712
chore(ci): enforce issue dependencies with a blocked label and a requ…
cemililik Oct 20, 2026
76de1e8
fix(ci): harden the issue-dependency check after review
cemililik Oct 20, 2026
edcaab0
fix(ci): strip multi-backtick code spans before reading closing links
cemililik Oct 20, 2026
a156682
fix(ci): drop indented code blocks before reading closing links
cemililik Oct 20, 2026
0c943e0
fix(ci): keep block boundaries when reading closing links
cemililik Oct 20, 2026
f63da37
fix(ci): let GitHub decide what a pull request closes; isolate sync f…
cemililik Oct 20, 2026
f464d28
Merge pull request #1035 from HodeTech/chore/issue-dependency-guard
cemililik Oct 20, 2026
78026eb
docs: the Issue dependencies check is required on development
cemililik Oct 20, 2026
fa182e2
Merge pull request #1040 from HodeTech/docs/issue-dependencies-required
cemililik Oct 20, 2026
d7891a7
chore(ci): triage gate - every new issue starts as needs-triage, only…
cemililik Oct 20, 2026
760bbc0
docs(ci): align the triage gate with the merged issue-dependency guard
cemililik Oct 20, 2026
1719ce9
fix(ci): make the triage gate state-based, bot-safe and self-repairing
cemililik Oct 20, 2026
e145b42
fix(ci): find the sweep's candidates with the issue search, not by pa…
cemililik Oct 20, 2026
269a6b1
Merge pull request #1041 from HodeTech/chore/triage-gate
cemililik Oct 20, 2026
84b2cbd
fix(merging): keep both adapters in SLERP (#1025)
hiyouwhatsapp Oct 20, 2026
902c03c
fix(safety): restore Llama-Guard scoring on transformers 5.x (Fixes #…
krisnaparahita Oct 20, 2026
79eeb44
feat(ci): bind the dependency override, read texts as GitHub renders …
cemililik Oct 20, 2026
1877d93
fix: reject ambiguous judge JSON verdicts (CR-A06-015) (#1038)
krisnaparahita Oct 20, 2026
7550f75
fix(judge): count a reply cut off at the token cap as a judge failure…
cemililik Oct 20, 2026
7d309ab
fix(judge): clear the three mypy errors in judge.py that count agains…
cemililik Oct 21, 2026
21b1fca
fix(judge): a lone score object in an unterminated array fails closed…
cemililik Oct 21, 2026
735a02f
fix(docs): align GDPR, erasure, retention and PII documentation with …
cemililik Oct 22, 2026
cbac6b9
fix(docs): make the ingestion quick-start example runnable (#395) (#1…
lesbass Oct 22, 2026
beea6f6
fix(purge): accept retention-only config file for --check-policy (#10…
chiragjangiir Oct 23, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions .agents/skills/sync-bilingual-docs/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,13 @@ Any change to these paths or their siblings:
- `docs/product_strategy.md` / `product_strategy-tr.md`
- `docs/roadmap.md` / `roadmap-tr.md`
- `docs/reference/*.md` (has matching `-tr.md`)
- `docs/guides/*.md` and `docs/qms/*.md` (each has a `-tr.md` mirror; `docs/standards/localization.md` lists the exceptions)
- `docs/usermanuals/{en,tr}/**/*.md` (the static-site SPA viewer source)

Do **not** use for:
- `docs/standards/*` (English only)
- `docs/marketing/*` (gitignored working-memory; mixed languages)
- `docs/qms/*` (English only)
- `docs/design/*` (English only)
- `docs/guides/*` (English only for now — Turkish is future work)

Do **not** reference any file under gitignored working-memory directories
(`docs/marketing/`, `docs/analysis/`) from the bilingual docs you edit —
Expand Down
3 changes: 1 addition & 2 deletions .claude/skills/sync-bilingual-docs/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,14 +23,13 @@ Any change to these paths or their siblings:
- `docs/product_strategy.md` / `product_strategy-tr.md`
- `docs/roadmap.md` / `roadmap-tr.md`
- `docs/reference/*.md` (has matching `-tr.md`)
- `docs/guides/*.md` and `docs/qms/*.md` (each has a `-tr.md` mirror; `docs/standards/localization.md` lists the exceptions)
- `docs/usermanuals/{en,tr}/**/*.md` (the static-site SPA viewer source)

Do **not** use for:
- `docs/standards/*` (English only)
- `docs/marketing/*` (gitignored working-memory; mixed languages)
- `docs/qms/*` (English only)
- `docs/design/*` (English only)
- `docs/guides/*` (English only for now — Turkish is future work)

Do **not** reference any file under gitignored working-memory directories
(`docs/marketing/`, `docs/analysis/`) from the bilingual docs you edit —
Expand Down
5 changes: 5 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ body:
value: |
Thanks for reporting a bug! Please fill out the sections below to help us fix it faster.

A maintainer triages every new issue before work on it starts; until then it
carries the `needs-triage` label. If you would like to fix it yourself, please
wait for triage before you start — see
[Issue triage](https://github.com/HodeTech/ForgeLM/blob/development/CONTRIBUTING.md#issue-triage).

- type: textarea
id: description
attributes:
Expand Down
7 changes: 5 additions & 2 deletions .github/ISSUE_TEMPLATE/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,11 @@ body:
[CONTRIBUTING.md](https://github.com/HodeTech/ForgeLM/blob/development/CONTRIBUTING.md).
You do not need to write the Turkish yourself: open the pull request with
the English change and say so, and a maintainer will complete the mirror.
Tell us below which copy you were reading. Documentation pull requests are
welcome directly.
Tell us below which copy you were reading. A documentation pull request is
welcome directly, without an issue. If you open an issue, a maintainer triages
it first (it carries the `needs-triage` label until then); please wait for that
before you start on it — see
[Issue triage](https://github.com/HodeTech/ForgeLM/blob/development/CONTRIBUTING.md#issue-triage).

- type: input
id: page
Expand Down
5 changes: 5 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ body:
value: |
Thanks for suggesting a feature! Please describe what you'd like and why.

A maintainer triages every new issue before work on it starts; until then it
carries the `needs-triage` label. If you would like to fix it yourself, please
wait for triage before you start — see
[Issue triage](https://github.com/HodeTech/ForgeLM/blob/development/CONTRIBUTING.md#issue-triage).

- type: textarea
id: problem
attributes:
Expand Down
9 changes: 8 additions & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@

<!-- `Fixes #123` closes the issue when this PR is merged. Use `Refs #123` instead when the PR
resolves only part of it, e.g. some findings of a grouped issue. Quote any finding IDs
(CR-…, DOC-…, TECH-…) you address. Write "None" if there is no issue. -->
(CR-…, DOC-…, TECH-…) you address. Write "None" if there is no issue.
Put one keyword before each issue (`Fixes #12, fixes #13`): GitHub closes only the first of
`Fixes #12, #13`. The "Issue dependencies" check fails while an issue this PR links (title,
description, commits or the Development sidebar) is blocked, unless GitHub lists its blockers
as closed by this PR too, or a maintainer adds `dependency-override`. Mention an issue without
a keyword if this PR does not work on it. -->
Fixes #

## Changes
Expand Down Expand Up @@ -45,3 +50,5 @@ Fixes #
- [ ] No new dependencies added (or added as optional: `pip install forgelm[...]`)
- [ ] Config template (`config_template.yaml`) updated if new config fields added
- [ ] CHANGELOG entry under `[Unreleased]` if a user or library consumer can observe the change
- [ ] The issue I link has been triaged (it does not carry `needs-triage`), or this PR links no issue —
see [Issue triage](https://github.com/HodeTech/ForgeLM/blob/development/CONTRIBUTING.md#issue-triage)
24 changes: 17 additions & 7 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,14 @@ jobs:
# is added here because it is CI-specific (artifact upload).
run: pytest -q --tb=short --cov-report=xml:coverage.xml

- name: Coverage of the workflow scripts
# tools/ sits outside the package gate above (its source is forgelm/). The three scripts that run as
# workflows with a token that can write to issues get a floor of their own; `-o addopts=` drops the
# package's --cov options, and `--include` keeps the other tools/ files out of the total.
run: |
python -m coverage run --source=tools -m pytest -q -p no:cov -o addopts="" tests/test_issue_dependencies.py tests/test_assign_on_comment.py tests/test_triage_gate.py
python -m coverage report --include="tools/issue_dependencies.py,tools/assign_on_comment.py,tools/triage_gate.py" --fail-under=90

- name: Upload coverage (3.11 only)
# Upload coverage from one combo only — avoids 4× artifact duplication; 3.11 is canonical (matches publish.yml build job).
if: matrix.python-version == '3.11' && always()
Expand Down Expand Up @@ -416,13 +424,15 @@ jobs:
run: python3 tools/check_yaml_snippets.py --strict

- name: User-manual YAML example schema-key drift
# check_yaml_snippets.py only validates snippets carrying the full
# model+training+data triplet; single-block fragments (the common
# case in a user-manual page explaining one section) are skipped
# there. This guard AST-walks forgelm/config.py's Pydantic schema
# and flags fragment keys that don't exist on it. Runs with --strict:
# the introduction-time fabricated-key backlog (38 instances across 8
# EN/TR page-pairs) has been cleaned, so any new drift now fails CI.
# check_yaml_snippets.py validates only full configs (model + training
# + data) with real Pydantic. This guard reads every fenced YAML block
# in docs/usermanuals/ and fails on a block it cannot read (another
# fence spelling, an unclosed fence, a parse error, a duplicate key),
# on a fragment key path the schema AST-walked from forgelm/config.py
# does not declare, and on a top-level block ForgeConfig does not have
# (full configs included) unless the guard lists it as another tool's
# YAML or a known-open block; each listed row must match exactly one
# block per language, so a row that no longer matches fails too.
run: python3 tools/check_usermanual_schema_drift.py --strict

- name: Deprecation removal-target drift (strict)
Expand Down
60 changes: 60 additions & 0 deletions .github/workflows/issue-dependencies.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Issue dependencies

# Keeps the `blocked` label in step with each issue's native "Blocked by" links (the rules live in
# tools/issue_dependencies.py and CONTRIBUTING.md, "Dependencies between issues"):
# - an open issue with a blocker that is not closed as completed gets `blocked`;
# - every other issue loses it, with one comment when an open issue is unblocked, and so does a pull request;
# - a blocker closed as not planned or as a duplicate leaves the issue blocked and asks a maintainer to re-plan it;
# - every open pull request whose "Issue dependencies" check no longer matches its linked issues (red although
# they are unblocked, or green although one became blocked) gets that check re-run.
#
# GitHub sends no workflow event when a "Blocked by" link is added or removed, so this runs when an issue is closed
# or reopened (what unblocks or re-blocks its dependents), every 15 minutes as the backstop for link edits (off the
# hour, when GitHub's scheduler is busiest), and on demand.
# Every run is a full reconciliation of state, not a reaction to a transition, so the concurrency group may drop a
# pending run, and a run that fails half-way is finished by the next one. tests/test_issue_dependencies.py pins:
# - the token holds `contents: read`, `issues: write`, `pull-requests: read`, `checks: read` and `actions: write`,
# nothing else;
# - no `${{ }}` appears in a `run:` line; the script reads the environment itself;
# - the checkout takes the default branch, one file, no stored credentials, pinned to a full commit SHA.
#
# Maintainers: to stop it, open Actions > "Issue dependencies" > the "..." menu > "Disable workflow". The links
# stay; only the label stops following them.

on:
issues:
types: [closed, reopened]
schedule:
- cron: "2,17,32,47 * * * *"
workflow_dispatch:

permissions: {}

concurrency:
group: issue-dependencies
cancel-in-progress: false

jobs:
sync:
# This repository, and only its default branch: a manual run started from another branch would check out that
# branch's copy of the script with this job's write token. After a rename or a transfer the guard must name the
# new repository, or the sync stops without a word (MAINTAINERS.md, "Repository automation").
if: ${{ github.repository == 'HodeTech/ForgeLM' && github.ref == 'refs/heads/development' }}
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read # the checkout reads one script from the default branch
issues: write # add or remove `blocked` / `needs-triage`, and post the notices
pull-requests: read # read the open pull requests and the issues they link
checks: read # read each one's latest "Issue dependencies" result
actions: write # re-run that check when the result no longer matches
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
sparse-checkout: tools/issue_dependencies.py
sparse-checkout-cone-mode: false
- name: Bring the blocked label in step with the Blocked-by links
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: python3 tools/issue_dependencies.py sync
3 changes: 2 additions & 1 deletion .github/workflows/nightly.yml
Original file line number Diff line number Diff line change
Expand Up @@ -388,7 +388,8 @@ jobs:
owner: context.repo.owner,
repo: context.repo.repo,
title: title,
labels: ['nightly-failure', 'bug'],
// Events caused by GITHUB_TOKEN start no other workflow, so the triage gate cannot label this issue.
labels: ['nightly-failure', 'bug', 'needs-triage'],
body: `## Nightly CI Failure\n\nThe nightly compatibility test failed.\n\n**Run:** [View details](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId})\n\nThis may indicate a breaking change in a dependency. Check the test logs for details.`,
});
}
89 changes: 89 additions & 0 deletions .github/workflows/pr-dependencies.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
name: PR dependencies

# The "Issue dependencies" check: fails while the pull request links an issue that is still blocked (Fixes / Closes /
# Resolves / Refs #N in its title, description or commits, or a Development-sidebar link), unless GitHub lists every
# open blocker as closed by the same pull request or a maintainer has labelled it `dependency-override` and nothing
# has changed since. Rules: tools/issue_dependencies.py and CONTRIBUTING.md, "Dependencies between issues". The
# "Issue dependencies" workflow re-runs this check whenever its result no longer matches the issues.
#
# Two jobs:
# - "Record dependency override" runs only when a `dependency-override` label is added. If a maintainer added it,
# it posts a comment recording the head commit and the issues the pull request links at that moment; the
# override counts for those and ends with a new commit or a link to another issue. It is the only job that may
# write, and it can only comment.
# - "Issue dependencies" is the required check. It waits for the recorder, so a record is in place before it reads
# one, and runs whether the recorder ran, was skipped (every other event) or failed.
#
# Why `pull_request_target`. A `pull_request` run takes this file from the pull request itself, so a pull request
# could rewrite the check that judges it. `pull_request_target` takes the workflow and the script from the default
# branch instead. Its known risk is running the pull request's code with a privileged token; these jobs never do:
# the checkout has no `ref`, so it reads one file from the default branch; nothing from the pull request is checked
# out, built or executed; no secret but GITHUB_TOKEN is used; and the script treats the title, the description and
# the commits as data. tests/test_issue_dependencies.py pins these properties:
# - `pull_request_target` only, no `paths:` or `branches:` filter (a required check must always report);
# - the check's token holds `contents: read`, `issues: read` and `pull-requests: read`, nothing else; the
# recorder's holds `contents: read` and `pull-requests: write`, and its `if:` reads the event's action and label;
# - each checkout has no `ref`, takes one file, keeps no credentials and is pinned to a full commit SHA, and no
# expression in this file names the pull request's head;
# - no `${{ }}` appears in a `run:` line; the script reads the event file itself.
# What it cannot stop: a pull request may add a different workflow with a job of the same name, and branch
# protection cannot tell two GitHub Actions checks of one name apart (true of every required Actions check), so a
# maintainer reads every change under `.github/` and `tools/` before merging it.
#
# Prerequisite: from 2 November 2026 GitHub blocks `pull_request_target` in public repositories unless an Actions
# policy allows it. This repository's policy (Settings > Actions > Policies) allows it for this file's path only;
# renaming or moving this file needs the policy updated in the same change, or the check stops reporting.
#
# One check per pull request at a time: a newer event cancels the check still going, so a burst of edits costs one
# check. The recorder is never cancelled, so a record is never lost to a burst.

on:
pull_request_target:
types: [opened, edited, reopened, synchronize, ready_for_review, labeled, unlabeled]

permissions: {}

jobs:
record-override:
name: Record dependency override
# Event data only: the action and the label's name. The script checks who added the label.
if: ${{ github.event.action == 'labeled' && github.event.label.name == 'dependency-override' }}
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read # the checkout reads one script from the default branch
pull-requests: write # read the pull request and its label changes; post the record comment
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
sparse-checkout: tools/issue_dependencies.py
sparse-checkout-cone-mode: false
- name: Record the commit and the linked issues the override accepts
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: python3 tools/issue_dependencies.py record-override

issue-dependencies:
name: Issue dependencies
needs: record-override
if: ${{ !cancelled() }}
runs-on: ubuntu-latest
timeout-minutes: 5
concurrency:
group: pr-dependencies-${{ github.event.pull_request.number }}
cancel-in-progress: true
permissions:
contents: read # the checkout reads one script from the default branch
issues: read # read the linked issues and their "Blocked by" links
pull-requests: read # read the pull request, its label changes and the override record
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
sparse-checkout: tools/issue_dependencies.py
sparse-checkout-cone-mode: false
- name: Fail if a linked issue is still blocked
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: python3 tools/issue_dependencies.py check-pr
Loading
Loading