| title | gh-render specification |
|---|---|
| status | approved |
| version | 0.1 |
| date | 2026-07-26 |
gh rendermaterializes deterministic, read-only local projections of GitHub objects. GitHub is authoritative; generated files are disposable views protected by explicit ownership and filesystem-safety rules.
- Purpose
- Vocabulary
- Command model
- Global rendering contract
- Filesystem safety
- Validation modes
- Exit codes
- Renderer extension contract
GitHub objects are useful inside a repository's local documentation and agent workflows, but GitHub remains their durable system of record. gh render closes that gap by deriving reviewable Markdown files from GitHub without creating a second writable tracker.
The extension reads GitHub and writes local files. It never creates, edits, closes, labels, or deletes GitHub objects.
| Term | Meaning |
|---|---|
| Object | A supported GitHub collection such as issues. |
| Renderer | The object-specific fetch, normalization, and serialization implementation. |
| Projection | The complete local file set derived from one object collection. |
| Selection | The normalized filters, ranking, and limit that determine projection membership. |
| Managed file | A file containing the exact gh-render ownership marker. |
| Unmanaged file | Any file without that marker, regardless of its filename. |
| Stale file | A managed file whose expected content differs from disk or whose source object no longer exists. |
The root grammar is:
gh render <object> [flags]
gh render and gh render --help display root help. Unknown objects return a usage error. Each object owns its specific flags and output schema.
Every renderer supports these common flags:
| Flag | Meaning |
|---|---|
--repo owner/repo |
Read from an explicit repository instead of resolving the current repository. |
--output <directory> |
Write to an explicit directory instead of the renderer default. |
--check |
Perform no writes and fail when the projection differs from disk. |
--dry-run |
Perform no writes and report the files that would change. |
--check and --dry-run are mutually exclusive. Repository resolution follows the authenticated GitHub CLI context and produces a clear error when no repository can be resolved.
Object renderers may define selection flags. Different selector types combine with AND unless the object specification states otherwise. Identity aliases such as @me are resolved to concrete GitHub logins before selection and rendering.
Every renderer must satisfy these invariants:
-
GitHub is the sole upstream authority.
-
Rendering is one-way. No command writes to GitHub.
-
Identical normalized GitHub data and normalized selection produce byte-identical files.
-
Generated output contains no render timestamp, host-specific path, random value, or unstable ordering.
-
Text files use UTF-8, LF line endings, and one final newline.
-
Records and collection fields use explicit deterministic ordering.
-
Every generated file contains this marker near its beginning:
<!-- gh-render:managed --> -
A renderer computes and validates its complete write plan before changing disk state.
-
A successful second render against unchanged GitHub data produces no file changes.
-
A filtered projection records its normalized selection in the collection index.
A renderer may create a missing output directory. It may create a missing target file and replace an existing managed target file.
A renderer must refuse the entire operation before writing when:
- a target path exists and is unmanaged;
- an expected directory path is a file;
- a target resolves outside the canonical output directory;
- two records resolve to the same target;
- the output directory cannot be canonicalized safely.
Each file replacement uses a temporary file in the target directory followed by an atomic rename. Temporary files are removed after failure when possible.
Stale deletion is restricted to regular files that:
- are directly owned by the active renderer;
- match that renderer's documented filename pattern; and
- contain the ownership marker.
The renderer does not recursively delete directories or follow symlinks during cleanup.
Normal mode writes the planned projection and reports a concise summary.
--dry-run fetches and renders the complete selected projection, performs all conflict checks, and prints the paths that would be created, replaced, or removed. It does not modify the filesystem.
--check performs the same read and validation work. It prints a concise stale summary and exits with the stale-projection code when disk differs from the expected projection. It produces no output and exits successfully when the projection is current.
Neither mode weakens unmanaged-file or path-safety checks.
| Code | Meaning |
|---|---|
0 |
Rendering succeeded, or --check found the projection current. |
1 |
Authentication, network, API, normalization, or filesystem failure. |
2 |
Invalid command, object, flag, or flag combination. |
3 |
--check found a stale projection. |
Errors go to stderr. Normal summaries and dry-run plans go to stdout. Errors identify the failed operation and relevant repository or path without exposing credentials.
Each object renderer must define:
- GitHub inclusion and exclusion rules.
- Pagination behavior.
- A normalized record model independent of API response structs.
- Selector combination, ranking, limiting, and tie-breaking behavior.
- How normalized selection is recorded in output.
- Stable record and collection ordering.
- Default output directory.
- Managed filename patterns.
- File schemas and examples.
- Stale-file ownership rules.
- Object-specific flags.
- Golden rendering and filesystem-safety tests.
Object specifications live under docs/objects/. A new renderer requires an approved object specification before implementation.