Conversation
…rarchy-cache feat: add GitHub hierarchy cache sync
…-openspec chore(openspec): restore project-runtime-01-safe-artifact-write-policy
Complete OpenSpec change governance-04-deterministic-agent-governance-loading: - Compact AGENTS.md/CLAUDE.md bootstrap; Cursor/Copilot alias surfaces - Canonical docs/agent-rules/ INDEX and domain rule files; docs nav - validate_agent_rule_applies_when.py; pre-commit and hierarchy-cache hardening - Unit tests for agent rules, applies_when validator, and cache script - openspec/config.yaml and change artifacts (TDD_EVIDENCE, validation) Refs: #181, #163, #178; nold-ai/specfact-cli#494 Made-with: Cursor
…tic-agent-governance-loading feat(governance): deterministic agent governance loading (governance-04)
📝 WalkthroughPull Request Summary: Release dev → main (Governance, Hierarchy Cache, Agent Rules)OverviewThis PR promotes the OpenSpec Scenario Coverage
Module/Command Surface Changes
Cross-Repo Dependency and Dev Environment
Manifest and Integrity
Documentation
Quality Gate Integration
Known Open Items
WalkthroughThis PR introduces a deterministic agent governance system by consolidating repository rules into a canonical Changes
Sequence Diagram(s)sequenceDiagram
actor Agent
participant Bootstrap as Session Bootstrap<br/>(10-session-bootstrap.md)
participant Index as Rule Index<br/>(INDEX.md)
participant Cache as GitHub Hierarchy Cache
participant Rules as Rule Files<br/>(05–80)
participant Checklist as Non-Negotiable<br/>Checklist
Agent->>Bootstrap: START: Load AGENTS.md
Bootstrap->>Bootstrap: Detect repo/worktree/branch
Bootstrap->>Bootstrap: Reject dev/main unless override
Bootstrap->>Cache: Check .specfact/backlog/...<br/>freshness
alt Cache missing or stale
Bootstrap->>Cache: python scripts/sync_...<br/>_github_hierarchy_cache.py
Cache->>Cache: Fetch Epic/Feature<br/>via gh api graphql
Cache->>Cache: Compute fingerprint,<br/>write markdown
end
Bootstrap->>Index: Load docs/agent-rules/INDEX.md
Index->>Index: Verify always-load subset<br/>and precedence
Index->>Checklist: Load 05-non-negotiable-checklist
Checklist->>Checklist: Apply mandatory gates<br/>(OpenSpec, TDD, quality)
Index->>Rules: Map task signal→<br/>required/optional rules
Rules->>Rules: Load applies_when-matched<br/>rule files (10–80)
Agent->>Rules: BEGIN implementation<br/>per rule directives
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes This PR spans heterogeneous changes across governance documentation, scripts, configuration, and tests. Key review areas: deterministic rule-loading contracts (INDEX.md, frontmatter schema, canonical signals); cache-sync fingerprinting and state reuse logic; pre-commit block staging and hook ordering; workspace environment setup; and comprehensive test coverage for new validator/cache/governance surfaces. While individual documentation files are straightforward, the interplay between bootstrap sequences, rule applicability, and environment setup requires careful verification of control flow and contract enforcement. Possibly related issues
Possibly related PRs
Suggested labels
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
|
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6cd6564cff
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| dependencies = [ | ||
| "json5>=0.9.28", | ||
| "icontract>=2.7.1", | ||
| "pytest>=8.4.2", | ||
| "pytest-cov>=7.0.0", |
There was a problem hiding this comment.
Declare beartype in the default hatch environment
The new governance scripts import beartype unconditionally (scripts/sync_github_hierarchy_cache.py and scripts/validate_agent_rule_applies_when.py), but the default Hatch environment dependencies added here do not include beartype. In a fresh checkout (or any environment that has not separately installed specfact-cli), running python scripts/sync_github_hierarchy_cache.py (now required by AGENTS bootstrap) or hatch run validate-agent-rule-signals fails immediately with ModuleNotFoundError: beartype, so the new workflow cannot run as documented.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 25
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
openspec/specs/backlog-sync/spec.md (1)
7-33:⚠️ Potential issue | 🟠 MajorPin the hierarchy-cache path contract explicitly in spec text.
repo-local hierarchy cacheis currently ambiguous. Cross-repo tooling/tests rely on the canonical files (.specfact/backlog/github_hierarchy_cache.mdand state JSON). Please make the path contract explicit here to prevent adapter drift between modules andspecfact-cli.As per coding guidelines: “`openspec/**/*.md`: Specification truth: proposal/tasks/spec deltas vs. bundle behavior, CHANGE_ORDER, and drift vs. shipped modules or docs.”Suggested spec wording update
-The system SHALL provide `specfact backlog sync` command for bidirectional backlog synchronization, and related governance workflows SHALL be able to resolve current Epic and Feature planning metadata from the repo-local hierarchy cache before performing manual GitHub lookups. +The system SHALL provide `specfact backlog sync` command for bidirectional backlog synchronization, and related governance workflows SHALL resolve current Epic and Feature planning metadata from `.specfact/backlog/github_hierarchy_cache.md` (with deterministic state tracked in `.specfact/backlog/github_hierarchy_cache_state.json`) before performing manual GitHub lookups. #### Scenario: Cache-first hierarchy lookup for parent issue assignment - **GIVEN** a contributor needs a parent Feature or Epic while preparing GitHub sync metadata -- **WHEN** the local hierarchy cache is present and current +- **WHEN** `.specfact/backlog/github_hierarchy_cache.md` is present and current - **THEN** the contributor can resolve the parent relationship from the cache without an additional GitHub lookup🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@openspec/specs/backlog-sync/spec.md` around lines 7 - 33, Update the spec text to explicitly pin the repo-local hierarchy cache path and state file names so adapters and specfact-cli share a canonical contract: replace ambiguous instances of "repo-local hierarchy cache" with the explicit paths `.specfact/backlog/github_hierarchy_cache.md` for the human-readable hierarchy and the accompanying state JSON (e.g., `.specfact/backlog/github_hierarchy_cache.state.json`), and add a short sentence in the "Cache-first hierarchy lookup for parent issue assignment" scenario noting that tools MUST read/write those exact files and only fall back to GitHub when those files are missing or stale; reference the command `specfact backlog sync` and the alias `specfact backlog ceremony sync` so the change is visible to consumers.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/agent-rules/50-quality-gates-and-review.md`:
- Around line 39-49: The quality gate list omits the bundle-import validation
step; update the "Quality gate order" sequence to include the command hatch run
check-bundle-imports in the proper place (e.g., after yaml-lint or before
verify-modules-signature) so the documented canonical order matches the updated
pre-commit flow and local validation checklist; modify the ordered list under
"Quality gate order" to insert hatch run check-bundle-imports and ensure
surrounding steps (hatch run yaml-lint and hatch run verify-modules-signature)
remain intact.
In `@docs/agent-rules/60-github-change-governance.md`:
- Around line 62-63: Replace the vague “about five minutes” with a precise
freshness rule: treat .specfact/backlog/github_hierarchy_cache.md as stale if
its recorded timestamp (use an explicit field like last_synced_at in the cache
file or the file mtime) is older than 300 seconds (5 * 60) compared to the
current UTC time; update the text and any checks that call python
scripts/sync_github_hierarchy_cache.py to compare UTC now against last_synced_at
(or mtime) using seconds to decide whether to run the sync.
In `@docs/agent-rules/70-release-commit-and-docs.md`:
- Line 45: Update Step 1 so branch-prefix guidance matches the bootstrap rules:
allow branches rooted from origin/dev with prefixes feature/*, bugfix/*,
hotfix/*, or chore/* (and optionally note the expected worktree location under
../specfact-cli-modules-worktrees/) — or explicitly state if restricting to only
feature/hotfix is intentional; modify the Step 1 text (the line "Branch from
`origin/dev` into a feature or hotfix branch.") to enumerate the allowed
prefixes or add the clarification.
In `@openspec/CHANGE_ORDER.md`:
- Line 78: The CHANGE_ORDER.md contains a pending entry for governance-03
(identifier governance-03-github-hierarchy-cache) but this change is archived;
remove the governance-03 row from the Pending table (or move it into an
Archived/Removed section) and update any parent/paired references (e.g., the
Parent Feature link to `#163` and paired specfact-cli#491 mention) so the pending
table reflects only active change artifacts and avoids drift.
In
`@openspec/changes/archive/2026-04-09-governance-03-github-hierarchy-cache/specs/backlog-sync/spec.md`:
- Around line 1-4: Add a top-level heading and ensure blank lines surround each
heading in the spec to satisfy markdown validation: insert a top-level H1 (e.g.,
"Archived Specs" or similar) above the existing "## MODIFIED Requirements", and
add a single blank line before and after each heading such as "## MODIFIED
Requirements" and "### Requirement: Restore backlog sync command functionality"
(and other headings referenced on lines 3, 12, 17, 21, 25, 31) so the `spec.md`
file, which mentions `specfact backlog sync`, has proper spacing and a top-level
heading.
In
`@openspec/changes/governance-04-deterministic-agent-governance-loading/CHANGE_VALIDATION.md`:
- Around line 27-34: Update the validation summary so it does not claim PASS
when the mandatory full review is failing: change the header/result from
"Result: PASS" to indicate PENDING/BLOCKED and add a clear note that
`.specfact/code-review.json` produced 934 findings and the full-review gate must
pass before marking the change validated; keep the existing details about
`.specfact/code-review.changed.json` passing and the commands used (`hatch run
specfact code review run --json --out .specfact/code-review.json` and the
changed-scope command) so readers see both outcomes and the required action to
re-run the full command until the full review passes.
In
`@openspec/changes/governance-04-deterministic-agent-governance-loading/proposal.md`:
- Around line 1-3: Add a top-level Markdown title before the existing "## Why"
heading to satisfy markdownlint and improve document structure; insert a line
like "# Proposal: Deterministic Agent Governance Loading" at the top of the
proposal (keeping the existing "## Why" section intact) so the document begins
with a single H1 heading.
In
`@openspec/changes/governance-04-deterministic-agent-governance-loading/specs/agent-governance-loading/spec.md`:
- Around line 1-14: The spec is missing a top-level heading; open
specs/agent-governance-loading/spec.md and add a top-level H1 heading
"Specification: Agent Governance Loading" above the existing "## ADDED
Requirements" line so the document conforms to markdownlint and matches the
pattern used in proposal.md; ensure the exact heading text is used and placed as
the first non-blank line of the file.
In
`@openspec/changes/governance-04-deterministic-agent-governance-loading/TDD_EVIDENCE.md`:
- Around line 44-47: Summary: The full-repo SpecFact quality gate still fails
(934 findings) causing release promotion to be blocked; either fix findings or
add an approved exception. Fix: run the exact verification command shown (`hatch
run specfact code review run --json --out .specfact/code-review.json --scope
full`) to regenerate `.specfact/code-review.json`, triage and remediate the
reported findings (start with high-severity items such as the legacy
`packages/specfact-backlog/src/specfact_backlog/backlog/commands.py` complexity
issues), or if remediation is infeasible create an explicit, approved exception
record in the release checklist for this PR referencing task `4.2` and attach
the `.specfact/code-review.json` artifact and justification; update the PR
description/checklist to point to the exception and ensure an approver signs off
before merge.
In
`@openspec/changes/project-runtime-01-safe-artifact-write-policy/specs/backlog-add/spec.md`:
- Around line 3-6: The markdown headings are missing required blank lines
(MD022) which breaks markdownlint; update the spec by inserting a single blank
line after the "Requirement: Backlog add local artifact helpers SHALL preserve
user-managed content" heading and another blank line after the "Scenario:
Existing local config is not silently replaced" heading so each heading is
followed by an empty line, ensuring the document adheres to markdownlint rules.
In
`@openspec/changes/project-runtime-01-safe-artifact-write-policy/specs/backlog-sync/spec.md`:
- Around line 1-6: Add a top-level H1 and ensure blank lines surround the
existing headings to satisfy markdownlint (MD041/MD022): insert a single H1 at
the top of the file (for example "Backlog sync requirements" or similar), then
ensure there is a blank line before and after "## ADDED Requirements", and add
blank lines before and after "### Requirement: Backlog sync local export paths
SHALL avoid silent overwrites" and before and after "#### Scenario: Existing
export target produces conflict or safe merge" so each heading has the required
surrounding blank lines.
In `@openspec/changes/project-runtime-01-safe-artifact-write-policy/tasks.md`:
- Around line 23-25: Reorder the checklist so quality gates run in the canonical
sequence: ensure the `hatch run format`, `hatch run type-check`, and `hatch run
lint` remain first, then list `hatch run yaml-lint` followed immediately by
`verify-modules-signature` (add or rename the step to `verify-modules-signature`
if needed), then run `hatch run contract-test`, then the relevant
`smart-test`/`test` coverage, and finally the `.specfact/code-review.json`
review step; move the version bump / re-sign changed manifests remediation (the
current 4.4) to immediately after `verify-modules-signature` so package
version/signature fixes occur before `contract-test`, and keep recording the
final review timestamp in `TDD_EVIDENCE.md` or PR notes as the last action.
In `@openspec/specs/github-hierarchy-cache/spec.md`:
- Around line 3-5: The Purpose section of this spec file (heading "Purpose") is
still "TBD" and must be replaced with a self-contained description of what the
github-hierarchy-cache spec standardizes; update the "Purpose" heading to a
concise summary that explains scope, intended behavior, and how it relates to
archived-change governance-03-github-hierarchy-cache, and ensure the
"Requirements" section references any CHANGE_ORDER, drift/shipping constraints
and the openspec/**/*.md conventions so the spec is complete before merging.
In `@README.md`:
- Line 56: Update the README run-sequence so the standalone final code-review
gate is explicitly listed and matches the pre-commit path: add the command
"hatch run specfact code review run --json --out .specfact/code-review.json" (or
the equivalent "specfact code review run --json --out
.specfact/code-review.json" if already run in a hatch context) into the top “Run
this sequence” block after tests, ensuring the sequence reads: format →
type-check → lint → yaml-lint → verify-modules-signature → contract-test →
smart-test → test → specfact code review; mention the same helper behavior
referenced by pre-commit-quality-checks.sh block2 and
scripts/pre_commit_code_review.py so manual local validation matches the
required governance order.
In `@scripts/pre-commit-quality-checks.sh`:
- Around line 149-169: The run_code_review_gate function only collects staged
Python files via staged_python_files and so skips review for non-Python contract
changes; change it to gather all review-relevant staged paths (not just
*.py/*.pyi) by calling the staged files helper that returns all staged paths
(e.g., staged_files or staged_paths) and filter/include files under packages/,
registry/, scripts/, tools/, tests/, and openspec/changes/* (but exclude any
TDD_EVIDENCE.md) before invoking scripts/pre_commit_code_review.py; ensure the
collected list is passed to scripts/pre_commit_code_review.py the same way
${py_array[@]} currently is and update the info/warn messages to reflect the
broader file set in run_code_review_gate.
- Around line 209-218: The current short-circuit in run_block2() uses
check_safe_change() too broadly and lets edits to tooling/config (e.g.,
pyproject.toml or this pre-commit script) skip the review and contract-test
decision; narrow the safe-change logic so tooling/config files do not trigger
the fast path by updating check_safe_change() usage or its predicate inside
run_block2() to exclude repo metadata that defines the quality pipeline (ensure
changes to pyproject.toml, scripts/pre-commit-quality-checks.sh, and other
pipeline-defining files bypass the safe path), then keep print_block2_overview,
run_code_review_gate and run_contract_tests_visible executing for those changes.
In `@scripts/sync_github_hierarchy_cache.py`:
- Around line 336-348: The fingerprint currently includes updated_at (causing
unnecessary churn) and omits fields that affect rendered output; in
compute_hierarchy_fingerprint replace updated_at with the exact fields used when
rendering the cache (e.g., include issue.url and issue.summary and any other
rendered inputs you write out such as title, labels, parent_number,
child_numbers) and keep deterministic ordering (sorted labels, sorted children
by number) so the hash reflects only rendered-output-relevant data; adjust the
canonical_rows construction in compute_hierarchy_fingerprint/HierarchyIssue
accordingly.
- Around line 98-100: The GraphQL subIssues connection currently uses
subIssues(first: 100) without pageInfo and the code does not handle nested
pagination, causing silent truncation; update the query to request pageInfo {
hasNextPage endCursor } for subIssues and modify the GraphQL-fetching logic that
builds the issue hierarchy (the code handling the subIssues connection /
response) to iterate using endCursor while hasNextPage to fetch all pages,
merging nodes across pages, or alternatively detect hasNextPage and raise a
clear error to prevent silent truncation; ensure the final hierarchy/fingerprint
includes all children and that any error path logs the problematic parent issue
number/URL.
In `@scripts/validate_agent_rule_applies_when.py`:
- Around line 55-59: path.read_text can raise OSError/UnicodeDecodeError and
currently crashes the validator; wrap the call to
path.read_text(encoding="utf-8") in a try/except that catches OSError and
UnicodeDecodeError, append a structured validation entry to the existing errors
list (e.g. f"{path.name}: failed to read file: {err}") and continue the loop,
then only call _parse_frontmatter(text) if read succeeded so parsed handling
remains unchanged.
- Around line 66-68: In validate_agent_rule_applies_when
(scripts/validate_agent_rule_applies_when.py) stop coercing list items to text:
when raw is a list, validate each entry is either a str or None and if any item
has a different type raise a TypeError with a clear message identifying
"applies_when" entries; only after validation build signals as the list of
strings (i.e., keep items as-is, not str(item)) and filter out None. This
replaces the current signals = [str(item) for item in raw if item is not None]
with explicit type checks and an immediate error on malformed types.
In `@src/specfact_cli_modules/dev_bootstrap.py`:
- Around line 58-62: The code uses os.environ.setdefault(...) which preserves
any previously exported SPECFACT_MODULES_REPO / SPECFACT_REPO_ROOT; instead,
always overwrite with the current checkout values: replace
os.environ.setdefault("SPECFACT_MODULES_REPO", str(resolved)) with an assignment
that sets os.environ["SPECFACT_MODULES_REPO"] = str(resolved), and if core is
not None replace os.environ.setdefault("SPECFACT_REPO_ROOT", str(core)) with
os.environ["SPECFACT_REPO_ROOT"] = str(core); locate these changes around the
resolved = repo_root.resolve() and core = resolve_core_repo(repo_root) usage in
dev_bootstrap.py.
In `@tests/unit/docs/test_agent_rules_governance.py`:
- Around line 68-74: The test function
test_agents_references_canonical_rule_docs verifies AGENTS.md contains specific
doc references and section text but the file lacks a trailing newline; open
AGENTS.md and add a single POSIX-compliant newline at EOF (ensure the file ends
with "\n") so the test environment and tools that expect a trailing newline
won't complain when running test_agents_references_canonical_rule_docs.
In `@tests/unit/scripts/test_pre_commit_code_review.py`:
- Around line 89-98: Extract the repeated mocked report payload into a single
shared test fixture/object and reuse it instead of inlining it twice;
specifically create a variable (e.g., sample_review_report =
{"overall_verdict":"FAIL","findings":[{"severity":"error","rule":"e1"},{"severity":"warning","rule":"w1"}]})
near the top of the test module and replace the inline literal passed to
_write_sample_review_report(repo_root, ...) with that variable in all
occurrences so the test_pre_commit_code_review.py assertions stay consistent and
easier to maintain.
In `@tests/unit/scripts/test_sync_github_hierarchy_cache.py`:
- Around line 459-474: Wrap the test body in a try/finally (or use a teardown
fixture) so cleanup always runs: after setting subprocess.run to _no_git via
monkeypatch.setattr and importing the module via _load_script_module(), ensure
_load_script_module.cache_clear() and
sys.modules.pop("sync_github_hierarchy_cache", None) are invoked in a finally
block; this guarantees that the monkeypatched subprocess.run is restored and the
import cache is cleared even if the assertion in
test_default_repo_name_falls_back_when_git_unavailable fails.
In `@tests/unit/tools/test_contract_first_smart_test.py`:
- Around line 44-61: Add a third "happy-path" unit test in
tests/unit/tools/test_contract_first_smart_test.py that patches
cfst_mod._git_staged_names to return a contract-relevant path (e.g.,
"contracts/some_contract.yaml" or any path your code treats as contract-related)
and assert cfst_mod._contract_test_status() == 1; reference the existing tests
for structure and reuse monkeypatch and the cfst_mod fixture, and ensure the new
test name makes intent clear (e.g.,
test_contract_test_status_returns_one_for_relevant_staged).
---
Outside diff comments:
In `@openspec/specs/backlog-sync/spec.md`:
- Around line 7-33: Update the spec text to explicitly pin the repo-local
hierarchy cache path and state file names so adapters and specfact-cli share a
canonical contract: replace ambiguous instances of "repo-local hierarchy cache"
with the explicit paths `.specfact/backlog/github_hierarchy_cache.md` for the
human-readable hierarchy and the accompanying state JSON (e.g.,
`.specfact/backlog/github_hierarchy_cache.state.json`), and add a short sentence
in the "Cache-first hierarchy lookup for parent issue assignment" scenario
noting that tools MUST read/write those exact files and only fall back to GitHub
when those files are missing or stale; reference the command `specfact backlog
sync` and the alias `specfact backlog ceremony sync` so the change is visible to
consumers.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 4f777531-c9df-4487-8b32-8337cb88bac1
📒 Files selected for processing (61)
.cursorrules.github/copilot-instructions.md.pre-commit-config.yamlAGENTS.mdCLAUDE.mdREADME.mddocs/_data/nav.ymldocs/agent-rules/05-non-negotiable-checklist.mddocs/agent-rules/10-session-bootstrap.mddocs/agent-rules/20-repository-context.mddocs/agent-rules/30-worktrees-and-branching.mddocs/agent-rules/40-openspec-and-tdd.mddocs/agent-rules/50-quality-gates-and-review.mddocs/agent-rules/60-github-change-governance.mddocs/agent-rules/70-release-commit-and-docs.mddocs/agent-rules/80-current-guidance-catalog.mddocs/agent-rules/INDEX.mdopenspec/CHANGE_ORDER.mdopenspec/changes/archive/2026-04-09-governance-03-github-hierarchy-cache/.openspec.yamlopenspec/changes/archive/2026-04-09-governance-03-github-hierarchy-cache/CHANGE_VALIDATION.mdopenspec/changes/archive/2026-04-09-governance-03-github-hierarchy-cache/TDD_EVIDENCE.mdopenspec/changes/archive/2026-04-09-governance-03-github-hierarchy-cache/design.mdopenspec/changes/archive/2026-04-09-governance-03-github-hierarchy-cache/proposal.mdopenspec/changes/archive/2026-04-09-governance-03-github-hierarchy-cache/specs/backlog-sync/spec.mdopenspec/changes/archive/2026-04-09-governance-03-github-hierarchy-cache/specs/github-hierarchy-cache/spec.mdopenspec/changes/archive/2026-04-09-governance-03-github-hierarchy-cache/tasks.mdopenspec/changes/governance-04-deterministic-agent-governance-loading/.openspec.yamlopenspec/changes/governance-04-deterministic-agent-governance-loading/CHANGE_VALIDATION.mdopenspec/changes/governance-04-deterministic-agent-governance-loading/TDD_EVIDENCE.mdopenspec/changes/governance-04-deterministic-agent-governance-loading/design.mdopenspec/changes/governance-04-deterministic-agent-governance-loading/proposal.mdopenspec/changes/governance-04-deterministic-agent-governance-loading/specs/agent-governance-loading/spec.mdopenspec/changes/governance-04-deterministic-agent-governance-loading/specs/github-hierarchy-cache/spec.mdopenspec/changes/governance-04-deterministic-agent-governance-loading/tasks.mdopenspec/changes/project-runtime-01-safe-artifact-write-policy/.openspec.yamlopenspec/changes/project-runtime-01-safe-artifact-write-policy/CHANGE_VALIDATION.mdopenspec/changes/project-runtime-01-safe-artifact-write-policy/design.mdopenspec/changes/project-runtime-01-safe-artifact-write-policy/proposal.mdopenspec/changes/project-runtime-01-safe-artifact-write-policy/specs/backlog-add/spec.mdopenspec/changes/project-runtime-01-safe-artifact-write-policy/specs/backlog-sync/spec.mdopenspec/changes/project-runtime-01-safe-artifact-write-policy/specs/runtime-artifact-write-safety/spec.mdopenspec/changes/project-runtime-01-safe-artifact-write-policy/tasks.mdopenspec/config.yamlopenspec/specs/backlog-sync/spec.mdopenspec/specs/github-hierarchy-cache/spec.mdpyproject.tomlscripts/pre-commit-quality-checks.shscripts/pre_commit_code_review.pyscripts/sync_github_hierarchy_cache.pyscripts/validate_agent_rule_applies_when.pysrc/specfact_cli_modules/dev_bootstrap.pytests/__init__.pytests/conftest.pytests/unit/docs/test_agent_rules_governance.pytests/unit/scripts/test_pre_commit_code_review.pytests/unit/scripts/test_sync_github_hierarchy_cache.pytests/unit/scripts/test_validate_agent_rule_applies_when.pytests/unit/test_dev_bootstrap.pytests/unit/test_pre_commit_quality_parity.pytests/unit/tools/test_contract_first_smart_test.pytools/contract_first_smart_test.py
| ## Quality gate order | ||
|
|
||
| 1. `hatch run format` | ||
| 2. `hatch run type-check` | ||
| 3. `hatch run lint` | ||
| 4. `hatch run yaml-lint` | ||
| 5. `hatch run verify-modules-signature --require-signature --payload-from-filesystem --enforce-version-bump` | ||
| 6. `hatch run contract-test` | ||
| 7. `hatch run smart-test` | ||
| 8. `hatch run test` | ||
|
|
There was a problem hiding this comment.
Document the bundle-import gate in the manual sequence.
This “canonical” gate order skips hatch run check-bundle-imports, even though the updated pre-commit flow and the PR’s required local validation checklist treat bundle import validation as part of the release surface. Leaving it out here will drift the rule page from the actual verification contract.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/agent-rules/50-quality-gates-and-review.md` around lines 39 - 49, The
quality gate list omits the bundle-import validation step; update the "Quality
gate order" sequence to include the command hatch run check-bundle-imports in
the proper place (e.g., after yaml-lint or before verify-modules-signature) so
the documented canonical order matches the updated pre-commit flow and local
validation checklist; modify the ordered list under "Quality gate order" to
insert hatch run check-bundle-imports and ensure surrounding steps (hatch run
yaml-lint and hatch run verify-modules-signature) remain intact.
| 1. If `.specfact/backlog/github_hierarchy_cache.md` is missing, or was last updated more than about five minutes ago, run `python scripts/sync_github_hierarchy_cache.py`. | ||
| 2. Re-read the issue state from GitHub or the refreshed cache-backed workflow and confirm the issue is still `in progress`. |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Replace “about five minutes” with an exact freshness rule.
For deterministic governance across modules and core workflows, make this threshold exact (for example, fixed seconds and explicit timestamp source/state field) instead of approximate wording.
Based on learnings: “Refresh .specfact/backlog/github_hierarchy_cache.md … when GitHub hierarchy metadata is missing or stale before parent or blocker work.”
🧰 Tools
🪛 LanguageTool
[uncategorized] ~62-~62: The official name of this software platform is spelled with a capital “H”.
Context: ... a current view of GitHub state: 1. If .specfact/backlog/github_hierarchy_cache.md is missing, or was ...
(GITHUB)
[uncategorized] ~62-~62: The official name of this software platform is spelled with a capital “H”.
Context: ...d more than about five minutes ago, run python scripts/sync_github_hierarchy_cache.py. 2. Re-read the iss...
(GITHUB)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/agent-rules/60-github-change-governance.md` around lines 62 - 63,
Replace the vague “about five minutes” with a precise freshness rule: treat
.specfact/backlog/github_hierarchy_cache.md as stale if its recorded timestamp
(use an explicit field like last_synced_at in the cache file or the file mtime)
is older than 300 seconds (5 * 60) compared to the current UTC time; update the
text and any checks that call python scripts/sync_github_hierarchy_cache.py to
compare UTC now against last_synced_at (or mtime) using seconds to decide
whether to run the sync.
|
|
||
| ## Registry and publish flow | ||
|
|
||
| 1. Branch from `origin/dev` into a feature or hotfix branch. |
There was a problem hiding this comment.
Keep the branch-prefix guidance aligned with the bootstrap rules.
Step 1 narrows this to feature or hotfix, but the canonical bootstrap guidance also allows bugfix/* and chore/*. That gives agents conflicting instructions during finalization/release flows unless this restriction is intentional and called out explicitly. Based on learnings "Work belongs on feature/, bugfix/, hotfix/, or chore/ branches, normally in a worktree rooted under ../specfact-cli-modules-worktrees/."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/agent-rules/70-release-commit-and-docs.md` at line 45, Update Step 1 so
branch-prefix guidance matches the bootstrap rules: allow branches rooted from
origin/dev with prefixes feature/*, bugfix/*, hotfix/*, or chore/* (and
optionally note the expected worktree location under
../specfact-cli-modules-worktrees/) — or explicitly state if restricting to only
feature/hotfix is intentional; modify the Step 1 text (the line "Branch from
`origin/dev` into a feature or hotfix branch.") to enumerate the allowed
prefixes or add the clarification.
| |--------|-------|---------------|----------|------------| | ||
| | governance | 01 | governance-01-evidence-output | [#169](https://github.com/nold-ai/specfact-cli-modules/issues/169) | Parent Feature: [#163](https://github.com/nold-ai/specfact-cli-modules/issues/163); core counterpart `specfact-cli#247`; validation runtime `#171` | | ||
| | governance | 02 | governance-02-exception-management | [#167](https://github.com/nold-ai/specfact-cli-modules/issues/167) | Parent Feature: [#163](https://github.com/nold-ai/specfact-cli-modules/issues/163); core counterpart `specfact-cli#248`; policy runtime `#158` | | ||
| | governance | 03 | governance-03-github-hierarchy-cache | [#178](https://github.com/nold-ai/specfact-cli-modules/issues/178) | Parent Feature: [#163](https://github.com/nold-ai/specfact-cli-modules/issues/163); paired core `governance-02-github-hierarchy-cache` [specfact-cli#491](https://github.com/nold-ai/specfact-cli/issues/491) | |
There was a problem hiding this comment.
governance-03 should not remain in the Pending table if it is archived.
Line 78 conflicts with the archived change artifacts included in this PR, which creates change-order drift and can mislead dependency planning.
As per coding guidelines: "openspec/**/*.md: Specification truth: proposal/tasks/spec deltas vs. bundle behavior, CHANGE_ORDER, and drift vs. shipped modules or docs."
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@openspec/CHANGE_ORDER.md` at line 78, The CHANGE_ORDER.md contains a pending
entry for governance-03 (identifier governance-03-github-hierarchy-cache) but
this change is archived; remove the governance-03 row from the Pending table (or
move it into an Archived/Removed section) and update any parent/paired
references (e.g., the Parent Feature link to `#163` and paired specfact-cli#491
mention) so the pending table reflects only active change artifacts and avoids
drift.
| ## MODIFIED Requirements | ||
|
|
||
| ### Requirement: Restore backlog sync command functionality | ||
| The system SHALL provide `specfact backlog sync` command for bidirectional backlog synchronization, and related governance workflows SHALL be able to resolve current Epic and Feature planning metadata from the repo-local hierarchy cache before performing manual GitHub lookups. |
There was a problem hiding this comment.
Minor markdown formatting inconsistencies.
Static analysis flagged missing top-level heading (line 1 starts with ## MODIFIED) and missing blank lines around headings (lines 3, 12, 17, 21, 25, 31). Since this is an archived spec, these are cosmetic—consider fixing if the archive format is subject to docs validation.
📝 Optional fix to add top-level heading and blank lines
+# Backlog Sync Specification
+
## MODIFIED Requirements
### Requirement: Restore backlog sync command functionality
+
The system SHALL provide `specfact backlog sync` command for bidirectional backlog synchronization, and related governance workflows SHALL be able to resolve current Epic and Feature planning metadata from the repo-local hierarchy cache before performing manual GitHub lookups.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| ## MODIFIED Requirements | |
| ### Requirement: Restore backlog sync command functionality | |
| The system SHALL provide `specfact backlog sync` command for bidirectional backlog synchronization, and related governance workflows SHALL be able to resolve current Epic and Feature planning metadata from the repo-local hierarchy cache before performing manual GitHub lookups. | |
| # Backlog Sync Specification | |
| ## MODIFIED Requirements | |
| ### Requirement: Restore backlog sync command functionality | |
| The system SHALL provide `specfact backlog sync` command for bidirectional backlog synchronization, and related governance workflows SHALL be able to resolve current Epic and Feature planning metadata from the repo-local hierarchy cache before performing manual GitHub lookups. |
🧰 Tools
🪛 markdownlint-cli2 (0.22.0)
[warning] 1-1: First line in a file should be a top-level heading
(MD041, first-line-heading, first-line-h1)
[warning] 3-3: Headings should be surrounded by blank lines
Expected: 1; Actual: 0; Below
(MD022, blanks-around-headings)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In
`@openspec/changes/archive/2026-04-09-governance-03-github-hierarchy-cache/specs/backlog-sync/spec.md`
around lines 1 - 4, Add a top-level heading and ensure blank lines surround each
heading in the spec to satisfy markdown validation: insert a top-level H1 (e.g.,
"Archived Specs" or similar) above the existing "## MODIFIED Requirements", and
add a single blank line before and after each heading such as "## MODIFIED
Requirements" and "### Requirement: Restore backlog sync command functionality"
(and other headings referenced on lines 3, 12, 17, 21, 25, 31) so the `spec.md`
file, which mentions `specfact backlog sync`, has proper spacing and a top-level
heading.
| resolved = repo_root.resolve() | ||
| os.environ.setdefault("SPECFACT_MODULES_REPO", str(resolved)) | ||
| core = resolve_core_repo(repo_root) | ||
| if core is not None: | ||
| os.environ.setdefault("SPECFACT_REPO_ROOT", str(core)) |
There was a problem hiding this comment.
Overwrite the workspace repo env vars here instead of preserving stale exports.
setdefault() lets an old shell-exported SPECFACT_MODULES_REPO / SPECFACT_REPO_ROOT survive, which can point pytest, pre-commit, and the sibling nold-ai/specfact-cli scripts at a different checkout than the one currently being bootstrapped.
💡 Proposed fix
- os.environ.setdefault("SPECFACT_MODULES_REPO", str(resolved))
+ os.environ["SPECFACT_MODULES_REPO"] = str(resolved)
core = resolve_core_repo(repo_root)
if core is not None:
- os.environ.setdefault("SPECFACT_REPO_ROOT", str(core))
+ os.environ["SPECFACT_REPO_ROOT"] = str(core)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| resolved = repo_root.resolve() | |
| os.environ.setdefault("SPECFACT_MODULES_REPO", str(resolved)) | |
| core = resolve_core_repo(repo_root) | |
| if core is not None: | |
| os.environ.setdefault("SPECFACT_REPO_ROOT", str(core)) | |
| resolved = repo_root.resolve() | |
| os.environ["SPECFACT_MODULES_REPO"] = str(resolved) | |
| core = resolve_core_repo(repo_root) | |
| if core is not None: | |
| os.environ["SPECFACT_REPO_ROOT"] = str(core) |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/specfact_cli_modules/dev_bootstrap.py` around lines 58 - 62, The code
uses os.environ.setdefault(...) which preserves any previously exported
SPECFACT_MODULES_REPO / SPECFACT_REPO_ROOT; instead, always overwrite with the
current checkout values: replace os.environ.setdefault("SPECFACT_MODULES_REPO",
str(resolved)) with an assignment that sets os.environ["SPECFACT_MODULES_REPO"]
= str(resolved), and if core is not None replace
os.environ.setdefault("SPECFACT_REPO_ROOT", str(core)) with
os.environ["SPECFACT_REPO_ROOT"] = str(core); locate these changes around the
resolved = repo_root.resolve() and core = resolve_core_repo(repo_root) usage in
dev_bootstrap.py.
| def test_agents_references_canonical_rule_docs() -> None: | ||
| agents_text = (REPO_ROOT / "AGENTS.md").read_text(encoding="utf-8") | ||
|
|
||
| assert "docs/agent-rules/INDEX.md" in agents_text | ||
| assert "docs/agent-rules/05-non-negotiable-checklist.md" in agents_text | ||
| assert "## Strategic context" in agents_text | ||
| assert "Shared design and governance context lives in the paired public `specfact-cli` repository" in agents_text |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
AGENTS.md reference check ensures canonical rule docs are discoverable.
Verifying that AGENTS.md contains references to docs/agent-rules/INDEX.md and docs/agent-rules/05-non-negotiable-checklist.md ensures the compact bootstrap surface points to the canonical governance docs. The section text assertions (## Strategic context, paired repo reference) verify structural integrity.
Minor: Add a trailing newline at end of file for POSIX compliance.
🔧 Add trailing newline
assert "docs/agent-rules/INDEX.md" in agents_text
assert "docs/agent-rules/05-non-negotiable-checklist.md" in agents_text
assert "## Strategic context" in agents_text
assert "Shared design and governance context lives in the paired public `specfact-cli` repository" in agents_text
+📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def test_agents_references_canonical_rule_docs() -> None: | |
| agents_text = (REPO_ROOT / "AGENTS.md").read_text(encoding="utf-8") | |
| assert "docs/agent-rules/INDEX.md" in agents_text | |
| assert "docs/agent-rules/05-non-negotiable-checklist.md" in agents_text | |
| assert "## Strategic context" in agents_text | |
| assert "Shared design and governance context lives in the paired public `specfact-cli` repository" in agents_text | |
| def test_agents_references_canonical_rule_docs() -> None: | |
| agents_text = (REPO_ROOT / "AGENTS.md").read_text(encoding="utf-8") | |
| assert "docs/agent-rules/INDEX.md" in agents_text | |
| assert "docs/agent-rules/05-non-negotiable-checklist.md" in agents_text | |
| assert "## Strategic context" in agents_text | |
| assert "Shared design and governance context lives in the paired public `specfact-cli` repository" in agents_text | |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/unit/docs/test_agent_rules_governance.py` around lines 68 - 74, The
test function test_agents_references_canonical_rule_docs verifies AGENTS.md
contains specific doc references and section text but the file lacks a trailing
newline; open AGENTS.md and add a single POSIX-compliant newline at EOF (ensure
the file ends with "\n") so the test environment and tools that expect a
trailing newline won't complain when running
test_agents_references_canonical_rule_docs.
| _write_sample_review_report( | ||
| repo_root, | ||
| { | ||
| "overall_verdict": "FAIL", | ||
| "findings": [ | ||
| {"severity": "error", "rule": "e1"}, | ||
| {"severity": "warning", "rule": "w1"}, | ||
| ], | ||
| }, | ||
| ) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Deduplicate the mocked report payload to reduce test drift.
This block repeats the same JSON payload used earlier in the test; extracting one shared fixture/object would keep the contract assertions easier to maintain.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/unit/scripts/test_pre_commit_code_review.py` around lines 89 - 98,
Extract the repeated mocked report payload into a single shared test
fixture/object and reuse it instead of inlining it twice; specifically create a
variable (e.g., sample_review_report =
{"overall_verdict":"FAIL","findings":[{"severity":"error","rule":"e1"},{"severity":"warning","rule":"w1"}]})
near the top of the test module and replace the inline literal passed to
_write_sample_review_report(repo_root, ...) with that variable in all
occurrences so the test_pre_commit_code_review.py assertions stay consistent and
easier to maintain.
| def test_default_repo_name_falls_back_when_git_unavailable(monkeypatch: pytest.MonkeyPatch) -> None: | ||
| """If ``git`` is missing, DEFAULT_REPO_NAME must use the checkout directory fallback.""" | ||
| _load_script_module.cache_clear() | ||
| sys.modules.pop("sync_github_hierarchy_cache", None) | ||
|
|
||
| def _no_git(*_args: Any, **_kwargs: Any) -> Any: | ||
| raise FileNotFoundError("git not found") | ||
|
|
||
| monkeypatch.setattr(subprocess, "run", _no_git) | ||
| module = _load_script_module() | ||
| script_path = Path(__file__).resolve().parents[3] / "scripts" / "sync_github_hierarchy_cache.py" | ||
| expected_fallback = script_path.resolve().parents[1].name | ||
| assert expected_fallback == module.DEFAULT_REPO_NAME | ||
|
|
||
| _load_script_module.cache_clear() | ||
| sys.modules.pop("sync_github_hierarchy_cache", None) |
There was a problem hiding this comment.
Test isolation: ensure cache cleanup runs even if assertions fail.
The _load_script_module.cache_clear() at line 473 won't execute if earlier assertions fail, potentially leaving subprocess.run monkeypatched for subsequent tests. Consider using a try/finally or pytest fixture with teardown to guarantee cleanup.
🛡️ Proposed fix using try/finally
def test_default_repo_name_falls_back_when_git_unavailable(monkeypatch: pytest.MonkeyPatch) -> None:
"""If ``git`` is missing, DEFAULT_REPO_NAME must use the checkout directory fallback."""
_load_script_module.cache_clear()
sys.modules.pop("sync_github_hierarchy_cache", None)
def _no_git(*_args: Any, **_kwargs: Any) -> Any:
raise FileNotFoundError("git not found")
monkeypatch.setattr(subprocess, "run", _no_git)
- module = _load_script_module()
- script_path = Path(__file__).resolve().parents[3] / "scripts" / "sync_github_hierarchy_cache.py"
- expected_fallback = script_path.resolve().parents[1].name
- assert expected_fallback == module.DEFAULT_REPO_NAME
-
- _load_script_module.cache_clear()
- sys.modules.pop("sync_github_hierarchy_cache", None)
+ try:
+ module = _load_script_module()
+ script_path = Path(__file__).resolve().parents[3] / "scripts" / "sync_github_hierarchy_cache.py"
+ expected_fallback = script_path.resolve().parents[1].name
+ assert expected_fallback == module.DEFAULT_REPO_NAME
+ finally:
+ _load_script_module.cache_clear()
+ sys.modules.pop("sync_github_hierarchy_cache", None)🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/unit/scripts/test_sync_github_hierarchy_cache.py` around lines 459 -
474, Wrap the test body in a try/finally (or use a teardown fixture) so cleanup
always runs: after setting subprocess.run to _no_git via monkeypatch.setattr and
importing the module via _load_script_module(), ensure
_load_script_module.cache_clear() and
sys.modules.pop("sync_github_hierarchy_cache", None) are invoked in a finally
block; this guarantees that the monkeypatched subprocess.run is restored and the
import cache is cleared even if the assertion in
test_default_repo_name_falls_back_when_git_unavailable fails.
| def test_contract_test_status_returns_one_when_git_fails(monkeypatch: pytest.MonkeyPatch, cfst_mod) -> None: | ||
| monkeypatch.setattr( | ||
| cfst_mod, | ||
| "_git_staged_names", | ||
| lambda _root: None, | ||
| ) | ||
| assert cfst_mod._contract_test_status() == 1 | ||
|
|
||
|
|
||
| def test_contract_test_status_returns_zero_when_only_irrelevant_staged( | ||
| monkeypatch: pytest.MonkeyPatch, cfst_mod | ||
| ) -> None: | ||
| monkeypatch.setattr( | ||
| cfst_mod, | ||
| "_git_staged_names", | ||
| lambda _root: ["docs/README.md"], | ||
| ) | ||
| assert cfst_mod._contract_test_status() == 0 |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major
Add the happy-path _contract_test_status() assertion.
The new status helper is only covered for “git failed” and “irrelevant staged paths”. We still need the primary branch where _git_staged_names() returns a contract-relevant path and _contract_test_status() must return 1, otherwise Block 2’s trigger path can regress unnoticed.
🧪 Suggested test
def test_contract_test_status_returns_zero_when_only_irrelevant_staged(
monkeypatch: pytest.MonkeyPatch, cfst_mod
) -> None:
monkeypatch.setattr(
cfst_mod,
"_git_staged_names",
lambda _root: ["docs/README.md"],
)
assert cfst_mod._contract_test_status() == 0
+
+
+def test_contract_test_status_returns_one_when_relevant_files_are_staged(
+ monkeypatch: pytest.MonkeyPatch, cfst_mod
+) -> None:
+ monkeypatch.setattr(
+ cfst_mod,
+ "_git_staged_names",
+ lambda _root: ["registry/index.json"],
+ )
+ assert cfst_mod._contract_test_status() == 1📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def test_contract_test_status_returns_one_when_git_fails(monkeypatch: pytest.MonkeyPatch, cfst_mod) -> None: | |
| monkeypatch.setattr( | |
| cfst_mod, | |
| "_git_staged_names", | |
| lambda _root: None, | |
| ) | |
| assert cfst_mod._contract_test_status() == 1 | |
| def test_contract_test_status_returns_zero_when_only_irrelevant_staged( | |
| monkeypatch: pytest.MonkeyPatch, cfst_mod | |
| ) -> None: | |
| monkeypatch.setattr( | |
| cfst_mod, | |
| "_git_staged_names", | |
| lambda _root: ["docs/README.md"], | |
| ) | |
| assert cfst_mod._contract_test_status() == 0 | |
| def test_contract_test_status_returns_one_when_git_fails(monkeypatch: pytest.MonkeyPatch, cfst_mod) -> None: | |
| monkeypatch.setattr( | |
| cfst_mod, | |
| "_git_staged_names", | |
| lambda _root: None, | |
| ) | |
| assert cfst_mod._contract_test_status() == 1 | |
| def test_contract_test_status_returns_zero_when_only_irrelevant_staged( | |
| monkeypatch: pytest.MonkeyPatch, cfst_mod | |
| ) -> None: | |
| monkeypatch.setattr( | |
| cfst_mod, | |
| "_git_staged_names", | |
| lambda _root: ["docs/README.md"], | |
| ) | |
| assert cfst_mod._contract_test_status() == 0 | |
| def test_contract_test_status_returns_one_when_relevant_files_are_staged( | |
| monkeypatch: pytest.MonkeyPatch, cfst_mod | |
| ) -> None: | |
| monkeypatch.setattr( | |
| cfst_mod, | |
| "_git_staged_names", | |
| lambda _root: ["registry/index.json"], | |
| ) | |
| assert cfst_mod._contract_test_status() == 1 |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@tests/unit/tools/test_contract_first_smart_test.py` around lines 44 - 61, Add
a third "happy-path" unit test in
tests/unit/tools/test_contract_first_smart_test.py that patches
cfst_mod._git_staged_names to return a contract-relevant path (e.g.,
"contracts/some_contract.yaml" or any path your code treats as contract-related)
and assert cfst_mod._contract_test_status() == 1; reference the existing tests
for structure and reuse monkeypatch and the cfst_mod fixture, and ensure the new
test name makes intent clear (e.g.,
test_contract_test_status_returns_one_for_relevant_staged).
Summary
Promote
devtomainafter merging PR #182 (deterministic agent governance loading). This release train also includes PR #180 (project-runtime OpenSpec recovery) and PR #179 (GitHub hierarchy cache script and governance), plus follow-up fixes on the governance/pre-commit path.Refs:
Scope
packages/registry/index.json,packages/*/module-package.yaml).github/workflows/*)docs/*,README.md,AGENTS.md)scripts/sign-modules.py,scripts/verify-modules-signature.py)Note:
.github/copilot-instructions.mdchanged; workflow YAML under.github/workflows/did not.Bundle Impact
No bundle or registry changes in
origin/main..origin/dev(nopackages/orregistry/diffs). Version lines below are unchanged for this promotion.nold-ai/specfact-project: unchangednold-ai/specfact-backlog: unchangednold-ai/specfact-codebase: unchangednold-ai/specfact-spec: unchangednold-ai/specfact-govern: unchangedValidation Evidence
Attach snippets or workflow run links after running the gates below on this branch (or cite green checks from the merged feature PRs).
Required local gates
hatch run formathatch run type-checkhatch run linthatch run yaml-linthatch run check-bundle-importshatch run contract-testhatch run smart-test(orhatch run test)Signature + version integrity (required)
hatch run verify-modules-signature --require-signature --payload-from-filesystem --enforce-version-bumpNo bundle content changed in this range; confirm signature gate still passes on
devbefore merge.CI and Branch Protection
verify-module-signaturesquality (3.11)quality (3.12)quality (3.13)Docs / Pages
docs/)docs-pages.yml, if changed)specfact-clidocs updated (if applicable)Checklist