Skip to content

feat(cli): emit rules in Kilo flat builds - #1069

Open
BlandineRdl wants to merge 4 commits into
ai-driven-dev:nextfrom
BlandineRdl:feat/868-kilo-flat-rules
Open

BlandineRdl wants to merge 4 commits into
ai-driven-dev:nextfrom
BlandineRdl:feat/868-kilo-flat-rules

Conversation

@BlandineRdl

@BlandineRdl BlandineRdl commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

🎯 What & why

A Kilo flat build (aidd translate <src> --to kilo --as flat) dropped canonical plugin Rules, although Kilo loads Markdown Rules listed in kilo.json[c].instructions. All-files Rules now ship with the archive and are wired into the resolved Kilo config.

🛠️ How it works

  • buildKiloFlatContract() declares rules as supported: each Rule is written to .kilo/rules/<plugin>-<group>-<name>.md, with relative links relocated.
  • A Rule with paths frontmatter (a scope Kilo cannot express) fails aidd translate --to kilo --as flat with PathScopedRuleUnsupportedError; nothing is emitted or referenced.
  • plugin install / setup / remove / update for Kilo skip such a Rule with a warning naming the plugin, the Rule and its paths, so one scoped Rule never blocks an install (InexpressibleRulePolicy: fail by default, skip in the install cache build).
  • emitConfigArtifact appends the sorted Rule paths to instructions after the user's entries (buildOpencodeFlatConfig mergedKeys), so a rebuild changes nothing.
  • Generic writeRules on BuildOutputStrategy (flat writes, marketplace no-op): the seam feat(cli): emit rules and commands in OpenCode flat builds #789 (OpenCode) can reuse. The skipped rules/ warning fires only when nothing was emitted.
  • Two Rules flattening to the same name fail with FlatRuleNameClashError naming both.
  • Instruction paths are project-root relative: Kilo resolves them with globUp from the session directory to the worktree (packages/opencode/src/session/instruction.ts), not from the config file's folder.

Refactor: one named options object for the flat build

Supporting the install/translate split needed a new caller-chosen setting on FlatBuildStrategy, which would have been its 9th constructor parameter and forced raising the dependency ratchet in orchestrator-deps.arch.test.ts. Instead, refactor(cli): pass flat build options as one named object applies Introduce Parameter Object:

  • force (a positional boolean, unreadable at call sites: …, true, absOut, …) and the new inexpressibleRules policy move into FlatBuildOptions { force, inexpressibleRules? }.
  • Both are build-behaviour choices made by the caller, not collaborators like fs or logger: they form one concept.
  • The constructor stays at 8 collaborators and the ratchet stays at 8, so it keeps doing its job: flagging a class that accumulates responsibilities.
  • No behaviour change: only the 13 construction sites changed shape; no assertion was touched.

The commit sits at the end of the branch rather than before the feature commits, to avoid rewriting pushed history; a squash merge flattens it anyway.

🧪 How to verify

  • cd cli && pnpm test (6889 passed, 1 skipped: the gated Kilo runtime e2e)
  • pnpm lint, pnpm typecheck, pnpm knip, pnpm build all pass
  • CI job cli / Kilo runtime smoke runs tests/e2e/kilo-runtime.e2e.test.ts, which now asserts the Rule is listed in instructions and its marker reaches the model

⚠️ Heads-up

  • Runtime acceptance is proven by the cli / Kilo runtime smoke CI job (no kilo binary locally).
  • Measured on Kilo 7.7.5 (real runtime, fake local model capturing the prompt):
    • top-level .kilo/rules/*.md load even without an instructions entry; files in a subdirectory load only when listed;
    • paths frontmatter is ignored: a scoped Rule reaches every session. This is why a path-scoped Rule is refused (translate) or skipped (install) rather than emitted;
    • a Rule both listed and auto-discovered appears once in the prompt, so the generated instructions entries are explicit, not duplicated.
  • Out of scope, pre-existing: aidd plugin install <local path> --tool kilo copies Rules verbatim into .kilo/rules/<plugin>/, so Kilo loads none of them. Tracked in fix(cli): kilo local-path install leaves plugin rules inert #1081.
  • Bundle budget raised 709 → 725 KB (bundle 710.8 KB), per the budget registry convention. Reviewer decision.
  • The aidd-test fixture Rule is path-scoped, so the Kilo e2e uses an all-files copy; a dedicated e2e asserts the unmodified fixture is refused.
  • Known limits: links to files the build does not emit stay unchanged (documented in cli/README.md); Rules removed upstream are not pruned on rebuild.
  • No shipped plugin has rules/ today, so the real framework's Kilo build output is unchanged.

🔗 Linked issue

Closes #868

✅ I certify

  • I DO CERTIFY I READ EACH LINE OF THE PULL REQUEST BECAUSE I AM A SOFTWARE ENGINEER, NOT A AI PUPPY.

🤖 Generated with Claude Code

BlandineRdl and others added 4 commits October 20, 2026 15:44
A Kilo flat archive dropped canonical plugin Rules although Kilo loads
Markdown Rules listed in kilo.json[c] instructions. All-files Rules are
now materialized under .kilo/rules/ and appended to instructions after
user entries, idempotently. A path-scoped Rule fails the build instead
of being silently broadened. The bundle budget rises to 725 KB.

Closes ai-driven-dev#868

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Two Rules flattening to one Kilo filename raised FlatTargetExistsError,
whose --force hint cannot resolve the clash. A dedicated error now names
both source Rules and the shared destination. The README states that a
Rule's links to files the build does not emit stay unchanged.

Refs ai-driven-dev#868

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Plugin install, setup and remove for Kilo build their cache tree through
the flat strategy, so a Rule scoped by `paths` started failing them where
`rules/` used to be skipped. The flat strategy now takes the caller's
policy for a Rule the target cannot scope: `aidd translate` still fails
the build, while the install cache build skips the Rule, names it with
its paths in a warning on the real logger, and installs the rest. The
Kilo contract declares the scope check instead of throwing from its
transform, and `instructions` never lists a skipped Rule.

The runtime e2e marker loses its tracker id, and the bundle budget entry
drops the issue number.

Refs ai-driven-dev#868

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A positional `force` boolean reads as a bare true or false at every call
site, and overwrite and the policy for an unscopable Rule are both the
caller's choices for one build. One named options object carries them,
so the strategy's dependency count stays at what the ratchet admits and
the ratchet keeps meaning a new collaborator.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@blafourcade blafourcade added this to the This week milestone Oct 22, 2026 — with Claude
@BlandineRdl

Copy link
Copy Markdown
Contributor Author

@waewoo hello ! J'ai pris une issue sur un sujet sur lequel tu bosses habituellement ( désolé j'avais pas fait attention ) si jamais tu veux en discuter c'est avec plaisir ! :)

@BlandineRdl
BlandineRdl marked this pull request as ready for review October 23, 2026 08:04
@BlandineRdl
BlandineRdl requested a review from a team as a code owner October 23, 2026 08:04
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