Skip to content

The documented stale-approval polling (the gate's only timeout control) uses a non-existent .age field: jq errors and never pages (CR-A10-003) #448

Open

Description

@cemililik

Wave 1 · theme D1-8: Docs describing features that do not exist (doc side of code findings) · highest severity High · 1 finding(s) · effort trivial

Summary

A pending approval 30 days old: the documented §7 cron prints a jq error, stale='' and no page. The overdue Art. 14 decision goes unnoticed.

Primary finding

CR-A10-003 — The documented stale-approval polling (the gate's only timeout control) uses a non-existent .age field: jq errors and never pages

severity High · reachability default · intent contract-violation · confidence confirmed · independently confirmed · reproduced end to end

What happens. human_approval_gate.md §3 (line 99) and §7 (lines 179-183) give the polling pattern, which §7 calls 'the contract' until an auto-fail timer ships. The jq filter is .pending[] | select(.age | test("^[0-9]+d")). The JSON envelope built by _summarise_pending (_approvals.py:241-250) has age_seconds (a float) and no age field. jq fails with 'null (null) cannot be matched' (exit 5). In §7, stale=$(...) is then empty and [ "$stale" -gt 0 ] is false, so overdue approvals never trigger the page. The TR mirror (human_approval_gate-tr.md:99,181) has the same filter. approvals_subcommand.md:71 also says age_seconds is 'an integer ... never negative'. The code returns a float that goes negative under clock skew (_approvals.py:222-223).

Where.

Evidence.

  • _approvals.py:241-250 summary keys: run_id, staging_path, staging_exists, requested_at, age_seconds, metrics, config_hash, reason

Recommended fix. Change the recipes to select(.age_seconds != null and .age_seconds > 86400) in EN and TR. Correct the age_seconds type note, or round it and clamp it at 0 in code.

Acceptance criteria

  • A regression test reproduces the failure scenario and asserts the corrected behaviour.
  • Documentation listed above is corrected in English and in its Turkish mirror in the same PR.
  • Every finding in this issue is resolved or explicitly closed with a reason.

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area: gdpr-approvalCode area: gdpr-approvalbugSomething isn't workingseverity: highWrong behaviour or a false claim users rely onsource: review-2026-10Found by the October 2026 code and documentation reviewwave: 1Roadmap wave 1: high severity, small effort

    Type

    No type

    Projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions