Skip to content

[Wave 4] Low/info — Module sweep: Compliance core: Annex IV, audit log, integrity manifest, provenance (part 3/5) #658

Open

Description

@cemililik

Wave 4 · 20 finding(s) in 20 fix package(s). Fix these whenever a pull request touches this module ("clean the module you touch").

Findings

  • CR-A07-109 (code, Low) — verify-annex-iv --pipeline exits 0 for a chain manifest whose manifest_hash was stripped, without version gating
    severity Low · reachability default · intent intended-but-harmful · confidence likely · independently confirmed · reproduced end to end · already tracked: KNOWN-OPEN TRUST-05 — docs/roadmap/phase-16-trust-surface-hardening.md:440-442 (S5 open)
    Failure scenario. A clean 2-stage run with forgelm_version 1.0.0 and an HMAC log; delete metadata from compliance/pipeline_manifest.json -> exit 0, success true, hash_state 'absent'.
    Where. forgelm/compliance.py:2083-2090, forgelm/cli/subcommands/_verify_annex_iv.py:227-236, forgelm/verify/_pipeline_evidence.py:452-462
    Recommended fix. Route an absent chain manifest_hash to UNVERIFIED:: (exit 1) when forgelm_version is at or after the release that began stamping it, mirroring _manifest_predates_pointer_fix. Alternatively, document the exit-0 'OK (UNVERIFIED)' case in cli.md:227.

  • CR-A07-110 (code, Low) — AuditLogger does not catch socket.gethostname() OSError
    severity Low · reachability default · intent intended-but-harmful · confidence likely · independently confirmed · reproduced end to end
    Failure scenario. getpass resolves 'alice', socket.gethostname raises OSError -> AuditLogger raises OSError (uncaught).
    Where. forgelm/compliance.py:215, forgelm/cli/subcommands/_approve.py:174-181, forgelm/trainer.py:354, forgelm/cli/subcommands/_purge.py:736-739
    Recommended fix. Wrap gethostname in try/except OSError, falling back to 'unknown-host' as _approve.py does.

  • CR-A07-111 (code, Low) — Docs and docstring claim an all-local-path config yields component_revisions: [], but local-path loads are recorded as local_path entries
    severity Low · reachability default · intent contract-violation · confidence likely · independently confirmed · reproduced end to end
    Failure scenario. An auditor follows the documented reading 'an all-local-path config gives []'. A non-empty list of local_path entries then looks like evidence of Hub-pinned loads, or the auditor wrongly concludes that the manifest is malformed.
    Where. forgelm/compliance.py:1466-1468, forgelm/model.py:72-76, forgelm/model.py:185-189, forgelm/compliance.py:1296-1298
    Recommended fix. Fix the three doc sites and the model.py:72-75 comment: local-path loads appear with resolution_source local_path and revision_pinned null. Or, if the documented behaviour is intended, filter local_path records out. Pin it with tests/test_revision_pin_chains.py::test_local_path_load_is_listed_as_local_path.

  • CR-A07-112 (code, Low) — --compliance-export grades a local-directory base model 'unresolved' + 'no base-model load' reason; a training run on the same config grades it 'local_path'
    severity Low · reachability default · intent intended-but-harmful · confidence likely · independently confirmed · reproduced end to end
    Failure scenario. An auditor compares the bundle from forgelm --config job.yaml --compliance-export ./pre (written before training) with the run's own compliance/ directory. For an identical local base model the two artefacts disagree ('unresolved' vs 'local_path'), and the pre-training one claims nothing could be determined.
    Where. forgelm/compliance.py:1374-1421, forgelm/compliance.py:1296-1298, forgelm/compliance.py:1105-1107, docs/reference/compliance_summary.md:159-160
    Recommended fix. When the registry has no record and os.path.isdir(repo_id), emit resolution_source 'local_path' (the reason may still say no load happened), mirroring the dataset side; or document explicitly that --compliance-export never emits local_path for the base model.

  • CR-A07-113 (code, Low) — compliance_summary says export_compliance_artifacts 'ZIPs the bundle'; no CLI path produces a ZIP and export_evidence_bundle has no caller
    severity Low · reachability default · intent contract-violation · confidence likely · independently confirmed · reproduced end to end
    Failure scenario. An auditor or operator expects an auditor-ready ZIP after --compliance-export, as the reference says, and finds loose files and no bundle.
    Where. docs/reference/compliance_summary.md:26-27, docs/reference/compliance_summary.md:89-91, docs/reference/compliance_summary-tr.md:25-26, docs/reference/compliance_summary-tr.md:88-90
    Recommended fix. Either wire export_evidence_bundle into --compliance-export (or a flag), or reword both sites (EN+TR) to say the export writes loose files and the ZIP helper is library-only.

  • CR-A07-114 (code, Low) — Governance split statistics have an undisclosed basis that depends on the trainer: text_length disappears silently for DPO/SimPO/ORPO/KTO/GRPO and measures the [ROLE]-formatted string plus EOS for SF…
    severity Low · reachability default · intent intended-but-harmful · confidence likely · independently confirmed · reproduced end to end
    Failure scenario. An auditor compares the text_length of a DPO run with that of an SFT run. The DPO report has no length data and no reason for its absence. The SFT numbers include role markers and the EOS token, and they disagree with the inlined data_audit splits for the same rows: 24 vs 14 characters in e2e. sample_count can be read as raw corpus rows or as rows after truncation or dedup, and the report supports neither reading.
    Where. forgelm/compliance.py:617-651, forgelm/compliance.py:643-651, forgelm/data.py:634-645, forgelm/data.py:648-666
    Recommended fix. Add a 'basis' block to each split entry (stage: post-merge/post-split/pre-tokenisation; length unit: characters of the rendered training string; truncation: model.max_length tokens not applied). For non-text trainers, compute lengths per prompt/chosen/rejected/completion column or record text_length: null with a reason. Document the schema in the compliance reference and the user manual.

  • CR-A07-115 (code, Low) — Governance 'median' and 'p95' are upper-biased order statistics: the median of an even-sized split is its upper middle value (n=2 gives the max), and p95 at n=20 is the max
    severity Low · reachability default · intent intended-but-harmful · confidence likely · independently confirmed · reproduced end to end
    Failure scenario. A split with lengths [1,4] reports median 4 (true median 2.5). A 20-row split [0..19] reports p95 19 (nearest-rank 18). In e2e, validation (4 rows) reports median 27 = max.
    Where. forgelm/compliance.py:638-639
    Recommended fix. Use statistics.median and a documented percentile definition (nearest-rank ceil(0.95*n)-1), or rename the keys to the order statistic actually returned.

  • CR-A07-116 (code, Low) — No test asserts the governance split statistics: the mutants sample_count+1 and median->lengths[0] survive the full suite
    severity Low · reachability default · intent intended-but-harmful · confidence likely · independently confirmed · reproduced end to end
    Failure scenario. Private copy with compliance.py:645 set to len(split_data)+1 and compliance.py:638 set to lengths[0]: the full suite reports 4746 passed, 24 skipped, 0 failed. The unmutated baseline copy reports 3 failed, all of them known offline-environment provenance tests unrelated to this code.
    Where. forgelm/compliance.py:645, tests/test_compliance.py:689-724, tests/test_faz27_narrow_exceptions.py:303-354
    Recommended fix. Add a test that runs prepare_dataset on a small primary + extra fixture through generate_data_governance_report and asserts the per-split counts, the source attribution and the length statistics (including an even-n median).

  • CR-A07-117 (code, Low) — Annex IV §2 / compliance_report training_parameters omit every method-specific objective parameter and most optimisation settings for all six trainer types; only config_hash and the model card (outsi…
    severity Low · reachability default · intent contract-violation · confidence likely · independently confirmed · reproduced end to end
    Failure scenario. A regulator receives the compliance/ bundle for a DPO model (SOC2 CC2.3 names the Annex IV bundle + compliance_report.json as what is shipped). It cannot tell beta 0.1 from 0.5, whether NEFTune or GaLore was active, the warmup/weight decay, or the step budget, so the documented claim that §2 lets the auditor 'reconstruct what was run' does not hold without obtaining the original YAML or model card separately.
    Where. forgelm/compliance.py:1516, forgelm/compliance.py:1836-1842, forgelm/trainer.py:496-552, forgelm/trainer.py:693-765
    Recommended fix. Add a per-trainer_type block (betas, gamma, GRPO generation parameters, reward source), the optimisation settings (max_steps, warmup, weight decay, NEFTune, GaLore, packing, early stopping, scheduler, seed, optim/precision) and the TRL/transformers versions to training_parameters, or embed the redacted validated config with its config_hash in the compliance bundle.

  • CR-A07-118 (code, Low) — The declaration of conformity is attributed to the 'deployer' under 'Article 16', and the introduction claims 'conformity declaration scaffolding' that does not exist
    severity Low · reachability default · intent contract-violation · confidence likely · single reviewer · not reproduced (code trace)
    Failure scenario. A team that fine-tunes and ships a high-risk system reads that the DoC is 'the deployer's' deliverable and assumes its downstream customer signs it. The provider-side Annex IV §8 copy and the Art. 47 declaration are never produced. A new reader, told by the introduction that scaffolding exists, looks for a DoC template that is not there.
    Where. docs/usermanuals/en/compliance/annex-iv.md:183,185, docs/usermanuals/tr/compliance/annex-iv.md:183, docs/usermanuals/en/compliance/overview.md:61, docs/usermanuals/tr/compliance/overview.md:61
    Recommended fix. Say 'the provider's (Art. 16(g), Art. 47) signed deliverable, and a copy belongs in Annex IV §8'. Remove 'conformity declaration scaffolding' from introduction.md (EN+TR), or ship an actual template.

  • CR-A07-119 (code, Low) — Art. 13 deployer instructions omit the provider's contact details (Art. 13(3)(a)) even when compliance.provider_contact is configured, while telling deployers to 'contact the provider'
    severity Low · reachability default · intent intended-but-harmful · confidence likely · single reviewer · reproduced end to end
    Failure scenario. A deployer receives deployer_instructions.md for a high-risk model. The model produces harmful output and Section 7 says to report it to the provider immediately, but the document has no contact. The Art. 13(3)(a) element is missing even though the operator supplied it.
    Where. forgelm/compliance.py:1686-1694, forgelm/compliance.py:1704-1712, forgelm/compliance.py:1745, forgelm/compliance.py:1758
    Recommended fix. Add a 'Provider Contact' row from comp_cfg.provider_contact through _sanitize_md, plus an optional authorised-representative field. Add a test asserting that the contact appears.

  • CR-A07-120 (code, Low) — verify_annex_iv_subcommand.md examples use a filename no writer produces (annex_iv.json) and a 'sha256:' hash prefix the verifier never emits
    severity Low · reachability default · intent contract-violation · confidence likely · single reviewer · reproduced end to end
    Failure scenario. An integrator copies the reference example into a CI gate that checks the path compliance/annex_iv.json or matches the hash with a 'sha256:' prefix. The path never exists (exit 1), or the prefix match never succeeds.
    Where. docs/reference/verify_annex_iv_subcommand.md:182-229, docs/reference/verify_annex_iv_subcommand-tr.md:182-229, forgelm/compliance.py:87
    Recommended fix. Change the examples to compliance/annex_iv_metadata.json and bare-hex hashes in both EN and TR.

  • CR-A07-121 (code, Low) — Duplicated manifest entries inflate verified_count: 'All 4 recorded artifact(s) present and unchanged' for a two-file directory
    severity Low · reachability default · intent intended-but-harmful · confidence likely · independently confirmed · reproduced end to end
    Failure scenario. A padded manifest makes a release record claim twice as many verified artifacts as exist.
    Where. forgelm/verify/_model_integrity.py:152-195, forgelm/verify/_model_integrity.py:225-229
    Recommended fix. Reject duplicate file values (after canonicalisation) as a malformed manifest (exit 1), or count distinct paths.

  • CR-A07-122 (code, Low) — corroborate_pipeline_stage_census: _live_passed_stages is O(n^2) in the number of stages (set comprehension on every stage_started event)
    severity Low · reachability default · intent intended-but-harmful · confidence likely · independently confirmed · reproduced end to end
    Failure scenario. A pipeline (or a key-holding producer) with tens of thousands of stages makes verify-annex-iv --pipeline take minutes in the corroboration step alone, growing quadratically up to the 32 MiB log cap (about 2 minutes at 40k stages, much longer if a smaller per-event size or a faster path increased the event count).
    Where. forgelm/compliance.py:3409, forgelm/compliance.py:3294-3362, forgelm/verify/_pipeline_evidence.py:600-605, forgelm/compliance.py:3353-3357
    Recommended fix. Make supersession amortised O(log n): keep live as a dict {name: position} plus a lazy max-heap of (-position, name); on stage_started pop heap entries with position >= cutoff and delete them from live if the dict still holds that same position. A scratch implementation (bench/fixlive.py) was equal to the original on 3000 random event traces (including unknown names and repeated starts) and ran N…

  • CR-A07-123 (code, Low) — verify-annex-iv --pipeline interpolates the first 16 characters of the manifest's own manifest_hash unescaped into a violation line
    severity Low · reachability default · intent intended-but-harmful · confidence likely · independently confirmed · not reproduced (code trace)
    Failure scenario. A pipeline manifest whose metadata.manifest_hash string contains control characters fails verification with exit 6. Up to 16 of its characters reach the terminal unescaped inside the 'manifest hash mismatch' violation line.
    Where. forgelm/compliance.py:2082-2090, forgelm/cli/subcommands/_verify_annex_iv.py:238-240
    Recommended fix. Use {expected_hash!r:.20} or str(expected_hash)[:16] passed through the same control-character escaping used elsewhere, and type-check expected_hash as str before slicing.

  • CR-A07-124 (code, Low) — library_api_reference.md does not document the pipeline verifier facade or its INPUT_ERROR::/UNVERIFIED::/IO_ERROR:: routing tokens. Library callers have no documented way to tell exits 1, 2 and 6 ap…
    severity Low · reachability default · intent intended-but-harmful · confidence likely · independently confirmed · reproduced end to end
    Failure scenario. An integrator calls verify_pipeline_manifest_at_path from Airflow and treats any non-empty list as tampering. An environment without FORGELM_AUDIT_SECRET always returns an UNVERIFIED:: entry, so routine runs page the security on-call. Alternatively, the integrator matches prose and misroutes.
    Where. docs/reference/library_api_reference.md:29, docs/reference/library_api_reference.md:89, forgelm/compliance.py:3529-3551, forgelm/compliance.py:3083-3096
    Recommended fix. Document the facade in library_api_reference.md (EN+TR): the token prefixes, the precedence order, the env dependency on FORGELM_AUDIT_SECRET, and the need to strip tokens before display. Alternatively, expose a public is_pipeline_integrity_failure / classify helper. Fix the stale docstring at compliance.py:3090-3094.

  • CR-A07-125 (code, Low) — verify-audit and the pipeline corroborator read the audit log without taking the writer's flock, so a live append can be reported as tampering (exit 6) or as an empty log (exit 1)
    severity Low · reachability default · intent intended-but-harmful · confidence likely · independently confirmed · reproduced with stubs
    Failure scenario. A monitoring job runs verify-audit against the log of a run in progress, or of a shared output directory, and gets a transient exit 6 that disappears on re-run. Responders learn to ignore exit 6.
    Where. forgelm/compliance.py:2594-2695, forgelm/compliance.py:548-599, forgelm/cli/subcommands/_verify_audit.py:71-106, forgelm/compliance.py:3445
    Recommended fix. Take fcntl.flock(LOCK_SH) with a bounded wait in _read_audit_log_lines; a no-op on Windows, as in the writer. Or at least treat an unterminated final line as 'incomplete/in progress' (not exit 6), and document that verify-audit should not be run against a log being written.

  • CR-A07-126 (code, Low) — Enabling monitoring removes the audit-log reference from Annex IV §6 'monitoring_and_logging'
    severity Low · reachability default · intent contract-violation · confidence likely · single reviewer · reproduced end to end
    Failure scenario. An operator enables monitoring to strengthen the Art. 12/17 posture. The resulting annex_iv_metadata.json §6 no longer names audit_log.jsonl, so an auditor reading §6 finds no record-keeping reference. The same run without monitoring would have had one.
    Where. forgelm/compliance.py:1856-1857, forgelm/compliance.py:1582-1589, forgelm/verify/_annex_iv.py:41, docs/usermanuals/en/compliance/annex-iv.md:85
    Recommended fix. Merge rather than replace: {'audit_log': 'audit_log.jsonl', **({'monitoring': manifest['monitoring']} if manifest.get('monitoring') else {})}. Add a test asserting audit_log stays present when monitoring is enabled.

  • CR-A07-127 (code, Low) — Dataset URLs with credentials are copied verbatim into compliance_report.json / data_provenance.json (path, fingerprint.path) by --compliance-export, and into error envelopes and pipeline_state.json…
    severity Low · reachability undocumented · intent intended-but-harmful · confidence likely · independently confirmed · reproduced end to end
    Failure scenario. An operator tries a presigned https dataset URL. The run fails, and the presigned signature is kept in pipeline_state.json and in the pipeline manifest, or is emitted in the compliance bundle by --compliance-export.
    Where. forgelm/compliance.py:722, forgelm/compliance.py:1540-1541, forgelm/compliance.py:1575-1579, forgelm/compliance.py:1100-1101
    Recommended fix. Reject http(s) values in dataset_name_or_path and extra_datasets at config validation with an actionable message, or mask URL userinfo/query before recording a path in manifests and error strings.

  • CR-A07-128 (code, Low) — AuditLogger chains past whitespace-only and other junk tail lines without parsing the last record, and the strict approval reader also skips whitespace-only lines: approve/reject/purge extend and act…
    severity Low · reachability default · intent intended-but-harmful · confidence likely · independently confirmed · reproduced end to end
    Failure scenario. A stray whitespace or form-feed line appears in the audit log through an editor, a log shipper or tampering. Subsequent approve, reject, purge and training resumes keep appending, and approve/reject decide normally, with no warning at write time. Only a later verify-audit reports exit 6, at the junk line.
    Where. forgelm/compliance.py:326, forgelm/compliance.py:313-321, forgelm/compliance.py:2658-2660, forgelm/compliance.py:2544-2551
    Recommended fix. With the shared LF-only splitter recommended above, have the writer refuse, under the same ValueError/OSError policy as the torn-tail guard, a tail whose last record is not a JSON object, and treat whitespace-only records as malformed. Make strict iter_audit_events raise on a whitespace-only line, so the approval guards and the verifier agree.

Done when

  • Every finding above is fixed or closed with a reason (tick it here).
  • Code fixes carry a test; doc fixes update the Turkish mirror in the same PR.

Found by the October 2026 ForgeLM code and documentation review against commit cab2947. Code links point at that commit; line numbers may have moved since. Finding IDs (CR-…, DOC-…, TECH-…) are stable references for this backlog.

Activity

  1. added
    bugSomething isn't working
    backlog: knownAlready tracked in docs/roadmap
    securitySecurity-relevant
    severity: lowMinor defect, inaccuracy or rough edge
    source: review-2026-10Found by the October 2026 code and documentation review
    wave: 4Roadmap wave 4: low / info / watch, when touching the module
    on Oct 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: complianceCode area: compliancebacklog: knownAlready tracked in docs/roadmapbugSomething isn't workingsecuritySecurity-relevantseverity: lowMinor defect, inaccuracy or rough edgesource: review-2026-10Found by the October 2026 code and documentation reviewwave: 4Roadmap wave 4: low / info / watch, when touching the module

    Type

    No type

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions