Skip to content

refactor(generate): enforce shared-file write order via a declarative step graph - #2081

Merged
dyoshikawa merged 7 commits into
dyoshikawa:mainfrom
saitota:m3i1
Jun 30, 2026
Merged

refactor(generate): enforce shared-file write order via a declarative step graph#2081
dyoshikawa merged 7 commits into
dyoshikawa:mainfrom
saitota:m3i1

Conversation

@saitota

@saitota saitota commented Jun 29, 2026

Copy link
Copy Markdown
Contributor

Description

The write order of features sharing one config file lived only in two prose comments in generate(). Reordering the await generate*Core(...) lines, or switching to Promise.all, would silently drop another feature's keys. PR #1858 pinned the two known pairs with tests; this makes the constraint explicit in data.

Changes

  • Each step declares writesSharedFile / dependsOn; resolveExecutionOrder topologically sorts them and throws on an unordered shared-file writer pair, an unknown dependency, or a cycle — so the array can be reordered freely and a silent data-loss trap becomes a startup error.
  • Declares every multi-writer tool, not just the two example pairs: claudecode, kilo, opencode, zed, qwencode, augmentcode, hermesagent, kiro, amp, codexcli, grokcli, vibe.

Test Plan

Refs #1858

saitota added 7 commits June 30, 2026 00:14
…teps and topological sort

Replace the implicit ordering convention (prose comments stating MCP must run before Rules, and Permissions after Ignore) with declarative GenerationStep entries carrying writesSharedFile/dependsOn, then derive the run order with resolveExecutionOrder. A shared file written by two steps with no declared order, an unknown dependency, or a cycle now throws at runtime instead of silently corrupting kilo.jsonc / opencode.json / .claude/settings.json.
Address review: the skills step previously ran outside resolveExecutionOrder, so a future shared-file write by skills would bypass the data-loss assertion. Run generateSkillsCore inside the step's run() and have rules dependsOn skills, so skills now obeys the same ordering invariant as every other step.
The prior change only covered the two example pairs from the issue (mcp/rules, ignore/permissions). In reality hooks also writes .claude/settings.json, and permissions also writes kilo.jsonc/opencode.json, so the assertSharedFilesOrdered guard had gaps: reordering permissions or hooks would not be caught. Declare every writer of each shared file (claude-settings, kilo-opencode-config) with writesSharedFile/dependsOn so the guard covers all three writers per file. Rename the mcp-instructions-config token to kilo-opencode-config since permissions writes there too.
Zed's .zed/settings.json (and global ~/.config/zed/settings.json) is read-modify-written by three features: ignore (private_files), mcp (context_servers), and permissions. Declare the shared zed-settings token on all three so assertSharedFilesOrdered enforces their order too, closing the same reorder-safety gap that was just fixed for claude-settings and kilo-opencode-config.
…er tools

Eight more tools read-modify-write a single config file from multiple features: qwencode/augmentcode/hermesagent (mcp+hooks+permissions), kiro (hooks+permissions), and amp/codexcli/grokcli/vibe (mcp+permissions). They were safe only by the incidental mcp->hooks->permissions run order, outside the assertSharedFilesOrdered guard. Declare a per-tool shared-file token on each writer and add hooks dependsOn mcp so every writer pair of every shared file is now totally ordered and machine-checked.
codexcli hooks writes [features] hooks=true and vibe hooks writes enable_experimental_hooks=true into the same config.toml that mcp and permissions already share, making them three-writer files. The hooks step was missing codexcli-config and vibe-config, so the hooks<->mcp/permissions order on those files was not machine-checked. Add both tokens to the hooks step.
@dyoshikawa
dyoshikawa merged commit 5d183c4 into dyoshikawa:main Jun 30, 2026
6 checks passed
@dyoshikawa

Copy link
Copy Markdown
Owner

@saitota Thank you!

saitota added a commit to saitota/rulesync that referenced this pull request Jul 2, 2026
…rce declarations

PR dyoshikawa#2081 declared each step's shared-file writers by hand as opaque tokens, checked only for internal graph consistency; a missing writer passed CI silently and was caught only in review (e.g. codexcli/vibe config.toml in ab61e38).

Derive the multi-writer set from PROCESSOR_REGISTRY x getSettablePaths (plus a getExtraSharedWritePaths contract for writes not visible as settable paths), and assert it matches the step graph, so a missing declaration fails CI instead of review.

Refs dyoshikawa#2081
saitota added a commit to saitota/rulesync that referenced this pull request Jul 2, 2026
…rce declarations

PR dyoshikawa#2081 declared each step's shared-file writers by hand as opaque tokens, checked only for internal graph consistency; a missing writer passed CI silently and was caught only in review (e.g. codexcli/vibe config.toml in ab61e38).

Derive the multi-writer set from PROCESSOR_REGISTRY x getSettablePaths (plus a getExtraSharedWritePaths contract for writes not visible as settable paths), and assert it matches the step graph, so a missing declaration fails CI instead of review.

Refs dyoshikawa#2081
saitota added a commit to saitota/rulesync that referenced this pull request Jul 2, 2026
…rce declarations

GENERATION_STEP_GRAPH declared each step's shared-file writers by hand; a missing writer passed CI silently and was caught only in review (e.g. codexcli/vibe config.toml in ab61e38).

Derive the multi-writer set from PROCESSOR_REGISTRY x getSettablePaths across both project and global scope, plus a getExtraSharedWritePaths contract for writes not visible as settable paths, and assert it matches the step graph so a missing declaration fails CI instead of review.

Deriving surfaced three drifted declarations, now fixed: .takt/config.yaml was unregistered; permissions was wrongly listed as a kilo.json writer (it writes kilo.jsonc); and the global-only .config/devin/config.json shared write was covered only by chance.

Refs dyoshikawa#2081
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.

2 participants