Skip to content

Commit c38b663

Browse files
committed
Focus agent contract on repository invariants
1 parent f0db5ab commit c38b663

1 file changed

Lines changed: 10 additions & 196 deletions

File tree

AGENTS.md

Lines changed: 10 additions & 196 deletions
Original file line numberDiff line numberDiff line change
@@ -1,199 +1,13 @@
1-
# qyl engineering contract
1+
# qyl repository contract
22

3-
This is the only editable agent/contributor instruction file in this repository.
4-
`CLAUDE.md` is a symlink to it. Keep public explanation in `README.md`, released history in release notes, and
5-
executable truth in code, schemas, generators, and tests. Do not add progress diaries, repair prompts, handoff
6-
documents, or a second rules file — every duplicated statement is a future contradiction.
3+
Owns collector behavior, OTLP ingestion, normalization, DuckDB persistence,
4+
product API, embedded dashboard, CLI, Runner, and workflow observation. The
5+
normative cross-repository architecture is `ARCHITECTURE-1.0.0.md`.
76

8-
## Where the law lives
7+
OTLP messages remain OpenTelemetry-owned. Public qyl HTTP/SSE/Runner/workflow
8+
models come from `Qyl.Api.Contracts`. Physical rows, SQL, retention, and
9+
checkpoints remain private. The collector must never export telemetry to its own
10+
ingest endpoint. NativeAOT is the collector delivery contract.
911

10-
`ARCHITECTURE-1.0.0.md` in this repository is the normative architecture (last revised 2026-07-28; reality keeps moving —
11-
divergence is a migration item, never a reason to doubt the document):
12-
component taxonomy, boundary law, the exhaustive dependency-edge list, the two generated loops, and gates G1–G11. Every
13-
other qyl repository points at it.
14-
`docs/component-taxonomy.html` is a view of the same content plus the target ↔ today naming ledger; on any conflict the
15-
Markdown wins.
16-
17-
This file does not restate the architecture. If you find yourself needing a fact about boundaries, edges, loops, or
18-
gates, read it there — a paraphrase here would be a second contract owner, which is the exact failure the architecture
19-
forbids.
20-
21-
## Definition of done
22-
23-
The standing goal is complete when gates G1–G11 pass, the `Ci` target below is green, and every touched repository is
24-
clean and pushed. Gates are the arbiter: a claim of completion without the corresponding gate output is not evidence.
25-
Gates arbitrate only what they measure — keep them aligned with the task's goal (upstream and downstream adjusted in
26-
the same change), and never count something impure by nature (network, registries, external state) as guaranteed merely
27-
because a gate went green.
28-
29-
Two classes of action are human-gated; everything else is autonomous. The uncertainty rule of thumb, and when an
30-
agent-performed action must be cited in the completion report:
31-
32-
- Pushed outside ANcpLua, or irreversible / permanently traceable → **uncertain**: name the action explicitly when
33-
reporting the task done.
34-
- Undoable, done before and known to work, or pure with no possible side effect → **not uncertain**: proceed with the
35-
regular pipeline and its default checks, no extra ceremony.
36-
37-
- **Registry-irreversible steps** — publishing to nuget.org/npm and unlisting package IDs. Prepare, verify, and stop
38-
with the exact command ready. This gate is structural, not conventional: in any repo whose CI would publish on a push
39-
to `main`, converting that trigger to a version tag **precedes every other push** to that repo — the tag is the human
40-
act, and after the conversion a rename can land on `main` with zero registry effect.
41-
- **Amending the decree** — changing what a gate *means* in
42-
`ARCHITECTURE-1.0.0.md`. If a gate is defective, annotate the defect in place with measured evidence and stop; rewrite
43-
only with explicit authorization in the goal. (Implementing a gate is normal work; redefining it is not.)
44-
45-
When you deviate from an instruction, say so and say why — a reasoned deviation report is worth more than silent
46-
compliance and far more than silent deviation.
47-
48-
## When code contradicts the law
49-
50-
You will find code that contradicts `ARCHITECTURE-1.0.0.md` — that is what a migration looks like from the inside, and
51-
your measured evidence is not a misreading. The document states the target; the code states today; the ledger reconciles
52-
the two. The protocol:
53-
54-
1. Verify the contradiction with one command and keep the output.
55-
2. Check the ledger — most contradictions are already registered as fold or rename rows. A registered gap needs no
56-
further investigation.
57-
3. A genuinely new gap gets a ledger row in the same commit as the evidence.
58-
4. Close the gap if it's in the active work order; otherwise proceed past it.
59-
60-
A contradiction never justifies stalling, a workspace-wide re-audit, or a silent edit to the law. **The ledger is the
61-
audit** — extend it, don't re-derive it; discovering the full rename scope is not a prerequisite for starting the first
62-
item of it.
63-
64-
Scope words in goals mean this: code-level work — renames, folds, deletions, generator and test changes — is always
65-
*execute* scope. Only the two registry-irreversible actions above are *prepare-and-stop* scope. "Prepare the rename" is
66-
not an instruction to audit instead of act.
67-
68-
## Fail-closed invariants (placement and reasoning)
69-
70-
The architecture states the self-export invariant; these are the layers in this codebase and why each exists. A rename
71-
must move them intact:
72-
73-
- `CollectorSelfExportGuard.ThrowIfSelfExporting` — sits directly after
74-
`AddQylCollectorCore` because that is where the ports become known and nothing is bound yet. It reads the explicit
75-
endpoint, so it is silent when nothing set one.
76-
- `EnableAutoDiscovery = false` in the collector's own service defaults — stops the process from *probing* for a
77-
collector (itself).
78-
- `RequireConfiguredEndpoint = true` on the hosting options — the layer the other two miss: without it the OTLP exporter
79-
falls back to its `localhost`
80-
default, which is this process's own ingest port. This flag makes "no endpoint" mean "do not export".
81-
- `CollectorHealthGuard.ThrowIfHealthSurfaceUnwired` — separate concern, same fail-closed pattern.
82-
83-
The collector consumes the producer stack for self-telemetry only, through the published hosting package like any other
84-
application — never a private copy of the composition logic. `qyl/internal/qyl.instrumentation` (`UseQyl`) once was such
85-
a copy; the architecture fold retired it on 2026-07-26 and the ledger carries that row as done. Re-measured 2026-07-28:
86-
`ActivitySources` holds one constant and no inventory, `CollectorDiscovery` has no second implementation — the file sets
87-
`EnableCollectorDiscovery` on the published options — and the collector's 55-library restored closure carries zero
88-
agent-framework packages. What remains on that row is the rename, not architecture.
89-
90-
What stays on this side of the wire is the part the hosting package cannot know: health checks and
91-
endpoints, exception capture, Kestrel and JSON conventions, and which of this application's own endpoints are span noise
92-
(`HealthProbeSpanFilter`) — a thin collector-defaults layer that *calls* the published composition. Re-deriving OTel
93-
wiring, an ActivitySource inventory, or collector discovery here is how the fork started, so the prohibition outlives the
94-
fork it describes — the testable consequence is
95-
gate G6: the collector fully exercisable with a plain OTLP client and no qyl producer packages.
96-
97-
## Delivery
98-
99-
qyl launched 1.0.0 on 2026-07-28. Work directly on `main`, preserve unrelated user changes, run the repository gates, make one intentional
100-
commit per coherent repository change, and push it. Generated files are changed through their schema or generator and
101-
regenerated in the same commit. Unpublished surfaces may converge directly; published package versions are immutable and
102-
move through new versions (architecture §6.1 owns the rename-versioning policy).
103-
104-
On the wire, metrics are counted, discarded, and acknowledged with
105-
`partial_success` — implement that behavior exactly; the product scope behind it is stated in the architecture (§1, §6).
106-
107-
## Contract ownership — repo-specific consequences
108-
109-
Loop 2 (architecture §4) owns the product-API rule. The consequences inside this repository:
110-
111-
- Do not declare parallel public DTOs in the collector, the CLI runtime, the dashboard, or MCP code. If an internal
112-
shape must cross an HTTP, gRPC, MCP, streaming, or generated-client boundary: change TypeSpec first, regenerate
113-
`Qyl.Api.Contracts` and client artifacts, then map to the generated contract. Accessibility modifiers do not decide
114-
contract status — anything serialized across a boundary is a contract.
115-
- OTLP ingestion's wire contract is the official OpenTelemetry protobuf schema. Vendoring a pinned upstream `.proto`
116-
input is allowed; redefining it as a qyl-owned DTO hierarchy is not.
117-
- Storage rows, ingest batches, query models, and projections may be owned locally precisely because they never cross a
118-
boundary.
119-
120-
## Implementation rules
121-
122-
- A public capability needs an executable owner: a product call path, an owned downstream consumer, or a conformance
123-
application exercising the complete contract. Mock-only tests and imaginary consumers are not acceptance evidence.
124-
- Reuse a released, AOT-compatible upstream implementation when it satisfies the contract. Implement a missing gap only
125-
when qyl needs it, and prove it through a complete executable vertical.
126-
- Tests and fixtures use real protocol types, valid programmatically generated data, or captured-and-sanitized
127-
datasets — hand-shaped JSON, substring checks over binary payloads, and echo-mocks prove nothing about
128-
interoperability.
129-
- `Version.props` owns the product version and shared package-version properties; `Directory.Packages.props` owns the
130-
central `PackageVersion`
131-
entries that consume them. Hardcoded versions elsewhere will drift.
132-
- The toolchain is the `global.json` SDK (`10.0.302`, `latestFeature`) and C# 14. Interceptors are supported on this
133-
SDK; use the current Roslyn APIs.
134-
- Native AOT is the collector's publish contract (`QylAot` defaults on; the Dockerfile publishes the native lane).
135-
`eng/scripts/collector-aot-smoke.sh`
136-
is that lane's executable owner; `-p:QylAot=false` is the JIT diagnostic build with full analyzer enforcement.
137-
- HTTP header attributes are denied at the collector persistence boundary unless the exact span key is in the small
138-
generated safe-header allowlist. Never persist `Authorization`, cookies, `Mcp-Param-*`, or arbitrary tool-defined
139-
header names in spans, logs, resources, entities, fixtures, or exception evidence — a leaked credential in stored
140-
telemetry is unrecoverable.
141-
- Never hand-edit generated C#, protobuf output, TypeScript contracts, or generated reports. Analyzer release manifests
142-
are maintained inputs and change with their analyzer rules.
143-
144-
## MCP telemetry and protocol-era discipline
145-
146-
qyl classifies and stores MCP telemetry, owns the collector semantic catalog, and hosts MCP code of its own. The
147-
2026-07-28 protocol revision changes what several MCP fields mean; these rules bind the ingest and enrichment path and
148-
qyl's MCP host:
149-
150-
- Protocol era is the negotiated protocol version, never the presence of a
151-
`_meta` envelope — the legacy-fallback probe also carries one.
152-
- MCP client and server identity is per-request and self-reported. Never promote `clientInfo` / `serverInfo` to a
153-
telemetry resource attribute, a routing dimension, or a behavior or security decision — display, logging, and
154-
debugging only.
155-
- A multi-round tool call is N linked requests correlated by an opaque, untrusted `requestState`. Render the rounds as
156-
linked spans, never a synthesized parent-child tree, and trust `requestState` only after verification.
157-
- Span and RPC status come from the JSON-RPC and tool outcome, never the HTTP status: a modern-path JSON-RPC error rides
158-
HTTP 400, and an error can arrive in-band on a committed 200. Map from the tool `isError` result and the protocol
159-
error-code family.
160-
- Wire concepts upstream semconv has not defined — `requestState`, round index, `resultType`, `subscriptions/listen`
161-
lifetime, cache hints — enter the collector semantic catalog under the experimental `qyl.mcp.*` staging namespace,
162-
deletion-targeted on every semconv bump that lands an upstream equivalent. Never mint an `mcp.*` alias for an
163-
unratified concept.
164-
165-
## Verification
166-
167-
Run the narrow tests for the changed component and finish repository-wide work with:
168-
169-
```bash
170-
dotnet run --project eng/build/build.csproj -- Ci
171-
```
172-
173-
The `Ci` target builds and tests the backend, builds and tests the product dashboard, runs its embedded Release-product
174-
Playwright smoke, verifies the exact generated contract package, and checks the collector semantic catalog. For
175-
schema-boundary changes, also compile and test the owning `qyl-api-schema`
176-
repository and restore the resulting `Qyl.Api.Contracts` package into a clean consumer.
177-
178-
## Cloudflare skills
179-
180-
`api.qyl.at` is Cloudflare-proxied, so zone behavior is part of this service's
181-
delivery path — the **Network → gRPC** setting in particular, since turning it
182-
off breaks OTLP/gRPC ingest while HTTP keeps working, which reads like a client
183-
bug. Load `/cloudflare:cloudflare` before changing or diagnosing anything at
184-
that layer; it retrieves from current Cloudflare docs rather than from memory.
185-
186-
`/cloudflare:wrangler` and `/cloudflare:web-perf` do not apply here: this repo
187-
deploys no Worker and owns no browser surface. Those belong to `qyl.at`.
188-
189-
## Durable references
190-
191-
- Product and local development: `README.md`
192-
- API authority: `https://github.com/ANcpLua/qyl-api-schema`
193-
- Automatic instrumentation evidence:
194-
`https://github.com/ANcpLua/Qyl.OpenTelemetry.AutoInstrumentation`
195-
- Semantic-convention generation:
196-
`https://github.com/ANcpLua/Qyl.OpenTelemetry.SemanticConventions`
197-
198-
When a claim can be derived from a manifest, generated report, public API baseline, or test, link that evidence instead
199-
of copying it into another Markdown ledger.
12+
Change generated files through their owner and regenerate. Run focused tests,
13+
then `dotnet run --project eng/build/build.csproj -- Ci`.

0 commit comments

Comments
 (0)