| name | doc-gate |
| description | When rendering/editor/streaming code changes lack documentation co-updates, enforce with a PreToolUse hook on git commit — checks staged diff for code vs doc coverage |
| source | auto-skill |
| extracted_at | 2026-06-16T01:15:00.000Z |
Doc Gate — Mechanical enforcement of documentation co-update
QWEN.md requires that the LAST step of every plan, phase, and handoff updates
the appropriate docs and skills with empirically verified findings. A plan is
NOT complete until docs reflect what was learned.
When this rule is repeatedly violated, the fix is a PreToolUse hook on
git commit — same pattern as todo_gate_guard.py and
rendering_naming_guard.py.
Why this keeps failing without a hook
- Documentation is the last step — by the time it's due, the agent is
already context-depleted and rushing to hand off
- No mechanical check exists at commit time
- The specific doc files vary by domain (rendering vs editor vs streaming),
creating decision fatigue
- Session boundaries erase the memory of what docs need updating
The hook pattern
Write a Python script in .qwen/local-scripts/:
- Trigger:
Bash tool call containing git commit
- Scan: run
git diff --staged --numstat and --name-only
- Count: sum added+deleted lines per domain (rendering, editor,
streaming) based on crate path prefixes
- Threshold: if a domain has >50 changed lines and NO doc file from
that domain (or global doc) is in the staged diff, deny
- Fail-open: if git commands fail or the repo root can't be found,
allow — never block on infrastructure error
Domain mappings
| Domain | Code patterns (prefix match) | Doc patterns (prefix match) |
|---|
| rendering | crates/titan-rendering, crates/titan-stratum-lighting | docs/rendering/, docs/design/, docs/agent-kb/, .qwen/skills/titan-rendering |
| editor | crates/titan-editor/ | docs/editor/, .qwen/skills/titan-editor/ |
| streaming | crates/titan-world | docs/editor/, .qwen/skills/titan-world-streaming/ |
Global doc files (count for all domains): docs/PLAN.md, docs/TODO.md,
docs/OPEN_BUGS.md, docs/POST_WORK_FINDINGS.md.
Swappable references for testing
Use module-level _staged_files_fn and _staged_diff_stat_fn variables
that default to None — the real functions are called only when these are
None. Tests set them to lambda overrides. This avoids fragile monkey-patching
of module namespace entries.
When to apply
When QWEN.md documentation-co-update rules have no mechanical enforcement and
the user reports repeated violations. Write the hook, add tests, commit.
Existing implementation
C:\Dev\Titan\.qwen\local-scripts\doc_gate_guard.py — 8 tests,
test_doc_gate_guard.py.
Related hooks
All four mechanical gates follow the same .qwen/local-scripts/*.py pattern,
auto-discovered by Qwen Code:
| Hook | Trigger | Rule |
|---|
rendering_naming_guard.py | Write/Edit | No proprietary names in code |
todo_gate_guard.py | Write/Edit | // TODO: must be in docs/TODO.md |
scope_guard.py | Write/Edit | No writes outside .qwen/task_scope.json |
doc_gate_guard.py | git commit | >50 lines code must co-update docs |
Common hook conventions
- Fail-open:
evaluate() / main() never raise; any error → allow
- File-based scoping: only
.rs/.wgsl for code-level hooks;
Bash for commit-level hooks
- Project-root discovery: walk up from
__file__ looking for .git
- Norm: all paths go through
_norm() → lowercase, backslash→slash
- Testing: pure-Python tests, no pytest,
python -X utf8 test_*.py