micro / micro/mu

Seven services call a model directly, and the rule against it is enforced one layer too high

Open
#1,469 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Go
Stars
434
Forks
21
Avg merge
18m
Merged PRs (30d)
326

Description

The rule and the hole

CLAUDE.md:

An agent may import a service; a service may never import an agent. … a service answers a question about state, an agent decides which question to ask, and a service calling an agent is asking the model what its own answer should be. Enforced by TestNoNewServiceCallsAnAgent, which asserts zero.

The test asserts zero and passes. Meanwhile seven services reach a model directly through internal/ai:

service what it asks a model for
chat composes replies in a room — carries its own prompts.json
news summaries and sentiment
blog
apps
images image generation
text
web

The rule is enforced against an import path (agent/), so a service that imports internal/ai instead does the forbidden thing and passes. internal/ai is legitimately substrate — a service may know the runtime — which is exactly what makes it a hole rather than an obvious violation.

This is the shape agent/moderate was extracted to fix: "an analyzer function variable inside internal/flag, filled in at boot by service/chat: three services asking a model what their own answer should be, through an edge no layering test could see." The extraction fixed the three; the pattern stayed.

They are not all the same act

Worth settling before any sweep, or this "fixes" things into something worse:

  • Generating an image is the state images answers about. A caller asks for an image and gets one; the model is the implementation, like an HTTP call to a provider. Same for text. These are probably fine.
  • Composing a reply in a room is a judgement. So is deciding a headline is worth surfacing, which is why agent/social exists and service/social does not do it.

The line is roughly: is the model producing the answer the caller asked for, or deciding what the answer should be? The first is a service using a tool. The second is a service being an agent.

What to do

  • Draw the line above properly and write it into CLAUDE.md next to the existing rule
  • Move the judgement cases out — chat first, tracked in #89, where it also unblocks XMPP
  • Make the test check what the rule means: a service reaching a model for a judgement, not merely the string agent/ in an import. Counted and pinned like test/service_hooks_test.go does for hooks, so the number cannot go up quietly
  • Leave the tool-shaped cases alone, with the reason recorded so they are not "fixed" later

Related

  • #89 — chat specifically, and why it blocks the XMPP work
  • The hooks ledger (test/service_hooks_test.go) is the precedent for counting an edge rather than forbidding it: "a test that failed on the whole list would be deleted within a week."

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with CLAUDE.md, the seven services listed in the issue, and the existing TestNoNewServiceCallsAnAgent test. Compare its import-path check with the edge-counting precedent in test/service_hooks_test.go, and review #89 for the chat case. Done means the rule is defined, judgment cases are tracked or moved, tool-shaped cases are documented, and the test prevents the counted edge from growing.

Written by the indexing model from the issue text.

Assessment

Tech stack
go
Domain
ai, backend-api-design, testing-qa
Issue type
Refactor
Difficulty
5/5
Estimated time
Over a week
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.