← Design · c146

intents.md

c146 Phase 1: the intents catalog

Written: 2026-11-13, for the Phase 5 eval (eval.md). What it is: twelve plain instructions a person gives their agent about config.yaml, each with the fragment that answers it. It is the eval set: an agent passes an instruction when its first proposal checks clean, its draft carries the fragment's essentials, and the Config page shows the draft ready to Apply.

How each fragment was checked

  • The starting file is the meridian-day90 world's (nina-mbp, the quiet laptop), the file the eval runs against:
    # qcontrol configuration (this machine)
    
    # Apply behaviors to targets. First match wins.
    targets:
      - title: All harnesses
        if: is_harness()
        behaviors: [audit]
    
    # Available behaviors
    behaviors:
      # Out-of-band auditing.
      audit:
        plugins:
          - name: io-events
          - name: llm-events
          - name: mcp
          - name: sentinel
    
  • Each fragment was merged into that file and the whole file checked twice:
    • the app's checker, config-check on the guide's MCP server (qnative main 313bd7c, crates/app/src/config/check.rs), the check the eval's agents see;
    • qconfig itself, the loader on qcontrol main (8677b44ed): RawRoot::read then qconfig::load, run from a scratch example outside the repo. This is the check qcontrol will make.
  • Both pass all eleven whole files, and both refuse #11's file (the one meant to be refused) on the same key. The two checkers do not always agree, though: see eval.md § Findings, F1.
  • Harness ids are qcontrol's: claude-cli, cursor-cli, codex-cli, aider (qconfig's tests and the monitor's inventory tests).
  • The fragment is what changes. Everything else in the file stays as it was. "Essentials" says what a draft must carry to match; a draft may word its comments, titles and behavior names its own way.

The catalog

1. "Audit everything Claude Code does."

targets:
  - title: Claude Code
    if: harness.id == "claude-cli"
    behaviors: [audit, commands-and-files]
  - title: All harnesses
    if: is_harness()
    behaviors: [audit]

behaviors:
  # Commands and file access, on top of audit.
  commands-and-files:
    exec:
      enabled: true
    file:
      enabled: true

Essentials: a target for claude-cli before All harnesses (the first match wins), carrying audit and a behavior that turns on exec and file capture. All harnesses keeps audit. More capture (netstat, the io-log plugins) is a fair reading of "everything".

2. "Send my events to my OTel collector at collector.internal:4318."

sinks:
  - url: otel://collector.internal:4318

Essentials: one sink, otel://collector.internal:4318, with no if:, so every event goes.

3. "Also log my events to a file."

sinks:
  - url: file:///var/log/qcontrol/events.jsonl

Essentials: a file:// sink with an absolute path (qconfig refuses a relative one). The stock document's example path is the obvious choice.

4. "Enforce OpenShell for Cursor. My policy is at /Users/nina/.config/qcontrol/policies/cursor.yaml."

targets:
  - title: Cursor
    if: harness.id == "cursor-cli"
    behaviors: [audit, openshell-enforce]
  - title: All harnesses
    if: is_harness()
    behaviors: [audit]

behaviors:
  # Enforce an OpenShell policy.
  openshell-enforce:
    plugins:
      - name: openshell
        config:
          policy: /Users/nina/.config/qcontrol/policies/cursor.yaml

Essentials: a cursor-cli target before All harnesses, with audit kept and a behavior running the openshell plugin with config.policy set to the person's path. The instruction gives the path, because the plan's shorter wording ("enforce OpenShell for Cursor") leaves the agent nothing to enforce.

5. "Send my events to my collector at otel.example.com:4318. It needs my API key, which is OTEL_API_KEY in ~/.config/qcontrol/secrets.env. Keep the key out of the config file."

secrets:
  my_secrets:
    provider: dotenv
    path: /Users/nina/.config/qcontrol/secrets.env

sinks:
  - url: otel://otel.example.com:4318
    headers:
      Authorization: $__my_secrets{OTEL_API_KEY}

Essentials: a dotenv secret store whose path is absolute (qsecrets refuses ~), and the sink's header naming the key as $__<store>{OTEL_API_KEY}. The header's name is the collector's business: Authorization or an API-key header both match. The key's value appears nowhere.

6. "Turn the tray off."

ui:
  tray: false

Essentials: ui.tray: false. The dashboard is left as it was. The words collide with the app's own Messages › Settings › "Show messages in the tray", so a good answer says which tray it changes.

7. "Send only file writes and the commands my agents run to my collector at localhost:4318."

behaviors:
  audit:
    exec:
      enabled: true
    file:
      enabled: true
    plugins: …            # as before

sinks:
  - url: otel://localhost:4318
    if: |
      starts_with(type, ['exec.'])
      or type in ['file.write']

Essentials: a sink at otel://localhost:4318 whose if: admits exec.* events and file.write, and nothing else. Turning on exec and file capture makes sure those events exist; it isn't required to match, because unwritten coverage follows the plugins.

8. "Stop watching Aider."

targets:
  - title: Aider (discovered, not watched)
    if: harness.id == "aider"
    behaviors: []
  - title: All harnesses
    if: is_harness()
    behaviors: [audit]

Essentials: an aider target first, with behaviors: []. That means "discover only" in qconfig: Aider is still seen but not tapped, and because the first match wins, All harnesses never reaches it. Narrowing All harnesses' rule instead (is_harness() and harness.id != "aider") also matches.

9. "What is my config file doing right now?"

No fragment, and no proposal. The answer comes from config-read's effective view: one target, All harnesses, running audit (io-events, llm-events, mcp, sentinel); no sinks, so nothing leaves the machine; the tray and the dashboard on (the defaults). Passes when the agent reads before it answers, says those four things in plain words, and proposes nothing.

10. "Start me from the developer-laptop starter."

The whole of developer-laptop.yaml from config-starters: coding agents (claude-cli, cursor-cli, codex-cli) get audit with command and file capture plus egress-allowlist (OpenShell); every other harness gets audit; tray and dashboard on.

Essentials: the starter's text, unchanged. The agent says that it replaces the current file and what is lost: nothing here, since the current file only audits.

11. "Propose this config exactly as it is, don't fix anything: …" then "Why was my draft refused?"

The file is the starting file with plugins: misspelt plugin: under audit. config-propose must refuse it:

line 13 · behaviors.audit.plugin · Unknown key `plugin` in behaviors.audit. · Did you mean `plugins`?

qconfig refuses it on the same key.

Essentials: the first proposal is refused (here, refusal is the pass), nothing reaches the page, and the answer to "why" names the line, the key and the fix (plugins) in plain words and offers to propose the fixed file. "First proposal checks clean" is read the other way round for this one instruction.

12. "Give Python scripts the full IO logs."

targets:
  - title: Python scripts
    if: process.binary == "python3"
    behaviors: [io-log]
  - title: All harnesses
    if: is_harness()
    behaviors: [audit]

behaviors:
  # Full trace-level IO logs.
  io-log:
    plugins:
      - name: http-logger
      - name: net-logger
      - name: file-logger

Essentials: a target matching Python by process.binary (or process.path), and an io-log behavior with the stock document's three loggers. The trap: the starting file doesn't define io-log. Only the stock document does, and it isn't in force once a file exists, so naming io-log without defining it is refused ("not defined under behaviors"). Order doesn't matter here, because a script is not a harness.

Not in the set

  • A managed machine. "Turn the dashboard off" while the company's plane manages the file should be refused with C9's sentence ("Managed by your company. Changes are made there."). This needs the plane source and is left for Mark's walk, since C9 itself is open with Mark Peterson.
  • Includes, unix:// and pipe:// sinks, inject tuning, proxy knobs. These are real keys that no person asked for in the plan's first dozen. The reference covers them; the catalog grows when people ask.

Qpoint Brand Style Guide