Skip to content

Support emitting GHA annotations#5

Merged
xen0n merged 23 commits intomainfrom
gha-annotations
Mar 14, 2026
Merged

Support emitting GHA annotations#5
xen0n merged 23 commits intomainfrom
gha-annotations

Conversation

@xen0n
Copy link
Contributor

@xen0n xen0n commented Mar 14, 2026

No description provided.

xen0n added 2 commits March 14, 2026 17:21
When `liyi check` detects `GITHUB_ACTIONS=true` in the environment
(set automatically by GitHub Actions runners), diagnostics are emitted
as `::notice`, `::warning`, or `::error` workflow commands with `file`,
`line`, and `title` parameters. This causes annotations to appear
inline in PR diffs — stale specs as warnings, errors as errors, and
informational items as notices.

No flags required; the env var is auto-detected. Exit codes, summary
line, and `--prompt` JSON output are unchanged.

Original prompt:

> Let's detect `GITHUB_ACTIONS=true` and emit the diagnostics then.
> I can't wait to try out.

AI-assisted-by: Claude Opus 4.6 (GitHub Copilot)
Signed-off-by: WANG Xuerui <git@xen0n.name>
Add an `intent` field to the `Diagnostic` struct and thread the sidecar
intent text through all item-spec diagnostics in `check.rs`. When
`GITHUB_ACTIONS=true`, the annotation message now appends
"Intent: <text>" after the diagnostic message, so reviewers see what
the code is supposed to do directly in the PR diff.

Sentinel values (`=doc`, `=trivial`) are excluded — only substantive
intent text is shown. Requirement diagnostics emit `intent: None`
since they don't carry item intent.

Original prompt:

> let's commit first, then do the "intent text in annotations" part.

AI-assisted-by: Claude Opus 4.6 (GitHub Copilot)
Signed-off-by: WANG Xuerui <git@xen0n.name>
@xen0n xen0n force-pushed the gha-annotations branch 2 times, most recently from 1f27b50 to 166a396 Compare March 14, 2026 12:06
xen0n added 21 commits March 14, 2026 20:09
Signed-off-by: WANG Xuerui <git@xen0n.name>
When a reviewed item's source code changes, `liyi check --fix` refuses to
auto-rehash — the spec remains stale with no structured resolution path
other than manual sidecar editing or agent triage. This is a gap: `liyi
approve` should surface these items alongside unreviewed and ReqChanged
candidates so a human can interactively confirm or update the intent.

Changes:
- Add `stale-reviewed-demands-human-judgment` design constraint
- Extend candidate types from two to three (Unreviewed, ReqChanged,
  StaleReviewed)
- Add "Planned: stale-reviewed approval" section with Problem, Design
  (candidate collection, TUI presentation, decisions, application
  logic), implementation notes, and edge cases
- Update CLI surface with `--stale-only` filter flag
- Update ordering to place StaleReviewed between Unreviewed and
  ReqChanged
- Update status line and motivation to reflect the expanded scope

Original prompt:

> For `liyi check` results, I want to review and approve previously-reviewed
> stale items with `liyi approve` too (the "not auto-rehashed — reviewed"
> kind). Is this covered already in `approve-impl.md`?
>
> Yes of course, please do and commit.

AI-assisted-by: Claude Opus 4.6 (GitHub Copilot)
Signed-off-by: WANG Xuerui <git@xen0n.name>
Introduce the structural foundation for approving stale-reviewed and
requirement-changed items alongside unreviewed items.

Structural changes:
- Add CandidateKind enum (Unreviewed, StaleReviewed, ReqChanged) and
  ApproveFilter enum (All, UnreviewedOnly, StaleOnly, ReqOnly)
- Extend ApprovalCandidate with kind, prev_source, current_source,
  changed_requirement, old/new_requirement_text fields
- Make apply_approval_decisions kind-aware: Yes on ReqChanged refreshes
  only the related edge hash (not reviewed/source_hash); No on
  StaleReviewed/ReqChanged leaves item unchanged as a todo marker
- Add lookup_prev_source() for recovering old source text from git
  history by matching stored source_hash
- Extend collect_approval_candidates() to collect all three candidate
  kinds in priority order (unreviewed -> stale -> req-changed), filtered
  by ApproveFilter
- Add --unreviewed-only, --stale-only, --req-only CLI flags
- TUI: kind-aware header (color-coded), draw_intent_stale_reviewed
  (source diff + intent), draw_intent_req_changed (requirement diff +
  intent)

No behavioral changes for existing unreviewed-only workflows -- the new
code paths are additive.

Original prompt:

> okay, implement the requirement-change and stale-reviewed review flows.
> do your refactor first, then implement the feature one by one, commit
> frequently.

AI-assisted-by: Claude Opus 4.6 (GitHub Copilot)
Signed-off-by: WANG Xuerui <git@xen0n.name>
Two-pass lookup_prev_source to handle span-shifted items:
- Fast path: try source history with current span (works pre-check-fix).
- Slow path: walk sidecar history to recover pre-shift span, then fetch
  source at that revision. Handles the case where `check --fix` shifted
  the span after the function moved but left the hash unchanged.

edit_intent_in_editor now includes contextual diffs:
- StaleReviewed: unified source diff (old → new) in the comment block.
- ReqChanged: unified requirement diff in the comment block.

Both use the `similar` TextDiff already available in the crate.

Original prompt:

> okay, implement the requirement-change and stale-reviewed review flows.
> do your refactor first, then implement the feature one by one, commit
> frequently.

AI-assisted-by: Claude Opus 4.6 (GitHub Copilot)
Signed-off-by: WANG Xuerui <git@xen0n.name>
Replace the per-sidecar requirement hash lookup with a global requirement
registry that scans all sidecar files in the repo. This correctly detects
req-changed items whose related requirements live in different sidecars
(e.g., items in approve.rs referencing requirements in approve-impl.md).

Key changes:
- build_requirement_registry: discovers all requirement specs across the
  repo, computes fresh hashes from marker-delimited spans, and stores
  current text for diff display.
- lookup_prev_requirement_text: two-pass git-history lookup (fast path
  with current span, slow path via sidecar history) to recover old
  requirement text for diff display.
- Candidates now carry populated old_requirement_text / new_requirement_text
  so the TUI can show unified diffs of what changed in the requirement.

Original prompt:

> okay, implement the requirement-change and stale-reviewed review flows.
> do your refactor first, then implement the feature one by one, commit
> frequently.

AI-assisted-by: Claude Opus 4.6 (GitHub Copilot)
Signed-off-by: WANG Xuerui <git@xen0n.name>
… flows

Update intent specs for all items modified across the refactor, stale-reviewed,
and req-changed commits. Add specs for new items: CandidateKind, ApproveFilter,
lookup_prev_source, RequirementInfo, build_requirement_registry,
lookup_prev_requirement_text, draw_header, draw_intent_unreviewed,
draw_intent_stale_reviewed, draw_intent_req_changed.

Set reviewed=false and remove stale source_hash on all re-inferred specs
per AGENTS.md rule 1 (agent-written intent must not carry reviewed=true).

Original prompt:

> okay, implement the requirement-change and stale-reviewed review flows.
> do your refactor first, then implement the feature one by one, commit
> frequently.

AI-assisted-by: Claude Opus 4.6 (GitHub Copilot)
Signed-off-by: WANG Xuerui <git@xen0n.name>
Signed-off-by: WANG Xuerui <git@xen0n.name>
Signed-off-by: WANG Xuerui <git@xen0n.name>
The comment on line 722 of check.rs mentioned `@liyi:intent` without
quine suppression, so the marker scanner treated it as a real directive.
Since that line falls within check_sidecar's source span [356, 1519],
the review-status check saw has_intent_marker == true and suppressed
the Unreviewed diagnostic for the check_sidecar spec.

Wrapping the mention in backticks triggers the existing inline-code
quine suppression in scan_markers, fixing the false positive.

Original prompt:

> why is `check_sidecar` `reviewed: false` but `liyi check` doesn't report it as unreviewed?
>
> Fix the comment would be enough.
>
> Please commit.

AI-assisted-by: Claude Opus 4.6 (GitHub Copilot)
Signed-off-by: WANG Xuerui <git@xen0n.name>
Signed-off-by: WANG Xuerui <git@xen0n.name>
Break the monolithic run_check (~310 lines) into a slim orchestrator
calling purpose-named helpers:

- emit_discovery_warnings()
- discover_requirements()       — pass 1a
- compute_requirement_hashes()  — pass 1b
- collect_source_related_refs() — pass 1c
- enrich_requirements_from_sidecars() — pass 1d
- emit_untracked_requirements()   — post-pass
- emit_unreferenced_requirements() — post-pass
- emit_cycle_diagnostics()        — post-pass

run_check is now ~60 lines of orchestration. No behavioral changes.

Original prompt:

> can the `run_check` and `check_sidecar` huge monoliths be broken
> to several manageable pieces?

AI-assisted-by: Claude Opus 4.6 (GitHub Copilot)
Signed-off-by: WANG Xuerui <git@xen0n.name>
Extract three helpers from the per-item checking loop in check_sidecar:

- check_review_status() — review flag + @liyi:intent marker check
- check_trivial_ignore() — source trivial/ignore markers + =trivial sentinel
- check_related_edges() — related edge validation, source marker sync, null hash fill

No behavioral changes.

Original prompt:

> can the `run_check` and `check_sidecar` huge monoliths be broken
> to several manageable pieces?

AI-assisted-by: Claude Opus 4.6 (GitHub Copilot)
Signed-off-by: WANG Xuerui <git@xen0n.name>
Move the Spec::Requirement hash-checking logic (~160 lines) into
check_requirement_hash(), covering hash match, PastEof with tree_path
recovery, and invalid span handling.

No behavioral changes.

Original prompt:

> can the `run_check` and `check_sidecar` huge monoliths be broken
> to several manageable pieces?

AI-assisted-by: Claude Opus 4.6 (GitHub Copilot)
Signed-off-by: WANG Xuerui <git@xen0n.name>
The ~550-line inline `Spec::Item` hash-checking block — the largest and
most complex piece of check_sidecar — is now decomposed into four
focused functions:

- `check_item_hash`: top-level dispatcher (Ok / PastEof / Invalid)
- `handle_missing_hash`: no source_hash yet → tree_path recovery + fix
- `handle_hash_mismatch`: tree_path → sibling scan → shift heuristic
- `handle_tree_path_resolved`: pure-shift vs content-drift at the
  resolved span, with reviewed/unreviewed rehash policy
- `handle_past_eof`: PastEof recovery with same shift/drift logic

check_sidecar is now a ~260-line orchestrator (mostly parse/validate
boilerplate) that delegates all per-spec logic to helpers.

Original prompt:

> can the `run_check` and `check_sidecar` huge monoliths be broken to several manageable pieces?

AI-assisted-by: Claude Opus 4.6 (GitHub Copilot)
Signed-off-by: WANG Xuerui <git@xen0n.name>
Add intent specs for the 16 newly extracted helper functions and
update run_check/check_sidecar intents to reflect delegation pattern.
Escape a documentary `@liyi:related` mention in a comment that caused
a false-positive "markers" requirement edge (same quine class as the
earlier `@liyi:intent` fix).

Original prompt:

> now manage sidecars

> please commit

AI-assisted-by: Claude Opus 4.6 (GitHub Copilot)
Signed-off-by: WANG Xuerui <git@xen0n.name>
Signed-off-by: WANG Xuerui <git@xen0n.name>
…decar_warnings

The old name was too generic — the function exclusively converts
discovery warning strings into AmbiguousSidecar diagnostics.  The new
name makes the purpose self-evident.

Original prompt:

> please rename `emit_discovery_warnings` according to its intent. I think the name is too generic for its purpose -- WDYT?

AI-assisted-by: Claude Opus 4.6 (GitHub Copilot)
Signed-off-by: WANG Xuerui <git@xen0n.name>
Signed-off-by: WANG Xuerui <git@xen0n.name>
Signed-off-by: WANG Xuerui <git@xen0n.name>
Human note: workflow fix by me, AI helped re-infer intents.

AI-assisted-by: Claude Opus 4.6 (GitHub Copilot)
Signed-off-by: WANG Xuerui <git@xen0n.name>
Signed-off-by: WANG Xuerui <git@xen0n.name>
@xen0n xen0n force-pushed the gha-annotations branch from 166a396 to 765a4b3 Compare March 14, 2026 12:09
@xen0n xen0n merged commit 765a4b3 into main Mar 14, 2026
5 checks passed
@xen0n xen0n deleted the gha-annotations branch March 14, 2026 12:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant