Gap
propose_pr() and commit_pages() (wiki_toolkit/write_gate.py) git add/git commit only the paths passed via --pages. Every skill that calls propose-pr — ingest, source-update, query — only ever passes wiki-page paths, never docs/catalog.jsonl, docs/log.jsonl, or docs/source-manifest.jsonl, even though the same session's earlier steps (build, log, source-scan --update) regenerate/append to those files. source-scan also re-stamps docs/sources/*.md frontmatter (processed flag flip, plus formatting/EOF normalization as a side effect of reserialization).
Net effect: after propose-pr, those files are left as uncommitted working-tree changes — the branch handed off for review doesn't carry the catalog/log/manifest/source state consistent with the page change it does carry.
This contradicts the existing "Write gate" spec statement (docs/design/toolkit-spec.md, "Write gate" section): "Every wiki write goes through a PR — no exceptions, no direct commits, for either LLM-authored content or deterministic tooling output (catalog, manifest, log)." Confirmed via git status after a real ingest run:
modified: docs/catalog.jsonl
modified: docs/log.jsonl
modified: docs/source-manifest.jsonl
modified: docs/sources/ratelimit-design-doc.md
modified: docs/sources/ratelimit-implementation-ticket.md
modified: docs/sources/ratelimit-slack-thread.md
modified: docs/sources/redis-eviction-fix.md
The narrow pages-only commit in _stage_and_commit is deliberate today (see the comment at write_gate.py around _stage_and_commit: "Scope the commit to pages even if something else was already staged... per the acceptance criteria") — this issue supersedes that acceptance criterion.
Root cause
Both propose_pr() and commit_pages() explicitly scope git add -- <pages> / git commit -- <pages> to the caller-supplied list, discarding anything else staged. No flag exists to widen this (cli.py's propose-pr/commit-pages commands only expose --pages/--frame/--message).
Resolution (agreed design)
- Centralize the fix in
write_gate.py — fix propose_pr() and commit_pages() once so every current and future skill caller gets correct behavior automatically, rather than relying on each SKILL.md to enumerate the right files.
- Always auto-include state files — no opt-in flag, no page-only mode preserved. There's no real scenario where a PR wants pages without the state files these skills always co-generate.
- Detection: git-dirty scan scoped to
docs_dir (the resolved wiki root, via resolve_docs_dir) — stage/commit --pages plus every other currently modified/untracked file under docs_dir. No fixed filename allowlist (self-updating if new generated files are added later) and no session-manifest bookkeeping needed.
- Hard-bound the scan to
docs_dir — never stages/commits anything outside it, so a caller's unrelated in-progress changes elsewhere in the repo working tree are left alone. Both propose_pr(root, pages, frame) and commit_pages(root, pages, message) currently receive Path.cwd() as root, not docs_dir — the fix needs docs_dir (or an equivalent) threaded through from cli.py's resolve_docs_dir() call so the dirty scan has the right boundary.
Scope
wiki_toolkit/write_gate.py: propose_pr(), _stage_and_commit(), commit_pages().
wiki_toolkit/cli.py: propose_pr_cmd/commit_pages_cmd need to thread docs_dir through instead of only Path.cwd().
- No
SKILL.md changes required — ingest, source-update, query keep passing page-only --pages; the fix in write_gate.py covers them all.
docs/design/toolkit-spec.md command-surface entries for commit-pages/propose-pr — already updated in this session to document the new contract.
Out of scope
- Real GitHub PR creation (still local branch + commit only, per existing "Not yet built" note).
- Any change to what
source-scan chooses to touch/stamp — only how those touches get committed once made.
Gap
propose_pr()andcommit_pages()(wiki_toolkit/write_gate.py)git add/git commitonly the paths passed via--pages. Every skill that callspropose-pr—ingest,source-update,query— only ever passes wiki-page paths, neverdocs/catalog.jsonl,docs/log.jsonl, ordocs/source-manifest.jsonl, even though the same session's earlier steps (build,log,source-scan --update) regenerate/append to those files.source-scanalso re-stampsdocs/sources/*.mdfrontmatter (processedflag flip, plus formatting/EOF normalization as a side effect of reserialization).Net effect: after
propose-pr, those files are left as uncommitted working-tree changes — the branch handed off for review doesn't carry the catalog/log/manifest/source state consistent with the page change it does carry.This contradicts the existing "Write gate" spec statement (
docs/design/toolkit-spec.md, "Write gate" section): "Every wiki write goes through a PR — no exceptions, no direct commits, for either LLM-authored content or deterministic tooling output (catalog, manifest, log)." Confirmed viagit statusafter a realingestrun:The narrow
pages-only commit in_stage_and_commitis deliberate today (see the comment atwrite_gate.pyaround_stage_and_commit: "Scope the commit topageseven if something else was already staged... per the acceptance criteria") — this issue supersedes that acceptance criterion.Root cause
Both
propose_pr()andcommit_pages()explicitly scopegit add -- <pages>/git commit -- <pages>to the caller-supplied list, discarding anything else staged. No flag exists to widen this (cli.py'spropose-pr/commit-pagescommands only expose--pages/--frame/--message).Resolution (agreed design)
write_gate.py— fixpropose_pr()andcommit_pages()once so every current and future skill caller gets correct behavior automatically, rather than relying on eachSKILL.mdto enumerate the right files.docs_dir(the resolved wiki root, viaresolve_docs_dir) — stage/commit--pagesplus every other currently modified/untracked file underdocs_dir. No fixed filename allowlist (self-updating if new generated files are added later) and no session-manifest bookkeeping needed.docs_dir— never stages/commits anything outside it, so a caller's unrelated in-progress changes elsewhere in the repo working tree are left alone. Bothpropose_pr(root, pages, frame)andcommit_pages(root, pages, message)currently receivePath.cwd()asroot, notdocs_dir— the fix needsdocs_dir(or an equivalent) threaded through fromcli.py'sresolve_docs_dir()call so the dirty scan has the right boundary.Scope
wiki_toolkit/write_gate.py:propose_pr(),_stage_and_commit(),commit_pages().wiki_toolkit/cli.py:propose_pr_cmd/commit_pages_cmdneed to threaddocs_dirthrough instead of onlyPath.cwd().SKILL.mdchanges required —ingest,source-update,querykeep passing page-only--pages; the fix inwrite_gate.pycovers them all.docs/design/toolkit-spec.mdcommand-surface entries forcommit-pages/propose-pr— already updated in this session to document the new contract.Out of scope
source-scanchooses to touch/stamp — only how those touches get committed once made.