Skip to content

docs(compliance): stop saying the approval gate enforces segregation of duties - #983

Closed
cemililik wants to merge 1 commit into
developmentfrom
docs/wave0-segregation-of-duties
Closed

cemililik wants to merge 1 commit into
developmentfrom
docs/wave0-segregation-of-duties

Conversation

@cemililik

@cemililik cemililik commented Oct 18, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

Stops six documents from saying the human-approval gate enforces segregation of duties or "dual control". It records who trained and who approved and writes both to the audit log; it does not compare them, and both identities are self-asserted. A deployer citing these pages to an auditor would have claimed a control ForgeLM does not provide.

Related issues

Refs #73, #99, #90 — the documentation halves are resolved here. The website copy ("dual-controlled" in site/compliance.html and six locales of translations.js, #94/#99) is a separate change; whether the code should ever compare the two identities is left open on the issues (see below).

Changes — each row shows what it said and what it says now

Where Said Now says
docs/reference/iso27001_control_mapping{,-tr}.md A.5.3 "enforces trainer ≠ approver" records the approver identity (from FORGELM_OPERATOR, else the OS user) so it is auditable; ForgeLM does not reject self-approval; keep the identities disjoint at your identity provider
docs/reference/soc2_trust_criteria_mapping{,-tr}.md CC1.2 "Approval gate operator ≠ trainer" the gate records the approver identity so trainer ≠ approver is auditable; ForgeLM does not enforce it
docs/qms/statement_of_applicability{,-tr}.md A.8.2 "approval gate non-trainer operator" "approval gate records the approver identity"
docs/guides/iso_soc2_deployer_guide{,-tr}.md "Every model promotion is dual-controlled and forensically attributed" when the gate is enabled, every promotion is attributed to the recorded approver identity; keeping trainer and approver different people is the deployer's control
same guide, checklist "F-compliance-110 raises ConfigError if you forget" require_human_approval it only raises when evaluation.safety.enabled is off for strict tiers; hand-written YAML without the gate is accepted; the CLI wizard turns the gate on for those tiers
same guide + docs/usermanuals/{en,tr}/operations/iso-soc2-deployer.md "two distinct operator IDs prove segregation of duties" both identities are self-asserted, so segregation holds only if your identity provider makes them different people
docs/qms/sop_change_management{,-tr}.md "attributed, dual-controlled, and forensically recorded" attributed to the recorded approver identity and forensically recorded when the gate is enabled; separating the two is the deployer's control

Checked against the code

  • _resolve_approver_identity() returns FORGELM_OPERATOR, else getpass.getuser(); forgelm approve logs it and never compares it with the run's operator.
  • _enforce_safety_gate_for_strict_tier (forgelm/config.py) raises only when the safety evaluation is not enabled.
  • The CLI wizard sets evaluation.require_human_approval = True for strict tiers (forgelm/wizard/_orchestrator.py); the browser wizard offers it as a toggle.

What this does and does not change for a deployer

A DPIA or an audit response can no longer cite ForgeLM for "trainer ≠ approver is enforced". The pages say what it does give: both identities on record, in an append-only chain. This lowers what could be relied on, and it matches statement_of_applicability.md (A.5.3), access_control.md and approve_subcommand.md, which already said it. I did not touch docs/design/iso27001_soc2_alignment.md: it is a design record, not a claim.

Not done here

  • Whether to add an opt-in check that refuses self-approval. The identity is an unauthenticated string and the training.started operator is user@host while the approver is bare user, so a naive comparison would miss the default case and could give a false assurance. That is a design decision, not a docs fix.
  • The site strings (compliance.art14.body, compliance.iso.change.body, six locales) and site/compliance.html.

Type

  • Documentation

Testing

  • The full CLAUDE.md gauntlet from a clean worktree of this branch: exit 0.

Checklist

  • English and Turkish mirrors updated together (Turkish wording needs the owner's review)
  • CHANGELOG entry under [Unreleased]

🤖 Generated with Claude Code

Summary by Sourcery

Align compliance documentation with the approval gate’s actual behavior by distinguishing auditable identity recording from deployer-enforced segregation of duties.

Enhancements:

  • Correct compliance documentation in English and Turkish to state that the approval gate records trainer and approver identities but does not enforce segregation of duties or reject self-approval.
  • Clarify that separating trainer and approver identities is the deployer's responsibility and that approval-gate configuration is not universally enforced for strict risk tiers.

Documentation:

  • Update ISO 27001, SOC 2, QMS, change-management, deployer-guide, and operations-manual documentation to accurately describe approval attribution and segregation-of-duties responsibilities.
  • Add an Unreleased changelog entry documenting the corrected compliance claims.

…of duties

The human-approval gate records who approved (FORGELM_OPERATOR, else the OS user) next to who trained, and writes
both to the audit log. It does not compare the two, and both identities are self-asserted. Several documents said
otherwise:

- the ISO 27001 mapping ("enforces trainer != approver") and the SOC 2 mapping ("approval gate operator != trainer");
- the Statement of Applicability row for A.8.2 ("non-trainer operator");
- the deployer guide, the change-management SOP and the user manual ("dual-controlled", "two distinct operator IDs
  prove segregation of duties");
- the deployer guide's checklist, which said F-compliance-110 raises ConfigError if require_human_approval is missing
  for a strict risk tier. It only checks that the safety evaluation is enabled; the CLI wizard turns the approval gate
  on for those tiers, hand-written YAML is not checked.

The same documents already said the right thing elsewhere (the SoA row for A.5.3, access_control.md, the approve
reference); this brings the rest in line. English and Turkish. The design spec in docs/design is a record of intent and
is left as written; the website copy that says "dual-controlled" is a separate change.

Refs #73, #99, #90

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@sourcery-ai

sourcery-ai Bot commented Oct 18, 2026

Copy link
Copy Markdown

Sorry @cemililik, you've used your own review budget of 250,000 diff characters for the last 7 days.

You can request another review in 13 hours and 47 minutes by commenting @sourcery-ai review. Upgrade to get a review now.

@coderabbitai

coderabbitai Bot commented Oct 18, 2026

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

Next included review available in 49 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used all 3 included reviews currently available. Your 48 included PR review attempts over the past 7 days set your current allowance at 3 reviews per hour.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: defaults
  • Review profile: CHILL
  • Plan: Essentials
  • Run ID: f2b4901d-baa1-43f2-9e7b-5ab5a93c76c9
📥 Commits

Reviewing files that changed from the base of the PR and between 56b4f0d and 3933c2c.

📒 Files selected for processing: 13
  • CHANGELOG.md
  • docs/guides/iso_soc2_deployer_guide-tr.md
  • docs/guides/iso_soc2_deployer_guide.md
  • docs/qms/sop_change_management-tr.md
  • docs/qms/sop_change_management.md
  • docs/qms/statement_of_applicability-tr.md
  • docs/qms/statement_of_applicability.md
  • docs/reference/iso27001_control_mapping-tr.md
  • docs/reference/iso27001_control_mapping.md
  • docs/reference/soc2_trust_criteria_mapping-tr.md
  • docs/reference/soc2_trust_criteria_mapping.md
  • docs/usermanuals/en/operations/iso-soc2-deployer.md
  • docs/usermanuals/tr/operations/iso-soc2-deployer.md
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.

@sourcery-ai

sourcery-ai Bot commented Oct 18, 2026

Copy link
Copy Markdown

Reviewer's Guide

This documentation-only change aligns English and Turkish compliance materials with the implementation: the human-approval gate records trainer and approver identities for audit purposes but does not enforce segregation of duties or reject self-approval. It also corrects the strict-tier configuration guidance and records the changes in the unreleased changelog.

Sequence diagram for audit identity recording without segregation enforcement

sequenceDiagram
    participant Trainer
    participant ForgeLM
    participant AuditLog
    participant IdentityProvider
    participant Approver

    Trainer->>ForgeLM: training.started
    ForgeLM->>AuditLog: Record trainer identity
    Approver->>ForgeLM: forgelm approve
    ForgeLM->>IdentityProvider: _resolve_approver_identity()
    IdentityProvider-->>ForgeLM: FORGELM_OPERATOR or getpass.getuser()
    ForgeLM->>AuditLog: Record human_approval.granted
    Note over ForgeLM,AuditLog: ForgeLM does not compare identities or reject self-approval
    AuditLog-->>IdentityProvider: Deployer compares identities externally
Loading

File-Level Changes

Change Details Files
Corrected compliance documentation to distinguish auditability from enforcement of segregation of duties.
  • Replaced claims that the approval gate enforces trainer/approver separation or dual control with statements that it records self-asserted identities and does not compare or reject self-approval.
  • Clarified that deployers must enforce identity separation through their identity provider.
  • Updated the English and Turkish ISO 27001, SOC 2, QMS, deployer-guide, and operations-manual content consistently.
  • Corrected the deployer-guide description of strict-tier safety validation: hand-written configurations without human approval are accepted; the CLI wizard enables the gate.
  • Added the documentation correction to the unreleased changelog.
CHANGELOG.md
docs/guides/iso_soc2_deployer_guide.md
docs/guides/iso_soc2_deployer_guide-tr.md
docs/qms/sop_change_management.md
docs/qms/sop_change_management-tr.md
docs/qms/statement_of_applicability.md
docs/qms/statement_of_applicability-tr.md
docs/reference/iso27001_control_mapping.md
docs/reference/iso27001_control_mapping-tr.md
docs/reference/soc2_trust_criteria_mapping.md
docs/reference/soc2_trust_criteria_mapping-tr.md
docs/usermanuals/en/operations/iso-soc2-deployer.md
docs/usermanuals/tr/operations/iso-soc2-deployer.md

Possibly linked issues

  • #CR-A18-007: The PR corrects several documentation claims from this finding, but leaves the site copy and regression-test acceptance criterion unresolved.
  • #DOC-D1B06-002: The PR corrects the English and Turkish ISO 27001 mappings, stating identities are recorded but self-approval is not rejected.
  • #DOC-D1B03-001: The PR updates both language versions of the cited SOP to state that approver identity is recorded, but ForgeLM does not enforce separation.

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@cemililik

Copy link
Copy Markdown
Collaborator Author

Superseded by #1000: the wave-0 fixes are combined by theme into five pull requests to cut the review load. This branch's commits are included there unchanged apart from the CHANGELOG.md merge; the branch stays on the remote.

@cemililik cemililik closed this Oct 18, 2026
cemililik added a commit that referenced this pull request Oct 18, 2026
… six-language site copy, described as the code behaves (#1000)

## Summary

Compliance- and privacy-facing wording corrected to what the code does,
plus the six-language site copy that repeated the same claims. Six
earlier branches combined into one pull request, replacing #983, #986,
#988 and #991 (to be closed as superseded) and two branches that were
never opened. **This is compliance wording; please read the before/after
tables.**

## ⚠️ Localization exception

`docs/standards/localization.md` keeps German, French, Spanish and
Chinese as deferred tiers and says not to machine-translate them. At the
owner's decision I **wrote the DE / FR / ES / ZH site strings myself**,
with no native review. English and Turkish are authoritative; the other
four need a native reader before they can be trusted. A follow-up issue
for that is drafted, not yet filed.

## Related issues

Refs #73, #99, #90, #94, #101, #87, #114, #75, #86, #89, #92, #98, #110,
#112. The code halves stay open: whether the gate should ever compare
trainer and approver, failing closed on a missing safety verdict
(breaking), attaching SBOMs to the release.

## Changes

| Theme | Said | Now says |
|---|---|---|
| **Segregation of duties** (ISO 27001 / SOC 2 mappings, SoA, SOP,
deployer guide, manual) | the approval gate "enforces trainer ≠
approver", "dual-controlled" | it **records** trainer and approver
identities and does not compare them; both are self-asserted; keeping
them different people is the deployer's control.
`require_human_approval` is not enforced for strict tiers by
`ConfigError` (it only is when safety evaluation is off) |
| **GDPR / KVKK manual page** | identifiable subjects are removed; "no
specific person's data is recoverable from the model"; an
`international_transfers` setting; audit report with source attribution
and SHA-256 manifest; retention "auto-deletes originals" | eight
identifier formats are masked, names and free text are not; the report
is counts and summaries, not written to the audit log; retention is
reported, not enforced; memorisation is not measured |
| **No-verdict safety evaluation** (8 documents + TR) | exit 0 ⇒ every
gate passed; classifier load failure halts the run; classification
errors count as unsafe | a safety evaluation that produced no verdict
(`evaluation_completed: false`) keeps the model and exits 0 (4 with the
approval gate) regardless of `auto_revert`; code unchanged |
| **SBOM** (reference, SOP, guides, manuals, mappings) | `gh release
download …`; "re-emit from the git tag"; "licence extraction in SBOM" |
workflow-run artifacts, 90-day retention; `gh run download`; the file is
the CI runner's `pip list` (test tooling included, no dependency graph,
no licences) |
| **Article 12 / 14** (audit-event catalogue, audit-log and
human-oversight pages) | the audit log is "mandated by Article 12"; the
gate "implements" Article 14 | the log is development-time evidence that
supports Annex IV; Article 12 logging and Article 14 in-use oversight
belong to the deployed system |
| **Site, six languages** | benchmark failure "fails (exit 3)";
"dual-controlled"; Slack/Teams/Discord; "PII redacted"; "satisfying the
access-request side of the GDPR"; wizard "Enforce automatic deletion";
"SHA-256 manifest"; DeepSpeed "presets" | exit 3 only with `auto_revert:
true`; records the approver; Slack-format, Teams and Discord need a
relay; common formats masked, names not detected; reverse-pii finds only
what is literally in the scanned files; retention mode reserved; SHA-256
sidecar; bring your own DeepSpeed JSON |

The site GGUF strings describe the `.sha256` sidecar, the retention
strings the reserved mode, and the DeepSpeed strings the preset caveat;
they match the GGUF, code and docs pull requests, so **merge those
first**. A new site test keeps the retired phrases out of English,
Turkish and the HTML (mutation-checked).

### What this changes for a deployer

A DPIA or audit response can no longer cite ForgeLM for: segregation of
duties being enforced, personal data being removed or unrecoverable from
the model, the SBOM being on the release page, or the audit log being
the Article 12 mechanism.

## Type

- [x] Documentation (and site copy)

## Testing

- The full `CLAUDE.md` gauntlet from a clean worktree of this branch:
exit 0. (An earlier run failed `tests/test_turkish_glossary_terms.py`
because the Article 12 rewrite wrote "Ek IV" instead of "Annex IV",
fixed here, then two timing-based regex linearity tests failed while two
gauntlets ran at once; they pass alone and in this final run.)
- `node --test tests/site/*.test.cjs`: 81 pass; English ↔ Turkish keys
in lockstep.

## Checklist

- [x] English and Turkish updated together (Turkish wording needs the
owner's review)
- [ ] DE / FR / ES / ZH reviewed by a native speaker — **not done**
- [x] CHANGELOG entries under `[Unreleased]`

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant