Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,11 @@
- `python3 -m py_compile scripts/sync_github_hierarchy_cache.py` → PASS
- `python3 scripts/sync_github_hierarchy_cache.py --force` → generated `.specfact/backlog/github_hierarchy_cache.md`
- Second `python3 scripts/sync_github_hierarchy_cache.py` run → `GitHub hierarchy cache unchanged (46 issues).`

## Final scoped quality gates

- Timestamp: `2026-04-09T22:04:18+02:00`
- Ruff: `PATH=/home/dom/git/nold-ai/specfact-cli/.venv/bin:$PATH PYTHONPATH=/home/dom/git/nold-ai/specfact-cli-worktrees/feature/governance-02-github-hierarchy-cache/src /home/dom/git/nold-ai/specfact-cli/.venv/bin/ruff check scripts/sync_github_hierarchy_cache.py tests/unit/scripts/test_sync_github_hierarchy_cache.py` → PASS
- basedpyright: `PATH=/home/dom/git/nold-ai/specfact-cli/.venv/bin:$PATH PYTHONPATH=/home/dom/git/nold-ai/specfact-cli-worktrees/feature/governance-02-github-hierarchy-cache/src /home/dom/git/nold-ai/specfact-cli/.venv/bin/basedpyright scripts/sync_github_hierarchy_cache.py tests/unit/scripts/test_sync_github_hierarchy_cache.py` → PASS (`0 errors, 0 warnings`)
- pytest: `hatch test -- tests/unit/scripts/test_sync_github_hierarchy_cache.py -q` → PASS (`15 passed`, after parity with modules-side script/tests)
- SpecFact code review: `hatch run specfact code review run --json --out .specfact/code-review.json scripts/sync_github_hierarchy_cache.py tests/unit/scripts/test_sync_github_hierarchy_cache.py` → PASS (`overall_verdict` PASS, `ci_exit_code` 0; low-severity DRY hints on icontract preconditions documented in `proposal.md`)
14 changes: 13 additions & 1 deletion openspec/changes/governance-02-github-hierarchy-cache/design.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
## Context
# Context

`specfact-cli` already maintains GitHub planning hierarchy through issue labels, parent-child links, and `openspec/CHANGE_ORDER.md`, but contributors still discover that structure by hitting the GitHub API manually. The new requirement is to make hierarchy lookup deterministic, cheap, and local: a generated markdown file under ignored `.specfact/backlog/` becomes the first source for parent Feature and Epic resolution, and the sync command is rerun only when the hierarchy changed.

Expand All @@ -7,12 +7,14 @@ This is a cross-cutting governance change because it affects GitHub automation,
## Goals / Non-Goals

**Goals:**

- Generate a deterministic markdown cache of Epic and Feature issues for this repository.
- Include enough metadata for issue-parenting work without another GitHub lookup: issue number, title, short summary, labels, parent/child relationships, and issue URLs.
- Make the sync fast on no-op runs by using a small fingerprint/state check before regenerating markdown.
- Update repo guidance so contributors use the cache first and only rerun sync when needed.

**Non-Goals:**

- Replacing GitHub as the authoritative source of issue hierarchy.
- Caching every issue type or full issue bodies.
- Synchronizing User Story issues into the cache in this first version.
Expand All @@ -21,33 +23,43 @@ This is a cross-cutting governance change because it affects GitHub automation,
## Decisions

### Use `gh api graphql` as the sole upstream source

The script will query GitHub through `gh api graphql` so it can access issue type, labels, relationships, and brief body content in one supported path. This avoids scraping markdown or depending on REST endpoints that do not expose hierarchy fields consistently.

Alternative considered:

- `gh issue list/view` JSON loops: simpler, but requires many calls and awkward relationship reconstruction.

### Split the sync into a lightweight fingerprint pass and a full render pass

The script will first fetch only the Epic and Feature issue identity set plus timestamps/relationship fingerprints, hash that data, and compare it with a local state file. If the fingerprint matches, the script exits successfully without rewriting markdown. If it differs, the script performs a fuller metadata query and regenerates the cache.

Alternative considered:

- Always regenerate markdown: deterministic but wastes GitHub calls and makes local workflows slower.

### Store human-readable cache plus machine-readable state under ignored `.specfact/backlog`

The canonical human-facing output will be `.specfact/backlog/github_hierarchy_cache.md`. A companion state file, `.specfact/backlog/github_hierarchy_cache_state.json`, will hold the last fingerprint and generator metadata. Both files stay local and ignored by Git so the cache can be recreated freely without creating repository drift.

Alternative considered:

- State embedded in markdown comments: workable, but couples machine state to user-facing output and complicates deterministic rendering.

### Render by deterministic section and sort order

The markdown will use fixed sections for Epics and Features, with issues sorted stably by type, then issue number. Relationship lists and labels will also be sorted deterministically so reruns only change the file when source metadata actually changes.

Alternative considered:

- Preserve GitHub API order: easier, but can drift between runs and create noisy diffs.

### Keep instruction updates in repo-local governance files

The change will update `openspec/config.yaml` and `AGENTS.md` in this repo so the workflow explicitly says: consult the cache first, regenerate it when fresh planning metadata is needed, and avoid ad hoc GitHub lookups unless the cache is stale or missing.

Alternative considered:

- Document the behavior only in the script help text: insufficient because agents and OpenSpec flows read governance files first.

## Risks / Trade-offs
Expand Down
13 changes: 10 additions & 3 deletions openspec/changes/governance-02-github-hierarchy-cache/proposal.md
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
# Governance: GitHub hierarchy cache (specfact-cli)

## Why

OpenSpec and agent workflows still have to query GitHub ad hoc to rediscover Epics, Features, and their parent links before creating or syncing change issues. That is slow, expensive, and error-prone, especially now that planning hierarchy matters in both `specfact-cli` and `specfact-cli-modules`.

## What Changes

- Add a deterministic repo-local hierarchy cache generator for GitHub Epic and Feature issues.
- Persist a central markdown inventory under `openspec/` with issue number, title, brief summary, labels, and hierarchy relationships.
- Add a lightweight fingerprint/state check so the sync exits quickly when Epic and Feature metadata has not changed.
- Update governance instructions in `openspec/config.yaml` and `AGENTS.md` to consult the cached hierarchy inventory first and rerun the sync script when fresh data is needed.
- Persist a repo-local markdown hierarchy cache at `.specfact/backlog/github_hierarchy_cache.md` (ignored; not committed) with issue number, title, brief summary, labels, and hierarchy relationships, plus a companion fingerprint/state file `.specfact/backlog/github_hierarchy_cache_state.json` so the sync can exit quickly when Epic and Feature metadata has not changed.
- Update governance instructions in `openspec/config.yaml` and `AGENTS.md` to consult the cached hierarchy first and rerun `python scripts/sync_github_hierarchy_cache.py` when fresh data is needed.
- Cover the script with tests so cache output and no-change behavior remain stable.

## Capabilities

### New Capabilities

- `github-hierarchy-cache`: Deterministic synchronization of GitHub Epic and Feature hierarchy metadata into a repo-local OpenSpec markdown cache for low-cost parent and planning lookups.

### Modified Capabilities

- `agile-feature-hierarchy`: Local governance workflows must be able to resolve current Epic and Feature planning metadata from the repo-local cache before performing manual GitHub lookups.

## Impact
Expand All @@ -29,3 +32,7 @@ OpenSpec and agent workflows still have to query GitHub ad hoc to rediscover Epi
- GitHub Issue: [#491](https://github.com/nold-ai/specfact-cli/issues/491)
- Parent Feature: [#486](https://github.com/nold-ai/specfact-cli/issues/486)
- Related Modules Change: `specfact-cli-modules/governance-03-github-hierarchy-cache`

## Code review note (SpecFact dogfood)

Icontract `@require` preconditions on `fetch_hierarchy_issues`, `render_cache_markdown`, and `sync_cache` intentionally use small, similarly shaped predicates. The code-review module may emit low-severity DRY / duplicate-shape hints for those helpers; that is accepted here because collapsing them would break icontract’s per-parameter argument binding (e.g. `**kwargs` predicates are not supported the same way).
Original file line number Diff line number Diff line change
@@ -1,28 +1,34 @@
## ADDED Requirements
# ADDED Requirements

## Requirement: Repository hierarchy cache sync

### Requirement: Repository hierarchy cache sync
The repository SHALL provide a deterministic sync mechanism that retrieves GitHub Epic and Feature issues for the current repository and writes a local hierarchy cache under ignored `.specfact/backlog/`.

#### Scenario: Generate hierarchy cache from GitHub metadata
### Scenario: Generate hierarchy cache from GitHub metadata

- **WHEN** the user runs the hierarchy cache sync script for the repository
- **THEN** the script retrieves GitHub issues whose Type is `Epic` or `Feature`
- **AND** writes a markdown cache under ignored `.specfact/backlog/` with each issue's number, title, URL, short summary, labels, and hierarchy relationships
- **AND** the output ordering is deterministic across repeated runs with unchanged source data

#### Scenario: Represent hierarchy relationships in cache output
### Scenario: Represent hierarchy relationships in cache output

- **WHEN** a synced Epic or Feature has parent or child hierarchy links
- **THEN** the markdown cache includes those relationships in normalized form
- **AND** missing relationships are rendered as explicit empty or none values rather than omitted ambiguously

#### Scenario: Fast exit on unchanged hierarchy state
### Scenario: Fast exit on unchanged hierarchy state

- **WHEN** the script detects that the current Epic and Feature hierarchy fingerprint matches the last synced fingerprint
- **THEN** it exits successfully without regenerating the markdown cache
- **AND** it reports that no hierarchy update was required

### Requirement: Repository governance must use cache-first hierarchy lookup
## Requirement: Repository governance must use cache-first hierarchy lookup

Repository governance instructions SHALL direct contributors and agents to consult the local hierarchy cache before performing manual GitHub lookups for Epic or Feature parenting.

#### Scenario: Cache-first governance guidance
### Scenario: Cache-first governance guidance

- **WHEN** a contributor reads `AGENTS.md` or `openspec/config.yaml` for GitHub issue setup guidance
- **THEN** the instructions tell them to consult the local hierarchy cache first
- **AND** the instructions define when the sync script must be rerun to refresh stale hierarchy metadata
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,4 @@
## 4. Verification

- [x] 4.1 Re-run the targeted tests and record the passing run in `openspec/changes/governance-02-github-hierarchy-cache/TDD_EVIDENCE.md`.
- [ ] 4.2 Run the required repo quality gates for the touched scope, including code review JSON refresh if stale.
- [x] 4.2 Run the required repo quality gates for the touched scope, including code review JSON refresh if stale.
17 changes: 11 additions & 6 deletions openspec/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,12 @@ rules:
- **Repository**: <owner>/<name>, - **Last Synced Status**: <status>).
- After creation, update proposal.md Source Tracking section with issue number, URL, repository,
and status.
- Resolve Parent Feature or Epic from `.specfact/backlog/github_hierarchy_cache.md` first; this
cache is ephemeral local state and MUST NOT be committed. If it is missing or stale, rerun
`python scripts/sync_github_hierarchy_cache.py` before manual GitHub lookup.
- >-
Resolve Parent Feature or Epic from `.specfact/backlog/github_hierarchy_cache.md` first (regenerate
via `python scripts/sync_github_hierarchy_cache.py` when missing or stale). The cache is ephemeral
local state and MUST NOT be committed. **Pending:** until backlog commands read this cache
automatically, treat this as contributor/agent workflow (docs + local script), not enforced CLI
behavior for every `specfact backlog` path.
- Source tracking: Only track public repos (specfact-cli, platform-frontend). Skip for internal repos (specfact-cli-internal)

specs:
Expand Down Expand Up @@ -160,9 +163,11 @@ rules:
- Place this task after quality gates and documentation, before PR creation.
- Include git workflow tasks: branch creation (first task), PR creation (last task)
- For public-facing changes in public repos (specfact-cli, platform-frontend):
- Before GitHub issue creation or parent linking, consult `.specfact/backlog/github_hierarchy_cache.md`;
rerun `python scripts/sync_github_hierarchy_cache.py` when the cache is missing or stale.
Treat this cache as ephemeral local state, not a committed OpenSpec artifact.
- >-
Before GitHub issue creation or parent linking, consult `.specfact/backlog/github_hierarchy_cache.md`;
rerun `python scripts/sync_github_hierarchy_cache.py` when the cache is missing or stale. Treat this
cache as ephemeral local state, not a committed OpenSpec artifact. **Pending:** wire cache-first
lookup into backlog add/sync codepaths when the governance hierarchy-cache work lands end-to-end.
- Include GitHub issue creation task with format:
- title `[Change] <Brief Description>`
- labels `enhancement` and `change-proposal`
Expand Down
Loading
Loading