Skip to content

Add generated surface sync state contract#258

Merged
paudley merged 7 commits into
mainfrom
paudley/180-install-sync-state-contract
Jun 23, 2026
Merged

Add generated surface sync state contract#258
paudley merged 7 commits into
mainfrom
paudley/180-install-sync-state-contract

Conversation

@paudley

@paudley paudley commented Jun 23, 2026

Copy link
Copy Markdown
Owner

Summary

  • Add a repo-local install/sync state contract for generated coding-ethos surfaces.
  • Wire dry-run, doctor, and repair-plan reporting through centralized JSON/TOON output.
  • Record tool config, Gemini prompt, agent skill, and agent hook artifacts with focused tests and README docs.

Closes #180

Validation

  • go test ./internal/syncstate ./internal/toolconfigs ./internal/agentskills ./internal/geminiprompts ./internal/agenthooks ./internal/agenthookscli ./internal/policycli
  • make build
  • make check (passes; reports the existing non-blocking Go coverage-goal warning)
  • bin/coding-ethos-run policy sync-tool-configs --repo . --ethos-root . --dry-run --format json
  • bin/coding-ethos-run policy install-state-doctor --repo . --format toon
  • bin/coding-ethos-run policy install-state-repair-plan --repo . --format json

@paudley paudley requested a review from ErinAudley as a code owner June 23, 2026 11:58
Copilot AI review requested due to automatic review settings June 23, 2026 11:58
@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@paudley, we couldn't start this review because you've reached your PR review rate limit.

More reviews will be available in 7 minutes and 51 seconds. Learn how PR review limits work.

Your organization has used up its prepaid credits, and credit purchases are no longer available. Enable the review add-on in the billing tab to keep reviews running — you're only billed for reviews past your plan's rate limits ($0.25/file).

⌛ How to resolve this issue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based credits.

🚦 How do rate limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan refill rate.

For paid Pro and Pro+ PR reviews, CodeRabbit uses rolling per-developer review limits. Reviews become available again as older review attempts age out of the rolling limit window.

Please see our Fair Usage Limits Policy for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: e50f1dba-62fb-4829-9fd7-221601e58f03

📥 Commits

Reviewing files that changed from the base of the PR and between 825c39e and f3ba945.

📒 Files selected for processing (9)
  • README.md
  • go/internal/agenthooks/settings_test.go
  • go/internal/agenthookscli/main.go
  • go/internal/agenthookscli/main_internal_test.go
  • go/internal/codeintelcli/main_internal_test.go
  • go/internal/policycli/main.go
  • go/internal/policycli/main_internal_test.go
  • go/internal/syncstate/state.go
  • go/internal/syncstate/state_test.go
📝 Walkthrough

Walkthrough

Introduces a new go/internal/syncstate package implementing persistent install/sync state (JSON) for generated repository artifacts, including Artifacts, Upsert, Plan, Doctor, and RepairPlan functions. Integrates StateArtifacts planners into agenthooks, agentskills, geminiprompts, and toolconfigs. Wires dry-run/upsert/install-state subcommands into policycli and agenthookscli. Refactors agenthooks settings to render in-memory before writing. Updates README with new commands and examples.

Changes

Install/sync state contract for generated agent surfaces

Layer / File(s) Summary
syncstate domain model, Artifacts, Upsert, and file I/O
go/internal/syncstate/state.go
Defines exported structs (State, Artifact, ArtifactInput, ProviderTarget, SourceHash, Report, sub-types), schema/path constants; implements Artifacts (SHA-256, path validation, stable sort), Upsert (merge-then-write with runtime version/commit from pyproject.toml and git rev-parse HEAD), and Read/ReadFile/WriteFile/FilePath helpers.
syncstate reporting: Plan, Doctor, RepairPlan, marshalers, and utilities
go/internal/syncstate/state.go
Adds Plan (dry-run artifact status and planned write count), Doctor (filesystem SHA comparison for artifacts and sources), and RepairPlan (coding-ethos-owned artifacts only); Report marshalers (TOON, SARIF, JSON, log fields); internal report composition helpers; merge/normalization utilities; and hash/sort/metadata helpers including fileSHA256, repoRelativePath, runtimeVersion, and runtimeCommit.
syncstate integration tests
go/internal/syncstate/state_test.go
Adds TestInstallSyncStatePlansRecordsAndDoctorsArtifacts (Plan → Upsert → Doctor pass/fail/drift → RepairPlan → source staleness) and TestRepairPlanOnlyIncludesCodingEthosOwnedArtifacts, with a writeTestFile helper.
agenthooks settings render refactor and StateArtifacts
go/internal/agenthooks/settings.go, go/internal/agenthooks/state_artifacts.go
Refactors syncSettingsFile/syncTextSettingsFile to render full content in-memory first via new renderSettingsFileContent/renderTextSettingsFileContent helpers, then write with os.WriteFile; adds StateArtifacts (renders all four hook settings, calls syncstate.Artifacts) and agentHookStateArtifactInputs (four ArtifactInput entries).
StateArtifacts for agentskills, geminiprompts, and toolconfigs
go/internal/agentskills/render.go, go/internal/geminiprompts/render.go, go/internal/toolconfigs/sync.go
Adds StateArtifacts to each surface package: agentskills with agentSkillSurface routing, geminiprompts with a single ArtifactInput for PromptPackPath, toolconfigs with entries for each rendered file plus hash manifest.
policycli dry-run, upsert, install-state subcommands, and tests
go/internal/policycli/main.go, go/internal/policycli/main_internal_test.go
Extends policycli with install-state-doctor and install-state-repair-plan subcommands; adds --dry-run/--format flags and plan→dry-run→upsert branching to syncToolConfigs, syncGeminiPrompts, and syncAgentSkills; adds syncStateOptions, upsertSyncState, writeSyncStateReport, and source-path helpers; updates usage text; adds dry-run and normal-mode tests with syncstate.Read assertion.
agenthookscli dry-run/upsert, codeintelcli stdout fix, and README
go/internal/agenthookscli/main.go, go/internal/agenthookscli/main_internal_test.go, go/internal/codeintelcli/main_internal_test.go, README.md
Extends agenthookscli syncSettings with --dry-run/--format, StateArtifacts plan step, dry-run/upsert branches, and writeSyncStateReport; adds TestSyncSettingsDryRunDoesNotWriteSettingsOrState and captureStdout helper; wraps codeintelcli test loop in captureStdout; documents new commands and install-sync.json state in README.

Sequence Diagram(s)

sequenceDiagram
  rect rgba(70, 130, 180, 0.5)
    Note over policycli,Disk: Dry-run path
  end
  participant policycli
  participant StateArtifacts as StateArtifacts(surface)
  participant syncstate
  participant Disk as Disk (install-sync.json)
  participant pyproject as pyproject.toml
  participant git as git rev-parse HEAD

  policycli->>StateArtifacts: StateArtifacts(ethosRoot, repoRoot, ...)
  StateArtifacts-->>policycli: []Artifact

  alt --dry-run
    policycli->>syncstate: Plan(repoRoot, requestedAction, artifacts)
    syncstate-->>policycli: Report
    policycli->>policycli: writeSyncStateReport → feedback output
  else normal sync
    policycli->>syncstate: Upsert(UpsertOptions)
    syncstate->>Disk: ReadFile (load or init State)
    syncstate->>pyproject: runtimeVersion()
    syncstate->>git: runtimeCommit()
    syncstate->>syncstate: merge sources/targets/artifacts, timestampArtifacts
    syncstate->>Disk: WriteFile (indented JSON)
    syncstate-->>policycli: State
    policycli->>policycli: emit per-path output + sync-state file path
  end

  policycli->>syncstate: Doctor(repoRoot) or RepairPlan(repoRoot)
  syncstate->>Disk: ReadFile
  syncstate->>syncstate: artifactReports (SHA compare) + sourceReports
  syncstate-->>policycli: Report (pass/fail/drifted/would-update)
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Suggested reviewers

  • ErinAudley

Poem

🐇 Hop hop, the state is saved!
A JSON file, no longer depraved.
SHA-256 guards every file,
Doctor checks with careful style.
Repair plans only what we own —
No drifted artifact left alone!
🌿 The ethos root has found its home.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: adding a generated surface sync state contract infrastructure.
Description check ✅ Passed The description covers the main change (sync state contract), provides validation steps, and includes the relevant issue closure (Closes #180).
Linked Issues check ✅ Passed The PR implementation comprehensively addresses all acceptance criteria from issue #180: state file writing, dry-run reporting, doctor detection, repair planning, and JSON/TOON output.
Out of Scope Changes check ✅ Passed All changes directly support the generated surface sync state contract objective; no unrelated or out-of-scope modifications were introduced.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch paudley/180-install-sync-state-contract

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces a sync state tracking mechanism to record, plan, and validate the installation state of generated surfaces (such as tool configs, Gemini prompts, and agent skills) under '.coding-ethos/state/install-sync.json'. It adds dry-run capabilities, a 'doctor' command to check for drift, and a 'repair-plan' command to plan updates for managed artifacts. The feedback suggests addressing a path traversal vulnerability in relative path resolution, gracefully falling back to a fresh state if the sync state file is corrupted, and avoiding hardcoded root paths in the agent hooks CLI to properly support submodule installations.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment thread go/internal/syncstate/state.go
Comment thread go/internal/syncstate/state.go Outdated
Comment thread go/internal/agenthookscli/main.go

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@go/internal/codeintelcli/main_internal_test.go`:
- Around line 949-956: The captureStdout call currently wraps the entire for
loop iterating over commands, causing all stdout from all command invocations to
accumulate before draining, which can block the pipe and hang the test with
large combined output. Move the captureStdout call inside the for loop so that
stdout is captured and drained for each individual command invocation
separately, preventing pipe buffer overflow.

In `@go/internal/geminiprompts/render.go`:
- Around line 125-132: The `PromptPackPath` used in the `syncstate.Artifacts`
call is currently resolving to a location outside of
`go/internal/geminiprompts/`, which violates the prompt-pack location contract.
Update the `PromptPackPath` constant or variable to point to the correct
location within the `go/internal/geminiprompts/` directory so that the artifact
is persisted to the proper location as required by the coding guidelines.

In `@go/internal/policycli/main.go`:
- Around line 497-499: The parseInstallStateFlags function is reusing
errToolConfigRepoRequired when --repo is missing, but this error message
references "tool config command" which is confusing in the install-state
context. Either create a new dedicated error constant for install-state commands
(e.g., errInstallStateRepoRequired) with an appropriate message, or modify the
error message to be generic enough to work for both tool-config and
install-state contexts. Update the error return statement in
parseInstallStateFlags to use the new error or modify the existing error message
to remove tool-config-specific terminology.
- Around line 578-598: The function compactExistingOrCandidatePaths has a
misleading name that implies it checks for file existence, but it only trims
whitespace, cleans paths using filepath.Clean, and removes duplicates. Rename
the function from compactExistingOrCandidatePaths to compactCandidatePaths to
accurately reflect its actual behavior of compacting and deduplicating candidate
paths without performing any existence validation. Remember to update all call
sites where compactExistingOrCandidatePaths is invoked.

In `@go/internal/syncstate/state.go`:
- Around line 128-157: The `LastWrittenUTC` field assignment in the `Artifacts`
function is dead code because this value is always overwritten by
`timestampArtifacts` in the `Upsert` function before the artifacts are
persisted. Remove the `LastWrittenUTC: now,` line from the Artifact struct
initialization within the loop to eliminate the misleading assignment that
suggests these timestamps are authoritative, since they are never actually used
and will be replaced anyway.
- Around line 717-733: The runtimeVersion function currently searches for the
first line matching "version = " across all sections of the pyproject.toml file,
which can incorrectly match version entries from other sections like
[tool.poetry]. Modify the function to scope the version search to only the
[project] section by tracking when the parser enters the [project] table (detect
lines starting with "[project]") and only return the version when found within
that specific section context, ensuring you don't pick up version entries from
unrelated configuration tables.
- Around line 694-715: The repoRelativePath function has a security flaw where
relative input paths like ../../foo bypass validation and return early at line
697 without checking if they escape the repo root. Additionally, the escape
guard using strings.HasPrefix(relative, "..") on line 705 is not separator-aware
and false-positives on directory names starting with literal `..foo`. Fix this
by normalizing both the relative and absolute path branches against repoRoot
using filepath.Rel and filepath.Join, then validate the result against the repo
root boundary using a separator-aware comparison instead of strings.HasPrefix to
ensure paths cannot escape the repo root whether provided as relative or
absolute.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: bc0db41f-68c6-497c-b00f-dcc32b8eea9b

📥 Commits

Reviewing files that changed from the base of the PR and between 12debae and 825c39e.

📒 Files selected for processing (13)
  • README.md
  • go/internal/agenthooks/settings.go
  • go/internal/agenthooks/state_artifacts.go
  • go/internal/agenthookscli/main.go
  • go/internal/agenthookscli/main_internal_test.go
  • go/internal/agentskills/render.go
  • go/internal/codeintelcli/main_internal_test.go
  • go/internal/geminiprompts/render.go
  • go/internal/policycli/main.go
  • go/internal/policycli/main_internal_test.go
  • go/internal/syncstate/state.go
  • go/internal/syncstate/state_test.go
  • go/internal/toolconfigs/sync.go
📜 Review details
⏰ Context from checks skipped due to timeout. (8)
  • GitHub Check: Unified lint
  • GitHub Check: Test (Python 3.11)
  • GitHub Check: Coding Ethos SARIF Gate / Coding Ethos SARIF Gate
  • GitHub Check: Test (Python 3.13)
  • GitHub Check: Validate GitHub workflows
  • GitHub Check: Go coverage
  • GitHub Check: CodeQL (go)
  • GitHub Check: Go fuzz smoke
🧰 Additional context used
📓 Path-based instructions (1)
go/internal/geminiprompts/**

📄 CodeRabbit inference engine (AGENTS.md)

Gemini prompt pack should be generated in go/internal/geminiprompts/

Files:

  • go/internal/geminiprompts/render.go
🔇 Additional comments (14)
go/internal/syncstate/state.go (2)

159-198: LGTM!


375-476: LGTM!

go/internal/syncstate/state_test.go (1)

13-158: LGTM!

go/internal/agenthooks/settings.go (1)

317-396: LGTM!

go/internal/agenthooks/state_artifacts.go (1)

12-111: LGTM!

go/internal/agentskills/render.go (1)

13-14: LGTM!

Also applies to: 77-109

go/internal/toolconfigs/sync.go (1)

15-15: LGTM!

Also applies to: 77-118

go/internal/policycli/main.go (3)

98-112: LGTM!


122-166: LGTM!

Also applies to: 239-275, 306-341


504-539: LGTM!

Also applies to: 552-576

go/internal/policycli/main_internal_test.go (1)

228-294: LGTM!

Also applies to: 816-828

go/internal/agenthookscli/main.go (1)

17-17: LGTM!

Also applies to: 95-152, 274-280

go/internal/agenthookscli/main_internal_test.go (1)

8-15: LGTM!

Also applies to: 103-134, 160-200

README.md (1)

793-794: LGTM!

Also applies to: 867-871, 884-909, 1484-1484

Comment thread go/internal/codeintelcli/main_internal_test.go Outdated
Comment thread go/internal/geminiprompts/render.go
Comment thread go/internal/policycli/main.go
Comment thread go/internal/policycli/main.go Outdated
Comment thread go/internal/syncstate/state.go
Comment thread go/internal/syncstate/state.go
Comment thread go/internal/syncstate/state.go
Copilot AI review requested due to automatic review settings June 23, 2026 12:24

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

coderabbitai[bot]
coderabbitai Bot previously approved these changes Jun 23, 2026
@paudley paudley enabled auto-merge (squash) June 23, 2026 12:27

paudley commented Jun 23, 2026

Copy link
Copy Markdown
Owner Author

Stage 2 pass completed.

Addressed review feedback in follow-up commits:

  • e3028c0: path traversal validation, corrupted runtime state recovery, and agent-hooks sync --ethos-root metadata.
  • ae0d9ee: per-command stdout capture, install-state error wording, candidate-path helper naming, removed dead timestamp assignment, and [project]-scoped runtime version parsing.
  • 6c5365a: lint spacing cleanup.

Validation rerun:

  • go test ./internal/syncstate ./internal/policycli ./internal/agenthookscli ./internal/codeintelcli ./internal/geminiprompts
  • make build
  • make check (passes with the existing non-blocking Go coverage-goal warning)

Auto-merge is enabled; GitHub currently reports REVIEW_REQUIRED.

Copilot AI review requested due to automatic review settings June 23, 2026 17:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@paudley paudley merged commit aba01ff into main Jun 23, 2026
22 of 23 checks passed
@paudley paudley deleted the paudley/180-install-sync-state-contract branch June 23, 2026 17:58
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.

[feature] Add install/sync state contract for generated agent surfaces

2 participants